begin of drag drop support on desktop
[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 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 // create explorer treeview
91 if (_create_info._open_mode & OWM_EXPLORE)
92 _left_hwnd = CreateWindowEx(0, WC_TREEVIEW, NULL,
93 WS_CHILD|WS_TABSTOP|WS_VISIBLE|WS_CHILD|TVS_HASLINES|TVS_LINESATROOT|TVS_HASBUTTONS|TVS_NOTOOLTIPS|TVS_SHOWSELALWAYS,
94 0, rect.top, _split_pos-SPLIT_WIDTH/2, rect.bottom-rect.top,
95 _hwnd, (HMENU)IDC_FILETREE, g_Globals._hInstance, 0);
96
97 if (_left_hwnd) {
98 InitializeTree();
99
100 InitDragDrop();
101 } else
102 UpdateFolderView(_create_info._shell_path.get_folder());
103
104 return 0;
105 }
106
107
108 void ShellBrowserChild::InitializeTree()
109 {
110 CONTEXT("ShellBrowserChild::InitializeTree()");
111
112 TreeView_SetImageList(_left_hwnd, _himlSmall, TVSIL_NORMAL);
113 TreeView_SetScrollTime(_left_hwnd, 100);
114
115 const String& root_name = Desktop().get_name(_create_info._root_shell_path);
116
117 _root._drive_type = DRIVE_UNKNOWN;
118 lstrcpy(_root._volname, root_name); // most of the time "Desktop"
119 _root._fs_flags = 0;
120 lstrcpy(_root._fs, TEXT("Shell"));
121
122 //@@ _root._entry->read_tree(shell_info._root_shell_path.get_folder(), info._shell_path, SORT_NAME/*_sortOrder*/);
123
124 /*@todo
125 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
126 -> see FileChildWindow::FileChildWindow()
127 */
128 _root._entry = new ShellDirectory(Desktop(), _create_info._root_shell_path, _hwnd);
129 _root._entry->read_directory();
130
131 /* already filled by ShellDirectory constructor
132 lstrcpy(_root._entry->_data.cFileName, TEXT("Desktop")); */
133
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 ShellDirectory* dir = static_cast<ShellDirectory*>(entry->_up);
215 ShellFolder folder = dir? dir->_folder: Desktop();
216 LPCITEMIDLIST pidl = static_cast<ShellEntry*>(entry)->_pidl;
217
218 CHECKERROR(ShellFolderContextMenu(folder, ::GetParent(hwndTreeView), 1, &pidl, pptScreen->x, pptScreen->y));
219 }
220 }
221
222 void ShellBrowserChild::OnTreeGetDispInfo(int idCtrl, LPNMHDR pnmh)
223 {
224 CONTEXT("ShellBrowserChild::OnTreeGetDispInfo()");
225
226 LPNMTVDISPINFO lpdi = (LPNMTVDISPINFO)pnmh;
227 ShellEntry* entry = (ShellEntry*)lpdi->item.lParam;
228
229 if (lpdi->item.mask & TVIF_TEXT)
230 lpdi->item.pszText = entry->_display_name;
231
232 if (lpdi->item.mask & (/*TVIF_TEXT|*/TVIF_IMAGE|TVIF_SELECTEDIMAGE)) {
233 ShellPath pidl_abs = entry->create_absolute_pidl(); // Caching of absolute PIDLs could enhance performance.
234 LPCITEMIDLIST pidl = pidl_abs;
235
236 SHFILEINFO sfi;
237 /*
238 if (lpdi->item.mask & TVIF_TEXT)
239 if (SHGetFileInfo((LPCTSTR)pidl, 0, &sfi, sizeof(sfi), SHGFI_PIDL|SHGFI_DISPLAYNAME))
240 lstrcpy(lpdi->item.pszText, sfi.szDisplayName); ///@todo look at cchTextMax if there is enough space available
241 else
242 lpdi->item.pszText = entry->_data.cFileName;
243 */
244 if (lpdi->item.mask & TVIF_IMAGE)
245 if ((HIMAGELIST)SHGetFileInfo((LPCTSTR)pidl, 0, &sfi, sizeof(sfi), SHGFI_PIDL|SHGFI_SYSICONINDEX|SHGFI_SMALLICON|SHGFI_LINKOVERLAY) == _himlSmall)
246 lpdi->item.iImage = sfi.iIcon;
247 else
248 lpdi->item.iImage = -1;
249
250 if (lpdi->item.mask & TVIF_SELECTEDIMAGE)
251 if ((HIMAGELIST)SHGetFileInfo((LPCTSTR)pidl, 0, &sfi, sizeof(sfi), SHGFI_PIDL|SHGFI_SYSICONINDEX|SHGFI_SMALLICON|SHGFI_OPENICON) == _himlSmall)
252 lpdi->item.iSelectedImage = sfi.iIcon;
253 else
254 lpdi->item.iSelectedImage = -1;
255 }
256 }
257
258 void ShellBrowserChild::OnTreeItemExpanding(int idCtrl, LPNMTREEVIEW pnmtv)
259 {
260 CONTEXT("ShellBrowserChild::OnTreeItemExpanding()");
261
262 if (pnmtv->action == TVE_COLLAPSE)
263 TreeView_Expand(_left_hwnd, pnmtv->itemNew.hItem, TVE_COLLAPSE|TVE_COLLAPSERESET);
264 else if (pnmtv->action == TVE_EXPAND) {
265 ShellDirectory* entry = (ShellDirectory*)TreeView_GetItemData(_left_hwnd, pnmtv->itemNew.hItem);
266
267 if (entry)
268 if (!InsertSubitems(pnmtv->itemNew.hItem, entry, entry->_folder)) {
269 entry->_shell_attribs &= ~SFGAO_HASSUBFOLDER;
270
271 // remove subitem "+"
272 TV_ITEM tvItem;
273
274 tvItem.mask = TVIF_CHILDREN;
275 tvItem.hItem = pnmtv->itemNew.hItem;
276 tvItem.cChildren = 0;
277
278 TreeView_SetItem(_left_hwnd, &tvItem);
279 }
280 }
281 }
282
283 int ShellBrowserChild::InsertSubitems(HTREEITEM hParentItem, Entry* entry, IShellFolder* pParentFolder)
284 {
285 CONTEXT("ShellBrowserChild::InsertSubitems()");
286
287 WaitCursor wait;
288
289 int cnt = 0;
290
291 SendMessage(_left_hwnd, WM_SETREDRAW, FALSE, 0);
292
293 try {
294 entry->smart_scan();
295 } catch(COMException& e) {
296 HandleException(e, g_Globals._hMainWnd);
297 }
298
299 TV_ITEM tvItem;
300 TV_INSERTSTRUCT tvInsert;
301
302 for(entry=entry->_down; entry; entry=entry->_next) {
303 #ifndef _LEFT_FILES
304 if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
305 #endif
306 {
307 ZeroMemory(&tvItem, sizeof(tvItem));
308
309 tvItem.mask = TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_CHILDREN;
310 tvItem.pszText = LPSTR_TEXTCALLBACK;
311 tvItem.iImage = tvItem.iSelectedImage = I_IMAGECALLBACK;
312 tvItem.lParam = (LPARAM)entry;
313 tvItem.cChildren = entry->_shell_attribs & SFGAO_HASSUBFOLDER? 1: 0;
314
315 if (entry->_shell_attribs & SFGAO_SHARE) {
316 tvItem.mask |= TVIF_STATE;
317 tvItem.stateMask |= TVIS_OVERLAYMASK;
318 tvItem.state |= INDEXTOOVERLAYMASK(1);
319 }
320
321 tvInsert.item = tvItem;
322 tvInsert.hInsertAfter = TVI_LAST;
323 tvInsert.hParent = hParentItem;
324
325 TreeView_InsertItem(_left_hwnd, &tvInsert);
326 }
327
328 ++cnt;
329 }
330
331 SendMessage(_left_hwnd, WM_SETREDRAW, TRUE, 0);
332
333 return cnt;
334 }
335
336 void ShellBrowserChild::OnTreeItemSelected(int idCtrl, LPNMTREEVIEW pnmtv)
337 {
338 CONTEXT("ShellBrowserChild::OnTreeItemSelected()");
339
340 ShellEntry* entry = (ShellEntry*)pnmtv->itemNew.lParam;
341
342 _last_sel = pnmtv->itemNew.hItem;
343
344 IShellFolder* folder;
345
346 if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
347 folder = static_cast<ShellDirectory*>(entry)->_folder;
348 else
349 folder = entry->get_parent_folder();
350
351 if (!folder) {
352 assert(folder);
353 return;
354 }
355
356 UpdateFolderView(folder);
357 }
358
359 void ShellBrowserChild::UpdateFolderView(IShellFolder* folder)
360 {
361 CONTEXT("ShellBrowserChild::UpdateFolderView()");
362
363 FOLDERSETTINGS fs;
364 IShellView* pLastShellView = _pShellView;
365
366 _folder = folder;
367
368 if (pLastShellView)
369 pLastShellView->GetCurrentInfo(&fs);
370 else {
371 fs.ViewMode = _create_info._open_mode&OWM_DETAILS? FVM_DETAILS: FVM_ICON;
372 fs.fFlags = FWF_NOCLIENTEDGE|FWF_BESTFITWINDOW;
373 }
374
375 HRESULT hr = folder->CreateViewObject(_hwnd, IID_IShellView, (void**)&_pShellView);
376
377 if (FAILED(hr)) {
378 _pShellView = NULL;
379 return;
380 }
381
382 RECT rect = {CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT};
383 hr = _pShellView->CreateViewWindow(pLastShellView, &fs, static_cast<IShellBrowser*>(this), &rect, &_right_hwnd/*&m_hWndListView*/);
384
385 if (pLastShellView) {
386 pLastShellView->GetCurrentInfo(&fs);
387 pLastShellView->UIActivate(SVUIA_DEACTIVATE);
388 pLastShellView->DestroyViewWindow();
389 pLastShellView->Release();
390
391 ClientRect clnt(_hwnd);
392 resize_children(clnt.right, clnt.bottom);
393 }
394
395 _pShellView->UIActivate(SVUIA_ACTIVATE_NOFOCUS);
396 }
397
398
399 LRESULT ShellBrowserChild::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
400 {
401 switch(nmsg) {
402 case WM_GETISHELLBROWSER: // for Registry Explorer Plugin
403 return (LRESULT)static_cast<IShellBrowser*>(this);
404
405 default:
406 return super::WndProc(nmsg, wparam, lparam);
407 }
408
409 return 0;
410 }
411
412 int ShellBrowserChild::Notify(int id, NMHDR* pnmh)
413 {
414 switch(pnmh->code) {
415 case TVN_GETDISPINFO: OnTreeGetDispInfo(id, pnmh); break;
416 case TVN_ITEMEXPANDING: OnTreeItemExpanding(id, (LPNMTREEVIEW)pnmh); break;
417 case TVN_SELCHANGED: OnTreeItemSelected(id, (LPNMTREEVIEW)pnmh); break;
418 case NM_RCLICK: OnTreeItemRClick(id, pnmh); break;
419 default: return super::Notify(id, pnmh);
420 }
421
422 return 0;
423 }
424
425
426 HRESULT ShellBrowserChild::OnDefaultCommand(LPIDA pida)
427 {
428 CONTEXT("ShellBrowserChild::OnDefaultCommand()");
429
430 if (pida->cidl>=1) {
431 if (_left_hwnd) { // explorer mode
432 if (_last_sel) {
433 ShellDirectory* parent = (ShellDirectory*)TreeView_GetItemData(_left_hwnd, _last_sel);
434
435 if (parent) {
436 try {
437 parent->smart_scan();
438 } catch(COMException& e) {
439 return e.Error();
440 }
441
442 UINT firstOffset = pida->aoffset[1];
443 LPITEMIDLIST pidl = (LPITEMIDLIST)((LPBYTE)pida+firstOffset);
444
445 Entry* entry = parent->find_entry(pidl);
446
447 if (entry && (entry->_data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY))
448 if (expand_folder(static_cast<ShellDirectory*>(entry)))
449 return S_OK;
450 }
451 }
452 } else { // no tree control
453 if (MainFrame::OpenShellFolders(pida, _hWndFrame))
454 return S_OK;
455
456 /* create new Frame Window
457 if (MainFrame::OpenShellFolders(pida, 0))
458 return S_OK;
459 */
460 }
461 }
462
463 return E_NOTIMPL;
464 }
465
466
467 bool ShellBrowserChild::expand_folder(ShellDirectory* entry)
468 {
469 CONTEXT("ShellBrowserChild::expand_folder()");
470
471 //HTREEITEM hitem_sel = TreeView_GetSelection(_left_hwnd);
472 if (!_last_sel)
473 return false;
474
475 if (!TreeView_Expand(_left_hwnd, _last_sel, TVE_EXPAND))
476 return false;
477
478 for(HTREEITEM hitem=TreeView_GetChild(_left_hwnd,_last_sel); hitem; hitem=TreeView_GetNextSibling(_left_hwnd,hitem)) {
479 if ((ShellDirectory*)TreeView_GetItemData(_left_hwnd,hitem) == entry) {
480 if (TreeView_SelectItem(_left_hwnd, hitem) &&
481 TreeView_Expand(_left_hwnd, hitem, TVE_EXPAND))
482 return true;
483
484 break;
485 }
486 }
487
488 return false;
489 }