:pserver:cvsanon@mok.lvcm.com:/CVS/ReactOS reactos
[reactos.git] / lib / shell32 / control / listview.c
1 /*
2  *  ReactOS shell32 - Control Panel ListCtrl implementation
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 <cpl.h>
26 #include <commctrl.h>
27 #include <stdlib.h>
28 #include <memory.h>
29 #include <tchar.h>
30 #include <process.h>
31 #include <stdio.h>
32     
33 #include <windowsx.h>
34 #include "control.h"
35 #include "listview.h"
36
37 #include "assert.h"
38 #include "trace.h"
39
40
41 static int _GetSystemDirectory(LPTSTR buffer, int buflen)
42 {
43 #if 0
44     return GetSystemDirectory(buffer, buflen);
45 #else
46     return GetCurrentDirectory(buflen, buffer);
47 //    if (lstrcpyn(buffer, szTestDirName, buflen - 1)) {
48 //        return lstrlen(buffer);
49 //    }
50 //    return 0;
51 #endif
52 }
53
54
55 ////////////////////////////////////////////////////////////////////////////////
56 // Global and Local Variables:
57 //
58
59 #define MAX_LIST_COLUMNS (IDS_LIST_COLUMN_LAST - IDS_LIST_COLUMN_FIRST + 1)
60 static int default_column_widths[MAX_LIST_COLUMNS] = { 250, 500 };
61 static int column_alignment[MAX_LIST_COLUMNS] = { LVCFMT_LEFT, LVCFMT_LEFT };
62
63 CPlApplet* pListHead; // holds pointer to linked list of cpl modules CPlApplet*
64 //static CPlApplet* pListHead; // holds pointer to linked list of cpl modules CPlApplet*
65
66
67 ////////////////////////////////////////////////////////////////////////////////
68 // Local module support methods
69 //
70
71 static void AddAppletsToListView(HWND hwndLV, CPlApplet* pApplet)
72 {
73     LVITEM item;
74     UINT count;
75
76     for (count = 0; count < pApplet->count; count++) {
77         int index = 0;
78         NEWCPLINFO* pCPLInfo = &pApplet->info[count];
79
80         CPlEntry*  pCPlEntry;
81         if (!(pCPlEntry = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pCPlEntry))))
82             return;
83     
84         pCPlEntry->hWnd = hwndLV;
85         pCPlEntry->nSubProg = count;
86         pCPlEntry->pCPlApplet = pApplet;
87
88         if (pCPLInfo->hIcon) { // add the icon to an image list
89             HIMAGELIST hImageList;
90             hImageList = ListView_GetImageList(hwndLV, LVSIL_NORMAL);
91             index = ImageList_AddIcon(hImageList, pCPLInfo->hIcon); 
92             hImageList = ListView_GetImageList(hwndLV, LVSIL_SMALL);
93             ImageList_AddIcon(hImageList, pCPLInfo->hIcon);
94             DestroyIcon(pCPLInfo->hIcon); 
95         }
96         item.mask = LVIF_TEXT | LVIF_STATE | LVIF_IMAGE | LVIF_PARAM; 
97         item.iItem = 0;//idx; 
98         item.iSubItem = 0; 
99         item.state = 0; 
100         item.stateMask = 0; 
101 //        item.pszText = LPSTR_TEXTCALLBACK; 
102 //        item.cchTextMax = 50;
103         item.pszText = pCPLInfo->szName;
104         item.cchTextMax = _tcslen(item.pszText);
105         item.iImage = index; 
106         item.lParam = (LPARAM)pCPlEntry;
107 #if (_WIN32_IE >= 0x0300)
108         item.iIndent = 0;
109 #endif
110         index = ListView_InsertItem(hwndLV, &item);
111         if (index != -1 && pCPLInfo->szInfo != NULL) {
112             ListView_SetItemText(hwndLV, index, 1, pCPLInfo->szInfo);
113         }
114     }
115 }
116
117 static void CreateListColumns(HWND hwndLV)
118 {
119     TCHAR szText[50];
120     int index;
121     LV_COLUMN lvC;
122  
123     // Create columns.
124     lvC.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
125     lvC.pszText = szText;
126
127     // Load the column labels from the resource file.
128     for (index = 0; index < MAX_LIST_COLUMNS; index++) {
129         lvC.iSubItem = index;
130         lvC.cx = default_column_widths[index];
131         lvC.fmt = column_alignment[index];
132         LoadString(hInst, IDS_LIST_COLUMN_FIRST + index, szText, 50);
133         if (ListView_InsertColumn(hwndLV, index, &lvC) == -1) {
134             // TODO: handle failure condition...
135             break;
136         }
137     }
138 }
139
140 // InitListViewImageLists - creates image lists for a list view control.
141 // This function only creates image lists. It does not insert the
142 // items into the control, which is necessary for the control to be 
143 // visible.   
144 // Returns TRUE if successful, or FALSE otherwise. 
145 // hwndLV - handle to the list view control. 
146 static BOOL InitListViewImageLists(HWND hwndLV) 
147
148 //    HICON hiconItem;     // icon for list view items 
149     HIMAGELIST hLarge;   // image list for icon view 
150     HIMAGELIST hSmall;   // image list for other views 
151  
152     // Create the full-sized icon image lists. 
153     hLarge = ImageList_Create(GetSystemMetrics(SM_CXICON), 
154         GetSystemMetrics(SM_CYICON), ILC_MASK, 1, 20); 
155     hSmall = ImageList_Create(GetSystemMetrics(SM_CXSMICON), 
156         GetSystemMetrics(SM_CYSMICON), ILC_MASK, 1, 20); 
157  
158     // Add an icon to each image list.  
159 //    hiconItem = LoadIcon(hInst, MAKEINTRESOURCE(IDI_ITEM)); 
160 //    hiconItem = LoadIcon(hInst, MAKEINTRESOURCE(IDI_CONTROL)); 
161 //    ImageList_AddIcon(hLarge, hiconItem); 
162 //    ImageList_AddIcon(hSmall, hiconItem); 
163 //    DestroyIcon(hiconItem); 
164         
165     /*********************************************************
166     Usually you have multiple icons; therefore, the previous
167     four lines of code can be inside a loop. The following code 
168     shows such a loop. The icons are defined in the application's
169     header file as resources, which are numbered consecutively
170     starting with IDS_FIRSTICON. The number of icons is
171     defined in the header file as C_ICONS.
172         
173     for(index = 0; index < C_ICONS; index++) {
174         hIconItem = LoadIcon (hInst, MAKEINTRESOURCE (IDS_FIRSTICON + index));
175         ImageList_AddIcon(hSmall, hIconItem);
176         ImageList_AddIcon(hLarge, hIconItem);
177         Destroy(hIconItem);
178     }
179     *********************************************************/
180  
181     // Assign the image lists to the list view control. 
182     ListView_SetImageList(hwndLV, hLarge, LVSIL_NORMAL); 
183     ListView_SetImageList(hwndLV, hSmall, LVSIL_SMALL); 
184     return TRUE; 
185
186
187 typedef LONG (WINAPI *CPlApplet_Ptr)(HWND, UINT, LONG, LONG);
188
189 static void AddEntryToList(HWND hwndLV, LPTSTR szName, LPTSTR szInfo, CPlEntry* pCPlEntry)
190
191     LVITEM item;
192     int index;
193
194     assert(pCPlEntry);
195     memset(&item, 0, sizeof(LVITEM));
196     item.mask = LVIF_TEXT | LVIF_STATE | LVIF_IMAGE | LVIF_PARAM; 
197     if (szName != NULL) {
198         item.pszText = szName;
199         item.cchTextMax = _tcslen(item.pszText);
200         item.iImage = pCPlEntry->nIconIndex; 
201     } else {
202         item.pszText = LPSTR_TEXTCALLBACK;
203         item.cchTextMax = MAX_CPL_NAME;
204         item.iImage = I_IMAGECALLBACK;
205     }
206     item.lParam = (LPARAM)pCPlEntry;
207 #if (_WIN32_IE >= 0x0300)
208     item.iIndent = 0;
209 #endif
210     index = ListView_InsertItem(hwndLV, &item);
211     if (index != -1) {
212         if (szInfo != NULL) {
213             ListView_SetItemText(hwndLV, index, 1, szInfo);
214         } else {
215             ListView_SetItemText(hwndLV, index, 1, LPSTR_TEXTCALLBACK);
216         }
217     }
218 }
219
220 #if 0
221 /*
222 static CPlApplet* Control_LoadApplet(HWND hwndLV, LPTSTR buffer, CPlApplet** pListHead)
223 {
224     HMODULE hCpl;
225     hCpl = LoadLibrary(buffer);
226     if (hCpl) {
227         CPlApplet_Ptr pCPlApplet;
228         pCPlApplet = (CPlApplet_Ptr)(FARPROC)GetProcAddress(hCpl, "CPlApplet");
229         if (pCPlApplet) {
230             if (pCPlApplet(hwndLV, CPL_INIT, 0, 0)) {
231                 int nSubProgs = pCPlApplet(hwndLV, CPL_GETCOUNT, 0, 0);
232                 if (nSubProgs == 0) {
233                     TRACE(_T("No subprogram in applet\n"));
234                 }
235
236                 {
237                 CPlApplet* applet;
238                 if (!(applet = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*applet)))) {
239                     return applet;
240                 }
241                 applet->next = *pListHead;
242                 *pListHead = applet;
243                 }
244                 strncpy(applet->filename, buffer, MAX_PATH);
245                 while (nSubProgs && nSubProgs--) {
246                     CPLINFO cplInfo;
247                     memset(&cplInfo, 0, sizeof(CPLINFO));
248                     pCPlApplet(hwndLV, CPL_INQUIRE, nSubProgs, (LPARAM)&cplInfo);
249                     if (cplInfo.idName == CPL_DYNAMIC_RES) {
250 #if UNICODE
251                         NEWCPLINFO cplNewInfo;
252                         memset(&cplNewInfo, 0, sizeof(NEWCPLINFO));
253                         cplNewInfo.dwSize = sizeof(NEWCPLINFO);
254                         pCPlApplet(hwndLV, CPL_NEWINQUIRE, nSubProgs, (LPARAM)&cplNewInfo);
255                         cplNewInfo.lData = hCpl;
256                         AddEntryToList(hwndLV, &cplNewInfo);
257 #endif
258                     } else {
259                         int index = 0;
260                         NEWCPLINFO cplNewInfo;
261                         memset(&cplNewInfo, 0, sizeof(NEWCPLINFO));
262                         cplNewInfo.dwSize = sizeof(NEWCPLINFO);
263                         if (LoadString(hCpl, cplInfo.idName, cplNewInfo.szName, sizeof(cplNewInfo.szName)/sizeof(TCHAR))) {
264                         }
265                         if (LoadString(hCpl, cplInfo.idInfo, cplNewInfo.szInfo, sizeof(cplNewInfo.szInfo)/sizeof(TCHAR))) {
266                         }
267                         cplNewInfo.hIcon = LoadImage(hCpl, (LPCTSTR)cplInfo.idIcon, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);
268                         cplNewInfo.lData = (LONG)hCpl;
269                         AddEntryToList(hwndLV, &cplNewInfo);
270                     }
271                 }
272                 return TRUE;
273             } else {
274                 TRACE(_T("Init of applet has failed\n"));
275             }
276         } else {
277             TRACE(_T("Not a valid control panel applet %s\n"), buffer);
278         }
279         FreeLibrary(hCpl);
280     } else {
281         TRACE(_T("Cannot load control panel applet %s\n"), buffer);
282     }
283     return FALSE;
284 }
285  */
286 #endif
287
288 static void LoadApplet(HWND hwndLV, LPTSTR buffer, CPlApplet** pListHead)
289 {
290     HMODULE hModule;
291     hModule = LoadLibrary(buffer);
292     if (hModule) {
293         CPlApplet_Ptr pCPlApplet;
294         pCPlApplet = (CPlApplet_Ptr)(FARPROC)GetProcAddress(hModule, "CPlApplet");
295         if (pCPlApplet) {
296             if (pCPlApplet(hwndLV, CPL_INIT, 0, 0)) {
297                 CPlApplet* applet;
298                 int nSubProgs = pCPlApplet(hwndLV, CPL_GETCOUNT, 0, 0);
299                 if (nSubProgs == 0) {
300                     TRACE(_T("No subprogram in applet\n"));
301                 }
302                 if (!(applet = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*applet)))) {
303                     goto loadapplet_error;
304                 }
305                 applet->next = *pListHead;
306                 *pListHead = applet;
307                 _tcsncpy(applet->filename, buffer, MAX_PATH);
308                 while (nSubProgs && nSubProgs--) {
309                     NEWCPLINFO cplNewInfo;
310                     memset(&cplNewInfo, 0, sizeof(NEWCPLINFO));
311                     cplNewInfo.dwSize = sizeof(NEWCPLINFO);
312                     pCPlApplet(hwndLV, CPL_NEWINQUIRE, nSubProgs, (LPARAM)&cplNewInfo);
313                     if (cplNewInfo.hIcon == 0) {
314                         CPLINFO cplInfo;
315                         memset(&cplInfo, 0, sizeof(CPLINFO));
316                         pCPlApplet(hwndLV, CPL_INQUIRE, nSubProgs, (LPARAM)&cplInfo);
317                         if (cplInfo.idIcon == 0 || cplInfo.idName == 0) {
318                             TRACE(_T("Couldn't get info from sp %u\n"), nSubProgs);
319                         } else {
320                             TCHAR szName[MAX_CPL_NAME];
321                             TCHAR szInfo[MAX_CPL_INFO];
322                             HICON hIcon = NULL;
323                             CPlEntry* pCPlEntry;
324                             if (!(pCPlEntry = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CPlEntry)))) {
325                                 goto loadapplet_error;
326                             }
327                             pCPlEntry->nSubProg = nSubProgs;
328                             pCPlEntry->lData = cplInfo.lData;
329                             pCPlEntry->pCPlApplet = applet;
330                             hIcon = LoadImage(hModule, (LPCTSTR)cplInfo.idIcon, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);
331                             LoadString(hModule, cplInfo.idName, szName, MAX_CPL_NAME);
332                             LoadString(hModule, cplInfo.idInfo, szInfo, MAX_CPL_INFO);
333                             if (hIcon) { // add the icon to an image list
334                                 HIMAGELIST hImageList;
335                                 hImageList = ListView_GetImageList(hwndLV, LVSIL_NORMAL);
336                                 pCPlEntry->nIconIndex = ImageList_AddIcon(hImageList, hIcon); 
337                                 hImageList = ListView_GetImageList(hwndLV, LVSIL_SMALL);
338                                 ImageList_AddIcon(hImageList, hIcon);
339                                 DestroyIcon(hIcon); 
340                             }
341                             AddEntryToList(hwndLV, szName, szInfo, pCPlEntry);
342                         }
343                     } else {
344                         HIMAGELIST hImageList;
345                         CPlEntry* pCPlEntry;
346                         TRACE(_T("Using CPL_NEWINQUIRE data\n"));
347                         if (!(pCPlEntry = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CPlEntry)))) {
348                             goto loadapplet_error;
349                         }
350                         applet->hModule = LoadLibrary(buffer);
351                         pCPlEntry->nSubProg = nSubProgs;
352                         pCPlEntry->lData = cplNewInfo.lData;
353                         pCPlEntry->pCPlApplet = applet;
354                         hImageList = ListView_GetImageList(hwndLV, LVSIL_NORMAL);
355                         pCPlEntry->nIconIndex = ImageList_AddIcon(hImageList, cplNewInfo.hIcon); 
356                         hImageList = ListView_GetImageList(hwndLV, LVSIL_SMALL);
357                         ImageList_AddIcon(hImageList, cplNewInfo.hIcon);
358                         DestroyIcon(cplNewInfo.hIcon); 
359                         AddEntryToList(hwndLV, NULL, NULL, pCPlEntry);
360                     }
361                 }
362             } else {
363                 TRACE(_T("Init of applet has failed\n"));
364             }
365         } else {
366             TRACE(_T("Not a valid control panel applet %s\n"), buffer);
367         }
368 loadapplet_error:
369         FreeLibrary(hModule);
370     } else {
371         TRACE(_T("Cannot load control panel applet %s\n"), buffer);
372     }
373 }
374
375
376 static BOOL InitListViewItems(HWND hwndLV, LPTSTR szPath)
377 {
378     WIN32_FIND_DATA data;
379     HANDLE hFind;
380     TCHAR buffer[MAX_PATH+10], *p;
381     UINT length;
382
383     length = _GetSystemDirectory(buffer, sizeof(buffer)/sizeof(TCHAR));
384     p = &buffer[length];
385         lstrcpy(p, _T("\\*.cpl"));
386     memset(&data, 0, sizeof(WIN32_FIND_DATA));
387         hFind = FindFirstFile(buffer, &data);
388         if (hFind != INVALID_HANDLE_VALUE) {
389                 do {
390                         if (!(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
391 #if 0
392                 CPlApplet* pApplet;
393                             lstrcpy(p+1, data.cFileName);
394                 pApplet = Control_LoadApplet(hwndLV, buffer, &pListHead);
395                 if (pApplet != NULL) {
396                     AddAppletsToListView(hwndLV, pApplet);
397                 }
398 #else
399                             lstrcpy(p+1, data.cFileName);
400                 LoadApplet(hwndLV, buffer, &pListHead);
401 #endif
402                         }
403                 } while (FindNextFile(hFind, &data));
404                 FindClose(hFind);
405     }
406     return TRUE;
407 }
408
409 // OnGetDispInfo - processes the LVN_GETDISPINFO notification message. 
410 static void OnGetDispInfo(HWND hWnd, NMLVDISPINFO* plvdi)
411 {
412     CPlEntry* pCPlEntry = (CPlEntry*)plvdi->item.lParam;
413
414     plvdi->item.pszText = NULL;
415     plvdi->item.cchTextMax = 0; 
416     if (pCPlEntry != NULL) {
417         CPlApplet* pApplet = pCPlEntry->pCPlApplet;
418         assert(pApplet);
419         if (pApplet->hModule) {
420             CPlApplet_Ptr pCPlApplet;
421             pCPlApplet = (CPlApplet_Ptr)(FARPROC)GetProcAddress(pApplet->hModule, "CPlApplet");
422             if (pCPlApplet)     {
423                 static NEWCPLINFO cplNewInfo;
424                 memset(&cplNewInfo, 0, sizeof(NEWCPLINFO));
425                 cplNewInfo.dwSize = sizeof(NEWCPLINFO);
426                 pCPlApplet(hWnd, CPL_NEWINQUIRE, pCPlEntry->nSubProg, (LPARAM)&cplNewInfo);
427
428                 if (plvdi->item.mask && LVIF_IMAGE) {
429                     if (cplNewInfo.hIcon) { // add the icon to an image list
430                         HIMAGELIST hImageList;
431                         hImageList = ListView_GetImageList(hWnd, LVSIL_NORMAL);
432                         pCPlEntry->nIconIndex = ImageList_ReplaceIcon(hImageList, pCPlEntry->nIconIndex, cplNewInfo.hIcon); 
433                         hImageList = ListView_GetImageList(hWnd, LVSIL_SMALL);
434                         ImageList_ReplaceIcon(hImageList, pCPlEntry->nIconIndex, cplNewInfo.hIcon);
435                         DestroyIcon(cplNewInfo.hIcon); 
436                     }
437                     plvdi->item.iImage = pCPlEntry->nIconIndex;
438                 }
439                 if (plvdi->item.mask && LVIF_STATE) {
440                 }
441                 if (plvdi->item.mask && LVIF_TEXT) {
442                     switch (plvdi->item.iSubItem) {
443                     case 0:
444                         plvdi->item.pszText = cplNewInfo.szName;
445                         plvdi->item.cchTextMax = _tcslen(plvdi->item.pszText); 
446                         break;
447                     case 1:
448                         plvdi->item.pszText = cplNewInfo.szInfo;
449                         plvdi->item.cchTextMax = _tcslen(plvdi->item.pszText); 
450                         break;
451                     case 2:
452                         plvdi->item.pszText = _T("");
453                         plvdi->item.cchTextMax = _tcslen(plvdi->item.pszText); 
454                         break;
455                     }
456                 }
457             }
458         }
459     }
460
461
462 static void OnDeleteItem(NMLISTVIEW* pnmlv)
463 {
464     CPlEntry* pCPlEntry = (CPlEntry*)pnmlv->lParam;
465     if (pCPlEntry != NULL) {
466         HeapFree(GetProcessHeap(), 0, pCPlEntry);
467     }
468 }
469
470 static void OnItemChanged(NMLISTVIEW* pnmlv)
471 {
472 }
473
474 void Control_LaunchApplet(HWND hwndLV, CPlEntry* pCPlEntry)
475 {
476     CPlApplet_Ptr pCPlApplet;
477     CPlApplet* pApplet = pCPlEntry->pCPlApplet;
478
479     assert(pApplet);
480     if (pApplet->hModule == NULL) {
481         pApplet->hModule = LoadLibrary(pApplet->filename);
482         pCPlApplet = (CPlApplet_Ptr)(FARPROC)GetProcAddress(pApplet->hModule, "CPlApplet");
483         if (pCPlApplet) {
484             if (pCPlApplet(hwndLV, CPL_INIT, 0, 0)) {
485                 unsigned int nSubProgs = pCPlApplet(hwndLV, CPL_GETCOUNT, 0, 0);
486                 if (nSubProgs < pCPlEntry->nSubProg) {
487                     TRACE(_T("Only %u subprograms in applet, requested %u\n"), nSubProgs, pCPlEntry->nSubProg);
488                     return;
489                 }
490             } else {
491                 TRACE(_T("Init of applet has failed\n"));
492                 return;
493             }
494         } else {
495             TRACE(_T("Not a valid control panel applet %s\n"), pApplet->filename);
496             return;
497         }
498     }
499     if (pApplet->hModule) {
500         pCPlApplet = (CPlApplet_Ptr)(FARPROC)GetProcAddress(pApplet->hModule, "CPlApplet");
501         if (pCPlApplet) {
502             TCHAR* extraPmts = NULL;
503             if (!pCPlApplet(hwndLV, CPL_STARTWPARMS, pCPlEntry->nSubProg, (LPARAM)extraPmts))
504                 pCPlApplet(hwndLV, CPL_DBLCLK, pCPlEntry->nSubProg, pCPlEntry->lData);
505         }
506
507     }
508 //        NEWCPLINFO* pCPLInfo = &(pCPlEntry->pCPlApplet->info[pCPlEntry->nSubProg]);
509 //        TCHAR* extraPmts = NULL;
510 //        if (pCPLInfo->dwSize && pApplet->proc) {
511 //            if (!pApplet->proc(pApplet->hWnd, CPL_STARTWPARMS, pCPlEntry->nSubProg, (LPARAM)extraPmts))
512 //                pApplet->proc(pApplet->hWnd, CPL_DBLCLK, pCPlEntry->nSubProg, pCPLInfo->lData);
513 //        }
514 }
515
516 static void OnDblClick(HWND hWnd, NMITEMACTIVATE* nmitem)
517 {
518     LVHITTESTINFO info;
519 /*
520 #ifdef _MSC_VER
521     switch (nmitem->uKeyFlags) {
522     case LVKF_ALT:     //  The ALT key is pressed.  
523         break;
524     case LVKF_CONTROL: //  The CTRL key is pressed.
525         break;
526     case LVKF_SHIFT:   //  The SHIFT key is pressed.   
527         break;
528     }
529 #endif
530  */
531     info.pt.x = nmitem->ptAction.x;
532     info.pt.y = nmitem->ptAction.y;
533     if (ListView_HitTest(hWnd, &info) != -1) {
534         LVITEM item;
535         item.mask = LVIF_PARAM;
536         item.iItem = info.iItem;
537         if (ListView_GetItem(hWnd, &item)) {
538             Control_LaunchApplet(hWnd, (CPlEntry*)item.lParam);
539         }
540     }
541 }
542
543 struct CompareData {
544     HWND hListWnd;
545     int  nSortColumn;
546 };
547
548 static int CALLBACK CompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
549 {
550     struct CompareData* pcd = (struct CompareData*)lParamSort;
551
552     TCHAR buf1[MAX_CPL_INFO];
553     TCHAR buf2[MAX_CPL_INFO];
554
555     ListView_GetItemText(pcd->hListWnd, lParam1, pcd->nSortColumn, buf1, sizeof(buf1)/sizeof(TCHAR));
556     ListView_GetItemText(pcd->hListWnd, lParam2, pcd->nSortColumn, buf2, sizeof(buf2)/sizeof(TCHAR));
557     return _tcscmp(buf1, buf2);
558 }
559
560 /*
561 static int CALLBACK CompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
562 {
563     TCHAR buf1[1000];
564     TCHAR buf2[1000];
565
566     ListView_GetItemText((HWND)lParamSort, lParam1, 0, buf1, sizeof(buf1)/sizeof(TCHAR));
567     ListView_GetItemText((HWND)lParamSort, lParam2, 0, buf2, sizeof(buf2)/sizeof(TCHAR));
568     return _tcscmp(buf1, buf2);
569 }
570
571 static void ListViewPopUpMenu(HWND hWnd, POINT pt)
572 {
573 }
574
575 BOOL ListView_SortItemsEx(
576     HWND hwnd, 
577     PFNLVCOMPARE pfnCompare, 
578     LPARAM lParamSort
579 );
580
581
582  */
583 #ifdef __GNUC__
584 //#define LVM_FIRST               0x1000      // ListView messages
585 #define LVM_SORTITEMSEX          (0x1000 + 81)
586 #define ListView_SortItemsEx(hwndLV, _pfnCompare, _lPrm) \
587   (BOOL)SendMessage((hwndLV), LVM_SORTITEMSEX, (WPARAM)(LPARAM)(_lPrm), (LPARAM)(PFNLVCOMPARE)(_pfnCompare))
588 #endif
589
590 BOOL SortListView(HWND hWnd, int nSortColumn)
591 {
592     struct CompareData cd = { hWnd, nSortColumn };
593     return ListView_SortItemsEx(hWnd, CompareFunc, &cd);
594 }
595
596 BOOL ListViewNotifyProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
597 {
598     NMITEMACTIVATE* nmitem = (LPNMITEMACTIVATE)lParam;
599
600     if (nmitem->hdr.idFrom == LIST_WINDOW) {
601         switch (((LPNMHDR)lParam)->code) { 
602         case LVN_GETDISPINFO: 
603             OnGetDispInfo(hWnd, (NMLVDISPINFO*)lParam); 
604             break; 
605         case LVN_DELETEITEM:
606             OnDeleteItem((NMLISTVIEW*)lParam);
607             //pnmv = (LPNMLISTVIEW) lParam
608             break;
609         case LVN_ITEMCHANGED:
610             OnItemChanged((NMLISTVIEW*)lParam);
611             break;
612         case NM_DBLCLK:
613             OnDblClick(hWnd, nmitem);
614             break;
615         //case NM_RCLICK:
616             //OnRightClick(hWnd, nmitem);
617             //break; 
618         default:
619             return FALSE;
620         }
621     }
622     return TRUE;
623 }
624
625 VOID DestroyListView(HWND hwndLV)
626 {
627     if (pListHead)
628         while ((pListHead = Control_UnloadApplet(pListHead)));
629 }
630
631 BOOL RefreshListView(HWND hwndLV, LPTSTR szPath)
632
633     if (hwndLV != NULL) {
634         ListView_DeleteAllItems(hwndLV);
635     }
636     return InitListViewItems(hwndLV, szPath);
637 }
638
639 HWND CreateListView(HWND hwndParent, int id)
640
641     RECT rcClient;
642     HWND hwndLV;
643  
644     // Get the dimensions of the parent window's client area, and create the list view control. 
645     GetClientRect(hwndParent, &rcClient); 
646     hwndLV = CreateWindowEx(WS_EX_CLIENTEDGE, WC_LISTVIEW, _T("List View"), 
647         WS_VISIBLE | WS_CHILD, 
648         0, 0, rcClient.right, rcClient.bottom, 
649         hwndParent, (HMENU)id, hInst, NULL); 
650     ListView_SetExtendedListViewStyle(hwndLV,  LVS_EX_FULLROWSELECT);
651     CreateListColumns(hwndLV);
652
653     // Initialize the image list, and add items to the control. 
654     if (!InitListViewImageLists(hwndLV) || !InitListViewItems(hwndLV, NULL/*szPath*/)) { 
655         DestroyWindow(hwndLV); 
656         return FALSE; 
657     } 
658     return hwndLV;
659
660