Added combobox to drivebar, yet to get working. Whole application needs alot of cleanup.
[reactos.git] / rosapps / winfile / treeview.c
1 /*
2 * ReactOS winfile
3 *
4 * treeview.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 <shellapi.h>
39 #include <ctype.h>
40 //#include <assert.h>
41 //#define ASSERT assert
42
43 #include "main.h"
44 #include "treeview.h"
45 #include "entries.h"
46 #include "utils.h"
47
48 #include "trace.h"
49
50
51 // Global variables and constants
52 // Image_Open, Image_Closed, and Image_Root - integer variables for indexes of the images.
53 // CX_BITMAP and CY_BITMAP - width and height of an icon.
54 // NUM_BITMAPS - number of bitmaps to add to the image list.
55 int Image_Open;
56 int Image_Closed;
57 int Image_Root;
58
59 #define CX_BITMAP 16
60 #define CY_BITMAP 16
61 #define NUM_BITMAPS 3
62
63
64 #if 0
65 /*
66 // AddItemToTree - adds items to a tree view control.
67 // Returns the handle to the newly added item.
68 // hwndTV - handle to the tree view control.
69 // lpszItem - text of the item to add.
70 // nLevel - level at which to add the item.
71
72 HTREEITEM AddItemToTree(HWND hwndTV, LPTSTR lpszItem, int nLevel)
73 {
74 TVITEM tvi;
75 TVINSERTSTRUCT tvins;
76 static HTREEITEM hPrev = (HTREEITEM)TVI_FIRST;
77 static HTREEITEM hPrevRootItem = NULL;
78 static HTREEITEM hPrevLev2Item = NULL;
79 HTREEITEM hti;
80
81 tvi.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_CHILDREN | TVIF_PARAM;
82 // Set the text of the item.
83 tvi.pszText = lpszItem;
84 tvi.cchTextMax = lstrlen(lpszItem);
85 // Assume the item is not a parent item, so give it an image.
86 tvi.iImage = Image_Root;
87 tvi.iSelectedImage = Image_Root;
88 tvi.cChildren = 1;
89
90 // Save the heading level in the item's application-defined data area.
91 tvi.lParam = (LPARAM)nLevel;
92
93 tvins.item = tvi;
94 tvins.hInsertAfter = hPrev;
95
96 // Set the parent item based on the specified level.
97 if (nLevel == 1)
98 tvins.hParent = TVI_ROOT;
99 else if (nLevel == 2)
100 tvins.hParent = hPrevRootItem;
101 else
102 tvins.hParent = hPrevLev2Item;
103
104 // Add the item to the tree view control.
105 hPrev = (HTREEITEM)SendMessage(hwndTV, TVM_INSERTITEM, 0, (LPARAM)(LPTVINSERTSTRUCT)&tvins);
106
107 // Save the handle to the item.
108 if (nLevel == 1)
109 hPrevRootItem = hPrev;
110 else if (nLevel == 2)
111 hPrevLev2Item = hPrev;
112
113 // The new item is a child item. Give the parent item a
114 // closed folder bitmap to indicate it now has child items.
115 if (nLevel > 1) {
116 hti = TreeView_GetParent(hwndTV, hPrev);
117 tvi.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE;
118 tvi.hItem = hti;
119 tvi.iImage = Image_Closed;
120 tvi.iSelectedImage = Image_Closed;
121 TreeView_SetItem(hwndTV, &tvi);
122 }
123 return hPrev;
124 }
125 */
126 #endif
127
128
129 static void init_output(HWND hWnd)
130 {
131 TCHAR b[16];
132 HFONT old_font;
133 HDC hdc = GetDC(hWnd);
134
135 if (GetNumberFormat(LOCALE_USER_DEFAULT, 0, _T("1000"), 0, b, 16) > 4)
136 Globals.num_sep = b[1];
137 else
138 Globals.num_sep = _T('.');
139
140 old_font = SelectFont(hdc, Globals.hFont);
141 GetTextExtentPoint32(hdc, _T(" "), 1, &Globals.spaceSize);
142 SelectFont(hdc, old_font);
143 ReleaseDC(hWnd, hdc);
144 }
145
146
147 HTREEITEM AddEntryToTree(HWND hwndTV, Entry* entry)
148 {
149 HTREEITEM hItem = 0;
150 TVITEM tvi;
151 TVINSERTSTRUCT tvins;
152 static HTREEITEM hPrev = (HTREEITEM)TVI_FIRST;
153 static HTREEITEM hPrevRootItem = NULL;
154 static HTREEITEM hPrevLev2Item = NULL;
155
156 //TRACE("AddEntryToTree(level:%u - %s)\n", entry->level, entry->data.cFileName);
157
158 tvi.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_CHILDREN | TVIF_PARAM;
159 /*
160 // Set the text of the item.
161 tvi.pszText = entry->data.cFileName;
162 tvi.cchTextMax = lstrlen(entry->data.cFileName);
163 // Assume the item is not a parent item, so give it an image.
164 tvi.iImage = Image_Root;
165 tvi.iSelectedImage = Image_Root;
166 tvi.cChildren = 1;
167 // Save the heading level in the item's application-defined data area.
168 //tvi.lParam = (LPARAM)entry->level;
169 */
170 tvi.pszText = LPSTR_TEXTCALLBACK;
171 tvi.cchTextMax = 0;
172 tvi.iImage = I_IMAGECALLBACK;
173 tvi.iSelectedImage = I_IMAGECALLBACK;
174 tvi.cChildren = I_CHILDRENCALLBACK;
175 // Save the entry pointer in the item's application-defined data area.
176 tvi.lParam = (LPARAM)entry;
177
178 tvins.item = tvi;
179 tvins.hInsertAfter = hPrev;
180
181 // Set the parent item based on the specified level.
182 if (entry->level == 0) {
183 tvins.hParent = TVI_ROOT;
184 } else if (entry->level == 1) {
185 tvins.hParent = hPrevRootItem;
186 } else {
187 tvins.hParent = hPrevLev2Item;
188 if (hPrevLev2Item) {
189 tvins.hParent = entry->up->hTreeItem;
190 }
191 }
192
193 // Add the item to the tree view control.
194 hPrev = (HTREEITEM)SendMessage(hwndTV, TVM_INSERTITEM, 0, (LPARAM)(LPTVINSERTSTRUCT)&tvins);
195
196 // Save the handle to the item.
197 if (entry->level == 0)
198 hPrevRootItem = hPrev;
199 else if (entry->level == 1)
200 hPrevLev2Item = hPrev;
201 /*
202 // The new item is a child item. Give the parent item a
203 // closed folder bitmap to indicate it now has child items.
204 if (entry->level > 1) {
205 HTREEITEM hti;
206 hti = TreeView_GetParent(hwndTV, hPrev);
207 tvi.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE;
208 tvi.hItem = hti;
209 tvi.iImage = Image_Closed;
210 tvi.iSelectedImage = Image_Closed;
211 TreeView_SetItem(hwndTV, &tvi);
212 }
213 */
214 hItem = hPrev;
215 return hItem;
216 }
217
218
219 static BOOL InitTreeViewItems(HWND hwndTV)
220 {
221 return TRUE;
222 }
223
224 // insert treectrl entries after index idx
225 static void insert_tree_entries(HWND hWnd, Entry* entry, int idx)
226 {
227 static HTREEITEM hItemVisible;
228 static int hItemVisibleIdx;
229
230 if (!entry)
231 return;
232
233 if (entry->hTreeItem)
234 return;
235
236 ShowWindow(hWnd, SW_HIDE);
237 for(; entry; entry=entry->next) {
238 #ifndef _LEFT_FILES
239 if (!(entry->data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY))
240 continue;
241 #endif
242 // don't display entries "." and ".." in the left pane
243 if ((entry->data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) && entry->data.cFileName[0]==_T('.')) {
244 if (entry->data.cFileName[1] == _T('\0') ||
245 (entry->data.cFileName[1] == _T('.') &&
246 entry->data.cFileName[2] == _T('\0'))) {
247 continue;
248 }
249 }
250 // if (idx != -1)
251 // idx++;
252 // ListBox_InsertItemData(hWnd, idx, entry);
253 //TRACE("Adding item %u [level:%u] - %s\n", ++idx, entry->level, entry->data.cFileName);
254
255
256 if (entry->hTreeItem) continue;
257
258
259 entry->hTreeItem = AddEntryToTree(hWnd, entry);
260 if (entry->expanded) {
261 insert_tree_entries(hWnd, entry->down, idx + 1);
262 TreeView_Expand(hWnd, entry->hTreeItem, TVE_EXPAND);
263 }
264 if (idx > hItemVisibleIdx) {
265 hItemVisibleIdx = idx;
266 hItemVisible = entry->hTreeItem;
267 }
268 }
269 if (hItemVisible && idx == 0) {
270 TreeView_SelectSetFirstVisible(hWnd, hItemVisible);
271 }
272 ShowWindow(hWnd, SW_SHOW);
273 }
274
275
276 // InitTreeViewImageLists - creates an image list, adds three bitmaps
277 // to it, and associates the image list with a tree view control.
278 // Returns TRUE if successful, or FALSE otherwise.
279 // hwndTV - handle to the tree view control.
280
281 static BOOL InitTreeViewImageLists(HWND hwndTV)
282 {
283 HIMAGELIST himl; // handle to image list
284 HBITMAP hbmp; // handle to bitmap
285
286 // Create the image list.
287 if ((himl = ImageList_Create(CX_BITMAP, CY_BITMAP,
288 FALSE, NUM_BITMAPS, 0)) == NULL)
289 return FALSE;
290
291 // Add the open file, closed file, and document bitmaps.
292 hbmp = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_FOLDER_RED));
293 Image_Open = ImageList_Add(himl, hbmp, (HBITMAP) NULL);
294 DeleteObject(hbmp);
295
296 hbmp = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_FOLDER_OPEN));
297 Image_Closed = ImageList_Add(himl, hbmp, (HBITMAP) NULL);
298 DeleteObject(hbmp);
299
300 hbmp = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_FOLDER));
301 Image_Root = ImageList_Add(himl, hbmp, (HBITMAP) NULL);
302 DeleteObject(hbmp);
303
304 // Fail if not all of the images were added.
305 if (ImageList_GetImageCount(himl) < 3)
306 return FALSE;
307
308 // Associate the image list with the tree view control.
309 TreeView_SetImageList(hwndTV, himl, TVSIL_NORMAL);
310
311 return TRUE;
312 }
313
314 BOOL OnTreeExpanding(HWND hwndTV, NMTREEVIEW* pnmtv)
315 {
316 static int expanding;
317
318 Entry* entry = (Entry*)pnmtv->itemNew.lParam;
319
320 if (expanding) return FALSE;
321 expanding = TRUE;
322 if (entry) {
323 insert_tree_entries(hwndTV, entry->down, 0);
324 // insert_tree_entries(hwndTV, entry, 0);
325 }
326 expanding = FALSE;
327 return TRUE;
328 }
329
330 #ifndef _MSC_VER
331 #define NMTVDISPINFO TV_DISPINFO
332 #define NMTVDISPINFO TV_DISPINFO
333 #endif
334
335 static void OnGetDispInfo(NMTVDISPINFO* ptvdi)
336 {
337 // static TCHAR buffer[200];
338 // LVITEM* pItem = &(ptvdi->item);
339 // Entry* entry = (Entry*)pItem->lParam;
340 Entry* entry = (Entry*)ptvdi->item.lParam;
341 ASSERT(entry);
342
343 if (ptvdi->item.mask & TVIF_CHILDREN ) {
344 ptvdi->item.cChildren = 5;
345 }
346 if (ptvdi->item.mask & TVIF_IMAGE) {
347 ptvdi->item.iImage = Image_Root;
348 }
349 if (ptvdi->item.mask & TVIF_SELECTEDIMAGE) {
350 ptvdi->item.iSelectedImage = Image_Closed;
351 }
352 if (ptvdi->item.mask & TVIF_TEXT) {
353 ptvdi->item.pszText = entry->data.cFileName;
354 ptvdi->item.cchTextMax = lstrlen(entry->data.cFileName);
355 }
356 }
357
358 // OnEndLabelEdit - processes the LVN_ENDLABELEDIT notification message.
359 // Returns TRUE if the label is changed, or FALSE otherwise.
360
361 static BOOL OnEndLabelEdit(NMTVDISPINFO* ptvdi)
362 {
363 // if (ptvdi->item.iItem == -1)
364 // return FALSE;
365
366 // Copy the new label text to the application-defined structure.
367 // lstrcpyn(rgPetInfo[ptvdi->item.iItem].szKind, ptvdi->item.pszText, 10);
368
369 return TRUE;
370 // To make a more robust application you should send an EM_LIMITTEXT
371 // message to the edit control to prevent the user from entering too
372 // many characters in the field.
373 }
374
375 void UpdateStatus(HWND hWnd, Entry* pEntry)
376 {
377 int file_count = 0;
378 int files_size = 0;
379 TCHAR suffix[10];
380 TCHAR number[50];
381 TCHAR Text[260];
382
383 while (pEntry) {
384 if (pEntry->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
385 } else {
386 ++file_count;
387 files_size += pEntry->data.nFileSizeLow;
388 }
389 pEntry = pEntry->next;
390 };
391
392 _tcscpy(suffix, _T(" bytes"));
393 {
394 NUMBERFMT numFmt;
395 memset(&numFmt, 0, sizeof(numFmt));
396 numFmt.NumDigits = 0;
397 numFmt.LeadingZero = 0;
398 numFmt.Grouping = 3;
399 numFmt.lpDecimalSep = _T(".");
400 numFmt.lpThousandSep = _T(",");
401 numFmt.NegativeOrder = 0;
402
403 wsprintf(Text, _T("%d"), files_size);
404 if (GetNumberFormat(LOCALE_USER_DEFAULT, 0, Text, &numFmt, number, sizeof(number))) {
405 wsprintf(Text, _T("Total %d file(s) (%s%s)"), file_count, number, suffix);
406 } else {
407 wsprintf(Text, _T("Total %d file(s) (%d%s)"), file_count, files_size, suffix);
408 }
409 SendMessage(Globals.hStatusBar, SB_SETTEXT, 1, (LPARAM)Text);
410 }
411 }
412
413 ////////////////////////////////////////////////////////////////////////////////
414 static WNDPROC g_orgTreeWndProc;
415
416 static LRESULT CALLBACK TreeWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
417 {
418 ChildWnd* child = (ChildWnd*)GetWindowLong(GetParent(hWnd), GWL_USERDATA);
419 Pane* pane = (Pane*)GetWindowLong(hWnd, GWL_USERDATA);
420 ASSERT(child);
421
422 switch (message) {
423 #ifndef _NO_EXTENSIONS
424 case WM_HSCROLL:
425 set_header(pane);
426 break;
427 #endif
428 case WM_NOTIFY:
429 switch (((LPNMHDR)lParam)->code) {
430 // case TVM_EXPAND:
431 // OnTreeExpand((int)wParam, (HTREEITEM*)lParam);
432 // break;
433 case TVN_GETDISPINFO:
434 OnGetDispInfo((NMTVDISPINFO*)lParam);
435 break;
436 case TVN_ITEMEXPANDING:
437 return OnTreeExpanding(hWnd, (NMTREEVIEW*)lParam);
438 break;
439 case TVN_SELCHANGED:
440
441 UpdateStatus(hWnd, child->left.cur->down);
442 break;
443 // return OnSelChanged((NMTREEVIEW*)lParam);
444 // break;
445 #if 0
446 case TVN_SINGLEEXPAND:
447 TRACE("TreeWndProc(...) TVN_SINGLEEXPAND\n");
448 //lpnmtv = (LPNMTREEVIEW)lParam;
449 //return TVNRET_DEFAULT;
450 // return TVNRET_SKIPOLD; // Skip default processing of the item being unselected.
451 // return TVNRET_SKIPNEW; // Skip default processing of the item being selected.
452 break;
453 #endif
454 case TVN_ENDLABELEDIT:
455 return OnEndLabelEdit((NMTVDISPINFO*)lParam);
456 break;
457 }
458 return 0;
459 break;
460 case WM_SETFOCUS:
461 child->nFocusPanel = pane == &child->right? 1: 0;
462 //ListBox_SetSel(hWnd, TRUE, 1);
463 //TODO: check menu items
464 if (!child->nFocusPanel) {
465 UpdateStatus(hWnd, pane->cur);
466 }
467 break;
468 case WM_KEYDOWN:
469 if (wParam == VK_TAB) {
470 //TODO: SetFocus(Globals.hDriveBar)
471 SetFocus(child->nFocusPanel ? child->left.hWnd: child->right.hWnd);
472 }
473 break;
474 }
475 return CallWindowProc(g_orgTreeWndProc, hWnd, message, wParam, lParam);
476 }
477
478 // CreateTreeView - creates a tree view control.
479 // Returns the handle to the new control if successful, or NULL otherwise.
480 // hwndParent - handle to the control's parent window.
481
482 static HWND CreateTreeView(HWND hwndParent, int id)
483 {
484 RECT rcClient;
485 HWND hwndTV;
486
487 // Get the dimensions of the parent window's client area, and create the tree view control.
488 GetClientRect(hwndParent, &rcClient);
489 hwndTV = CreateWindowEx(0, WC_TREEVIEW, _T("Tree View"),
490 WS_VISIBLE | WS_CHILD | WS_BORDER | WS_EX_CLIENTEDGE | TVS_HASLINES | TVS_HASBUTTONS | TVS_LINESATROOT,
491 0, 0, rcClient.right, rcClient.bottom,
492 hwndParent, (HMENU)id, hInst, NULL);
493 // Initialize the image list, and add items to the control.
494 if (!InitTreeViewImageLists(hwndTV) || !InitTreeViewItems(hwndTV)) {
495 DestroyWindow(hwndTV);
496 return NULL;
497 }
498 return hwndTV;
499 }
500
501 //void create_tree_window(HWND parent, Pane* pane, int id, int id_header, LPTSTR lpszFileName)
502 void CreateTreeWnd(HWND parent, Pane* pane, int id)
503 {
504 static int s_init = 0;
505 Entry* entry = pane->root;
506
507 pane->treePane = 1;
508
509 pane->hWnd = CreateTreeView(parent, id);
510 SetWindowLong(pane->hWnd, GWL_USERDATA, (LPARAM)pane);
511 g_orgTreeWndProc = SubclassWindow(pane->hWnd, TreeWndProc);
512 SendMessage(pane->hWnd, WM_SETFONT, (WPARAM)Globals.hFont, FALSE);
513
514 // insert entries into treectrl
515 if (entry) {
516 insert_tree_entries(pane->hWnd, entry, 0);
517 }
518
519 // calculate column widths
520 if (!s_init) {
521 s_init = 1;
522 init_output(pane->hWnd);
523 }
524 // calc_widths(pane, TRUE);
525 //#ifndef _NO_EXTENSIONS
526 // pane->hwndHeader = create_header(parent, pane, id_header);
527 //#endif
528 }