remove empty dir
[reactos.git] / rosapps / winfile / listview.c
1 /*
2 * ReactOS winfile
3 *
4 * listview.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 #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
24 #include <windows.h>
25 #include <commctrl.h>
26 #include <stdlib.h>
27 #include <malloc.h>
28 #include <memory.h>
29 #include <tchar.h>
30 #include <process.h>
31 #include <stdio.h>
32
33 #include <windowsx.h>
34 #include "main.h"
35 #include "listview.h"
36 #include "dialogs.h"
37 #include "run.h"
38 #include "trace.h"
39
40
41 ////////////////////////////////////////////////////////////////////////////////
42 // Global and Local Variables:
43 //
44
45 static WNDPROC g_orgListWndProc;
46
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 };
50
51
52 ////////////////////////////////////////////////////////////////////////////////
53 // Local module support methods
54 //
55
56 static void AddEntryToList(HWND hwndLV, int idx, Entry* entry)
57 {
58 LVITEM item;
59
60 item.mask = LVIF_TEXT | LVIF_PARAM;
61 item.iItem = 0;//idx;
62 item.iSubItem = 0;
63 item.state = 0;
64 item.stateMask = 0;
65 // item.pszText = entry->data.cFileName;
66 // item.cchTextMax = strlen(entry->data.cFileName);
67 item.pszText = LPSTR_TEXTCALLBACK;
68 item.cchTextMax = 0;
69 item.iImage = 0;
70 // item.iImage = I_IMAGECALLBACK;
71 item.lParam = (LPARAM)entry;
72 #if (_WIN32_IE >= 0x0300)
73 item.iIndent = 0;
74 #endif
75 ListView_InsertItem(hwndLV, &item);
76 }
77
78 // insert listctrl entries after index idx
79 static void InsertListEntries(HWND hWnd, Entry* entry, int idx)
80 {
81 ShowWindow(hWnd, SW_HIDE);
82
83 if (idx == -1) {
84 }
85 idx = 0;
86
87 for (; entry; entry = entry->next) {
88 #ifndef _LEFT_FILES
89 if (entry->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
90 continue;
91 #endif
92 AddEntryToList(hWnd, idx, entry);
93 ++idx;
94 }
95 ShowWindow(hWnd, SW_SHOW);
96 }
97
98 static void CreateListColumns(HWND hWndListView)
99 {
100 TCHAR szText[50];
101 int index;
102 LV_COLUMN lvC;
103
104 // Create columns.
105 lvC.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
106 lvC.pszText = szText;
107
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...
116 break;
117 }
118 }
119 }
120
121 // OnGetDispInfo - processes the LVN_GETDISPINFO notification message.
122 static void OnGetDispInfo(NMLVDISPINFO* plvdi)
123 {
124 SYSTEMTIME SystemTime;
125 FILETIME LocalFileTime;
126 static TCHAR buffer[200];
127 Entry* entry = (Entry*)plvdi->item.lParam;
128 ASSERT(entry);
129
130 plvdi->item.pszText = NULL;
131 switch (plvdi->item.iSubItem) {
132 case 0:
133 plvdi->item.pszText = entry->data.cFileName;
134 // item.cchTextMax = strlen(entry->data.cFileName);
135 // plvdi->item.pszText = rgPetInfo[plvdi->item.iItem].szKind;
136 break;
137 case 1:
138 if (entry->bhfi_valid) {
139 NUMBERFMT numFmt;
140 memset(&numFmt, 0, sizeof(numFmt));
141 numFmt.NumDigits = 0;
142 numFmt.LeadingZero = 0;
143 numFmt.Grouping = 3;
144 numFmt.lpDecimalSep = _T(".");
145 numFmt.lpThousandSep = _T(",");
146 numFmt.NegativeOrder = 0;
147
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;
155 } else {
156 plvdi->item.pszText = buffer;
157 }
158 } else {
159 plvdi->item.pszText = _T("unknown");
160 }
161 break;
162 case 2:
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;
168 }
169 }
170 }
171 break;
172 case 3:
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;
179 }
180 }
181 }
182 break;
183 case 4:
184 plvdi->item.pszText = _T("");
185 _tcscpy(buffer, _T(" "));
186
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;
200 break;
201 default:
202 _tcscpy(buffer, _T(" "));
203 plvdi->item.pszText = buffer;
204 break;
205 }
206 }
207 /*
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.
220 */
221
222
223 // OnEndLabelEdit - processes the LVN_ENDLABELEDIT
224 // notification message.
225 // Returns TRUE if the label is changed, or FALSE otherwise.
226
227 static BOOL OnEndLabelEdit(NMLVDISPINFO* plvdi)
228 {
229 if (plvdi->item.iItem == -1)
230 return FALSE;
231
232 // Copy the new label text to the application-defined structure.
233 // lstrcpyn(rgPetInfo[plvdi->item.iItem].szKind, plvdi->item.pszText, 10);
234
235 return TRUE;
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.
239 }
240
241 static int CALLBACK CompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
242 {
243 Entry* pItem1 = (Entry*)lParam1;
244 Entry* pItem2 = (Entry*)lParam2;
245
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);
250 break;
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;
254 // }
255 break;
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;
259 }
260 break;
261 case ID_VIEW_SORT_BY_DATE:
262 return CompareFileTime(&pItem1->bhfi.ftLastWriteTime, &pItem2->bhfi.ftLastWriteTime);
263 break;
264 }
265 }
266 return 0;
267 }
268
269 static void CmdSortItems(HWND hWnd, UINT cmd)
270 {
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);
277 }
278
279 void RefreshList(HWND hWnd, Entry* entry)
280 {
281 if (hWnd != NULL) {
282 ListView_DeleteAllItems(hWnd);
283 if (entry != NULL) {
284 //TRACE("RefreshList(...) entry name: %s\n", entry->data.cFileName);
285 InsertListEntries(hWnd, entry, -1);
286 }
287 }
288 }
289
290 static BOOL _CmdWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
291 {
292 switch (LOWORD(wParam)) {
293 case ID_FILE_OPEN:
294 {
295 LVITEM item;
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);
303 }
304 }
305 }
306 break;
307 case ID_FILE_MOVE:
308 //OnFileMove(hWnd);
309 break;
310 case ID_FILE_COPY:
311 case ID_FILE_COPY_CLIPBOARD:
312 case ID_FILE_DELETE:
313 case ID_FILE_RENAME:
314 break;
315 case ID_FILE_PROPERTIES:
316 {
317 LVITEM item;
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) {
325 }
326 }
327 }
328 }
329 break;
330
331 case ID_FILE_COMPRESS:
332 case ID_FILE_UNCOMPRESS:
333 break;
334 case ID_FILE_RUN:
335 OnFileRun();
336 break;
337 case ID_FILE_PRINT:
338 case ID_FILE_ASSOCIATE:
339 case ID_FILE_CREATE_DIRECTORY:
340 break;
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));
346 break;
347 case ID_WINDOW_REFRESH:
348 RefreshList(hWnd, NULL/*entry*/);
349 break;
350 default:
351 return FALSE;
352 }
353 return TRUE;
354 }
355
356 static LRESULT CALLBACK ListWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
357 {
358 ChildWnd* child = (ChildWnd*)GetWindowLong(GetParent(hWnd), GWL_USERDATA);
359 Pane* pane = (Pane*)GetWindowLong(hWnd, GWL_USERDATA);
360 ASSERT(child);
361
362 switch (message) {
363 case WM_COMMAND:
364 if (_CmdWndProc(hWnd, message, wParam, lParam)) {
365 return 0;
366 }
367 break;
368 case WM_DISPATCH_COMMAND:
369 return _CmdWndProc(hWnd, message, wParam, lParam);
370 case WM_NOTIFY:
371 switch (((LPNMHDR)lParam)->code) {
372 case LVN_GETDISPINFO:
373 OnGetDispInfo((NMLVDISPINFO*)lParam);
374 return 0;
375 case NM_DBLCLK:
376 {
377 NMITEMACTIVATE* nmitem = (LPNMITEMACTIVATE)lParam;
378 LVHITTESTINFO info;
379
380 if (nmitem->hdr.hwndFrom != hWnd) break;
381 // if (nmitem->hdr.idFrom != IDW_LISTVIEW) break;
382 // if (nmitem->hdr.code != ???) break;
383 #ifdef _MSC_VER
384 switch (nmitem->uKeyFlags) {
385 case LVKF_ALT: // The ALT key is pressed.
386 // properties dialog box ?
387 break;
388 case LVKF_CONTROL: // The CTRL key is pressed.
389 // run dialog box for providing parameters...
390 break;
391 case LVKF_SHIFT: // The SHIFT key is pressed.
392 break;
393 }
394 #endif
395 info.pt.x = nmitem->ptAction.x;
396 info.pt.y = nmitem->ptAction.y;
397 if (ListView_HitTest(hWnd, &info) != -1) {
398 LVITEM item;
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);
404 }
405 }
406 }
407 return 0;
408 case LVN_ENDLABELEDIT:
409 return OnEndLabelEdit((NMLVDISPINFO*)lParam);
410 }
411 break;
412
413 case WM_SETFOCUS:
414 child->nFocusPanel = pane==&child->right? 1: 0;
415 //ListView_SetSelectionMark(hWnd, 0);
416 //TODO: check menu items
417 break;
418
419 case WM_KEYDOWN:
420 if (wParam == VK_TAB) {
421 //TODO: SetFocus(Globals.hDriveBar)
422 SetFocus(child->nFocusPanel ? child->hTreeWnd: child->hListWnd);
423 }
424 break;
425 }
426 return CallWindowProc(g_orgListWndProc, hWnd, message, wParam, lParam);
427 }
428
429 void CreateListWnd(HWND parent, Pane* pane, int id, LPTSTR lpszPathName)
430 {
431 RECT rcClient; // dimensions of client area
432 Entry* entry = pane->root;
433
434 // pane->treePane = 0;
435
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.
442 /*
443 if (!InitListViewImageLists(pane->hWnd) ||
444 !InitListViewItems(pane->hWnd, lpszPathName)) {
445 DestroyWindow(pane->hWnd);
446 return FALSE;
447 }
448 */
449 ListView_SetExtendedListViewStyle(pane->hWnd, LVS_EX_FULLROWSELECT);
450 CreateListColumns(pane->hWnd);
451
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);
455
456 // insert entries into listbox
457 if (entry) {
458 InsertListEntries(pane->hWnd, entry, -1);
459 }
460 }
461
462 HWND CreateListView(HWND hwndParent, ChildWnd* pChildWnd, int id)
463 {
464 RECT rcClient;
465 HWND hwndLV;
466
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);
474
475 // Initialize the image list, and add items to the control.
476 /*
477 if (!InitListViewImageLists(hwndLV) ||
478 !InitListViewItems(hwndLV, szName)) {
479 DestroyWindow(hwndLV);
480 return FALSE;
481 }
482 */
483 CreateListColumns(hwndLV);
484 g_orgListWndProc = SubclassWindow(hwndLV, ListWndProc);
485 return hwndLV;
486 }
487