#include "precomp.h" -> #include <precomp.h>
[reactos.git] / reactos / subsys / system / explorer / shell / shellbrowser.cpp
1 /*
2 * Copyright 2003, 2004, 2005 Martin Fuchs
3 *
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.
8 *
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.
13 *
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
17 */
18
19
20 //
21 // Explorer clone
22 //
23 // shellbrowser.cpp
24 //
25 // Martin Fuchs, 23.07.2003
26 //
27
28
29 #include <precomp.h>
30
31 #include "../resource.h"
32
33
34 ShellBrowser::ShellBrowser(HWND hwnd, HWND left_hwnd, WindowHandle& right_hwnd, ShellPathInfo& create_info,
35 HIMAGELIST himl, BrowserCallback* cb, CtxMenuInterfaces& cm_ifs)
36 : _hwnd(hwnd),
37 _left_hwnd(left_hwnd),
38 _right_hwnd(right_hwnd),
39 _create_info(create_info),
40 _himl(himl),
41 _callback(cb),
42 _cm_ifs(cm_ifs)
43 {
44 _pShellView = NULL;
45 _pDropTarget = NULL;
46 _last_sel = 0;
47
48 _cur_dir = NULL;
49 }
50
51 ShellBrowser::~ShellBrowser()
52 {
53 (void)TreeView_SetImageList(_left_hwnd, 0, TVSIL_NORMAL);
54
55 if (_pShellView)
56 _pShellView->Release();
57
58 if (_pDropTarget) {
59 _pDropTarget->Release();
60 _pDropTarget = NULL;
61 }
62
63 if (_right_hwnd) {
64 DestroyWindow(_right_hwnd);
65 _right_hwnd = 0;
66 }
67 }
68
69
70 LRESULT ShellBrowser::Init(HWND hWndFrame)
71 {
72 CONTEXT("ShellBrowser::Init()");
73
74 _hWndFrame = hWndFrame;
75
76 const String& root_name = GetDesktopFolder().get_name(_create_info._root_shell_path, SHGDN_FORADDRESSBAR);
77
78 _root._drive_type = DRIVE_UNKNOWN;
79 lstrcpy(_root._volname, root_name);
80 _root._fs_flags = 0;
81 lstrcpy(_root._fs, TEXT("Desktop"));
82
83 _root._entry = new ShellDirectory(GetDesktopFolder(), _create_info._root_shell_path, _hwnd);
84
85 jump_to(_create_info._shell_path);
86
87 // -> set_curdir()
88 _root._entry->read_directory();
89
90 /* already filled by ShellDirectory constructor
91 lstrcpy(_root._entry->_data.cFileName, TEXT("Desktop")); */
92
93 return 0;
94 }
95
96 void ShellBrowser::jump_to(LPCITEMIDLIST pidl)
97 {
98 Entry* entry = NULL;
99
100 //@@
101 if (!_cur_dir)
102 _cur_dir = static_cast<ShellDirectory*>(_root._entry);
103
104 /*@todo
105 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
106 _root._entry->read_tree(shell_info._root_shell_path.get_folder(), info._shell_path, SORT_NAME);
107 -> see FileChildWindow::FileChildWindow()
108 */
109
110 if (_cur_dir) {
111 static DynamicFct<LPITEMIDLIST(WINAPI*)(LPCITEMIDLIST, LPCITEMIDLIST)> ILFindChild(TEXT("SHELL32"), 24);
112
113 LPCITEMIDLIST child_pidl;
114
115 if (ILFindChild)
116 child_pidl = (*ILFindChild)(_cur_dir->create_absolute_pidl(), pidl);
117 else
118 child_pidl = pidl; // This is not correct in the common case, but works on the desktop level.
119
120 if (child_pidl) {
121 _cur_dir->smart_scan();
122
123 entry = _cur_dir->find_entry(child_pidl);
124
125 if (entry)
126 _callback->entry_selected(entry);
127 }
128 }
129
130 //@@ work around as long as we don't iterate correctly through the ShellEntry tree
131 if (!entry)
132 UpdateFolderView(ShellFolder(pidl));
133 }
134
135
136 void ShellBrowser::InitializeTree(HIMAGELIST himl)
137 {
138 CONTEXT("ShellBrowserChild::InitializeTree()");
139
140 (void)TreeView_SetImageList(_left_hwnd, himl, TVSIL_NORMAL);
141 TreeView_SetScrollTime(_left_hwnd, 100);
142
143 TV_INSERTSTRUCT tvInsert;
144
145 tvInsert.hParent = 0;
146 tvInsert.hInsertAfter = TVI_LAST;
147
148 TV_ITEM& tvItem = tvInsert.item;
149 tvItem.mask = TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_CHILDREN;
150 tvItem.lParam = (LPARAM)_root._entry;
151 tvItem.pszText = LPSTR_TEXTCALLBACK;
152 tvItem.iImage = tvItem.iSelectedImage = I_IMAGECALLBACK;
153 tvItem.cChildren = 1;
154
155 HTREEITEM hItem = TreeView_InsertItem(_left_hwnd, &tvInsert);
156 TreeView_SelectItem(_left_hwnd, hItem);
157 TreeView_Expand(_left_hwnd, hItem, TVE_EXPAND);
158 }
159
160 bool ShellBrowser::InitDragDrop()
161 {
162 CONTEXT("ShellBrowser::InitDragDrop()");
163
164 _pDropTarget = new TreeDropTarget(_left_hwnd);
165
166 if (!_pDropTarget)
167 return false;
168
169 _pDropTarget->AddRef();
170
171 if (FAILED(RegisterDragDrop(_left_hwnd, _pDropTarget))) {//calls addref
172 _pDropTarget->Release(); // free TreeDropTarget
173 _pDropTarget = NULL;
174 return false;
175 }
176 else
177 _pDropTarget->Release();
178
179 FORMATETC ftetc;
180
181 ftetc.dwAspect = DVASPECT_CONTENT;
182 ftetc.lindex = -1;
183 ftetc.tymed = TYMED_HGLOBAL;
184 ftetc.cfFormat = CF_HDROP;
185
186 _pDropTarget->AddSuportedFormat(ftetc);
187
188 return true;
189 }
190
191
192 void ShellBrowser::OnTreeItemRClick(int idCtrl, LPNMHDR pnmh)
193 {
194 CONTEXT("ShellBrowser::OnTreeItemRClick()");
195
196 TVHITTESTINFO tvhti;
197
198 GetCursorPos(&tvhti.pt);
199 ScreenToClient(_left_hwnd, &tvhti.pt);
200
201 tvhti.flags = LVHT_NOWHERE;
202 (void)TreeView_HitTest(_left_hwnd, &tvhti);
203
204 if (TVHT_ONITEM & tvhti.flags) {
205 LPARAM itemData = TreeView_GetItemData(_left_hwnd, tvhti.hItem);
206
207 if (itemData) {
208 Entry* entry = (Entry*)itemData;
209 ClientToScreen(_left_hwnd, &tvhti.pt);
210
211 CHECKERROR(entry->do_context_menu(_hwnd, tvhti.pt, _cm_ifs));
212 }
213 }
214 }
215
216 void ShellBrowser::OnTreeGetDispInfo(int idCtrl, LPNMHDR pnmh)
217 {
218 CONTEXT("ShellBrowser::OnTreeGetDispInfo()");
219
220 LPNMTVDISPINFO lpdi = (LPNMTVDISPINFO)pnmh;
221 ShellEntry* entry = (ShellEntry*)lpdi->item.lParam;
222
223 if (entry) {
224 if (lpdi->item.mask & TVIF_TEXT)
225 lpdi->item.pszText = entry->_display_name;
226
227 if (lpdi->item.mask & (/*TVIF_TEXT|*/TVIF_IMAGE|TVIF_SELECTEDIMAGE)) {
228 ShellPath pidl_abs = entry->create_absolute_pidl(); // Caching of absolute PIDLs could enhance performance.
229 LPCITEMIDLIST pidl = pidl_abs;
230
231 SHFILEINFO sfi;
232 /*
233 if (lpdi->item.mask & TVIF_TEXT)
234 if (SHGetFileInfo((LPCTSTR)pidl, 0, &sfi, sizeof(sfi), SHGFI_PIDL|SHGFI_DISPLAYNAME))
235 lstrcpy(lpdi->item.pszText, sfi.szDisplayName); ///@todo look at cchTextMax if there is enough space available
236 else
237 lpdi->item.pszText = entry->_data.cFileName;
238 */
239 if (lpdi->item.mask & TVIF_IMAGE)
240 if ((HIMAGELIST)SHGetFileInfo((LPCTSTR)pidl, 0, &sfi, sizeof(sfi), SHGFI_PIDL|SHGFI_SYSICONINDEX|SHGFI_SMALLICON|SHGFI_LINKOVERLAY) == _himl)
241 lpdi->item.iImage = sfi.iIcon;
242 else
243 lpdi->item.iImage = -1;
244
245 if (lpdi->item.mask & TVIF_SELECTEDIMAGE)
246 if ((HIMAGELIST)SHGetFileInfo((LPCTSTR)pidl, 0, &sfi, sizeof(sfi), SHGFI_PIDL|SHGFI_SYSICONINDEX|SHGFI_SMALLICON|SHGFI_OPENICON) == _himl)
247 lpdi->item.iSelectedImage = sfi.iIcon;
248 else
249 lpdi->item.iSelectedImage = -1;
250 }
251 }
252 }
253
254 void ShellBrowser::OnTreeItemExpanding(int idCtrl, LPNMTREEVIEW pnmtv)
255 {
256 CONTEXT("ShellBrowser::OnTreeItemExpanding()");
257
258 if (pnmtv->action == TVE_COLLAPSE)
259 TreeView_Expand(_left_hwnd, pnmtv->itemNew.hItem, TVE_COLLAPSE|TVE_COLLAPSERESET);
260 else if (pnmtv->action == TVE_EXPAND) {
261 ShellDirectory* entry = (ShellDirectory*)TreeView_GetItemData(_left_hwnd, pnmtv->itemNew.hItem);
262
263 if (entry)
264 if (!InsertSubitems(pnmtv->itemNew.hItem, entry, entry->_folder)) {
265 entry->_shell_attribs &= ~SFGAO_HASSUBFOLDER;
266
267 // remove subitem "+"
268 TV_ITEM tvItem;
269
270 tvItem.mask = TVIF_CHILDREN;
271 tvItem.hItem = pnmtv->itemNew.hItem;
272 tvItem.cChildren = 0;
273
274 TreeView_SetItem(_left_hwnd, &tvItem);
275 }
276 }
277 }
278
279 int ShellBrowser::InsertSubitems(HTREEITEM hParentItem, Entry* entry, IShellFolder* pParentFolder)
280 {
281 CONTEXT("ShellBrowser::InsertSubitems()");
282
283 WaitCursor wait;
284
285 int cnt = 0;
286
287 SendMessage(_left_hwnd, WM_SETREDRAW, FALSE, 0);
288
289 try {
290 entry->smart_scan();
291 } catch(COMException& e) {
292 HandleException(e, g_Globals._hMainWnd);
293 }
294
295 TV_ITEM tvItem;
296 TV_INSERTSTRUCT tvInsert;
297
298 for(entry=entry->_down; entry; entry=entry->_next) {
299 #ifndef _LEFT_FILES
300 if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
301 #endif
302 {
303 ZeroMemory(&tvItem, sizeof(tvItem));
304
305 tvItem.mask = TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_CHILDREN;
306 tvItem.pszText = LPSTR_TEXTCALLBACK;
307 tvItem.iImage = tvItem.iSelectedImage = I_IMAGECALLBACK;
308 tvItem.lParam = (LPARAM)entry;
309 tvItem.cChildren = entry->_shell_attribs & SFGAO_HASSUBFOLDER? 1: 0;
310
311 if (entry->_shell_attribs & SFGAO_SHARE) {
312 tvItem.mask |= TVIF_STATE;
313 tvItem.stateMask |= TVIS_OVERLAYMASK;
314 tvItem.state |= INDEXTOOVERLAYMASK(1);
315 }
316
317 tvInsert.item = tvItem;
318 tvInsert.hInsertAfter = TVI_LAST;
319 tvInsert.hParent = hParentItem;
320
321 (void)TreeView_InsertItem(_left_hwnd, &tvInsert);
322 }
323
324 ++cnt;
325 }
326
327 SendMessage(_left_hwnd, WM_SETREDRAW, TRUE, 0);
328
329 return cnt;
330 }
331
332 void ShellBrowser::OnTreeItemSelected(int idCtrl, LPNMTREEVIEW pnmtv)
333 {
334 CONTEXT("ShellBrowser::OnTreeItemSelected()");
335
336 ShellEntry* entry = (ShellEntry*)pnmtv->itemNew.lParam;
337
338 _last_sel = pnmtv->itemNew.hItem;
339
340 if (entry)
341 _callback->entry_selected(entry);
342 }
343
344 void ShellBrowser::UpdateFolderView(IShellFolder* folder)
345 {
346 CONTEXT("ShellBrowser::UpdateFolderView()");
347
348 FOLDERSETTINGS fs;
349 IShellView* pLastShellView = _pShellView;
350
351 _folder = folder;
352
353 if (pLastShellView)
354 pLastShellView->GetCurrentInfo(&fs);
355 else {
356 fs.ViewMode = _create_info._open_mode&OWM_DETAILS? FVM_DETAILS: FVM_ICON;
357 fs.fFlags = FWF_NOCLIENTEDGE|FWF_BESTFITWINDOW;
358 }
359
360 HRESULT hr = folder->CreateViewObject(_hwnd, IID_IShellView, (void**)&_pShellView);
361
362 if (FAILED(hr)) {
363 _pShellView = NULL;
364 return;
365 }
366
367 RECT rect = {CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT};
368 hr = _pShellView->CreateViewWindow(pLastShellView, &fs, static_cast<IShellBrowser*>(this), &rect, &_right_hwnd/*&m_hWndListView*/);
369
370 if (pLastShellView) {
371 pLastShellView->GetCurrentInfo(&fs);
372 pLastShellView->UIActivate(SVUIA_DEACTIVATE);
373 pLastShellView->DestroyViewWindow();
374 pLastShellView->Release();
375 }
376
377 _pShellView->UIActivate(SVUIA_ACTIVATE_NOFOCUS);
378 }
379
380
381 HRESULT ShellBrowser::OnDefaultCommand(LPIDA pida)
382 {
383 CONTEXT("ShellBrowser::OnDefaultCommand()");
384
385 if (pida->cidl >= 1) {
386 if (_left_hwnd) { // explorer mode
387 if (_last_sel) {
388 ShellDirectory* parent = (ShellDirectory*)TreeView_GetItemData(_left_hwnd, _last_sel);
389
390 if (parent) {
391 try {
392 parent->smart_scan();
393 } catch(COMException& e) {
394 return e.Error();
395 }
396
397 UINT firstOffset = pida->aoffset[1];
398 LPITEMIDLIST pidl = (LPITEMIDLIST)((LPBYTE)pida+firstOffset);
399
400 Entry* entry = parent->find_entry(pidl);
401
402 if (entry && (entry->_data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY))
403 if (entry->_etype == ET_SHELL)
404 if (_last_sel && select_entry(_last_sel, entry))
405 return S_OK;
406 }
407 }
408 } else { // no tree control
409 if (MainFrameBase::OpenShellFolders(pida, _hWndFrame))
410 return S_OK;
411
412 /* create new Frame Window
413 if (MainFrame::OpenShellFolders(pida, 0))
414 return S_OK;
415 */
416 }
417 }
418
419 return E_NOTIMPL;
420 }
421
422
423 HTREEITEM ShellBrowser::select_entry(HTREEITEM hitem, Entry* entry, bool expand)
424 {
425 CONTEXT("ShellBrowser::select_entry()");
426
427 if (expand && !TreeView_Expand(_left_hwnd, hitem, TVE_EXPAND))
428 return 0;
429
430 for(hitem=TreeView_GetChild(_left_hwnd,hitem); hitem; hitem=TreeView_GetNextSibling(_left_hwnd,hitem)) {
431 if ((Entry*)TreeView_GetItemData(_left_hwnd,hitem) == entry) {
432 if (TreeView_SelectItem(_left_hwnd, hitem)) {
433 if (expand)
434 TreeView_Expand(_left_hwnd, hitem, TVE_EXPAND);
435
436 return hitem;
437 }
438
439 break;
440 }
441 }
442
443 return 0;
444 }
445
446
447 bool ShellBrowser::jump_to_pidl(LPCITEMIDLIST pidl)
448 {
449 if (!_root._entry)
450 return false;
451
452 // iterate through the hierarchy and open all folders to reach pidl
453 WaitCursor wait;
454
455 HTREEITEM hitem = TreeView_GetRoot(_left_hwnd);
456 Entry* entry = _root._entry;
457
458 for(const void*p=pidl;;) {
459 if (!p)
460 return true;
461
462 if (!entry || !hitem)
463 break;
464
465 entry->smart_scan(SORT_NAME);
466
467 Entry* found = entry->find_entry(p);
468 p = entry->get_next_path_component(p);
469
470 if (found)
471 hitem = select_entry(hitem, found);
472
473 entry = found;
474 }
475
476 return false;
477 }
478
479
480 #ifndef _NO_MDI
481
482 MDIShellBrowserChild::MDIShellBrowserChild(HWND hwnd, const ShellChildWndInfo& info)
483 : super(hwnd, info),
484 _create_info(info),
485 _shellpath_info(info) //@@ copies info -> no referenz to _create_info !
486 {
487 /**todo Conversion of shell path into path string -> store into URL history
488 const String& path = GetDesktopFolder().get_name(info._shell_path, SHGDN_FORADDRESSBAR);
489 const String& parsingpath = GetDesktopFolder().get_name(info._shell_path, SHGDN_FORPARSING);
490
491 // store path into history
492 if (info._path && *info._path)
493 _url_history.push(info._path);
494 */
495 }
496
497
498 MDIShellBrowserChild* MDIShellBrowserChild::create(const ShellChildWndInfo& info)
499 {
500 ChildWindow* child = ChildWindow::create(info, info._pos.rcNormalPosition,
501 WINDOW_CREATOR_INFO(MDIShellBrowserChild,ShellChildWndInfo), CLASSNAME_CHILDWND, NULL, info._pos.showCmd==SW_SHOWMAXIMIZED? WS_MAXIMIZE: 0);
502
503 return static_cast<MDIShellBrowserChild*>(child);
504 }
505
506
507 LRESULT MDIShellBrowserChild::Init(LPCREATESTRUCT pcs)
508 {
509 CONTEXT("MDIShellBrowserChild::Init()");
510
511 if (super::Init(pcs))
512 return 1;
513
514 init_himl();
515
516 update_shell_browser();
517
518 if (&*_shellBrowser)
519 if (_left_hwnd)
520 _shellBrowser->Init(_himlSmall);
521 else
522 _shellBrowser->UpdateFolderView(_create_info._shell_path.get_folder());
523
524 return 0;
525 }
526
527
528 LRESULT MDIShellBrowserChild::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
529 {
530 switch(nmsg) {
531 case PM_DISPATCH_COMMAND: {
532 switch(LOWORD(wparam)) {
533 case ID_WINDOW_NEW: {CONTEXT("MDIShellBrowserChild PM_DISPATCH_COMMAND ID_WINDOW_NEW");
534 MDIShellBrowserChild::create(_create_info);
535 break;}
536
537 case ID_REFRESH:
538 //@todo refresh shell child
539 break;
540
541 case ID_VIEW_SDI:
542 MainFrameBase::Create(_url, false);
543 break;
544
545 default:
546 return super::WndProc(nmsg, wparam, lparam);
547 }
548 return TRUE;}
549
550 default:
551 return super::WndProc(nmsg, wparam, lparam);
552 }
553
554 return 0;
555 }
556
557 void MDIShellBrowserChild::update_shell_browser()
558 {
559 int split_pos = DEFAULT_SPLIT_POS;
560
561 if (_shellBrowser.get()) {
562 split_pos = _split_pos;
563 delete _shellBrowser.release();
564 }
565
566 // create explorer treeview
567 if (_create_info._open_mode & OWM_EXPLORE) {
568 if (!_left_hwnd) {
569 ClientRect rect(_hwnd);
570
571 _left_hwnd = CreateWindowEx(0, WC_TREEVIEW, NULL,
572 WS_CHILD|WS_TABSTOP|WS_VISIBLE|WS_CHILD|TVS_HASLINES|TVS_LINESATROOT|TVS_HASBUTTONS|TVS_SHOWSELALWAYS,//|TVS_NOTOOLTIPS
573 0, rect.top, split_pos-SPLIT_WIDTH/2, rect.bottom-rect.top,
574 _hwnd, (HMENU)IDC_FILETREE, g_Globals._hInstance, 0);
575 }
576 } else {
577 if (_left_hwnd) {
578 DestroyWindow(_left_hwnd);
579 _left_hwnd = 0;
580 }
581 }
582
583 _shellBrowser = auto_ptr<ShellBrowser>(new ShellBrowser(_hwnd, _left_hwnd, _right_hwnd,
584 _shellpath_info, _himlSmall, this, _cm_ifs));
585
586 _shellBrowser->Init(_hwndFrame);
587 }
588
589
590 String MDIShellBrowserChild::jump_to_int(LPCTSTR url)
591 {
592 String dir, fname;
593
594 if (!_tcsnicmp(url, TEXT("shell://"), 8)) {
595 if (_shellBrowser->jump_to_pidl(ShellPath(url+8)))
596 return url;
597 }
598
599 if (SplitFileSysURL(url, dir, fname)) {
600
601 ///@todo use fname
602
603 if (_shellBrowser->jump_to_pidl(ShellPath(dir)))
604 return FmtString(TEXT("file://%s"), (LPCTSTR)dir);
605 }
606
607 return String();
608 }
609
610
611 void MDIShellBrowserChild::entry_selected(Entry* entry)
612 {
613 if (entry->_etype == ET_SHELL) {
614 ShellEntry* shell_entry = static_cast<ShellEntry*>(entry);
615 IShellFolder* folder;
616
617 if (shell_entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
618 folder = static_cast<ShellDirectory*>(shell_entry)->_folder;
619 else
620 folder = shell_entry->get_parent_folder();
621
622 if (!folder) {
623 assert(folder);
624 return;
625 }
626
627 TCHAR path[MAX_PATH];
628
629 if (shell_entry->get_path(path)) {
630 String url;
631
632 if (path[0] == ':')
633 url.printf(TEXT("shell://%s"), path);
634 else
635 url.printf(TEXT("file://%s"), path);
636
637 set_url(url);
638 }
639
640 _shellBrowser->UpdateFolderView(folder);
641
642 // set size of new created shell view windows
643 ClientRect rt(_hwnd);
644 resize_children(rt.right, rt.bottom);
645 }
646 }
647
648 #endif