* open MDI cabinet folders instead of new mainframe windows
[reactos.git] / reactos / subsys / system / explorer / shell / shellbrowser.cpp
1 /*
2 * Copyright 2003 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 "../utility/utility.h"
30
31 #include "../explorer.h"
32 #include "../globals.h"
33
34 #include "../explorer_intres.h"
35
36
37 static LPARAM TreeView_GetItemData(HWND hwndTreeView, HTREEITEM hItem)
38 {
39 TVITEM tvItem;
40
41 tvItem.mask = TVIF_PARAM;
42 tvItem.hItem = hItem;
43
44 if (!TreeView_GetItem(hwndTreeView, &tvItem))
45 return 0;
46
47 return tvItem.lParam;
48 }
49
50
51 ShellBrowserChild::ShellBrowserChild(HWND hwnd, const ShellChildWndInfo& info)
52 : super(hwnd),
53 _create_info(info)
54 {
55 _pShellView = NULL;
56 _pDropTarget = NULL;
57 _himlSmall = 0;
58 _last_sel = 0;
59 }
60
61 ShellBrowserChild::~ShellBrowserChild()
62 {
63 if (_pShellView)
64 _pShellView->Release();
65
66 if (_pDropTarget) {
67 _pDropTarget->Release();
68 _pDropTarget = NULL;
69 }
70 }
71
72
73 LRESULT ShellBrowserChild::Init(LPCREATESTRUCT pcs)
74 {
75 if (super::Init(pcs))
76 return 1;
77
78 _hWndFrame = GetParent(pcs->hwndParent);
79
80 ClientRect rect(_hwnd);
81
82 SHFILEINFO sfi;
83
84 _himlSmall = (HIMAGELIST)SHGetFileInfo(TEXT("C:\\"), 0, &sfi, sizeof(SHFILEINFO), SHGFI_SYSICONINDEX|SHGFI_SMALLICON);
85 // _himlLarge = (HIMAGELIST)SHGetFileInfo(TEXT("C:\\"), 0, &sfi, sizeof(SHFILEINFO), SHGFI_SYSICONINDEX|SHGFI_LARGEICON);
86
87
88 // create explorer treeview
89 if (_create_info._open_mode & OWM_EXPLORE)
90 _left_hwnd = CreateWindowEx(0, WC_TREEVIEW, NULL,
91 WS_CHILD|WS_TABSTOP|WS_VISIBLE|WS_CHILD|TVS_HASLINES|TVS_LINESATROOT|TVS_HASBUTTONS|TVS_NOTOOLTIPS|TVS_SHOWSELALWAYS,
92 0, rect.top, _split_pos-SPLIT_WIDTH/2, rect.bottom-rect.top,
93 _hwnd, (HMENU)IDC_FILETREE, g_Globals._hInstance, 0);
94
95 if (_left_hwnd) {
96 InitializeTree();
97
98 InitDragDrop();
99 } else
100 UpdateFolderView(_create_info._shell_path.get_folder());
101
102 return 0;
103 }
104
105
106 void ShellBrowserChild::InitializeTree()
107 {
108 TreeView_SetImageList(_left_hwnd, _himlSmall, TVSIL_NORMAL);
109 TreeView_SetScrollTime(_left_hwnd, 100);
110
111 const String& root_name = Desktop().get_name(_create_info._root_shell_path);
112
113 _root._drive_type = DRIVE_UNKNOWN;
114 lstrcpy(_root._volname, root_name); // most of the time "Desktop"
115 _root._fs_flags = 0;
116 lstrcpy(_root._fs, TEXT("Shell"));
117
118 //@@ _root._entry->read_tree(shell_info._root_shell_path.get_folder(), info._shell_path, SORT_NAME/*_sortOrder*/);
119
120 /* TODO:
121 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
122 -> see FileChildWindow::FileChildWindow()
123 */
124 _root._entry = new ShellDirectory(Desktop(), _create_info._root_shell_path, _hwnd);
125 _root._entry->read_directory();
126
127 /* already filled by ShellDirectory constructor
128 lstrcpy(_root._entry->_data.cFileName, TEXT("Desktop")); */
129
130
131 TV_ITEM tvItem;
132
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;
138
139 TV_INSERTSTRUCT tvInsert;
140
141 tvInsert.hParent = 0;
142 tvInsert.hInsertAfter = TVI_LAST;
143 tvInsert.item = tvItem;
144
145 HTREEITEM hItem = TreeView_InsertItem(_left_hwnd, &tvInsert);
146 TreeView_SelectItem(_left_hwnd, hItem);
147 TreeView_Expand(_left_hwnd, hItem, TVE_EXPAND);
148 }
149
150
151 bool ShellBrowserChild::InitDragDrop()
152 {
153 _pDropTarget = new TreeDropTarget(_left_hwnd);
154
155 if (!_pDropTarget)
156 return false;
157
158 _pDropTarget->AddRef();
159
160 if (FAILED(RegisterDragDrop(_left_hwnd, _pDropTarget))) {//calls addref
161 _pDropTarget->Release(); // free TreeDropTarget
162 _pDropTarget = NULL;
163 return false;
164 }
165 else
166 _pDropTarget->Release();
167
168 FORMATETC ftetc;
169
170 ftetc.dwAspect = DVASPECT_CONTENT;
171 ftetc.lindex = -1;
172 ftetc.tymed = TYMED_HGLOBAL;
173 ftetc.cfFormat = CF_HDROP;
174
175 _pDropTarget->AddSuportedFormat(ftetc);
176
177 return true;
178 }
179
180
181 void ShellBrowserChild::OnTreeItemRClick(int idCtrl, LPNMHDR pnmh)
182 {
183 TVHITTESTINFO tvhti;
184
185 GetCursorPos(&tvhti.pt);
186 ScreenToClient(_left_hwnd, &tvhti.pt);
187
188 tvhti.flags = LVHT_NOWHERE;
189 TreeView_HitTest(_left_hwnd, &tvhti);
190
191 if (TVHT_ONITEM & tvhti.flags) {
192 ClientToScreen(_left_hwnd, &tvhti.pt);
193 Tree_DoItemMenu(_left_hwnd, tvhti.hItem , &tvhti.pt);
194 }
195 }
196
197 void ShellBrowserChild::Tree_DoItemMenu(HWND hwndTreeView, HTREEITEM hItem, LPPOINT pptScreen)
198 {
199 LPARAM itemData = TreeView_GetItemData(hwndTreeView, hItem);
200
201 if (itemData) {
202 HWND hwndParent = ::GetParent(hwndTreeView);
203 Entry* entry = (Entry*)itemData;
204
205 IShellFolder* shell_folder;
206 ShellDirectory* dir;
207
208 if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
209 dir = static_cast<ShellDirectory*>(entry);
210 shell_folder = dir->_folder;
211 } else {
212 dir = static_cast<ShellDirectory*>(entry->_up);
213 shell_folder = dir? dir->_folder: Desktop();
214 }
215
216 shell_folder->AddRef();
217
218 if (shell_folder) {
219 LPCITEMIDLIST pidl = static_cast<ShellEntry*>(entry)->_pidl;
220
221 IContextMenu* pcm;
222
223 HRESULT hr = shell_folder->GetUIObjectOf(hwndParent, 1, &pidl, IID_IContextMenu, NULL, (LPVOID*)&pcm);
224 // HRESULT hr = CDefFolderMenu_Create2(dir?dir->_pidl:DesktopFolder(), hwndParent, 1, &pidl, shell_folder, NULL, 0, NULL, &pcm);
225
226 if (SUCCEEDED(hr)) {
227 HMENU hPopup = CreatePopupMenu();
228
229 if (hPopup) {
230 hr = pcm->QueryContextMenu(hPopup, 0, 1, 0x7fff, CMF_NORMAL|CMF_EXPLORE);
231
232 if (SUCCEEDED(hr)) {
233 IContextMenu2* pcm2;
234
235 pcm->QueryInterface(IID_IContextMenu2, (LPVOID*)&pcm2);
236
237 UINT idCmd = TrackPopupMenu(hPopup,
238 TPM_LEFTALIGN | TPM_RETURNCMD | TPM_RIGHTBUTTON,
239 pptScreen->x,
240 pptScreen->y,
241 0,
242 hwndParent,
243 NULL);
244
245 if (pcm2) {
246 pcm2->Release();
247 pcm2 = NULL;
248 }
249
250 if (idCmd) {
251 CMINVOKECOMMANDINFO cmi;
252 cmi.cbSize = sizeof(CMINVOKECOMMANDINFO);
253 cmi.fMask = 0;
254 cmi.hwnd = hwndParent;
255 cmi.lpVerb = (LPCSTR)(INT_PTR)(idCmd - 1);
256 cmi.lpParameters = NULL;
257 cmi.lpDirectory = NULL;
258 cmi.nShow = SW_SHOWNORMAL;
259 cmi.dwHotKey = 0;
260 cmi.hIcon = NULL;
261 hr = pcm->InvokeCommand(&cmi);
262 }
263 }
264 }
265
266 pcm->Release();
267 }
268
269 shell_folder->Release();
270 }
271 }
272 }
273
274 void ShellBrowserChild::OnTreeGetDispInfo(int idCtrl, LPNMHDR pnmh)
275 {
276 LPNMTVDISPINFO lpdi = (LPNMTVDISPINFO)pnmh;
277 ShellEntry* entry = (ShellEntry*)lpdi->item.lParam;
278
279 if (lpdi->item.mask & TVIF_TEXT) {
280 /* if (SHGetFileInfo((LPCTSTR)&*entry->_pidl, 0, &sfi, sizeof(sfi), SHGFI_PIDL|SHGFI_DISPLAYNAME))
281 lstrcpy(lpdi->item.pszText, sfi.szDisplayName); */
282 lstrcpy(lpdi->item.pszText, entry->_data.cFileName);
283 }
284
285 if (lpdi->item.mask & (TVIF_IMAGE|TVIF_SELECTEDIMAGE)) {
286 ShellPath pidl_abs = entry->create_absolute_pidl(); // Caching of absolute PIDLs could enhance performance.
287 LPCITEMIDLIST pidl = pidl_abs;
288
289 SHFILEINFO sfi;
290
291 if (lpdi->item.mask & TVIF_IMAGE)
292 if ((HIMAGELIST)SHGetFileInfo((LPCTSTR)pidl, 0, &sfi, sizeof(sfi), SHGFI_PIDL|SHGFI_SYSICONINDEX|SHGFI_SMALLICON|SHGFI_LINKOVERLAY) == _himlSmall)
293 lpdi->item.iImage = sfi.iIcon;
294 else
295 lpdi->item.iImage = -1;
296
297 if (lpdi->item.mask & TVIF_SELECTEDIMAGE)
298 if ((HIMAGELIST)SHGetFileInfo((LPCTSTR)pidl, 0, &sfi, sizeof(sfi), SHGFI_PIDL|SHGFI_SYSICONINDEX|SHGFI_SMALLICON|SHGFI_OPENICON) == _himlSmall)
299 lpdi->item.iSelectedImage = sfi.iIcon;
300 else
301 lpdi->item.iSelectedImage = -1;
302 }
303 }
304
305 void ShellBrowserChild::OnTreeItemExpanding(int idCtrl, LPNMTREEVIEW pnmtv)
306 {
307 if (pnmtv->action == TVE_COLLAPSE)
308 TreeView_Expand(_left_hwnd, pnmtv->itemNew.hItem, TVE_COLLAPSE|TVE_COLLAPSERESET);
309 else if (pnmtv->action == TVE_EXPAND) {
310 ShellDirectory* entry = (ShellDirectory*)TreeView_GetItemData(_left_hwnd, pnmtv->itemNew.hItem);
311
312 if (entry)
313 if (!InsertSubitems(pnmtv->itemNew.hItem, entry, entry->_folder)) {
314 // remove subitem "+"
315 TV_ITEM tvItem;
316
317 tvItem.mask = TVIF_CHILDREN;
318 tvItem.hItem = pnmtv->itemNew.hItem;
319 tvItem.cChildren = 0;
320
321 TreeView_SetItem(_left_hwnd, &tvItem);
322 }
323 }
324 }
325
326 int ShellBrowserChild::InsertSubitems(HTREEITEM hParentItem, Entry* entry, IShellFolder* pParentFolder)
327 {
328 WaitCursor wait;
329
330 int cnt = 0;
331
332 SendMessage(_left_hwnd, WM_SETREDRAW, FALSE, 0);
333
334 try {
335 entry->smart_scan();
336 } catch(COMException& e) {
337 HandleException(e, g_Globals._hMainWnd);
338 }
339
340 TV_ITEM tvItem;
341 TV_INSERTSTRUCT tvInsert;
342
343 for(entry=entry->_down; entry; entry=entry->_next) {
344 #ifndef _LEFT_FILES
345 if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
346 #endif
347 {
348 ZeroMemory(&tvItem, sizeof(tvItem));
349
350 tvItem.mask = TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_CHILDREN;
351 tvItem.pszText = LPSTR_TEXTCALLBACK;
352 tvItem.iImage = tvItem.iSelectedImage = I_IMAGECALLBACK;
353 tvItem.lParam = (LPARAM)entry;
354 tvItem.cChildren = entry->_shell_attribs & SFGAO_HASSUBFOLDER? 1: 0;
355
356 if (entry->_shell_attribs & SFGAO_SHARE) {
357 tvItem.mask |= TVIF_STATE;
358 tvItem.stateMask |= TVIS_OVERLAYMASK;
359 tvItem.state |= INDEXTOOVERLAYMASK(1);
360 }
361
362 tvInsert.item = tvItem;
363 tvInsert.hInsertAfter = TVI_LAST;
364 tvInsert.hParent = hParentItem;
365
366 TreeView_InsertItem(_left_hwnd, &tvInsert);
367 }
368
369 ++cnt;
370 }
371
372 SendMessage(_left_hwnd, WM_SETREDRAW, TRUE, 0);
373
374 return cnt;
375 }
376
377 void ShellBrowserChild::OnTreeItemSelected(int idCtrl, LPNMTREEVIEW pnmtv)
378 {
379 ShellEntry* entry = (ShellEntry*)pnmtv->itemNew.lParam;
380
381 _last_sel = pnmtv->itemNew.hItem;
382
383 IShellFolder* folder;
384
385 if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
386 folder = static_cast<ShellDirectory*>(entry)->_folder;
387 else
388 folder = entry->get_parent_folder();
389
390 if (!folder) {
391 assert(folder);
392 return;
393 }
394
395 UpdateFolderView(folder);
396 }
397
398 void ShellBrowserChild::UpdateFolderView(IShellFolder* folder)
399 {
400 FOLDERSETTINGS fs;
401 IShellView* pLastShellView = _pShellView;
402
403 _folder = folder;
404
405 if (pLastShellView)
406 pLastShellView->GetCurrentInfo(&fs);
407 else {
408 fs.ViewMode = _create_info._open_mode&OWM_DETAILS? FVM_DETAILS: FVM_ICON;
409 fs.fFlags = FWF_NOCLIENTEDGE;
410 }
411
412 HRESULT hr = folder->CreateViewObject(_hwnd, IID_IShellView, (void**)&_pShellView);
413
414 if (FAILED(hr)) {
415 _pShellView = NULL;
416 return;
417 }
418
419 RECT rect = {CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT};
420 hr = _pShellView->CreateViewWindow(pLastShellView, &fs, static_cast<IShellBrowser*>(this), &rect, &_right_hwnd/*&m_hWndListView*/);
421
422 if (pLastShellView) {
423 pLastShellView->GetCurrentInfo(&fs);
424 pLastShellView->UIActivate(SVUIA_DEACTIVATE);
425 pLastShellView->DestroyViewWindow();
426 pLastShellView->Release();
427
428 ClientRect clnt(_hwnd);
429 resize_children(clnt.right, clnt.bottom);
430 }
431
432 _pShellView->UIActivate(SVUIA_ACTIVATE_NOFOCUS);
433 }
434
435
436 LRESULT ShellBrowserChild::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
437 {
438 switch(nmsg) {
439 case WM_GETISHELLBROWSER: // for Registry Explorer Plugin
440 return (LRESULT)static_cast<IShellBrowser*>(this);
441
442 default:
443 return super::WndProc(nmsg, wparam, lparam);
444 }
445
446 return 0;
447 }
448
449 int ShellBrowserChild::Notify(int id, NMHDR* pnmh)
450 {
451 switch(pnmh->code) {
452 case TVN_GETDISPINFO: OnTreeGetDispInfo(id, pnmh); break;
453 case TVN_ITEMEXPANDING: OnTreeItemExpanding(id, (LPNMTREEVIEW)pnmh); break;
454 case TVN_SELCHANGED: OnTreeItemSelected(id, (LPNMTREEVIEW)pnmh); break;
455 case NM_RCLICK: OnTreeItemRClick(id, pnmh); break;
456 default: return super::Notify(id, pnmh);
457 }
458
459 return 0;
460 }
461
462
463 HRESULT ShellBrowserChild::OnDefaultCommand(LPIDA pIDList)
464 {
465 if (pIDList->cidl>=1) {
466 if (_left_hwnd) { // explorer mode
467 if (_last_sel) {
468 ShellDirectory* parent = (ShellDirectory*)TreeView_GetItemData(_left_hwnd, _last_sel);
469
470 if (parent) {
471 try {
472 parent->smart_scan();
473 } catch(COMException& e) {
474 return e.Error();
475 }
476
477 UINT firstOffset = pIDList->aoffset[1];
478 LPITEMIDLIST pidl = (LPITEMIDLIST)((LPBYTE)pIDList+firstOffset);
479
480 Entry* entry = parent->find_entry(pidl);
481
482 if (entry && (entry->_data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY))
483 if (expand_folder(static_cast<ShellDirectory*>(entry)))
484 return S_OK;
485 }
486 }
487 } else { // no tree control
488 if (MainFrame::OpenShellFolders(pIDList, _hWndFrame))
489 return S_OK;
490
491 /* create new Frame Window
492 if (MainFrame::OpenShellFolders(pIDList, 0))
493 return S_OK;
494 */
495 }
496 }
497
498 return E_NOTIMPL;
499 }
500
501
502 bool ShellBrowserChild::expand_folder(ShellDirectory* entry)
503 {
504 //HTREEITEM hitem_sel = TreeView_GetSelection(_left_hwnd);
505 if (!_last_sel)
506 return false;
507
508 if (!TreeView_Expand(_left_hwnd, _last_sel, TVE_EXPAND))
509 return false;
510
511 for(HTREEITEM hitem=TreeView_GetChild(_left_hwnd,_last_sel); hitem; hitem=TreeView_GetNextSibling(_left_hwnd,hitem)) {
512 if ((ShellDirectory*)TreeView_GetItemData(_left_hwnd,hitem) == entry) {
513 if (TreeView_SelectItem(_left_hwnd, hitem) &&
514 TreeView_Expand(_left_hwnd, hitem, TVE_EXPAND))
515 return true;
516
517 break;
518 }
519 }
520
521 return false;
522 }