📄 shellbrowser.cpp
字号:
/*
* Copyright 2003, 2004, 2005, 2006 Martin Fuchs
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
//
// Explorer clone
//
// shellbrowser.cpp
//
// Martin Fuchs, 23.07.2003
//
#include <precomp.h>
#include "../resource.h"
// work around GCC's wide string constant bug
#ifdef __GNUC__
const LPCTSTR C_DRIVE = C_DRIVE_STR;
#endif
ShellBrowser::ShellBrowser(HWND hwnd, HWND hwndFrame, HWND left_hwnd, WindowHandle& right_hwnd, ShellPathInfo& create_info,
BrowserCallback* cb, CtxMenuInterfaces& cm_ifs)
#ifndef __MINGW32__ // IShellFolderViewCB missing in MinGW (as of 25.09.2005)
: super(IID_IShellFolderViewCB),
#else
:
#endif
_hwnd(hwnd),
_hwndFrame(hwndFrame),
_left_hwnd(left_hwnd),
_right_hwnd(right_hwnd),
_create_info(create_info),
_callback(cb),
_cm_ifs(cm_ifs)
{
_pShellView = NULL;
_pDropTarget = NULL;
_last_sel = 0;
_cur_dir = NULL;
_himl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), ILC_MASK|ILC_COLOR24, 2, 0);
ImageList_SetBkColor(_himl, GetSysColor(COLOR_WINDOW));
}
ShellBrowser::~ShellBrowser()
{
(void)TreeView_SetImageList(_left_hwnd, _himl_old, TVSIL_NORMAL);
ImageList_Destroy(_himl);
if (_pShellView)
_pShellView->Release();
if (_pDropTarget) {
_pDropTarget->Release();
_pDropTarget = NULL;
}
if (_right_hwnd) {
DestroyWindow(_right_hwnd);
_right_hwnd = 0;
}
}
void ShellBrowser::Init()
{
CONTEXT("ShellBrowser::Init()");
const String& root_name = GetDesktopFolder().get_name(_create_info._root_shell_path, SHGDN_FORADDRESSBAR);
_root._drive_type = DRIVE_UNKNOWN;
lstrcpy(_root._volname, root_name);
_root._fs_flags = 0;
lstrcpy(_root._fs, TEXT("Desktop"));
_root._entry = new ShellDirectory(GetDesktopFolder(), _create_info._root_shell_path, _hwnd);
_root._entry->read_directory(SCAN_DONT_ACCESS|SCAN_NO_FILESYSTEM); // avoid to handle desktop root folder as file system directory
if (_left_hwnd) {
InitializeTree();
InitDragDrop();
}
jump_to(_create_info._shell_path);
/* already filled by ShellDirectory constructor
lstrcpy(_root._entry->_data.cFileName, TEXT("Desktop")); */
}
void ShellBrowser::jump_to(LPCITEMIDLIST pidl)
{
Entry* entry = NULL;
if (!_cur_dir)
_cur_dir = static_cast<ShellDirectory*>(_root._entry);
//LOG(FmtString(TEXT("ShellBrowser::jump_to(): pidl=%s"), (LPCTSTR)FileSysShellPath(pidl)));
// We could call read_tree() here to iterate through the hierarchy and open all folders
// from _create_info._root_shell_path (_cur_dir) to _create_info._shell_path (pidl).
// To make it easier we just use ILFindChild() instead.
if (_cur_dir) {
static DynamicFct<LPITEMIDLIST(WINAPI*)(LPCITEMIDLIST, LPCITEMIDLIST)> ILFindChild(TEXT("SHELL32"), 24);
if (ILFindChild) {
for(;;) {
LPCITEMIDLIST child_pidl = (*ILFindChild)(_cur_dir->create_absolute_pidl(), pidl);
if (!child_pidl || !child_pidl->mkid.cb)
break;
_cur_dir->smart_scan(SORT_NAME, SCAN_DONT_ACCESS);
entry = _cur_dir->find_entry(child_pidl);
if (!entry)
break;
_cur_dir = static_cast<ShellDirectory*>(entry);
_callback->entry_selected(entry);
}
} else {
_cur_dir->smart_scan(SORT_NAME, SCAN_DONT_ACCESS);
entry = _cur_dir->find_entry(pidl); // This is not correct in the common case, but works on the desktop level.
if (entry) {
_cur_dir = static_cast<ShellDirectory*>(entry);
_callback->entry_selected(entry);
}
}
}
// If not already called, now directly call UpdateFolderView() using pidl
if (!entry)
UpdateFolderView(ShellFolder(pidl));
}
void ShellBrowser::InitializeTree()
{
CONTEXT("ShellBrowserChild::InitializeTree()");
_himl_old = TreeView_SetImageList(_left_hwnd, _himl, TVSIL_NORMAL);
TreeView_SetScrollTime(_left_hwnd, 100);
TV_INSERTSTRUCT tvInsert;
TV_ITEM& tvItem = tvInsert.item;
tvInsert.hParent = 0;
tvInsert.hInsertAfter = TVI_LAST;
tvItem.mask = TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_CHILDREN;
tvItem.lParam = (LPARAM)_root._entry;
tvItem.pszText = _root._volname; //LPSTR_TEXTCALLBACK;
tvItem.iImage = tvItem.iSelectedImage = I_IMAGECALLBACK;
tvItem.cChildren = 1;
HTREEITEM hItem = TreeView_InsertItem(_left_hwnd, &tvInsert);
TreeView_SelectItem(_left_hwnd, hItem);
TreeView_Expand(_left_hwnd, hItem, TVE_EXPAND);
}
bool ShellBrowser::InitDragDrop()
{
CONTEXT("ShellBrowser::InitDragDrop()");
_pDropTarget = new TreeDropTarget(_left_hwnd);
if (!_pDropTarget)
return false;
_pDropTarget->AddRef();
if (FAILED(RegisterDragDrop(_left_hwnd, _pDropTarget))) {//calls addref
_pDropTarget->Release(); // free TreeDropTarget
_pDropTarget = NULL;
return false;
} else
_pDropTarget->Release();
FORMATETC ftetc;
ftetc.dwAspect = DVASPECT_CONTENT;
ftetc.lindex = -1;
ftetc.tymed = TYMED_HGLOBAL;
ftetc.cfFormat = CF_HDROP;
_pDropTarget->AddSuportedFormat(ftetc);
return true;
}
void ShellBrowser::OnTreeGetDispInfo(int idCtrl, LPNMHDR pnmh)
{
CONTEXT("ShellBrowser::OnTreeGetDispInfo()");
LPNMTVDISPINFO lpdi = (LPNMTVDISPINFO)pnmh;
ShellEntry* entry = (ShellEntry*)lpdi->item.lParam;
if (entry) {
if (lpdi->item.mask & TVIF_TEXT)
lpdi->item.pszText = entry->_display_name;
if (lpdi->item.mask & (TVIF_IMAGE|TVIF_SELECTEDIMAGE)) {
if (lpdi->item.mask & TVIF_IMAGE)
lpdi->item.iImage = get_image_idx(
entry->safe_extract_icon((ICONCACHE_FLAGS)(ICF_HICON|ICF_OVERLAYS)));
if (lpdi->item.mask & TVIF_SELECTEDIMAGE)
lpdi->item.iSelectedImage = get_image_idx(
entry->safe_extract_icon((ICONCACHE_FLAGS)(ICF_HICON|ICF_OVERLAYS|ICF_OPEN)));
}
}
}
int ShellBrowser::get_image_idx(int icon_id)
{
if (icon_id != ICID_NONE) {
map<int,int>::const_iterator found = _image_map.find(icon_id);
if (found != _image_map.end())
return found->second;
int idx = ImageList_AddIcon(_himl, g_Globals._icon_cache.get_icon(icon_id).get_hicon());
_image_map[icon_id] = idx;
return idx;
} else
return -1;
}
void ShellBrowser::invalidate_cache()
{
(void)TreeView_SetImageList(_left_hwnd, _himl_old, TVSIL_NORMAL);
ImageList_Destroy(_himl);
_himl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), ILC_MASK|ILC_COLOR24, 2, 0);
ImageList_SetBkColor(_himl, GetSysColor(COLOR_WINDOW));
_himl_old = TreeView_SetImageList(_left_hwnd, _himl, TVSIL_NORMAL);
for(map<int,int>::const_iterator it=_image_map.begin(); it!=_image_map.end(); ++it)
g_Globals._icon_cache.free_icon(it->first);
_image_map.clear();
}
void ShellBrowser::OnTreeItemExpanding(int idCtrl, LPNMTREEVIEW pnmtv)
{
CONTEXT("ShellBrowser::OnTreeItemExpanding()");
if (pnmtv->action == TVE_COLLAPSE)
TreeView_Expand(_left_hwnd, pnmtv->itemNew.hItem, TVE_COLLAPSE|TVE_COLLAPSERESET);
else if (pnmtv->action == TVE_EXPAND) {
ShellDirectory* entry = (ShellDirectory*)TreeView_GetItemData(_left_hwnd, pnmtv->itemNew.hItem);
if (entry)
if (!InsertSubitems(pnmtv->itemNew.hItem, entry, entry->_folder)) {
entry->_shell_attribs &= ~SFGAO_HASSUBFOLDER;
// remove subitem "+"
TV_ITEM tvItem;
tvItem.mask = TVIF_CHILDREN;
tvItem.hItem = pnmtv->itemNew.hItem;
tvItem.cChildren = 0;
TreeView_SetItem(_left_hwnd, &tvItem);
}
}
}
int ShellBrowser::InsertSubitems(HTREEITEM hParentItem, Entry* entry, IShellFolder* pParentFolder)
{
CONTEXT("ShellBrowser::InsertSubitems()");
WaitCursor wait;
int cnt = 0;
SendMessage(_left_hwnd, WM_SETREDRAW, FALSE, 0);
try {
entry->smart_scan(SORT_NAME, SCAN_DONT_ACCESS);
} catch(COMException& e) {
HandleException(e, g_Globals._hMainWnd);
}
// remove old children items
HTREEITEM hchild, hnext;
hnext = TreeView_GetChild(_left_hwnd, hParentItem);
while((hchild=hnext) != 0) {
hnext = TreeView_GetNextSibling(_left_hwnd, hchild);
TreeView_DeleteItem(_left_hwnd, hchild);
}
TV_ITEM tvItem;
TV_INSERTSTRUCT tvInsert;
for(entry=entry->_down; entry; entry=entry->_next) {
#ifndef _LEFT_FILES
if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
#endif
{
// ignore hidden directories
if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
continue;
// ignore directory entries "." and ".."
if (entry->_data.cFileName[0]==TEXT('.') &&
(entry->_data.cFileName[1]==TEXT('\0') ||
(entry->_data.cFileName[1]==TEXT('.') && entry->_data.cFileName[2]==TEXT('\0'))))
continue;
ZeroMemory(&tvItem, sizeof(tvItem));
tvItem.mask = TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_CHILDREN;
tvItem.pszText = LPSTR_TEXTCALLBACK;
tvItem.iImage = tvItem.iSelectedImage = I_IMAGECALLBACK;
tvItem.lParam = (LPARAM)entry;
tvItem.cChildren = entry->_shell_attribs & SFGAO_HASSUBFOLDER? 1: 0;
if (entry->_shell_attribs & SFGAO_SHARE) {
tvItem.mask |= TVIF_STATE;
tvItem.stateMask |= TVIS_OVERLAYMASK;
tvItem.state |= INDEXTOOVERLAYMASK(1);
}
tvInsert.item = tvItem;
tvInsert.hInsertAfter = TVI_LAST;
tvInsert.hParent = hParentItem;
(void)TreeView_InsertItem(_left_hwnd, &tvInsert);
++cnt;
}
}
SendMessage(_left_hwnd, WM_SETREDRAW, TRUE, 0);
return cnt;
}
void ShellBrowser::OnTreeItemSelected(int idCtrl, LPNMTREEVIEW pnmtv)
{
CONTEXT("ShellBrowser::OnTreeItemSelected()");
Entry* entry = (Entry*)pnmtv->itemNew.lParam;
_last_sel = pnmtv->itemNew.hItem;
if (entry)
_callback->entry_selected(entry);
}
void ShellBrowser::OnTreeItemRClick(int idCtrl, LPNMHDR pnmh)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -