2 * Copyright 2003 Martin Fuchs
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.
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.
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
25 // Martin Fuchs, 23.07.2003
29 #include "../utility/utility.h"
30 #include "../utility/shellclasses.h"
31 #include "../globals.h" // for _prescan_nodes
36 // allocate and initialise a directory entry
37 Entry::Entry(ENTRY_TYPE etype
)
47 _icon_id
= ICID_UNKNOWN
;
48 _display_name
= _data
.cFileName
;
51 Entry::Entry(Entry
* parent
, ENTRY_TYPE etype
)
61 _icon_id
= ICID_UNKNOWN
;
62 _display_name
= _data
.cFileName
;
65 Entry::Entry(const Entry
& other
)
75 _expanded
= other
._expanded
;
76 _scanned
= other
._scanned
;
77 _level
= other
._level
;
81 _shell_attribs
= other
._shell_attribs
;
82 _display_name
= other
._display_name
==other
._data
.cFileName
? _data
.cFileName
: _tcsdup(other
._display_name
);
84 _etype
= other
._etype
;
85 _icon_id
= other
._icon_id
;
88 _bhfi_valid
= other
._bhfi_valid
;
91 // free a directory entry
94 if (_icon_id
> ICID_NONE
)
95 g_Globals
._icon_cache
.free_icon(_icon_id
);
97 if (_display_name
!= _data
.cFileName
)
102 // read directory tree and expand to the given location
103 Entry
* Entry::read_tree(const void* path
, SORT_ORDER sortOrder
)
105 CONTEXT("Entry::read_tree()");
107 HCURSOR old_cursor
= SetCursor(LoadCursor(0, IDC_WAIT
));
110 Entry
* next_entry
= entry
;
112 for(const void*p
=path
; p
&&next_entry
; p
=entry
->get_next_path_component(p
)) {
115 entry
->read_directory(sortOrder
);
118 entry
->_expanded
= true;
120 next_entry
= entry
->find_entry(p
);
123 SetCursor(old_cursor
);
129 void Entry::read_directory(SORT_ORDER sortOrder
, int scan_flags
)
131 CONTEXT("Entry::read_directory(SORT_ORDER)");
133 // call into subclass
134 read_directory(scan_flags
);
136 if (g_Globals
._prescan_nodes
) { //@todo _prescan_nodes should not be used for reading the start menu.
137 for(Entry
*entry
=_down
; entry
; entry
=entry
->_next
)
138 if (entry
->_data
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) {
139 entry
->read_directory(scan_flags
);
140 entry
->sort_directory(sortOrder
);
144 sort_directory(sortOrder
);
150 memset(this, 0, sizeof(Root
));
156 _entry
->free_subentries();
160 // directories first...
161 static int compareType(const WIN32_FIND_DATA
* fd1
, const WIN32_FIND_DATA
* fd2
)
163 int dir1
= fd1
->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
;
164 int dir2
= fd2
->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
;
166 return dir2
==dir1
? 0: dir2
<dir1
? -1: 1;
170 static int compareName(const void* arg1
, const void* arg2
)
172 const WIN32_FIND_DATA
* fd1
= &(*(Entry
**)arg1
)->_data
;
173 const WIN32_FIND_DATA
* fd2
= &(*(Entry
**)arg2
)->_data
;
175 int cmp
= compareType(fd1
, fd2
);
179 return lstrcmpi(fd1
->cFileName
, fd2
->cFileName
);
182 static int compareExt(const void* arg1
, const void* arg2
)
184 const WIN32_FIND_DATA
* fd1
= &(*(Entry
**)arg1
)->_data
;
185 const WIN32_FIND_DATA
* fd2
= &(*(Entry
**)arg2
)->_data
;
186 const TCHAR
*name1
, *name2
, *ext1
, *ext2
;
188 int cmp
= compareType(fd1
, fd2
);
192 name1
= fd1
->cFileName
;
193 name2
= fd2
->cFileName
;
195 ext1
= _tcsrchr(name1
, TEXT('.'));
196 ext2
= _tcsrchr(name2
, TEXT('.'));
208 cmp
= lstrcmpi(ext1
, ext2
);
212 return lstrcmpi(name1
, name2
);
215 static int compareSize(const void* arg1
, const void* arg2
)
217 WIN32_FIND_DATA
* fd1
= &(*(Entry
**)arg1
)->_data
;
218 WIN32_FIND_DATA
* fd2
= &(*(Entry
**)arg2
)->_data
;
220 int cmp
= compareType(fd1
, fd2
);
224 cmp
= fd2
->nFileSizeHigh
- fd1
->nFileSizeHigh
;
231 cmp
= fd2
->nFileSizeLow
- fd1
->nFileSizeLow
;
233 return cmp
<0? -1: cmp
>0? 1: 0;
236 static int compareDate(const void* arg1
, const void* arg2
)
238 WIN32_FIND_DATA
* fd1
= &(*(Entry
**)arg1
)->_data
;
239 WIN32_FIND_DATA
* fd2
= &(*(Entry
**)arg2
)->_data
;
241 int cmp
= compareType(fd1
, fd2
);
245 return CompareFileTime(&fd2
->ftLastWriteTime
, &fd1
->ftLastWriteTime
);
249 static int (*sortFunctions
[])(const void* arg1
, const void* arg2
) = {
250 compareName
, // SORT_NAME
251 compareExt
, // SORT_EXT
252 compareSize
, // SORT_SIZE
253 compareDate
// SORT_DATE
257 void Entry::sort_directory(SORT_ORDER sortOrder
)
259 Entry
* entry
= _down
;
264 for(entry
=_down
; entry
; entry
=entry
->_next
)
268 array
= (Entry
**) alloca(len
*sizeof(Entry
*));
271 for(entry
=_down
; entry
; entry
=entry
->_next
)
274 // call qsort with the appropriate compare function
275 qsort(array
, len
, sizeof(array
[0]), sortFunctions
[sortOrder
]);
279 for(p
=array
; --len
; p
++)
287 void Entry::smart_scan(int scan_flags
)
289 CONTEXT("Entry::smart_scan()");
293 read_directory(SORT_NAME
, scan_flags
); // we could use IShellFolder2::GetDefaultColumn to determine sort order
298 ShellPath
Entry::create_absolute_pidl() const
300 CONTEXT("Entry::create_absolute_pidl()");
302 TCHAR path
[MAX_PATH
];
305 return ShellPath(path
);
311 void Entry::extract_icon()
313 ICON_ID icon_id
= ICID_NONE
;
315 IExtractIcon
* pExtract
;
316 if (SUCCEEDED(GetUIObjectOf(0, IID_IExtractIcon
, (LPVOID
*)&pExtract
))) {
317 TCHAR path
[MAX_PATH
];
321 if (SUCCEEDED(pExtract
->GetIconLocation(GIL_FORSHELL
, path
, MAX_PATH
, &idx
, &flags
))) {
322 if (flags
& GIL_NOTFILENAME
)
323 icon_id
= g_Globals
._icon_cache
.extract(pExtract
, path
, idx
)._id
;
326 idx
= 0; // special case for some control panel applications ("System")
328 icon_id
= g_Globals
._icon_cache
.extract_from_file(path
, idx
)._id
;
331 /* using create_absolute_pidl() [see below] results in more correct icons for some control panel applets ("NVidia").
332 if (icon_id == ICID_NONE) {
335 if (SHGetFileInfo(path, 0, &sfi, sizeof(sfi), SHGFI_ICON|SHGFI_SMALLICON))
336 icon_id = g_Globals._icon_cache.add(sfi.hIcon)._id;
340 if (icon_id == ICID_NONE) {
341 LPBYTE b = (LPBYTE) alloca(0x10000);
344 FILE* file = fopen(path, "rb");
346 int l = fread(b, 1, 0x10000, file);
350 icon_id = g_Globals._icon_cache.add(CreateIconFromResourceEx(b, l, TRUE, 0x00030000, 16, 16, LR_DEFAULTCOLOR));
356 if (icon_id
== ICID_NONE
) {
359 const ShellPath
& pidl_abs
= create_absolute_pidl();
360 LPCITEMIDLIST pidl
= pidl_abs
;
362 if (SHGetFileInfo((LPCTSTR
)pidl
, 0, &sfi
, sizeof(sfi
), SHGFI_PIDL
|SHGFI_ICON
|SHGFI_SMALLICON
)) //@@ besser SHGFI_SYSICONINDEX ?
363 icon_id
= g_Globals
._icon_cache
.add(sfi
.hIcon
)._id
;
370 BOOL
Entry::launch_entry(HWND hwnd
, UINT nCmdShow
)
377 // start program, open document...
378 return launch_file(hwnd
, cmd
, nCmdShow
);
382 HRESULT
Entry::GetUIObjectOf(HWND hWnd
, REFIID riid
, LPVOID
* ppvOut
)
384 TCHAR path
[MAX_PATH
];
389 ShellPath shell_path(path);
391 IShellFolder* pFolder;
392 LPCITEMIDLIST pidl_last = NULL;
394 static DynamicFct<HRESULT(WINAPI*)(LPCITEMIDLIST, REFIID, LPVOID*, LPCITEMIDLIST*)> SHBindToParent(TEXT("SHELL32"), "SHBindToParent");
399 HRESULT hr = (*SHBindToParent)(shell_path, IID_IShellFolder, (LPVOID*)&pFolder, &pidl_last);
403 ShellFolder shell_folder(pFolder);
405 shell_folder->Release();
407 return shell_folder->GetUIObjectOf(hWnd, 1, &pidl_last, riid, NULL, ppvOut);
412 if (!_up
->get_path(path
))
415 ShellPath
shell_path(path
);
416 ShellFolder
shell_folder(shell_path
);
419 LPWSTR wname
= _data
.cFileName
;
421 WCHAR wname
[MAX_PATH
];
422 MultiByteToWideChar(CP_ACP
, 0, _data
.cFileName
, -1, wname
, MAX_PATH
);
425 LPITEMIDLIST pidl_last
= NULL
;
426 HRESULT hr
= shell_folder
->ParseDisplayName(hWnd
, NULL
, wname
, NULL
, &pidl_last
, NULL
);
431 hr
= shell_folder
->GetUIObjectOf(hWnd
, 1, (LPCITEMIDLIST
*)&pidl_last
, riid
, NULL
, ppvOut
);
433 ShellMalloc()->Free((void*)pidl_last
);
439 // recursively free all child entries
440 void Entry::free_subentries()
442 Entry
*entry
, *next
=_down
;
451 entry
->free_subentries();