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