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