1 /*
2  * Copyright 2003, 2004, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
17  */
18 
19 
20  //
21  // Explorer clone
22  //
23  // filechild.cpp
24  //
25  // Martin Fuchs, 23.07.2003
26  //
27 
28 
29 #include <precomp.h>
30 
31 #include "ntobjfs.h"
32 #include "regfs.h"
33 #include "fatfs.h"
34 
35 FileChildWndInfo::FileChildWndInfo(HWND hmdiclient, LPCTSTR path, ENTRY_TYPE etype)
36  :	super(hmdiclient),
37 	_etype(etype)
38 {
39 #ifndef _NO_WIN_FS
40 	if (etype == ET_UNKNOWN)
41 #ifdef __WINE__
42 		if (*path == '/')
43 			_etype = ET_UNIX;
44 		else
45 #endif
46 			_etype = ET_WINDOWS;
47 #endif
48 
49 	_path = path;
50 
51 	_pos.length = sizeof(WINDOWPLACEMENT);
52 	_pos.flags = 0;
53 	_pos.showCmd = SW_SHOWNORMAL;
54 	_pos.rcNormalPosition.left = CW_USEDEFAULT;
55 	_pos.rcNormalPosition.top = CW_USEDEFAULT;
56 	_pos.rcNormalPosition.right = CW_USEDEFAULT;
57 	_pos.rcNormalPosition.bottom = CW_USEDEFAULT;
58 
59 	_open_mode = OWM_EXPLORE|OWM_DETAILS;
60 }
61 
62 
63 ShellChildWndInfo::ShellChildWndInfo(HWND hmdiclient, LPCTSTR path, const ShellPath& root_shell_path)
64  :	FileChildWndInfo(hmdiclient, path, ET_SHELL),
65 	_shell_path(path&&*path? path: root_shell_path),
66 	_root_shell_path(root_shell_path)
67 {
68 }
69 
70 
71 NtObjChildWndInfo::NtObjChildWndInfo(HWND hmdiclient, LPCTSTR path)
72  :	FileChildWndInfo(hmdiclient, path, ET_NTOBJS)
73 {
74 }
75 
76 
77 RegistryChildWndInfo::RegistryChildWndInfo(HWND hmdiclient, LPCTSTR path)
78  :	FileChildWndInfo(hmdiclient, path, ET_REGISTRY)
79 {
80 }
81 
82 
83 FATChildWndInfo::FATChildWndInfo(HWND hmdiclient, LPCTSTR path)
84  :	FileChildWndInfo(hmdiclient, path, ET_FAT)
85 {
86 }
87 
88 
89 WebChildWndInfo::WebChildWndInfo(HWND hmdiclient, LPCTSTR url)
90  :	FileChildWndInfo(hmdiclient, url, ET_WEB)
91 {
92 }
93 
94 
95 INT_PTR CALLBACK ExecuteDialog::WndProc(HWND hwnd, UINT nmsg, WPARAM wparam, LPARAM lparam)
96 {
97 	static struct ExecuteDialog* dlg;
98 
99 	switch(nmsg) {
100 	  case WM_INITDIALOG:
101 		dlg = (struct ExecuteDialog*) lparam;
102 		return 1;
103 
104 	  case WM_COMMAND: {
105 		int id = (int)wparam;
106 
107 		if (id == IDOK) {
108 			GetWindowText(GetDlgItem(hwnd, 201), dlg->cmd, COUNTOF(dlg->cmd));
109 			dlg->cmdshow = Button_GetState(GetDlgItem(hwnd,214))&BST_CHECKED?
110 											SW_SHOWMINIMIZED: SW_SHOWNORMAL;
111 			EndDialog(hwnd, id);
112 		} else if (id == IDCANCEL)
113 			EndDialog(hwnd, id);
114 
115 		return 1;}
116 	}
117 
118 	return 0;
119 }
120 
121 
122  // FileChildWindow
123 
124 FileChildWindow::FileChildWindow(HWND hwnd, const FileChildWndInfo& info)
125  :	super(hwnd, info)
126 {
127 	CONTEXT("FileChildWindow::FileChildWindow()");
128 
129 	TCHAR drv[_MAX_DRIVE+1];
130 	Entry* entry = NULL;
131 
132 	_left = NULL;
133 	_right = NULL;
134 
135 	switch(info._etype) {
136 #ifdef __WINE__
137 	  case ET_UNIX:
138 		_root._drive_type = GetDriveType(info._path);
139 		_root._sort_order = SORT_NAME;
140 
141 		_tsplitpath(info._path, drv, NULL, NULL, NULL);
142 		lstrcat(drv, TEXT("/"));
143 		lstrcpy(_root._volname, TEXT("root fs"));
144 		_root._fs_flags = 0;
145 		lstrcpy(_root._fs, TEXT("unixfs"));
146 		lstrcpy(_root._path, TEXT("/"));
147 		_root._entry = new UnixDirectory(_root._path);
148 		entry = _root.read_tree(info._path+_tcslen(_root._path));
149 		break;
150 #endif
151 
152 	  case ET_NTOBJS:
153 		_root._drive_type = DRIVE_UNKNOWN;
154 		_root._sort_order = SORT_NAME;
155 
156 		_tsplitpath_s(info._path, drv, COUNTOF(drv), NULL, 0, NULL, 0, NULL, 0);
157 		lstrcat(drv, TEXT("\\"));
158 		lstrcpy(_root._volname, TEXT("NT Object Namespace"));
159 		lstrcpy(_root._fs, TEXT("NTOBJ"));
160 		lstrcpy(_root._path, drv);
161 		_root._entry = new NtObjDirectory(_root._path);
162 		entry = _root.read_tree(info._path+_tcslen(_root._path));
163 		break;
164 
165 	  case ET_REGISTRY:
166 		_root._drive_type = DRIVE_UNKNOWN;
167 		_root._sort_order = SORT_NONE;
168 
169 		_tsplitpath_s(info._path, drv, COUNTOF(drv), NULL, 0, NULL, 0, NULL, 0);
170 		lstrcat(drv, TEXT("\\"));
171 		lstrcpy(_root._volname, TEXT("Registry"));
172 		lstrcpy(_root._fs, TEXT("Registry"));
173 		lstrcpy(_root._path, drv);
174 		_root._entry = new RegistryRoot();
175 		entry = _root.read_tree(info._path+_tcslen(_root._path));
176 		break;
177 #ifdef _DEBUG
178 	  case ET_FAT: {
179 		_root._drive_type = DRIVE_UNKNOWN;
180 		_root._sort_order = SORT_NONE;
181 
182 		_tsplitpath_s(info._path, drv, COUNTOF(drv), NULL, 0, NULL, 0, NULL, 0);
183 		lstrcat(drv, TEXT("\\"));
184 		lstrcpy(_root._volname, TEXT("FAT XXX"));	//@@
185 		lstrcpy(_root._fs, TEXT("FAT"));
186 		lstrcpy(_root._path, drv);
187 		FATDrive* drive = new FATDrive(TEXT("c:/reactos-emu/c.img"));	//TEXT("\\\\.\\F:"));	//@@
188 
189 		if (drive->_hDrive != INVALID_HANDLE_VALUE) {
190 			_root._entry = drive;
191 			entry = _root.read_tree(info._path+_tcslen(_root._path));
192 		}
193 		break;}
194 #endif
195 #ifndef _NO_WIN_FS
196 	  default:	// ET_WINDOWS
197 		_root._drive_type = GetDriveType(info._path);
198 		_root._sort_order = SORT_NAME;
199 
200 		_tsplitpath_s(info._path, drv, COUNTOF(drv), NULL, 0, NULL, 0, NULL, 0);
201 		lstrcat(drv, TEXT("\\"));
202 		GetVolumeInformation(drv, _root._volname, _MAX_FNAME, 0, 0, &_root._fs_flags, _root._fs, COUNTOF(_root._fs));
203 		lstrcpy(_root._path, drv);
204 		_root._entry = new WinDirectory(_root._path);
205 		entry = _root.read_tree(info._path+_tcslen(_root._path));
206 		break;
207 #else
208 	default:
209 #endif
210 
211 	  case ET_SHELL: {	//@@ separate FileChildWindow into ShellChildWindow, WinChildWindow, UnixChildWindow ?
212 		_root._drive_type = DRIVE_UNKNOWN;
213 		_root._sort_order = SORT_NAME;
214 
215 		lstrcpy(drv, TEXT("\\"));
216 		lstrcpy(_root._volname, TEXT("Desktop"));
217 		_root._fs_flags = 0;
218 		lstrcpy(_root._fs, TEXT("Shell"));
219 
220 		_root._entry = new ShellDirectory(GetDesktopFolder(), DesktopFolderPath(), hwnd);
221 		const ShellChildWndInfo& shell_info = static_cast<const ShellChildWndInfo&>(info);
222 		entry = _root.read_tree(&*shell_info._shell_path);
223 		break;}
224 	}
225 
226 	if (_root._entry) {
227 		if (info._etype != ET_SHELL)
228 			wsprintf(_root._entry->_data.cFileName, TEXT("%s - %s"), drv, _root._fs);
229 	/*@@else
230 			lstrcpy(_root._entry->_data.cFileName, TEXT("GetDesktopFolder"));*/
231 
232 		_root._entry->_data.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
233 
234 
235 		///@todo use OWM_ROOTED flag
236 
237 		if (info._open_mode & OWM_EXPLORE)	///@todo Is not-explore-mode for FileChildWindow completely implemented?
238 			_left_hwnd = *(_left=new Pane(_hwnd, IDW_TREE_LEFT, IDW_HEADER_LEFT, _root._entry, true, COL_CONTENT));
239 
240 		_right_hwnd = *(_right=new Pane(_hwnd, IDW_TREE_RIGHT, IDW_HEADER_RIGHT, NULL, false,
241 										COL_TYPE|COL_SIZE|COL_DATE|COL_TIME|COL_ATTRIBUTES|COL_INDEX|COL_LINKS|COL_CONTENT));
242 	}
243 
244 	_header_wdths_ok = false;
245 
246 	if (!_left_hwnd && !_right_hwnd)
247 		return;
248 
249 	if (entry)
250 		set_curdir(entry);
251 	else if (_root._entry)
252 		set_curdir(_root._entry);
253 
254 	if (_left_hwnd) {
255 		int idx = ListBox_FindItemData(_left_hwnd, ListBox_GetCurSel(_left_hwnd), _left->_cur);
256 		ListBox_SetCurSel(_left_hwnd, idx);
257 		//SetFocus(_left_hwnd);
258 	}
259 
260 	 // store path into history
261 	if (info._path && *info._path)
262 		_url_history.push(info._path);
263 }
264 
265 
266 void FileChildWindow::set_curdir(Entry* entry)
267 {
268 	CONTEXT("FileChildWindow::set_curdir()");
269 
270 	_path[0] = TEXT('\0');
271 
272 	_left->_cur = entry;
273 	_right->_root = entry&&entry->_down? entry->_down: entry;
274 	_right->_cur = entry;
275 
276 	if (entry) {
277 		WaitCursor wait;
278 
279 		if (!entry->_scanned)
280 			scan_entry(entry);
281 		else {
282 			HiddenWindow hide(_right_hwnd);
283 
284 			ListBox_ResetContent(_right_hwnd);
285 			_right->insert_entries(entry->_down);
286 
287 			_right->calc_widths(false);	///@todo make configurable (This call takes really _very_ long compared to all other processing!)
288 
289 			_right->set_header();
290 		}
291 
292 		entry->get_path(_path, COUNTOF(_path));
293 	}
294 
295 	if (_hwnd)	// only change window title if the window already exists
296 		SetWindowText(_hwnd, _path);
297 
298 	if (_path[0])
299 	{
300 		if (SetCurrentDirectory(_path))
301 			set_url(_path);	//set_url(FmtString(TEXT("file://%s"), _path));
302 		else
303 			_path[0] = TEXT('\0');
304 	}
305 }
306 
307 
308  // expand a directory entry
309 
310 bool FileChildWindow::expand_entry(Entry* dir)
311 {
312 	int idx;
313 	Entry* p;
314 
315 	if (!dir || dir->_expanded || !dir->_down)
316 		return false;
317 
318 	p = dir->_down;
319 
320 	if (p->_data.cFileName[0]=='.' && p->_data.cFileName[1]=='\0' && p->_next) {
321 		p = p->_next;
322 
323 		if (p->_data.cFileName[0]=='.' && p->_data.cFileName[1]=='.' &&
324 				p->_data.cFileName[2]=='\0' && p->_next)
325 			p = p->_next;
326 	}
327 
328 	 // no subdirectories ?
329 	if (!(p->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&	// not a directory?
330 		!p->_down)	// not a file with NTFS sub-streams?
331 		return FALSE;
332 
333 	idx = ListBox_FindItemData(_left_hwnd, 0, dir);
334 
335 	dir->_expanded = true;
336 
337 	 // insert entries in left pane
338 	HiddenWindow hide(_left_hwnd);
339 
340 	_left->insert_entries(p, idx);
341 
342 	if (!_header_wdths_ok) {
343 		if (_left->calc_widths(false)) {
344 			_left->set_header();
345 
346 			_header_wdths_ok = true;
347 		}
348 	}
349 
350 	return true;
351 }
352 
353 
354 void FileChildWindow::collapse_entry(Pane* pane, Entry* dir)
355 {
356 	int idx = ListBox_FindItemData(*pane, 0, dir);
357 
358 	SendMessage(*pane, WM_SETREDRAW, FALSE, 0);	//ShowWindow(*pane, SW_HIDE);
359 
360 	 // hide sub entries
361 	for(;;) {
362 		LRESULT res = ListBox_GetItemData(*pane, idx+1);
363 		Entry* sub = (Entry*) res;
364 
365 		if (res==LB_ERR || !sub || sub->_level<=dir->_level)
366 			break;
367 
368 		ListBox_DeleteString(*pane, idx+1);
369 	}
370 
371 	dir->_expanded = false;
372 
373 	SendMessage(*pane, WM_SETREDRAW, TRUE, 0);	//ShowWindow(*pane, SW_SHOW);
374 }
375 
376 
377 FileChildWindow* FileChildWindow::create(const FileChildWndInfo& info)
378 {
379 	CONTEXT("FileChildWindow::create()");
380 
381 	MDICREATESTRUCT mcs;
382 
383 	mcs.szClass = CLASSNAME_WINEFILETREE;
384 	mcs.szTitle = (LPTSTR)info._path;
385 	mcs.hOwner	= g_Globals._hInstance;
386 	mcs.x		= info._pos.rcNormalPosition.left;
387 	mcs.y		= info._pos.rcNormalPosition.top;
388 	mcs.cx		= info._pos.rcNormalPosition.right - info._pos.rcNormalPosition.left;
389 	mcs.cy		= info._pos.rcNormalPosition.bottom - info._pos.rcNormalPosition.top;
390 	mcs.style	= 0;
391 	mcs.lParam	= 0;
392 
393 	FileChildWindow* child = static_cast<FileChildWindow*>(
394 		create_mdi_child(info, mcs, WINDOW_CREATOR_INFO(FileChildWindow,FileChildWndInfo)));
395 
396 	if (!child->_left_hwnd && !child->_right_hwnd) {
397 		SendMessage(info._hmdiclient, WM_MDIDESTROY, (WPARAM)child->_hwnd, 0);
398 		MessageBox(info._hmdiclient, TEXT("Error opening child window"), TEXT("ROS Explorer"), MB_OK);
399 	}
400 
401 	return child;
402 }
403 
404 
405 void FileChildWindow::resize_children(int cx, int cy)
406 {
407 	HDWP hdwp = BeginDeferWindowPos(4);
408 	RECT rt;
409 
410 	rt.left   = 0;
411 	rt.top    = 0;
412 	rt.right  = cx;
413 	rt.bottom = cy;
414 
415 	cx = _split_pos + SPLIT_WIDTH/2;
416 
417 	if (_left && _right) {
418 		WINDOWPOS wp;
419 		HD_LAYOUT hdl;
420 
421 		hdl.prc   = &rt;
422 		hdl.pwpos = &wp;
423 
424 		Header_Layout(_left->_hwndHeader, &hdl);
425 
426 		hdwp = DeferWindowPos(hdwp, _left->_hwndHeader, wp.hwndInsertAfter,
427 							wp.x-1, wp.y, _split_pos-SPLIT_WIDTH/2+1, wp.cy, wp.flags);
428 
429 		hdwp = DeferWindowPos(hdwp, _right->_hwndHeader, wp.hwndInsertAfter,
430 								rt.left+cx+1, wp.y, wp.cx-cx+2, wp.cy, wp.flags);
431 	}
432 
433 	if (_left_hwnd)
434 		hdwp = DeferWindowPos(hdwp, _left_hwnd, 0, rt.left, rt.top, _split_pos-SPLIT_WIDTH/2-rt.left, rt.bottom-rt.top, SWP_NOZORDER|SWP_NOACTIVATE);
435 
436 	if (_right_hwnd)
437 		hdwp = DeferWindowPos(hdwp, _right_hwnd, 0, rt.left+cx+1, rt.top, rt.right-cx, rt.bottom-rt.top, SWP_NOZORDER|SWP_NOACTIVATE);
438 
439 	EndDeferWindowPos(hdwp);
440 }
441 
442 
443 LRESULT FileChildWindow::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
444 {
445 	switch(nmsg) {
446 		case WM_DRAWITEM: {
447 			LPDRAWITEMSTRUCT dis = (LPDRAWITEMSTRUCT)lparam;
448 			Entry* entry = (Entry*) dis->itemData;
449 
450 			if (dis->CtlID == IDW_TREE_LEFT) {
451 				_left->draw_item(dis, entry);
452 				return TRUE;
453 			} else if (dis->CtlID == IDW_TREE_RIGHT) {
454 				_right->draw_item(dis, entry);
455 				return TRUE;
456 			}
457 
458 			goto def;}
459 
460 		case WM_SIZE:
461 			if (wparam != SIZE_MINIMIZED)
462 				resize_children(LOWORD(lparam), HIWORD(lparam));
463 			return DefMDIChildProc(_hwnd, nmsg, wparam, lparam);
464 
465 		case PM_GET_FILEWND_PTR:
466 			return (LRESULT)this;
467 
468 		case WM_SETFOCUS: {
469 			TCHAR path[MAX_PATH];
470 
471 			if (_left && _left->_cur) {
472 				_left->_cur->get_path(path, COUNTOF(path));
473 				SetCurrentDirectory(path);
474 			}
475 
476 			SetFocus(_focus_pane? _right_hwnd: _left_hwnd);
477 			goto def;}
478 
479 		case PM_DISPATCH_COMMAND: {
480 			Pane* pane = GetFocus()==_left_hwnd? _left: _right;
481 
482 			switch(LOWORD(wparam)) {
483 			  case ID_WINDOW_NEW: {CONTEXT("FileChildWindow PM_DISPATCH_COMMAND ID_WINDOW_NEW");
484 				if (_root._entry->_etype == ET_SHELL)
485 					FileChildWindow::create(ShellChildWndInfo(GetParent(_hwnd)/*_hmdiclient*/, _path, DesktopFolderPath()));
486 				else
487 					FileChildWindow::create(FileChildWndInfo(GetParent(_hwnd)/*_hmdiclient*/, _path));
488 				break;}
489 
490 			  case ID_REFRESH: {CONTEXT("ID_REFRESH");
491 				refresh();
492 				break;}
493 
494 			  case ID_ACTIVATE: {CONTEXT("ID_ACTIVATE");
495 				activate_entry(pane);
496 				break;}
497 
498 			  default:
499 				if (pane->command(LOWORD(wparam)))
500 					return TRUE;
501 				else
502 					return super::WndProc(nmsg, wparam, lparam);
503 			}
504 
505 			return TRUE;}
506 
507 		case WM_CONTEXTMENU: {
508 			 // first select the current item in the listbox
509 			HWND hpanel = (HWND) wparam;
510 			POINT pt;
511 			pt.x = LOWORD(lparam);
512 			pt.y = HIWORD(lparam);
513 			POINT pt_screen = pt;
514 			ScreenToClient(hpanel, &pt);
515 			SendMessage(hpanel, WM_LBUTTONDOWN, 0, MAKELONG(pt.x, pt.y));
516 			SendMessage(hpanel, WM_LBUTTONUP, 0, MAKELONG(pt.x, pt.y));
517 
518 			 // now create the popup menu using shell namespace and IContextMenu
519 			Pane* pane = GetFocus()==_left_hwnd? _left: _right;
520 			int idx = ListBox_GetCurSel(*pane);
521 			if (idx != -1) {
522 				Entry* entry = (Entry*) ListBox_GetItemData(*pane, idx);
523 
524 				HRESULT hr = entry->do_context_menu(_hwnd, pt_screen, _cm_ifs);
525 
526 				if (SUCCEEDED(hr))
527 					refresh();
528 				else
529 					CHECKERROR(hr);
530 			}
531 			break;}
532 
533 		default: def:
534 			return super::WndProc(nmsg, wparam, lparam);
535 	}
536 
537 	return 0;
538 }
539 
540 
541 void FileChildWindow::refresh()
542 {
543 	WaitCursor wait;
544 	bool expanded = _left->_cur->_expanded;
545 
546 	scan_entry(_left->_cur);
547 
548 	if (expanded)
549 		expand_entry(_left->_cur);
550 }
551 
552 
553 int FileChildWindow::Command(int id, int code)
554 {
555 	Pane* pane = GetFocus()==_left_hwnd? _left: _right;
556 
557 	switch(code) {
558 	  case LBN_SELCHANGE: {
559 		int idx = ListBox_GetCurSel(*pane);
560 		Entry* entry = (Entry*) ListBox_GetItemData(*pane, idx);
561 
562 		if (pane == _left)
563 			set_curdir(entry);
564 		else
565 			pane->_cur = entry;
566 		break;}
567 
568 	  case LBN_DBLCLK:
569 		activate_entry(pane);
570 		break;
571 	}
572 
573 	return 0;
574 }
575 
576 
577 void FileChildWindow::activate_entry(Pane* pane)	///@todo enable using RETURN key accelerator
578 {
579 	Entry* entry = pane->_cur;
580 
581 	if (!entry)
582 		return;
583 
584 	WaitCursor wait;
585 
586 	if ((entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ||	// a directory?
587 		entry->_down)	// a file with NTFS sub-streams?
588 	{
589 		int scanned_old = entry->_scanned;
590 
591 		if (!scanned_old)
592 			scan_entry(entry);
593 
594 		if (entry->_data.cFileName[0]==TEXT('.') && entry->_data.cFileName[1]==TEXT('\0'))
595 			return;
596 
597 		if (entry->_data.cFileName[0]==TEXT('.') && entry->_data.cFileName[1]==TEXT('.') && entry->_data.cFileName[2]==TEXT('\0')) {
598 			entry = _left->_cur->_up;
599 			collapse_entry(_left, entry);
600 			goto focus_entry;
601 		} else if (entry->_expanded)
602 			collapse_entry(pane, _left->_cur);
603 		else {
604 			expand_entry(_left->_cur);
605 
606 			if (!pane->_treePane) focus_entry: {
607 				int idx = ListBox_FindItemData(_left_hwnd, ListBox_GetCurSel(_left_hwnd), entry);
608 				ListBox_SetCurSel(_left_hwnd, idx);
609 
610 				set_curdir(entry);
611 			}
612 		}
613 
614 		if (!scanned_old) {
615 			pane->calc_widths(false);
616 
617 			pane->set_header();
618 		}
619 	} else {
620 		entry->launch_entry(_hwnd);
621 	}
622 }
623 
624 
625 void FileChildWindow::scan_entry(Entry* entry)
626 {
627 	CONTEXT("FileChildWindow::scan_entry()");
628 
629 	int idx = ListBox_GetCurSel(_left_hwnd);
630 
631 	 // delete sub entries in left pane
632 	for(;;) {
633 		LRESULT res = ListBox_GetItemData(_left_hwnd, idx+1);
634 		Entry* sub = (Entry*) res;
635 
636 		if (res==LB_ERR || !sub || sub->_level<=entry->_level)
637 			break;
638 
639 		ListBox_DeleteString(_left_hwnd, idx+1);
640 	}
641 
642 	 // empty right pane
643 	ListBox_ResetContent(_right_hwnd);
644 
645 	 // release memory
646 	entry->free_subentries();
647 	entry->_expanded = false;
648 
649 	 // read contents from disk
650 	entry->read_directory_base(_root._sort_order);	///@todo use modifyable sort order instead of fixed file system default
651 
652 	 // insert found entries in right pane
653 	HiddenWindow hide(_right_hwnd);
654 	_right->insert_entries(entry->_down);
655 
656 	_right->calc_widths(false);
657 	_right->set_header();
658 
659 	_header_wdths_ok = false;
660 }
661 
662 
663 int FileChildWindow::Notify(int id, NMHDR* pnmh)
664 {
665 	return (pnmh->idFrom==IDW_HEADER_LEFT? _left: _right)->Notify(id, pnmh);
666 }
667 
668 
669 String FileChildWindow::jump_to_int(LPCTSTR url)
670 {
671 	String dir, fname;
672 
673 	if (SplitFileSysURL(url, dir, fname)) {
674 		Entry* entry = NULL;
675 
676 		 // call read_tree() to iterate through the hierarchy and open all folders to reach dir
677 		if (_root._entry)
678 			switch(_root._entry->_etype) {
679 			  case ET_SHELL: {	//@@ separate into FileChildWindow in ShellChildWindow, WinChildWindow, UnixChildWindow ?
680 				ShellPath shell_path(dir);
681 				entry = _root.read_tree(&*shell_path);
682 				break;}
683 
684 #ifdef __WINE__
685 			  case ET_UNIX: {
686 				LPCTSTR path = dir;
687 
688 				if (!_tcsicmp(path, _root._path, _tcslen(_root._path)))
689 					path += _tcslen(_root._path);
690 
691 				entry = _root.read_tree(path);
692 				break;}
693 #endif
694 
695 			  default: { // ET_NTOBJS, ET_REGISTRY, ET_FAT, ET_WINDOWS
696 				LPCTSTR path = dir;
697 
698 				if (!_tcsnicmp(path, _root._path, _tcslen(_root._path)))
699 					path += _tcslen(_root._path);
700 
701 				entry = _root.read_tree(path);
702 				break;}
703 			}
704 
705 			if (entry) {
706 				 // refresh left pane entries
707 				HiddenWindow hide(_left_hwnd);
708 
709 				ListBox_ResetContent(_left_hwnd);
710 
711 				_left->insert_entries(_root._entry);
712 
713 				if (!_header_wdths_ok) {
714 					if (_left->calc_widths(false)) {
715 						_left->set_header();
716 
717 						_header_wdths_ok = true;
718 					}
719 				}
720 
721 				set_curdir(entry);
722 
723 				if (_left_hwnd) {
724 					int idx = ListBox_FindItemData(_left_hwnd, -1, entry);
725 
726 					if (idx != -1) { // The item should always be found.
727 						ListBox_SetCurSel(_left_hwnd, idx);
728 						SetFocus(_left_hwnd);
729 					}
730 				}
731 
732 				///@todo use fname
733 
734 				return dir;	//FmtString(TEXT("file://%s"), (LPCTSTR)dir);
735 			}
736 	}
737 
738 	return String();
739 }
740