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"
31 #include "../explorer.h"
32 #include "../globals.h"
34 #include "../explorer_intres.h"
37 static LPARAM
TreeView_GetItemData(HWND hwndTreeView
, HTREEITEM hItem
)
41 tvItem
.mask
= TVIF_PARAM
;
44 if (!TreeView_GetItem(hwndTreeView
, &tvItem
))
51 ShellBrowserChild::ShellBrowserChild(HWND hwnd
, HWND left_hwnd
, WindowHandle
& right_hwnd
, ShellPathInfo
& create_info
)
53 _left_hwnd(left_hwnd
),
54 _right_hwnd(right_hwnd
),
55 _create_info(create_info
)
63 _split_pos
= DEFAULT_SPLIT_POS
;
64 _last_split
= DEFAULT_SPLIT_POS
;
71 ShellBrowserChild::~ShellBrowserChild()
74 _pShellView
->Release();
77 _pDropTarget
->Release();
82 DestroyWindow(_right_hwnd
);
88 LRESULT
ShellBrowserChild::Init(HWND hWndFrame
)
90 CONTEXT("ShellBrowserChild::Init()");
92 _hWndFrame
= hWndFrame
;
94 ClientRect
rect(_hwnd
);
98 _himlSmall
= (HIMAGELIST
)SHGetFileInfo(TEXT("C:\\"), 0, &sfi
, sizeof(SHFILEINFO
), SHGFI_SYSICONINDEX
|SHGFI_SMALLICON
);
99 // _himlLarge = (HIMAGELIST)SHGetFileInfo(TEXT("C:\\"), 0, &sfi, sizeof(SHFILEINFO), SHGFI_SYSICONINDEX|SHGFI_LARGEICON);
106 const String
& root_name
= GetDesktopFolder().get_name(_create_info
._root_shell_path
, SHGDN_FORPARSING
);
108 _root
._drive_type
= DRIVE_UNKNOWN
;
109 lstrcpy(_root
._volname
, root_name
); // most of the time "Desktop"
111 lstrcpy(_root
._fs
, TEXT("Desktop"));
113 _root
._entry
= new ShellDirectory(GetDesktopFolder(), _create_info
._root_shell_path
, _hwnd
);
115 jump_to(_create_info
._shell_path
);
118 _root
._entry
->read_directory();
120 /* already filled by ShellDirectory constructor
121 lstrcpy(_root._entry->_data.cFileName, TEXT("Desktop")); */
127 void ShellBrowserChild::InitializeTree()
129 CONTEXT("ShellBrowserChild::InitializeTree()");
131 TreeView_SetImageList(_left_hwnd
, _himlSmall
, TVSIL_NORMAL
);
132 TreeView_SetScrollTime(_left_hwnd
, 100);
136 tvItem
.mask
= TVIF_PARAM
| TVIF_TEXT
| TVIF_IMAGE
| TVIF_SELECTEDIMAGE
| TVIF_CHILDREN
;
137 tvItem
.lParam
= (LPARAM
)_root
._entry
;
138 tvItem
.pszText
= LPSTR_TEXTCALLBACK
;
139 tvItem
.iImage
= tvItem
.iSelectedImage
= I_IMAGECALLBACK
;
140 tvItem
.cChildren
= 1;
142 TV_INSERTSTRUCT tvInsert
;
144 tvInsert
.hParent
= 0;
145 tvInsert
.hInsertAfter
= TVI_LAST
;
146 tvInsert
.item
= tvItem
;
148 HTREEITEM hItem
= TreeView_InsertItem(_left_hwnd
, &tvInsert
);
149 TreeView_SelectItem(_left_hwnd
, hItem
);
150 TreeView_Expand(_left_hwnd
, hItem
, TVE_EXPAND
);
154 bool ShellBrowserChild::InitDragDrop()
156 CONTEXT("ShellBrowserChild::InitDragDrop()");
158 _pDropTarget
= new TreeDropTarget(_left_hwnd
);
163 _pDropTarget
->AddRef();
165 if (FAILED(RegisterDragDrop(_left_hwnd
, _pDropTarget
))) {//calls addref
166 _pDropTarget
->Release(); // free TreeDropTarget
171 _pDropTarget
->Release();
175 ftetc
.dwAspect
= DVASPECT_CONTENT
;
177 ftetc
.tymed
= TYMED_HGLOBAL
;
178 ftetc
.cfFormat
= CF_HDROP
;
180 _pDropTarget
->AddSuportedFormat(ftetc
);
186 void ShellBrowserChild::OnTreeItemRClick(int idCtrl
, LPNMHDR pnmh
)
188 CONTEXT("ShellBrowserChild::OnTreeItemRClick()");
192 GetCursorPos(&tvhti
.pt
);
193 ScreenToClient(_left_hwnd
, &tvhti
.pt
);
195 tvhti
.flags
= LVHT_NOWHERE
;
196 TreeView_HitTest(_left_hwnd
, &tvhti
);
198 if (TVHT_ONITEM
& tvhti
.flags
) {
199 ClientToScreen(_left_hwnd
, &tvhti
.pt
);
200 Tree_DoItemMenu(_left_hwnd
, tvhti
.hItem
, &tvhti
.pt
);
204 void ShellBrowserChild::Tree_DoItemMenu(HWND hwndTreeView
, HTREEITEM hItem
, LPPOINT pptScreen
)
206 CONTEXT("ShellBrowserChild::Tree_DoItemMenu()");
208 LPARAM itemData
= TreeView_GetItemData(hwndTreeView
, hItem
);
211 Entry
* entry
= (Entry
*)itemData
;
213 if (entry
->_etype
== ET_SHELL
) {
214 ShellDirectory
* dir
= static_cast<ShellDirectory
*>(entry
->_up
);
215 ShellFolder folder
= dir
? dir
->_folder
: GetDesktopFolder();
216 LPCITEMIDLIST pidl
= static_cast<ShellEntry
*>(entry
)->_pidl
;
218 CHECKERROR(ShellFolderContextMenu(folder
, ::GetParent(hwndTreeView
), 1, &pidl
, pptScreen
->x
, pptScreen
->y
));
220 ShellPath shell_path
= entry
->create_absolute_pidl();
221 LPCITEMIDLIST pidl
= shell_path
;
223 ///@todo use parent folder instead of desktop
224 CHECKERROR(ShellFolderContextMenu(GetDesktopFolder(), _hwnd
, 1, &pidl
, pptScreen
->x
, pptScreen
->y
));
229 void ShellBrowserChild::OnTreeGetDispInfo(int idCtrl
, LPNMHDR pnmh
)
231 CONTEXT("ShellBrowserChild::OnTreeGetDispInfo()");
233 LPNMTVDISPINFO lpdi
= (LPNMTVDISPINFO
)pnmh
;
234 ShellEntry
* entry
= (ShellEntry
*)lpdi
->item
.lParam
;
236 if (lpdi
->item
.mask
& TVIF_TEXT
)
237 lpdi
->item
.pszText
= entry
->_display_name
;
239 if (lpdi
->item
.mask
& (/*TVIF_TEXT|*/TVIF_IMAGE
|TVIF_SELECTEDIMAGE
)) {
240 ShellPath pidl_abs
= entry
->create_absolute_pidl(); // Caching of absolute PIDLs could enhance performance.
241 LPCITEMIDLIST pidl
= pidl_abs
;
245 if (lpdi->item.mask & TVIF_TEXT)
246 if (SHGetFileInfo((LPCTSTR)pidl, 0, &sfi, sizeof(sfi), SHGFI_PIDL|SHGFI_DISPLAYNAME))
247 lstrcpy(lpdi->item.pszText, sfi.szDisplayName); ///@todo look at cchTextMax if there is enough space available
249 lpdi->item.pszText = entry->_data.cFileName;
251 if (lpdi
->item
.mask
& TVIF_IMAGE
)
252 if ((HIMAGELIST
)SHGetFileInfo((LPCTSTR
)pidl
, 0, &sfi
, sizeof(sfi
), SHGFI_PIDL
|SHGFI_SYSICONINDEX
|SHGFI_SMALLICON
|SHGFI_LINKOVERLAY
) == _himlSmall
)
253 lpdi
->item
.iImage
= sfi
.iIcon
;
255 lpdi
->item
.iImage
= -1;
257 if (lpdi
->item
.mask
& TVIF_SELECTEDIMAGE
)
258 if ((HIMAGELIST
)SHGetFileInfo((LPCTSTR
)pidl
, 0, &sfi
, sizeof(sfi
), SHGFI_PIDL
|SHGFI_SYSICONINDEX
|SHGFI_SMALLICON
|SHGFI_OPENICON
) == _himlSmall
)
259 lpdi
->item
.iSelectedImage
= sfi
.iIcon
;
261 lpdi
->item
.iSelectedImage
= -1;
265 void ShellBrowserChild::OnTreeItemExpanding(int idCtrl
, LPNMTREEVIEW pnmtv
)
267 CONTEXT("ShellBrowserChild::OnTreeItemExpanding()");
269 if (pnmtv
->action
== TVE_COLLAPSE
)
270 TreeView_Expand(_left_hwnd
, pnmtv
->itemNew
.hItem
, TVE_COLLAPSE
|TVE_COLLAPSERESET
);
271 else if (pnmtv
->action
== TVE_EXPAND
) {
272 ShellDirectory
* entry
= (ShellDirectory
*)TreeView_GetItemData(_left_hwnd
, pnmtv
->itemNew
.hItem
);
275 if (!InsertSubitems(pnmtv
->itemNew
.hItem
, entry
, entry
->_folder
)) {
276 entry
->_shell_attribs
&= ~SFGAO_HASSUBFOLDER
;
278 // remove subitem "+"
281 tvItem
.mask
= TVIF_CHILDREN
;
282 tvItem
.hItem
= pnmtv
->itemNew
.hItem
;
283 tvItem
.cChildren
= 0;
285 TreeView_SetItem(_left_hwnd
, &tvItem
);
290 int ShellBrowserChild::InsertSubitems(HTREEITEM hParentItem
, Entry
* entry
, IShellFolder
* pParentFolder
)
292 CONTEXT("ShellBrowserChild::InsertSubitems()");
298 SendMessage(_left_hwnd
, WM_SETREDRAW
, FALSE
, 0);
302 } catch(COMException
& e
) {
303 HandleException(e
, g_Globals
._hMainWnd
);
307 TV_INSERTSTRUCT tvInsert
;
309 for(entry
=entry
->_down
; entry
; entry
=entry
->_next
) {
311 if (entry
->_data
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
314 ZeroMemory(&tvItem
, sizeof(tvItem
));
316 tvItem
.mask
= TVIF_PARAM
| TVIF_TEXT
| TVIF_IMAGE
| TVIF_SELECTEDIMAGE
| TVIF_CHILDREN
;
317 tvItem
.pszText
= LPSTR_TEXTCALLBACK
;
318 tvItem
.iImage
= tvItem
.iSelectedImage
= I_IMAGECALLBACK
;
319 tvItem
.lParam
= (LPARAM
)entry
;
320 tvItem
.cChildren
= entry
->_shell_attribs
& SFGAO_HASSUBFOLDER
? 1: 0;
322 if (entry
->_shell_attribs
& SFGAO_SHARE
) {
323 tvItem
.mask
|= TVIF_STATE
;
324 tvItem
.stateMask
|= TVIS_OVERLAYMASK
;
325 tvItem
.state
|= INDEXTOOVERLAYMASK(1);
328 tvInsert
.item
= tvItem
;
329 tvInsert
.hInsertAfter
= TVI_LAST
;
330 tvInsert
.hParent
= hParentItem
;
332 TreeView_InsertItem(_left_hwnd
, &tvInsert
);
338 SendMessage(_left_hwnd
, WM_SETREDRAW
, TRUE
, 0);
343 void ShellBrowserChild::OnTreeItemSelected(int idCtrl
, LPNMTREEVIEW pnmtv
)
345 CONTEXT("ShellBrowserChild::OnTreeItemSelected()");
347 _last_sel
= pnmtv
->itemNew
.hItem
;
348 Entry
* entry
= (Entry
*)pnmtv
->itemNew
.lParam
;
353 void ShellBrowserChild::UpdateFolderView(IShellFolder
* folder
)
355 CONTEXT("ShellBrowserChild::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_BESTFITWINDOW
;
369 HRESULT hr
= folder
->CreateViewObject(_hwnd
, IID_IShellView
, (void**)&_pShellView
);
376 RECT rect
= {CW_USEDEFAULT
, CW_USEDEFAULT
, CW_USEDEFAULT
, CW_USEDEFAULT
};
377 hr
= _pShellView
->CreateViewWindow(pLastShellView
, &fs
, static_cast<IShellBrowser
*>(this), &rect
, &_right_hwnd
/*&m_hWndListView*/);
379 if (pLastShellView
) {
380 pLastShellView
->GetCurrentInfo(&fs
);
381 pLastShellView
->UIActivate(SVUIA_DEACTIVATE
);
382 pLastShellView
->DestroyViewWindow();
383 pLastShellView
->Release();
388 _pShellView
->UIActivate(SVUIA_ACTIVATE_NOFOCUS
);
392 void ShellBrowserChild::resize_children()
394 RECT rect
= _clnt_rect
;
396 HDWP hdwp
= BeginDeferWindowPos(2);
401 cx
= _split_pos
+ SPLIT_WIDTH
/2;
403 hdwp
= DeferWindowPos(hdwp
, _left_hwnd
, 0, rect
.left
, rect
.top
, _split_pos
-SPLIT_WIDTH
/2-rect
.left
, rect
.bottom
-rect
.top
, SWP_NOZORDER
|SWP_NOACTIVATE
);
410 hdwp
= DeferWindowPos(hdwp
, _right_hwnd
, 0, rect
.left
+cx
+1, rect
.top
, rect
.right
-cx
, rect
.bottom
-rect
.top
, SWP_NOZORDER
|SWP_NOACTIVATE
);
412 EndDeferWindowPos(hdwp
);
416 LRESULT
ShellBrowserChild::WndProc(UINT nmsg
, WPARAM wparam
, LPARAM lparam
)
419 case WM_GETISHELLBROWSER
: // for Registry Explorer Plugin
420 return (LRESULT
)static_cast<IShellBrowser
*>(this);
426 PaintCanvas
canvas(_hwnd
);
427 ClientRect
rt(_hwnd
);
428 rt
.left
= _split_pos
-SPLIT_WIDTH
/2;
429 rt
.right
= _split_pos
+SPLIT_WIDTH
/2+1;
432 WindowRect
right_rect(_right_hwnd
);
433 ScreenToClient(_hwnd
, &right_rect
);
434 rt
.top
= right_rect
.top
;
435 rt
.bottom
= right_rect
.bottom
;
438 HBRUSH lastBrush
= SelectBrush(canvas
, GetStockBrush(COLOR_SPLITBAR
));
439 Rectangle(canvas
, rt
.left
, rt
.top
-1, rt
.right
, rt
.bottom
+1);
440 SelectObject(canvas
, lastBrush
);
444 if (LOWORD(lparam
) == HTCLIENT
) {
447 ScreenToClient(_hwnd
, &pt
);
449 if (pt
.x
>=_split_pos
-SPLIT_WIDTH
/2 && pt
.x
<_split_pos
+SPLIT_WIDTH
/2+1) {
450 SetCursor(LoadCursor(0, IDC_SIZEWE
));
456 case WM_LBUTTONDOWN
: {
457 int x
= GET_X_LPARAM(lparam
);
459 ClientRect
rt(_hwnd
);
461 if (x
>=_split_pos
-SPLIT_WIDTH
/2 && x
<_split_pos
+SPLIT_WIDTH
/2+1) {
462 _last_split
= _split_pos
;
469 if (GetCapture() == _hwnd
)
474 if (wparam
== VK_ESCAPE
)
475 if (GetCapture() == _hwnd
) {
476 _split_pos
= _last_split
;
480 SetCursor(LoadCursor(0, IDC_ARROW
));
485 if (GetCapture() == _hwnd
) {
486 int x
= LOWORD(lparam
);
488 ClientRect
rt(_hwnd
);
490 if (x
>=0 && x
<rt
.right
) {
493 rt
.left
= x
-SPLIT_WIDTH
/2;
494 rt
.right
= x
+SPLIT_WIDTH
/2+1;
495 InvalidateRect(_hwnd
, &rt
, FALSE
);
496 UpdateWindow(_left_hwnd
);
498 UpdateWindow(_right_hwnd
);
505 return DefWindowProc(_hwnd
, nmsg
, wparam
, lparam
);
511 int ShellBrowserChild::Command(int id
, int code
)
517 case ID_BROWSE_FORWARD
:
522 //@@ not necessary in this simply case: jump_to(_cur_dir->_up);
524 //@@ -> move into jump_to()
525 HTREEITEM hitem
= TreeView_GetParent(_left_hwnd
, _last_sel
);
528 TreeView_SelectItem(_left_hwnd
, hitem
); // sends TVN_SELCHANGED notification
531 jump_to(_cur_dir
->_up
);
542 int ShellBrowserChild::Notify(int id
, NMHDR
* pnmh
)
545 case TVN_GETDISPINFO
: OnTreeGetDispInfo(id
, pnmh
); break;
546 case TVN_ITEMEXPANDING
: OnTreeItemExpanding(id
, (LPNMTREEVIEW
)pnmh
); break;
547 case TVN_SELCHANGED
: OnTreeItemSelected(id
, (LPNMTREEVIEW
)pnmh
); break;
548 case NM_RCLICK
: OnTreeItemRClick(id
, pnmh
); break;
555 HRESULT
ShellBrowserChild::OnDefaultCommand(LPIDA pida
)
557 CONTEXT("ShellBrowserChild::OnDefaultCommand()");
559 if (pida
->cidl
>= 1) {
560 if (_left_hwnd
) { // explorer mode
562 ShellDirectory
* parent
= _cur_dir
;//@@(ShellDirectory*)TreeView_GetItemData(_left_hwnd, _last_sel);
566 parent
->smart_scan();
567 } catch(COMException
& e
) {
571 UINT firstOffset
= pida
->aoffset
[1];
572 LPITEMIDLIST pidl
= (LPITEMIDLIST
)((LPBYTE
)pida
+firstOffset
);
574 Entry
* entry
= parent
->find_entry(pidl
);
576 if (entry
&& (entry
->_data
.dwFileAttributes
&FILE_ATTRIBUTE_DIRECTORY
))
577 if (entry
->_etype
== ET_SHELL
)
578 if (expand_folder(static_cast<ShellDirectory
*>(entry
)))
582 } else { // no tree control
583 if (MainFrame::OpenShellFolders(pida
, _hWndFrame
))
586 /* create new Frame Window
587 if (MainFrame::OpenShellFolders(pida, 0))
597 bool ShellBrowserChild::expand_folder(ShellDirectory
* entry
)
599 CONTEXT("ShellBrowserChild::expand_folder()");
601 //HTREEITEM hitem_sel = TreeView_GetSelection(_left_hwnd);
605 if (!TreeView_Expand(_left_hwnd
, _last_sel
, TVE_EXPAND
))
608 for(HTREEITEM hitem
=TreeView_GetChild(_left_hwnd
,_last_sel
); hitem
; hitem
=TreeView_GetNextSibling(_left_hwnd
,hitem
)) {
609 if ((ShellDirectory
*)TreeView_GetItemData(_left_hwnd
,hitem
) == entry
) {
610 if (TreeView_SelectItem(_left_hwnd
, hitem
) &&
611 TreeView_Expand(_left_hwnd
, hitem
, TVE_EXPAND
))
622 void ShellBrowserChild::jump_to(LPCTSTR path
)
624 ///@todo implement "file://", ... parsing
625 jump_to(ShellPath(path
));
629 void ShellBrowserChild::jump_to(LPCITEMIDLIST pidl
)
635 _cur_dir
= static_cast<ShellDirectory
*>(_root
._entry
);
638 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
639 _root._entry->read_tree(shell_info._root_shell_path.get_folder(), info._shell_path, SORT_NAME);
640 -> see FileChildWindow::FileChildWindow()
644 static DynamicFct
<LPITEMIDLIST(WINAPI
*)(LPCITEMIDLIST
, LPCITEMIDLIST
)> ILFindChild(TEXT("SHELL32"), 24);
646 LPCITEMIDLIST child_pidl
;
649 child_pidl
= (*ILFindChild
)(_cur_dir
->create_absolute_pidl(), pidl
);
651 child_pidl
= pidl
; // This is not correct in the common case, but works on the desktop level.
654 _cur_dir
->smart_scan();
656 entry
= _cur_dir
->find_entry(child_pidl
);
663 //@@ work around as long as we don't iterate correctly through the ShellEntry tree
665 UpdateFolderView(ShellFolder(pidl
));
669 void ShellBrowserChild::jump_to(Entry
* entry
)
671 if (entry
->_etype
== ET_SHELL
) {
672 IShellFolder
* folder
;
673 ShellDirectory
* se
= static_cast<ShellDirectory
*>(entry
);
675 if (se
->_data
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
676 folder
= static_cast<ShellDirectory
*>(se
)->_folder
;
678 folder
= se
->get_parent_folder();
685 UpdateFolderView(folder
);