Implemented control panel window in cabinet view mode.
[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._mode_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 LPITEMIDLIST pidl = entry->create_absolute_pidl(_hwnd);
287 SHFILEINFO sfi;
288
289 if (lpdi->item.mask & TVIF_IMAGE) {
290 if (SHGetFileInfo((LPCTSTR)pidl, 0, &sfi, sizeof(sfi), SHGFI_PIDL|SHGFI_SYSICONINDEX|SHGFI_SMALLICON|SHGFI_LINKOVERLAY))
291 lpdi->item.iImage = sfi.iIcon;
292 }
293
294 if (lpdi->item.mask & TVIF_SELECTEDIMAGE) {
295 if (SHGetFileInfo((LPCTSTR)pidl, 0, &sfi, sizeof(sfi), SHGFI_PIDL|SHGFI_SYSICONINDEX|SHGFI_SMALLICON|SHGFI_OPENICON))
296 lpdi->item.iSelectedImage = sfi.iIcon;
297 }
298
299 if (pidl != &*entry->_pidl)
300 ShellMalloc()->Free(pidl);
301 }
302 }
303
304 void ShellBrowserChild::OnTreeItemExpanding(int idCtrl, LPNMTREEVIEW pnmtv)
305 {
306 if (pnmtv->action == TVE_COLLAPSE)
307 TreeView_Expand(_left_hwnd, pnmtv->itemNew.hItem, TVE_COLLAPSE|TVE_COLLAPSERESET);
308 else if (pnmtv->action == TVE_EXPAND) {
309 ShellDirectory* entry = (ShellDirectory*)TreeView_GetItemData(_left_hwnd, pnmtv->itemNew.hItem);
310
311 if (entry)
312 InsertSubitems(pnmtv->itemNew.hItem, entry, entry->_folder);
313 }
314 }
315
316 void ShellBrowserChild::InsertSubitems(HTREEITEM hParentItem, Entry* entry, IShellFolder* pParentFolder)
317 {
318 WaitCursor wait;
319
320 SendMessage(_left_hwnd, WM_SETREDRAW, FALSE, 0);
321
322 entry->smart_scan();
323
324 TV_ITEM tvItem;
325 TV_INSERTSTRUCT tvInsert;
326
327 for(entry=entry->_down; entry; entry=entry->_next) {
328 #ifndef _LEFT_FILES
329 if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
330 #endif
331 {
332 ZeroMemory(&tvItem, sizeof(tvItem));
333
334 tvItem.mask = TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_CHILDREN;
335 tvItem.pszText = LPSTR_TEXTCALLBACK;
336 tvItem.iImage = tvItem.iSelectedImage = I_IMAGECALLBACK;
337 tvItem.lParam = (LPARAM)entry;
338 tvItem.cChildren = entry->_shell_attribs & SFGAO_HASSUBFOLDER? 1: 0;
339
340 if (entry->_shell_attribs & SFGAO_SHARE) {
341 tvItem.mask |= TVIF_STATE;
342 tvItem.stateMask |= TVIS_OVERLAYMASK;
343 tvItem.state |= INDEXTOOVERLAYMASK(1);
344 }
345
346 tvInsert.item = tvItem;
347 tvInsert.hInsertAfter = TVI_LAST;
348 tvInsert.hParent = hParentItem;
349
350 TreeView_InsertItem(_left_hwnd, &tvInsert);
351 }
352 }
353
354 SendMessage(_left_hwnd, WM_SETREDRAW, TRUE, 0);
355 }
356
357 void ShellBrowserChild::OnTreeItemSelected(int idCtrl, LPNMTREEVIEW pnmtv)
358 {
359 ShellEntry* entry = (ShellEntry*)pnmtv->itemNew.lParam;
360
361 _last_sel = pnmtv->itemNew.hItem;
362
363 IShellFolder* folder;
364
365 if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
366 folder = static_cast<ShellDirectory*>(entry)->_folder;
367 else
368 folder = entry->get_parent_folder();
369
370 if (!folder) {
371 assert(folder);
372 return;
373 }
374
375 UpdateFolderView(folder);
376 }
377
378 void ShellBrowserChild::UpdateFolderView(IShellFolder* folder)
379 {
380 FOLDERSETTINGS fs;
381 IShellView* pLastShellView = _pShellView;
382
383 if (pLastShellView)
384 pLastShellView->GetCurrentInfo(&fs);
385 else {
386 fs.ViewMode = _left_hwnd? FVM_DETAILS: FVM_ICON;
387 fs.fFlags = FWF_NOCLIENTEDGE;
388 }
389
390 HRESULT hr = folder->CreateViewObject(_hwnd, IID_IShellView, (void**)&_pShellView);
391
392 if (FAILED(hr)) {
393 _pShellView = NULL;
394 return;
395 }
396
397 RECT rect = {CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT};
398 hr = _pShellView->CreateViewWindow(pLastShellView, &fs, static_cast<IShellBrowser*>(this), &rect, &_right_hwnd/*&m_hWndListView*/);
399
400 if (pLastShellView) {
401 pLastShellView->GetCurrentInfo(&fs);
402 pLastShellView->UIActivate(SVUIA_DEACTIVATE);
403 pLastShellView->DestroyViewWindow();
404 pLastShellView->Release();
405
406 ClientRect clnt(_hwnd);
407 resize_children(clnt.right, clnt.bottom);
408 }
409
410 _pShellView->UIActivate(SVUIA_ACTIVATE_NOFOCUS);
411 }
412
413
414 LRESULT ShellBrowserChild::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
415 {
416 switch(nmsg) {
417 case WM_GETISHELLBROWSER: // for Registry Explorer Plugin
418 return (LRESULT)static_cast<IShellBrowser*>(this);
419
420 default:
421 return super::WndProc(nmsg, wparam, lparam);
422 }
423
424 return 0;
425 }
426
427 int ShellBrowserChild::Notify(int id, NMHDR* pnmh)
428 {
429 switch(pnmh->code) {
430 case TVN_GETDISPINFO: OnTreeGetDispInfo(id, pnmh); break;
431 case TVN_ITEMEXPANDING: OnTreeItemExpanding(id, (LPNMTREEVIEW)pnmh); break;
432 case TVN_SELCHANGED: OnTreeItemSelected(id, (LPNMTREEVIEW)pnmh); break;
433 case NM_RCLICK: OnTreeItemRClick(id, pnmh); break;
434 default: return super::Notify(id, pnmh);
435 }
436
437 return 0;
438 }
439
440
441 // process default command: look for folders and traverse into them
442 HRESULT ShellBrowserChild::OnDefaultCommand(IShellView* ppshv)
443 {
444 static UINT CF_IDLIST = RegisterClipboardFormat(CFSTR_SHELLIDLIST);
445
446 HRESULT ret = E_NOTIMPL;
447
448 IDataObject* selection;
449 HRESULT hr = ppshv->GetItemObject(SVGIO_SELECTION, IID_IDataObject, (void**)&selection);
450 if (FAILED(hr))
451 return hr;
452
453
454 FORMATETC fetc;
455 fetc.cfFormat = CF_IDLIST;
456 fetc.ptd = NULL;
457 fetc.dwAspect = DVASPECT_CONTENT;
458 fetc.lindex = -1;
459 fetc.tymed = TYMED_HGLOBAL;
460
461 hr = selection->QueryGetData(&fetc);
462 if (FAILED(hr))
463 return hr;
464
465
466 STGMEDIUM stgm = {sizeof(STGMEDIUM), {0}, 0};
467
468 hr = selection->GetData(&fetc, &stgm);
469 if (FAILED(hr))
470 return hr;
471
472
473 DWORD pData = (DWORD)GlobalLock(stgm.hGlobal);
474 CIDA* pIDList = (CIDA*)pData;
475
476 if (pIDList->cidl >= 1) {
477 //UINT folderOffset = pIDList->aoffset[0];
478 //LPITEMIDLIST folder = (LPITEMIDLIST)(pData+folderOffset);
479
480 UINT firstOffset = pIDList->aoffset[1];
481 LPITEMIDLIST pidl = (LPITEMIDLIST)(pData+firstOffset);
482
483 //HTREEITEM hitem_sel = TreeView_GetSelection(_left_hwnd);
484
485 if (_last_sel) {
486 ShellDirectory* parent = (ShellDirectory*)TreeView_GetItemData(_left_hwnd, _last_sel);
487
488 if (parent) {
489 parent->smart_scan();
490
491 Entry* entry = parent->find_entry(pidl);
492
493 if (entry && (entry->_data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY))
494 if (expand_folder(static_cast<ShellDirectory*>(entry)))
495 ret = S_OK;
496 }
497 }
498 }
499
500 GlobalUnlock(stgm.hGlobal);
501 ReleaseStgMedium(&stgm);
502
503 selection->Release();
504
505 return ret;
506 }
507
508 bool ShellBrowserChild::expand_folder(ShellDirectory* entry)
509 {
510 //HTREEITEM hitem_sel = TreeView_GetSelection(_left_hwnd);
511 if (!_last_sel)
512 return false;
513
514 if (!TreeView_Expand(_left_hwnd, _last_sel, TVE_EXPAND))
515 return false;
516
517 for(HTREEITEM hitem=TreeView_GetChild(_left_hwnd,_last_sel); hitem; hitem=TreeView_GetNextSibling(_left_hwnd,hitem)) {
518 if ((ShellDirectory*)TreeView_GetItemData(_left_hwnd,hitem) == entry) {
519 if (TreeView_SelectItem(_left_hwnd, hitem) &&
520 TreeView_Expand(_left_hwnd, hitem, TVE_EXPAND))
521 return true;
522
523 break;
524 }
525 }
526
527 return false;
528 }