Initial IBM OpenAFS 1.0 tree
[openafs.git] / src / WINNT / afsapplib / fastlist.cpp
1 extern "C" {
2 #include <afs/param.h>
3 #include <afs/stds.h>
4 }
5
6 #define NO_DEBUG_ALLOC // Turn off memory-allocation instrumentation for this
7
8 #include <windows.h>
9 #include <windowsx.h>
10 #include <commctrl.h>
11 #include <stdlib.h>
12 #include <WINNT/fastlist.h>
13 #include <WINNT/subclass.h>
14 #include <WINNT/hashlist.h>
15 #include <WINNT/TaLocale.h>
16
17
18 /*
19  * DEFINITIONS ________________________________________________________________
20  *
21  */
22
23 #define clrTRANSPARENT PALETTERGB(255,0,255)
24
25 typedef struct _FASTLISTITEM
26    {
27    LPARAM lpUser;
28    int index;
29
30    int iFirstImage;
31    int iSecondImage;
32
33    HLISTITEM hTreeParent;
34    HLISTITEM hTreeChild;
35    HLISTITEM hTreePrevious;
36    HLISTITEM hTreeNext;
37    HLISTITEM hListPrevious;
38    HLISTITEM hListNext;
39    HLISTITEM hSelectPrevious;
40    HLISTITEM hSelectNext;
41
42    BOOL fExpanded;
43    BOOL fVisible;
44    DWORD dwFlags;
45    BOOL fSelected;
46    BOOL fFocused;
47
48    LPTSTR *apszText;
49    size_t  cpszText;
50    } FASTLISTITEM, *LPFASTLISTITEM, LISTITEM, *LPLISTITEM;
51
52
53         // We over-allocate the aVisibleHeap[] array whenever it fills up,
54         // in order to perform fewer allocations. The allocation size
55         // defined by the macros below causes the following progression
56         // of array sizes:
57         //
58         //    128, 256, 512, 1024, 2048, 4096, 8192, 16384, 24576, 32768, ...
59         //
60 #define cREALLOC_VISIBLEHEAP_MIN   128
61 #define cREALLOC_VISIBLEHEAP_MAX  4096
62 #define cREALLOC_VISIBLEHEAP(pfl) min(max(pfl->cVisibleHeap, cREALLOC_VISIBLEHEAP_MIN),cREALLOC_VISIBLEHEAP_MAX)
63
64 #define dwSigFASTLIST  TEXT('FAST')
65
66 typedef struct FASTLIST
67    {
68    DWORD dwSig;
69    HWND hList;
70    HWND hHeader;
71    HWND hScrollH;
72    HWND hScrollV;
73    DWORD dwStyle;
74    LONG dxPixel;
75    LONG dyPixel;
76    CRITICAL_SECTION cs;
77    HFONT hf;
78    BOOL fSortBeforePaint;
79    BOOL fSyncScrollBeforePaint;
80    BOOL fSyncIndicesBeforePaint;
81    BOOL fRepaintRequired;
82    BOOL fSyncHeaderRequired;
83    size_t nBegin;
84    HIMAGELIST hilSmall;
85    HIMAGELIST hilLarge;
86    LPFASTLISTTEXTCALLBACK pfnText;
87    DWORD dwCookieText;
88    LPFASTLISTSORTFUNC pfnSort;
89    int iColSort;
90    BOOL fRevSort;
91    HLISTITEM hEnsureVisible;
92
93    LPHASHLIST lItems;
94    LPHASHLISTKEY lkUserParam;
95    HLISTITEM hTreeFirst;
96    HLISTITEM hListFirst;
97    HLISTITEM hSelectFirst;
98    HLISTITEM hFocused;
99    HLISTITEM hAnchor;
100
101    BOOL fButtonDown;
102    BOOL fRightDrag;
103    BOOL fDragging;
104    BOOL fTriedToDrag;
105    POINT ptScreenDown;
106    HLISTITEM hHitOnDown;
107    BOOL fSelectedOnDown;
108
109    HD_ITEM *aColumns;
110    size_t cColumns;
111
112    HLISTITEM *aVisibleHeap;
113    size_t cVisibleHeap;
114    size_t cVisible;
115    } FASTLIST, *LPFASTLIST;
116
117
118 /*
119  * GLOBAL TO FASTLIST _________________________________________________________
120  *
121  */
122
123 static struct FASTLIST_GLOBAL
124    {
125    HBITMAP bmp;
126    LONG cxBmp;
127    LONG cyBmp;
128    HLISTITEM *aObjects;
129    size_t cObjects;
130    CRITICAL_SECTION cs;
131    LPFASTLIST pfl;
132    } fg;
133
134 #define cREALLOC_OBJECTHEAP   512
135
136
137 /*
138  * DISPLAY CONSTANTS __________________________________________________________
139  *
140  */
141
142 #ifndef cxRECT
143 #define cxRECT(_r) ((_r).right - (_r).left)
144 #endif
145
146 #ifndef cyRECT
147 #define cyRECT(_r) ((_r).bottom - (_r).top)
148 #endif
149
150 #ifndef limit
151 #define limit(_a,_x,_b) min(max((_x),(_a)),(_b))
152 #endif
153
154 #ifndef DivRoundUp
155 #define DivRoundUp(_a,_b)  (((_a) + (_b) -1) / (_b))
156 #endif
157
158 #define cyABOVE_LARGE_ICON      2
159 #define cyBELOW_LARGE_ICON      1
160 #define cyABOVE_LARGE_TEXT      1
161 #define cyBELOW_LARGE_TEXT      1
162 #define cxBEFORE_LARGE_TEXT     1
163 #define cxAFTER_LARGE_TEXT      2
164
165 #define cyABOVE_SMALL_TEXT      1
166 #define cyBELOW_SMALL_TEXT      1
167 #define cxBEFORE_SMALL_ICON     2
168 #define cxAFTER_SMALL_ICON      2
169 #define cxBEFORE_SMALL_TEXT     1
170 #define cxAFTER_SMALL_TEXT      2
171
172 #define cxBEFORE_LIST_ICON      2
173 #define cxAFTER_LIST_ICON       2
174 #define cyABOVE_LIST_TEXT       1
175 #define cyBELOW_LIST_TEXT       1
176 #define cxBETWEEN_LIST_ICONS    2
177 #define cxBEFORE_LIST_TEXT      1
178 #define cxAFTER_LIST_TEXT       4
179 #define cxBEFORE_COLUMN_TEXT    5
180
181 #define cxSPACE_TREELINE        2
182 #define cySPACE_TREELINE        2
183 #define cxTREE_BOX              8
184 #define cyTREE_BOX              8
185
186 #define TREELINE_HORIZONTAL     0x00000001
187 #define TREELINE_BOX            0x00000002
188 #define TREELINE_BOXOPEN       (TREELINE_BOX)
189 #define TREELINE_BOXCLOSED     (0x00000004 | TREELINE_BOX)
190 #define TREELINE_UP             0x00000008
191 #define TREELINE_DOWN           0x00000010
192
193
194 /*
195  * VARIABLES __________________________________________________________________
196  *
197  */
198
199
200 /*
201  * PROTOTYPES _________________________________________________________________
202  *
203  */
204
205 #define fIsShiftDown()   (GetKeyState(VK_SHIFT) < 0)
206 #define fIsControlDown() (GetKeyState(VK_CONTROL) < 0)
207
208 BOOL OpenGlobalBitmap (HDC hdc, RECT *prClient);
209 void CloseGlobalBitmap (void);
210
211 BOOL OpenGlobalArray (size_t cObjects);
212 void CloseGlobalArray (void);
213
214 BOOL CALLBACK FastList_ControlProc (HWND hList, UINT msg, WPARAM wp, LPARAM lp);
215 BOOL CALLBACK FastList_ParentProc (HWND hDlg, UINT msg, WPARAM wp, LPARAM lp);
216
217 void FastList_OnCreate (HWND hList);
218 void FastList_OnDestroy (HWND hList);
219 void FastList_OnStyleChange (HWND hList);
220 void FastList_OnSize (HWND hList);
221 void FastList_OnPaint (HWND hList);
222 void FastList_OnPaintItem (LPFASTLIST pfl, LPFASTLISTDRAWITEM pdi);
223 void FastList_OnPaintItem_DrawImage (LPFASTLIST pfl, LPFASTLISTDRAWITEM pdi, int iImage, LONG xImage, LONG yImage, BOOL fHLines);
224 void FastList_OnPaintItem_GetItemColors (LPFASTLIST pfl, LPFASTLISTDRAWITEM pdi, COLORREF *pclrFore, COLORREF *pclrBack);
225 void FastList_OnPaintItem_TreeLines (LPFASTLIST pfl, LPFASTLISTDRAWITEM pdi, DWORD dwLines, RECT *prLines);
226 void FastList_OnPaintItem_Large (LPFASTLIST pfl, LPFASTLISTDRAWITEM pdi);
227 void FastList_OnPaintItem_Small (LPFASTLIST pfl, LPFASTLISTDRAWITEM pdi);
228 void FastList_OnPaintItem_Tree (LPFASTLIST pfl, LPFASTLISTDRAWITEM pdi, HLISTITEM hItem, RECT *prThisColumn);
229 void FastList_OnPaintItem_List (LPFASTLIST pfl, LPFASTLISTDRAWITEM pdi);
230 void FastList_OnScroll (HWND hList, UINT msg, WPARAM wp, LPARAM lp);
231 void FastList_OnRightButtonDown (HWND hList);
232 void FastList_OnLeftButtonDown (HWND hList);
233 void FastList_OnLeftButtonDouble (HWND hList);
234 void FastList_OnKeyPress (HWND hList, TCHAR ch);
235 BOOL FastList_OnKeyPress_EnsureSelection (LPFASTLIST pfl);
236 void FastList_OnKeyPress_ChangeSelection (LPFASTLIST pfl, HLISTITEM hSelect);
237
238 void FastList_OnCommand_Begin (HWND hList);
239 void FastList_OnCommand_End (HWND hList, BOOL fForce);
240 HLISTITEM FastList_OnCommand_AddItem (HWND hList, LPFASTLISTADDITEM pai);
241 void FastList_OnCommand_RemoveItem (HWND hList, HLISTITEM hItem);
242 LPCTSTR FastList_OnCommand_GetItemText (HWND hList, HLISTITEM hItem, int iColumn);
243 void FastList_OnCommand_SetItemText (HWND hList, LPFASTLISTITEMCOLUMN pflic, LPCTSTR pszText);
244 LPARAM FastList_OnCommand_GetItemParam (HWND hList, HLISTITEM hItem);
245 void FastList_OnCommand_SetItemParam (HWND hList, HLISTITEM hItem, LPARAM lpUser);
246 DWORD FastList_OnCommand_GetItemFlags (HWND hList, HLISTITEM hItem);
247 void FastList_OnCommand_SetItemFlags (HWND hList, HLISTITEM hItem, DWORD dwFlags);
248 int FastList_OnCommand_GetItemImage (HWND hList, HLISTITEM hItem, int iImage);
249 void FastList_OnCommand_SetItemImage (HWND hList, LPFASTLISTITEMIMAGE pflii, int iImage);
250 void FastList_OnCommand_GetImageLists (HWND hList, HIMAGELIST *phiSmall, HIMAGELIST *phiLarge);
251 void FastList_OnCommand_SetImageLists (HWND hList, HIMAGELIST hiSmall, HIMAGELIST hiLarge);
252 HIMAGELIST FastList_OnCommand_CreateDragImage (HWND hList, HLISTITEM hItem);
253 LPFASTLISTSORTFUNC FastList_OnCommand_GetSortFunc (HWND hList);
254 void FastList_OnCommand_SetSortFunc (HWND hList, LPFASTLISTSORTFUNC pfn);
255 void FastList_OnCommand_GetSortStyle (HWND hList, int *piColSort, BOOL *pfRevSort);
256 void FastList_OnCommand_SetSortStyle (HWND hList, int iColSort, BOOL fRevSort);
257 void FastList_OnCommand_Sort (HWND hList);
258 int FastList_OnCommand_GetColumnCount (HWND hList);
259 BOOL FastList_OnCommand_GetColumn (HWND hList, int iColumn, LPFASTLISTCOLUMN pcol);
260 void FastList_OnCommand_SetColumn (HWND hList, int iColumn, LPFASTLISTCOLUMN pcol);
261 BOOL FastList_OnCommand_IsSelected (HWND hList, HLISTITEM hItem);
262 void FastList_OnCommand_SelectItem (HWND hList, HLISTITEM hItem, BOOL fSelect);
263 HLISTITEM FastList_OnCommand_FindList (HWND hList, HLISTITEM hItem, DWORD dwCode);
264 HLISTITEM FastList_OnCommand_FindTree (HWND hList, HLISTITEM hItem, DWORD dwCode);
265 HLISTITEM FastList_OnCommand_FindSelected (HWND hList, HLISTITEM hItem);
266 HLISTITEM FastList_OnCommand_FindItem (HWND hList, LPENUM *ppEnum, LPARAM lpUser);
267 HLISTITEM FastList_OnCommand_FindNextItem (HWND hList, LPENUM *ppEnum);
268 HLISTITEM FastList_OnCommand_FindVisible (HWND hList, HLISTITEM hItem, DWORD dwCode);
269 void FastList_OnCommand_FindClose (HWND hList, LPENUM *ppEnum);
270 int FastList_OnCommand_GetItemCount (HWND hList, BOOL fVisibleOnly);
271 BOOL FastList_OnCommand_IsExpanded (HWND hList, HLISTITEM hItem);
272 void FastList_OnCommand_Expand (HWND hList, HLISTITEM hItem, BOOL fExpand);
273 BOOL FastList_OnCommand_ItemVisible (HWND hList, HLISTITEM hItem, BOOL fSet);
274 HLISTITEM FastList_OnCommand_ItemFocus (HWND hList, HLISTITEM hItem, BOOL fSet);
275 HLISTITEM FastList_OnCommand_ItemFromPoint (HWND hList, POINT *pptClient, BOOL fStrict);
276 void FastList_OnCommand_GetItemRegions (HWND hList, HLISTITEM hItem, LPFASTLISTITEMREGIONS pReg, POINT *pptTest = NULL, BOOL *pfHit = NULL);
277 LPFASTLISTTEXTCALLBACK FastList_OnCommand_GetTextCallback (HWND hList);
278 void FastList_OnCommand_SetTextCallback (HWND hList, LPFASTLISTTEXTCALLBACK pfn, DWORD dwCookie);
279
280 BOOL FastList_Notify_GetItemText (LPFASTLIST pfl, HLISTITEM hItem, int icol, LPTSTR pszText, size_t cchText, size_t *pcchRequired);
281 BOOL FastList_Notify_ItemChanged (LPFASTLIST pfl, HLISTITEM hItem);
282 BOOL FastList_Notify_AddItem (LPFASTLIST pfl, HLISTITEM hItem);
283 BOOL FastList_Notify_RemoveItem (LPFASTLIST pfl, HLISTITEM hItem);
284 BOOL FastList_Notify_ColumnClick (LPFASTLIST pfl, int icol, BOOL fDouble);
285 BOOL FastList_Notify_ColumnResize (LPFASTLIST pfl, int icol, LONG cxWidth);
286 BOOL FastList_Notify_ItemSelect (LPFASTLIST pfl);
287 BOOL FastList_Notify_ItemExpand (LPFASTLIST pfl, HLISTITEM hItem);
288 BOOL FastList_Notify_Generic (LPFASTLIST pfl, DWORD dwCode);
289 BOOL FastList_Notify_Drag (LPFASTLIST pfl, DWORD dwCode, LPFLN_DRAG_PARAMS pfln);
290
291 void FastList_Repaint (LPFASTLIST pfl);
292 HLISTITEM FastList_CreateItem (LPFASTLIST pfl);
293 void FastList_DeleteItem (LPFASTLIST pfl, HLISTITEM hItem);
294 int FastList_GetListHeight (LPFASTLIST pfl);
295 int FastList_GetListWidth (LPFASTLIST pfl);
296 void FastList_CalcItemRect (LPFASTLIST pfl, int index, RECT *prItem, BOOL fScrollH, BOOL fScrollV);
297 void FastList_CalcFieldSize (LPFASTLIST pfl, LONG *pcxField, LONG *pcyField, BOOL fScrollH, BOOL fScrollV);
298 void FastList_GetHeaderRect (LPFASTLIST pfl, RECT *prHeader);
299 LONG FastList_GetHeaderHeight (LPFASTLIST pfl);
300 void FastList_UpdateColumn (LPFASTLIST pfl, int iColumn);
301 void FastList_SyncHeader (LPFASTLIST pfl);
302 void FastList_SyncScroll (LPFASTLIST pfl);
303 void FastList_SyncScrollPos (LPFASTLIST pfl);
304 void FastList_CallPaintItem (LPFASTLIST pfl, HDC hdc, BOOL fDraw, BOOL fDragImage, int iItem, RECT *prItem, LPFASTLISTITEMREGIONS pReg, POINT *pptTest, BOOL *pfHit);
305 LPTSTR FastList_GetColumnText (LPFASTLIST pfl, HLISTITEM hItem, int icol, BOOL fAlternateBuffer = FALSE);
306 void FastList_PerformSort (LPFASTLIST pfl);
307 void FastList_PerformSortLevel (LPFASTLIST pfl, HLISTITEM hParent, BOOL fTreeSort);
308 int __cdecl FastList_SortFunction (const void *lp1, const void *lp2);
309 void FastList_RepairOneVisibilityFlag (LPFASTLIST pfl, HLISTITEM hItem);
310 void FastList_RepairVisibilityFlags (LPFASTLIST pfl, HLISTITEM hParent = NULL, BOOL fForceHidden = FALSE);
311 void FastList_RepairVisibilityIndices (LPFASTLIST pfl, HLISTITEM hParent = NULL);
312 void FastList_ScrollHeader (LPFASTLIST pfl);
313 void FastList_PerformSelectTest (LPFASTLIST pfl, HLISTITEM hItem);
314 void FastList_PerformSelectItem (LPFASTLIST pfl, HLISTITEM hItem, BOOL fSelect);
315 void FastList_PerformSelectRange (LPFASTLIST pfl, HLISTITEM hItem1, HLISTITEM hItem2);
316 void FastList_PerformEnsureVisible (LPFASTLIST pfl);
317 void FastList_ButtonDown (HWND hList, BOOL fRightButton, BOOL fActivate);
318 void FastList_MouseMove (HWND hList);
319 void FastList_ButtonUp (HWND hList, BOOL fRightButton, BOOL fActivate);
320
321 LPFASTLIST GetFastList (HWND hList);
322
323 BOOL CALLBACK FastList_KeyUserParam_Compare (LPHASHLISTKEY pKey, PVOID pObject, PVOID pData);
324 HASHVALUE CALLBACK FastList_KeyUserParam_HashObject (LPHASHLISTKEY pKey, PVOID pObject);
325 HASHVALUE CALLBACK FastList_KeyUserParam_HashData (LPHASHLISTKEY pKey, PVOID pData);
326
327
328 /*
329  * REALLOC ____________________________________________________________________
330  *
331  */
332
333 #ifdef REALLOC
334 #undef REALLOC
335 #endif
336 #define REALLOC(_a,_c,_r,_i) FastList_ReallocFunction ((LPVOID*)&_a,sizeof(*_a),&_c,_r,_i)
337 BOOL FastList_ReallocFunction (LPVOID *ppTarget, size_t cbElement, size_t *pcTarget, size_t cReq, size_t cInc)
338 {
339    LPVOID pNew;
340    size_t cNew;
341
342    if (cReq <= *pcTarget)
343       return TRUE;
344
345    if ((cNew = cInc * ((cReq + cInc-1) / cInc)) <= 0)
346       return FALSE;
347
348    if ((pNew = (LPVOID)Allocate (cbElement * cNew)) == NULL)
349       return FALSE;
350    memset (pNew, 0x00, cbElement * cNew);
351
352    if (*pcTarget != 0)
353       {
354       memcpy (pNew, *ppTarget, cbElement * (*pcTarget));
355       Free (*ppTarget);
356       }
357
358    *ppTarget = pNew;
359    *pcTarget = cNew;
360    return TRUE;
361 }
362
363
364 /*
365  * WINDOW CLASS SUPPORT _______________________________________________________
366  *
367  */
368
369 BOOL RegisterFastListClass (void)
370 {
371    static BOOL fRegistered = FALSE;
372
373    if (!fRegistered)
374       {
375       memset (&fg, 0x00, sizeof(fg));
376       InitializeCriticalSection (&fg.cs);
377
378       WNDCLASS wc;
379       GetClassInfo (THIS_HINST, TEXT("LISTBOX"), &wc);
380       wc.style = CS_GLOBALCLASS | CS_DBLCLKS;
381       wc.cbWndExtra = 4;
382       wc.lpfnWndProc = (WNDPROC)FastList_ControlProc;
383       wc.hInstance = THIS_HINST;
384       wc.hCursor = LoadCursor (NULL, MAKEINTRESOURCE (IDC_ARROW));
385       wc.hbrBackground = NULL;
386       wc.lpszClassName = WC_FASTLIST;
387
388       if (RegisterClass (&wc))
389          fRegistered = TRUE;
390       }
391
392    return fRegistered;
393 }
394
395
396 BOOL OpenGlobalBitmap (HDC hdc, RECT *prClient)
397 {
398    EnterCriticalSection (&fg.cs);
399
400    if ((!fg.bmp) || (fg.cxBmp < prClient->right) || (fg.cyBmp < prClient->bottom))
401       {
402       if (fg.bmp)
403          DeleteObject (fg.bmp);
404
405       fg.cxBmp = 128 * DivRoundUp (prClient->right, 128);
406       fg.cyBmp =  64 * DivRoundUp (prClient->bottom, 64);
407       fg.bmp = CreateCompatibleBitmap (hdc, fg.cxBmp, fg.cyBmp);
408       }
409
410    if (!fg.bmp)
411       {
412       LeaveCriticalSection (&fg.cs);
413       return FALSE;
414       }
415
416    return TRUE;
417 }
418
419
420 void CloseGlobalBitmap (void)
421 {
422    LeaveCriticalSection (&fg.cs);
423 }
424
425
426 BOOL OpenGlobalArray (size_t cObjects)
427 {
428    EnterCriticalSection (&fg.cs);
429    REALLOC (fg.aObjects, fg.cObjects, cObjects, cREALLOC_OBJECTHEAP);
430    memset (fg.aObjects, 0x00, sizeof(HLISTITEM) * fg.cObjects);
431    return TRUE;
432 }
433
434
435 void CloseGlobalArray (void)
436 {
437    LeaveCriticalSection (&fg.cs);
438 }
439
440
441 BOOL CALLBACK FastList_ControlProc (HWND hList, UINT msg, WPARAM wp, LPARAM lp)
442 {
443    switch (msg)
444       {
445       case WM_CREATE:
446          FastList_OnCreate (hList);
447          if (GetParent (hList))
448             Subclass_AddHook (GetParent (hList), FastList_ParentProc);
449          break;
450
451       case WM_DESTROY:
452          if (GetParent (hList))
453             Subclass_RemoveHook (GetParent (hList), FastList_ParentProc);
454          FastList_OnDestroy (hList);
455          break;
456
457       case WM_GETDLGCODE:
458          return DLGC_WANTARROWS;
459
460       case WM_STYLECHANGED:
461          FastList_OnStyleChange (hList);
462          break;
463
464       case WM_ENABLE:
465          FastList_Repaint (GetFastList (hList));
466          break;
467
468       case WM_SIZE:
469          FastList_OnSize (hList);
470          break;
471
472       case WM_PAINT:
473          FastList_OnPaint (hList);
474          break;
475
476       case WM_LBUTTONDOWN:
477          FastList_OnLeftButtonDown (hList);
478          break;
479
480       case WM_RBUTTONDOWN:
481          FastList_OnRightButtonDown (hList);
482          break;
483
484       case WM_LBUTTONUP:
485          FastList_ButtonUp (hList, FALSE, FALSE);
486          break;
487
488       case WM_RBUTTONUP:
489          FastList_ButtonUp (hList, TRUE, FALSE);
490          break;
491
492       case WM_MOUSEMOVE:
493          FastList_MouseMove (hList);
494          break;
495
496       case WM_LBUTTONDBLCLK:
497          FastList_OnLeftButtonDouble (hList);
498          break;
499
500       case WM_KEYDOWN:
501          FastList_OnKeyPress (hList, (TCHAR)wp);
502          break;
503
504       case WM_NOTIFY:
505          switch (((NMHDR*)lp)->code)
506             {
507             case HDN_ITEMCLICK:
508                FastList_Notify_ColumnClick (GetFastList (hList), ((HD_NOTIFY*)lp)->iItem, FALSE);
509                break;
510
511             case HDN_ITEMDBLCLICK:
512                FastList_Notify_ColumnClick (GetFastList (hList), ((HD_NOTIFY*)lp)->iItem, TRUE);
513                break;
514
515             case HDN_ITEMCHANGED:
516             case HDN_ENDTRACK:
517             case HDN_TRACK:
518                FastList_UpdateColumn (GetFastList (hList), ((HD_NOTIFY*)lp)->iItem);
519                break;
520             }
521          break;
522
523       case WM_HSCROLL:
524       case WM_VSCROLL:
525          FastList_OnScroll (hList, msg, wp, lp);
526          break;
527
528       case FLM_BEGIN:
529          FastList_OnCommand_Begin (hList);
530          return TRUE;
531
532       case FLM_END:
533          FastList_OnCommand_End (hList, (BOOL)wp);
534          return TRUE;
535
536       case FLM_ADDITEM:
537          return (BOOL)FastList_OnCommand_AddItem (hList, (LPFASTLISTADDITEM)lp);
538
539       case FLM_REMOVEITEM:
540          FastList_OnCommand_RemoveItem (hList, (HLISTITEM)wp);
541          return TRUE;
542
543       case FLM_GETITEMTEXT:
544          return (BOOL)FastList_OnCommand_GetItemText (hList, (HLISTITEM)wp, (int)lp);
545
546       case FLM_SETITEMTEXT:
547          FastList_OnCommand_SetItemText (hList, (LPFASTLISTITEMCOLUMN)wp, (LPCTSTR)lp);
548          return TRUE;
549
550       case FLM_GETITEMPARAM:
551          return (BOOL)FastList_OnCommand_GetItemParam (hList, (HLISTITEM)wp);
552
553       case FLM_SETITEMPARAM:
554          FastList_OnCommand_SetItemParam (hList, (HLISTITEM)wp, lp);
555          return TRUE;
556
557       case FLM_GETITEMFLAGS:
558          return (BOOL)FastList_OnCommand_GetItemFlags (hList, (HLISTITEM)wp);
559
560       case FLM_SETITEMFLAGS:
561          FastList_OnCommand_SetItemFlags (hList, (HLISTITEM)wp, (DWORD)lp);
562          return TRUE;
563
564       case FLM_GETITEMIMAGE:
565          return (BOOL)FastList_OnCommand_GetItemImage (hList, (HLISTITEM)wp, (int)lp);
566
567       case FLM_SETITEMIMAGE:
568          FastList_OnCommand_SetItemImage (hList, (LPFASTLISTITEMIMAGE)wp, (int)lp);
569          return TRUE;
570
571       case FLM_ISEXPANDED:
572          return (BOOL)FastList_OnCommand_IsExpanded (hList, (HLISTITEM)wp);
573
574       case FLM_EXPAND:
575          FastList_OnCommand_Expand (hList, (HLISTITEM)wp, (BOOL)lp);
576          return TRUE;
577
578       case FLM_ITEMVISIBLE:
579          return (BOOL)FastList_OnCommand_ItemVisible (hList, (HLISTITEM)wp, (BOOL)lp);
580
581       case FLM_ITEMFOCUS:
582          return (BOOL)FastList_OnCommand_ItemFocus (hList, (HLISTITEM)wp, (BOOL)lp);
583
584       case FLM_GETIMAGELISTS:
585          FastList_OnCommand_GetImageLists (hList, (HIMAGELIST*)wp, (HIMAGELIST*)lp);
586          return TRUE;
587
588       case FLM_SETIMAGELISTS:
589          FastList_OnCommand_SetImageLists (hList, (HIMAGELIST)wp, (HIMAGELIST)lp);
590          return TRUE;
591
592       case FLM_CREATEDRAGIMAGE:
593          return (BOOL)FastList_OnCommand_CreateDragImage (hList, (HLISTITEM)wp);
594
595       case FLM_GETSORTFUNC:
596          return (BOOL)FastList_OnCommand_GetSortFunc (hList);
597
598       case FLM_SETSORTFUNC:
599          FastList_OnCommand_SetSortFunc (hList, (LPFASTLISTSORTFUNC)lp);
600          return TRUE;
601
602       case FLM_GETSORTSTYLE:
603          FastList_OnCommand_GetSortStyle (hList, (int*)wp, (BOOL*)lp);
604          return TRUE;
605
606       case FLM_SETSORTSTYLE:
607          FastList_OnCommand_SetSortStyle (hList, (int)wp, (BOOL)lp);
608          return TRUE;
609
610       case FLM_SORT:
611          FastList_OnCommand_Sort (hList);
612          return TRUE;
613
614       case FLM_GETCOLUMNCOUNT:
615          return (BOOL)FastList_OnCommand_GetColumnCount (hList);
616
617       case FLM_GETCOLUMN:
618          return FastList_OnCommand_GetColumn (hList, (int)wp, (LPFASTLISTCOLUMN)lp);
619
620       case FLM_SETCOLUMN:
621          FastList_OnCommand_SetColumn (hList, (int)wp, (LPFASTLISTCOLUMN)lp);
622          return TRUE;
623
624       case FLM_ISSELECTED:
625          return (BOOL)FastList_OnCommand_IsSelected (hList, (HLISTITEM)wp);
626
627       case FLM_SELECTITEM:
628          FastList_OnCommand_SelectItem (hList, (HLISTITEM)wp, (BOOL)lp);
629          return TRUE;
630
631       case FLM_FINDLIST:
632          return (BOOL)FastList_OnCommand_FindList (hList, (HLISTITEM)wp, (DWORD)lp);
633
634       case FLM_FINDTREE:
635          return (BOOL)FastList_OnCommand_FindTree (hList, (HLISTITEM)wp, (DWORD)lp);
636
637       case FLM_FINDSELECTED:
638          return (BOOL)FastList_OnCommand_FindSelected (hList, (HLISTITEM)wp);
639
640       case FLM_FINDITEM:
641          return (BOOL)FastList_OnCommand_FindItem (hList, (LPENUM*)wp, lp);
642
643       case FLM_FINDNEXTITEM:
644          return (BOOL)FastList_OnCommand_FindNextItem (hList, (LPENUM*)wp);
645
646       case FLM_FINDCLOSE:
647          FastList_OnCommand_FindClose (hList, (LPENUM*)wp);
648          return TRUE;
649
650       case FLM_FINDVISIBLE:
651          return (BOOL)FastList_OnCommand_FindVisible (hList, (HLISTITEM)wp, (DWORD)lp);
652
653       case FLM_GETITEMCOUNT:
654          return (BOOL)FastList_OnCommand_GetItemCount (hList, (BOOL)wp);
655
656       case FLM_ITEMFROMPOINT:
657          return (BOOL)FastList_OnCommand_ItemFromPoint (hList, (POINT*)wp, (BOOL)lp);
658
659       case FLM_GETITEMREGIONS:
660          FastList_OnCommand_GetItemRegions (hList, (HLISTITEM)wp, (LPFASTLISTITEMREGIONS)lp);
661          return TRUE;
662
663       case FLM_GETTEXTCALLBACK:
664          return (BOOL)FastList_OnCommand_GetTextCallback (hList);
665
666       case FLM_SETTEXTCALLBACK:
667          FastList_OnCommand_SetTextCallback (hList, (LPFASTLISTTEXTCALLBACK)wp, (DWORD)lp);
668          return TRUE;
669       }
670
671    return DefWindowProc (hList, msg, wp, lp);
672 }
673
674
675 BOOL CALLBACK FastList_ParentProc (HWND hDlg, UINT msg, WPARAM wp, LPARAM lp)
676 {
677    PVOID oldProc = Subclass_FindNextHook (hDlg, FastList_ParentProc);
678
679    switch (msg)
680       {
681       case WM_DRAWITEM:
682          LPFASTLIST pfl;
683          if ((pfl = GetFastList (GetDlgItem (hDlg, wp))) != NULL)
684             {
685             FastList_OnPaintItem (pfl, (LPFASTLISTDRAWITEM)lp);
686             return TRUE;
687             }
688          break;
689
690       case WM_NOTIFY:
691          if ((pfl = GetFastList (GetDlgItem (hDlg, wp))) != NULL)
692             {
693             switch (((NMHDR*)lp)->code)
694                {
695                case FLN_COLUMNCLICK:
696                   if (pfl->dwStyle & FLS_AUTOSORTHEADER)
697                      {
698                      LPFLN_COLUMNCLICK_PARAMS pp = (LPFLN_COLUMNCLICK_PARAMS)lp;
699
700                      if (pfl->iColSort == pp->icol)
701                         FastList_SetSortStyle (pfl->hList, pp->icol, !pfl->fRevSort);
702                      else
703                         FastList_SetSortStyle (pfl->hList, pp->icol, FALSE);
704                      }
705                   break;
706                }
707             }
708          break;
709       }
710
711    return CallWindowProc ((WNDPROC)oldProc, hDlg, msg, wp, lp);
712 }
713
714
715 /*
716  * MESSAGE HANDLING ___________________________________________________________
717  *
718  */
719
720 void FastList_OnCreate (HWND hList)
721 {
722    LPFASTLIST pfl = New (FASTLIST);
723    memset (pfl, 0x00, sizeof(FASTLIST));
724    SetWindowLong (hList, 0, (LONG)pfl);
725    pfl->pfnSort = NULL;
726    pfl->pfnText = NULL;
727    pfl->dwCookieText = 0;
728
729    InitializeCriticalSection (&pfl->cs);
730    pfl->dwSig = dwSigFASTLIST;
731    pfl->hList = hList;
732    pfl->dwStyle = GetWindowLong (hList, GWL_STYLE);
733    pfl->hf = (HFONT)GetStockObject (DEFAULT_GUI_FONT);
734
735    pfl->lItems = New (HASHLIST);
736    pfl->lItems->SetCriticalSection (&pfl->cs);
737    pfl->lkUserParam = pfl->lItems->CreateKey ("User Param", FastList_KeyUserParam_Compare, FastList_KeyUserParam_HashObject, FastList_KeyUserParam_HashData);
738
739    FastList_Begin (pfl->hList);
740
741    if (!(pfl->dwStyle & WS_CLIPCHILDREN))
742       SetWindowLong (pfl->hList, GWL_STYLE, pfl->dwStyle | WS_CLIPCHILDREN);
743
744    FastList_SyncHeader (pfl);
745    FastList_Repaint (pfl);
746    FastList_End (pfl->hList);
747 }
748
749
750 void FastList_OnDestroy (HWND hList)
751 {
752    LPFASTLIST pfl;
753    if ((pfl = GetFastList (hList)) != NULL)
754       {
755       if (pfl->hHeader)
756          DestroyWindow (pfl->hHeader);
757       if (pfl->hScrollH)
758          DestroyWindow (pfl->hScrollH);
759       if (pfl->hScrollV)
760          DestroyWindow (pfl->hScrollV);
761       if (pfl->aVisibleHeap)
762          Free (pfl->aVisibleHeap);
763       if (pfl->aColumns)
764          {
765          for (size_t iColumn = 0; iColumn < pfl->cColumns; ++iColumn)
766             if (pfl->aColumns[ iColumn ].pszText)
767                Free (pfl->aColumns[ iColumn ].pszText);
768          Free (pfl->aColumns);
769          }
770       DeleteObject (pfl->hf);
771       Delete (pfl->lItems);
772       DeleteCriticalSection (&pfl->cs);
773       SetWindowLong (hList, 0, 0);
774       Delete (pfl);
775       }
776 }
777
778
779 void FastList_OnStyleChange (HWND hList)
780 {
781    LPFASTLIST pfl;
782    if ((pfl = GetFastList (hList)) != NULL)
783       {
784       BOOL fWasTree = ((pfl->dwStyle & FLS_VIEW_TREE) == FLS_VIEW_TREE) ? TRUE : FALSE;
785
786       pfl->dwStyle = GetWindowLong (hList, GWL_STYLE);
787
788       BOOL fIsTree = ((pfl->dwStyle & FLS_VIEW_TREE) == FLS_VIEW_TREE) ? TRUE : FALSE;
789       if (fWasTree != fIsTree)
790          {
791          FastList_RepairVisibilityFlags (pfl);
792          pfl->fSortBeforePaint = TRUE;
793          }
794
795       FastList_SyncHeader (pfl);
796       FastList_Repaint (pfl);
797       }
798 }
799
800
801 void FastList_OnSize (HWND hList)
802 {
803    LPFASTLIST pfl;
804    if ((pfl = GetFastList (hList)) != NULL)
805       {
806       FastList_SyncHeader (pfl);
807       FastList_Repaint (pfl);
808       }
809 }
810
811
812 void FastList_OnPaint (HWND hList)
813 {
814    LPFASTLIST pfl;
815    if ((pfl = GetFastList (hList)) != NULL)
816       {
817       if (pfl->nBegin)
818          {
819          pfl->fRepaintRequired = TRUE;
820          }
821       else // (!pfl->nBegin)
822          {
823          if (pfl->fSortBeforePaint)
824             FastList_PerformSort (pfl);
825          if (pfl->fSyncIndicesBeforePaint)
826             FastList_RepairVisibilityIndices (pfl);
827          if (pfl->fSyncScrollBeforePaint)
828             FastList_SyncScroll (pfl);
829          if (pfl->hEnsureVisible)
830             FastList_PerformEnsureVisible (pfl);
831          }
832
833       PAINTSTRUCT ps;
834       HDC hdc = BeginPaint (hList, &ps);
835
836       // If we're showing both scrollbars, grey-wash the rectangle
837       // in the bottom-right where they meet.
838       //
839       if ((pfl->hScrollH) && (pfl->hScrollV))
840          {
841          RECT rBox;
842          GetClientRect (pfl->hList, &rBox);
843          rBox.left = rBox.right - GetSystemMetrics(SM_CXVSCROLL);
844          rBox.top = rBox.bottom - GetSystemMetrics(SM_CYHSCROLL);
845
846          HBRUSH hbr = CreateSolidBrush (GetSysColor (COLOR_BTNFACE));
847          FillRect (hdc, &rBox, hbr);
848          DeleteObject (hbr);
849          }
850
851       // Just what client area is left, after we skip the scrollbar and
852       // header regions?
853       //
854       RECT rClient;
855       GetClientRect (pfl->hList, &rClient);
856       if (pfl->hScrollV)
857          rClient.right -= GetSystemMetrics(SM_CXVSCROLL);
858       if (pfl->hScrollH)
859          rClient.bottom -= GetSystemMetrics(SM_CYHSCROLL);
860       if (pfl->hHeader)
861          rClient.top += FastList_GetHeaderHeight (pfl);
862
863       // If possible, we want to draw onto an off-screen bitmap then blit
864       // the relevant sections onto the client area; this eliminates flicker
865       // as items are erased and redrawn.
866       //
867       HDC hdcTarget = hdc;
868       HBITMAP bmpTargetOld = NULL;
869
870       BOOL fDoubleBuffer;
871       if ((fDoubleBuffer = OpenGlobalBitmap (hdc, &rClient)) == TRUE)
872          {
873          hdcTarget = CreateCompatibleDC (hdc);
874          bmpTargetOld = (HBITMAP)SelectObject (hdcTarget, fg.bmp);
875          }
876
877       HFONT hfOld = (HFONT)SelectObject (hdcTarget, pfl->hf);
878
879       // Determine which objects we can display on the screen, and paint
880       // each in turn. If there is no object at a particular location,
881       // just white- or grey-fill the background.
882       //
883       RECT rTemplate;
884       FastList_CalcItemRect (pfl, 0, &rTemplate, !!pfl->hScrollH, !!pfl->hScrollV);
885
886       HBRUSH hbrBackground = CreateSolidBrush (GetSysColor( (IsWindowEnabled(pfl->hList)) ? COLOR_WINDOW : COLOR_BTNFACE ));
887       FillRect (hdcTarget, &rClient, hbrBackground);
888
889       if (!pfl->nBegin)
890          {
891          switch (pfl->dwStyle & FLS_VIEW_MASK)
892             {
893             case FLS_VIEW_LARGE:
894                {
895                // In Large mode, icons are stacked left-to-right, top-to-bottom.
896                // Find the top index, and for each vertical index thereafter,
897                // find and paint the relevant horizontal indices.
898                //
899                LONG cxItems = cxRECT(rClient) / cxRECT(rTemplate);
900
901                LONG iyTopItem = pfl->dyPixel / cyRECT(rTemplate);
902                LONG iyBottomItem = iyTopItem + DivRoundUp (cyRECT(rClient), cyRECT(rTemplate));
903
904                for (LONG iyItem = iyTopItem; iyItem <= iyBottomItem; ++iyItem)
905                   {
906                   for (LONG ixItem = 0; ixItem < cxItems; ++ixItem)
907                      {
908                      int iItem = (int)( ixItem + cxItems * iyItem );
909                      if (iItem < (int)pfl->cVisible)
910                         {
911                         RECT rItem;
912                         rItem.top = rClient.top + iyItem * cyRECT(rTemplate) - pfl->dyPixel;
913                         rItem.bottom = rItem.top + cyRECT(rTemplate);
914                         rItem.left = rClient.left + ixItem * cxRECT(rTemplate) - pfl->dxPixel;
915                         rItem.right = rItem.left + cxRECT(rTemplate);
916
917                         FastList_CallPaintItem (pfl, hdcTarget, TRUE, FALSE, iItem, &rItem, NULL, NULL, NULL);
918                         }
919                      }
920                   }
921                break;
922                }
923
924             case FLS_VIEW_SMALL:
925                {
926                // In Small mode, icons are stacked top-to-bottom, left-to-right.
927                // Find the left index, and for each horizontal index thereafter,
928                // find and paint the relevant vertical indices.
929                //
930                LONG cyItems = cyRECT(rClient) / cyRECT(rTemplate);
931
932                LONG ixLeftItem = pfl->dxPixel / cxRECT(rTemplate);
933                LONG ixRightItem = ixLeftItem + DivRoundUp (cxRECT(rClient), cxRECT(rTemplate));
934
935                for (LONG ixItem = ixLeftItem; ixItem <= ixRightItem; ++ixItem)
936                   {
937                   for (LONG iyItem = 0; iyItem < cyItems; ++iyItem)
938                      {
939                      int iItem = (int)( iyItem + cyItems * ixItem );
940                      if (iItem < (int)pfl->cVisible)
941                         {
942                         RECT rItem;
943                         rItem.top = rClient.top + iyItem * cyRECT(rTemplate) - pfl->dyPixel;
944                         rItem.bottom = rItem.top + cyRECT(rTemplate);
945                         rItem.left = rClient.left + ixItem * cxRECT(rTemplate) - pfl->dxPixel;
946                         rItem.right = rItem.left + cxRECT(rTemplate);
947
948                         FastList_CallPaintItem (pfl, hdcTarget, TRUE, FALSE, iItem, &rItem, NULL, NULL, NULL);
949                         }
950                      }
951                   }
952                break;
953                }
954
955             case FLS_VIEW_LIST:
956             case FLS_VIEW_TREE:
957             case FLS_VIEW_TREELIST:
958                {
959                // In these modes, icons are stacked top-to-bottom; each takes
960                // the entire width we can give it. Find the top index, and
961                // paint all indices which fit on the screen.
962                //
963                LONG iTopItem = pfl->dyPixel / cyRECT(rTemplate);
964                LONG iBottomItem = iTopItem + DivRoundUp (cyRECT(rClient), cyRECT(rTemplate));
965
966                for (LONG iItem = iTopItem; iItem <= iBottomItem; ++iItem)
967                   {
968                   if (iItem < (int)pfl->cVisible)
969                      {
970                      RECT rItem;
971                      rItem.top = rClient.top + iItem * cyRECT(rTemplate) - pfl->dyPixel;
972                      rItem.bottom = rItem.top + cyRECT(rTemplate);
973                      rItem.left = rClient.left - pfl->dxPixel;
974                      rItem.right = rItem.left + cxRECT(rTemplate);
975
976                      FastList_CallPaintItem (pfl, hdcTarget, TRUE, FALSE, iItem, &rItem, NULL, NULL, NULL);
977                      }
978                   }
979                break;
980                }
981             }
982          }
983
984       if (fDoubleBuffer)
985          {
986          BitBlt (hdc, rClient.left, rClient.top, cxRECT(rClient), cyRECT(rClient),
987                  hdcTarget, rClient.left, rClient.top, SRCCOPY);
988          SelectObject (hdcTarget, bmpTargetOld);
989          DeleteDC (hdcTarget);
990          CloseGlobalBitmap();
991          }
992
993       DeleteObject (hbrBackground);
994       SelectObject (hdcTarget, hfOld);
995       EndPaint (hList, &ps);
996       }
997 }
998
999
1000 void FastList_OnPaintItem (LPFASTLIST pfl, LPFASTLISTDRAWITEM pdi)
1001 {
1002    // Initialize the output FASTLISTITEMREGIONS structure.
1003    // We were given the object's overall rectangle, and will
1004    // calculate each of the other regions as we go along.
1005    //
1006    memset (&pdi->reg, 0x00, sizeof(pdi->reg));
1007    pdi->reg.rItem = pdi->rItem;
1008    pdi->fTextTestHit = FALSE;
1009
1010    // The actual paint routine we'll use depends on the current view.
1011    //
1012    switch (pfl->dwStyle & FLS_VIEW_MASK)
1013       {
1014       case FLS_VIEW_LARGE:
1015          FastList_OnPaintItem_Large (pfl, pdi);
1016          break;
1017
1018       case FLS_VIEW_SMALL:
1019          FastList_OnPaintItem_Small (pfl, pdi);
1020          break;
1021
1022       case FLS_VIEW_LIST:
1023       case FLS_VIEW_TREE:
1024       case FLS_VIEW_TREELIST:
1025          FastList_OnPaintItem_List (pfl, pdi);
1026          break;
1027       }
1028
1029    UnionRect (&pdi->reg.rSelect, &pdi->reg.rHighlight, &pdi->reg.rImage);
1030 }
1031
1032
1033 void FastList_OnPaintItem_TreeLines (LPFASTLIST pfl, LPFASTLISTDRAWITEM pdi, DWORD dwLines, RECT *prThisColumn)
1034 {
1035    if (prThisColumn->left >= prThisColumn->right)
1036       return;
1037
1038    RECT rLines;
1039    rLines.left = prThisColumn->left;
1040    rLines.right = rLines.left +GetSystemMetrics(SM_CXSMICON) +cxAFTER_LIST_ICON;
1041    rLines.top = prThisColumn->top;
1042    rLines.bottom = prThisColumn->bottom;
1043    prThisColumn->left = rLines.right;
1044
1045    LONG xBox = rLines.left + (GetSystemMetrics(SM_CXSMICON) - cxTREE_BOX)/2;
1046    LONG xLine = xBox + cxTREE_BOX/2;
1047
1048    LONG yLine = rLines.top + cyRECT(rLines)/2;
1049    LONG yBox = yLine - cyTREE_BOX/2;
1050
1051    if (dwLines & TREELINE_BOX)
1052       {
1053       pdi->reg.rButton.left = xBox;
1054       pdi->reg.rButton.right = xBox +cxTREE_BOX +1;
1055       pdi->reg.rButton.top = yBox;
1056       pdi->reg.rButton.bottom = yBox +cyTREE_BOX +1;
1057       }
1058
1059    if (pdi->fDraw)
1060       {
1061       COLORREF clrBG = GetSysColor (COLOR_WINDOW);
1062       COLORREF clrFG = GetSysColor (COLOR_BTNSHADOW);
1063       if (!IsWindowEnabled (pfl->hList))
1064          clrBG = GetSysColor (COLOR_BTNFACE);
1065
1066       if (dwLines & TREELINE_HORIZONTAL)
1067          {
1068          for (LONG xx = (dwLines & TREELINE_BOX) ? (xBox +cxTREE_BOX) : (dwLines & TREELINE_UP) ? xLine : (rLines.left+1); xx <= rLines.right+1; xx += cxSPACE_TREELINE)
1069             SetPixel (pdi->hdc, xx, yLine, clrFG);
1070          }
1071
1072       if (dwLines & TREELINE_UP)
1073          {
1074          for (LONG yy = (dwLines & TREELINE_BOX) ? yBox : yLine; yy >= rLines.top; yy -= cySPACE_TREELINE)
1075             SetPixel (pdi->hdc, xLine, yy, clrFG);
1076          }
1077
1078       if (dwLines & TREELINE_DOWN)
1079          {
1080          for (LONG yy = (dwLines & TREELINE_BOX) ? (yBox +cyTREE_BOX) : yLine; yy <= rLines.bottom; yy += cySPACE_TREELINE)
1081             SetPixel (pdi->hdc, xLine, yy, clrFG);
1082          }
1083
1084       if (dwLines & TREELINE_BOX)
1085          {
1086          HPEN hpNew = CreatePen (PS_SOLID, 1, clrFG);
1087          HPEN hpOld = (HPEN)SelectObject (pdi->hdc, hpNew);
1088
1089          MoveToEx (pdi->hdc, xBox, yBox, NULL);
1090          LineTo (pdi->hdc, xBox +cxTREE_BOX, yBox);
1091          LineTo (pdi->hdc, xBox +cxTREE_BOX, yBox +cyTREE_BOX);
1092          LineTo (pdi->hdc, xBox, yBox +cyTREE_BOX);
1093          LineTo (pdi->hdc, xBox, yBox);
1094
1095          SelectObject (pdi->hdc, GetStockObject (BLACK_PEN));
1096
1097          MoveToEx (pdi->hdc, xBox +2, yLine, NULL);
1098          LineTo (pdi->hdc, xBox +cxTREE_BOX -1, yLine);
1099
1100          if ((dwLines & TREELINE_BOXCLOSED) == TREELINE_BOXCLOSED)
1101             {
1102             MoveToEx (pdi->hdc, xLine, yBox +2, NULL);
1103             LineTo (pdi->hdc, xLine, yBox +cyTREE_BOX -1);
1104             }
1105
1106          SelectObject (pdi->hdc, hpOld);
1107          DeleteObject (hpNew);
1108          }
1109       }
1110 }
1111
1112
1113 BOOL FastList_OnPaintItem_DrawImage (LPFASTLIST pfl, LPFASTLISTDRAWITEM pdi, HIMAGELIST hil, int iImage, LONG xImage, LONG yImage, BOOL fHLines)
1114 {
1115    if (hil == NULL)
1116       return FALSE;
1117    if (iImage == IMAGE_NOIMAGE)
1118       return FALSE;
1119
1120    RECT rImage;
1121    rImage.left = xImage;
1122    rImage.right = rImage.left + GetSystemMetrics ((hil == pfl->hilLarge) ? SM_CXICON : SM_CXSMICON);
1123    rImage.top = yImage;
1124    rImage.bottom = rImage.top + GetSystemMetrics ((hil == pfl->hilLarge) ? SM_CYICON : SM_CYSMICON);
1125
1126    if (pdi->fDraw)
1127       {
1128       if (iImage == IMAGE_BLANKIMAGE)
1129          {
1130          if (fHLines && ((pfl->dwStyle & FLS_VIEW_TREE) == FLS_VIEW_TREE))
1131             {
1132             RECT rLine;
1133             rLine.left = xImage;
1134             rLine.right = rLine.left + GetSystemMetrics(SM_CXSMICON);
1135             rLine.top = yImage;
1136             rLine.bottom = rLine.top + GetSystemMetrics(SM_CYSMICON);
1137             FastList_OnPaintItem_TreeLines (pfl, pdi, TREELINE_HORIZONTAL, &rLine);
1138             }
1139          }
1140       else // An actual image was specified to be drawn.
1141          {
1142          if (!ImageList_Draw (hil, iImage, pdi->hdc, rImage.left, rImage.top, (pdi->fDragImage) ? ILD_TRANSPARENT : (!IsWindowEnabled(pfl->hList)) ? ILD_TRANSPARENT : (pdi->hItem->fSelected) ? ILD_SELECTED : (pdi->hItem->fFocused) ? ILD_FOCUS : ILD_TRANSPARENT))
1143             return FALSE;
1144          }
1145       }
1146
1147    if (IsRectEmpty (&pdi->reg.rImage))
1148       pdi->reg.rImage = rImage;
1149    else
1150       UnionRect (&pdi->reg.rImage, &pdi->reg.rImage, &rImage);
1151
1152    return TRUE;
1153 }
1154
1155
1156 void FastList_OnPaintItem_GetItemColors (LPFASTLIST pfl, LPFASTLISTDRAWITEM pdi, COLORREF *pclrFore, COLORREF *pclrBack)
1157 {
1158    if (pdi->fDragImage)
1159       {
1160       *pclrFore = GetSysColor (COLOR_WINDOWTEXT);
1161       *pclrBack = clrTRANSPARENT;
1162       }
1163    else if (!IsWindowEnabled (pfl->hList))
1164       {
1165       *pclrFore = GetSysColor (COLOR_GRAYTEXT);
1166       *pclrBack = GetSysColor (COLOR_BTNFACE);
1167       }
1168    else if ( (pdi->hItem->fSelected) || (pdi->hItem->dwFlags & FLIF_DROPHIGHLIGHT) )
1169       {
1170       *pclrFore = GetSysColor (COLOR_HIGHLIGHTTEXT);
1171       *pclrBack = GetSysColor (COLOR_HIGHLIGHT);
1172       }
1173    else // normal colors
1174       {
1175       *pclrFore = GetSysColor (COLOR_WINDOWTEXT);
1176       *pclrBack = GetSysColor (COLOR_WINDOW);
1177       }
1178 }
1179
1180
1181 void FastList_OnPaintItem_Large (LPFASTLIST pfl, LPFASTLISTDRAWITEM pdi)
1182 {
1183    // If there is an image associated with this item, draw it. Remember
1184    // that if there are *two* images, draw the second one--it's the primary
1185    // image, or the user wouldn't have bothered adding it.
1186    //
1187    int iImage = pdi->hItem->iFirstImage;
1188    if (pdi->hItem->iSecondImage != IMAGE_NOIMAGE)
1189       iImage = pdi->hItem->iSecondImage;
1190    if (iImage != IMAGE_NOIMAGE)
1191       {
1192       LONG xImage = pdi->rItem.left + (cxRECT(pdi->rItem) - GetSystemMetrics(SM_CXICON))/2;
1193       LONG yImage = pdi->rItem.top + cyABOVE_LARGE_ICON;
1194       FastList_OnPaintItem_DrawImage (pfl, pdi, pfl->hilLarge, iImage, xImage, yImage, FALSE);
1195       }
1196
1197    // If there is any column-0 text supplied for this item, draw that below
1198    // the item's icon.
1199    //
1200    LPTSTR pszColumnText = FastList_GetColumnText (pfl, pdi->hItem, 0);
1201    if (pszColumnText && *pszColumnText)
1202       {
1203       RECT rTextSize;
1204       SetRectEmpty (&rTextSize);
1205       rTextSize.right = cxRECT(pdi->rItem) - cxBEFORE_LARGE_TEXT - cxAFTER_LARGE_TEXT -2;
1206
1207       if (!DrawTextEx (pdi->hdc, pszColumnText, lstrlen(pszColumnText), &rTextSize, DT_CENTER | DT_CALCRECT | DT_WORDBREAK | DT_END_ELLIPSIS | DT_NOPREFIX, 0))
1208          rTextSize.right = rTextSize.left;
1209
1210       RECT rText = pdi->rItem;
1211       rText.left += (cxRECT(pdi->rItem)-cxRECT(rTextSize))/2;
1212       rText.right -= (cxRECT(pdi->rItem)-cxRECT(rTextSize))/2;
1213       rText.top += cyABOVE_LARGE_ICON + GetSystemMetrics(SM_CYICON) + cyBELOW_LARGE_ICON + cyABOVE_LARGE_TEXT;
1214       rText.bottom = min( pdi->rItem.bottom - cyBELOW_LARGE_TEXT, (rText.top + cyRECT(rTextSize)) );
1215
1216       pdi->reg.rHighlight = rText;
1217       pdi->reg.rHighlight.left -= cxBEFORE_LARGE_TEXT;
1218       pdi->reg.rHighlight.right += cxAFTER_LARGE_TEXT;
1219       pdi->reg.rHighlight.top -= cyABOVE_LARGE_TEXT;
1220       pdi->reg.rHighlight.bottom += cyBELOW_LARGE_TEXT;
1221
1222       pdi->reg.rLabel = pdi->reg.rHighlight;
1223       if (PtInRect (&rText, pdi->ptTextTest))
1224          pdi->fTextTestHit = TRUE;
1225
1226       if (pdi->fDraw)
1227          {
1228          COLORREF clrFore;
1229          COLORREF clrBack;
1230          FastList_OnPaintItem_GetItemColors (pfl, pdi, &clrFore, &clrBack);
1231
1232          if (pdi->hItem->fSelected)
1233             {
1234             HBRUSH hbr = CreateSolidBrush (clrBack);
1235             FillRect (pdi->hdc, &pdi->reg.rHighlight, hbr);
1236             DeleteObject (hbr);
1237             }
1238
1239          COLORREF clrBackOld = SetBkColor (pdi->hdc, clrBack);
1240          COLORREF clrForeOld = SetTextColor (pdi->hdc, clrFore);
1241
1242          DrawTextEx (pdi->hdc, pszColumnText, lstrlen(pszColumnText),
1243                      &rText, DT_CENTER | DT_WORDBREAK | DT_END_ELLIPSIS | DT_NOPREFIX, 0);
1244
1245          SetTextColor (pdi->hdc, clrForeOld);
1246          SetBkColor (pdi->hdc, clrBackOld);
1247
1248          if (pdi->hItem->fFocused && IsWindowEnabled (pfl->hList))
1249             DrawFocusRect (pdi->hdc, &pdi->reg.rHighlight);
1250          }
1251       }
1252 }
1253
1254
1255 void FastList_OnPaintItem_Small (LPFASTLIST pfl, LPFASTLISTDRAWITEM pdi)
1256 {
1257    // If there is an image associated with this item, draw it. Remember
1258    // that if there are *two* images, draw the second one--it's the primary
1259    // image, or the user wouldn't have bothered adding it.
1260    //
1261    BOOL fImage = FALSE;
1262
1263    int iImage = pdi->hItem->iFirstImage;
1264    if (pdi->hItem->iSecondImage != IMAGE_NOIMAGE)
1265       iImage = pdi->hItem->iSecondImage;
1266    if (iImage != IMAGE_NOIMAGE)
1267       {
1268       LONG xImage = pdi->rItem.left + cxBEFORE_SMALL_ICON;
1269       LONG yImage = pdi->rItem.top + (cyRECT(pdi->rItem) - GetSystemMetrics(SM_CYSMICON))/2;
1270       fImage = FastList_OnPaintItem_DrawImage (pfl, pdi, pfl->hilSmall, iImage, xImage, yImage, FALSE);
1271       }
1272
1273    // If there is any column-0 text supplied for this item, draw that to the
1274    // right of the item's icon.
1275    //
1276    LPTSTR pszColumnText = FastList_GetColumnText (pfl, pdi->hItem, 0);
1277    if (pszColumnText && *pszColumnText)
1278       {
1279       RECT rTextSize = pdi->rItem;
1280       if (!DrawTextEx (pdi->hdc, pszColumnText, lstrlen(pszColumnText), &rTextSize, DT_CALCRECT | DT_END_ELLIPSIS | DT_NOPREFIX, 0))
1281          rTextSize.right = rTextSize.left;
1282
1283       RECT rText = pdi->rItem;
1284       rText.left += cxBEFORE_SMALL_TEXT + fImage * (GetSystemMetrics(SM_CXSMICON) + cxBEFORE_SMALL_ICON + cxAFTER_SMALL_ICON);
1285       rText.right = min( pdi->rItem.right - cxAFTER_SMALL_TEXT, (rText.left + cxRECT(rTextSize)) );
1286       rText.top += cyABOVE_SMALL_TEXT;
1287       rText.bottom -= cyBELOW_SMALL_TEXT;
1288
1289       pdi->reg.rHighlight = rText;
1290       pdi->reg.rHighlight.left -= cxBEFORE_SMALL_TEXT;
1291       pdi->reg.rHighlight.right += cxAFTER_SMALL_TEXT;
1292       pdi->reg.rHighlight.top -= cyABOVE_SMALL_TEXT;
1293       pdi->reg.rHighlight.bottom += cyBELOW_SMALL_TEXT;
1294
1295       pdi->reg.rLabel = pdi->reg.rHighlight;
1296       if (PtInRect (&rText, pdi->ptTextTest))
1297          pdi->fTextTestHit = TRUE;
1298
1299       if (pdi->fDraw)
1300          {
1301          COLORREF clrFore;
1302          COLORREF clrBack;
1303          FastList_OnPaintItem_GetItemColors (pfl, pdi, &clrFore, &clrBack);
1304
1305          if (pdi->hItem->fSelected)
1306             {
1307             HBRUSH hbr = CreateSolidBrush (clrBack);
1308             FillRect (pdi->hdc, &pdi->reg.rHighlight, hbr);
1309             DeleteObject (hbr);
1310             }
1311
1312          COLORREF clrBackOld = SetBkColor (pdi->hdc, clrBack);
1313          COLORREF clrForeOld = SetTextColor (pdi->hdc, clrFore);
1314
1315          DrawTextEx (pdi->hdc, pszColumnText, lstrlen(pszColumnText),
1316                      &rText, DT_END_ELLIPSIS | DT_NOPREFIX, 0);
1317
1318          SetTextColor (pdi->hdc, clrForeOld);
1319          SetBkColor (pdi->hdc, clrBackOld);
1320
1321          if (pdi->hItem->fFocused && IsWindowEnabled (pfl->hList))
1322             DrawFocusRect (pdi->hdc, &pdi->reg.rHighlight);
1323          }
1324       }
1325 }
1326
1327
1328 void FastList_OnPaintItem_Tree (LPFASTLIST pfl, LPFASTLISTDRAWITEM pdi, HLISTITEM hItem, RECT *prThisColumn)
1329 {
1330    // This routine is supposed to draw the tree structure off to the left
1331    // of an item. To do that, it calls itself recursively to draw the
1332    // icon areas of each of its parents.
1333    //
1334    if (hItem->hTreeParent != NULL)
1335       {
1336       FastList_OnPaintItem_Tree (pfl, pdi, hItem->hTreeParent, prThisColumn);
1337       }
1338
1339    // For any given item, the first thing to determine is how many icons
1340    // that item has.
1341    //
1342    int iLeftImage = hItem->iFirstImage;
1343    int iRightImage = IMAGE_NOIMAGE;
1344
1345    if (hItem->iSecondImage != IMAGE_NOIMAGE)
1346       {
1347       iLeftImage = hItem->iSecondImage;
1348       iRightImage = hItem->iFirstImage;
1349       }
1350
1351    // What lines/boxes should we draw?
1352    //
1353    BOOL fHLines = FALSE;
1354
1355    if ((hItem->hTreeParent) || (pfl->dwStyle & FLS_LINESATROOT))
1356       {
1357       fHLines = TRUE;
1358
1359       DWORD dwLines = 0;
1360       if (hItem == pdi->hItem)
1361          dwLines |= TREELINE_HORIZONTAL;
1362       if ((hItem == pdi->hItem) && (hItem->hTreeChild))
1363          dwLines |= (hItem->fExpanded) ? TREELINE_BOXOPEN : TREELINE_BOXCLOSED;
1364       if (hItem->hTreeNext)
1365          dwLines |= TREELINE_DOWN;
1366       if ((dwLines != 0) && !((!hItem->hTreeParent && !hItem->hTreePrevious) && (hItem == pdi->hItem)))
1367          dwLines |= TREELINE_UP;
1368
1369       FastList_OnPaintItem_TreeLines (pfl, pdi, dwLines, prThisColumn);
1370       }
1371
1372    // The lines we (maybe) just drew took the place of an icon for this
1373    // item. Maybe draw blank space, to pad for the second icon
1374    //
1375    if ( (hItem != pdi->hItem) && (iLeftImage != IMAGE_NOIMAGE) && (iRightImage != IMAGE_NOIMAGE) )
1376       {
1377       FastList_OnPaintItem_TreeLines (pfl, pdi, 0, prThisColumn);
1378       }
1379 }
1380
1381
1382 void FastList_OnPaintItem_List (LPFASTLIST pfl, LPFASTLISTDRAWITEM pdi)
1383 {
1384    // Painting a tree or list is done column by column. Remember
1385    // We may have a "long column"--that's a column that doesn't
1386    // necessarily respect the header column boundaries.
1387    //
1388    RECT rItemRemaining = pdi->rItem;
1389    pdi->reg.rHighlight = pdi->rItem;
1390
1391    int icolLong = -1;
1392    if (pdi->fDragImage)
1393       {
1394       icolLong = 0;
1395       }
1396    else if ((pfl->dwStyle & FLS_VIEW_TREELIST) == FLS_VIEW_TREE)
1397       {
1398       icolLong = 0;
1399       }
1400    else if (pfl->dwStyle & FLS_LONGCOLUMNS)
1401       {
1402       for (size_t icol = 0; icol < pfl->cColumns; ++icol)
1403          {
1404          if (!pfl->aColumns[ icol ].pszText)
1405             break;
1406
1407          LPTSTR pszColumnText = FastList_GetColumnText (pfl, pdi->hItem, icol);
1408          if (*pszColumnText)
1409             {
1410             icolLong = icol;
1411             if ((pfl->aColumns[ icol ].fmt & HDF_JUSTIFYMASK) != HDF_LEFT)
1412                icolLong = -1; // this can't be the long column
1413             }
1414          }
1415       }
1416
1417    // Select appropriate text colors. Since FastLists always draw highlights
1418    // all the way across the item, we won't change colors from column to
1419    // column.
1420    //
1421    COLORREF clrFore;
1422    COLORREF clrBack;
1423    FastList_OnPaintItem_GetItemColors (pfl, pdi, &clrFore, &clrBack);
1424
1425    COLORREF clrBackOld = SetBkColor (pdi->hdc, clrBack);
1426    COLORREF clrForeOld = SetTextColor (pdi->hdc, clrFore);
1427
1428    // Now that we know where the long column is, we're ready to start
1429    // painting columns.
1430    //
1431    for (int icol = 0; (icolLong == -1) || (icol <= icolLong); ++icol)
1432       {
1433       if (icol && !pfl->cColumns)
1434          break;
1435       else if ((pfl->cColumns) && ((icol >= (int)pfl->cColumns) || (!pfl->aColumns[ icol ].pszText)))
1436          break;
1437
1438       // The rules for the long column are slightly different than the
1439       // rules for all other columns.
1440       //
1441       LONG cxColumn = pdi->rItem.right - rItemRemaining.left;
1442       int fmtColumn = HDF_LEFT;
1443
1444       if ((icol != icolLong) && (pfl->cColumns) && (icol < (int)pfl->cColumns))
1445          {
1446          cxColumn = pfl->aColumns[ icol ].cxy;
1447          fmtColumn = pfl->aColumns[ icol ].fmt & HDF_JUSTIFYMASK;
1448          }
1449
1450       // Remember where this column ends and the next column begins.
1451       //
1452       RECT rThisColumn = rItemRemaining;
1453       rThisColumn.right = rThisColumn.left + cxColumn;
1454
1455       // Get the text for this column, and figure out how big it will be.
1456       //
1457       LPTSTR pszColumnText = FastList_GetColumnText (pfl, pdi->hItem, icol);
1458
1459       RECT rTextSize = rThisColumn;
1460       if (!DrawTextEx (pdi->hdc, pszColumnText, lstrlen(pszColumnText), &rTextSize, DT_CALCRECT | DT_END_ELLIPSIS | DT_NOPREFIX, 0))
1461          rTextSize.right = rTextSize.left;
1462
1463       // Draw the icons and/or lines for the column. The behavior here
1464       // depends on whether we're drawing a tree or a list, or both.
1465       //
1466       BOOL fHLines = FALSE;
1467       if ((icol == 0) && ((pfl->dwStyle & FLS_VIEW_TREE) == FLS_VIEW_TREE) && (!pdi->fDragImage))
1468          {
1469          FastList_OnPaintItem_Tree (pfl, pdi, pdi->hItem, &rThisColumn);
1470          fHLines = TRUE;
1471          }
1472
1473       if (icol == 0)
1474          {
1475          int iLeftImage = pdi->hItem->iFirstImage;
1476          int iRightImage = IMAGE_NOIMAGE;
1477
1478          if ((pdi->hItem->iSecondImage != IMAGE_NOIMAGE) &&
1479              (rThisColumn.left < rThisColumn.right) &&
1480              (!pdi->fDragImage))
1481             {
1482             iLeftImage = pdi->hItem->iSecondImage;
1483             iRightImage = pdi->hItem->iFirstImage;
1484             }
1485
1486          if ((iLeftImage != IMAGE_NOIMAGE) &&
1487              (rThisColumn.left < rThisColumn.right))
1488             {
1489             rThisColumn.left += cxBEFORE_LIST_ICON;
1490             pdi->reg.rImage = rThisColumn;
1491
1492             LONG xImage = rThisColumn.left;
1493             LONG yImage = rThisColumn.top + (cyRECT(pdi->rItem) - GetSystemMetrics(SM_CYSMICON))/2;
1494             if (FastList_OnPaintItem_DrawImage (pfl, pdi, pfl->hilSmall, iLeftImage, xImage, yImage, fHLines))
1495                {
1496                rThisColumn.left += GetSystemMetrics (SM_CXSMICON);
1497                if (iRightImage != IMAGE_NOIMAGE)
1498                   {
1499                   rThisColumn.left += cxBETWEEN_LIST_ICONS;
1500                   xImage = rThisColumn.left;
1501                   yImage = rThisColumn.top + (cyRECT(pdi->rItem) - GetSystemMetrics(SM_CYSMICON))/2;
1502                   if (FastList_OnPaintItem_DrawImage (pfl, pdi, pfl->hilSmall, iRightImage, xImage, yImage, fHLines))
1503                      rThisColumn.left += GetSystemMetrics (SM_CXSMICON);
1504                   }
1505                }
1506
1507             pdi->reg.rImage.right = rThisColumn.left;
1508             rThisColumn.left += cxAFTER_LIST_ICON;
1509             }
1510          }
1511
1512       // Determine where the text will go in this column. We have to pass
1513       // that information back (for column 0) as reg.rLabel; we also have
1514       // to test the text's position against {pdi->ptTextTest} to see if the
1515       // user just clicked on the text.
1516       //
1517       RECT rText = rThisColumn;
1518       rText.left += cxBEFORE_LIST_TEXT;
1519       rText.right -= cxAFTER_LIST_TEXT;
1520       rText.top += cyABOVE_LIST_TEXT;
1521       rText.bottom -= cyBELOW_LIST_TEXT;
1522
1523       if (icol != 0)
1524          rText.left += cxBEFORE_COLUMN_TEXT;
1525
1526       RECT rTextJustified = rText;
1527       rTextJustified.right = min( rText.right, rText.left + cxRECT(rTextSize) );
1528
1529       LONG dxJustify = 0;
1530       if (fmtColumn == HDF_CENTER)
1531          dxJustify = (cxRECT(rText) - cxRECT(rTextJustified))/2;
1532       else if (fmtColumn == HDF_RIGHT)
1533          dxJustify = cxRECT(rText) - cxRECT(rTextJustified);
1534       rTextJustified.left += dxJustify;
1535       rTextJustified.right += dxJustify;
1536
1537       if (icol == 0)
1538          pdi->reg.rLabel = rTextJustified;
1539
1540       if (PtInRect (&rTextJustified, pdi->ptTextTest))
1541          pdi->fTextTestHit = TRUE;
1542
1543       // Having determined where the text will go, we should record
1544       // where the highlight will be drawn.
1545       //
1546       if (icol == 0)
1547          {
1548          pdi->reg.rHighlight.left = rThisColumn.left;
1549          }
1550       if (icol == icolLong)
1551          {
1552          pdi->reg.rHighlight.right = min( pdi->reg.rHighlight.right, rTextJustified.right + cxAFTER_LIST_TEXT );
1553          }
1554
1555       // Okay, it's time to actually draw the text. Didn't think we'd
1556       // ever get here, did you?  :)
1557       //
1558       if (pdi->fDraw)
1559          {
1560          if ((!pdi->fDragImage) && ((pdi->hItem->fSelected) || (pdi->hItem->dwFlags & FLIF_DROPHIGHLIGHT)))
1561             {
1562             RECT rHighlightInColumn;
1563             IntersectRect (&rHighlightInColumn, &pdi->reg.rHighlight, &rThisColumn);
1564
1565             HBRUSH hbr = CreateSolidBrush (clrBack);
1566             FillRect (pdi->hdc, &rHighlightInColumn, hbr);
1567             DeleteObject (hbr);
1568             }
1569
1570          DrawTextEx (pdi->hdc, pszColumnText, lstrlen(pszColumnText),
1571                      &rTextJustified, DT_END_ELLIPSIS | DT_NOPREFIX | DT_SINGLELINE | DT_VCENTER, 0);
1572          }
1573
1574       rItemRemaining.left = rThisColumn.right;
1575       }
1576
1577    // Finally, draw the focus rectangle and restore the original text colors.
1578    //
1579    if (pdi->hItem->fFocused && pdi->fDraw && IsWindowEnabled (pfl->hList) && (!pdi->fDragImage))
1580       DrawFocusRect (pdi->hdc, &pdi->reg.rHighlight);
1581
1582    SetTextColor (pdi->hdc, clrForeOld);
1583    SetBkColor (pdi->hdc, clrBackOld);
1584 }
1585
1586
1587 void FastList_OnScroll (HWND hList, UINT msg, WPARAM wp, LPARAM lp)
1588 {
1589    LPFASTLIST pfl;
1590    if ((pfl = GetFastList (hList)) != NULL)
1591       {
1592       HWND hScroll = (msg == WM_VSCROLL) ? pfl->hScrollV : pfl->hScrollH;
1593
1594       SCROLLINFO si;
1595       memset (&si, 0x00, sizeof(si));
1596       si.cbSize = sizeof(si);
1597       si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS | SIF_TRACKPOS;
1598       GetScrollInfo (hScroll, SB_CTL, &si);
1599
1600       LONG posNew = si.nPos;
1601       BOOL fRepaint = TRUE;
1602
1603       switch (LOWORD(wp))
1604          {
1605          case SB_TOP:
1606             posNew = 0;
1607             break;
1608
1609          case SB_BOTTOM:
1610             posNew = si.nMax - si.nPage;
1611             break;
1612
1613          case SB_PAGEUP:
1614             posNew -= si.nPage;
1615             break;
1616
1617          case SB_PAGEDOWN:
1618             posNew += si.nPage;
1619             break;
1620
1621          case SB_LINEUP:
1622             posNew -= FastList_GetListHeight (pfl);
1623             break;
1624
1625          case SB_LINEDOWN:
1626             posNew += FastList_GetListHeight (pfl);
1627             break;
1628
1629          case SB_THUMBTRACK:
1630             posNew = si.nTrackPos;
1631             fRepaint = FALSE;
1632             break;
1633          }
1634
1635       posNew = limit( 0, posNew, (LONG)(si.nMax - si.nPage) );
1636       SetScrollPos (hScroll, SB_CTL, posNew, fRepaint);
1637
1638       // If we just scrolled vertically, repaint.  If we just scrolled
1639       // horizontally, we'll have to sync the header first.
1640       //
1641       if (msg == WM_HSCROLL)
1642          pfl->dxPixel = posNew;
1643       else
1644          pfl->dyPixel = posNew;
1645
1646       FastList_Repaint (pfl);
1647       if (msg == WM_HSCROLL)
1648          FastList_ScrollHeader (pfl);
1649       }
1650 }
1651
1652
1653 void FastList_OnRightButtonDown (HWND hList)
1654 {
1655    FastList_ButtonDown (hList, TRUE, FALSE);
1656    FastList_Notify_Generic (GetFastList(hList), FLN_RCLICK);
1657 }
1658
1659
1660 void FastList_OnLeftButtonDown (HWND hList)
1661 {
1662    FastList_ButtonDown (hList, FALSE, FALSE);
1663    FastList_Notify_Generic (GetFastList(hList), FLN_LCLICK);
1664 }
1665
1666
1667 void FastList_OnLeftButtonDouble (HWND hList)
1668 {
1669    FastList_ButtonDown (hList, FALSE, TRUE);
1670    FastList_ButtonUp (hList, FALSE, TRUE);
1671    FastList_Notify_Generic (GetFastList(hList), FLN_LDBLCLICK);
1672 }
1673
1674
1675 void FastList_OnKeyPress (HWND hList, TCHAR ch)
1676 {
1677    LPFASTLIST pfl;
1678    if ((pfl = GetFastList (hList)) != NULL)
1679       {
1680       switch (ch)
1681          {
1682          case VK_LEFT:
1683             if (!FastList_OnKeyPress_EnsureSelection (pfl))
1684                break;
1685             if ((pfl->hFocused->hTreeChild) && (pfl->hFocused->fExpanded) && (!(pfl->hFocused->dwFlags & FLIF_DISALLOW_COLLAPSE)) && (pfl->dwStyle & FLS_VIEW_TREE))
1686                {
1687                FastList_Collapse (hList, pfl->hFocused);
1688                break;
1689                }
1690             if (pfl->hFocused->index != 0)
1691                FastList_OnKeyPress_ChangeSelection (pfl, pfl->aVisibleHeap[ pfl->hFocused->index -1 ]);
1692             break;
1693
1694          case VK_UP:
1695             if (!FastList_OnKeyPress_EnsureSelection (pfl))
1696                break;
1697             if (pfl->hFocused->index != 0)
1698                FastList_OnKeyPress_ChangeSelection (pfl, pfl->aVisibleHeap[ pfl->hFocused->index -1 ]);
1699             break;
1700
1701          case VK_RIGHT:
1702             if (!FastList_OnKeyPress_EnsureSelection (pfl))
1703                break;
1704             if ((pfl->hFocused->hTreeChild) && (!pfl->hFocused->fExpanded) && (pfl->dwStyle & FLS_VIEW_TREE))
1705                {
1706                FastList_Expand (hList, pfl->hFocused);
1707                FastList_Notify_ItemExpand (pfl, pfl->hFocused);
1708                break;
1709                }
1710             if (pfl->hFocused->index < (int)(pfl->cVisible-1))
1711                FastList_OnKeyPress_ChangeSelection (pfl, pfl->aVisibleHeap[ pfl->hFocused->index +1 ]);
1712             break;
1713
1714          case VK_DOWN:
1715             if (!FastList_OnKeyPress_EnsureSelection (pfl))
1716                break;
1717             if (pfl->hFocused->index < (int)(pfl->cVisible-1))
1718                FastList_OnKeyPress_ChangeSelection (pfl, pfl->aVisibleHeap[ pfl->hFocused->index +1 ]);
1719             break;
1720
1721          case VK_HOME:
1722             if (!FastList_OnKeyPress_EnsureSelection (pfl))
1723                break;
1724             if (pfl->cVisible)
1725                FastList_OnKeyPress_ChangeSelection (pfl, pfl->aVisibleHeap[0]);
1726             break;
1727
1728          case VK_END:
1729             if (!FastList_OnKeyPress_EnsureSelection (pfl))
1730                break;
1731             if (pfl->cVisible)
1732                FastList_OnKeyPress_ChangeSelection (pfl, pfl->aVisibleHeap[ pfl->cVisible -1 ]);
1733             break;
1734
1735          case TEXT(' '):
1736             if (!FastList_OnKeyPress_EnsureSelection (pfl))
1737                break;
1738             FastList_Begin (pfl->hList);
1739             if (pfl->hFocused->fSelected && fIsControlDown())
1740                FastList_SelectItem (pfl->hList, pfl->hFocused, FALSE);
1741             else if (!pfl->hFocused->fSelected && !fIsShiftDown())
1742                FastList_SelectItem (pfl->hList, pfl->hFocused, TRUE);
1743             else if (!pfl->hFocused->fSelected)
1744                FastList_OnKeyPress_ChangeSelection (pfl, pfl->hFocused);
1745             FastList_End (pfl->hList);
1746             break;
1747          }
1748       }
1749 }
1750
1751
1752 BOOL FastList_OnKeyPress_EnsureSelection (LPFASTLIST pfl)
1753 {
1754    if (pfl->fSortBeforePaint)
1755       FastList_PerformSort (pfl);
1756    if (pfl->fSyncIndicesBeforePaint)
1757       FastList_RepairVisibilityIndices (pfl);
1758    if (pfl->hFocused && pfl->hFocused->fVisible)
1759       return TRUE;
1760
1761    // Find the item at the top of the list.
1762    //
1763    RECT rClient;
1764    GetClientRect (pfl->hList, &rClient);
1765    if (pfl->hScrollV)
1766       rClient.right -= GetSystemMetrics(SM_CXVSCROLL);
1767    if (pfl->hScrollH)
1768       rClient.bottom -= GetSystemMetrics(SM_CYHSCROLL);
1769    if (pfl->hHeader)
1770       rClient.top += FastList_GetHeaderHeight (pfl);
1771
1772    RECT rTemplate;
1773    FastList_CalcItemRect (pfl, 0, &rTemplate, !!pfl->hScrollH, !!pfl->hScrollV);
1774
1775    int indexTopLeft = -1;
1776
1777    switch (pfl->dwStyle & FLS_VIEW_MASK)
1778       {
1779       case FLS_VIEW_LARGE:
1780          {
1781          LONG cxItems = cxRECT(rClient) / cxRECT(rTemplate);
1782          indexTopLeft = pfl->dyPixel / cyRECT(rTemplate);
1783          break;
1784          }
1785
1786       case FLS_VIEW_SMALL:
1787          {
1788          LONG cyItems = cyRECT(rClient) / cyRECT(rTemplate);
1789          indexTopLeft = pfl->dxPixel / cxRECT(rTemplate);
1790          break;
1791          }
1792
1793       case FLS_VIEW_LIST:
1794       case FLS_VIEW_TREE:
1795       case FLS_VIEW_TREELIST:
1796          indexTopLeft = pfl->dyPixel / cyRECT(rTemplate);
1797          break;
1798       }
1799
1800    if ((indexTopLeft >= 0) && (indexTopLeft < (int)pfl->cVisible))
1801       {
1802       FastList_SetFocus (pfl->hList, pfl->aVisibleHeap[ indexTopLeft ]);
1803       }
1804
1805    return FALSE;
1806 }
1807
1808
1809 void FastList_OnKeyPress_ChangeSelection (LPFASTLIST pfl, HLISTITEM hSelect)
1810 {
1811    FastList_Begin (pfl->hList);
1812    FastList_SetFocus (pfl->hList, hSelect);
1813    FastList_EnsureVisible (pfl->hList, hSelect);
1814
1815    // If Control is not down, begin by de-selecting all items
1816    // If Shift is not down, anchor and select the specified item
1817    // If Shift is down, select from the anchor to the specified item
1818    //
1819    HLISTITEM hAnchor = pfl->hAnchor;
1820    if (!fIsControlDown())
1821       {
1822       FastList_SelectNone (pfl->hList);
1823       }
1824    if ((!fIsShiftDown() || !hAnchor) && (!fIsControlDown()))
1825       {
1826       FastList_SelectItem (pfl->hList, hSelect, TRUE);
1827       pfl->hAnchor = hSelect;
1828       }
1829    if (fIsShiftDown() && hAnchor)
1830       {
1831       FastList_PerformSelectRange (pfl, hAnchor, hSelect);
1832       if (hAnchor->fSelected)
1833          pfl->hAnchor = hAnchor;
1834       }
1835
1836    FastList_End (pfl->hList);
1837    FastList_Notify_ItemSelect (pfl);
1838 }
1839
1840
1841 /*
1842  * EXPORTED ROUTINES __________________________________________________________
1843  *
1844  */
1845
1846 void FastList_OnCommand_Begin (HWND hList)
1847 {
1848    LPFASTLIST pfl;
1849    if ((pfl = GetFastList (hList)) != NULL)
1850       {
1851       pfl->nBegin ++;
1852       }
1853 }
1854
1855
1856 void FastList_OnCommand_End (HWND hList, BOOL fForce)
1857 {
1858    LPFASTLIST pfl;
1859    if ((pfl = GetFastList (hList)) != NULL)
1860       {
1861       if (fForce)
1862          pfl->nBegin = 0;
1863       else if (pfl->nBegin)
1864          pfl->nBegin --;
1865
1866       if (pfl->nBegin == 0)
1867          {
1868          if (pfl->fSyncHeaderRequired)
1869             FastList_SyncHeader (pfl);
1870          if (pfl->fSortBeforePaint)
1871             pfl->fRepaintRequired = TRUE;
1872          if (pfl->fSyncScrollBeforePaint)
1873             pfl->fRepaintRequired = TRUE;
1874          if (pfl->fRepaintRequired)
1875             FastList_Repaint (pfl);
1876          }
1877       }
1878 }
1879
1880
1881 HLISTITEM FastList_OnCommand_AddItem (HWND hList, LPFASTLISTADDITEM pai)
1882 {
1883    HLISTITEM hItem = NULL;
1884
1885    LPFASTLIST pfl;
1886    if ((pfl = GetFastList (hList)) != NULL)
1887       {
1888       hItem = FastList_CreateItem (pfl);
1889
1890       // Add this item to the hList chain
1891       //
1892       if ((hItem->hListNext = pfl->hListFirst) != NULL)
1893          hItem->hListNext->hListPrevious = hItem;
1894       pfl->hListFirst = hItem;
1895
1896       // Add this item to the hTree chain
1897       //
1898       if ((hItem->hTreeParent = pai->hParent) != NULL)
1899          {
1900          if ((hItem->hTreeNext = hItem->hTreeParent->hTreeChild) != NULL)
1901             hItem->hTreeNext->hTreePrevious = hItem;
1902          hItem->hTreeParent->hTreeChild = hItem;
1903          }
1904       else // (hItem->hTreeParent == NULL)
1905          {
1906          if ((hItem->hTreeNext = pfl->hTreeFirst) != NULL)
1907             hItem->hTreeNext->hTreePrevious = hItem;
1908          pfl->hTreeFirst = hItem;
1909          }
1910
1911       hItem->iFirstImage = pai->iFirstImage;
1912       hItem->iSecondImage = pai->iSecondImage;
1913       hItem->dwFlags = pai->dwFlags;
1914       hItem->lpUser = pai->lParam;
1915
1916       if (pai->pszText)
1917          {
1918          REALLOC (hItem->apszText, hItem->cpszText, 1, max(1,pfl->cColumns));
1919          hItem->apszText[0] = (LPTSTR)Allocate (sizeof(TCHAR)*(1+lstrlen(pai->pszText)));
1920          lstrcpy (hItem->apszText[0], pai->pszText);
1921          }
1922
1923       hItem->fVisible = FALSE;
1924       FastList_RepairOneVisibilityFlag (pfl, hItem);
1925
1926       pfl->lItems->Add (hItem);
1927       pfl->fSortBeforePaint = TRUE;
1928       pfl->fSyncScrollBeforePaint = TRUE;
1929       FastList_Notify_AddItem (pfl, hItem);
1930       FastList_Repaint (pfl);
1931       }
1932
1933    return hItem;
1934 }
1935
1936
1937 void FastList_OnCommand_RemoveItem (HWND hList, HLISTITEM hItem)
1938 {
1939    LPFASTLIST pfl;
1940    if ((pfl = GetFastList (hList)) != NULL)
1941       {
1942       if (hItem != NULL)
1943          {
1944          FastList_DeleteItem (pfl, hItem);
1945          }
1946       else
1947          {
1948          FastList_Begin (pfl->hList);
1949
1950          while ((hItem = (HLISTITEM)(pfl->lItems->GetFirstObject())) != NULL)
1951             FastList_DeleteItem (pfl, hItem);
1952
1953          FastList_End (pfl->hList);
1954          }
1955
1956       FastList_Repaint (pfl);
1957       FastList_Notify_ItemSelect (pfl);
1958       }
1959 }
1960
1961
1962 LPCTSTR FastList_OnCommand_GetItemText (HWND hList, HLISTITEM hItem, int iColumn)
1963 {
1964    if (hItem == NULL)
1965       return NULL;
1966    if ((iColumn < 0) || (iColumn >= (int)(hItem->cpszText)))
1967       return NULL;
1968    return hItem->apszText[ iColumn ];
1969 }
1970
1971
1972 void FastList_OnCommand_SetItemText (HWND hList, LPFASTLISTITEMCOLUMN pflic, LPCTSTR pszText)
1973 {
1974    LPFASTLIST pfl;
1975    if ((pfl = GetFastList (hList)) != NULL)
1976       {
1977       if ((pflic) && (pflic->hItem) && (pflic->icol >= 0))
1978          {
1979          if (REALLOC (pflic->hItem->apszText, pflic->hItem->cpszText, pflic->icol+1, 1))
1980             {
1981             if (pflic->hItem->apszText[ pflic->icol ] != NULL)
1982                {
1983                Free (pflic->hItem->apszText[ pflic->icol ]);
1984                pflic->hItem->apszText[ pflic->icol ] = NULL;
1985                }
1986             if (pszText != NULL)
1987                {
1988                pflic->hItem->apszText[ pflic->icol ] = (LPTSTR)Allocate (sizeof(TCHAR)*(1+lstrlen(pszText)));
1989                lstrcpy (pflic->hItem->apszText[ pflic->icol ], pszText);
1990                }
1991             pfl->fSortBeforePaint = TRUE;
1992             FastList_Notify_ItemChanged (pfl, pflic->hItem);
1993             FastList_Repaint (pfl);
1994             }
1995          }
1996       }
1997 }
1998
1999
2000 LPARAM FastList_OnCommand_GetItemParam (HWND hList, HLISTITEM hItem)
2001 {
2002    return (hItem == NULL) ? 0 : (hItem->lpUser);
2003 }
2004
2005
2006 void FastList_OnCommand_SetItemParam (HWND hList, HLISTITEM hItem, LPARAM lpUser)
2007 {
2008    LPFASTLIST pfl;
2009    if ((pfl = GetFastList (hList)) != NULL)
2010       {
2011       if (hItem != NULL)
2012          {
2013          hItem->lpUser = lpUser;
2014          pfl->lItems->Update (hItem);
2015
2016          pfl->fSortBeforePaint = TRUE;
2017          FastList_Notify_ItemChanged (pfl, hItem);
2018          FastList_Repaint (pfl);
2019          }
2020       }
2021 }
2022
2023
2024 DWORD FastList_OnCommand_GetItemFlags (HWND hList, HLISTITEM hItem)
2025 {
2026    return (hItem == NULL) ? 0 : (hItem->dwFlags);
2027 }
2028
2029
2030 void FastList_OnCommand_SetItemFlags (HWND hList, HLISTITEM hItem, DWORD dwFlagsNew)
2031 {
2032    LPFASTLIST pfl;
2033    if ((pfl = GetFastList (hList)) != NULL)
2034       {
2035       if (hItem != NULL)
2036          {
2037          FastList_Begin (hList);
2038
2039          DWORD dwFlagsOld = hItem->dwFlags;
2040          hItem->dwFlags = dwFlagsNew;
2041
2042          if ( (dwFlagsOld & FLIF_TREEVIEW_ONLY) != (dwFlagsNew & FLIF_TREEVIEW_ONLY) )
2043             {
2044             FastList_RepairOneVisibilityFlag (pfl, hItem);
2045             FastList_Repaint (pfl);
2046             }
2047
2048          if ( (dwFlagsNew & FLIF_DISALLOW_SELECT) && (hItem->fSelected) )
2049             {
2050             FastList_PerformSelectItem (pfl, hItem, FALSE);
2051             FastList_Repaint (pfl);
2052             }
2053
2054          if ( (dwFlagsNew & FLIF_DISALLOW_COLLAPSE) && !(hItem->fExpanded) )
2055             {
2056             FastList_OnCommand_Expand (hList, hItem, TRUE);
2057             FastList_Repaint (pfl);
2058             }
2059
2060          if ( (dwFlagsOld & FLIF_DROPHIGHLIGHT) != (dwFlagsNew & FLIF_DROPHIGHLIGHT) )
2061             FastList_Repaint (pfl);
2062
2063          FastList_Notify_ItemChanged (pfl, hItem);
2064          FastList_End (hList);
2065          }
2066       }
2067 }
2068
2069
2070 int FastList_OnCommand_GetItemImage (HWND hList, HLISTITEM hItem, int iImage)
2071 {
2072    if (hItem && (iImage==0 || iImage==1))
2073       return (iImage == 0) ? (hItem->iFirstImage) : (hItem->iSecondImage);
2074
2075    return IMAGE_NOIMAGE;
2076 }
2077
2078
2079 void FastList_OnCommand_SetItemImage (HWND hList, LPFASTLISTITEMIMAGE pflii, int iImage)
2080 {
2081    LPFASTLIST pfl;
2082    if ((pfl = GetFastList (hList)) != NULL)
2083       {
2084       if (pflii && pflii->hItem && (pflii->iImage==0 || pflii->iImage==1))
2085          {
2086          if (pflii->iImage == 0)
2087             pflii->hItem->iFirstImage = iImage;
2088          else // (pflii->iImage == 1)
2089             pflii->hItem->iSecondImage = iImage;
2090
2091          FastList_Notify_ItemChanged (pfl, pflii->hItem);
2092          FastList_Repaint (pfl);
2093          }
2094       }
2095 }
2096
2097
2098 BOOL FastList_OnCommand_IsExpanded (HWND hList, HLISTITEM hItem)
2099 {
2100    return (hItem) ? (hItem->fExpanded) : FALSE;
2101 }
2102
2103
2104 void FastList_OnCommand_Expand (HWND hList, HLISTITEM hItem, BOOL fExpand)
2105 {
2106    LPFASTLIST pfl;
2107    if ((pfl = GetFastList (hList)) != NULL)
2108       {
2109       if (hItem && (hItem->fExpanded != fExpand) && (fExpand || !(hItem->dwFlags & FLIF_DISALLOW_COLLAPSE)))
2110          {
2111          BOOL fTreeMode = (pfl->dwStyle & FLS_VIEW_TREE) ? TRUE : FALSE;
2112
2113          hItem->fExpanded = fExpand;
2114          pfl->fSyncScrollBeforePaint = TRUE;
2115          pfl->fSyncIndicesBeforePaint = TRUE;
2116          FastList_RepairVisibilityFlags (pfl, hItem, (!hItem->fExpanded || !hItem->fVisible) && (fTreeMode));
2117          FastList_Notify_ItemChanged (pfl, hItem);
2118          FastList_Repaint (pfl);
2119          }
2120       }
2121 }
2122
2123
2124 BOOL FastList_OnCommand_ItemVisible (HWND hList, HLISTITEM hItem, BOOL fSet)
2125 {
2126    if (!fSet)
2127       return (hItem) ? (hItem->fVisible) : FALSE;
2128
2129    LPFASTLIST pfl;
2130    if ((pfl = GetFastList (hList)) != NULL)
2131       {
2132       if (hItem && hItem->fVisible)
2133          {
2134          pfl->hEnsureVisible = hItem;
2135          FastList_Repaint (pfl);
2136          }
2137       }
2138    return TRUE;
2139 }
2140
2141
2142 HLISTITEM FastList_OnCommand_ItemFocus (HWND hList, HLISTITEM hItem, BOOL fSet)
2143 {
2144    LPFASTLIST pfl;
2145    if ((pfl = GetFastList (hList)) != NULL)
2146       {
2147       if (!fSet)
2148          return (hItem) ? (HLISTITEM)(hItem->fFocused) : pfl->hFocused;
2149
2150       if (pfl->hFocused != hItem)
2151          {
2152          if (pfl->hFocused != NULL)
2153             {
2154             pfl->hFocused->fFocused = FALSE;
2155             FastList_Notify_ItemChanged (pfl, pfl->hFocused);
2156             }
2157          if ((pfl->hFocused = hItem) != NULL)
2158             {
2159             pfl->hFocused->fFocused = TRUE;
2160             FastList_Notify_ItemChanged (pfl, pfl->hFocused);
2161             }
2162          FastList_Repaint (pfl);
2163          }
2164       }
2165
2166    return NULL;
2167 }
2168
2169
2170 void FastList_OnCommand_GetImageLists (HWND hList, HIMAGELIST *phiSmall, HIMAGELIST *phiLarge)
2171 {
2172    LPFASTLIST pfl;
2173    if ((pfl = GetFastList (hList)) != NULL)
2174       {
2175       if (phiSmall)
2176          *phiSmall = pfl->hilSmall;
2177       if (phiLarge)
2178          *phiLarge = pfl->hilLarge;
2179       }
2180 }
2181
2182
2183 void FastList_OnCommand_SetImageLists (HWND hList, HIMAGELIST hiSmall, HIMAGELIST hiLarge)
2184 {
2185    LPFASTLIST pfl;
2186    if ((pfl = GetFastList (hList)) != NULL)
2187       {
2188       pfl->hilSmall = hiSmall;
2189       pfl->hilLarge = hiLarge;
2190       FastList_Repaint (pfl);
2191       }
2192 }
2193
2194
2195 HIMAGELIST FastList_OnCommand_CreateDragImage (HWND hList, HLISTITEM hItem)
2196 {
2197    HIMAGELIST hil = NULL;
2198
2199    LPFASTLIST pfl;
2200    if ((pfl = GetFastList (hList)) != NULL)
2201       {
2202       if (hItem)
2203          {
2204
2205          // First find out how big this item is. Note that we'll ask
2206          // for the item regions, while specifying the fDragImage flag
2207          // in the paintitem request--that will make it draw only the
2208          // dragimage stuff.
2209          //
2210          HDC hdc = CreateCompatibleDC (NULL);
2211          HFONT hfOld = (HFONT)SelectObject (hdc, pfl->hf);
2212
2213          RECT rItem;
2214          FastList_CalcItemRect (pfl, 0, &rItem, !!pfl->hScrollH, !!pfl->hScrollV);
2215
2216          FASTLISTITEMREGIONS reg;
2217          FastList_CallPaintItem (pfl, hdc, FALSE, TRUE, hItem->index, &rItem, &reg, NULL, NULL);
2218
2219          SelectObject (hdc, hfOld);
2220          DeleteDC (hdc);
2221
2222          RECT rDragImage = reg.rSelect;
2223
2224          rDragImage.left -= reg.rSelect.left;
2225          rDragImage.right -= reg.rSelect.left;
2226          rDragImage.top -= reg.rSelect.top;
2227          rDragImage.bottom -= reg.rSelect.top;
2228
2229          rItem.left -= reg.rSelect.left;
2230          rItem.right -= reg.rSelect.left;
2231          rItem.top -= reg.rSelect.top;
2232          rItem.bottom -= reg.rSelect.top;
2233
2234          // Okay, now we know how big the item will be. Create a bitmap,
2235          // draw onto it, and stuff it into an imagelist.
2236          //
2237          hdc = GetDC (NULL);
2238          HDC hdcBitmap = CreateCompatibleDC (hdc);
2239          HBITMAP bmp = CreateCompatibleBitmap (hdc, cxRECT(rDragImage), cyRECT(rDragImage));
2240          HBITMAP bmpOld = (HBITMAP)SelectObject (hdcBitmap, bmp);
2241          hfOld = (HFONT)SelectObject (hdcBitmap, pfl->hf);
2242
2243          HBRUSH hbr = CreateSolidBrush (clrTRANSPARENT);
2244          FillRect (hdcBitmap, &rDragImage, hbr);
2245          DeleteObject (hbr);
2246
2247          FastList_CallPaintItem (pfl, hdcBitmap, TRUE, TRUE, hItem->index, &rItem, NULL, NULL, NULL);
2248
2249          SelectObject (hdcBitmap, hfOld);
2250          SelectObject (hdcBitmap, bmpOld);
2251          DeleteDC (hdcBitmap);
2252          ReleaseDC (NULL, hdc);
2253
2254          if ((hil = ImageList_Create (cxRECT(rDragImage), cyRECT(rDragImage), ILC_MASK, 1, 1)) != NULL)
2255             {
2256             ImageList_AddMasked (hil, bmp, clrTRANSPARENT);
2257             }
2258
2259          DeleteObject (bmp);
2260          }
2261       }
2262
2263    return hil;
2264 }
2265
2266
2267 LPFASTLISTSORTFUNC FastList_OnCommand_GetSortFunc (HWND hList)
2268 {
2269    LPFASTLIST pfl;
2270    if ((pfl = GetFastList (hList)) != NULL)
2271       {
2272       return pfl->pfnSort;
2273       }
2274
2275    return NULL;
2276 }
2277
2278
2279 void FastList_OnCommand_SetSortFunc (HWND hList, LPFASTLISTSORTFUNC pfn)
2280 {
2281    LPFASTLIST pfl;
2282    if ((pfl = GetFastList (hList)) != NULL)
2283       {
2284       pfl->pfnSort = pfn;
2285       pfl->fSortBeforePaint = TRUE;
2286       }
2287 }
2288
2289
2290 void FastList_OnCommand_GetSortStyle (HWND hList, int *piColSort, BOOL *pfRevSort)
2291 {
2292    LPFASTLIST pfl;
2293    if ((pfl = GetFastList (hList)) != NULL)
2294       {
2295       if (piColSort)
2296          *piColSort = pfl->iColSort;
2297       if (pfRevSort)
2298          *pfRevSort = pfl->fRevSort;
2299       }
2300 }
2301
2302
2303 void FastList_OnCommand_SetSortStyle (HWND hList, int iColSort, BOOL fRevSort)
2304 {
2305    LPFASTLIST pfl;
2306    if ((pfl = GetFastList (hList)) != NULL)
2307       {
2308       pfl->iColSort = iColSort;
2309       pfl->fRevSort = fRevSort;
2310       pfl->fSortBeforePaint = TRUE;
2311       FastList_Repaint (pfl);
2312       }
2313 }
2314
2315
2316 void FastList_OnCommand_Sort (HWND hList)
2317 {
2318    LPFASTLIST pfl;
2319    if ((pfl = GetFastList (hList)) != NULL)
2320       {
2321       pfl->fSortBeforePaint = FALSE;
2322       FastList_Repaint (pfl);
2323       }
2324 }
2325
2326
2327 int FastList_OnCommand_GetColumnCount (HWND hList)
2328 {
2329    int cColumns = 0;
2330
2331    LPFASTLIST pfl;
2332    if ((pfl = GetFastList (hList)) != NULL)
2333       {
2334       for (cColumns = 0; cColumns < (int)pfl->cColumns; ++cColumns)
2335          {
2336          if (pfl->aColumns[ cColumns ].pszText == NULL)
2337             break;
2338          }
2339       }
2340
2341    return cColumns;
2342 }
2343
2344
2345 BOOL FastList_OnCommand_GetColumn (HWND hList, int iColumn, LPFASTLISTCOLUMN pcol)
2346 {
2347    LPFASTLIST pfl;
2348    if ((pfl = GetFastList (hList)) == NULL)
2349       return FALSE;
2350
2351    if ((iColumn < 0) || (iColumn >= (int)pfl->cColumns) || (!pfl->aColumns[ iColumn ].pszText))
2352       return FALSE;
2353
2354    if (pcol)
2355       {
2356       pcol->dwFlags = FLCF_JUSTIFY_LEFT;
2357       if ((pfl->aColumns[ iColumn ].fmt & HDF_JUSTIFYMASK) == HDF_RIGHT)
2358          pcol->dwFlags = FLCF_JUSTIFY_RIGHT;
2359       else if ((pfl->aColumns[ iColumn ].fmt & HDF_JUSTIFYMASK) == HDF_CENTER)
2360          pcol->dwFlags = FLCF_JUSTIFY_CENTER;
2361       pcol->cxWidth = pfl->aColumns[ iColumn ].cxy;
2362       lstrcpy (pcol->szText, pfl->aColumns[ iColumn ].pszText);
2363       }
2364
2365    return TRUE;
2366 }
2367
2368
2369 void FastList_OnCommand_SetColumn (HWND hList, int iColumn, LPFASTLISTCOLUMN pcol)
2370 {
2371    LPFASTLIST pfl;
2372    if ((pfl = GetFastList (hList)) != NULL)
2373       {
2374       iColumn = limit (0, iColumn, (int)pfl->cColumns);
2375
2376       if (REALLOC (pfl->aColumns, pfl->cColumns, 1+iColumn, 1))
2377          {
2378          if (pfl->aColumns[ iColumn ].pszText)
2379             Free (pfl->aColumns[ iColumn ].pszText);
2380          memset (&pfl->aColumns[ iColumn ], 0x00, sizeof(HD_ITEM));
2381
2382          if (pcol)
2383             {
2384             if ((pcol->dwFlags & FLCF_JUSTIFY_MASK) == FLCF_JUSTIFY_RIGHT)
2385                pfl->aColumns[ iColumn ].fmt |= HDF_RIGHT;
2386             else if ((pcol->dwFlags & FLCF_JUSTIFY_MASK) == FLCF_JUSTIFY_CENTER)
2387                pfl->aColumns[ iColumn ].fmt |= HDF_CENTER;
2388             else
2389                pfl->aColumns[ iColumn ].fmt |= HDF_LEFT;
2390
2391             pfl->aColumns[ iColumn ].cxy = pcol->cxWidth;
2392             pfl->aColumns[ iColumn ].pszText = (LPTSTR)Allocate (sizeof(TCHAR)*(1+lstrlen(pcol->szText)));
2393             lstrcpy (pfl->aColumns[ iColumn ].pszText, pcol->szText);
2394
2395             pfl->aColumns[ iColumn ].mask = HDI_WIDTH | HDI_TEXT | HDI_FORMAT;
2396             }
2397
2398          FastList_SyncHeader (pfl);
2399          }
2400       }
2401 }
2402
2403
2404 BOOL FastList_OnCommand_IsSelected (HWND hList, HLISTITEM hItem)
2405 {
2406    return hItem->fSelected;
2407 }
2408
2409
2410 void FastList_OnCommand_SelectItem (HWND hList, HLISTITEM hItem, BOOL fSelect)
2411 {
2412    LPFASTLIST pfl;
2413    if ((pfl = GetFastList (hList)) != NULL)
2414       {
2415       if (hItem != NULL)
2416          {
2417          if (!fSelect || !(hItem->dwFlags & FLIF_DISALLOW_SELECT))
2418             {
2419             if (fSelect)
2420                FastList_PerformSelectTest (pfl, hItem);
2421             FastList_PerformSelectItem (pfl, hItem, fSelect);
2422             }
2423          }
2424       else
2425          {
2426          BOOL fTreeMode = (pfl->dwStyle & FLS_VIEW_TREE);
2427          BOOL fMultiple = (pfl->dwStyle & FLS_SELECTION_MULTIPLE);
2428          BOOL fLevel = (pfl->dwStyle & (FLS_SELECTION_LEVEL & ~FLS_SELECTION_MULTIPLE));
2429          BOOL fSibling = (pfl->dwStyle & (FLS_SELECTION_SIBLING & ~FLS_SELECTION_MULTIPLE));
2430
2431          if ( (!fSelect) ||
2432               (fMultiple && (!fTreeMode || (!fLevel && !fSibling))) )
2433             {
2434             FastList_Begin (hList);
2435
2436             for (LPENUM pEnum = pfl->lItems->FindFirst(); pEnum; pEnum = pEnum->FindNext())
2437                {
2438                hItem = (HLISTITEM)(pEnum->GetObject());
2439                if (!fSelect || !(hItem->dwFlags & FLIF_DISALLOW_SELECT))
2440                   FastList_PerformSelectItem (pfl, hItem, fSelect);
2441                }
2442
2443             FastList_End (hList);
2444             }
2445          }
2446       }
2447 }
2448
2449
2450 HLISTITEM FastList_OnCommand_FindList (HWND hList, HLISTITEM hItem, DWORD dwCode)
2451 {
2452    LPFASTLIST pfl;
2453    if ((pfl = GetFastList (hList)) != NULL)
2454       {
2455       switch (dwCode)
2456          {
2457          case FLM_FINDLIST_FIRST:
2458             return pfl->hListFirst;
2459
2460          case FLM_FINDLIST_PREVIOUS:
2461             return (hItem) ? (hItem->hListPrevious) : NULL;
2462
2463          case FLM_FINDLIST_NEXT:
2464             return (hItem) ? (hItem->hListNext) : (pfl->hListFirst);
2465          }
2466       }
2467
2468    return NULL;
2469 }
2470
2471
2472 HLISTITEM FastList_OnCommand_FindTree (HWND hList, HLISTITEM hItem, DWORD dwCode)
2473 {
2474    LPFASTLIST pfl;
2475    if ((pfl = GetFastList (hList)) != NULL)
2476       {
2477       switch (dwCode)
2478          {
2479          case FLM_FINDTREE_PARENT:
2480             return (hItem) ? (hItem->hTreeParent) : NULL;
2481
2482          case FLM_FINDTREE_CHILD:
2483             return (hItem) ? (hItem->hTreeChild) : (pfl->hTreeFirst);
2484
2485          case FLM_FINDTREE_PREVIOUS:
2486             return (hItem) ? (hItem->hTreePrevious) : NULL;
2487
2488          case FLM_FINDTREE_NEXT:
2489             return (hItem) ? (hItem->hTreeNext) : NULL;
2490          }
2491       }
2492
2493    return NULL;
2494 }
2495
2496 HLISTITEM FastList_OnCommand_FindSelected (HWND hList, HLISTITEM hItem)
2497 {
2498    if (hItem != NULL)
2499       {
2500       return hItem->hSelectNext;
2501       }
2502
2503    LPFASTLIST pfl;
2504    if ((pfl = GetFastList (hList)) != NULL)
2505       {
2506       return pfl->hSelectFirst;
2507       }
2508
2509    return NULL;
2510 }
2511
2512
2513 HLISTITEM FastList_OnCommand_FindItem (HWND hList, LPENUM *ppEnum, LPARAM lpUser)
2514 {
2515    LPFASTLIST pfl;
2516    if ((pfl = GetFastList (hList)) != NULL)
2517       {
2518       if (!ppEnum)
2519          return (HLISTITEM)(pfl->lkUserParam->GetFirstObject (&lpUser));
2520       else if (ppEnum && (((*ppEnum) = pfl->lkUserParam->FindFirst (&lpUser)) != NULL))
2521          return (HLISTITEM)((*ppEnum)->GetObject());
2522       }
2523    return NULL;
2524 }
2525
2526
2527 HLISTITEM FastList_OnCommand_FindNextItem (HWND hList, LPENUM *ppEnum)
2528 {
2529    if (ppEnum && (((*ppEnum) = (*ppEnum)->FindNext()) != NULL))
2530       return (HLISTITEM)((*ppEnum)->GetObject());
2531    return NULL;
2532 }
2533
2534
2535 void FastList_OnCommand_FindClose (HWND hList, LPENUM *ppEnum)
2536 {
2537    if (ppEnum && (*ppEnum))
2538       {
2539       Delete (*ppEnum);
2540       *ppEnum = NULL;
2541       }
2542 }
2543
2544
2545 HLISTITEM FastList_OnCommand_FindVisible (HWND hList, HLISTITEM hItem, DWORD dwCode)
2546 {
2547    LPFASTLIST pfl;
2548    if ((pfl = GetFastList (hList)) != NULL)
2549       {
2550       if (pfl->fSortBeforePaint)
2551          FastList_PerformSort (pfl);
2552       if (pfl->fSyncIndicesBeforePaint)
2553          FastList_RepairVisibilityIndices (pfl);
2554
2555       switch (dwCode)
2556          {
2557          case FLM_FINDVISIBLE_FIRST:
2558             return (pfl->cVisible) ? pfl->aVisibleHeap[0] : NULL;
2559
2560          case FLM_FINDVISIBLE_NEXT:
2561             if (!hItem->fVisible)
2562                return NULL;
2563             if (hItem->index == (int)(pfl->cVisible-1))
2564                return NULL;
2565             return pfl->aVisibleHeap[ hItem->index +1 ];
2566          }
2567       }
2568
2569    return NULL;
2570 }
2571
2572
2573 int FastList_OnCommand_GetItemCount (HWND hList, BOOL fVisibleOnly)
2574 {
2575    LPFASTLIST pfl;
2576    if ((pfl = GetFastList (hList)) == NULL)
2577       return 0;
2578
2579    return (fVisibleOnly) ? pfl->cVisible : pfl->lItems->GetCount();
2580 }
2581
2582
2583 HLISTITEM FastList_OnCommand_ItemFromPoint (HWND hList, POINT *pptClient, BOOL fStrict)
2584 {
2585    HLISTITEM hItem = NULL;
2586
2587    LPFASTLIST pfl;
2588    if ((pfl = GetFastList (hList)) != NULL)
2589       {
2590       if (pfl->fSortBeforePaint)
2591          FastList_PerformSort (pfl);
2592       if (pfl->fSyncIndicesBeforePaint)
2593          FastList_RepairVisibilityIndices (pfl);
2594
2595       // Adjust the client coordinates we were given based on the current
2596       // position of the scrollbars.
2597       //
2598       LONG xField = pptClient->x + pfl->dxPixel;
2599       LONG yField = pptClient->y + pfl->dyPixel;
2600
2601       // Every item's overall RECT is symmetrical, regardless of the
2602       // current view mode. Find out how big those RECTs are.
2603       //
2604       RECT rTemplate;
2605       FastList_CalcItemRect (pfl, 0, &rTemplate, !!pfl->hScrollH, !!pfl->hScrollV);
2606
2607       // How large is the current client area?
2608       //
2609       RECT rClient;
2610       GetClientRect (pfl->hList, &rClient);
2611       if (pfl->hScrollV)
2612          rClient.right -= GetSystemMetrics(SM_CXVSCROLL);
2613       if (pfl->hScrollH)
2614          rClient.bottom -= GetSystemMetrics(SM_CYHSCROLL);
2615       if (pfl->hHeader)
2616          rClient.top += FastList_GetHeaderHeight (pfl);
2617
2618       // The specified point exists in exactly one item (or none at all).
2619       // Just which item that is depends on what the current view mode is.
2620       //
2621       int index = -1;
2622
2623       switch (pfl->dwStyle & FLS_VIEW_MASK)
2624          {
2625          case FLS_VIEW_LARGE:
2626             {
2627             LONG ixIndex = xField / cxRECT(rTemplate);
2628             LONG iyIndex = yField / cyRECT(rTemplate);
2629
2630             LONG cxArray = cxRECT(rClient) / cxRECT(rTemplate);
2631             cxArray = max( cxArray, 1 );
2632
2633             index = ixIndex + iyIndex * cxArray;
2634             break;
2635             }
2636
2637          case FLS_VIEW_SMALL:
2638             {
2639             LONG ixIndex = xField / cxRECT(rTemplate);
2640             LONG iyIndex = yField / cyRECT(rTemplate);
2641
2642             LONG cyArray = cyRECT(rClient) / cyRECT(rTemplate);
2643             cyArray = max( cyArray, 1 );
2644
2645             index = iyIndex + ixIndex * cyArray;
2646             break;
2647             }
2648
2649          case FLS_VIEW_LIST:
2650          case FLS_VIEW_TREE:
2651          case FLS_VIEW_TREELIST:
2652             index = (yField - rClient.top) / cyRECT(rTemplate);
2653             break;
2654          }
2655
2656       if ((index >= 0) && (index < (int)pfl->cVisible))
2657          hItem = pfl->aVisibleHeap[ index ];
2658
2659       // If there is indeed an item underneath that point, test the item's
2660       // regions--and our window styles--to see if a click on the specified
2661       // point actually *changes* that item.
2662       //
2663       if (hItem && fStrict)
2664          {
2665          BOOL fHit = FALSE;
2666
2667          FASTLISTITEMREGIONS reg;
2668          FastList_OnCommand_GetItemRegions (hList, hItem, &reg, pptClient, &fHit);
2669
2670          if (!PtInRect (&reg.rSelect, *pptClient))
2671             hItem = NULL;
2672          else if (PtInRect (&reg.rHighlight, *pptClient) && !fHit && (pfl->dwStyle & FLS_HIT_TEXTONLY))
2673             hItem = NULL;
2674          }
2675       }
2676
2677    return hItem;
2678 }
2679
2680
2681 void FastList_OnCommand_GetItemRegions (HWND hList, HLISTITEM hItem, LPFASTLISTITEMREGIONS pReg, POINT *pptTest, BOOL *pfHit)
2682 {
2683    LPFASTLIST pfl;
2684    if ((pfl = GetFastList (hList)) != NULL)
2685       {
2686       if (pfl->fSortBeforePaint)
2687          FastList_PerformSort (pfl);
2688       if (pfl->fSyncIndicesBeforePaint)
2689          FastList_RepairVisibilityIndices (pfl);
2690
2691       if (hItem && !hItem->fVisible && pReg)
2692          {
2693          memset (pReg, 0x00, sizeof(FASTLISTITEMREGIONS));
2694          }
2695       else if (hItem && hItem->fVisible)
2696          {
2697          HDC hdc = CreateCompatibleDC (NULL);
2698          HFONT hfOld = (HFONT)SelectObject (hdc, pfl->hf);
2699
2700          RECT rItem;
2701          FastList_CalcItemRect (pfl, hItem->index, &rItem, !!pfl->hScrollH, !!pfl->hScrollV);
2702          rItem.left -= pfl->dxPixel;
2703          rItem.right -= pfl->dxPixel;
2704          rItem.top -= pfl->dyPixel;
2705          rItem.bottom -= pfl->dyPixel;
2706
2707          FastList_CallPaintItem (pfl, hdc, FALSE, FALSE, hItem->index, &rItem, pReg, pptTest, pfHit);
2708
2709          SelectObject (hdc, hfOld);
2710          DeleteDC (hdc);
2711          }
2712       }
2713 }
2714
2715
2716 LPFASTLISTTEXTCALLBACK FastList_OnCommand_GetTextCallback (HWND hList)
2717 {
2718    LPFASTLIST pfl;
2719    if ((pfl = GetFastList (hList)) != NULL)
2720       return pfl->pfnText;
2721
2722    return NULL;
2723 }
2724
2725
2726 void FastList_OnCommand_SetTextCallback (HWND hList, LPFASTLISTTEXTCALLBACK pfn, DWORD dwCookie)
2727 {
2728    LPFASTLIST pfl;
2729    if ((pfl = GetFastList (hList)) != NULL)
2730       {
2731       pfl->pfnText = pfn;
2732       pfl->dwCookieText = dwCookie;
2733       }
2734 }
2735
2736
2737 /*
2738  * NOTIFICATION ROUTINES ______________________________________________________
2739  *
2740  */
2741
2742 BOOL FastList_Notify_GetItemText (LPFASTLIST pfl, HLISTITEM hItem, int icol, LPTSTR pszText, size_t cchText, size_t *pcchRequired)
2743 {
2744    FLN_GETITEMTEXT_PARAMS fln;
2745    fln.hdr.hwndFrom = pfl->hList;
2746    fln.hdr.idFrom = GetWindowLong (pfl->hList, GWL_ID);
2747    fln.hdr.code = FLN_GETITEMTEXT;
2748    fln.item.hItem = hItem;
2749    fln.item.icol = icol;
2750    fln.item.lParam = hItem->lpUser;
2751    fln.item.pszText = pszText;
2752    fln.item.cchTextMax = cchText;
2753
2754    if (pfl->pfnText)
2755       {
2756       if (!(*pfl->pfnText)( pfl->hList, &fln, pfl->dwCookieText ))
2757          return FALSE;
2758       }
2759    else
2760       {
2761       if (!GetParent (pfl->hList))
2762          return FALSE;
2763       if (!SendMessage (GetParent (pfl->hList), WM_NOTIFY, (WPARAM)fln.hdr.idFrom, (LPARAM)&fln))
2764          return FALSE;
2765       }
2766
2767    *pcchRequired = fln.item.cchTextMax;
2768    return TRUE;
2769 }
2770
2771
2772 BOOL FastList_Notify_ItemChanged (LPFASTLIST pfl, HLISTITEM hItem)
2773 {
2774    if (!GetParent (pfl->hList))
2775       return FALSE;
2776
2777    FLN_ITEMCHANGED_PARAMS fln;
2778    fln.hdr.hwndFrom = pfl->hList;
2779    fln.hdr.idFrom = GetWindowLong (pfl->hList, GWL_ID);
2780    fln.hdr.code = FLN_ITEMCHANGED;
2781    fln.hItem = hItem;
2782    return (BOOL)SendMessage (GetParent (pfl->hList), WM_NOTIFY, (WPARAM)fln.hdr.idFrom, (LPARAM)&fln);
2783 }
2784
2785
2786 BOOL FastList_Notify_AddItem (LPFASTLIST pfl, HLISTITEM hItem)
2787 {
2788    if (!GetParent (pfl->hList))
2789       return FALSE;
2790
2791    FLN_ADDITEM_PARAMS fln;
2792    fln.hdr.hwndFrom = pfl->hList;
2793    fln.hdr.idFrom = GetWindowLong (pfl->hList, GWL_ID);
2794    fln.hdr.code = FLN_ADDITEM;
2795    fln.hItem = hItem;
2796    return (BOOL)SendMessage (GetParent (pfl->hList), WM_NOTIFY, (WPARAM)fln.hdr.idFrom, (LPARAM)&fln);
2797 }
2798
2799
2800 BOOL FastList_Notify_RemoveItem (LPFASTLIST pfl, HLISTITEM hItem)
2801 {
2802    if (!GetParent (pfl->hList))
2803       return FALSE;
2804
2805    FLN_REMOVEITEM_PARAMS fln;
2806    fln.hdr.hwndFrom = pfl->hList;
2807    fln.hdr.idFrom = GetWindowLong (pfl->hList, GWL_ID);
2808    fln.hdr.code = FLN_REMOVEITEM;
2809    fln.hItem = hItem;
2810    return (BOOL)SendMessage (GetParent (pfl->hList), WM_NOTIFY, (WPARAM)fln.hdr.idFrom, (LPARAM)&fln);
2811 }
2812
2813
2814 BOOL FastList_Notify_ColumnClick (LPFASTLIST pfl, int icol, BOOL fDouble)
2815 {
2816    if (!GetParent (pfl->hList))
2817       return FALSE;
2818
2819    FLN_COLUMNCLICK_PARAMS fln;
2820    fln.hdr.hwndFrom = pfl->hList;
2821    fln.hdr.idFrom = GetWindowLong (pfl->hList, GWL_ID);
2822    fln.hdr.code = FLN_COLUMNCLICK;
2823    fln.icol = icol;
2824    fln.fDouble = fDouble;
2825    return (BOOL)SendMessage (GetParent (pfl->hList), WM_NOTIFY, (WPARAM)fln.hdr.idFrom, (LPARAM)&fln);
2826 }
2827
2828
2829 BOOL FastList_Notify_ColumnResize (LPFASTLIST pfl, int icol, LONG cxWidth)
2830 {
2831    if (!GetParent (pfl->hList))
2832       return FALSE;
2833
2834    FLN_COLUMNRESIZE_PARAMS fln;
2835    fln.hdr.hwndFrom = pfl->hList;
2836    fln.hdr.idFrom = GetWindowLong (pfl->hList, GWL_ID);
2837    fln.hdr.code = FLN_COLUMNRESIZE;
2838    fln.icol = icol;
2839    fln.cxWidth = cxWidth;
2840    return (BOOL)SendMessage (GetParent (pfl->hList), WM_NOTIFY, (WPARAM)fln.hdr.idFrom, (LPARAM)&fln);
2841 }
2842
2843
2844 BOOL FastList_Notify_ItemSelect (LPFASTLIST pfl)
2845 {
2846    if (!GetParent (pfl->hList))
2847       return FALSE;
2848
2849    FLN_ITEMSELECT_PARAMS fln;
2850    fln.hdr.hwndFrom = pfl->hList;
2851    fln.hdr.idFrom = GetWindowLong (pfl->hList, GWL_ID);
2852    fln.hdr.code = FLN_ITEMSELECT;
2853    fln.hItem = pfl->hSelectFirst;
2854    return (BOOL)SendMessage (GetParent (pfl->hList), WM_NOTIFY, (WPARAM)fln.hdr.idFrom, (LPARAM)&fln);
2855 }
2856
2857
2858 BOOL FastList_Notify_ItemExpand (LPFASTLIST pfl, HLISTITEM hItem)
2859 {
2860    if (!GetParent (pfl->hList))
2861       return FALSE;
2862
2863    FLN_ITEMEXPAND_PARAMS fln;
2864    fln.hdr.hwndFrom = pfl->hList;
2865    fln.hdr.idFrom = GetWindowLong (pfl->hList, GWL_ID);
2866    fln.hdr.code = FLN_ITEMEXPAND;
2867    fln.hItem = hItem;
2868    fln.fExpanded = hItem->fExpanded;
2869    return (BOOL)SendMessage (GetParent (pfl->hList), WM_NOTIFY, (WPARAM)fln.hdr.idFrom, (LPARAM)&fln);
2870 }
2871
2872
2873 BOOL FastList_Notify_Generic (LPFASTLIST pfl, DWORD dwCode)
2874 {
2875    if (!GetParent (pfl->hList))
2876       return FALSE;
2877
2878    NMHDR hdr;
2879    hdr.hwndFrom = pfl->hList;
2880    hdr.idFrom = GetWindowLong (pfl->hList, GWL_ID);
2881    hdr.code = dwCode;
2882    return (BOOL)SendMessage (GetParent (pfl->hList), WM_NOTIFY, (WPARAM)hdr.idFrom, (LPARAM)&hdr);
2883 }
2884
2885
2886 BOOL FastList_Notify_Drag (LPFASTLIST pfl, DWORD dwCode, LPFLN_DRAG_PARAMS pfln)
2887 {
2888    if (!GetParent (pfl->hList))
2889       return FALSE;
2890
2891    pfln->hdr.hwndFrom = pfl->hList;
2892    pfln->hdr.idFrom = GetWindowLong (pfl->hList, GWL_ID);
2893    pfln->hdr.code = dwCode;
2894    return (BOOL)SendMessage (GetParent (pfl->hList), WM_NOTIFY, (WPARAM)pfln->hdr.idFrom, (LPARAM)pfln);
2895 }
2896
2897
2898 /*
2899  * SUPPORT ROUTINES ___________________________________________________________
2900  *
2901  */
2902
2903 void FastList_Repaint (LPFASTLIST pfl)
2904 {
2905    if ((pfl->fRepaintRequired = (pfl->nBegin) ? TRUE : FALSE) == FALSE)
2906       {
2907       RECT rClient;
2908       GetClientRect (pfl->hList, &rClient);
2909       InvalidateRect (pfl->hList, &rClient, TRUE);
2910       UpdateWindow (pfl->hList);
2911       }
2912 }
2913
2914
2915 HLISTITEM FastList_CreateItem (LPFASTLIST pfl)
2916 {
2917    HLISTITEM hItem = New (LISTITEM);
2918    memset (hItem, 0x00, sizeof(LISTITEM));
2919    hItem->fExpanded = TRUE;
2920    return hItem;
2921 }
2922
2923
2924 void FastList_DeleteItem (LPFASTLIST pfl, HLISTITEM hItem)
2925 {
2926    if (hItem)
2927       {
2928       // Remove any children of this item, even if we're not in
2929       // treeview mode now.
2930       //
2931       while (hItem->hTreeChild)
2932          {
2933          FastList_DeleteItem (pfl, hItem->hTreeChild);
2934          }
2935
2936       FastList_Notify_RemoveItem (pfl, hItem); // notify *before* delete
2937
2938        if (pfl->hFocused == hItem)
2939           pfl->hFocused = FALSE;
2940
2941       // Unlink this item from the List chain
2942       //
2943       if (pfl->hListFirst == hItem)
2944          pfl->hListFirst = hItem->hListNext;
2945       if (hItem->hListPrevious)
2946          hItem->hListPrevious->hListNext = hItem->hListNext;
2947       if (hItem->hListNext)
2948          hItem->hListNext->hListPrevious = hItem->hListPrevious;
2949
2950       // Unlink this item from the Tree chain
2951       //
2952       if (pfl->hTreeFirst == hItem)
2953          pfl->hTreeFirst = hItem->hTreeNext;
2954       if (hItem->hTreePrevious)
2955          hItem->hTreePrevious->hTreeNext = hItem->hTreeNext;
2956       if (hItem->hTreeNext)
2957          hItem->hTreeNext->hTreePrevious = hItem->hTreePrevious;
2958       if (hItem->hTreeParent && (hItem->hTreeParent->hTreeChild == hItem))
2959          hItem->hTreeParent->hTreeChild = hItem->hTreeNext;
2960
2961       // Unlink this item from the Selection chain
2962       //
2963       if (hItem->hSelectPrevious)
2964          hItem->hSelectPrevious->hSelectNext = hItem->hSelectNext;
2965       if (hItem->hSelectNext)
2966          hItem->hSelectNext->hSelectPrevious = hItem->hSelectPrevious;
2967       if (pfl->hSelectFirst == hItem)
2968          pfl->hSelectFirst = hItem->hSelectNext;
2969       if (pfl->hAnchor == hItem)
2970          pfl->hAnchor = NULL;
2971       hItem->hSelectPrevious = NULL;
2972       hItem->hSelectNext = NULL;
2973
2974       // If we're holding onto this item as the next guy who we should
2975       // ensure is visible, forget about it there too.
2976       //
2977       if (pfl->hEnsureVisible == hItem)
2978          pfl->hEnsureVisible = NULL;
2979
2980       // Remove this item from the hashlist, and deallocate any memory
2981       // associated with it.
2982       //
2983       pfl->lItems->Remove (hItem);
2984
2985       if (hItem->apszText)
2986          {
2987          for (size_t iCol = 0; iCol < hItem->cpszText; ++iCol)
2988             {
2989             if (hItem->apszText[ iCol ] != NULL)
2990                Free (hItem->apszText[ iCol ]);
2991             }
2992          Free (hItem->apszText);
2993          }
2994
2995 #ifdef DEBUG
2996       memset (hItem, 0xFE, sizeof(_FASTLISTITEM));
2997 #endif
2998       Delete (hItem);
2999
3000       pfl->fSyncScrollBeforePaint = TRUE;
3001       pfl->fSyncIndicesBeforePaint = TRUE;
3002       }
3003 }
3004
3005
3006 int FastList_GetListHeight (LPFASTLIST pfl)
3007 {
3008    static HFONT hfLast = NULL;
3009    static int cyLast = 0;
3010
3011    if (!cyLast || (pfl->hf != hfLast))
3012       {
3013       hfLast = pfl->hf;
3014
3015       TEXTMETRIC tm;
3016       HDC hdc = CreateCompatibleDC (NULL);
3017       HFONT hfOld = (HFONT)SelectObject (hdc, pfl->hf);
3018       GetTextMetrics (hdc, &tm);
3019       SelectObject (hdc, hfOld);
3020       DeleteDC (hdc);
3021
3022       cyLast = tm.tmHeight + cyABOVE_LIST_TEXT + cyBELOW_LIST_TEXT;
3023       cyLast = max( cyLast, GetSystemMetrics(SM_CYSMICON) );
3024       if (cyLast & 1)
3025          cyLast ++;  // make the height even (for vertical dotted lines)
3026       }
3027
3028    return cyLast;
3029 }
3030
3031
3032 int FastList_GetListWidth (LPFASTLIST pfl)
3033 {
3034    LONG cxWidth = 0;
3035
3036    for (size_t iColumn = 0; iColumn < pfl->cColumns; ++iColumn)
3037       if (pfl->aColumns[ iColumn ].pszText)
3038          cxWidth += pfl->aColumns[ iColumn ].cxy;
3039
3040    if (cxWidth == 0)  // no columns specified? pretend there's just one.
3041       {
3042       RECT rClient;
3043       GetClientRect (pfl->hList, &rClient);
3044       if (pfl->hScrollV)
3045          rClient.right -= GetSystemMetrics(SM_CXVSCROLL);
3046       cxWidth = cxRECT(rClient);
3047       }
3048
3049    return cxWidth;
3050 }
3051
3052
3053 LONG FastList_GetHeaderHeight (LPFASTLIST pfl)
3054 {
3055    TEXTMETRIC tm;
3056    HDC hdc = CreateCompatibleDC (NULL);
3057    HFONT hfOld = (HFONT)SelectObject (hdc, pfl->hf);
3058    GetTextMetrics (hdc, &tm);
3059    SelectObject (hdc, hfOld);
3060    DeleteDC (hdc);
3061    return 4*GetSystemMetrics(SM_CYBORDER) + tm.tmHeight;
3062 }
3063
3064
3065 void FastList_GetHeaderRect (LPFASTLIST pfl, RECT *prHeader)
3066 {
3067    GetClientRect (pfl->hList, prHeader);
3068    prHeader->bottom = prHeader->top + FastList_GetHeaderHeight (pfl);
3069 }
3070
3071
3072 void FastList_UpdateColumn (LPFASTLIST pfl, int iColumn)
3073 {
3074    if ((iColumn >= 0) && (iColumn < (int)pfl->cColumns) && (pfl->aColumns[ iColumn ].pszText))
3075       {
3076       HD_ITEM col;
3077       memset (&col, 0x00, sizeof(col));
3078       col.mask |= HDI_WIDTH;
3079       Header_GetItem (pfl->hHeader, iColumn, &col);
3080
3081       if (pfl->aColumns[ iColumn ].cxy != col.cxy)
3082          {
3083          pfl->aColumns[ iColumn ].cxy = col.cxy;
3084          pfl->fSyncScrollBeforePaint = TRUE;
3085          FastList_Notify_ColumnResize (pfl, iColumn, col.cxy);
3086          FastList_Repaint (pfl);
3087          }
3088       }
3089 }
3090
3091
3092 void FastList_SyncHeader (LPFASTLIST pfl)
3093 {
3094    if ((pfl->fSyncHeaderRequired = (pfl->nBegin) ? TRUE : FALSE) == FALSE)
3095       {
3096       BOOL fNeedsHeader = ((pfl->dwStyle & FLS_VIEW_LIST) == FLS_VIEW_LIST);
3097       BOOL fNoSortHeader = (pfl->dwStyle & FLS_NOSORTHEADER);
3098
3099       if (pfl->cColumns == 0)
3100          fNeedsHeader = FALSE;
3101
3102       RECT rHeader;
3103       if (fNeedsHeader)
3104          FastList_GetHeaderRect (pfl, &rHeader);
3105
3106       if (fNeedsHeader && !pfl->hHeader)
3107          {
3108          pfl->hHeader = CreateWindow (WC_HEADER, TEXT(""),
3109                                       0x80 | WS_CHILD | WS_VISIBLE | ((fNoSortHeader) ? 0 : HDS_BUTTONS),
3110                                       rHeader.left, rHeader.top,
3111                                       cxRECT(rHeader), cyRECT(rHeader),
3112                                       pfl->hList,
3113                                       (HMENU)0,
3114                                       THIS_HINST,
3115                                       (LPVOID)0);
3116
3117          SendMessage (pfl->hHeader, WM_SETFONT, (WPARAM)(pfl->hf), 0);
3118          }
3119       else if (fNeedsHeader && pfl->hHeader)
3120          {
3121          DWORD dwStyle = GetWindowLong (pfl->hHeader, GWL_STYLE);
3122          if (fNoSortHeader)
3123             dwStyle &= ~HDS_BUTTONS;
3124          else
3125             dwStyle |= HDS_BUTTONS;
3126          SetWindowLong (pfl->hHeader, GWL_STYLE, dwStyle);
3127
3128          SetWindowPos (pfl->hHeader, 0,
3129                        rHeader.left, rHeader.top, cxRECT(rHeader), cyRECT(rHeader),
3130                        SWP_NOACTIVATE | SWP_NOZORDER);
3131          }
3132       else if (!fNeedsHeader && pfl->hHeader)
3133          {
3134          DestroyWindow (pfl->hHeader);
3135          pfl->hHeader = NULL;
3136          }
3137
3138       // If we ended up with a header window, update its columns.
3139       //
3140       if (pfl->hHeader)
3141          {
3142          while (Header_DeleteItem (pfl->hHeader, 0))
3143             ;
3144          for (size_t iCol = 0; iCol < pfl->cColumns; ++iCol)
3145             Header_InsertItem (pfl->hHeader, iCol, &pfl->aColumns[ iCol ]);
3146          }
3147
3148       pfl->fSyncScrollBeforePaint = TRUE;
3149       }
3150 }
3151
3152
3153 void FastList_SyncScroll (LPFASTLIST pfl)
3154 {
3155    pfl->fSyncScrollBeforePaint = FALSE;
3156
3157    RECT rClient;
3158    GetClientRect (pfl->hList, &rClient);
3159    if (pfl->hHeader)
3160       rClient.top += FastList_GetHeaderHeight (pfl);
3161
3162    LONG cxField = 0;
3163    LONG cyField = 0;
3164
3165    // First find out how much space we'd need, horizontally and vertically,
3166    // to display all the items we can. Remember that the number of items
3167    // we can display changes depending on whether the scrollbars appear or
3168    // not.
3169    //
3170    BOOL fAllowHScroll = ((pfl->dwStyle & FLS_VIEW_MASK) != FLS_VIEW_LARGE);
3171    BOOL fAllowVScroll = ((pfl->dwStyle & FLS_VIEW_MASK) != FLS_VIEW_SMALL);
3172
3173    BOOL fNeedHScroll = FALSE;
3174    BOOL fNeedVScroll = FALSE;
3175
3176    FastList_CalcFieldSize (pfl, &cxField, &cyField, fNeedHScroll, fNeedVScroll);
3177
3178    if ( (cxField > cxRECT(rClient)) && (fAllowHScroll) )
3179       fNeedHScroll = TRUE;
3180    if ( (cyField > cyRECT(rClient)) && (fAllowVScroll) )
3181       fNeedVScroll = TRUE;
3182
3183    FastList_CalcFieldSize (pfl, &cxField, &cyField, fNeedHScroll, fNeedVScroll);
3184
3185    RECT rTest = rClient;
3186    if (pfl->hScrollV)
3187       rTest.right -= GetSystemMetrics(SM_CXVSCROLL);
3188    if (pfl->hScrollH)
3189       rTest.bottom -= GetSystemMetrics(SM_CYHSCROLL);
3190
3191    if ( (cxField > cxRECT(rTest)) && (fAllowHScroll) )
3192       fNeedHScroll = TRUE;
3193    if ( (cyField > cyRECT(rTest)) && (fAllowVScroll) )
3194       fNeedVScroll = TRUE;
3195
3196    // Now that we know the field size, create, remove or position
3197    // the scroll bars.
3198    //
3199    if (pfl->hScrollV && !fNeedVScroll)
3200       {
3201       DestroyWindow (pfl->hScrollV);
3202       pfl->hScrollV = NULL;
3203       pfl->dyPixel = 0;
3204       }
3205    else if (fNeedVScroll)
3206       {
3207       RECT rScroll = rClient;
3208       rScroll.left = rScroll.right - GetSystemMetrics (SM_CXVSCROLL);
3209       if (fNeedHScroll)
3210          rScroll.bottom -= GetSystemMetrics (SM_CYHSCROLL);
3211
3212       if (!pfl->hScrollV)
3213          {
3214          pfl->hScrollV = CreateWindow ("ScrollBar", TEXT(""),
3215                                        WS_CHILD | WS_VISIBLE | SBS_VERT,
3216                                        rScroll.left, rScroll.top,
3217                                        cxRECT(rScroll), cyRECT(rScroll),
3218                                        pfl->hList,
3219                                        (HMENU)0,
3220                                        THIS_HINST,
3221                                        (LPVOID)0);
3222          }
3223       else
3224          {
3225          SetWindowPos (pfl->hScrollV, 0, 
3226                        rScroll.left, rScroll.top, cxRECT(rScroll), cyRECT(rScroll),
3227                        SWP_NOZORDER | SWP_NOACTIVATE);
3228          }
3229       }
3230    else // (!fNeedVScroll)
3231       {
3232       pfl->dyPixel = 0;
3233       }
3234
3235    if (pfl->hScrollH && !fNeedHScroll)
3236       {
3237       DestroyWindow (pfl->hScrollH);
3238       pfl->hScrollH = NULL;
3239       pfl->dxPixel = 0;
3240       }
3241    else if (fNeedHScroll)
3242       {
3243       RECT rScroll = rClient;
3244       rScroll.top = rScroll.bottom - GetSystemMetrics (SM_CYHSCROLL);
3245       if (fNeedVScroll)
3246          rScroll.right -= GetSystemMetrics (SM_CXVSCROLL);
3247
3248       if (!pfl->hScrollH)
3249          {
3250          pfl->hScrollH = CreateWindow ("ScrollBar", TEXT(""),
3251                                        WS_CHILD | WS_VISIBLE | SBS_HORZ,
3252                                        rScroll.left, rScroll.top,
3253                                        cxRECT(rScroll), cyRECT(rScroll),
3254                                        pfl->hList,
3255                                        (HMENU)0,
3256                                        THIS_HINST,
3257                                        (LPVOID)0);
3258          }
3259       else
3260          {
3261          SetWindowPos (pfl->hScrollH, 0, 
3262                        rScroll.left, rScroll.top, cxRECT(rScroll), cyRECT(rScroll),
3263                        SWP_NOZORDER | SWP_NOACTIVATE);
3264          }
3265       }
3266    else // (!fNeedHScroll)
3267       {
3268       pfl->dxPixel = 0;
3269       }
3270
3271    FastList_SyncScrollPos (pfl);
3272 }
3273
3274
3275 void FastList_SyncScrollPos (LPFASTLIST pfl)
3276 {
3277    RECT rClient;
3278    GetClientRect (pfl->hList, &rClient);
3279    if (pfl->hScrollV)
3280       rClient.right -= GetSystemMetrics(SM_CXVSCROLL);
3281    if (pfl->hScrollH)
3282       rClient.bottom -= GetSystemMetrics(SM_CYHSCROLL);
3283    if (pfl->hHeader)
3284       rClient.top += FastList_GetHeaderHeight (pfl);
3285
3286    LONG cxField;
3287    LONG cyField;
3288    FastList_CalcFieldSize (pfl, &cxField, &cyField, !!pfl->hScrollH, !!pfl->hScrollV);
3289
3290    if (pfl->hScrollH)
3291       pfl->dxPixel = limit( 0, pfl->dxPixel, (cxField-cxRECT(rClient)) );
3292    if (pfl->hScrollV)
3293       pfl->dyPixel = limit( 0, pfl->dyPixel, (cyField-cyRECT(rClient)) );
3294
3295    SCROLLINFO si;
3296    if (pfl->hScrollH)
3297       {
3298       memset (&si, 0x00, sizeof(SCROLLINFO));
3299       si.cbSize = sizeof(si);
3300       si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
3301       si.nMin = 0;
3302       si.nMax = cxField;
3303       si.nPage = cxRECT(rClient);
3304       si.nPos = pfl->dxPixel;
3305       SetScrollInfo (pfl->hScrollH, SB_CTL, &si, TRUE);
3306       }
3307
3308    if (pfl->hScrollV)
3309       {
3310       memset (&si, 0x00, sizeof(SCROLLINFO));
3311       si.cbSize = sizeof(si);
3312       si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
3313       si.nMin = 0;
3314       si.nMax = cyField;
3315       si.nPage = cyRECT(rClient);
3316       si.nPos = pfl->dyPixel;
3317       SetScrollInfo (pfl->hScrollV, SB_CTL, &si, TRUE);
3318       }
3319
3320    FastList_ScrollHeader (pfl);
3321 }
3322
3323
3324 void FastList_CalcFieldSize (LPFASTLIST pfl, LONG *pcxField, LONG *pcyField, BOOL fScrollH, BOOL fScrollV)
3325 {
3326    RECT rClient;
3327    GetClientRect (pfl->hList, &rClient);
3328    if (fScrollV)
3329       rClient.right -= GetSystemMetrics(SM_CXVSCROLL);
3330    if (fScrollH)
3331       rClient.bottom -= GetSystemMetrics(SM_CYHSCROLL);
3332    if (pfl->hHeader)
3333       rClient.top += FastList_GetHeaderHeight (pfl);
3334
3335    *pcxField = 0;
3336    *pcyField = 0;
3337
3338    RECT rItem;
3339    FastList_CalcItemRect (pfl, 0, &rItem, fScrollH, fScrollV);
3340
3341    LONG cxArray = 0;
3342    LONG cyArray = 0;
3343
3344    switch (pfl->dwStyle & FLS_VIEW_MASK)
3345       {
3346       case FLS_VIEW_LARGE:
3347          cxArray = cxRECT(rClient) / cxRECT(rItem);
3348          cxArray = max (cxArray, 1);
3349          if (cxArray)
3350             cyArray = DivRoundUp (pfl->cVisible, cxArray);
3351          break;
3352
3353       case FLS_VIEW_SMALL:
3354          cyArray = cyRECT(rClient) / cyRECT(rItem);
3355          cyArray = max (cyArray, 1);
3356          if (cyArray)
3357             cxArray = DivRoundUp (pfl->cVisible, cyArray);
3358          break;
3359
3360       case FLS_VIEW_LIST:
3361       case FLS_VIEW_TREE:
3362       case FLS_VIEW_TREELIST:
3363          cxArray = 1;
3364          cyArray = pfl->cVisible;
3365          break;
3366       }
3367
3368    *pcxField = cxArray * cxRECT(rItem);
3369    *pcyField = cyArray * cyRECT(rItem);
3370 }
3371
3372
3373 void FastList_CalcItemRect (LPFASTLIST pfl, int index, RECT *prItem, BOOL fScrollH, BOOL fScrollV)
3374 {
3375    SetRectEmpty (prItem);
3376
3377    RECT rClient;
3378    GetClientRect (pfl->hList, &rClient);
3379    if (fScrollV)
3380       rClient.right -= GetSystemMetrics(SM_CXVSCROLL);
3381    if (fScrollH)
3382       rClient.bottom -= GetSystemMetrics(SM_CYHSCROLL);
3383    if (pfl->hHeader)
3384       rClient.top += FastList_GetHeaderHeight (pfl);
3385
3386    switch (pfl->dwStyle & FLS_VIEW_MASK)
3387       {
3388       case FLS_VIEW_LARGE:
3389          {
3390          // Large layout shows a two-dimensional list of 32x32 icons,
3391          // each followed beneath by text. The layout of the grid is
3392          // determined by well-known system metrics. There will be no
3393          // horizontal scrollbar; just a vertical one (if necessary).
3394          // Slots are filled in left-to-right, top-to-bottom.
3395          //
3396          LONG cxRect = GetSystemMetrics (SM_CXICONSPACING);
3397          LONG cyRect = GetSystemMetrics (SM_CYICONSPACING);
3398          int cxLayout = cxRECT(rClient) / cxRect;
3399          cxLayout = max (cxLayout, 1);
3400
3401          int vIndex = index / cxLayout;  // 0 = top row
3402          int hIndex = index % cxLayout;  // 0 = left row
3403
3404          prItem->left = hIndex * cxRect;
3405          prItem->right = prItem->left + cxRect;
3406          prItem->top = vIndex * cyRect;
3407          prItem->bottom = prItem->top + cyRect;
3408          break;
3409          }
3410
3411       case FLS_VIEW_SMALL:
3412          {
3413          // Small layout is similar: it shows a two-dimensional list of
3414          // 16x16 icons, each followed to the right by text. The vertical
3415          // layout of the grid is the same as the list default vertical
3416          // size; the horizontal layout is twice the default system metric.
3417          // Another difference: Small mode uses only a horizontal scrollbar,
3418          // not a vertical one. Slots are filled in top-to-bottom,
3419          // left-to-right.
3420          //
3421          LONG cxRect = GetSystemMetrics (SM_CXICONSPACING) * 2;
3422          LONG cyRect = FastList_GetListHeight (pfl);
3423          int cyLayout = cyRECT(rClient) / cyRect;
3424          cyLayout = max (cyLayout, 1);
3425
3426          int hIndex = index / cyLayout;  // 0 = left row
3427          int vIndex = index % cyLayout;  // 0 = top row
3428
3429          prItem->left = hIndex * cxRect;
3430          prItem->right = prItem->left + cxRect;
3431          prItem->top = vIndex * cyRect;
3432          prItem->bottom = prItem->top + cyRect;
3433          break;
3434          }
3435
3436       case FLS_VIEW_LIST:
3437       case FLS_VIEW_TREE:
3438       case FLS_VIEW_TREELIST:
3439          {
3440          // List and Tree layouts are fairly straight-forward: they each show
3441          // a one-dimensional array of items, each of which runs horizontally
3442          // from edge to edge. The vertical layout of the grid is the default
3443          // vertical size (see FastList_GetListHeight()). A vertical scrollbar
3444          // is applied if necessary; a horizontal scrollbar is added for the
3445          // header.
3446          //
3447          LONG cyRect = FastList_GetListHeight (pfl);
3448
3449          prItem->left = 0;
3450          prItem->right = FastList_GetListWidth (pfl);
3451          prItem->top = rClient.top + index * cyRect;
3452          prItem->bottom = prItem->top + cyRect;
3453          break;
3454          }
3455       }
3456 }
3457
3458
3459 void FastList_CallPaintItem (LPFASTLIST pfl, HDC hdc, BOOL fDraw, BOOL fDragImage, int iItem, RECT *prItem, LPFASTLISTITEMREGIONS pReg, POINT *pptTest, BOOL *pfHit)
3460 {
3461    FASTLISTDRAWITEM di;
3462    memset (&di, 0x00, sizeof(di));
3463    di.hdc = hdc;
3464    di.fDraw = fDraw;
3465    di.fDragImage = fDragImage;
3466    di.hWnd = pfl->hList;
3467    di.hItem = pfl->aVisibleHeap[ iItem ];
3468    di.lParam = di.hItem->lpUser;
3469    di.rItem = *prItem;
3470
3471    if (pptTest)
3472       di.ptTextTest = *pptTest;
3473    if (pfHit)
3474       *pfHit = FALSE;
3475
3476    FastList_OnPaintItem (pfl, &di);
3477 // if (GetParent (pfl->hList))
3478 //    SendMessage (GetParent (pfl->hList), WM_DRAWITEM, (WPARAM)GetWindowLong (pfl->hList, GWL_ID), (LPARAM)&di);
3479 // else
3480 //    FastList_OnPaintItem (pfl, &di);
3481
3482    if (pfHit)
3483       *pfHit = di.fTextTestHit;
3484    if (pReg)
3485       memcpy (pReg, &di.reg, sizeof(FASTLISTITEMREGIONS));
3486 }
3487
3488
3489 LPTSTR FastList_GetColumnText (LPFASTLIST pfl, HLISTITEM hItem, int icol, BOOL fAlternateBuffer)
3490 {
3491    if ((icol < (int)hItem->cpszText) && (hItem->apszText[ icol ] != NULL))
3492       return hItem->apszText[ icol ];
3493
3494    if (!GetParent (pfl->hList))
3495       return TEXT("");
3496
3497    // We'll have to send a message to the parent window, and hope the user
3498    // catches it to tell us what the text should be. We have two dynamically-
3499    // allocated buffers we use for storing text.
3500    //
3501    static LPTSTR pszTextA = NULL;
3502    static size_t cchTextA = 0;
3503
3504    static LPTSTR pszTextB = NULL;
3505    static size_t cchTextB = 0;
3506
3507    LPTSTR *ppszText = (fAlternateBuffer) ? &pszTextB : &pszTextA;
3508    size_t *pcchText = (fAlternateBuffer) ? &cchTextB : &cchTextA;
3509
3510    for (size_t cchRequired = 256; ; )
3511       {
3512       if (!*pcchText || (*pcchText < cchRequired))
3513          {
3514          if (*ppszText)
3515             Free (*ppszText);
3516          if ((*ppszText = (LPTSTR)Allocate (sizeof(TCHAR)*(1+cchRequired))) == NULL)
3517             *pcchText = 0;
3518          else
3519             *pcchText = cchRequired;
3520          }
3521       if (!*pcchText)
3522          break;
3523
3524       *(*ppszText) = TEXT('\0');
3525       if (!FastList_Notify_GetItemText (pfl, hItem, icol, *ppszText, *pcchText, &cchRequired))
3526          break;
3527       if (cchRequired <= *pcchText)
3528          break;
3529       }
3530
3531    return (*ppszText) ? (*ppszText) : TEXT("");
3532 }
3533
3534
3535 void FastList_PerformSort (LPFASTLIST pfl)
3536 {
3537    pfl->fSortBeforePaint = FALSE;
3538
3539    // Sorting would be easy, except that we have to handle the tree case.
3540    // For a tree, "sorting" means ordering *sibling* items--we make no
3541    // attempt to order cousins, but all the children under a given parent
3542    // must be sorted. We don't sort the Visible heap directly--instead,
3543    // we sort the hashlist of items, and correct the Visible heap to match.
3544    // That way, the user can later expand/collapse items in the tree
3545    // without our having to re-sort.
3546    //
3547    // In sorting the hashlist items, we're not so much interested in their
3548    // order of enumeration as we're interested in pointing each object's
3549    // {hPrevious} and {hNext} pointers to the right objects. (When sorting
3550    // a tree, this is even more clear; hPrevious and hNext point only to
3551    // siblings.)  To perform a sort on these items, we must generate an
3552    // array of objects, qsort the array, then update the items' pointers.
3553    // That array of objects is in {fg}, the fastlist global structure.
3554    //
3555    // The flow of sorting a list is:
3556    //    1- fill an array with HLISTITEM pointers for all items in the list
3557    //    2- qsort the array, using the {pfl->fnSort} comparison function
3558    //    3- update the HLISTITEMs' {hPrevious}, {hNext}, and {index} members
3559    //    4- call FastList_Update to fix the hash on the revised {index} members
3560    //
3561    // The flow for sorting a tree is more complex:
3562    //    1- set indexNext = 0
3563    //    2- call SortLevel(NULL), to specify that we're sorting root items)
3564    //    SortLevel(hParent):
3565    //       3- fill an array with HLISTITEM pointers for this item and siblings
3566    //       4- qsort the array, using the {pfl->fnSort} comparison function
3567    //       5- update the HLISTITEMs' {hPrevious}, {hNext} members
3568    //       6- use {indexNext++} to assign index values to all visible items
3569    //       7- if hParent->hTreeChild, call SortLevel(hParent->hTreeChild)
3570    //    8- call FastList_Update to fix the hash on the revised {index} members
3571    //
3572    FastList_Begin (pfl->hList);
3573
3574    size_t cItems;
3575    if ((cItems = pfl->lItems->GetCount()) != 0)
3576       {
3577       if (OpenGlobalArray (cItems))
3578          {
3579          fg.pfl = pfl;
3580          FastList_PerformSortLevel (pfl, NULL, ((pfl->dwStyle & FLS_VIEW_TREE) == FLS_VIEW_TREE));
3581
3582          CloseGlobalArray();
3583          }
3584       }
3585
3586    pfl->fSyncIndicesBeforePaint = TRUE;
3587    FastList_End (pfl->hList);
3588 }
3589
3590
3591 void FastList_PerformSortLevel (LPFASTLIST pfl, HLISTITEM hParent, BOOL fTreeSort)
3592 {
3593    // Find the first item in the chain that we're going to sort.
3594    // Then walk that chain, filling in the global array of HLISTITEMs
3595    // If we're sorting the thing as a tree, only add {hItem}'s siblings
3596    // to the fg.aObjects[] array; if we're sorting as a list, add
3597    // all items. Add items regardless of whether they're invisible.
3598    //
3599    size_t cObjectsToSort = 0;
3600
3601    if (fTreeSort)
3602       {
3603       for (HLISTITEM hItem = (hParent) ? hParent->hTreeChild : pfl->hTreeFirst; hItem; hItem = hItem->hTreeNext)
3604          fg.aObjects[ cObjectsToSort++ ] = hItem;
3605       }
3606    else // Viewing by list, so sort items as if there were no tree structure
3607       {
3608       for (HLISTITEM hItem = pfl->hListFirst; hItem; hItem = hItem->hListNext)
3609          fg.aObjects[ cObjectsToSort++ ] = hItem;
3610       }
3611
3612    if (cObjectsToSort != 0)
3613       {
3614       // Perform a qsort on the array
3615       //
3616       FastList_SortFunction (0, 0);
3617       qsort (fg.aObjects, cObjectsToSort, sizeof(HLISTITEM), FastList_SortFunction);
3618
3619       // Walk the array, adjusting objects' next/previous pointers.
3620       //
3621       if (fTreeSort)
3622          {
3623          HLISTITEM hTreePrevious = NULL;
3624          for (size_t iObject = 0; iObject < cObjectsToSort; ++iObject)
3625             {
3626             fg.aObjects[ iObject ]->hTreePrevious = hTreePrevious;
3627             fg.aObjects[ iObject ]->hTreeNext = (iObject < cObjectsToSort-1) ? fg.aObjects[ iObject+1 ] : NULL;
3628             hTreePrevious = fg.aObjects[ iObject ];
3629             }
3630          if (hParent == NULL)
3631             pfl->hTreeFirst = fg.aObjects[0];
3632          else
3633             hParent->hTreeChild = fg.aObjects[0];
3634          }
3635       else // (!(pfl->dwStyle & FLS_VIEW_TREE))
3636          {
3637          HLISTITEM hListPrevious = NULL;
3638          for (size_t iObject = 0; iObject < cObjectsToSort; ++iObject)
3639             {
3640             fg.aObjects[ iObject ]->hListPrevious = hListPrevious;
3641             fg.aObjects[ iObject ]->hListNext = (iObject < cObjectsToSort-1) ? fg.aObjects[ iObject+1 ] : NULL;
3642             hListPrevious = fg.aObjects[ iObject ];
3643             }
3644          pfl->hListFirst = fg.aObjects[0];
3645          }
3646
3647       // If this is to be a treesort, walk the chain ascending children
3648       // recursively as we come to them.
3649       //
3650       if (fTreeSort)
3651          {
3652          for (HLISTITEM hWalk = fg.aObjects[0]; hWalk; hWalk = hWalk->hTreeNext)
3653             {
3654             if (hWalk && hWalk->hTreeChild)
3655                FastList_PerformSortLevel (pfl, hWalk, fTreeSort);
3656             }
3657          }
3658       }
3659 }
3660
3661
3662 int __cdecl FastList_SortFunction (const void *lp1, const void *lp2)
3663 {
3664    if (fg.pfl->pfnSort)
3665       return (*(fg.pfl->pfnSort))(fg.pfl->hList, ((lp1) ? (*(HLISTITEM*)lp1) : 0), ((lp1) ? (*(HLISTITEM*)lp1)->lpUser : 0), ((lp2) ? (*(HLISTITEM*)lp2) : 0), ((lp2) ? (*(HLISTITEM*)lp2)->lpUser : 0));
3666    else
3667       return FastList_SortFunc_AlphaNumeric (fg.pfl->hList, ((lp1) ? (*(HLISTITEM*)lp1) : 0), ((lp1) ? (*(HLISTITEM*)lp1)->lpUser : 0), ((lp2) ? (*(HLISTITEM*)lp2) : 0), ((lp2) ? (*(HLISTITEM*)lp2)->lpUser : 0));
3668 }
3669
3670
3671 void FastList_RepairOneVisibilityFlag (LPFASTLIST pfl, HLISTITEM hItem)
3672 {
3673    // There are two reasons to hide an item: because we're in tree mode
3674    // and one of its parent is collapsed, or because we're not in tree mode
3675    // and it has the only-show-me-in-tree-mode flag set.
3676    //
3677    BOOL fTreeMode = (pfl->dwStyle & FLS_VIEW_TREE) ? TRUE : FALSE;
3678
3679    hItem->fVisible = TRUE;
3680
3681    if ((hItem->dwFlags & FLIF_TREEVIEW_ONLY) && (!fTreeMode))
3682       {
3683       hItem->fVisible = FALSE;
3684       }
3685    else if (fTreeMode)
3686       {
3687       for (HLISTITEM hParent = hItem->hTreeParent; hParent; hParent = hParent->hTreeParent)
3688          {
3689          if ((!hParent->fVisible) || (!hParent->fExpanded))
3690             {
3691             hItem->fVisible = FALSE;
3692             break;
3693             }
3694          }
3695       }
3696 }
3697
3698
3699 void FastList_RepairVisibilityFlags (LPFASTLIST pfl, HLISTITEM hParent, BOOL fForceHidden)
3700 {
3701    BOOL fTreeMode = (pfl->dwStyle & FLS_VIEW_TREE) ? TRUE : FALSE;
3702
3703    // This routine fixes the {fVisible} flags for all items, based on
3704    // their FLIF_TREEVIEW_ONLY flags and whether their parent items are
3705    // visible.
3706    //
3707    // Technically we could acheive this by a normal FindFirst()/FindNext()
3708    // enumeration of the items in the hashlist; on each item, we'd check
3709    // its parents and flags. But we can be a smidgen faster by walking
3710    // the list in tree order--that way, when we hit a collapsed tree, we
3711    // can mark all its children as hidden without any additional work.
3712    //
3713    // Naturally, since we're a FASTlist, we'll take any little smidgen of
3714    // speed we can get.  Find the first item in the chain that we're going
3715    // to fix.
3716    //
3717    for (HLISTITEM hItem = (hParent) ? hParent->hTreeChild : pfl->hTreeFirst; hItem; hItem = hItem->hTreeNext)
3718       {
3719       // If we were passed {fForceHidden==TRUE}, we know that we're in tree
3720       // mode and one of our parents is collapsed--that's great, because it
3721       // means we can set fVisible=FALSE without thinking. If we weren't passed
3722       // that flag, we know that we're either not in treeview mode or none
3723       // of our parents are collapsed--so we don't have to walk up the
3724       // list of parents to check that.
3725       //
3726       if (fForceHidden)
3727          hItem->fVisible = FALSE;
3728       else
3729          hItem->fVisible = fTreeMode || !(hItem->dwFlags & FLIF_TREEVIEW_ONLY);
3730
3731       // We've fixed this item. If it has any children, fix them too.
3732       // Remember that we may be able to blindly tell children to be hidden.
3733       //
3734       if (hItem->hTreeChild)
3735          {
3736          BOOL fChildrenHidden = fForceHidden;
3737          if (fTreeMode && (!hItem->fExpanded || !hItem->fVisible))
3738             fChildrenHidden = TRUE;
3739
3740          FastList_RepairVisibilityFlags (pfl, hItem, fChildrenHidden);
3741          }
3742       }
3743 }
3744
3745
3746 void FastList_RepairVisibilityIndices (LPFASTLIST pfl, HLISTITEM hParent)
3747 {
3748    BOOL fTreeMode = (pfl->dwStyle & FLS_VIEW_TREE) ? TRUE : FALSE;
3749
3750    if (hParent == NULL)  // Starting a new repair?  Initialize pfl->cVisible.
3751       {
3752       pfl->cVisible = 0;
3753       pfl->fSyncIndicesBeforePaint = FALSE;
3754       }
3755
3756    // This routine fixes the {index} settings for all items, by walking
3757    // the list/tree in (presumably already-sorted) order and making
3758    // sure all items with {fVisible} set have smoothly increasing indices.
3759    // It rebuilds the Visible heap as it goes along.
3760    //
3761    for (HLISTITEM hItem = (!fTreeMode) ? (pfl->hListFirst) : (hParent) ? (hParent->hTreeChild) : (pfl->hTreeFirst);
3762         hItem != NULL;
3763         hItem = (!fTreeMode) ? (hItem->hListNext) : (hItem->hTreeNext))
3764       {
3765       if (hItem->fVisible)
3766          {
3767          hItem->index = pfl->cVisible;
3768
3769          // Update the aVisibleHeap to match the new item.
3770          //
3771          REALLOC (pfl->aVisibleHeap, pfl->cVisibleHeap, 1+ hItem->index, cREALLOC_VISIBLEHEAP(pfl));
3772          pfl->aVisibleHeap[ hItem->index ] = hItem;
3773          pfl->cVisible ++;
3774          }
3775
3776       if (fTreeMode && hItem->hTreeChild)
3777          {
3778          FastList_RepairVisibilityIndices (pfl, hItem);
3779          }
3780       }
3781 }
3782
3783
3784 void FastList_ScrollHeader (LPFASTLIST pfl)
3785 {
3786    if (pfl->hHeader)
3787       {
3788       RECT rHeader;
3789       GetWindowRect (pfl->hHeader, &rHeader);
3790
3791       LONG cx = rHeader.right + pfl->dxPixel;
3792
3793       SetWindowPos (pfl->hHeader, 0,
3794                     0 - pfl->dxPixel, 0, cx, cyRECT(rHeader),
3795                     SWP_NOZORDER | SWP_NOACTIVATE);
3796       }
3797 }
3798
3799
3800 void FastList_PerformSelectTest (LPFASTLIST pfl, HLISTITEM hItem)
3801 {
3802    if (pfl->hSelectFirst && !hItem->fSelected)
3803       {
3804       BOOL fMultiple = (pfl->dwStyle & FLS_SELECTION_MULTIPLE);
3805       BOOL fTreeMode = (pfl->dwStyle & FLS_VIEW_TREE);
3806       BOOL fLevel = (pfl->dwStyle & (FLS_SELECTION_LEVEL & ~FLS_SELECTION_MULTIPLE));
3807       BOOL fSibling = (pfl->dwStyle & (FLS_SELECTION_SIBLING & ~FLS_SELECTION_MULTIPLE));
3808
3809       BOOL fClearSelection = FALSE;
3810
3811       if (!fMultiple)
3812          {
3813          fClearSelection = TRUE;
3814          }
3815       else if (fTreeMode)
3816          {
3817          if (fLevel)
3818             {
3819             size_t iLevelItem = 0;
3820             for (HLISTITEM hParent = hItem->hTreeParent; hParent; hParent = hParent->hTreeParent)
3821                ++iLevelItem;
3822
3823             size_t iLevelSelected = 0;
3824             for (hParent = pfl->hSelectFirst->hTreeParent; hParent; hParent = hParent->hTreeParent)
3825                ++iLevelSelected;
3826
3827             if (iLevelItem != iLevelSelected)
3828                fClearSelection = TRUE;
3829             }
3830
3831          if (fSibling)
3832             {
3833             BOOL fFound = FALSE;
3834             for (HLISTITEM hSearch = hItem->hTreePrevious; !fFound && hSearch; hSearch = hSearch->hTreePrevious)
3835                {
3836                if (hSearch == pfl->hSelectFirst)
3837                   fFound = TRUE;
3838                }
3839             for (hSearch = hItem->hTreeNext; !fFound && hSearch; hSearch = hSearch->hTreeNext)
3840                {
3841                if (hSearch == pfl->hSelectFirst)
3842                   fFound = TRUE;
3843                }
3844             if (!fFound)
3845                fClearSelection = TRUE;
3846             }
3847          }
3848
3849       if (fClearSelection)
3850          {
3851          FastList_OnCommand_SelectItem (pfl->hList, NULL, FALSE);
3852          }
3853       }
3854 }
3855
3856
3857 void FastList_PerformSelectItem (LPFASTLIST pfl, HLISTITEM hItem, BOOL fSelect)
3858 {
3859    if (hItem->fSelected != fSelect)
3860       {
3861       if ((hItem->fSelected = fSelect) == FALSE)
3862          {
3863          if (hItem->hSelectPrevious)
3864             hItem->hSelectPrevious->hSelectNext = hItem->hSelectNext;
3865          if (hItem->hSelectNext)
3866             hItem->hSelectNext->hSelectPrevious = hItem->hSelectPrevious;
3867          if (pfl->hSelectFirst == hItem)
3868             pfl->hSelectFirst = hItem->hSelectNext;
3869          if (pfl->hAnchor == hItem)
3870             pfl->hAnchor = NULL;
3871          hItem->hSelectPrevious = NULL;
3872          hItem->hSelectNext = NULL;
3873          }
3874       else // (hItem->fSelected == TRUE)
3875          {
3876          if ((hItem->hSelectNext = pfl->hSelectFirst) != NULL)
3877             hItem->hSelectNext->hSelectPrevious = hItem;
3878          pfl->hSelectFirst = hItem;
3879          if (hItem->hSelectNext == NULL)
3880             pfl->hAnchor = hItem;
3881          }
3882
3883       FastList_Notify_ItemChanged (pfl, hItem);
3884       FastList_Repaint (pfl);
3885       }
3886 }
3887
3888
3889 void FastList_PerformSelectRange (LPFASTLIST pfl, HLISTITEM hItem1, HLISTITEM hItem2)
3890 {
3891    if (hItem1->fVisible && hItem2->fVisible)
3892       {
3893       FastList_Begin (pfl->hList);
3894
3895       int iIndex1 = min (hItem1->index, hItem2->index);
3896       int iIndex2 = max (hItem1->index, hItem2->index);
3897       for (int iIndex = iIndex1; iIndex <= iIndex2; ++iIndex)
3898          FastList_SelectItem (pfl->hList, pfl->aVisibleHeap[ iIndex ], TRUE);
3899
3900       FastList_Repaint (pfl);
3901       FastList_End (pfl->hList);
3902       }
3903 }
3904
3905
3906 void FastList_PerformEnsureVisible (LPFASTLIST pfl)
3907 {
3908    if (pfl->hEnsureVisible && pfl->lItems->fIsInList (pfl->hEnsureVisible))
3909       {
3910       RECT rClient;
3911       GetClientRect (pfl->hList, &rClient);
3912       if (pfl->hScrollV)
3913          rClient.right -= GetSystemMetrics(SM_CXVSCROLL);
3914       if (pfl->hScrollH)
3915          rClient.bottom -= GetSystemMetrics(SM_CYHSCROLL);
3916       if (pfl->hHeader)
3917          rClient.top += FastList_GetHeaderHeight (pfl);
3918
3919       RECT rItem;
3920       FastList_CalcItemRect (pfl, pfl->hEnsureVisible->index, &rItem, !!pfl->hScrollH, !!pfl->hScrollV);
3921
3922       if (rItem.right > (cxRECT(rClient) + pfl->dxPixel))
3923          pfl->dxPixel = rItem.right - cxRECT(rClient);
3924       if (rItem.left < pfl->dxPixel)
3925          pfl->dxPixel = rItem.left;
3926       if (rItem.bottom > (pfl->dyPixel + rClient.bottom))
3927          pfl->dyPixel = rItem.bottom - rClient.bottom;
3928       if (rItem.top < (pfl->dyPixel + rClient.top))
3929          pfl->dyPixel = rItem.top - rClient.top;
3930
3931       pfl->hEnsureVisible = NULL;
3932       FastList_SyncScrollPos (pfl);
3933       }
3934 }
3935
3936
3937 void FastList_ButtonDown (HWND hList, BOOL fRightButton, BOOL fActivate)
3938 {
3939    if (GetParent (hList))
3940       PostMessage (GetParent(hList), WM_NEXTDLGCTL, (WPARAM)hList, TRUE);
3941    else
3942       SetFocus (hList);
3943
3944    LPFASTLIST pfl;
3945    if ((pfl = GetFastList (hList)) != NULL)
3946       {
3947
3948       // The rules for selection are somewhat complex; they follow the
3949       // conventions used by the Windows shell:
3950       //
3951       // * if the user button-downs on the open/close box of a treeview item,
3952       //   expand or collapse that item, and perform no further processing.
3953       //
3954       // * if the user button-downs on an item,
3955       //   and if that item is not selected, then:
3956       //   - if the Control is not down, deselect all items
3957       //   - if the Shift key is down, select all items between the Anchor
3958       //     and the clicked-on item
3959       //   - if the Shift key is not down, select (and anchor on) the item
3960       //
3961       // * if the user button-downs and subsequently moves the mouse enough to
3962       //   indicate a drag operation prior to the next button-up,
3963       //   notify the parent window.
3964       //
3965       // * if the user double-clicked, treat it as a button-down followed
3966       //   immediately thereafter by a button-up
3967       //
3968       // * if the user button-ups without having triggered a drag operation,
3969       //   - if the user had button-downed on an already-selected item,
3970       //     de-select that item
3971       //
3972       // * if the user button-ups after having triggered a drag operation,
3973       //   notify the parent window.
3974       //
3975       // Here, the user has just button-downed, either as part of a single-click
3976       // or a double-click. We therefore will attempt the initial selection
3977       // process, store the screen coordinates at which the user clicked, and
3978       // set capture onto this list window that we may look for drag operations.
3979
3980       // First find out where the user button-downed.
3981       //
3982       DWORD dwPos = GetMessagePos();
3983
3984       POINT ptScreen;
3985       ptScreen.x = LOWORD(dwPos);
3986       ptScreen.y = HIWORD(dwPos);
3987
3988       POINT ptClient = ptScreen;
3989       ScreenToClient (hList, &ptClient);
3990
3991       // Determine what item is underneath the given point; also determine
3992       // the region of the item which was clicked.
3993       //
3994       HLISTITEM hStrict = NULL;
3995       HLISTITEM hNonStrict = NULL;
3996       FASTLISTITEMREGIONS regNonStrict;
3997       if ((hStrict = FastList_ItemFromPoint (hList, &ptClient, TRUE)) == NULL)
3998          {
3999          if ((hNonStrict = FastList_ItemFromPoint (hList, &ptClient, FALSE)) != NULL)
4000             FastList_GetItemRegions (hList, hNonStrict, &regNonStrict);
4001          }
4002
4003       // If the user has clicked on the open/close button of an item, expand
4004       // or collapse that item.
4005       //
4006       FastList_Begin (hList);
4007
4008       if ( (!hStrict) && (hNonStrict) && (PtInRect (&regNonStrict.rButton, ptClient)) )
4009          {
4010          FastList_OnCommand_Expand (hList, hNonStrict, !hNonStrict->fExpanded);
4011          FastList_Notify_ItemExpand (pfl, hNonStrict);
4012          FastList_End (hList);
4013          return;
4014          }
4015
4016       // If the user failed to click on any item at all, and if the control
4017       // key is not down, de-select all items. Regardless, return thereafter.
4018       //
4019       if (!hStrict)
4020          {
4021          if (!fIsControlDown())
4022             FastList_SelectNone (hList);
4023          FastList_SetFocus (hList,NULL);
4024          FastList_End (hList);
4025          FastList_Notify_ItemSelect (pfl);
4026          return;
4027          }
4028
4029       // If the user has double-clicked on an item, and that item is a parent
4030       // of other item, and if the tree is showing, expand or collapse that item.
4031       //
4032       if (fActivate && hStrict->hTreeChild && (pfl->dwStyle & FLS_VIEW_TREE))
4033          {
4034          FastList_OnCommand_Expand (hList, hStrict, !hStrict->fExpanded);
4035          FastList_End (hList);
4036          return;
4037          }
4038
4039       // The user has clicked on an item.  Record what we know about what
4040       // the user has done, and apply message capturing to the list.
4041       //
4042       pfl->fButtonDown = TRUE;
4043       pfl->fRightDrag = fRightButton;
4044       pfl->fDragging = FALSE;
4045       pfl->fTriedToDrag = FALSE;
4046       pfl->ptScreenDown = ptScreen;
4047       pfl->hHitOnDown = hStrict;
4048       pfl->fSelectedOnDown = hStrict->fSelected;
4049       SetCapture (hList);
4050
4051       // There are eight states about which we need to concern ourselves:
4052       //
4053       // Control:  Down (C) or Not Down (c)
4054       // Shift:    Down (S) or Not Down (s)
4055       // Item:     Selected (I) or Not Selected (i)
4056       //
4057       // csi: de-select all items, anchor and select the specified item
4058       // csI: de-select all items, anchor and select the specified item
4059       // cSi: de-select all items, select from the anchor to the specified item
4060       // cSI: de-select all items, select from the anchor to the specified item
4061       // Csi: anchor and select the specified item
4062       // CsI: anchor and select the specified item
4063       // CSi: select from the anchor to the specified item
4064       // CSI: select from the anchor to the specified item
4065       //
4066       // From this we can deduce the following rules:
4067       //
4068       // if Control is not down, begin by de-selecting all items
4069       // if Shift is not down, anchor and select the specified item
4070       // if Shift is down, select from the anchor to the specified item
4071       //
4072       HLISTITEM hAnchor = pfl->hAnchor;
4073       if (!fIsControlDown())
4074          {
4075          FastList_SelectNone (hList);
4076          }
4077       if (!fIsShiftDown() || !hAnchor)
4078          {
4079          FastList_SelectItem (hList, hStrict, TRUE);
4080          pfl->hAnchor = hStrict;
4081          }
4082       if (fIsShiftDown() && hAnchor)
4083          {
4084          FastList_PerformSelectRange (pfl, hAnchor, hStrict);
4085          if (hAnchor->fSelected)
4086             pfl->hAnchor = hAnchor;
4087          }
4088
4089       // Finally, place the focus on the clicked-on item, and return.
4090       // We will complete the selection/dragging operation on mouseup.
4091       //
4092       FastList_SetFocus (hList, hStrict);
4093       FastList_End (hList);
4094       FastList_Notify_ItemSelect (pfl);
4095       }
4096 }
4097
4098
4099 void FastList_MouseMove (HWND hList)
4100 {
4101    LPFASTLIST pfl;
4102    if ((pfl = GetFastList (hList)) != NULL)
4103       {
4104       // If there is no possibility that the user may be dragging something,
4105       // ignore the message.
4106       //
4107       if (!pfl->fButtonDown)
4108          return;
4109
4110       // If the user has triggered a drag operation, notify the parent.
4111       //
4112       if ((!pfl->fTriedToDrag) || (pfl->fDragging))
4113          {
4114          DWORD dwCode = (pfl->fDragging) ? FLN_DRAG : FLN_BEGINDRAG;
4115          DWORD dwPos = GetMessagePos();
4116
4117          POINT ptScreenTo;
4118          ptScreenTo.x = LOWORD(dwPos);
4119          ptScreenTo.y = HIWORD(dwPos);
4120
4121          if (!pfl->fTriedToDrag)
4122             {
4123             if ( (abs(ptScreenTo.x - pfl->ptScreenDown.x) >= GetSystemMetrics(SM_CXDRAG)) ||
4124                  (abs(ptScreenTo.y - pfl->ptScreenDown.y) >= GetSystemMetrics(SM_CYDRAG)) )
4125                {
4126                pfl->fTriedToDrag = TRUE;
4127                }
4128             }
4129          if (pfl->fTriedToDrag)
4130             {
4131             FLN_DRAG_PARAMS fln;
4132             fln.hFirst = pfl->hSelectFirst;
4133             fln.ptScreenFrom = pfl->ptScreenDown;
4134             fln.ptScreenTo = ptScreenTo;
4135             fln.fRightButton = pfl->fRightDrag;
4136             fln.fShift = fIsShiftDown();
4137             fln.fControl = fIsControlDown();
4138             if ( (FastList_Notify_Drag (pfl, dwCode, &fln)) && (dwCode == FLN_BEGINDRAG) )
4139                pfl->fDragging = TRUE;
4140             }
4141          }
4142       }
4143 }
4144
4145
4146 void FastList_ButtonUp (HWND hList, BOOL fRightButton, BOOL fActivate)
4147 {
4148    LPFASTLIST pfl;
4149    if ((pfl = GetFastList (hList)) != NULL)
4150       {
4151       // First, our caller may have just notified us that a double-click
4152       // took place; if so, we may have already handled it (in the ButtonDown)
4153       // routine. Or, moreover, if we never got a button-down at all,
4154       // there's nothing for us to do here.
4155       //
4156       if (!pfl->fButtonDown)
4157          return;
4158       if (pfl->fRightDrag != fRightButton)
4159          return;
4160
4161       // Release capture; we no longer need it.
4162       //
4163       pfl->fButtonDown = FALSE;
4164       ReleaseCapture();
4165
4166       // If the user right-button-ups without having triggered a drag operation,
4167       // send a WM_CONTEXTMENU message.
4168       //
4169       if ( (!pfl->fTriedToDrag) && (fRightButton) )
4170          {
4171          if (GetParent (hList))
4172             {
4173             PostMessage (GetParent (hList), WM_CONTEXTMENU, (WPARAM)hList, (LPARAM)GetMessagePos());
4174             }
4175          }
4176
4177       // If the user button-ups without having triggered a drag operation,
4178       // and if the user had button-downed on an already-selected item,
4179       // de-select that item.
4180       //
4181       if ( (!pfl->fTriedToDrag) && (pfl->hHitOnDown) && (pfl->fSelectedOnDown) && (fIsControlDown()) )
4182          {
4183          FastList_SelectItem (hList, pfl->hHitOnDown, FALSE);
4184          FastList_Notify_ItemSelect (pfl);
4185          return;
4186          }
4187
4188       // If the user button-ups after triggering a drag operation, notify
4189       // our parent.
4190       //
4191       if (pfl->fDragging)
4192          {
4193          DWORD dwPos = GetMessagePos();
4194
4195          POINT ptScreenTo;
4196          ptScreenTo.x = LOWORD(dwPos);
4197          ptScreenTo.y = HIWORD(dwPos);
4198
4199          FLN_DRAG_PARAMS fln;
4200          fln.hFirst = pfl->hSelectFirst;
4201          fln.ptScreenFrom = pfl->ptScreenDown;
4202          fln.ptScreenTo = ptScreenTo;
4203          fln.fRightButton = pfl->fRightDrag;
4204          fln.fShift = fIsShiftDown();
4205          fln.fControl = fIsControlDown();
4206          FastList_Notify_Drag (pfl, FLN_ENDDRAG, &fln);
4207          }
4208       }
4209 }
4210
4211
4212 /*
4213  * UTILITY FUNCTIONS __________________________________________________________
4214  *
4215  */
4216
4217 LPFASTLIST GetFastList (HWND hList)
4218 {
4219    LPFASTLIST pfl;
4220    try {
4221       if ((pfl = (LPFASTLIST)GetWindowLong (hList, 0))->dwSig != dwSigFASTLIST)
4222          pfl = NULL;
4223       else if (pfl->hList != hList)
4224          pfl = NULL;
4225    } catch(...) {
4226       pfl = NULL;
4227    }
4228    return pfl;
4229 }
4230
4231
4232 BOOL fIsFastList (HWND hList)
4233 {
4234    return (GetFastList (hList)) ? TRUE : FALSE;
4235 }
4236
4237
4238 void FastList_Enter (HWND hList)
4239 {
4240    LPFASTLIST pfl;
4241    if ((pfl = GetFastList (hList)) != NULL)
4242       {
4243       EnterCriticalSection (&pfl->cs);
4244       }
4245 }
4246
4247
4248 void FastList_Leave (HWND hList)
4249 {
4250    LPFASTLIST pfl;
4251    if ((pfl = GetFastList (hList)) != NULL)
4252       {
4253       LeaveCriticalSection (&pfl->cs);
4254       }
4255 }
4256
4257
4258 int CALLBACK FastList_SortFunc_AlphaNumeric (HWND hList, HLISTITEM hItem1, LPARAM lpItem1, HLISTITEM hItem2, LPARAM lpItem2)
4259 {
4260    LPFASTLIST pfl;
4261    if ((pfl = GetFastList (hList)) == NULL)
4262       return 0;
4263
4264    if (!hItem1 || !hItem2)
4265       return 0;
4266
4267    LPTSTR pszText1 = FastList_GetColumnText (pfl, hItem1, pfl->iColSort, FALSE);
4268    LPTSTR pszText2 = FastList_GetColumnText (pfl, hItem2, pfl->iColSort, TRUE);
4269
4270    if ( (pfl->iColSort < (int)pfl->cColumns) &&
4271         (pfl->aColumns[ pfl->iColSort ].pszText != NULL) &&
4272         ((pfl->aColumns[ pfl->iColSort ].fmt & HDF_JUSTIFYMASK) == HDF_RIGHT) )
4273       {
4274       double dItem1 = atof (pszText1);
4275       double dItem2 = atof (pszText2);
4276
4277       if (pfl->fRevSort)
4278          return (dItem1 < dItem2) ? 1 : (dItem1 > dItem2) ? -1 : 0;
4279       else
4280          return (dItem1 < dItem2) ? -1 : (dItem1 > dItem2) ? 1 : 0;
4281       }
4282    else // left- or center- justified; use an alphabetic sort
4283       {
4284       if (pfl->fRevSort)
4285          return lstrcmpi (pszText2, pszText1);
4286       else
4287          return lstrcmpi (pszText1, pszText2);
4288       }
4289 }
4290
4291
4292 int CALLBACK FastList_SortFunc_Alphabetic (HWND hList, HLISTITEM hItem1, LPARAM lpItem1, HLISTITEM hItem2, LPARAM lpItem2)
4293 {
4294    LPFASTLIST pfl;
4295    if ((pfl = GetFastList (hList)) == NULL)
4296       return 0;
4297
4298    if (!hItem1 || !hItem2)
4299       return 0;
4300
4301    LPTSTR pszText1 = FastList_GetColumnText (pfl, hItem1, pfl->iColSort, FALSE);
4302    LPTSTR pszText2 = FastList_GetColumnText (pfl, hItem2, pfl->iColSort, TRUE);
4303
4304    if (pfl->fRevSort)
4305       return lstrcmpi (pszText2, pszText1);
4306    else
4307       return lstrcmpi (pszText1, pszText2);
4308 }
4309
4310
4311 int CALLBACK FastList_SortFunc_Numeric (HWND hList, HLISTITEM hItem1, LPARAM lpItem1, HLISTITEM hItem2, LPARAM lpItem2)
4312 {
4313    LPFASTLIST pfl;
4314    if ((pfl = GetFastList (hList)) == NULL)
4315       return 0;
4316
4317    if (!hItem1 || !hItem2)
4318       return 0;
4319
4320    LPTSTR pszText1 = FastList_GetColumnText (pfl, hItem1, pfl->iColSort, FALSE);
4321    LPTSTR pszText2 = FastList_GetColumnText (pfl, hItem2, pfl->iColSort, TRUE);
4322
4323    double dItem1 = atof (pszText1);
4324    double dItem2 = atof (pszText2);
4325
4326    if (pfl->fRevSort)
4327       return (dItem1 < dItem2) ? 1 : (dItem1 > dItem2) ? -1 : 0;
4328    else
4329       return (dItem1 < dItem2) ? -1 : (dItem1 > dItem2) ? 1 : 0;
4330 }
4331
4332
4333 /*
4334  * HASHLIST KEYS ______________________________________________________________
4335  *
4336  */
4337
4338 BOOL CALLBACK FastList_KeyUserParam_Compare (LPHASHLISTKEY pKey, PVOID pObject, PVOID pData)
4339 {
4340    return (((HLISTITEM)pObject)->lpUser == *(LPARAM*)pData) ? TRUE : FALSE;
4341 }
4342
4343 HASHVALUE CALLBACK FastList_KeyUserParam_HashObject (LPHASHLISTKEY pKey, PVOID pObject)
4344 {
4345    return FastList_KeyUserParam_HashData (pKey, &((HLISTITEM)pObject)->lpUser);
4346 }
4347
4348 HASHVALUE CALLBACK FastList_KeyUserParam_HashData (LPHASHLISTKEY pKey, PVOID pData)
4349 {
4350    return (HASHVALUE)*(LPARAM*)pData;
4351 }
4352