update for HEAD-2003091401
[reactos.git] / subsys / system / explorer / shell / filechild.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  // filechild.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 FileChildWndInfo::FileChildWndInfo(LPCTSTR path)
38 {
39 #ifdef __linux__
40         if (*path == '/')
41                 _etype = ET_UNIX;
42         else
43 #endif
44                 _etype = ET_WINDOWS;
45
46         _path = path;
47
48         _pos.length = sizeof(WINDOWPLACEMENT);
49         _pos.flags = 0;
50         _pos.showCmd = SW_SHOWNORMAL;
51         _pos.rcNormalPosition.left = CW_USEDEFAULT;
52         _pos.rcNormalPosition.top = CW_USEDEFAULT;
53         _pos.rcNormalPosition.right = CW_USEDEFAULT;
54         _pos.rcNormalPosition.bottom = CW_USEDEFAULT;
55
56         _mode_explore = true;
57 }
58
59
60 ShellChildWndInfo::ShellChildWndInfo(LPCTSTR path, const ShellPath& root_shell_path)
61  :      FileChildWndInfo(path),
62         _shell_path(path),
63         _root_shell_path(root_shell_path)
64 {
65         _etype = ET_SHELL;
66         _path = path;
67 }
68
69
70 FileChildWindow::FileChildWindow(HWND hwnd, const FileChildWndInfo& info)
71  :      ChildWindow(hwnd)
72 {
73         TCHAR drv[_MAX_DRIVE+1];
74         Entry* entry;
75
76         if (info._etype == ET_SHELL)    //@@ evtl. Aufteilung von FileChildWindow in ShellChildWindow, WinChildWindow, UnixChildWindow
77         {
78                 _root._drive_type = DRIVE_UNKNOWN;
79                 lstrcpy(drv, TEXT("\\"));
80                 lstrcpy(_root._volname, TEXT("Desktop"));
81                 _root._fs_flags = 0;
82                 lstrcpy(_root._fs, TEXT("Shell"));
83
84                 const ShellChildWndInfo& shell_info = static_cast<const ShellChildWndInfo&>(info);
85                 _root._entry = new ShellDirectory(Desktop(), DesktopFolder(), hwnd);
86                 entry = _root._entry->read_tree((LPCTSTR)&*shell_info._shell_path, SORT_NAME/*_sortOrder*/);
87         }
88         else
89 #ifdef __linux__
90         if (info._etype == ET_UNIX)
91         {
92                 _root._drive_type = GetDriveType(info._path);
93
94                 _tsplitpath(info._path, drv, NULL, NULL, NULL);
95                 lstrcat(drv, TEXT("/"));
96                 lstrcpy(_root._volname, TEXT("root fs"));
97                 _root._fs_flags = 0;
98                 lstrcpy(_root._fs, TEXT("unixfs"));
99                 lstrcpy(_root._path, TEXT("/"));
100                 _root._entry = new UnixDirectory(_root._path);
101                 entry = _root._entry->read_tree(info._path, SORT_NAME/*_sortOrder*/);
102         }
103         else
104 #endif
105         {// if (info._etype == ET_WINDOWS)
106                 _root._drive_type = GetDriveType(info._path);
107
108                 _tsplitpath(info._path, drv, NULL, NULL, NULL);
109                 lstrcat(drv, TEXT("\\"));
110                 GetVolumeInformation(drv, _root._volname, _MAX_FNAME, 0, 0, &_root._fs_flags, _root._fs, _MAX_DIR);
111                 lstrcpy(_root._path, drv);
112                 _root._entry = new WinDirectory(_root._path);
113                 entry = _root._entry->read_tree(info._path, SORT_NAME/*_sortOrder*/);
114         }
115
116         if (info._etype != ET_SHELL)
117                 wsprintf(_root._entry->_data.cFileName, TEXT("%s - %s"), drv, _root._fs);
118 /*@@else
119                 lstrcpy(_root._entry->_data.cFileName, TEXT("Desktop"));*/
120
121         _root._entry->_data.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
122
123
124         if (info._mode_explore) //TODO: Is not-explore-mode for FileChildWindow completely implemented?
125                 _left_hwnd = *(_left=new Pane(_hwnd, IDW_TREE_LEFT, IDW_HEADER_LEFT, _root._entry, true, 0));
126
127         _right_hwnd = *(_right=new Pane(_hwnd, IDW_TREE_RIGHT, IDW_HEADER_RIGHT, NULL, false, COL_SIZE|COL_DATE|COL_TIME|COL_ATTRIBUTES|COL_INDEX|COL_LINKS));
128
129         _sortOrder = SORT_NAME;
130         _header_wdths_ok = false;
131
132         set_curdir(entry, hwnd);
133
134         if (_left_hwnd) {
135                 int idx = ListBox_FindItemData(_left_hwnd, ListBox_GetCurSel(_left_hwnd), _left->_cur);
136                 ListBox_SetCurSel(_left_hwnd, idx);
137         }
138
139          //TODO: scroll to visibility
140
141 }
142
143 FileChildWindow::~FileChildWindow()
144 {
145 }
146
147
148 void FileChildWindow::set_curdir(Entry* entry, HWND hwnd)
149 {
150         _path[0] = TEXT('\0');
151
152         _left->_cur = entry;
153         _right->_root = entry&&entry->_down? entry->_down: entry;
154         _right->_cur = entry;
155
156         if (entry) {
157                 if (!entry->_scanned)
158                         scan_entry(entry, hwnd);
159                 else {
160                         ListBox_ResetContent(_right_hwnd);
161                         _right->insert_entries(entry->_down, -1);
162                         _right->calc_widths(false);
163                         _right->set_header();
164                 }
165
166                 entry->get_path(_path);
167         }
168
169         if (hwnd)       // only change window title, if the window already exists
170                 SetWindowText(hwnd, _path);
171
172         if (_path[0])
173                 if (!SetCurrentDirectory(_path))
174                         _path[0] = TEXT('\0');
175 }
176
177
178  // expand a directory entry
179
180 bool FileChildWindow::expand_entry(Entry* dir)
181 {
182         int idx;
183         Entry* p;
184
185         if (!dir || dir->_expanded || !dir->_down)
186                 return false;
187
188         p = dir->_down;
189
190         if (p->_data.cFileName[0]=='.' && p->_data.cFileName[1]=='\0' && p->_next) {
191                 p = p->_next;
192
193                 if (p->_data.cFileName[0]=='.' && p->_data.cFileName[1]=='.' &&
194                                 p->_data.cFileName[2]=='\0' && p->_next)
195                         p = p->_next;
196         }
197
198          // no subdirectories ?
199         if (!(p->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
200                 return FALSE;
201
202         idx = ListBox_FindItemData(_left_hwnd, 0, dir);
203
204         dir->_expanded = true;
205
206          // insert entries in left pane
207         _left->insert_entries(p, idx);
208
209         if (!_header_wdths_ok) {
210                 if (_left->calc_widths(false)) {
211                         _left->set_header();
212
213                         _header_wdths_ok = true;
214                 }
215         }
216
217         return true;
218 }
219
220
221 void FileChildWindow::collapse_entry(Pane* pane, Entry* dir)
222 {
223         int idx = ListBox_FindItemData(*pane, 0, dir);
224
225         SendMessage(*pane, WM_SETREDRAW, FALSE, 0);     //ShowWindow(*pane, SW_HIDE);
226
227          // hide sub entries
228         for(;;) {
229                 LRESULT res = ListBox_GetItemData(*pane, idx+1);
230                 Entry* sub = (Entry*) res;
231
232                 if (res==LB_ERR || !sub || sub->_level<=dir->_level)
233                         break;
234
235                 ListBox_DeleteString(*pane, idx+1);
236         }
237
238         dir->_expanded = false;
239
240         SendMessage(*pane, WM_SETREDRAW, TRUE, 0);      //ShowWindow(*pane, SW_SHOW);
241 }
242
243
244 FileChildWindow* FileChildWindow::create(HWND hmdiclient, const FileChildWndInfo& info)
245 {
246         MDICREATESTRUCT mcs;
247
248         mcs.szClass = CLASSNAME_WINEFILETREE;
249         mcs.szTitle = (LPTSTR)info._path;
250         mcs.hOwner      = g_Globals._hInstance;
251         mcs.x           = info._pos.rcNormalPosition.left;
252         mcs.y           = info._pos.rcNormalPosition.top;
253         mcs.cx          = info._pos.rcNormalPosition.right - info._pos.rcNormalPosition.left;
254         mcs.cy          = info._pos.rcNormalPosition.bottom - info._pos.rcNormalPosition.top;
255         mcs.style       = 0;
256         mcs.lParam      = 0;
257
258         FileChildWindow* child = static_cast<FileChildWindow*>(
259                 create_mdi_child(hmdiclient, mcs, WINDOW_CREATOR_INFO(FileChildWindow,FileChildWndInfo), &info));
260
261         return child;
262 }
263
264
265 void FileChildWindow::resize_children(int cx, int cy)
266 {
267         HDWP hdwp = BeginDeferWindowPos(4);
268         RECT rt;
269
270         rt.left   = 0;
271         rt.top    = 0;
272         rt.right  = cx;
273         rt.bottom = cy;
274
275         cx = _split_pos + SPLIT_WIDTH/2;
276
277         {
278                 WINDOWPOS wp;
279                 HD_LAYOUT hdl;
280
281                 hdl.prc   = &rt;
282                 hdl.pwpos = &wp;
283
284                 Header_Layout(_left->_hwndHeader, &hdl);
285
286                 hdwp = DeferWindowPos(hdwp, _left->_hwndHeader, wp.hwndInsertAfter,
287                                                         wp.x-1, wp.y, _split_pos-SPLIT_WIDTH/2+1, wp.cy, wp.flags);
288
289                 hdwp = DeferWindowPos(hdwp, _right->_hwndHeader, wp.hwndInsertAfter,
290                                                                 rt.left+cx+1, wp.y, wp.cx-cx+2, wp.cy, wp.flags);
291         }
292
293         if (_left_hwnd)
294                 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);
295
296         hdwp = DeferWindowPos(hdwp, _right_hwnd, 0, rt.left+cx+1, rt.top, rt.right-cx, rt.bottom-rt.top, SWP_NOZORDER|SWP_NOACTIVATE);
297
298         EndDeferWindowPos(hdwp);
299 }
300
301
302 LRESULT FileChildWindow::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
303 {
304         switch(nmsg) {
305                 case WM_DRAWITEM: {
306                         LPDRAWITEMSTRUCT dis = (LPDRAWITEMSTRUCT)lparam;
307                         Entry* entry = (Entry*) dis->itemData;
308
309                         if (dis->CtlID == IDW_TREE_LEFT)
310                                 _left->draw_item(dis, entry);
311                         else
312                                 _right->draw_item(dis, entry);
313
314                         return TRUE;}
315
316                 case WM_SIZE:
317                         if (wparam != SIZE_MINIMIZED)
318                                 resize_children(LOWORD(lparam), HIWORD(lparam));
319                         return DefMDIChildProc(_hwnd, nmsg, wparam, lparam);
320
321                 case PM_GET_FILEWND_PTR:
322                         return (LRESULT)this;
323
324                 case WM_SETFOCUS: {
325                         TCHAR path[MAX_PATH];
326
327                         if (_left->_cur) {
328                                 _left->_cur->get_path(path);
329                                 SetCurrentDirectory(path);
330                         }
331
332                         SetFocus(_focus_pane? _right_hwnd: _left_hwnd);
333                         goto def;}
334
335                 case PM_DISPATCH_COMMAND: {
336                         Pane* pane = GetFocus()==_left_hwnd? _left: _right;
337
338                         switch(LOWORD(wparam)) {
339                           case ID_WINDOW_NEW:
340                                 if (_root._entry->_etype == ET_SHELL)
341                                         FileChildWindow::create(GetParent(_hwnd)/*_hmdiclient*/, ShellChildWndInfo(_path,DesktopFolder()));
342                                 else
343                                         FileChildWindow::create(GetParent(_hwnd)/*_hmdiclient*/, FileChildWndInfo(_path));
344                                 break;
345
346                           case ID_REFRESH: {
347                                 bool expanded = _left->_cur->_expanded;
348
349                                 scan_entry(_left->_cur, _hwnd);
350
351                                 if (expanded)
352                                         expand_entry(_left->_cur);
353                                 break;}
354
355                           case ID_ACTIVATE:
356                                 activate_entry(pane, _hwnd);
357                                 break;
358
359                           default:
360                                 return pane->command(LOWORD(wparam));
361                         }
362
363                         return TRUE;}
364
365                 default: def:
366                         return super::WndProc(nmsg, wparam, lparam);
367         }
368
369         return 0;
370 }
371
372
373 int FileChildWindow::Command(int id, int code)
374 {
375         Pane* pane = GetFocus()==_left_hwnd? _left: _right;
376
377         switch(code) {
378           case LBN_SELCHANGE: {
379                 int idx = ListBox_GetCurSel(*pane);
380                 Entry* entry = (Entry*) ListBox_GetItemData(*pane, idx);
381
382                 if (pane == _left)
383                         set_curdir(entry, _hwnd);
384                 else
385                         pane->_cur = entry;
386                 break;}
387
388           case LBN_DBLCLK:
389                 activate_entry(pane, _hwnd);
390                 break;
391         }
392
393         return 0;
394 }
395
396
397 void FileChildWindow::activate_entry(Pane* pane, HWND hwnd)
398 {
399         Entry* entry = pane->_cur;
400
401         if (!entry)
402                 return;
403
404         if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
405                 int scanned_old = entry->_scanned;
406
407                 if (!scanned_old)
408                         scan_entry(entry, hwnd);
409
410                 if (entry->_data.cFileName[0]=='.' && entry->_data.cFileName[1]=='\0')
411                         return;
412
413                 if (entry->_data.cFileName[0]=='.' && entry->_data.cFileName[1]=='.' && entry->_data.cFileName[2]=='\0') {
414                         entry = _left->_cur->_up;
415                         collapse_entry(_left, entry);
416                         goto focus_entry;
417                 } else if (entry->_expanded)
418                         collapse_entry(pane, _left->_cur);
419                 else {
420                         expand_entry(_left->_cur);
421
422                         if (!pane->_treePane) focus_entry: {
423                                 int idx = ListBox_FindItemData(_left_hwnd, ListBox_GetCurSel(_left_hwnd), entry);
424                                 ListBox_SetCurSel(_left_hwnd, idx);
425                                 set_curdir(entry, _hwnd);
426                         }
427                 }
428
429                 if (!scanned_old) {
430                         pane->calc_widths(FALSE);
431
432                         pane->set_header();
433                 }
434         } else {
435                 entry->launch_entry(_hwnd);
436         }
437 }
438
439
440 void FileChildWindow::scan_entry(Entry* entry, HWND hwnd)
441 {
442         int idx = ListBox_GetCurSel(_left_hwnd);
443         HCURSOR old_cursor = SetCursor(LoadCursor(0, IDC_WAIT));
444
445          // delete sub entries in left pane
446         for(;;) {
447                 LRESULT res = ListBox_GetItemData(_left_hwnd, idx+1);
448                 Entry* sub = (Entry*) res;
449
450                 if (res==LB_ERR || !sub || sub->_level<=entry->_level)
451                         break;
452
453                 ListBox_DeleteString(_left_hwnd, idx+1);
454         }
455
456          // empty right pane
457         ListBox_ResetContent(_right_hwnd);
458
459          // release memory
460         entry->free_subentries();
461         entry->_expanded = false;
462
463          // read contents from disk
464         entry->read_directory(_sortOrder);
465
466          // insert found entries in right pane
467         _right->insert_entries(entry->_down, -1);
468
469         _right->calc_widths(false);
470         _right->set_header();
471
472         _header_wdths_ok = FALSE;
473
474         SetCursor(old_cursor);
475 }
476
477
478 int FileChildWindow::Notify(int id, NMHDR* pnmh)
479 {
480         return (pnmh->idFrom==IDW_HEADER_LEFT? _left: _right)->Notify(id, pnmh);
481 }
482
483
484 BOOL CALLBACK ExecuteDialog::WndProc(HWND hwnd, UINT nmsg, WPARAM wparam, LPARAM lparam)
485 {
486         static struct ExecuteDialog* dlg;
487
488         switch(nmsg) {
489           case WM_INITDIALOG:
490                 dlg = (struct ExecuteDialog*) lparam;
491                 return 1;
492
493           case WM_COMMAND: {
494                 int id = (int)wparam;
495
496                 if (id == IDOK) {
497                         GetWindowText(GetDlgItem(hwnd, 201), dlg->cmd, MAX_PATH);
498                         dlg->cmdshow = Button_GetState(GetDlgItem(hwnd,214))&BST_CHECKED?
499                                                                                         SW_SHOWMINIMIZED: SW_SHOWNORMAL;
500                         EndDialog(hwnd, id);
501                 } else if (id == IDCANCEL)
502                         EndDialog(hwnd, id);
503
504                 return 1;}
505         }
506
507         return 0;
508 }