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
29 #include "../utility/utility.h"
31 #include "../explorer.h"
32 #include "../globals.h"
34 #include "../explorer_intres.h"
37 ShellBrowserChild::ShellBrowserChild(HWND hwnd
, const ShellChildWndInfo
& info
)
46 // store path into history
47 if (info
._path
&& *info
._path
)
48 _url_history
.push(info
._path
);
51 ShellBrowserChild::~ShellBrowserChild()
54 _pShellView
->Release();
57 _pDropTarget
->Release();
63 LRESULT
ShellBrowserChild::Init(LPCREATESTRUCT pcs
)
65 CONTEXT("ShellBrowserChild::Init()");
70 _hWndFrame
= GetParent(pcs
->hwndParent
);
72 ClientRect
rect(_hwnd
);
76 _himlSmall
= (HIMAGELIST
)SHGetFileInfo(TEXT("C:\\"), 0, &sfi
, sizeof(SHFILEINFO
), SHGFI_SYSICONINDEX
|SHGFI_SMALLICON
);
77 // _himlLarge = (HIMAGELIST)SHGetFileInfo(TEXT("C:\\"), 0, &sfi, sizeof(SHFILEINFO), SHGFI_SYSICONINDEX|SHGFI_LARGEICON);
80 const String
& root_name
= GetDesktopFolder().get_name(_create_info
._root_shell_path
, SHGDN_FORPARSING
);
82 _root
._drive_type
= DRIVE_UNKNOWN
;
83 _root
._sort_order
= SORT_NAME
;
85 lstrcpy(_root
._volname
, root_name
); // most of the time "Desktop"
87 lstrcpy(_root
._fs
, TEXT("Desktop"));
89 //@@ _root._entry->read_tree(shell_info._root_shell_path.get_folder(), info._shell_path, _root._sort_order/*_sortOrder*/);
92 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
93 -> see FileChildWindow::FileChildWindow()
95 _root
._entry
= new ShellDirectory(GetDesktopFolder(), _create_info
._root_shell_path
, _hwnd
);
96 _root
._entry
->read_directory_base(_root
._sort_order
);
98 /* already filled by ShellDirectory constructor
99 lstrcpy(_root._entry->_data.cFileName, TEXT("Desktop")); */
102 // create explorer treeview
103 if (_create_info
._open_mode
& OWM_EXPLORE
)
104 _left_hwnd
= CreateWindowEx(0, WC_TREEVIEW
, NULL
,
105 WS_CHILD
|WS_TABSTOP
|WS_VISIBLE
|WS_CHILD
|TVS_HASLINES
|TVS_LINESATROOT
|TVS_HASBUTTONS
|TVS_SHOWSELALWAYS
,//|TVS_NOTOOLTIPS
106 0, rect
.top
, _split_pos
-SPLIT_WIDTH
/2, rect
.bottom
-rect
.top
,
107 _hwnd
, (HMENU
)IDC_FILETREE
, g_Globals
._hInstance
, 0);
110 TreeView_SetImageList(_left_hwnd
, _himlSmall
, TVSIL_NORMAL
);
111 TreeView_SetScrollTime(_left_hwnd
, 100);
117 UpdateFolderView(_create_info
._shell_path
.get_folder());
123 void ShellBrowserChild::InitializeTree()
125 CONTEXT("ShellBrowserChild::InitializeTree()");
127 TV_INSERTSTRUCT tvInsert
;
129 tvInsert
.hParent
= 0;
130 tvInsert
.hInsertAfter
= TVI_LAST
;
132 TV_ITEM
& tvItem
= tvInsert
.item
;
133 tvItem
.mask
= TVIF_PARAM
| TVIF_TEXT
| TVIF_IMAGE
| TVIF_SELECTEDIMAGE
| TVIF_CHILDREN
;
134 tvItem
.lParam
= (LPARAM
)_root
._entry
;
135 tvItem
.pszText
= LPSTR_TEXTCALLBACK
;
136 tvItem
.iImage
= tvItem
.iSelectedImage
= I_IMAGECALLBACK
;
137 tvItem
.cChildren
= 1;
139 HTREEITEM hItem
= TreeView_InsertItem(_left_hwnd
, &tvInsert
);
140 TreeView_SelectItem(_left_hwnd
, hItem
);
141 TreeView_Expand(_left_hwnd
, hItem
, TVE_EXPAND
);
145 bool ShellBrowserChild::InitDragDrop()
147 CONTEXT("ShellBrowserChild::InitDragDrop()");
149 _pDropTarget
= new TreeDropTarget(_left_hwnd
);
154 _pDropTarget
->AddRef();
156 if (FAILED(RegisterDragDrop(_left_hwnd
, _pDropTarget
))) {//calls addref
157 _pDropTarget
->Release(); // free TreeDropTarget
162 _pDropTarget
->Release();
166 ftetc
.dwAspect
= DVASPECT_CONTENT
;
168 ftetc
.tymed
= TYMED_HGLOBAL
;
169 ftetc
.cfFormat
= CF_HDROP
;
171 _pDropTarget
->AddSuportedFormat(ftetc
);
177 void ShellBrowserChild::OnTreeItemRClick(int idCtrl
, LPNMHDR pnmh
)
179 CONTEXT("ShellBrowserChild::OnTreeItemRClick()");
183 GetCursorPos(&tvhti
.pt
);
184 ScreenToClient(_left_hwnd
, &tvhti
.pt
);
186 tvhti
.flags
= LVHT_NOWHERE
;
187 TreeView_HitTest(_left_hwnd
, &tvhti
);
189 if (TVHT_ONITEM
& tvhti
.flags
) {
190 ClientToScreen(_left_hwnd
, &tvhti
.pt
);
191 Tree_DoItemMenu(_left_hwnd
, tvhti
.hItem
, &tvhti
.pt
);
195 void ShellBrowserChild::Tree_DoItemMenu(HWND hwndTreeView
, HTREEITEM hItem
, LPPOINT pptScreen
)
197 CONTEXT("ShellBrowserChild::Tree_DoItemMenu()");
199 LPARAM itemData
= TreeView_GetItemData(hwndTreeView
, hItem
);
202 Entry
* entry
= (Entry
*)itemData
;
204 if (entry
->_etype
== ET_SHELL
) {
205 ShellDirectory
* dir
= static_cast<ShellDirectory
*>(entry
->_up
);
206 ShellFolder folder
= dir
? dir
->_folder
: GetDesktopFolder();
207 LPCITEMIDLIST pidl
= static_cast<ShellEntry
*>(entry
)->_pidl
;
209 CHECKERROR(ShellFolderContextMenu(folder
, ::GetParent(hwndTreeView
), 1, &pidl
, pptScreen
->x
, pptScreen
->y
));
211 ShellPath shell_path
= entry
->create_absolute_pidl();
212 LPCITEMIDLIST pidl
= shell_path
;
214 ///@todo use parent folder instead of desktop
215 CHECKERROR(ShellFolderContextMenu(GetDesktopFolder(), _hwnd
, 1, &pidl
, pptScreen
->x
, pptScreen
->y
));
220 void ShellBrowserChild::OnTreeGetDispInfo(int idCtrl
, LPNMHDR pnmh
)
222 CONTEXT("ShellBrowserChild::OnTreeGetDispInfo()");
224 LPNMTVDISPINFO lpdi
= (LPNMTVDISPINFO
)pnmh
;
225 ShellEntry
* entry
= (ShellEntry
*)lpdi
->item
.lParam
;
227 if (lpdi
->item
.mask
& TVIF_TEXT
)
228 lpdi
->item
.pszText
= entry
->_display_name
;
230 if (lpdi
->item
.mask
& (/*TVIF_TEXT|*/TVIF_IMAGE
|TVIF_SELECTEDIMAGE
)) {
231 ShellPath pidl_abs
= entry
->create_absolute_pidl(); // Caching of absolute PIDLs could enhance performance.
232 LPCITEMIDLIST pidl
= pidl_abs
;
236 if (lpdi->item.mask & TVIF_TEXT)
237 if (SHGetFileInfo((LPCTSTR)pidl, 0, &sfi, sizeof(sfi), SHGFI_PIDL|SHGFI_DISPLAYNAME))
238 lstrcpy(lpdi->item.pszText, sfi.szDisplayName); ///@todo look at cchTextMax if there is enough space available
240 lpdi->item.pszText = entry->_data.cFileName;
242 if (lpdi
->item
.mask
& TVIF_IMAGE
)
243 if ((HIMAGELIST
)SHGetFileInfo((LPCTSTR
)pidl
, 0, &sfi
, sizeof(sfi
), SHGFI_PIDL
|SHGFI_SYSICONINDEX
|SHGFI_SMALLICON
|SHGFI_LINKOVERLAY
) == _himlSmall
)
244 lpdi
->item
.iImage
= sfi
.iIcon
;
246 lpdi
->item
.iImage
= -1;
248 if (lpdi
->item
.mask
& TVIF_SELECTEDIMAGE
)
249 if ((HIMAGELIST
)SHGetFileInfo((LPCTSTR
)pidl
, 0, &sfi
, sizeof(sfi
), SHGFI_PIDL
|SHGFI_SYSICONINDEX
|SHGFI_SMALLICON
|SHGFI_OPENICON
) == _himlSmall
)
250 lpdi
->item
.iSelectedImage
= sfi
.iIcon
;
252 lpdi
->item
.iSelectedImage
= -1;
256 void ShellBrowserChild::OnTreeItemExpanding(int idCtrl
, LPNMTREEVIEW pnmtv
)
258 CONTEXT("ShellBrowserChild::OnTreeItemExpanding()");
260 if (pnmtv
->action
== TVE_COLLAPSE
)
261 TreeView_Expand(_left_hwnd
, pnmtv
->itemNew
.hItem
, TVE_COLLAPSE
|TVE_COLLAPSERESET
);
262 else if (pnmtv
->action
== TVE_EXPAND
) {
263 ShellDirectory
* entry
= (ShellDirectory
*)TreeView_GetItemData(_left_hwnd
, pnmtv
->itemNew
.hItem
);
266 if (!InsertSubitems(pnmtv
->itemNew
.hItem
, entry
, entry
->_folder
)) {
267 entry
->_shell_attribs
&= ~SFGAO_HASSUBFOLDER
;
269 // remove subitem "+"
272 tvItem
.mask
= TVIF_CHILDREN
;
273 tvItem
.hItem
= pnmtv
->itemNew
.hItem
;
274 tvItem
.cChildren
= 0;
276 TreeView_SetItem(_left_hwnd
, &tvItem
);
281 int ShellBrowserChild::InsertSubitems(HTREEITEM hParentItem
, Entry
* entry
, IShellFolder
* pParentFolder
)
283 CONTEXT("ShellBrowserChild::InsertSubitems()");
289 SendMessage(_left_hwnd
, WM_SETREDRAW
, FALSE
, 0);
293 } catch(COMException
& e
) {
294 HandleException(e
, g_Globals
._hMainWnd
);
298 TV_INSERTSTRUCT tvInsert
;
300 for(entry
=entry
->_down
; entry
; entry
=entry
->_next
) {
302 if (entry
->_data
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
305 ZeroMemory(&tvItem
, sizeof(tvItem
));
307 tvItem
.mask
= TVIF_PARAM
| TVIF_TEXT
| TVIF_IMAGE
| TVIF_SELECTEDIMAGE
| TVIF_CHILDREN
;
308 tvItem
.pszText
= LPSTR_TEXTCALLBACK
;
309 tvItem
.iImage
= tvItem
.iSelectedImage
= I_IMAGECALLBACK
;
310 tvItem
.lParam
= (LPARAM
)entry
;
311 tvItem
.cChildren
= entry
->_shell_attribs
& SFGAO_HASSUBFOLDER
? 1: 0;
313 if (entry
->_shell_attribs
& SFGAO_SHARE
) {
314 tvItem
.mask
|= TVIF_STATE
;
315 tvItem
.stateMask
|= TVIS_OVERLAYMASK
;
316 tvItem
.state
|= INDEXTOOVERLAYMASK(1);
319 tvInsert
.item
= tvItem
;
320 tvInsert
.hInsertAfter
= TVI_LAST
;
321 tvInsert
.hParent
= hParentItem
;
323 TreeView_InsertItem(_left_hwnd
, &tvInsert
);
329 SendMessage(_left_hwnd
, WM_SETREDRAW
, TRUE
, 0);
334 void ShellBrowserChild::OnTreeItemSelected(int idCtrl
, LPNMTREEVIEW pnmtv
)
336 CONTEXT("ShellBrowserChild::OnTreeItemSelected()");
338 ShellEntry
* entry
= (ShellEntry
*)pnmtv
->itemNew
.lParam
;
340 _last_sel
= pnmtv
->itemNew
.hItem
;
342 if (entry
->_etype
== ET_SHELL
) {
343 IShellFolder
* folder
;
345 if (entry
->_data
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
346 folder
= static_cast<ShellDirectory
*>(entry
)->_folder
;
348 folder
= entry
->get_parent_folder();
355 TCHAR path
[MAX_PATH
];
357 if (entry
->get_path(path
)) {
361 url
.printf(TEXT("shell://%s"), path
);
363 url
.printf(TEXT("file://%s"), path
);
368 UpdateFolderView(folder
);
372 void ShellBrowserChild::UpdateFolderView(IShellFolder
* folder
)
374 CONTEXT("ShellBrowserChild::UpdateFolderView()");
377 IShellView
* pLastShellView
= _pShellView
;
382 pLastShellView
->GetCurrentInfo(&fs
);
384 fs
.ViewMode
= _create_info
._open_mode
&OWM_DETAILS
? FVM_DETAILS
: FVM_ICON
;
385 fs
.fFlags
= FWF_NOCLIENTEDGE
|FWF_BESTFITWINDOW
;
388 HRESULT hr
= folder
->CreateViewObject(_hwnd
, IID_IShellView
, (void**)&_pShellView
);
395 RECT rect
= {CW_USEDEFAULT
, CW_USEDEFAULT
, CW_USEDEFAULT
, CW_USEDEFAULT
};
396 hr
= _pShellView
->CreateViewWindow(pLastShellView
, &fs
, static_cast<IShellBrowser
*>(this), &rect
, &_right_hwnd
/*&m_hWndListView*/);
398 if (pLastShellView
) {
399 pLastShellView
->GetCurrentInfo(&fs
);
400 pLastShellView
->UIActivate(SVUIA_DEACTIVATE
);
401 pLastShellView
->DestroyViewWindow();
402 pLastShellView
->Release();
404 ClientRect
clnt(_hwnd
);
405 resize_children(clnt
.right
, clnt
.bottom
);
408 _pShellView
->UIActivate(SVUIA_ACTIVATE_NOFOCUS
);
412 LRESULT
ShellBrowserChild::WndProc(UINT nmsg
, WPARAM wparam
, LPARAM lparam
)
415 case WM_GETISHELLBROWSER
: // for Registry Explorer Plugin
416 return (LRESULT
)static_cast<IShellBrowser
*>(this);
418 case PM_GET_SHELLBROWSER_PTR
:
419 return (LRESULT
)this;
421 case PM_DISPATCH_COMMAND
: {
422 switch(LOWORD(wparam
)) {
423 case ID_WINDOW_NEW
: {CONTEXT("ShellBrowserChild PM_DISPATCH_COMMAND ID_WINDOW_NEW");
424 ShellBrowserChild::create(_create_info
);
428 //@todo refresh shell child
432 return super::WndProc(nmsg
, wparam
, lparam
);
437 return super::WndProc(nmsg
, wparam
, lparam
);
443 int ShellBrowserChild::Notify(int id
, NMHDR
* pnmh
)
446 case TVN_GETDISPINFO
: OnTreeGetDispInfo(id
, pnmh
); break;
447 case TVN_SELCHANGED
: OnTreeItemSelected(id
, (LPNMTREEVIEW
)pnmh
); break;
448 case TVN_ITEMEXPANDING
: OnTreeItemExpanding(id
, (LPNMTREEVIEW
)pnmh
); break;
449 case NM_RCLICK
: OnTreeItemRClick(id
, pnmh
); break;
450 default: return super::Notify(id
, pnmh
);
457 HRESULT
ShellBrowserChild::OnDefaultCommand(LPIDA pida
)
459 CONTEXT("ShellBrowserChild::OnDefaultCommand()");
461 if (pida
->cidl
>= 1) {
462 if (_left_hwnd
) { // explorer mode
464 ShellDirectory
* parent
= (ShellDirectory
*)TreeView_GetItemData(_left_hwnd
, _last_sel
);
468 parent
->smart_scan();
469 } catch(COMException
& e
) {
473 UINT firstOffset
= pida
->aoffset
[1];
474 LPITEMIDLIST pidl
= (LPITEMIDLIST
)((LPBYTE
)pida
+firstOffset
);
476 Entry
* entry
= parent
->find_entry(pidl
);
478 if (entry
&& (entry
->_data
.dwFileAttributes
&FILE_ATTRIBUTE_DIRECTORY
))
479 if (entry
->_etype
== ET_SHELL
)
480 if (_last_sel
&& select_entry(_last_sel
, entry
))
484 } else { // no tree control
485 if (MainFrame::OpenShellFolders(pida
, _hWndFrame
))
488 /* create new Frame Window
489 if (MainFrame::OpenShellFolders(pida, 0))
499 HTREEITEM
ShellBrowserChild::select_entry(HTREEITEM hitem
, Entry
* entry
, bool expand
)
501 CONTEXT("ShellBrowserChild::select_entry()");
503 if (expand
&& !TreeView_Expand(_left_hwnd
, hitem
, TVE_EXPAND
))
506 for(hitem
=TreeView_GetChild(_left_hwnd
,hitem
); hitem
; hitem
=TreeView_GetNextSibling(_left_hwnd
,hitem
)) {
507 if ((Entry
*)TreeView_GetItemData(_left_hwnd
,hitem
) == entry
) {
508 if (TreeView_SelectItem(_left_hwnd
, hitem
)) {
510 TreeView_Expand(_left_hwnd
, hitem
, TVE_EXPAND
);
523 String
ShellBrowserChild::jump_to_int(LPCTSTR url
)
527 if (!_tcsnicmp(url
, TEXT("shell://"), 8)) {
528 if (jump_to_pidl(ShellPath(url
+8)))
532 if (SplitFileSysURL(url
, dir
, fname
)) {
536 if (jump_to_pidl(ShellPath(dir
)))
537 return FmtString(TEXT("file://%s"), (LPCTSTR
)dir
);
544 bool ShellBrowserChild::jump_to_pidl(LPCITEMIDLIST pidl
)
549 // iterate through the hierarchy and open all folders to reach pidl
552 HTREEITEM hitem
= TreeView_GetRoot(_left_hwnd
);
553 Entry
* entry
= _root
._entry
;
555 for(const void*p
=pidl
;;) {
559 if (!entry
|| !hitem
)
562 entry
->smart_scan(SORT_NAME
);
564 Entry
* found
= entry
->find_entry(p
);
565 p
= entry
->get_next_path_component(p
);
568 hitem
= select_entry(hitem
, found
);