6 * Copyright (C) 2002 Robert Dickenson <robd@reactos.org>
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.
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.
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.
23 #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
37 //#define ASSERT assert
47 // Global variables and constants
48 // Image_Open, Image_Closed, and Image_Root - integer variables for indexes of the images.
49 // CX_BITMAP and CY_BITMAP - width and height of an icon.
50 // NUM_BITMAPS - number of bitmaps to add to the image list.
55 static int s_init
= 0;
62 Root
* FindPathRoot(HWND hwndTV
, HTREEITEM hItem
, LPTSTR szPath
, int* pPathLen
, int max
)
66 item
.mask
= TVIF_PARAM
;
67 item
.hItem
= TreeView_GetParent(hwndTV
, hItem
);
69 if (TreeView_GetItem(hwndTV
, &item
)) {
70 if (item
.lParam
== 0) {
72 pRoot
= FindPathRoot(hwndTV
, item
.hItem
, szPath
, pPathLen
, max
);
73 szPath
[*pPathLen
] = _T('\\');
75 item
.mask
= TVIF_TEXT
;
77 item
.pszText
= &szPath
[*pPathLen
];
78 item
.cchTextMax
= max
- *pPathLen
;
79 if (TreeView_GetItem(hwndTV
, &item
)) {
80 *pPathLen
+= _tcslen(item
.pszText
);
83 // found root key with valid key value
84 pRoot
= (Root
*)item
.lParam
;
85 item
.mask
= TVIF_TEXT
;
87 item
.pszText
= szPath
;
88 item
.cchTextMax
= max
;
89 if (TreeView_GetItem(hwndTV
, &item
)) {
90 *pPathLen
+= _tcslen(item
.pszText
);
97 static void init_output(HWND hWnd
)
101 HDC hdc
= GetDC(hWnd
);
103 if (GetNumberFormat(LOCALE_USER_DEFAULT
, 0, _T("1000"), 0, b
, 16) > 4)
104 Globals
.num_sep
= b
[1];
106 Globals
.num_sep
= _T('.');
108 old_font
= SelectFont(hdc
, Globals
.hFont
);
109 GetTextExtentPoint32(hdc
, _T(" "), 1, &Globals
.spaceSize
);
110 SelectFont(hdc
, old_font
);
111 ReleaseDC(hWnd
, hdc
);
116 HTREEITEM
AddEntryToTree(HWND hwndTV
, Entry
* entry
)
120 TVINSERTSTRUCT tvins
;
121 static HTREEITEM hPrev
= (HTREEITEM
)TVI_FIRST
;
122 static HTREEITEM hPrevRootItem
= NULL
;
123 static HTREEITEM hPrevLev2Item
= NULL
;
125 //TRACE("AddEntryToTree(level:%u - %s)\n", entry->level, entry->data.cFileName);
126 tvi
.mask
= TVIF_TEXT
| TVIF_IMAGE
| TVIF_SELECTEDIMAGE
| TVIF_CHILDREN
| TVIF_PARAM
;
127 tvi
.pszText
= LPSTR_TEXTCALLBACK
;
129 tvi
.iImage
= I_IMAGECALLBACK
;
130 tvi
.iSelectedImage
= I_IMAGECALLBACK
;
131 tvi
.cChildren
= I_CHILDRENCALLBACK
;
132 // Save the entry pointer in the item's application-defined data area.
133 tvi
.lParam
= (LPARAM
)entry
;
135 tvins
.hInsertAfter
= hPrev
;
136 // Set the parent item based on the specified level.
137 if (entry
->level
== 0) {
138 tvins
.hParent
= TVI_ROOT
;
139 } else if (entry
->level
== 1) {
140 tvins
.hParent
= hPrevRootItem
;
142 tvins
.hParent
= hPrevLev2Item
;
144 tvins
.hParent
= entry
->up
->hTreeItem
;
147 // Add the item to the tree view control.
148 hPrev
= (HTREEITEM
)SendMessage(hwndTV
, TVM_INSERTITEM
, 0, (LPARAM
)(LPTVINSERTSTRUCT
)&tvins
);
149 // Save the handle to the item.
150 if (entry
->level
== 0)
151 hPrevRootItem
= hPrev
;
152 else if (entry
->level
== 1)
153 hPrevLev2Item
= hPrev
;
160 static HTREEITEM
AddEntryToTree(HWND hwndTV
, HTREEITEM hParent
, LPTSTR label
, Root
* entry
, DWORD dwChildren
)
164 TVINSERTSTRUCT tvins
;
166 tvi
.mask
= TVIF_TEXT
| TVIF_IMAGE
| TVIF_SELECTEDIMAGE
| TVIF_CHILDREN
| TVIF_PARAM
;
168 tvi
.cchTextMax
= lstrlen(tvi
.pszText
);
169 tvi
.iImage
= Image_Closed
;
170 tvi
.iSelectedImage
= Image_Open
;
171 tvi
.cChildren
= dwChildren
;
172 tvi
.lParam
= (LPARAM
)entry
;
174 if (entry
) tvins
.hInsertAfter
= (HTREEITEM
)TVI_LAST
;
175 else tvins
.hInsertAfter
= (HTREEITEM
)TVI_SORT
;
176 tvins
.hParent
= hParent
;
177 hItem
= (HTREEITEM
)SendMessage(hwndTV
, TVM_INSERTITEM
, 0, (LPARAM
)(LPTVINSERTSTRUCT
)&tvins
);
183 // insert treectrl entries after index idx
184 static void insert_tree_entries(HWND hWnd
, Entry
* entry
/*Root* pRoot*/, int idx
)
186 static HTREEITEM hItemVisible
;
187 static int hItemVisibleIdx
;
188 // Entry* entry = &pRoot->entry;
191 if (entry
->hTreeItem
) return;
192 ShowWindow(hWnd
, SW_HIDE
);
193 for(; entry
; entry
=entry
->next
) {
195 if (!(entry
->data
.dwFileAttributes
&FILE_ATTRIBUTE_DIRECTORY
))
198 // don't display entries "." and ".." in the left pane
199 if ((entry
->data
.dwFileAttributes
&FILE_ATTRIBUTE_DIRECTORY
) && entry
->data
.cFileName
[0]==_T('.')) {
200 if (entry
->data
.cFileName
[1] == _T('\0') ||
201 (entry
->data
.cFileName
[1] == _T('.') &&
202 entry
->data
.cFileName
[2] == _T('\0'))) {
206 if (entry
->hTreeItem
) continue;
207 // entry->hTreeItem = AddEntryToTree(hWnd, entry);
209 if (entry
->up
&& entry
->up
->hTreeItem
) {
210 // entry->hTreeItem = AddEntryToTree(hWnd, entry->up->hTreeItem, entry->data.cFileName, entry, 0);
211 entry
->hTreeItem
= AddEntryToTree(hWnd
, entry
->up
->hTreeItem
, entry
->data
.cFileName
, NULL
, 1);
213 entry
->hTreeItem
= AddEntryToTree(hWnd
, TVI_ROOT
, entry
->data
.cFileName
, NULL
, 0);
214 // AddEntryToTree(hwndTV, pnmtv->itemNew.hItem, Name, NULL, dwCount);
216 if (entry
->expanded
) {
217 // insert_tree_entries(hWnd, entry->down, idx + 1);
218 insert_tree_entries(hWnd
, entry
->down
, idx
+ 1);
219 TreeView_Expand(hWnd
, entry
->hTreeItem
, TVE_EXPAND
);
221 if (idx
> hItemVisibleIdx
) {
222 hItemVisibleIdx
= idx
;
223 hItemVisible
= entry
->hTreeItem
;
226 if (hItemVisible
&& idx
== 0) {
227 TreeView_SelectSetFirstVisible(hWnd
, hItemVisible
);
229 ShowWindow(hWnd
, SW_SHOW
);
232 static BOOL
InitTreeViewItems(HWND hwndTV
, ChildWnd
* pChildWnd
)
235 TVINSERTSTRUCT tvins
;
237 TCHAR buffer
[MAX_PATH
];
239 wsprintf(buffer
, _T("%s - [%s]"), pChildWnd
->pRoot
->path
, pChildWnd
->pRoot
->fs
);
240 tvi
.mask
= TVIF_TEXT
| TVIF_IMAGE
| TVIF_SELECTEDIMAGE
| TVIF_CHILDREN
| TVIF_PARAM
;
241 tvi
.pszText
= buffer
;
242 tvi
.cchTextMax
= lstrlen(tvi
.pszText
);
243 tvi
.iImage
= Image_Root
;
244 tvi
.iSelectedImage
= Image_Root
;
246 // tvi.lParam = (LPARAM)&pChildWnd->pRoot->entry;
247 tvi
.lParam
= (LPARAM
)pChildWnd
->pRoot
;
248 // tvi.lParam = (LPARAM)pChildWnd;
250 tvins
.hInsertAfter
= (HTREEITEM
)TVI_FIRST
;
251 tvins
.hParent
= TVI_ROOT
;
252 // Add the item to the tree view control.
253 hRoot
= (HTREEITEM
)SendMessage(hwndTV
, TVM_INSERTITEM
, 0, (LPARAM
)(LPTVINSERTSTRUCT
)&tvins
);
254 pChildWnd
->pRoot
->entry
.hTreeItem
= hRoot
;
255 // TreeView_Expand(hwndTV, hRoot, TVE_EXPAND);
256 // insert_tree_entries(hwndTV, &pChildWnd->pRoot->entry, 0);
257 // insert entries into treectrl
258 // if (pChildWnd->pRoot) {
259 // insert_tree_entries(hwndTV, &pChildWnd->pRoot->entry, 0);
261 // TreeView_Expand(hwndTV, pChildWnd->pRoot->entry.hTreeItem, TVE_EXPAND);
262 // calculate column widths
271 // InitTreeViewImageLists - creates an image list, adds three bitmaps
272 // to it, and associates the image list with a tree view control.
273 // Returns TRUE if successful, or FALSE otherwise.
274 // hwndTV - handle to the tree view control.
276 static BOOL
InitTreeViewImageLists(HWND hwndTV
)
278 HIMAGELIST himl
; // handle to image list
279 HBITMAP hbmp
; // handle to bitmap
281 // Create the image list.
282 if ((himl
= ImageList_Create(CX_BITMAP
, CY_BITMAP
,
283 FALSE
, NUM_BITMAPS
, 0)) == NULL
)
286 // Add the open file, closed file, and document bitmaps.
287 hbmp
= LoadBitmap(hInst
, MAKEINTRESOURCE(IDB_FOLDER_RED
));
288 Image_Open
= ImageList_Add(himl
, hbmp
, (HBITMAP
) NULL
);
291 hbmp
= LoadBitmap(hInst
, MAKEINTRESOURCE(IDB_FOLDER_OPEN
));
292 Image_Closed
= ImageList_Add(himl
, hbmp
, (HBITMAP
) NULL
);
295 hbmp
= LoadBitmap(hInst
, MAKEINTRESOURCE(IDB_FOLDER
));
296 Image_Root
= ImageList_Add(himl
, hbmp
, (HBITMAP
) NULL
);
299 // Fail if not all of the images were added.
300 if (ImageList_GetImageCount(himl
) < 3)
303 // Associate the image list with the tree view control.
304 TreeView_SetImageList(hwndTV
, himl
, TVSIL_NORMAL
);
309 BOOL
OnTreeExpanding(HWND hwndTV
, NMTREEVIEW
* pnmtv
)
315 static int expanding
;
316 if (expanding
) return FALSE
;
317 if (pnmtv
->itemNew
.state
& TVIS_EXPANDEDONCE
) {
321 // check if this is either the root or a subkey item...
322 if ((Root
*)pnmtv
->itemNew
.lParam
== NULL
) {
323 szPath
[0] = _T('\0');
324 pRoot
= FindPathRoot(hwndTV
, pnmtv
->itemNew
.hItem
, szPath
, &keyPathLen
, sizeof(szPath
)/sizeof(TCHAR
));
326 pRoot
= (Root
*)pnmtv
->itemNew
.lParam
;
327 szPath
[0] = _T('\0');
330 // Root* pNewRoot = NULL;
331 // insert_tree_entries(hwndTV, &pRoot->entry, 0);
333 insert_tree_entries(hwndTV
, pRoot
->entry
.down
, 0);
335 // entry->hTreeItem = AddEntryToTree(hWnd, entry->up->hTreeItem, entry->data.cFileName, NULL, 1);
339 LONG errCode = RegOpenKeyEx(hKey, szPath, 0, KEY_READ, &hNewKey);
340 if (errCode == ERROR_SUCCESS) {
341 TCHAR Name[MAX_PATH];
342 DWORD cName = MAX_PATH;
343 FILETIME LastWriteTime;
345 //ShowWindow(hwndTV, SW_HIDE);
346 while (RegEnumKeyEx(hNewKey, dwIndex, Name, &cName, NULL, NULL, NULL, &LastWriteTime) == ERROR_SUCCESS) {
348 errCode = RegOpenKeyEx(hNewKey, Name, 0, KEY_READ, &hKey);
349 if (errCode == ERROR_SUCCESS) {
350 TCHAR SubName[MAX_PATH];
351 DWORD cSubName = MAX_PATH;
352 while (RegEnumKeyEx(hKey, dwCount, SubName, &cSubName, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) {
357 AddEntryToTree(hwndTV, pnmtv->itemNew.hItem, Name, NULL, dwCount);
361 //ShowWindow(hwndTV, SW_SHOWNOACTIVATE);
362 RegCloseKey(hNewKey);
371 static void read_directory_win(Entry* parent, LPCTSTR path)
373 Entry* entry = (Entry*)malloc(sizeof(Entry));
374 int level = parent->level + 1;
379 TCHAR buffer[MAX_PATH], *p;
380 for(p=buffer; *path; )
382 lstrcpy(p, _T("\\*"));
383 memset(entry, 0, sizeof(Entry));
384 hFind = FindFirstFile(buffer, &entry->data);
385 if (hFind != INVALID_HANDLE_VALUE) {
386 parent->down = entry;
390 entry->expanded = FALSE;
391 entry->scanned = FALSE;
392 entry->level = level;
393 entry->unix_dir = FALSE;
394 entry->bhfi_valid = FALSE;
395 lstrcpy(p+1, entry->data.cFileName);
396 hFile = CreateFile(buffer, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
397 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
398 if (hFile != INVALID_HANDLE_VALUE) {
399 if (GetFileInformationByHandle(hFile, &entry->bhfi))
400 entry->bhfi_valid = TRUE;
405 entry = (Entry*) malloc(sizeof(Entry));
406 memset(entry, 0, sizeof(Entry));
409 } while(FindNextFile(hFind, &entry->data));
416 parent->scanned = TRUE;
420 void read_directory(Entry* parent, LPCTSTR path, int sortOrder)
422 TCHAR buffer[MAX_PATH];
427 read_directory_win(parent, path);
428 if (Globals.prescan_node) {
434 for(entry=parent->down; entry; entry=entry->next)
435 if (entry->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
436 lstrcpy(d, entry->data.cFileName);
437 read_directory_win(entry, buffer);
438 SortDirectory(entry, sortOrder);
441 SortDirectory(parent, sortOrder);
444 Entry* read_tree_win(Root* root, LPCTSTR path, int sortOrder)
446 TCHAR buffer[MAX_PATH];
447 Entry* entry = &root->entry;
451 entry->unix_dir = FALSE;
453 while(*s && *s!=_T('\\') && *s!=_T('/'))
455 while(*s==_T('\\') || *s==_T('/'))
459 read_directory(entry, buffer, sortOrder);
461 entry->expanded = TRUE;
464 entry = find_entry_win(entry, s);
470 void OnGetDispInfo(NMTVDISPINFO
* ptvdi
)
472 // static TCHAR buffer[200];
473 // LVITEM* pItem = &(ptvdi->item);
474 // Entry* entry = (Entry*)pItem->lParam;
475 Root
* entry
= (Root
*)ptvdi
->item
.lParam
;
478 if (ptvdi
->item
.mask
& TVIF_CHILDREN
) {
479 ptvdi
->item
.cChildren
= 5;
481 if (ptvdi
->item
.mask
& TVIF_IMAGE
) {
482 ptvdi
->item
.iImage
= Image_Root
;
484 if (ptvdi
->item
.mask
& TVIF_SELECTEDIMAGE
) {
485 ptvdi
->item
.iSelectedImage
= Image_Closed
;
487 if (ptvdi
->item
.mask
& TVIF_TEXT
) {
488 // ptvdi->item.pszText = entry->data.cFileName;
489 // ptvdi->item.cchTextMax = lstrlen(entry->data.cFileName);
493 void UpdateStatus(HWND hWnd
, Entry
* pEntry
)
502 if (pEntry
->data
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) {
505 files_size
+= pEntry
->data
.nFileSizeLow
;
507 pEntry
= pEntry
->next
;
509 _tcscpy(suffix
, _T(" bytes"));
512 memset(&numFmt
, 0, sizeof(numFmt
));
513 numFmt
.NumDigits
= 0;
514 numFmt
.LeadingZero
= 0;
516 numFmt
.lpDecimalSep
= _T(".");
517 numFmt
.lpThousandSep
= _T(",");
518 numFmt
.NegativeOrder
= 0;
520 wsprintf(Text
, _T("%d"), files_size
);
521 if (GetNumberFormat(LOCALE_USER_DEFAULT
, 0, Text
, &numFmt
, number
, sizeof(number
))) {
522 wsprintf(Text
, _T("Total %d file(s) (%s%s)"), file_count
, number
, suffix
);
524 wsprintf(Text
, _T("Total %d file(s) (%d%s)"), file_count
, files_size
, suffix
);
526 SendMessage(Globals
.hStatusBar
, SB_SETTEXT
, 1, (LPARAM
)Text
);
530 // CreateTreeView - creates a tree view control.
531 // Returns the handle to the new control if successful, or NULL otherwise.
532 // hwndParent - handle to the control's parent window.
534 HWND
CreateTreeView(HWND hwndParent
, ChildWnd
* pChildWnd
, int id
)
539 // Get the dimensions of the parent window's client area, and create the tree view control.
540 GetClientRect(hwndParent
, &rcClient
);
541 hwndTV
= CreateWindowEx(0, WC_TREEVIEW
, _T("Tree View"),
542 WS_VISIBLE
| WS_CHILD
| WS_BORDER
| WS_EX_CLIENTEDGE
| TVS_HASLINES
| TVS_HASBUTTONS
| TVS_LINESATROOT
,
543 0, 0, rcClient
.right
, rcClient
.bottom
,
544 hwndParent
, (HMENU
)id
, hInst
, NULL
);
545 // Initialize the image list, and add items to the control.
546 if (!InitTreeViewImageLists(hwndTV
) || !InitTreeViewItems(hwndTV
, pChildWnd
)) {
547 DestroyWindow(hwndTV
);
550 SendMessage(hwndTV
, WM_SETFONT
, (WPARAM
)Globals
.hFont
, FALSE
);
554 ////////////////////////////////////////////////////////////////////////////////
556 #ifdef _SUBWND_TREEVIEW
558 static WNDPROC g_orgTreeWndProc
;
560 // OnEndLabelEdit - processes the LVN_ENDLABELEDIT notification message.
561 // Returns TRUE if the label is changed, or FALSE otherwise.
563 static BOOL
OnEndLabelEdit(NMTVDISPINFO
* ptvdi
)
565 // if (ptvdi->item.iItem == -1)
568 // Copy the new label text to the application-defined structure.
569 // lstrcpyn(rgPetInfo[ptvdi->item.iItem].szKind, ptvdi->item.pszText, 10);
572 // To make a more robust application you should send an EM_LIMITTEXT
573 // message to the edit control to prevent the user from entering too
574 // many characters in the field.
577 static LRESULT CALLBACK
TreeWndProc(HWND hWnd
, UINT message
, WPARAM wParam
, LPARAM lParam
)
579 ChildWnd
* child
= (ChildWnd
*)GetWindowLong(GetParent(hWnd
), GWL_USERDATA
);
580 Pane
* pane
= (Pane
*)GetWindowLong(hWnd
, GWL_USERDATA
);
584 #ifndef _NO_EXTENSIONS
590 switch (((LPNMHDR
)lParam
)->code
) {
592 // OnTreeExpand((int)wParam, (HTREEITEM*)lParam);
594 case TVN_GETDISPINFO
:
595 OnGetDispInfo((NMTVDISPINFO
*)lParam
);
597 case TVN_ITEMEXPANDING
:
598 return OnTreeExpanding(hWnd
, (NMTREEVIEW
*)lParam
);
602 UpdateStatus(hWnd
, child
->left
.cur
->down
);
604 // return OnSelChanged((NMTREEVIEW*)lParam);
607 case TVN_SINGLEEXPAND
:
608 TRACE("TreeWndProc(...) TVN_SINGLEEXPAND\n");
609 //lpnmtv = (LPNMTREEVIEW)lParam;
610 //return TVNRET_DEFAULT;
611 // return TVNRET_SKIPOLD; // Skip default processing of the item being unselected.
612 // return TVNRET_SKIPNEW; // Skip default processing of the item being selected.
615 case TVN_ENDLABELEDIT
:
616 return OnEndLabelEdit((NMTVDISPINFO
*)lParam
);
622 child
->nFocusPanel
= pane
== &child
->right
? 1: 0;
623 //ListBox_SetSel(hWnd, TRUE, 1);
624 //TODO: check menu items
625 if (!child
->nFocusPanel
) {
626 UpdateStatus(hWnd
, pane
->cur
);
630 if (wParam
== VK_TAB
) {
631 //TODO: SetFocus(Globals.hDriveBar)
632 SetFocus(child
->nFocusPanel
? child
->hTreeWnd
: child
->hListWnd
);
636 return CallWindowProc(g_orgTreeWndProc
, hWnd
, message
, wParam
, lParam
);
639 void CreateTreeWnd(HWND parent
, Pane
* pane
, int id
)
641 Entry
* entry
= pane
->root
;
642 pane
->hWnd
= CreateTreeView(parent
, NULL
, id
);
643 SetWindowLong(pane
->hWnd
, GWL_USERDATA
, (LPARAM
)pane
);
644 g_orgTreeWndProc
= SubclassWindow(pane
->hWnd
, TreeWndProc
);
645 SendMessage(pane
->hWnd
, WM_SETFONT
, (WPARAM
)Globals
.hFont
, FALSE
);
647 // insert entries into treectrl
649 insert_tree_entries(pane
->hWnd
, entry
, 0);
652 // calculate column widths
655 init_output(pane
->hWnd
);