2 * Copyright 2003, 2004, 2005 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 "../resource.h"
34 ShellBrowser::ShellBrowser(HWND hwnd
, HWND left_hwnd
, WindowHandle
& right_hwnd
, ShellPathInfo
& create_info
,
35 HIMAGELIST himl
, BrowserCallback
* cb
, CtxMenuInterfaces
& cm_ifs
)
36 #ifndef __MINGW32__ // IShellFolderViewCB missing in MinGW (as of 25.09.2005)
37 : super(IID_IShellFolderViewCB
),
42 _left_hwnd(left_hwnd
),
43 _right_hwnd(right_hwnd
),
44 _create_info(create_info
),
56 ShellBrowser::~ShellBrowser()
58 (void)TreeView_SetImageList(_left_hwnd
, 0, TVSIL_NORMAL
);
61 _pShellView
->Release();
64 _pDropTarget
->Release();
69 DestroyWindow(_right_hwnd
);
75 LRESULT
ShellBrowser::Init(HWND hWndFrame
)
77 CONTEXT("ShellBrowser::Init()");
79 _hWndFrame
= hWndFrame
;
81 const String
& root_name
= GetDesktopFolder().get_name(_create_info
._root_shell_path
, SHGDN_FORADDRESSBAR
);
83 _root
._drive_type
= DRIVE_UNKNOWN
;
84 lstrcpy(_root
._volname
, root_name
);
86 lstrcpy(_root
._fs
, TEXT("Desktop"));
88 _root
._entry
= new ShellDirectory(GetDesktopFolder(), _create_info
._root_shell_path
, _hwnd
);
90 jump_to(_create_info
._shell_path
);
93 _root
._entry
->read_directory();
95 /* already filled by ShellDirectory constructor
96 lstrcpy(_root._entry->_data.cFileName, TEXT("Desktop")); */
101 void ShellBrowser::jump_to(LPCITEMIDLIST pidl
)
107 _cur_dir
= static_cast<ShellDirectory
*>(_root
._entry
);
110 we should call read_tree() here to iterate through the hierarchy and open all folders from shell_info._root_shell_path to shell_info._shell_path
111 _root._entry->read_tree(shell_info._root_shell_path.get_folder(), info._shell_path, SORT_NAME);
112 -> see FileChildWindow::FileChildWindow()
115 LOG(FmtString(TEXT("ShellBrowser::jump_to(): pidl=%s"), (LPCTSTR
)FileSysShellPath(pidl
)));
118 static DynamicFct
<LPITEMIDLIST(WINAPI
*)(LPCITEMIDLIST
, LPCITEMIDLIST
)> ILFindChild(TEXT("SHELL32"), 24);
120 LPCITEMIDLIST child_pidl
;
123 child_pidl
= (*ILFindChild
)(_cur_dir
->create_absolute_pidl(), pidl
);
125 child_pidl
= pidl
; // This is not correct in the common case, but works on the desktop level.
128 _cur_dir
->smart_scan();
130 entry
= _cur_dir
->find_entry(child_pidl
);
133 _cur_dir
= static_cast<ShellDirectory
*>(entry
);
134 _callback
->entry_selected(entry
);
139 //@@ work around as long as we don't iterate correctly through the ShellEntry tree
141 UpdateFolderView(ShellFolder(pidl
));
145 void ShellBrowser::InitializeTree(HIMAGELIST himl
)
147 CONTEXT("ShellBrowserChild::InitializeTree()");
149 (void)TreeView_SetImageList(_left_hwnd
, himl
, TVSIL_NORMAL
);
150 TreeView_SetScrollTime(_left_hwnd
, 100);
152 TV_INSERTSTRUCT tvInsert
;
154 tvInsert
.hParent
= 0;
155 tvInsert
.hInsertAfter
= TVI_LAST
;
157 TV_ITEM
& tvItem
= tvInsert
.item
;
158 tvItem
.mask
= TVIF_PARAM
| TVIF_TEXT
| TVIF_IMAGE
| TVIF_SELECTEDIMAGE
| TVIF_CHILDREN
;
159 tvItem
.lParam
= (LPARAM
)_root
._entry
;
160 tvItem
.pszText
= LPSTR_TEXTCALLBACK
;
161 tvItem
.iImage
= tvItem
.iSelectedImage
= I_IMAGECALLBACK
;
162 tvItem
.cChildren
= 1;
164 HTREEITEM hItem
= TreeView_InsertItem(_left_hwnd
, &tvInsert
);
165 TreeView_SelectItem(_left_hwnd
, hItem
);
166 TreeView_Expand(_left_hwnd
, hItem
, TVE_EXPAND
);
169 bool ShellBrowser::InitDragDrop()
171 CONTEXT("ShellBrowser::InitDragDrop()");
173 _pDropTarget
= new TreeDropTarget(_left_hwnd
);
178 _pDropTarget
->AddRef();
180 if (FAILED(RegisterDragDrop(_left_hwnd
, _pDropTarget
))) {//calls addref
181 _pDropTarget
->Release(); // free TreeDropTarget
186 _pDropTarget
->Release();
190 ftetc
.dwAspect
= DVASPECT_CONTENT
;
192 ftetc
.tymed
= TYMED_HGLOBAL
;
193 ftetc
.cfFormat
= CF_HDROP
;
195 _pDropTarget
->AddSuportedFormat(ftetc
);
201 void ShellBrowser::OnTreeItemRClick(int idCtrl
, LPNMHDR pnmh
)
203 CONTEXT("ShellBrowser::OnTreeItemRClick()");
207 GetCursorPos(&tvhti
.pt
);
208 ScreenToClient(_left_hwnd
, &tvhti
.pt
);
210 tvhti
.flags
= LVHT_NOWHERE
;
211 (void)TreeView_HitTest(_left_hwnd
, &tvhti
);
213 if (TVHT_ONITEM
& tvhti
.flags
) {
214 LPARAM itemData
= TreeView_GetItemData(_left_hwnd
, tvhti
.hItem
);
217 Entry
* entry
= (Entry
*)itemData
;
218 ClientToScreen(_left_hwnd
, &tvhti
.pt
);
220 CHECKERROR(entry
->do_context_menu(_hwnd
, tvhti
.pt
, _cm_ifs
));
225 void ShellBrowser::OnTreeGetDispInfo(int idCtrl
, LPNMHDR pnmh
)
227 CONTEXT("ShellBrowser::OnTreeGetDispInfo()");
229 LPNMTVDISPINFO lpdi
= (LPNMTVDISPINFO
)pnmh
;
230 ShellEntry
* entry
= (ShellEntry
*)lpdi
->item
.lParam
;
233 if (lpdi
->item
.mask
& TVIF_TEXT
)
234 lpdi
->item
.pszText
= entry
->_display_name
;
236 if (lpdi
->item
.mask
& (/*TVIF_TEXT|*/TVIF_IMAGE
|TVIF_SELECTEDIMAGE
)) {
237 ShellPath pidl_abs
= entry
->create_absolute_pidl(); // Caching of absolute PIDLs could enhance performance.
238 LPCITEMIDLIST pidl
= pidl_abs
;
242 if (lpdi->item.mask & TVIF_TEXT)
243 if (SHGetFileInfo((LPCTSTR)pidl, 0, &sfi, sizeof(sfi), SHGFI_PIDL|SHGFI_DISPLAYNAME))
244 lstrcpy(lpdi->item.pszText, sfi.szDisplayName); ///@todo look at cchTextMax if there is enough space available
246 lpdi->item.pszText = entry->_data.cFileName;
248 if (lpdi
->item
.mask
& TVIF_IMAGE
)
249 if ((HIMAGELIST
)SHGetFileInfo((LPCTSTR
)pidl
, 0, &sfi
, sizeof(sfi
), SHGFI_PIDL
|SHGFI_SYSICONINDEX
|SHGFI_SMALLICON
|SHGFI_LINKOVERLAY
) == _himl
)
250 lpdi
->item
.iImage
= sfi
.iIcon
;
252 lpdi
->item
.iImage
= -1;
254 if (lpdi
->item
.mask
& TVIF_SELECTEDIMAGE
)
255 if ((HIMAGELIST
)SHGetFileInfo((LPCTSTR
)pidl
, 0, &sfi
, sizeof(sfi
), SHGFI_PIDL
|SHGFI_SYSICONINDEX
|SHGFI_SMALLICON
|SHGFI_OPENICON
) == _himl
)
256 lpdi
->item
.iSelectedImage
= sfi
.iIcon
;
258 lpdi
->item
.iSelectedImage
= -1;
263 void ShellBrowser::OnTreeItemExpanding(int idCtrl
, LPNMTREEVIEW pnmtv
)
265 CONTEXT("ShellBrowser::OnTreeItemExpanding()");
267 if (pnmtv
->action
== TVE_COLLAPSE
)
268 TreeView_Expand(_left_hwnd
, pnmtv
->itemNew
.hItem
, TVE_COLLAPSE
|TVE_COLLAPSERESET
);
269 else if (pnmtv
->action
== TVE_EXPAND
) {
270 ShellDirectory
* entry
= (ShellDirectory
*)TreeView_GetItemData(_left_hwnd
, pnmtv
->itemNew
.hItem
);
273 if (!InsertSubitems(pnmtv
->itemNew
.hItem
, entry
, entry
->_folder
)) {
274 entry
->_shell_attribs
&= ~SFGAO_HASSUBFOLDER
;
276 // remove subitem "+"
279 tvItem
.mask
= TVIF_CHILDREN
;
280 tvItem
.hItem
= pnmtv
->itemNew
.hItem
;
281 tvItem
.cChildren
= 0;
283 TreeView_SetItem(_left_hwnd
, &tvItem
);
288 int ShellBrowser::InsertSubitems(HTREEITEM hParentItem
, Entry
* entry
, IShellFolder
* pParentFolder
)
290 CONTEXT("ShellBrowser::InsertSubitems()");
296 SendMessage(_left_hwnd
, WM_SETREDRAW
, FALSE
, 0);
300 } catch(COMException
& e
) {
301 HandleException(e
, g_Globals
._hMainWnd
);
305 TV_INSERTSTRUCT tvInsert
;
307 for(entry
=entry
->_down
; entry
; entry
=entry
->_next
) {
309 if (entry
->_data
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
312 ZeroMemory(&tvItem
, sizeof(tvItem
));
314 tvItem
.mask
= TVIF_PARAM
| TVIF_TEXT
| TVIF_IMAGE
| TVIF_SELECTEDIMAGE
| TVIF_CHILDREN
;
315 tvItem
.pszText
= LPSTR_TEXTCALLBACK
;
316 tvItem
.iImage
= tvItem
.iSelectedImage
= I_IMAGECALLBACK
;
317 tvItem
.lParam
= (LPARAM
)entry
;
318 tvItem
.cChildren
= entry
->_shell_attribs
& SFGAO_HASSUBFOLDER
? 1: 0;
320 if (entry
->_shell_attribs
& SFGAO_SHARE
) {
321 tvItem
.mask
|= TVIF_STATE
;
322 tvItem
.stateMask
|= TVIS_OVERLAYMASK
;
323 tvItem
.state
|= INDEXTOOVERLAYMASK(1);
326 tvInsert
.item
= tvItem
;
327 tvInsert
.hInsertAfter
= TVI_LAST
;
328 tvInsert
.hParent
= hParentItem
;
330 (void)TreeView_InsertItem(_left_hwnd
, &tvInsert
);
336 SendMessage(_left_hwnd
, WM_SETREDRAW
, TRUE
, 0);
341 void ShellBrowser::OnTreeItemSelected(int idCtrl
, LPNMTREEVIEW pnmtv
)
343 CONTEXT("ShellBrowser::OnTreeItemSelected()");
345 ShellEntry
* entry
= (ShellEntry
*)pnmtv
->itemNew
.lParam
;
347 _last_sel
= pnmtv
->itemNew
.hItem
;
350 _callback
->entry_selected(entry
);
353 void ShellBrowser::UpdateFolderView(IShellFolder
* folder
)
355 CONTEXT("ShellBrowser::UpdateFolderView()");
358 IShellView
* pLastShellView
= _pShellView
;
363 pLastShellView
->GetCurrentInfo(&fs
);
365 fs
.ViewMode
= _create_info
._open_mode
&OWM_DETAILS
? FVM_DETAILS
: FVM_ICON
;
366 fs
.fFlags
= FWF_NOCLIENTEDGE
|FWF_BESTFITWINDOW
;
369 #ifndef __MINGW32__ // IShellFolderViewCB missing in MinGW (as of 25.09.2005)
370 SFV_CREATE sfv_create
;
372 sfv_create
.cbSize
= sizeof(SFV_CREATE
);
373 sfv_create
.pshf
= folder
;
374 sfv_create
.psvOuter
= NULL
;
375 sfv_create
.psfvcb
= this;
377 HRESULT hr
= SHCreateShellFolderView(&sfv_create
, &_pShellView
);
379 HRESULT hr
= folder
->CreateViewObject(_hwnd
, IID_IShellView
, (void**)&_pShellView
);
387 RECT rect
= {CW_USEDEFAULT
, CW_USEDEFAULT
, CW_USEDEFAULT
, CW_USEDEFAULT
};
388 hr
= _pShellView
->CreateViewWindow(pLastShellView
, &fs
, static_cast<IShellBrowser
*>(this), &rect
, &_right_hwnd
/*&m_hWndListView*/);
390 if (pLastShellView
) {
391 pLastShellView
->GetCurrentInfo(&fs
);
392 pLastShellView
->UIActivate(SVUIA_DEACTIVATE
);
393 pLastShellView
->DestroyViewWindow();
394 pLastShellView
->Release();
397 _pShellView
->UIActivate(SVUIA_ACTIVATE_NOFOCUS
);
401 #ifndef __MINGW32__ // IShellFolderViewCB missing in MinGW (as of 25.09.2005)
403 /// shell view callback
404 HRESULT STDMETHODCALLTYPE
ShellBrowser::MessageSFVCB(UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
406 if (uMsg
== SFVM_INITMENUPOPUP
) {
407 //@todo never reached
408 InsertMenu((HMENU
)lParam
, 0, MF_BYPOSITION
, 12345, TEXT("TEST ENTRY"));
418 HRESULT
ShellBrowser::OnDefaultCommand(LPIDA pida
)
420 CONTEXT("ShellBrowser::OnDefaultCommand()");
422 if (pida
->cidl
>= 1) {
423 if (_left_hwnd
) { // explorer mode
425 ShellDirectory
* parent
= (ShellDirectory
*)TreeView_GetItemData(_left_hwnd
, _last_sel
);
429 parent
->smart_scan();
430 } catch(COMException
& e
) {
434 UINT firstOffset
= pida
->aoffset
[1];
435 LPITEMIDLIST pidl
= (LPITEMIDLIST
)((LPBYTE
)pida
+firstOffset
);
437 Entry
* entry
= parent
->find_entry(pidl
);
439 if (entry
&& (entry
->_data
.dwFileAttributes
&FILE_ATTRIBUTE_DIRECTORY
))
440 if (entry
->_etype
== ET_SHELL
)
441 if (_last_sel
&& select_entry(_last_sel
, entry
))
445 } else { // no tree control
446 if (MainFrameBase::OpenShellFolders(pida
, _hWndFrame
))
449 /* create new Frame Window
450 if (MainFrame::OpenShellFolders(pida, 0))
460 HTREEITEM
ShellBrowser::select_entry(HTREEITEM hitem
, Entry
* entry
, bool expand
)
462 CONTEXT("ShellBrowser::select_entry()");
464 if (expand
&& !TreeView_Expand(_left_hwnd
, hitem
, TVE_EXPAND
))
467 for(hitem
=TreeView_GetChild(_left_hwnd
,hitem
); hitem
; hitem
=TreeView_GetNextSibling(_left_hwnd
,hitem
)) {
468 if ((Entry
*)TreeView_GetItemData(_left_hwnd
,hitem
) == entry
) {
469 if (TreeView_SelectItem(_left_hwnd
, hitem
)) {
471 TreeView_Expand(_left_hwnd
, hitem
, TVE_EXPAND
);
484 bool ShellBrowser::jump_to_pidl(LPCITEMIDLIST pidl
)
489 // iterate through the hierarchy and open all folders to reach pidl
492 HTREEITEM hitem
= TreeView_GetRoot(_left_hwnd
);
493 Entry
* entry
= _root
._entry
;
495 for(const void*p
=pidl
;;) {
499 if (!entry
|| !hitem
)
502 entry
->smart_scan(SORT_NAME
);
504 Entry
* found
= entry
->find_entry(p
);
505 p
= entry
->get_next_path_component(p
);
508 hitem
= select_entry(hitem
, found
);
519 MDIShellBrowserChild::MDIShellBrowserChild(HWND hwnd
, const ShellChildWndInfo
& info
)
522 _shellpath_info(info
) //@@ copies info -> no referenz to _create_info !
524 /**todo Conversion of shell path into path string -> store into URL history
525 const String& path = GetDesktopFolder().get_name(info._shell_path, SHGDN_FORADDRESSBAR);
526 const String& parsingpath = GetDesktopFolder().get_name(info._shell_path, SHGDN_FORPARSING);
528 // store path into history
529 if (info._path && *info._path)
530 _url_history.push(info._path);
535 MDIShellBrowserChild
* MDIShellBrowserChild::create(const ShellChildWndInfo
& info
)
537 ChildWindow
* child
= ChildWindow::create(info
, info
._pos
.rcNormalPosition
,
538 WINDOW_CREATOR_INFO(MDIShellBrowserChild
,ShellChildWndInfo
), CLASSNAME_CHILDWND
, NULL
, info
._pos
.showCmd
==SW_SHOWMAXIMIZED
? WS_MAXIMIZE
: 0);
540 return static_cast<MDIShellBrowserChild
*>(child
);
544 LRESULT
MDIShellBrowserChild::Init(LPCREATESTRUCT pcs
)
546 CONTEXT("MDIShellBrowserChild::Init()");
548 if (super::Init(pcs
))
553 update_shell_browser();
557 _shellBrowser
->Init(_himlSmall
);
559 _shellBrowser
->UpdateFolderView(_create_info
._shell_path
.get_folder());
565 LRESULT
MDIShellBrowserChild::WndProc(UINT nmsg
, WPARAM wparam
, LPARAM lparam
)
568 case PM_DISPATCH_COMMAND
: {
569 switch(LOWORD(wparam
)) {
570 case ID_WINDOW_NEW
: {CONTEXT("MDIShellBrowserChild PM_DISPATCH_COMMAND ID_WINDOW_NEW");
571 MDIShellBrowserChild::create(_create_info
);
575 //@todo refresh shell child
579 MainFrameBase::Create(_url
, false);
583 return super::WndProc(nmsg
, wparam
, lparam
);
588 return super::WndProc(nmsg
, wparam
, lparam
);
594 void MDIShellBrowserChild::update_shell_browser()
596 int split_pos
= DEFAULT_SPLIT_POS
;
598 if (_shellBrowser
.get()) {
599 split_pos
= _split_pos
;
600 delete _shellBrowser
.release();
603 // create explorer treeview
604 if (_create_info
._open_mode
& OWM_EXPLORE
) {
606 ClientRect
rect(_hwnd
);
608 _left_hwnd
= CreateWindowEx(0, WC_TREEVIEW
, NULL
,
609 WS_CHILD
|WS_TABSTOP
|WS_VISIBLE
|WS_CHILD
|TVS_HASLINES
|TVS_LINESATROOT
|TVS_HASBUTTONS
|TVS_SHOWSELALWAYS
,//|TVS_NOTOOLTIPS
610 0, rect
.top
, split_pos
-SPLIT_WIDTH
/2, rect
.bottom
-rect
.top
,
611 _hwnd
, (HMENU
)IDC_FILETREE
, g_Globals
._hInstance
, 0);
615 DestroyWindow(_left_hwnd
);
620 _shellBrowser
= auto_ptr
<ShellBrowser
>(new ShellBrowser(_hwnd
, _left_hwnd
, _right_hwnd
,
621 _shellpath_info
, _himlSmall
, this, _cm_ifs
));
623 _shellBrowser
->Init(_hwndFrame
);
627 String
MDIShellBrowserChild::jump_to_int(LPCTSTR url
)
631 if (!_tcsnicmp(url
, TEXT("shell://"), 8)) {
632 if (_shellBrowser
->jump_to_pidl(ShellPath(url
+8)))
636 if (SplitFileSysURL(url
, dir
, fname
)) {
640 if (_shellBrowser
->jump_to_pidl(ShellPath(dir
)))
641 return FmtString(TEXT("file://%s"), (LPCTSTR
)dir
);
648 void MDIShellBrowserChild::entry_selected(Entry
* entry
)
650 if (entry
->_etype
== ET_SHELL
) {
651 ShellEntry
* shell_entry
= static_cast<ShellEntry
*>(entry
);
652 IShellFolder
* folder
;
654 if (shell_entry
->_data
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
655 folder
= static_cast<ShellDirectory
*>(shell_entry
)->_folder
;
657 folder
= shell_entry
->get_parent_folder();
664 TCHAR path
[MAX_PATH
];
666 if (shell_entry
->get_path(path
, COUNTOF(path
))) {
670 url
.printf(TEXT("shell://%s"), path
);
672 url
.printf(TEXT("file://%s"), path
);
677 _shellBrowser
->UpdateFolderView(folder
);
679 // set size of new created shell view windows
680 ClientRect
rt(_hwnd
);
681 resize_children(rt
.right
, rt
.bottom
);