Fixed a race condition on RPC worker thread creation, and a typo.
[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, ITERATOR *i, 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     
3629     TRACE("()\n");
3630
3631     ZeroMemory(&dis, sizeof(dis));
3632     
3633     /* Get scroll info once before loop */
3634     LISTVIEW_GetOrigin(infoPtr, &Origin);
3635     
3636     /* iterate through the invalidated rows */
3637     while(iterator_next(i))
3638     {
3639         item.iItem = i->nItem;
3640         item.iSubItem = 0;
3641         item.mask = LVIF_PARAM | LVIF_STATE;
3642         item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3643         if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
3644            
3645         dis.CtlType = ODT_LISTVIEW;
3646         dis.CtlID = uID;
3647         dis.itemID = item.iItem;
3648         dis.itemAction = ODA_DRAWENTIRE;
3649         dis.itemState = 0;
3650         if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
3651         if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS;
3652         dis.hwndItem = infoPtr->hwndSelf;
3653         dis.hDC = hdc;
3654         LISTVIEW_GetItemOrigin(infoPtr, dis.itemID, &Position);
3655         dis.rcItem.left = Position.x + Origin.x;
3656         dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth;
3657         dis.rcItem.top = Position.y + Origin.y;
3658         dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3659         dis.itemData = item.lParam;
3660
3661         TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), debugrect(&dis.rcItem));
3662         SendMessageW(hwndParent, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
3663     }
3664 }
3665
3666 /***
3667  * DESCRIPTION:
3668  * Draws listview items when in report display mode.
3669  *
3670  * PARAMETER(S):
3671  * [I] infoPtr : valid pointer to the listview structure
3672  * [I] hdc : device context handle
3673  * [I] cdmode : custom draw mode
3674  *
3675  * RETURN:
3676  * None
3677  */
3678 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3679 {
3680     INT rgntype;
3681     RECT rcClip, rcItem;
3682     POINT Origin, Position;
3683     RANGE colRange;
3684     ITERATOR j;
3685
3686     TRACE("()\n");
3687
3688     /* figure out what to draw */
3689     rgntype = GetClipBox(hdc, &rcClip);
3690     if (rgntype == NULLREGION) return;
3691     
3692     /* Get scroll info once before loop */
3693     LISTVIEW_GetOrigin(infoPtr, &Origin);
3694     
3695     /* narrow down the columns we need to paint */
3696     for(colRange.lower = 0; colRange.lower < infoPtr->hdpaColumns->nItemCount; colRange.lower++)
3697     {
3698         LISTVIEW_GetHeaderRect(infoPtr, colRange.lower, &rcItem);
3699         if (rcItem.right + Origin.x >= rcClip.left) break;
3700     }
3701     for(colRange.upper = infoPtr->hdpaColumns->nItemCount; colRange.upper > 0; colRange.upper--)
3702     {
3703         LISTVIEW_GetHeaderRect(infoPtr, colRange.upper - 1, &rcItem);
3704         if (rcItem.left + Origin.x < rcClip.right) break;
3705     }
3706     iterator_rangeitems(&j, colRange);
3707
3708     /* in full row select, we _have_ to draw the main item */
3709     if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
3710         j.nSpecial = 0;
3711
3712     /* iterate through the invalidated rows */
3713     while(iterator_next(i))
3714     {
3715         /* iterate through the invalidated columns */
3716         while(iterator_next(&j))
3717         {
3718             LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
3719             Position.x += Origin.x;
3720             Position.y += Origin.y;
3721
3722             if (rgntype == COMPLEXREGION && !((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && j.nItem == 0))
3723             {
3724                 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
3725                 rcItem.top = 0;
3726                 rcItem.bottom = infoPtr->nItemHeight;
3727                 OffsetRect(&rcItem, Position.x, Position.y);
3728                 if (!RectVisible(hdc, &rcItem)) continue;
3729             }
3730
3731             LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, j.nItem, Position, cdmode);
3732         }
3733     }
3734     iterator_destroy(&j);
3735 }
3736
3737 /***
3738  * DESCRIPTION:
3739  * Draws listview items when in list display mode.
3740  *
3741  * PARAMETER(S):
3742  * [I] infoPtr : valid pointer to the listview structure
3743  * [I] hdc : device context handle
3744  * [I] cdmode : custom draw mode
3745  *
3746  * RETURN:
3747  * None
3748  */
3749 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3750 {
3751     POINT Origin, Position;
3752
3753     /* Get scroll info once before loop */
3754     LISTVIEW_GetOrigin(infoPtr, &Origin);
3755     
3756     while(iterator_prev(i))
3757     {
3758         LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
3759         Position.x += Origin.x;
3760         Position.y += Origin.y;
3761
3762         LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, 0, Position, cdmode);
3763     }
3764 }
3765
3766
3767 /***
3768  * DESCRIPTION:
3769  * Draws listview items.
3770  *
3771  * PARAMETER(S):
3772  * [I] infoPtr : valid pointer to the listview structure
3773  * [I] hdc : device context handle
3774  *
3775  * RETURN:
3776  * NoneX
3777  */
3778 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc)
3779 {
3780     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3781     COLORREF oldTextColor, oldClrTextBk, oldClrText;
3782     NMLVCUSTOMDRAW nmlvcd;
3783     HFONT hOldFont;
3784     DWORD cdmode;
3785     INT oldBkMode;
3786     RECT rcClient;
3787     ITERATOR i;
3788
3789     LISTVIEW_DUMP(infoPtr);
3790   
3791     infoPtr->bIsDrawing = TRUE;
3792
3793     /* save dc values we're gonna trash while drawing */
3794     hOldFont = SelectObject(hdc, infoPtr->hFont);
3795     oldBkMode = GetBkMode(hdc);
3796     infoPtr->clrTextBkDefault = GetBkColor(hdc);
3797     oldTextColor = GetTextColor(hdc);
3798
3799     oldClrTextBk = infoPtr->clrTextBk;
3800     oldClrText   = infoPtr->clrText;
3801    
3802     GetClientRect(infoPtr->hwndSelf, &rcClient);
3803     customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient);
3804     cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3805     if (cdmode & CDRF_SKIPDEFAULT) goto enddraw;
3806
3807     /* Use these colors to draw the items */
3808     infoPtr->clrTextBk = nmlvcd.clrTextBk;
3809     infoPtr->clrText = nmlvcd.clrText;
3810
3811     /* nothing to draw */
3812     if(infoPtr->nItemCount == 0) goto enddraw;
3813
3814     /* figure out what we need to draw */
3815     iterator_visibleitems(&i, infoPtr, hdc);
3816     
3817     /* send cache hint notification */
3818     if (infoPtr->dwStyle & LVS_OWNERDATA)
3819     {
3820         RANGE range = iterator_range(&i);
3821         NMLVCACHEHINT nmlv;
3822         
3823         ZeroMemory(&nmlv, sizeof(NMLVCACHEHINT));
3824         nmlv.iFrom = range.lower;
3825         nmlv.iTo   = range.upper - 1;
3826         notify_hdr(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
3827     }
3828
3829     if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
3830         LISTVIEW_RefreshOwnerDraw(infoPtr, &i, hdc);
3831     else
3832     {
3833         if (uView == LVS_REPORT)
3834             LISTVIEW_RefreshReport(infoPtr, &i, hdc, cdmode);
3835         else /* LVS_LIST, LVS_ICON or LVS_SMALLICON */
3836             LISTVIEW_RefreshList(infoPtr, &i, hdc, cdmode);
3837
3838         /* if we have a focus rect, draw it */
3839         if (infoPtr->bFocus)
3840             DrawFocusRect(hdc, &infoPtr->rcFocus);
3841     }
3842     iterator_destroy(&i);
3843     
3844 enddraw:
3845     if (cdmode & CDRF_NOTIFYPOSTPAINT)
3846         notify_customdraw(infoPtr, CDDS_POSTPAINT, &nmlvcd);
3847
3848     infoPtr->clrTextBk = oldClrTextBk;
3849     infoPtr->clrText = oldClrText;
3850
3851     SelectObject(hdc, hOldFont);
3852     SetBkMode(hdc, oldBkMode);
3853     SetBkColor(hdc, infoPtr->clrTextBkDefault);
3854     SetTextColor(hdc, oldTextColor);
3855     infoPtr->bIsDrawing = FALSE;
3856 }
3857
3858
3859 /***
3860  * DESCRIPTION:
3861  * Calculates the approximate width and height of a given number of items.
3862  *
3863  * PARAMETER(S):
3864  * [I] infoPtr : valid pointer to the listview structure
3865  * [I] nItemCount : number of items
3866  * [I] wWidth : width
3867  * [I] wHeight : height
3868  *
3869  * RETURN:
3870  * Returns a DWORD. The width in the low word and the height in high word.
3871  */
3872 static DWORD LISTVIEW_ApproximateViewRect(LISTVIEW_INFO *infoPtr, INT nItemCount,
3873                                             WORD wWidth, WORD wHeight)
3874 {
3875   UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3876   INT nItemCountPerColumn = 1;
3877   INT nColumnCount = 0;
3878   DWORD dwViewRect = 0;
3879
3880   if (nItemCount == -1)
3881     nItemCount = infoPtr->nItemCount;
3882
3883   if (uView == LVS_LIST)
3884   {
3885     if (wHeight == 0xFFFF)
3886     {
3887       /* use current height */
3888       wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3889     }
3890
3891     if (wHeight < infoPtr->nItemHeight)
3892       wHeight = infoPtr->nItemHeight;
3893
3894     if (nItemCount > 0)
3895     {
3896       if (infoPtr->nItemHeight > 0)
3897       {
3898         nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
3899         if (nItemCountPerColumn == 0)
3900           nItemCountPerColumn = 1;
3901
3902         if (nItemCount % nItemCountPerColumn != 0)
3903           nColumnCount = nItemCount / nItemCountPerColumn;
3904         else
3905           nColumnCount = nItemCount / nItemCountPerColumn + 1;
3906       }
3907     }
3908
3909     /* Microsoft padding magic */
3910     wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
3911     wWidth = nColumnCount * infoPtr->nItemWidth + 2;
3912
3913     dwViewRect = MAKELONG(wWidth, wHeight);
3914   }
3915   else if (uView == LVS_REPORT)
3916     FIXME("uView == LVS_REPORT: not implemented\n");
3917   else if (uView == LVS_SMALLICON)
3918     FIXME("uView == LVS_SMALLICON: not implemented\n");
3919   else if (uView == LVS_ICON)
3920     FIXME("uView == LVS_ICON: not implemented\n");
3921
3922   return dwViewRect;
3923 }
3924
3925 /* << LISTVIEW_CreateDragImage >> */
3926
3927
3928 /***
3929  * DESCRIPTION:
3930  * Removes all listview items and subitems.
3931  *
3932  * PARAMETER(S):
3933  * [I] infoPtr : valid pointer to the listview structure
3934  *
3935  * RETURN:
3936  *   SUCCESS : TRUE
3937  *   FAILURE : FALSE
3938  */
3939 static BOOL LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr)
3940 {
3941     NMLISTVIEW nmlv;
3942     HDPA hdpaSubItems = NULL;
3943     BOOL bSuppress;
3944     ITEMHDR *hdrItem;
3945     INT i, j;
3946
3947     TRACE("()\n");
3948
3949     /* we do it directly, to avoid notifications */
3950     ranges_clear(infoPtr->selectionRanges);
3951     infoPtr->nSelectionMark = -1;
3952     infoPtr->nFocusedItem = -1;
3953     SetRectEmpty(&infoPtr->rcFocus);
3954     /* But we are supposed to leave nHotItem as is! */
3955
3956
3957     /* send LVN_DELETEALLITEMS notification */
3958     ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3959     nmlv.iItem = -1;
3960     bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
3961
3962     for (i = infoPtr->nItemCount - 1; i >= 0; i--)
3963     {
3964         /* send LVN_DELETEITEM notification, if not supressed */
3965         if (!bSuppress) notify_deleteitem(infoPtr, i);
3966         if (!(infoPtr->dwStyle & LVS_OWNERDATA))
3967         {
3968             hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
3969             for (j = 0; j < hdpaSubItems->nItemCount; j++)
3970             {
3971                 hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, j);
3972                 if (is_textW(hdrItem->pszText)) COMCTL32_Free(hdrItem->pszText);
3973                 COMCTL32_Free(hdrItem);
3974             }
3975             DPA_Destroy(hdpaSubItems);
3976             DPA_DeletePtr(infoPtr->hdpaItems, i);
3977         }
3978         DPA_DeletePtr(infoPtr->hdpaPosX, i);
3979         DPA_DeletePtr(infoPtr->hdpaPosY, i);
3980         infoPtr->nItemCount --;
3981     }
3982     
3983     LISTVIEW_UpdateScroll(infoPtr);
3984
3985     LISTVIEW_InvalidateList(infoPtr);
3986     
3987     return TRUE;
3988 }
3989
3990 /***
3991  * DESCRIPTION:
3992  * Scrolls, and updates the columns, when a column is changing width.
3993  *
3994  * PARAMETER(S):
3995  * [I] infoPtr : valid pointer to the listview structure
3996  * [I] nColumn : column to scroll
3997  * [I] dx : amount of scroll, in pixels
3998  *
3999  * RETURN:
4000  *   None.
4001  */
4002 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO *infoPtr, INT nColumn, INT dx)
4003 {
4004     COLUMN_INFO *lpColumnInfo;
4005     RECT rcOld, rcCol;
4006     INT nCol;
4007    
4008     lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, min(nColumn, infoPtr->hdpaColumns->nItemCount - 1));
4009     rcCol = lpColumnInfo->rcHeader;
4010     if (nColumn >= infoPtr->hdpaColumns->nItemCount)
4011         rcCol.left = rcCol.right;
4012     
4013     /* ajust the other columns */
4014     for (nCol = nColumn; nCol < infoPtr->hdpaColumns->nItemCount; nCol++)
4015     {
4016         lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nCol);
4017         lpColumnInfo->rcHeader.left += dx;
4018         lpColumnInfo->rcHeader.right += dx;
4019     }
4020
4021     /* do not update screen if not in report mode */
4022     if (!is_redrawing(infoPtr) || (infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return;
4023     
4024     /* if we have a focus, must first erase the focus rect */
4025     if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, FALSE);
4026     
4027     /* Need to reset the item width when inserting a new column */
4028     infoPtr->nItemWidth += dx;
4029
4030     LISTVIEW_UpdateScroll(infoPtr);
4031
4032     /* scroll to cover the deleted column, and invalidate for redraw */
4033     rcOld = infoPtr->rcList;
4034     rcOld.left = rcCol.left;
4035     ScrollWindowEx(infoPtr->hwndSelf, dx, 0, &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
4036     
4037     /* we can restore focus now */
4038     if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, TRUE);
4039 }
4040
4041 /***
4042  * DESCRIPTION:
4043  * Removes a column from the listview control.
4044  *
4045  * PARAMETER(S):
4046  * [I] infoPtr : valid pointer to the listview structure
4047  * [I] nColumn : column index
4048  *
4049  * RETURN:
4050  *   SUCCESS : TRUE
4051  *   FAILURE : FALSE
4052  */
4053 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
4054 {
4055     RECT rcCol;
4056     
4057     TRACE("nColumn=%d\n", nColumn);
4058
4059     if (nColumn < 0 || infoPtr->hdpaColumns->nItemCount == 0
4060            || nColumn >= infoPtr->hdpaColumns->nItemCount) return FALSE;
4061
4062     /* While the MSDN specifically says that column zero should not be deleted,
4063        it does in fact work on WinNT, and at least one app depends on it. On
4064        WinNT, deleting column zero deletes the last column of items but the
4065        first header. Since no app will ever depend on that bizarre behavior, 
4066        we just delete the last column including the header.
4067      */
4068     if (nColumn == 0)
4069         nColumn = infoPtr->hdpaColumns->nItemCount - 1;
4070
4071     LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
4072     
4073     if (!Header_DeleteItem(infoPtr->hwndHeader, nColumn))
4074         return FALSE;
4075
4076     COMCTL32_Free(DPA_GetPtr(infoPtr->hdpaColumns, nColumn));
4077     DPA_DeletePtr(infoPtr->hdpaColumns, nColumn);
4078   
4079     if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4080     {
4081         SUBITEM_INFO *lpSubItem, *lpDelItem;
4082         HDPA hdpaSubItems;
4083         INT nItem, nSubItem, i;
4084         
4085         if (nColumn == 0)
4086             return LISTVIEW_DeleteAllItems(infoPtr);
4087             
4088         for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
4089         {
4090             hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
4091             nSubItem = 0;
4092             lpDelItem = 0;
4093             for (i = 1; i < hdpaSubItems->nItemCount; i++)
4094             {
4095                 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
4096                 if (lpSubItem->iSubItem == nColumn)
4097                 {
4098                     nSubItem = i;
4099                     lpDelItem = lpSubItem;
4100                 }
4101                 else if (lpSubItem->iSubItem > nColumn) 
4102                 {
4103                     lpSubItem->iSubItem--;
4104                 }
4105             }
4106
4107             /* if we found our subitem, zapp it */      
4108             if (nSubItem > 0)
4109             {
4110                 /* free string */
4111                 if (is_textW(lpDelItem->hdr.pszText))
4112                     COMCTL32_Free(lpDelItem->hdr.pszText);
4113
4114                 /* free item */
4115                 COMCTL32_Free(lpDelItem);
4116
4117                 /* free dpa memory */
4118                 DPA_DeletePtr(hdpaSubItems, nSubItem);
4119             }
4120         }
4121     }
4122
4123     /* update the other column info */
4124     LISTVIEW_ScrollColumns(infoPtr, nColumn, -(rcCol.right - rcCol.left));
4125
4126     return TRUE;
4127 }
4128
4129 /***
4130  * DESCRIPTION:
4131  * Invalidates the listview after an item's insertion or deletion.
4132  *
4133  * PARAMETER(S):
4134  * [I] infoPtr : valid pointer to the listview structure
4135  * [I] nItem : item index
4136  * [I] dir : -1 if deleting, 1 if inserting
4137  *
4138  * RETURN:
4139  *   None
4140  */
4141 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO *infoPtr, INT nItem, INT dir)
4142 {
4143     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4144     INT nPerCol, nItemCol, nItemRow;
4145     RECT rcScroll;
4146     POINT Origin;
4147
4148     /* if we don't refresh, what's the point of scrolling? */
4149     if (!is_redrawing(infoPtr)) return;
4150     
4151     assert (abs(dir) == 1);
4152
4153     /* arrange icons if autoarrange is on */
4154     if (is_autoarrange(infoPtr))
4155     {
4156         BOOL arrange = TRUE;
4157         if (dir < 0 && nItem >= infoPtr->nItemCount) arrange = FALSE;
4158         if (dir > 0 && nItem == infoPtr->nItemCount - 1) arrange = FALSE;
4159         if (arrange) LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
4160     }
4161
4162     /* scrollbars need updating */
4163     LISTVIEW_UpdateScroll(infoPtr);
4164
4165     /* figure out the item's position */ 
4166     if (uView == LVS_REPORT)
4167         nPerCol = infoPtr->nItemCount + 1;
4168     else if (uView == LVS_LIST)
4169         nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
4170     else /* LVS_ICON, or LVS_SMALLICON */
4171         return;
4172     
4173     nItemCol = nItem / nPerCol;
4174     nItemRow = nItem % nPerCol;
4175     LISTVIEW_GetOrigin(infoPtr, &Origin);
4176
4177     /* move the items below up a slot */
4178     rcScroll.left = nItemCol * infoPtr->nItemWidth;
4179     rcScroll.top = nItemRow * infoPtr->nItemHeight;
4180     rcScroll.right = rcScroll.left + infoPtr->nItemWidth;
4181     rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4182     OffsetRect(&rcScroll, Origin.x, Origin.y);
4183     TRACE("rcScroll=%s, dx=%d\n", debugrect(&rcScroll), dir * infoPtr->nItemHeight);
4184     if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4185     {
4186         TRACE("Scrolling rcScroll=%s, rcList=%s\n", debugrect(&rcScroll), debugrect(&infoPtr->rcList));
4187         ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight, 
4188                        &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4189     }
4190
4191     /* report has only that column, so we're done */
4192     if (uView == LVS_REPORT) return;
4193
4194     /* now for LISTs, we have to deal with the columns to the right */
4195     rcScroll.left = (nItemCol + 1) * infoPtr->nItemWidth;
4196     rcScroll.top = 0;
4197     rcScroll.right = (infoPtr->nItemCount / nPerCol + 1) * infoPtr->nItemWidth;
4198     rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4199     OffsetRect(&rcScroll, Origin.x, Origin.y);
4200     if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4201         ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4202                        &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4203 }
4204
4205 /***
4206  * DESCRIPTION:
4207  * Removes an item from the listview control.
4208  *
4209  * PARAMETER(S):
4210  * [I] infoPtr : valid pointer to the listview structure
4211  * [I] nItem : item index
4212  *
4213  * RETURN:
4214  *   SUCCESS : TRUE
4215  *   FAILURE : FALSE
4216  */
4217 static BOOL LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
4218 {
4219     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4220     LVITEMW item;
4221
4222     TRACE("(nItem=%d)\n", nItem);
4223
4224     if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
4225     
4226     /* remove selection, and focus */
4227     item.state = 0;
4228     item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
4229     LISTVIEW_SetItemState(infoPtr, nItem, &item);
4230             
4231     /* send LVN_DELETEITEM notification. */
4232     notify_deleteitem(infoPtr, nItem);
4233
4234     /* we need to do this here, because we'll be deleting stuff */  
4235     if (uView == LVS_SMALLICON || uView == LVS_ICON)
4236         LISTVIEW_InvalidateItem(infoPtr, nItem);
4237     
4238     if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4239     {
4240         HDPA hdpaSubItems;
4241         ITEMHDR *hdrItem;
4242         INT i;
4243
4244         hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);  
4245         for (i = 0; i < hdpaSubItems->nItemCount; i++)
4246         {
4247             hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, i);
4248             if (is_textW(hdrItem->pszText)) COMCTL32_Free(hdrItem->pszText);
4249             COMCTL32_Free(hdrItem);
4250         }
4251         DPA_Destroy(hdpaSubItems);
4252     }
4253
4254     if (uView == LVS_SMALLICON || uView == LVS_ICON)
4255     {
4256         DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
4257         DPA_DeletePtr(infoPtr->hdpaPosY, nItem);
4258     }
4259
4260     infoPtr->nItemCount--;
4261     LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
4262
4263     /* now is the invalidation fun */
4264     LISTVIEW_ScrollOnInsert(infoPtr, nItem, -1);
4265     return TRUE;
4266 }
4267
4268
4269 /***
4270  * DESCRIPTION:
4271  * Callback implementation for editlabel control
4272  *
4273  * PARAMETER(S):
4274  * [I] infoPtr : valid pointer to the listview structure
4275  * [I] pszText : modified text
4276  * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4277  *
4278  * RETURN:
4279  *   SUCCESS : TRUE
4280  *   FAILURE : FALSE
4281  */
4282 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, LPWSTR pszText, BOOL isW)
4283 {
4284     NMLVDISPINFOW dispInfo;
4285
4286     TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
4287
4288     ZeroMemory(&dispInfo, sizeof(dispInfo));
4289     dispInfo.item.mask = LVIF_PARAM | LVIF_STATE;
4290     dispInfo.item.iItem = infoPtr->nEditLabelItem;
4291     dispInfo.item.iSubItem = 0;
4292     dispInfo.item.stateMask = ~0;
4293     if (!LISTVIEW_GetItemW(infoPtr, &dispInfo.item)) return FALSE;
4294     /* add the text from the edit in */
4295     dispInfo.item.mask |= LVIF_TEXT;
4296     dispInfo.item.pszText = pszText;
4297     dispInfo.item.cchTextMax = textlenT(pszText, isW);
4298
4299     /* Do we need to update the Item Text */
4300     if (!notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW)) return FALSE;
4301     if (!pszText) return TRUE;
4302
4303     ZeroMemory(&dispInfo, sizeof(dispInfo));
4304     dispInfo.item.mask = LVIF_TEXT;
4305     dispInfo.item.iItem = infoPtr->nEditLabelItem;
4306     dispInfo.item.iSubItem = 0;
4307     dispInfo.item.pszText = pszText;
4308     dispInfo.item.cchTextMax = textlenT(pszText, isW);
4309     return LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
4310 }
4311
4312 /***
4313  * DESCRIPTION:
4314  * Begin in place editing of specified list view item
4315  *
4316  * PARAMETER(S):
4317  * [I] infoPtr : valid pointer to the listview structure
4318  * [I] nItem : item index
4319  * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4320  *
4321  * RETURN:
4322  *   SUCCESS : TRUE
4323  *   FAILURE : FALSE
4324  */
4325 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
4326 {
4327     WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
4328     NMLVDISPINFOW dispInfo;
4329     RECT rect;
4330
4331     TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
4332
4333     if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
4334     if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
4335
4336     infoPtr->nEditLabelItem = nItem;
4337
4338     /* Is the EditBox still there, if so remove it */
4339     if(infoPtr->hwndEdit != 0)
4340     {
4341         SetFocus(infoPtr->hwndSelf);
4342         infoPtr->hwndEdit = 0;
4343     }
4344
4345     LISTVIEW_SetSelection(infoPtr, nItem);
4346     LISTVIEW_SetItemFocus(infoPtr, nItem);
4347     LISTVIEW_InvalidateItem(infoPtr, nItem);
4348
4349     rect.left = LVIR_LABEL;
4350     if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
4351     
4352     ZeroMemory(&dispInfo, sizeof(dispInfo));
4353     dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
4354     dispInfo.item.iItem = nItem;
4355     dispInfo.item.iSubItem = 0;
4356     dispInfo.item.stateMask = ~0;
4357     dispInfo.item.pszText = szDispText;
4358     dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
4359     if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
4360
4361     infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, WS_VISIBLE,
4362                     rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, isW);
4363     if (!infoPtr->hwndEdit) return 0;
4364     
4365     if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
4366     {
4367         SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
4368         infoPtr->hwndEdit = 0;
4369         return 0;
4370     }
4371
4372     ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
4373     SetFocus(infoPtr->hwndEdit);
4374     SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
4375     return infoPtr->hwndEdit;
4376 }
4377
4378
4379 /***
4380  * DESCRIPTION:
4381  * Ensures the specified item is visible, scrolling into view if necessary.
4382  *
4383  * PARAMETER(S):
4384  * [I] infoPtr : valid pointer to the listview structure
4385  * [I] nItem : item index
4386  * [I] bPartial : partially or entirely visible
4387  *
4388  * RETURN:
4389  *   SUCCESS : TRUE
4390  *   FAILURE : FALSE
4391  */
4392 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
4393 {
4394     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4395     INT nScrollPosHeight = 0;
4396     INT nScrollPosWidth = 0;
4397     INT nHorzAdjust = 0;
4398     INT nVertAdjust = 0;
4399     INT nHorzDiff = 0;
4400     INT nVertDiff = 0;
4401     RECT rcItem, rcTemp;
4402
4403     rcItem.left = LVIR_BOUNDS;
4404     if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
4405
4406     if (bPartial && IntersectRect(&rcTemp, &infoPtr->rcList, &rcItem)) return TRUE;
4407     
4408     if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
4409     {
4410         /* scroll left/right, but in LVS_REPORT mode */
4411         if (uView == LVS_LIST)
4412             nScrollPosWidth = infoPtr->nItemWidth;
4413         else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4414             nScrollPosWidth = 1;
4415
4416         if (rcItem.left < infoPtr->rcList.left)
4417         {
4418             nHorzAdjust = -1;
4419             if (uView != LVS_REPORT) nHorzDiff = rcItem.left - infoPtr->rcList.left;
4420         }
4421         else
4422         {
4423             nHorzAdjust = 1;
4424             if (uView != LVS_REPORT) nHorzDiff = rcItem.right - infoPtr->rcList.right;
4425         }
4426     }
4427
4428     if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
4429     {
4430         /* scroll up/down, but not in LVS_LIST mode */
4431         if (uView == LVS_REPORT)
4432             nScrollPosHeight = infoPtr->nItemHeight;
4433         else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4434             nScrollPosHeight = 1;
4435
4436         if (rcItem.top < infoPtr->rcList.top)
4437         {
4438             nVertAdjust = -1;
4439             if (uView != LVS_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top;
4440         }
4441         else
4442         {
4443             nVertAdjust = 1;
4444             if (uView != LVS_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
4445         }
4446     }
4447
4448     if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
4449
4450     if (nScrollPosWidth)
4451     {
4452         INT diff = nHorzDiff / nScrollPosWidth;
4453         if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust;
4454         LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff, 0);
4455     }
4456
4457     if (nScrollPosHeight)
4458     {
4459         INT diff = nVertDiff / nScrollPosHeight;
4460         if (nVertDiff % nScrollPosHeight) diff += nVertAdjust;
4461         LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff, 0);
4462     }
4463
4464     return TRUE;
4465 }
4466
4467 /***
4468  * DESCRIPTION:
4469  * Searches for an item with specific characteristics.
4470  *
4471  * PARAMETER(S):
4472  * [I] hwnd : window handle
4473  * [I] nStart : base item index
4474  * [I] lpFindInfo : item information to look for
4475  *
4476  * RETURN:
4477  *   SUCCESS : index of item
4478  *   FAILURE : -1
4479  */
4480 static INT LISTVIEW_FindItemW(LISTVIEW_INFO *infoPtr, INT nStart,
4481                               const LVFINDINFOW *lpFindInfo)
4482 {
4483     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4484     WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4485     BOOL bWrap = FALSE, bNearest = FALSE;
4486     INT nItem = nStart + 1, nLast = infoPtr->nItemCount, nNearestItem = -1;
4487     ULONG xdist, ydist, dist, mindist = 0x7fffffff;
4488     POINT Position, Destination;
4489     LVITEMW lvItem;
4490
4491     if (!lpFindInfo || nItem < 0) return -1;
4492     
4493     lvItem.mask = 0;
4494     if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
4495     {
4496         lvItem.mask |= LVIF_TEXT;
4497         lvItem.pszText = szDispText;
4498         lvItem.cchTextMax = DISP_TEXT_SIZE;
4499     }
4500
4501     if (lpFindInfo->flags & LVFI_WRAP)
4502         bWrap = TRUE;
4503
4504     if ((lpFindInfo->flags & LVFI_NEARESTXY) && 
4505         (uView == LVS_ICON || uView ==LVS_SMALLICON))
4506     {
4507         POINT Origin;
4508         RECT rcArea;
4509         
4510         LISTVIEW_GetOrigin(infoPtr, &Origin);
4511         Destination.x = lpFindInfo->pt.x - Origin.x;
4512         Destination.y = lpFindInfo->pt.y - Origin.y;
4513         switch(lpFindInfo->vkDirection)
4514         {
4515         case VK_DOWN:  Destination.y += infoPtr->nItemHeight; break;
4516         case VK_UP:    Destination.y -= infoPtr->nItemHeight; break;
4517         case VK_RIGHT: Destination.x += infoPtr->nItemWidth; break;
4518         case VK_LEFT:  Destination.x -= infoPtr->nItemWidth; break;
4519         case VK_HOME:  Destination.x = Destination.y = 0; break;
4520         case VK_NEXT:  Destination.y += infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4521         case VK_PRIOR: Destination.y -= infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4522         case VK_END:
4523             LISTVIEW_GetAreaRect(infoPtr, &rcArea);
4524             Destination.x = rcArea.right; 
4525             Destination.y = rcArea.bottom; 
4526             break;
4527         default: ERR("Unknown vkDirection=%d\n", lpFindInfo->vkDirection);
4528         }
4529         bNearest = TRUE;
4530     }
4531
4532     /* if LVFI_PARAM is specified, all other flags are ignored */
4533     if (lpFindInfo->flags & LVFI_PARAM)
4534     {
4535         lvItem.mask |= LVIF_PARAM;
4536         bNearest = FALSE;
4537         lvItem.mask &= ~LVIF_TEXT;
4538     }
4539
4540 again:
4541     for (; nItem < nLast; nItem++)
4542     {
4543         lvItem.iItem = nItem;
4544         lvItem.iSubItem = 0;
4545         if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
4546
4547         if (lvItem.mask & LVIF_PARAM)
4548         {
4549             if (lpFindInfo->lParam == lvItem.lParam)
4550                 return nItem;
4551             else
4552                 continue;
4553         }
4554         
4555         if (lvItem.mask & LVIF_TEXT)
4556         {
4557             if (lpFindInfo->flags & LVFI_PARTIAL)
4558             {
4559                 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL) continue;
4560             }
4561             else
4562             {
4563                 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0) continue;
4564             }
4565         }
4566
4567         if (!bNearest) return nItem;
4568         
4569         /* This is very inefficient. To do a good job here,
4570          * we need a sorted array of (x,y) item positions */
4571         LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
4572
4573         /* compute the distance^2 to the destination */
4574         xdist = Destination.x - Position.x;
4575         ydist = Destination.y - Position.y;
4576         dist = xdist * xdist + ydist * ydist;
4577
4578         /* remember the distance, and item if it's closer */
4579         if (dist < mindist)
4580         {
4581             mindist = dist;
4582             nNearestItem = nItem;
4583         }
4584     }
4585
4586     if (bWrap)
4587     {
4588         nItem = 0;
4589         nLast = min(nStart + 1, infoPtr->nItemCount);
4590         bWrap = FALSE;
4591         goto again;
4592     }
4593
4594     return nNearestItem;
4595 }
4596
4597 /***
4598  * DESCRIPTION:
4599  * Searches for an item with specific characteristics.
4600  *
4601  * PARAMETER(S):
4602  * [I] hwnd : window handle
4603  * [I] nStart : base item index
4604  * [I] lpFindInfo : item information to look for
4605  *
4606  * RETURN:
4607  *   SUCCESS : index of item
4608  *   FAILURE : -1
4609  */
4610 static INT LISTVIEW_FindItemA(LISTVIEW_INFO *infoPtr, INT nStart,
4611                               const LVFINDINFOA *lpFindInfo)
4612 {
4613     BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
4614     LVFINDINFOW fiw;
4615     INT res;
4616
4617     memcpy(&fiw, lpFindInfo, sizeof(fiw));
4618     if (hasText) fiw.psz = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
4619     res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
4620     if (hasText) textfreeT((LPWSTR)fiw.psz, FALSE);
4621     return res;
4622 }
4623
4624 /***
4625  * DESCRIPTION:
4626  * Retrieves the background image of the listview control.
4627  *
4628  * PARAMETER(S):
4629  * [I] infoPtr : valid pointer to the listview structure
4630  * [O] lpBkImage : background image attributes
4631  *
4632  * RETURN:
4633  *   SUCCESS : TRUE
4634  *   FAILURE : FALSE
4635  */
4636 /* static BOOL LISTVIEW_GetBkImage(LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage)   */
4637 /* {   */
4638 /*   FIXME (listview, "empty stub!\n"); */
4639 /*   return FALSE;   */
4640 /* }   */
4641
4642 /***
4643  * DESCRIPTION:
4644  * Retrieves column attributes.
4645  *
4646  * PARAMETER(S):
4647  * [I] infoPtr : valid pointer to the listview structure
4648  * [I] nColumn :  column index
4649  * [IO] lpColumn : column information
4650  * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
4651  *           otherwise it is in fact a LPLVCOLUMNA
4652  *
4653  * RETURN:
4654  *   SUCCESS : TRUE
4655  *   FAILURE : FALSE
4656  */
4657 static BOOL LISTVIEW_GetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn, LPLVCOLUMNW lpColumn, BOOL isW)
4658 {
4659     COLUMN_INFO *lpColumnInfo;
4660     HDITEMW hdi;
4661
4662     if (!lpColumn || nColumn < 0 || nColumn >= infoPtr->hdpaColumns->nItemCount) return FALSE;
4663     lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
4664
4665     /* initialize memory */
4666     ZeroMemory(&hdi, sizeof(hdi));
4667
4668     if (lpColumn->mask & LVCF_TEXT)
4669     {
4670         hdi.mask |= HDI_TEXT;
4671         hdi.pszText = lpColumn->pszText;
4672         hdi.cchTextMax = lpColumn->cchTextMax;
4673     }
4674
4675     if (lpColumn->mask & LVCF_IMAGE)
4676         hdi.mask |= HDI_IMAGE;
4677
4678     if (lpColumn->mask & LVCF_ORDER)
4679         hdi.mask |= HDI_ORDER;
4680
4681     if (!SendMessageW(infoPtr->hwndHeader, isW ? HDM_GETITEMW : HDM_GETITEMA, nColumn, (LPARAM)&hdi)) return FALSE;
4682
4683     if (lpColumn->mask & LVCF_FMT)
4684         lpColumn->fmt = lpColumnInfo->fmt;
4685
4686     if (lpColumn->mask & LVCF_WIDTH)
4687         lpColumn->cx = lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left;
4688
4689     if (lpColumn->mask & LVCF_IMAGE)
4690         lpColumn->iImage = hdi.iImage;
4691
4692     if (lpColumn->mask & LVCF_ORDER)
4693         lpColumn->iOrder = hdi.iOrder;
4694
4695     return TRUE;
4696 }
4697
4698
4699 static BOOL LISTVIEW_GetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
4700 {
4701     INT i;
4702
4703     if (!lpiArray)
4704         return FALSE;
4705
4706     /* FIXME: little hack */
4707     for (i = 0; i < iCount; i++)
4708         lpiArray[i] = i;
4709
4710     return TRUE;
4711 }
4712
4713 /***
4714  * DESCRIPTION:
4715  * Retrieves the column width.
4716  *
4717  * PARAMETER(S):
4718  * [I] infoPtr : valid pointer to the listview structure
4719  * [I] int : column index
4720  *
4721  * RETURN:
4722  *   SUCCESS : column width
4723  *   FAILURE : zero
4724  */
4725 static INT LISTVIEW_GetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn)
4726 {
4727     INT nColumnWidth = 0;
4728     RECT rcHeader;
4729
4730     TRACE("nColumn=%d\n", nColumn);
4731
4732     /* we have a 'column' in LIST and REPORT mode only */
4733     switch(infoPtr->dwStyle & LVS_TYPEMASK)
4734     {
4735     case LVS_LIST:
4736         nColumnWidth = infoPtr->nItemWidth;
4737         break;
4738     case LVS_REPORT:
4739         if (nColumn < 0 || nColumn >= infoPtr->hdpaColumns->nItemCount) return 0;
4740         LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
4741         nColumnWidth = rcHeader.right - rcHeader.left;
4742         break;
4743     }
4744
4745     TRACE("nColumnWidth=%d\n", nColumnWidth);
4746     return nColumnWidth;
4747 }
4748
4749 /***
4750  * DESCRIPTION:
4751  * In list or report display mode, retrieves the number of items that can fit
4752  * vertically in the visible area. In icon or small icon display mode,
4753  * retrieves the total number of visible items.
4754  *
4755  * PARAMETER(S):
4756  * [I] infoPtr : valid pointer to the listview structure
4757  *
4758  * RETURN:
4759  * Number of fully visible items.
4760  */
4761 static INT LISTVIEW_GetCountPerPage(LISTVIEW_INFO *infoPtr)
4762 {
4763     switch (infoPtr->dwStyle & LVS_TYPEMASK)
4764     {
4765     case LVS_ICON:
4766     case LVS_SMALLICON:
4767         return infoPtr->nItemCount;
4768     case LVS_REPORT:
4769         return LISTVIEW_GetCountPerColumn(infoPtr);
4770     case LVS_LIST:
4771         return LISTVIEW_GetCountPerRow(infoPtr) * LISTVIEW_GetCountPerColumn(infoPtr);
4772     }
4773     assert(FALSE);
4774     return 0;
4775 }
4776
4777 /***
4778  * DESCRIPTION:
4779  * Retrieves an image list handle.
4780  *
4781  * PARAMETER(S):
4782  * [I] infoPtr : valid pointer to the listview structure
4783  * [I] nImageList : image list identifier
4784  *
4785  * RETURN:
4786  *   SUCCESS : image list handle
4787  *   FAILURE : NULL
4788  */
4789 static HIMAGELIST LISTVIEW_GetImageList(LISTVIEW_INFO *infoPtr, INT nImageList)
4790 {
4791     switch (nImageList)
4792     {
4793     case LVSIL_NORMAL: return infoPtr->himlNormal;
4794     case LVSIL_SMALL: return infoPtr->himlSmall;
4795     case LVSIL_STATE: return infoPtr->himlState;
4796     }
4797     return NULL;
4798 }
4799
4800 /* LISTVIEW_GetISearchString */
4801
4802 /***
4803  * DESCRIPTION:
4804  * Retrieves item attributes.
4805  *
4806  * PARAMETER(S):
4807  * [I] hwnd : window handle
4808  * [IO] lpLVItem : item info
4809  * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
4810  *           if FALSE, the lpLVItem is a LPLVITEMA.
4811  *
4812  * NOTE:
4813  *   This is the internal 'GetItem' interface -- it tries to
4814  *   be smart, and avoids text copies, if possible, by modifing
4815  *   lpLVItem->pszText to point to the text string. Please note
4816  *   that this is not always possible (e.g. OWNERDATA), so on
4817  *   entry you *must* supply valid values for pszText, and cchTextMax.
4818  *   The only difference to the documented interface is that upon
4819  *   return, you should use *only* the lpLVItem->pszText, rather than
4820  *   the buffer pointer you provided on input. Most code already does
4821  *   that, so it's not a problem.
4822  *   For the two cases when the text must be copied (that is,
4823  *   for LVM_GETITEM, and LVMGETITEMTEXT), use LISTVIEW_GetItemExtT.
4824  *
4825  * RETURN:
4826  *   SUCCESS : TRUE
4827  *   FAILURE : FALSE
4828  */
4829 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
4830 {
4831     ITEMHDR callbackHdr = { LPSTR_TEXTCALLBACKW, I_IMAGECALLBACK };
4832     NMLVDISPINFOW dispInfo;
4833     ITEM_INFO *lpItem;
4834     ITEMHDR* pItemHdr;
4835     HDPA hdpaSubItems;
4836
4837     TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
4838
4839     if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
4840         return FALSE;
4841
4842     if (lpLVItem->mask == 0) return TRUE;
4843
4844     /* a quick optimization if all we're asked is the focus state
4845      * these queries are worth optimising since they are common,
4846      * and can be answered in constant time, without the heavy accesses */
4847     if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIS_FOCUSED) &&
4848          !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
4849     {
4850         lpLVItem->state = 0;
4851         if (infoPtr->nFocusedItem == lpLVItem->iItem)
4852             lpLVItem->state |= LVIS_FOCUSED;
4853         return TRUE;
4854     }
4855
4856     ZeroMemory(&dispInfo, sizeof(dispInfo));
4857
4858     /* if the app stores all the data, handle it separately */
4859     if (infoPtr->dwStyle & LVS_OWNERDATA)
4860     {
4861         dispInfo.item.state = 0;
4862
4863         /* apprently, we should not callback for lParam in LVS_OWNERDATA */
4864         if ((lpLVItem->mask & ~(LVIF_STATE | LVIF_PARAM)) || infoPtr->uCallbackMask)
4865         {
4866             /* NOTE: copy only fields which we _know_ are initialized, some apps
4867              *       depend on the uninitialized fields being 0 */
4868             dispInfo.item.mask = lpLVItem->mask & ~LVIF_PARAM;
4869             dispInfo.item.iItem = lpLVItem->iItem;
4870             dispInfo.item.iSubItem = lpLVItem->iSubItem;
4871             if (lpLVItem->mask & LVIF_TEXT)
4872             {
4873                 dispInfo.item.pszText = lpLVItem->pszText;
4874                 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;                
4875             }
4876             if (lpLVItem->mask & LVIF_STATE)
4877                 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
4878             notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
4879             dispInfo.item.stateMask = lpLVItem->stateMask;
4880             *lpLVItem = dispInfo.item;
4881             TRACE("   getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
4882         }
4883         
4884         /* make sure lParam is zeroed out */
4885         if (lpLVItem->mask & LVIF_PARAM) lpLVItem->lParam = 0;
4886         
4887         /* we store only a little state, so if we're not asked, we're done */
4888         if (!(lpLVItem->mask & LVIF_STATE) || lpLVItem->iSubItem) return TRUE;
4889
4890         /* if focus is handled by us, report it */
4891         if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED ) 
4892         {
4893             lpLVItem->state &= ~LVIS_FOCUSED;
4894             if (infoPtr->nFocusedItem == lpLVItem->iItem)
4895                 lpLVItem->state |= LVIS_FOCUSED;
4896         }
4897
4898         /* and do the same for selection, if we handle it */
4899         if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED ) 
4900         {
4901             lpLVItem->state &= ~LVIS_SELECTED;
4902             if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
4903                 lpLVItem->state |= LVIS_SELECTED;
4904         }
4905         
4906         return TRUE;
4907     }
4908
4909     /* find the item and subitem structures before we proceed */
4910     hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
4911     lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
4912     assert (lpItem);
4913
4914     if (lpLVItem->iSubItem)
4915     {
4916         SUBITEM_INFO *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
4917         pItemHdr = lpSubItem ? &lpSubItem->hdr : &callbackHdr;
4918     }
4919     else
4920         pItemHdr = &lpItem->hdr;
4921
4922     /* Do we need to query the state from the app? */
4923     if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && lpLVItem->iSubItem == 0)
4924     {
4925         dispInfo.item.mask |= LVIF_STATE;
4926         dispInfo.item.stateMask = infoPtr->uCallbackMask;
4927     }
4928   
4929     /* Do we need to enquire about the image? */
4930     if ((lpLVItem->mask & LVIF_IMAGE) && pItemHdr->iImage == I_IMAGECALLBACK)
4931         dispInfo.item.mask |= LVIF_IMAGE;
4932
4933     /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
4934     if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(pItemHdr->pszText))
4935     {
4936         dispInfo.item.mask |= LVIF_TEXT;
4937         dispInfo.item.pszText = lpLVItem->pszText;
4938         dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
4939         if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
4940             *dispInfo.item.pszText = '\0';
4941     }
4942
4943     /* If we don't have all the requested info, query the application */
4944     if (dispInfo.item.mask != 0)
4945     {
4946         dispInfo.item.iItem = lpLVItem->iItem;
4947         dispInfo.item.iSubItem = lpLVItem->iSubItem;
4948         dispInfo.item.lParam = lpItem->lParam;
4949         notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
4950         TRACE("   getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
4951     }
4952
4953     /* we should not store values for subitems */
4954     if (lpLVItem->iSubItem) dispInfo.item.mask &= ~LVIF_DI_SETITEM;
4955
4956     /* Now, handle the iImage field */
4957     if (dispInfo.item.mask & LVIF_IMAGE)
4958     {
4959         lpLVItem->iImage = dispInfo.item.iImage;
4960         if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->iImage == I_IMAGECALLBACK)
4961             pItemHdr->iImage = dispInfo.item.iImage;
4962     }
4963     else if (lpLVItem->mask & LVIF_IMAGE)
4964         lpLVItem->iImage = pItemHdr->iImage;
4965
4966     /* The pszText field */
4967     if (dispInfo.item.mask & LVIF_TEXT)
4968     {
4969         if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
4970             textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
4971
4972         lpLVItem->pszText = dispInfo.item.pszText;
4973     }
4974     else if (lpLVItem->mask & LVIF_TEXT)
4975     {
4976         if (isW) lpLVItem->pszText = pItemHdr->pszText;
4977         else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
4978     }
4979
4980     /* if this is a subitem, we're done */
4981     if (lpLVItem->iSubItem) return TRUE;
4982   
4983     /* Next is the lParam field */
4984     if (dispInfo.item.mask & LVIF_PARAM)
4985     {
4986         lpLVItem->lParam = dispInfo.item.lParam;
4987         if ((dispInfo.item.mask & LVIF_DI_SETITEM))
4988             lpItem->lParam = dispInfo.item.lParam;
4989     }
4990     else if (lpLVItem->mask & LVIF_PARAM)
4991         lpLVItem->lParam = lpItem->lParam;
4992
4993     /* ... the state field (this one is different due to uCallbackmask) */
4994     if (lpLVItem->mask & LVIF_STATE) 
4995     {
4996         lpLVItem->state = lpItem->state;
4997         if (dispInfo.item.mask & LVIF_STATE)
4998         {
4999             lpLVItem->state &= ~dispInfo.item.stateMask;
5000             lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
5001         }
5002         if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED ) 
5003         {
5004             lpLVItem->state &= ~LVIS_FOCUSED;
5005             if (infoPtr->nFocusedItem == lpLVItem->iItem)
5006                 lpLVItem->state |= LVIS_FOCUSED;
5007         }
5008         if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED ) 
5009         {
5010             lpLVItem->state &= ~LVIS_SELECTED;
5011             if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5012                 lpLVItem->state |= LVIS_SELECTED;
5013         }           
5014     }
5015
5016     /* and last, but not least, the indent field */
5017     if (lpLVItem->mask & LVIF_INDENT)
5018         lpLVItem->iIndent = lpItem->iIndent;
5019
5020     return TRUE;
5021 }
5022
5023 /***
5024  * DESCRIPTION:
5025  * Retrieves item attributes.
5026  *
5027  * PARAMETER(S):
5028  * [I] hwnd : window handle
5029  * [IO] lpLVItem : item info
5030  * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5031  *           if FALSE, the lpLVItem is a LPLVITEMA.
5032  *
5033  * NOTE:
5034  *   This is the external 'GetItem' interface -- it properly copies
5035  *   the text in the provided buffer.
5036  *
5037  * RETURN:
5038  *   SUCCESS : TRUE
5039  *   FAILURE : FALSE
5040  */
5041 static BOOL LISTVIEW_GetItemExtT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5042 {
5043     LPWSTR pszText;
5044     BOOL bResult;
5045
5046     if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5047         return FALSE;
5048
5049     pszText = lpLVItem->pszText;
5050     bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
5051     if (bResult && lpLVItem->pszText != pszText)
5052         textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
5053     lpLVItem->pszText = pszText;
5054
5055     return bResult;
5056 }
5057
5058
5059 /***
5060  * DESCRIPTION:
5061  * Retrieves the position (upper-left) of the listview control item.
5062  * Note that for LVS_ICON style, the upper-left is that of the icon
5063  * and not the bounding box.
5064  *
5065  * PARAMETER(S):
5066  * [I] infoPtr : valid pointer to the listview structure
5067  * [I] nItem : item index
5068  * [O] lpptPosition : coordinate information
5069  *
5070  * RETURN:
5071  *   SUCCESS : TRUE
5072  *   FAILURE : FALSE
5073  */
5074 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
5075 {
5076     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5077     POINT Origin;
5078
5079     TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
5080
5081     if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5082
5083     LISTVIEW_GetOrigin(infoPtr, &Origin);
5084     LISTVIEW_GetItemOrigin(infoPtr, nItem, lpptPosition);
5085
5086     if (uView == LVS_ICON)
5087     {
5088         lpptPosition->x += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
5089         lpptPosition->y += ICON_TOP_PADDING;
5090     }
5091     lpptPosition->x += Origin.x;
5092     lpptPosition->y += Origin.y;
5093     
5094     TRACE ("  lpptPosition=%s\n", debugpoint(lpptPosition));
5095     return TRUE;
5096 }
5097
5098
5099 /***
5100  * DESCRIPTION:
5101  * Retrieves the bounding rectangle for a listview control item.
5102  *
5103  * PARAMETER(S):
5104  * [I] infoPtr : valid pointer to the listview structure
5105  * [I] nItem : item index
5106  * [IO] lprc : bounding rectangle coordinates
5107  *     lprc->left specifies the portion of the item for which the bounding
5108  *     rectangle will be retrieved.
5109  *
5110  *     LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5111  *        including the icon and label.
5112  *         *
5113  *         * For LVS_ICON
5114  *         * Experiment shows that native control returns:
5115  *         *  width = min (48, length of text line)
5116  *         *    .left = position.x - (width - iconsize.cx)/2
5117  *         *    .right = .left + width
5118  *         *  height = #lines of text * ntmHeight + icon height + 8
5119  *         *    .top = position.y - 2
5120  *         *    .bottom = .top + height
5121  *         *  separation between items .y = itemSpacing.cy - height
5122  *         *                           .x = itemSpacing.cx - width
5123  *     LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5124  *         *
5125  *         * For LVS_ICON
5126  *         * Experiment shows that native control returns:
5127  *         *  width = iconSize.cx + 16
5128  *         *    .left = position.x - (width - iconsize.cx)/2
5129  *         *    .right = .left + width
5130  *         *  height = iconSize.cy + 4
5131  *         *    .top = position.y - 2
5132  *         *    .bottom = .top + height
5133  *         *  separation between items .y = itemSpacing.cy - height
5134  *         *                           .x = itemSpacing.cx - width
5135  *     LVIR_LABEL Returns the bounding rectangle of the item text.
5136  *         *
5137  *         * For LVS_ICON
5138  *         * Experiment shows that native control returns:
5139  *         *  width = text length
5140  *         *    .left = position.x - width/2
5141  *         *    .right = .left + width
5142  *         *  height = ntmH * linecount + 2
5143  *         *    .top = position.y + iconSize.cy + 6
5144  *         *    .bottom = .top + height
5145  *         *  separation between items .y = itemSpacing.cy - height
5146  *         *                           .x = itemSpacing.cx - width
5147  *     LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5148  *      rectangles, but excludes columns in report view.
5149  *
5150  * RETURN:
5151  *   SUCCESS : TRUE
5152  *   FAILURE : FALSE
5153  *
5154  * NOTES
5155  *   Note that the bounding rectangle of the label in the LVS_ICON view depends
5156  *   upon whether the window has the focus currently and on whether the item
5157  *   is the one with the focus.  Ensure that the control's record of which
5158  *   item has the focus agrees with the items' records.
5159  */
5160 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5161 {
5162     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5163     WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5164     BOOL doLabel = TRUE, oversizedBox = FALSE;
5165     POINT Position, Origin;
5166     LVITEMW lvItem;
5167     RECT label_rect;
5168
5169     TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
5170
5171     if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5172
5173     LISTVIEW_GetOrigin(infoPtr, &Origin);
5174     LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
5175
5176     /* Be smart and try to figure out the minimum we have to do */
5177     if (lprc->left == LVIR_ICON) doLabel = FALSE;
5178     if (uView == LVS_REPORT && lprc->left == LVIR_BOUNDS) doLabel = FALSE;
5179     if (uView == LVS_ICON && lprc->left != LVIR_ICON &&
5180         infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
5181         oversizedBox = TRUE;
5182
5183     /* get what we need from the item before hand, so we make
5184      * only one request. This can speed up things, if data
5185      * is stored on the app side */
5186     lvItem.mask = 0;
5187     if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5188     if (doLabel) lvItem.mask |= LVIF_TEXT;
5189     lvItem.iItem = nItem;
5190     lvItem.iSubItem = 0;
5191     lvItem.pszText = szDispText;
5192     lvItem.cchTextMax = DISP_TEXT_SIZE;
5193     if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5194     /* we got the state already up, simulate it here, to avoid a reget */
5195     if (uView == LVS_ICON && (lprc->left != LVIR_ICON))
5196     {
5197         lvItem.mask |= LVIF_STATE;
5198         lvItem.stateMask = LVIS_FOCUSED;
5199         lvItem.state = (oversizedBox ? LVIS_FOCUSED : 0);
5200     }
5201
5202     if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && lprc->left == LVIR_SELECTBOUNDS)
5203         lprc->left = LVIR_BOUNDS;
5204     switch(lprc->left)
5205     {
5206     case LVIR_ICON:
5207         LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL);
5208         break;
5209
5210     case LVIR_LABEL:
5211         LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, NULL, lprc);
5212         break;
5213
5214     case LVIR_BOUNDS:
5215         LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL);
5216         break;
5217
5218     case LVIR_SELECTBOUNDS:
5219         LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, &label_rect);
5220         UnionRect(lprc, lprc, &label_rect);
5221         break;
5222
5223     default:
5224         WARN("Unknown value: %ld\n", lprc->left);
5225         return FALSE;
5226     }
5227
5228     OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
5229
5230     TRACE(" rect=%s\n", debugrect(lprc));
5231
5232     return TRUE;
5233 }
5234
5235 /***
5236  * DESCRIPTION:
5237  * Retrieves the spacing between listview control items.
5238  *
5239  * PARAMETER(S):
5240  * [I] infoPtr : valid pointer to the listview structure
5241  * [IO] lprc : rectangle to receive the output
5242  *             on input, lprc->top = nSubItem
5243  *                       lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
5244  * 
5245  * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
5246  *       not only those of the first column.
5247  *       Fortunately, LISTVIEW_GetItemMetrics does the right thing.
5248  * 
5249  * RETURN:
5250  *     TRUE: success
5251  *     FALSE: failure
5252  */
5253 static BOOL LISTVIEW_GetSubItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5254 {
5255     POINT Position;
5256     LVITEMW lvItem;
5257     
5258     if (!lprc || (infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return FALSE;
5259     
5260     TRACE("(nItem=%d, nSubItem=%ld)\n", nItem, lprc->top);
5261     /* On WinNT, a subitem of '0' calls LISTVIEW_GetItemRect */
5262     if (lprc->top == 0)
5263         return LISTVIEW_GetItemRect(infoPtr, nItem, lprc);
5264
5265     if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &Position)) return FALSE;
5266
5267     lvItem.mask = lprc->top == 0 ? LVIF_INDENT : 0;
5268     lvItem.iItem = nItem;
5269     lvItem.iSubItem = lprc->top;
5270     
5271     if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5272     switch(lprc->left)
5273     {
5274     case LVIR_ICON:
5275         LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL);
5276         break;
5277
5278     case LVIR_LABEL:
5279     case LVIR_BOUNDS:
5280         LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL);
5281         break;
5282
5283     default:
5284         ERR("Unknown bounds=%ld\n", lprc->left);
5285         return FALSE;
5286     }
5287
5288     OffsetRect(lprc, Position.x, Position.y);
5289     return TRUE;
5290 }
5291
5292
5293 /***
5294  * DESCRIPTION:
5295  * Retrieves the width of a label.
5296  *
5297  * PARAMETER(S):
5298  * [I] infoPtr : valid pointer to the listview structure
5299  *
5300  * RETURN:
5301  *   SUCCESS : string width (in pixels)
5302  *   FAILURE : zero
5303  */
5304 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *infoPtr, INT nItem)
5305 {
5306     WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5307     LVITEMW lvItem;
5308
5309     TRACE("(nItem=%d)\n", nItem);
5310
5311     lvItem.mask = LVIF_TEXT;
5312     lvItem.iItem = nItem;
5313     lvItem.iSubItem = 0;
5314     lvItem.pszText = szDispText;
5315     lvItem.cchTextMax = DISP_TEXT_SIZE;
5316     if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5317   
5318     return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
5319 }
5320
5321 /***
5322  * DESCRIPTION:
5323  * Retrieves the spacing between listview control items.
5324  *
5325  * PARAMETER(S):
5326  * [I] infoPtr : valid pointer to the listview structure
5327  * [I] bSmall : flag for small or large icon
5328  *
5329  * RETURN:
5330  * Horizontal + vertical spacing
5331  */
5332 static LONG LISTVIEW_GetItemSpacing(LISTVIEW_INFO *infoPtr, BOOL bSmall)
5333 {
5334   LONG lResult;
5335
5336   if (!bSmall)
5337   {
5338     lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
5339   }
5340   else
5341   {
5342     if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON)
5343       lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
5344     else
5345       lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
5346   }
5347   return lResult;
5348 }
5349
5350 /***
5351  * DESCRIPTION:
5352  * Retrieves the state of a listview control item.
5353  *
5354  * PARAMETER(S):
5355  * [I] infoPtr : valid pointer to the listview structure
5356  * [I] nItem : item index
5357  * [I] uMask : state mask
5358  *
5359  * RETURN:
5360  * State specified by the mask.
5361  */
5362 static UINT LISTVIEW_GetItemState(LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
5363 {
5364     LVITEMW lvItem;
5365
5366     if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5367
5368     lvItem.iItem = nItem;
5369     lvItem.iSubItem = 0;
5370     lvItem.mask = LVIF_STATE;
5371     lvItem.stateMask = uMask;
5372     if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5373
5374     return lvItem.state & uMask;
5375 }
5376
5377 /***
5378  * DESCRIPTION:
5379  * Retrieves the text of a listview control item or subitem.
5380  *
5381  * PARAMETER(S):
5382  * [I] hwnd : window handle
5383  * [I] nItem : item index
5384  * [IO] lpLVItem : item information
5385  * [I] isW :  TRUE if lpLVItem is Unicode
5386  *
5387  * RETURN:
5388  *   SUCCESS : string length
5389  *   FAILURE : 0
5390  */
5391 static INT LISTVIEW_GetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
5392 {
5393     if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5394
5395     lpLVItem->mask = LVIF_TEXT;
5396     lpLVItem->iItem = nItem;
5397     if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
5398
5399     return textlenT(lpLVItem->pszText, isW);
5400 }
5401
5402 /***
5403  * DESCRIPTION:
5404  * Searches for an item based on properties + relationships.
5405  *
5406  * PARAMETER(S):
5407  * [I] infoPtr : valid pointer to the listview structure
5408  * [I] nItem : item index
5409  * [I] uFlags : relationship flag
5410  *
5411  * RETURN:
5412  *   SUCCESS : item index
5413  *   FAILURE : -1
5414  */
5415 static INT LISTVIEW_GetNextItem(LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
5416 {
5417     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5418     UINT uMask = 0;
5419     LVFINDINFOW lvFindInfo;
5420     INT nCountPerColumn;
5421     INT nCountPerRow;
5422     INT i;
5423
5424     TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
5425     if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
5426
5427     ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
5428
5429     if (uFlags & LVNI_CUT)
5430       uMask |= LVIS_CUT;
5431
5432     if (uFlags & LVNI_DROPHILITED)
5433       uMask |= LVIS_DROPHILITED;
5434
5435     if (uFlags & LVNI_FOCUSED)
5436       uMask |= LVIS_FOCUSED;
5437
5438     if (uFlags & LVNI_SELECTED)
5439       uMask |= LVIS_SELECTED;
5440
5441     /* if we're asked for the focused item, that's only one, 
5442      * so it's worth optimizing */
5443     if (uFlags & LVNI_FOCUSED)
5444     {
5445         if (!(LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) == uMask) return -1;
5446         return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
5447     }
5448     
5449     if (uFlags & LVNI_ABOVE)
5450     {
5451       if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5452       {
5453         while (nItem >= 0)
5454         {
5455           nItem--;
5456           if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5457             return nItem;
5458         }
5459       }
5460       else
5461       {
5462         /* Special case for autoarrange - move 'til the top of a list */
5463         if (is_autoarrange(infoPtr))
5464         {
5465           nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5466           while (nItem - nCountPerRow >= 0)
5467           {
5468             nItem -= nCountPerRow;
5469             if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5470               return nItem;
5471           }
5472           return -1;
5473         }
5474         lvFindInfo.flags = LVFI_NEARESTXY;
5475         lvFindInfo.vkDirection = VK_UP;
5476         ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5477         while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5478         {
5479           if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5480             return nItem;
5481         }
5482       }
5483     }
5484     else if (uFlags & LVNI_BELOW)
5485     {
5486       if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5487       {
5488         while (nItem < infoPtr->nItemCount)
5489         {
5490           nItem++;
5491           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5492             return nItem;
5493         }
5494       }
5495       else
5496       {
5497         /* Special case for autoarrange - move 'til the bottom of a list */
5498         if (is_autoarrange(infoPtr))
5499         {
5500           nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5501           while (nItem + nCountPerRow < infoPtr->nItemCount )
5502           {
5503             nItem += nCountPerRow;
5504             if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5505               return nItem;
5506           }
5507           return -1;
5508         }
5509         lvFindInfo.flags = LVFI_NEARESTXY;
5510         lvFindInfo.vkDirection = VK_DOWN;
5511         ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5512         while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5513         {
5514           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5515             return nItem;
5516         }
5517       }
5518     }
5519     else if (uFlags & LVNI_TOLEFT)
5520     {
5521       if (uView == LVS_LIST)
5522       {
5523         nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5524         while (nItem - nCountPerColumn >= 0)
5525         {
5526           nItem -= nCountPerColumn;
5527           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5528             return nItem;
5529         }
5530       }
5531       else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5532       {
5533         /* Special case for autoarrange - move 'ti the beginning of a row */
5534         if (is_autoarrange(infoPtr))
5535         {
5536           nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5537           while (nItem % nCountPerRow > 0)
5538           {
5539             nItem --;
5540             if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5541               return nItem;
5542           }
5543           return -1;
5544         }
5545         lvFindInfo.flags = LVFI_NEARESTXY;
5546         lvFindInfo.vkDirection = VK_LEFT;
5547         ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5548         while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5549         {
5550           if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5551             return nItem;
5552         }
5553       }
5554     }
5555     else if (uFlags & LVNI_TORIGHT)
5556     {
5557       if (uView == LVS_LIST)
5558       {
5559         nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5560         while (nItem + nCountPerColumn < infoPtr->nItemCount)
5561         {
5562           nItem += nCountPerColumn;
5563           if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5564             return nItem;
5565         }
5566       }
5567       else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5568       {
5569         /* Special case for autoarrange - move 'til the end of a row */
5570         if (is_autoarrange(infoPtr))
5571         {
5572           nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5573           while (nItem % nCountPerRow < nCountPerRow - 1 )
5574           {
5575             nItem ++;
5576             if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5577               return nItem;
5578           }
5579           return -1;
5580         }
5581         lvFindInfo.flags = LVFI_NEARESTXY;
5582         lvFindInfo.vkDirection = VK_RIGHT;
5583         ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5584         while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5585         {
5586           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5587             return nItem;
5588         }
5589       }
5590     }
5591     else
5592     {
5593       nItem++;
5594
5595       /* search by index */
5596       for (i = nItem; i < infoPtr->nItemCount; i++)
5597       {
5598         if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
5599           return i;
5600       }
5601     }
5602
5603     return -1;
5604 }
5605
5606 /* LISTVIEW_GetNumberOfWorkAreas */
5607
5608 /***
5609  * DESCRIPTION:
5610  * Retrieves the origin coordinates when in icon or small icon display mode.
5611  *
5612  * PARAMETER(S):
5613  * [I] infoPtr : valid pointer to the listview structure
5614  * [O] lpptOrigin : coordinate information
5615  *
5616  * RETURN:
5617  *   None.
5618  */
5619 static void LISTVIEW_GetOrigin(LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
5620 {
5621     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5622     INT nHorzPos = 0, nVertPos = 0;
5623     SCROLLINFO scrollInfo;
5624
5625     scrollInfo.cbSize = sizeof(SCROLLINFO);    
5626     scrollInfo.fMask = SIF_POS;
5627     
5628     if ((infoPtr->dwStyle & WS_HSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
5629         nHorzPos = scrollInfo.nPos;
5630     if ((infoPtr->dwStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
5631         nVertPos = scrollInfo.nPos;
5632
5633     TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
5634
5635     lpptOrigin->x = infoPtr->rcList.left;
5636     lpptOrigin->y = infoPtr->rcList.top;
5637     if (uView == LVS_LIST)
5638         nHorzPos *= infoPtr->nItemWidth;
5639     else if (uView == LVS_REPORT)
5640         nVertPos *= infoPtr->nItemHeight;
5641     
5642     lpptOrigin->x -= nHorzPos;
5643     lpptOrigin->y -= nVertPos;
5644
5645     TRACE(" origin=%s\n", debugpoint(lpptOrigin));
5646 }
5647
5648 /***
5649  * DESCRIPTION:
5650  * Retrieves the width of a string.
5651  *
5652  * PARAMETER(S):
5653  * [I] hwnd : window handle
5654  * [I] lpszText : text string to process
5655  * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
5656  *
5657  * RETURN:
5658  *   SUCCESS : string width (in pixels)
5659  *   FAILURE : zero
5660  */
5661 static INT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
5662 {
5663     SIZE stringSize;
5664     
5665     stringSize.cx = 0;    
5666     if (is_textT(lpszText, isW))
5667     {
5668         HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
5669         HDC hdc = GetDC(infoPtr->hwndSelf);
5670         HFONT hOldFont = SelectObject(hdc, hFont);
5671
5672         if (isW)
5673             GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
5674         else
5675             GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
5676         SelectObject(hdc, hOldFont);
5677         ReleaseDC(infoPtr->hwndSelf, hdc);
5678     }
5679     return stringSize.cx;
5680 }
5681
5682 /***
5683  * DESCRIPTION:
5684  * Determines which listview item is located at the specified position.
5685  *
5686  * PARAMETER(S):
5687  * [I] infoPtr : valid pointer to the listview structure
5688  * [IO] lpht : hit test information
5689  * [I] subitem : fill out iSubItem.
5690  * [I] select : return the index only if the hit selects the item
5691  *
5692  * NOTE:
5693  * (mm 20001022): We must not allow iSubItem to be touched, for
5694  * an app might pass only a structure with space up to iItem!
5695  * (MS Office 97 does that for instance in the file open dialog)
5696  * 
5697  * RETURN:
5698  *   SUCCESS : item index
5699  *   FAILURE : -1
5700  */
5701 static INT LISTVIEW_HitTest(LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select)
5702 {
5703     WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5704     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5705     RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch;
5706     POINT Origin, Position, opt;
5707     LVITEMW lvItem;
5708     ITERATOR i;
5709     
5710     TRACE("(pt=%s, subitem=%d, select=%d)\n", debugpoint(&lpht->pt), subitem, select);
5711     
5712     lpht->flags = 0;
5713     lpht->iItem = -1;
5714     if (subitem) lpht->iSubItem = 0;
5715
5716     if (infoPtr->rcList.left > lpht->pt.x)
5717         lpht->flags |= LVHT_TOLEFT;
5718     else if (infoPtr->rcList.right < lpht->pt.x)
5719         lpht->flags |= LVHT_TORIGHT;
5720     
5721     if (infoPtr->rcList.top > lpht->pt.y)
5722         lpht->flags |= LVHT_ABOVE;
5723     else if (infoPtr->rcList.bottom < lpht->pt.y)
5724         lpht->flags |= LVHT_BELOW;
5725
5726     TRACE("lpht->flags=0x%x\n", lpht->flags);
5727     if (lpht->flags) return -1;
5728
5729     lpht->flags |= LVHT_NOWHERE;
5730
5731     LISTVIEW_GetOrigin(infoPtr, &Origin);
5732    
5733     /* first deal with the large items */
5734     rcSearch.left = lpht->pt.x;
5735     rcSearch.top = lpht->pt.y;
5736     rcSearch.right = rcSearch.left + 1;
5737     rcSearch.bottom = rcSearch.top + 1;
5738     
5739     iterator_frameditems(&i, infoPtr, &rcSearch);
5740     iterator_next(&i); /* go to first item in the sequence */
5741     lpht->iItem = i.nItem;
5742     iterator_destroy(&i);
5743    
5744     TRACE("lpht->iItem=%d\n", lpht->iItem); 
5745     if (lpht->iItem == -1) return -1;
5746
5747     lvItem.mask = LVIF_STATE | LVIF_TEXT;
5748     if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5749     lvItem.stateMask = LVIS_STATEIMAGEMASK;
5750     if (uView == LVS_ICON) lvItem.stateMask |= LVIS_FOCUSED;
5751     lvItem.iItem = lpht->iItem;
5752     lvItem.iSubItem = 0;
5753     lvItem.pszText = szDispText;
5754     lvItem.cchTextMax = DISP_TEXT_SIZE;
5755     if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1;
5756     if (!infoPtr->bFocus) lvItem.state &= ~LVIS_FOCUSED; 
5757     
5758     LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel);
5759     LISTVIEW_GetItemOrigin(infoPtr, lpht->iItem, &Position);
5760     opt.x = lpht->pt.x - Position.x - Origin.x;
5761     opt.y = lpht->pt.y - Position.y - Origin.y;
5762     
5763     if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
5764         rcBounds = rcBox;
5765     else
5766         UnionRect(&rcBounds, &rcIcon, &rcLabel);
5767     TRACE("rcBounds=%s\n", debugrect(&rcBounds));
5768     if (!PtInRect(&rcBounds, opt)) return -1;
5769
5770     if (PtInRect(&rcIcon, opt))
5771         lpht->flags |= LVHT_ONITEMICON;
5772     else if (PtInRect(&rcLabel, opt))
5773         lpht->flags |= LVHT_ONITEMLABEL;
5774     else if (infoPtr->himlState && ((lvItem.state & LVIS_STATEIMAGEMASK) >> 12) && PtInRect(&rcState, opt))
5775         lpht->flags |= LVHT_ONITEMSTATEICON;
5776     if (lpht->flags & LVHT_ONITEM)
5777         lpht->flags &= ~LVHT_NOWHERE;
5778    
5779     TRACE("lpht->flags=0x%x\n", lpht->flags); 
5780     if (uView == LVS_REPORT && lpht->iItem != -1 && subitem)
5781     {
5782         INT j;
5783
5784         rcBounds.right = rcBounds.left;
5785         for (j = 0; j < infoPtr->hdpaColumns->nItemCount; j++)
5786         {
5787             rcBounds.left = rcBounds.right;
5788             rcBounds.right += LISTVIEW_GetColumnWidth(infoPtr, j);
5789             if (PtInRect(&rcBounds, opt))
5790             {
5791                 lpht->iSubItem = j;
5792                 break;
5793             }
5794         }
5795     }
5796
5797     if (!select || lpht->iItem == -1) return lpht->iItem;
5798
5799     if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)) return lpht->iItem;
5800     
5801     if (uView == LVS_REPORT) UnionRect(&rcBounds, &rcIcon, &rcLabel);
5802     return PtInRect(&rcBounds, opt) ? lpht->iItem : -1;
5803 }
5804
5805
5806 /* LISTVIEW_InsertCompare:  callback routine for comparing pszText members of the LV_ITEMS
5807    in a LISTVIEW on insert.  Passed to DPA_Sort in LISTVIEW_InsertItem.
5808    This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
5809    and not during the processing of a LVM_SORTITEMS message. Applications should provide
5810    their own sort proc. when sending LVM_SORTITEMS.
5811 */
5812 /* Platform SDK:
5813     (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
5814         if:
5815           LVS_SORTXXX must be specified,
5816           LVS_OWNERDRAW is not set,
5817           <item>.pszText is not LPSTR_TEXTCALLBACK.
5818
5819     (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
5820     are sorted based on item text..."
5821 */
5822 static INT WINAPI LISTVIEW_InsertCompare(  LPVOID first, LPVOID second,  LPARAM lParam)
5823 {
5824     ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
5825     ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
5826     INT cmpv = textcmpWT(lv_first->hdr.pszText, lv_second->hdr.pszText, TRUE); 
5827
5828     /* if we're sorting descending, negate the return value */
5829     return (((LISTVIEW_INFO *)lParam)->dwStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
5830 }
5831
5832 /***
5833  * nESCRIPTION:
5834  * Inserts a new item in the listview control.
5835  *
5836  * PARAMETER(S):
5837  * [I] infoPtr : valid pointer to the listview structure
5838  * [I] lpLVItem : item information
5839  * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
5840  *
5841  * RETURN:
5842  *   SUCCESS : new item index
5843  *   FAILURE : -1
5844  */
5845 static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
5846 {
5847     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5848     INT nItem;
5849     HDPA hdpaSubItems;
5850     NMLISTVIEW nmlv;
5851     ITEM_INFO *lpItem;
5852     BOOL is_sorted, has_changed;
5853     LVITEMW item;
5854
5855     TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
5856
5857     if (infoPtr->dwStyle & LVS_OWNERDATA) return infoPtr->nItemCount++;
5858
5859     /* make sure it's an item, and not a subitem; cannot insert a subitem */
5860     if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iSubItem) return -1;
5861
5862     if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return -1;
5863
5864     if ( !(lpItem = (ITEM_INFO *)COMCTL32_Alloc(sizeof(ITEM_INFO))) )
5865         return -1;
5866     
5867     /* insert item in listview control data structure */
5868     if ( !(hdpaSubItems = DPA_Create(8)) ) goto fail;
5869     if ( !DPA_SetPtr(hdpaSubItems, 0, lpItem) ) assert (FALSE);
5870
5871     is_sorted = (infoPtr->dwStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
5872                 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
5873
5874     nItem = is_sorted ? infoPtr->nItemCount : min(lpLVItem->iItem, infoPtr->nItemCount);
5875     TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem);
5876     nItem = DPA_InsertPtr( infoPtr->hdpaItems, nItem, hdpaSubItems );
5877     if (nItem == -1) goto fail;
5878     infoPtr->nItemCount++;
5879   
5880     /* set the item attributes */ 
5881     item = *lpLVItem;
5882     item.iItem = nItem;
5883     item.state &= ~LVIS_STATEIMAGEMASK;
5884     if (!set_main_item(infoPtr, &item, TRUE, isW, &has_changed)) goto undo;
5885
5886     /* if we're sorted, sort the list, and update the index */
5887     if (is_sorted)
5888     {
5889         DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, (LPARAM)infoPtr );
5890         nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
5891         assert(nItem != -1);
5892     }
5893
5894     /* make room for the position, if we are in the right mode */
5895     if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5896     {
5897         if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
5898             goto undo;
5899         if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
5900         {
5901             DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
5902             goto undo;
5903         }
5904     }
5905     
5906     /* Add the subitem list to the items array. Do this last in case we go to
5907      * fail during the above.
5908      */
5909     LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
5910     
5911     /* send LVN_INSERTITEM notification */
5912     ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
5913     nmlv.iItem = nItem;
5914     nmlv.lParam = lpItem->lParam;
5915     notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
5916
5917     /* align items (set position of each item) */
5918     if ((uView == LVS_SMALLICON || uView == LVS_ICON))
5919     {
5920         POINT pt;
5921
5922         if (infoPtr->dwStyle & LVS_ALIGNLEFT)
5923             LISTVIEW_NextIconPosLeft(infoPtr, &pt);
5924         else
5925             LISTVIEW_NextIconPosTop(infoPtr, &pt);
5926
5927         LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, TRUE);
5928     }
5929
5930     /* now is the invalidation fun */
5931     LISTVIEW_ScrollOnInsert(infoPtr, nItem, 1);
5932     return nItem;
5933
5934 undo:
5935     DPA_DeletePtr(infoPtr->hdpaItems, nItem);
5936     infoPtr->nItemCount--;
5937 fail:
5938     DPA_DeletePtr(hdpaSubItems, 0);
5939     DPA_Destroy (hdpaSubItems);
5940     COMCTL32_Free (lpItem);
5941     return -1;
5942 }
5943
5944 /***
5945  * DESCRIPTION:
5946  * Redraws a range of items.
5947  *
5948  * PARAMETER(S):
5949  * [I] infoPtr : valid pointer to the listview structure
5950  * [I] nFirst : first item
5951  * [I] nLast : last item
5952  *
5953  * RETURN:
5954  *   SUCCESS : TRUE
5955  *   FAILURE : FALSE
5956  */
5957 static BOOL LISTVIEW_RedrawItems(LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
5958 {
5959     INT i;
5960  
5961     if (nLast < nFirst || min(nFirst, nLast) < 0 || 
5962         max(nFirst, nLast) >= infoPtr->nItemCount)
5963         return FALSE;
5964     
5965     for (i = nFirst; i <= nLast; i++)
5966         LISTVIEW_InvalidateItem(infoPtr, i);
5967
5968     return TRUE;
5969 }
5970
5971 /***
5972  * DESCRIPTION:
5973  * Scroll the content of a listview.
5974  *
5975  * PARAMETER(S):
5976  * [I] infoPtr : valid pointer to the listview structure
5977  * [I] dx : horizontal scroll amount in pixels
5978  * [I] dy : vertical scroll amount in pixels
5979  *
5980  * RETURN:
5981  *   SUCCESS : TRUE
5982  *   FAILURE : FALSE
5983  *
5984  * COMMENTS:
5985  *  If the control is in report mode (LVS_REPORT) the control can
5986  *  be scrolled only in line increments. "dy" will be rounded to the
5987  *  nearest number of pixels that are a whole line. Ex: if line height
5988  *  is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
5989  *  is passed the the scroll will be 0.  (per MSDN 7/2002)
5990  *
5991  *  For:  (per experimentaion with native control and CSpy ListView)
5992  *     LVS_ICON       dy=1 = 1 pixel  (vertical only)
5993  *                    dx ignored
5994  *     LVS_SMALLICON  dy=1 = 1 pixel  (vertical only)
5995  *                    dx ignored
5996  *     LVS_LIST       dx=1 = 1 column (horizontal only)
5997  *                           but will only scroll 1 column per message
5998  *                           no matter what the value.
5999  *                    dy must be 0 or FALSE returned.
6000  *     LVS_REPORT     dx=1 = 1 pixel
6001  *                    dy=  see above
6002  *
6003  */
6004 static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
6005 {
6006     switch(infoPtr->dwStyle & LVS_TYPEMASK) {
6007     case LVS_REPORT:
6008         dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
6009         dy /= infoPtr->nItemHeight;
6010         break;
6011     case LVS_LIST:
6012         if (dy != 0) return FALSE;
6013         break;
6014     default: /* icon */
6015         dx = 0;
6016         break;
6017     }   
6018
6019     if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
6020     if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
6021   
6022     return TRUE;
6023 }
6024
6025 /***
6026  * DESCRIPTION:
6027  * Sets the background color.
6028  *
6029  * PARAMETER(S):
6030  * [I] infoPtr : valid pointer to the listview structure
6031  * [I] clrBk : background color
6032  *
6033  * RETURN:
6034  *   SUCCESS : TRUE
6035  *   FAILURE : FALSE
6036  */
6037 static BOOL LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
6038 {
6039     TRACE("(clrBk=%lx)\n", clrBk);
6040
6041     if(infoPtr->clrBk != clrBk) {
6042         if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
6043         infoPtr->clrBk = clrBk;
6044         if (clrBk == CLR_NONE)
6045             infoPtr->hBkBrush = (HBRUSH)GetClassLongW(infoPtr->hwndSelf, GCL_HBRBACKGROUND);
6046         else
6047             infoPtr->hBkBrush = CreateSolidBrush(clrBk);
6048         LISTVIEW_InvalidateList(infoPtr);
6049     }
6050
6051    return TRUE;
6052 }
6053
6054 /* LISTVIEW_SetBkImage */
6055
6056 /*** Helper for {Insert,Set}ColumnT *only* */
6057 static void column_fill_hditem(LISTVIEW_INFO *infoPtr, HDITEMW *lphdi, INT nColumn, const LVCOLUMNW *lpColumn, BOOL isW)
6058 {
6059     if (lpColumn->mask & LVCF_FMT)
6060     {
6061         /* format member is valid */
6062         lphdi->mask |= HDI_FORMAT;
6063
6064         /* set text alignment (leftmost column must be left-aligned) */
6065         if (nColumn == 0 || lpColumn->fmt & LVCFMT_LEFT)
6066             lphdi->fmt |= HDF_LEFT;
6067         else if (lpColumn->fmt & LVCFMT_RIGHT)
6068             lphdi->fmt |= HDF_RIGHT;
6069         else if (lpColumn->fmt & LVCFMT_CENTER)
6070             lphdi->fmt |= HDF_CENTER;
6071
6072         if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6073             lphdi->fmt |= HDF_BITMAP_ON_RIGHT;
6074
6075         if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6076         {
6077             lphdi->fmt |= HDF_IMAGE;
6078             lphdi->iImage = I_IMAGECALLBACK;
6079         }
6080     }
6081
6082     if (lpColumn->mask & LVCF_WIDTH)
6083     {
6084         lphdi->mask |= HDI_WIDTH;
6085         if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
6086         {
6087             /* make it fill the remainder of the controls width */
6088             RECT rcHeader;
6089             INT item_index;
6090
6091             for(item_index = 0; item_index < (nColumn - 1); item_index++)
6092             {
6093                 LISTVIEW_GetHeaderRect(infoPtr, item_index, &rcHeader);
6094                 lphdi->cxy += rcHeader.right - rcHeader.left;
6095             }
6096
6097             /* retrieve the layout of the header */
6098             GetClientRect(infoPtr->hwndSelf, &rcHeader);
6099             TRACE("start cxy=%d rcHeader=%s\n", lphdi->cxy, debugrect(&rcHeader));
6100
6101             lphdi->cxy = (rcHeader.right - rcHeader.left) - lphdi->cxy;
6102         }
6103         else
6104             lphdi->cxy = lpColumn->cx;
6105     }
6106
6107     if (lpColumn->mask & LVCF_TEXT)
6108     {
6109         lphdi->mask |= HDI_TEXT | HDI_FORMAT;
6110         lphdi->fmt |= HDF_STRING;
6111         lphdi->pszText = lpColumn->pszText;
6112         lphdi->cchTextMax = textlenT(lpColumn->pszText, isW);
6113     }
6114
6115     if (lpColumn->mask & LVCF_IMAGE)
6116     {
6117         lphdi->mask |= HDI_IMAGE;
6118         lphdi->iImage = lpColumn->iImage;
6119     }
6120
6121     if (lpColumn->mask & LVCF_ORDER)
6122     {
6123         lphdi->mask |= HDI_ORDER;
6124         lphdi->iOrder = lpColumn->iOrder;
6125     }
6126 }
6127
6128
6129 /***
6130  * DESCRIPTION:
6131  * Inserts a new column.
6132  *
6133  * PARAMETER(S):
6134  * [I] infoPtr : valid pointer to the listview structure
6135  * [I] nColumn : column index
6136  * [I] lpColumn : column information
6137  * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
6138  *
6139  * RETURN:
6140  *   SUCCESS : new column index
6141  *   FAILURE : -1
6142  */
6143 static INT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6144                                   const LVCOLUMNW *lpColumn, BOOL isW)
6145 {
6146     COLUMN_INFO *lpColumnInfo;
6147     INT nNewColumn;
6148     HDITEMW hdi;
6149
6150     TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6151
6152     if (!lpColumn || nColumn < 0) return -1;
6153     nColumn = min(nColumn, infoPtr->hdpaColumns->nItemCount);
6154     
6155     ZeroMemory(&hdi, sizeof(HDITEMW));
6156     column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6157
6158     /* insert item in header control */
6159     nNewColumn = SendMessageW(infoPtr->hwndHeader, 
6160                               isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
6161                               (WPARAM)nColumn, (LPARAM)&hdi);
6162     if (nNewColumn == -1) return -1;
6163     if (nNewColumn != nColumn) ERR("nColumn=%d, nNewColumn=%d\n", nColumn, nNewColumn);
6164    
6165     /* create our own column info */ 
6166     if (!(lpColumnInfo = COMCTL32_Alloc(sizeof(COLUMN_INFO)))) goto fail;
6167     if (DPA_InsertPtr(infoPtr->hdpaColumns, nNewColumn, lpColumnInfo) == -1) goto fail;
6168
6169     if (lpColumn->mask & LVCF_FMT) lpColumnInfo->fmt = lpColumn->fmt;
6170     if (!Header_GetItemRect(infoPtr->hwndHeader, nNewColumn, &lpColumnInfo->rcHeader)) goto fail;
6171
6172     /* now we have to actually adjust the data */
6173     if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
6174     {
6175         SUBITEM_INFO *lpSubItem, *lpMainItem, **lpNewItems = 0;
6176         HDPA hdpaSubItems;
6177         INT nItem, i;
6178         
6179         /* preallocate memory, so we can fail gracefully */
6180         if (nNewColumn == 0)
6181         {
6182             lpNewItems = COMCTL32_Alloc(sizeof(SUBITEM_INFO *) * infoPtr->nItemCount);
6183             if (!lpNewItems) goto fail;
6184             for (i = 0; i < infoPtr->nItemCount; i++)
6185                 if (!(lpNewItems[i] = COMCTL32_Alloc(sizeof(SUBITEM_INFO)))) break;
6186             if (i != infoPtr->nItemCount)
6187             {
6188                 for(; i >=0; i--) COMCTL32_Free(lpNewItems[i]);
6189                 COMCTL32_Free(lpNewItems);
6190                 goto fail;
6191             }
6192         }
6193         
6194         for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
6195         {
6196             hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
6197             for (i = 1; i < hdpaSubItems->nItemCount; i++)
6198             {
6199                 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
6200                 if (lpSubItem->iSubItem >= nNewColumn)
6201                     lpSubItem->iSubItem++;
6202             }
6203
6204             /* for inserting column 0, we have to special-case the main item */ 
6205             if (nNewColumn == 0)
6206             {
6207                 lpMainItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
6208                 lpSubItem = lpNewItems[nItem];
6209                 lpSubItem->hdr = lpMainItem->hdr;
6210                 lpSubItem->iSubItem = 1;
6211                 ZeroMemory(&lpMainItem->hdr, sizeof(lpMainItem->hdr));
6212                 lpMainItem->iSubItem = 0;
6213                 DPA_InsertPtr(hdpaSubItems, 1, lpSubItem);
6214             }
6215         }
6216
6217         COMCTL32_Free(lpNewItems);
6218     }
6219
6220     /* make space for the new column */
6221     LISTVIEW_ScrollColumns(infoPtr, nNewColumn + 1, lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
6222     
6223     return nNewColumn;
6224
6225 fail:
6226     if (nNewColumn != -1) SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nNewColumn, 0);
6227     if (lpColumnInfo)
6228     {
6229         DPA_DeletePtr(infoPtr->hdpaColumns, nNewColumn);
6230         COMCTL32_Free(lpColumnInfo);
6231     }
6232     return -1;
6233 }
6234
6235 /***
6236  * DESCRIPTION:
6237  * Sets the attributes of a header item.
6238  *
6239  * PARAMETER(S):
6240  * [I] infoPtr : valid pointer to the listview structure
6241  * [I] nColumn : column index
6242  * [I] lpColumn : column attributes
6243  * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
6244  *
6245  * RETURN:
6246  *   SUCCESS : TRUE
6247  *   FAILURE : FALSE
6248  */
6249 static BOOL LISTVIEW_SetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6250                                 const LVCOLUMNW *lpColumn, BOOL isW)
6251 {
6252     HDITEMW hdi, hdiget;
6253     BOOL bResult;
6254
6255     TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6256     
6257     if (!lpColumn || nColumn < 0 || nColumn >= infoPtr->hdpaColumns->nItemCount) return FALSE;
6258
6259     ZeroMemory(&hdi, sizeof(HDITEMW));
6260     if (lpColumn->mask & LVCF_FMT)
6261     {
6262         hdi.mask |= HDI_FORMAT;
6263         hdiget.mask = HDI_FORMAT;
6264         if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
6265             hdi.fmt = hdiget.fmt & HDF_STRING;
6266     }
6267     column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6268
6269     /* set header item attributes */
6270     bResult = SendMessageW(infoPtr->hwndHeader, isW ? HDM_SETITEMW : HDM_SETITEMA, (WPARAM)nColumn, (LPARAM)&hdi);
6271     if (!bResult) return FALSE;
6272
6273     if (lpColumn->mask & LVCF_FMT)
6274     {
6275         COLUMN_INFO *lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
6276         int oldFmt = lpColumnInfo->fmt;
6277         
6278         lpColumnInfo->fmt = lpColumn->fmt;
6279         if ((oldFmt ^ lpColumn->fmt) & (LVCFMT_JUSTIFYMASK | LVCFMT_IMAGE))
6280         {
6281             UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6282             if (uView == LVS_REPORT) LISTVIEW_InvalidateColumn(infoPtr, nColumn);
6283         }
6284     }
6285
6286     return TRUE;
6287 }
6288
6289 /***
6290  * DESCRIPTION:
6291  * Sets the column order array
6292  *
6293  * PARAMETERS:
6294  * [I] infoPtr : valid pointer to the listview structure
6295  * [I] iCount : number of elements in column order array
6296  * [I] lpiArray : pointer to column order array
6297  *
6298  * RETURN:
6299  *   SUCCESS : TRUE
6300  *   FAILURE : FALSE
6301  */
6302 static BOOL LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, const INT *lpiArray)
6303 {
6304   FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
6305
6306   if (!lpiArray)
6307     return FALSE;
6308
6309   return TRUE;
6310 }
6311
6312
6313 /***
6314  * DESCRIPTION:
6315  * Sets the width of a column
6316  *
6317  * PARAMETERS:
6318  * [I] infoPtr : valid pointer to the listview structure
6319  * [I] nColumn : column index
6320  * [I] cx : column width
6321  *
6322  * RETURN:
6323  *   SUCCESS : TRUE
6324  *   FAILURE : FALSE
6325  */
6326 static BOOL LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn, INT cx)
6327 {
6328     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6329     WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
6330     INT max_cx = 0;
6331     HDITEMW hdi;
6332
6333     TRACE("(nColumn=%d, cx=%d\n", nColumn, cx);
6334
6335     /* set column width only if in report or list mode */
6336     if (uView != LVS_REPORT && uView != LVS_LIST) return FALSE;
6337
6338     /* take care of invalid cx values */
6339     if(uView == LVS_REPORT && cx < -2) cx = LVSCW_AUTOSIZE;
6340     else if (uView == LVS_LIST && cx < 1) return FALSE;
6341
6342     /* resize all columns if in LVS_LIST mode */
6343     if(uView == LVS_LIST) 
6344     {
6345         infoPtr->nItemWidth = cx;
6346         LISTVIEW_InvalidateList(infoPtr);
6347         return TRUE;
6348     }
6349
6350     if (nColumn < 0 || nColumn >= infoPtr->hdpaColumns->nItemCount) return FALSE;
6351
6352     if (cx == LVSCW_AUTOSIZE || (cx == LVSCW_AUTOSIZE_USEHEADER && nColumn < infoPtr->hdpaColumns->nItemCount -1))
6353     {
6354         INT nLabelWidth;
6355         LVITEMW lvItem;
6356
6357         lvItem.mask = LVIF_TEXT;        
6358         lvItem.iItem = 0;
6359         lvItem.iSubItem = nColumn;
6360         lvItem.pszText = szDispText;
6361         lvItem.cchTextMax = DISP_TEXT_SIZE;
6362         for (; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
6363         {
6364             if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
6365             nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6366             if (max_cx < nLabelWidth) max_cx = nLabelWidth;
6367         }
6368         if (infoPtr->himlSmall && (nColumn == 0 || (LISTVIEW_GetColumnInfo(infoPtr, nColumn)->fmt & LVCFMT_IMAGE)))
6369             max_cx += infoPtr->iconSize.cx;
6370         max_cx += TRAILING_LABEL_PADDING;
6371     }
6372
6373     /* autosize based on listview items width */
6374     if(cx == LVSCW_AUTOSIZE)
6375         cx = max_cx;
6376     else if(cx == LVSCW_AUTOSIZE_USEHEADER)
6377     {
6378         /* if iCol is the last column make it fill the remainder of the controls width */
6379         if(nColumn == infoPtr->hdpaColumns->nItemCount - 1) 
6380         {
6381             RECT rcHeader;
6382             POINT Origin;
6383
6384             LISTVIEW_GetOrigin(infoPtr, &Origin);
6385             LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
6386
6387             cx = infoPtr->rcList.right - Origin.x - rcHeader.left;
6388         }
6389         else
6390         {
6391             /* Despite what the MS docs say, if this is not the last
6392                column, then MS resizes the column to the width of the
6393                largest text string in the column, including headers
6394                and items. This is different from LVSCW_AUTOSIZE in that
6395                LVSCW_AUTOSIZE ignores the header string length. */
6396             cx = 0;
6397
6398             /* retrieve header text */
6399             hdi.mask = HDI_TEXT;
6400             hdi.cchTextMax = DISP_TEXT_SIZE;
6401             hdi.pszText = szDispText;
6402             if (Header_GetItemW(infoPtr->hwndHeader, nColumn, (LPARAM)&hdi))
6403             {
6404                 HDC hdc = GetDC(infoPtr->hwndSelf);
6405                 HFONT old_font = SelectObject(hdc, (HFONT)SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0, 0));
6406                 SIZE size;
6407
6408                 if (GetTextExtentPoint32W(hdc, hdi.pszText, lstrlenW(hdi.pszText), &size))
6409                     cx = size.cx + TRAILING_HEADER_PADDING;
6410                 /* FIXME: Take into account the header image, if one is present */
6411                 SelectObject(hdc, old_font);
6412                 ReleaseDC(infoPtr->hwndSelf, hdc);
6413             }
6414             cx = max (cx, max_cx);
6415         }
6416     }
6417
6418     if (cx < 0) return FALSE;
6419
6420     /* call header to update the column change */
6421     hdi.mask = HDI_WIDTH;
6422     hdi.cxy = cx;
6423     TRACE("hdi.cxy=%d\n", hdi.cxy);
6424     return Header_SetItemW(infoPtr->hwndHeader, nColumn, (LPARAM)&hdi);
6425 }
6426
6427 /***
6428  * DESCRIPTION:
6429  * Sets the extended listview style.
6430  *
6431  * PARAMETERS:
6432  * [I] infoPtr : valid pointer to the listview structure
6433  * [I] dwMask : mask
6434  * [I] dwStyle : style
6435  *
6436  * RETURN:
6437  *   SUCCESS : previous style
6438  *   FAILURE : 0
6439  */
6440 static DWORD LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwStyle)
6441 {
6442     DWORD dwOldStyle = infoPtr->dwLvExStyle;
6443
6444     /* set new style */
6445     if (dwMask)
6446         infoPtr->dwLvExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
6447     else
6448         infoPtr->dwLvExStyle = dwStyle;
6449
6450     return dwOldStyle;
6451 }
6452
6453 /***
6454  * DESCRIPTION:
6455  * Sets the new hot cursor used during hot tracking and hover selection.
6456  *
6457  * PARAMETER(S):
6458  * [I] infoPtr : valid pointer to the listview structure
6459  * [I} hCurosr : the new hot cursor handle
6460  *
6461  * RETURN:
6462  * Returns the previous hot cursor
6463  */
6464 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
6465 {
6466     HCURSOR oldCursor = infoPtr->hHotCursor;
6467     
6468     infoPtr->hHotCursor = hCursor;
6469
6470     return oldCursor;
6471 }
6472
6473
6474 /***
6475  * DESCRIPTION:
6476  * Sets the hot item index.
6477  *
6478  * PARAMETERS:
6479  * [I] infoPtr : valid pointer to the listview structure
6480  * [I] iIndex : index
6481  *
6482  * RETURN:
6483  *   SUCCESS : previous hot item index
6484  *   FAILURE : -1 (no hot item)
6485  */
6486 static INT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
6487 {
6488     INT iOldIndex = infoPtr->nHotItem;
6489     
6490     infoPtr->nHotItem = iIndex;
6491     
6492     return iOldIndex;
6493 }
6494
6495
6496 /***
6497  * DESCRIPTION:
6498  * Sets the amount of time the cursor must hover over an item before it is selected.
6499  *
6500  * PARAMETER(S):
6501  * [I] infoPtr : valid pointer to the listview structure
6502  * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
6503  *
6504  * RETURN:
6505  * Returns the previous hover time
6506  */
6507 static DWORD LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
6508 {
6509     DWORD oldHoverTime = infoPtr->dwHoverTime;
6510     
6511     infoPtr->dwHoverTime = dwHoverTime;
6512     
6513     return oldHoverTime;
6514 }
6515
6516 /***
6517  * DESCRIPTION:
6518  * Sets spacing for icons of LVS_ICON style.
6519  *
6520  * PARAMETER(S):
6521  * [I] infoPtr : valid pointer to the listview structure
6522  * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
6523  * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
6524  *
6525  * RETURN:
6526  *   MAKELONG(oldcx, oldcy)
6527  */
6528 static DWORD LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, INT cx, INT cy)
6529 {
6530     DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6531     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6532
6533     TRACE("requested=(%d,%d)\n", cx, cy);
6534     
6535     /* this is supported only for LVS_ICON style */
6536     if (uView != LVS_ICON) return oldspacing;
6537   
6538     /* set to defaults, if instructed to */
6539     if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
6540     if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
6541
6542     /* if 0 then compute width
6543      * FIXME: Should scan each item and determine max width of
6544      *        icon or label, then make that the width */
6545     if (cx == 0)
6546         cx = infoPtr->iconSpacing.cx;
6547
6548     /* if 0 then compute height */
6549     if (cy == 0) 
6550         cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
6551              ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
6552     
6553
6554     infoPtr->iconSpacing.cx = cx;
6555     infoPtr->iconSpacing.cy = cy;
6556
6557     TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%ld,%ld), ntmH=%d\n",
6558           LOWORD(oldspacing), HIWORD(oldspacing), cx, cy, 
6559           infoPtr->iconSize.cx, infoPtr->iconSize.cy,
6560           infoPtr->ntmHeight);
6561
6562     /* these depend on the iconSpacing */
6563     LISTVIEW_UpdateItemSize(infoPtr);
6564
6565     return oldspacing;
6566 }
6567
6568 inline void set_icon_size(SIZE *size, HIMAGELIST himl, BOOL small)
6569 {
6570     INT cx, cy;
6571     
6572     if (himl && ImageList_GetIconSize(himl, &cx, &cy))
6573     {
6574         size->cx = cx;
6575         size->cy = cy;
6576     }
6577     else
6578     {
6579         size->cx = GetSystemMetrics(small ? SM_CXSMICON : SM_CXICON);
6580         size->cy = GetSystemMetrics(small ? SM_CYSMICON : SM_CYICON);
6581     }
6582 }
6583
6584 /***
6585  * DESCRIPTION:
6586  * Sets image lists.
6587  *
6588  * PARAMETER(S):
6589  * [I] infoPtr : valid pointer to the listview structure
6590  * [I] nType : image list type
6591  * [I] himl : image list handle
6592  *
6593  * RETURN:
6594  *   SUCCESS : old image list
6595  *   FAILURE : NULL
6596  */
6597 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
6598 {
6599     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6600     INT oldHeight = infoPtr->nItemHeight;
6601     HIMAGELIST himlOld = 0;
6602
6603     TRACE("(nType=%d, himl=%p\n", nType, himl);
6604
6605     switch (nType)
6606     {
6607     case LVSIL_NORMAL:
6608         himlOld = infoPtr->himlNormal;
6609         infoPtr->himlNormal = himl;
6610         if (uView == LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, FALSE);
6611         LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
6612     break;
6613
6614     case LVSIL_SMALL:
6615         himlOld = infoPtr->himlSmall;
6616         infoPtr->himlSmall = himl;
6617          if (uView != LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, TRUE);
6618     break;
6619
6620     case LVSIL_STATE:
6621         himlOld = infoPtr->himlState;
6622         infoPtr->himlState = himl;
6623         set_icon_size(&infoPtr->iconStateSize, himl, TRUE);
6624         ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
6625     break;
6626
6627     default:
6628         ERR("Unknown icon type=%d\n", nType);
6629         return NULL;
6630     }
6631
6632     infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
6633     if (infoPtr->nItemHeight != oldHeight)
6634         LISTVIEW_UpdateScroll(infoPtr);
6635
6636     return himlOld;
6637 }
6638
6639 /***
6640  * DESCRIPTION:
6641  * Preallocates memory (does *not* set the actual count of items !)
6642  *
6643  * PARAMETER(S):
6644  * [I] infoPtr : valid pointer to the listview structure
6645  * [I] nItems : item count (projected number of items to allocate)
6646  * [I] dwFlags : update flags
6647  *
6648  * RETURN:
6649  *   SUCCESS : TRUE
6650  *   FAILURE : FALSE
6651  */
6652 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
6653 {
6654     TRACE("(nItems=%d, dwFlags=%lx)\n", nItems, dwFlags);
6655
6656     if (infoPtr->dwStyle & LVS_OWNERDATA)
6657     {
6658         UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6659         INT nOldCount = infoPtr->nItemCount;
6660
6661         if (nItems < nOldCount)
6662         {
6663             RANGE range = { nItems, nOldCount };
6664             ranges_del(infoPtr->selectionRanges, range);
6665             if (infoPtr->nFocusedItem >= nItems)
6666             {
6667                 infoPtr->nFocusedItem = -1;
6668                 SetRectEmpty(&infoPtr->rcFocus);
6669             }
6670         }
6671
6672         infoPtr->nItemCount = nItems;
6673         LISTVIEW_UpdateScroll(infoPtr);
6674
6675         /* the flags are valid only in ownerdata report and list modes */
6676         if (uView == LVS_ICON || uView == LVS_SMALLICON) dwFlags = 0;
6677
6678         if (!(dwFlags & LVSICF_NOSCROLL) && infoPtr->nFocusedItem != -1)
6679             LISTVIEW_EnsureVisible(infoPtr, infoPtr->nFocusedItem, FALSE);
6680
6681         if (!(dwFlags & LVSICF_NOINVALIDATEALL))
6682             LISTVIEW_InvalidateList(infoPtr);
6683         else
6684         {
6685             INT nFrom, nTo;
6686             POINT Origin;
6687             RECT rcErase;
6688             
6689             LISTVIEW_GetOrigin(infoPtr, &Origin);
6690             nFrom = min(nOldCount, nItems);
6691             nTo = max(nOldCount, nItems);
6692     
6693             if (uView == LVS_REPORT)
6694             {
6695                 rcErase.left = 0;
6696                 rcErase.top = nFrom * infoPtr->nItemHeight;
6697                 rcErase.right = infoPtr->nItemWidth;
6698                 rcErase.bottom = nTo * infoPtr->nItemHeight;
6699                 OffsetRect(&rcErase, Origin.x, Origin.y);
6700                 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
6701                     LISTVIEW_InvalidateRect(infoPtr, &rcErase);
6702             }
6703             else /* LVS_LIST */
6704             {
6705                 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
6706
6707                 rcErase.left = (nFrom / nPerCol) * infoPtr->nItemWidth;
6708                 rcErase.top = (nFrom % nPerCol) * infoPtr->nItemHeight;
6709                 rcErase.right = rcErase.left + infoPtr->nItemWidth;
6710                 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
6711                 OffsetRect(&rcErase, Origin.x, Origin.y);
6712                 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
6713                     LISTVIEW_InvalidateRect(infoPtr, &rcErase);
6714
6715                 rcErase.left = (nFrom / nPerCol + 1) * infoPtr->nItemWidth;
6716                 rcErase.top = 0;
6717                 rcErase.right = (nTo / nPerCol + 1) * 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         }
6724     }
6725     else
6726     {
6727         /* According to MSDN for non-LVS_OWNERDATA this is just
6728          * a performance issue. The control allocates its internal
6729          * data structures for the number of items specified. It
6730          * cuts down on the number of memory allocations. Therefore
6731          * we will just issue a WARN here
6732          */
6733         WARN("for non-ownerdata performance option not implemented.\n");
6734     }
6735
6736     return TRUE;
6737 }
6738
6739 /***
6740  * DESCRIPTION:
6741  * Sets the position of an item.
6742  *
6743  * PARAMETER(S):
6744  * [I] infoPtr : valid pointer to the listview structure
6745  * [I] nItem : item index
6746  * [I] pt : coordinate
6747  *
6748  * RETURN:
6749  *   SUCCESS : TRUE
6750  *   FAILURE : FALSE
6751  */
6752 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT pt)
6753 {
6754     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6755     POINT Origin;
6756
6757     TRACE("(nItem=%d, &pt=%s\n", nItem, debugpoint(&pt));
6758
6759     if (nItem < 0 || nItem >= infoPtr->nItemCount ||
6760         !(uView == LVS_ICON || uView == LVS_SMALLICON)) return FALSE;
6761
6762     LISTVIEW_GetOrigin(infoPtr, &Origin);
6763
6764     /* This point value seems to be an undocumented feature.
6765      * The best guess is that it means either at the origin, 
6766      * or at true beginning of the list. I will assume the origin. */
6767     if ((pt.x == -1) && (pt.y == -1))
6768         pt = Origin;
6769     
6770     if (uView == LVS_ICON)
6771     {
6772         pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
6773         pt.y -= ICON_TOP_PADDING;
6774     }
6775     pt.x -= Origin.x;
6776     pt.y -= Origin.y;
6777
6778     infoPtr->bAutoarrange = FALSE;
6779
6780     return LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, FALSE);
6781 }
6782
6783 /***
6784  * DESCRIPTION:
6785  * Sets the state of one or many items.
6786  *
6787  * PARAMETER(S):
6788  * [I] infoPtr : valid pointer to the listview structure
6789  * [I] nItem : item index
6790  * [I] lpLVItem : item or subitem info
6791  *
6792  * RETURN:
6793  *   SUCCESS : TRUE
6794  *   FAILURE : FALSE
6795  */
6796 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem)
6797 {
6798     BOOL bResult = TRUE;
6799     LVITEMW lvItem;
6800
6801     lvItem.iItem = nItem;
6802     lvItem.iSubItem = 0;
6803     lvItem.mask = LVIF_STATE;
6804     lvItem.state = lpLVItem->state;
6805     lvItem.stateMask = lpLVItem->stateMask;
6806     TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
6807
6808     if (nItem == -1)
6809     {
6810         /* apply to all items */
6811         for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
6812             if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
6813     }
6814     else
6815         bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
6816
6817     return bResult;
6818 }
6819
6820 /***
6821  * DESCRIPTION:
6822  * Sets the text of an item or subitem.
6823  *
6824  * PARAMETER(S):
6825  * [I] hwnd : window handle
6826  * [I] nItem : item index
6827  * [I] lpLVItem : item or subitem info
6828  * [I] isW : TRUE if input is Unicode
6829  *
6830  * RETURN:
6831  *   SUCCESS : TRUE
6832  *   FAILURE : FALSE
6833  */
6834 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem, BOOL isW)
6835 {
6836     LVITEMW lvItem;
6837
6838     if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
6839     
6840     lvItem.iItem = nItem;
6841     lvItem.iSubItem = lpLVItem->iSubItem;
6842     lvItem.mask = LVIF_TEXT;
6843     lvItem.pszText = lpLVItem->pszText;
6844     lvItem.cchTextMax = lpLVItem->cchTextMax;
6845     
6846     TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
6847
6848     return LISTVIEW_SetItemT(infoPtr, &lvItem, isW); 
6849 }
6850
6851 /***
6852  * DESCRIPTION:
6853  * Set item index that marks the start of a multiple selection.
6854  *
6855  * PARAMETER(S):
6856  * [I] infoPtr : valid pointer to the listview structure
6857  * [I] nIndex : index
6858  *
6859  * RETURN:
6860  * Index number or -1 if there is no selection mark.
6861  */
6862 static INT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
6863 {
6864   INT nOldIndex = infoPtr->nSelectionMark;
6865
6866   TRACE("(nIndex=%d)\n", nIndex);
6867
6868   infoPtr->nSelectionMark = nIndex;
6869
6870   return nOldIndex;
6871 }
6872
6873 /***
6874  * DESCRIPTION:
6875  * Sets the text background color.
6876  *
6877  * PARAMETER(S):
6878  * [I] infoPtr : valid pointer to the listview structure
6879  * [I] clrTextBk : text background color
6880  *
6881  * RETURN:
6882  *   SUCCESS : TRUE
6883  *   FAILURE : FALSE
6884  */
6885 static BOOL LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
6886 {
6887     TRACE("(clrTextBk=%lx)\n", clrTextBk);
6888
6889     if (infoPtr->clrTextBk != clrTextBk)
6890     {
6891         infoPtr->clrTextBk = clrTextBk;
6892         LISTVIEW_InvalidateList(infoPtr);
6893     }
6894     
6895   return TRUE;
6896 }
6897
6898 /***
6899  * DESCRIPTION:
6900  * Sets the text foreground color.
6901  *
6902  * PARAMETER(S):
6903  * [I] infoPtr : valid pointer to the listview structure
6904  * [I] clrText : text color
6905  *
6906  * RETURN:
6907  *   SUCCESS : TRUE
6908  *   FAILURE : FALSE
6909  */
6910 static BOOL LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
6911 {
6912     TRACE("(clrText=%lx)\n", clrText);
6913
6914     if (infoPtr->clrText != clrText)
6915     {
6916         infoPtr->clrText = clrText;
6917         LISTVIEW_InvalidateList(infoPtr);
6918     }
6919
6920     return TRUE;
6921 }
6922
6923 /* LISTVIEW_SetToolTips */
6924 /* LISTVIEW_SetUnicodeFormat */
6925 /* LISTVIEW_SetWorkAreas */
6926
6927 /***
6928  * DESCRIPTION:
6929  * Callback internally used by LISTVIEW_SortItems()
6930  *
6931  * PARAMETER(S):
6932  * [I] first : pointer to first ITEM_INFO to compare
6933  * [I] second : pointer to second ITEM_INFO to compare
6934  * [I] lParam : HWND of control
6935  *
6936  * RETURN:
6937  *   if first comes before second : negative
6938  *   if first comes after second : positive
6939  *   if first and second are equivalent : zero
6940  */
6941 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
6942 {
6943   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW((HWND)lParam, 0);
6944   ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
6945   ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
6946
6947   /* Forward the call to the client defined callback */
6948   return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
6949 }
6950
6951 /***
6952  * DESCRIPTION:
6953  * Sorts the listview items.
6954  *
6955  * PARAMETER(S):
6956  * [I] infoPtr : valid pointer to the listview structure
6957  * [I] pfnCompare : application-defined value
6958  * [I] lParamSort : pointer to comparision callback
6959  *
6960  * RETURN:
6961  *   SUCCESS : TRUE
6962  *   FAILURE : FALSE
6963  */
6964 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
6965 {
6966     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6967     HDPA hdpaSubItems;
6968     ITEM_INFO *lpItem;
6969     LPVOID selectionMarkItem;
6970     LVITEMW item;
6971     int i;
6972
6973     TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
6974
6975     if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
6976
6977     if (!infoPtr->hdpaItems) return FALSE;
6978
6979     /* if there are 0 or 1 items, there is no need to sort */
6980     if (infoPtr->nItemCount < 2) return TRUE;
6981
6982     if (infoPtr->nFocusedItem >= 0)
6983     {
6984         hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
6985         lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
6986         if (lpItem) lpItem->state |= LVIS_FOCUSED;
6987     }
6988     /* FIXME: go thorugh selected items and mark them so in lpItem->state */
6989     /*        clear the lpItem->state for non-selected ones */
6990     /*        remove the selection ranges */
6991     
6992     infoPtr->pfnCompare = pfnCompare;
6993     infoPtr->lParamSort = lParamSort;
6994     DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr->hwndSelf);
6995
6996     /* Adjust selections and indices so that they are the way they should
6997      * be after the sort (otherwise, the list items move around, but
6998      * whatever is at the item's previous original position will be
6999      * selected instead)
7000      */
7001     selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
7002     for (i=0; i < infoPtr->nItemCount; i++)
7003     {
7004         hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
7005         lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
7006
7007         if (lpItem->state & LVIS_SELECTED)
7008         {
7009             item.state = LVIS_SELECTED;
7010             item.stateMask = LVIS_SELECTED;
7011             LISTVIEW_SetItemState(infoPtr, i, &item);
7012         }
7013         if (lpItem->state & LVIS_FOCUSED)
7014         {
7015             infoPtr->nFocusedItem = i;
7016             lpItem->state &= ~LVIS_FOCUSED;
7017         }
7018     }
7019     if (selectionMarkItem != NULL)
7020         infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
7021     /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7022
7023     /* refresh the display */
7024     if (uView != LVS_ICON && uView != LVS_SMALLICON)
7025         LISTVIEW_InvalidateList(infoPtr);
7026
7027     return TRUE;
7028 }
7029
7030 /***
7031  * DESCRIPTION:
7032  * Updates an items or rearranges the listview control.
7033  *
7034  * PARAMETER(S):
7035  * [I] infoPtr : valid pointer to the listview structure
7036  * [I] nItem : item index
7037  *
7038  * RETURN:
7039  *   SUCCESS : TRUE
7040  *   FAILURE : FALSE
7041  */
7042 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
7043 {
7044     TRACE("(nItem=%d)\n", nItem);
7045
7046     if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
7047
7048     /* rearrange with default alignment style */
7049     if (is_autoarrange(infoPtr))
7050         LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
7051     else
7052         LISTVIEW_InvalidateItem(infoPtr, nItem);
7053
7054     return TRUE;
7055 }
7056
7057         
7058 /***
7059  * DESCRIPTION:
7060  * Creates the listview control.
7061  *
7062  * PARAMETER(S):
7063  * [I] hwnd : window handle
7064  * [I] lpcs : the create parameters
7065  *
7066  * RETURN:
7067  *   Success: 0
7068  *   Failure: -1
7069  */
7070 static LRESULT LISTVIEW_Create(HWND hwnd, const CREATESTRUCTW *lpcs)
7071 {
7072   LISTVIEW_INFO *infoPtr;
7073   UINT uView = lpcs->style & LVS_TYPEMASK;
7074   LOGFONTW logFont;
7075
7076   TRACE("(lpcs=%p)\n", lpcs);
7077
7078   /* initialize info pointer */
7079   infoPtr = (LISTVIEW_INFO *)COMCTL32_Alloc(sizeof(LISTVIEW_INFO));
7080   if (!infoPtr) return -1;
7081
7082   SetWindowLongW(hwnd, 0, (LONG)infoPtr);
7083
7084   infoPtr->hwndSelf = hwnd;
7085   infoPtr->dwStyle = lpcs->style;
7086   /* determine the type of structures to use */
7087   infoPtr->notifyFormat = SendMessageW(GetParent(infoPtr->hwndSelf), WM_NOTIFYFORMAT,
7088                                        (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
7089
7090   /* initialize color information  */
7091   infoPtr->clrBk = CLR_NONE;
7092   infoPtr->clrText = comctl32_color.clrWindowText;
7093   infoPtr->clrTextBk = CLR_DEFAULT;
7094   LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
7095
7096   /* set default values */
7097   infoPtr->nFocusedItem = -1;
7098   infoPtr->nSelectionMark = -1;
7099   infoPtr->nHotItem = -1;
7100   infoPtr->bRedraw = TRUE;
7101   infoPtr->bFirstPaint = TRUE;
7102   infoPtr->bNoItemMetrics = TRUE;
7103   infoPtr->bDoChangeNotify = TRUE;
7104   infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
7105   infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
7106   infoPtr->nEditLabelItem = -1;
7107   infoPtr->dwHoverTime = -1; /* default system hover time */
7108
7109   /* get default font (icon title) */
7110   SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
7111   infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
7112   infoPtr->hFont = infoPtr->hDefaultFont;
7113   LISTVIEW_SaveTextMetrics(infoPtr);
7114
7115   /* create header */
7116   infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, NULL,
7117     WS_CHILD | HDS_HORZ | (DWORD)((LVS_NOSORTHEADER & lpcs->style)?0:HDS_BUTTONS),
7118     0, 0, 0, 0, hwnd, NULL,
7119     lpcs->hInstance, NULL);
7120   if (!infoPtr->hwndHeader) goto fail;
7121
7122   /* set header unicode format */
7123   SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT, (WPARAM)TRUE, (LPARAM)NULL);
7124
7125   /* set header font */
7126   SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, (LPARAM)TRUE);
7127
7128   /* allocate memory for the data structure */
7129   if (!(infoPtr->selectionRanges = ranges_create(10))) goto fail;
7130   if (!(infoPtr->hdpaItems = DPA_Create(10))) goto fail;
7131   if (!(infoPtr->hdpaPosX  = DPA_Create(10))) goto fail;
7132   if (!(infoPtr->hdpaPosY  = DPA_Create(10))) goto fail;
7133   if (!(infoPtr->hdpaColumns = DPA_Create(10))) goto fail;
7134
7135   /* initialize the icon sizes */
7136   set_icon_size(&infoPtr->iconSize, infoPtr->himlNormal, uView != LVS_ICON);
7137   set_icon_size(&infoPtr->iconStateSize, infoPtr->himlState, TRUE);
7138
7139   /* init item size to avoid division by 0 */
7140   LISTVIEW_UpdateItemSize (infoPtr);
7141   
7142   if (uView == LVS_REPORT)
7143   {
7144     if (!(LVS_NOCOLUMNHEADER & lpcs->style))
7145     {
7146       ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
7147     }
7148     else
7149     {
7150       /* set HDS_HIDDEN flag to hide the header bar */
7151       SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
7152                     GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
7153     }
7154   }
7155
7156   return 0;
7157
7158 fail:
7159     DestroyWindow(infoPtr->hwndHeader);
7160     ranges_destroy(infoPtr->selectionRanges);
7161     DPA_Destroy(infoPtr->hdpaItems);
7162     DPA_Destroy(infoPtr->hdpaPosX);
7163     DPA_Destroy(infoPtr->hdpaPosY);
7164     DPA_Destroy(infoPtr->hdpaColumns);
7165     COMCTL32_Free(infoPtr);
7166     return -1;
7167 }
7168
7169 /***
7170  * DESCRIPTION:
7171  * Erases the background of the listview control.
7172  *
7173  * PARAMETER(S):
7174  * [I] infoPtr : valid pointer to the listview structure
7175  * [I] hdc : device context handle
7176  *
7177  * RETURN:
7178  *   SUCCESS : TRUE
7179  *   FAILURE : FALSE
7180  */
7181 static inline BOOL LISTVIEW_EraseBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc)
7182 {
7183     RECT rc;
7184
7185     TRACE("(hdc=%p)\n", hdc);
7186
7187     if (!GetClipBox(hdc, &rc)) return FALSE;
7188
7189     return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
7190 }
7191         
7192
7193 /***
7194  * DESCRIPTION:
7195  * Helper function for LISTVIEW_[HV]Scroll *only*.
7196  * Performs vertical/horizontal scrolling by a give amount.
7197  *
7198  * PARAMETER(S):
7199  * [I] infoPtr : valid pointer to the listview structure
7200  * [I] dx : amount of horizontal scroll
7201  * [I] dy : amount of vertical scroll
7202  */
7203 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
7204 {
7205     /* now we can scroll the list */
7206     ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList, 
7207                    &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
7208     /* if we have focus, adjust rect */
7209     OffsetRect(&infoPtr->rcFocus, dx, dy);
7210     UpdateWindow(infoPtr->hwndSelf);
7211 }
7212
7213 /***
7214  * DESCRIPTION:
7215  * Performs vertical scrolling.
7216  *
7217  * PARAMETER(S):
7218  * [I] infoPtr : valid pointer to the listview structure
7219  * [I] nScrollCode : scroll code
7220  * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7221  * [I] hScrollWnd  : scrollbar control window handle
7222  *
7223  * RETURN:
7224  * Zero
7225  *
7226  * NOTES:
7227  *   SB_LINEUP/SB_LINEDOWN:
7228  *        for LVS_ICON, LVS_SMALLICON is 37 by experiment
7229  *        for LVS_REPORT is 1 line
7230  *        for LVS_LIST cannot occur
7231  *
7232  */
7233 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode, 
7234                                 INT nScrollDiff, HWND hScrollWnd)
7235 {
7236     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7237     INT nOldScrollPos, nNewScrollPos;
7238     SCROLLINFO scrollInfo;
7239     BOOL is_an_icon;
7240
7241     TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode, 
7242         debugscrollcode(nScrollCode), nScrollDiff);
7243
7244     if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7245
7246     scrollInfo.cbSize = sizeof(SCROLLINFO);
7247     scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7248
7249     is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
7250
7251     if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
7252
7253     nOldScrollPos = scrollInfo.nPos;
7254     switch (nScrollCode)
7255     {
7256     case SB_INTERNAL:
7257         break;
7258
7259     case SB_LINEUP:
7260         nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
7261         break;
7262
7263     case SB_LINEDOWN:
7264         nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
7265         break;
7266
7267     case SB_PAGEUP:
7268         nScrollDiff = -scrollInfo.nPage;
7269         break;
7270
7271     case SB_PAGEDOWN:
7272         nScrollDiff = scrollInfo.nPage;
7273         break;
7274
7275     case SB_THUMBPOSITION:
7276     case SB_THUMBTRACK:
7277         nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7278         break;
7279
7280     default:
7281         nScrollDiff = 0;
7282     }
7283
7284     /* quit right away if pos isn't changing */
7285     if (nScrollDiff == 0) return 0;
7286     
7287     /* calculate new position, and handle overflows */
7288     nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7289     if (nScrollDiff > 0) {
7290         if (nNewScrollPos < nOldScrollPos ||
7291             nNewScrollPos > scrollInfo.nMax)
7292             nNewScrollPos = scrollInfo.nMax;
7293     } else {
7294         if (nNewScrollPos > nOldScrollPos ||
7295             nNewScrollPos < scrollInfo.nMin)
7296             nNewScrollPos = scrollInfo.nMin;
7297     }
7298
7299     /* set the new position, and reread in case it changed */
7300     scrollInfo.fMask = SIF_POS;
7301     scrollInfo.nPos = nNewScrollPos;
7302     nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
7303     
7304     /* carry on only if it really changed */
7305     if (nNewScrollPos == nOldScrollPos) return 0;
7306     
7307     /* now adjust to client coordinates */
7308     nScrollDiff = nOldScrollPos - nNewScrollPos;
7309     if (uView == LVS_REPORT) nScrollDiff *= infoPtr->nItemHeight;
7310    
7311     /* and scroll the window */ 
7312     scroll_list(infoPtr, 0, nScrollDiff);
7313
7314     return 0;
7315 }
7316
7317 /***
7318  * DESCRIPTION:
7319  * Performs horizontal scrolling.
7320  *
7321  * PARAMETER(S):
7322  * [I] infoPtr : valid pointer to the listview structure
7323  * [I] nScrollCode : scroll code
7324  * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7325  * [I] hScrollWnd  : scrollbar control window handle
7326  *
7327  * RETURN:
7328  * Zero
7329  *
7330  * NOTES:
7331  *   SB_LINELEFT/SB_LINERIGHT:
7332  *        for LVS_ICON, LVS_SMALLICON  1 pixel
7333  *        for LVS_REPORT is 1 pixel
7334  *        for LVS_LIST  is 1 column --> which is a 1 because the
7335  *                                      scroll is based on columns not pixels
7336  *
7337  */
7338 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7339                                 INT nScrollDiff, HWND hScrollWnd)
7340 {
7341     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7342     INT nOldScrollPos, nNewScrollPos;
7343     SCROLLINFO scrollInfo;
7344
7345     TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode, 
7346         debugscrollcode(nScrollCode), nScrollDiff);
7347
7348     if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7349
7350     scrollInfo.cbSize = sizeof(SCROLLINFO);
7351     scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7352
7353     if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
7354
7355     nOldScrollPos = scrollInfo.nPos;
7356
7357     switch (nScrollCode)
7358     {
7359     case SB_INTERNAL:
7360         break;
7361
7362     case SB_LINELEFT:
7363         nScrollDiff = -1;
7364         break;
7365
7366     case SB_LINERIGHT:
7367         nScrollDiff = 1;
7368         break;
7369
7370     case SB_PAGELEFT:
7371         nScrollDiff = -scrollInfo.nPage;
7372         break;
7373
7374     case SB_PAGERIGHT:
7375         nScrollDiff = scrollInfo.nPage;
7376         break;
7377
7378     case SB_THUMBPOSITION:
7379     case SB_THUMBTRACK:
7380         nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7381         break;
7382
7383     default:
7384         nScrollDiff = 0;
7385     }
7386
7387     /* quit right away if pos isn't changing */
7388     if (nScrollDiff == 0) return 0;
7389     
7390     /* calculate new position, and handle overflows */
7391     nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7392     if (nScrollDiff > 0) {
7393         if (nNewScrollPos < nOldScrollPos ||
7394             nNewScrollPos > scrollInfo.nMax)
7395             nNewScrollPos = scrollInfo.nMax;
7396     } else {
7397         if (nNewScrollPos > nOldScrollPos ||
7398             nNewScrollPos < scrollInfo.nMin)
7399             nNewScrollPos = scrollInfo.nMin;
7400     }
7401
7402     /* set the new position, and reread in case it changed */
7403     scrollInfo.fMask = SIF_POS;
7404     scrollInfo.nPos = nNewScrollPos;
7405     nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
7406     
7407     /* carry on only if it really changed */
7408     if (nNewScrollPos == nOldScrollPos) return 0;
7409     
7410     if(uView == LVS_REPORT)
7411         LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
7412       
7413     /* now adjust to client coordinates */
7414     nScrollDiff = nOldScrollPos - nNewScrollPos;
7415     if (uView == LVS_LIST) nScrollDiff *= infoPtr->nItemWidth;
7416    
7417     /* and scroll the window */
7418     scroll_list(infoPtr, nScrollDiff, 0);
7419
7420   return 0;
7421 }
7422
7423 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
7424 {
7425     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7426     INT gcWheelDelta = 0;
7427     UINT pulScrollLines = 3;
7428     SCROLLINFO scrollInfo;
7429
7430     TRACE("(wheelDelta=%d)\n", wheelDelta);
7431
7432     SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
7433     gcWheelDelta -= wheelDelta;
7434
7435     scrollInfo.cbSize = sizeof(SCROLLINFO);
7436     scrollInfo.fMask = SIF_POS;
7437
7438     switch(uView)
7439     {
7440     case LVS_ICON:
7441     case LVS_SMALLICON:
7442        /*
7443         *  listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
7444         *  should be fixed in the future.
7445         */
7446         LISTVIEW_VScroll(infoPtr, SB_INTERNAL, (gcWheelDelta < 0) ?
7447                 -LISTVIEW_SCROLL_ICON_LINE_SIZE : LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
7448         break;
7449
7450     case LVS_REPORT:
7451         if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
7452         {
7453             int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
7454             cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
7455             LISTVIEW_VScroll(infoPtr, SB_INTERNAL, cLineScroll, 0);
7456         }
7457         break;
7458
7459     case LVS_LIST:
7460         LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
7461         break;
7462     }
7463     return 0;
7464 }
7465
7466 /***
7467  * DESCRIPTION:
7468  * ???
7469  *
7470  * PARAMETER(S):
7471  * [I] infoPtr : valid pointer to the listview structure
7472  * [I] nVirtualKey : virtual key
7473  * [I] lKeyData : key data
7474  *
7475  * RETURN:
7476  * Zero
7477  */
7478 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
7479 {
7480   UINT uView =  infoPtr->dwStyle & LVS_TYPEMASK;
7481   INT nItem = -1;
7482   NMLVKEYDOWN nmKeyDown;
7483
7484   TRACE("(nVirtualKey=%d, lKeyData=%ld)\n", nVirtualKey, lKeyData);
7485
7486   /* send LVN_KEYDOWN notification */
7487   nmKeyDown.wVKey = nVirtualKey;
7488   nmKeyDown.flags = 0;
7489   notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
7490
7491   switch (nVirtualKey)
7492   {
7493   case VK_RETURN:
7494     if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
7495     {
7496       notify(infoPtr, NM_RETURN);
7497       notify(infoPtr, LVN_ITEMACTIVATE);
7498     }
7499     break;
7500
7501   case VK_HOME:
7502     if (infoPtr->nItemCount > 0)
7503       nItem = 0;
7504     break;
7505
7506   case VK_END:
7507     if (infoPtr->nItemCount > 0)
7508       nItem = infoPtr->nItemCount - 1;
7509     break;
7510
7511   case VK_LEFT:
7512     nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TOLEFT);
7513     break;
7514
7515   case VK_UP:
7516     nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_ABOVE);
7517     break;
7518
7519   case VK_RIGHT:
7520     nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TORIGHT);
7521     break;
7522
7523   case VK_DOWN:
7524     nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_BELOW);
7525     break;
7526
7527   case VK_PRIOR:
7528     if (uView == LVS_REPORT)
7529       nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr);
7530     else
7531       nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
7532                                     * LISTVIEW_GetCountPerRow(infoPtr);
7533     if(nItem < 0) nItem = 0;
7534     break;
7535
7536   case VK_NEXT:
7537     if (uView == LVS_REPORT)
7538       nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr);
7539     else
7540       nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
7541                                     * LISTVIEW_GetCountPerRow(infoPtr);
7542     if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
7543     break;
7544   }
7545
7546   if ((nItem != -1) && (nItem != infoPtr->nFocusedItem))
7547       LISTVIEW_KeySelection(infoPtr, nItem);
7548
7549   return 0;
7550 }
7551
7552 /***
7553  * DESCRIPTION:
7554  * Kills the focus.
7555  *
7556  * PARAMETER(S):
7557  * [I] infoPtr : valid pointer to the listview structure
7558  *
7559  * RETURN:
7560  * Zero
7561  */
7562 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
7563 {
7564     TRACE("()\n");
7565
7566     /* if we did not have the focus, there's nothing to do */
7567     if (!infoPtr->bFocus) return 0;
7568    
7569     /* send NM_KILLFOCUS notification */
7570     notify(infoPtr, NM_KILLFOCUS);
7571
7572     /* if we have a focus rectagle, get rid of it */
7573     LISTVIEW_ShowFocusRect(infoPtr, FALSE);
7574     
7575     /* set window focus flag */
7576     infoPtr->bFocus = FALSE;
7577
7578     /* invalidate the selected items before reseting focus flag */
7579     LISTVIEW_InvalidateSelectedItems(infoPtr);
7580     
7581     return 0;
7582 }
7583
7584 /***
7585  * DESCRIPTION:
7586  * Processes double click messages (left mouse button).
7587  *
7588  * PARAMETER(S):
7589  * [I] infoPtr : valid pointer to the listview structure
7590  * [I] wKey : key flag
7591  * [I] pts : mouse coordinate
7592  *
7593  * RETURN:
7594  * Zero
7595  */
7596 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7597 {
7598     LVHITTESTINFO htInfo;
7599
7600     TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7601
7602     /* send NM_RELEASEDCAPTURE notification */
7603     notify(infoPtr, NM_RELEASEDCAPTURE);
7604
7605     htInfo.pt.x = pts.x;
7606     htInfo.pt.y = pts.y;
7607
7608     /* send NM_DBLCLK notification */
7609     LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
7610     notify_click(infoPtr, NM_DBLCLK, &htInfo);
7611
7612     /* To send the LVN_ITEMACTIVATE, it must be on an Item */
7613     if(htInfo.iItem != -1) notify(infoPtr, LVN_ITEMACTIVATE);
7614
7615     return 0;
7616 }
7617
7618 /***
7619  * DESCRIPTION:
7620  * Processes mouse down messages (left mouse button).
7621  *
7622  * PARAMETER(S):
7623  * [I] infoPtr : valid pointer to the listview structure
7624  * [I] wKey : key flag
7625  * [I] pts : mouse coordinate
7626  *
7627  * RETURN:
7628  * Zero
7629  */
7630 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7631 {
7632   LVHITTESTINFO lvHitTestInfo;
7633   static BOOL bGroupSelect = TRUE;
7634   POINT pt = { pts.x, pts.y };
7635   INT nItem;
7636
7637   TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7638
7639   /* send NM_RELEASEDCAPTURE notification */
7640   notify(infoPtr, NM_RELEASEDCAPTURE);
7641
7642   if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
7643
7644   /* set left button down flag */
7645   infoPtr->bLButtonDown = TRUE;
7646
7647   lvHitTestInfo.pt.x = pts.x;
7648   lvHitTestInfo.pt.y = pts.y;
7649
7650   nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
7651   TRACE("at %s, nItem=%d\n", debugpoint(&pt), nItem);
7652   infoPtr->nEditLabelItem = -1;
7653   if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
7654   {
7655     if (infoPtr->dwStyle & LVS_SINGLESEL)
7656     {
7657       if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
7658         infoPtr->nEditLabelItem = nItem;
7659       else
7660         LISTVIEW_SetSelection(infoPtr, nItem);
7661     }
7662     else
7663     {
7664       if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
7665       {
7666         if (bGroupSelect)
7667         {
7668           LISTVIEW_AddGroupSelection(infoPtr, nItem);
7669           LISTVIEW_SetItemFocus(infoPtr, nItem);
7670           infoPtr->nSelectionMark = nItem;
7671         }
7672         else
7673         {
7674           LVITEMW item;
7675
7676           item.state = LVIS_SELECTED | LVIS_FOCUSED;
7677           item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
7678
7679           LISTVIEW_SetItemState(infoPtr,nItem,&item);
7680           infoPtr->nSelectionMark = nItem;
7681         }
7682       }
7683       else if (wKey & MK_CONTROL)
7684       {
7685         LVITEMW item;
7686
7687         bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
7688         
7689         item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
7690         item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
7691         LISTVIEW_SetItemState(infoPtr, nItem, &item);
7692         infoPtr->nSelectionMark = nItem;
7693       }
7694       else  if (wKey & MK_SHIFT)
7695       {
7696         LISTVIEW_SetGroupSelection(infoPtr, nItem);
7697       }
7698       else
7699       {
7700         if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
7701           infoPtr->nEditLabelItem = nItem;
7702
7703         /* set selection (clears other pre-existing selections) */
7704         LISTVIEW_SetSelection(infoPtr, nItem);
7705       }
7706     }
7707   }
7708   else
7709   {
7710     /* remove all selections */
7711     LISTVIEW_DeselectAll(infoPtr);
7712   }
7713
7714   return 0;
7715 }
7716
7717 /***
7718  * DESCRIPTION:
7719  * Processes mouse up messages (left mouse button).
7720  *
7721  * PARAMETER(S):
7722  * [I] infoPtr : valid pointer to the listview structure
7723  * [I] wKey : key flag
7724  * [I] pts : mouse coordinate
7725  *
7726  * RETURN:
7727  * Zero
7728  */
7729 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7730 {
7731     LVHITTESTINFO lvHitTestInfo;
7732     
7733     TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7734
7735     if (!infoPtr->bLButtonDown) return 0;
7736
7737     lvHitTestInfo.pt.x = pts.x;
7738     lvHitTestInfo.pt.y = pts.y;
7739
7740     /* send NM_CLICK notification */
7741     LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
7742     notify_click(infoPtr, NM_CLICK, &lvHitTestInfo);
7743
7744     /* set left button flag */
7745     infoPtr->bLButtonDown = FALSE;
7746
7747     /* if we clicked on a selected item, edit the label */
7748     if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
7749         LISTVIEW_EditLabelT(infoPtr, lvHitTestInfo.iItem, TRUE);
7750
7751     return 0;
7752 }
7753
7754 /***
7755  * DESCRIPTION:
7756  * Destroys the listview control (called after WM_DESTROY).
7757  *
7758  * PARAMETER(S):
7759  * [I] infoPtr : valid pointer to the listview structure
7760  *
7761  * RETURN:
7762  * Zero
7763  */
7764 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
7765 {
7766   TRACE("()\n");
7767
7768   /* delete all items */
7769   LISTVIEW_DeleteAllItems(infoPtr);
7770
7771   /* destroy data structure */
7772   DPA_Destroy(infoPtr->hdpaItems);
7773   DPA_Destroy(infoPtr->hdpaPosX);
7774   DPA_Destroy(infoPtr->hdpaPosY);
7775   DPA_Destroy(infoPtr->hdpaColumns);
7776   ranges_destroy(infoPtr->selectionRanges);
7777
7778   /* destroy image lists */
7779   if (!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS))
7780   {
7781       if (infoPtr->himlNormal)
7782           ImageList_Destroy(infoPtr->himlNormal);
7783       if (infoPtr->himlSmall)
7784           ImageList_Destroy(infoPtr->himlSmall);
7785       if (infoPtr->himlState)
7786           ImageList_Destroy(infoPtr->himlState);
7787   }
7788
7789   /* destroy font, bkgnd brush */
7790   infoPtr->hFont = 0;
7791   if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
7792   if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
7793
7794   /* free listview info pointer*/
7795   COMCTL32_Free(infoPtr);
7796
7797   SetWindowLongW(infoPtr->hwndSelf, 0, 0);
7798   return 0;
7799 }
7800
7801 /***
7802  * DESCRIPTION:
7803  * Handles notifications from header.
7804  *
7805  * PARAMETER(S):
7806  * [I] infoPtr : valid pointer to the listview structure
7807  * [I] nCtrlId : control identifier
7808  * [I] lpnmh : notification information
7809  *
7810  * RETURN:
7811  * Zero
7812  */
7813 static LRESULT LISTVIEW_HeaderNotification(LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
7814 {
7815     UINT uView =  infoPtr->dwStyle & LVS_TYPEMASK;
7816     
7817     TRACE("(lpnmh=%p)\n", lpnmh);
7818
7819     if (!lpnmh || lpnmh->iItem < 0 || lpnmh->iItem >= infoPtr->hdpaColumns->nItemCount) return 0;
7820     
7821     switch (lpnmh->hdr.code)
7822     {    
7823         case HDN_TRACKW:
7824         case HDN_TRACKA:
7825         case HDN_ITEMCHANGEDW:
7826         case HDN_ITEMCHANGEDA:
7827         {
7828             COLUMN_INFO *lpColumnInfo;
7829             INT dx, cxy;
7830
7831             if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
7832             {
7833                 HDITEMW hdi;
7834     
7835                 hdi.mask = HDI_WIDTH;
7836                 if (!Header_GetItemW(infoPtr->hwndHeader, lpnmh->iItem, (LPARAM)&hdi)) return 0;
7837                 cxy = hdi.cxy;
7838             }
7839             else
7840                 cxy = lpnmh->pitem->cxy;
7841             
7842             /* determine how much we change since the last know position */
7843             lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
7844             dx = cxy - (lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
7845             if (dx != 0)
7846             {
7847                 RECT rcCol = lpColumnInfo->rcHeader;
7848
7849                 lpColumnInfo->rcHeader.right += dx;
7850                 LISTVIEW_ScrollColumns(infoPtr, lpnmh->iItem + 1, dx);
7851                 if (uView == LVS_REPORT && is_redrawing(infoPtr))
7852                 {
7853                     /* this trick works for left aligned columns only */
7854                     if ((lpColumnInfo->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
7855                     {
7856                         rcCol.right = min (rcCol.right, lpColumnInfo->rcHeader.right);
7857                         rcCol.left = max (rcCol.left, rcCol.right - 3 * infoPtr->ntmAveCharWidth);
7858                     }
7859                     rcCol.top = infoPtr->rcList.top;
7860                     rcCol.bottom = infoPtr->rcList.bottom;
7861                     LISTVIEW_InvalidateRect(infoPtr, &rcCol);
7862                 }
7863             }
7864         }
7865         break;
7866
7867         case HDN_ITEMCLICKW:
7868         case HDN_ITEMCLICKA:
7869         {
7870             /* Handle sorting by Header Column */
7871             NMLISTVIEW nmlv;
7872
7873             ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
7874             nmlv.iItem = -1;
7875             nmlv.iSubItem = lpnmh->iItem;
7876             notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
7877         }
7878         break;
7879     }
7880
7881     return 0;
7882 }
7883
7884 /***
7885  * DESCRIPTION:
7886  * Determines the type of structure to use.
7887  *
7888  * PARAMETER(S):
7889  * [I] infoPtr : valid pointer to the listview structureof the sender
7890  * [I] hwndFrom : listview window handle
7891  * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
7892  *
7893  * RETURN:
7894  * Zero
7895  */
7896 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
7897 {
7898     TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom, nCommand);
7899
7900     if (nCommand != NF_REQUERY) return 0;
7901     
7902     infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY);
7903     
7904     return 0;
7905 }
7906
7907 /***
7908  * DESCRIPTION:
7909  * Paints/Repaints the listview control.
7910  *
7911  * PARAMETER(S):
7912  * [I] infoPtr : valid pointer to the listview structure
7913  * [I] hdc : device context handle
7914  *
7915  * RETURN:
7916  * Zero
7917  */
7918 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
7919 {
7920     TRACE("(hdc=%p)\n", hdc);
7921
7922     infoPtr->bFirstPaint = FALSE;
7923     if (infoPtr->bNoItemMetrics && infoPtr->nItemCount)
7924     {
7925         UINT uView =  infoPtr->dwStyle & LVS_TYPEMASK;
7926         
7927         infoPtr->bNoItemMetrics = FALSE;
7928         LISTVIEW_UpdateItemSize(infoPtr);
7929         if (uView == LVS_ICON || uView == LVS_SMALLICON)
7930             LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
7931         LISTVIEW_UpdateScroll(infoPtr);
7932     }
7933     if (hdc) 
7934         LISTVIEW_Refresh(infoPtr, hdc);
7935     else
7936     {
7937         PAINTSTRUCT ps;
7938
7939         hdc = BeginPaint(infoPtr->hwndSelf, &ps);
7940         if (!hdc) return 1;
7941         if (ps.fErase) LISTVIEW_FillBkgnd(infoPtr, hdc, &ps.rcPaint);
7942         LISTVIEW_Refresh(infoPtr, hdc);
7943         EndPaint(infoPtr->hwndSelf, &ps);
7944     }
7945
7946     return 0;
7947 }
7948
7949 /***
7950  * DESCRIPTION:
7951  * Processes double click messages (right mouse button).
7952  *
7953  * PARAMETER(S):
7954  * [I] infoPtr : valid pointer to the listview structure
7955  * [I] wKey : key flag
7956  * [I] pts : mouse coordinate
7957  *
7958  * RETURN:
7959  * Zero
7960  */
7961 static LRESULT LISTVIEW_RButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7962 {
7963     LVHITTESTINFO lvHitTestInfo;
7964     
7965     TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
7966
7967     /* send NM_RELEASEDCAPTURE notification */
7968     notify(infoPtr, NM_RELEASEDCAPTURE);
7969
7970     /* send NM_RDBLCLK notification */
7971     lvHitTestInfo.pt.x = pts.x;
7972     lvHitTestInfo.pt.y = pts.y;
7973     LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
7974     notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
7975
7976     return 0;
7977 }
7978
7979 /***
7980  * DESCRIPTION:
7981  * Processes mouse down messages (right mouse button).
7982  *
7983  * PARAMETER(S):
7984  * [I] infoPtr : valid pointer to the listview structure
7985  * [I] wKey : key flag
7986  * [I] pts : mouse coordinate
7987  *
7988  * RETURN:
7989  * Zero
7990  */
7991 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7992 {
7993     LVHITTESTINFO lvHitTestInfo;
7994     INT nItem;
7995
7996     TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
7997
7998     /* send NM_RELEASEDCAPTURE notification */
7999     notify(infoPtr, NM_RELEASEDCAPTURE);
8000
8001     /* make sure the listview control window has the focus */
8002     if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
8003
8004     /* set right button down flag */
8005     infoPtr->bRButtonDown = TRUE;
8006
8007     /* determine the index of the selected item */
8008     lvHitTestInfo.pt.x = pts.x;
8009     lvHitTestInfo.pt.y = pts.y;
8010     nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8011   
8012     if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8013     {
8014         LISTVIEW_SetItemFocus(infoPtr, nItem);
8015         if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
8016             !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8017             LISTVIEW_SetSelection(infoPtr, nItem);
8018     }
8019     else
8020     {
8021         LISTVIEW_DeselectAll(infoPtr);
8022     }
8023
8024     return 0;
8025 }
8026
8027 /***
8028  * DESCRIPTION:
8029  * Processes mouse up messages (right mouse button).
8030  *
8031  * PARAMETER(S):
8032  * [I] infoPtr : valid pointer to the listview structure
8033  * [I] wKey : key flag
8034  * [I] pts : mouse coordinate
8035  *
8036  * RETURN:
8037  * Zero
8038  */
8039 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
8040 {
8041     LVHITTESTINFO lvHitTestInfo;
8042     POINT pt;
8043
8044     TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
8045
8046     if (!infoPtr->bRButtonDown) return 0;
8047  
8048     /* set button flag */
8049     infoPtr->bRButtonDown = FALSE;
8050
8051     /* Send NM_RClICK notification */
8052     lvHitTestInfo.pt.x = pts.x;
8053     lvHitTestInfo.pt.y = pts.y;
8054     LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8055     notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo);
8056
8057     /* Change to screen coordinate for WM_CONTEXTMENU */
8058     pt = lvHitTestInfo.pt;
8059     ClientToScreen(infoPtr->hwndSelf, &pt);
8060
8061     /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
8062     SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
8063                  (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
8064
8065     return 0;
8066 }
8067
8068
8069 /***
8070  * DESCRIPTION:
8071  * Sets the cursor.
8072  *
8073  * PARAMETER(S):
8074  * [I] infoPtr : valid pointer to the listview structure
8075  * [I] hwnd : window handle of window containing the cursor
8076  * [I] nHittest : hit-test code
8077  * [I] wMouseMsg : ideintifier of the mouse message
8078  *
8079  * RETURN:
8080  * TRUE if cursor is set
8081  * FALSE otherwise
8082  */
8083 static BOOL LISTVIEW_SetCursor(LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
8084 {
8085     LVHITTESTINFO lvHitTestInfo;
8086
8087     if(!(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)) return FALSE;
8088
8089     if(!infoPtr->hHotCursor)  return FALSE;
8090
8091     GetCursorPos(&lvHitTestInfo.pt);
8092     if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) return FALSE;
8093
8094     SetCursor(infoPtr->hHotCursor);
8095
8096     return TRUE;
8097 }
8098
8099 /***
8100  * DESCRIPTION:
8101  * Sets the focus.
8102  *
8103  * PARAMETER(S):
8104  * [I] infoPtr : valid pointer to the listview structure
8105  * [I] hwndLoseFocus : handle of previously focused window
8106  *
8107  * RETURN:
8108  * Zero
8109  */
8110 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
8111 {
8112     TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus);
8113
8114     /* if we have the focus already, there's nothing to do */
8115     if (infoPtr->bFocus) return 0;
8116    
8117     /* send NM_SETFOCUS notification */
8118     notify(infoPtr, NM_SETFOCUS);
8119
8120     /* set window focus flag */
8121     infoPtr->bFocus = TRUE;
8122
8123     /* put the focus rect back on */
8124     LISTVIEW_ShowFocusRect(infoPtr, TRUE);
8125
8126     /* redraw all visible selected items */
8127     LISTVIEW_InvalidateSelectedItems(infoPtr);
8128
8129     return 0;
8130 }
8131
8132 /***
8133  * DESCRIPTION:
8134  * Sets the font.
8135  *
8136  * PARAMETER(S):
8137  * [I] infoPtr : valid pointer to the listview structure
8138  * [I] fRedraw : font handle
8139  * [I] fRedraw : redraw flag
8140  *
8141  * RETURN:
8142  * Zero
8143  */
8144 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
8145 {
8146     HFONT oldFont = infoPtr->hFont;
8147
8148     TRACE("(hfont=%p,redraw=%hu)\n", hFont, fRedraw);
8149
8150     infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
8151     if (infoPtr->hFont == oldFont) return 0;
8152     
8153     LISTVIEW_SaveTextMetrics(infoPtr);
8154
8155     if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT)
8156         SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
8157
8158     if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
8159
8160     return 0;
8161 }
8162
8163 /***
8164  * DESCRIPTION:
8165  * Message handling for WM_SETREDRAW.
8166  * For the Listview, it invalidates the entire window (the doc specifies otherwise)
8167  *
8168  * PARAMETER(S):
8169  * [I] infoPtr : valid pointer to the listview structure
8170  * [I] bRedraw: state of redraw flag
8171  *
8172  * RETURN:
8173  * DefWinProc return value
8174  */
8175 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
8176 {
8177     TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr->bRedraw, bRedraw);
8178
8179     /* we can not use straight equality here because _any_ non-zero value is TRUE */
8180     if ((infoPtr->bRedraw && bRedraw) || (!infoPtr->bRedraw && !bRedraw)) return 0;
8181
8182     infoPtr->bRedraw = bRedraw;
8183
8184     if(!bRedraw) return 0;
8185     
8186     if (is_autoarrange(infoPtr))
8187         LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8188     LISTVIEW_UpdateScroll(infoPtr);
8189
8190     /* despite what the WM_SETREDRAW docs says, apps expect us
8191      * to invalidate the listview here... stupid! */
8192     LISTVIEW_InvalidateList(infoPtr);
8193
8194     return 0;
8195 }
8196
8197 /***
8198  * DESCRIPTION:
8199  * Resizes the listview control. This function processes WM_SIZE
8200  * messages.  At this time, the width and height are not used.
8201  *
8202  * PARAMETER(S):
8203  * [I] infoPtr : valid pointer to the listview structure
8204  * [I] Width : new width
8205  * [I] Height : new height
8206  *
8207  * RETURN:
8208  * Zero
8209  */
8210 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
8211 {
8212     RECT rcOld = infoPtr->rcList;
8213
8214     TRACE("(width=%d, height=%d)\n", Width, Height);
8215
8216     LISTVIEW_UpdateSize(infoPtr);
8217     if (EqualRect(&rcOld, &infoPtr->rcList)) return 0;
8218   
8219     /* do not bother with display related stuff if we're not redrawing */ 
8220     if (!is_redrawing(infoPtr)) return 0;
8221     
8222     if (is_autoarrange(infoPtr)) 
8223         LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8224
8225     LISTVIEW_UpdateScroll(infoPtr);
8226
8227     /* refresh all only for lists whose height changed significantly */
8228     if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_LIST && 
8229         (rcOld.bottom - rcOld.top) / infoPtr->nItemHeight !=
8230         (infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight)
8231         LISTVIEW_InvalidateList(infoPtr);
8232
8233   return 0;
8234 }
8235
8236 /***
8237  * DESCRIPTION:
8238  * Sets the size information.
8239  *
8240  * PARAMETER(S):
8241  * [I] infoPtr : valid pointer to the listview structure
8242  *
8243  * RETURN:
8244  *  None
8245  */
8246 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
8247 {
8248     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8249
8250     TRACE("uView=%d, rcList(old)=%s\n", uView, debugrect(&infoPtr->rcList));
8251     
8252     GetClientRect(infoPtr->hwndSelf, &infoPtr->rcList);
8253
8254     if (uView == LVS_LIST)
8255     {
8256         /* Apparently the "LIST" style is supposed to have the same
8257          * number of items in a column even if there is no scroll bar.
8258          * Since if a scroll bar already exists then the bottom is already
8259          * reduced, only reduce if the scroll bar does not currently exist.
8260          * The "2" is there to mimic the native control. I think it may be
8261          * related to either padding or edges.  (GLA 7/2002)
8262          */
8263         if (!(infoPtr->dwStyle & WS_HSCROLL))
8264             infoPtr->rcList.bottom -= GetSystemMetrics(SM_CYHSCROLL);
8265         infoPtr->rcList.bottom = max (infoPtr->rcList.bottom - 2, 0);
8266     }
8267     else if (uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_NOCOLUMNHEADER))
8268     {
8269         HDLAYOUT hl;
8270         WINDOWPOS wp;
8271
8272         hl.prc = &infoPtr->rcList;
8273         hl.pwpos = &wp;
8274         Header_Layout(infoPtr->hwndHeader, &hl);
8275
8276         SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8277
8278         infoPtr->rcList.top = max(wp.cy, 0);
8279     }
8280
8281     TRACE("  rcList=%s\n", debugrect(&infoPtr->rcList));
8282 }
8283
8284 /***
8285  * DESCRIPTION:
8286  * Processes WM_STYLECHANGED messages.
8287  *
8288  * PARAMETER(S):
8289  * [I] infoPtr : valid pointer to the listview structure
8290  * [I] wStyleType : window style type (normal or extended)
8291  * [I] lpss : window style information
8292  *
8293  * RETURN:
8294  * Zero
8295  */
8296 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
8297                                  const STYLESTRUCT *lpss)
8298 {
8299     UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
8300     UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
8301
8302     TRACE("(styletype=%x, styleOld=0x%08lx, styleNew=0x%08lx)\n",
8303           wStyleType, lpss->styleOld, lpss->styleNew);
8304
8305     if (wStyleType != GWL_STYLE) return 0;
8306   
8307     /* FIXME: if LVS_NOSORTHEADER changed, update header */
8308     /*        what if LVS_OWNERDATA changed? */
8309     /*        or LVS_SINGLESEL */
8310     /*        or LVS_SORT{AS,DES}CENDING */
8311
8312     infoPtr->dwStyle = lpss->styleNew;
8313
8314     if (((lpss->styleOld & WS_HSCROLL) != 0)&&
8315         ((lpss->styleNew & WS_HSCROLL) == 0))
8316        ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
8317
8318     if (((lpss->styleOld & WS_VSCROLL) != 0)&&
8319         ((lpss->styleNew & WS_VSCROLL) == 0))
8320        ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
8321
8322     if (uNewView != uOldView)
8323     {
8324         SIZE oldIconSize = infoPtr->iconSize;
8325         HIMAGELIST himl;
8326     
8327         SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8328         ShowWindow(infoPtr->hwndHeader, SW_HIDE);
8329
8330         ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
8331         SetRectEmpty(&infoPtr->rcFocus);
8332
8333         himl = (uNewView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
8334         set_icon_size(&infoPtr->iconSize, himl, uNewView != LVS_ICON);
8335     
8336         if (uNewView == LVS_ICON)
8337         {
8338             if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
8339             {
8340                 TRACE("icon old size=(%ld,%ld), new size=(%ld,%ld)\n",
8341                       oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
8342                 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
8343             }
8344         }
8345         else if (uNewView == LVS_REPORT)
8346         {
8347             HDLAYOUT hl;
8348             WINDOWPOS wp;
8349
8350             hl.prc = &infoPtr->rcList;
8351             hl.pwpos = &wp;
8352             Header_Layout(infoPtr->hwndHeader, &hl);
8353             SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8354         }
8355
8356         LISTVIEW_UpdateItemSize(infoPtr);
8357     }
8358
8359     if (uNewView == LVS_REPORT)
8360         ShowWindow(infoPtr->hwndHeader, (lpss->styleNew & LVS_NOCOLUMNHEADER) ? SW_HIDE : SW_SHOWNORMAL);
8361      
8362     if ( (uNewView == LVS_ICON || uNewView == LVS_SMALLICON) &&
8363          (uNewView != uOldView || ((lpss->styleNew ^ lpss->styleOld) & LVS_ALIGNMASK)) )
8364          LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8365
8366     /* update the size of the client area */
8367     LISTVIEW_UpdateSize(infoPtr);
8368
8369     /* add scrollbars if needed */
8370     LISTVIEW_UpdateScroll(infoPtr);
8371
8372     /* invalidate client area + erase background */
8373     LISTVIEW_InvalidateList(infoPtr);
8374
8375     return 0;
8376 }
8377
8378 /***
8379  * DESCRIPTION:
8380  * Window procedure of the listview control.
8381  *
8382  */
8383 static LRESULT WINAPI
8384 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
8385 {
8386   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8387
8388   TRACE("(uMsg=%x wParam=%x lParam=%lx)\n", uMsg, wParam, lParam);
8389
8390   if (!infoPtr && (uMsg != WM_CREATE))
8391     return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8392
8393   if (infoPtr)
8394   {
8395     infoPtr->dwStyle = GetWindowLongW(hwnd, GWL_STYLE);
8396   }
8397
8398   switch (uMsg)
8399   {
8400   case LVM_APPROXIMATEVIEWRECT:
8401     return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
8402                                         LOWORD(lParam), HIWORD(lParam));
8403   case LVM_ARRANGE:
8404     return LISTVIEW_Arrange(infoPtr, (INT)wParam);
8405
8406 /* case LVM_CANCELEDITLABEL: */
8407
8408 /* case LVM_CREATEDRAGIMAGE: */
8409
8410   case LVM_DELETEALLITEMS:
8411     return LISTVIEW_DeleteAllItems(infoPtr);
8412
8413   case LVM_DELETECOLUMN:
8414     return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
8415
8416   case LVM_DELETEITEM:
8417     return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
8418
8419   case LVM_EDITLABELW:
8420     return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
8421
8422   case LVM_EDITLABELA:
8423     return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
8424
8425   /* case LVM_ENABLEGROUPVIEW: */
8426
8427   case LVM_ENSUREVISIBLE:
8428     return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
8429
8430   case LVM_FINDITEMW:
8431     return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
8432
8433   case LVM_FINDITEMA:
8434     return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
8435
8436   case LVM_GETBKCOLOR:
8437     return infoPtr->clrBk;
8438
8439   /* case LVM_GETBKIMAGE: */
8440
8441   case LVM_GETCALLBACKMASK:
8442     return infoPtr->uCallbackMask;
8443
8444   case LVM_GETCOLUMNA:
8445     return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8446
8447   case LVM_GETCOLUMNW:
8448     return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8449
8450   case LVM_GETCOLUMNORDERARRAY:
8451     return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
8452
8453   case LVM_GETCOLUMNWIDTH:
8454     return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
8455
8456   case LVM_GETCOUNTPERPAGE:
8457     return LISTVIEW_GetCountPerPage(infoPtr);
8458
8459   case LVM_GETEDITCONTROL:
8460     return (LRESULT)infoPtr->hwndEdit;
8461
8462   case LVM_GETEXTENDEDLISTVIEWSTYLE:
8463     return infoPtr->dwLvExStyle;
8464
8465   /* case LVM_GETGROUPINFO: */
8466
8467   /* case LVM_GETGROUPMETRICS: */
8468
8469   case LVM_GETHEADER:
8470     return (LRESULT)infoPtr->hwndHeader;
8471
8472   case LVM_GETHOTCURSOR:
8473     return (LRESULT)infoPtr->hHotCursor;
8474
8475   case LVM_GETHOTITEM:
8476     return infoPtr->nHotItem;
8477
8478   case LVM_GETHOVERTIME:
8479     return infoPtr->dwHoverTime;
8480
8481   case LVM_GETIMAGELIST:
8482     return (LRESULT)LISTVIEW_GetImageList(infoPtr, (INT)wParam);
8483
8484   /* case LVM_GETINSERTMARK: */
8485
8486   /* case LVM_GETINSERTMARKCOLOR: */
8487
8488   /* case LVM_GETINSERTMARKRECT: */
8489
8490   case LVM_GETISEARCHSTRINGA:
8491   case LVM_GETISEARCHSTRINGW:
8492     FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
8493     return FALSE;
8494
8495   case LVM_GETITEMA:
8496     return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE);
8497
8498   case LVM_GETITEMW:
8499     return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE);
8500
8501   case LVM_GETITEMCOUNT:
8502     return infoPtr->nItemCount;
8503
8504   case LVM_GETITEMPOSITION:
8505     return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
8506
8507   case LVM_GETITEMRECT:
8508     return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
8509
8510   case LVM_GETITEMSPACING:
8511     return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
8512
8513   case LVM_GETITEMSTATE:
8514     return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
8515
8516   case LVM_GETITEMTEXTA:
8517     return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
8518
8519   case LVM_GETITEMTEXTW:
8520     return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
8521
8522   case LVM_GETNEXTITEM:
8523     return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
8524
8525   case LVM_GETNUMBEROFWORKAREAS:
8526     FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
8527     return 1;
8528
8529   case LVM_GETORIGIN:
8530     if (!lParam) return FALSE;
8531     LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
8532     return TRUE;
8533
8534   /* case LVM_GETOUTLINECOLOR: */
8535
8536   /* case LVM_GETSELECTEDCOLUMN: */
8537
8538   case LVM_GETSELECTEDCOUNT:
8539     return LISTVIEW_GetSelectedCount(infoPtr);
8540
8541   case LVM_GETSELECTIONMARK:
8542     return infoPtr->nSelectionMark;
8543
8544   case LVM_GETSTRINGWIDTHA:
8545     return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
8546
8547   case LVM_GETSTRINGWIDTHW:
8548     return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
8549
8550   case LVM_GETSUBITEMRECT:
8551     return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
8552
8553   case LVM_GETTEXTBKCOLOR:
8554     return infoPtr->clrTextBk;
8555
8556   case LVM_GETTEXTCOLOR:
8557     return infoPtr->clrText;
8558
8559   /* case LVM_GETTILEINFO: */
8560
8561   /* case LVM_GETTILEVIEWINFO: */
8562
8563   case LVM_GETTOOLTIPS:
8564     FIXME("LVM_GETTOOLTIPS: unimplemented\n");
8565     return FALSE;
8566
8567   case LVM_GETTOPINDEX:
8568     return LISTVIEW_GetTopIndex(infoPtr);
8569
8570   /*case LVM_GETUNICODEFORMAT:
8571     FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
8572     return FALSE;*/
8573
8574   /* case LVM_GETVIEW: */
8575
8576   case LVM_GETVIEWRECT:
8577     return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
8578
8579   case LVM_GETWORKAREAS:
8580     FIXME("LVM_GETWORKAREAS: unimplemented\n");
8581     return FALSE;
8582
8583   /* case LVM_HASGROUP: */
8584
8585   case LVM_HITTEST:
8586     return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, FALSE);
8587
8588   case LVM_INSERTCOLUMNA:
8589     return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8590
8591   case LVM_INSERTCOLUMNW:
8592     return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8593
8594   /* case LVM_INSERTGROUP: */
8595
8596   /* case LVM_INSERTGROUPSORTED: */
8597
8598   case LVM_INSERTITEMA:
8599     return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
8600
8601   case LVM_INSERTITEMW:
8602     return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
8603
8604   /* case LVM_INSERTMARKHITTEST: */
8605
8606   /* case LVM_ISGROUPVIEWENABLED: */
8607
8608   /* case LVM_MAPIDTOINDEX: */
8609
8610   /* case LVM_MAPINDEXTOID: */
8611
8612   /* case LVM_MOVEGROUP: */
8613
8614   /* case LVM_MOVEITEMTOGROUP: */
8615
8616   case LVM_REDRAWITEMS:
8617     return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
8618
8619   /* case LVM_REMOVEALLGROUPS: */
8620
8621   /* case LVM_REMOVEGROUP: */
8622
8623   case LVM_SCROLL:
8624     return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
8625
8626   case LVM_SETBKCOLOR:
8627     return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
8628
8629   /* case LVM_SETBKIMAGE: */
8630
8631   case LVM_SETCALLBACKMASK:
8632     infoPtr->uCallbackMask = (UINT)wParam;
8633     return TRUE;
8634
8635   case LVM_SETCOLUMNA:
8636     return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8637
8638   case LVM_SETCOLUMNW:
8639     return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8640
8641   case LVM_SETCOLUMNORDERARRAY:
8642     return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
8643
8644   case LVM_SETCOLUMNWIDTH:
8645     return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, SLOWORD(lParam));
8646
8647   case LVM_SETEXTENDEDLISTVIEWSTYLE:
8648     return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
8649
8650   /* case LVM_SETGROUPINFO: */
8651
8652   /* case LVM_SETGROUPMETRICS: */
8653
8654   case LVM_SETHOTCURSOR:
8655     return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
8656
8657   case LVM_SETHOTITEM:
8658     return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
8659
8660   case LVM_SETHOVERTIME:
8661     return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
8662
8663   case LVM_SETICONSPACING:
8664     return LISTVIEW_SetIconSpacing(infoPtr, SLOWORD(lParam), SHIWORD(lParam));
8665
8666   case LVM_SETIMAGELIST:
8667     return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
8668
8669   /* case LVM_SETINFOTIP: */
8670
8671   /* case LVM_SETINSERTMARK: */
8672
8673   /* case LVM_SETINSERTMARKCOLOR: */
8674
8675   case LVM_SETITEMA:
8676     return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
8677
8678   case LVM_SETITEMW:
8679     return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
8680
8681   case LVM_SETITEMCOUNT:
8682     return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
8683
8684   case LVM_SETITEMPOSITION:
8685     {
8686         POINT pt = { SLOWORD(lParam), SHIWORD(lParam) };
8687         return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, pt);
8688     }
8689
8690   case LVM_SETITEMPOSITION32:
8691     if (lParam == 0) return FALSE;
8692     return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, *((POINT*)lParam));
8693
8694   case LVM_SETITEMSTATE:
8695     return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
8696
8697   case LVM_SETITEMTEXTA:
8698     return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
8699
8700   case LVM_SETITEMTEXTW:
8701     return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
8702
8703   /* case LVM_SETOUTLINECOLOR: */
8704
8705   /* case LVM_SETSELECTEDCOLUMN: */
8706
8707   case LVM_SETSELECTIONMARK:
8708     return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
8709
8710   case LVM_SETTEXTBKCOLOR:
8711     return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
8712
8713   case LVM_SETTEXTCOLOR:
8714     return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
8715
8716   /* case LVM_SETTILEINFO: */
8717
8718   /* case LVM_SETTILEVIEWINFO: */
8719
8720   /* case LVM_SETTILEWIDTH: */
8721
8722   /* case LVM_SETTOOLTIPS: */
8723
8724   /* case LVM_SETUNICODEFORMAT: */
8725
8726   /* case LVM_SETVIEW: */
8727
8728   /* case LVM_SETWORKAREAS: */
8729
8730   /* case LVM_SORTGROUPS: */
8731
8732   case LVM_SORTITEMS:
8733     return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
8734
8735   /* LVM_SORTITEMSEX: */
8736
8737   case LVM_SUBITEMHITTEST:
8738     return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
8739
8740   case LVM_UPDATE:
8741     return LISTVIEW_Update(infoPtr, (INT)wParam);
8742
8743   case WM_CHAR:
8744     return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
8745
8746   case WM_COMMAND:
8747     return LISTVIEW_Command(infoPtr, wParam, lParam);
8748
8749   case WM_CREATE:
8750     return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
8751
8752   case WM_ERASEBKGND:
8753     return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
8754
8755   case WM_GETDLGCODE:
8756     return DLGC_WANTCHARS | DLGC_WANTARROWS;
8757
8758   case WM_GETFONT:
8759     return (LRESULT)infoPtr->hFont;
8760
8761   case WM_HSCROLL:
8762     return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
8763
8764   case WM_KEYDOWN:
8765     return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
8766
8767   case WM_KILLFOCUS:
8768     return LISTVIEW_KillFocus(infoPtr);
8769
8770   case WM_LBUTTONDBLCLK:
8771     return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8772
8773   case WM_LBUTTONDOWN:
8774     return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8775
8776   case WM_LBUTTONUP:
8777     return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8778
8779   case WM_MOUSEMOVE:
8780     return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8781
8782   case WM_MOUSEHOVER:
8783     return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8784
8785   case WM_NCDESTROY:
8786     return LISTVIEW_NCDestroy(infoPtr);
8787
8788   case WM_NOTIFY:
8789     if (lParam && ((LPNMHDR)lParam)->hwndFrom == infoPtr->hwndHeader)
8790         return LISTVIEW_HeaderNotification(infoPtr, (LPNMHEADERW)lParam);
8791     else return 0;
8792
8793   case WM_NOTIFYFORMAT:
8794     return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
8795
8796   case WM_PAINT:
8797     return LISTVIEW_Paint(infoPtr, (HDC)wParam);
8798
8799   case WM_RBUTTONDBLCLK:
8800     return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8801
8802   case WM_RBUTTONDOWN:
8803     return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8804
8805   case WM_RBUTTONUP:
8806     return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8807
8808   case WM_SETCURSOR:
8809     if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
8810       return TRUE;
8811     goto fwd_msg;
8812
8813   case WM_SETFOCUS:
8814     return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
8815
8816   case WM_SETFONT:
8817     return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
8818
8819   case WM_SETREDRAW:
8820     return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
8821
8822   case WM_SIZE:
8823     return LISTVIEW_Size(infoPtr, (int)SLOWORD(lParam), (int)SHIWORD(lParam));
8824
8825   case WM_STYLECHANGED:
8826     return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
8827
8828   case WM_SYSCOLORCHANGE:
8829     COMCTL32_RefreshSysColors();
8830     return 0;
8831
8832 /*      case WM_TIMER: */
8833
8834   case WM_VSCROLL:
8835     return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
8836
8837   case WM_MOUSEWHEEL:
8838       if (wParam & (MK_SHIFT | MK_CONTROL))
8839           return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8840       return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
8841
8842   case WM_WINDOWPOSCHANGED:
8843       if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE)) 
8844       {
8845           SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
8846                        SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
8847           LISTVIEW_UpdateSize(infoPtr);
8848           LISTVIEW_UpdateScroll(infoPtr);
8849       }
8850       return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8851
8852 /*      case WM_WININICHANGE: */
8853
8854   default:
8855     if ((uMsg >= WM_USER) && (uMsg < WM_APP))
8856       ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
8857
8858   fwd_msg:
8859     /* call default window procedure */
8860     return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8861   }
8862
8863   return 0;
8864 }
8865
8866 /***
8867  * DESCRIPTION:
8868  * Registers the window class.
8869  *
8870  * PARAMETER(S):
8871  * None
8872  *
8873  * RETURN:
8874  * None
8875  */
8876 void LISTVIEW_Register(void)
8877 {
8878     WNDCLASSW wndClass;
8879
8880     ZeroMemory(&wndClass, sizeof(WNDCLASSW));
8881     wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
8882     wndClass.lpfnWndProc = (WNDPROC)LISTVIEW_WindowProc;
8883     wndClass.cbClsExtra = 0;
8884     wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
8885     wndClass.hCursor = LoadCursorW(0, IDC_ARROWW);
8886     wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
8887     wndClass.lpszClassName = WC_LISTVIEWW;
8888     RegisterClassW(&wndClass);
8889 }
8890
8891 /***
8892  * DESCRIPTION:
8893  * Unregisters the window class.
8894  *
8895  * PARAMETER(S):
8896  * None
8897  *
8898  * RETURN:
8899  * None
8900  */
8901 void LISTVIEW_Unregister(void)
8902 {
8903     UnregisterClassW(WC_LISTVIEWW, NULL);
8904 }
8905
8906 /***
8907  * DESCRIPTION:
8908  * Handle any WM_COMMAND messages
8909  *
8910  * PARAMETER(S):
8911  * [I] infoPtr : valid pointer to the listview structure
8912  * [I] wParam : the first message parameter
8913  * [I] lParam : the second message parameter
8914  *
8915  * RETURN:
8916  *   Zero.
8917  */
8918 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
8919 {
8920     switch (HIWORD(wParam))
8921     {
8922         case EN_UPDATE:
8923         {
8924             /*
8925              * Adjust the edit window size
8926              */
8927             WCHAR buffer[1024];
8928             HDC           hdc = GetDC(infoPtr->hwndEdit);
8929             HFONT         hFont, hOldFont = 0;
8930             RECT          rect;
8931             SIZE          sz;
8932             int           len;
8933
8934             if (!infoPtr->hwndEdit || !hdc) return 0;
8935             len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
8936             GetWindowRect(infoPtr->hwndEdit, &rect);
8937
8938             /* Select font to get the right dimension of the string */
8939             hFont = (HFONT)SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
8940             if(hFont != 0)
8941             {
8942                 hOldFont = SelectObject(hdc, hFont);
8943             }
8944
8945             if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
8946             {
8947                 TEXTMETRICW textMetric;
8948
8949                 /* Add Extra spacing for the next character */
8950                 GetTextMetricsW(hdc, &textMetric);
8951                 sz.cx += (textMetric.tmMaxCharWidth * 2);
8952
8953                 SetWindowPos (
8954                     infoPtr->hwndEdit,
8955                     HWND_TOP,
8956                     0,
8957                     0,
8958                     sz.cx,
8959                     rect.bottom - rect.top,
8960                     SWP_DRAWFRAME|SWP_NOMOVE);
8961             }
8962             if(hFont != 0)
8963                 SelectObject(hdc, hOldFont);
8964
8965             ReleaseDC(infoPtr->hwndSelf, hdc);
8966
8967             break;
8968         }
8969
8970         default:
8971           return SendMessageW (GetParent (infoPtr->hwndSelf), WM_COMMAND, wParam, lParam);
8972     }
8973
8974     return 0;
8975 }
8976
8977
8978 /***
8979  * DESCRIPTION:
8980  * Subclassed edit control windproc function
8981  *
8982  * PARAMETER(S):
8983  * [I] hwnd : the edit window handle
8984  * [I] uMsg : the message that is to be processed
8985  * [I] wParam : first message parameter
8986  * [I] lParam : second message parameter
8987  * [I] isW : TRUE if input is Unicode
8988  *
8989  * RETURN:
8990  *   Zero.
8991  */
8992 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL isW)
8993 {
8994     LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(GetParent(hwnd), 0);
8995     BOOL cancel = FALSE;
8996
8997     TRACE("(hwnd=%p, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
8998           hwnd, uMsg, wParam, lParam, isW);
8999
9000     switch (uMsg)
9001     {
9002         case WM_GETDLGCODE:
9003           return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
9004
9005         case WM_KILLFOCUS:
9006             break;
9007
9008         case WM_DESTROY:
9009         {
9010             WNDPROC editProc = infoPtr->EditWndProc;
9011             infoPtr->EditWndProc = 0;
9012             SetWindowLongW(hwnd, GWL_WNDPROC, (LONG)editProc);
9013             return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
9014         }
9015
9016         case WM_KEYDOWN:
9017             if (VK_ESCAPE == (INT)wParam)
9018             {
9019                 cancel = TRUE;
9020                 break;
9021             }
9022             else if (VK_RETURN == (INT)wParam)
9023                 break;
9024
9025         default:
9026             return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
9027     }
9028
9029     /* kill the edit */
9030     if (infoPtr->hwndEdit)
9031     {
9032         LPWSTR buffer = NULL;
9033
9034         infoPtr->hwndEdit = 0;
9035         if (!cancel)
9036         {
9037             DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
9038
9039             if (len)
9040             {
9041                 if ( (buffer = COMCTL32_Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
9042                 {
9043                     if (isW) GetWindowTextW(hwnd, buffer, len+1);
9044                     else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
9045                 }
9046             }
9047         }
9048         LISTVIEW_EndEditLabelT(infoPtr, buffer, isW);
9049
9050         if (buffer) COMCTL32_Free(buffer);
9051
9052     }
9053
9054     SendMessageW(hwnd, WM_CLOSE, 0, 0);
9055     return 0;
9056 }
9057
9058 /***
9059  * DESCRIPTION:
9060  * Subclassed edit control Unicode windproc function
9061  *
9062  * PARAMETER(S):
9063  * [I] hwnd : the edit window handle
9064  * [I] uMsg : the message that is to be processed
9065  * [I] wParam : first message parameter
9066  * [I] lParam : second message parameter
9067  *
9068  * RETURN:
9069  */
9070 LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9071 {
9072     return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
9073 }
9074
9075 /***
9076  * DESCRIPTION:
9077  * Subclassed edit control ANSI windproc function
9078  *
9079  * PARAMETER(S):
9080  * [I] hwnd : the edit window handle
9081  * [I] uMsg : the message that is to be processed
9082  * [I] wParam : first message parameter
9083  * [I] lParam : second message parameter
9084  *
9085  * RETURN:
9086  */
9087 LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9088 {
9089     return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
9090 }
9091
9092 /***
9093  * DESCRIPTION:
9094  * Creates a subclassed edit cotrol
9095  *
9096  * PARAMETER(S):
9097  * [I] infoPtr : valid pointer to the listview structure
9098  * [I] text : initial text for the edit
9099  * [I] style : the window style
9100  * [I] isW : TRUE if input is Unicode
9101  *
9102  * RETURN:
9103  */
9104 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style,
9105         INT x, INT y, INT width, INT height, BOOL isW)
9106 {
9107     WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
9108     HWND hedit;
9109     SIZE sz;
9110     HDC hdc;
9111     HDC hOldFont=0;
9112     TEXTMETRICW textMetric;
9113     HINSTANCE hinst = (HINSTANCE)GetWindowLongW(infoPtr->hwndSelf, GWL_HINSTANCE);
9114
9115     TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
9116
9117     style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|WS_BORDER;
9118     hdc = GetDC(infoPtr->hwndSelf);
9119
9120     /* Select the font to get appropriate metric dimensions */
9121     if(infoPtr->hFont != 0)
9122         hOldFont = SelectObject(hdc, infoPtr->hFont);
9123
9124     /*Get String Lenght in pixels */
9125     GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
9126
9127     /*Add Extra spacing for the next character */
9128     GetTextMetricsW(hdc, &textMetric);
9129     sz.cx += (textMetric.tmMaxCharWidth * 2);
9130
9131     if(infoPtr->hFont != 0)
9132         SelectObject(hdc, hOldFont);
9133
9134     ReleaseDC(infoPtr->hwndSelf, hdc);
9135     if (isW)
9136         hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9137     else
9138         hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9139
9140     if (!hedit) return 0;
9141
9142     infoPtr->EditWndProc = (WNDPROC)
9143         (isW ? SetWindowLongW(hedit, GWL_WNDPROC, (LONG)EditLblWndProcW) :
9144                SetWindowLongA(hedit, GWL_WNDPROC, (LONG)EditLblWndProcA) );
9145
9146     SendMessageW(hedit, WM_SETFONT, (WPARAM)infoPtr->hFont, FALSE);
9147
9148     return hedit;
9149 }