update for HEAD-2003091401
[reactos.git] / subsys / system / explorer / shell / shellfs.cpp
1 /*
2  * Copyright 2003 Martin Fuchs
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  */
18
19
20  //
21  // Explorer clone
22  //
23  // shellfs.cpp
24  //
25  // Martin Fuchs, 23.07.2003
26  //
27
28
29 #include "../utility/utility.h"
30 #include "../utility/shellclasses.h"
31
32 #include "../globals.h"
33
34 #include "entries.h"
35 #include "shellfs.h"
36
37
38 bool ShellDirectory::fill_w32fdata_shell(LPCITEMIDLIST pidl, SFGAOF attribs, WIN32_FIND_DATA* pw32fdata, BY_HANDLE_FILE_INFORMATION* pbhfi)
39 {
40         bool bhfi_valid = false;
41
42         if (!( (attribs & SFGAO_FILESYSTEM) && SUCCEEDED(
43                                 SHGetDataFromIDList(_folder, pidl, SHGDFIL_FINDDATA, pw32fdata, sizeof(WIN32_FIND_DATA))) )) {
44                 WIN32_FILE_ATTRIBUTE_DATA fad;
45                 IDataObject* pDataObj;
46
47                 STGMEDIUM medium = {0, {0}, 0};
48                 FORMATETC fmt = {g_Globals._cfStrFName, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
49
50                 HRESULT hr = _folder->GetUIObjectOf(0, 1, &pidl, IID_IDataObject, 0, (LPVOID*)&pDataObj);
51
52                 if (SUCCEEDED(hr)) {
53                         hr = pDataObj->GetData(&fmt, &medium);
54
55                         pDataObj->Release();
56
57                         if (SUCCEEDED(hr)) {
58                                 LPCTSTR path = (LPCTSTR)GlobalLock(medium.UNION_MEMBER(hGlobal));
59                                 UINT sem_org = SetErrorMode(SEM_FAILCRITICALERRORS);
60
61                                 if (GetFileAttributesEx(path, GetFileExInfoStandard, &fad)) {
62                                         pw32fdata->dwFileAttributes = fad.dwFileAttributes;
63                                         pw32fdata->ftCreationTime = fad.ftCreationTime;
64                                         pw32fdata->ftLastAccessTime = fad.ftLastAccessTime;
65                                         pw32fdata->ftLastWriteTime = fad.ftLastWriteTime;
66
67                                         if (!(fad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
68                                                 pw32fdata->nFileSizeLow = fad.nFileSizeLow;
69                                                 pw32fdata->nFileSizeHigh = fad.nFileSizeHigh;
70                                         }
71                                 }
72
73                                 HANDLE hFile = CreateFile(path, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
74                                                                                         0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
75
76                                 if (hFile != INVALID_HANDLE_VALUE) {
77                                         if (GetFileInformationByHandle(hFile, pbhfi))
78                                                 bhfi_valid = true;
79
80                                         CloseHandle(hFile);
81                                 }
82
83                                 SetErrorMode(sem_org);
84
85                                 GlobalUnlock(medium.UNION_MEMBER(hGlobal));
86                                 GlobalFree(medium.UNION_MEMBER(hGlobal));
87                         }
88                 }
89         }
90
91         if (!(attribs & SFGAO_FILESYSTEM))      // Archiv files should not be displayed as folders in explorer view.
92                 if (attribs & (SFGAO_FOLDER|SFGAO_HASSUBFOLDER))
93                         pw32fdata->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
94
95         if (attribs & SFGAO_READONLY)
96                 pw32fdata->dwFileAttributes |= FILE_ATTRIBUTE_READONLY;
97
98         if (attribs & SFGAO_COMPRESSED)
99                 pw32fdata->dwFileAttributes |= FILE_ATTRIBUTE_COMPRESSED;
100
101         return bhfi_valid;
102 }
103
104
105 LPITEMIDLIST ShellEntry::create_absolute_pidl(HWND hwnd)
106 {
107         if (_up/* && _up->_etype==ET_SHELL*/) {
108                 ShellDirectory* dir = static_cast<ShellDirectory*>(_up);
109
110                 return _pidl.create_absolute_pidl(dir->_pidl, hwnd);
111         }
112
113         return &*_pidl;
114 }
115
116
117  // get full path of a shell entry
118 void ShellEntry::get_path(PTSTR path) const
119 {
120         path[0] = TEXT('\0');
121
122         HRESULT hr = path_from_pidl(get_parent_folder(), &*_pidl, path, MAX_PATH);
123 }
124
125
126  // get full path of a shell folder
127 void ShellDirectory::get_path(PTSTR path) const
128 {
129         path[0] = TEXT('\0');
130
131         SFGAOF attribs = 0;
132         HRESULT hr = S_OK;
133
134         if (!_folder.empty())
135                 hr = const_cast<ShellFolder&>(_folder)->GetAttributesOf(1, (LPCITEMIDLIST*)&_pidl, &attribs);
136
137         if (SUCCEEDED(hr) && (attribs&SFGAO_FILESYSTEM))
138                 hr = path_from_pidl(get_parent_folder(), &*_pidl, path, MAX_PATH);
139 }
140
141
142 BOOL ShellEntry::launch_entry(HWND hwnd, UINT nCmdShow)
143 {
144         BOOL ret = TRUE;
145
146         SHELLEXECUTEINFO shexinfo;
147
148         shexinfo.cbSize = sizeof(SHELLEXECUTEINFO);
149         shexinfo.fMask = SEE_MASK_INVOKEIDLIST; // SEE_MASK_IDLIST is also possible.
150         shexinfo.hwnd = hwnd;
151         shexinfo.lpVerb = NULL;
152         shexinfo.lpFile = NULL;
153         shexinfo.lpParameters = NULL;
154         shexinfo.lpDirectory = NULL;
155         shexinfo.nShow = nCmdShow;
156         shexinfo.lpIDList = create_absolute_pidl(hwnd);
157
158         if (!ShellExecuteEx(&shexinfo)) {
159                 display_error(hwnd, GetLastError());
160                 ret = FALSE;
161         }
162
163         if (shexinfo.lpIDList != &*_pidl)
164                 ShellMalloc()->Free(shexinfo.lpIDList);
165
166         return ret;
167 }
168
169
170 static HICON extract_icon(IShellFolder* folder, LPCITEMIDLIST pidl)
171 {
172         IExtractIcon* pExtract;
173
174         if (SUCCEEDED(folder->GetUIObjectOf(0, 1, (LPCITEMIDLIST*)&pidl, IID_IExtractIcon, 0, (LPVOID*)&pExtract))) {
175                 TCHAR path[_MAX_PATH];
176                 unsigned flags;
177                 HICON hIcon;
178                 int idx;
179
180                 if (SUCCEEDED(pExtract->GetIconLocation(GIL_FORSHELL, path, _MAX_PATH, &idx, &flags))) {
181                         if (!(flags & GIL_NOTFILENAME)) {
182                                 if (idx == -1)
183                                         idx = 0;        // special case for some control panel applications
184
185                                 if ((int)ExtractIconEx(path, idx, 0, &hIcon, 1) > 0)
186                                         flags &= ~GIL_DONTCACHE;
187                         } else {
188                                 HICON hIconLarge = 0;
189
190                                 HRESULT hr = pExtract->Extract(path, idx, &hIconLarge, &hIcon, MAKELONG(0/*GetSystemMetrics(SM_CXICON)*/,GetSystemMetrics(SM_CXSMICON)));
191
192                                 if (SUCCEEDED(hr))
193                                         DestroyIcon(hIconLarge);
194                         }
195
196                         return hIcon;
197                 }
198         }
199
200         return 0;
201 }
202
203
204 void ShellDirectory::read_directory()
205 {
206         int level = _level + 1;
207
208         Entry* first_entry = NULL;
209         Entry* last = NULL;
210
211         /*if (_folder.empty())
212                 return;*/
213
214         ShellItemEnumerator enumerator(_folder, SHCONTF_FOLDERS|SHCONTF_NONFOLDERS|SHCONTF_INCLUDEHIDDEN|SHCONTF_SHAREABLE|SHCONTF_STORAGE);
215
216         HRESULT hr_next = S_OK;
217
218         do {
219 #define FETCH_ITEM_COUNT        32
220                 LPITEMIDLIST pidls[FETCH_ITEM_COUNT];
221                 ULONG cnt = 0;
222                 ULONG n;
223
224                 memset(pidls, 0, sizeof(pidls));
225
226                 hr_next = enumerator->Next(FETCH_ITEM_COUNT, pidls, &cnt);
227
228                 /* don't break yet now: Registry Explorer Plugin returns E_FAIL!
229                 if (!SUCCEEDED(hr_next))
230                         break; */
231
232                 if (hr_next == S_FALSE)
233                         break;
234
235                 for(n=0; n<cnt; ++n) {
236                         WIN32_FIND_DATA w32fd;
237                         BY_HANDLE_FILE_INFORMATION bhfi;
238                         bool bhfi_valid = false;
239
240                         memset(&w32fd, 0, sizeof(WIN32_FIND_DATA));
241
242                         SFGAOF attribs = ~SFGAO_FILESYSTEM; //SFGAO_HASSUBFOLDER|SFGAO_FOLDER; SFGAO_FILESYSTEM sorgt dafür, daß "My Documents" anstatt von "Martin's Documents" angezeigt wird
243                         HRESULT hr = _folder->GetAttributesOf(1, (LPCITEMIDLIST*)&pidls[n], &attribs);
244
245                         if (SUCCEEDED(hr)) {
246                                 if (attribs != ~SFGAO_FILESYSTEM)
247                                         bhfi_valid = fill_w32fdata_shell(pidls[n], attribs, &w32fd, &bhfi);
248                                 else
249                                         attribs = 0;
250                         } else
251                                 attribs = 0;
252
253                         Entry* entry;
254
255                         if (w32fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
256                                 entry = new ShellDirectory(this, pidls[n], _hwnd);
257                         else
258                                 entry = new ShellEntry(this, pidls[n]);
259
260                         if (!first_entry)
261                                 first_entry = entry;
262
263                         if (last)
264                                 last->_next = entry;
265
266                         memcpy(&entry->_data, &w32fd, sizeof(WIN32_FIND_DATA));
267
268                         if (bhfi_valid)
269                                 memcpy(&entry->_bhfi, &bhfi, sizeof(BY_HANDLE_FILE_INFORMATION));
270
271                         if (!entry->_data.cFileName[0])
272                                 /*hr = */name_from_pidl(_folder, pidls[n], entry->_data.cFileName, MAX_PATH, SHGDN_INFOLDER|0x2000/*0x2000=SHGDN_INCLUDE_NONFILESYS*/);
273
274                          // get display icons for files and virtual objects
275                         if (!(entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ||
276                                 !(attribs & SFGAO_FILESYSTEM)) {
277                                 entry->_hIcon = extract_icon(_folder, pidls[n]);
278
279                                 if (!entry->_hIcon)
280                                         entry->_hIcon = (HICON)-1;      // don't try again later
281                         }
282
283                         entry->_down = NULL;
284                         entry->_expanded = false;
285                         entry->_scanned = false;
286                         entry->_level = level;
287                         entry->_shell_attribs = attribs;
288                         entry->_bhfi_valid = bhfi_valid;
289
290                         last = entry;
291                 }
292         } while(SUCCEEDED(hr_next));
293
294         if (last)
295                 last->_next = NULL;
296
297         _down = first_entry;
298         _scanned = true;
299 }
300
301 const void* ShellDirectory::get_next_path_component(const void* p)
302 {
303         LPITEMIDLIST pidl = (LPITEMIDLIST)p;
304
305         if (!pidl || !pidl->mkid.cb)
306                 return NULL;
307
308          // go to next element
309         pidl = (LPITEMIDLIST)((LPBYTE)pidl+pidl->mkid.cb);
310
311         return pidl;
312 }
313
314 Entry* ShellDirectory::find_entry(const void* p)
315 {
316         LPITEMIDLIST pidl = (LPITEMIDLIST) p;
317
318         for(Entry*entry=_down; entry; entry=entry->_next) {
319                 ShellEntry* e = static_cast<ShellEntry*>(entry);
320
321                 if (e->_pidl && e->_pidl->mkid.cb==pidl->mkid.cb && !memcmp(e->_pidl, pidl, e->_pidl->mkid.cb))
322                         return entry;
323         }
324
325         return NULL;
326 }