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