Changed shell namespacew sort order to file name instead of displayed name
[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 lpdi->item.pszText = entry->_display_name;
281
282 if (lpdi->item.mask & (/*TVIF_TEXT|*/TVIF_IMAGE|TVIF_SELECTEDIMAGE)) {
283 ShellPath pidl_abs = entry->create_absolute_pidl(); // Caching of absolute PIDLs could enhance performance.
284 LPCITEMIDLIST pidl = pidl_abs;
285
286 SHFILEINFO sfi;
287 /*
288 if (lpdi->item.mask & TVIF_TEXT)
289 if (SHGetFileInfo((LPCTSTR)pidl, 0, &sfi, sizeof(sfi), SHGFI_PIDL|SHGFI_DISPLAYNAME))
290 lstrcpy(lpdi->item.pszText, sfi.szDisplayName); //TODO: look at cchTextMax if there is enough space available
291 else
292 lpdi->item.pszText = entry->_data.cFileName;
293 */
294 if (lpdi->item.mask & TVIF_IMAGE)
295 if ((HIMAGELIST)SHGetFileInfo((LPCTSTR)pidl, 0, &sfi, sizeof(sfi), SHGFI_PIDL|SHGFI_SYSICONINDEX|SHGFI_SMALLICON|SHGFI_LINKOVERLAY) == _himlSmall)
296 lpdi->item.iImage = sfi.iIcon;
297 else
298 lpdi->item.iImage = -1;
299
300 if (lpdi->item.mask & TVIF_SELECTEDIMAGE)
301 if ((HIMAGELIST)SHGetFileInfo((LPCTSTR)pidl, 0, &sfi, sizeof(sfi), SHGFI_PIDL|SHGFI_SYSICONINDEX|SHGFI_SMALLICON|SHGFI_OPENICON) == _himlSmall)
302 lpdi->item.iSelectedImage = sfi.iIcon;
303 else
304 lpdi->item.iSelectedImage = -1;
305 }
306 }
307
308 void ShellBrowserChild::OnTreeItemExpanding(int idCtrl, LPNMTREEVIEW pnmtv)
309 {
310 if (pnmtv->action == TVE_COLLAPSE)
311 TreeView_Expand(_left_hwnd, pnmtv->itemNew.hItem, TVE_COLLAPSE|TVE_COLLAPSERESET);
312 else if (pnmtv->action == TVE_EXPAND) {
313 ShellDirectory* entry = (ShellDirectory*)TreeView_GetItemData(_left_hwnd, pnmtv->itemNew.hItem);
314
315 if (entry)
316 if (!InsertSubitems(pnmtv->itemNew.hItem, entry, entry->_folder)) {
317 entry->_shell_attribs &= ~SFGAO_HASSUBFOLDER;
318
319 // remove subitem "+"
320 TV_ITEM tvItem;
321
322 tvItem.mask = TVIF_CHILDREN;
323 tvItem.hItem = pnmtv->itemNew.hItem;
324 tvItem.cChildren = 0;
325
326 TreeView_SetItem(_left_hwnd, &tvItem);
327 }
328 }
329 }
330
331 int ShellBrowserChild::InsertSubitems(HTREEITEM hParentItem, Entry* entry, IShellFolder* pParentFolder)
332 {
333 WaitCursor wait;
334
335 int cnt = 0;
336
337 SendMessage(_left_hwnd, WM_SETREDRAW, FALSE, 0);
338
339 try {
340 entry->smart_scan();
341 } catch(COMException& e) {
342 HandleException(e, g_Globals._hMainWnd);
343 }
344
345 TV_ITEM tvItem;
346 TV_INSERTSTRUCT tvInsert;
347
348 for(entry=entry->_down; entry; entry=entry->_next) {
349 #ifndef _LEFT_FILES
350 if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
351 #endif
352 {
353 ZeroMemory(&tvItem, sizeof(tvItem));
354
355 tvItem.mask = TVIF_PARAM | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_CHILDREN;
356 tvItem.pszText = LPSTR_TEXTCALLBACK;
357 tvItem.iImage = tvItem.iSelectedImage = I_IMAGECALLBACK;
358 tvItem.lParam = (LPARAM)entry;
359 tvItem.cChildren = entry->_shell_attribs & SFGAO_HASSUBFOLDER? 1: 0;
360
361 if (entry->_shell_attribs & SFGAO_SHARE) {
362 tvItem.mask |= TVIF_STATE;
363 tvItem.stateMask |= TVIS_OVERLAYMASK;
364 tvItem.state |= INDEXTOOVERLAYMASK(1);
365 }
366
367 tvInsert.item = tvItem;
368 tvInsert.hInsertAfter = TVI_LAST;
369 tvInsert.hParent = hParentItem;
370
371 TreeView_InsertItem(_left_hwnd, &tvInsert);
372 }
373
374 ++cnt;
375 }
376
377 SendMessage(_left_hwnd, WM_SETREDRAW, TRUE, 0);
378
379 return cnt;
380 }
381
382 void ShellBrowserChild::OnTreeItemSelected(int idCtrl, LPNMTREEVIEW pnmtv)
383 {
384 ShellEntry* entry = (ShellEntry*)pnmtv->itemNew.lParam;
385
386 _last_sel = pnmtv->itemNew.hItem;
387
388 IShellFolder* folder;
389
390 if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
391 folder = static_cast<ShellDirectory*>(entry)->_folder;
392 else
393 folder = entry->get_parent_folder();
394
395 if (!folder) {
396 assert(folder);
397 return;
398 }
399
400 UpdateFolderView(folder);
401 }
402
403 void ShellBrowserChild::UpdateFolderView(IShellFolder* folder)
404 {
405 FOLDERSETTINGS fs;
406 IShellView* pLastShellView = _pShellView;
407
408 _folder = folder;
409
410 if (pLastShellView)
411 pLastShellView->GetCurrentInfo(&fs);
412 else {
413 fs.ViewMode = _create_info._open_mode&OWM_DETAILS? FVM_DETAILS: FVM_ICON;
414 fs.fFlags = FWF_NOCLIENTEDGE;
415 }
416
417 HRESULT hr = folder->CreateViewObject(_hwnd, IID_IShellView, (void**)&_pShellView);
418
419 if (FAILED(hr)) {
420 _pShellView = NULL;
421 return;
422 }
423
424 RECT rect = {CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT};
425 hr = _pShellView->CreateViewWindow(pLastShellView, &fs, static_cast<IShellBrowser*>(this), &rect, &_right_hwnd/*&m_hWndListView*/);
426
427 if (pLastShellView) {
428 pLastShellView->GetCurrentInfo(&fs);
429 pLastShellView->UIActivate(SVUIA_DEACTIVATE);
430 pLastShellView->DestroyViewWindow();
431 pLastShellView->Release();
432
433 ClientRect clnt(_hwnd);
434 resize_children(clnt.right, clnt.bottom);
435 }
436
437 _pShellView->UIActivate(SVUIA_ACTIVATE_NOFOCUS);
438 }
439
440
441 LRESULT ShellBrowserChild::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
442 {
443 switch(nmsg) {
444 case WM_GETISHELLBROWSER: // for Registry Explorer Plugin
445 return (LRESULT)static_cast<IShellBrowser*>(this);
446
447 default:
448 return super::WndProc(nmsg, wparam, lparam);
449 }
450
451 return 0;
452 }
453
454 int ShellBrowserChild::Notify(int id, NMHDR* pnmh)
455 {
456 switch(pnmh->code) {
457 case TVN_GETDISPINFO: OnTreeGetDispInfo(id, pnmh); break;
458 case TVN_ITEMEXPANDING: OnTreeItemExpanding(id, (LPNMTREEVIEW)pnmh); break;
459 case TVN_SELCHANGED: OnTreeItemSelected(id, (LPNMTREEVIEW)pnmh); break;
460 case NM_RCLICK: OnTreeItemRClick(id, pnmh); break;
461 default: return super::Notify(id, pnmh);
462 }
463
464 return 0;
465 }
466
467
468 HRESULT ShellBrowserChild::OnDefaultCommand(LPIDA pIDList)
469 {
470 if (pIDList->cidl>=1) {
471 if (_left_hwnd) { // explorer mode
472 if (_last_sel) {
473 ShellDirectory* parent = (ShellDirectory*)TreeView_GetItemData(_left_hwnd, _last_sel);
474
475 if (parent) {
476 try {
477 parent->smart_scan();
478 } catch(COMException& e) {
479 return e.Error();
480 }
481
482 UINT firstOffset = pIDList->aoffset[1];
483 LPITEMIDLIST pidl = (LPITEMIDLIST)((LPBYTE)pIDList+firstOffset);
484
485 Entry* entry = parent->find_entry(pidl);
486
487 if (entry && (entry->_data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY))
488 if (expand_folder(static_cast<ShellDirectory*>(entry)))
489 return S_OK;
490 }
491 }
492 } else { // no tree control
493 if (MainFrame::OpenShellFolders(pIDList, _hWndFrame))
494 return S_OK;
495
496 /* create new Frame Window
497 if (MainFrame::OpenShellFolders(pIDList, 0))
498 return S_OK;
499 */
500 }
501 }
502
503 return E_NOTIMPL;
504 }
505
506
507 bool ShellBrowserChild::expand_folder(ShellDirectory* entry)
508 {
509 //HTREEITEM hitem_sel = TreeView_GetSelection(_left_hwnd);
510 if (!_last_sel)
511 return false;
512
513 if (!TreeView_Expand(_left_hwnd, _last_sel, TVE_EXPAND))
514 return false;
515
516 for(HTREEITEM hitem=TreeView_GetChild(_left_hwnd,_last_sel); hitem; hitem=TreeView_GetNextSibling(_left_hwnd,hitem)) {
517 if ((ShellDirectory*)TreeView_GetItemData(_left_hwnd,hitem) == entry) {
518 if (TreeView_SelectItem(_left_hwnd, hitem) &&
519 TreeView_Expand(_left_hwnd, hitem, TVE_EXPAND))
520 return true;
521
522 break;
523 }
524 }
525
526 return false;
527 }