Committing latest progress, still much TODO:
[reactos.git] / rosapps / winfile / childwnd.c
1 /*
2 * ReactOS winfile
3 *
4 * childwnd.c
5 *
6 * Copyright (C) 2002 Robert Dickenson <robd@reactos.org>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23 #ifdef _MSC_VER
24 #include "stdafx.h"
25 #else
26 #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
27 #include <windows.h>
28 #include <commctrl.h>
29 #include <stdlib.h>
30 #include <malloc.h>
31 #include <memory.h>
32 #include <tchar.h>
33 #include <process.h>
34 #include <stdio.h>
35 #endif
36
37 #include <windowsx.h>
38 #include <ctype.h>
39 #include <assert.h>
40 #define ASSERT assert
41
42 #include "main.h"
43 #include "childwnd.h"
44 #include "framewnd.h"
45 #include "utils.h"
46 #include "treeview.h"
47 #include "listview.h"
48 #include "debug.h"
49 #include "draw.h"
50
51
52 #ifdef _NO_EXTENSIONS
53 //#define COLOR_SPLITBAR WHITE_BRUSH
54 #define COLOR_SPLITBAR LTGRAY_BRUSH
55 #else
56 #define COLOR_SPLITBAR LTGRAY_BRUSH
57 #endif
58
59 ////////////////////////////////////////////////////////////////////////////////
60 // Global Variables:
61 //
62
63 HWND hSplitWnd; // Splitter Bar Control Window
64
65
66 ////////////////////////////////////////////////////////////////////////////////
67 // Local module support methods
68 //
69
70 #ifndef _NO_EXTENSIONS
71
72 void set_header(Pane* pane)
73 {
74 HD_ITEM item;
75 int scroll_pos = GetScrollPos(pane->hWnd, SB_HORZ);
76 int i=0, x=0;
77
78 item.mask = HDI_WIDTH;
79 item.cxy = 0;
80
81 for(; x+pane->widths[i]<scroll_pos && i<COLUMNS; i++) {
82 x += pane->widths[i];
83 Header_SetItem(pane->hwndHeader, i, &item);
84 }
85
86 if (i < COLUMNS) {
87 x += pane->widths[i];
88 item.cxy = x - scroll_pos;
89 Header_SetItem(pane->hwndHeader, i++, &item);
90
91 for(; i<COLUMNS; i++) {
92 item.cxy = pane->widths[i];
93 x += pane->widths[i];
94 Header_SetItem(pane->hwndHeader, i, &item);
95 }
96 }
97 }
98
99 static LRESULT pane_notify(Pane* pane, NMHDR* pnmh)
100 {
101 switch(pnmh->code) {
102 case HDN_TRACK:
103 case HDN_ENDTRACK:
104 {
105 HD_NOTIFY* phdn = (HD_NOTIFY*) pnmh;
106 int idx = phdn->iItem;
107 int dx = phdn->pitem->cxy - pane->widths[idx];
108 int i;
109
110 RECT clnt;
111 GetClientRect(pane->hWnd, &clnt);
112 // move immediate to simulate HDS_FULLDRAG (for now [04/2000] not realy needed with WINELIB)
113 Header_SetItem(pane->hwndHeader, idx, phdn->pitem);
114 pane->widths[idx] += dx;
115 for (i = idx; ++i <= COLUMNS; )
116 pane->positions[i] += dx;
117 {
118 int scroll_pos = GetScrollPos(pane->hWnd, SB_HORZ);
119 RECT rt_scr = {pane->positions[idx+1]-scroll_pos, 0, clnt.right, clnt.bottom};
120 RECT rt_clip = {pane->positions[idx]-scroll_pos, 0, clnt.right, clnt.bottom};
121 if (rt_scr.left < 0) rt_scr.left = 0;
122 if (rt_clip.left < 0) rt_clip.left = 0;
123 ScrollWindowEx(pane->hWnd, dx, 0, &rt_scr, &rt_clip, 0, 0, SW_INVALIDATE);
124 rt_clip.right = pane->positions[idx+1];
125 RedrawWindow(pane->hWnd, &rt_clip, 0, RDW_INVALIDATE|RDW_UPDATENOW);
126 if (pnmh->code == HDN_ENDTRACK) {
127 ListBox_SetHorizontalExtent(pane->hWnd, pane->positions[COLUMNS]);
128 if (GetScrollPos(pane->hWnd, SB_HORZ) != scroll_pos)
129 set_header(pane);
130 }
131 }
132 }
133 return FALSE;
134 case HDN_DIVIDERDBLCLICK:
135 {
136 HD_NOTIFY* phdn = (HD_NOTIFY*) pnmh;
137 HD_ITEM item;
138 calc_single_width(pane, phdn->iItem);
139 item.mask = HDI_WIDTH;
140 item.cxy = pane->widths[phdn->iItem];
141 Header_SetItem(pane->hwndHeader, phdn->iItem, &item);
142 InvalidateRect(pane->hWnd, 0, TRUE);
143 break;
144 }
145 }
146 return 0;
147 }
148
149 #endif
150
151
152 static BOOL pane_command(Pane* pane, UINT cmd)
153 {
154 switch(cmd) {
155 case ID_VIEW_NAME:
156 if (pane->visible_cols) {
157 pane->visible_cols = 0;
158 calc_widths(pane, TRUE);
159 #ifndef _NO_EXTENSIONS
160 set_header(pane);
161 #endif
162 InvalidateRect(pane->hWnd, 0, TRUE);
163 CheckMenuItem(Globals.hMenuView, ID_VIEW_NAME, MF_BYCOMMAND|MF_CHECKED);
164 // CheckMenuItem(Globals.hMenuView, ID_VIEW_ALL_ATTRIBUTES, MF_BYCOMMAND);
165 // CheckMenuItem(Globals.hMenuView, ID_VIEW_SELECTED_ATTRIBUTES, MF_BYCOMMAND);
166 }
167 break;
168 #if 0
169 case ID_VIEW_ALL_ATTRIBUTES:
170 if (pane->visible_cols != COL_ALL) {
171 pane->visible_cols = COL_ALL;
172 calc_widths(pane, TRUE);
173 #ifndef _NO_EXTENSIONS
174 set_header(pane);
175 #endif
176 InvalidateRect(pane->hWnd, 0, TRUE);
177 CheckMenuItem(Globals.hMenuView, ID_VIEW_NAME, MF_BYCOMMAND);
178 // CheckMenuItem(Globals.hMenuView, ID_VIEW_ALL_ATTRIBUTES, MF_BYCOMMAND|MF_CHECKED);
179 // CheckMenuItem(Globals.hMenuView, ID_VIEW_SELECTED_ATTRIBUTES, MF_BYCOMMAND);
180 }
181 break;
182 #ifndef _NO_EXTENSIONS
183 case ID_PREFERED_SIZES: {
184 calc_widths(pane, TRUE);
185 set_header(pane);
186 InvalidateRect(pane->hWnd, 0, TRUE);
187 break;}
188 #endif
189 #endif
190 // TODO: more command ids...
191 default:
192 return FALSE;
193 }
194 return TRUE;
195 }
196
197 ////////////////////////////////////////////////////////////////////////////////
198 //
199 // hSplitWnd = CreateWindow(szFrameClass, "splitter window", WS_VISIBLE|WS_CHILD,
200 // CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
201 // Globals.hMainWnd, (HMENU)SPLIT_WINDOW, hInstance, NULL);
202 // if (!hSplitWnd)
203 // return FALSE;
204 // hTreeWnd = CreateTreeView(Globals.hMDIClient, "c:\\foobar.txt");
205 // if (!hTreeWnd)
206 // return FALSE;
207 // hListWnd = CreateListView(Globals.hMDIClient, "");
208 // if (!hListWnd)
209 // return FALSE;
210 //
211 ////////////////////////////////////////////////////////////////////////////////
212
213 static void draw_splitbar(HWND hWnd, int x)
214 {
215 RECT rt;
216 HDC hdc = GetDC(hWnd);
217
218 GetClientRect(hWnd, &rt);
219 rt.left = x - SPLIT_WIDTH/2;
220 rt.right = x + SPLIT_WIDTH/2+1;
221 InvertRect(hdc, &rt);
222 ReleaseDC(hWnd, hdc);
223 }
224
225 #if 1
226 static void OnPaint(HWND hWnd, ChildWnd* pChildWnd)
227 {
228 HBRUSH lastBrush;
229 PAINTSTRUCT ps;
230 RECT rt;
231
232 BeginPaint(hWnd, &ps);
233 GetClientRect(hWnd, &rt);
234 lastBrush = SelectObject(ps.hdc, (HBRUSH)GetStockObject(COLOR_SPLITBAR));
235 Rectangle(ps.hdc, rt.left, rt.top-1, rt.right, rt.bottom+1);
236 SelectObject(ps.hdc, lastBrush);
237 // rt.top = rt.bottom - GetSystemMetrics(SM_CYHSCROLL);
238 // FillRect(ps.hdc, &rt, GetStockObject(BLACK_BRUSH));
239 EndPaint(hWnd, &ps);
240 }
241 #else
242 static void OnPaint(HWND hWnd, ChildWnd* pChildWnd)
243 {
244 HBRUSH lastBrush;
245 PAINTSTRUCT ps;
246 RECT rt;
247
248 GetClientRect(hWnd, &rt);
249 BeginPaint(hWnd, &ps);
250
251 lastBrush = SelectObject(ps.hdc, (HBRUSH)GetStockObject(WHITE_BRUSH));
252 Rectangle(ps.hdc, rt.left, rt.top-1, rt.right, rt.bottom+1);
253 SelectObject(ps.hdc, lastBrush);
254 rt.top = rt.bottom - GetSystemMetrics(SM_CYHSCROLL);
255 FillRect(ps.hdc, &rt, GetStockObject(BLACK_BRUSH));
256 /*
257 rt.left = pChildWnd->nSplitPos-SPLIT_WIDTH/2;
258 rt.right = pChildWnd->nSplitPos+SPLIT_WIDTH/2+1;
259 lastBrush = SelectBrush(ps.hdc, (HBRUSH)GetStockObject(COLOR_SPLITBAR));
260 Rectangle(ps.hdc, rt.left, rt.top-1, rt.right, rt.bottom+1);
261 SelectObject(ps.hdc, lastBrush);
262 #ifdef _NO_EXTENSIONS
263 rt.top = rt.bottom - GetSystemMetrics(SM_CYHSCROLL);
264 FillRect(ps.hdc, &rt, GetStockObject(BLACK_BRUSH));
265 #endif
266 */
267 EndPaint(hWnd, &ps);
268 }
269 #endif
270
271 //void ResizeWnd(ChildWnd* child, int cx, int cy);
272 static void ResizeWnd(ChildWnd* child, int cx, int cy)
273 {
274 HDWP hdwp = BeginDeferWindowPos(4);
275 RECT rt = {0, 0, cx, cy};
276
277 cx = child->nSplitPos + SPLIT_WIDTH/2;
278 #ifndef _NO_EXTENSIONS
279 {
280 WINDOWPOS wp;
281 HD_LAYOUT hdl = {&rt, &wp};
282 Header_Layout(child->left.hwndHeader, &hdl);
283 DeferWindowPos(hdwp, child->left.hwndHeader, wp.hwndInsertAfter,
284 wp.x-1, wp.y, child->nSplitPos-SPLIT_WIDTH/2+1, wp.cy, wp.flags);
285 DeferWindowPos(hdwp, child->right.hwndHeader, wp.hwndInsertAfter,
286 rt.left+cx+1, wp.y, wp.cx-cx+2, wp.cy, wp.flags);
287 }
288 #endif
289 DeferWindowPos(hdwp, child->left.hWnd, 0, rt.left, rt.top, child->nSplitPos-SPLIT_WIDTH/2-rt.left, rt.bottom-rt.top, SWP_NOZORDER|SWP_NOACTIVATE);
290 DeferWindowPos(hdwp, child->right.hWnd, 0, rt.left+cx+1, rt.top, rt.right-cx, rt.bottom-rt.top, SWP_NOZORDER|SWP_NOACTIVATE);
291 EndDeferWindowPos(hdwp);
292 }
293
294 static void OnSize(ChildWnd* pChildWnd, WPARAM wParam, LPARAM lParam)
295 {
296 if (wParam != SIZE_MINIMIZED) {
297 ResizeWnd(pChildWnd, LOWORD(lParam), HIWORD(lParam));
298 }
299 }
300
301 //
302 // FUNCTION: ChildWndProc(HWND, unsigned, WORD, LONG)
303 //
304 // PURPOSE: Processes messages for the child windows.
305 //
306 // WM_COMMAND - process the application menu
307 // WM_PAINT - Paint the main window
308 // WM_DESTROY - post a quit message and return
309 //
310 //
311 LRESULT CALLBACK ChildWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
312 {
313 static int last_split;
314
315 ChildWnd* pChildWnd = (ChildWnd*)GetWindowLong(hWnd, GWL_USERDATA);
316 ASSERT(pChildWnd);
317
318 switch(message) {
319 case WM_CREATE:
320 CreateTreeWnd(pChildWnd->hWnd, &pChildWnd->left, IDW_TREE_LEFT, pChildWnd->szPath);
321 CreateListWnd(pChildWnd->hWnd, &pChildWnd->right, IDW_TREE_RIGHT, pChildWnd->szPath);
322 //create_tree_window(pChildWnd->hWnd, &pChildWnd->left, IDW_TREE_LEFT, IDW_HEADER_LEFT, pChildWnd->szPath);
323 //create_list_window(pChildWnd->hWnd, &pChildWnd->right, IDW_TREE_RIGHT, IDW_HEADER_RIGHT);
324 return 0;
325 break;
326
327 case WM_PAINT:
328 OnPaint(hWnd, pChildWnd);
329 break;
330
331 case WM_NCDESTROY:
332 // free_child_window(pChildWnd);
333 SetWindowLong(hWnd, GWL_USERDATA, 0);
334 break;
335
336 case WM_SETCURSOR:
337 if (LOWORD(lParam) == HTCLIENT) {
338 POINT pt;
339 GetCursorPos(&pt);
340 ScreenToClient(hWnd, &pt);
341 if (pt.x>=pChildWnd->nSplitPos-SPLIT_WIDTH/2 && pt.x<pChildWnd->nSplitPos+SPLIT_WIDTH/2+1) {
342 SetCursor(LoadCursor(0, IDC_SIZEWE));
343 return TRUE;
344 }
345 }
346 goto def;
347
348 case WM_LBUTTONDOWN: {
349 RECT rt;
350 int x = LOWORD(lParam);
351
352 GetClientRect(hWnd, &rt);
353 if (x>=pChildWnd->nSplitPos-SPLIT_WIDTH/2 && x<pChildWnd->nSplitPos+SPLIT_WIDTH/2+1) {
354 last_split = pChildWnd->nSplitPos;
355 #ifdef _NO_EXTENSIONS
356 draw_splitbar(hWnd, last_split);
357 #endif
358 SetCapture(hWnd);
359 }
360 break;}
361
362 case WM_LBUTTONUP:
363 if (GetCapture() == hWnd) {
364 #ifdef _NO_EXTENSIONS
365 RECT rt;
366 int x = LOWORD(lParam);
367 draw_splitbar(hWnd, last_split);
368 last_split = -1;
369 GetClientRect(hWnd, &rt);
370 pChildWnd->nSplitPos = x;
371 ResizeWnd(pChildWnd, rt.right, rt.bottom);
372 #endif
373 ReleaseCapture();
374 }
375 break;
376
377 #ifdef _NO_EXTENSIONS
378 case WM_CAPTURECHANGED:
379 if (GetCapture()==hWnd && last_split>=0)
380 draw_splitbar(hWnd, last_split);
381 break;
382 #endif
383
384 case WM_KEYDOWN:
385 if (wParam == VK_ESCAPE)
386 if (GetCapture() == hWnd) {
387 RECT rt;
388 #ifdef _NO_EXTENSIONS
389 draw_splitbar(hWnd, last_split);
390 #else
391 pChildWnd->nSplitPos = last_split;
392 #endif
393 GetClientRect(hWnd, &rt);
394 ResizeWnd(pChildWnd, rt.right, rt.bottom);
395 last_split = -1;
396 ReleaseCapture();
397 SetCursor(LoadCursor(0, IDC_ARROW));
398 }
399 break;
400
401 case WM_MOUSEMOVE:
402 if (GetCapture() == hWnd) {
403 RECT rt;
404 int x = LOWORD(lParam);
405
406 #ifdef _NO_EXTENSIONS
407 HDC hdc = GetDC(hWnd);
408 GetClientRect(hWnd, &rt);
409
410 rt.left = last_split-SPLIT_WIDTH/2;
411 rt.right = last_split+SPLIT_WIDTH/2+1;
412 InvertRect(hdc, &rt);
413
414 last_split = x;
415 rt.left = x-SPLIT_WIDTH/2;
416 rt.right = x+SPLIT_WIDTH/2+1;
417 InvertRect(hdc, &rt);
418
419 ReleaseDC(hWnd, hdc);
420 #else
421 GetClientRect(hWnd, &rt);
422
423 if (x>=0 && x<rt.right) {
424 pChildWnd->nSplitPos = x;
425 ResizeWnd(pChildWnd, rt.right, rt.bottom);
426 rt.left = x-SPLIT_WIDTH/2;
427 rt.right = x+SPLIT_WIDTH/2+1;
428 InvalidateRect(hWnd, &rt, FALSE);
429 UpdateWindow(pChildWnd->left.hWnd);
430 UpdateWindow(hWnd);
431 UpdateWindow(pChildWnd->right.hWnd);
432 }
433 #endif
434 }
435 break;
436
437 #ifndef _NO_EXTENSIONS
438 case WM_GETMINMAXINFO:
439 DefMDIChildProc(hWnd, message, wParam, lParam);
440
441 {LPMINMAXINFO lpmmi = (LPMINMAXINFO)lParam;
442
443 lpmmi->ptMaxTrackSize.x <<= 1;//2*GetSystemMetrics(SM_CXSCREEN) / SM_CXVIRTUALSCREEN
444 lpmmi->ptMaxTrackSize.y <<= 1;//2*GetSystemMetrics(SM_CYSCREEN) / SM_CYVIRTUALSCREEN
445 break;}
446 #endif
447
448 case WM_SETFOCUS:
449 SetCurrentDirectory(pChildWnd->szPath);
450 SetFocus(pChildWnd->nFocusPanel? pChildWnd->right.hWnd: pChildWnd->left.hWnd);
451 break;
452
453 case WM_COMMAND:
454 {
455 Pane* pane = GetFocus()==pChildWnd->left.hWnd? &pChildWnd->left: &pChildWnd->right;
456
457 switch(HIWORD(wParam)) {
458 case LBN_SELCHANGE: {
459 int idx = ListBox_GetCurSel(pane->hWnd);
460 Entry* entry = (Entry*) ListBox_GetItemData(pane->hWnd, idx);
461
462 if (pane == &pChildWnd->left)
463 set_curdir(pChildWnd, entry);
464 else
465 pane->cur = entry;
466 break;}
467
468 case LBN_DBLCLK:
469 activate_entry(pChildWnd, pane);
470 break;
471 }
472 }
473 break;
474
475 case WM_DISPATCH_COMMAND: {
476 Pane* pane = GetFocus()==pChildWnd->left.hWnd? &pChildWnd->left: &pChildWnd->right;
477
478 switch(LOWORD(wParam)) {
479 case ID_WINDOW_NEW_WINDOW:
480 //CreateChildWindow(pChildWnd->szPath);
481 CreateChildWindow(-1);
482 // {
483 // ChildWnd* new_child = alloc_child_window(pChildWnd->szPath);
484 // if (!create_child_window(new_child))
485 // free(new_child);
486 // }
487 break;
488 #if 0
489 case ID_REFRESH:
490 scan_entry(pChildWnd, pane->cur);
491 break;
492 case ID_ACTIVATE:
493 activate_entry(pChildWnd, pane);
494 break;
495 #endif
496 case ID_WINDOW_CASCADE:
497 SendMessage(Globals.hMDIClient, WM_MDICASCADE, 0, 0);
498 break;
499 case ID_WINDOW_TILE_HORZ:
500 SendMessage(Globals.hMDIClient, WM_MDITILE, MDITILE_HORIZONTAL, 0);
501 break;
502 case ID_WINDOW_TILE_VERT:
503 SendMessage(Globals.hMDIClient, WM_MDITILE, MDITILE_VERTICAL, 0);
504 break;
505 case ID_WINDOW_ARRANGE_ICONS:
506 SendMessage(Globals.hMDIClient, WM_MDIICONARRANGE, 0, 0);
507 break;
508 default:
509 return pane_command(pane, LOWORD(wParam));
510 }
511
512 return TRUE;}
513
514 //#ifndef _NO_EXTENSIONS
515 case WM_NOTIFY: {
516 int idCtrl = (int)wParam;
517 NMHDR* pnmh = (NMHDR*)lParam;
518
519 //return pane_notify(pnmh->idFrom==IDW_HEADER_LEFT? &pChildWnd->left: &pChildWnd->right, pnmh);
520 if (idCtrl == IDW_TREE_LEFT) {
521 SendMessage(pChildWnd->left.hWnd, message, wParam, lParam);
522 }
523 if (idCtrl == IDW_TREE_RIGHT) {
524 SendMessage(pChildWnd->right.hWnd, message, wParam, lParam);
525 }
526 }
527 //#endif
528 break;
529
530 case WM_SIZE:
531 if (wParam != SIZE_MINIMIZED) {
532 OnSize(pChildWnd, wParam, lParam);
533 }
534 // fall through
535 default: def:
536 return DefMDIChildProc(hWnd, message, wParam, lParam);
537 }
538 return 0;
539 }
540
541 /*
542 RegenerateUserEnvironment
543 */
544 ATOM RegisterChildWnd(HINSTANCE hInstance, int res_id)
545 {
546 WNDCLASSEX wcFrame = {
547 sizeof(WNDCLASSEX),
548 CS_HREDRAW | CS_VREDRAW/*style*/,
549 FrameWndProc,
550 0/*cbClsExtra*/,
551 0/*cbWndExtra*/,
552 hInstance,
553 LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WINFILE)),
554 LoadCursor(0, IDC_ARROW),
555 0/*hbrBackground*/,
556 0/*lpszMenuName*/,
557 szFrameClass,
558 (HICON)LoadImage(hInstance, MAKEINTRESOURCE(IDI_WINFILE), IMAGE_ICON,
559 GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_SHARED)
560 };
561 ATOM hFrameWndClass = RegisterClassEx(&wcFrame); // register frame window class
562 return hFrameWndClass;
563 }