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