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.
26 #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
38 //#include <winspool.h>
53 ////////////////////////////////////////////////////////////////////////////////
57 extern HINSTANCE hInst
;
59 static WNDPROC g_orgListWndProc
;
62 ////////////////////////////////////////////////////////////////////////////////
63 // Local module support methods
66 static void init_output(HWND hWnd
)
70 HDC hdc
= GetDC(hWnd
);
72 if (GetNumberFormat(LOCALE_USER_DEFAULT
, 0, _T("1000"), 0, b
, 16) > 4)
73 Globals
.num_sep
= b
[1];
75 Globals
.num_sep
= _T('.');
77 old_font
= SelectFont(hdc
, Globals
.hFont
);
78 GetTextExtentPoint32(hdc
, _T(" "), 1, &Globals
.spaceSize
);
79 SelectFont(hdc
, old_font
);
83 static void AddEntryToList(HWND hwndLV
, int idx
, Entry
* entry
)
87 item
.mask
= LVIF_TEXT
| LVIF_PARAM
;
92 // item.pszText = entry->data.cFileName;
93 item
.pszText
= LPSTR_TEXTCALLBACK
;
94 // item.cchTextMax = strlen(entry->data.cFileName);
97 // item.iImage = I_IMAGECALLBACK;
98 item
.lParam
= (LPARAM
)entry
;
99 #if (_WIN32_IE >= 0x0300)
102 ListView_InsertItem(hwndLV
, &item
);
105 // insert listctrl entries after index idx
106 static void InsertListEntries(HWND hWnd
, Entry
* entry
, int idx
)
108 ShowWindow(hWnd
, SW_HIDE
);
114 for (; entry
; entry
= entry
->next
) {
116 if (entry
->data
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
119 //ListBox_InsertItemData(hWnd, idx, entry);
120 AddEntryToList(hWnd
, idx
, entry
);
123 ShowWindow(hWnd
, SW_SHOW
);
126 #define MAX_LIST_COLUMNS 5
127 static int default_column_widths
[MAX_LIST_COLUMNS
] = { 175, 100, 100, 100, 70 };
128 static int column_alignment
[MAX_LIST_COLUMNS
] = { LVCFMT_LEFT
, LVCFMT_RIGHT
, LVCFMT_RIGHT
, LVCFMT_RIGHT
, LVCFMT_LEFT
};
130 static void CreateListColumns(HWND hWndListView
)
137 lvC
.mask
= LVCF_FMT
| LVCF_WIDTH
| LVCF_TEXT
| LVCF_SUBITEM
;
138 lvC
.pszText
= szText
;
140 // Load the column labels from the resource file.
141 for (index
= 0; index
< MAX_LIST_COLUMNS
; index
++) {
142 lvC
.iSubItem
= index
;
143 lvC
.cx
= default_column_widths
[index
];
144 lvC
.fmt
= column_alignment
[index
];
145 LoadString(hInst
, IDS_LIST_COLUMN_FIRST
+ index
, szText
, sizeof(szText
));
146 if (ListView_InsertColumn(hWndListView
, index
, &lvC
) == -1) {
147 // TODO: handle failure condition...
153 static HWND
CreateListView(HWND hwndParent
, int id
)
155 RECT rcClient
; // dimensions of client area
156 HWND hwndLV
; // handle to list view control
158 // Get the dimensions of the parent window's client area, and create the list view control.
159 GetClientRect(hwndParent
, &rcClient
);
160 hwndLV
= CreateWindowEx(0, WC_LISTVIEW
, _T("List View"),
161 // WS_VISIBLE | WS_CHILD | WS_BORDER | LVS_REPORT | LVS_NOCOLUMNHEADER,
162 WS_VISIBLE
| WS_CHILD
| WS_BORDER
| LVS_REPORT
,
163 0, 0, rcClient
.right
, rcClient
.bottom
,
164 hwndParent
, (HMENU
)id
, hInst
, NULL
);
166 // Initialize the image list, and add items to the control.
168 if (!InitListViewImageLists(hwndLV) ||
169 !InitListViewItems(hwndLV, lpszPathName)) {
170 DestroyWindow(hwndLV);
174 ListView_SetExtendedListViewStyle(hwndLV
, LVS_EX_FULLROWSELECT
);
175 CreateListColumns(hwndLV
);
182 LCID Locale, // locale
183 DWORD dwFlags, // options
184 LPCTSTR lpValue, // input number string
185 CONST NUMBERFMT *lpFormat, // formatting information
186 LPTSTR lpNumberStr, // output buffer
187 int cchNumber // size of output buffer
191 typedef struct _numberfmt {
196 LPTSTR lpThousandSep;
198 } NUMBERFMT, *LPNUMBERFMT;
201 typedef struct _BY_HANDLE_FILE_INFORMATION {
202 DWORD dwFileAttributes;
203 FILETIME ftCreationTime;
204 FILETIME ftLastAccessTime;
205 FILETIME ftLastWriteTime;
206 DWORD dwVolumeSerialNumber;
209 DWORD nNumberOfLinks;
210 DWORD nFileIndexHigh;
212 } BY_HANDLE_FILE_INFORMATION, *PBY_HANDLE_FILE_INFORMATION;
219 BOOL FileTimeToLocalFileTime(
220 CONST FILETIME *lpFileTime, // UTC file time to convert
221 LPFILETIME lpLocalFileTime // converted file time
224 BOOL FileTimeToSystemTime(
225 CONST FILETIME *lpFileTime, // file time to convert
226 LPSYSTEMTIME lpSystemTime // receives system time
230 // OnGetDispInfo - processes the LVN_GETDISPINFO
231 // notification message.
233 static void OnGetDispInfo(NMLVDISPINFO
* plvdi
)
235 SYSTEMTIME SystemTime
;
236 FILETIME LocalFileTime
;
237 static TCHAR buffer
[200];
239 // LVITEM* pItem = &(plvdi->item);
240 // Entry* entry = (Entry*)pItem->lParam;
241 Entry
* entry
= (Entry
*)plvdi
->item
.lParam
;
244 plvdi
->item
.pszText
= NULL
;
246 switch (plvdi
->item
.iSubItem
) {
248 plvdi
->item
.pszText
= entry
->data
.cFileName
;
249 // item.cchTextMax = strlen(entry->data.cFileName);
250 // plvdi->item.pszText = rgPetInfo[plvdi->item.iItem].szKind;
253 if (entry
->bhfi_valid
) {
255 memset(&numFmt
, 0, sizeof(numFmt
));
256 numFmt
.NumDigits
= 0;
257 numFmt
.LeadingZero
= 0;
259 numFmt
.lpDecimalSep
= _T(".");
260 numFmt
.lpThousandSep
= _T(",");
261 numFmt
.NegativeOrder
= 0;
263 //entry->bhfi.nFileSizeLow;
264 //entry->bhfi.nFileSizeHigh;
265 //entry->bhfi.ftCreationTime
266 wsprintf(buffer
, _T("%u"), entry
->bhfi
.nFileSizeLow
);
267 if (GetNumberFormat(LOCALE_USER_DEFAULT
, 0, buffer
, &numFmt
,
268 buffer
+ sizeof(buffer
)/2, sizeof(buffer
)/2)) {
269 plvdi
->item
.pszText
= buffer
+ sizeof(buffer
)/2;
271 plvdi
->item
.pszText
= buffer
;
274 plvdi
->item
.pszText
= _T("unknown");
278 plvdi
->item
.pszText
= _T("error");
279 if (FileTimeToLocalFileTime(&entry
->bhfi
.ftLastWriteTime
, &LocalFileTime
)) {
280 if (FileTimeToSystemTime(&LocalFileTime
, &SystemTime
)) {
281 if (GetDateFormat(LOCALE_USER_DEFAULT
, DATE_SHORTDATE
, &SystemTime
, NULL
, buffer
, sizeof(buffer
))) {
282 plvdi
->item
.pszText
= buffer
;
288 plvdi
->item
.pszText
= _T("error");
289 if (FileTimeToLocalFileTime(&entry
->bhfi
.ftLastWriteTime
, &LocalFileTime
)) {
290 if (FileTimeToSystemTime(&LocalFileTime
, &SystemTime
)) {
291 // if (GetTimeFormat(UserDefaultLCID, 0, &SystemTime, NULL, buffer, sizeof(buffer))) {
292 if (GetTimeFormat(LOCALE_USER_DEFAULT
, 0, &SystemTime
, NULL
, buffer
, sizeof(buffer
))) {
293 plvdi
->item
.pszText
= buffer
;
299 plvdi
->item
.pszText
= _T("");
300 _tcscpy(buffer
, _T(" "));
302 if (entry
->bhfi
.dwFileAttributes
& FILE_ATTRIBUTE_ARCHIVE
) _tcscat(buffer
, _T("a")); else _tcscat(buffer
, _T(" "));
303 if (entry
->bhfi
.dwFileAttributes
& FILE_ATTRIBUTE_COMPRESSED
) _tcscat(buffer
, _T("c")); else _tcscat(buffer
, _T(" "));
304 if (entry
->bhfi
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) _tcscat(buffer
, _T("d")); else _tcscat(buffer
, _T(" "));
305 if (entry
->bhfi
.dwFileAttributes
& FILE_ATTRIBUTE_ENCRYPTED
) _tcscat(buffer
, _T("e")); else _tcscat(buffer
, _T(" "));
306 if (entry
->bhfi
.dwFileAttributes
& FILE_ATTRIBUTE_HIDDEN
) _tcscat(buffer
, _T("h")); else _tcscat(buffer
, _T(" "));
307 if (entry
->bhfi
.dwFileAttributes
& FILE_ATTRIBUTE_NORMAL
) _tcscat(buffer
, _T("n")); else _tcscat(buffer
, _T(" "));
308 if (entry
->bhfi
.dwFileAttributes
& FILE_ATTRIBUTE_OFFLINE
) _tcscat(buffer
, _T("o")); else _tcscat(buffer
, _T(" "));
309 if (entry
->bhfi
.dwFileAttributes
& FILE_ATTRIBUTE_READONLY
) _tcscat(buffer
, _T("r")); else _tcscat(buffer
, _T(" "));
310 if (entry
->bhfi
.dwFileAttributes
& FILE_ATTRIBUTE_REPARSE_POINT
) _tcscat(buffer
, _T("p")); else _tcscat(buffer
, _T(" "));
311 if (entry
->bhfi
.dwFileAttributes
& FILE_ATTRIBUTE_SPARSE_FILE
) _tcscat(buffer
, _T("f")); else _tcscat(buffer
, _T(" "));
312 if (entry
->bhfi
.dwFileAttributes
& FILE_ATTRIBUTE_SYSTEM
) _tcscat(buffer
, _T("s")); else _tcscat(buffer
, _T(" "));
313 if (entry
->bhfi
.dwFileAttributes
& FILE_ATTRIBUTE_TEMPORARY
) _tcscat(buffer
, _T("t")); else _tcscat(buffer
, _T(" "));
314 plvdi
->item
.pszText
= buffer
;
317 _tcscpy(buffer
, _T(" "));
318 plvdi
->item
.pszText
= buffer
;
323 FILE_ATTRIBUTE_ARCHIVE The file or directory is an archive file. Applications use this attribute to mark files for backup or removal.
324 FILE_ATTRIBUTE_COMPRESSED The file or directory is compressed. For a file, this means that all of the data in the file is compressed. For a directory, this means that compression is the default for newly created files and subdirectories.
325 FILE_ATTRIBUTE_DIRECTORY The handle identifies a directory.
326 FILE_ATTRIBUTE_ENCRYPTED The file or directory is encrypted. For a file, this means that all data in the file is encrypted. For a directory, this means that encryption is the default for newly created files and subdirectories.
327 FILE_ATTRIBUTE_HIDDEN The file or directory is hidden. It is not included in an ordinary directory listing.
328 FILE_ATTRIBUTE_NORMAL The file has no other attributes. This attribute is valid only if used alone.
329 FILE_ATTRIBUTE_OFFLINE The file data is not immediately available. This attribute indicates that the file data has been physically moved to offline storage. This attribute is used by Remote Storage, the hierarchical storage management software in Windows 2000. Applications should not arbitrarily change this attribute.
330 FILE_ATTRIBUTE_READONLY The file or directory is read-only. Applications can read the file but cannot write to it or delete it. In the case of a directory, applications cannot delete it.
331 FILE_ATTRIBUTE_REPARSE_POINT The file has an associated reparse point.
332 FILE_ATTRIBUTE_SPARSE_FILE The file is a sparse file.
333 FILE_ATTRIBUTE_SYSTEM The file or directory is part of the operating system or is used exclusively by the operating system.
334 FILE_ATTRIBUTE_TEMPORARY The file is being used for temporary storage. File systems attempt to keep all the data in memory for quicker access, rather than flushing the data back to mass storage. A temporary file should be deleted by the application as soon as it is no longer needed.
338 // OnEndLabelEdit - processes the LVN_ENDLABELEDIT
339 // notification message.
340 // Returns TRUE if the label is changed, or FALSE otherwise.
342 static BOOL
OnEndLabelEdit(NMLVDISPINFO
* plvdi
)
344 if (plvdi
->item
.iItem
== -1)
347 // Copy the new label text to the application-defined structure.
348 // lstrcpyn(rgPetInfo[plvdi->item.iItem].szKind, plvdi->item.pszText, 10);
351 // To make a more robust application you should send an EM_LIMITTEXT
352 // message to the edit control to prevent the user from entering too
353 // many characters in the field.
357 typedef struct _BY_HANDLE_FILE_INFORMATION {
358 DWORD dwFileAttributes;
359 FILETIME ftCreationTime;
360 FILETIME ftLastAccessTime;
361 FILETIME ftLastWriteTime;
362 DWORD dwVolumeSerialNumber;
365 DWORD nNumberOfLinks;
366 DWORD nFileIndexHigh;
368 } BY_HANDLE_FILE_INFORMATION, *PBY_HANDLE_FILE_INFORMATION;
371 typedef struct _WIN32_FIND_DATA {
372 DWORD dwFileAttributes;
373 FILETIME ftCreationTime;
374 FILETIME ftLastAccessTime;
375 FILETIME ftLastWriteTime;
380 TCHAR cFileName[ MAX_PATH ];
381 TCHAR cAlternateFileName[ 14 ];
382 } WIN32_FIND_DATA, *PWIN32_FIND_DATA;
385 static int CALLBACK
CompareFunc(LPARAM lParam1
, LPARAM lParam2
, LPARAM lParamSort
)
387 Entry
* pItem1
= (Entry
*)lParam1
;
388 Entry
* pItem2
= (Entry
*)lParam2
;
390 if (pItem1
!= NULL
&& pItem2
!= NULL
) {
391 switch (lParamSort
) {
392 case ID_VIEW_SORT_BY_NAME
:
393 return _tcscmp(pItem1
->data
.cFileName
, pItem2
->data
.cFileName
);
395 case ID_VIEW_SORT_BY_TYPE
:
396 // if (pItem1->bhfi.nFileSizeLow != pItem2->bhfi.nFileSizeLow) {
397 // return (pItem1->bhfi.nFileSizeLow < pItem2->bhfi.nFileSizeLow) ? -1 : 1;
400 case ID_VIEW_SORT_BY_SIZE
:
401 if (pItem1
->bhfi
.nFileSizeLow
!= pItem2
->bhfi
.nFileSizeLow
) {
402 return (pItem1
->bhfi
.nFileSizeLow
< pItem2
->bhfi
.nFileSizeLow
) ? -1 : 1;
405 case ID_VIEW_SORT_BY_DATE
:
406 return CompareFileTime(&pItem1
->bhfi
.ftLastWriteTime
, &pItem2
->bhfi
.ftLastWriteTime
);
413 static void CmdSortItems(HWND hWnd
, UINT cmd
)
415 CheckMenuItem(Globals
.hMenuView
, ID_VIEW_SORT_BY_NAME
, MF_BYCOMMAND
);
416 CheckMenuItem(Globals
.hMenuView
, ID_VIEW_SORT_BY_TYPE
, MF_BYCOMMAND
);
417 CheckMenuItem(Globals
.hMenuView
, ID_VIEW_SORT_BY_SIZE
, MF_BYCOMMAND
);
418 CheckMenuItem(Globals
.hMenuView
, ID_VIEW_SORT_BY_DATE
, MF_BYCOMMAND
);
419 ListView_SortItems(hWnd
, &CompareFunc
, cmd
);
420 CheckMenuItem(Globals
.hMenuView
, cmd
, MF_BYCOMMAND
| MF_CHECKED
);
423 static BOOL
_CmdWndProc(HWND hWnd
, UINT message
, WPARAM wParam
, LPARAM lParam
)
425 UINT cmd
= LOWORD(wParam
);
433 item
.mask
= LVIF_PARAM
;
434 // UINT selected_count = ListView_GetSelectedCount(hWnd);
436 item
.iItem
= ListView_GetNextItem(hWnd
, -1, LVNI_SELECTED
);
437 if (item
.iItem
!= -1) {
438 if (ListView_GetItem(hWnd
, &item
)) {
439 Entry
* entry
= (Entry
*)item
.lParam
;
440 OpenTarget(hWnd
, entry
->data
.cFileName
);
450 case ID_FILE_COPY_CLIPBOARD
:
453 case ID_FILE_PROPERTIES
:
454 case ID_FILE_COMPRESS
:
455 case ID_FILE_UNCOMPRESS
:
461 case ID_FILE_ASSOCIATE
:
462 case ID_FILE_CREATE_DIRECTORY
:
464 case ID_VIEW_SORT_BY_NAME
:
465 case ID_VIEW_SORT_BY_TYPE
:
466 case ID_VIEW_SORT_BY_SIZE
:
467 case ID_VIEW_SORT_BY_DATE
:
468 CmdSortItems(hWnd
, cmd
);
477 ////////////////////////////////////////////////////////////////////////////////
479 static LRESULT CALLBACK
ListWndProc(HWND hWnd
, UINT message
, WPARAM wParam
, LPARAM lParam
)
481 ChildWnd
* child
= (ChildWnd
*)GetWindowLong(GetParent(hWnd
), GWL_USERDATA
);
482 Pane
* pane
= (Pane
*)GetWindowLong(hWnd
, GWL_USERDATA
);
488 //CreateListView(hWnd);
492 if (!_CmdWndProc(hWnd
, message
, wParam
, lParam
)) {
493 return CallWindowProc(g_orgListWndProc
, hWnd
, message
, wParam
, lParam
);
496 case WM_DISPATCH_COMMAND
:
497 return _CmdWndProc(hWnd
, message
, wParam
, lParam
);
500 switch (((LPNMHDR
)lParam
)->code
) {
501 case LVN_GETDISPINFO
:
502 OnGetDispInfo((NMLVDISPINFO
*)lParam
);
506 NMITEMACTIVATE
* nmitem
= (LPNMITEMACTIVATE
)lParam
;
509 if (nmitem
->hdr
.hwndFrom
!= hWnd
) break;
510 // if (nmitem->hdr.idFrom != IDW_LISTVIEW) break;
511 // if (nmitem->hdr.code != ???) break;
512 switch (nmitem
->uKeyFlags
) {
513 case LVKF_ALT
: // The ALT key is pressed.
514 // properties dialog box ?
516 case LVKF_CONTROL
: // The CTRL key is pressed.
517 // run dialog box for providing parameters...
519 case LVKF_SHIFT
: // The SHIFT key is pressed.
522 info
.pt
.x
= nmitem
->ptAction
.x
;
523 info
.pt
.y
= nmitem
->ptAction
.y
;
524 if (ListView_HitTest(hWnd
, &info
) != -1) {
526 item
.mask
= LVIF_PARAM
;
527 item
.iItem
= info
.iItem
;
528 if (ListView_GetItem(hWnd
, &item
)) {
529 Entry
* entry
= (Entry
*)item
.lParam
;
530 OpenTarget(hWnd
, entry
->data
.cFileName
);
536 case LVN_ENDLABELEDIT
:
537 return OnEndLabelEdit((NMLVDISPINFO
*)lParam
);
540 return CallWindowProc(g_orgListWndProc
, hWnd
, message
, wParam
, lParam
);
546 child->nFocusPanel = pane==&child->right? 1: 0;
547 ListBox_SetSel(hWnd, TRUE, 1);
548 //TODO: check menu items
551 if (wParam == VK_TAB) {
552 //TODO: SetFocus(Globals.hDriveBar)
553 SetFocus(child->nFocusPanel? child->left.hWnd: child->right.hWnd);
558 return CallWindowProc(g_orgListWndProc
, hWnd
, message
, wParam
, lParam
);
565 void CreateListWnd(HWND parent
, Pane
* pane
, int id
, LPTSTR lpszPathName
)
567 // static int s_init = 0;
568 Entry
* entry
= pane
->root
;
572 pane
->hWnd
= CreateListView(parent
, id
);
574 pane
->hWnd
= CreateWindow(_T("ListBox"), _T(""), WS_CHILD
|WS_VISIBLE
|WS_HSCROLL
|WS_VSCROLL
|
575 LBS_DISABLENOSCROLL
|LBS_NOINTEGRALHEIGHT
|LBS_OWNERDRAWFIXED
|LBS_NOTIFY
,
576 0, 0, 0, 0, parent
, (HMENU
)id
, Globals
.hInstance
, 0);
578 SetWindowLong(pane
->hWnd
, GWL_USERDATA
, (LPARAM
)pane
);
579 g_orgListWndProc
= SubclassWindow(pane
->hWnd
, ListWndProc
);
580 SendMessage(pane
->hWnd
, WM_SETFONT
, (WPARAM
)Globals
.hFont
, FALSE
);
582 // insert entries into listbox
584 InsertListEntries(pane
->hWnd
, entry
, -1);
587 // calculate column widths
590 // init_output(pane->hWnd);
592 // calc_widths(pane, TRUE);
595 void RefreshList(HWND hWnd
, Entry
* entry
)
598 ListView_DeleteAllItems(hWnd
);
600 //TRACE("RefreshList(...) entry name: %s\n", entry->data.cFileName);
601 InsertListEntries(hWnd
, entry
, -1);
607 Pane* pane = (Pane*)GetWindowLong(hWnd, GWL_USERDATA);
609 // ListBox_RemoveAll(hWnd, TRUE, 1);
610 ListView_DeleteAllItems(pane->hWnd);
612 InsertListEntries(pane->hWnd, entry, -1);