update for HEAD-2003091401
[reactos.git] / subsys / system / explorer / Seashell / SeaShellExt / PIDL.cpp
1 ////////////////////////////////////////////////////////////////////
2 // PIDL.cpp: implementation of the CPIDL class.
3 //
4 // By Oz Solomonovich (osolo@bigfoot.com)
5
6 #include "stdafx.h"
7 #include <atlbase.h>
8 #include "PIDL.h"
9
10 LPSHELLFOLDER CPIDL::m_sfDesktop  = NULL; // cached destkop folder
11 LPMALLOC      CPIDL::m_pAllocator = NULL; // cached system allocator
12
13 CPIDL::pidl_initializer CPIDL::m_initializer; // automatic init. obj
14
15
16 ////////////////////////////////////////////////////////////////////
17 // Initialization object
18 ////////////////////////////////////////////////////////////////////
19
20 // pidl_initializer is used to initialize the static data.  The 
21 // constructor and destructor are automatically called for us when
22 // the program starts/ends.
23
24 CPIDL::pidl_initializer::pidl_initializer()
25 {
26     SHGetDesktopFolder(&m_sfDesktop); // cache d'top folder obj ptr 
27     SHGetMalloc(&m_pAllocator);       // cache sys allocator obj ptr
28 }
29
30 CPIDL::pidl_initializer::~pidl_initializer()
31 {
32     m_sfDesktop->Release();
33     m_pAllocator->Release();
34 }
35
36
37 ////////////////////////////////////////////////////////////////////
38 // CPIDL Implementation
39 ////////////////////////////////////////////////////////////////////
40 CPIDL::CPIDL(LPCTSTR szPath, LPSHELLFOLDER psf) : 
41      m_pidl(NULL) 
42
43         Set(szPath, psf); 
44 }
45
46
47 CPIDL::~CPIDL()
48 {
49     Free();  // just free used memory
50 }
51
52
53 ////////////////////////////////////////////////////////////////////
54 // Assignment Functions
55
56 HRESULT CPIDL::Set(const CPIDL& cpidl)
57 {
58     return MakeCopyOf(cpidl.m_pidl);
59 }
60
61 HRESULT CPIDL::Set(LPITEMIDLIST pidl)
62 {
63     Free();
64     m_pidl = pidl;
65     return ERROR_SUCCESS;
66 }
67
68 HRESULT CPIDL::Set(LPCTSTR szPath, LPSHELLFOLDER psf)
69 {
70     OLECHAR olePath[MAX_PATH];
71     ULONG   chEaten;
72     ULONG   dwAttributes;
73     
74     Free();
75 #ifndef _UNICODE
76     MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, szPath, -1, 
77         olePath, MAX_PATH);
78 #else
79         lstrcpy(olePath,szPath);
80 #endif
81     return psf->ParseDisplayName(NULL, NULL, olePath, &chEaten, 
82         &m_pidl, &dwAttributes);
83 }
84
85 HRESULT CPIDL::MakeCopyOf(LPITEMIDLIST pidl)
86 {
87     Free();
88     if (pidl) {
89         UINT sz = m_pAllocator->GetSize((LPVOID)pidl);
90         AllocMem(sz);
91         CopyMemory((LPVOID)m_pidl, (LPVOID)pidl, sz);
92     }
93     return ERROR_SUCCESS;
94 }
95
96 HRESULT CPIDL::MakeAbsPIDLOf(LPSHELLFOLDER psf, LPITEMIDLIST pidl)
97 {
98 USES_CONVERSION;
99     STRRET  str;
100     HRESULT hr = psf->GetDisplayNameOf(pidl, SHGDN_FORPARSING, &str);
101     if (SUCCEEDED(hr)) {
102         ExtractCStr(str);
103         hr = Set(A2T(str.cStr));
104     }
105     return hr;
106 }
107
108
109 ////////////////////////////////////////////////////////////////////
110 // CPIDL Operations
111
112 void CPIDL::Free()
113 {
114     if (m_pidl) {
115         m_pAllocator->Free(m_pidl);
116         m_pidl = NULL;
117     }
118 }
119
120 #define CB_SIZE  (sizeof(piid->cb))  // size of termination item
121
122 UINT CPIDL::GetSize() const
123 {
124     UINT        cbTotal = 0;
125     LPSHITEMID  piid    = GetFirstItemID();
126     
127     if (piid) {
128         do {
129             cbTotal += piid->cb;
130             GetNextItemID(piid);
131         } while (piid->cb);
132         cbTotal += CB_SIZE; // count the terminator
133     }
134     
135     return cbTotal;
136 }
137
138 void CPIDL::Split(CPIDL& parent, CPIDL& obj) const
139 {
140     int         size = 0;
141     SHITEMID    *piid, *piidLast;
142     
143     // find last item-id and calculate total size of pidl
144     piid = piidLast = &m_pidl->mkid;
145     while (piid->cb)
146     {
147         piidLast = piid;
148         size += (piid->cb);
149         piid =  (SHITEMID *)((LPBYTE)piid + (piid->cb));
150     }
151     
152     // copy parent folder portion
153     size -= piidLast->cb;  // don't count "object" item-id
154         if (size > 0)
155         {
156             parent.AllocMem(size + CB_SIZE);
157                 CopyMemory(parent.m_pidl, m_pidl, size);
158                 ZeroMemory((LPBYTE)parent.m_pidl + size, CB_SIZE); // terminator
159     }
160     // copy "object" portion
161     size = piidLast->cb + CB_SIZE;
162         if (size > 0)
163         {
164             obj.AllocMem(size);
165                 CopyMemory(obj.m_pidl, piidLast, size);
166         }
167 }
168
169 CPIDL CPIDL::operator + (CPIDL& pidl) const
170 {
171     CPIDL ret;
172     Concat(*this, pidl, ret);
173     return ret;
174 }
175
176 void CPIDL::Concat(const CPIDL &a, const CPIDL& b, CPIDL& result)
177 {
178     result.Free();
179     
180     // both empty->empty | a empty->return b | b empty->return a
181     if (a.m_pidl == NULL  &&  b.m_pidl == NULL) return;
182     if (a.m_pidl == NULL) { result.Set(b); return; }
183     if (a.m_pidl == NULL) { result.Set(a); return; }
184     
185     UINT cb1 = a.GetSize() - sizeof(a.m_pidl->mkid.cb);
186     UINT cb2 = b.GetSize(); 
187     result.AllocMem(cb1 + cb2); // allocate enough memory 
188     CopyMemory(result.m_pidl, a.m_pidl, cb1);                 // 1st
189     CopyMemory(((LPBYTE)result.m_pidl) + cb1, b.m_pidl, cb2); // 2nd
190 }
191
192 HRESULT CPIDL::GetUIObjectOf(REFIID riid, LPVOID *ppvOut, 
193     HWND hWnd /*= NULL*/, LPSHELLFOLDER psf /*= m_sfDesktop*/)
194 {
195     CPIDL           parent, obj;
196     LPSHELLFOLDER   psfParent;
197         HRESULT                 hr=S_OK;
198     
199     Split(parent, obj);
200         // if no idl the use desktop folder
201         if (parent.m_pidl == NULL || parent.m_pidl->mkid.cb == 0)
202         {
203                 psfParent = psf;
204                 psfParent->AddRef();
205         }
206         else
207         {
208                 // otherwise get the parent
209              hr = psf->BindToObject(parent, NULL, IID_IShellFolder, 
210                     (LPVOID *)&psfParent); // get the IShellFolder of the parent
211         }
212     if (SUCCEEDED(hr)) 
213         {
214         hr = psfParent->GetUIObjectOf(hWnd, 1, obj, riid, 0, ppvOut);
215                 psfParent->Release();
216     }
217     return hr;
218 }
219
220 void CPIDL::ExtractCStr(STRRET& strRet) const
221 {
222     switch (strRet.uType) 
223     {
224         case STRRET_WSTR:
225         {
226             // pOleStr points to a WCHAR string - convert it to ANSI
227             LPWSTR pOleStr = strRet.pOleStr;
228             WideCharToMultiByte(CP_ACP, 0, pOleStr, -1,
229                 strRet.cStr, MAX_PATH, NULL, NULL);
230             m_pAllocator->Free(pOleStr);
231             break;
232         }
233         
234         case STRRET_OFFSET:
235             // The string lives inside the pidl, so copy it out.
236             strncpy(strRet.cStr, (LPSTR)
237                 ((LPBYTE)m_pidl + strRet.uOffset), MAX_PATH);
238             break;
239     }
240 }
241
242
243 ////////////////////////////////////////////////////////////////////
244 // CPIDL Private Operations
245
246 void CPIDL::AllocMem(int iAllocSize)
247 {
248     Free();
249     m_pidl = (LPITEMIDLIST)m_pAllocator->Alloc(iAllocSize);
250 }