Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1502 lines
37 KiB

  1. /**************************************************************************
  2. THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
  3. ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
  4. THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
  5. PARTICULAR PURPOSE.
  6. Copyright 1998 Microsoft Corporation. All Rights Reserved.
  7. **************************************************************************/
  8. /**************************************************************************
  9. File: ShlFldr.cpp
  10. Description: Implements CShellFolder.
  11. **************************************************************************/
  12. /**************************************************************************
  13. #include statements
  14. **************************************************************************/
  15. #include "ShlFldr.h"
  16. #include "ShlView.h"
  17. #include "ExtrIcon.h"
  18. #include "ContMenu.h"
  19. #include "DataObj.h"
  20. #include "DropTgt.h"
  21. #include "ViewList.h"
  22. #include "Guid.h"
  23. #include "resource.h"
  24. #include "Utility.h"
  25. #include "ParseXML.h"
  26. /**************************************************************************
  27. global variables
  28. **************************************************************************/
  29. #define DEFAULT_DATA TEXT("Data")
  30. extern CViewList *g_pViewList;
  31. /**************************************************************************
  32. CShellFolder::CShellFolder()
  33. **************************************************************************/
  34. CShellFolder::CShellFolder(CShellFolder *pParent, LPCITEMIDLIST pidl)
  35. {
  36. g_DllRefCount++;
  37. m_psfParent = pParent;
  38. if(m_psfParent)
  39. m_psfParent->AddRef();
  40. m_pPidlMgr = new CPidlMgr();
  41. if(!m_pPidlMgr)
  42. {
  43. delete this;
  44. return;
  45. }
  46. //get the shell's IMalloc pointer
  47. //we'll keep this until we get destroyed
  48. if(FAILED(SHGetMalloc(&m_pMalloc)))
  49. {
  50. delete this;
  51. return;
  52. }
  53. m_pidlFQ = NULL;
  54. m_pidlRel = NULL;
  55. if(pidl)
  56. {
  57. m_pidlRel = m_pPidlMgr->Copy(pidl);
  58. }
  59. m_pXMLDoc = NULL;
  60. m_ObjRefCount = 1;
  61. }
  62. /**************************************************************************
  63. CShellFolder::~CShellFolder()
  64. **************************************************************************/
  65. CShellFolder::~CShellFolder()
  66. {
  67. if(m_pidlRel)
  68. {
  69. m_pPidlMgr->Delete(m_pidlRel);
  70. m_pidlRel = NULL;
  71. }
  72. if(m_pidlFQ)
  73. {
  74. m_pPidlMgr->Delete(m_pidlFQ);
  75. m_pidlFQ = NULL;
  76. }
  77. if(m_psfParent)
  78. m_psfParent->Release();
  79. if(m_pMalloc)
  80. {
  81. m_pMalloc->Release();
  82. }
  83. if(m_pPidlMgr)
  84. {
  85. delete m_pPidlMgr;
  86. }
  87. if (m_pXMLDoc)
  88. SAFERELEASE(m_pXMLDoc);
  89. g_DllRefCount--;
  90. }
  91. ///////////////////////////////////////////////////////////////////////////
  92. //
  93. // IUnknown Implementation
  94. //
  95. /**************************************************************************
  96. CShellFolder::QueryInterface
  97. **************************************************************************/
  98. STDMETHODIMP CShellFolder::QueryInterface(REFIID riid, LPVOID *ppReturn)
  99. {
  100. *ppReturn = NULL;
  101. //IUnknown
  102. if(IsEqualIID(riid, IID_IUnknown))
  103. {
  104. *ppReturn = this;
  105. }
  106. //IShellFolder
  107. else if(IsEqualIID(riid, IID_IShellFolder))
  108. {
  109. *ppReturn = (IShellFolder*)this;
  110. }
  111. //IPersist
  112. else if(IsEqualIID(riid, IID_IPersist))
  113. {
  114. *ppReturn = (IPersist*)this;
  115. }
  116. //IPersistFolder
  117. else if(IsEqualIID(riid, IID_IPersistFolder))
  118. {
  119. *ppReturn = (IPersistFolder*)this;
  120. }
  121. if(*ppReturn)
  122. {
  123. (*(LPUNKNOWN*)ppReturn)->AddRef();
  124. return S_OK;
  125. }
  126. return E_NOINTERFACE;
  127. }
  128. #define DC_NAME TEXT("David Campbell")
  129. #define DC_DATA TEXT("Really Loves Cheese")
  130. /**************************************************************************
  131. CShellFolder::AddRef
  132. **************************************************************************/
  133. STDMETHODIMP_(DWORD) CShellFolder::AddRef(VOID)
  134. {
  135. return ++m_ObjRefCount;
  136. }
  137. /**************************************************************************
  138. CShellFolder::Release
  139. **************************************************************************/
  140. STDMETHODIMP_(DWORD) CShellFolder::Release(VOID)
  141. {
  142. if(--m_ObjRefCount == 0)
  143. {
  144. delete this;
  145. return 0;
  146. }
  147. return m_ObjRefCount;
  148. }
  149. ///////////////////////////////////////////////////////////////////////////
  150. //
  151. // IPersist Implementation
  152. //
  153. /**************************************************************************
  154. CShellView::GetClassID()
  155. **************************************************************************/
  156. STDMETHODIMP CShellFolder::GetClassID(LPCLSID lpClassID)
  157. {
  158. *lpClassID = CLSID_SampleNameSpace;
  159. return S_OK;
  160. }
  161. ///////////////////////////////////////////////////////////////////////////
  162. //
  163. // IPersistFolder Implementation
  164. //
  165. /**************************************************************************
  166. CShellView::Initialize()
  167. **************************************************************************/
  168. STDMETHODIMP CShellFolder::Initialize(LPCITEMIDLIST pidlFQ)
  169. {
  170. if(m_pidlFQ)
  171. {
  172. m_pPidlMgr->Delete(m_pidlFQ);
  173. m_pidlFQ = NULL;
  174. }
  175. m_pidlFQ = m_pPidlMgr->Copy(pidlFQ);
  176. return S_OK;
  177. }
  178. ///////////////////////////////////////////////////////////////////////////
  179. //
  180. // IShellFolder Implementation
  181. //
  182. /**************************************************************************
  183. CShellFolder::BindToObject()
  184. **************************************************************************/
  185. STDMETHODIMP CShellFolder::BindToObject( LPCITEMIDLIST pidl,
  186. LPBC pbcReserved,
  187. REFIID riid,
  188. LPVOID *ppvOut)
  189. {
  190. *ppvOut = NULL;
  191. //Make sure the item is a folder.
  192. ULONG ulAttribs = SFGAO_FOLDER;
  193. this->GetAttributesOf(1, &pidl, &ulAttribs);
  194. if(!(ulAttribs & SFGAO_FOLDER))
  195. return E_INVALIDARG;
  196. CShellFolder *pShellFolder = new CShellFolder(this, pidl);
  197. if(!pShellFolder)
  198. return E_OUTOFMEMORY;
  199. LPITEMIDLIST pidlTemp = m_pPidlMgr->Concatenate(m_pidlFQ, pidl);
  200. pShellFolder->Initialize(pidlTemp);
  201. m_pPidlMgr->Delete(pidlTemp);
  202. HRESULT hr = pShellFolder->QueryInterface(riid, ppvOut);
  203. pShellFolder->Release();
  204. return hr;
  205. }
  206. /**************************************************************************
  207. CShellFolder::BindToStorage()
  208. **************************************************************************/
  209. STDMETHODIMP CShellFolder::BindToStorage( LPCITEMIDLIST pidl,
  210. LPBC pbcReserved,
  211. REFIID riid,
  212. LPVOID *ppvOut)
  213. {
  214. *ppvOut = NULL;
  215. return E_NOTIMPL;
  216. }
  217. /**************************************************************************
  218. CShellFolder::CompareIDs()
  219. **************************************************************************/
  220. STDMETHODIMP CShellFolder::CompareIDs( LPARAM lParam,
  221. LPCITEMIDLIST pidl1,
  222. LPCITEMIDLIST pidl2)
  223. {
  224. HRESULT hr = E_FAIL;
  225. LPITEMIDLIST pidlTemp1;
  226. LPITEMIDLIST pidlTemp2;
  227. //walk down the lists, comparing each individual item
  228. pidlTemp1 = (LPITEMIDLIST)pidl1;
  229. pidlTemp2 = (LPITEMIDLIST)pidl2;
  230. while(pidlTemp1 && pidlTemp2)
  231. {
  232. hr = CompareItems(pidlTemp1, pidlTemp2);
  233. if(HRESULT_CODE(hr))
  234. {
  235. //the items are different
  236. break;
  237. }
  238. pidlTemp1 = m_pPidlMgr->GetNextItem(pidlTemp1);
  239. pidlTemp2 = m_pPidlMgr->GetNextItem(pidlTemp2);
  240. if(pidlTemp1 && !pidlTemp1->mkid.cb)
  241. {
  242. pidlTemp1 = NULL;
  243. }
  244. if(pidlTemp2 && !pidlTemp2->mkid.cb)
  245. {
  246. pidlTemp2 = NULL;
  247. }
  248. hr = E_FAIL;
  249. }
  250. if(!pidlTemp1 && pidlTemp2)
  251. {
  252. //pidl1 is at a higher level than pidl2
  253. return MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(-1));
  254. }
  255. else if(pidlTemp1 && !pidlTemp2)
  256. {
  257. //pidl2 is at a higher level than pidl1
  258. return MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(1));
  259. }
  260. else if(SUCCEEDED(hr))
  261. {
  262. //the items are at the same level but are different
  263. return hr;
  264. }
  265. //the items are the same
  266. return MAKE_HRESULT(SEVERITY_SUCCESS, 0, 0);
  267. }
  268. /**************************************************************************
  269. CShellFolder::CreateViewObject()
  270. **************************************************************************/
  271. STDMETHODIMP CShellFolder::CreateViewObject( HWND hwndOwner,
  272. REFIID riid,
  273. LPVOID *ppvOut)
  274. {
  275. HRESULT hr = E_NOINTERFACE;
  276. if(IsEqualIID(riid, IID_IShellView))
  277. {
  278. CShellView *pShellView;
  279. *ppvOut = NULL;
  280. pShellView = new CShellView(this, m_pidlRel);
  281. if(!pShellView)
  282. return E_OUTOFMEMORY;
  283. hr = pShellView->QueryInterface(riid, ppvOut);
  284. pShellView->Release();
  285. }
  286. else if(IsEqualIID(riid, IID_IDropTarget))
  287. {
  288. CDropTarget *pdt = new CDropTarget(this);
  289. if(pdt)
  290. {
  291. *ppvOut = pdt;
  292. return S_OK;
  293. }
  294. }
  295. else if(IsEqualIID(riid, IID_IContextMenu))
  296. {
  297. /*
  298. Create a context menu object for this folder. This can be used for the
  299. background of a view.
  300. */
  301. CContextMenu *pcm = new CContextMenu(this);
  302. if(pcm)
  303. {
  304. *ppvOut = pcm;
  305. return S_OK;
  306. }
  307. }
  308. return hr;
  309. }
  310. /**************************************************************************
  311. CShellFolder::EnumObjects()
  312. **************************************************************************/
  313. STDMETHODIMP CShellFolder::EnumObjects( HWND hwndOwner,
  314. DWORD dwFlags,
  315. LPENUMIDLIST *ppEnumIDList)
  316. {
  317. *ppEnumIDList = NULL;
  318. TCHAR szXMLUrl[MAX_PATH];
  319. LPTSTR pszXMLUrl = szXMLUrl;
  320. HRESULT hr;
  321. if (m_pidlRel == NULL)
  322. {
  323. // The root of namespace
  324. pszXMLUrl = (TCHAR *)g_szXMLUrl;
  325. }
  326. else if (m_pPidlMgr->GetUrl(m_pidlRel, pszXMLUrl, MAX_PATH) < 0 )
  327. return E_FAIL;
  328. if (m_pXMLDoc == NULL)
  329. {
  330. hr = GetSourceXML(&m_pXMLDoc, pszXMLUrl);
  331. if (!SUCCEEDED(hr) || !m_pXMLDoc)
  332. {
  333. SAFERELEASE(m_pXMLDoc);
  334. return hr;
  335. }
  336. BSTR bstrVal;
  337. hr = m_pXMLDoc->get_version(&bstrVal);
  338. // Check if the version is correct ???????
  339. //
  340. SysFreeString(bstrVal);
  341. bstrVal = NULL;
  342. }
  343. *ppEnumIDList = new CEnumIDList(m_pXMLDoc, dwFlags);
  344. if(!*ppEnumIDList)
  345. return E_OUTOFMEMORY;
  346. return S_OK;
  347. }
  348. /**************************************************************************
  349. CShellFolder::GetAttributesOf()
  350. **************************************************************************/
  351. STDMETHODIMP CShellFolder::GetAttributesOf( UINT uCount,
  352. LPCITEMIDLIST aPidls[],
  353. LPDWORD pdwAttribs)
  354. {
  355. UINT i;
  356. if(IsBadWritePtr(pdwAttribs, sizeof(DWORD)))
  357. {
  358. return E_INVALIDARG;
  359. }
  360. if(0 == uCount)
  361. {
  362. /*
  363. This can happen in the Win95 shell when the view is run in rooted mode.
  364. When this occurs, return the attributes for a plain old folder.
  365. */
  366. *pdwAttribs = SFGAO_FOLDER |
  367. SFGAO_HASSUBFOLDER |
  368. SFGAO_BROWSABLE |
  369. SFGAO_DROPTARGET;
  370. }
  371. for(i = 0; i < uCount; i++)
  372. {
  373. DWORD dwAttribs = 0;
  374. //Add the flags common to all items, if applicable.
  375. dwAttribs |= SFGAO_CANRENAME | SFGAO_CANDELETE | SFGAO_CANCOPY | SFGAO_CANMOVE;
  376. //is this item a folder?
  377. if(m_pPidlMgr->IsFolder(m_pPidlMgr->GetLastItem(aPidls[i])))
  378. {
  379. dwAttribs |= SFGAO_FOLDER | SFGAO_BROWSABLE | SFGAO_DROPTARGET | SFGAO_CANLINK;
  380. //does this folder item have any sub folders?
  381. if(HasSubFolder(aPidls[i]))
  382. dwAttribs |= SFGAO_HASSUBFOLDER;
  383. }
  384. *pdwAttribs &= dwAttribs;
  385. }
  386. return S_OK;
  387. }
  388. /**************************************************************************
  389. CShellFolder::GetUIObjectOf()
  390. **************************************************************************/
  391. STDMETHODIMP CShellFolder::GetUIObjectOf( HWND hwndOwner,
  392. UINT uCount,
  393. LPCITEMIDLIST *pPidls,
  394. REFIID riid,
  395. LPUINT puReserved,
  396. LPVOID *ppvOut)
  397. {
  398. *ppvOut = NULL;
  399. if(IsEqualIID(riid, IID_IContextMenu))
  400. {
  401. CContextMenu *pcm = new CContextMenu(this, pPidls, uCount);
  402. if(pcm)
  403. {
  404. *ppvOut = pcm;
  405. return S_OK;
  406. }
  407. }
  408. else if(IsEqualIID(riid, IID_IDataObject))
  409. {
  410. CDataObject *pdo = new CDataObject(this, pPidls, uCount);
  411. if(pdo)
  412. {
  413. *ppvOut = pdo;
  414. return S_OK;
  415. }
  416. }
  417. if(uCount != 1)
  418. return E_INVALIDARG;
  419. if(IsEqualIID(riid, IID_IExtractIcon))
  420. {
  421. CExtractIcon *pei;
  422. LPITEMIDLIST pidl;
  423. pidl = m_pPidlMgr->Concatenate(m_pidlRel, pPidls[0]);
  424. pei = new CExtractIcon(pidl);
  425. /*
  426. The temp PIDL can be deleted because the new CExtractIcon either failed or
  427. made its own copy of it.
  428. */
  429. m_pPidlMgr->Delete(pidl);
  430. if(pei)
  431. {
  432. *ppvOut = pei;
  433. return S_OK;
  434. }
  435. return E_OUTOFMEMORY;
  436. }
  437. else if(IsEqualIID(riid, IID_IDropTarget))
  438. {
  439. CShellFolder *psfTemp = NULL;
  440. BindToObject(pPidls[0], NULL, IID_IShellFolder, (LPVOID*)&psfTemp);
  441. if(psfTemp)
  442. {
  443. CDropTarget *pdt = new CDropTarget(psfTemp);
  444. psfTemp->Release();
  445. if(pdt)
  446. {
  447. *ppvOut = pdt;
  448. return S_OK;
  449. }
  450. }
  451. }
  452. return E_NOINTERFACE;
  453. }
  454. /**************************************************************************
  455. CShellFolder::GetDisplayNameOf()
  456. **************************************************************************/
  457. STDMETHODIMP CShellFolder::GetDisplayNameOf( LPCITEMIDLIST pidl,
  458. DWORD dwFlags,
  459. LPSTRRET lpName)
  460. {
  461. TCHAR szText[MAX_PATH] = TEXT("");
  462. int cchOleStr;
  463. if(dwFlags & SHGDN_FORPARSING)
  464. {
  465. //a "path" is being requested - is it full or relative?
  466. if(dwFlags & SHGDN_INFOLDER)
  467. {
  468. //the relative path is being requested
  469. m_pPidlMgr->GetRelativeName(pidl, szText, ARRAYSIZE(szText));
  470. }
  471. else
  472. {
  473. GetFullName(pidl, szText, ARRAYSIZE(szText));
  474. }
  475. }
  476. else
  477. {
  478. //only the text of the last item is being requested
  479. LPITEMIDLIST pidlLast = m_pPidlMgr->GetLastItem(pidl);
  480. m_pPidlMgr->GetRelativeName(pidlLast, szText, ARRAYSIZE(szText));
  481. }
  482. //put this in to see what SHGDN options are specified for different displays
  483. #if 0
  484. if(dwFlags & SHGDN_FORPARSING)
  485. lstrcat(szText, " [FP]");
  486. if(dwFlags & SHGDN_INFOLDER)
  487. lstrcat(szText, " [IF]");
  488. if(dwFlags & SHGDN_FORADDRESSBAR)
  489. lstrcat(szText, " [AB]");
  490. #endif
  491. //get the number of characters required
  492. cchOleStr = lstrlen(szText) + 1;
  493. //allocate the wide character string
  494. lpName->pOleStr = (LPWSTR)m_pMalloc->Alloc(cchOleStr * sizeof(WCHAR));
  495. if(!lpName->pOleStr)
  496. return E_OUTOFMEMORY;
  497. lpName->uType = STRRET_WSTR;
  498. LocalToWideChar(lpName->pOleStr, szText, cchOleStr);
  499. return S_OK;
  500. }
  501. /**************************************************************************
  502. CShellFolder::ParseDisplayName()
  503. **************************************************************************/
  504. STDMETHODIMP CShellFolder::ParseDisplayName( HWND hwndOwner,
  505. LPBC pbcReserved,
  506. LPOLESTR lpDisplayName,
  507. LPDWORD pdwEaten,
  508. LPITEMIDLIST *pPidlNew,
  509. LPDWORD pdwAttributes)
  510. {
  511. return E_NOTIMPL;
  512. }
  513. /**************************************************************************
  514. CShellFolder::SetNameOf()
  515. **************************************************************************/
  516. STDMETHODIMP CShellFolder::SetNameOf( HWND hwndOwner,
  517. LPCITEMIDLIST pidl,
  518. LPCOLESTR lpName,
  519. DWORD dwFlags,
  520. LPITEMIDLIST *ppidlOut)
  521. {
  522. if(!pidl)
  523. return E_INVALIDARG;
  524. if(m_pPidlMgr->IsFolder(pidl))
  525. {
  526. TCHAR szOld[MAX_PATH];
  527. TCHAR szNew[MAX_PATH];
  528. LPTSTR pszTemp;
  529. LPITEMIDLIST pidlNew;
  530. LPITEMIDLIST pidlFQOld;
  531. LPITEMIDLIST pidlFQNew;
  532. //get the old name
  533. GetPath(pidl, szOld, MAX_PATH);
  534. //build the new name
  535. GetPath(pidl, szNew, MAX_PATH);
  536. for(pszTemp = szNew + lstrlen(szNew) - 1; pszTemp > szNew; pszTemp--)
  537. {
  538. if('\\' == *pszTemp)
  539. {
  540. *(pszTemp + 1) = 0;
  541. break;
  542. }
  543. }
  544. pszTemp = szNew + lstrlen(szNew);
  545. WideCharToLocal(pszTemp, (LPWSTR)lpName, MAX_PATH);
  546. if(!MoveFile(szOld, szNew))
  547. {
  548. MessageBeep(MB_ICONERROR);
  549. return E_FAIL;
  550. }
  551. //create a PIDL for the renamed folder using the relative name
  552. WideCharToLocal(szNew, (LPWSTR)lpName, MAX_PATH);
  553. pidlNew = m_pPidlMgr->CreateFolderPidl(szNew);
  554. pidlFQOld = CreateFQPidl(pidl);
  555. pidlFQNew = CreateFQPidl(pidlNew);
  556. SHChangeNotify(SHCNE_RENAMEFOLDER, SHCNF_IDLIST, pidlFQOld, pidlFQNew);
  557. NotifyViews(SHCNE_RENAMEFOLDER, pidl, pidlNew);
  558. if(ppidlOut)
  559. {
  560. *ppidlOut = pidlNew;
  561. }
  562. else
  563. {
  564. m_pPidlMgr->Delete(pidlNew);
  565. }
  566. m_pPidlMgr->Delete(pidlFQOld);
  567. m_pPidlMgr->Delete(pidlFQNew);
  568. }
  569. else
  570. {
  571. TCHAR szOld[MAX_PATH];
  572. TCHAR szNew[MAX_PATH];
  573. TCHAR szData[MAX_DATA];
  574. TCHAR szFile[MAX_PATH];
  575. LPITEMIDLIST pidlNew;
  576. LPITEMIDLIST pidlFQOld;
  577. LPITEMIDLIST pidlFQNew;
  578. //get the new name
  579. WideCharToLocal(szNew, (LPWSTR)lpName, MAX_PATH);
  580. //get the file name
  581. GetPath(pidl, szFile, MAX_PATH);
  582. //get the old item name
  583. m_pPidlMgr->GetName(pidl, szOld, MAX_PATH);
  584. //get the old item's data
  585. m_pPidlMgr->GetData(pidl, szData, MAX_PATH);
  586. //remove the old entry from the INI file
  587. WritePrivateProfileString( c_szSection,
  588. szOld,
  589. NULL,
  590. szFile);
  591. //add the new entry into the INI file
  592. WritePrivateProfileString( c_szSection,
  593. szNew,
  594. szData,
  595. szFile);
  596. m_pPidlMgr->GetData(pidl, szData, MAX_DATA);
  597. pidlNew = m_pPidlMgr->CreateItemPidl(szNew, szData);
  598. pidlFQOld = CreateFQPidl(pidl);
  599. pidlFQNew = CreateFQPidl(pidlNew);
  600. SHChangeNotify(SHCNE_RENAMEITEM, SHCNF_IDLIST, pidlFQOld, pidlFQNew);
  601. NotifyViews(SHCNE_RENAMEITEM, pidl, pidlNew);
  602. if(0 == lstrcmpi(szNew, DC_NAME))
  603. {
  604. SetItemData((LPCITEMIDLIST)pidlNew, DC_DATA);
  605. }
  606. if(ppidlOut)
  607. {
  608. *ppidlOut = pidlNew;
  609. m_pPidlMgr->Delete((LPITEMIDLIST)pidl);
  610. }
  611. else
  612. {
  613. m_pPidlMgr->Delete(pidlNew);
  614. }
  615. m_pPidlMgr->Delete(pidlFQOld);
  616. m_pPidlMgr->Delete(pidlFQNew);
  617. }
  618. return S_OK;
  619. }
  620. /**************************************************************************
  621. CShellFolder::AddFolder()
  622. **************************************************************************/
  623. STDMETHODIMP CShellFolder::AddFolder(LPCTSTR pszName, LPITEMIDLIST *ppidlOut)
  624. {
  625. HRESULT hr = E_FAIL;
  626. //create a folder
  627. TCHAR szFolder[MAX_PATH] = TEXT("");
  628. if(m_pidlRel)
  629. {
  630. GetPath(NULL, szFolder, MAX_PATH);
  631. }
  632. else
  633. {
  634. lstrcpy(szFolder, g_szStoragePath);
  635. }
  636. SmartAppendBackslash(szFolder);
  637. lstrcat(szFolder, pszName);
  638. if(ppidlOut)
  639. *ppidlOut = NULL;
  640. if(CreateDirectory(szFolder, NULL))
  641. {
  642. LPITEMIDLIST pidl;
  643. //set the attributes that define one of our folders
  644. DWORD dwAttr = GetFileAttributes(szFolder);
  645. SetFileAttributes(szFolder, dwAttr | FILTER_ATTRIBUTES);
  646. //add an empty items.ini file because this also defines one of our folders
  647. TCHAR szFile[MAX_PATH];
  648. lstrcpy(szFile, szFolder);
  649. SmartAppendBackslash(szFile);
  650. lstrcat(szFile, c_szDataFile);
  651. HANDLE hFile;
  652. hFile = CreateFile( szFile,
  653. GENERIC_WRITE,
  654. 0,
  655. NULL,
  656. CREATE_ALWAYS,
  657. FILE_ATTRIBUTE_NORMAL,
  658. NULL);
  659. CloseHandle(hFile);
  660. pidl = m_pPidlMgr->CreateFolderPidl(pszName);
  661. if(pidl)
  662. {
  663. LPITEMIDLIST pidlFQ;
  664. hr = S_OK;
  665. pidlFQ = CreateFQPidl(pidl);
  666. SHChangeNotify(SHCNE_MKDIR, SHCNF_IDLIST, pidlFQ, NULL);
  667. NotifyViews(SHCNE_MKDIR, pidl, NULL);
  668. m_pPidlMgr->Delete(pidlFQ);
  669. if(ppidlOut)
  670. *ppidlOut = pidl;
  671. else
  672. m_pPidlMgr->Delete(pidl);
  673. }
  674. }
  675. return hr;
  676. }
  677. /**************************************************************************
  678. CShellFolder::AddItem()
  679. **************************************************************************/
  680. STDMETHODIMP CShellFolder::AddItem( LPCTSTR pszName,
  681. LPCTSTR pszData,
  682. LPITEMIDLIST *ppidlOut)
  683. {
  684. if(ppidlOut)
  685. *ppidlOut = NULL;
  686. //create an item
  687. HRESULT hr = E_FAIL;
  688. TCHAR szFile[MAX_PATH];
  689. LPCTSTR psz = DEFAULT_DATA;
  690. if(pszData && *pszData)
  691. psz = pszData;
  692. //get the file name
  693. if(m_pidlRel)
  694. {
  695. GetPath(NULL, szFile, MAX_PATH);
  696. }
  697. else
  698. {
  699. lstrcpy(szFile, g_szStoragePath);
  700. }
  701. SmartAppendBackslash(szFile);
  702. lstrcat(szFile, c_szDataFile);
  703. //add the new entry into the INI file
  704. if(WritePrivateProfileString( c_szSection,
  705. pszName,
  706. DEFAULT_DATA,
  707. szFile))
  708. {
  709. LPITEMIDLIST pidl;
  710. pidl = m_pPidlMgr->CreateItemPidl(pszName, psz);
  711. if(pidl)
  712. {
  713. LPITEMIDLIST pidlFQ;
  714. hr = S_OK;
  715. pidlFQ = CreateFQPidl(pidl);
  716. SHChangeNotify(SHCNE_CREATE, SHCNF_IDLIST, pidlFQ, NULL);
  717. NotifyViews(SHCNE_CREATE, pidl, NULL);
  718. m_pPidlMgr->Delete(pidlFQ);
  719. if(ppidlOut)
  720. *ppidlOut = pidl;
  721. else
  722. m_pPidlMgr->Delete(pidl);
  723. }
  724. }
  725. return hr;
  726. }
  727. /**************************************************************************
  728. CShellFolder::SetItemData()
  729. **************************************************************************/
  730. STDMETHODIMP CShellFolder::SetItemData(LPCITEMIDLIST pidl, LPCTSTR pszData)
  731. {
  732. BOOL fResult;
  733. if(m_pPidlMgr->IsFolder(pidl))
  734. {
  735. return E_INVALIDARG;
  736. }
  737. if(!pszData)
  738. fResult = m_pPidlMgr->SetData(pidl, TEXT(""));
  739. else
  740. fResult = m_pPidlMgr->SetData(pidl, pszData);
  741. TCHAR szName[MAX_PATH];
  742. TCHAR szFile[MAX_PATH];
  743. //get the file name
  744. GetPath(pidl, szFile, MAX_PATH);
  745. //get the old item name
  746. m_pPidlMgr->GetName(pidl, szName, MAX_PATH);
  747. //change/add the name in the INI file
  748. WritePrivateProfileString( c_szSection,
  749. szName,
  750. pszData,
  751. szFile);
  752. NotifyViews(SHCNE_UPDATEITEM, pidl, NULL);
  753. return fResult ? S_OK : E_FAIL;
  754. }
  755. /**************************************************************************
  756. CShellFolder::GetFullName()
  757. **************************************************************************/
  758. VOID CShellFolder::GetFullName(LPCITEMIDLIST pidl, LPTSTR pszText, DWORD dwSize)
  759. {
  760. *pszText = 0;
  761. //Get the name of our fully-qualified PIDL from the desktop folder.
  762. IShellFolder *psfDesktop = NULL;
  763. SHGetDesktopFolder(&psfDesktop);
  764. if(psfDesktop)
  765. {
  766. STRRET str;
  767. if(SUCCEEDED(psfDesktop->GetDisplayNameOf( m_pidlFQ,
  768. SHGDN_NORMAL |
  769. SHGDN_FORPARSING |
  770. SHGDN_INCLUDE_NONFILESYS,
  771. &str)))
  772. {
  773. GetTextFromSTRRET(m_pMalloc, &str, m_pidlFQ, pszText, dwSize);
  774. if(*pszText)
  775. {
  776. SmartAppendBackslash(pszText);
  777. }
  778. }
  779. psfDesktop->Release();
  780. }
  781. //add the current item's text
  782. m_pPidlMgr->GetRelativeName( pidl,
  783. pszText + lstrlen(pszText),
  784. dwSize - lstrlen(pszText));
  785. }
  786. /**************************************************************************
  787. CShellFolder::GetUniqueName()
  788. **************************************************************************/
  789. #define NEW_FOLDER_NAME TEXT("New Folder")
  790. #define NEW_ITEM_NAME TEXT("New Item")
  791. STDMETHODIMP CShellFolder::GetUniqueName(BOOL fFolder, LPTSTR pszName, DWORD dwSize)
  792. {
  793. HRESULT hr;
  794. IEnumIDList *pEnum = NULL;
  795. LPTSTR pszTemp;
  796. if(fFolder)
  797. {
  798. pszTemp = NEW_FOLDER_NAME;
  799. }
  800. else
  801. {
  802. pszTemp = NEW_ITEM_NAME;
  803. }
  804. hr = EnumObjects(NULL, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS | SHCONTF_INCLUDEHIDDEN, &pEnum);
  805. if(pEnum)
  806. {
  807. BOOL fUnique = FALSE;
  808. lstrcpyn(pszName, pszTemp, dwSize);
  809. while(!fUnique)
  810. {
  811. //see if this name already exists in this folder
  812. LPITEMIDLIST pidl;
  813. DWORD dwFetched;
  814. int i = 1;
  815. next:
  816. pEnum->Reset();
  817. while((S_OK == pEnum->Next(1, &pidl, &dwFetched)) && dwFetched)
  818. {
  819. STRRET str;
  820. TCHAR szText[MAX_PATH];
  821. GetDisplayNameOf(pidl, SHGDN_NORMAL | SHGDN_INFOLDER, &str);
  822. GetTextFromSTRRET(m_pMalloc, &str, pidl, szText, MAX_PATH);
  823. if(0 == lstrcmpi(szText, pszName))
  824. {
  825. wsprintf(pszName, TEXT("%s %d"), pszTemp, i++);
  826. goto next;
  827. }
  828. }
  829. fUnique = TRUE;
  830. }
  831. pEnum->Release();
  832. }
  833. return hr;
  834. }
  835. /**************************************************************************
  836. CShellFolder::CreateFQPidl()
  837. **************************************************************************/
  838. LPITEMIDLIST CShellFolder::CreateFQPidl(LPCITEMIDLIST pidl)
  839. {
  840. return m_pPidlMgr->Concatenate(m_pidlFQ, pidl);
  841. }
  842. /**************************************************************************
  843. CShellFolder::GetPath()
  844. **************************************************************************/
  845. VOID CShellFolder::GetPath(LPCITEMIDLIST pidl, LPTSTR pszPath, DWORD dwSize)
  846. {
  847. CShellFolder **ppsf;
  848. CShellFolder *psfCurrent;
  849. int nCount;
  850. *pszPath = 0;
  851. //we need the number of parent items in the chain
  852. for(nCount = 0, psfCurrent = this; psfCurrent; nCount++)
  853. {
  854. psfCurrent = psfCurrent->m_psfParent;
  855. }
  856. ppsf = (CShellFolder**)m_pMalloc->Alloc(nCount * sizeof(CShellFolder*));
  857. if(ppsf)
  858. {
  859. int i;
  860. //fill in the interface pointer array
  861. for(i = 0, psfCurrent = this; i < nCount; i++)
  862. {
  863. *(ppsf + i) = psfCurrent;
  864. psfCurrent = psfCurrent->m_psfParent;
  865. }
  866. //Get the name of the root of our storage.
  867. lstrcpyn(pszPath, g_szStoragePath, dwSize);
  868. SmartAppendBackslash(pszPath);
  869. /*
  870. Starting at the top of the parent chain, walk down, getting the text for
  871. each folder's PIDL.
  872. */
  873. for(i = nCount - 1; i >= 0; i--)
  874. {
  875. psfCurrent = *(ppsf + i);
  876. if(psfCurrent)
  877. {
  878. LPTSTR pszCurrent = pszPath + lstrlen(pszPath);
  879. DWORD dwCurrentSize = dwSize - lstrlen(pszPath);
  880. m_pPidlMgr->GetRelativeName( psfCurrent->m_pidlRel,
  881. pszCurrent,
  882. dwCurrentSize);
  883. SmartAppendBackslash(pszPath);
  884. }
  885. }
  886. //add the item's path
  887. if(pidl)
  888. {
  889. if(m_pPidlMgr->IsFolder(pidl))
  890. {
  891. m_pPidlMgr->GetRelativeName(pidl, pszPath + lstrlen(pszPath),
  892. dwSize - lstrlen(pszPath));
  893. }
  894. else
  895. {
  896. lstrcpyn( pszPath + lstrlen(pszPath),
  897. c_szDataFile,
  898. dwSize - lstrlen(pszPath));
  899. }
  900. }
  901. m_pMalloc->Free(ppsf);
  902. }
  903. }
  904. /**************************************************************************
  905. CShellFolder::HasSubFolder()
  906. **************************************************************************/
  907. BOOL CShellFolder::HasSubFolder(LPCITEMIDLIST pidl)
  908. {
  909. TCHAR szPath[MAX_PATH];
  910. TCHAR szTemp[MAX_PATH];
  911. HANDLE hFind;
  912. WIN32_FIND_DATA wfd;
  913. BOOL fReturn = FALSE;
  914. if(!m_pPidlMgr->IsFolder(pidl))
  915. return FALSE;
  916. GetPath(pidl, szPath, MAX_PATH);
  917. lstrcpy(szTemp, szPath);
  918. SmartAppendBackslash(szTemp);
  919. lstrcat(szTemp, TEXT("*.*"));
  920. hFind = FindFirstFile(szTemp, &wfd);
  921. if(INVALID_HANDLE_VALUE != hFind)
  922. {
  923. do
  924. {
  925. if((FILE_ATTRIBUTE_DIRECTORY & wfd.dwFileAttributes) &&
  926. ((wfd.dwFileAttributes & FILTER_ATTRIBUTES) == FILTER_ATTRIBUTES) &&
  927. lstrcmpi(wfd.cFileName, TEXT(".")) &&
  928. lstrcmpi(wfd.cFileName, TEXT("..")))
  929. {
  930. //We found one of our directories. Make sure it contains a data file.
  931. //build the path of the directory or file found
  932. lstrcpy(szTemp, szPath);
  933. SmartAppendBackslash(szTemp);
  934. lstrcat(szTemp, wfd.cFileName);
  935. SmartAppendBackslash(szTemp);
  936. lstrcat(szTemp, c_szDataFile);
  937. HANDLE hDataFile = FindFirstFile(szTemp, &wfd);
  938. if(INVALID_HANDLE_VALUE != hDataFile)
  939. {
  940. fReturn = TRUE;
  941. FindClose(hDataFile);
  942. break;
  943. }
  944. }
  945. }
  946. while(FindNextFile(hFind, &wfd));
  947. FindClose(hFind);
  948. }
  949. return fReturn;
  950. }
  951. /**************************************************************************
  952. CShellFolder::DeleteItems()
  953. **************************************************************************/
  954. STDMETHODIMP CShellFolder::DeleteItems(LPITEMIDLIST *aPidls, UINT uCount)
  955. {
  956. HRESULT hr = E_FAIL;
  957. UINT i;
  958. for(i = 0; i < uCount; i++)
  959. {
  960. if(m_pPidlMgr->IsFolder(aPidls[i]))
  961. {
  962. TCHAR szPath[MAX_PATH];
  963. GetPath(aPidls[i], szPath, MAX_PATH);
  964. DeleteDirectory(szPath);
  965. LPITEMIDLIST pidlFQ = CreateFQPidl(aPidls[i]);
  966. SHChangeNotify(SHCNE_RMDIR, SHCNF_IDLIST, pidlFQ, NULL);
  967. NotifyViews(SHCNE_RMDIR, aPidls[i], NULL);
  968. m_pPidlMgr->Delete(pidlFQ);
  969. hr = S_OK;
  970. }
  971. else
  972. {
  973. TCHAR szFile[MAX_PATH];
  974. TCHAR szName[MAX_NAME];
  975. //get the file name
  976. GetPath(aPidls[i], szFile, MAX_PATH);
  977. //get the item name
  978. m_pPidlMgr->GetName(aPidls[i], szName, MAX_NAME);
  979. //remove the entry from the INI file
  980. WritePrivateProfileString( c_szSection,
  981. szName,
  982. NULL,
  983. szFile);
  984. LPITEMIDLIST pidlFQ = CreateFQPidl(aPidls[i]);
  985. SHChangeNotify(SHCNE_DELETE, SHCNF_IDLIST, pidlFQ, NULL);
  986. NotifyViews(SHCNE_DELETE, aPidls[i], NULL);
  987. m_pPidlMgr->Delete(pidlFQ);
  988. hr = S_OK;
  989. }
  990. }
  991. return hr;
  992. }
  993. /**************************************************************************
  994. CShellFolder::CopyItems()
  995. **************************************************************************/
  996. STDMETHODIMP CShellFolder::CopyItems( CShellFolder *psfSource,
  997. LPITEMIDLIST *aPidls,
  998. UINT uCount)
  999. {
  1000. HRESULT hr = E_FAIL;
  1001. TCHAR szFromFolder[MAX_PATH];
  1002. TCHAR szToFolder[MAX_PATH];
  1003. UINT i;
  1004. //get the storage path of the folder being copied from
  1005. psfSource->GetPath(NULL, szFromFolder, MAX_PATH);
  1006. SmartAppendBackslash(szFromFolder);
  1007. //get the storage path of the folder being copied to
  1008. this->GetPath(NULL, szToFolder, MAX_PATH);
  1009. SmartAppendBackslash(szToFolder);
  1010. for(i = 0; i < uCount; i++)
  1011. {
  1012. TCHAR szFrom[MAX_PATH];
  1013. TCHAR szTo[MAX_PATH];
  1014. lstrcpy(szFrom, szFromFolder);
  1015. lstrcpy(szTo, szToFolder);
  1016. if(m_pPidlMgr->IsFolder(aPidls[i]))
  1017. {
  1018. LPTSTR pszTemp;
  1019. pszTemp = szFrom + lstrlen(szFrom);
  1020. m_pPidlMgr->GetRelativeName(aPidls[i], pszTemp, MAX_PATH - lstrlen(szFrom));
  1021. SmartAppendBackslash(szTo);
  1022. //need to double NULL terminate the names
  1023. *(szFrom + lstrlen(szFrom) + 1) = 0;
  1024. *(szTo + lstrlen(szTo) + 1) = 0;
  1025. SHFILEOPSTRUCT sfi;
  1026. sfi.hwnd = NULL;
  1027. sfi.wFunc = FO_COPY;
  1028. sfi.pFrom = szFrom;
  1029. sfi.pTo = szTo;
  1030. sfi.fFlags = FOF_NOCONFIRMMKDIR | FOF_SILENT;
  1031. if(0 == SHFileOperation(&sfi))
  1032. {
  1033. LPITEMIDLIST pidlFQ;
  1034. pidlFQ = CreateFQPidl(aPidls[i]);
  1035. SHChangeNotify(SHCNE_MKDIR, SHCNF_IDLIST, pidlFQ, NULL);
  1036. NotifyViews(SHCNE_MKDIR, aPidls[i], NULL);
  1037. m_pPidlMgr->Delete(pidlFQ);
  1038. hr = S_OK;
  1039. }
  1040. }
  1041. else
  1042. {
  1043. TCHAR szName[MAX_NAME];
  1044. TCHAR szData[MAX_DATA];
  1045. lstrcat(szFrom, c_szDataFile);
  1046. lstrcat(szTo, c_szDataFile);
  1047. m_pPidlMgr->GetRelativeName(aPidls[i], szName, MAX_NAME);
  1048. if(GetPrivateProfileString(c_szSection, szName, TEXT(""), szData, MAX_DATA, szFrom))
  1049. {
  1050. //add the entry to the destination
  1051. if(WritePrivateProfileString(c_szSection, szName, szData, szTo))
  1052. {
  1053. LPITEMIDLIST pidlFQ;
  1054. //remove the entry from the source
  1055. WritePrivateProfileString(c_szSection, szName, NULL, szFrom);
  1056. pidlFQ = CreateFQPidl(aPidls[i]);
  1057. SHChangeNotify(SHCNE_CREATE, SHCNF_IDLIST, pidlFQ, NULL);
  1058. NotifyViews(SHCNE_CREATE, aPidls[i], NULL);
  1059. m_pPidlMgr->Delete(pidlFQ);
  1060. hr = S_OK;
  1061. }
  1062. }
  1063. }
  1064. }
  1065. return hr;
  1066. }
  1067. /**************************************************************************
  1068. CShellFolder::NotifyViews()
  1069. This function is used to notify any existing views that something has
  1070. changed. This is necessary because there is no public way to register for
  1071. change notifications that get generated in response to SHChangeNotify.
  1072. Each CShellView adds itself to g_pViewList when it gets created and
  1073. removes itself from g_pViewList when it is destroyed.
  1074. **************************************************************************/
  1075. VOID CShellFolder::NotifyViews( DWORD dwType,
  1076. LPCITEMIDLIST pidlOld,
  1077. LPCITEMIDLIST pidlNew)
  1078. {
  1079. IShellFolder *psfDesktop;
  1080. SHGetDesktopFolder(&psfDesktop);
  1081. if(psfDesktop)
  1082. {
  1083. if(g_pViewList)
  1084. {
  1085. CShellView *pView;
  1086. pView = g_pViewList->GetNextView(NULL);
  1087. while(pView)
  1088. {
  1089. LPITEMIDLIST pidlView = pView->GetFQPidl();
  1090. //is this view a view of this folder?
  1091. HRESULT hr;
  1092. hr = psfDesktop->CompareIDs(0, m_pidlFQ, pidlView);
  1093. if(SUCCEEDED(hr) && 0 == HRESULT_CODE(hr))
  1094. {
  1095. switch(dwType)
  1096. {
  1097. case SHCNE_MKDIR:
  1098. case SHCNE_CREATE:
  1099. pView->AddItem(pidlOld);
  1100. break;
  1101. case SHCNE_RMDIR:
  1102. case SHCNE_DELETE:
  1103. pView->DeleteItem(pidlOld);
  1104. break;
  1105. case SHCNE_RENAMEFOLDER:
  1106. case SHCNE_RENAMEITEM:
  1107. pView->RenameItem(pidlOld, pidlNew);
  1108. break;
  1109. case SHCNE_UPDATEITEM:
  1110. pView->UpdateData(pidlOld);
  1111. break;
  1112. }
  1113. }
  1114. pView = g_pViewList->GetNextView(pView);
  1115. }
  1116. }
  1117. psfDesktop->Release();
  1118. }
  1119. }
  1120. /**************************************************************************
  1121. CShellFolder::CompareItems()
  1122. **************************************************************************/
  1123. STDMETHODIMP CShellFolder::CompareItems( LPCITEMIDLIST pidl1,
  1124. LPCITEMIDLIST pidl2)
  1125. {
  1126. TCHAR szString1[MAX_PATH] = TEXT("");
  1127. TCHAR szString2[MAX_PATH] = TEXT("");
  1128. /*
  1129. Special case - If one of the items is a folder and the other is an item, always
  1130. make the folder come before the item.
  1131. */
  1132. if(m_pPidlMgr->IsFolder(pidl1) != m_pPidlMgr->IsFolder(pidl2))
  1133. {
  1134. return MAKE_HRESULT( SEVERITY_SUCCESS,
  1135. 0,
  1136. USHORT(m_pPidlMgr->IsFolder(pidl1) ? -1 : 1));
  1137. }
  1138. m_pPidlMgr->GetRelativeName(pidl1, szString1, ARRAYSIZE(szString1));
  1139. m_pPidlMgr->GetRelativeName(pidl2, szString2, ARRAYSIZE(szString2));
  1140. return MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(lstrcmpi(szString1, szString2)));
  1141. }