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