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