2 * Copyright 2003, 2004 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
31 //#include "entries.h"
34 // allocate and initialise a directory entry
35 Entry::Entry(ENTRY_TYPE etype
)
45 _icon_id
= ICID_UNKNOWN
;
46 _display_name
= _data
.cFileName
;
51 Entry::Entry(Entry
* parent
, ENTRY_TYPE etype
)
61 _icon_id
= ICID_UNKNOWN
;
62 _display_name
= _data
.cFileName
;
67 Entry::Entry(const Entry
& other
)
77 _expanded
= other
._expanded
;
78 _scanned
= other
._scanned
;
79 _level
= other
._level
;
83 _shell_attribs
= other
._shell_attribs
;
84 _display_name
= other
._display_name
==other
._data
.cFileName
? _data
.cFileName
: _tcsdup(other
._display_name
);
85 _type_name
= other
._type_name
? _tcsdup(other
._type_name
): NULL
;
86 _content
= other
._content
? _tcsdup(other
._content
): NULL
;
88 _etype
= other
._etype
;
89 _icon_id
= other
._icon_id
;
92 _bhfi_valid
= other
._bhfi_valid
;
95 // free a directory entry
98 if (_icon_id
> ICID_NONE
)
99 g_Globals
._icon_cache
.free_icon(_icon_id
);
101 if (_display_name
!= _data
.cFileName
)
112 // read directory tree and expand to the given location
113 Entry
* Entry::read_tree(const void* path
, SORT_ORDER sortOrder
, int scan_flags
)
115 CONTEXT("Entry::read_tree()");
121 for(const void*p
=path
; p
&& entry
; ) {
122 entry
->smart_scan(sortOrder
, scan_flags
);
125 entry
->_expanded
= true;
127 Entry
* found
= entry
->find_entry(p
);
128 p
= entry
->get_next_path_component(p
);
137 void Entry::read_directory_base(SORT_ORDER sortOrder
, int scan_flags
)
139 CONTEXT("Entry::read_directory_base()");
141 // call into subclass
142 read_directory(scan_flags
);
144 if (g_Globals
._prescan_nodes
) { //@todo _prescan_nodes should not be used for reading the start menu.
145 for(Entry
*entry
=_down
; entry
; entry
=entry
->_next
)
146 if (entry
->_data
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) {
147 entry
->read_directory(scan_flags
);
148 entry
->sort_directory(sortOrder
);
152 sort_directory(sortOrder
);
158 memset(this, 0, sizeof(Root
));
164 _entry
->free_subentries();
168 // directories first...
169 static int compareType(const WIN32_FIND_DATA
* fd1
, const WIN32_FIND_DATA
* fd2
)
171 int order1
= fd1
->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
;
172 int order2
= fd2
->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
;
174 /* Handle "." and ".." as special case and move them at the very first beginning. */
175 if (order1
&& order2
) {
176 order1
= fd1
->cFileName
[0]!='.'? 1: fd1
->cFileName
[1]=='.'? 2: fd1
->cFileName
[1]=='\0'? 3: 1;
177 order2
= fd2
->cFileName
[0]!='.'? 1: fd2
->cFileName
[1]=='.'? 2: fd2
->cFileName
[1]=='\0'? 3: 1;
180 return order2
==order1
? 0: order2
<order1
? -1: 1;
184 static int compareNothing(const void* arg1
, const void* arg2
)
189 static int compareName(const void* arg1
, const void* arg2
)
191 const WIN32_FIND_DATA
* fd1
= &(*(Entry
**)arg1
)->_data
;
192 const WIN32_FIND_DATA
* fd2
= &(*(Entry
**)arg2
)->_data
;
194 int cmp
= compareType(fd1
, fd2
);
198 return lstrcmpi(fd1
->cFileName
, fd2
->cFileName
);
201 static int compareExt(const void* arg1
, const void* arg2
)
203 const WIN32_FIND_DATA
* fd1
= &(*(Entry
**)arg1
)->_data
;
204 const WIN32_FIND_DATA
* fd2
= &(*(Entry
**)arg2
)->_data
;
205 const TCHAR
*name1
, *name2
, *ext1
, *ext2
;
207 int cmp
= compareType(fd1
, fd2
);
211 name1
= fd1
->cFileName
;
212 name2
= fd2
->cFileName
;
214 ext1
= _tcsrchr(name1
, TEXT('.'));
215 ext2
= _tcsrchr(name2
, TEXT('.'));
227 cmp
= lstrcmpi(ext1
, ext2
);
231 return lstrcmpi(name1
, name2
);
234 static int compareSize(const void* arg1
, const void* arg2
)
236 WIN32_FIND_DATA
* fd1
= &(*(Entry
**)arg1
)->_data
;
237 WIN32_FIND_DATA
* fd2
= &(*(Entry
**)arg2
)->_data
;
239 int cmp
= compareType(fd1
, fd2
);
243 cmp
= fd2
->nFileSizeHigh
- fd1
->nFileSizeHigh
;
250 cmp
= fd2
->nFileSizeLow
- fd1
->nFileSizeLow
;
252 return cmp
<0? -1: cmp
>0? 1: 0;
255 static int compareDate(const void* arg1
, const void* arg2
)
257 WIN32_FIND_DATA
* fd1
= &(*(Entry
**)arg1
)->_data
;
258 WIN32_FIND_DATA
* fd2
= &(*(Entry
**)arg2
)->_data
;
260 int cmp
= compareType(fd1
, fd2
);
264 return CompareFileTime(&fd2
->ftLastWriteTime
, &fd1
->ftLastWriteTime
);
268 static int (*sortFunctions
[])(const void* arg1
, const void* arg2
) = {
269 compareNothing
, // SORT_NONE
270 compareName
, // SORT_NAME
271 compareExt
, // SORT_EXT
272 compareSize
, // SORT_SIZE
273 compareDate
// SORT_DATE
277 void Entry::sort_directory(SORT_ORDER sortOrder
)
279 if (sortOrder
!= SORT_NONE
) {
280 Entry
* entry
= _down
;
285 for(entry
=_down
; entry
; entry
=entry
->_next
)
289 array
= (Entry
**) alloca(len
*sizeof(Entry
*));
292 for(entry
=_down
; entry
; entry
=entry
->_next
)
295 // call qsort with the appropriate compare function
296 qsort(array
, len
, sizeof(array
[0]), sortFunctions
[sortOrder
]);
300 for(p
=array
; --len
; p
++)
309 void Entry::smart_scan(SORT_ORDER sortOrder
, int scan_flags
)
311 CONTEXT("Entry::smart_scan()");
315 read_directory_base(sortOrder
, scan_flags
); ///@todo We could use IShellFolder2::GetDefaultColumn to determine sort order.
320 void Entry::extract_icon()
322 TCHAR path
[MAX_PATH
];
324 ICON_ID icon_id
= ICID_NONE
;
326 if (get_path(path
) && _tcsncmp(path
,TEXT("::{"),3))
327 icon_id
= g_Globals
._icon_cache
.extract(path
);
329 if (icon_id
== ICID_NONE
) {
330 IExtractIcon
* pExtract
;
331 if (SUCCEEDED(GetUIObjectOf(0, IID_IExtractIcon
, (LPVOID
*)&pExtract
))) {
335 if (SUCCEEDED(pExtract
->GetIconLocation(GIL_FORSHELL
, path
, MAX_PATH
, &idx
, &flags
))) {
336 if (flags
& GIL_NOTFILENAME
)
337 icon_id
= g_Globals
._icon_cache
.extract(pExtract
, path
, idx
);
340 idx
= 0; // special case for some control panel applications ("System")
342 icon_id
= g_Globals
._icon_cache
.extract(path
, idx
);
345 /* using create_absolute_pidl() [see below] results in more correct icons for some control panel applets ("NVidia").
346 if (icon_id == ICID_NONE) {
349 if (SHGetFileInfo(path, 0, &sfi, sizeof(sfi), SHGFI_ICON|SHGFI_SMALLICON))
350 icon_id = g_Globals._icon_cache.add(sfi.hIcon)._id;
353 if (icon_id == ICID_NONE) {
354 LPBYTE b = (LPBYTE) alloca(0x10000);
357 FILE* file = fopen(path, "rb");
359 int l = fread(b, 1, 0x10000, file);
363 icon_id = g_Globals._icon_cache.add(CreateIconFromResourceEx(b, l, TRUE, 0x00030000, 16, 16, LR_DEFAULTCOLOR));
369 if (icon_id
== ICID_NONE
) {
372 const ShellPath
& pidl_abs
= create_absolute_pidl();
373 LPCITEMIDLIST pidl
= pidl_abs
;
375 HIMAGELIST himlSys
= (HIMAGELIST
) SHGetFileInfo((LPCTSTR
)pidl
, 0, &sfi
, sizeof(sfi
), SHGFI_SYSICONINDEX
|SHGFI_PIDL
|SHGFI_SMALLICON
);
377 icon_id
= g_Globals
._icon_cache
.add(sfi
.iIcon
);
379 if (SHGetFileInfo((LPCTSTR)pidl, 0, &sfi, sizeof(sfi), SHGFI_PIDL|SHGFI_ICON|SHGFI_SMALLICON))
380 icon_id = g_Globals._icon_cache.add(sfi.hIcon)._id;
389 BOOL
Entry::launch_entry(HWND hwnd
, UINT nCmdShow
)
396 // add path to the recent file list
397 SHAddToRecentDocs(SHARD_PATH
, cmd
);
399 // start program, open document...
400 return launch_file(hwnd
, cmd
, nCmdShow
);
404 HRESULT
Entry::do_context_menu(HWND hwnd
, const POINT
& pos
, CtxMenuInterfaces
& cm_ifs
)
406 ShellPath shell_path
= create_absolute_pidl();
407 LPCITEMIDLIST pidl_abs
= shell_path
;
410 return S_FALSE
; // no action for registry entries, etc.
412 static DynamicFct
<HRESULT(WINAPI
*)(LPCITEMIDLIST
, REFIID
, LPVOID
*, LPCITEMIDLIST
*)> SHBindToParent(TEXT("SHELL32"), "SHBindToParent");
414 if (SHBindToParent
) {
415 IShellFolder
* parentFolder
;
416 LPCITEMIDLIST pidlLast
;
418 // get and use the parent folder to display correct context menu in all cases -> correct "Properties" dialog for directories, ...
419 HRESULT hr
= (*SHBindToParent
)(pidl_abs
, IID_IShellFolder
, (LPVOID
*)&parentFolder
, &pidlLast
);
422 hr
= ShellFolderContextMenu(parentFolder
, hwnd
, 1, &pidlLast
, pos
.x
, pos
.y
, cm_ifs
);
424 parentFolder
->Release();
429 /**@todo use parent folder instead of desktop folder
432 ShellPath parent_path;
435 parent_path = dir->create_absolute_pidl();
437 parent_path = DesktopFolderPath();
439 ShellPath shell_path = create_relative_pidl(parent_path);
440 LPCITEMIDLIST pidl = shell_path;
442 ShellFolder parent_folder = parent_path;
443 return ShellFolderContextMenu(parent_folder, hwnd, 1, &pidl, pos.x, pos.y);
445 return ShellFolderContextMenu(GetDesktopFolder(), hwnd
, 1, &pidl_abs
, pos
.x
, pos
.y
, cm_ifs
);
450 HRESULT
Entry::GetUIObjectOf(HWND hWnd
, REFIID riid
, LPVOID
* ppvOut
)
452 TCHAR path
[MAX_PATH
];
457 ShellPath shell_path(path);
459 IShellFolder* pFolder;
460 LPCITEMIDLIST pidl_last = NULL;
462 static DynamicFct<HRESULT(WINAPI*)(LPCITEMIDLIST, REFIID, LPVOID*, LPCITEMIDLIST*)> SHBindToParent(TEXT("SHELL32"), "SHBindToParent");
467 HRESULT hr = (*SHBindToParent)(shell_path, IID_IShellFolder, (LPVOID*)&pFolder, &pidl_last);
471 ShellFolder shell_folder(pFolder);
473 shell_folder->Release();
475 return shell_folder->GetUIObjectOf(hWnd, 1, &pidl_last, riid, NULL, ppvOut);
480 if (!_up
->get_path(path
))
483 ShellPath
shell_path(path
);
484 ShellFolder
shell_folder(shell_path
);
487 LPWSTR wname
= _data
.cFileName
;
489 WCHAR wname
[MAX_PATH
];
490 MultiByteToWideChar(CP_ACP
, 0, _data
.cFileName
, -1, wname
, MAX_PATH
);
493 LPITEMIDLIST pidl_last
= NULL
;
494 HRESULT hr
= shell_folder
->ParseDisplayName(hWnd
, NULL
, wname
, NULL
, &pidl_last
, NULL
);
499 hr
= shell_folder
->GetUIObjectOf(hWnd
, 1, (LPCITEMIDLIST
*)&pidl_last
, riid
, NULL
, ppvOut
);
501 ShellMalloc()->Free((void*)pidl_last
);
507 // recursively free all child entries
508 void Entry::free_subentries()
510 Entry
*entry
, *next
=_down
;
519 entry
->free_subentries();
526 Entry
* Root::read_tree(LPCTSTR path
, int scan_flags
)
531 entry
= _entry
->read_tree(path
, _sort_order
);
533 entry
= _entry
->read_tree(NULL
, _sort_order
);
535 _entry
->smart_scan();
538 _entry
->_expanded
= true;
545 Entry
* Root::read_tree(LPCITEMIDLIST pidl
, int scan_flags
)
547 return _entry
->read_tree(pidl
, _sort_order
);