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
41 ////////////////////////////////////////////////////////////////////////////////
42 // Global and Local Variables:
45 static WNDPROC g_orgListWndProc
;
47 #define MAX_LIST_COLUMNS 5
48 static int default_column_widths
[MAX_LIST_COLUMNS
] = { 175, 100, 100, 100, 70 };
49 static int column_alignment
[MAX_LIST_COLUMNS
] = { LVCFMT_LEFT
, LVCFMT_RIGHT
, LVCFMT_RIGHT
, LVCFMT_RIGHT
, LVCFMT_LEFT
};
52 ////////////////////////////////////////////////////////////////////////////////
53 // Local module support methods
56 static void AddEntryToList(HWND hwndLV
, int idx
, Entry
* entry
)
60 item
.mask
= LVIF_TEXT
| LVIF_PARAM
;
65 // item.pszText = entry->data.cFileName;
66 // item.cchTextMax = strlen(entry->data.cFileName);
67 item
.pszText
= LPSTR_TEXTCALLBACK
;
70 // item.iImage = I_IMAGECALLBACK;
71 item
.lParam
= (LPARAM
)entry
;
72 #if (_WIN32_IE >= 0x0300)
75 ListView_InsertItem(hwndLV
, &item
);
78 // insert listctrl entries after index idx
79 static void InsertListEntries(HWND hWnd
, Entry
* entry
, int idx
)
81 ShowWindow(hWnd
, SW_HIDE
);
87 for (; entry
; entry
= entry
->next
) {
89 if (entry
->data
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
92 AddEntryToList(hWnd
, idx
, entry
);
95 ShowWindow(hWnd
, SW_SHOW
);
98 static void CreateListColumns(HWND hWndListView
)
105 lvC
.mask
= LVCF_FMT
| LVCF_WIDTH
| LVCF_TEXT
| LVCF_SUBITEM
;
106 lvC
.pszText
= szText
;
108 // Load the column labels from the resource file.
109 for (index
= 0; index
< MAX_LIST_COLUMNS
; index
++) {
110 lvC
.iSubItem
= index
;
111 lvC
.cx
= default_column_widths
[index
];
112 lvC
.fmt
= column_alignment
[index
];
113 LoadString(hInst
, IDS_LIST_COLUMN_FIRST
+ index
, szText
, sizeof(szText
)/sizeof(TCHAR
));
114 if (ListView_InsertColumn(hWndListView
, index
, &lvC
) == -1) {
115 // TODO: handle failure condition...
121 // OnGetDispInfo - processes the LVN_GETDISPINFO notification message.
122 static void OnGetDispInfo(NMLVDISPINFO
* plvdi
)
124 SYSTEMTIME SystemTime
;
125 FILETIME LocalFileTime
;
126 static TCHAR buffer
[200];
127 Entry
* entry
= (Entry
*)plvdi
->item
.lParam
;
130 plvdi
->item
.pszText
= NULL
;
131 switch (plvdi
->item
.iSubItem
) {
133 plvdi
->item
.pszText
= entry
->data
.cFileName
;
134 // item.cchTextMax = strlen(entry->data.cFileName);
135 // plvdi->item.pszText = rgPetInfo[plvdi->item.iItem].szKind;
138 if (entry
->bhfi_valid
) {
140 memset(&numFmt
, 0, sizeof(numFmt
));
141 numFmt
.NumDigits
= 0;
142 numFmt
.LeadingZero
= 0;
144 numFmt
.lpDecimalSep
= _T(".");
145 numFmt
.lpThousandSep
= _T(",");
146 numFmt
.NegativeOrder
= 0;
148 //entry->bhfi.nFileSizeLow;
149 //entry->bhfi.nFileSizeHigh;
150 //entry->bhfi.ftCreationTime
151 wsprintf(buffer
, _T("%u"), entry
->bhfi
.nFileSizeLow
);
152 if (GetNumberFormat(LOCALE_USER_DEFAULT
, 0, buffer
, &numFmt
,
153 buffer
+ sizeof(buffer
)/2, sizeof(buffer
)/2)) {
154 plvdi
->item
.pszText
= buffer
+ sizeof(buffer
)/2;
156 plvdi
->item
.pszText
= buffer
;
159 plvdi
->item
.pszText
= _T("unknown");
163 plvdi
->item
.pszText
= _T("error");
164 if (FileTimeToLocalFileTime(&entry
->bhfi
.ftLastWriteTime
, &LocalFileTime
)) {
165 if (FileTimeToSystemTime(&LocalFileTime
, &SystemTime
)) {
166 if (GetDateFormat(LOCALE_USER_DEFAULT
, DATE_SHORTDATE
, &SystemTime
, NULL
, buffer
, sizeof(buffer
))) {
167 plvdi
->item
.pszText
= buffer
;
173 plvdi
->item
.pszText
= _T("error");
174 if (FileTimeToLocalFileTime(&entry
->bhfi
.ftLastWriteTime
, &LocalFileTime
)) {
175 if (FileTimeToSystemTime(&LocalFileTime
, &SystemTime
)) {
176 // if (GetTimeFormat(UserDefaultLCID, 0, &SystemTime, NULL, buffer, sizeof(buffer))) {
177 if (GetTimeFormat(LOCALE_USER_DEFAULT
, 0, &SystemTime
, NULL
, buffer
, sizeof(buffer
))) {
178 plvdi
->item
.pszText
= buffer
;
184 plvdi
->item
.pszText
= _T("");
185 _tcscpy(buffer
, _T(" "));
187 if (entry
->bhfi
.dwFileAttributes
& FILE_ATTRIBUTE_ARCHIVE
) _tcscat(buffer
, _T("a")); else _tcscat(buffer
, _T(" "));
188 if (entry
->bhfi
.dwFileAttributes
& FILE_ATTRIBUTE_COMPRESSED
) _tcscat(buffer
, _T("c")); else _tcscat(buffer
, _T(" "));
189 if (entry
->bhfi
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) _tcscat(buffer
, _T("d")); else _tcscat(buffer
, _T(" "));
190 if (entry
->bhfi
.dwFileAttributes
& FILE_ATTRIBUTE_ENCRYPTED
) _tcscat(buffer
, _T("e")); else _tcscat(buffer
, _T(" "));
191 if (entry
->bhfi
.dwFileAttributes
& FILE_ATTRIBUTE_HIDDEN
) _tcscat(buffer
, _T("h")); else _tcscat(buffer
, _T(" "));
192 if (entry
->bhfi
.dwFileAttributes
& FILE_ATTRIBUTE_NORMAL
) _tcscat(buffer
, _T("n")); else _tcscat(buffer
, _T(" "));
193 if (entry
->bhfi
.dwFileAttributes
& FILE_ATTRIBUTE_OFFLINE
) _tcscat(buffer
, _T("o")); else _tcscat(buffer
, _T(" "));
194 if (entry
->bhfi
.dwFileAttributes
& FILE_ATTRIBUTE_READONLY
) _tcscat(buffer
, _T("r")); else _tcscat(buffer
, _T(" "));
195 if (entry
->bhfi
.dwFileAttributes
& FILE_ATTRIBUTE_REPARSE_POINT
) _tcscat(buffer
, _T("p")); else _tcscat(buffer
, _T(" "));
196 if (entry
->bhfi
.dwFileAttributes
& FILE_ATTRIBUTE_SPARSE_FILE
) _tcscat(buffer
, _T("f")); else _tcscat(buffer
, _T(" "));
197 if (entry
->bhfi
.dwFileAttributes
& FILE_ATTRIBUTE_SYSTEM
) _tcscat(buffer
, _T("s")); else _tcscat(buffer
, _T(" "));
198 if (entry
->bhfi
.dwFileAttributes
& FILE_ATTRIBUTE_TEMPORARY
) _tcscat(buffer
, _T("t")); else _tcscat(buffer
, _T(" "));
199 plvdi
->item
.pszText
= buffer
;
202 _tcscpy(buffer
, _T(" "));
203 plvdi
->item
.pszText
= buffer
;
208 FILE_ATTRIBUTE_ARCHIVE The file or directory is an archive file. Applications use this attribute to mark files for backup or removal.
209 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.
210 FILE_ATTRIBUTE_DIRECTORY The handle identifies a directory.
211 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.
212 FILE_ATTRIBUTE_HIDDEN The file or directory is hidden. It is not included in an ordinary directory listing.
213 FILE_ATTRIBUTE_NORMAL The file has no other attributes. This attribute is valid only if used alone.
214 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.
215 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.
216 FILE_ATTRIBUTE_REPARSE_POINT The file has an associated reparse point.
217 FILE_ATTRIBUTE_SPARSE_FILE The file is a sparse file.
218 FILE_ATTRIBUTE_SYSTEM The file or directory is part of the operating system or is used exclusively by the operating system.
219 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.
223 // OnEndLabelEdit - processes the LVN_ENDLABELEDIT
224 // notification message.
225 // Returns TRUE if the label is changed, or FALSE otherwise.
227 static BOOL
OnEndLabelEdit(NMLVDISPINFO
* plvdi
)
229 if (plvdi
->item
.iItem
== -1)
232 // Copy the new label text to the application-defined structure.
233 // lstrcpyn(rgPetInfo[plvdi->item.iItem].szKind, plvdi->item.pszText, 10);
236 // To make a more robust application you should send an EM_LIMITTEXT
237 // message to the edit control to prevent the user from entering too
238 // many characters in the field.
241 static int CALLBACK
CompareFunc(LPARAM lParam1
, LPARAM lParam2
, LPARAM lParamSort
)
243 Entry
* pItem1
= (Entry
*)lParam1
;
244 Entry
* pItem2
= (Entry
*)lParam2
;
246 if (pItem1
!= NULL
&& pItem2
!= NULL
) {
247 switch (lParamSort
) {
248 case ID_VIEW_SORT_BY_NAME
:
249 return _tcscmp(pItem1
->data
.cFileName
, pItem2
->data
.cFileName
);
251 case ID_VIEW_SORT_BY_TYPE
:
252 // if (pItem1->bhfi.nFileSizeLow != pItem2->bhfi.nFileSizeLow) {
253 // return (pItem1->bhfi.nFileSizeLow < pItem2->bhfi.nFileSizeLow) ? -1 : 1;
256 case ID_VIEW_SORT_BY_SIZE
:
257 if (pItem1
->bhfi
.nFileSizeLow
!= pItem2
->bhfi
.nFileSizeLow
) {
258 return (pItem1
->bhfi
.nFileSizeLow
< pItem2
->bhfi
.nFileSizeLow
) ? -1 : 1;
261 case ID_VIEW_SORT_BY_DATE
:
262 return CompareFileTime(&pItem1
->bhfi
.ftLastWriteTime
, &pItem2
->bhfi
.ftLastWriteTime
);
269 static void CmdSortItems(HWND hWnd
, UINT cmd
)
271 CheckMenuItem(Globals
.hMenuView
, ID_VIEW_SORT_BY_NAME
, MF_BYCOMMAND
);
272 CheckMenuItem(Globals
.hMenuView
, ID_VIEW_SORT_BY_TYPE
, MF_BYCOMMAND
);
273 CheckMenuItem(Globals
.hMenuView
, ID_VIEW_SORT_BY_SIZE
, MF_BYCOMMAND
);
274 CheckMenuItem(Globals
.hMenuView
, ID_VIEW_SORT_BY_DATE
, MF_BYCOMMAND
);
275 ListView_SortItems(hWnd
, &CompareFunc
, cmd
);
276 CheckMenuItem(Globals
.hMenuView
, cmd
, MF_BYCOMMAND
| MF_CHECKED
);
279 void RefreshList(HWND hWnd
, Entry
* entry
)
282 ListView_DeleteAllItems(hWnd
);
284 //TRACE("RefreshList(...) entry name: %s\n", entry->data.cFileName);
285 InsertListEntries(hWnd
, entry
, -1);
290 static BOOL
_CmdWndProc(HWND hWnd
, UINT message
, WPARAM wParam
, LPARAM lParam
)
292 switch (LOWORD(wParam
)) {
296 item
.mask
= LVIF_PARAM
;
297 // UINT selected_count = ListView_GetSelectedCount(hWnd);
298 item
.iItem
= ListView_GetNextItem(hWnd
, -1, LVNI_SELECTED
);
299 if (item
.iItem
!= -1) {
300 if (ListView_GetItem(hWnd
, &item
)) {
301 Entry
* entry
= (Entry
*)item
.lParam
;
302 OpenTarget(hWnd
, entry
->data
.cFileName
);
311 case ID_FILE_COPY_CLIPBOARD
:
315 case ID_FILE_PROPERTIES
:
318 item
.mask
= LVIF_PARAM
;
319 item
.iItem
= ListView_GetNextItem(hWnd
, -1, LVNI_SELECTED
);
320 if (item
.iItem
!= -1) {
321 if (ListView_GetItem(hWnd
, &item
)) {
322 Entry
* entry
= (Entry
*)item
.lParam
;
323 struct PropertiesDialog dlg
= { _T("empty"), 0, entry
};
324 if (DialogBoxParam(Globals
.hInstance
, MAKEINTRESOURCE(IDD_DIALOG_PROPERTIES
), hWnd
, PropertiesDlgProc
, (LPARAM
)&dlg
) == IDOK
) {
331 case ID_FILE_COMPRESS
:
332 case ID_FILE_UNCOMPRESS
:
338 case ID_FILE_ASSOCIATE
:
339 case ID_FILE_CREATE_DIRECTORY
:
341 case ID_VIEW_SORT_BY_NAME
:
342 case ID_VIEW_SORT_BY_TYPE
:
343 case ID_VIEW_SORT_BY_SIZE
:
344 case ID_VIEW_SORT_BY_DATE
:
345 CmdSortItems(hWnd
, LOWORD(wParam
));
347 case ID_WINDOW_REFRESH
:
348 RefreshList(hWnd
, NULL
/*entry*/);
356 static LRESULT CALLBACK
ListWndProc(HWND hWnd
, UINT message
, WPARAM wParam
, LPARAM lParam
)
358 ChildWnd
* child
= (ChildWnd
*)GetWindowLong(GetParent(hWnd
), GWL_USERDATA
);
359 Pane
* pane
= (Pane
*)GetWindowLong(hWnd
, GWL_USERDATA
);
364 if (_CmdWndProc(hWnd
, message
, wParam
, lParam
)) {
368 case WM_DISPATCH_COMMAND
:
369 return _CmdWndProc(hWnd
, message
, wParam
, lParam
);
371 switch (((LPNMHDR
)lParam
)->code
) {
372 case LVN_GETDISPINFO
:
373 OnGetDispInfo((NMLVDISPINFO
*)lParam
);
377 NMITEMACTIVATE
* nmitem
= (LPNMITEMACTIVATE
)lParam
;
380 if (nmitem
->hdr
.hwndFrom
!= hWnd
) break;
381 // if (nmitem->hdr.idFrom != IDW_LISTVIEW) break;
382 // if (nmitem->hdr.code != ???) break;
384 switch (nmitem
->uKeyFlags
) {
385 case LVKF_ALT
: // The ALT key is pressed.
386 // properties dialog box ?
388 case LVKF_CONTROL
: // The CTRL key is pressed.
389 // run dialog box for providing parameters...
391 case LVKF_SHIFT
: // The SHIFT key is pressed.
395 info
.pt
.x
= nmitem
->ptAction
.x
;
396 info
.pt
.y
= nmitem
->ptAction
.y
;
397 if (ListView_HitTest(hWnd
, &info
) != -1) {
399 item
.mask
= LVIF_PARAM
;
400 item
.iItem
= info
.iItem
;
401 if (ListView_GetItem(hWnd
, &item
)) {
402 Entry
* entry
= (Entry
*)item
.lParam
;
403 OpenTarget(hWnd
, entry
->data
.cFileName
);
408 case LVN_ENDLABELEDIT
:
409 return OnEndLabelEdit((NMLVDISPINFO
*)lParam
);
414 child
->nFocusPanel
= pane
==&child
->right
? 1: 0;
415 //ListView_SetSelectionMark(hWnd, 0);
416 //TODO: check menu items
420 if (wParam
== VK_TAB
) {
421 //TODO: SetFocus(Globals.hDriveBar)
422 SetFocus(child
->nFocusPanel
? child
->hTreeWnd
: child
->hListWnd
);
426 return CallWindowProc(g_orgListWndProc
, hWnd
, message
, wParam
, lParam
);
429 void CreateListWnd(HWND parent
, Pane
* pane
, int id
, LPTSTR lpszPathName
)
431 RECT rcClient
; // dimensions of client area
432 Entry
* entry
= pane
->root
;
434 // pane->treePane = 0;
436 GetClientRect(parent
, &rcClient
);
437 pane
->hWnd
= CreateWindowEx(0, WC_LISTVIEW
, _T("List View"),
438 WS_VISIBLE
| WS_CHILD
| WS_BORDER
| LVS_REPORT
/* | LVS_NOCOLUMNHEADER*/,
439 0, 0, rcClient
.right
, rcClient
.bottom
,
440 parent
, (HMENU
)id
, hInst
, NULL
);
441 // Initialize the image list, and add items to the control.
443 if (!InitListViewImageLists(pane->hWnd) ||
444 !InitListViewItems(pane->hWnd, lpszPathName)) {
445 DestroyWindow(pane->hWnd);
449 ListView_SetExtendedListViewStyle(pane
->hWnd
, LVS_EX_FULLROWSELECT
);
450 CreateListColumns(pane
->hWnd
);
452 SetWindowLong(pane
->hWnd
, GWL_USERDATA
, (LPARAM
)pane
);
453 g_orgListWndProc
= SubclassWindow(pane
->hWnd
, ListWndProc
);
454 SendMessage(pane
->hWnd
, WM_SETFONT
, (WPARAM
)Globals
.hFont
, FALSE
);
456 // insert entries into listbox
458 InsertListEntries(pane
->hWnd
, entry
, -1);
462 HWND
CreateListView(HWND hwndParent
, ChildWnd
* pChildWnd
, int id
)
467 // Get the dimensions of the parent window's client area, and create the list view control.
468 GetClientRect(hwndParent
, &rcClient
);
469 hwndLV
= CreateWindowEx(WS_EX_CLIENTEDGE
, WC_LISTVIEW
, _T("List View"),
470 WS_VISIBLE
| WS_CHILD
| LVS_REPORT
,
471 0, 0, rcClient
.right
, rcClient
.bottom
,
472 hwndParent
, (HMENU
)id
, hInst
, NULL
);
473 ListView_SetExtendedListViewStyle(hwndLV
, LVS_EX_FULLROWSELECT
);
475 // Initialize the image list, and add items to the control.
477 if (!InitListViewImageLists(hwndLV) ||
478 !InitListViewItems(hwndLV, szName)) {
479 DestroyWindow(hwndLV);
483 CreateListColumns(hwndLV
);
484 g_orgListWndProc
= SubclassWindow(hwndLV
, ListWndProc
);