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