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