- Factor out common text shifting logic.
[wine] / dlls / comctl32 / listview.c
1 /*
2  * Listview control
3  *
4  * Copyright 1998, 1999 Eric Kohl
5  * Copyright 1999 Luc Tourangeau
6  * Copyright 2000 Jason Mawdsley
7  * Copyright 2001 CodeWeavers Inc.
8  * Copyright 2002 Dimitrie O. Paun
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public
12  * License as published by the Free Software Foundation; either
13  * version 2.1 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with this library; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23  *
24  * NOTES
25  *
26  * This code was audited for completeness against the documented features
27  * of Comctl32.dll version 6.0 on May. 20, 2005, by James Hawkins.
28  * 
29  * Unless otherwise noted, we believe this code to be complete, as per
30  * the specification mentioned above.
31  * If you discover missing features, or bugs, please note them below.
32  * 
33  * TODO:
34  *
35  * Default Message Processing
36  *   -- EN_KILLFOCUS should be handled in WM_COMMAND
37  *   -- WM_CREATE: create the icon and small icon image lists at this point only if
38  *      the LVS_SHAREIMAGELISTS style is not specified.
39  *   -- WM_ERASEBKGND: forward this message to the parent window if the bkgnd
40  *      color is CLR_NONE.
41  *   -- WM_WINDOWPOSCHANGED: arrange the list items if the current view is icon
42  *      or small icon and the LVS_AUTOARRANGE style is specified.
43  *   -- WM_TIMER
44  *   -- WM_WININICHANGE
45  *
46  * Features
47  *   -- Hot item handling, mouse hovering
48  *   -- Workareas support
49  *   -- Tilemode support
50  *   -- Groups support
51  *
52  * Bugs
53  *   -- Expand large item in ICON mode when the cursor is flying over the icon or text.
54  *   -- Support CustomDraw options for _WIN32_IE >= 0x560 (see NMLVCUSTOMDRAW docs).
55  *   -- LVA_SNAPTOGRID not implemented
56  *   -- LISTVIEW_ApproximateViewRect partially implemented
57  *   -- LISTVIEW_[GS]etColumnOrderArray stubs
58  *   -- LISTVIEW_SetColumnWidth ignores header images & bitmap
59  *   -- LISTVIEW_SetIconSpacing is incomplete
60  *   -- LISTVIEW_SortItems is broken
61  *   -- LISTVIEW_StyleChanged doesn't handle some changes too well
62  *
63  * Speedups
64  *   -- LISTVIEW_GetNextItem needs to be rewritten. It is currently
65  *      linear in the number of items in the list, and this is
66  *      unacceptable for large lists.
67  *   -- in sorted mode, LISTVIEW_InsertItemT sorts the array,
68  *      instead of inserting in the right spot
69  *   -- we should keep an ordered array of coordinates in iconic mode
70  *      this would allow to frame items (iterator_frameditems),
71  *      and find nearest item (LVFI_NEARESTXY) a lot more efficiently
72  *
73  * Flags
74  *   -- LVIF_COLUMNS
75  *   -- LVIF_GROUPID
76  *   -- LVIF_NORECOMPUTE
77  *
78  * States
79  *   -- LVIS_ACTIVATING (not currently supported by comctl32.dll version 6.0)
80  *   -- LVIS_CUT
81  *   -- LVIS_DROPHILITED
82  *   -- LVIS_OVERLAYMASK
83  *
84  * Styles
85  *   -- LVS_NOLABELWRAP
86  *   -- LVS_NOSCROLL (see Q137520)
87  *   -- LVS_SORTASCENDING, LVS_SORTDESCENDING
88  *   -- LVS_ALIGNTOP
89  *   -- LVS_TYPESTYLEMASK
90  *
91  * Extended Styles
92  *   -- LVS_EX_BORDERSELECT
93  *   -- LVS_EX_FLATSB
94  *   -- LVS_EX_GRIDLINES
95  *   -- LVS_EX_HEADERDRAGDROP
96  *   -- LVS_EX_INFOTIP
97  *   -- LVS_EX_LABELTIP
98  *   -- LVS_EX_MULTIWORKAREAS
99  *   -- LVS_EX_ONECLICKACTIVATE
100  *   -- LVS_EX_REGIONAL
101  *   -- LVS_EX_SIMPLESELECT
102  *   -- LVS_EX_TRACKSELECT
103  *   -- LVS_EX_TWOCLICKACTIVATE
104  *   -- LVS_EX_UNDERLINECOLD
105  *   -- LVS_EX_UNDERLINEHOT
106  *   
107  * Notifications:
108  *   -- LVN_BEGINSCROLL, LVN_ENDSCROLL
109  *   -- LVN_GETINFOTIP
110  *   -- LVN_HOTTRACK
111  *   -- LVN_MARQUEEBEGIN
112  *   -- LVN_ODFINDITEM
113  *   -- LVN_SETDISPINFO
114  *   -- NM_HOVER
115  *   -- LVN_BEGINRDRAG
116  *
117  * Messages:
118  *   -- LVM_CANCELEDITLABEL
119  *   -- LVM_ENABLEGROUPVIEW
120  *   -- LVM_GETBKIMAGE, LVM_SETBKIMAGE
121  *   -- LVM_GETGROUPINFO, LVM_SETGROUPINFO
122  *   -- LVM_GETGROUPMETRICS, LVM_SETGROUPMETRICS
123  *   -- LVM_GETINSERTMARK, LVM_SETINSERTMARK
124  *   -- LVM_GETINSERTMARKCOLOR, LVM_SETINSERTMARKCOLOR
125  *   -- LVM_GETINSERTMARKRECT
126  *   -- LVM_GETNUMBEROFWORKAREAS
127  *   -- LVM_GETOUTLINECOLOR, LVM_SETOUTLINECOLOR
128  *   -- LVM_GETSELECTEDCOLUMN, LVM_SETSELECTEDCOLUMN
129  *   -- LVM_GETISEARCHSTRINGW, LVM_GETISEARCHSTRINGA
130  *   -- LVM_GETTILEINFO, LVM_SETTILEINFO
131  *   -- LVM_GETTILEVIEWINFO, LVM_SETTILEVIEWINFO
132  *   -- LVM_GETUNICODEFORMAT, LVM_SETUNICODEFORMAT
133  *   -- LVM_GETVIEW, LVM_SETVIEW
134  *   -- LVM_GETWORKAREAS, LVM_SETWORKAREAS
135  *   -- LVM_HASGROUP, LVM_INSERTGROUP, LVM_REMOVEGROUP, LVM_REMOVEALLGROUPS
136  *   -- LVM_INSERTGROUPSORTED
137  *   -- LVM_INSERTMARKHITTEST
138  *   -- LVM_ISGROUPVIEWENABLED
139  *   -- LVM_MAPIDTOINDEX, LVM_MAPINDEXTOID
140  *   -- LVM_MOVEGROUP
141  *   -- LVM_MOVEITEMTOGROUP
142  *   -- LVM_SETINFOTIP
143  *   -- LVM_SETTILEWIDTH
144  *   -- LVM_SORTGROUPS
145  *   -- LVM_SORTITEMSEX
146  *
147  * Macros:
148  *   -- ListView_GetCheckSate, ListView_SetCheckState
149  *   -- ListView_GetHoverTime, ListView_SetHoverTime
150  *   -- ListView_GetISearchString
151  *   -- ListView_GetNumberOfWorkAreas
152  *   -- ListView_GetOrigin
153  *   -- ListView_GetTextBkColor
154  *   -- ListView_GetUnicodeFormat, ListView_SetUnicodeFormat
155  *   -- ListView_GetWorkAreas, ListView_SetWorkAreas
156  *   -- ListView_SortItemsEx
157  *
158  * Functions:
159  *   -- LVGroupComparE
160  *
161  * Known differences in message stream from native control (not known if
162  * these differences cause problems):
163  *   LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
164  *   LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
165  *   WM_CREATE does not issue WM_QUERYUISTATE and associated registry
166  *     processing for "USEDOUBLECLICKTIME".
167  */
168
169 #include "config.h"
170 #include "wine/port.h"
171
172 #include <assert.h>
173 #include <ctype.h>
174 #include <string.h>
175 #include <stdlib.h>
176 #include <stdarg.h>
177 #include <stdio.h>
178
179 #include "windef.h"
180 #include "winbase.h"
181 #include "winnt.h"
182 #include "wingdi.h"
183 #include "winuser.h"
184 #include "winnls.h"
185 #include "commctrl.h"
186 #include "comctl32.h"
187
188 #include "wine/debug.h"
189 #include "wine/unicode.h"
190
191 WINE_DEFAULT_DEBUG_CHANNEL(listview);
192
193 /* make sure you set this to 0 for production use! */
194 #define DEBUG_RANGES 1
195
196 typedef struct tagCOLUMN_INFO
197 {
198   RECT rcHeader;        /* tracks the header's rectangle */
199   int fmt;              /* same as LVCOLUMN.fmt */
200 } COLUMN_INFO;
201
202 typedef struct tagITEMHDR
203 {
204   LPWSTR pszText;
205   INT iImage;
206 } ITEMHDR, *LPITEMHDR;
207
208 typedef struct tagSUBITEM_INFO
209 {
210   ITEMHDR hdr;
211   INT iSubItem;
212 } SUBITEM_INFO;
213
214 typedef struct tagITEM_INFO
215 {
216   ITEMHDR hdr;
217   UINT state;
218   LPARAM lParam;
219   INT iIndent;
220 } ITEM_INFO;
221
222 typedef struct tagRANGE
223 {
224   INT lower;
225   INT upper;
226 } RANGE;
227
228 typedef struct tagRANGES
229 {
230   HDPA hdpa;
231 } *RANGES;
232
233 typedef struct tagITERATOR
234 {
235   INT nItem;
236   INT nSpecial;
237   RANGE range;
238   RANGES ranges;
239   INT index;
240 } ITERATOR;
241
242 typedef struct tagLISTVIEW_INFO
243 {
244   HWND hwndSelf;
245   HBRUSH hBkBrush;
246   COLORREF clrBk;
247   COLORREF clrText;
248   COLORREF clrTextBk;
249   COLORREF clrTextBkDefault;
250   HIMAGELIST himlNormal;
251   HIMAGELIST himlSmall;
252   HIMAGELIST himlState;
253   BOOL bLButtonDown;
254   BOOL bRButtonDown;
255   POINT ptClickPos;         /* point where the user clicked */ 
256   BOOL bNoItemMetrics;          /* flags if item metrics are not yet computed */
257   INT nItemHeight;
258   INT nItemWidth;
259   RANGES selectionRanges;
260   INT nSelectionMark;
261   INT nHotItem;
262   SHORT notifyFormat;
263   HWND hwndNotify;
264   RECT rcList;                 /* This rectangle is really the window
265                                 * client rectangle possibly reduced by the 
266                                 * horizontal scroll bar and/or header - see 
267                                 * LISTVIEW_UpdateSize. This rectangle offset
268                                 * by the LISTVIEW_GetOrigin value is in
269                                 * client coordinates   */
270   SIZE iconSize;
271   SIZE iconSpacing;
272   SIZE iconStateSize;
273   UINT uCallbackMask;
274   HWND hwndHeader;
275   HCURSOR hHotCursor;
276   HFONT hDefaultFont;
277   HFONT hFont;
278   INT ntmHeight;                /* Some cached metrics of the font used */
279   INT ntmAveCharWidth;          /* by the listview to draw items */
280   BOOL bRedraw;                 /* Turns on/off repaints & invalidations */
281   BOOL bAutoarrange;            /* Autoarrange flag when NOT in LVS_AUTOARRANGE */
282   BOOL bFocus;
283   BOOL bDoChangeNotify;         /* send change notification messages? */
284   INT nFocusedItem;
285   RECT rcFocus;
286   DWORD dwStyle;                /* the cached window GWL_STYLE */
287   DWORD dwLvExStyle;            /* extended listview style */
288   INT nItemCount;               /* the number of items in the list */
289   HDPA hdpaItems;               /* array ITEM_INFO pointers */
290   HDPA hdpaPosX;                /* maintains the (X, Y) coordinates of the */
291   HDPA hdpaPosY;                /* items in LVS_ICON, and LVS_SMALLICON modes */
292   HDPA hdpaColumns;             /* array of COLUMN_INFO pointers */
293   POINT currIconPos;            /* this is the position next icon will be placed */
294   PFNLVCOMPARE pfnCompare;
295   LPARAM lParamSort;
296   HWND hwndEdit;
297   WNDPROC EditWndProc;
298   INT nEditLabelItem;
299   DWORD dwHoverTime;
300   HWND hwndToolTip;
301
302   DWORD cditemmode;             /* Keep the custom draw flags for an item/row */
303
304   DWORD lastKeyPressTimestamp;
305   WPARAM charCode;
306   INT nSearchParamLength;
307   WCHAR szSearchParam[ MAX_PATH ];
308   BOOL bIsDrawing;
309   INT nMeasureItemHeight;
310 } LISTVIEW_INFO;
311
312 /*
313  * constants
314  */
315 /* How many we debug buffer to allocate */
316 #define DEBUG_BUFFERS 20
317 /* The size of a single debug bbuffer */
318 #define DEBUG_BUFFER_SIZE 256
319
320 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
321 #define SB_INTERNAL      -1
322
323 /* maximum size of a label */
324 #define DISP_TEXT_SIZE 512
325
326 /* padding for items in list and small icon display modes */
327 #define WIDTH_PADDING 12
328
329 /* padding for items in list, report and small icon display modes */
330 #define HEIGHT_PADDING 1
331
332 /* offset of items in report display mode */
333 #define REPORT_MARGINX 2
334
335 /* padding for icon in large icon display mode
336  *   ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
337  *                                 that HITTEST will see.
338  *   ICON_TOP_PADDING_HITABLE - spacing between above and icon.
339  *   ICON_TOP_PADDING - sum of the two above.
340  *   ICON_BOTTOM_PADDING - between bottom of icon and top of text
341  *   LABEL_HOR_PADDING - between text and sides of box
342  *   LABEL_VERT_PADDING - between bottom of text and end of box
343  *
344  *   ICON_LR_PADDING - additional width above icon size.
345  *   ICON_LR_HALF - half of the above value
346  */
347 #define ICON_TOP_PADDING_NOTHITABLE  2
348 #define ICON_TOP_PADDING_HITABLE     2
349 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
350 #define ICON_BOTTOM_PADDING          4
351 #define LABEL_HOR_PADDING            5
352 #define LABEL_VERT_PADDING           7
353 #define ICON_LR_PADDING              16
354 #define ICON_LR_HALF                 (ICON_LR_PADDING/2)
355
356 /* default label width for items in list and small icon display modes */
357 #define DEFAULT_LABEL_WIDTH 40
358
359 /* default column width for items in list display mode */
360 #define DEFAULT_COLUMN_WIDTH 128
361
362 /* Size of "line" scroll for V & H scrolls */
363 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
364
365 /* Padding betwen image and label */
366 #define IMAGE_PADDING  2
367
368 /* Padding behind the label */
369 #define TRAILING_LABEL_PADDING  12
370 #define TRAILING_HEADER_PADDING  11
371
372 /* Border for the icon caption */
373 #define CAPTION_BORDER  2
374
375 /* Standard DrawText flags */
376 #define LV_ML_DT_FLAGS  (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
377 #define LV_FL_DT_FLAGS  (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_NOCLIP)
378 #define LV_SL_DT_FLAGS  (DT_VCENTER | DT_NOPREFIX | DT_EDITCONTROL | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
379
380 /* The time in milliseconds to reset the search in the list */
381 #define KEY_DELAY       450
382
383 /* Dump the LISTVIEW_INFO structure to the debug channel */
384 #define LISTVIEW_DUMP(iP) do { \
385   TRACE("hwndSelf=%p, clrBk=0x%06lx, clrText=0x%06lx, clrTextBk=0x%06lx, ItemHeight=%d, ItemWidth=%d, Style=0x%08lx\n", \
386         iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
387         iP->nItemHeight, iP->nItemWidth, infoPtr->dwStyle); \
388   TRACE("hwndSelf=%p, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08lx, Focus=%d\n", \
389         iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
390         iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, iP->bFocus ); \
391   TRACE("hwndSelf=%p, ntmH=%d, icSz.cx=%ld, icSz.cy=%ld, icSp.cx=%ld, icSp.cy=%ld, notifyFmt=%d\n", \
392         iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
393         iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
394   TRACE("hwndSelf=%p, rcList=%s\n", iP->hwndSelf, debugrect(&iP->rcList)); \
395 } while(0)
396
397 /*
398  * forward declarations
399  */
400 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL);
401 static void LISTVIEW_GetItemBox(LISTVIEW_INFO *, INT, LPRECT);
402 static void LISTVIEW_GetItemOrigin(LISTVIEW_INFO *, INT, LPPOINT);
403 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *, INT, LPPOINT);
404 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *, INT, LPRECT);
405 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *, INT);
406 static void LISTVIEW_GetOrigin(LISTVIEW_INFO *, LPPOINT);
407 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *, LPRECT);
408 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *, INT);
409 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *, const LVITEMW *, BOOL);
410 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *);
411 static void LISTVIEW_SetSelection(LISTVIEW_INFO *, INT);
412 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *);
413 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *, INT, BOOL);
414 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *, WPARAM, LPARAM);
415 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *, PFNLVCOMPARE, LPARAM);
416 static INT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *, LPCWSTR, BOOL);
417 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *, INT);
418 static UINT LISTVIEW_GetItemState(LISTVIEW_INFO *, INT, UINT);
419 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *, INT, const LVITEMW *);
420 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *, INT, INT, HWND);
421 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *, INT, INT, HWND);
422 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *);
423 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *, INT, BOOL);
424 static HWND CreateEditLabelT(LISTVIEW_INFO *, LPCWSTR, DWORD, INT, INT, INT, INT, BOOL);
425 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *, INT, HIMAGELIST);
426 static INT LISTVIEW_HitTest(LISTVIEW_INFO *, LPLVHITTESTINFO, BOOL, BOOL);
427
428 /******** Text handling functions *************************************/
429
430 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
431  * text string. The string may be ANSI or Unicode, in which case
432  * the boolean isW tells us the type of the string.
433  *
434  * The name of the function tell what type of strings it expects:
435  *   W: Unicode, T: ANSI/Unicode - function of isW
436  */
437
438 static inline BOOL is_textW(LPCWSTR text)
439 {
440     return text != NULL && text != LPSTR_TEXTCALLBACKW;
441 }
442
443 static inline BOOL is_textT(LPCWSTR text, BOOL isW)
444 {
445     /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
446     return is_textW(text);
447 }
448
449 static inline int textlenT(LPCWSTR text, BOOL isW)
450 {
451     return !is_textT(text, isW) ? 0 :
452            isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
453 }
454
455 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
456 {
457     if (isDestW)
458         if (isSrcW) lstrcpynW(dest, src, max);
459         else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
460     else
461         if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
462         else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
463 }
464
465 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
466 {
467     LPWSTR wstr = (LPWSTR)text;
468
469     if (!isW && is_textT(text, isW))
470     {
471         INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
472         wstr = Alloc(len * sizeof(WCHAR));
473         if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
474     }
475     TRACE("   wstr=%s\n", text == LPSTR_TEXTCALLBACKW ?  "(callback)" : debugstr_w(wstr));
476     return wstr;
477 }
478
479 static inline void textfreeT(LPWSTR wstr, BOOL isW)
480 {
481     if (!isW && is_textT(wstr, isW)) Free (wstr);
482 }
483
484 /*
485  * dest is a pointer to a Unicode string
486  * src is a pointer to a string (Unicode if isW, ANSI if !isW)
487  */
488 static BOOL textsetptrT(LPWSTR *dest, LPWSTR src, BOOL isW)
489 {
490     BOOL bResult = TRUE;
491     
492     if (src == LPSTR_TEXTCALLBACKW)
493     {
494         if (is_textW(*dest)) Free(*dest);
495         *dest = LPSTR_TEXTCALLBACKW;
496     }
497     else
498     {
499         LPWSTR pszText = textdupTtoW(src, isW);
500         if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
501         bResult = Str_SetPtrW(dest, pszText);
502         textfreeT(pszText, isW);
503     }
504     return bResult;
505 }
506
507 /*
508  * compares a Unicode to a Unicode/ANSI text string
509  */
510 static inline int textcmpWT(LPCWSTR aw, LPCWSTR bt, BOOL isW)
511 {
512     if (!aw) return bt ? -1 : 0;
513     if (!bt) return aw ? 1 : 0;
514     if (aw == LPSTR_TEXTCALLBACKW)
515         return bt == LPSTR_TEXTCALLBACKW ? 0 : -1;
516     if (bt != LPSTR_TEXTCALLBACKW)
517     {
518         LPWSTR bw = textdupTtoW(bt, isW);
519         int r = bw ? lstrcmpW(aw, bw) : 1;
520         textfreeT(bw, isW);
521         return r;
522     }       
523             
524     return 1;
525 }
526     
527 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
528 {
529     int res;
530
531     n = min(min(n, strlenW(s1)), strlenW(s2));
532     res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
533     return res ? res - sizeof(WCHAR) : res;
534 }
535
536 /******** Debugging functions *****************************************/
537
538 static inline LPCSTR debugtext_t(LPCWSTR text, BOOL isW)
539 {
540     if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
541     return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
542 }
543
544 static inline LPCSTR debugtext_tn(LPCWSTR text, BOOL isW, INT n)
545 {
546     if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
547     n = min(textlenT(text, isW), n);
548     return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
549 }
550
551 static char* debug_getbuf()
552 {
553     static int index = 0;
554     static char buffers[DEBUG_BUFFERS][DEBUG_BUFFER_SIZE];
555     return buffers[index++ % DEBUG_BUFFERS];
556 }
557
558 static inline const char* debugrange(const RANGE *lprng)
559 {
560     if (lprng) 
561     {
562         char* buf = debug_getbuf();
563         snprintf(buf, DEBUG_BUFFER_SIZE, "[%d, %d)", lprng->lower, lprng->upper);
564         return buf;
565     } else return "(null)";
566 }
567
568 static inline const char* debugpoint(const POINT *lppt)
569 {
570     if (lppt) 
571     {
572         char* buf = debug_getbuf();
573         snprintf(buf, DEBUG_BUFFER_SIZE, "(%ld, %ld)", lppt->x, lppt->y);
574         return buf;
575     } else return "(null)";
576 }
577
578 static inline const char* debugrect(const RECT *rect)
579 {
580     if (rect) 
581     {
582         char* buf = debug_getbuf();
583         snprintf(buf, DEBUG_BUFFER_SIZE, "[(%ld, %ld);(%ld, %ld)]",
584                  rect->left, rect->top, rect->right, rect->bottom);
585         return buf;
586     } else return "(null)";
587 }
588
589 static const char* debugscrollinfo(const SCROLLINFO *pScrollInfo)
590 {
591     char* buf = debug_getbuf(), *text = buf;
592     int len, size = DEBUG_BUFFER_SIZE;
593     
594     if (pScrollInfo == NULL) return "(null)";
595     len = snprintf(buf, size, "{cbSize=%d, ", pScrollInfo->cbSize);
596     if (len == -1) goto end; buf += len; size -= len;
597     if (pScrollInfo->fMask & SIF_RANGE)
598         len = snprintf(buf, size, "nMin=%d, nMax=%d, ", pScrollInfo->nMin, pScrollInfo->nMax);
599     else len = 0;
600     if (len == -1) goto end; buf += len; size -= len;
601     if (pScrollInfo->fMask & SIF_PAGE)
602         len = snprintf(buf, size, "nPage=%u, ", pScrollInfo->nPage);
603     else len = 0;
604     if (len == -1) goto end; buf += len; size -= len;
605     if (pScrollInfo->fMask & SIF_POS)
606         len = snprintf(buf, size, "nPos=%d, ", pScrollInfo->nPos);
607     else len = 0;
608     if (len == -1) goto end; buf += len; size -= len;
609     if (pScrollInfo->fMask & SIF_TRACKPOS)
610         len = snprintf(buf, size, "nTrackPos=%d, ", pScrollInfo->nTrackPos);
611     else len = 0;
612     if (len == -1) goto end; buf += len; size -= len;
613     goto undo;
614 end:
615     buf = text + strlen(text);
616 undo:
617     if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
618     return text;
619
620
621 static const char* debugnmlistview(const NMLISTVIEW *plvnm)
622 {
623     if (plvnm)
624     {
625         char* buf = debug_getbuf();
626         snprintf(buf, DEBUG_BUFFER_SIZE, "iItem=%d, iSubItem=%d, uNewState=0x%x,"
627                  " uOldState=0x%x, uChanged=0x%x, ptAction=%s, lParam=%ld\n",
628                  plvnm->iItem, plvnm->iSubItem, plvnm->uNewState, plvnm->uOldState,
629                  plvnm->uChanged, debugpoint(&plvnm->ptAction), plvnm->lParam);
630         return buf;
631     } else return "(null)";
632 }
633
634 static const char* debuglvitem_t(const LVITEMW *lpLVItem, BOOL isW)
635 {
636     char* buf = debug_getbuf(), *text = buf;
637     int len, size = DEBUG_BUFFER_SIZE;
638     
639     if (lpLVItem == NULL) return "(null)";
640     len = snprintf(buf, size, "{iItem=%d, iSubItem=%d, ", lpLVItem->iItem, lpLVItem->iSubItem);
641     if (len == -1) goto end; buf += len; size -= len;
642     if (lpLVItem->mask & LVIF_STATE)
643         len = snprintf(buf, size, "state=%x, stateMask=%x, ", lpLVItem->state, lpLVItem->stateMask);
644     else len = 0;
645     if (len == -1) goto end; buf += len; size -= len;
646     if (lpLVItem->mask & LVIF_TEXT)
647         len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem->pszText, isW, 80), lpLVItem->cchTextMax);
648     else len = 0;
649     if (len == -1) goto end; buf += len; size -= len;
650     if (lpLVItem->mask & LVIF_IMAGE)
651         len = snprintf(buf, size, "iImage=%d, ", lpLVItem->iImage);
652     else len = 0;
653     if (len == -1) goto end; buf += len; size -= len;
654     if (lpLVItem->mask & LVIF_PARAM)
655         len = snprintf(buf, size, "lParam=%lx, ", lpLVItem->lParam);
656     else len = 0;
657     if (len == -1) goto end; buf += len; size -= len;
658     if (lpLVItem->mask & LVIF_INDENT)
659         len = snprintf(buf, size, "iIndent=%d, ", lpLVItem->iIndent);
660     else len = 0;
661     if (len == -1) goto end; buf += len; size -= len;
662     goto undo;
663 end:
664     buf = text + strlen(text);
665 undo:
666     if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
667     return text;
668 }
669
670 static const char* debuglvcolumn_t(const LVCOLUMNW *lpColumn, BOOL isW)
671 {
672     char* buf = debug_getbuf(), *text = buf;
673     int len, size = DEBUG_BUFFER_SIZE;
674     
675     if (lpColumn == NULL) return "(null)";
676     len = snprintf(buf, size, "{");
677     if (len == -1) goto end; buf += len; size -= len;
678     if (lpColumn->mask & LVCF_SUBITEM)
679         len = snprintf(buf, size, "iSubItem=%d, ",  lpColumn->iSubItem);
680     else len = 0;
681     if (len == -1) goto end; buf += len; size -= len;
682     if (lpColumn->mask & LVCF_FMT)
683         len = snprintf(buf, size, "fmt=%x, ", lpColumn->fmt);
684     else len = 0;
685     if (len == -1) goto end; buf += len; size -= len;
686     if (lpColumn->mask & LVCF_WIDTH)
687         len = snprintf(buf, size, "cx=%d, ", lpColumn->cx);
688     else len = 0;
689     if (len == -1) goto end; buf += len; size -= len;
690     if (lpColumn->mask & LVCF_TEXT)
691         len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn->pszText, isW, 80), lpColumn->cchTextMax);
692     else len = 0;
693     if (len == -1) goto end; buf += len; size -= len;
694     if (lpColumn->mask & LVCF_IMAGE)
695         len = snprintf(buf, size, "iImage=%d, ", lpColumn->iImage);
696     else len = 0;
697     if (len == -1) goto end; buf += len; size -= len;
698     if (lpColumn->mask & LVCF_ORDER)
699         len = snprintf(buf, size, "iOrder=%d, ", lpColumn->iOrder);
700     else len = 0;
701     if (len == -1) goto end; buf += len; size -= len;
702     goto undo;
703 end:
704     buf = text + strlen(text);
705 undo:
706     if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
707     return text;
708 }
709
710 static const char* debuglvhittestinfo(const LVHITTESTINFO *lpht)
711 {
712     if (lpht) 
713     {
714         char* buf = debug_getbuf();
715         snprintf(buf, DEBUG_BUFFER_SIZE, "{pt=%s, flags=0x%x, iItem=%d, iSubItem=%d}",
716                  debugpoint(&lpht->pt), lpht->flags, lpht->iItem, lpht->iSubItem);
717         return buf;
718     } else return "(null)";
719 }
720
721 /* Return the corresponding text for a given scroll value */
722 static inline LPCSTR debugscrollcode(int nScrollCode)
723 {
724   switch(nScrollCode)
725   {
726   case SB_LINELEFT: return "SB_LINELEFT";
727   case SB_LINERIGHT: return "SB_LINERIGHT";
728   case SB_PAGELEFT: return "SB_PAGELEFT";
729   case SB_PAGERIGHT: return "SB_PAGERIGHT";
730   case SB_THUMBPOSITION: return "SB_THUMBPOSITION";
731   case SB_THUMBTRACK: return "SB_THUMBTRACK";
732   case SB_ENDSCROLL: return "SB_ENDSCROLL";
733   case SB_INTERNAL: return "SB_INTERNAL";
734   default: return "unknown";
735   }
736 }
737
738
739 /******** Notification functions i************************************/
740
741 static LRESULT notify_hdr(LISTVIEW_INFO *infoPtr, INT code, LPNMHDR pnmh)
742 {
743     LRESULT result;
744     
745     TRACE("(code=%d)\n", code);
746
747     pnmh->hwndFrom = infoPtr->hwndSelf;
748     pnmh->idFrom = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
749     pnmh->code = code;
750     result = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY,
751                           (WPARAM)pnmh->idFrom, (LPARAM)pnmh);
752
753     TRACE("  <= %ld\n", result);
754
755     return result;
756 }
757
758 static inline LRESULT notify(LISTVIEW_INFO *infoPtr, INT code)
759 {
760     NMHDR nmh;
761     return notify_hdr(infoPtr, code, &nmh);
762 }
763
764 static inline void notify_itemactivate(LISTVIEW_INFO *infoPtr, LVHITTESTINFO *htInfo)
765 {
766     NMITEMACTIVATE nmia;
767     LVITEMW item;
768
769     if (htInfo) {
770       nmia.uNewState = 0;
771       nmia.uOldState = 0;
772       nmia.uChanged  = 0;
773       nmia.uKeyFlags = 0;
774       
775       item.mask = LVIF_PARAM|LVIF_STATE;
776       item.iItem = htInfo->iItem;
777       item.iSubItem = 0;
778       if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) {
779           nmia.lParam = item.lParam;
780           nmia.uOldState = item.state;
781           nmia.uNewState = item.state | LVIS_ACTIVATING;
782           nmia.uChanged  = LVIF_STATE;
783       }
784       
785       nmia.iItem = htInfo->iItem;
786       nmia.iSubItem = htInfo->iSubItem;
787       nmia.ptAction = htInfo->pt;     
788       
789       if (GetKeyState(VK_SHIFT) & 0x8000) nmia.uKeyFlags |= LVKF_SHIFT;
790       if (GetKeyState(VK_CONTROL) & 0x8000) nmia.uKeyFlags |= LVKF_CONTROL;
791       if (GetKeyState(VK_MENU) & 0x8000) nmia.uKeyFlags |= LVKF_ALT;
792     }
793     notify_hdr(infoPtr, LVN_ITEMACTIVATE, (LPNMHDR)&nmia);
794 }
795
796 static inline LRESULT notify_listview(LISTVIEW_INFO *infoPtr, INT code, LPNMLISTVIEW plvnm)
797 {
798     TRACE("(code=%d, plvnm=%s)\n", code, debugnmlistview(plvnm));
799     return notify_hdr(infoPtr, code, (LPNMHDR)plvnm);
800 }
801
802 static LRESULT notify_click(LISTVIEW_INFO *infoPtr,  INT code, LVHITTESTINFO *lvht)
803 {
804     NMLISTVIEW nmlv;
805     LVITEMW item;
806    
807     TRACE("code=%d, lvht=%s\n", code, debuglvhittestinfo(lvht)); 
808     ZeroMemory(&nmlv, sizeof(nmlv));
809     nmlv.iItem = lvht->iItem;
810     nmlv.iSubItem = lvht->iSubItem;
811     nmlv.ptAction = lvht->pt;
812     item.mask = LVIF_PARAM;
813     item.iItem = lvht->iItem;
814     item.iSubItem = 0;
815     if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
816     return notify_listview(infoPtr, code, &nmlv);
817 }
818
819 static void notify_deleteitem(LISTVIEW_INFO *infoPtr, INT nItem)
820 {
821     NMLISTVIEW nmlv;
822     LVITEMW item;
823    
824     ZeroMemory(&nmlv, sizeof (NMLISTVIEW));
825     nmlv.iItem = nItem;
826     item.mask = LVIF_PARAM;
827     item.iItem = nItem;
828     item.iSubItem = 0;
829     if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
830     notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
831 }
832
833 static int get_ansi_notification(INT unicodeNotificationCode)
834 {
835     switch (unicodeNotificationCode)
836     {
837     case LVN_BEGINLABELEDITW: return LVN_BEGINLABELEDITA;
838     case LVN_ENDLABELEDITW: return LVN_ENDLABELEDITA;
839     case LVN_GETDISPINFOW: return LVN_GETDISPINFOA;
840     case LVN_SETDISPINFOW: return LVN_SETDISPINFOA;
841     case LVN_ODFINDITEMW: return LVN_ODFINDITEMA;
842     case LVN_GETINFOTIPW: return LVN_GETINFOTIPA;
843     }
844     ERR("unknown notification %x\n", unicodeNotificationCode);
845     assert(FALSE);
846     return 0;
847 }
848
849 /*
850   Send notification. depends on dispinfoW having same
851   structure as dispinfoA.
852   infoPtr : listview struct
853   notificationCode : *Unicode* notification code
854   pdi : dispinfo structure (can be unicode or ansi)
855   isW : TRUE if dispinfo is Unicode
856 */
857 static BOOL notify_dispinfoT(LISTVIEW_INFO *infoPtr, INT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
858 {
859     BOOL bResult = FALSE;
860     BOOL convertToAnsi = FALSE, convertToUnicode = FALSE;
861     INT cchTempBufMax = 0, savCchTextMax = 0, realNotifCode;
862     LPWSTR pszTempBuf = NULL, savPszText = NULL;
863
864     if ((pdi->item.mask & LVIF_TEXT) && is_textT(pdi->item.pszText, isW))
865     {
866         convertToAnsi = (isW && infoPtr->notifyFormat == NFR_ANSI);
867         convertToUnicode = (!isW && infoPtr->notifyFormat == NFR_UNICODE);
868     }
869
870     if (convertToAnsi || convertToUnicode)
871     {
872         if (notificationCode != LVN_GETDISPINFOW)
873         {
874             cchTempBufMax = convertToUnicode ?
875                 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0):
876                 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
877         }
878         else
879         {
880             cchTempBufMax = pdi->item.cchTextMax;
881             *pdi->item.pszText = 0; /* make sure we don't process garbage */
882         }
883
884         pszTempBuf = Alloc( (convertToUnicode ? sizeof(WCHAR) : sizeof(CHAR)) * cchTempBufMax);
885         if (!pszTempBuf) return FALSE;
886
887         if (convertToUnicode)
888             MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1,
889                                 pszTempBuf, cchTempBufMax);
890         else
891             WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) pszTempBuf,
892                                 cchTempBufMax, NULL, NULL);
893
894         savCchTextMax = pdi->item.cchTextMax;
895         savPszText = pdi->item.pszText;
896         pdi->item.pszText = pszTempBuf;
897         pdi->item.cchTextMax = cchTempBufMax;
898     }
899
900     if (infoPtr->notifyFormat == NFR_ANSI)
901         realNotifCode = get_ansi_notification(notificationCode);
902     else
903         realNotifCode = notificationCode;
904     TRACE(" pdi->item=%s\n", debuglvitem_t(&pdi->item, infoPtr->notifyFormat != NFR_ANSI));
905     bResult = notify_hdr(infoPtr, realNotifCode, &pdi->hdr);
906
907     if (convertToUnicode || convertToAnsi)
908     {
909         if (convertToUnicode) /* note : pointer can be changed by app ! */
910             WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) savPszText,
911                                 savCchTextMax, NULL, NULL);
912         else
913             MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
914                                 savPszText, savCchTextMax);
915         pdi->item.pszText = savPszText; /* restores our buffer */
916         pdi->item.cchTextMax = savCchTextMax;
917         Free (pszTempBuf);
918     }
919     return bResult;
920 }
921
922 static void customdraw_fill(NMLVCUSTOMDRAW *lpnmlvcd, LISTVIEW_INFO *infoPtr, HDC hdc, 
923                             const RECT *rcBounds, const LVITEMW *lplvItem)
924 {
925     ZeroMemory(lpnmlvcd, sizeof(NMLVCUSTOMDRAW));
926     lpnmlvcd->nmcd.hdc = hdc;
927     lpnmlvcd->nmcd.rc = *rcBounds;
928     lpnmlvcd->clrTextBk = infoPtr->clrTextBk;
929     lpnmlvcd->clrText   = infoPtr->clrText;
930     if (!lplvItem) return;
931     lpnmlvcd->nmcd.dwItemSpec = lplvItem->iItem + 1;
932     lpnmlvcd->iSubItem = lplvItem->iSubItem;
933     if (lplvItem->state & LVIS_SELECTED) lpnmlvcd->nmcd.uItemState |= CDIS_SELECTED;
934     if (lplvItem->state & LVIS_FOCUSED) lpnmlvcd->nmcd.uItemState |= CDIS_FOCUS;
935     if (lplvItem->iItem == infoPtr->nHotItem) lpnmlvcd->nmcd.uItemState |= CDIS_HOT;
936     lpnmlvcd->nmcd.lItemlParam = lplvItem->lParam;
937 }
938
939 static inline DWORD notify_customdraw (LISTVIEW_INFO *infoPtr, DWORD dwDrawStage, NMLVCUSTOMDRAW *lpnmlvcd)
940 {
941     BOOL isForItem = (lpnmlvcd->nmcd.dwItemSpec != 0);
942     DWORD result;
943
944     lpnmlvcd->nmcd.dwDrawStage = dwDrawStage;
945     if (isForItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_ITEM; 
946     if (lpnmlvcd->iSubItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_SUBITEM;
947     if (isForItem) lpnmlvcd->nmcd.dwItemSpec--;
948     result = notify_hdr(infoPtr, NM_CUSTOMDRAW, &lpnmlvcd->nmcd.hdr);
949     if (isForItem) lpnmlvcd->nmcd.dwItemSpec++;
950     return result;
951 }
952
953 static void prepaint_setup (LISTVIEW_INFO *infoPtr, HDC hdc, NMLVCUSTOMDRAW *lpnmlvcd)
954 {
955     /* apprently, for selected items, we have to override the returned values */
956     if (lpnmlvcd->nmcd.uItemState & CDIS_SELECTED)
957     {
958         if (infoPtr->bFocus)
959         {
960             lpnmlvcd->clrTextBk = comctl32_color.clrHighlight;
961             lpnmlvcd->clrText   = comctl32_color.clrHighlightText;
962         }
963         else if (infoPtr->dwStyle & LVS_SHOWSELALWAYS)
964         {
965             lpnmlvcd->clrTextBk = comctl32_color.clr3dFace;
966             lpnmlvcd->clrText   = comctl32_color.clrBtnText;
967         }
968     }
969
970     /* Set the text attributes */
971     if (lpnmlvcd->clrTextBk != CLR_NONE)
972     {
973         SetBkMode(hdc, OPAQUE);
974         if (lpnmlvcd->clrTextBk == CLR_DEFAULT)
975             SetBkColor(hdc, infoPtr->clrTextBkDefault);
976         else
977             SetBkColor(hdc,lpnmlvcd->clrTextBk);
978     }
979     else
980         SetBkMode(hdc, TRANSPARENT);
981     SetTextColor(hdc, lpnmlvcd->clrText);
982 }
983
984 static inline DWORD notify_postpaint (LISTVIEW_INFO *infoPtr, NMLVCUSTOMDRAW *lpnmlvcd)
985 {
986     return notify_customdraw(infoPtr, CDDS_POSTPAINT, lpnmlvcd);
987 }
988
989 /******** Item iterator functions **********************************/
990
991 static RANGES ranges_create(int count);
992 static void ranges_destroy(RANGES ranges);
993 static BOOL ranges_add(RANGES ranges, RANGE range);
994 static BOOL ranges_del(RANGES ranges, RANGE range);
995 static void ranges_dump(RANGES ranges);
996
997 static inline BOOL ranges_additem(RANGES ranges, INT nItem)
998 {
999     RANGE range = { nItem, nItem + 1 };
1000
1001     return ranges_add(ranges, range);
1002 }
1003
1004 static inline BOOL ranges_delitem(RANGES ranges, INT nItem)
1005 {
1006     RANGE range = { nItem, nItem + 1 };
1007
1008     return ranges_del(ranges, range);
1009 }
1010
1011 /***
1012  * ITERATOR DOCUMENTATION
1013  *
1014  * The iterator functions allow for easy, and convenient iteration
1015  * over items of iterest in the list. Typically, you create a
1016  * iterator, use it, and destroy it, as such:
1017  *   ITERATOR i;
1018  *
1019  *   iterator_xxxitems(&i, ...);
1020  *   while (iterator_{prev,next}(&i)
1021  *   {
1022  *       //code which uses i.nItem
1023  *   }
1024  *   iterator_destroy(&i);
1025  *
1026  *   where xxx is either: framed, or visible.
1027  * Note that it is important that the code destroys the iterator
1028  * after it's done with it, as the creation of the iterator may
1029  * allocate memory, which thus needs to be freed.
1030  * 
1031  * You can iterate both forwards, and backwards through the list,
1032  * by using iterator_next or iterator_prev respectively.
1033  * 
1034  * Lower numbered items are draw on top of higher number items in
1035  * LVS_ICON, and LVS_SMALLICON (which are the only modes where
1036  * items may overlap). So, to test items, you should use
1037  *    iterator_next
1038  * which lists the items top to bottom (in Z-order).
1039  * For drawing items, you should use
1040  *    iterator_prev
1041  * which lists the items bottom to top (in Z-order).
1042  * If you keep iterating over the items after the end-of-items
1043  * marker (-1) is returned, the iterator will start from the
1044  * beginning. Typically, you don't need to test for -1,
1045  * because iterator_{next,prev} will return TRUE if more items
1046  * are to be iterated over, or FALSE otherwise.
1047  *
1048  * Note: the iterator is defined to be bidirectional. That is,
1049  *       any number of prev followed by any number of next, or
1050  *       five versa, should leave the iterator at the same item:
1051  *           prev * n, next * n = next * n, prev * n
1052  *
1053  * The iterator has a notion of an out-of-order, special item,
1054  * which sits at the start of the list. This is used in
1055  * LVS_ICON, and LVS_SMALLICON mode to handle the focused item,
1056  * which needs to be first, as it may overlap other items.
1057  *           
1058  * The code is a bit messy because we have:
1059  *   - a special item to deal with
1060  *   - simple range, or composite range
1061  *   - empty range.
1062  * If you find bugs, or want to add features, please make sure you
1063  * always check/modify *both* iterator_prev, and iterator_next.
1064  */
1065
1066 /****
1067  * This function iterates through the items in increasing order,
1068  * but prefixed by the special item, then -1. That is:
1069  *    special, 1, 2, 3, ..., n, -1.
1070  * Each item is listed only once.
1071  */
1072 static inline BOOL iterator_next(ITERATOR* i)
1073 {
1074     if (i->nItem == -1)
1075     {
1076         i->nItem = i->nSpecial;
1077         if (i->nItem != -1) return TRUE;
1078     }
1079     if (i->nItem == i->nSpecial)
1080     {
1081         if (i->ranges) i->index = 0;
1082         goto pickarange;
1083     }
1084
1085     i->nItem++;
1086 testitem:
1087     if (i->nItem == i->nSpecial) i->nItem++;
1088     if (i->nItem < i->range.upper) return TRUE;
1089
1090 pickarange:
1091     if (i->ranges)
1092     {
1093         if (i->index < DPA_GetPtrCount(i->ranges->hdpa))
1094             i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, i->index++);
1095         else goto end;
1096     }
1097     else if (i->nItem >= i->range.upper) goto end;
1098
1099     i->nItem = i->range.lower;
1100     if (i->nItem >= 0) goto testitem;
1101 end:
1102     i->nItem = -1;
1103     return FALSE;
1104 }
1105
1106 /****
1107  * This function iterates through the items in decreasing order,
1108  * followed by the special item, then -1. That is:
1109  *    n, n-1, ..., 3, 2, 1, special, -1.
1110  * Each item is listed only once.
1111  */
1112 static inline BOOL iterator_prev(ITERATOR* i)
1113 {
1114     BOOL start = FALSE;
1115
1116     if (i->nItem == -1)
1117     {
1118         start = TRUE;
1119         if (i->ranges) i->index = DPA_GetPtrCount(i->ranges->hdpa);
1120         goto pickarange;
1121     }
1122     if (i->nItem == i->nSpecial)
1123     {
1124         i->nItem = -1;
1125         return FALSE;
1126     }
1127
1128 testitem:
1129     i->nItem--;
1130     if (i->nItem == i->nSpecial) i->nItem--;
1131     if (i->nItem >= i->range.lower) return TRUE;
1132
1133 pickarange:
1134     if (i->ranges)
1135     {
1136         if (i->index > 0)
1137             i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, --i->index);
1138         else goto end;
1139     }
1140     else if (!start && i->nItem < i->range.lower) goto end;
1141
1142     i->nItem = i->range.upper;
1143     if (i->nItem > 0) goto testitem;
1144 end:
1145     return (i->nItem = i->nSpecial) != -1;
1146 }
1147
1148 static RANGE iterator_range(ITERATOR* i)
1149 {
1150     RANGE range;
1151
1152     if (!i->ranges) return i->range;
1153
1154     if (DPA_GetPtrCount(i->ranges->hdpa) > 0)
1155     {
1156         range.lower = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, 0)).lower;
1157         range.upper = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, DPA_GetPtrCount(i->ranges->hdpa) - 1)).upper;
1158     }
1159     else range.lower = range.upper = 0;
1160
1161     return range;
1162 }
1163
1164 /***
1165  * Releases resources associated with this ierator.
1166  */
1167 static inline void iterator_destroy(ITERATOR* i)
1168 {
1169     ranges_destroy(i->ranges);
1170 }
1171
1172 /***
1173  * Create an empty iterator.
1174  */
1175 static inline BOOL iterator_empty(ITERATOR* i)
1176 {
1177     ZeroMemory(i, sizeof(*i));
1178     i->nItem = i->nSpecial = i->range.lower = i->range.upper = -1;
1179     return TRUE;
1180 }
1181
1182 /***
1183  * Create an iterator over a range.
1184  */
1185 static inline BOOL iterator_rangeitems(ITERATOR* i, RANGE range)
1186 {
1187     iterator_empty(i);
1188     i->range = range;
1189     return TRUE;
1190 }
1191
1192 /***
1193  * Create an iterator over a bunch of ranges.
1194  * Please note that the iterator will take ownership of the ranges,
1195  * and will free them upon destruction.
1196  */
1197 static inline BOOL iterator_rangesitems(ITERATOR* i, RANGES ranges)
1198 {
1199     iterator_empty(i);
1200     i->ranges = ranges;
1201     return TRUE;
1202 }
1203
1204 /***
1205  * Creates an iterator over the items which intersect lprc.
1206  */
1207 static BOOL iterator_frameditems(ITERATOR* i, LISTVIEW_INFO* infoPtr, const RECT *lprc)
1208 {
1209     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1210     RECT frame = *lprc, rcItem, rcTemp;
1211     POINT Origin;
1212     
1213     /* in case we fail, we want to return an empty iterator */
1214     if (!iterator_empty(i)) return FALSE;
1215
1216     LISTVIEW_GetOrigin(infoPtr, &Origin);
1217
1218     TRACE("(lprc=%s)\n", debugrect(lprc));
1219     OffsetRect(&frame, -Origin.x, -Origin.y);
1220
1221     if (uView == LVS_ICON || uView == LVS_SMALLICON)
1222     {
1223         INT nItem;
1224         
1225         if (uView == LVS_ICON && infoPtr->nFocusedItem != -1)
1226         {
1227             LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcItem);
1228             if (IntersectRect(&rcTemp, &rcItem, lprc))
1229                 i->nSpecial = infoPtr->nFocusedItem;
1230         }
1231         if (!(iterator_rangesitems(i, ranges_create(50)))) return FALSE;
1232         /* to do better here, we need to have PosX, and PosY sorted */
1233         TRACE("building icon ranges:\n");
1234         for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
1235         {
1236             rcItem.left = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1237             rcItem.top = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1238             rcItem.right = rcItem.left + infoPtr->nItemWidth;
1239             rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1240             if (IntersectRect(&rcTemp, &rcItem, &frame))
1241                 ranges_additem(i->ranges, nItem);
1242         }
1243         return TRUE;
1244     }
1245     else if (uView == LVS_REPORT)
1246     {
1247         RANGE range;
1248         
1249         if (frame.left >= infoPtr->nItemWidth) return TRUE;
1250         if (frame.top >= infoPtr->nItemHeight * infoPtr->nItemCount) return TRUE;
1251         
1252         range.lower = max(frame.top / infoPtr->nItemHeight, 0);
1253         range.upper = min((frame.bottom - 1) / infoPtr->nItemHeight, infoPtr->nItemCount - 1) + 1;
1254         if (range.upper <= range.lower) return TRUE;
1255         if (!iterator_rangeitems(i, range)) return FALSE;
1256         TRACE("    report=%s\n", debugrange(&i->range));
1257     }
1258     else
1259     {
1260         INT nPerCol = max((infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight, 1);
1261         INT nFirstRow = max(frame.top / infoPtr->nItemHeight, 0);
1262         INT nLastRow = min((frame.bottom - 1) / infoPtr->nItemHeight, nPerCol - 1);
1263         INT nFirstCol = max(frame.left / infoPtr->nItemWidth, 0);
1264         INT nLastCol = min((frame.right - 1) / infoPtr->nItemWidth, (infoPtr->nItemCount + nPerCol - 1) / nPerCol);
1265         INT lower = nFirstCol * nPerCol + nFirstRow;
1266         RANGE item_range;
1267         INT nCol;
1268
1269         TRACE("nPerCol=%d, nFirstRow=%d, nLastRow=%d, nFirstCol=%d, nLastCol=%d, lower=%d\n",
1270               nPerCol, nFirstRow, nLastRow, nFirstCol, nLastCol, lower);
1271         
1272         if (nLastCol < nFirstCol || nLastRow < nFirstRow) return TRUE;
1273
1274         if (!(iterator_rangesitems(i, ranges_create(nLastCol - nFirstCol + 1)))) return FALSE;
1275         TRACE("building list ranges:\n");
1276         for (nCol = nFirstCol; nCol <= nLastCol; nCol++)
1277         {
1278             item_range.lower = nCol * nPerCol + nFirstRow;
1279             if(item_range.lower >= infoPtr->nItemCount) break;
1280             item_range.upper = min(nCol * nPerCol + nLastRow + 1, infoPtr->nItemCount);
1281             TRACE("   list=%s\n", debugrange(&item_range));
1282             ranges_add(i->ranges, item_range);
1283         }
1284     }
1285
1286     return TRUE;
1287 }
1288
1289 /***
1290  * Creates an iterator over the items which intersect the visible region of hdc.
1291  */
1292 static BOOL iterator_visibleitems(ITERATOR *i, LISTVIEW_INFO *infoPtr, HDC  hdc)
1293 {
1294     POINT Origin, Position;
1295     RECT rcItem, rcClip;
1296     INT rgntype;
1297     
1298     rgntype = GetClipBox(hdc, &rcClip);
1299     if (rgntype == NULLREGION) return iterator_empty(i);
1300     if (!iterator_frameditems(i, infoPtr, &rcClip)) return FALSE;
1301     if (rgntype == SIMPLEREGION) return TRUE;
1302
1303     /* first deal with the special item */
1304     if (i->nSpecial != -1)
1305     {
1306         LISTVIEW_GetItemBox(infoPtr, i->nSpecial, &rcItem);
1307         if (!RectVisible(hdc, &rcItem)) i->nSpecial = -1;
1308     }
1309     
1310     /* if we can't deal with the region, we'll just go with the simple range */
1311     LISTVIEW_GetOrigin(infoPtr, &Origin);
1312     TRACE("building visible range:\n");
1313     if (!i->ranges && i->range.lower < i->range.upper)
1314     {
1315         if (!(i->ranges = ranges_create(50))) return TRUE;
1316         if (!ranges_add(i->ranges, i->range))
1317         {
1318             ranges_destroy(i->ranges);
1319             i->ranges = 0;
1320             return TRUE;
1321         }
1322     }
1323
1324     /* now delete the invisible items from the list */
1325     while(iterator_next(i))
1326     {
1327         LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
1328         rcItem.left = Position.x + Origin.x;
1329         rcItem.top = Position.y + Origin.y;
1330         rcItem.right = rcItem.left + infoPtr->nItemWidth;
1331         rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1332         if (!RectVisible(hdc, &rcItem))
1333             ranges_delitem(i->ranges, i->nItem);
1334     }
1335     /* the iterator should restart on the next iterator_next */
1336     TRACE("done\n");
1337     
1338     return TRUE;
1339 }
1340
1341 /******** Misc helper functions ************************************/
1342
1343 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
1344                                       WPARAM wParam, LPARAM lParam, BOOL isW)
1345 {
1346     if (isW) return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
1347     else return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
1348 }
1349
1350 static inline BOOL is_autoarrange(LISTVIEW_INFO *infoPtr)
1351 {
1352     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1353     
1354     return ((infoPtr->dwStyle & LVS_AUTOARRANGE) || infoPtr->bAutoarrange) &&
1355            (uView == LVS_ICON || uView == LVS_SMALLICON);
1356 }
1357
1358 /******** Internal API functions ************************************/
1359
1360 static inline COLUMN_INFO * LISTVIEW_GetColumnInfo(LISTVIEW_INFO *infoPtr, INT nSubItem)
1361 {
1362     static COLUMN_INFO mainItem;
1363
1364     if (nSubItem == 0 && DPA_GetPtrCount(infoPtr->hdpaColumns) == 0) return &mainItem;
1365     assert (nSubItem >= 0 && nSubItem < DPA_GetPtrCount(infoPtr->hdpaColumns));
1366     return (COLUMN_INFO *)DPA_GetPtr(infoPtr->hdpaColumns, nSubItem);
1367 }
1368         
1369 static inline void LISTVIEW_GetHeaderRect(LISTVIEW_INFO *infoPtr, INT nSubItem, RECT *lprc)
1370 {
1371     *lprc = LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->rcHeader;
1372 }
1373         
1374 static inline BOOL LISTVIEW_GetItemW(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem)
1375 {
1376     return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
1377 }
1378
1379 /* Listview invalidation functions: use _only_ these functions to invalidate */
1380
1381 static inline BOOL is_redrawing(LISTVIEW_INFO *infoPtr)
1382 {
1383     return infoPtr->bRedraw;
1384 }
1385
1386 static inline void LISTVIEW_InvalidateRect(LISTVIEW_INFO *infoPtr, const RECT* rect)
1387 {
1388     if(!is_redrawing(infoPtr)) return; 
1389     TRACE(" invalidating rect=%s\n", debugrect(rect));
1390     InvalidateRect(infoPtr->hwndSelf, rect, TRUE);
1391 }
1392
1393 static inline void LISTVIEW_InvalidateItem(LISTVIEW_INFO *infoPtr, INT nItem)
1394 {
1395     RECT rcBox;
1396
1397     if(!is_redrawing(infoPtr)) return; 
1398     LISTVIEW_GetItemBox(infoPtr, nItem, &rcBox);
1399     LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1400 }
1401
1402 static inline void LISTVIEW_InvalidateSubItem(LISTVIEW_INFO *infoPtr, INT nItem, INT nSubItem)
1403 {
1404     POINT Origin, Position;
1405     RECT rcBox;
1406     
1407     if(!is_redrawing(infoPtr)) return; 
1408     assert ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT);
1409     LISTVIEW_GetOrigin(infoPtr, &Origin);
1410     LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
1411     LISTVIEW_GetHeaderRect(infoPtr, nSubItem, &rcBox);
1412     rcBox.top = 0;
1413     rcBox.bottom = infoPtr->nItemHeight;
1414     OffsetRect(&rcBox, Origin.x + Position.x, Origin.y + Position.y);
1415     LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1416 }
1417
1418 static inline void LISTVIEW_InvalidateList(LISTVIEW_INFO *infoPtr)
1419 {
1420     LISTVIEW_InvalidateRect(infoPtr, NULL);
1421 }
1422
1423 static inline void LISTVIEW_InvalidateColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
1424 {
1425     RECT rcCol;
1426     
1427     if(!is_redrawing(infoPtr)) return; 
1428     LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
1429     rcCol.top = infoPtr->rcList.top;
1430     rcCol.bottom = infoPtr->rcList.bottom;
1431     LISTVIEW_InvalidateRect(infoPtr, &rcCol);
1432 }
1433
1434 /***
1435  * DESCRIPTION:
1436  * Retrieves the number of items that can fit vertically in the client area.
1437  *
1438  * PARAMETER(S):
1439  * [I] infoPtr : valid pointer to the listview structure
1440  *
1441  * RETURN:
1442  * Number of items per row.
1443  */
1444 static inline INT LISTVIEW_GetCountPerRow(LISTVIEW_INFO *infoPtr)
1445 {
1446     INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1447
1448     return max(nListWidth/infoPtr->nItemWidth, 1);
1449 }
1450
1451 /***
1452  * DESCRIPTION:
1453  * Retrieves the number of items that can fit horizontally in the client
1454  * area.
1455  *
1456  * PARAMETER(S):
1457  * [I] infoPtr : valid pointer to the listview structure
1458  *
1459  * RETURN:
1460  * Number of items per column.
1461  */
1462 static inline INT LISTVIEW_GetCountPerColumn(LISTVIEW_INFO *infoPtr)
1463 {
1464     INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1465
1466     return max(nListHeight / infoPtr->nItemHeight, 1);
1467 }
1468
1469
1470 /*************************************************************************
1471  *              LISTVIEW_ProcessLetterKeys
1472  *
1473  *  Processes keyboard messages generated by pressing the letter keys
1474  *  on the keyboard.
1475  *  What this does is perform a case insensitive search from the
1476  *  current position with the following quirks:
1477  *  - If two chars or more are pressed in quick succession we search
1478  *    for the corresponding string (e.g. 'abc').
1479  *  - If there is a delay we wipe away the current search string and
1480  *    restart with just that char.
1481  *  - If the user keeps pressing the same character, whether slowly or
1482  *    fast, so that the search string is entirely composed of this
1483  *    character ('aaaaa' for instance), then we search for first item
1484  *    that starting with that character.
1485  *  - If the user types the above character in quick succession, then
1486  *    we must also search for the corresponding string ('aaaaa'), and
1487  *    go to that string if there is a match.
1488  *
1489  * PARAMETERS
1490  *   [I] hwnd : handle to the window
1491  *   [I] charCode : the character code, the actual character
1492  *   [I] keyData : key data
1493  *
1494  * RETURNS
1495  *
1496  *  Zero.
1497  *
1498  * BUGS
1499  *
1500  *  - The current implementation has a list of characters it will
1501  *    accept and it ignores averything else. In particular it will
1502  *    ignore accentuated characters which seems to match what
1503  *    Windows does. But I'm not sure it makes sense to follow
1504  *    Windows there.
1505  *  - We don't sound a beep when the search fails.
1506  *
1507  * SEE ALSO
1508  *
1509  *  TREEVIEW_ProcessLetterKeys
1510  */
1511 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData)
1512 {
1513     INT nItem;
1514     INT endidx,idx;
1515     LVITEMW item;
1516     WCHAR buffer[MAX_PATH];
1517     DWORD lastKeyPressTimestamp = infoPtr->lastKeyPressTimestamp;
1518
1519     /* simple parameter checking */
1520     if (!charCode || !keyData) return 0;
1521
1522     /* only allow the valid WM_CHARs through */
1523     if (!isalnum(charCode) &&
1524         charCode != '.' && charCode != '`' && charCode != '!' &&
1525         charCode != '@' && charCode != '#' && charCode != '$' &&
1526         charCode != '%' && charCode != '^' && charCode != '&' &&
1527         charCode != '*' && charCode != '(' && charCode != ')' &&
1528         charCode != '-' && charCode != '_' && charCode != '+' &&
1529         charCode != '=' && charCode != '\\'&& charCode != ']' &&
1530         charCode != '}' && charCode != '[' && charCode != '{' &&
1531         charCode != '/' && charCode != '?' && charCode != '>' &&
1532         charCode != '<' && charCode != ',' && charCode != '~')
1533         return 0;
1534
1535     /* if there's one item or less, there is no where to go */
1536     if (infoPtr->nItemCount <= 1) return 0;
1537
1538     /* update the search parameters */
1539     infoPtr->lastKeyPressTimestamp = GetTickCount();
1540     if (infoPtr->lastKeyPressTimestamp - lastKeyPressTimestamp < KEY_DELAY) {
1541         if (infoPtr->nSearchParamLength < MAX_PATH)
1542             infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
1543         if (infoPtr->charCode != charCode)
1544             infoPtr->charCode = charCode = 0;
1545     } else {
1546         infoPtr->charCode=charCode;
1547         infoPtr->szSearchParam[0]=charCode;
1548         infoPtr->nSearchParamLength=1;
1549         /* Redundant with the 1 char string */
1550         charCode=0;
1551     }
1552
1553     /* and search from the current position */
1554     nItem=-1;
1555     if (infoPtr->nFocusedItem >= 0) {
1556         endidx=infoPtr->nFocusedItem;
1557         idx=endidx;
1558         /* if looking for single character match,
1559          * then we must always move forward
1560          */
1561         if (infoPtr->nSearchParamLength == 1)
1562             idx++;
1563     } else {
1564         endidx=infoPtr->nItemCount;
1565         idx=0;
1566     }
1567     do {
1568         if (idx == infoPtr->nItemCount) {
1569             if (endidx == infoPtr->nItemCount || endidx == 0)
1570                 break;
1571             idx=0;
1572         }
1573
1574         /* get item */
1575         item.mask = LVIF_TEXT;
1576         item.iItem = idx;
1577         item.iSubItem = 0;
1578         item.pszText = buffer;
1579         item.cchTextMax = MAX_PATH;
1580         if (!LISTVIEW_GetItemW(infoPtr, &item)) return 0;
1581
1582         /* check for a match */
1583         if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
1584             nItem=idx;
1585             break;
1586         } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
1587                     (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
1588             /* This would work but we must keep looking for a longer match */
1589             nItem=idx;
1590         }
1591         idx++;
1592     } while (idx != endidx);
1593
1594     if (nItem != -1)
1595         LISTVIEW_KeySelection(infoPtr, nItem);
1596
1597     return 0;
1598 }
1599
1600 /*************************************************************************
1601  * LISTVIEW_UpdateHeaderSize [Internal]
1602  *
1603  * Function to resize the header control
1604  *
1605  * PARAMS
1606  * [I]  hwnd : handle to a window
1607  * [I]  nNewScrollPos : scroll pos to set
1608  *
1609  * RETURNS
1610  * None.
1611  */
1612 static void LISTVIEW_UpdateHeaderSize(LISTVIEW_INFO *infoPtr, INT nNewScrollPos)
1613 {
1614     RECT winRect;
1615     POINT point[2];
1616
1617     TRACE("nNewScrollPos=%d\n", nNewScrollPos);
1618
1619     GetWindowRect(infoPtr->hwndHeader, &winRect);
1620     point[0].x = winRect.left;
1621     point[0].y = winRect.top;
1622     point[1].x = winRect.right;
1623     point[1].y = winRect.bottom;
1624
1625     MapWindowPoints(HWND_DESKTOP, infoPtr->hwndSelf, point, 2);
1626     point[0].x = -nNewScrollPos;
1627     point[1].x += nNewScrollPos;
1628
1629     SetWindowPos(infoPtr->hwndHeader,0,
1630         point[0].x,point[0].y,point[1].x,point[1].y,
1631         SWP_NOZORDER | SWP_NOACTIVATE);
1632 }
1633
1634 /***
1635  * DESCRIPTION:
1636  * Update the scrollbars. This functions should be called whenever
1637  * the content, size or view changes.
1638  *
1639  * PARAMETER(S):
1640  * [I] infoPtr : valid pointer to the listview structure
1641  *
1642  * RETURN:
1643  * None
1644  */
1645 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *infoPtr)
1646 {
1647     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1648     SCROLLINFO horzInfo, vertInfo;
1649
1650     if ((infoPtr->dwStyle & LVS_NOSCROLL) || !is_redrawing(infoPtr)) return;
1651
1652     ZeroMemory(&horzInfo, sizeof(SCROLLINFO));
1653     horzInfo.cbSize = sizeof(SCROLLINFO);
1654     horzInfo.nPage = infoPtr->rcList.right - infoPtr->rcList.left;
1655
1656     /* for now, we'll set info.nMax to the _count_, and adjust it later */
1657     if (uView == LVS_LIST)
1658     {
1659         INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
1660         horzInfo.nMax = (infoPtr->nItemCount + nPerCol - 1) / nPerCol;
1661
1662         /* scroll by at least one column per page */
1663         if(horzInfo.nPage < infoPtr->nItemWidth)
1664                 horzInfo.nPage = infoPtr->nItemWidth;
1665
1666         horzInfo.nPage /= infoPtr->nItemWidth;
1667     }
1668     else if (uView == LVS_REPORT)
1669     {
1670         horzInfo.nMax = infoPtr->nItemWidth;
1671     }
1672     else /* LVS_ICON, or LVS_SMALLICON */
1673     {
1674         RECT rcView;
1675
1676         if (LISTVIEW_GetViewRect(infoPtr, &rcView)) horzInfo.nMax = rcView.right - rcView.left;
1677     }
1678   
1679     horzInfo.fMask = SIF_RANGE | SIF_PAGE;
1680     horzInfo.nMax = max(horzInfo.nMax - 1, 0);
1681     SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo, TRUE);
1682     TRACE("horzInfo=%s\n", debugscrollinfo(&horzInfo));
1683
1684     /* Setting the horizontal scroll can change the listview size
1685      * (and potentially everything else) so we need to recompute
1686      * everything again for the vertical scroll
1687      */
1688
1689     ZeroMemory(&vertInfo, sizeof(SCROLLINFO));
1690     vertInfo.cbSize = sizeof(SCROLLINFO);
1691     vertInfo.nPage = infoPtr->rcList.bottom - infoPtr->rcList.top;
1692
1693     if (uView == LVS_REPORT)
1694     {
1695         vertInfo.nMax = infoPtr->nItemCount;
1696         
1697         /* scroll by at least one page */
1698         if(vertInfo.nPage < infoPtr->nItemHeight)
1699           vertInfo.nPage = infoPtr->nItemHeight;
1700
1701         vertInfo.nPage /= infoPtr->nItemHeight;
1702     }
1703     else if (uView != LVS_LIST) /* LVS_ICON, or LVS_SMALLICON */
1704     {
1705         RECT rcView;
1706
1707         if (LISTVIEW_GetViewRect(infoPtr, &rcView)) vertInfo.nMax = rcView.bottom - rcView.top;
1708     }
1709
1710     vertInfo.fMask = SIF_RANGE | SIF_PAGE;
1711     vertInfo.nMax = max(vertInfo.nMax - 1, 0);
1712     SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &vertInfo, TRUE);
1713     TRACE("vertInfo=%s\n", debugscrollinfo(&vertInfo));
1714
1715     /* Update the Header Control */
1716     if (uView == LVS_REPORT)
1717     {
1718         horzInfo.fMask = SIF_POS;
1719         GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo);
1720         LISTVIEW_UpdateHeaderSize(infoPtr, horzInfo.nPos);
1721     }
1722 }
1723
1724
1725 /***
1726  * DESCRIPTION:
1727  * Shows/hides the focus rectangle. 
1728  *
1729  * PARAMETER(S):
1730  * [I] infoPtr : valid pointer to the listview structure
1731  * [I] fShow : TRUE to show the focus, FALSE to hide it.
1732  *
1733  * RETURN:
1734  * None
1735  */
1736 static void LISTVIEW_ShowFocusRect(LISTVIEW_INFO *infoPtr, BOOL fShow)
1737 {
1738     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1739     HDC hdc;
1740
1741     TRACE("fShow=%d, nItem=%d\n", fShow, infoPtr->nFocusedItem);
1742
1743     if (infoPtr->nFocusedItem < 0) return;
1744
1745     /* we need some gymnastics in ICON mode to handle large items */
1746     if ( (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON )
1747     {
1748         RECT rcBox;
1749
1750         LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcBox); 
1751         if ((rcBox.bottom - rcBox.top) > infoPtr->nItemHeight)
1752         {
1753             LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1754             return;
1755         }
1756     }
1757
1758     if (!(hdc = GetDC(infoPtr->hwndSelf))) return;
1759
1760     /* for some reason, owner draw should work only in report mode */
1761     if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
1762     {
1763         DRAWITEMSTRUCT dis;
1764         LVITEMW item;
1765
1766         item.iItem = infoPtr->nFocusedItem;
1767         item.iSubItem = 0;
1768         item.mask = LVIF_PARAM;
1769         if (!LISTVIEW_GetItemW(infoPtr, &item)) goto done;
1770            
1771         ZeroMemory(&dis, sizeof(dis)); 
1772         dis.CtlType = ODT_LISTVIEW;
1773         dis.CtlID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
1774         dis.itemID = item.iItem;
1775         dis.itemAction = ODA_FOCUS;
1776         if (fShow) dis.itemState |= ODS_FOCUS;
1777         dis.hwndItem = infoPtr->hwndSelf;
1778         dis.hDC = hdc;
1779         LISTVIEW_GetItemBox(infoPtr, dis.itemID, &dis.rcItem);
1780         dis.itemData = item.lParam;
1781
1782         SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
1783     }
1784     else
1785     {
1786         DrawFocusRect(hdc, &infoPtr->rcFocus);
1787     }
1788 done:
1789     ReleaseDC(infoPtr->hwndSelf, hdc);
1790 }
1791
1792 /***
1793  * Invalidates all visible selected items.
1794  */
1795 static void LISTVIEW_InvalidateSelectedItems(LISTVIEW_INFO *infoPtr)
1796 {
1797     ITERATOR i; 
1798    
1799     iterator_frameditems(&i, infoPtr, &infoPtr->rcList); 
1800     while(iterator_next(&i))
1801     {
1802         if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED))
1803             LISTVIEW_InvalidateItem(infoPtr, i.nItem);
1804     }
1805     iterator_destroy(&i);
1806 }
1807
1808             
1809 /***
1810  * DESCRIPTION:            [INTERNAL]
1811  * Computes an item's (left,top) corner, relative to rcView.
1812  * That is, the position has NOT been made relative to the Origin.
1813  * This is deliberate, to avoid computing the Origin over, and
1814  * over again, when this function is call in a loop. Instead,
1815  * one ca factor the computation of the Origin before the loop,
1816  * and offset the value retured by this function, on every iteration.
1817  * 
1818  * PARAMETER(S):
1819  * [I] infoPtr : valid pointer to the listview structure
1820  * [I] nItem  : item number
1821  * [O] lpptOrig : item top, left corner
1822  *
1823  * RETURN:
1824  *   None.
1825  */
1826 static void LISTVIEW_GetItemOrigin(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
1827 {
1828     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1829
1830     assert(nItem >= 0 && nItem < infoPtr->nItemCount);
1831
1832     if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1833     {
1834         lpptPosition->x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1835         lpptPosition->y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1836     }
1837     else if (uView == LVS_LIST)
1838     {
1839         INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
1840         lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
1841         lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight;
1842     }
1843     else /* LVS_REPORT */
1844     {
1845         lpptPosition->x = 0;
1846         lpptPosition->y = nItem * infoPtr->nItemHeight;
1847     }
1848 }
1849     
1850 /***
1851  * DESCRIPTION:            [INTERNAL]
1852  * Compute the rectangles of an item.  This is to localize all
1853  * the computations in one place. If you are not interested in some
1854  * of these values, simply pass in a NULL -- the fucntion is smart
1855  * enough to compute only what's necessary. The function computes
1856  * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
1857  * one, the BOX rectangle. This rectangle is very cheap to compute,
1858  * and is guaranteed to contain all the other rectangles. Computing
1859  * the ICON rect is also cheap, but all the others are potentaily
1860  * expensive. This gives an easy and effective optimization when
1861  * searching (like point inclusion, or rectangle intersection):
1862  * first test against the BOX, and if TRUE, test agains the desired
1863  * rectangle.
1864  * If the function does not have all the necessary information
1865  * to computed the requested rectangles, will crash with a
1866  * failed assertion. This is done so we catch all programming
1867  * errors, given that the function is called only from our code.
1868  *
1869  * We have the following 'special' meanings for a few fields:
1870  *   * If LVIS_FOCUSED is set, we assume the item has the focus
1871  *     This is important in ICON mode, where it might get a larger
1872  *     then usual rectange
1873  *
1874  * Please note that subitem support works only in REPORT mode.
1875  *
1876  * PARAMETER(S):
1877  * [I] infoPtr : valid pointer to the listview structure
1878  * [I] lpLVItem : item to compute the measures for
1879  * [O] lprcBox : ptr to Box rectangle
1880  *                The internal LVIR_BOX rectangle
1881  * [0] lprcState : ptr to State icon rectangle
1882  *                The internal LVIR_STATE rectangle
1883  * [O] lprcIcon : ptr to Icon rectangle
1884  *                Same as LVM_GETITEMRECT with LVIR_ICON
1885  * [O] lprcLabel : ptr to Label rectangle
1886  *                Same as LVM_GETITEMRECT with LVIR_LABEL
1887  *
1888  * RETURN:
1889  *   None.
1890  */
1891 static void LISTVIEW_GetItemMetrics(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem,
1892                                     LPRECT lprcBox, LPRECT lprcState, 
1893                                     LPRECT lprcIcon, LPRECT lprcLabel)
1894 {
1895     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1896     BOOL doState = FALSE, doIcon = FALSE, doLabel = FALSE, oversizedBox = FALSE;
1897     RECT Box, State, Icon, Label;
1898     COLUMN_INFO *lpColumnInfo = NULL;
1899
1900     TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem, TRUE));
1901         
1902     /* Be smart and try to figure out the minimum we have to do */
1903     if (lpLVItem->iSubItem) assert(uView == LVS_REPORT);
1904     if (uView == LVS_ICON && (lprcBox || lprcLabel))
1905     {
1906         assert((lpLVItem->mask & LVIF_STATE) && (lpLVItem->stateMask & LVIS_FOCUSED));
1907         if (lpLVItem->state & LVIS_FOCUSED) oversizedBox = doLabel = TRUE;
1908     }
1909     if (lprcLabel) doLabel = TRUE;
1910     if (doLabel || lprcIcon) doIcon = TRUE;
1911     if (doIcon || lprcState) doState = TRUE;
1912     
1913     /************************************************************/
1914     /* compute the box rectangle (it should be cheap to do)     */
1915     /************************************************************/
1916     if (lpLVItem->iSubItem || uView == LVS_REPORT)
1917         lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpLVItem->iSubItem);
1918
1919     if (lpLVItem->iSubItem)    
1920     {
1921         Box = lpColumnInfo->rcHeader;
1922     }
1923     else
1924     {
1925         Box.left = 0;
1926         Box.right = infoPtr->nItemWidth;
1927     }
1928     Box.top = 0;
1929     Box.bottom = infoPtr->nItemHeight;
1930                 
1931     /************************************************************/
1932     /* compute STATEICON bounding box                           */
1933     /************************************************************/
1934     if (doState)
1935     {
1936         if (uView == LVS_ICON)
1937         {
1938             State.left = Box.left - infoPtr->iconStateSize.cx - 2;
1939             if (infoPtr->himlNormal) 
1940                 State.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
1941             State.top  = Box.top + infoPtr->iconSize.cy - infoPtr->iconStateSize.cy + 4;
1942         }
1943         else
1944         {
1945             /* we need the ident in report mode, if we don't have it, we fail */
1946             State.left = Box.left;
1947             if (uView == LVS_REPORT) 
1948             {
1949                 if (lpLVItem->iSubItem == 0)
1950                 {
1951                     State.left += REPORT_MARGINX;
1952                     assert(lpLVItem->mask & LVIF_INDENT);
1953                     State.left += infoPtr->iconSize.cx * lpLVItem->iIndent;
1954                 }
1955             }
1956             State.top  = Box.top;
1957         }       
1958         State.right    = State.left;
1959         State.bottom   = State.top;
1960         if (infoPtr->himlState && lpLVItem->iSubItem == 0)
1961         {
1962             State.right  += infoPtr->iconStateSize.cx;
1963             State.bottom += infoPtr->iconStateSize.cy;
1964         }
1965         if (lprcState) *lprcState = State;
1966         TRACE("    - state=%s\n", debugrect(&State));
1967     }
1968
1969     /************************************************************/
1970     /* compute ICON bounding box (ala LVM_GETITEMRECT)          */
1971     /************************************************************/
1972     if (doIcon)
1973     {
1974         if (uView == LVS_ICON)
1975         {
1976             Icon.left   = Box.left;
1977             if (infoPtr->himlNormal) 
1978                 Icon.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
1979             Icon.top    = Box.top + ICON_TOP_PADDING;
1980             Icon.right  = Icon.left;
1981             Icon.bottom = Icon.top;
1982             if (infoPtr->himlNormal)
1983             {
1984                 Icon.right  += infoPtr->iconSize.cx;
1985                 Icon.bottom += infoPtr->iconSize.cy;
1986             }
1987         }
1988         else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
1989         {
1990             Icon.left = State.right;
1991             Icon.top    = Box.top;
1992             Icon.right  = Icon.left;
1993             if (infoPtr->himlSmall &&
1994                 (!lpColumnInfo || lpLVItem->iSubItem == 0 || (lpColumnInfo->fmt & LVCFMT_IMAGE) ||
1995                  ((infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES) && lpLVItem->iImage != I_IMAGECALLBACK)))
1996                 Icon.right += infoPtr->iconSize.cx;
1997             Icon.bottom = Icon.top + infoPtr->nItemHeight;
1998         }
1999         if(lprcIcon) *lprcIcon = Icon;
2000         TRACE("    - icon=%s\n", debugrect(&Icon));
2001      }
2002
2003     /************************************************************/
2004     /* compute LABEL bounding box (ala LVM_GETITEMRECT)         */
2005     /************************************************************/
2006     if (doLabel)
2007     {
2008         SIZE labelSize = { 0, 0 };
2009
2010         /* calculate how far to the right can the label strech */
2011         Label.right = Box.right;
2012         if (uView == LVS_REPORT)
2013         {
2014             if (lpLVItem->iSubItem == 0) Label = lpColumnInfo->rcHeader;
2015         }
2016
2017         if (lpLVItem->iSubItem || ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && uView == LVS_REPORT))
2018         {
2019            labelSize.cx = infoPtr->nItemWidth;
2020            labelSize.cy = infoPtr->nItemHeight;
2021            goto calc_label;
2022         }
2023         
2024         /* we need the text in non owner draw mode */
2025         assert(lpLVItem->mask & LVIF_TEXT);
2026         if (is_textT(lpLVItem->pszText, TRUE))
2027         {
2028             HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2029             HDC hdc = GetDC(infoPtr->hwndSelf);
2030             HFONT hOldFont = SelectObject(hdc, hFont);
2031             UINT uFormat;
2032             RECT rcText;
2033
2034             /* compute rough rectangle where the label will go */
2035             SetRectEmpty(&rcText);
2036             rcText.right = infoPtr->nItemWidth - TRAILING_LABEL_PADDING;
2037             rcText.bottom = infoPtr->nItemHeight;
2038             if (uView == LVS_ICON) 
2039                 rcText.bottom -= ICON_TOP_PADDING + infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2040
2041             /* now figure out the flags */
2042             if (uView == LVS_ICON)
2043                 uFormat = oversizedBox ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS;
2044             else
2045                 uFormat = LV_SL_DT_FLAGS;
2046             
2047             DrawTextW (hdc, lpLVItem->pszText, -1, &rcText, uFormat | DT_CALCRECT);
2048
2049             labelSize.cx = min(rcText.right - rcText.left + TRAILING_LABEL_PADDING, infoPtr->nItemWidth);
2050             labelSize.cy = rcText.bottom - rcText.top;
2051
2052             SelectObject(hdc, hOldFont);
2053             ReleaseDC(infoPtr->hwndSelf, hdc);
2054         }
2055
2056 calc_label:
2057         if (uView == LVS_ICON)
2058         {
2059             Label.left = Box.left + (infoPtr->nItemWidth - labelSize.cx) / 2;
2060             Label.top  = Box.top + ICON_TOP_PADDING_HITABLE +
2061                          infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2062             Label.right = Label.left + labelSize.cx;
2063             Label.bottom = Label.top + infoPtr->nItemHeight;
2064             if (!oversizedBox && labelSize.cy > infoPtr->ntmHeight)
2065             {
2066                 labelSize.cy = min(Box.bottom - Label.top, labelSize.cy);
2067                 labelSize.cy /= infoPtr->ntmHeight;
2068                 labelSize.cy = max(labelSize.cy, 1);
2069                 labelSize.cy *= infoPtr->ntmHeight;
2070              }
2071              Label.bottom = Label.top + labelSize.cy + HEIGHT_PADDING;
2072         }
2073         else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
2074         {
2075             Label.left = Icon.right;
2076             Label.top = Box.top;
2077             Label.right = min(Label.left + labelSize.cx, Label.right);
2078             Label.bottom = Label.top + infoPtr->nItemHeight;
2079         }
2080   
2081         if (lprcLabel) *lprcLabel = Label;
2082         TRACE("    - label=%s\n", debugrect(&Label));
2083     }
2084
2085     /* Fix the Box if necessary */
2086     if (lprcBox)
2087     {
2088         if (oversizedBox) UnionRect(lprcBox, &Box, &Label);
2089         else *lprcBox = Box;
2090     }
2091     TRACE("    - box=%s\n", debugrect(&Box));
2092 }
2093
2094 /***
2095  * DESCRIPTION:            [INTERNAL]
2096  *
2097  * PARAMETER(S):
2098  * [I] infoPtr : valid pointer to the listview structure
2099  * [I] nItem : item number
2100  * [O] lprcBox : ptr to Box rectangle
2101  *
2102  * RETURN:
2103  *   None.
2104  */
2105 static void LISTVIEW_GetItemBox(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprcBox)
2106 {
2107     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2108     WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
2109     POINT Position, Origin;
2110     LVITEMW lvItem;
2111
2112     LISTVIEW_GetOrigin(infoPtr, &Origin);
2113     LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
2114
2115     /* Be smart and try to figure out the minimum we have to do */
2116     lvItem.mask = 0;
2117     if (uView == LVS_ICON && infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
2118         lvItem.mask |= LVIF_TEXT;
2119     lvItem.iItem = nItem;
2120     lvItem.iSubItem = 0;
2121     lvItem.pszText = szDispText;
2122     lvItem.cchTextMax = DISP_TEXT_SIZE;
2123     if (lvItem.mask) LISTVIEW_GetItemW(infoPtr, &lvItem);
2124     if (uView == LVS_ICON)
2125     {
2126         lvItem.mask |= LVIF_STATE;
2127         lvItem.stateMask = LVIS_FOCUSED;
2128         lvItem.state = (lvItem.mask & LVIF_TEXT ? LVIS_FOCUSED : 0);
2129     }
2130     LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprcBox, 0, 0, 0);
2131
2132     OffsetRect(lprcBox, Position.x + Origin.x, Position.y + Origin.y);
2133 }
2134
2135
2136 /***
2137  * DESCRIPTION:
2138  * Returns the current icon position, and advances it along the top.
2139  * The returned position is not offset by Origin.
2140  *
2141  * PARAMETER(S):
2142  * [I] infoPtr : valid pointer to the listview structure
2143  * [O] lpPos : will get the current icon position
2144  *
2145  * RETURN:
2146  * None
2147  */
2148 static void LISTVIEW_NextIconPosTop(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2149 {
2150     INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
2151     
2152     *lpPos = infoPtr->currIconPos;
2153     
2154     infoPtr->currIconPos.x += infoPtr->nItemWidth;
2155     if (infoPtr->currIconPos.x + infoPtr->nItemWidth <= nListWidth) return;
2156
2157     infoPtr->currIconPos.x  = 0;
2158     infoPtr->currIconPos.y += infoPtr->nItemHeight;
2159 }
2160
2161     
2162 /***
2163  * DESCRIPTION:
2164  * Returns the current icon position, and advances it down the left edge.
2165  * The returned position is not offset by Origin.
2166  *
2167  * PARAMETER(S):
2168  * [I] infoPtr : valid pointer to the listview structure
2169  * [O] lpPos : will get the current icon position
2170  *
2171  * RETURN:
2172  * None
2173  */
2174 static void LISTVIEW_NextIconPosLeft(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2175 {
2176     INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
2177     
2178     *lpPos = infoPtr->currIconPos;
2179     
2180     infoPtr->currIconPos.y += infoPtr->nItemHeight;
2181     if (infoPtr->currIconPos.y + infoPtr->nItemHeight <= nListHeight) return;
2182
2183     infoPtr->currIconPos.x += infoPtr->nItemWidth;
2184     infoPtr->currIconPos.y  = 0;
2185 }
2186
2187     
2188 /***
2189  * DESCRIPTION:
2190  * Moves an icon to the specified position.
2191  * It takes care of invalidating the item, etc.
2192  *
2193  * PARAMETER(S):
2194  * [I] infoPtr : valid pointer to the listview structure
2195  * [I] nItem : the item to move
2196  * [I] lpPos : the new icon position
2197  * [I] isNew : flags the item as being new
2198  *
2199  * RETURN:
2200  *   Success: TRUE
2201  *   Failure: FALSE
2202  */
2203 static BOOL LISTVIEW_MoveIconTo(LISTVIEW_INFO *infoPtr, INT nItem, const POINT *lppt, BOOL isNew)
2204 {
2205     POINT old;
2206     
2207     if (!isNew)
2208     { 
2209         old.x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
2210         old.y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
2211     
2212         if (lppt->x == old.x && lppt->y == old.y) return TRUE;
2213         LISTVIEW_InvalidateItem(infoPtr, nItem);
2214     }
2215
2216     /* Allocating a POINTER for every item is too resource intensive,
2217      * so we'll keep the (x,y) in different arrays */
2218     if (!DPA_SetPtr(infoPtr->hdpaPosX, nItem, (void *)lppt->x)) return FALSE;
2219     if (!DPA_SetPtr(infoPtr->hdpaPosY, nItem, (void *)lppt->y)) return FALSE;
2220
2221     LISTVIEW_InvalidateItem(infoPtr, nItem);
2222
2223     return TRUE;
2224 }
2225
2226 /***
2227  * DESCRIPTION:
2228  * Arranges listview items in icon display mode.
2229  *
2230  * PARAMETER(S):
2231  * [I] infoPtr : valid pointer to the listview structure
2232  * [I] nAlignCode : alignment code
2233  *
2234  * RETURN:
2235  *   SUCCESS : TRUE
2236  *   FAILURE : FALSE
2237  */
2238 static BOOL LISTVIEW_Arrange(LISTVIEW_INFO *infoPtr, INT nAlignCode)
2239 {
2240     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2241     void (*next_pos)(LISTVIEW_INFO *, LPPOINT);
2242     POINT pos;
2243     INT i;
2244
2245     if (uView != LVS_ICON && uView != LVS_SMALLICON) return FALSE;
2246   
2247     TRACE("nAlignCode=%d\n", nAlignCode);
2248
2249     if (nAlignCode == LVA_DEFAULT)
2250     {
2251         if (infoPtr->dwStyle & LVS_ALIGNLEFT) nAlignCode = LVA_ALIGNLEFT;
2252         else nAlignCode = LVA_ALIGNTOP;
2253     }
2254    
2255     switch (nAlignCode)
2256     {
2257     case LVA_ALIGNLEFT:  next_pos = LISTVIEW_NextIconPosLeft; break;
2258     case LVA_ALIGNTOP:   next_pos = LISTVIEW_NextIconPosTop;  break;
2259     case LVA_SNAPTOGRID: next_pos = LISTVIEW_NextIconPosTop;  break; /* FIXME */
2260     default: return FALSE;
2261     }
2262     
2263     infoPtr->bAutoarrange = TRUE;
2264     infoPtr->currIconPos.x = infoPtr->currIconPos.y = 0;
2265     for (i = 0; i < infoPtr->nItemCount; i++)
2266     {
2267         next_pos(infoPtr, &pos);
2268         LISTVIEW_MoveIconTo(infoPtr, i, &pos, FALSE);
2269     }
2270
2271     return TRUE;
2272 }
2273   
2274 /***
2275  * DESCRIPTION:
2276  * Retrieves the bounding rectangle of all the items, not offset by Origin.
2277  *
2278  * PARAMETER(S):
2279  * [I] infoPtr : valid pointer to the listview structure
2280  * [O] lprcView : bounding rectangle
2281  *
2282  * RETURN:
2283  *   SUCCESS : TRUE
2284  *   FAILURE : FALSE
2285  */
2286 static void LISTVIEW_GetAreaRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2287 {
2288     INT i, x, y;
2289
2290     SetRectEmpty(lprcView);
2291
2292     switch (infoPtr->dwStyle & LVS_TYPEMASK)
2293     {
2294     case LVS_ICON:
2295     case LVS_SMALLICON:
2296         for (i = 0; i < infoPtr->nItemCount; i++)
2297         {
2298             x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, i);
2299            y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, i);
2300             lprcView->right = max(lprcView->right, x);
2301             lprcView->bottom = max(lprcView->bottom, y);
2302         }
2303         if (infoPtr->nItemCount > 0)
2304         {
2305             lprcView->right += infoPtr->nItemWidth;
2306             lprcView->bottom += infoPtr->nItemHeight;
2307         }
2308         break;
2309
2310     case LVS_LIST:
2311         y = LISTVIEW_GetCountPerColumn(infoPtr);
2312         x = infoPtr->nItemCount / y;
2313         if (infoPtr->nItemCount % y) x++;
2314         lprcView->right = x * infoPtr->nItemWidth;
2315         lprcView->bottom = y * infoPtr->nItemHeight;
2316         break;
2317         
2318     case LVS_REPORT:        
2319         lprcView->right = infoPtr->nItemWidth;
2320         lprcView->bottom = infoPtr->nItemCount * infoPtr->nItemHeight;
2321         break;
2322     }
2323 }
2324
2325 /***
2326  * DESCRIPTION:
2327  * Retrieves the bounding rectangle of all the items.
2328  *
2329  * PARAMETER(S):
2330  * [I] infoPtr : valid pointer to the listview structure
2331  * [O] lprcView : bounding rectangle
2332  *
2333  * RETURN:
2334  *   SUCCESS : TRUE
2335  *   FAILURE : FALSE
2336  */
2337 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2338 {
2339     POINT ptOrigin;
2340
2341     TRACE("(lprcView=%p)\n", lprcView);
2342
2343     if (!lprcView) return FALSE;
2344  
2345     LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
2346     LISTVIEW_GetAreaRect(infoPtr, lprcView); 
2347     OffsetRect(lprcView, ptOrigin.x, ptOrigin.y); 
2348
2349     TRACE("lprcView=%s\n", debugrect(lprcView));
2350
2351     return TRUE;
2352 }
2353
2354 /***
2355  * DESCRIPTION:
2356  * Retrieves the subitem pointer associated with the subitem index.
2357  *
2358  * PARAMETER(S):
2359  * [I] hdpaSubItems : DPA handle for a specific item
2360  * [I] nSubItem : index of subitem
2361  *
2362  * RETURN:
2363  *   SUCCESS : subitem pointer
2364  *   FAILURE : NULL
2365  */
2366 static SUBITEM_INFO* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems, INT nSubItem)
2367 {
2368     SUBITEM_INFO *lpSubItem;
2369     INT i;
2370
2371     /* we should binary search here if need be */
2372     for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
2373     {
2374         lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
2375         if (lpSubItem->iSubItem == nSubItem)
2376             return lpSubItem;
2377     }
2378
2379     return NULL;
2380 }
2381
2382
2383 /***
2384  * DESCRIPTION:
2385  * Caclulates the desired item width.
2386  *
2387  * PARAMETER(S):
2388  * [I] infoPtr : valid pointer to the listview structure
2389  *
2390  * RETURN:
2391  *  The desired item width.
2392  */
2393 static INT LISTVIEW_CalculateItemWidth(LISTVIEW_INFO *infoPtr)
2394 {
2395     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2396     INT nItemWidth = 0;
2397
2398     TRACE("uView=%d\n", uView);
2399
2400     if (uView == LVS_ICON)
2401         nItemWidth = infoPtr->iconSpacing.cx;
2402     else if (uView == LVS_REPORT)
2403     {
2404         RECT rcHeader;
2405
2406         if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0)
2407         {
2408             LISTVIEW_GetHeaderRect(infoPtr, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, &rcHeader);
2409             nItemWidth = rcHeader.right;
2410         }
2411     }
2412     else /* LVS_SMALLICON, or LVS_LIST */
2413     {
2414         INT i;
2415         
2416         for (i = 0; i < infoPtr->nItemCount; i++)
2417             nItemWidth = max(LISTVIEW_GetLabelWidth(infoPtr, i), nItemWidth);
2418
2419         if (infoPtr->himlSmall) nItemWidth += infoPtr->iconSize.cx; 
2420         if (infoPtr->himlState) nItemWidth += infoPtr->iconStateSize.cx;
2421
2422         nItemWidth = max(DEFAULT_COLUMN_WIDTH, nItemWidth + WIDTH_PADDING);
2423     }
2424
2425     return max(nItemWidth, 1);
2426 }
2427
2428 /***
2429  * DESCRIPTION:
2430  * Caclulates the desired item height.
2431  *
2432  * PARAMETER(S):
2433  * [I] infoPtr : valid pointer to the listview structure
2434  *
2435  * RETURN:
2436  *  The desired item height.
2437  */
2438 static INT LISTVIEW_CalculateItemHeight(LISTVIEW_INFO *infoPtr)
2439 {
2440     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2441     INT nItemHeight;
2442
2443     TRACE("uView=%d\n", uView);
2444
2445     if (uView == LVS_ICON)
2446         nItemHeight = infoPtr->iconSpacing.cy;
2447     else
2448     {
2449         nItemHeight = infoPtr->ntmHeight; 
2450         if (infoPtr->himlState)
2451             nItemHeight = max(nItemHeight, infoPtr->iconStateSize.cy);
2452         if (infoPtr->himlSmall)
2453             nItemHeight = max(nItemHeight, infoPtr->iconSize.cy);
2454         if (infoPtr->himlState || infoPtr->himlSmall)
2455             nItemHeight += HEIGHT_PADDING;
2456     if (infoPtr->nMeasureItemHeight > 0)
2457         nItemHeight = infoPtr->nMeasureItemHeight;
2458     }
2459
2460     return max(nItemHeight, 1);
2461 }
2462
2463 /***
2464  * DESCRIPTION:
2465  * Updates the width, and height of an item.
2466  *
2467  * PARAMETER(S):
2468  * [I] infoPtr : valid pointer to the listview structure
2469  *
2470  * RETURN:
2471  *  None.
2472  */
2473 static inline void LISTVIEW_UpdateItemSize(LISTVIEW_INFO *infoPtr)
2474 {
2475     infoPtr->nItemWidth = LISTVIEW_CalculateItemWidth(infoPtr);
2476     infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
2477 }
2478
2479
2480 /***
2481  * DESCRIPTION:
2482  * Retrieves and saves important text metrics info for the current
2483  * Listview font.
2484  *
2485  * PARAMETER(S):
2486  * [I] infoPtr : valid pointer to the listview structure
2487  *
2488  */
2489 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO *infoPtr)
2490 {
2491     HDC hdc = GetDC(infoPtr->hwndSelf);
2492     HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2493     HFONT hOldFont = SelectObject(hdc, hFont);
2494     TEXTMETRICW tm;
2495
2496     if (GetTextMetricsW(hdc, &tm))
2497     {
2498         infoPtr->ntmHeight = tm.tmHeight;
2499         infoPtr->ntmAveCharWidth = tm.tmAveCharWidth;
2500     }
2501     SelectObject(hdc, hOldFont);
2502     ReleaseDC(infoPtr->hwndSelf, hdc);
2503     
2504     TRACE("tmHeight=%d\n", infoPtr->ntmHeight);
2505 }
2506
2507 /***
2508  * DESCRIPTION:
2509  * A compare function for ranges
2510  *
2511  * PARAMETER(S)
2512  * [I] range1 : pointer to range 1;
2513  * [I] range2 : pointer to range 2;
2514  * [I] flags : flags
2515  *
2516  * RETURNS:
2517  * > 0 : if range 1 > range 2
2518  * < 0 : if range 2 > range 1
2519  * = 0 : if range intersects range 2
2520  */
2521 static INT CALLBACK ranges_cmp(LPVOID range1, LPVOID range2, LPARAM flags)
2522 {
2523     INT cmp;
2524     
2525     if (((RANGE*)range1)->upper <= ((RANGE*)range2)->lower) 
2526         cmp = -1;
2527     else if (((RANGE*)range2)->upper <= ((RANGE*)range1)->lower) 
2528         cmp = 1;
2529     else 
2530         cmp = 0;
2531     
2532     TRACE("range1=%s, range2=%s, cmp=%d\n", debugrange((RANGE*)range1), debugrange((RANGE*)range2), cmp);
2533
2534     return cmp;
2535 }
2536
2537 #if DEBUG_RANGES
2538 #define ranges_check(ranges, desc) ranges_assert(ranges, desc, __FUNCTION__, __LINE__)
2539 #else
2540 #define ranges_check(ranges, desc) do { } while(0)
2541 #endif
2542
2543 static void ranges_assert(RANGES ranges, LPCSTR desc, const char *func, int line)
2544 {
2545     INT i;
2546     RANGE *prev, *curr;
2547     
2548     TRACE("*** Checking %s:%d:%s ***\n", func, line, desc);
2549     assert (ranges);
2550     assert (DPA_GetPtrCount(ranges->hdpa) >= 0);
2551     ranges_dump(ranges);
2552     prev = (RANGE *)DPA_GetPtr(ranges->hdpa, 0);
2553     if (DPA_GetPtrCount(ranges->hdpa) > 0)
2554         assert (prev->lower >= 0 && prev->lower < prev->upper);
2555     for (i = 1; i < DPA_GetPtrCount(ranges->hdpa); i++)
2556     {
2557         curr = (RANGE *)DPA_GetPtr(ranges->hdpa, i);
2558         assert (prev->upper <= curr->lower);
2559         assert (curr->lower < curr->upper);
2560         prev = curr;
2561     }
2562     TRACE("--- Done checking---\n");
2563 }
2564
2565 static RANGES ranges_create(int count)
2566 {
2567     RANGES ranges = (RANGES)Alloc(sizeof(struct tagRANGES));
2568     if (!ranges) return NULL;
2569     ranges->hdpa = DPA_Create(count);
2570     if (ranges->hdpa) return ranges;
2571     Free(ranges);
2572     return NULL;
2573 }
2574
2575 static void ranges_clear(RANGES ranges)
2576 {
2577     INT i;
2578         
2579     for(i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2580         Free(DPA_GetPtr(ranges->hdpa, i));
2581     DPA_DeleteAllPtrs(ranges->hdpa);
2582 }
2583
2584
2585 static void ranges_destroy(RANGES ranges)
2586 {
2587     if (!ranges) return;
2588     ranges_clear(ranges);
2589     DPA_Destroy(ranges->hdpa);
2590     Free(ranges);
2591 }
2592
2593 static RANGES ranges_clone(RANGES ranges)
2594 {
2595     RANGES clone;
2596     INT i;
2597            
2598     if (!(clone = ranges_create(DPA_GetPtrCount(ranges->hdpa)))) goto fail;
2599
2600     for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2601     {
2602         RANGE *newrng = (RANGE *)Alloc(sizeof(RANGE));
2603         if (!newrng) goto fail;
2604         *newrng = *((RANGE*)DPA_GetPtr(ranges->hdpa, i));
2605         DPA_SetPtr(clone->hdpa, i, newrng);
2606     }
2607     return clone;
2608     
2609 fail:
2610     TRACE ("clone failed\n");
2611     ranges_destroy(clone);
2612     return NULL;
2613 }
2614
2615 static RANGES ranges_diff(RANGES ranges, RANGES sub)
2616 {
2617     INT i;
2618
2619     for (i = 0; i < DPA_GetPtrCount(sub->hdpa); i++)
2620         ranges_del(ranges, *((RANGE *)DPA_GetPtr(sub->hdpa, i)));
2621
2622     return ranges;
2623 }
2624
2625 static void ranges_dump(RANGES ranges)
2626 {
2627     INT i;
2628
2629     for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2630         TRACE("   %s\n", debugrange(DPA_GetPtr(ranges->hdpa, i)));
2631 }
2632
2633 static inline BOOL ranges_contain(RANGES ranges, INT nItem)
2634 {
2635     RANGE srchrng = { nItem, nItem + 1 };
2636
2637     TRACE("(nItem=%d)\n", nItem);
2638     ranges_check(ranges, "before contain");
2639     return DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED) != -1;
2640 }
2641
2642 static INT ranges_itemcount(RANGES ranges)
2643 {
2644     INT i, count = 0;
2645     
2646     for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2647     {
2648         RANGE *sel = DPA_GetPtr(ranges->hdpa, i);
2649         count += sel->upper - sel->lower;
2650     }
2651
2652     return count;
2653 }
2654
2655 static BOOL ranges_shift(RANGES ranges, INT nItem, INT delta, INT nUpper)
2656 {
2657     RANGE srchrng = { nItem, nItem + 1 }, *chkrng;
2658     INT index;
2659
2660     index = DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2661     if (index == -1) return TRUE;
2662
2663     for (; index < DPA_GetPtrCount(ranges->hdpa); index++)
2664     {
2665         chkrng = DPA_GetPtr(ranges->hdpa, index);
2666         if (chkrng->lower >= nItem)
2667             chkrng->lower = max(min(chkrng->lower + delta, nUpper - 1), 0);
2668         if (chkrng->upper > nItem)
2669             chkrng->upper = max(min(chkrng->upper + delta, nUpper), 0);
2670     }
2671     return TRUE;
2672 }
2673
2674 static BOOL ranges_add(RANGES ranges, RANGE range)
2675 {
2676     RANGE srchrgn;
2677     INT index;
2678
2679     TRACE("(%s)\n", debugrange(&range));
2680     ranges_check(ranges, "before add");
2681
2682     /* try find overlapping regions first */
2683     srchrgn.lower = range.lower - 1;
2684     srchrgn.upper = range.upper + 1;
2685     index = DPA_Search(ranges->hdpa, &srchrgn, 0, ranges_cmp, 0, DPAS_SORTED);
2686    
2687     if (index == -1)
2688     {
2689         RANGE *newrgn;
2690
2691         TRACE("Adding new range\n");
2692
2693         /* create the brand new range to insert */      
2694         newrgn = (RANGE *)Alloc(sizeof(RANGE));
2695         if(!newrgn) goto fail;
2696         *newrgn = range;
2697         
2698         /* figure out where to insert it */
2699         index = DPA_Search(ranges->hdpa, newrgn, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2700         TRACE("index=%d\n", index);
2701         if (index == -1) index = 0;
2702         
2703         /* and get it over with */
2704         if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2705         {
2706             Free(newrgn);
2707             goto fail;
2708         }
2709     }
2710     else
2711     {
2712         RANGE *chkrgn, *mrgrgn;
2713         INT fromindex, mergeindex;
2714
2715         chkrgn = DPA_GetPtr(ranges->hdpa, index);
2716         TRACE("Merge with %s @%d\n", debugrange(chkrgn), index);
2717
2718         chkrgn->lower = min(range.lower, chkrgn->lower);
2719         chkrgn->upper = max(range.upper, chkrgn->upper);
2720         
2721         TRACE("New range %s @%d\n", debugrange(chkrgn), index);
2722
2723         /* merge now common anges */
2724         fromindex = 0;
2725         srchrgn.lower = chkrgn->lower - 1;
2726         srchrgn.upper = chkrgn->upper + 1;
2727             
2728         do
2729         {
2730             mergeindex = DPA_Search(ranges->hdpa, &srchrgn, fromindex, ranges_cmp, 0, 0);
2731             if (mergeindex == -1) break;
2732             if (mergeindex == index) 
2733             {
2734                 fromindex = index + 1;
2735                 continue;
2736             }
2737           
2738             TRACE("Merge with index %i\n", mergeindex);
2739             
2740             mrgrgn = DPA_GetPtr(ranges->hdpa, mergeindex);
2741             chkrgn->lower = min(chkrgn->lower, mrgrgn->lower);
2742             chkrgn->upper = max(chkrgn->upper, mrgrgn->upper);
2743             Free(mrgrgn);
2744             DPA_DeletePtr(ranges->hdpa, mergeindex);
2745             if (mergeindex < index) index --;
2746         } while(1);
2747     }
2748
2749     ranges_check(ranges, "after add");
2750     return TRUE;
2751     
2752 fail:
2753     ranges_check(ranges, "failed add");
2754     return FALSE;
2755 }
2756
2757 static BOOL ranges_del(RANGES ranges, RANGE range)
2758 {
2759     RANGE *chkrgn;
2760     INT index;
2761
2762     TRACE("(%s)\n", debugrange(&range));
2763     ranges_check(ranges, "before del");
2764     
2765     /* we don't use DPAS_SORTED here, since we need *
2766      * to find the first overlapping range          */
2767     index = DPA_Search(ranges->hdpa, &range, 0, ranges_cmp, 0, 0);
2768     while(index != -1) 
2769     {
2770         chkrgn = DPA_GetPtr(ranges->hdpa, index);
2771         
2772         TRACE("Matches range %s @%d\n", debugrange(chkrgn), index); 
2773
2774         /* case 1: Same range */
2775         if ( (chkrgn->upper == range.upper) &&
2776              (chkrgn->lower == range.lower) )
2777         {
2778             DPA_DeletePtr(ranges->hdpa, index);
2779             break;
2780         }
2781         /* case 2: engulf */
2782         else if ( (chkrgn->upper <= range.upper) &&
2783                   (chkrgn->lower >= range.lower) ) 
2784         {
2785             DPA_DeletePtr(ranges->hdpa, index);
2786         }
2787         /* case 3: overlap upper */
2788         else if ( (chkrgn->upper <= range.upper) &&
2789                   (chkrgn->lower < range.lower) )
2790         {
2791             chkrgn->upper = range.lower;
2792         }
2793         /* case 4: overlap lower */
2794         else if ( (chkrgn->upper > range.upper) &&
2795                   (chkrgn->lower >= range.lower) )
2796         {
2797             chkrgn->lower = range.upper;
2798             break;
2799         }
2800         /* case 5: fully internal */
2801         else
2802         {
2803             RANGE tmprgn = *chkrgn, *newrgn;
2804
2805             if (!(newrgn = (RANGE *)Alloc(sizeof(RANGE)))) goto fail;
2806             newrgn->lower = chkrgn->lower;
2807             newrgn->upper = range.lower;
2808             chkrgn->lower = range.upper;
2809             if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2810             {
2811                 Free(newrgn);
2812                 goto fail;
2813             }
2814             chkrgn = &tmprgn;
2815             break;
2816         }
2817
2818         index = DPA_Search(ranges->hdpa, &range, index, ranges_cmp, 0, 0);
2819     }
2820
2821     ranges_check(ranges, "after del");
2822     return TRUE;
2823
2824 fail:
2825     ranges_check(ranges, "failed del");
2826     return FALSE;
2827 }
2828
2829 /***
2830 * DESCRIPTION:
2831 * Removes all selection ranges
2832 *
2833 * Parameters(s):
2834 * [I] infoPtr : valid pointer to the listview structure
2835 * [I] toSkip : item range to skip removing the selection
2836 *
2837 * RETURNS:
2838 *   SUCCESS : TRUE
2839 *   FAILURE : TRUE
2840 */
2841 static BOOL LISTVIEW_DeselectAllSkipItems(LISTVIEW_INFO *infoPtr, RANGES toSkip)
2842 {
2843     LVITEMW lvItem;
2844     ITERATOR i;
2845     RANGES clone;
2846
2847     TRACE("()\n");
2848
2849     lvItem.state = 0;
2850     lvItem.stateMask = LVIS_SELECTED;
2851     
2852     /* need to clone the DPA because callbacks can change it */
2853     if (!(clone = ranges_clone(infoPtr->selectionRanges))) return FALSE;
2854     iterator_rangesitems(&i, ranges_diff(clone, toSkip));
2855     while(iterator_next(&i))
2856         LISTVIEW_SetItemState(infoPtr, i.nItem, &lvItem);
2857     /* note that the iterator destructor will free the cloned range */
2858     iterator_destroy(&i);
2859
2860     return TRUE;
2861 }
2862
2863 static inline BOOL LISTVIEW_DeselectAllSkipItem(LISTVIEW_INFO *infoPtr, INT nItem)
2864 {
2865     RANGES toSkip;
2866    
2867     if (!(toSkip = ranges_create(1))) return FALSE;
2868     if (nItem != -1) ranges_additem(toSkip, nItem);
2869     LISTVIEW_DeselectAllSkipItems(infoPtr, toSkip);
2870     ranges_destroy(toSkip);
2871     return TRUE;
2872 }
2873
2874 static inline BOOL LISTVIEW_DeselectAll(LISTVIEW_INFO *infoPtr)
2875 {
2876     return LISTVIEW_DeselectAllSkipItem(infoPtr, -1);
2877 }
2878
2879 /***
2880  * DESCRIPTION:
2881  * Retrieves the number of items that are marked as selected.
2882  *
2883  * PARAMETER(S):
2884  * [I] infoPtr : valid pointer to the listview structure
2885  *
2886  * RETURN:
2887  * Number of items selected.
2888  */
2889 static INT LISTVIEW_GetSelectedCount(LISTVIEW_INFO *infoPtr)
2890 {
2891     INT nSelectedCount = 0;
2892
2893     if (infoPtr->uCallbackMask & LVIS_SELECTED)
2894     {
2895         INT i;
2896         for (i = 0; i < infoPtr->nItemCount; i++)
2897         {
2898             if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
2899                 nSelectedCount++;
2900         }
2901     }
2902     else
2903         nSelectedCount = ranges_itemcount(infoPtr->selectionRanges);
2904
2905     TRACE("nSelectedCount=%d\n", nSelectedCount);
2906     return nSelectedCount;
2907 }
2908
2909 /***
2910  * DESCRIPTION:
2911  * Manages the item focus.
2912  *
2913  * PARAMETER(S):
2914  * [I] infoPtr : valid pointer to the listview structure
2915  * [I] nItem : item index
2916  *
2917  * RETURN:
2918  *   TRUE : focused item changed
2919  *   FALSE : focused item has NOT changed
2920  */
2921 static inline BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem)
2922 {
2923     INT oldFocus = infoPtr->nFocusedItem;
2924     LVITEMW lvItem;
2925
2926     if (nItem == infoPtr->nFocusedItem) return FALSE;
2927     
2928     lvItem.state =  nItem == -1 ? 0 : LVIS_FOCUSED;
2929     lvItem.stateMask = LVIS_FOCUSED;
2930     LISTVIEW_SetItemState(infoPtr, nItem == -1 ? infoPtr->nFocusedItem : nItem, &lvItem);
2931
2932     return oldFocus != infoPtr->nFocusedItem;
2933 }
2934
2935 /* Helper function for LISTVIEW_ShiftIndices *only* */
2936 static INT shift_item(LISTVIEW_INFO *infoPtr, INT nShiftItem, INT nItem, INT direction)
2937 {
2938     if (nShiftItem < nItem) return nShiftItem;
2939
2940     if (nShiftItem > nItem) return nShiftItem + direction;
2941
2942     if (direction > 0) return nShiftItem + direction;
2943
2944     return min(nShiftItem, infoPtr->nItemCount - 1);
2945 }
2946
2947 /**
2948 * DESCRIPTION:
2949 * Updates the various indices after an item has been inserted or deleted.
2950 *
2951 * PARAMETER(S):
2952 * [I] infoPtr : valid pointer to the listview structure
2953 * [I] nItem : item index
2954 * [I] direction : Direction of shift, +1 or -1.
2955 *
2956 * RETURN:
2957 * None
2958 */
2959 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction)
2960 {
2961     INT nNewFocus;
2962     BOOL bOldChange;
2963
2964     /* temporarily disable change notification while shifting items */
2965     bOldChange = infoPtr->bDoChangeNotify;
2966     infoPtr->bDoChangeNotify = FALSE;
2967
2968     TRACE("Shifting %iu, %i steps\n", nItem, direction);
2969
2970     ranges_shift(infoPtr->selectionRanges, nItem, direction, infoPtr->nItemCount);
2971
2972     assert(abs(direction) == 1);
2973
2974     infoPtr->nSelectionMark = shift_item(infoPtr, infoPtr->nSelectionMark, nItem, direction);
2975
2976     nNewFocus = shift_item(infoPtr, infoPtr->nFocusedItem, nItem, direction);
2977     if (nNewFocus != infoPtr->nFocusedItem)
2978         LISTVIEW_SetItemFocus(infoPtr, nNewFocus);
2979     
2980     /* But we are not supposed to modify nHotItem! */
2981
2982     infoPtr->bDoChangeNotify = bOldChange;
2983 }
2984
2985
2986 /**
2987  * DESCRIPTION:
2988  * Adds a block of selections.
2989  *
2990  * PARAMETER(S):
2991  * [I] infoPtr : valid pointer to the listview structure
2992  * [I] nItem : item index
2993  *
2994  * RETURN:
2995  * None
2996  */
2997 static void LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2998 {
2999     INT nFirst = min(infoPtr->nSelectionMark, nItem);
3000     INT nLast = max(infoPtr->nSelectionMark, nItem);
3001     NMLVODSTATECHANGE nmlv;
3002     LVITEMW item;
3003     BOOL bOldChange;
3004     INT i;
3005
3006     /* Temporarily disable change notification
3007      * If the control is LVS_OWNERDATA, we need to send
3008      * only one LVN_ODSTATECHANGED notification.
3009      * See MSDN documentation for LVN_ITEMCHANGED.
3010      */
3011     bOldChange = infoPtr->bDoChangeNotify;
3012     if (infoPtr->dwStyle & LVS_OWNERDATA) infoPtr->bDoChangeNotify = FALSE;
3013
3014     if (nFirst == -1) nFirst = nItem;
3015
3016     item.state = LVIS_SELECTED;
3017     item.stateMask = LVIS_SELECTED;
3018
3019     for (i = nFirst; i <= nLast; i++)
3020         LISTVIEW_SetItemState(infoPtr,i,&item);
3021
3022     ZeroMemory(&nmlv, sizeof(nmlv));
3023     nmlv.iFrom = nFirst;
3024     nmlv.iTo = nLast;
3025     nmlv.uNewState = 0;
3026     nmlv.uOldState = item.state;
3027
3028     notify_hdr(infoPtr, LVN_ODSTATECHANGED, (LPNMHDR)&nmlv);
3029     infoPtr->bDoChangeNotify = bOldChange;
3030 }
3031
3032
3033 /***
3034  * DESCRIPTION:
3035  * Sets a single group selection.
3036  *
3037  * PARAMETER(S):
3038  * [I] infoPtr : valid pointer to the listview structure
3039  * [I] nItem : item index
3040  *
3041  * RETURN:
3042  * None
3043  */
3044 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3045 {
3046     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3047     RANGES selection;
3048     LVITEMW item;
3049     ITERATOR i;
3050
3051     if (!(selection = ranges_create(100))) return;
3052
3053     item.state = LVIS_SELECTED; 
3054     item.stateMask = LVIS_SELECTED;
3055
3056     if ((uView == LVS_LIST) || (uView == LVS_REPORT))
3057     {
3058         if (infoPtr->nSelectionMark == -1)
3059         {
3060             infoPtr->nSelectionMark = nItem;
3061             ranges_additem(selection, nItem);
3062         }
3063         else
3064         {
3065             RANGE sel;
3066             
3067             sel.lower = min(infoPtr->nSelectionMark, nItem);
3068             sel.upper = max(infoPtr->nSelectionMark, nItem) + 1;
3069             ranges_add(selection, sel);
3070         }
3071     }
3072     else
3073     {
3074         RECT rcItem, rcSel, rcSelMark;
3075         POINT ptItem;
3076         
3077         rcItem.left = LVIR_BOUNDS;
3078         if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return;
3079         rcSelMark.left = LVIR_BOUNDS;
3080         if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) return;
3081         UnionRect(&rcSel, &rcItem, &rcSelMark);
3082         iterator_frameditems(&i, infoPtr, &rcSel);
3083         while(iterator_next(&i))
3084         {
3085             LISTVIEW_GetItemPosition(infoPtr, i.nItem, &ptItem);
3086             if (PtInRect(&rcSel, ptItem)) ranges_additem(selection, i.nItem);
3087         }
3088         iterator_destroy(&i);
3089     }
3090
3091     LISTVIEW_DeselectAllSkipItems(infoPtr, selection);
3092     iterator_rangesitems(&i, selection);
3093     while(iterator_next(&i))
3094         LISTVIEW_SetItemState(infoPtr, i.nItem, &item);
3095     /* this will also destroy the selection */
3096     iterator_destroy(&i);
3097     
3098     LISTVIEW_SetItemFocus(infoPtr, nItem);
3099 }
3100
3101 /***
3102  * DESCRIPTION:
3103  * Sets a single selection.
3104  *
3105  * PARAMETER(S):
3106  * [I] infoPtr : valid pointer to the listview structure
3107  * [I] nItem : item index
3108  *
3109  * RETURN:
3110  * None
3111  */
3112 static void LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3113 {
3114     LVITEMW lvItem;
3115
3116     TRACE("nItem=%d\n", nItem);
3117     
3118     LISTVIEW_DeselectAllSkipItem(infoPtr, nItem);
3119
3120     lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
3121     lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
3122     LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3123
3124     infoPtr->nSelectionMark = nItem;
3125 }
3126
3127 /***
3128  * DESCRIPTION:
3129  * Set selection(s) with keyboard.
3130  *
3131  * PARAMETER(S):
3132  * [I] infoPtr : valid pointer to the listview structure
3133  * [I] nItem : item index
3134  *
3135  * RETURN:
3136  *   SUCCESS : TRUE (needs to be repainted)
3137  *   FAILURE : FALSE (nothing has changed)
3138  */
3139 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem)
3140 {
3141   /* FIXME: pass in the state */
3142   WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
3143   WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
3144   BOOL bResult = FALSE;
3145
3146   if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
3147   {
3148     if (infoPtr->dwStyle & LVS_SINGLESEL)
3149     {
3150       bResult = TRUE;
3151       LISTVIEW_SetSelection(infoPtr, nItem);
3152     }
3153     else
3154     {
3155       if (wShift)
3156       {
3157         bResult = TRUE;
3158         LISTVIEW_SetGroupSelection(infoPtr, nItem);
3159       }
3160       else if (wCtrl)
3161       {
3162         bResult = LISTVIEW_SetItemFocus(infoPtr, nItem);
3163       }
3164       else
3165       {
3166         bResult = TRUE;
3167         LISTVIEW_SetSelection(infoPtr, nItem);
3168       }
3169     }
3170     LISTVIEW_EnsureVisible(infoPtr, nItem, FALSE);
3171   }
3172
3173   UpdateWindow(infoPtr->hwndSelf); /* update client area */
3174   return bResult;
3175 }
3176
3177 static BOOL LISTVIEW_GetItemAtPt(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, POINT pt)
3178 {
3179     LVHITTESTINFO lvHitTestInfo;
3180
3181     ZeroMemory(&lvHitTestInfo, sizeof(lvHitTestInfo));
3182     lvHitTestInfo.pt.x = pt.x;
3183     lvHitTestInfo.pt.y = pt.y;
3184
3185     LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
3186
3187     lpLVItem->mask = LVIF_PARAM;
3188     lpLVItem->iItem = lvHitTestInfo.iItem;
3189     lpLVItem->iSubItem = 0;
3190
3191     return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
3192 }
3193
3194 /***
3195  * DESCRIPTION:
3196  * Called when the mouse is being actively tracked and has hovered for a specified
3197  * amount of time
3198  *
3199  * PARAMETER(S):
3200  * [I] infoPtr : valid pointer to the listview structure
3201  * [I] fwKeys : key indicator
3202  * [I] x,y : mouse position
3203  *
3204  * RETURN:
3205  *   0 if the message was processed, non-zero if there was an error
3206  *
3207  * INFO:
3208  * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
3209  * over the item for a certain period of time.
3210  *
3211  */
3212 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, WORD fwKyes, INT x, INT y)
3213 {
3214     if (infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)
3215     {
3216         LVITEMW item;
3217         POINT pt;
3218
3219         pt.x = x;
3220         pt.y = y;
3221
3222         if (LISTVIEW_GetItemAtPt(infoPtr, &item, pt))
3223             LISTVIEW_SetSelection(infoPtr, item.iItem);
3224     }
3225
3226     return 0;
3227 }
3228
3229 /***
3230  * DESCRIPTION:
3231  * Called whenever WM_MOUSEMOVE is received.
3232  *
3233  * PARAMETER(S):
3234  * [I] infoPtr : valid pointer to the listview structure
3235  * [I] fwKeys : key indicator
3236  * [I] x,y : mouse position
3237  *
3238  * RETURN:
3239  *   0 if the message is processed, non-zero if there was an error
3240  */
3241 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, INT x, INT y)
3242 {
3243     TRACKMOUSEEVENT trackinfo;
3244
3245     if (infoPtr->bLButtonDown && DragDetect(infoPtr->hwndSelf, infoPtr->ptClickPos))
3246     {
3247         LVHITTESTINFO lvHitTestInfo;
3248         NMLISTVIEW nmlv;
3249
3250         lvHitTestInfo.pt = infoPtr->ptClickPos;
3251         LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
3252
3253         ZeroMemory(&nmlv, sizeof(nmlv));
3254         nmlv.iItem = lvHitTestInfo.iItem;
3255         nmlv.ptAction = infoPtr->ptClickPos;
3256
3257         notify_listview(infoPtr, LVN_BEGINDRAG, &nmlv);
3258
3259         return 0;
3260     } 
3261
3262   /* see if we are supposed to be tracking mouse hovering */
3263   if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) {
3264      /* fill in the trackinfo struct */
3265      trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
3266      trackinfo.dwFlags = TME_QUERY;
3267      trackinfo.hwndTrack = infoPtr->hwndSelf;
3268      trackinfo.dwHoverTime = infoPtr->dwHoverTime;
3269
3270      /* see if we are already tracking this hwnd */
3271      _TrackMouseEvent(&trackinfo);
3272
3273      if(!(trackinfo.dwFlags & TME_HOVER)) {
3274        trackinfo.dwFlags = TME_HOVER;
3275
3276        /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
3277        _TrackMouseEvent(&trackinfo);
3278     }
3279   }
3280
3281   return 0;
3282 }
3283
3284
3285 /***
3286  * Tests wheather the item is assignable to a list with style lStyle 
3287  */
3288 static inline BOOL is_assignable_item(const LVITEMW *lpLVItem, LONG lStyle)
3289 {
3290     if ( (lpLVItem->mask & LVIF_TEXT) && 
3291         (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) &&
3292         (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE;
3293     
3294     return TRUE;
3295 }
3296
3297
3298 /***
3299  * DESCRIPTION:
3300  * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
3301  *
3302  * PARAMETER(S):
3303  * [I] infoPtr : valid pointer to the listview structure
3304  * [I] lpLVItem : valid pointer to new item atttributes
3305  * [I] isNew : the item being set is being inserted
3306  * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3307  * [O] bChanged : will be set to TRUE if the item really changed
3308  *
3309  * RETURN:
3310  *   SUCCESS : TRUE
3311  *   FAILURE : FALSE
3312  */
3313 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isNew, BOOL isW, BOOL *bChanged)
3314 {
3315     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3316     ITEM_INFO *lpItem;
3317     NMLISTVIEW nmlv;
3318     UINT uChanged = 0;
3319     LVITEMW item;
3320
3321     TRACE("()\n");
3322
3323     assert(lpLVItem->iItem >= 0 && lpLVItem->iItem < infoPtr->nItemCount);
3324     
3325     if (lpLVItem->mask == 0) return TRUE;   
3326
3327     if (infoPtr->dwStyle & LVS_OWNERDATA)
3328     {
3329         /* a virtual listview we stores only selection and focus */
3330         if (lpLVItem->mask & ~LVIF_STATE)
3331             return FALSE;
3332         lpItem = NULL;
3333     }
3334     else
3335     {
3336         HDPA hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3337         lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
3338         assert (lpItem);
3339     }
3340
3341     /* we need to get the lParam and state of the item */
3342     item.iItem = lpLVItem->iItem;
3343     item.iSubItem = lpLVItem->iSubItem;
3344     item.mask = LVIF_STATE | LVIF_PARAM;
3345     item.stateMask = ~0;
3346     item.state = 0;
3347     item.lParam = 0;
3348     if (!isNew && !LISTVIEW_GetItemW(infoPtr, &item)) return FALSE;
3349
3350     TRACE("oldState=%x, newState=%x\n", item.state, lpLVItem->state);
3351     /* determine what fields will change */    
3352     if ((lpLVItem->mask & LVIF_STATE) && ((item.state ^ lpLVItem->state) & lpLVItem->stateMask & ~infoPtr->uCallbackMask))
3353         uChanged |= LVIF_STATE;
3354
3355     if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage))
3356         uChanged |= LVIF_IMAGE;
3357
3358     if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam))
3359         uChanged |= LVIF_PARAM;
3360
3361     if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent))
3362         uChanged |= LVIF_INDENT;
3363
3364     if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
3365         uChanged |= LVIF_TEXT;
3366    
3367     TRACE("uChanged=0x%x\n", uChanged); 
3368     if (!uChanged) return TRUE;
3369     *bChanged = TRUE;
3370     
3371     ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3372     nmlv.iItem = lpLVItem->iItem;
3373     nmlv.uNewState = (item.state & ~lpLVItem->stateMask) | (lpLVItem->state & lpLVItem->stateMask);
3374     nmlv.uOldState = item.state;
3375     nmlv.uChanged = uChanged;
3376     nmlv.lParam = item.lParam;
3377     
3378     /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
3379     /* and we are _NOT_ virtual (LVS_OWERNDATA), and change notifications */
3380     /* are enabled */
3381     if(lpItem && !isNew && infoPtr->bDoChangeNotify &&
3382        notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv))
3383         return FALSE;
3384
3385     /* copy information */
3386     if (lpLVItem->mask & LVIF_TEXT)
3387         textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
3388
3389     if (lpLVItem->mask & LVIF_IMAGE)
3390         lpItem->hdr.iImage = lpLVItem->iImage;
3391
3392     if (lpLVItem->mask & LVIF_PARAM)
3393         lpItem->lParam = lpLVItem->lParam;
3394
3395     if (lpLVItem->mask & LVIF_INDENT)
3396         lpItem->iIndent = lpLVItem->iIndent;
3397
3398     if (uChanged & LVIF_STATE)
3399     {
3400         if (lpItem && (lpLVItem->stateMask & ~infoPtr->uCallbackMask & ~(LVIS_FOCUSED | LVIS_SELECTED)))
3401         {
3402             lpItem->state &= ~lpLVItem->stateMask;
3403             lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
3404         }
3405         if (lpLVItem->state & lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED)
3406         {
3407             if (infoPtr->dwStyle & LVS_SINGLESEL) LISTVIEW_DeselectAllSkipItem(infoPtr, lpLVItem->iItem);
3408             ranges_additem(infoPtr->selectionRanges, lpLVItem->iItem);
3409         }
3410         else if (lpLVItem->stateMask & LVIS_SELECTED)
3411             ranges_delitem(infoPtr->selectionRanges, lpLVItem->iItem);
3412         
3413         /* if we are asked to change focus, and we manage it, do it */
3414         if (lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
3415         {
3416             if (lpLVItem->state & LVIS_FOCUSED)
3417             {
3418                 LISTVIEW_SetItemFocus(infoPtr, -1);
3419                 infoPtr->nFocusedItem = lpLVItem->iItem;
3420                 LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, uView == LVS_LIST);
3421             }
3422             else if (infoPtr->nFocusedItem == lpLVItem->iItem)
3423                 infoPtr->nFocusedItem = -1;
3424         }
3425     }
3426
3427     /* if we're inserting the item, we're done */
3428     if (isNew) return TRUE;
3429     
3430     /* send LVN_ITEMCHANGED notification */
3431     if (lpLVItem->mask & LVIF_PARAM) nmlv.lParam = lpLVItem->lParam;
3432     if (infoPtr->bDoChangeNotify) notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
3433
3434     return TRUE;
3435 }
3436
3437 /***
3438  * DESCRIPTION:
3439  * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes.
3440  *
3441  * PARAMETER(S):
3442  * [I] infoPtr : valid pointer to the listview structure
3443  * [I] lpLVItem : valid pointer to new subitem atttributes
3444  * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3445  * [O] bChanged : will be set to TRUE if the item really changed
3446  *
3447  * RETURN:
3448  *   SUCCESS : TRUE
3449  *   FAILURE : FALSE
3450  */
3451 static BOOL set_sub_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW, BOOL *bChanged)
3452 {
3453     HDPA hdpaSubItems;
3454     SUBITEM_INFO *lpSubItem;
3455
3456     /* we do not support subitems for virtual listviews */
3457     if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
3458     
3459     /* set subitem only if column is present */
3460     if (lpLVItem->iSubItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
3461    
3462     /* First do some sanity checks */
3463     if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE)) return FALSE;
3464     if (!(lpLVItem->mask & (LVIF_TEXT | LVIF_IMAGE))) return TRUE;
3465    
3466     /* get the subitem structure, and create it if not there */
3467     hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3468     assert (hdpaSubItems);
3469     
3470     lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
3471     if (!lpSubItem)
3472     {
3473         SUBITEM_INFO *tmpSubItem;
3474         INT i;
3475
3476         lpSubItem = (SUBITEM_INFO *)Alloc(sizeof(SUBITEM_INFO));
3477         if (!lpSubItem) return FALSE;
3478         /* we could binary search here, if need be...*/
3479         for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
3480         {
3481             tmpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
3482             if (tmpSubItem->iSubItem > lpLVItem->iSubItem) break;
3483         }
3484         if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1)
3485         {
3486             Free(lpSubItem);
3487             return FALSE;
3488         }
3489         lpSubItem->iSubItem = lpLVItem->iSubItem;
3490         lpSubItem->hdr.iImage = I_IMAGECALLBACK;
3491         *bChanged = TRUE;
3492     }
3493     
3494     if (lpLVItem->mask & LVIF_IMAGE)
3495         if (lpSubItem->hdr.iImage != lpLVItem->iImage)
3496         {
3497             lpSubItem->hdr.iImage = lpLVItem->iImage;
3498             *bChanged = TRUE;
3499         }
3500
3501     if (lpLVItem->mask & LVIF_TEXT)
3502         if (lpSubItem->hdr.pszText != lpLVItem->pszText)
3503         {
3504             textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW);
3505             *bChanged = TRUE;
3506         }
3507
3508     return TRUE;
3509 }
3510
3511 /***
3512  * DESCRIPTION:
3513  * Sets item attributes.
3514  *
3515  * PARAMETER(S):
3516  * [I] infoPtr : valid pointer to the listview structure
3517  * [I] lpLVItem : new item atttributes
3518  * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3519  *
3520  * RETURN:
3521  *   SUCCESS : TRUE
3522  *   FAILURE : FALSE
3523  */
3524 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
3525 {
3526     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3527     LPWSTR pszText = NULL;
3528     BOOL bResult, bChanged = FALSE;
3529     
3530     TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
3531
3532     if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
3533         return FALSE;
3534
3535     /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
3536     if ((lpLVItem->mask & LVIF_TEXT) && is_textW(lpLVItem->pszText))
3537     {
3538         pszText = lpLVItem->pszText;
3539         ((LVITEMW *)lpLVItem)->pszText = textdupTtoW(lpLVItem->pszText, isW);
3540     }
3541     
3542     /* actually set the fields */
3543     if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE;
3544     
3545     if (lpLVItem->iSubItem)
3546         bResult = set_sub_item(infoPtr, lpLVItem, TRUE, &bChanged);
3547     else
3548         bResult = set_main_item(infoPtr, lpLVItem, FALSE, TRUE, &bChanged);
3549
3550     /* redraw item, if necessary */
3551     if (bChanged && !infoPtr->bIsDrawing)
3552     {
3553         /* this little optimization eliminates some nasty flicker */
3554         if ( uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) &&
3555              (!(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) || lpLVItem->iSubItem) )
3556             LISTVIEW_InvalidateSubItem(infoPtr, lpLVItem->iItem, lpLVItem->iSubItem);
3557         else
3558             LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem);
3559     }
3560     /* restore text */
3561     if (pszText)
3562     {
3563         textfreeT(lpLVItem->pszText, isW);
3564         ((LVITEMW *)lpLVItem)->pszText = pszText;
3565     }
3566
3567     return bResult;
3568 }
3569
3570 /***
3571  * DESCRIPTION:
3572  * Retrieves the index of the item at coordinate (0, 0) of the client area.
3573  *
3574  * PARAMETER(S):
3575  * [I] infoPtr : valid pointer to the listview structure
3576  *
3577  * RETURN:
3578  * item index
3579  */
3580 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *infoPtr)
3581 {
3582     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3583     INT nItem = 0;
3584     SCROLLINFO scrollInfo;
3585
3586     scrollInfo.cbSize = sizeof(SCROLLINFO);
3587     scrollInfo.fMask = SIF_POS;
3588
3589     if (uView == LVS_LIST)
3590     {
3591         if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
3592             nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
3593     }
3594     else if (uView == LVS_REPORT)
3595     {
3596         if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3597             nItem = scrollInfo.nPos;
3598     } 
3599     else
3600     {
3601         if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3602             nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight);
3603     }
3604
3605     TRACE("nItem=%d\n", nItem);
3606     
3607     return nItem;
3608 }
3609
3610
3611 /***
3612  * DESCRIPTION:
3613  * Erases the background of the given rectangle
3614  *
3615  * PARAMETER(S):
3616  * [I] infoPtr : valid pointer to the listview structure
3617  * [I] hdc : device context handle
3618  * [I] lprcBox : clipping rectangle
3619  *
3620  * RETURN:
3621  *   Success: TRUE
3622  *   Failure: FALSE
3623  */
3624 static inline BOOL LISTVIEW_FillBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *lprcBox)
3625 {
3626     if (!infoPtr->hBkBrush) return FALSE;
3627
3628     TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc, debugrect(lprcBox), infoPtr->hBkBrush);
3629
3630     return FillRect(hdc, lprcBox, infoPtr->hBkBrush);
3631 }
3632
3633 /***
3634  * DESCRIPTION:
3635  * Draws an item.
3636  *
3637  * PARAMETER(S):
3638  * [I] infoPtr : valid pointer to the listview structure
3639  * [I] hdc : device context handle
3640  * [I] nItem : item index
3641  * [I] nSubItem : subitem index
3642  * [I] pos : item position in client coordinates
3643  * [I] cdmode : custom draw mode
3644  *
3645  * RETURN:
3646  *   Success: TRUE
3647  *   Failure: FALSE
3648  */
3649 static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, INT nSubItem, POINT pos, DWORD cdmode)
3650 {
3651     UINT uFormat, uView = infoPtr->dwStyle & LVS_TYPEMASK;
3652     WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3653     static const WCHAR szCallback[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 };
3654     DWORD cdsubitemmode = CDRF_DODEFAULT;
3655     RECT* lprcFocus, rcSelect, rcBox, rcState, rcIcon, rcLabel;
3656     NMLVCUSTOMDRAW nmlvcd;
3657     HIMAGELIST himl;
3658     LVITEMW lvItem;
3659
3660     TRACE("(hdc=%p, nItem=%d, nSubItem=%d, pos=%s)\n", hdc, nItem, nSubItem, debugpoint(&pos));
3661
3662     /* get information needed for drawing the item */
3663     lvItem.mask = LVIF_TEXT | LVIF_IMAGE;
3664     if (nSubItem == 0) lvItem.mask |= LVIF_STATE | LVIF_PARAM;
3665     if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
3666     lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
3667     lvItem.iItem = nItem;
3668     lvItem.iSubItem = nSubItem;
3669     lvItem.state = 0;
3670     lvItem.lParam = 0;
3671     lvItem.cchTextMax = DISP_TEXT_SIZE;
3672     lvItem.pszText = szDispText;
3673     if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
3674     if (nSubItem > 0 && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)) 
3675         lvItem.state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3676     if (lvItem.pszText == LPSTR_TEXTCALLBACKW) lvItem.pszText = (LPWSTR)szCallback;
3677     TRACE("   lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3678
3679     /* now check if we need to update the focus rectangle */
3680     lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
3681     
3682     if (!lprcFocus) lvItem.state &= ~LVIS_FOCUSED;
3683     LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel);
3684     OffsetRect(&rcBox, pos.x, pos.y);
3685     OffsetRect(&rcState, pos.x, pos.y);
3686     OffsetRect(&rcIcon, pos.x, pos.y);
3687     OffsetRect(&rcLabel, pos.x, pos.y);
3688     TRACE("    rcBox=%s, rcState=%s, rcIcon=%s. rcLabel=%s\n", 
3689         debugrect(&rcBox), debugrect(&rcState), debugrect(&rcIcon), debugrect(&rcLabel));
3690
3691     /* fill in the custom draw structure */
3692     customdraw_fill(&nmlvcd, infoPtr, hdc, &rcBox, &lvItem);
3693
3694     if (nSubItem > 0) cdmode = infoPtr->cditemmode;
3695     if (cdmode & CDRF_NOTIFYITEMDRAW)
3696         cdsubitemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3697     if (nSubItem == 0) infoPtr->cditemmode = cdsubitemmode;
3698     if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3699     /* we have to send a CDDS_SUBITEM customdraw explicitly for subitem 0 */
3700     if (nSubItem == 0 && cdsubitemmode == CDRF_NOTIFYITEMDRAW)
3701     {
3702         cdsubitemmode = notify_customdraw(infoPtr, CDDS_SUBITEM | CDDS_ITEMPREPAINT, &nmlvcd);
3703         if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3704     }
3705     if (nSubItem == 0 || (cdmode & CDRF_NOTIFYITEMDRAW))
3706         prepaint_setup(infoPtr, hdc, &nmlvcd);
3707
3708     /* in full row select, subitems, will just use main item's colors */
3709     if (nSubItem && uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3710         nmlvcd.clrTextBk = CLR_NONE;
3711     
3712     /* state icons */
3713     if (infoPtr->himlState && !IsRectEmpty(&rcState))
3714     {
3715         UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12;
3716         if (uStateImage)
3717         {
3718              TRACE("uStateImage=%d\n", uStateImage);
3719              ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, rcState.left, rcState.top, ILD_NORMAL);
3720         }
3721     }
3722
3723     /* small icons */
3724     himl = (uView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
3725     if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon))
3726     {
3727         TRACE("iImage=%d\n", lvItem.iImage);
3728         ImageList_Draw(himl, lvItem.iImage, hdc, rcIcon.left, rcIcon.top,
3729                         (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus) ? ILD_SELECTED : ILD_NORMAL);
3730     }
3731
3732     /* Don't bother painting item being edited */
3733     if (infoPtr->hwndEdit && nItem == infoPtr->nEditLabelItem && nSubItem == 0) goto postpaint;
3734
3735     /* draw the selection background, if we're drawing the main item */
3736     if (nSubItem == 0)
3737     {
3738         rcSelect = rcLabel;
3739         if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3740             rcSelect.right = rcBox.right;
3741    
3742         if (nmlvcd.clrTextBk != CLR_NONE) 
3743             ExtTextOutW(hdc, rcSelect.left, rcSelect.top, ETO_OPAQUE, &rcSelect, 0, 0, 0);
3744         if(lprcFocus) *lprcFocus = rcSelect;
3745     }
3746    
3747     /* figure out the text drawing flags */
3748     uFormat = (uView == LVS_ICON ? (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS) : LV_SL_DT_FLAGS);
3749     if (uView == LVS_ICON)
3750         uFormat = (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS);
3751     else if (nSubItem)
3752     {
3753         switch (LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->fmt & LVCFMT_JUSTIFYMASK)
3754         {
3755         case LVCFMT_RIGHT:  uFormat |= DT_RIGHT;  break;
3756         case LVCFMT_CENTER: uFormat |= DT_CENTER; break;
3757         default:            uFormat |= DT_LEFT;
3758         }
3759     }
3760     if (!(uFormat & (DT_RIGHT | DT_CENTER)))
3761     {
3762         if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon)) rcLabel.left += IMAGE_PADDING;
3763         else rcLabel.left += LABEL_HOR_PADDING;
3764     }
3765     else if (uFormat & DT_RIGHT) rcLabel.right -= LABEL_HOR_PADDING;
3766     DrawTextW(hdc, lvItem.pszText, -1, &rcLabel, uFormat);
3767
3768 postpaint:
3769     if (cdsubitemmode & CDRF_NOTIFYPOSTPAINT)
3770         notify_postpaint(infoPtr, &nmlvcd);
3771     return TRUE;
3772 }
3773
3774 /***
3775  * DESCRIPTION:
3776  * Draws listview items when in owner draw mode.
3777  *
3778  * PARAMETER(S):
3779  * [I] infoPtr : valid pointer to the listview structure
3780  * [I] hdc : device context handle
3781  *
3782  * RETURN:
3783  * None
3784  */
3785 static void LISTVIEW_RefreshOwnerDraw(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3786 {
3787     UINT uID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
3788     DWORD cditemmode = CDRF_DODEFAULT;
3789     NMLVCUSTOMDRAW nmlvcd;
3790     POINT Origin, Position;
3791     DRAWITEMSTRUCT dis;
3792     LVITEMW item;
3793     
3794     TRACE("()\n");
3795
3796     ZeroMemory(&dis, sizeof(dis));
3797     
3798     /* Get scroll info once before loop */
3799     LISTVIEW_GetOrigin(infoPtr, &Origin);
3800     
3801     /* iterate through the invalidated rows */
3802     while(iterator_next(i))
3803     {
3804         item.iItem = i->nItem;
3805         item.iSubItem = 0;
3806         item.mask = LVIF_PARAM | LVIF_STATE;
3807         item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3808         if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
3809            
3810         dis.CtlType = ODT_LISTVIEW;
3811         dis.CtlID = uID;
3812         dis.itemID = item.iItem;
3813         dis.itemAction = ODA_DRAWENTIRE;
3814         dis.itemState = 0;
3815         if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
3816         if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS;
3817         dis.hwndItem = infoPtr->hwndSelf;
3818         dis.hDC = hdc;
3819         LISTVIEW_GetItemOrigin(infoPtr, dis.itemID, &Position);
3820         dis.rcItem.left = Position.x + Origin.x;
3821         dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth;
3822         dis.rcItem.top = Position.y + Origin.y;
3823         dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3824         dis.itemData = item.lParam;
3825
3826         TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), debugrect(&dis.rcItem));
3827
3828     /*
3829      * Even if we do not send the CDRF_NOTIFYITEMDRAW we need to fill the nmlvcd
3830      * structure for the rest. of the paint cycle
3831      */
3832         customdraw_fill(&nmlvcd, infoPtr, hdc, &dis.rcItem, &item);
3833         if (cdmode & CDRF_NOTIFYITEMDRAW)
3834             cditemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3835     
3836         if (!(cditemmode & CDRF_SKIPDEFAULT))
3837         {
3838             prepaint_setup (infoPtr, hdc, &nmlvcd);
3839             SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
3840         }
3841
3842         if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3843             notify_postpaint(infoPtr, &nmlvcd);
3844     }
3845 }
3846
3847 /***
3848  * DESCRIPTION:
3849  * Draws listview items when in report display mode.
3850  *
3851  * PARAMETER(S):
3852  * [I] infoPtr : valid pointer to the listview structure
3853  * [I] hdc : device context handle
3854  * [I] cdmode : custom draw mode
3855  *
3856  * RETURN:
3857  * None
3858  */
3859 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3860 {
3861     INT rgntype;
3862     RECT rcClip, rcItem;
3863     POINT Origin, Position;
3864     RANGE colRange;
3865     ITERATOR j;
3866
3867     TRACE("()\n");
3868
3869     /* figure out what to draw */
3870     rgntype = GetClipBox(hdc, &rcClip);
3871     if (rgntype == NULLREGION) return;
3872     
3873     /* Get scroll info once before loop */
3874     LISTVIEW_GetOrigin(infoPtr, &Origin);
3875     
3876     /* narrow down the columns we need to paint */
3877     for(colRange.lower = 0; colRange.lower < DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.lower++)
3878     {
3879         LISTVIEW_GetHeaderRect(infoPtr, colRange.lower, &rcItem);
3880         if (rcItem.right + Origin.x >= rcClip.left) break;
3881     }
3882     for(colRange.upper = DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.upper > 0; colRange.upper--)
3883     {
3884         LISTVIEW_GetHeaderRect(infoPtr, colRange.upper - 1, &rcItem);
3885         if (rcItem.left + Origin.x < rcClip.right) break;
3886     }
3887     iterator_rangeitems(&j, colRange);
3888
3889     /* in full row select, we _have_ to draw the main item */
3890     if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
3891         j.nSpecial = 0;
3892
3893     /* iterate through the invalidated rows */
3894     while(iterator_next(i))
3895     {
3896         /* iterate through the invalidated columns */
3897         while(iterator_next(&j))
3898         {
3899             LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
3900             Position.x += Origin.x;
3901             Position.y += Origin.y;
3902
3903             if (rgntype == COMPLEXREGION && !((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && j.nItem == 0))
3904             {
3905                 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
3906                 rcItem.top = 0;
3907                 rcItem.bottom = infoPtr->nItemHeight;
3908                 OffsetRect(&rcItem, Position.x, Position.y);
3909                 if (!RectVisible(hdc, &rcItem)) continue;
3910             }
3911
3912             LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, j.nItem, Position, cdmode);
3913         }
3914     }
3915     iterator_destroy(&j);
3916 }
3917
3918 /***
3919  * DESCRIPTION:
3920  * Draws listview items when in list display mode.
3921  *
3922  * PARAMETER(S):
3923  * [I] infoPtr : valid pointer to the listview structure
3924  * [I] hdc : device context handle
3925  * [I] cdmode : custom draw mode
3926  *
3927  * RETURN:
3928  * None
3929  */
3930 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3931 {
3932     POINT Origin, Position;
3933
3934     /* Get scroll info once before loop */
3935     LISTVIEW_GetOrigin(infoPtr, &Origin);
3936     
3937     while(iterator_prev(i))
3938     {
3939         LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
3940         Position.x += Origin.x;
3941         Position.y += Origin.y;
3942
3943         LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, 0, Position, cdmode);
3944     }
3945 }
3946
3947
3948 /***
3949  * DESCRIPTION:
3950  * Draws listview items.
3951  *
3952  * PARAMETER(S):
3953  * [I] infoPtr : valid pointer to the listview structure
3954  * [I] hdc : device context handle
3955  *
3956  * RETURN:
3957  * NoneX
3958  */
3959 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc)
3960 {
3961     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3962     COLORREF oldTextColor, oldClrTextBk, oldClrText;
3963     NMLVCUSTOMDRAW nmlvcd;
3964     HFONT hOldFont;
3965     DWORD cdmode;
3966     INT oldBkMode;
3967     RECT rcClient;
3968     ITERATOR i;
3969
3970     LISTVIEW_DUMP(infoPtr);
3971   
3972     infoPtr->bIsDrawing = TRUE;
3973
3974     /* save dc values we're gonna trash while drawing */
3975     hOldFont = SelectObject(hdc, infoPtr->hFont);
3976     oldBkMode = GetBkMode(hdc);
3977     infoPtr->clrTextBkDefault = GetBkColor(hdc);
3978     oldTextColor = GetTextColor(hdc);
3979
3980     oldClrTextBk = infoPtr->clrTextBk;
3981     oldClrText   = infoPtr->clrText;
3982    
3983     infoPtr->cditemmode = CDRF_DODEFAULT;
3984
3985     GetClientRect(infoPtr->hwndSelf, &rcClient);
3986     customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient, 0);
3987     cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3988     if (cdmode & CDRF_SKIPDEFAULT) goto enddraw;
3989     prepaint_setup(infoPtr, hdc, &nmlvcd);
3990
3991     /* Use these colors to draw the items */
3992     infoPtr->clrTextBk = nmlvcd.clrTextBk;
3993     infoPtr->clrText = nmlvcd.clrText;
3994
3995     /* nothing to draw */
3996     if(infoPtr->nItemCount == 0) goto enddraw;
3997
3998     /* figure out what we need to draw */
3999     iterator_visibleitems(&i, infoPtr, hdc);
4000     
4001     /* send cache hint notification */
4002     if (infoPtr->dwStyle & LVS_OWNERDATA)
4003     {
4004         RANGE range = iterator_range(&i);
4005         NMLVCACHEHINT nmlv;
4006         
4007         ZeroMemory(&nmlv, sizeof(NMLVCACHEHINT));
4008         nmlv.iFrom = range.lower;
4009         nmlv.iTo   = range.upper - 1;
4010         notify_hdr(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
4011     }
4012
4013     if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
4014         LISTVIEW_RefreshOwnerDraw(infoPtr, &i, hdc, cdmode);
4015     else
4016     {
4017         if (uView == LVS_REPORT)
4018             LISTVIEW_RefreshReport(infoPtr, &i, hdc, cdmode);
4019         else /* LVS_LIST, LVS_ICON or LVS_SMALLICON */
4020             LISTVIEW_RefreshList(infoPtr, &i, hdc, cdmode);
4021
4022         /* if we have a focus rect, draw it */
4023         if (infoPtr->bFocus)
4024             DrawFocusRect(hdc, &infoPtr->rcFocus);
4025     }
4026     iterator_destroy(&i);
4027     
4028 enddraw:
4029     if (cdmode & CDRF_NOTIFYPOSTPAINT)
4030         notify_postpaint(infoPtr, &nmlvcd);
4031
4032     infoPtr->clrTextBk = oldClrTextBk;
4033     infoPtr->clrText = oldClrText;
4034
4035     SelectObject(hdc, hOldFont);
4036     SetBkMode(hdc, oldBkMode);
4037     SetBkColor(hdc, infoPtr->clrTextBkDefault);
4038     SetTextColor(hdc, oldTextColor);
4039     infoPtr->bIsDrawing = FALSE;
4040 }
4041
4042
4043 /***
4044  * DESCRIPTION:
4045  * Calculates the approximate width and height of a given number of items.
4046  *
4047  * PARAMETER(S):
4048  * [I] infoPtr : valid pointer to the listview structure
4049  * [I] nItemCount : number of items
4050  * [I] wWidth : width
4051  * [I] wHeight : height
4052  *
4053  * RETURN:
4054  * Returns a DWORD. The width in the low word and the height in high word.
4055  */
4056 static DWORD LISTVIEW_ApproximateViewRect(LISTVIEW_INFO *infoPtr, INT nItemCount,
4057                                             WORD wWidth, WORD wHeight)
4058 {
4059   UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4060   INT nItemCountPerColumn = 1;
4061   INT nColumnCount = 0;
4062   DWORD dwViewRect = 0;
4063
4064   if (nItemCount == -1)
4065     nItemCount = infoPtr->nItemCount;
4066
4067   if (uView == LVS_LIST)
4068   {
4069     if (wHeight == 0xFFFF)
4070     {
4071       /* use current height */
4072       wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4073     }
4074
4075     if (wHeight < infoPtr->nItemHeight)
4076       wHeight = infoPtr->nItemHeight;
4077
4078     if (nItemCount > 0)
4079     {
4080       if (infoPtr->nItemHeight > 0)
4081       {
4082         nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
4083         if (nItemCountPerColumn == 0)
4084           nItemCountPerColumn = 1;
4085
4086         if (nItemCount % nItemCountPerColumn != 0)
4087           nColumnCount = nItemCount / nItemCountPerColumn;
4088         else
4089           nColumnCount = nItemCount / nItemCountPerColumn + 1;
4090       }
4091     }
4092
4093     /* Microsoft padding magic */
4094     wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
4095     wWidth = nColumnCount * infoPtr->nItemWidth + 2;
4096
4097     dwViewRect = MAKELONG(wWidth, wHeight);
4098   }
4099   else if (uView == LVS_REPORT)
4100   {
4101     RECT rcBox;
4102
4103     if (infoPtr->nItemCount > 0)
4104     {
4105       LISTVIEW_GetItemBox(infoPtr, 0, &rcBox);
4106       wWidth = rcBox.right - rcBox.left;
4107       wHeight = (rcBox.bottom - rcBox.top) * nItemCount;
4108     }
4109     else
4110     {
4111       /* use current height and width */
4112       if (wHeight == 0xffff)
4113           wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4114       if (wWidth == 0xffff)
4115           wWidth = infoPtr->rcList.right - infoPtr->rcList.left;
4116     }
4117
4118     dwViewRect = MAKELONG(wWidth, wHeight);
4119   }
4120   else if (uView == LVS_SMALLICON)
4121     FIXME("uView == LVS_SMALLICON: not implemented\n");
4122   else if (uView == LVS_ICON)
4123     FIXME("uView == LVS_ICON: not implemented\n");
4124
4125   return dwViewRect;
4126 }
4127
4128
4129 /***
4130  * DESCRIPTION:
4131  * Create a drag image list for the specified item.
4132  *
4133  * PARAMETER(S):
4134  * [I] infoPtr : valid pointer to the listview structure
4135  * [I] iItem   : index of item
4136  * [O] lppt    : Upperr-left corner of the image
4137  *
4138  * RETURN:
4139  * Returns a handle to the image list if successful, NULL otherwise.
4140  */
4141 static HIMAGELIST LISTVIEW_CreateDragImage(LISTVIEW_INFO *infoPtr, INT iItem, LPPOINT lppt)
4142 {
4143     RECT rcItem;
4144     SIZE size;
4145     POINT pos;
4146     HDC hdc, hdcOrig;
4147     HBITMAP hbmp, hOldbmp;
4148     HIMAGELIST dragList = 0;
4149     TRACE("iItem=%d Count=%d \n", iItem, infoPtr->nItemCount);
4150
4151     if (iItem < 0 || iItem >= infoPtr->nItemCount)
4152         return 0;
4153
4154     rcItem.left = LVIR_BOUNDS;
4155     if (!LISTVIEW_GetItemRect(infoPtr, iItem, &rcItem))
4156         return 0;
4157
4158     lppt->x = rcItem.left;
4159     lppt->y = rcItem.top;
4160
4161     size.cx = rcItem.right - rcItem.left;
4162     size.cy = rcItem.bottom - rcItem.top;
4163
4164     hdcOrig = GetDC(infoPtr->hwndSelf);
4165     hdc = CreateCompatibleDC(hdcOrig);
4166     hbmp = CreateCompatibleBitmap(hdcOrig, size.cx, size.cy);
4167     hOldbmp = SelectObject(hdc, hbmp);
4168
4169     rcItem.left = rcItem.top = 0;
4170     rcItem.right = size.cx;
4171     rcItem.bottom = size.cy;
4172     FillRect(hdc, &rcItem, infoPtr->hBkBrush);
4173     
4174     pos.x = pos.y = 0;
4175     if (LISTVIEW_DrawItem(infoPtr, hdc, iItem, 0, pos, infoPtr->cditemmode))
4176     {
4177         dragList = ImageList_Create(size.cx, size.cy, ILC_COLOR, 10, 10);
4178         SelectObject(hdc, hOldbmp);
4179         ImageList_Add(dragList, hbmp, 0);
4180     }
4181     else
4182         SelectObject(hdc, hOldbmp);
4183
4184     DeleteObject(hbmp);
4185     DeleteDC(hdc);
4186     ReleaseDC(infoPtr->hwndSelf, hdcOrig);
4187
4188     TRACE("ret=%p\n", dragList);
4189
4190     return dragList;
4191 }
4192
4193
4194 /***
4195  * DESCRIPTION:
4196  * Removes all listview items and subitems.
4197  *
4198  * PARAMETER(S):
4199  * [I] infoPtr : valid pointer to the listview structure
4200  *
4201  * RETURN:
4202  *   SUCCESS : TRUE
4203  *   FAILURE : FALSE
4204  */
4205 static BOOL LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr)
4206 {
4207     NMLISTVIEW nmlv;
4208     HDPA hdpaSubItems = NULL;
4209     BOOL bSuppress;
4210     ITEMHDR *hdrItem;
4211     INT i, j;
4212
4213     TRACE("()\n");
4214
4215     /* we do it directly, to avoid notifications */
4216     ranges_clear(infoPtr->selectionRanges);
4217     infoPtr->nSelectionMark = -1;
4218     infoPtr->nFocusedItem = -1;
4219     SetRectEmpty(&infoPtr->rcFocus);
4220     /* But we are supposed to leave nHotItem as is! */
4221
4222
4223     /* send LVN_DELETEALLITEMS notification */
4224     ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4225     nmlv.iItem = -1;
4226     bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
4227
4228     for (i = infoPtr->nItemCount - 1; i >= 0; i--)
4229     {
4230         /* send LVN_DELETEITEM notification, if not suppressed */
4231         if (!bSuppress) notify_deleteitem(infoPtr, i);
4232         if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4233         {
4234             hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
4235             for (j = 0; j < DPA_GetPtrCount(hdpaSubItems); j++)
4236             {
4237                 hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, j);
4238                 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4239                 Free(hdrItem);
4240             }
4241             DPA_Destroy(hdpaSubItems);
4242             DPA_DeletePtr(infoPtr->hdpaItems, i);
4243         }
4244         DPA_DeletePtr(infoPtr->hdpaPosX, i);
4245         DPA_DeletePtr(infoPtr->hdpaPosY, i);
4246         infoPtr->nItemCount --;
4247     }
4248     
4249     LISTVIEW_UpdateScroll(infoPtr);
4250
4251     LISTVIEW_InvalidateList(infoPtr);
4252     
4253     return TRUE;
4254 }
4255
4256 /***
4257  * DESCRIPTION:
4258  * Scrolls, and updates the columns, when a column is changing width.
4259  *
4260  * PARAMETER(S):
4261  * [I] infoPtr : valid pointer to the listview structure
4262  * [I] nColumn : column to scroll
4263  * [I] dx : amount of scroll, in pixels
4264  *
4265  * RETURN:
4266  *   None.
4267  */
4268 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO *infoPtr, INT nColumn, INT dx)
4269 {
4270     COLUMN_INFO *lpColumnInfo;
4271     RECT rcOld, rcCol;
4272     INT nCol;
4273    
4274     if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) < 1) return;
4275     lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1));
4276     rcCol = lpColumnInfo->rcHeader;
4277     if (nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns))
4278         rcCol.left = rcCol.right;
4279     
4280     /* ajust the other columns */
4281     for (nCol = nColumn; nCol < DPA_GetPtrCount(infoPtr->hdpaColumns); nCol++)
4282     {
4283         lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nCol);
4284         lpColumnInfo->rcHeader.left += dx;
4285         lpColumnInfo->rcHeader.right += dx;
4286     }
4287
4288     /* do not update screen if not in report mode */
4289     if (!is_redrawing(infoPtr) || (infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return;
4290     
4291     /* if we have a focus, must first erase the focus rect */
4292     if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, FALSE);
4293     
4294     /* Need to reset the item width when inserting a new column */
4295     infoPtr->nItemWidth += dx;
4296
4297     LISTVIEW_UpdateScroll(infoPtr);
4298
4299     /* scroll to cover the deleted column, and invalidate for redraw */
4300     rcOld = infoPtr->rcList;
4301     rcOld.left = rcCol.left;
4302     ScrollWindowEx(infoPtr->hwndSelf, dx, 0, &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
4303     
4304     /* we can restore focus now */
4305     if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, TRUE);
4306 }
4307
4308 /***
4309  * DESCRIPTION:
4310  * Removes a column from the listview control.
4311  *
4312  * PARAMETER(S):
4313  * [I] infoPtr : valid pointer to the listview structure
4314  * [I] nColumn : column index
4315  *
4316  * RETURN:
4317  *   SUCCESS : TRUE
4318  *   FAILURE : FALSE
4319  */
4320 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
4321 {
4322     RECT rcCol;
4323     
4324     TRACE("nColumn=%d\n", nColumn);
4325
4326     if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) == 0
4327            || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
4328
4329     /* While the MSDN specifically says that column zero should not be deleted,
4330        what actually happens is that the column itself is deleted but no items or subitems
4331        are removed.
4332      */
4333
4334     LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
4335     
4336     if (!Header_DeleteItem(infoPtr->hwndHeader, nColumn))
4337         return FALSE;
4338
4339     Free(DPA_GetPtr(infoPtr->hdpaColumns, nColumn));
4340     DPA_DeletePtr(infoPtr->hdpaColumns, nColumn);
4341   
4342     if (!(infoPtr->dwStyle & LVS_OWNERDATA) && nColumn)
4343     {
4344         SUBITEM_INFO *lpSubItem, *lpDelItem;
4345         HDPA hdpaSubItems;
4346         INT nItem, nSubItem, i;
4347         
4348         for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
4349         {
4350             hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
4351             nSubItem = 0;
4352             lpDelItem = 0;
4353             for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
4354             {
4355                 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
4356                 if (lpSubItem->iSubItem == nColumn)
4357                 {
4358                     nSubItem = i;
4359                     lpDelItem = lpSubItem;
4360                 }
4361                 else if (lpSubItem->iSubItem > nColumn) 
4362                 {
4363                     lpSubItem->iSubItem--;
4364                 }
4365             }
4366
4367             /* if we found our subitem, zapp it */      
4368             if (nSubItem > 0)
4369             {
4370                 /* free string */
4371                 if (is_textW(lpDelItem->hdr.pszText))
4372                     Free(lpDelItem->hdr.pszText);
4373
4374                 /* free item */
4375                 Free(lpDelItem);
4376
4377                 /* free dpa memory */
4378                 DPA_DeletePtr(hdpaSubItems, nSubItem);
4379             }
4380         }
4381     }
4382
4383     /* update the other column info */
4384     if(DPA_GetPtrCount(infoPtr->hdpaColumns) == 0)
4385         LISTVIEW_InvalidateList(infoPtr);
4386     else
4387         LISTVIEW_ScrollColumns(infoPtr, nColumn, -(rcCol.right - rcCol.left));
4388
4389     return TRUE;
4390 }
4391
4392 /***
4393  * DESCRIPTION:
4394  * Invalidates the listview after an item's insertion or deletion.
4395  *
4396  * PARAMETER(S):
4397  * [I] infoPtr : valid pointer to the listview structure
4398  * [I] nItem : item index
4399  * [I] dir : -1 if deleting, 1 if inserting
4400  *
4401  * RETURN:
4402  *   None
4403  */
4404 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO *infoPtr, INT nItem, INT dir)
4405 {
4406     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4407     INT nPerCol, nItemCol, nItemRow;
4408     RECT rcScroll;
4409     POINT Origin;
4410
4411     /* if we don't refresh, what's the point of scrolling? */
4412     if (!is_redrawing(infoPtr)) return;
4413     
4414     assert (abs(dir) == 1);
4415
4416     /* arrange icons if autoarrange is on */
4417     if (is_autoarrange(infoPtr))
4418     {
4419         BOOL arrange = TRUE;
4420         if (dir < 0 && nItem >= infoPtr->nItemCount) arrange = FALSE;
4421         if (dir > 0 && nItem == infoPtr->nItemCount - 1) arrange = FALSE;
4422         if (arrange) LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
4423     }
4424
4425     /* scrollbars need updating */
4426     LISTVIEW_UpdateScroll(infoPtr);
4427
4428     /* figure out the item's position */ 
4429     if (uView == LVS_REPORT)
4430         nPerCol = infoPtr->nItemCount + 1;
4431     else if (uView == LVS_LIST)
4432         nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
4433     else /* LVS_ICON, or LVS_SMALLICON */
4434         return;
4435     
4436     nItemCol = nItem / nPerCol;
4437     nItemRow = nItem % nPerCol;
4438     LISTVIEW_GetOrigin(infoPtr, &Origin);
4439
4440     /* move the items below up a slot */
4441     rcScroll.left = nItemCol * infoPtr->nItemWidth;
4442     rcScroll.top = nItemRow * infoPtr->nItemHeight;
4443     rcScroll.right = rcScroll.left + infoPtr->nItemWidth;
4444     rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4445     OffsetRect(&rcScroll, Origin.x, Origin.y);
4446     TRACE("rcScroll=%s, dx=%d\n", debugrect(&rcScroll), dir * infoPtr->nItemHeight);
4447     if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4448     {
4449         TRACE("Scrolling rcScroll=%s, rcList=%s\n", debugrect(&rcScroll), debugrect(&infoPtr->rcList));
4450         ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight, 
4451                        &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4452     }
4453
4454     /* report has only that column, so we're done */
4455     if (uView == LVS_REPORT) return;
4456
4457     /* now for LISTs, we have to deal with the columns to the right */
4458     rcScroll.left = (nItemCol + 1) * infoPtr->nItemWidth;
4459     rcScroll.top = 0;
4460     rcScroll.right = (infoPtr->nItemCount / nPerCol + 1) * infoPtr->nItemWidth;
4461     rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4462     OffsetRect(&rcScroll, Origin.x, Origin.y);
4463     if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4464         ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4465                        &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4466 }
4467
4468 /***
4469  * DESCRIPTION:
4470  * Removes an item from the listview control.
4471  *
4472  * PARAMETER(S):
4473  * [I] infoPtr : valid pointer to the listview structure
4474  * [I] nItem : item index
4475  *
4476  * RETURN:
4477  *   SUCCESS : TRUE
4478  *   FAILURE : FALSE
4479  */
4480 static BOOL LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
4481 {
4482     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4483     LVITEMW item;
4484
4485     TRACE("(nItem=%d)\n", nItem);
4486
4487     if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
4488     
4489     /* remove selection, and focus */
4490     item.state = 0;
4491     item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
4492     LISTVIEW_SetItemState(infoPtr, nItem, &item);
4493             
4494     /* send LVN_DELETEITEM notification. */
4495     notify_deleteitem(infoPtr, nItem);
4496
4497     /* we need to do this here, because we'll be deleting stuff */  
4498     if (uView == LVS_SMALLICON || uView == LVS_ICON)
4499         LISTVIEW_InvalidateItem(infoPtr, nItem);
4500     
4501     if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4502     {
4503         HDPA hdpaSubItems;
4504         ITEMHDR *hdrItem;
4505         INT i;
4506
4507         hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);  
4508         for (i = 0; i < DPA_GetPtrCount(hdpaSubItems); i++)
4509         {
4510             hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, i);
4511             if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4512             Free(hdrItem);
4513         }
4514         DPA_Destroy(hdpaSubItems);
4515     }
4516
4517     if (uView == LVS_SMALLICON || uView == LVS_ICON)
4518     {
4519         DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
4520         DPA_DeletePtr(infoPtr->hdpaPosY, nItem);
4521     }
4522
4523     infoPtr->nItemCount--;
4524     LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
4525
4526     /* now is the invalidation fun */
4527     LISTVIEW_ScrollOnInsert(infoPtr, nItem, -1);
4528     return TRUE;
4529 }
4530
4531
4532 /***
4533  * DESCRIPTION:
4534  * Callback implementation for editlabel control
4535  *
4536  * PARAMETER(S):
4537  * [I] infoPtr : valid pointer to the listview structure
4538  * [I] pszText : modified text
4539  * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4540  *
4541  * RETURN:
4542  *   SUCCESS : TRUE
4543  *   FAILURE : FALSE
4544  */
4545 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, LPWSTR pszText, BOOL isW)
4546 {
4547     NMLVDISPINFOW dispInfo;
4548
4549     TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
4550
4551     ZeroMemory(&dispInfo, sizeof(dispInfo));
4552     dispInfo.item.mask = LVIF_PARAM | LVIF_STATE;
4553     dispInfo.item.iItem = infoPtr->nEditLabelItem;
4554     dispInfo.item.iSubItem = 0;
4555     dispInfo.item.stateMask = ~0;
4556     if (!LISTVIEW_GetItemW(infoPtr, &dispInfo.item)) return FALSE;
4557     /* add the text from the edit in */
4558     dispInfo.item.mask |= LVIF_TEXT;
4559     dispInfo.item.pszText = pszText;
4560     dispInfo.item.cchTextMax = textlenT(pszText, isW);
4561
4562     /* Do we need to update the Item Text */
4563     if (!notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW)) return FALSE;
4564     if (!pszText) return TRUE;
4565
4566     if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4567     {
4568         HDPA hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nEditLabelItem);
4569         ITEM_INFO* lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
4570         if (lpItem && lpItem->hdr.pszText == LPSTR_TEXTCALLBACKW)
4571         {
4572             LISTVIEW_InvalidateItem(infoPtr, infoPtr->nEditLabelItem);
4573             return TRUE;
4574         }
4575     }
4576
4577     ZeroMemory(&dispInfo, sizeof(dispInfo));
4578     dispInfo.item.mask = LVIF_TEXT;
4579     dispInfo.item.iItem = infoPtr->nEditLabelItem;
4580     dispInfo.item.iSubItem = 0;
4581     dispInfo.item.pszText = pszText;
4582     dispInfo.item.cchTextMax = textlenT(pszText, isW);
4583     return LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
4584 }
4585
4586 /***
4587  * DESCRIPTION:
4588  * Begin in place editing of specified list view item
4589  *
4590  * PARAMETER(S):
4591  * [I] infoPtr : valid pointer to the listview structure
4592  * [I] nItem : item index
4593  * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4594  *
4595  * RETURN:
4596  *   SUCCESS : TRUE
4597  *   FAILURE : FALSE
4598  */
4599 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
4600 {
4601     WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
4602     NMLVDISPINFOW dispInfo;
4603     RECT rect;
4604
4605     TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
4606
4607     if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
4608     if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
4609
4610     infoPtr->nEditLabelItem = nItem;
4611
4612     /* Is the EditBox still there, if so remove it */
4613     if(infoPtr->hwndEdit != 0)
4614     {
4615         SetFocus(infoPtr->hwndSelf);
4616         infoPtr->hwndEdit = 0;
4617     }
4618
4619     LISTVIEW_SetSelection(infoPtr, nItem);
4620     LISTVIEW_SetItemFocus(infoPtr, nItem);
4621     LISTVIEW_InvalidateItem(infoPtr, nItem);
4622
4623     rect.left = LVIR_LABEL;
4624     if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
4625     
4626     ZeroMemory(&dispInfo, sizeof(dispInfo));
4627     dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
4628     dispInfo.item.iItem = nItem;
4629     dispInfo.item.iSubItem = 0;
4630     dispInfo.item.stateMask = ~0;
4631     dispInfo.item.pszText = szDispText;
4632     dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
4633     if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
4634
4635     infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, WS_VISIBLE,
4636                     rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, isW);
4637     if (!infoPtr->hwndEdit) return 0;
4638     
4639     if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
4640     {
4641         SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
4642         infoPtr->hwndEdit = 0;
4643         return 0;
4644     }
4645
4646     ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
4647     SetFocus(infoPtr->hwndEdit);
4648     SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
4649     return infoPtr->hwndEdit;
4650 }
4651
4652
4653 /***
4654  * DESCRIPTION:
4655  * Ensures the specified item is visible, scrolling into view if necessary.
4656  *
4657  * PARAMETER(S):
4658  * [I] infoPtr : valid pointer to the listview structure
4659  * [I] nItem : item index
4660  * [I] bPartial : partially or entirely visible
4661  *
4662  * RETURN:
4663  *   SUCCESS : TRUE
4664  *   FAILURE : FALSE
4665  */
4666 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
4667 {
4668     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4669     INT nScrollPosHeight = 0;
4670     INT nScrollPosWidth = 0;
4671     INT nHorzAdjust = 0;
4672     INT nVertAdjust = 0;
4673     INT nHorzDiff = 0;
4674     INT nVertDiff = 0;
4675     RECT rcItem, rcTemp;
4676
4677     rcItem.left = LVIR_BOUNDS;
4678     if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
4679
4680     if (bPartial && IntersectRect(&rcTemp, &infoPtr->rcList, &rcItem)) return TRUE;
4681     
4682     if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
4683     {
4684         /* scroll left/right, but in LVS_REPORT mode */
4685         if (uView == LVS_LIST)
4686             nScrollPosWidth = infoPtr->nItemWidth;
4687         else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4688             nScrollPosWidth = 1;
4689
4690         if (rcItem.left < infoPtr->rcList.left)
4691         {
4692             nHorzAdjust = -1;
4693             if (uView != LVS_REPORT) nHorzDiff = rcItem.left - infoPtr->rcList.left;
4694         }
4695         else
4696         {
4697             nHorzAdjust = 1;
4698             if (uView != LVS_REPORT) nHorzDiff = rcItem.right - infoPtr->rcList.right;
4699         }
4700     }
4701
4702     if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
4703     {
4704         /* scroll up/down, but not in LVS_LIST mode */
4705         if (uView == LVS_REPORT)
4706             nScrollPosHeight = infoPtr->nItemHeight;
4707         else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4708             nScrollPosHeight = 1;
4709
4710         if (rcItem.top < infoPtr->rcList.top)
4711         {
4712             nVertAdjust = -1;
4713             if (uView != LVS_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top;
4714         }
4715         else
4716         {
4717             nVertAdjust = 1;
4718             if (uView != LVS_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
4719         }
4720     }
4721
4722     if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
4723
4724     if (nScrollPosWidth)
4725     {
4726         INT diff = nHorzDiff / nScrollPosWidth;
4727         if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust;
4728         LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff, 0);
4729     }
4730
4731     if (nScrollPosHeight)
4732     {
4733         INT diff = nVertDiff / nScrollPosHeight;
4734         if (nVertDiff % nScrollPosHeight) diff += nVertAdjust;
4735         LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff, 0);
4736     }
4737
4738     return TRUE;
4739 }
4740
4741 /***
4742  * DESCRIPTION:
4743  * Searches for an item with specific characteristics.
4744  *
4745  * PARAMETER(S):
4746  * [I] hwnd : window handle
4747  * [I] nStart : base item index
4748  * [I] lpFindInfo : item information to look for
4749  *
4750  * RETURN:
4751  *   SUCCESS : index of item
4752  *   FAILURE : -1
4753  */
4754 static INT LISTVIEW_FindItemW(LISTVIEW_INFO *infoPtr, INT nStart,
4755                               const LVFINDINFOW *lpFindInfo)
4756 {
4757     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4758     WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4759     BOOL bWrap = FALSE, bNearest = FALSE;
4760     INT nItem = nStart + 1, nLast = infoPtr->nItemCount, nNearestItem = -1;
4761     ULONG xdist, ydist, dist, mindist = 0x7fffffff;
4762     POINT Position, Destination;
4763     LVITEMW lvItem;
4764
4765     if (!lpFindInfo || nItem < 0) return -1;
4766     
4767     lvItem.mask = 0;
4768     if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
4769     {
4770         lvItem.mask |= LVIF_TEXT;
4771         lvItem.pszText = szDispText;
4772         lvItem.cchTextMax = DISP_TEXT_SIZE;
4773     }
4774
4775     if (lpFindInfo->flags & LVFI_WRAP)
4776         bWrap = TRUE;
4777
4778     if ((lpFindInfo->flags & LVFI_NEARESTXY) && 
4779         (uView == LVS_ICON || uView ==LVS_SMALLICON))
4780     {
4781         POINT Origin;
4782         RECT rcArea;
4783         
4784         LISTVIEW_GetOrigin(infoPtr, &Origin);
4785         Destination.x = lpFindInfo->pt.x - Origin.x;
4786         Destination.y = lpFindInfo->pt.y - Origin.y;
4787         switch(lpFindInfo->vkDirection)
4788         {
4789         case VK_DOWN:  Destination.y += infoPtr->nItemHeight; break;
4790         case VK_UP:    Destination.y -= infoPtr->nItemHeight; break;
4791         case VK_RIGHT: Destination.x += infoPtr->nItemWidth; break;
4792         case VK_LEFT:  Destination.x -= infoPtr->nItemWidth; break;
4793         case VK_HOME:  Destination.x = Destination.y = 0; break;
4794         case VK_NEXT:  Destination.y += infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4795         case VK_PRIOR: Destination.y -= infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4796         case VK_END:
4797             LISTVIEW_GetAreaRect(infoPtr, &rcArea);
4798             Destination.x = rcArea.right; 
4799             Destination.y = rcArea.bottom; 
4800             break;
4801         default: ERR("Unknown vkDirection=%d\n", lpFindInfo->vkDirection);
4802         }
4803         bNearest = TRUE;
4804     }
4805
4806     /* if LVFI_PARAM is specified, all other flags are ignored */
4807     if (lpFindInfo->flags & LVFI_PARAM)
4808     {
4809         lvItem.mask |= LVIF_PARAM;
4810         bNearest = FALSE;
4811         lvItem.mask &= ~LVIF_TEXT;
4812     }
4813
4814 again:
4815     for (; nItem < nLast; nItem++)
4816     {
4817         lvItem.iItem = nItem;
4818         lvItem.iSubItem = 0;
4819         if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
4820
4821         if (lvItem.mask & LVIF_PARAM)
4822         {
4823             if (lpFindInfo->lParam == lvItem.lParam)
4824                 return nItem;
4825             else
4826                 continue;
4827         }
4828         
4829         if (lvItem.mask & LVIF_TEXT)
4830         {
4831             if (lpFindInfo->flags & LVFI_PARTIAL)
4832             {
4833                 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL) continue;
4834             }
4835             else
4836             {
4837                 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0) continue;
4838             }
4839         }
4840
4841         if (!bNearest) return nItem;
4842         
4843         /* This is very inefficient. To do a good job here,
4844          * we need a sorted array of (x,y) item positions */
4845         LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
4846
4847         /* compute the distance^2 to the destination */
4848         xdist = Destination.x - Position.x;
4849         ydist = Destination.y - Position.y;
4850         dist = xdist * xdist + ydist * ydist;
4851
4852         /* remember the distance, and item if it's closer */
4853         if (dist < mindist)
4854         {
4855             mindist = dist;
4856             nNearestItem = nItem;
4857         }
4858     }
4859
4860     if (bWrap)
4861     {
4862         nItem = 0;
4863         nLast = min(nStart + 1, infoPtr->nItemCount);
4864         bWrap = FALSE;
4865         goto again;
4866     }
4867
4868     return nNearestItem;
4869 }
4870
4871 /***
4872  * DESCRIPTION:
4873  * Searches for an item with specific characteristics.
4874  *
4875  * PARAMETER(S):
4876  * [I] hwnd : window handle
4877  * [I] nStart : base item index
4878  * [I] lpFindInfo : item information to look for
4879  *
4880  * RETURN:
4881  *   SUCCESS : index of item
4882  *   FAILURE : -1
4883  */
4884 static INT LISTVIEW_FindItemA(LISTVIEW_INFO *infoPtr, INT nStart,
4885                               const LVFINDINFOA *lpFindInfo)
4886 {
4887     BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
4888     LVFINDINFOW fiw;
4889     INT res;
4890
4891     memcpy(&fiw, lpFindInfo, sizeof(fiw));
4892     if (hasText) fiw.psz = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
4893     res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
4894     if (hasText) textfreeT((LPWSTR)fiw.psz, FALSE);
4895     return res;
4896 }
4897
4898 /***
4899  * DESCRIPTION:
4900  * Retrieves the background image of the listview control.
4901  *
4902  * PARAMETER(S):
4903  * [I] infoPtr : valid pointer to the listview structure
4904  * [O] lpBkImage : background image attributes
4905  *
4906  * RETURN:
4907  *   SUCCESS : TRUE
4908  *   FAILURE : FALSE
4909  */
4910 /* static BOOL LISTVIEW_GetBkImage(LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage)   */
4911 /* {   */
4912 /*   FIXME (listview, "empty stub!\n"); */
4913 /*   return FALSE;   */
4914 /* }   */
4915
4916 /***
4917  * DESCRIPTION:
4918  * Retrieves column attributes.
4919  *
4920  * PARAMETER(S):
4921  * [I] infoPtr : valid pointer to the listview structure
4922  * [I] nColumn :  column index
4923  * [IO] lpColumn : column information
4924  * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
4925  *           otherwise it is in fact a LPLVCOLUMNA
4926  *
4927  * RETURN:
4928  *   SUCCESS : TRUE
4929  *   FAILURE : FALSE
4930  */
4931 static BOOL LISTVIEW_GetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn, LPLVCOLUMNW lpColumn, BOOL isW)
4932 {
4933     COLUMN_INFO *lpColumnInfo;
4934     HDITEMW hdi;
4935
4936     if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
4937     lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
4938
4939     /* initialize memory */
4940     ZeroMemory(&hdi, sizeof(hdi));
4941
4942     if (lpColumn->mask & LVCF_TEXT)
4943     {
4944         hdi.mask |= HDI_TEXT;
4945         hdi.pszText = lpColumn->pszText;
4946         hdi.cchTextMax = lpColumn->cchTextMax;
4947     }
4948
4949     if (lpColumn->mask & LVCF_IMAGE)
4950         hdi.mask |= HDI_IMAGE;
4951
4952     if (lpColumn->mask & LVCF_ORDER)
4953         hdi.mask |= HDI_ORDER;
4954
4955     if (!SendMessageW(infoPtr->hwndHeader, isW ? HDM_GETITEMW : HDM_GETITEMA, nColumn, (LPARAM)&hdi)) return FALSE;
4956
4957     if (lpColumn->mask & LVCF_FMT)
4958         lpColumn->fmt = lpColumnInfo->fmt;
4959
4960     if (lpColumn->mask & LVCF_WIDTH)
4961         lpColumn->cx = lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left;
4962
4963     if (lpColumn->mask & LVCF_IMAGE)
4964         lpColumn->iImage = hdi.iImage;
4965
4966     if (lpColumn->mask & LVCF_ORDER)
4967         lpColumn->iOrder = hdi.iOrder;
4968
4969     return TRUE;
4970 }
4971
4972
4973 static BOOL LISTVIEW_GetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
4974 {
4975     INT i;
4976
4977     if (!lpiArray)
4978         return FALSE;
4979
4980     /* FIXME: little hack */
4981     for (i = 0; i < iCount; i++)
4982         lpiArray[i] = i;
4983
4984     return TRUE;
4985 }
4986
4987 /***
4988  * DESCRIPTION:
4989  * Retrieves the column width.
4990  *
4991  * PARAMETER(S):
4992  * [I] infoPtr : valid pointer to the listview structure
4993  * [I] int : column index
4994  *
4995  * RETURN:
4996  *   SUCCESS : column width
4997  *   FAILURE : zero
4998  */
4999 static INT LISTVIEW_GetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn)
5000 {
5001     INT nColumnWidth = 0;
5002     RECT rcHeader;
5003
5004     TRACE("nColumn=%d\n", nColumn);
5005
5006     /* we have a 'column' in LIST and REPORT mode only */
5007     switch(infoPtr->dwStyle & LVS_TYPEMASK)
5008     {
5009     case LVS_LIST:
5010         nColumnWidth = infoPtr->nItemWidth;
5011         break;
5012     case LVS_REPORT:
5013         if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
5014         LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
5015         nColumnWidth = rcHeader.right - rcHeader.left;
5016         break;
5017     }
5018
5019     TRACE("nColumnWidth=%d\n", nColumnWidth);
5020     return nColumnWidth;
5021 }
5022
5023 /***
5024  * DESCRIPTION:
5025  * In list or report display mode, retrieves the number of items that can fit
5026  * vertically in the visible area. In icon or small icon display mode,
5027  * retrieves the total number of visible items.
5028  *
5029  * PARAMETER(S):
5030  * [I] infoPtr : valid pointer to the listview structure
5031  *
5032  * RETURN:
5033  * Number of fully visible items.
5034  */
5035 static INT LISTVIEW_GetCountPerPage(LISTVIEW_INFO *infoPtr)
5036 {
5037     switch (infoPtr->dwStyle & LVS_TYPEMASK)
5038     {
5039     case LVS_ICON:
5040     case LVS_SMALLICON:
5041         return infoPtr->nItemCount;
5042     case LVS_REPORT:
5043         return LISTVIEW_GetCountPerColumn(infoPtr);
5044     case LVS_LIST:
5045         return LISTVIEW_GetCountPerRow(infoPtr) * LISTVIEW_GetCountPerColumn(infoPtr);
5046     }
5047     assert(FALSE);
5048     return 0;
5049 }
5050
5051 /***
5052  * DESCRIPTION:
5053  * Retrieves an image list handle.
5054  *
5055  * PARAMETER(S):
5056  * [I] infoPtr : valid pointer to the listview structure
5057  * [I] nImageList : image list identifier
5058  *
5059  * RETURN:
5060  *   SUCCESS : image list handle
5061  *   FAILURE : NULL
5062  */
5063 static HIMAGELIST LISTVIEW_GetImageList(LISTVIEW_INFO *infoPtr, INT nImageList)
5064 {
5065     switch (nImageList)
5066     {
5067     case LVSIL_NORMAL: return infoPtr->himlNormal;
5068     case LVSIL_SMALL: return infoPtr->himlSmall;
5069     case LVSIL_STATE: return infoPtr->himlState;
5070     }
5071     return NULL;
5072 }
5073
5074 /* LISTVIEW_GetISearchString */
5075
5076 /***
5077  * DESCRIPTION:
5078  * Retrieves item attributes.
5079  *
5080  * PARAMETER(S):
5081  * [I] hwnd : window handle
5082  * [IO] lpLVItem : item info
5083  * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5084  *           if FALSE, the lpLVItem is a LPLVITEMA.
5085  *
5086  * NOTE:
5087  *   This is the internal 'GetItem' interface -- it tries to
5088  *   be smart, and avoids text copies, if possible, by modifing
5089  *   lpLVItem->pszText to point to the text string. Please note
5090  *   that this is not always possible (e.g. OWNERDATA), so on
5091  *   entry you *must* supply valid values for pszText, and cchTextMax.
5092  *   The only difference to the documented interface is that upon
5093  *   return, you should use *only* the lpLVItem->pszText, rather than
5094  *   the buffer pointer you provided on input. Most code already does
5095  *   that, so it's not a problem.
5096  *   For the two cases when the text must be copied (that is,
5097  *   for LVM_GETITEM, and LVM_GETITEMTEXT), use LISTVIEW_GetItemExtT.
5098  *
5099  * RETURN:
5100  *   SUCCESS : TRUE
5101  *   FAILURE : FALSE
5102  */
5103 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5104 {
5105     ITEMHDR callbackHdr = { LPSTR_TEXTCALLBACKW, I_IMAGECALLBACK };
5106     NMLVDISPINFOW dispInfo;
5107     ITEM_INFO *lpItem;
5108     ITEMHDR* pItemHdr;
5109     HDPA hdpaSubItems;
5110     INT isubitem;
5111
5112     TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
5113
5114     if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5115         return FALSE;
5116
5117     if (lpLVItem->mask == 0) return TRUE;
5118
5119     /* make a local copy */
5120     isubitem = lpLVItem->iSubItem;
5121
5122     /* a quick optimization if all we're asked is the focus state
5123      * these queries are worth optimising since they are common,
5124      * and can be answered in constant time, without the heavy accesses */
5125     if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIS_FOCUSED) &&
5126          !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
5127     {
5128         lpLVItem->state = 0;
5129         if (infoPtr->nFocusedItem == lpLVItem->iItem)
5130             lpLVItem->state |= LVIS_FOCUSED;
5131         return TRUE;
5132     }
5133
5134     ZeroMemory(&dispInfo, sizeof(dispInfo));
5135
5136     /* if the app stores all the data, handle it separately */
5137     if (infoPtr->dwStyle & LVS_OWNERDATA)
5138     {
5139         dispInfo.item.state = 0;
5140
5141         /* apprently, we should not callback for lParam in LVS_OWNERDATA */
5142         if ((lpLVItem->mask & ~(LVIF_STATE | LVIF_PARAM)) || infoPtr->uCallbackMask)
5143         {
5144             /* NOTE: copy only fields which we _know_ are initialized, some apps
5145              *       depend on the uninitialized fields being 0 */
5146             dispInfo.item.mask = lpLVItem->mask & ~LVIF_PARAM;
5147             dispInfo.item.iItem = lpLVItem->iItem;
5148             dispInfo.item.iSubItem = isubitem;
5149             if (lpLVItem->mask & LVIF_TEXT)
5150             {
5151                 dispInfo.item.pszText = lpLVItem->pszText;
5152                 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;                
5153             }
5154             if (lpLVItem->mask & LVIF_STATE)
5155                 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
5156             notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5157             dispInfo.item.stateMask = lpLVItem->stateMask;
5158             if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
5159             {
5160                 /* full size structure expected - _WIN32IE >= 0x560 */
5161                 *lpLVItem = dispInfo.item;
5162             }
5163             else if (lpLVItem->mask & LVIF_INDENT)
5164             {
5165                 /* indent member expected - _WIN32IE >= 0x300 */
5166                 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iGroupId ));
5167             }
5168             else
5169             {
5170                 /* minimal structure expected */
5171                 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iIndent ));
5172             }
5173             TRACE("   getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
5174         }
5175         
5176         /* make sure lParam is zeroed out */
5177         if (lpLVItem->mask & LVIF_PARAM) lpLVItem->lParam = 0;
5178         
5179         /* we store only a little state, so if we're not asked, we're done */
5180         if (!(lpLVItem->mask & LVIF_STATE) || isubitem) return TRUE;
5181
5182         /* if focus is handled by us, report it */
5183         if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED ) 
5184         {
5185             lpLVItem->state &= ~LVIS_FOCUSED;
5186             if (infoPtr->nFocusedItem == lpLVItem->iItem)
5187                 lpLVItem->state |= LVIS_FOCUSED;
5188         }
5189
5190         /* and do the same for selection, if we handle it */
5191         if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED ) 
5192         {
5193             lpLVItem->state &= ~LVIS_SELECTED;
5194             if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5195                 lpLVItem->state |= LVIS_SELECTED;
5196         }
5197         
5198         return TRUE;
5199     }
5200
5201     /* find the item and subitem structures before we proceed */
5202     hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
5203     lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
5204     assert (lpItem);
5205
5206     if (isubitem)
5207     {
5208         SUBITEM_INFO *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, isubitem);
5209         pItemHdr = lpSubItem ? &lpSubItem->hdr : &callbackHdr;
5210         if (!lpSubItem)
5211         {
5212             WARN(" iSubItem invalid (%08x), ignored.\n", isubitem);
5213             isubitem = 0;
5214         }
5215     }
5216     else
5217         pItemHdr = &lpItem->hdr;
5218
5219     /* Do we need to query the state from the app? */
5220     if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && isubitem == 0)
5221     {
5222         dispInfo.item.mask |= LVIF_STATE;
5223         dispInfo.item.stateMask = infoPtr->uCallbackMask;
5224     }
5225   
5226     /* Do we need to enquire about the image? */
5227     if ((lpLVItem->mask & LVIF_IMAGE) && pItemHdr->iImage == I_IMAGECALLBACK &&
5228         (isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES)))
5229     {
5230         dispInfo.item.mask |= LVIF_IMAGE;
5231         dispInfo.item.iImage = I_IMAGECALLBACK;
5232     }
5233
5234     /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
5235     if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(pItemHdr->pszText))
5236     {
5237         dispInfo.item.mask |= LVIF_TEXT;
5238         dispInfo.item.pszText = lpLVItem->pszText;
5239         dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5240         if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
5241             *dispInfo.item.pszText = '\0';
5242     }
5243
5244     /* If we don't have all the requested info, query the application */
5245     if (dispInfo.item.mask != 0)
5246     {
5247         dispInfo.item.iItem = lpLVItem->iItem;
5248         dispInfo.item.iSubItem = lpLVItem->iSubItem; /* yes: the original subitem */
5249         dispInfo.item.lParam = lpItem->lParam;
5250         notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5251         TRACE("   getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
5252     }
5253
5254     /* we should not store values for subitems */
5255     if (isubitem) dispInfo.item.mask &= ~LVIF_DI_SETITEM;
5256
5257     /* Now, handle the iImage field */
5258     if (dispInfo.item.mask & LVIF_IMAGE)
5259     {
5260         lpLVItem->iImage = dispInfo.item.iImage;
5261         if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->iImage == I_IMAGECALLBACK)
5262             pItemHdr->iImage = dispInfo.item.iImage;
5263     }
5264     else if (lpLVItem->mask & LVIF_IMAGE)
5265     {
5266         if(isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES))
5267             lpLVItem->iImage = pItemHdr->iImage;
5268         else
5269             lpLVItem->iImage = 0;
5270     }
5271
5272     /* The pszText field */
5273     if (dispInfo.item.mask & LVIF_TEXT)
5274     {
5275         if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
5276             textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
5277
5278         lpLVItem->pszText = dispInfo.item.pszText;
5279     }
5280     else if (lpLVItem->mask & LVIF_TEXT)
5281     {
5282         if (isW) lpLVItem->pszText = pItemHdr->pszText;
5283         else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
5284     }
5285
5286     /* if this is a subitem, we're done */
5287     if (isubitem) return TRUE;
5288   
5289     /* Next is the lParam field */
5290     if (dispInfo.item.mask & LVIF_PARAM)
5291     {
5292         lpLVItem->lParam = dispInfo.item.lParam;
5293         if ((dispInfo.item.mask & LVIF_DI_SETITEM))
5294             lpItem->lParam = dispInfo.item.lParam;
5295     }
5296     else if (lpLVItem->mask & LVIF_PARAM)
5297         lpLVItem->lParam = lpItem->lParam;
5298
5299     /* ... the state field (this one is different due to uCallbackmask) */
5300     if (lpLVItem->mask & LVIF_STATE) 
5301     {
5302         lpLVItem->state = lpItem->state;
5303         if (dispInfo.item.mask & LVIF_STATE)
5304         {
5305             lpLVItem->state &= ~dispInfo.item.stateMask;
5306             lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
5307         }
5308         if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED ) 
5309         {
5310             lpLVItem->state &= ~LVIS_FOCUSED;
5311             if (infoPtr->nFocusedItem == lpLVItem->iItem)
5312                 lpLVItem->state |= LVIS_FOCUSED;
5313         }
5314         if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED ) 
5315         {
5316             lpLVItem->state &= ~LVIS_SELECTED;
5317             if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5318                 lpLVItem->state |= LVIS_SELECTED;
5319         }           
5320     }
5321
5322     /* and last, but not least, the indent field */
5323     if (lpLVItem->mask & LVIF_INDENT)
5324         lpLVItem->iIndent = lpItem->iIndent;
5325
5326     return TRUE;
5327 }
5328
5329 /***
5330  * DESCRIPTION:
5331  * Retrieves item attributes.
5332  *
5333  * PARAMETER(S):
5334  * [I] hwnd : window handle
5335  * [IO] lpLVItem : item info
5336  * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5337  *           if FALSE, the lpLVItem is a LPLVITEMA.
5338  *
5339  * NOTE:
5340  *   This is the external 'GetItem' interface -- it properly copies
5341  *   the text in the provided buffer.
5342  *
5343  * RETURN:
5344  *   SUCCESS : TRUE
5345  *   FAILURE : FALSE
5346  */
5347 static BOOL LISTVIEW_GetItemExtT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5348 {
5349     LPWSTR pszText;
5350     BOOL bResult;
5351
5352     if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5353         return FALSE;
5354
5355     pszText = lpLVItem->pszText;
5356     bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
5357     if (bResult && lpLVItem->pszText != pszText)
5358         textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
5359     lpLVItem->pszText = pszText;
5360
5361     return bResult;
5362 }
5363
5364
5365 /***
5366  * DESCRIPTION:
5367  * Retrieves the position (upper-left) of the listview control item.
5368  * Note that for LVS_ICON style, the upper-left is that of the icon
5369  * and not the bounding box.
5370  *
5371  * PARAMETER(S):
5372  * [I] infoPtr : valid pointer to the listview structure
5373  * [I] nItem : item index
5374  * [O] lpptPosition : coordinate information
5375  *
5376  * RETURN:
5377  *   SUCCESS : TRUE
5378  *   FAILURE : FALSE
5379  */
5380 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
5381 {
5382     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5383     POINT Origin;
5384
5385     TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
5386
5387     if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5388
5389     LISTVIEW_GetOrigin(infoPtr, &Origin);
5390     LISTVIEW_GetItemOrigin(infoPtr, nItem, lpptPosition);
5391
5392     if (uView == LVS_ICON)
5393     {
5394         lpptPosition->x += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
5395         lpptPosition->y += ICON_TOP_PADDING;
5396     }
5397     lpptPosition->x += Origin.x;
5398     lpptPosition->y += Origin.y;
5399     
5400     TRACE ("  lpptPosition=%s\n", debugpoint(lpptPosition));
5401     return TRUE;
5402 }
5403
5404
5405 /***
5406  * DESCRIPTION:
5407  * Retrieves the bounding rectangle for a listview control item.
5408  *
5409  * PARAMETER(S):
5410  * [I] infoPtr : valid pointer to the listview structure
5411  * [I] nItem : item index
5412  * [IO] lprc : bounding rectangle coordinates
5413  *     lprc->left specifies the portion of the item for which the bounding
5414  *     rectangle will be retrieved.
5415  *
5416  *     LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5417  *        including the icon and label.
5418  *         *
5419  *         * For LVS_ICON
5420  *         * Experiment shows that native control returns:
5421  *         *  width = min (48, length of text line)
5422  *         *    .left = position.x - (width - iconsize.cx)/2
5423  *         *    .right = .left + width
5424  *         *  height = #lines of text * ntmHeight + icon height + 8
5425  *         *    .top = position.y - 2
5426  *         *    .bottom = .top + height
5427  *         *  separation between items .y = itemSpacing.cy - height
5428  *         *                           .x = itemSpacing.cx - width
5429  *     LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5430  *         *
5431  *         * For LVS_ICON
5432  *         * Experiment shows that native control returns:
5433  *         *  width = iconSize.cx + 16
5434  *         *    .left = position.x - (width - iconsize.cx)/2
5435  *         *    .right = .left + width
5436  *         *  height = iconSize.cy + 4
5437  *         *    .top = position.y - 2
5438  *         *    .bottom = .top + height
5439  *         *  separation between items .y = itemSpacing.cy - height
5440  *         *                           .x = itemSpacing.cx - width
5441  *     LVIR_LABEL Returns the bounding rectangle of the item text.
5442  *         *
5443  *         * For LVS_ICON
5444  *         * Experiment shows that native control returns:
5445  *         *  width = text length
5446  *         *    .left = position.x - width/2
5447  *         *    .right = .left + width
5448  *         *  height = ntmH * linecount + 2
5449  *         *    .top = position.y + iconSize.cy + 6
5450  *         *    .bottom = .top + height
5451  *         *  separation between items .y = itemSpacing.cy - height
5452  *         *                           .x = itemSpacing.cx - width
5453  *     LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5454  *      rectangles, but excludes columns in report view.
5455  *
5456  * RETURN:
5457  *   SUCCESS : TRUE
5458  *   FAILURE : FALSE
5459  *
5460  * NOTES
5461  *   Note that the bounding rectangle of the label in the LVS_ICON view depends
5462  *   upon whether the window has the focus currently and on whether the item
5463  *   is the one with the focus.  Ensure that the control's record of which
5464  *   item has the focus agrees with the items' records.
5465  */
5466 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5467 {
5468     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5469     WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5470     BOOL doLabel = TRUE, oversizedBox = FALSE;
5471     POINT Position, Origin;
5472     LVITEMW lvItem;
5473     RECT label_rect;
5474
5475     TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
5476
5477     if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5478
5479     LISTVIEW_GetOrigin(infoPtr, &Origin);
5480     LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
5481
5482     /* Be smart and try to figure out the minimum we have to do */
5483     if (lprc->left == LVIR_ICON) doLabel = FALSE;
5484     if (uView == LVS_REPORT && lprc->left == LVIR_BOUNDS) doLabel = FALSE;
5485     if (uView == LVS_ICON && lprc->left != LVIR_ICON &&
5486         infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
5487         oversizedBox = TRUE;
5488
5489     /* get what we need from the item before hand, so we make
5490      * only one request. This can speed up things, if data
5491      * is stored on the app side */
5492     lvItem.mask = 0;
5493     if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5494     if (doLabel) lvItem.mask |= LVIF_TEXT;
5495     lvItem.iItem = nItem;
5496     lvItem.iSubItem = 0;
5497     lvItem.pszText = szDispText;
5498     lvItem.cchTextMax = DISP_TEXT_SIZE;
5499     if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5500     /* we got the state already up, simulate it here, to avoid a reget */
5501     if (uView == LVS_ICON && (lprc->left != LVIR_ICON))
5502     {
5503         lvItem.mask |= LVIF_STATE;
5504         lvItem.stateMask = LVIS_FOCUSED;
5505         lvItem.state = (oversizedBox ? LVIS_FOCUSED : 0);
5506     }
5507
5508     if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && lprc->left == LVIR_SELECTBOUNDS)
5509         lprc->left = LVIR_BOUNDS;
5510     switch(lprc->left)
5511     {
5512     case LVIR_ICON:
5513         LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL);
5514         break;
5515
5516     case LVIR_LABEL:
5517         LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, NULL, lprc);
5518         break;
5519
5520     case LVIR_BOUNDS:
5521         LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL);
5522         break;
5523
5524     case LVIR_SELECTBOUNDS:
5525         LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, &label_rect);
5526         UnionRect(lprc, lprc, &label_rect);
5527         break;
5528
5529     default:
5530         WARN("Unknown value: %ld\n", lprc->left);
5531         return FALSE;
5532     }
5533
5534     OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
5535
5536     TRACE(" rect=%s\n", debugrect(lprc));
5537
5538     return TRUE;
5539 }
5540
5541 /***
5542  * DESCRIPTION:
5543  * Retrieves the spacing between listview control items.
5544  *
5545  * PARAMETER(S):
5546  * [I] infoPtr : valid pointer to the listview structure
5547  * [IO] lprc : rectangle to receive the output
5548  *             on input, lprc->top = nSubItem
5549  *                       lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
5550  * 
5551  * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
5552  *       not only those of the first column.
5553  *       Fortunately, LISTVIEW_GetItemMetrics does the right thing.
5554  * 
5555  * RETURN:
5556  *     TRUE: success
5557  *     FALSE: failure
5558  */
5559 static BOOL LISTVIEW_GetSubItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5560 {
5561     POINT Position;
5562     LVITEMW lvItem;
5563     
5564     if (!lprc) return FALSE;
5565
5566     TRACE("(nItem=%d, nSubItem=%ld)\n", nItem, lprc->top);
5567     /* On WinNT, a subitem of '0' calls LISTVIEW_GetItemRect */
5568     if (lprc->top == 0)
5569         return LISTVIEW_GetItemRect(infoPtr, nItem, lprc);
5570
5571     if ((infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return FALSE;
5572
5573     if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &Position)) return FALSE;
5574
5575     lvItem.mask = 0;
5576     lvItem.iItem = nItem;
5577     lvItem.iSubItem = lprc->top;
5578     
5579     if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5580     switch(lprc->left)
5581     {
5582     case LVIR_ICON:
5583         LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL);
5584         break;
5585
5586     case LVIR_LABEL:
5587     case LVIR_BOUNDS:
5588         LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL);
5589         break;
5590
5591     default:
5592         ERR("Unknown bounds=%ld\n", lprc->left);
5593         return FALSE;
5594     }
5595
5596     OffsetRect(lprc, Position.x, Position.y);
5597     return TRUE;
5598 }
5599
5600
5601 /***
5602  * DESCRIPTION:
5603  * Retrieves the width of a label.
5604  *
5605  * PARAMETER(S):
5606  * [I] infoPtr : valid pointer to the listview structure
5607  *
5608  * RETURN:
5609  *   SUCCESS : string width (in pixels)
5610  *   FAILURE : zero
5611  */
5612 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *infoPtr, INT nItem)
5613 {
5614     WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5615     LVITEMW lvItem;
5616
5617     TRACE("(nItem=%d)\n", nItem);
5618
5619     lvItem.mask = LVIF_TEXT;
5620     lvItem.iItem = nItem;
5621     lvItem.iSubItem = 0;
5622     lvItem.pszText = szDispText;
5623     lvItem.cchTextMax = DISP_TEXT_SIZE;
5624     if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5625   
5626     return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
5627 }
5628
5629 /***
5630  * DESCRIPTION:
5631  * Retrieves the spacing between listview control items.
5632  *
5633  * PARAMETER(S):
5634  * [I] infoPtr : valid pointer to the listview structure
5635  * [I] bSmall : flag for small or large icon
5636  *
5637  * RETURN:
5638  * Horizontal + vertical spacing
5639  */
5640 static LONG LISTVIEW_GetItemSpacing(LISTVIEW_INFO *infoPtr, BOOL bSmall)
5641 {
5642   LONG lResult;
5643
5644   if (!bSmall)
5645   {
5646     lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
5647   }
5648   else
5649   {
5650     if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON)
5651       lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
5652     else
5653       lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
5654   }
5655   return lResult;
5656 }
5657
5658 /***
5659  * DESCRIPTION:
5660  * Retrieves the state of a listview control item.
5661  *
5662  * PARAMETER(S):
5663  * [I] infoPtr : valid pointer to the listview structure
5664  * [I] nItem : item index
5665  * [I] uMask : state mask
5666  *
5667  * RETURN:
5668  * State specified by the mask.
5669  */
5670 static UINT LISTVIEW_GetItemState(LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
5671 {
5672     LVITEMW lvItem;
5673
5674     if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5675
5676     lvItem.iItem = nItem;
5677     lvItem.iSubItem = 0;
5678     lvItem.mask = LVIF_STATE;
5679     lvItem.stateMask = uMask;
5680     if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5681
5682     return lvItem.state & uMask;
5683 }
5684
5685 /***
5686  * DESCRIPTION:
5687  * Retrieves the text of a listview control item or subitem.
5688  *
5689  * PARAMETER(S):
5690  * [I] hwnd : window handle
5691  * [I] nItem : item index
5692  * [IO] lpLVItem : item information
5693  * [I] isW :  TRUE if lpLVItem is Unicode
5694  *
5695  * RETURN:
5696  *   SUCCESS : string length
5697  *   FAILURE : 0
5698  */
5699 static INT LISTVIEW_GetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
5700 {
5701     if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5702
5703     lpLVItem->mask = LVIF_TEXT;
5704     lpLVItem->iItem = nItem;
5705     if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
5706
5707     return textlenT(lpLVItem->pszText, isW);
5708 }
5709
5710 /***
5711  * DESCRIPTION:
5712  * Searches for an item based on properties + relationships.
5713  *
5714  * PARAMETER(S):
5715  * [I] infoPtr : valid pointer to the listview structure
5716  * [I] nItem : item index
5717  * [I] uFlags : relationship flag
5718  *
5719  * RETURN:
5720  *   SUCCESS : item index
5721  *   FAILURE : -1
5722  */
5723 static INT LISTVIEW_GetNextItem(LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
5724 {
5725     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5726     UINT uMask = 0;
5727     LVFINDINFOW lvFindInfo;
5728     INT nCountPerColumn;
5729     INT nCountPerRow;
5730     INT i;
5731
5732     TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
5733     if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
5734
5735     ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
5736
5737     if (uFlags & LVNI_CUT)
5738       uMask |= LVIS_CUT;
5739
5740     if (uFlags & LVNI_DROPHILITED)
5741       uMask |= LVIS_DROPHILITED;
5742
5743     if (uFlags & LVNI_FOCUSED)
5744       uMask |= LVIS_FOCUSED;
5745
5746     if (uFlags & LVNI_SELECTED)
5747       uMask |= LVIS_SELECTED;
5748
5749     /* if we're asked for the focused item, that's only one, 
5750      * so it's worth optimizing */
5751     if (uFlags & LVNI_FOCUSED)
5752     {
5753         if ((LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) != uMask) return -1;
5754         return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
5755     }
5756     
5757     if (uFlags & LVNI_ABOVE)
5758     {
5759       if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5760       {
5761         while (nItem >= 0)
5762         {
5763           nItem--;
5764           if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5765             return nItem;
5766         }
5767       }
5768       else
5769       {
5770         /* Special case for autoarrange - move 'til the top of a list */
5771         if (is_autoarrange(infoPtr))
5772         {
5773           nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5774           while (nItem - nCountPerRow >= 0)
5775           {
5776             nItem -= nCountPerRow;
5777             if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5778               return nItem;
5779           }
5780           return -1;
5781         }
5782         lvFindInfo.flags = LVFI_NEARESTXY;
5783         lvFindInfo.vkDirection = VK_UP;
5784         ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5785         while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5786         {
5787           if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5788             return nItem;
5789         }
5790       }
5791     }
5792     else if (uFlags & LVNI_BELOW)
5793     {
5794       if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5795       {
5796         while (nItem < infoPtr->nItemCount)
5797         {
5798           nItem++;
5799           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5800             return nItem;
5801         }
5802       }
5803       else
5804       {
5805         /* Special case for autoarrange - move 'til the bottom of a list */
5806         if (is_autoarrange(infoPtr))
5807         {
5808           nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5809           while (nItem + nCountPerRow < infoPtr->nItemCount )
5810           {
5811             nItem += nCountPerRow;
5812             if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5813               return nItem;
5814           }
5815           return -1;
5816         }
5817         lvFindInfo.flags = LVFI_NEARESTXY;
5818         lvFindInfo.vkDirection = VK_DOWN;
5819         ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5820         while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5821         {
5822           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5823             return nItem;
5824         }
5825       }
5826     }
5827     else if (uFlags & LVNI_TOLEFT)
5828     {
5829       if (uView == LVS_LIST)
5830       {
5831         nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5832         while (nItem - nCountPerColumn >= 0)
5833         {
5834           nItem -= nCountPerColumn;
5835           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5836             return nItem;
5837         }
5838       }
5839       else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5840       {
5841         /* Special case for autoarrange - move 'ti the beginning of a row */
5842         if (is_autoarrange(infoPtr))
5843         {
5844           nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5845           while (nItem % nCountPerRow > 0)
5846           {
5847             nItem --;
5848             if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5849               return nItem;
5850           }
5851           return -1;
5852         }
5853         lvFindInfo.flags = LVFI_NEARESTXY;
5854         lvFindInfo.vkDirection = VK_LEFT;
5855         ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5856         while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5857         {
5858           if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5859             return nItem;
5860         }
5861       }
5862     }
5863     else if (uFlags & LVNI_TORIGHT)
5864     {
5865       if (uView == LVS_LIST)
5866       {
5867         nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5868         while (nItem + nCountPerColumn < infoPtr->nItemCount)
5869         {
5870           nItem += nCountPerColumn;
5871           if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5872             return nItem;
5873         }
5874       }
5875       else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5876       {
5877         /* Special case for autoarrange - move 'til the end of a row */
5878         if (is_autoarrange(infoPtr))
5879         {
5880           nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5881           while (nItem % nCountPerRow < nCountPerRow - 1 )
5882           {
5883             nItem ++;
5884             if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5885               return nItem;
5886           }
5887           return -1;
5888         }
5889         lvFindInfo.flags = LVFI_NEARESTXY;
5890         lvFindInfo.vkDirection = VK_RIGHT;
5891         ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5892         while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5893         {
5894           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5895             return nItem;
5896         }
5897       }
5898     }
5899     else
5900     {
5901       nItem++;
5902
5903       /* search by index */
5904       for (i = nItem; i < infoPtr->nItemCount; i++)
5905       {
5906         if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
5907           return i;
5908       }
5909     }
5910
5911     return -1;
5912 }
5913
5914 /* LISTVIEW_GetNumberOfWorkAreas */
5915
5916 /***
5917  * DESCRIPTION:
5918  * Retrieves the origin coordinates when in icon or small icon display mode.
5919  *
5920  * PARAMETER(S):
5921  * [I] infoPtr : valid pointer to the listview structure
5922  * [O] lpptOrigin : coordinate information
5923  *
5924  * RETURN:
5925  *   None.
5926  */
5927 static void LISTVIEW_GetOrigin(LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
5928 {
5929     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5930     INT nHorzPos = 0, nVertPos = 0;
5931     SCROLLINFO scrollInfo;
5932
5933     scrollInfo.cbSize = sizeof(SCROLLINFO);    
5934     scrollInfo.fMask = SIF_POS;
5935     
5936     if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
5937         nHorzPos = scrollInfo.nPos;
5938     if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
5939         nVertPos = scrollInfo.nPos;
5940
5941     TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
5942
5943     lpptOrigin->x = infoPtr->rcList.left;
5944     lpptOrigin->y = infoPtr->rcList.top;
5945     if (uView == LVS_LIST)
5946         nHorzPos *= infoPtr->nItemWidth;
5947     else if (uView == LVS_REPORT)
5948         nVertPos *= infoPtr->nItemHeight;
5949     
5950     lpptOrigin->x -= nHorzPos;
5951     lpptOrigin->y -= nVertPos;
5952
5953     TRACE(" origin=%s\n", debugpoint(lpptOrigin));
5954 }
5955
5956 /***
5957  * DESCRIPTION:
5958  * Retrieves the width of a string.
5959  *
5960  * PARAMETER(S):
5961  * [I] hwnd : window handle
5962  * [I] lpszText : text string to process
5963  * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
5964  *
5965  * RETURN:
5966  *   SUCCESS : string width (in pixels)
5967  *   FAILURE : zero
5968  */
5969 static INT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
5970 {
5971     SIZE stringSize;
5972     
5973     stringSize.cx = 0;    
5974     if (is_textT(lpszText, isW))
5975     {
5976         HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
5977         HDC hdc = GetDC(infoPtr->hwndSelf);
5978         HFONT hOldFont = SelectObject(hdc, hFont);
5979
5980         if (isW)
5981             GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
5982         else
5983             GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
5984         SelectObject(hdc, hOldFont);
5985         ReleaseDC(infoPtr->hwndSelf, hdc);
5986     }
5987     return stringSize.cx;
5988 }
5989
5990 /***
5991  * DESCRIPTION:
5992  * Determines which listview item is located at the specified position.
5993  *
5994  * PARAMETER(S):
5995  * [I] infoPtr : valid pointer to the listview structure
5996  * [IO] lpht : hit test information
5997  * [I] subitem : fill out iSubItem.
5998  * [I] select : return the index only if the hit selects the item
5999  *
6000  * NOTE:
6001  * (mm 20001022): We must not allow iSubItem to be touched, for
6002  * an app might pass only a structure with space up to iItem!
6003  * (MS Office 97 does that for instance in the file open dialog)
6004  * 
6005  * RETURN:
6006  *   SUCCESS : item index
6007  *   FAILURE : -1
6008  */
6009 static INT LISTVIEW_HitTest(LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select)
6010 {
6011     WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
6012     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6013     RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch;
6014     POINT Origin, Position, opt;
6015     LVITEMW lvItem;
6016     ITERATOR i;
6017     INT iItem;
6018     
6019     TRACE("(pt=%s, subitem=%d, select=%d)\n", debugpoint(&lpht->pt), subitem, select);
6020     
6021     lpht->flags = 0;
6022     lpht->iItem = -1;
6023     if (subitem) lpht->iSubItem = 0;
6024
6025     if (infoPtr->rcList.left > lpht->pt.x)
6026         lpht->flags |= LVHT_TOLEFT;
6027     else if (infoPtr->rcList.right < lpht->pt.x)
6028         lpht->flags |= LVHT_TORIGHT;
6029     
6030     if (infoPtr->rcList.top > lpht->pt.y)
6031         lpht->flags |= LVHT_ABOVE;
6032     else if (infoPtr->rcList.bottom < lpht->pt.y)
6033         lpht->flags |= LVHT_BELOW;
6034
6035     TRACE("lpht->flags=0x%x\n", lpht->flags);
6036     if (lpht->flags) return -1;
6037
6038     lpht->flags |= LVHT_NOWHERE;
6039
6040     LISTVIEW_GetOrigin(infoPtr, &Origin);
6041    
6042     /* first deal with the large items */
6043     rcSearch.left = lpht->pt.x;
6044     rcSearch.top = lpht->pt.y;
6045     rcSearch.right = rcSearch.left + 1;
6046     rcSearch.bottom = rcSearch.top + 1;
6047     
6048     iterator_frameditems(&i, infoPtr, &rcSearch);
6049     iterator_next(&i); /* go to first item in the sequence */
6050     iItem = i.nItem;
6051     iterator_destroy(&i);
6052    
6053     TRACE("lpht->iItem=%d\n", iItem); 
6054     if (iItem == -1) return -1;
6055
6056     lvItem.mask = LVIF_STATE | LVIF_TEXT;
6057     if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
6058     lvItem.stateMask = LVIS_STATEIMAGEMASK;
6059     if (uView == LVS_ICON) lvItem.stateMask |= LVIS_FOCUSED;
6060     lvItem.iItem = iItem;
6061     lvItem.iSubItem = 0;
6062     lvItem.pszText = szDispText;
6063     lvItem.cchTextMax = DISP_TEXT_SIZE;
6064     if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1;
6065     if (!infoPtr->bFocus) lvItem.state &= ~LVIS_FOCUSED; 
6066     
6067     LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel);
6068     LISTVIEW_GetItemOrigin(infoPtr, iItem, &Position);
6069     opt.x = lpht->pt.x - Position.x - Origin.x;
6070     opt.y = lpht->pt.y - Position.y - Origin.y;
6071     
6072     if (uView == LVS_REPORT)
6073         rcBounds = rcBox;
6074     else
6075         UnionRect(&rcBounds, &rcIcon, &rcLabel);
6076     TRACE("rcBounds=%s\n", debugrect(&rcBounds));
6077     if (!PtInRect(&rcBounds, opt)) return -1;
6078
6079     if (PtInRect(&rcIcon, opt))
6080         lpht->flags |= LVHT_ONITEMICON;
6081     else if (PtInRect(&rcLabel, opt))
6082         lpht->flags |= LVHT_ONITEMLABEL;
6083     else if (infoPtr->himlState && ((lvItem.state & LVIS_STATEIMAGEMASK) >> 12) && PtInRect(&rcState, opt))
6084         lpht->flags |= LVHT_ONITEMSTATEICON;
6085     if (lpht->flags & LVHT_ONITEM)
6086         lpht->flags &= ~LVHT_NOWHERE;
6087    
6088     TRACE("lpht->flags=0x%x\n", lpht->flags); 
6089     if (uView == LVS_REPORT && subitem)
6090     {
6091         INT j;
6092
6093         rcBounds.right = rcBounds.left;
6094         for (j = 0; j < DPA_GetPtrCount(infoPtr->hdpaColumns); j++)
6095         {
6096             rcBounds.left = rcBounds.right;
6097             rcBounds.right += LISTVIEW_GetColumnWidth(infoPtr, j);
6098             if (PtInRect(&rcBounds, opt))
6099             {
6100                 lpht->iSubItem = j;
6101                 break;
6102             }
6103         }
6104     }
6105
6106     if (select && !(uView == LVS_REPORT &&
6107                     ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) ||
6108                      (infoPtr->dwStyle & LVS_OWNERDRAWFIXED))))
6109     {
6110         if (uView == LVS_REPORT)
6111         {
6112             UnionRect(&rcBounds, &rcIcon, &rcLabel);
6113             UnionRect(&rcBounds, &rcBounds, &rcState);
6114         }
6115         if (!PtInRect(&rcBounds, opt)) iItem = -1;
6116     }
6117     return lpht->iItem = iItem;
6118 }
6119
6120
6121 /* LISTVIEW_InsertCompare:  callback routine for comparing pszText members of the LV_ITEMS
6122    in a LISTVIEW on insert.  Passed to DPA_Sort in LISTVIEW_InsertItem.
6123    This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6124    and not during the processing of a LVM_SORTITEMS message. Applications should provide
6125    their own sort proc. when sending LVM_SORTITEMS.
6126 */
6127 /* Platform SDK:
6128     (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6129         if:
6130           LVS_SORTXXX must be specified,
6131           LVS_OWNERDRAW is not set,
6132           <item>.pszText is not LPSTR_TEXTCALLBACK.
6133
6134     (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6135     are sorted based on item text..."
6136 */
6137 static INT WINAPI LISTVIEW_InsertCompare(  LPVOID first, LPVOID second,  LPARAM lParam)
6138 {
6139     ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
6140     ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
6141     INT cmpv = textcmpWT(lv_first->hdr.pszText, lv_second->hdr.pszText, TRUE); 
6142
6143     /* if we're sorting descending, negate the return value */
6144     return (((LISTVIEW_INFO *)lParam)->dwStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
6145 }
6146
6147 /***
6148  * DESCRIPTION:
6149  * Inserts a new item in the listview control.
6150  *
6151  * PARAMETER(S):
6152  * [I] infoPtr : valid pointer to the listview structure
6153  * [I] lpLVItem : item information
6154  * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6155  *
6156  * RETURN:
6157  *   SUCCESS : new item index
6158  *   FAILURE : -1
6159  */
6160 static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
6161 {
6162     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6163     INT nItem;
6164     HDPA hdpaSubItems;
6165     NMLISTVIEW nmlv;
6166     ITEM_INFO *lpItem;
6167     BOOL is_sorted, has_changed;
6168     LVITEMW item;
6169
6170     TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
6171
6172     if (infoPtr->dwStyle & LVS_OWNERDATA) return infoPtr->nItemCount++;
6173
6174     /* make sure it's an item, and not a subitem; cannot insert a subitem */
6175     if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iSubItem) return -1;
6176
6177     if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return -1;
6178
6179     if (!(lpItem = (ITEM_INFO *)Alloc(sizeof(ITEM_INFO)))) return -1;
6180     
6181     /* insert item in listview control data structure */
6182     if ( !(hdpaSubItems = DPA_Create(8)) ) goto fail;
6183     if ( !DPA_SetPtr(hdpaSubItems, 0, lpItem) ) assert (FALSE);
6184
6185     is_sorted = (infoPtr->dwStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
6186                 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
6187
6188     nItem = is_sorted ? infoPtr->nItemCount : min(lpLVItem->iItem, infoPtr->nItemCount);
6189     TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem);
6190     nItem = DPA_InsertPtr( infoPtr->hdpaItems, nItem, hdpaSubItems );
6191     if (nItem == -1) goto fail;
6192     infoPtr->nItemCount++;
6193
6194     /* shift indices first so they don't get tangled */
6195     LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
6196
6197     /* set the item attributes */
6198     if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
6199     {
6200         /* full size structure expected - _WIN32IE >= 0x560 */
6201         item = *lpLVItem;
6202     }
6203     else if (lpLVItem->mask & LVIF_INDENT)
6204     {
6205         /* indent member expected - _WIN32IE >= 0x300 */
6206         memcpy(&item, lpLVItem, offsetof( LVITEMW, iGroupId ));
6207     }
6208     else
6209     {
6210         /* minimal structure expected */
6211         memcpy(&item, lpLVItem, offsetof( LVITEMW, iIndent ));
6212     }
6213     item.iItem = nItem;
6214     if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) item.state &= ~LVIS_STATEIMAGEMASK;
6215     if (!set_main_item(infoPtr, &item, TRUE, isW, &has_changed)) goto undo;
6216
6217     /* if we're sorted, sort the list, and update the index */
6218     if (is_sorted)
6219     {
6220         DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, (LPARAM)infoPtr );
6221         nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
6222         assert(nItem != -1);
6223     }
6224
6225     /* make room for the position, if we are in the right mode */
6226     if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6227     {
6228         if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
6229             goto undo;
6230         if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
6231         {
6232             DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
6233             goto undo;
6234         }
6235     }
6236     
6237     /* send LVN_INSERTITEM notification */
6238     ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6239     nmlv.iItem = nItem;
6240     nmlv.lParam = lpItem->lParam;
6241     notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
6242
6243     /* align items (set position of each item) */
6244     if ((uView == LVS_SMALLICON || uView == LVS_ICON))
6245     {
6246         POINT pt;
6247
6248         if (infoPtr->dwStyle & LVS_ALIGNLEFT)
6249             LISTVIEW_NextIconPosLeft(infoPtr, &pt);
6250         else
6251             LISTVIEW_NextIconPosTop(infoPtr, &pt);
6252
6253         LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, TRUE);
6254     }
6255
6256     /* now is the invalidation fun */
6257     LISTVIEW_ScrollOnInsert(infoPtr, nItem, 1);
6258     return nItem;
6259
6260 undo:
6261     LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
6262     DPA_DeletePtr(infoPtr->hdpaItems, nItem);
6263     infoPtr->nItemCount--;
6264 fail:
6265     DPA_DeletePtr(hdpaSubItems, 0);
6266     DPA_Destroy (hdpaSubItems);
6267     Free (lpItem);
6268     return -1;
6269 }
6270
6271 /***
6272  * DESCRIPTION:
6273  * Redraws a range of items.
6274  *
6275  * PARAMETER(S):
6276  * [I] infoPtr : valid pointer to the listview structure
6277  * [I] nFirst : first item
6278  * [I] nLast : last item
6279  *
6280  * RETURN:
6281  *   SUCCESS : TRUE
6282  *   FAILURE : FALSE
6283  */
6284 static BOOL LISTVIEW_RedrawItems(LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
6285 {
6286     INT i;
6287  
6288     if (nLast < nFirst || min(nFirst, nLast) < 0 || 
6289         max(nFirst, nLast) >= infoPtr->nItemCount)
6290         return FALSE;
6291     
6292     for (i = nFirst; i <= nLast; i++)
6293         LISTVIEW_InvalidateItem(infoPtr, i);
6294
6295     return TRUE;
6296 }
6297
6298 /***
6299  * DESCRIPTION:
6300  * Scroll the content of a listview.
6301  *
6302  * PARAMETER(S):
6303  * [I] infoPtr : valid pointer to the listview structure
6304  * [I] dx : horizontal scroll amount in pixels
6305  * [I] dy : vertical scroll amount in pixels
6306  *
6307  * RETURN:
6308  *   SUCCESS : TRUE
6309  *   FAILURE : FALSE
6310  *
6311  * COMMENTS:
6312  *  If the control is in report mode (LVS_REPORT) the control can
6313  *  be scrolled only in line increments. "dy" will be rounded to the
6314  *  nearest number of pixels that are a whole line. Ex: if line height
6315  *  is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6316  *  is passed the the scroll will be 0.  (per MSDN 7/2002)
6317  *
6318  *  For:  (per experimentaion with native control and CSpy ListView)
6319  *     LVS_ICON       dy=1 = 1 pixel  (vertical only)
6320  *                    dx ignored
6321  *     LVS_SMALLICON  dy=1 = 1 pixel  (vertical only)
6322  *                    dx ignored
6323  *     LVS_LIST       dx=1 = 1 column (horizontal only)
6324  *                           but will only scroll 1 column per message
6325  *                           no matter what the value.
6326  *                    dy must be 0 or FALSE returned.
6327  *     LVS_REPORT     dx=1 = 1 pixel
6328  *                    dy=  see above
6329  *
6330  */
6331 static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
6332 {
6333     switch(infoPtr->dwStyle & LVS_TYPEMASK) {
6334     case LVS_REPORT:
6335         dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
6336         dy /= infoPtr->nItemHeight;
6337         break;
6338     case LVS_LIST:
6339         if (dy != 0) return FALSE;
6340         break;
6341     default: /* icon */
6342         dx = 0;
6343         break;
6344     }   
6345
6346     if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
6347     if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
6348   
6349     return TRUE;
6350 }
6351
6352 /***
6353  * DESCRIPTION:
6354  * Sets the background color.
6355  *
6356  * PARAMETER(S):
6357  * [I] infoPtr : valid pointer to the listview structure
6358  * [I] clrBk : background color
6359  *
6360  * RETURN:
6361  *   SUCCESS : TRUE
6362  *   FAILURE : FALSE
6363  */
6364 static BOOL LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
6365 {
6366     TRACE("(clrBk=%lx)\n", clrBk);
6367
6368     if(infoPtr->clrBk != clrBk) {
6369         if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
6370         infoPtr->clrBk = clrBk;
6371         if (clrBk == CLR_NONE)
6372             infoPtr->hBkBrush = (HBRUSH)GetClassLongPtrW(infoPtr->hwndSelf, GCLP_HBRBACKGROUND);
6373         else
6374             infoPtr->hBkBrush = CreateSolidBrush(clrBk);
6375         LISTVIEW_InvalidateList(infoPtr);
6376     }
6377
6378    return TRUE;
6379 }
6380
6381 /* LISTVIEW_SetBkImage */
6382
6383 /*** Helper for {Insert,Set}ColumnT *only* */
6384 static void column_fill_hditem(LISTVIEW_INFO *infoPtr, HDITEMW *lphdi, INT nColumn, const LVCOLUMNW *lpColumn, BOOL isW)
6385 {
6386     if (lpColumn->mask & LVCF_FMT)
6387     {
6388         /* format member is valid */
6389         lphdi->mask |= HDI_FORMAT;
6390
6391         /* set text alignment (leftmost column must be left-aligned) */
6392         if (nColumn == 0 || (lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
6393             lphdi->fmt |= HDF_LEFT;
6394         else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_RIGHT)
6395             lphdi->fmt |= HDF_RIGHT;
6396         else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_CENTER)
6397             lphdi->fmt |= HDF_CENTER;
6398
6399         if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6400             lphdi->fmt |= HDF_BITMAP_ON_RIGHT;
6401
6402         if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6403         {
6404             lphdi->fmt |= HDF_IMAGE;
6405             lphdi->iImage = I_IMAGECALLBACK;
6406         }
6407     }
6408
6409     if (lpColumn->mask & LVCF_WIDTH)
6410     {
6411         lphdi->mask |= HDI_WIDTH;
6412         if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
6413         {
6414             /* make it fill the remainder of the controls width */
6415             RECT rcHeader;
6416             INT item_index;
6417
6418             for(item_index = 0; item_index < (nColumn - 1); item_index++)
6419             {
6420                 LISTVIEW_GetHeaderRect(infoPtr, item_index, &rcHeader);
6421                 lphdi->cxy += rcHeader.right - rcHeader.left;
6422             }
6423
6424             /* retrieve the layout of the header */
6425             GetClientRect(infoPtr->hwndSelf, &rcHeader);
6426             TRACE("start cxy=%d rcHeader=%s\n", lphdi->cxy, debugrect(&rcHeader));
6427
6428             lphdi->cxy = (rcHeader.right - rcHeader.left) - lphdi->cxy;
6429         }
6430         else
6431             lphdi->cxy = lpColumn->cx;
6432     }
6433
6434     if (lpColumn->mask & LVCF_TEXT)
6435     {
6436         lphdi->mask |= HDI_TEXT | HDI_FORMAT;
6437         lphdi->fmt |= HDF_STRING;
6438         lphdi->pszText = lpColumn->pszText;
6439         lphdi->cchTextMax = textlenT(lpColumn->pszText, isW);
6440     }
6441
6442     if (lpColumn->mask & LVCF_IMAGE)
6443     {
6444         lphdi->mask |= HDI_IMAGE;
6445         lphdi->iImage = lpColumn->iImage;
6446     }
6447
6448     if (lpColumn->mask & LVCF_ORDER)
6449     {
6450         lphdi->mask |= HDI_ORDER;
6451         lphdi->iOrder = lpColumn->iOrder;
6452     }
6453 }
6454
6455
6456 /***
6457  * DESCRIPTION:
6458  * Inserts a new column.
6459  *
6460  * PARAMETER(S):
6461  * [I] infoPtr : valid pointer to the listview structure
6462  * [I] nColumn : column index
6463  * [I] lpColumn : column information
6464  * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
6465  *
6466  * RETURN:
6467  *   SUCCESS : new column index
6468  *   FAILURE : -1
6469  */
6470 static INT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6471                                   const LVCOLUMNW *lpColumn, BOOL isW)
6472 {
6473     COLUMN_INFO *lpColumnInfo;
6474     INT nNewColumn;
6475     HDITEMW hdi;
6476
6477     TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6478
6479     if (!lpColumn || nColumn < 0) return -1;
6480     nColumn = min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns));
6481     
6482     ZeroMemory(&hdi, sizeof(HDITEMW));
6483     column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6484
6485     /* insert item in header control */
6486     nNewColumn = SendMessageW(infoPtr->hwndHeader, 
6487                               isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
6488                               (WPARAM)nColumn, (LPARAM)&hdi);
6489     if (nNewColumn == -1) return -1;
6490     if (nNewColumn != nColumn) ERR("nColumn=%d, nNewColumn=%d\n", nColumn, nNewColumn);
6491    
6492     /* create our own column info */ 
6493     if (!(lpColumnInfo = Alloc(sizeof(COLUMN_INFO)))) goto fail;
6494     if (DPA_InsertPtr(infoPtr->hdpaColumns, nNewColumn, lpColumnInfo) == -1) goto fail;
6495
6496     if (lpColumn->mask & LVCF_FMT) lpColumnInfo->fmt = lpColumn->fmt;
6497     if (!Header_GetItemRect(infoPtr->hwndHeader, nNewColumn, &lpColumnInfo->rcHeader)) goto fail;
6498
6499     /* now we have to actually adjust the data */
6500     if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
6501     {
6502         SUBITEM_INFO *lpSubItem;
6503         HDPA hdpaSubItems;
6504         INT nItem, i;
6505         
6506         for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
6507         {
6508             hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
6509             for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
6510             {
6511                 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
6512                 if (lpSubItem->iSubItem >= nNewColumn)
6513                     lpSubItem->iSubItem++;
6514             }
6515         }
6516     }
6517
6518     /* make space for the new column */
6519     LISTVIEW_ScrollColumns(infoPtr, nNewColumn + 1, lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
6520     
6521     return nNewColumn;
6522
6523 fail:
6524     if (nNewColumn != -1) SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nNewColumn, 0);
6525     if (lpColumnInfo)
6526     {
6527         DPA_DeletePtr(infoPtr->hdpaColumns, nNewColumn);
6528         Free(lpColumnInfo);
6529     }
6530     return -1;
6531 }
6532
6533 /***
6534  * DESCRIPTION:
6535  * Sets the attributes of a header item.
6536  *
6537  * PARAMETER(S):
6538  * [I] infoPtr : valid pointer to the listview structure
6539  * [I] nColumn : column index
6540  * [I] lpColumn : column attributes
6541  * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
6542  *
6543  * RETURN:
6544  *   SUCCESS : TRUE
6545  *   FAILURE : FALSE
6546  */
6547 static BOOL LISTVIEW_SetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6548                                 const LVCOLUMNW *lpColumn, BOOL isW)
6549 {
6550     HDITEMW hdi, hdiget;
6551     BOOL bResult;
6552
6553     TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6554     
6555     if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6556
6557     ZeroMemory(&hdi, sizeof(HDITEMW));
6558     if (lpColumn->mask & LVCF_FMT)
6559     {
6560         hdi.mask |= HDI_FORMAT;
6561         hdiget.mask = HDI_FORMAT;
6562         if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
6563             hdi.fmt = hdiget.fmt & HDF_STRING;
6564     }
6565     column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6566
6567     /* set header item attributes */
6568     bResult = SendMessageW(infoPtr->hwndHeader, isW ? HDM_SETITEMW : HDM_SETITEMA, (WPARAM)nColumn, (LPARAM)&hdi);
6569     if (!bResult) return FALSE;
6570
6571     if (lpColumn->mask & LVCF_FMT)
6572     {
6573         COLUMN_INFO *lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
6574         int oldFmt = lpColumnInfo->fmt;
6575         
6576         lpColumnInfo->fmt = lpColumn->fmt;
6577         if ((oldFmt ^ lpColumn->fmt) & (LVCFMT_JUSTIFYMASK | LVCFMT_IMAGE))
6578         {
6579             UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6580             if (uView == LVS_REPORT) LISTVIEW_InvalidateColumn(infoPtr, nColumn);
6581         }
6582     }
6583
6584     return TRUE;
6585 }
6586
6587 /***
6588  * DESCRIPTION:
6589  * Sets the column order array
6590  *
6591  * PARAMETERS:
6592  * [I] infoPtr : valid pointer to the listview structure
6593  * [I] iCount : number of elements in column order array
6594  * [I] lpiArray : pointer to column order array
6595  *
6596  * RETURN:
6597  *   SUCCESS : TRUE
6598  *   FAILURE : FALSE
6599  */
6600 static BOOL LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, const INT *lpiArray)
6601 {
6602   FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
6603
6604   if (!lpiArray)
6605     return FALSE;
6606
6607   return TRUE;
6608 }
6609
6610
6611 /***
6612  * DESCRIPTION:
6613  * Sets the width of a column
6614  *
6615  * PARAMETERS:
6616  * [I] infoPtr : valid pointer to the listview structure
6617  * [I] nColumn : column index
6618  * [I] cx : column width
6619  *
6620  * RETURN:
6621  *   SUCCESS : TRUE
6622  *   FAILURE : FALSE
6623  */
6624 static BOOL LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn, INT cx)
6625 {
6626     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6627     WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
6628     INT max_cx = 0;
6629     HDITEMW hdi;
6630
6631     TRACE("(nColumn=%d, cx=%d\n", nColumn, cx);
6632
6633     /* set column width only if in report or list mode */
6634     if (uView != LVS_REPORT && uView != LVS_LIST) return FALSE;
6635
6636     /* take care of invalid cx values */
6637     if(uView == LVS_REPORT && cx < -2) cx = LVSCW_AUTOSIZE;
6638     else if (uView == LVS_LIST && cx < 1) return FALSE;
6639
6640     /* resize all columns if in LVS_LIST mode */
6641     if(uView == LVS_LIST) 
6642     {
6643         infoPtr->nItemWidth = cx;
6644         LISTVIEW_InvalidateList(infoPtr);
6645         return TRUE;
6646     }
6647
6648     if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6649
6650     if (cx == LVSCW_AUTOSIZE || (cx == LVSCW_AUTOSIZE_USEHEADER && nColumn < DPA_GetPtrCount(infoPtr->hdpaColumns) -1))
6651     {
6652         INT nLabelWidth;
6653         LVITEMW lvItem;
6654
6655         lvItem.mask = LVIF_TEXT;        
6656         lvItem.iItem = 0;
6657         lvItem.iSubItem = nColumn;
6658         lvItem.pszText = szDispText;
6659         lvItem.cchTextMax = DISP_TEXT_SIZE;
6660         for (; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
6661         {
6662             if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
6663             nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6664             if (max_cx < nLabelWidth) max_cx = nLabelWidth;
6665         }
6666         if (infoPtr->himlSmall && (nColumn == 0 || (LISTVIEW_GetColumnInfo(infoPtr, nColumn)->fmt & LVCFMT_IMAGE)))
6667             max_cx += infoPtr->iconSize.cx;
6668         max_cx += TRAILING_LABEL_PADDING;
6669     }
6670
6671     /* autosize based on listview items width */
6672     if(cx == LVSCW_AUTOSIZE)
6673         cx = max_cx;
6674     else if(cx == LVSCW_AUTOSIZE_USEHEADER)
6675     {
6676         /* if iCol is the last column make it fill the remainder of the controls width */
6677         if(nColumn == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1) 
6678         {
6679             RECT rcHeader;
6680             POINT Origin;
6681
6682             LISTVIEW_GetOrigin(infoPtr, &Origin);
6683             LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
6684
6685             cx = infoPtr->rcList.right - Origin.x - rcHeader.left;
6686         }
6687         else
6688         {
6689             /* Despite what the MS docs say, if this is not the last
6690                column, then MS resizes the column to the width of the
6691                largest text string in the column, including headers
6692                and items. This is different from LVSCW_AUTOSIZE in that
6693                LVSCW_AUTOSIZE ignores the header string length. */
6694             cx = 0;
6695
6696             /* retrieve header text */
6697             hdi.mask = HDI_TEXT;
6698             hdi.cchTextMax = DISP_TEXT_SIZE;
6699             hdi.pszText = szDispText;
6700             if (Header_GetItemW(infoPtr->hwndHeader, nColumn, (LPARAM)&hdi))
6701             {
6702                 HDC hdc = GetDC(infoPtr->hwndSelf);
6703                 HFONT old_font = SelectObject(hdc, (HFONT)SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0, 0));
6704                 SIZE size;
6705
6706                 if (GetTextExtentPoint32W(hdc, hdi.pszText, lstrlenW(hdi.pszText), &size))
6707                     cx = size.cx + TRAILING_HEADER_PADDING;
6708                 /* FIXME: Take into account the header image, if one is present */
6709                 SelectObject(hdc, old_font);
6710                 ReleaseDC(infoPtr->hwndSelf, hdc);
6711             }
6712             cx = max (cx, max_cx);
6713         }
6714     }
6715
6716     if (cx < 0) return FALSE;
6717
6718     /* call header to update the column change */
6719     hdi.mask = HDI_WIDTH;
6720     hdi.cxy = cx;
6721     TRACE("hdi.cxy=%d\n", hdi.cxy);
6722     return Header_SetItemW(infoPtr->hwndHeader, nColumn, (LPARAM)&hdi);
6723 }
6724
6725 /***
6726  * Creates the checkbox imagelist.  Helper for LISTVIEW_SetExtendedListViewStyle
6727  *
6728  */
6729 static HIMAGELIST LISTVIEW_CreateCheckBoxIL(LISTVIEW_INFO *infoPtr)
6730 {
6731     HDC hdc_wnd, hdc;
6732     HBITMAP hbm_im, hbm_mask, hbm_orig;
6733     RECT rc;
6734     HBRUSH hbr_white = GetStockObject(WHITE_BRUSH);
6735     HBRUSH hbr_black = GetStockObject(BLACK_BRUSH);
6736     HIMAGELIST himl;
6737
6738     himl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
6739                             ILC_COLOR | ILC_MASK, 2, 2);
6740     hdc_wnd = GetDC(infoPtr->hwndSelf);
6741     hdc = CreateCompatibleDC(hdc_wnd);
6742     hbm_im = CreateCompatibleBitmap(hdc_wnd, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
6743     hbm_mask = CreateBitmap(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 1, 1, NULL);
6744     ReleaseDC(infoPtr->hwndSelf, hdc_wnd);
6745
6746     rc.left = rc.top = 0;
6747     rc.right = GetSystemMetrics(SM_CXSMICON);
6748     rc.bottom = GetSystemMetrics(SM_CYSMICON);
6749
6750     hbm_orig = SelectObject(hdc, hbm_mask);
6751     FillRect(hdc, &rc, hbr_white);
6752     InflateRect(&rc, -3, -3);
6753     FillRect(hdc, &rc, hbr_black);
6754
6755     SelectObject(hdc, hbm_im);
6756     DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO);
6757     SelectObject(hdc, hbm_orig);
6758     ImageList_Add(himl, hbm_im, hbm_mask); 
6759
6760     SelectObject(hdc, hbm_im);
6761     DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO | DFCS_CHECKED);
6762     SelectObject(hdc, hbm_orig);
6763     ImageList_Add(himl, hbm_im, hbm_mask);
6764
6765     DeleteObject(hbm_mask);
6766     DeleteObject(hbm_im);
6767     DeleteDC(hdc);
6768
6769     return himl;
6770 }
6771
6772 /***
6773  * DESCRIPTION:
6774  * Sets the extended listview style.
6775  *
6776  * PARAMETERS:
6777  * [I] infoPtr : valid pointer to the listview structure
6778  * [I] dwMask : mask
6779  * [I] dwStyle : style
6780  *
6781  * RETURN:
6782  *   SUCCESS : previous style
6783  *   FAILURE : 0
6784  */
6785 static DWORD LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwStyle)
6786 {
6787     DWORD dwOldStyle = infoPtr->dwLvExStyle;
6788
6789     /* set new style */
6790     if (dwMask)
6791         infoPtr->dwLvExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
6792     else
6793         infoPtr->dwLvExStyle = dwStyle;
6794
6795     if((infoPtr->dwLvExStyle ^ dwOldStyle) & LVS_EX_CHECKBOXES)
6796     {
6797         HIMAGELIST himl = 0;
6798         if(infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
6799             himl = LISTVIEW_CreateCheckBoxIL(infoPtr);
6800         LISTVIEW_SetImageList(infoPtr, LVSIL_STATE, himl);
6801     }
6802
6803     return dwOldStyle;
6804 }
6805
6806 /***
6807  * DESCRIPTION:
6808  * Sets the new hot cursor used during hot tracking and hover selection.
6809  *
6810  * PARAMETER(S):
6811  * [I] infoPtr : valid pointer to the listview structure
6812  * [I} hCurosr : the new hot cursor handle
6813  *
6814  * RETURN:
6815  * Returns the previous hot cursor
6816  */
6817 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
6818 {
6819     HCURSOR oldCursor = infoPtr->hHotCursor;
6820     
6821     infoPtr->hHotCursor = hCursor;
6822
6823     return oldCursor;
6824 }
6825
6826
6827 /***
6828  * DESCRIPTION:
6829  * Sets the hot item index.
6830  *
6831  * PARAMETERS:
6832  * [I] infoPtr : valid pointer to the listview structure
6833  * [I] iIndex : index
6834  *
6835  * RETURN:
6836  *   SUCCESS : previous hot item index
6837  *   FAILURE : -1 (no hot item)
6838  */
6839 static INT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
6840 {
6841     INT iOldIndex = infoPtr->nHotItem;
6842     
6843     infoPtr->nHotItem = iIndex;
6844     
6845     return iOldIndex;
6846 }
6847
6848
6849 /***
6850  * DESCRIPTION:
6851  * Sets the amount of time the cursor must hover over an item before it is selected.
6852  *
6853  * PARAMETER(S):
6854  * [I] infoPtr : valid pointer to the listview structure
6855  * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
6856  *
6857  * RETURN:
6858  * Returns the previous hover time
6859  */
6860 static DWORD LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
6861 {
6862     DWORD oldHoverTime = infoPtr->dwHoverTime;
6863     
6864     infoPtr->dwHoverTime = dwHoverTime;
6865     
6866     return oldHoverTime;
6867 }
6868
6869 /***
6870  * DESCRIPTION:
6871  * Sets spacing for icons of LVS_ICON style.
6872  *
6873  * PARAMETER(S):
6874  * [I] infoPtr : valid pointer to the listview structure
6875  * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
6876  * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
6877  *
6878  * RETURN:
6879  *   MAKELONG(oldcx, oldcy)
6880  */
6881 static DWORD LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, INT cx, INT cy)
6882 {
6883     DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6884     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6885
6886     TRACE("requested=(%d,%d)\n", cx, cy);
6887     
6888     /* this is supported only for LVS_ICON style */
6889     if (uView != LVS_ICON) return oldspacing;
6890   
6891     /* set to defaults, if instructed to */
6892     if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
6893     if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
6894
6895     /* if 0 then compute width
6896      * FIXME: Should scan each item and determine max width of
6897      *        icon or label, then make that the width */
6898     if (cx == 0)
6899         cx = infoPtr->iconSpacing.cx;
6900
6901     /* if 0 then compute height */
6902     if (cy == 0) 
6903         cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
6904              ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
6905     
6906
6907     infoPtr->iconSpacing.cx = cx;
6908     infoPtr->iconSpacing.cy = cy;
6909
6910     TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%ld,%ld), ntmH=%d\n",
6911           LOWORD(oldspacing), HIWORD(oldspacing), cx, cy, 
6912           infoPtr->iconSize.cx, infoPtr->iconSize.cy,
6913           infoPtr->ntmHeight);
6914
6915     /* these depend on the iconSpacing */
6916     LISTVIEW_UpdateItemSize(infoPtr);
6917
6918     return oldspacing;
6919 }
6920
6921 inline void set_icon_size(SIZE *size, HIMAGELIST himl, BOOL small)
6922 {
6923     INT cx, cy;
6924     
6925     if (himl && ImageList_GetIconSize(himl, &cx, &cy))
6926     {
6927         size->cx = cx;
6928         size->cy = cy;
6929     }
6930     else
6931     {
6932         size->cx = GetSystemMetrics(small ? SM_CXSMICON : SM_CXICON);
6933         size->cy = GetSystemMetrics(small ? SM_CYSMICON : SM_CYICON);
6934     }
6935 }
6936
6937 /***
6938  * DESCRIPTION:
6939  * Sets image lists.
6940  *
6941  * PARAMETER(S):
6942  * [I] infoPtr : valid pointer to the listview structure
6943  * [I] nType : image list type
6944  * [I] himl : image list handle
6945  *
6946  * RETURN:
6947  *   SUCCESS : old image list
6948  *   FAILURE : NULL
6949  */
6950 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
6951 {
6952     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6953     INT oldHeight = infoPtr->nItemHeight;
6954     HIMAGELIST himlOld = 0;
6955
6956     TRACE("(nType=%d, himl=%p\n", nType, himl);
6957
6958     switch (nType)
6959     {
6960     case LVSIL_NORMAL:
6961         himlOld = infoPtr->himlNormal;
6962         infoPtr->himlNormal = himl;
6963         if (uView == LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, FALSE);
6964         LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
6965     break;
6966
6967     case LVSIL_SMALL:
6968         himlOld = infoPtr->himlSmall;
6969         infoPtr->himlSmall = himl;
6970          if (uView != LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, TRUE);
6971     break;
6972
6973     case LVSIL_STATE:
6974         himlOld = infoPtr->himlState;
6975         infoPtr->himlState = himl;
6976         set_icon_size(&infoPtr->iconStateSize, himl, TRUE);
6977         ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
6978     break;
6979
6980     default:
6981         ERR("Unknown icon type=%d\n", nType);
6982         return NULL;
6983     }
6984
6985     infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
6986     if (infoPtr->nItemHeight != oldHeight)
6987         LISTVIEW_UpdateScroll(infoPtr);
6988
6989     return himlOld;
6990 }
6991
6992 /***
6993  * DESCRIPTION:
6994  * Preallocates memory (does *not* set the actual count of items !)
6995  *
6996  * PARAMETER(S):
6997  * [I] infoPtr : valid pointer to the listview structure
6998  * [I] nItems : item count (projected number of items to allocate)
6999  * [I] dwFlags : update flags
7000  *
7001  * RETURN:
7002  *   SUCCESS : TRUE
7003  *   FAILURE : FALSE
7004  */
7005 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
7006 {
7007     TRACE("(nItems=%d, dwFlags=%lx)\n", nItems, dwFlags);
7008
7009     if (infoPtr->dwStyle & LVS_OWNERDATA)
7010     {
7011         UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7012         INT nOldCount = infoPtr->nItemCount;
7013
7014         if (nItems < nOldCount)
7015         {
7016             RANGE range = { nItems, nOldCount };
7017             ranges_del(infoPtr->selectionRanges, range);
7018             if (infoPtr->nFocusedItem >= nItems)
7019             {
7020                 infoPtr->nFocusedItem = -1;
7021                 SetRectEmpty(&infoPtr->rcFocus);
7022             }
7023         }
7024
7025         infoPtr->nItemCount = nItems;
7026         LISTVIEW_UpdateScroll(infoPtr);
7027
7028         /* the flags are valid only in ownerdata report and list modes */
7029         if (uView == LVS_ICON || uView == LVS_SMALLICON) dwFlags = 0;
7030
7031         if (!(dwFlags & LVSICF_NOSCROLL) && infoPtr->nFocusedItem != -1)
7032             LISTVIEW_EnsureVisible(infoPtr, infoPtr->nFocusedItem, FALSE);
7033
7034         if (!(dwFlags & LVSICF_NOINVALIDATEALL))
7035             LISTVIEW_InvalidateList(infoPtr);
7036         else
7037         {
7038             INT nFrom, nTo;
7039             POINT Origin;
7040             RECT rcErase;
7041             
7042             LISTVIEW_GetOrigin(infoPtr, &Origin);
7043             nFrom = min(nOldCount, nItems);
7044             nTo = max(nOldCount, nItems);
7045     
7046             if (uView == LVS_REPORT)
7047             {
7048                 rcErase.left = 0;
7049                 rcErase.top = nFrom * infoPtr->nItemHeight;
7050                 rcErase.right = infoPtr->nItemWidth;
7051                 rcErase.bottom = nTo * infoPtr->nItemHeight;
7052                 OffsetRect(&rcErase, Origin.x, Origin.y);
7053                 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7054                     LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7055             }
7056             else /* LVS_LIST */
7057             {
7058                 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
7059
7060                 rcErase.left = (nFrom / nPerCol) * infoPtr->nItemWidth;
7061                 rcErase.top = (nFrom % nPerCol) * infoPtr->nItemHeight;
7062                 rcErase.right = rcErase.left + infoPtr->nItemWidth;
7063                 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
7064                 OffsetRect(&rcErase, Origin.x, Origin.y);
7065                 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7066                     LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7067
7068                 rcErase.left = (nFrom / nPerCol + 1) * infoPtr->nItemWidth;
7069                 rcErase.top = 0;
7070                 rcErase.right = (nTo / nPerCol + 1) * infoPtr->nItemWidth;
7071                 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
7072                 OffsetRect(&rcErase, Origin.x, Origin.y);
7073                 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7074                     LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7075             }
7076         }
7077     }
7078     else
7079     {
7080         /* According to MSDN for non-LVS_OWNERDATA this is just
7081          * a performance issue. The control allocates its internal
7082          * data structures for the number of items specified. It
7083          * cuts down on the number of memory allocations. Therefore
7084          * we will just issue a WARN here
7085          */
7086         WARN("for non-ownerdata performance option not implemented.\n");
7087     }
7088
7089     return TRUE;
7090 }
7091
7092 /***
7093  * DESCRIPTION:
7094  * Sets the position of an item.
7095  *
7096  * PARAMETER(S):
7097  * [I] infoPtr : valid pointer to the listview structure
7098  * [I] nItem : item index
7099  * [I] pt : coordinate
7100  *
7101  * RETURN:
7102  *   SUCCESS : TRUE
7103  *   FAILURE : FALSE
7104  */
7105 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT pt)
7106 {
7107     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7108     POINT Origin;
7109
7110     TRACE("(nItem=%d, &pt=%s\n", nItem, debugpoint(&pt));
7111
7112     if (nItem < 0 || nItem >= infoPtr->nItemCount ||
7113         !(uView == LVS_ICON || uView == LVS_SMALLICON)) return FALSE;
7114
7115     LISTVIEW_GetOrigin(infoPtr, &Origin);
7116
7117     /* This point value seems to be an undocumented feature.
7118      * The best guess is that it means either at the origin, 
7119      * or at true beginning of the list. I will assume the origin. */
7120     if ((pt.x == -1) && (pt.y == -1))
7121         pt = Origin;
7122     
7123     if (uView == LVS_ICON)
7124     {
7125         pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
7126         pt.y -= ICON_TOP_PADDING;
7127     }
7128     pt.x -= Origin.x;
7129     pt.y -= Origin.y;
7130
7131     infoPtr->bAutoarrange = FALSE;
7132
7133     return LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, FALSE);
7134 }
7135
7136 /***
7137  * DESCRIPTION:
7138  * Sets the state of one or many items.
7139  *
7140  * PARAMETER(S):
7141  * [I] infoPtr : valid pointer to the listview structure
7142  * [I] nItem : item index
7143  * [I] lpLVItem : item or subitem info
7144  *
7145  * RETURN:
7146  *   SUCCESS : TRUE
7147  *   FAILURE : FALSE
7148  */
7149 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem)
7150 {
7151     BOOL bResult = TRUE;
7152     LVITEMW lvItem;
7153
7154     lvItem.iItem = nItem;
7155     lvItem.iSubItem = 0;
7156     lvItem.mask = LVIF_STATE;
7157     lvItem.state = lpLVItem->state;
7158     lvItem.stateMask = lpLVItem->stateMask;
7159     TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
7160
7161     if (nItem == -1)
7162     {
7163         /* apply to all items */
7164         for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
7165             if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
7166     }
7167     else
7168         bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
7169
7170     /*
7171      * Update selection mark
7172      *
7173      * Investigation on windows 2k showed that selection mark was updated
7174      * whenever a new selection was made, but if the selected item was
7175      * unselected it was not updated.
7176      *
7177      * we are probably still not 100% accurate, but this at least sets the
7178      * proper selection mark when it is needed
7179      */
7180
7181     if (bResult && (lvItem.state & lvItem.stateMask & LVIS_SELECTED) &&
7182         ((infoPtr->nSelectionMark == -1) || (lvItem.iItem <= infoPtr->nSelectionMark)))
7183     {
7184         int i;
7185         infoPtr->nSelectionMark = -1;
7186         for (i = 0; i < infoPtr->nItemCount; i++)
7187         {
7188             if (infoPtr->uCallbackMask & LVIS_SELECTED)
7189             {
7190                 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
7191                 {
7192                     infoPtr->nSelectionMark = i;
7193                     break;
7194                 }
7195             }
7196             else if (ranges_contain(infoPtr->selectionRanges, i))
7197             {
7198                 infoPtr->nSelectionMark = i;
7199                 break;
7200             }
7201         }
7202     }
7203
7204     return bResult;
7205 }
7206
7207 /***
7208  * DESCRIPTION:
7209  * Sets the text of an item or subitem.
7210  *
7211  * PARAMETER(S):
7212  * [I] hwnd : window handle
7213  * [I] nItem : item index
7214  * [I] lpLVItem : item or subitem info
7215  * [I] isW : TRUE if input is Unicode
7216  *
7217  * RETURN:
7218  *   SUCCESS : TRUE
7219  *   FAILURE : FALSE
7220  */
7221 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem, BOOL isW)
7222 {
7223     LVITEMW lvItem;
7224
7225     if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
7226     
7227     lvItem.iItem = nItem;
7228     lvItem.iSubItem = lpLVItem->iSubItem;
7229     lvItem.mask = LVIF_TEXT;
7230     lvItem.pszText = lpLVItem->pszText;
7231     lvItem.cchTextMax = lpLVItem->cchTextMax;
7232     
7233     TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
7234
7235     return LISTVIEW_SetItemT(infoPtr, &lvItem, isW); 
7236 }
7237
7238 /***
7239  * DESCRIPTION:
7240  * Set item index that marks the start of a multiple selection.
7241  *
7242  * PARAMETER(S):
7243  * [I] infoPtr : valid pointer to the listview structure
7244  * [I] nIndex : index
7245  *
7246  * RETURN:
7247  * Index number or -1 if there is no selection mark.
7248  */
7249 static INT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
7250 {
7251   INT nOldIndex = infoPtr->nSelectionMark;
7252
7253   TRACE("(nIndex=%d)\n", nIndex);
7254
7255   infoPtr->nSelectionMark = nIndex;
7256
7257   return nOldIndex;
7258 }
7259
7260 /***
7261  * DESCRIPTION:
7262  * Sets the text background color.
7263  *
7264  * PARAMETER(S):
7265  * [I] infoPtr : valid pointer to the listview structure
7266  * [I] clrTextBk : text background color
7267  *
7268  * RETURN:
7269  *   SUCCESS : TRUE
7270  *   FAILURE : FALSE
7271  */
7272 static BOOL LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
7273 {
7274     TRACE("(clrTextBk=%lx)\n", clrTextBk);
7275
7276     if (infoPtr->clrTextBk != clrTextBk)
7277     {
7278         infoPtr->clrTextBk = clrTextBk;
7279         LISTVIEW_InvalidateList(infoPtr);
7280     }
7281     
7282   return TRUE;
7283 }
7284
7285 /***
7286  * DESCRIPTION:
7287  * Sets the text foreground color.
7288  *
7289  * PARAMETER(S):
7290  * [I] infoPtr : valid pointer to the listview structure
7291  * [I] clrText : text color
7292  *
7293  * RETURN:
7294  *   SUCCESS : TRUE
7295  *   FAILURE : FALSE
7296  */
7297 static BOOL LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
7298 {
7299     TRACE("(clrText=%lx)\n", clrText);
7300
7301     if (infoPtr->clrText != clrText)
7302     {
7303         infoPtr->clrText = clrText;
7304         LISTVIEW_InvalidateList(infoPtr);
7305     }
7306
7307     return TRUE;
7308 }
7309
7310 /***
7311  * DESCRIPTION:
7312  * Determines which listview item is located at the specified position.
7313  *
7314  * PARAMETER(S):
7315  * [I] infoPtr        : valid pointer to the listview structure
7316  * [I] hwndNewToolTip : handle to new ToolTip
7317  *
7318  * RETURN:
7319  *   old tool tip
7320  */
7321 static HWND LISTVIEW_SetToolTips( LISTVIEW_INFO *infoPtr, HWND hwndNewToolTip)
7322 {
7323   HWND hwndOldToolTip = infoPtr->hwndToolTip;
7324   infoPtr->hwndToolTip = hwndNewToolTip;
7325   return hwndOldToolTip;
7326 }
7327
7328 /* LISTVIEW_SetUnicodeFormat */
7329 /* LISTVIEW_SetWorkAreas */
7330
7331 /***
7332  * DESCRIPTION:
7333  * Callback internally used by LISTVIEW_SortItems()
7334  *
7335  * PARAMETER(S):
7336  * [I] first : pointer to first ITEM_INFO to compare
7337  * [I] second : pointer to second ITEM_INFO to compare
7338  * [I] lParam : HWND of control
7339  *
7340  * RETURN:
7341  *   if first comes before second : negative
7342  *   if first comes after second : positive
7343  *   if first and second are equivalent : zero
7344  */
7345 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
7346 {
7347   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam;
7348   ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
7349   ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
7350
7351   /* Forward the call to the client defined callback */
7352   return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
7353 }
7354
7355 /***
7356  * DESCRIPTION:
7357  * Sorts the listview items.
7358  *
7359  * PARAMETER(S):
7360  * [I] infoPtr : valid pointer to the listview structure
7361  * [I] pfnCompare : application-defined value
7362  * [I] lParamSort : pointer to comparision callback
7363  *
7364  * RETURN:
7365  *   SUCCESS : TRUE
7366  *   FAILURE : FALSE
7367  */
7368 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
7369 {
7370     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7371     HDPA hdpaSubItems;
7372     ITEM_INFO *lpItem;
7373     LPVOID selectionMarkItem;
7374     LVITEMW item;
7375     int i;
7376
7377     TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
7378
7379     if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
7380
7381     if (!pfnCompare) return FALSE;
7382     if (!infoPtr->hdpaItems) return FALSE;
7383
7384     /* if there are 0 or 1 items, there is no need to sort */
7385     if (infoPtr->nItemCount < 2) return TRUE;
7386
7387     if (infoPtr->nFocusedItem >= 0)
7388     {
7389         hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
7390         lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
7391         if (lpItem) lpItem->state |= LVIS_FOCUSED;
7392     }
7393     /* FIXME: go thorugh selected items and mark them so in lpItem->state */
7394     /*        clear the lpItem->state for non-selected ones */
7395     /*        remove the selection ranges */
7396     
7397     infoPtr->pfnCompare = pfnCompare;
7398     infoPtr->lParamSort = lParamSort;
7399     DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr);
7400
7401     /* Adjust selections and indices so that they are the way they should
7402      * be after the sort (otherwise, the list items move around, but
7403      * whatever is at the item's previous original position will be
7404      * selected instead)
7405      */
7406     selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
7407     for (i=0; i < infoPtr->nItemCount; i++)
7408     {
7409         hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
7410         lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
7411
7412         if (lpItem->state & LVIS_SELECTED)
7413         {
7414             item.state = LVIS_SELECTED;
7415             item.stateMask = LVIS_SELECTED;
7416             LISTVIEW_SetItemState(infoPtr, i, &item);
7417         }
7418         if (lpItem->state & LVIS_FOCUSED)
7419         {
7420             infoPtr->nFocusedItem = i;
7421             lpItem->state &= ~LVIS_FOCUSED;
7422         }
7423     }
7424     if (selectionMarkItem != NULL)
7425         infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
7426     /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7427
7428     /* refresh the display */
7429     if (uView != LVS_ICON && uView != LVS_SMALLICON)
7430         LISTVIEW_InvalidateList(infoPtr);
7431
7432     return TRUE;
7433 }
7434
7435 /***
7436  * DESCRIPTION:
7437  * Updates an items or rearranges the listview control.
7438  *
7439  * PARAMETER(S):
7440  * [I] infoPtr : valid pointer to the listview structure
7441  * [I] nItem : item index
7442  *
7443  * RETURN:
7444  *   SUCCESS : TRUE
7445  *   FAILURE : FALSE
7446  */
7447 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
7448 {
7449     TRACE("(nItem=%d)\n", nItem);
7450
7451     if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
7452
7453     /* rearrange with default alignment style */
7454     if (is_autoarrange(infoPtr))
7455         LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
7456     else
7457         LISTVIEW_InvalidateItem(infoPtr, nItem);
7458
7459     return TRUE;
7460 }
7461
7462         
7463 /***
7464  * DESCRIPTION:
7465  * Creates the listview control.
7466  *
7467  * PARAMETER(S):
7468  * [I] hwnd : window handle
7469  * [I] lpcs : the create parameters
7470  *
7471  * RETURN:
7472  *   Success: 0
7473  *   Failure: -1
7474  */
7475 static LRESULT LISTVIEW_Create(HWND hwnd, const CREATESTRUCTW *lpcs)
7476 {
7477   LISTVIEW_INFO *infoPtr;
7478   UINT uView = lpcs->style & LVS_TYPEMASK;
7479   LOGFONTW logFont;
7480
7481   TRACE("(lpcs=%p)\n", lpcs);
7482
7483   /* initialize info pointer */
7484   infoPtr = (LISTVIEW_INFO *)Alloc(sizeof(LISTVIEW_INFO));
7485   if (!infoPtr) return -1;
7486
7487   SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr);
7488
7489   infoPtr->hwndSelf = hwnd;
7490   infoPtr->dwStyle = lpcs->style;
7491   /* determine the type of structures to use */
7492   infoPtr->hwndNotify = lpcs->hwndParent;
7493   infoPtr->notifyFormat = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT,
7494                                        (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
7495
7496   /* initialize color information  */
7497   infoPtr->clrBk = CLR_NONE;
7498   infoPtr->clrText = comctl32_color.clrWindowText;
7499   infoPtr->clrTextBk = CLR_DEFAULT;
7500   LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
7501
7502   /* set default values */
7503   infoPtr->nFocusedItem = -1;
7504   infoPtr->nSelectionMark = -1;
7505   infoPtr->nHotItem = -1;
7506   infoPtr->bRedraw = TRUE;
7507   infoPtr->bNoItemMetrics = TRUE;
7508   infoPtr->bDoChangeNotify = TRUE;
7509   infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
7510   infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
7511   infoPtr->nEditLabelItem = -1;
7512   infoPtr->dwHoverTime = -1; /* default system hover time */
7513   infoPtr->nMeasureItemHeight = 0;
7514
7515   /* get default font (icon title) */
7516   SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
7517   infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
7518   infoPtr->hFont = infoPtr->hDefaultFont;
7519   LISTVIEW_SaveTextMetrics(infoPtr);
7520
7521   /* create header */
7522   infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, NULL,
7523     WS_CHILD | HDS_HORZ | (DWORD)((LVS_NOSORTHEADER & lpcs->style)?0:HDS_BUTTONS),
7524     0, 0, 0, 0, hwnd, NULL,
7525     lpcs->hInstance, NULL);
7526   if (!infoPtr->hwndHeader) goto fail;
7527
7528   /* set header unicode format */
7529   SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT, (WPARAM)TRUE, (LPARAM)NULL);
7530
7531   /* set header font */
7532   SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, (LPARAM)TRUE);
7533
7534   /* allocate memory for the data structure */
7535   if (!(infoPtr->selectionRanges = ranges_create(10))) goto fail;
7536   if (!(infoPtr->hdpaItems = DPA_Create(10))) goto fail;
7537   if (!(infoPtr->hdpaPosX  = DPA_Create(10))) goto fail;
7538   if (!(infoPtr->hdpaPosY  = DPA_Create(10))) goto fail;
7539   if (!(infoPtr->hdpaColumns = DPA_Create(10))) goto fail;
7540
7541   /* initialize the icon sizes */
7542   set_icon_size(&infoPtr->iconSize, infoPtr->himlNormal, uView != LVS_ICON);
7543   set_icon_size(&infoPtr->iconStateSize, infoPtr->himlState, TRUE);
7544
7545   /* init item size to avoid division by 0 */
7546   LISTVIEW_UpdateItemSize (infoPtr);
7547   
7548   if (uView == LVS_REPORT)
7549   {
7550     if (!(LVS_NOCOLUMNHEADER & lpcs->style))
7551     {
7552       ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
7553     }
7554     else
7555     {
7556       /* set HDS_HIDDEN flag to hide the header bar */
7557       SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
7558                     GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
7559     }
7560   }
7561
7562   return 0;
7563
7564 fail:
7565     DestroyWindow(infoPtr->hwndHeader);
7566     ranges_destroy(infoPtr->selectionRanges);
7567     DPA_Destroy(infoPtr->hdpaItems);
7568     DPA_Destroy(infoPtr->hdpaPosX);
7569     DPA_Destroy(infoPtr->hdpaPosY);
7570     DPA_Destroy(infoPtr->hdpaColumns);
7571     Free(infoPtr);
7572     return -1;
7573 }
7574
7575 /***
7576  * DESCRIPTION:
7577  * Enables the listview control.
7578  *
7579  * PARAMETER(S):
7580  * [I] infoPtr : valid pointer to the listview structure
7581  * [I] bEnable : specifies whether to enable or disable the window
7582  *
7583  * RETURN:
7584  *   SUCCESS : TRUE
7585  *   FAILURE : FALSE
7586  */
7587 static BOOL LISTVIEW_Enable(LISTVIEW_INFO *infoPtr, BOOL bEnable)
7588 {
7589     if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED)
7590         InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
7591     return TRUE;
7592 }
7593
7594 /***
7595  * DESCRIPTION:
7596  * Erases the background of the listview control.
7597  *
7598  * PARAMETER(S):
7599  * [I] infoPtr : valid pointer to the listview structure
7600  * [I] hdc : device context handle
7601  *
7602  * RETURN:
7603  *   SUCCESS : TRUE
7604  *   FAILURE : FALSE
7605  */
7606 static inline BOOL LISTVIEW_EraseBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc)
7607 {
7608     RECT rc;
7609
7610     TRACE("(hdc=%p)\n", hdc);
7611
7612     if (!GetClipBox(hdc, &rc)) return FALSE;
7613
7614     return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
7615 }
7616         
7617
7618 /***
7619  * DESCRIPTION:
7620  * Helper function for LISTVIEW_[HV]Scroll *only*.
7621  * Performs vertical/horizontal scrolling by a give amount.
7622  *
7623  * PARAMETER(S):
7624  * [I] infoPtr : valid pointer to the listview structure
7625  * [I] dx : amount of horizontal scroll
7626  * [I] dy : amount of vertical scroll
7627  */
7628 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
7629 {
7630     /* now we can scroll the list */
7631     ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList, 
7632                    &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
7633     /* if we have focus, adjust rect */
7634     OffsetRect(&infoPtr->rcFocus, dx, dy);
7635     UpdateWindow(infoPtr->hwndSelf);
7636 }
7637
7638 /***
7639  * DESCRIPTION:
7640  * Performs vertical scrolling.
7641  *
7642  * PARAMETER(S):
7643  * [I] infoPtr : valid pointer to the listview structure
7644  * [I] nScrollCode : scroll code
7645  * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7646  * [I] hScrollWnd  : scrollbar control window handle
7647  *
7648  * RETURN:
7649  * Zero
7650  *
7651  * NOTES:
7652  *   SB_LINEUP/SB_LINEDOWN:
7653  *        for LVS_ICON, LVS_SMALLICON is 37 by experiment
7654  *        for LVS_REPORT is 1 line
7655  *        for LVS_LIST cannot occur
7656  *
7657  */
7658 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode, 
7659                                 INT nScrollDiff, HWND hScrollWnd)
7660 {
7661     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7662     INT nOldScrollPos, nNewScrollPos;
7663     SCROLLINFO scrollInfo;
7664     BOOL is_an_icon;
7665
7666     TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode, 
7667         debugscrollcode(nScrollCode), nScrollDiff);
7668
7669     if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7670
7671     scrollInfo.cbSize = sizeof(SCROLLINFO);
7672     scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7673
7674     is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
7675
7676     if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
7677
7678     nOldScrollPos = scrollInfo.nPos;
7679     switch (nScrollCode)
7680     {
7681     case SB_INTERNAL:
7682         break;
7683
7684     case SB_LINEUP:
7685         nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
7686         break;
7687
7688     case SB_LINEDOWN:
7689         nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
7690         break;
7691
7692     case SB_PAGEUP:
7693         nScrollDiff = -scrollInfo.nPage;
7694         break;
7695
7696     case SB_PAGEDOWN:
7697         nScrollDiff = scrollInfo.nPage;
7698         break;
7699
7700     case SB_THUMBPOSITION:
7701     case SB_THUMBTRACK:
7702         nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7703         break;
7704
7705     default:
7706         nScrollDiff = 0;
7707     }
7708
7709     /* quit right away if pos isn't changing */
7710     if (nScrollDiff == 0) return 0;
7711     
7712     /* calculate new position, and handle overflows */
7713     nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7714     if (nScrollDiff > 0) {
7715         if (nNewScrollPos < nOldScrollPos ||
7716             nNewScrollPos > scrollInfo.nMax)
7717             nNewScrollPos = scrollInfo.nMax;
7718     } else {
7719         if (nNewScrollPos > nOldScrollPos ||
7720             nNewScrollPos < scrollInfo.nMin)
7721             nNewScrollPos = scrollInfo.nMin;
7722     }
7723
7724     /* set the new position, and reread in case it changed */
7725     scrollInfo.fMask = SIF_POS;
7726     scrollInfo.nPos = nNewScrollPos;
7727     nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
7728     
7729     /* carry on only if it really changed */
7730     if (nNewScrollPos == nOldScrollPos) return 0;
7731     
7732     /* now adjust to client coordinates */
7733     nScrollDiff = nOldScrollPos - nNewScrollPos;
7734     if (uView == LVS_REPORT) nScrollDiff *= infoPtr->nItemHeight;
7735    
7736     /* and scroll the window */ 
7737     scroll_list(infoPtr, 0, nScrollDiff);
7738
7739     return 0;
7740 }
7741
7742 /***
7743  * DESCRIPTION:
7744  * Performs horizontal scrolling.
7745  *
7746  * PARAMETER(S):
7747  * [I] infoPtr : valid pointer to the listview structure
7748  * [I] nScrollCode : scroll code
7749  * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7750  * [I] hScrollWnd  : scrollbar control window handle
7751  *
7752  * RETURN:
7753  * Zero
7754  *
7755  * NOTES:
7756  *   SB_LINELEFT/SB_LINERIGHT:
7757  *        for LVS_ICON, LVS_SMALLICON  1 pixel
7758  *        for LVS_REPORT is 1 pixel
7759  *        for LVS_LIST  is 1 column --> which is a 1 because the
7760  *                                      scroll is based on columns not pixels
7761  *
7762  */
7763 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7764                                 INT nScrollDiff, HWND hScrollWnd)
7765 {
7766     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7767     INT nOldScrollPos, nNewScrollPos;
7768     SCROLLINFO scrollInfo;
7769
7770     TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode, 
7771         debugscrollcode(nScrollCode), nScrollDiff);
7772
7773     if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7774
7775     scrollInfo.cbSize = sizeof(SCROLLINFO);
7776     scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7777
7778     if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
7779
7780     nOldScrollPos = scrollInfo.nPos;
7781
7782     switch (nScrollCode)
7783     {
7784     case SB_INTERNAL:
7785         break;
7786
7787     case SB_LINELEFT:
7788         nScrollDiff = -1;
7789         break;
7790
7791     case SB_LINERIGHT:
7792         nScrollDiff = 1;
7793         break;
7794
7795     case SB_PAGELEFT:
7796         nScrollDiff = -scrollInfo.nPage;
7797         break;
7798
7799     case SB_PAGERIGHT:
7800         nScrollDiff = scrollInfo.nPage;
7801         break;
7802
7803     case SB_THUMBPOSITION:
7804     case SB_THUMBTRACK:
7805         nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7806         break;
7807
7808     default:
7809         nScrollDiff = 0;
7810     }
7811
7812     /* quit right away if pos isn't changing */
7813     if (nScrollDiff == 0) return 0;
7814     
7815     /* calculate new position, and handle overflows */
7816     nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7817     if (nScrollDiff > 0) {
7818         if (nNewScrollPos < nOldScrollPos ||
7819             nNewScrollPos > scrollInfo.nMax)
7820             nNewScrollPos = scrollInfo.nMax;
7821     } else {
7822         if (nNewScrollPos > nOldScrollPos ||
7823             nNewScrollPos < scrollInfo.nMin)
7824             nNewScrollPos = scrollInfo.nMin;
7825     }
7826
7827     /* set the new position, and reread in case it changed */
7828     scrollInfo.fMask = SIF_POS;
7829     scrollInfo.nPos = nNewScrollPos;
7830     nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
7831     
7832     /* carry on only if it really changed */
7833     if (nNewScrollPos == nOldScrollPos) return 0;
7834     
7835     if(uView == LVS_REPORT)
7836         LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
7837       
7838     /* now adjust to client coordinates */
7839     nScrollDiff = nOldScrollPos - nNewScrollPos;
7840     if (uView == LVS_LIST) nScrollDiff *= infoPtr->nItemWidth;
7841    
7842     /* and scroll the window */
7843     scroll_list(infoPtr, nScrollDiff, 0);
7844
7845   return 0;
7846 }
7847
7848 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
7849 {
7850     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7851     INT gcWheelDelta = 0;
7852     INT pulScrollLines = 3;
7853     SCROLLINFO scrollInfo;
7854
7855     TRACE("(wheelDelta=%d)\n", wheelDelta);
7856
7857     SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
7858     gcWheelDelta -= wheelDelta;
7859
7860     scrollInfo.cbSize = sizeof(SCROLLINFO);
7861     scrollInfo.fMask = SIF_POS;
7862
7863     switch(uView)
7864     {
7865     case LVS_ICON:
7866     case LVS_SMALLICON:
7867        /*
7868         *  listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
7869         *  should be fixed in the future.
7870         */
7871         LISTVIEW_VScroll(infoPtr, SB_INTERNAL, (gcWheelDelta < 0) ?
7872                 -LISTVIEW_SCROLL_ICON_LINE_SIZE : LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
7873         break;
7874
7875     case LVS_REPORT:
7876         if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
7877         {
7878             int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
7879             cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
7880             LISTVIEW_VScroll(infoPtr, SB_INTERNAL, cLineScroll, 0);
7881         }
7882         break;
7883
7884     case LVS_LIST:
7885         LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
7886         break;
7887     }
7888     return 0;
7889 }
7890
7891 /***
7892  * DESCRIPTION:
7893  * ???
7894  *
7895  * PARAMETER(S):
7896  * [I] infoPtr : valid pointer to the listview structure
7897  * [I] nVirtualKey : virtual key
7898  * [I] lKeyData : key data
7899  *
7900  * RETURN:
7901  * Zero
7902  */
7903 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
7904 {
7905   UINT uView =  infoPtr->dwStyle & LVS_TYPEMASK;
7906   INT nItem = -1;
7907   NMLVKEYDOWN nmKeyDown;
7908
7909   TRACE("(nVirtualKey=%d, lKeyData=%ld)\n", nVirtualKey, lKeyData);
7910
7911   /* send LVN_KEYDOWN notification */
7912   nmKeyDown.wVKey = nVirtualKey;
7913   nmKeyDown.flags = 0;
7914   notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
7915
7916   switch (nVirtualKey)
7917   {
7918   case VK_RETURN:
7919     if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
7920     {
7921       notify(infoPtr, NM_RETURN);
7922       notify(infoPtr, LVN_ITEMACTIVATE);
7923     }
7924     break;
7925
7926   case VK_HOME:
7927     if (infoPtr->nItemCount > 0)
7928       nItem = 0;
7929     break;
7930
7931   case VK_END:
7932     if (infoPtr->nItemCount > 0)
7933       nItem = infoPtr->nItemCount - 1;
7934     break;
7935
7936   case VK_LEFT:
7937     nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TOLEFT);
7938     break;
7939
7940   case VK_UP:
7941     nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_ABOVE);
7942     break;
7943
7944   case VK_RIGHT:
7945     nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TORIGHT);
7946     break;
7947
7948   case VK_DOWN:
7949     nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_BELOW);
7950     break;
7951
7952   case VK_PRIOR:
7953     if (uView == LVS_REPORT)
7954       nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr);
7955     else
7956       nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
7957                                     * LISTVIEW_GetCountPerRow(infoPtr);
7958     if(nItem < 0) nItem = 0;
7959     break;
7960
7961   case VK_NEXT:
7962     if (uView == LVS_REPORT)
7963       nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr);
7964     else
7965       nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
7966                                     * LISTVIEW_GetCountPerRow(infoPtr);
7967     if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
7968     break;
7969   }
7970
7971   if ((nItem != -1) && (nItem != infoPtr->nFocusedItem))
7972       LISTVIEW_KeySelection(infoPtr, nItem);
7973
7974   return 0;
7975 }
7976
7977 /***
7978  * DESCRIPTION:
7979  * Kills the focus.
7980  *
7981  * PARAMETER(S):
7982  * [I] infoPtr : valid pointer to the listview structure
7983  *
7984  * RETURN:
7985  * Zero
7986  */
7987 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
7988 {
7989     TRACE("()\n");
7990
7991     /* if we did not have the focus, there's nothing to do */
7992     if (!infoPtr->bFocus) return 0;
7993    
7994     /* send NM_KILLFOCUS notification */
7995     notify(infoPtr, NM_KILLFOCUS);
7996
7997     /* if we have a focus rectagle, get rid of it */
7998     LISTVIEW_ShowFocusRect(infoPtr, FALSE);
7999     
8000     /* set window focus flag */
8001     infoPtr->bFocus = FALSE;
8002
8003     /* invalidate the selected items before reseting focus flag */
8004     LISTVIEW_InvalidateSelectedItems(infoPtr);
8005     
8006     return 0;
8007 }
8008
8009 /***
8010  * DESCRIPTION:
8011  * Processes double click messages (left mouse button).
8012  *
8013  * PARAMETER(S):
8014  * [I] infoPtr : valid pointer to the listview structure
8015  * [I] wKey : key flag
8016  * [I] x,y : mouse coordinate
8017  *
8018  * RETURN:
8019  * Zero
8020  */
8021 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8022 {
8023     LVHITTESTINFO htInfo;
8024
8025     TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8026
8027     /* send NM_RELEASEDCAPTURE notification */
8028     notify(infoPtr, NM_RELEASEDCAPTURE);
8029
8030     htInfo.pt.x = x;
8031     htInfo.pt.y = y;
8032
8033     /* send NM_DBLCLK notification */
8034     LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
8035     notify_click(infoPtr, NM_DBLCLK, &htInfo);
8036
8037     /* To send the LVN_ITEMACTIVATE, it must be on an Item */
8038     if(htInfo.iItem != -1) notify_itemactivate(infoPtr,&htInfo);
8039
8040     return 0;
8041 }
8042
8043 /***
8044  * DESCRIPTION:
8045  * Processes mouse down messages (left mouse button).
8046  *
8047  * PARAMETER(S):
8048  * [I] infoPtr : valid pointer to the listview structure
8049  * [I] wKey : key flag
8050  * [I] x,y : mouse coordinate
8051  *
8052  * RETURN:
8053  * Zero
8054  */
8055 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8056 {
8057   LVHITTESTINFO lvHitTestInfo;
8058   static BOOL bGroupSelect = TRUE;
8059   POINT pt = { x, y };
8060   INT nItem;
8061
8062   TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8063
8064   /* send NM_RELEASEDCAPTURE notification */
8065   notify(infoPtr, NM_RELEASEDCAPTURE);
8066
8067   if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
8068
8069   /* set left button down flag and record the click position */
8070   infoPtr->bLButtonDown = TRUE;
8071   infoPtr->ptClickPos = pt;
8072
8073   lvHitTestInfo.pt.x = x;
8074   lvHitTestInfo.pt.y = y;
8075
8076   nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8077   TRACE("at %s, nItem=%d\n", debugpoint(&pt), nItem);
8078   infoPtr->nEditLabelItem = -1;
8079   if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8080   {
8081     if ((infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) && (lvHitTestInfo.flags & LVHT_ONITEMSTATEICON))
8082     {
8083         DWORD state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_STATEIMAGEMASK) >> 12;
8084         if(state == 1 || state == 2)
8085         {
8086             LVITEMW lvitem;
8087             state ^= 3;
8088             lvitem.state = state << 12;
8089             lvitem.stateMask = LVIS_STATEIMAGEMASK;
8090             LISTVIEW_SetItemState(infoPtr, nItem, &lvitem);
8091         }
8092         return 0;
8093     }
8094
8095     if (infoPtr->dwStyle & LVS_SINGLESEL)
8096     {
8097       if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8098         infoPtr->nEditLabelItem = nItem;
8099       else
8100         LISTVIEW_SetSelection(infoPtr, nItem);
8101     }
8102     else
8103     {
8104       if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
8105       {
8106         if (bGroupSelect)
8107         {
8108           LISTVIEW_AddGroupSelection(infoPtr, nItem);
8109           LISTVIEW_SetItemFocus(infoPtr, nItem);
8110           infoPtr->nSelectionMark = nItem;
8111         }
8112         else
8113         {
8114           LVITEMW item;
8115
8116           item.state = LVIS_SELECTED | LVIS_FOCUSED;
8117           item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8118
8119           LISTVIEW_SetItemState(infoPtr,nItem,&item);
8120           infoPtr->nSelectionMark = nItem;
8121         }
8122       }
8123       else if (wKey & MK_CONTROL)
8124       {
8125         LVITEMW item;
8126
8127         bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
8128         
8129         item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
8130         item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8131         LISTVIEW_SetItemState(infoPtr, nItem, &item);
8132         infoPtr->nSelectionMark = nItem;
8133       }
8134       else  if (wKey & MK_SHIFT)
8135       {
8136         LISTVIEW_SetGroupSelection(infoPtr, nItem);
8137       }
8138       else
8139       {
8140         if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8141           infoPtr->nEditLabelItem = nItem;
8142
8143         /* set selection (clears other pre-existing selections) */
8144         LISTVIEW_SetSelection(infoPtr, nItem);
8145       }
8146     }
8147   }
8148   else
8149   {
8150     /* remove all selections */
8151     LISTVIEW_DeselectAll(infoPtr);
8152     ReleaseCapture();
8153   }
8154
8155   return 0;
8156 }
8157
8158 /***
8159  * DESCRIPTION:
8160  * Processes mouse up messages (left mouse button).
8161  *
8162  * PARAMETER(S):
8163  * [I] infoPtr : valid pointer to the listview structure
8164  * [I] wKey : key flag
8165  * [I] x,y : mouse coordinate
8166  *
8167  * RETURN:
8168  * Zero
8169  */
8170 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8171 {
8172     LVHITTESTINFO lvHitTestInfo;
8173     
8174     TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8175
8176     if (!infoPtr->bLButtonDown) return 0;
8177
8178     lvHitTestInfo.pt.x = x;
8179     lvHitTestInfo.pt.y = y;
8180
8181     /* send NM_CLICK notification */
8182     LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8183     notify_click(infoPtr, NM_CLICK, &lvHitTestInfo);
8184
8185     /* set left button flag */
8186     infoPtr->bLButtonDown = FALSE;
8187
8188     /* if we clicked on a selected item, edit the label */
8189     if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
8190         LISTVIEW_EditLabelT(infoPtr, lvHitTestInfo.iItem, TRUE);
8191
8192     return 0;
8193 }
8194
8195 /***
8196  * DESCRIPTION:
8197  * Destroys the listview control (called after WM_DESTROY).
8198  *
8199  * PARAMETER(S):
8200  * [I] infoPtr : valid pointer to the listview structure
8201  *
8202  * RETURN:
8203  * Zero
8204  */
8205 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
8206 {
8207   TRACE("()\n");
8208
8209   /* delete all items */
8210   LISTVIEW_DeleteAllItems(infoPtr);
8211
8212   /* destroy data structure */
8213   DPA_Destroy(infoPtr->hdpaItems);
8214   DPA_Destroy(infoPtr->hdpaPosX);
8215   DPA_Destroy(infoPtr->hdpaPosY);
8216   DPA_Destroy(infoPtr->hdpaColumns);
8217   ranges_destroy(infoPtr->selectionRanges);
8218
8219   /* destroy image lists */
8220   if (!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS))
8221   {
8222       if (infoPtr->himlNormal)
8223           ImageList_Destroy(infoPtr->himlNormal);
8224       if (infoPtr->himlSmall)
8225           ImageList_Destroy(infoPtr->himlSmall);
8226       if (infoPtr->himlState)
8227           ImageList_Destroy(infoPtr->himlState);
8228   }
8229
8230   /* destroy font, bkgnd brush */
8231   infoPtr->hFont = 0;
8232   if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
8233   if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
8234
8235   SetWindowLongPtrW(infoPtr->hwndSelf, 0, 0);
8236
8237   /* free listview info pointer*/
8238   Free(infoPtr);
8239
8240   return 0;
8241 }
8242
8243 /***
8244  * DESCRIPTION:
8245  * Handles notifications from header.
8246  *
8247  * PARAMETER(S):
8248  * [I] infoPtr : valid pointer to the listview structure
8249  * [I] nCtrlId : control identifier
8250  * [I] lpnmh : notification information
8251  *
8252  * RETURN:
8253  * Zero
8254  */
8255 static LRESULT LISTVIEW_HeaderNotification(LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
8256 {
8257     UINT uView =  infoPtr->dwStyle & LVS_TYPEMASK;
8258     
8259     TRACE("(lpnmh=%p)\n", lpnmh);
8260
8261     if (!lpnmh || lpnmh->iItem < 0 || lpnmh->iItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
8262     
8263     switch (lpnmh->hdr.code)
8264     {    
8265         case HDN_TRACKW:
8266         case HDN_TRACKA:
8267         case HDN_ITEMCHANGEDW:
8268         case HDN_ITEMCHANGEDA:
8269         {
8270             COLUMN_INFO *lpColumnInfo;
8271             INT dx, cxy;
8272
8273             if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
8274             {
8275                 HDITEMW hdi;
8276     
8277                 hdi.mask = HDI_WIDTH;
8278                 if (!Header_GetItemW(infoPtr->hwndHeader, lpnmh->iItem, (LPARAM)&hdi)) return 0;
8279                 cxy = hdi.cxy;
8280             }
8281             else
8282                 cxy = lpnmh->pitem->cxy;
8283             
8284             /* determine how much we change since the last know position */
8285             lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
8286             dx = cxy - (lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
8287             if (dx != 0)
8288             {
8289                 RECT rcCol = lpColumnInfo->rcHeader;
8290
8291                 lpColumnInfo->rcHeader.right += dx;
8292                 LISTVIEW_ScrollColumns(infoPtr, lpnmh->iItem + 1, dx);
8293                 LISTVIEW_UpdateItemSize(infoPtr);
8294                 if (uView == LVS_REPORT && is_redrawing(infoPtr))
8295                 {
8296                     /* this trick works for left aligned columns only */
8297                     if ((lpColumnInfo->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
8298                     {
8299                         rcCol.right = min (rcCol.right, lpColumnInfo->rcHeader.right);
8300                         rcCol.left = max (rcCol.left, rcCol.right - 3 * infoPtr->ntmAveCharWidth);
8301                     }
8302                     rcCol.top = infoPtr->rcList.top;
8303                     rcCol.bottom = infoPtr->rcList.bottom;
8304                     LISTVIEW_InvalidateRect(infoPtr, &rcCol);
8305                 }
8306             }
8307         }
8308         break;
8309
8310         case HDN_ITEMCLICKW:
8311         case HDN_ITEMCLICKA:
8312         {
8313             /* Handle sorting by Header Column */
8314             NMLISTVIEW nmlv;
8315
8316             ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8317             nmlv.iItem = -1;
8318             nmlv.iSubItem = lpnmh->iItem;
8319             notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
8320         }
8321         break;
8322
8323         case HDN_DIVIDERDBLCLICKW:
8324         case HDN_DIVIDERDBLCLICKA:
8325             LISTVIEW_SetColumnWidth(infoPtr, lpnmh->iItem, LVSCW_AUTOSIZE);
8326             break;
8327     }
8328
8329     return 0;
8330 }
8331
8332 /***
8333  * DESCRIPTION:
8334  * Determines the type of structure to use.
8335  *
8336  * PARAMETER(S):
8337  * [I] infoPtr : valid pointer to the listview structureof the sender
8338  * [I] hwndFrom : listview window handle
8339  * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
8340  *
8341  * RETURN:
8342  * Zero
8343  */
8344 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
8345 {
8346     TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom, nCommand);
8347
8348     if (nCommand != NF_REQUERY) return 0;
8349     
8350     infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY);
8351     
8352     return 0;
8353 }
8354
8355 /***
8356  * DESCRIPTION:
8357  * Paints/Repaints the listview control.
8358  *
8359  * PARAMETER(S):
8360  * [I] infoPtr : valid pointer to the listview structure
8361  * [I] hdc : device context handle
8362  *
8363  * RETURN:
8364  * Zero
8365  */
8366 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
8367 {
8368     TRACE("(hdc=%p)\n", hdc);
8369
8370     if (infoPtr->bNoItemMetrics && infoPtr->nItemCount)
8371     {
8372         UINT uView =  infoPtr->dwStyle & LVS_TYPEMASK;
8373         
8374         infoPtr->bNoItemMetrics = FALSE;
8375         LISTVIEW_UpdateItemSize(infoPtr);
8376         if (uView == LVS_ICON || uView == LVS_SMALLICON)
8377             LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8378         LISTVIEW_UpdateScroll(infoPtr);
8379     }
8380     if (hdc) 
8381         LISTVIEW_Refresh(infoPtr, hdc);
8382     else
8383     {
8384         PAINTSTRUCT ps;
8385
8386         hdc = BeginPaint(infoPtr->hwndSelf, &ps);
8387         if (!hdc) return 1;
8388         if (ps.fErase) LISTVIEW_FillBkgnd(infoPtr, hdc, &ps.rcPaint);
8389         LISTVIEW_Refresh(infoPtr, hdc);
8390         EndPaint(infoPtr->hwndSelf, &ps);
8391     }
8392
8393     return 0;
8394 }
8395
8396
8397 /***
8398  * DESCRIPTION:
8399  * Paints/Repaints the listview control.
8400  *
8401  * PARAMETER(S):
8402  * [I] infoPtr : valid pointer to the listview structure
8403  * [I] hdc : device context handle
8404  * [I] options : drawing options
8405  *
8406  * RETURN:
8407  * Zero
8408  */
8409 static LRESULT LISTVIEW_PrintClient(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD options)
8410 {
8411     FIXME("Partial Stub: (hdc=%p options=0x%08lx)\n", hdc, options);
8412
8413     if ((options & PRF_CHECKVISIBLE) && !IsWindowVisible(infoPtr->hwndSelf))
8414         return 0;
8415
8416     if (options & PRF_ERASEBKGND)
8417         LISTVIEW_EraseBkgnd(infoPtr, hdc);
8418
8419     if (options & PRF_CLIENT)
8420         LISTVIEW_Paint(infoPtr, hdc);
8421
8422     return 0;
8423 }
8424
8425
8426 /***
8427  * DESCRIPTION:
8428  * Processes double click messages (right mouse button).
8429  *
8430  * PARAMETER(S):
8431  * [I] infoPtr : valid pointer to the listview structure
8432  * [I] wKey : key flag
8433  * [I] x,y : mouse coordinate
8434  *
8435  * RETURN:
8436  * Zero
8437  */
8438 static LRESULT LISTVIEW_RButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8439 {
8440     LVHITTESTINFO lvHitTestInfo;
8441     
8442     TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
8443
8444     /* send NM_RELEASEDCAPTURE notification */
8445     notify(infoPtr, NM_RELEASEDCAPTURE);
8446
8447     /* send NM_RDBLCLK notification */
8448     lvHitTestInfo.pt.x = x;
8449     lvHitTestInfo.pt.y = y;
8450     LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8451     notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
8452
8453     return 0;
8454 }
8455
8456 /***
8457  * DESCRIPTION:
8458  * Processes mouse down messages (right mouse button).
8459  *
8460  * PARAMETER(S):
8461  * [I] infoPtr : valid pointer to the listview structure
8462  * [I] wKey : key flag
8463  * [I] x,y : mouse coordinate
8464  *
8465  * RETURN:
8466  * Zero
8467  */
8468 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8469 {
8470     LVHITTESTINFO lvHitTestInfo;
8471     INT nItem;
8472
8473     TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
8474
8475     /* send NM_RELEASEDCAPTURE notification */
8476     notify(infoPtr, NM_RELEASEDCAPTURE);
8477
8478     /* make sure the listview control window has the focus */
8479     if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
8480
8481     /* set right button down flag */
8482     infoPtr->bRButtonDown = TRUE;
8483
8484     /* determine the index of the selected item */
8485     lvHitTestInfo.pt.x = x;
8486     lvHitTestInfo.pt.y = y;
8487     nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8488   
8489     if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8490     {
8491         LISTVIEW_SetItemFocus(infoPtr, nItem);
8492         if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
8493             !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8494             LISTVIEW_SetSelection(infoPtr, nItem);
8495     }
8496     else
8497     {
8498         LISTVIEW_DeselectAll(infoPtr);
8499     }
8500
8501     return 0;
8502 }
8503
8504 /***
8505  * DESCRIPTION:
8506  * Processes mouse up messages (right mouse button).
8507  *
8508  * PARAMETER(S):
8509  * [I] infoPtr : valid pointer to the listview structure
8510  * [I] wKey : key flag
8511  * [I] x,y : mouse coordinate
8512  *
8513  * RETURN:
8514  * Zero
8515  */
8516 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8517 {
8518     LVHITTESTINFO lvHitTestInfo;
8519     POINT pt;
8520
8521     TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
8522
8523     if (!infoPtr->bRButtonDown) return 0;
8524  
8525     /* set button flag */
8526     infoPtr->bRButtonDown = FALSE;
8527
8528     /* Send NM_RClICK notification */
8529     lvHitTestInfo.pt.x = x;
8530     lvHitTestInfo.pt.y = y;
8531     LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8532     notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo);
8533
8534     /* Change to screen coordinate for WM_CONTEXTMENU */
8535     pt = lvHitTestInfo.pt;
8536     ClientToScreen(infoPtr->hwndSelf, &pt);
8537
8538     /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
8539     SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
8540                  (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
8541
8542     return 0;
8543 }
8544
8545
8546 /***
8547  * DESCRIPTION:
8548  * Sets the cursor.
8549  *
8550  * PARAMETER(S):
8551  * [I] infoPtr : valid pointer to the listview structure
8552  * [I] hwnd : window handle of window containing the cursor
8553  * [I] nHittest : hit-test code
8554  * [I] wMouseMsg : ideintifier of the mouse message
8555  *
8556  * RETURN:
8557  * TRUE if cursor is set
8558  * FALSE otherwise
8559  */
8560 static BOOL LISTVIEW_SetCursor(LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
8561 {
8562     LVHITTESTINFO lvHitTestInfo;
8563
8564     if(!(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)) return FALSE;
8565
8566     if(!infoPtr->hHotCursor)  return FALSE;
8567
8568     GetCursorPos(&lvHitTestInfo.pt);
8569     if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) return FALSE;
8570
8571     SetCursor(infoPtr->hHotCursor);
8572
8573     return TRUE;
8574 }
8575
8576 /***
8577  * DESCRIPTION:
8578  * Sets the focus.
8579  *
8580  * PARAMETER(S):
8581  * [I] infoPtr : valid pointer to the listview structure
8582  * [I] hwndLoseFocus : handle of previously focused window
8583  *
8584  * RETURN:
8585  * Zero
8586  */
8587 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
8588 {
8589     TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus);
8590
8591     /* if we have the focus already, there's nothing to do */
8592     if (infoPtr->bFocus) return 0;
8593    
8594     /* send NM_SETFOCUS notification */
8595     notify(infoPtr, NM_SETFOCUS);
8596
8597     /* set window focus flag */
8598     infoPtr->bFocus = TRUE;
8599
8600     /* put the focus rect back on */
8601     LISTVIEW_ShowFocusRect(infoPtr, TRUE);
8602
8603     /* redraw all visible selected items */
8604     LISTVIEW_InvalidateSelectedItems(infoPtr);
8605
8606     return 0;
8607 }
8608
8609 /***
8610  * DESCRIPTION:
8611  * Sets the font.
8612  *
8613  * PARAMETER(S):
8614  * [I] infoPtr : valid pointer to the listview structure
8615  * [I] fRedraw : font handle
8616  * [I] fRedraw : redraw flag
8617  *
8618  * RETURN:
8619  * Zero
8620  */
8621 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
8622 {
8623     HFONT oldFont = infoPtr->hFont;
8624
8625     TRACE("(hfont=%p,redraw=%hu)\n", hFont, fRedraw);
8626
8627     infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
8628     if (infoPtr->hFont == oldFont) return 0;
8629     
8630     LISTVIEW_SaveTextMetrics(infoPtr);
8631
8632     if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT)
8633         SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
8634
8635     if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
8636
8637     return 0;
8638 }
8639
8640 /***
8641  * DESCRIPTION:
8642  * Message handling for WM_SETREDRAW.
8643  * For the Listview, it invalidates the entire window (the doc specifies otherwise)
8644  *
8645  * PARAMETER(S):
8646  * [I] infoPtr : valid pointer to the listview structure
8647  * [I] bRedraw: state of redraw flag
8648  *
8649  * RETURN:
8650  * DefWinProc return value
8651  */
8652 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
8653 {
8654     TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr->bRedraw, bRedraw);
8655
8656     /* we cannot use straight equality here because _any_ non-zero value is TRUE */
8657     if ((infoPtr->bRedraw && bRedraw) || (!infoPtr->bRedraw && !bRedraw)) return 0;
8658
8659     infoPtr->bRedraw = bRedraw;
8660
8661     if(!bRedraw) return 0;
8662     
8663     if (is_autoarrange(infoPtr))
8664         LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8665     LISTVIEW_UpdateScroll(infoPtr);
8666
8667     /* despite what the WM_SETREDRAW docs says, apps expect us
8668      * to invalidate the listview here... stupid! */
8669     LISTVIEW_InvalidateList(infoPtr);
8670
8671     return 0;
8672 }
8673
8674 /***
8675  * DESCRIPTION:
8676  * Resizes the listview control. This function processes WM_SIZE
8677  * messages.  At this time, the width and height are not used.
8678  *
8679  * PARAMETER(S):
8680  * [I] infoPtr : valid pointer to the listview structure
8681  * [I] Width : new width
8682  * [I] Height : new height
8683  *
8684  * RETURN:
8685  * Zero
8686  */
8687 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
8688 {
8689     RECT rcOld = infoPtr->rcList;
8690
8691     TRACE("(width=%d, height=%d)\n", Width, Height);
8692
8693     LISTVIEW_UpdateSize(infoPtr);
8694     if (EqualRect(&rcOld, &infoPtr->rcList)) return 0;
8695   
8696     /* do not bother with display related stuff if we're not redrawing */ 
8697     if (!is_redrawing(infoPtr)) return 0;
8698     
8699     if (is_autoarrange(infoPtr)) 
8700         LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8701
8702     LISTVIEW_UpdateScroll(infoPtr);
8703
8704     /* refresh all only for lists whose height changed significantly */
8705     if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_LIST && 
8706         (rcOld.bottom - rcOld.top) / infoPtr->nItemHeight !=
8707         (infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight)
8708         LISTVIEW_InvalidateList(infoPtr);
8709
8710   return 0;
8711 }
8712
8713 /***
8714  * DESCRIPTION:
8715  * Sets the size information.
8716  *
8717  * PARAMETER(S):
8718  * [I] infoPtr : valid pointer to the listview structure
8719  *
8720  * RETURN:
8721  *  None
8722  */
8723 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
8724 {
8725     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8726
8727     TRACE("uView=%d, rcList(old)=%s\n", uView, debugrect(&infoPtr->rcList));
8728     
8729     GetClientRect(infoPtr->hwndSelf, &infoPtr->rcList);
8730
8731     if (uView == LVS_LIST)
8732     {
8733         /* Apparently the "LIST" style is supposed to have the same
8734          * number of items in a column even if there is no scroll bar.
8735          * Since if a scroll bar already exists then the bottom is already
8736          * reduced, only reduce if the scroll bar does not currently exist.
8737          * The "2" is there to mimic the native control. I think it may be
8738          * related to either padding or edges.  (GLA 7/2002)
8739          */
8740         if (!(GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_HSCROLL))
8741             infoPtr->rcList.bottom -= GetSystemMetrics(SM_CYHSCROLL);
8742         infoPtr->rcList.bottom = max (infoPtr->rcList.bottom - 2, 0);
8743     }
8744     else if (uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_NOCOLUMNHEADER))
8745     {
8746         HDLAYOUT hl;
8747         WINDOWPOS wp;
8748
8749         hl.prc = &infoPtr->rcList;
8750         hl.pwpos = &wp;
8751         Header_Layout(infoPtr->hwndHeader, &hl);
8752
8753         SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8754
8755         infoPtr->rcList.top = max(wp.cy, 0);
8756     }
8757
8758     TRACE("  rcList=%s\n", debugrect(&infoPtr->rcList));
8759 }
8760
8761 /***
8762  * DESCRIPTION:
8763  * Processes WM_STYLECHANGED messages.
8764  *
8765  * PARAMETER(S):
8766  * [I] infoPtr : valid pointer to the listview structure
8767  * [I] wStyleType : window style type (normal or extended)
8768  * [I] lpss : window style information
8769  *
8770  * RETURN:
8771  * Zero
8772  */
8773 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
8774                                  const STYLESTRUCT *lpss)
8775 {
8776     UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
8777     UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
8778
8779     TRACE("(styletype=%x, styleOld=0x%08lx, styleNew=0x%08lx)\n",
8780           wStyleType, lpss->styleOld, lpss->styleNew);
8781
8782     if (wStyleType != GWL_STYLE) return 0;
8783   
8784     /* FIXME: if LVS_NOSORTHEADER changed, update header */
8785     /*        what if LVS_OWNERDATA changed? */
8786     /*        or LVS_SINGLESEL */
8787     /*        or LVS_SORT{AS,DES}CENDING */
8788
8789     infoPtr->dwStyle = lpss->styleNew;
8790
8791     if (((lpss->styleOld & WS_HSCROLL) != 0)&&
8792         ((lpss->styleNew & WS_HSCROLL) == 0))
8793        ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
8794
8795     if (((lpss->styleOld & WS_VSCROLL) != 0)&&
8796         ((lpss->styleNew & WS_VSCROLL) == 0))
8797        ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
8798
8799     if (uNewView != uOldView)
8800     {
8801         SIZE oldIconSize = infoPtr->iconSize;
8802         HIMAGELIST himl;
8803     
8804         SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8805         ShowWindow(infoPtr->hwndHeader, SW_HIDE);
8806
8807         ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
8808         SetRectEmpty(&infoPtr->rcFocus);
8809
8810         himl = (uNewView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
8811         set_icon_size(&infoPtr->iconSize, himl, uNewView != LVS_ICON);
8812     
8813         if (uNewView == LVS_ICON)
8814         {
8815             if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
8816             {
8817                 TRACE("icon old size=(%ld,%ld), new size=(%ld,%ld)\n",
8818                       oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
8819                 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
8820             }
8821         }
8822         else if (uNewView == LVS_REPORT)
8823         {
8824             HDLAYOUT hl;
8825             WINDOWPOS wp;
8826
8827             hl.prc = &infoPtr->rcList;
8828             hl.pwpos = &wp;
8829             Header_Layout(infoPtr->hwndHeader, &hl);
8830             SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8831         }
8832
8833         LISTVIEW_UpdateItemSize(infoPtr);
8834     }
8835
8836     if (uNewView == LVS_REPORT)
8837         ShowWindow(infoPtr->hwndHeader, (lpss->styleNew & LVS_NOCOLUMNHEADER) ? SW_HIDE : SW_SHOWNORMAL);
8838      
8839     if ( (uNewView == LVS_ICON || uNewView == LVS_SMALLICON) &&
8840          (uNewView != uOldView || ((lpss->styleNew ^ lpss->styleOld) & LVS_ALIGNMASK)) )
8841          LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8842
8843     /* update the size of the client area */
8844     LISTVIEW_UpdateSize(infoPtr);
8845
8846     /* add scrollbars if needed */
8847     LISTVIEW_UpdateScroll(infoPtr);
8848
8849     /* invalidate client area + erase background */
8850     LISTVIEW_InvalidateList(infoPtr);
8851
8852     return 0;
8853 }
8854
8855 /***
8856  * DESCRIPTION:
8857  * Window procedure of the listview control.
8858  *
8859  */
8860 static LRESULT WINAPI
8861 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
8862 {
8863   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
8864
8865   TRACE("(uMsg=%x wParam=%x lParam=%lx)\n", uMsg, wParam, lParam);
8866
8867   if (!infoPtr && (uMsg != WM_CREATE))
8868     return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8869
8870   switch (uMsg)
8871   {
8872   case LVM_APPROXIMATEVIEWRECT:
8873     return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
8874                                         LOWORD(lParam), HIWORD(lParam));
8875   case LVM_ARRANGE:
8876     return LISTVIEW_Arrange(infoPtr, (INT)wParam);
8877
8878 /* case LVM_CANCELEDITLABEL: */
8879
8880   case LVM_CREATEDRAGIMAGE:
8881     return (LRESULT)LISTVIEW_CreateDragImage(infoPtr, (INT)wParam, (LPPOINT)lParam);
8882
8883   case LVM_DELETEALLITEMS:
8884     return LISTVIEW_DeleteAllItems(infoPtr);
8885
8886   case LVM_DELETECOLUMN:
8887     return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
8888
8889   case LVM_DELETEITEM:
8890     return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
8891
8892   case LVM_EDITLABELW:
8893     return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
8894
8895   case LVM_EDITLABELA:
8896     return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
8897
8898   /* case LVM_ENABLEGROUPVIEW: */
8899
8900   case LVM_ENSUREVISIBLE:
8901     return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
8902
8903   case LVM_FINDITEMW:
8904     return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
8905
8906   case LVM_FINDITEMA:
8907     return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
8908
8909   case LVM_GETBKCOLOR:
8910     return infoPtr->clrBk;
8911
8912   /* case LVM_GETBKIMAGE: */
8913
8914   case LVM_GETCALLBACKMASK:
8915     return infoPtr->uCallbackMask;
8916
8917   case LVM_GETCOLUMNA:
8918     return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8919
8920   case LVM_GETCOLUMNW:
8921     return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8922
8923   case LVM_GETCOLUMNORDERARRAY:
8924     return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
8925
8926   case LVM_GETCOLUMNWIDTH:
8927     return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
8928
8929   case LVM_GETCOUNTPERPAGE:
8930     return LISTVIEW_GetCountPerPage(infoPtr);
8931
8932   case LVM_GETEDITCONTROL:
8933     return (LRESULT)infoPtr->hwndEdit;
8934
8935   case LVM_GETEXTENDEDLISTVIEWSTYLE:
8936     return infoPtr->dwLvExStyle;
8937
8938   /* case LVM_GETGROUPINFO: */
8939
8940   /* case LVM_GETGROUPMETRICS: */
8941
8942   case LVM_GETHEADER:
8943     return (LRESULT)infoPtr->hwndHeader;
8944
8945   case LVM_GETHOTCURSOR:
8946     return (LRESULT)infoPtr->hHotCursor;
8947
8948   case LVM_GETHOTITEM:
8949     return infoPtr->nHotItem;
8950
8951   case LVM_GETHOVERTIME:
8952     return infoPtr->dwHoverTime;
8953
8954   case LVM_GETIMAGELIST:
8955     return (LRESULT)LISTVIEW_GetImageList(infoPtr, (INT)wParam);
8956
8957   /* case LVM_GETINSERTMARK: */
8958
8959   /* case LVM_GETINSERTMARKCOLOR: */
8960
8961   /* case LVM_GETINSERTMARKRECT: */
8962
8963   case LVM_GETISEARCHSTRINGA:
8964   case LVM_GETISEARCHSTRINGW:
8965     FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
8966     return FALSE;
8967
8968   case LVM_GETITEMA:
8969     return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE);
8970
8971   case LVM_GETITEMW:
8972     return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE);
8973
8974   case LVM_GETITEMCOUNT:
8975     return infoPtr->nItemCount;
8976
8977   case LVM_GETITEMPOSITION:
8978     return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
8979
8980   case LVM_GETITEMRECT:
8981     return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
8982
8983   case LVM_GETITEMSPACING:
8984     return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
8985
8986   case LVM_GETITEMSTATE:
8987     return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
8988
8989   case LVM_GETITEMTEXTA:
8990     return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
8991
8992   case LVM_GETITEMTEXTW:
8993     return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
8994
8995   case LVM_GETNEXTITEM:
8996     return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
8997
8998   case LVM_GETNUMBEROFWORKAREAS:
8999     FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
9000     return 1;
9001
9002   case LVM_GETORIGIN:
9003     if (!lParam) return FALSE;
9004     LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
9005     return TRUE;
9006
9007   /* case LVM_GETOUTLINECOLOR: */
9008
9009   /* case LVM_GETSELECTEDCOLUMN: */
9010
9011   case LVM_GETSELECTEDCOUNT:
9012     return LISTVIEW_GetSelectedCount(infoPtr);
9013
9014   case LVM_GETSELECTIONMARK:
9015     return infoPtr->nSelectionMark;
9016
9017   case LVM_GETSTRINGWIDTHA:
9018     return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
9019
9020   case LVM_GETSTRINGWIDTHW:
9021     return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
9022
9023   case LVM_GETSUBITEMRECT:
9024     return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
9025
9026   case LVM_GETTEXTBKCOLOR:
9027     return infoPtr->clrTextBk;
9028
9029   case LVM_GETTEXTCOLOR:
9030     return infoPtr->clrText;
9031
9032   /* case LVM_GETTILEINFO: */
9033
9034   /* case LVM_GETTILEVIEWINFO: */
9035
9036   case LVM_GETTOOLTIPS:
9037     if( !infoPtr->hwndToolTip )
9038         infoPtr->hwndToolTip = COMCTL32_CreateToolTip( hwnd );
9039     return (LRESULT)infoPtr->hwndToolTip;
9040
9041   case LVM_GETTOPINDEX:
9042     return LISTVIEW_GetTopIndex(infoPtr);
9043
9044   /*case LVM_GETUNICODEFORMAT:
9045     FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
9046     return FALSE;*/
9047
9048   /* case LVM_GETVIEW: */
9049
9050   case LVM_GETVIEWRECT:
9051     return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
9052
9053   case LVM_GETWORKAREAS:
9054     FIXME("LVM_GETWORKAREAS: unimplemented\n");
9055     return FALSE;
9056
9057   /* case LVM_HASGROUP: */
9058
9059   case LVM_HITTEST:
9060     return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, FALSE);
9061
9062   case LVM_INSERTCOLUMNA:
9063     return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9064
9065   case LVM_INSERTCOLUMNW:
9066     return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9067
9068   /* case LVM_INSERTGROUP: */
9069
9070   /* case LVM_INSERTGROUPSORTED: */
9071
9072   case LVM_INSERTITEMA:
9073     return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
9074
9075   case LVM_INSERTITEMW:
9076     return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
9077
9078   /* case LVM_INSERTMARKHITTEST: */
9079
9080   /* case LVM_ISGROUPVIEWENABLED: */
9081
9082   /* case LVM_MAPIDTOINDEX: */
9083
9084   /* case LVM_MAPINDEXTOID: */
9085
9086   /* case LVM_MOVEGROUP: */
9087
9088   /* case LVM_MOVEITEMTOGROUP: */
9089
9090   case LVM_REDRAWITEMS:
9091     return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
9092
9093   /* case LVM_REMOVEALLGROUPS: */
9094
9095   /* case LVM_REMOVEGROUP: */
9096
9097   case LVM_SCROLL:
9098     return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
9099
9100   case LVM_SETBKCOLOR:
9101     return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
9102
9103   /* case LVM_SETBKIMAGE: */
9104
9105   case LVM_SETCALLBACKMASK:
9106     infoPtr->uCallbackMask = (UINT)wParam;
9107     return TRUE;
9108
9109   case LVM_SETCOLUMNA:
9110     return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9111
9112   case LVM_SETCOLUMNW:
9113     return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9114
9115   case LVM_SETCOLUMNORDERARRAY:
9116     return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
9117
9118   case LVM_SETCOLUMNWIDTH:
9119     return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, (short)LOWORD(lParam));
9120
9121   case LVM_SETEXTENDEDLISTVIEWSTYLE:
9122     return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
9123
9124   /* case LVM_SETGROUPINFO: */
9125
9126   /* case LVM_SETGROUPMETRICS: */
9127
9128   case LVM_SETHOTCURSOR:
9129     return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
9130
9131   case LVM_SETHOTITEM:
9132     return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
9133
9134   case LVM_SETHOVERTIME:
9135     return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
9136
9137   case LVM_SETICONSPACING:
9138     return LISTVIEW_SetIconSpacing(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
9139
9140   case LVM_SETIMAGELIST:
9141     return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
9142
9143   /* case LVM_SETINFOTIP: */
9144
9145   /* case LVM_SETINSERTMARK: */
9146
9147   /* case LVM_SETINSERTMARKCOLOR: */
9148
9149   case LVM_SETITEMA:
9150     return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
9151
9152   case LVM_SETITEMW:
9153     return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
9154
9155   case LVM_SETITEMCOUNT:
9156     return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
9157
9158   case LVM_SETITEMPOSITION:
9159     {
9160         POINT pt;
9161         pt.x = (short)LOWORD(lParam);
9162         pt.y = (short)HIWORD(lParam);
9163         return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, pt);
9164     }
9165
9166   case LVM_SETITEMPOSITION32:
9167     if (lParam == 0) return FALSE;
9168     return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, *((POINT*)lParam));
9169
9170   case LVM_SETITEMSTATE:
9171     return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
9172
9173   case LVM_SETITEMTEXTA:
9174     return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9175
9176   case LVM_SETITEMTEXTW:
9177     return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9178
9179   /* case LVM_SETOUTLINECOLOR: */
9180
9181   /* case LVM_SETSELECTEDCOLUMN: */
9182
9183   case LVM_SETSELECTIONMARK:
9184     return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
9185
9186   case LVM_SETTEXTBKCOLOR:
9187     return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
9188
9189   case LVM_SETTEXTCOLOR:
9190     return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
9191
9192   /* case LVM_SETTILEINFO: */
9193
9194   /* case LVM_SETTILEVIEWINFO: */
9195
9196   /* case LVM_SETTILEWIDTH: */
9197
9198   case LVM_SETTOOLTIPS:
9199     return (LRESULT)LISTVIEW_SetToolTips(infoPtr, (HWND)lParam);
9200
9201   /* case LVM_SETUNICODEFORMAT: */
9202
9203   /* case LVM_SETVIEW: */
9204
9205   /* case LVM_SETWORKAREAS: */
9206
9207   /* case LVM_SORTGROUPS: */
9208
9209   case LVM_SORTITEMS:
9210     return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
9211
9212   /* LVM_SORTITEMSEX: */
9213
9214   case LVM_SUBITEMHITTEST:
9215     return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
9216
9217   case LVM_UPDATE:
9218     return LISTVIEW_Update(infoPtr, (INT)wParam);
9219
9220   case WM_CHAR:
9221     return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
9222
9223   case WM_COMMAND:
9224     return LISTVIEW_Command(infoPtr, wParam, lParam);
9225
9226   case WM_CREATE:
9227     return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
9228
9229   case WM_ENABLE:
9230     return LISTVIEW_Enable(infoPtr, (BOOL)wParam);
9231
9232   case WM_ERASEBKGND:
9233     return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
9234
9235   case WM_GETDLGCODE:
9236     return DLGC_WANTCHARS | DLGC_WANTARROWS;
9237
9238   case WM_GETFONT:
9239     return (LRESULT)infoPtr->hFont;
9240
9241   case WM_HSCROLL:
9242     return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
9243
9244   case WM_KEYDOWN:
9245     return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
9246
9247   case WM_KILLFOCUS:
9248     return LISTVIEW_KillFocus(infoPtr);
9249
9250   case WM_LBUTTONDBLCLK:
9251     return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9252
9253   case WM_LBUTTONDOWN:
9254     return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9255
9256   case WM_LBUTTONUP:
9257     return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9258
9259   case WM_MOUSEMOVE:
9260     return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9261
9262   case WM_MOUSEHOVER:
9263     return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9264
9265   case WM_NCDESTROY:
9266     return LISTVIEW_NCDestroy(infoPtr);
9267
9268   case WM_NOTIFY:
9269     if (lParam && ((LPNMHDR)lParam)->hwndFrom == infoPtr->hwndHeader)
9270         return LISTVIEW_HeaderNotification(infoPtr, (LPNMHEADERW)lParam);
9271     else return 0;
9272
9273   case WM_NOTIFYFORMAT:
9274     return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
9275
9276   case WM_PRINTCLIENT:
9277     return LISTVIEW_PrintClient(infoPtr, (HDC)wParam, (DWORD)lParam);
9278
9279   case WM_PAINT:
9280     return LISTVIEW_Paint(infoPtr, (HDC)wParam);
9281
9282   case WM_RBUTTONDBLCLK:
9283     return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9284
9285   case WM_RBUTTONDOWN:
9286     return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9287
9288   case WM_RBUTTONUP:
9289     return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9290
9291   case WM_SETCURSOR:
9292     if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
9293       return TRUE;
9294     goto fwd_msg;
9295
9296   case WM_SETFOCUS:
9297     return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
9298
9299   case WM_SETFONT:
9300     return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
9301
9302   case WM_SETREDRAW:
9303     return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
9304
9305   case WM_SIZE:
9306     return LISTVIEW_Size(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
9307
9308   case WM_STYLECHANGED:
9309     return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
9310
9311   case WM_SYSCOLORCHANGE:
9312     COMCTL32_RefreshSysColors();
9313     return 0;
9314
9315 /*      case WM_TIMER: */
9316
9317   case WM_VSCROLL:
9318     return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
9319
9320   case WM_MOUSEWHEEL:
9321       if (wParam & (MK_SHIFT | MK_CONTROL))
9322           return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9323       return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
9324
9325   case WM_WINDOWPOSCHANGED:
9326       if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE)) 
9327       {
9328       UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
9329           SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
9330                        SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
9331
9332       if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
9333       {
9334           MEASUREITEMSTRUCT mis;
9335           mis.CtlType = ODT_LISTVIEW;
9336           mis.CtlID = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
9337           mis.itemID = -1;
9338           mis.itemWidth = 0;
9339           mis.itemData = 0;
9340           mis.itemHeight= infoPtr->nItemHeight;
9341           SendMessageW(infoPtr->hwndNotify, WM_MEASUREITEM, mis.CtlID, (LPARAM)&mis);
9342           if (infoPtr->nItemHeight != max(mis.itemHeight, 1))
9343               infoPtr->nMeasureItemHeight = infoPtr->nItemHeight = max(mis.itemHeight, 1);
9344       }
9345
9346           LISTVIEW_UpdateSize(infoPtr);
9347           LISTVIEW_UpdateScroll(infoPtr);
9348       }
9349       return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9350
9351 /*      case WM_WININICHANGE: */
9352
9353   default:
9354     if ((uMsg >= WM_USER) && (uMsg < WM_APP))
9355       ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
9356
9357   fwd_msg:
9358     /* call default window procedure */
9359     return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9360   }
9361
9362 }
9363
9364 /***
9365  * DESCRIPTION:
9366  * Registers the window class.
9367  *
9368  * PARAMETER(S):
9369  * None
9370  *
9371  * RETURN:
9372  * None
9373  */
9374 void LISTVIEW_Register(void)
9375 {
9376     WNDCLASSW wndClass;
9377
9378     ZeroMemory(&wndClass, sizeof(WNDCLASSW));
9379     wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
9380     wndClass.lpfnWndProc = LISTVIEW_WindowProc;
9381     wndClass.cbClsExtra = 0;
9382     wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
9383     wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
9384     wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
9385     wndClass.lpszClassName = WC_LISTVIEWW;
9386     RegisterClassW(&wndClass);
9387 }
9388
9389 /***
9390  * DESCRIPTION:
9391  * Unregisters the window class.
9392  *
9393  * PARAMETER(S):
9394  * None
9395  *
9396  * RETURN:
9397  * None
9398  */
9399 void LISTVIEW_Unregister(void)
9400 {
9401     UnregisterClassW(WC_LISTVIEWW, NULL);
9402 }
9403
9404 /***
9405  * DESCRIPTION:
9406  * Handle any WM_COMMAND messages
9407  *
9408  * PARAMETER(S):
9409  * [I] infoPtr : valid pointer to the listview structure
9410  * [I] wParam : the first message parameter
9411  * [I] lParam : the second message parameter
9412  *
9413  * RETURN:
9414  *   Zero.
9415  */
9416 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
9417 {
9418     switch (HIWORD(wParam))
9419     {
9420         case EN_UPDATE:
9421         {
9422             /*
9423              * Adjust the edit window size
9424              */
9425             WCHAR buffer[1024];
9426             HDC           hdc = GetDC(infoPtr->hwndEdit);
9427             HFONT         hFont, hOldFont = 0;
9428             RECT          rect;
9429             SIZE          sz;
9430             int           len;
9431
9432             if (!infoPtr->hwndEdit || !hdc) return 0;
9433             len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
9434             GetWindowRect(infoPtr->hwndEdit, &rect);
9435
9436             /* Select font to get the right dimension of the string */
9437             hFont = (HFONT)SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
9438             if(hFont != 0)
9439             {
9440                 hOldFont = SelectObject(hdc, hFont);
9441             }
9442
9443             if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
9444             {
9445                 TEXTMETRICW textMetric;
9446
9447                 /* Add Extra spacing for the next character */
9448                 GetTextMetricsW(hdc, &textMetric);
9449                 sz.cx += (textMetric.tmMaxCharWidth * 2);
9450
9451                 SetWindowPos (
9452                     infoPtr->hwndEdit,
9453                     HWND_TOP,
9454                     0,
9455                     0,
9456                     sz.cx,
9457                     rect.bottom - rect.top,
9458                     SWP_DRAWFRAME|SWP_NOMOVE);
9459             }
9460             if(hFont != 0)
9461                 SelectObject(hdc, hOldFont);
9462
9463             ReleaseDC(infoPtr->hwndEdit, hdc);
9464
9465             break;
9466         }
9467
9468         default:
9469           return SendMessageW (infoPtr->hwndNotify, WM_COMMAND, wParam, lParam);
9470     }
9471
9472     return 0;
9473 }
9474
9475
9476 /***
9477  * DESCRIPTION:
9478  * Subclassed edit control windproc function
9479  *
9480  * PARAMETER(S):
9481  * [I] hwnd : the edit window handle
9482  * [I] uMsg : the message that is to be processed
9483  * [I] wParam : first message parameter
9484  * [I] lParam : second message parameter
9485  * [I] isW : TRUE if input is Unicode
9486  *
9487  * RETURN:
9488  *   Zero.
9489  */
9490 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL isW)
9491 {
9492     LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(GetParent(hwnd), 0);
9493     BOOL cancel = FALSE;
9494
9495     TRACE("(hwnd=%p, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
9496           hwnd, uMsg, wParam, lParam, isW);
9497
9498     switch (uMsg)
9499     {
9500         case WM_GETDLGCODE:
9501           return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
9502
9503         case WM_KILLFOCUS:
9504             break;
9505
9506         case WM_DESTROY:
9507         {
9508             WNDPROC editProc = infoPtr->EditWndProc;
9509             infoPtr->EditWndProc = 0;
9510             SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (DWORD_PTR)editProc);
9511             return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
9512         }
9513
9514         case WM_KEYDOWN:
9515             if (VK_ESCAPE == (INT)wParam)
9516             {
9517                 cancel = TRUE;
9518                 break;
9519             }
9520             else if (VK_RETURN == (INT)wParam)
9521                 break;
9522
9523         default:
9524             return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
9525     }
9526
9527     /* kill the edit */
9528     if (infoPtr->hwndEdit)
9529     {
9530         LPWSTR buffer = NULL;
9531
9532         infoPtr->hwndEdit = 0;
9533         if (!cancel)
9534         {
9535             DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
9536
9537             if (len)
9538             {
9539                 if ( (buffer = Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
9540                 {
9541                     if (isW) GetWindowTextW(hwnd, buffer, len+1);
9542                     else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
9543                 }
9544             }
9545         }
9546         LISTVIEW_EndEditLabelT(infoPtr, buffer, isW);
9547
9548         if (buffer) Free(buffer);
9549
9550     }
9551
9552     SendMessageW(hwnd, WM_CLOSE, 0, 0);
9553     return 0;
9554 }
9555
9556 /***
9557  * DESCRIPTION:
9558  * Subclassed edit control Unicode windproc function
9559  *
9560  * PARAMETER(S):
9561  * [I] hwnd : the edit window handle
9562  * [I] uMsg : the message that is to be processed
9563  * [I] wParam : first message parameter
9564  * [I] lParam : second message parameter
9565  *
9566  * RETURN:
9567  */
9568 LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9569 {
9570     return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
9571 }
9572
9573 /***
9574  * DESCRIPTION:
9575  * Subclassed edit control ANSI windproc function
9576  *
9577  * PARAMETER(S):
9578  * [I] hwnd : the edit window handle
9579  * [I] uMsg : the message that is to be processed
9580  * [I] wParam : first message parameter
9581  * [I] lParam : second message parameter
9582  *
9583  * RETURN:
9584  */
9585 LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9586 {
9587     return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
9588 }
9589
9590 /***
9591  * DESCRIPTION:
9592  * Creates a subclassed edit cotrol
9593  *
9594  * PARAMETER(S):
9595  * [I] infoPtr : valid pointer to the listview structure
9596  * [I] text : initial text for the edit
9597  * [I] style : the window style
9598  * [I] isW : TRUE if input is Unicode
9599  *
9600  * RETURN:
9601  */
9602 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style,
9603         INT x, INT y, INT width, INT height, BOOL isW)
9604 {
9605     WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
9606     HWND hedit;
9607     SIZE sz;
9608     HDC hdc;
9609     HDC hOldFont=0;
9610     TEXTMETRICW textMetric;
9611     HINSTANCE hinst = (HINSTANCE)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_HINSTANCE);
9612
9613     TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
9614
9615     style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|ES_AUTOHSCROLL|WS_BORDER;
9616     hdc = GetDC(infoPtr->hwndSelf);
9617
9618     /* Select the font to get appropriate metric dimensions */
9619     if(infoPtr->hFont != 0)
9620         hOldFont = SelectObject(hdc, infoPtr->hFont);
9621
9622     /*Get String Length in pixels */
9623     GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
9624
9625     /*Add Extra spacing for the next character */
9626     GetTextMetricsW(hdc, &textMetric);
9627     sz.cx += (textMetric.tmMaxCharWidth * 2);
9628
9629     if(infoPtr->hFont != 0)
9630         SelectObject(hdc, hOldFont);
9631
9632     ReleaseDC(infoPtr->hwndSelf, hdc);
9633     if (isW)
9634         hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9635     else
9636         hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9637
9638     if (!hedit) return 0;
9639
9640     infoPtr->EditWndProc = (WNDPROC)
9641         (isW ? SetWindowLongPtrW(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcW) :
9642                SetWindowLongPtrA(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcA) );
9643
9644     SendMessageW(hedit, WM_SETFONT, (WPARAM)infoPtr->hFont, FALSE);
9645
9646     return hedit;
9647 }