Fixed test so 0xffffffff is properly recognized.
[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, bottom=%d)\n",
3039         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     if (ppszText == NULL)
5118     {
5119        lstrcpynA(lpLVItem->pszText, "", lpLVItem->cchTextMax);
5120     }
5121   }
5122   else if (lpLVItem->mask & LVIF_TEXT)
5123   {
5124     if (internal==TRUE)
5125     {
5126       lpLVItem->pszText=*ppszText;
5127     } else {
5128       lstrcpynA(lpLVItem->pszText, *ppszText, lpLVItem->cchTextMax);
5129     }
5130   }
5131
5132   if (lpLVItem->iSubItem == 0)
5133   {
5134     if (dispInfo.item.mask & LVIF_STATE)
5135     {
5136       lpLVItem->state = lpItem->state;
5137       lpLVItem->state &= ~dispInfo.item.stateMask;
5138       lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
5139
5140       lpLVItem->state &= ~LVIS_SELECTED;
5141       if ((dispInfo.item.stateMask & LVIS_SELECTED) && 
5142          (LISTVIEW_IsSelected(hwnd,dispInfo.item.iItem)))
5143         lpLVItem->state |= LVIS_SELECTED;
5144     }
5145     else if (lpLVItem->mask & LVIF_STATE)
5146     {
5147       lpLVItem->state = lpItem->state & lpLVItem->stateMask;
5148
5149       lpLVItem->state &= ~LVIS_SELECTED;
5150       if ((lpLVItem->stateMask & LVIS_SELECTED) && 
5151           (LISTVIEW_IsSelected(hwnd,lpLVItem->iItem)))
5152          lpLVItem->state |= LVIS_SELECTED;
5153     }
5154
5155     if (lpLVItem->mask & LVIF_PARAM)
5156     {
5157       lpLVItem->lParam = lpItem->lParam;
5158     }
5159
5160     if (lpLVItem->mask & LVIF_INDENT)
5161     {
5162       lpLVItem->iIndent = lpItem->iIndent;
5163     }
5164   }
5165
5166   return TRUE;
5167 }
5168
5169 /* LISTVIEW_GetItemW */
5170 /* LISTVIEW_GetHotCursor */
5171
5172 /***
5173  * DESCRIPTION:
5174  * Retrieves the index of the hot item.
5175  *
5176  * PARAMETERS:
5177  * [I] HWND  : window handle
5178  *
5179  * RETURN:
5180  *   SUCCESS : hot item index
5181  *   FAILURE : -1 (no hot item)
5182  */
5183 static LRESULT LISTVIEW_GetHotItem(HWND hwnd)
5184 {
5185     LISTVIEW_INFO *infoPtr;
5186
5187     /* make sure we can get the listview info */
5188     if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0)))
5189         return (-1);
5190
5191     return (infoPtr->nHotItem);
5192 }
5193
5194 /* LISTVIEW_GetHoverTime */
5195
5196 /***
5197  * DESCRIPTION:
5198  * Retrieves the number of items in the listview control.
5199  * 
5200  * PARAMETER(S):
5201  * [I] HWND : window handle
5202  * 
5203  * RETURN:
5204  * Number of items.
5205  */
5206 static LRESULT LISTVIEW_GetItemCount(HWND hwnd)
5207 {
5208   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5209
5210   return GETITEMCOUNT(infoPtr);
5211 }
5212
5213 /***
5214  * DESCRIPTION:
5215  * Retrieves the position (upper-left) of the listview control item.
5216  * 
5217  * PARAMETER(S):
5218  * [I] HWND : window handle
5219  * [I] INT : item index
5220  * [O] LPPOINT : coordinate information
5221  *
5222  * RETURN:
5223  *   SUCCESS : TRUE
5224  *   FAILURE : FALSE
5225  */
5226 static BOOL LISTVIEW_GetItemPosition(HWND hwnd, INT nItem, 
5227                                      LPPOINT lpptPosition)
5228 {
5229   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5230   UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5231   BOOL bResult = FALSE;
5232   HDPA hdpaSubItems;
5233   LISTVIEW_ITEM *lpItem;
5234   INT nCountPerColumn;
5235   INT nRow;
5236
5237   TRACE("(hwnd=%x,nItem=%d,lpptPosition=%p)\n", hwnd, nItem, 
5238         lpptPosition);
5239   
5240   if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) && 
5241       (lpptPosition != NULL))
5242   {
5243     if (uView == LVS_LIST)
5244     {
5245       bResult = TRUE;
5246       nItem = nItem - ListView_GetTopIndex(hwnd);
5247       nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
5248       if (nItem < 0)
5249       {
5250         nRow = nItem % nCountPerColumn;
5251         if (nRow == 0)
5252         {
5253           lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
5254           lpptPosition->y = 0;
5255         }
5256         else
5257         {
5258           lpptPosition->x = (nItem / nCountPerColumn -1) * infoPtr->nItemWidth;
5259           lpptPosition->y = (nRow + nCountPerColumn) * infoPtr->nItemHeight;  
5260         }
5261       }
5262       else
5263       {
5264         lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
5265         lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight;
5266       }
5267     }
5268     else if (uView == LVS_REPORT)
5269     {
5270       SCROLLINFO scrollInfo;
5271       bResult = TRUE;
5272       lpptPosition->x = REPORT_MARGINX;
5273       lpptPosition->y = ((nItem - ListView_GetTopIndex(hwnd)) * 
5274                          infoPtr->nItemHeight) + infoPtr->rcList.top;
5275
5276           /* Adjust position by scrollbar offset */
5277           ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
5278           scrollInfo.cbSize = sizeof(SCROLLINFO);
5279           scrollInfo.fMask = SIF_POS;
5280           GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
5281           lpptPosition->x -= scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE;
5282     }
5283     else
5284     {
5285       hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
5286       if (hdpaSubItems != NULL)
5287       {
5288         lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
5289         if (lpItem != NULL)
5290         {
5291           bResult = TRUE;
5292           lpptPosition->x = lpItem->ptPosition.x;
5293           lpptPosition->y = lpItem->ptPosition.y;
5294         }
5295       }
5296     }
5297   }
5298   return bResult;
5299 }
5300
5301 /***
5302  * DESCRIPTION:
5303  * Retrieves the bounding rectangle for a listview control item.
5304  * 
5305  * PARAMETER(S):
5306  * [I] HWND : window handle
5307  * [I] INT : item index
5308  * [IO] LPRECT : bounding rectangle coordinates
5309  *     lprc->left specifies the portion of the item for which the bounding
5310  *     rectangle will be retrieved.
5311  *
5312  *     LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5313  *        including the icon and label.
5314  *     LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5315  *     LVIR_LABEL Returns the bounding rectangle of the item text.
5316  *     LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5317  *      rectangles, but excludes columns in report view.
5318  * 
5319  * RETURN:
5320  *   SUCCESS : TRUE
5321  *   FAILURE : FALSE
5322  */
5323 static LRESULT LISTVIEW_GetItemRect(HWND hwnd, INT nItem, LPRECT lprc)
5324 {
5325   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5326   UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5327   BOOL bResult = FALSE;
5328   POINT ptOrigin;
5329   POINT ptItem;
5330   HDC hdc;
5331   HFONT hOldFont;
5332   INT nLeftPos;
5333   INT nLabelWidth;
5334   INT nIndent;
5335   TEXTMETRICA tm;
5336   LVITEMA lvItem;
5337
5338   TRACE("(hwnd=%x, nItem=%d, lprc=%p)\n", hwnd, nItem, lprc);
5339
5340   if (uView & LVS_REPORT)
5341   {
5342     ZeroMemory(&lvItem, sizeof(LVITEMA));
5343     lvItem.mask = LVIF_INDENT;
5344     lvItem.iItem = nItem;
5345     lvItem.iSubItem = 0;
5346     LISTVIEW_GetItemA(hwnd, &lvItem, TRUE);
5347
5348     /* do indent */
5349     if (lvItem.iIndent>0 && infoPtr->iconSize.cx > 0)
5350     {
5351       nIndent = infoPtr->iconSize.cx * lvItem.iIndent; 
5352     } 
5353     else
5354      nIndent = 0;
5355   }
5356   else
5357     nIndent = 0;
5358  
5359   if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) && (lprc != NULL))
5360   {
5361     if (ListView_GetItemPosition(hwnd, nItem, &ptItem) != FALSE)
5362     {
5363       switch(lprc->left)  
5364       {  
5365       case LVIR_ICON: 
5366         if (uView == LVS_ICON)
5367         {
5368           if (infoPtr->himlNormal != NULL)
5369           {
5370             if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5371             {
5372               bResult = TRUE;
5373               lprc->left = ptItem.x + ptOrigin.x;
5374               lprc->top = ptItem.y + ptOrigin.y;
5375               lprc->right = lprc->left + infoPtr->iconSize.cx;
5376               lprc->bottom = (lprc->top + infoPtr->iconSize.cy + 
5377                               ICON_BOTTOM_PADDING + ICON_TOP_PADDING);
5378             }
5379           }
5380         }
5381         else if (uView == LVS_SMALLICON)
5382         {
5383           if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5384           {
5385             bResult = TRUE;
5386             lprc->left = ptItem.x + ptOrigin.x;
5387             lprc->top = ptItem.y + ptOrigin.y;
5388             lprc->bottom = lprc->top + infoPtr->nItemHeight;
5389
5390             if (infoPtr->himlState != NULL)
5391               lprc->left += infoPtr->iconSize.cx;
5392               
5393             if (infoPtr->himlSmall != NULL)
5394               lprc->right = lprc->left + infoPtr->iconSize.cx;
5395             else
5396               lprc->right = lprc->left;
5397           }
5398         }
5399         else 
5400         {
5401           bResult = TRUE;
5402           lprc->left = ptItem.x;
5403           if (uView & LVS_REPORT)
5404             lprc->left += nIndent;
5405           lprc->top = ptItem.y;
5406           lprc->bottom = lprc->top + infoPtr->nItemHeight;
5407
5408           if (infoPtr->himlState != NULL)
5409           {
5410             lprc->left += infoPtr->iconSize.cx;
5411           }
5412             
5413           if (infoPtr->himlSmall != NULL)
5414           {
5415             lprc->right = lprc->left + infoPtr->iconSize.cx;
5416           }
5417           else
5418           {
5419             lprc->right = lprc->left;
5420           }
5421         }
5422         break;
5423
5424       case LVIR_LABEL: 
5425         if (uView == LVS_ICON)
5426         {
5427           if (infoPtr->himlNormal != NULL)
5428           {
5429             if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5430             {
5431               bResult = TRUE;
5432               lprc->left = ptItem.x + ptOrigin.x;
5433               lprc->top = (ptItem.y + ptOrigin.y + infoPtr->iconSize.cy +
5434                            ICON_BOTTOM_PADDING + ICON_TOP_PADDING);
5435               nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5436               if (infoPtr->iconSpacing.cx - nLabelWidth > 1)
5437               {
5438                 lprc->left += (infoPtr->iconSpacing.cx - nLabelWidth) / 2;
5439                 lprc->right = lprc->left + nLabelWidth;
5440               }
5441               else
5442               {
5443                 lprc->left += 1;
5444                 lprc->right = lprc->left + infoPtr->iconSpacing.cx - 1;
5445               }
5446             
5447               hdc = GetDC(hwnd);
5448               hOldFont = SelectObject(hdc, infoPtr->hFont);
5449               GetTextMetricsA(hdc, &tm);
5450               lprc->bottom = lprc->top + tm.tmHeight + HEIGHT_PADDING;
5451               SelectObject(hdc, hOldFont);
5452               ReleaseDC(hwnd, hdc);
5453             }              
5454           }
5455         }
5456         else if (uView == LVS_SMALLICON)
5457         {
5458           if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5459           {
5460             bResult = TRUE;
5461             nLeftPos = lprc->left = ptItem.x + ptOrigin.x; 
5462             lprc->top = ptItem.y + ptOrigin.y;
5463             lprc->bottom = lprc->top + infoPtr->nItemHeight;
5464             
5465             if (infoPtr->himlState != NULL)
5466             {
5467               lprc->left += infoPtr->iconSize.cx;
5468             }
5469             
5470             if (infoPtr->himlSmall != NULL)
5471             {
5472               lprc->left += infoPtr->iconSize.cx;
5473             }
5474             
5475             nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5476             nLabelWidth += TRAILING_PADDING;
5477             if (lprc->left + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5478             {
5479               lprc->right = lprc->left + nLabelWidth;
5480             }
5481             else
5482             {
5483               lprc->right = nLeftPos + infoPtr->nItemWidth;
5484             }
5485           }
5486         }
5487         else
5488         {
5489           bResult = TRUE;
5490           if (uView & LVS_REPORT)
5491             nLeftPos = lprc->left = ptItem.x + nIndent;
5492           else 
5493             nLeftPos = lprc->left = ptItem.x; 
5494           lprc->top = ptItem.y;
5495           lprc->bottom = lprc->top + infoPtr->nItemHeight;
5496
5497           if (infoPtr->himlState != NULL)
5498           {
5499             lprc->left += infoPtr->iconSize.cx;
5500           }
5501
5502           if (infoPtr->himlSmall != NULL)
5503           {
5504             lprc->left += infoPtr->iconSize.cx;
5505           }
5506
5507           nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5508           nLabelWidth += TRAILING_PADDING;
5509           if (infoPtr->himlSmall)
5510             nLabelWidth += IMAGE_PADDING;
5511           if (lprc->left + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5512           {
5513             lprc->right = lprc->left + nLabelWidth;
5514           }
5515           else
5516           {
5517             lprc->right = nLeftPos + infoPtr->nItemWidth;
5518           }
5519         }
5520         break;
5521
5522       case LVIR_BOUNDS:
5523         if (uView == LVS_ICON)
5524         {
5525           if (infoPtr->himlNormal != NULL)
5526           {
5527             if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5528             {
5529               bResult = TRUE;
5530               lprc->left = ptItem.x + ptOrigin.x;
5531               lprc->top = ptItem.y + ptOrigin.y; 
5532               lprc->right = lprc->left + infoPtr->iconSpacing.cx;
5533               lprc->bottom = lprc->top + infoPtr->iconSpacing.cy;
5534             }
5535           } 
5536         }
5537         else if (uView == LVS_SMALLICON)
5538         {
5539           if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5540           {
5541             bResult = TRUE;
5542             lprc->left = ptItem.x + ptOrigin.x; 
5543             lprc->right = lprc->left;
5544             lprc->top = ptItem.y + ptOrigin.y;
5545             lprc->bottom = lprc->top + infoPtr->nItemHeight;
5546             if (infoPtr->himlState != NULL)
5547               lprc->right += infoPtr->iconSize.cx;
5548             if (infoPtr->himlSmall != NULL)
5549               lprc->right += infoPtr->iconSize.cx;
5550
5551             nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5552             nLabelWidth += TRAILING_PADDING;
5553             if (infoPtr->himlSmall)
5554               nLabelWidth += IMAGE_PADDING;
5555             if (lprc->right + nLabelWidth < lprc->left + infoPtr->nItemWidth)
5556             {
5557               lprc->right += nLabelWidth;
5558             }
5559             else
5560             {
5561               lprc->right = lprc->left + infoPtr->nItemWidth;
5562             }
5563           }
5564         }
5565         else
5566         {
5567           bResult = TRUE;
5568           lprc->left = ptItem.x; 
5569           if (!(infoPtr->dwExStyle&LVS_EX_FULLROWSELECT) && uView&LVS_REPORT)
5570             lprc->left += nIndent;
5571           lprc->right = lprc->left;
5572           lprc->top = ptItem.y;
5573           lprc->bottom = lprc->top + infoPtr->nItemHeight;
5574
5575           if (infoPtr->dwExStyle & LVS_EX_FULLROWSELECT)
5576           {
5577             RECT br;
5578             int nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
5579             Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
5580
5581             lprc->right = max(lprc->left, br.right - REPORT_MARGINX);
5582           }
5583           else
5584           {
5585              if (infoPtr->himlState != NULL)
5586             {
5587               lprc->right += infoPtr->iconSize.cx;
5588             }
5589
5590             if (infoPtr->himlSmall != NULL)
5591             {
5592               lprc->right += infoPtr->iconSize.cx;
5593             }
5594
5595             nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5596             nLabelWidth += TRAILING_PADDING;
5597             if (lprc->right + nLabelWidth < lprc->left + infoPtr->nItemWidth)
5598             {
5599               lprc->right += nLabelWidth;
5600             }
5601             else
5602             {
5603               lprc->right = lprc->left + infoPtr->nItemWidth;
5604             }
5605           }
5606         }
5607         break;
5608         
5609       case LVIR_SELECTBOUNDS:
5610         if (uView == LVS_ICON)
5611         {
5612           if (infoPtr->himlNormal != NULL)
5613           {
5614             if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5615             {
5616               bResult = TRUE;
5617               lprc->left = ptItem.x + ptOrigin.x;
5618               lprc->top = ptItem.y + ptOrigin.y;
5619               lprc->right = lprc->left + infoPtr->iconSpacing.cx;
5620               lprc->bottom = lprc->top + infoPtr->iconSpacing.cy;
5621             }
5622           } 
5623         }
5624         else if (uView == LVS_SMALLICON)
5625         {
5626           if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5627           {
5628             bResult = TRUE;
5629             nLeftPos= lprc->left = ptItem.x + ptOrigin.x;  
5630             lprc->top = ptItem.y + ptOrigin.y;
5631             lprc->bottom = lprc->top + infoPtr->nItemHeight;
5632             
5633             if (infoPtr->himlState != NULL)
5634             {
5635               lprc->left += infoPtr->iconSize.cx;
5636             }
5637             
5638             lprc->right = lprc->left;
5639             
5640             if (infoPtr->himlSmall != NULL)
5641             {
5642               lprc->right += infoPtr->iconSize.cx;
5643             }
5644             
5645             nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5646             nLabelWidth += TRAILING_PADDING;
5647             if (lprc->right + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5648             {
5649               lprc->right += nLabelWidth;
5650             }
5651             else
5652             {
5653               lprc->right = nLeftPos + infoPtr->nItemWidth;
5654             }
5655           }
5656         }
5657         else
5658         {
5659           bResult = TRUE;
5660           if (!(infoPtr->dwExStyle&LVS_EX_FULLROWSELECT) && (uView&LVS_REPORT))
5661             nLeftPos = lprc->left = ptItem.x + nIndent; 
5662           else
5663             nLeftPos = lprc->left = ptItem.x; 
5664           lprc->top = ptItem.y;
5665           lprc->bottom = lprc->top + infoPtr->nItemHeight;
5666
5667           if (infoPtr->himlState != NULL)
5668           {
5669             lprc->left += infoPtr->iconSize.cx;
5670           }
5671           
5672           lprc->right = lprc->left;
5673         
5674           if (infoPtr->dwExStyle & LVS_EX_FULLROWSELECT)
5675           {
5676             RECT br;
5677             int nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
5678             Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
5679
5680             lprc->right = max(lprc->left, br.right - REPORT_MARGINX);
5681           }
5682           else
5683           {
5684             if (infoPtr->himlSmall != NULL)
5685             {
5686               lprc->right += infoPtr->iconSize.cx;
5687             }
5688
5689             nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5690             nLabelWidth += TRAILING_PADDING;
5691             if (infoPtr->himlSmall)
5692               nLabelWidth += IMAGE_PADDING;
5693             if (lprc->right + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5694             {
5695               lprc->right += nLabelWidth;
5696             }
5697             else
5698             {
5699               lprc->right = nLeftPos + infoPtr->nItemWidth;
5700             }
5701           }
5702         }
5703         break;
5704       }
5705     }
5706   }
5707   return bResult;
5708 }
5709
5710 /***
5711  * DESCRIPTION:
5712  * Retrieves the width of a label.
5713  * 
5714  * PARAMETER(S):
5715  * [I] HWND : window handle
5716  * 
5717  * RETURN:
5718  *   SUCCESS : string width (in pixels)
5719  *   FAILURE : zero
5720  */
5721 static INT LISTVIEW_GetLabelWidth(HWND hwnd, INT nItem)
5722 {
5723   CHAR szDispText[DISP_TEXT_SIZE];
5724   INT nLabelWidth = 0;
5725   LVITEMA lvItem;
5726
5727   TRACE("(hwnd=%x, nItem=%d)\n", hwnd, nItem);
5728
5729   ZeroMemory(&lvItem, sizeof(LVITEMA));
5730   lvItem.mask = LVIF_TEXT;
5731   lvItem.iItem = nItem;
5732   lvItem.cchTextMax = DISP_TEXT_SIZE;
5733   lvItem.pszText = szDispText;
5734   if (LISTVIEW_GetItemA(hwnd, &lvItem, TRUE) != FALSE)
5735   {
5736     nLabelWidth = ListView_GetStringWidthA(hwnd, lvItem.pszText); 
5737   }
5738     
5739   return nLabelWidth;
5740 }
5741
5742 /***
5743  * DESCRIPTION:
5744  * Retrieves the spacing between listview control items.
5745  * 
5746  * PARAMETER(S):
5747  * [I] HWND : window handle
5748  * [I] BOOL : flag for small or large icon 
5749  *
5750  * RETURN:
5751  * Horizontal + vertical spacing
5752  */
5753 static LRESULT LISTVIEW_GetItemSpacing(HWND hwnd, BOOL bSmall)
5754 {
5755   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5756   LONG lResult;
5757
5758   if (bSmall == FALSE)
5759   {
5760     lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
5761   }
5762   else
5763   {
5764     LONG style = GetWindowLongA(hwnd, GWL_STYLE);
5765     if ((style & LVS_TYPEMASK) == LVS_ICON)
5766     {
5767       lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
5768     }
5769     else
5770     {
5771       lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
5772     }
5773   }
5774   return lResult;
5775 }
5776
5777 /***
5778  * DESCRIPTION:
5779  * Retrieves the state of a listview control item.
5780  * 
5781  * PARAMETER(S):
5782  * [I] HWND : window handle
5783  * [I] INT : item index
5784  * [I] UINT : state mask
5785  * 
5786  * RETURN:
5787  * State specified by the mask.
5788  */
5789 static LRESULT LISTVIEW_GetItemState(HWND hwnd, INT nItem, UINT uMask)
5790 {
5791   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5792   LVITEMA lvItem;
5793   UINT uState = 0;
5794
5795   if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
5796   {
5797     ZeroMemory(&lvItem, sizeof(LVITEMA));
5798     lvItem.iItem = nItem;
5799     lvItem.stateMask = uMask;
5800     lvItem.mask = LVIF_STATE;
5801     if (LISTVIEW_GetItemA(hwnd, &lvItem, TRUE) != FALSE)
5802     {
5803       uState = lvItem.state;
5804     }
5805   }
5806
5807   return uState;
5808 }
5809
5810 /***
5811  * DESCRIPTION:
5812  * Retrieves the text of a listview control item or subitem. 
5813  * 
5814  * PARAMETER(S):
5815  * [I] HWND : window handle
5816  * [I] INT : item index
5817  * [IO] LPLVITEMA : item information
5818  * 
5819  * RETURN:
5820  *   SUCCESS : string length
5821  *   FAILURE : 0
5822  */
5823 static LRESULT LISTVIEW_GetItemTextA(HWND hwnd, INT nItem, LPLVITEMA lpLVItem)
5824 {
5825   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5826   INT nLength = 0;
5827   
5828   if (lpLVItem != NULL)
5829   {
5830     if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
5831     {
5832       lpLVItem->mask = LVIF_TEXT;
5833       lpLVItem->iItem = nItem;
5834       if (LISTVIEW_GetItemA(hwnd, lpLVItem, FALSE) != FALSE)
5835       {
5836         nLength = lstrlenA(lpLVItem->pszText);
5837       }
5838     }
5839   }
5840
5841   return nLength;
5842 }
5843
5844 /***
5845  * DESCRIPTION:
5846  * Searches for an item based on properties + relationships.
5847  * 
5848  * PARAMETER(S):
5849  * [I] HWND : window handle
5850  * [I] INT : item index
5851  * [I] INT : relationship flag
5852  * 
5853  * RETURN:
5854  *   SUCCESS : item index
5855  *   FAILURE : -1
5856  */
5857 static LRESULT LISTVIEW_GetNextItem(HWND hwnd, INT nItem, UINT uFlags)
5858 {
5859   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5860   UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5861   UINT uMask = 0;
5862   LVFINDINFO lvFindInfo;
5863   INT nCountPerColumn;
5864   INT i;
5865   
5866   if ((nItem >= -1) && (nItem < GETITEMCOUNT(infoPtr)))
5867   { 
5868     ZeroMemory(&lvFindInfo, sizeof(LVFINDINFO));
5869
5870     if (uFlags & LVNI_CUT)
5871       uMask |= LVIS_CUT;
5872     
5873     if (uFlags & LVNI_DROPHILITED)
5874       uMask |= LVIS_DROPHILITED;
5875           
5876     if (uFlags & LVNI_FOCUSED)
5877       uMask |= LVIS_FOCUSED;
5878
5879     if (uFlags & LVNI_SELECTED)
5880       uMask |= LVIS_SELECTED;
5881
5882     if (uFlags & LVNI_ABOVE)
5883     {
5884       if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5885       {
5886         while (nItem >= 0)
5887         {
5888           nItem--;
5889           if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
5890             return nItem;
5891         }
5892       }
5893       else
5894       {
5895         lvFindInfo.flags = LVFI_NEARESTXY;
5896         lvFindInfo.vkDirection = VK_UP;
5897         ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
5898         while ((nItem = ListView_FindItem(hwnd, nItem, &lvFindInfo)) != -1)
5899         {
5900           if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
5901             return nItem;
5902         }
5903       }
5904     }
5905     else if (uFlags & LVNI_BELOW)
5906     {
5907       if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5908       {
5909         while (nItem < GETITEMCOUNT(infoPtr))
5910         {
5911           nItem++;
5912           if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
5913             return nItem;
5914         }
5915       }
5916       else
5917       {
5918         lvFindInfo.flags = LVFI_NEARESTXY;
5919         lvFindInfo.vkDirection = VK_DOWN;
5920         ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
5921         while ((nItem = ListView_FindItem(hwnd, nItem, &lvFindInfo)) != -1)
5922         {
5923           if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
5924             return nItem;
5925         }
5926       }
5927     }
5928     else if (uFlags & LVNI_TOLEFT)
5929     {
5930       if (uView == LVS_LIST)
5931       {
5932         nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
5933         while (nItem - nCountPerColumn >= 0)
5934         {
5935           nItem -= nCountPerColumn;
5936           if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
5937             return nItem;
5938         }
5939       }
5940       else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5941       {
5942         lvFindInfo.flags = LVFI_NEARESTXY;
5943         lvFindInfo.vkDirection = VK_LEFT;
5944         ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
5945         while ((nItem = ListView_FindItem(hwnd, nItem, &lvFindInfo)) != -1)
5946         {
5947           if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
5948             return nItem;
5949         }
5950       }
5951     }
5952     else if (uFlags & LVNI_TORIGHT)
5953     {
5954       if (uView == LVS_LIST)
5955       {
5956         nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
5957         while (nItem + nCountPerColumn < GETITEMCOUNT(infoPtr))
5958         {
5959           nItem += nCountPerColumn;
5960           if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
5961             return nItem;
5962         }
5963       }
5964       else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5965       {
5966         lvFindInfo.flags = LVFI_NEARESTXY;
5967         lvFindInfo.vkDirection = VK_RIGHT;
5968         ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
5969         while ((nItem = ListView_FindItem(hwnd, nItem, &lvFindInfo)) != -1)
5970         {
5971           if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
5972             return nItem;
5973         }
5974       }
5975     }
5976     else
5977     {
5978       nItem++;
5979
5980       /* search by index */
5981       for (i = nItem; i < GETITEMCOUNT(infoPtr); i++)
5982       {
5983         if ((ListView_GetItemState(hwnd, i, uMask) & uMask) == uMask)
5984           return i;
5985       }
5986     }
5987   }
5988
5989   return -1;
5990 }
5991
5992 /* LISTVIEW_GetNumberOfWorkAreas */
5993
5994 /***
5995  * DESCRIPTION:
5996  * Retrieves the origin coordinates when in icon or small icon display mode.
5997  * 
5998  * PARAMETER(S):
5999  * [I] HWND : window handle
6000  * [O] LPPOINT : coordinate information
6001  * 
6002  * RETURN:
6003  *   SUCCESS : TRUE
6004  *   FAILURE : FALSE
6005  */
6006 static LRESULT LISTVIEW_GetOrigin(HWND hwnd, LPPOINT lpptOrigin)
6007 {
6008   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
6009   UINT uView = lStyle & LVS_TYPEMASK;
6010   BOOL bResult = FALSE;
6011   
6012   TRACE("(hwnd=%x, lpptOrigin=%p)\n", hwnd, lpptOrigin);
6013
6014   if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6015   {
6016     SCROLLINFO scrollInfo;
6017     ZeroMemory(lpptOrigin, sizeof(POINT));
6018     ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
6019     scrollInfo.cbSize = sizeof(SCROLLINFO);
6020
6021     if (lStyle & WS_HSCROLL)
6022     {
6023       scrollInfo.fMask = SIF_POS;
6024       if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE) 
6025       {
6026         lpptOrigin->x = -scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE; 
6027       }
6028     }
6029
6030     if (lStyle & WS_VSCROLL)
6031     {
6032       scrollInfo.fMask = SIF_POS;
6033       if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
6034       {
6035         lpptOrigin->y = -scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE;
6036       }
6037     }
6038       
6039     bResult = TRUE;
6040   }
6041   
6042   return bResult;
6043 }
6044
6045 /***
6046  * DESCRIPTION:
6047  * Retrieves the number of items that are marked as selected.
6048  * 
6049  * PARAMETER(S):
6050  * [I] HWND : window handle
6051  * 
6052  * RETURN:
6053  * Number of items selected.
6054  */
6055 static LRESULT LISTVIEW_GetSelectedCount(HWND hwnd)
6056 {
6057 /* REDO THIS */
6058   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6059   INT nSelectedCount = 0;
6060   INT i;
6061
6062   for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
6063   {
6064     if (ListView_GetItemState(hwnd, i, LVIS_SELECTED) & LVIS_SELECTED)
6065     {
6066       nSelectedCount++;
6067     }
6068   }
6069   
6070   return nSelectedCount;
6071 }
6072
6073 /***
6074  * DESCRIPTION:
6075  * Retrieves item index that marks the start of a multiple selection.
6076  * 
6077  * PARAMETER(S):
6078  * [I] HWND : window handle
6079  * 
6080  * RETURN:
6081  * Index number or -1 if there is no selection mark.
6082  */
6083 static LRESULT LISTVIEW_GetSelectionMark(HWND hwnd)
6084 {
6085   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6086
6087   return infoPtr->nSelectionMark;
6088 }
6089
6090 /***
6091  * DESCRIPTION:
6092  * Retrieves the width of a string.
6093  * 
6094  * PARAMETER(S):
6095  * [I] HWND : window handle
6096  * 
6097  * RETURN:
6098  *   SUCCESS : string width (in pixels)
6099  *   FAILURE : zero
6100  */
6101 static LRESULT LISTVIEW_GetStringWidthA(HWND hwnd, LPCSTR lpszText)
6102 {
6103   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6104   HFONT hFont, hOldFont;
6105   SIZE stringSize;
6106   HDC hdc;
6107
6108   ZeroMemory(&stringSize, sizeof(SIZE));
6109   if (lpszText != NULL && lpszText != LPSTR_TEXTCALLBACKA)
6110   {
6111     hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
6112     hdc = GetDC(hwnd);
6113     hOldFont = SelectObject(hdc, hFont);
6114     GetTextExtentPointA(hdc, lpszText, lstrlenA(lpszText), &stringSize);
6115     SelectObject(hdc, hOldFont);
6116     ReleaseDC(hwnd, hdc);
6117   }
6118
6119   return stringSize.cx;
6120 }
6121
6122 /***
6123  * DESCRIPTION:
6124  * Retrieves the text backgound color.
6125  * 
6126  * PARAMETER(S):
6127  * [I] HWND : window handle
6128  * 
6129  * RETURN:
6130  * COLORREF associated with the the background.
6131  */
6132 static LRESULT LISTVIEW_GetTextBkColor(HWND hwnd)
6133 {
6134   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongA(hwnd, 0);
6135
6136   return infoPtr->clrTextBk;
6137 }
6138
6139 /***
6140  * DESCRIPTION:
6141  * Retrieves the text color.
6142  * 
6143  * PARAMETER(S):
6144  * [I] HWND : window handle
6145  * 
6146  * RETURN:
6147  * COLORREF associated with the text.
6148  */
6149 static LRESULT LISTVIEW_GetTextColor(HWND hwnd)
6150 {
6151   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongA(hwnd, 0);
6152
6153   return infoPtr->clrText;
6154 }
6155
6156 /***
6157  * DESCRIPTION:
6158  * Determines which section of the item was selected (if any).
6159  * 
6160  * PARAMETER(S):
6161  * [I] HWND : window handle
6162  * [IO] LPLVHITTESTINFO : hit test information
6163  * [I] subitem : fill out iSubItem.
6164  *
6165  * RETURN:
6166  *   SUCCESS : item index
6167  *   FAILURE : -1
6168  */
6169 static INT LISTVIEW_HitTestItem(
6170   HWND hwnd, LPLVHITTESTINFO lpHitTestInfo, BOOL subitem
6171 ) {
6172   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6173   RECT rcItem;
6174   INT i,topindex,bottomindex;
6175   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
6176   UINT uView = lStyle & LVS_TYPEMASK;
6177
6178
6179   TRACE("(hwnd=%x, x=%ld, y=%ld)\n", hwnd, lpHitTestInfo->pt.x,
6180         lpHitTestInfo->pt.y);
6181
6182   topindex = ListView_GetTopIndex(hwnd);
6183   if (uView == LVS_REPORT)
6184   {
6185     bottomindex = topindex + LISTVIEW_GetCountPerColumn(hwnd) + 1;  
6186     bottomindex = min(bottomindex,GETITEMCOUNT(infoPtr));
6187   }
6188   else
6189   {
6190     bottomindex = GETITEMCOUNT(infoPtr);
6191   }
6192
6193   for (i = topindex; i < bottomindex; i++)
6194   {
6195     rcItem.left = LVIR_BOUNDS;
6196     if (LISTVIEW_GetItemRect(hwnd, i, &rcItem) != FALSE)
6197     {
6198       if (PtInRect(&rcItem, lpHitTestInfo->pt) != FALSE)
6199       {
6200         rcItem.left = LVIR_ICON;
6201         if (LISTVIEW_GetItemRect(hwnd, i, &rcItem) != FALSE)
6202         {
6203           if (PtInRect(&rcItem, lpHitTestInfo->pt) != FALSE)
6204           {
6205             lpHitTestInfo->flags = LVHT_ONITEMICON;
6206             lpHitTestInfo->iItem = i;
6207             if (subitem) lpHitTestInfo->iSubItem = 0;
6208             return i;
6209           }
6210         }
6211       
6212         rcItem.left = LVIR_LABEL;
6213         if (LISTVIEW_GetItemRect(hwnd, i, &rcItem) != FALSE)
6214         {
6215           if (PtInRect(&rcItem, lpHitTestInfo->pt) != FALSE)
6216           {
6217             lpHitTestInfo->flags = LVHT_ONITEMLABEL;
6218             lpHitTestInfo->iItem = i;
6219             if (subitem) lpHitTestInfo->iSubItem = 0;
6220             return i;
6221           }
6222         }
6223         
6224         lpHitTestInfo->flags = LVHT_ONITEMSTATEICON;
6225         lpHitTestInfo->iItem = i;
6226         if (subitem) lpHitTestInfo->iSubItem = 0;
6227         return i;
6228       }
6229     }
6230   }
6231      
6232   lpHitTestInfo->flags = LVHT_NOWHERE;
6233
6234   return -1;
6235 }
6236
6237 /***
6238  * DESCRIPTION:
6239  * Determines which listview item is located at the specified position.
6240  * 
6241  * PARAMETER(S):
6242  * [I] HWND : window handle
6243  * [IO} LPLVHITTESTINFO : hit test information
6244  *
6245  * RETURN:
6246  *   SUCCESS : item index
6247  *   FAILURE : -1
6248  */
6249 static LRESULT LISTVIEW_HitTest(HWND hwnd, LPLVHITTESTINFO lpHitTestInfo)
6250 {
6251   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6252   INT nItem = -1;
6253
6254   lpHitTestInfo->flags = 0;
6255
6256   if (infoPtr->rcList.left > lpHitTestInfo->pt.x)
6257   {
6258     lpHitTestInfo->flags = LVHT_TOLEFT;
6259   }
6260   else if (infoPtr->rcList.right < lpHitTestInfo->pt.x) 
6261   {
6262     lpHitTestInfo->flags = LVHT_TORIGHT;
6263   }
6264   if (infoPtr->rcList.top > lpHitTestInfo->pt.y)
6265   {
6266     lpHitTestInfo->flags |= LVHT_ABOVE;
6267   }
6268   else if (infoPtr->rcList.bottom < lpHitTestInfo->pt.y) 
6269   {
6270     lpHitTestInfo->flags |= LVHT_BELOW;
6271   }
6272
6273   if (lpHitTestInfo->flags == 0)
6274   {
6275     /* NOTE (mm 20001022): We must not allow iSubItem to be touched, for 
6276      * an app might pass only a structure with space up to iItem!
6277      * (MS Office 97 does that for instance in the file open dialog)
6278      */
6279     nItem = LISTVIEW_HitTestItem(hwnd, lpHitTestInfo, FALSE);
6280   }
6281   
6282   return nItem;
6283 }
6284
6285 /***
6286  * DESCRIPTION:
6287  * Inserts a new column.
6288  * 
6289  * PARAMETER(S):
6290  * [I] HWND : window handle
6291  * [I] INT : column index
6292  * [I] LPLVCOLUMNA : column information
6293  *
6294  * RETURN:
6295  *   SUCCESS : new column index
6296  *   FAILURE : -1
6297  */
6298 static LRESULT LISTVIEW_InsertColumnA(HWND hwnd, INT nColumn, 
6299                                       LPLVCOLUMNA lpColumn)
6300 {
6301   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6302   HDITEMA hdi;
6303   INT nNewColumn = -1;
6304
6305   TRACE("(hwnd=%x, nColumn=%d, lpColumn=%p)\n",hwnd, nColumn, 
6306         lpColumn);
6307
6308   if (lpColumn != NULL) 
6309   {
6310     /* initialize memory */
6311     ZeroMemory(&hdi, sizeof(HDITEMA));
6312
6313     if (lpColumn->mask & LVCF_FMT) 
6314     {
6315       /* format member is valid */
6316       hdi.mask |= HDI_FORMAT;
6317
6318       /* set text alignment (leftmost column must be left-aligned) */
6319       if (nColumn == 0)
6320       {
6321         hdi.fmt |= HDF_LEFT;
6322       }
6323       else
6324       {
6325         if (lpColumn->fmt & LVCFMT_LEFT)
6326         {
6327           hdi.fmt |= HDF_LEFT;
6328         }
6329         else if (lpColumn->fmt & LVCFMT_RIGHT)
6330         {
6331           hdi.fmt |= HDF_RIGHT;
6332         }
6333         else if (lpColumn->fmt & LVCFMT_CENTER)
6334         {
6335           hdi.fmt |= HDF_CENTER;
6336         }
6337       }
6338  
6339       if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6340       {
6341         hdi.fmt |= HDF_BITMAP_ON_RIGHT;
6342         /* ??? */
6343       }
6344
6345       if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6346       {
6347         /* ??? */
6348       }
6349       
6350       if (lpColumn->fmt & LVCFMT_IMAGE)
6351       {
6352         hdi.fmt |= HDF_IMAGE;
6353         hdi.iImage = I_IMAGECALLBACK;
6354       }
6355     }
6356
6357     if (lpColumn->mask & LVCF_WIDTH) 
6358     {
6359       hdi.mask |= HDI_WIDTH;
6360       hdi.cxy = lpColumn->cx;
6361     }
6362   
6363     if (lpColumn->mask & LVCF_TEXT) 
6364     {
6365       hdi.mask |= HDI_TEXT | HDI_FORMAT;
6366       hdi.pszText = lpColumn->pszText;
6367       hdi.cchTextMax = ((lpColumn->pszText!=NULL) && (lpColumn->pszText!=LPSTR_TEXTCALLBACKA) ? strlen(lpColumn->pszText) : 0);
6368       hdi.fmt |= HDF_STRING;
6369     }
6370   
6371     if (lpColumn->mask & LVCF_IMAGE) 
6372     {
6373       hdi.mask |= HDI_IMAGE;
6374       hdi.iImage = lpColumn->iImage;
6375     }
6376
6377     if (lpColumn->mask & LVCF_ORDER) 
6378     {
6379       hdi.mask |= HDI_ORDER;
6380       hdi.iOrder = lpColumn->iOrder;
6381     }
6382
6383     /* insert item in header control */
6384     nNewColumn = SendMessageA(infoPtr->hwndHeader, HDM_INSERTITEMA,
6385                              (WPARAM)nColumn, (LPARAM)&hdi);
6386     
6387     /* Need to reset the item width when inserting a new column */
6388     infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
6389
6390     LISTVIEW_UpdateScroll(hwnd);
6391     InvalidateRect(hwnd, NULL, FALSE);
6392   }
6393
6394   return nNewColumn;
6395 }
6396
6397 static LRESULT LISTVIEW_InsertColumnW(HWND hwnd, INT nColumn, 
6398                                       LPLVCOLUMNW lpColumn)
6399 {
6400   LVCOLUMNA     lvca;
6401   LRESULT               lres;
6402       
6403   memcpy(&lvca,lpColumn,sizeof(lvca));
6404   if (lpColumn->mask & LVCF_TEXT) {
6405     if (lpColumn->pszText == LPSTR_TEXTCALLBACKW)
6406       lvca.pszText = LPSTR_TEXTCALLBACKA;
6407     else
6408       lvca.pszText = HEAP_strdupWtoA(GetProcessHeap(),0,lpColumn->pszText);
6409   }
6410   lres = LISTVIEW_InsertColumnA(hwnd,nColumn,&lvca);
6411   if (lpColumn->mask & LVCF_TEXT) {
6412     if (lpColumn->pszText != LPSTR_TEXTCALLBACKW)
6413       HeapFree(GetProcessHeap(),0,lvca.pszText);
6414   }
6415   return lres;
6416 }
6417
6418 /* LISTVIEW_InsertCompare:  callback routine for comparing pszText members of the LV_ITEMS
6419    in a LISTVIEW on insert.  Passed to DPA_Sort in LISTVIEW_InsertItem.
6420    This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6421    and not during the processing of a LVM_SORTITEMS message. Applications should provide
6422    their own sort proc. when sending LVM_SORTITEMS.
6423 */
6424 /* Platform SDK:
6425     (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6426         if:
6427           LVS_SORTXXX must be specified, 
6428           LVS_OWNERDRAW is not set, 
6429           <item>.pszText is not LPSTR_TEXTCALLBACK.
6430
6431     (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices 
6432     are sorted based on item text..." 
6433 */
6434 static INT WINAPI LISTVIEW_InsertCompare(  LPVOID first, LPVOID second,  LPARAM lParam)
6435 {
6436   HDPA  hdpa_first = (HDPA) first;
6437   HDPA  hdpa_second = (HDPA) second;
6438   LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( hdpa_first, 0 );
6439   LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( hdpa_second, 0 );
6440   LONG lStyle = GetWindowLongA((HWND) lParam, GWL_STYLE);
6441   INT  cmpv = lstrcmpA( lv_first->pszText, lv_second->pszText );
6442   /* if we're sorting descending, negate the return value */
6443   return (lStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
6444 }
6445
6446 /***
6447  * nESCRIPTION:
6448  * Inserts a new item in the listview control.
6449  * 
6450  * PARAMETER(S):
6451  * [I] HWND : window handle
6452  * [I] LPLVITEMA : item information
6453  *
6454  * RETURN:
6455  *   SUCCESS : new item index
6456  *   FAILURE : -1
6457  */
6458 static LRESULT LISTVIEW_InsertItemA(HWND hwnd, LPLVITEMA lpLVItem)
6459 {
6460   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6461   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
6462   UINT uView = lStyle & LVS_TYPEMASK;
6463   LONG lCtrlId = GetWindowLongA(hwnd, GWL_ID);
6464   NMLISTVIEW nmlv;
6465   INT nItem = -1;
6466   HDPA hdpaSubItems;
6467   INT nItemWidth = 0;
6468   LISTVIEW_ITEM *lpItem = NULL;
6469
6470   TRACE("(hwnd=%x,lpLVItem=%p)\n", hwnd, lpLVItem);
6471
6472   if (lStyle & LVS_OWNERDATA)
6473   {
6474     nItem = infoPtr->hdpaItems->nItemCount;
6475     infoPtr->hdpaItems->nItemCount ++;
6476     return nItem;
6477   }
6478
6479   if (lpLVItem != NULL)
6480   {
6481     /* make sure it's not a subitem; cannot insert a subitem */
6482     if (lpLVItem->iSubItem == 0)
6483     {
6484       lpItem = (LISTVIEW_ITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_ITEM));
6485       if (lpItem != NULL)
6486       {
6487         ZeroMemory(lpItem, sizeof(LISTVIEW_ITEM));
6488         if (LISTVIEW_InitItem(hwnd, lpItem, lpLVItem) != FALSE)
6489         {
6490           /* insert item in listview control data structure */
6491           hdpaSubItems = DPA_Create(8);
6492           if (hdpaSubItems != NULL)
6493           {
6494             nItem = DPA_InsertPtr(hdpaSubItems, 0, lpItem);
6495             if (nItem != -1)
6496             {
6497               if ( ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
6498                       && !(lStyle & LVS_OWNERDRAWFIXED)
6499                       && (LPSTR_TEXTCALLBACKA != lpLVItem->pszText) )
6500               {
6501                 /* Insert the item in the proper sort order based on the pszText
6502                   member. See comments for LISTVIEW_InsertCompare() for greater detail */
6503                   nItem = DPA_InsertPtr( infoPtr->hdpaItems, 
6504                           GETITEMCOUNT( infoPtr ) + 1, hdpaSubItems );
6505                   DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, hwnd );
6506                   nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
6507               }
6508               else
6509               {
6510                 nItem = DPA_InsertPtr(infoPtr->hdpaItems, lpLVItem->iItem, 
6511                                     hdpaSubItems);
6512               }
6513               if (nItem != -1)
6514               {
6515                 LISTVIEW_ShiftIndices(hwnd,nItem,1);
6516
6517                 /* manage item focus */
6518                 if (lpLVItem->mask & LVIF_STATE)
6519                 {
6520                   lpItem->state &= ~(LVIS_FOCUSED|LVIS_SELECTED);
6521                   if (lpLVItem->stateMask & LVIS_SELECTED)
6522                   {
6523                     LISTVIEW_SetSelection(hwnd, nItem);
6524                   }
6525                   else if (lpLVItem->stateMask & LVIS_FOCUSED)
6526                   {
6527                     LISTVIEW_SetItemFocus(hwnd, nItem);
6528                   }           
6529                 }
6530                 
6531                 /* send LVN_INSERTITEM notification */
6532                 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6533                 nmlv.hdr.hwndFrom = hwnd;
6534                 nmlv.hdr.idFrom = lCtrlId;
6535                 nmlv.hdr.code = LVN_INSERTITEM;
6536                 nmlv.iItem = nItem;
6537                 nmlv.lParam = lpItem->lParam;;
6538                 ListView_LVNotify(GetParent(hwnd), lCtrlId, &nmlv);
6539                 
6540                 if ((uView == LVS_SMALLICON) || (uView == LVS_LIST))
6541                 {
6542                   nItemWidth = LISTVIEW_CalculateWidth(hwnd, lpLVItem->iItem); 
6543                   if (nItemWidth > infoPtr->nItemWidth)
6544                   {
6545                     infoPtr->nItemWidth = nItemWidth;
6546                   }
6547                 }
6548
6549                 /* align items (set position of each item) */
6550                 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6551                 {
6552                   if (lStyle & LVS_ALIGNLEFT)
6553                   {
6554                     LISTVIEW_AlignLeft(hwnd);
6555                   }
6556                   else
6557                   {
6558                     LISTVIEW_AlignTop(hwnd);
6559                   }
6560                 }
6561                 
6562                 LISTVIEW_UpdateScroll(hwnd);
6563                 /* refresh client area */
6564                 InvalidateRect(hwnd, NULL, FALSE);
6565               }
6566             }
6567           }
6568         }
6569       }
6570     }
6571   }
6572
6573   /* free memory if unsuccessful */
6574   if ((nItem == -1) && (lpItem != NULL))
6575   {
6576     COMCTL32_Free(lpItem);
6577   }
6578   
6579   return nItem;
6580 }
6581
6582 static LRESULT LISTVIEW_InsertItemW(HWND hwnd, LPLVITEMW lpLVItem) {
6583   LVITEMA lvia;
6584   LRESULT lres;
6585
6586   memcpy(&lvia,lpLVItem,sizeof(LVITEMA));
6587   if (lvia.mask & LVIF_TEXT) {
6588     if (lpLVItem->pszText == LPSTR_TEXTCALLBACKW)
6589       lvia.pszText = LPSTR_TEXTCALLBACKA;
6590     else
6591       lvia.pszText = HEAP_strdupWtoA(GetProcessHeap(),0,lpLVItem->pszText);
6592   }
6593   lres = LISTVIEW_InsertItemA(hwnd, &lvia);
6594   if (lvia.mask & LVIF_TEXT) {
6595     if (lpLVItem->pszText != LPSTR_TEXTCALLBACKW)
6596       HeapFree(GetProcessHeap(),0,lvia.pszText);
6597   }
6598   return lres;
6599 }
6600
6601 /* LISTVIEW_InsertItemW */
6602
6603 /***
6604  * DESCRIPTION:
6605  * Redraws a range of items.
6606  * 
6607  * PARAMETER(S):
6608  * [I] HWND : window handle
6609  * [I] INT : first item
6610  * [I] INT : last item
6611  *
6612  * RETURN:
6613  *   SUCCESS : TRUE
6614  *   FAILURE : FALSE
6615  */
6616 static LRESULT LISTVIEW_RedrawItems(HWND hwnd, INT nFirst, INT nLast)
6617 {
6618   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0); 
6619   BOOL bResult = FALSE;
6620   RECT rcItem;
6621
6622   if (nFirst <= nLast)
6623   {
6624     if ((nFirst >= 0) && (nFirst < GETITEMCOUNT(infoPtr)))
6625     {
6626       if ((nLast >= 0) && (nLast < GETITEMCOUNT(infoPtr)))
6627       {
6628           INT i;
6629           for (i = nFirst; i <= nLast; i++)
6630           {
6631                   rcItem.left = LVIR_BOUNDS;
6632                   LISTVIEW_GetItemRect(hwnd, i, &rcItem);
6633                   InvalidateRect(hwnd, &rcItem, TRUE);
6634           }
6635       }
6636     }
6637   }
6638
6639   return bResult;
6640 }
6641
6642 /* LISTVIEW_Scroll */
6643
6644 /***
6645  * DESCRIPTION:
6646  * Sets the background color.
6647  * 
6648  * PARAMETER(S):
6649  * [I] HWND : window handle
6650  * [I] COLORREF : background color
6651  *
6652  * RETURN:
6653  *   SUCCESS : TRUE
6654  *   FAILURE : FALSE
6655  */
6656 static LRESULT LISTVIEW_SetBkColor(HWND hwnd, COLORREF clrBk)
6657 {
6658   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6659
6660   infoPtr->clrBk = clrBk;
6661   InvalidateRect(hwnd, NULL, TRUE);
6662   
6663   return TRUE;
6664 }
6665
6666 /* LISTVIEW_SetBkImage */
6667
6668 /***
6669  * DESCRIPTION:
6670  * Sets the callback mask. This mask will be used when the parent
6671  * window stores state information (some or all).
6672  * 
6673  * PARAMETER(S):
6674  * [I] HWND : window handle
6675  * [I] UINT : state mask
6676  *
6677  * RETURN:
6678  *   SUCCESS : TRUE
6679  *   FAILURE : FALSE
6680  */
6681 static BOOL LISTVIEW_SetCallbackMask(HWND hwnd, UINT uMask)
6682 {
6683   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6684
6685   infoPtr->uCallbackMask = uMask;
6686
6687   return TRUE;
6688 }
6689
6690 /***
6691  * DESCRIPTION:
6692  * Sets the attributes of a header item.
6693  * 
6694  * PARAMETER(S):
6695  * [I] HWND : window handle
6696  * [I] INT : column index
6697  * [I] LPLVCOLUMNA : column attributes
6698  *
6699  * RETURN:
6700  *   SUCCESS : TRUE
6701  *   FAILURE : FALSE
6702  */
6703 static LRESULT LISTVIEW_SetColumnA(HWND hwnd, INT nColumn, 
6704                                    LPLVCOLUMNA lpColumn)
6705 {
6706   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6707   BOOL bResult = FALSE;
6708   HDITEMA hdi, hdiget;
6709
6710   if ((lpColumn != NULL) && (nColumn >= 0) && 
6711       (nColumn < Header_GetItemCount(infoPtr->hwndHeader)))
6712   {
6713     /* initialize memory */
6714     ZeroMemory(&hdi, sizeof(HDITEMA));
6715
6716     if (lpColumn->mask & LVCF_FMT) 
6717     {
6718       /* format member is valid */
6719       hdi.mask |= HDI_FORMAT;
6720
6721       /* get current format first */
6722       hdiget.mask = HDI_FORMAT;
6723       if (Header_GetItemA(infoPtr->hwndHeader, nColumn, &hdiget))
6724               /* preserve HDF_STRING if present */
6725               hdi.fmt = hdiget.fmt & HDF_STRING;
6726
6727       /* set text alignment (leftmost column must be left-aligned) */
6728       if (nColumn == 0)
6729       {
6730         hdi.fmt |= HDF_LEFT;
6731       }
6732       else
6733       {
6734         if (lpColumn->fmt & LVCFMT_LEFT)
6735         {
6736           hdi.fmt |= HDF_LEFT;
6737         }
6738         else if (lpColumn->fmt & LVCFMT_RIGHT)
6739         {
6740           hdi.fmt |= HDF_RIGHT;
6741         }
6742         else if (lpColumn->fmt & LVCFMT_CENTER)
6743         {
6744           hdi.fmt |= HDF_CENTER;
6745         }
6746       }
6747       
6748       if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6749       {
6750         hdi.fmt |= HDF_BITMAP_ON_RIGHT;
6751       }
6752
6753       if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6754       {
6755         hdi.fmt |= HDF_IMAGE;
6756       }
6757       
6758       if (lpColumn->fmt & LVCFMT_IMAGE)
6759       {
6760         hdi.fmt |= HDF_IMAGE;
6761         hdi.iImage = I_IMAGECALLBACK;
6762       }
6763     }
6764
6765     if (lpColumn->mask & LVCF_WIDTH) 
6766     {
6767       hdi.mask |= HDI_WIDTH;
6768       hdi.cxy = lpColumn->cx;
6769     }
6770     
6771     if (lpColumn->mask & LVCF_TEXT) 
6772     {
6773       hdi.mask |= HDI_TEXT | HDI_FORMAT;
6774       hdi.pszText = lpColumn->pszText;
6775       hdi.cchTextMax = ((lpColumn->pszText!=NULL) && (lpColumn->pszText!=LPSTR_TEXTCALLBACKA) ? strlen(lpColumn->pszText) : 0);
6776       hdi.fmt |= HDF_STRING;
6777     }
6778   
6779     if (lpColumn->mask & LVCF_IMAGE) 
6780     {
6781       hdi.mask |= HDI_IMAGE;
6782       hdi.iImage = lpColumn->iImage;
6783     }
6784
6785     if (lpColumn->mask & LVCF_ORDER) 
6786     {
6787       hdi.mask |= HDI_ORDER;
6788       hdi.iOrder = lpColumn->iOrder;
6789     }
6790
6791     /* set header item attributes */
6792     bResult = Header_SetItemA(infoPtr->hwndHeader, nColumn, &hdi);
6793   }
6794   
6795   return bResult;
6796 }
6797
6798 /* LISTVIEW_SetColumnW */
6799
6800 /***
6801  * DESCRIPTION:
6802  * Sets the column order array
6803  *
6804  * PARAMETERS:
6805  * [I] HWND : window handle
6806  * [I] INT : number of elements in column order array
6807  * [I] INT : pointer to column order array
6808  *
6809  * RETURN:
6810  *   SUCCESS : TRUE
6811  *   FAILURE : FALSE
6812  */
6813 static LRESULT LISTVIEW_SetColumnOrderArray(HWND hwnd, INT iCount, LPINT lpiArray)
6814 {
6815 /*  LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0); */
6816
6817     FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
6818
6819     if (!lpiArray)
6820         return FALSE;
6821
6822     return TRUE;
6823 }
6824
6825
6826 /***
6827  * DESCRIPTION:
6828  * Sets the width of a column
6829  *
6830  * PARAMETERS:
6831  * [I] HWND : window handle
6832  * [I] INT : column index
6833  * [I] INT : column width
6834  *
6835  * RETURN:
6836  *   SUCCESS : TRUE
6837  *   FAILURE : FALSE
6838  */
6839 static LRESULT LISTVIEW_SetColumnWidth(HWND hwnd, INT iCol, INT cx)
6840 {
6841     LISTVIEW_INFO *infoPtr;
6842     HDITEMA hdi;
6843     LRESULT lret;
6844     LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
6845     UINT uView = lStyle & LVS_TYPEMASK; 
6846     HDC hdc;
6847     HFONT header_font;
6848     HFONT old_font;
6849     SIZE size;
6850     CHAR text_buffer[DISP_TEXT_SIZE];
6851     INT header_item_count;
6852     INT item_index;
6853     RECT rcHeader;
6854
6855
6856     /* make sure we can get the listview info */
6857     if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0)))
6858       return (FALSE);
6859
6860     if (!infoPtr->hwndHeader) /* make sure we have a header */
6861       return (FALSE);
6862
6863     /* set column width only if in report or list mode */
6864     if ((uView != LVS_REPORT) && (uView != LVS_LIST))
6865       return (FALSE);            
6866
6867     /* take care of invalid cx values */
6868     if((uView == LVS_REPORT) && (cx < -2))
6869       cx = LVSCW_AUTOSIZE;
6870     else if (uView == LVS_LIST && (cx < 1))
6871       return FALSE;
6872  
6873     /* resize all columns if in LVS_LIST mode */
6874     if(uView == LVS_LIST) {
6875       infoPtr->nItemWidth = cx;
6876       InvalidateRect(hwnd, NULL, TRUE); /* force redraw of the listview */
6877       return TRUE;
6878     }
6879
6880     /* autosize based on listview items width */
6881     if(cx == LVSCW_AUTOSIZE)
6882     {
6883       /* set the width of the header to the width of the widest item */
6884       for(item_index = 0; item_index < GETITEMCOUNT(infoPtr); item_index++)
6885       {
6886         if(cx < LISTVIEW_GetLabelWidth(hwnd, item_index))
6887           cx = LISTVIEW_GetLabelWidth(hwnd, item_index);
6888       } 
6889     } /* autosize based on listview header width */
6890     else if(cx == LVSCW_AUTOSIZE_USEHEADER)
6891     {
6892       header_item_count = Header_GetItemCount(infoPtr->hwndHeader);
6893  
6894       /* if iCol is the last column make it fill the remainder of the controls width */
6895       if(iCol == (header_item_count - 1)) {
6896         /* get the width of every item except the current one */
6897         hdi.mask = HDI_WIDTH;
6898         cx = 0;
6899         
6900         for(item_index = 0; item_index < (header_item_count - 1); item_index++) {
6901           Header_GetItemA(infoPtr->hwndHeader, item_index, (LPARAM)(&hdi));
6902           cx+=hdi.cxy;
6903         }
6904  
6905         /* retrieve the layout of the header */
6906         GetWindowRect(infoPtr->hwndHeader, &rcHeader);
6907
6908         cx = (rcHeader.right - rcHeader.left) - cx;
6909       }                                  
6910       else
6911       {
6912         /* retrieve header font */
6913         header_font = SendMessageA(infoPtr->hwndHeader, WM_GETFONT, 0L, 0L);
6914  
6915         /* retrieve header text */
6916         hdi.mask = HDI_TEXT;
6917         hdi.cchTextMax = sizeof(text_buffer);
6918         hdi.pszText = text_buffer;             
6919     
6920         Header_GetItemA(infoPtr->hwndHeader, iCol, (LPARAM)(&hdi));
6921  
6922         /* determine the width of the text in the header */
6923         hdc = GetDC(hwnd);
6924         old_font = SelectObject(hdc, header_font); /* select the font into hdc */
6925
6926         GetTextExtentPoint32A(hdc, text_buffer, strlen(text_buffer), &size);
6927  
6928         SelectObject(hdc, old_font); /* restore the old font */    
6929         ReleaseDC(hwnd, hdc);
6930  
6931         /* set the width of this column to the width of the text */
6932         cx = size.cx;
6933       }
6934   }
6935
6936   /* call header to update the column change */
6937   hdi.mask = HDI_WIDTH;                          
6938
6939   hdi.cxy = cx;
6940   lret = Header_SetItemA(infoPtr->hwndHeader, (WPARAM)iCol, (LPARAM)&hdi);
6941  
6942   InvalidateRect(hwnd, NULL, TRUE); /* force redraw of the listview */
6943  
6944   return lret;
6945 }
6946
6947 /***
6948  * DESCRIPTION:
6949  * Sets the extended listview style.
6950  *
6951  * PARAMETERS:
6952  * [I] HWND  : window handle
6953  * [I] DWORD : mask
6954  * [I] DWORD : style
6955  *
6956  * RETURN:
6957  *   SUCCESS : previous style
6958  *   FAILURE : 0
6959  */
6960 static LRESULT LISTVIEW_SetExtendedListViewStyle(HWND hwnd, DWORD dwMask, DWORD dwStyle)
6961 {
6962     LISTVIEW_INFO *infoPtr;
6963     DWORD dwOldStyle;
6964
6965     /* make sure we can get the listview info */
6966     if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0)))
6967         return (0);
6968
6969     /* store previous style */
6970     dwOldStyle = infoPtr->dwExStyle;
6971
6972     /* set new style */
6973     if (dwMask)
6974       infoPtr->dwExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
6975     else
6976       infoPtr->dwExStyle = dwStyle;
6977
6978     return (dwOldStyle);
6979 }
6980
6981 /* LISTVIEW_SetHotCursor */
6982
6983 /***
6984  * DESCRIPTION:
6985  * Sets the hot item index.
6986  *
6987  * PARAMETERS:
6988  * [I] HWND  : window handle
6989  * [I] INT   : index
6990  *
6991  * RETURN:
6992  *   SUCCESS : previous hot item index
6993  *   FAILURE : -1 (no hot item)
6994  */
6995 static LRESULT LISTVIEW_SetHotItem(HWND hwnd, INT iIndex)
6996 {
6997     LISTVIEW_INFO *infoPtr;
6998     INT iOldIndex;
6999
7000     /* make sure we can get the listview info */
7001     if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0)))
7002         return (-1);
7003
7004     /* store previous index */
7005     iOldIndex = infoPtr->nHotItem;
7006
7007     /* set new style */
7008     infoPtr->nHotItem = iIndex;
7009
7010     return (iOldIndex);
7011 }
7012
7013 /***
7014  * DESCRIPTION:
7015  * Sets the amount of time the cursor must hover over an item before it is selected.
7016  *
7017  * PARAMETER(S):
7018  * [I] HWND : window handle
7019  * [I] DWORD : dwHoverTime, if -1 the hover time is set to the default
7020  *
7021  * RETURN:
7022  * Returns the previous hover time
7023  */
7024 static LRESULT LISTVIEW_SetHoverTime(HWND hwnd, DWORD dwHoverTime)
7025 {
7026   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7027   DWORD oldHoverTime = infoPtr->dwHoverTime;
7028
7029   infoPtr->dwHoverTime = dwHoverTime;
7030
7031   return oldHoverTime;
7032 }
7033
7034 /* LISTVIEW_SetIconSpacing */
7035
7036 /***
7037  * DESCRIPTION:
7038  * Sets image lists.
7039  * 
7040  * PARAMETER(S):
7041  * [I] HWND : window handle
7042  * [I] INT : image list type  
7043  * [I] HIMAGELIST : image list handle
7044  *
7045  * RETURN:
7046  *   SUCCESS : old image list
7047  *   FAILURE : NULL
7048  */
7049 static LRESULT LISTVIEW_SetImageList(HWND hwnd, INT nType, HIMAGELIST himl)
7050 {
7051   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7052   HIMAGELIST himlOld = 0;
7053   INT oldHeight;
7054
7055   switch (nType) 
7056   {
7057   case LVSIL_NORMAL:
7058     himlOld = infoPtr->himlNormal;
7059     infoPtr->himlNormal = himl;
7060     break;
7061
7062   case LVSIL_SMALL:
7063     himlOld = infoPtr->himlSmall;
7064     infoPtr->himlSmall = himl;
7065     break;
7066
7067   case LVSIL_STATE:
7068     himlOld = infoPtr->himlState;
7069     infoPtr->himlState = himl;
7070     ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
7071     break;
7072   }
7073
7074   oldHeight = infoPtr->nItemHeight;
7075   infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
7076   if (infoPtr->nItemHeight != oldHeight)
7077     LISTVIEW_UpdateScroll(hwnd);
7078
7079   return (LRESULT)himlOld;
7080 }
7081
7082
7083 /***
7084  * DESCRIPTION:
7085  * Sets the attributes of an item.
7086  * 
7087  * PARAMETER(S):
7088  * [I] HWND : window handle
7089  * [I] LPLVITEM : item information 
7090  *
7091  * RETURN:
7092  *   SUCCESS : TRUE
7093  *   FAILURE : FALSE
7094  */
7095 static LRESULT LISTVIEW_SetItemA(HWND hwnd, LPLVITEMA lpLVItem)
7096 {
7097   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7098   BOOL bResult = FALSE;
7099
7100   if (lpLVItem != NULL)
7101   {
7102     if ((lpLVItem->iItem >= 0) && (lpLVItem->iItem < GETITEMCOUNT(infoPtr)))
7103     {
7104       if (lpLVItem->iSubItem == 0)
7105       {
7106         bResult = LISTVIEW_SetItem(hwnd, lpLVItem);
7107       }
7108       else
7109       {
7110         bResult = LISTVIEW_SetSubItem(hwnd, lpLVItem);
7111       }
7112     }
7113   }
7114
7115
7116   return bResult;
7117 }
7118
7119 /* LISTVIEW_SetItemW  */
7120
7121 /***
7122  * DESCRIPTION:
7123  * Preallocates memory (does *not* set the actual count of items !)
7124  * 
7125  * PARAMETER(S):
7126  * [I] HWND : window handle
7127  * [I] INT   : item count (projected number of items to allocate)
7128  * [I] DWORD : update flags
7129  *
7130  * RETURN:
7131  *   SUCCESS : TRUE
7132  *   FAILURE : FALSE
7133  */
7134 static BOOL LISTVIEW_SetItemCount(HWND hwnd, INT nItems, DWORD dwFlags)
7135 {
7136   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongA(hwnd, 0);
7137
7138   if (GetWindowLongA(hwnd, GWL_STYLE) & LVS_OWNERDATA)
7139   {
7140       int precount,topvisible;
7141       TRACE("LVS_OWNERDATA is set!\n");
7142
7143       /*
7144        * Internally remove all the selections. 
7145        */
7146       do
7147       {
7148         LISTVIEW_SELECTION *selection;
7149         selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,0);
7150         if (selection)
7151             LISTVIEW_RemoveSelectionRange(hwnd,selection->lower,
7152                                           selection->upper);
7153       }
7154       while (infoPtr->hdpaSelectionRanges->nItemCount>0);
7155  
7156       precount = infoPtr->hdpaItems->nItemCount;
7157       topvisible = ListView_GetTopIndex(hwnd) +
7158                    LISTVIEW_GetCountPerColumn(hwnd) + 1;
7159
7160       infoPtr->hdpaItems->nItemCount = nItems;
7161
7162       LISTVIEW_UpdateSize(hwnd);
7163       LISTVIEW_UpdateScroll(hwnd);
7164       if (min(precount,infoPtr->hdpaItems->nItemCount)<topvisible) 
7165         InvalidateRect(hwnd, NULL, TRUE);
7166   }
7167   else
7168   {
7169       FIXME("setitemcount not done for non-ownerdata\n");
7170   }
7171
7172   return TRUE;
7173 }
7174
7175 /***
7176  * DESCRIPTION:
7177  * Sets the position of an item.
7178  * 
7179  * PARAMETER(S):
7180  * [I] HWND : window handle
7181  * [I] INT : item index
7182  * [I] LONG : x coordinate
7183  * [I] LONG : y coordinate
7184  *
7185  * RETURN:
7186  *   SUCCESS : TRUE
7187  *   FAILURE : FALSE
7188  */
7189 static BOOL LISTVIEW_SetItemPosition(HWND hwnd, INT nItem,
7190                                      LONG nPosX, LONG nPosY)
7191 {
7192   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongA(hwnd, 0);
7193   UINT lStyle = GetWindowLongA(hwnd, GWL_STYLE);
7194   UINT uView = lStyle & LVS_TYPEMASK;
7195   LISTVIEW_ITEM *lpItem;
7196   HDPA hdpaSubItems;
7197   BOOL bResult = FALSE;
7198
7199   TRACE("(hwnd=%x,nItem=%d,X=%ld,Y=%ld)\n", hwnd, nItem, nPosX, nPosY);
7200   
7201   if (lStyle & LVS_OWNERDATA)
7202     return FALSE;
7203
7204   if ((nItem >= 0) || (nItem < GETITEMCOUNT(infoPtr)))
7205   {
7206     if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
7207     {
7208       hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
7209       if (hdpaSubItems != NULL)
7210       {
7211         lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
7212         if (lpItem != NULL)
7213         {
7214           bResult = TRUE;
7215           lpItem->ptPosition.x = nPosX;
7216           lpItem->ptPosition.y = nPosY;
7217         }
7218       }
7219     }
7220   }
7221
7222   return bResult;
7223 }
7224
7225 /***
7226  * DESCRIPTION:
7227  * Sets the state of one or many items.
7228  * 
7229  * PARAMETER(S):
7230  * [I] HWND : window handle
7231  * [I]INT : item index
7232  * [I] LPLVITEM : item or subitem info
7233  *
7234  * RETURN:
7235  *   SUCCESS : TRUE
7236  *   FAILURE : FALSE
7237  */
7238 static LRESULT LISTVIEW_SetItemState(HWND hwnd, INT nItem, LPLVITEMA lpLVItem)
7239 {
7240   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7241   BOOL bResult = FALSE;
7242   LVITEMA lvItem;
7243   INT i;
7244
7245   if (nItem == -1)
7246   {
7247     bResult = TRUE;
7248     ZeroMemory(&lvItem, sizeof(LVITEMA));
7249     lvItem.mask = LVIF_STATE;
7250     lvItem.state = lpLVItem->state;
7251     lvItem.stateMask = lpLVItem->stateMask ;
7252     
7253     /* apply to all items */
7254     for (i = 0; i< GETITEMCOUNT(infoPtr); i++)
7255     {
7256       lvItem.iItem = i;
7257       if (ListView_SetItemA(hwnd, &lvItem) == FALSE)
7258       {
7259         bResult = FALSE;
7260       }
7261     }
7262   }
7263   else
7264   {
7265     ZeroMemory(&lvItem, sizeof(LVITEMA));
7266     lvItem.mask = LVIF_STATE;
7267     lvItem.state = lpLVItem->state;
7268     lvItem.stateMask = lpLVItem->stateMask;
7269     lvItem.iItem = nItem;
7270     bResult = ListView_SetItemA(hwnd, &lvItem);
7271   }
7272
7273   return bResult;
7274 }
7275
7276 /***
7277  * DESCRIPTION:
7278  * Sets the text of an item or subitem.
7279  * 
7280  * PARAMETER(S):
7281  * [I] HWND : window handle
7282  * [I] INT : item index
7283  * [I] LPLVITEMA : item or subitem info
7284  *
7285  * RETURN:
7286  *   SUCCESS : TRUE
7287  *   FAILURE : FALSE
7288  */
7289 static BOOL LISTVIEW_SetItemTextA(HWND hwnd, INT nItem, LPLVITEMA lpLVItem)
7290 {
7291   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7292   BOOL bResult = FALSE;
7293   LVITEMA lvItem;
7294
7295   if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
7296   {
7297     ZeroMemory(&lvItem, sizeof(LVITEMA));
7298     lvItem.mask = LVIF_TEXT;
7299     lvItem.pszText = lpLVItem->pszText;
7300     lvItem.iItem = nItem;
7301     lvItem.iSubItem = lpLVItem->iSubItem;
7302     bResult = ListView_SetItemA(hwnd, &lvItem);
7303   }
7304   
7305   return bResult;
7306 }
7307
7308 /* LISTVIEW_SetItemTextW */
7309
7310 /***
7311  * DESCRIPTION:
7312  * Set item index that marks the start of a multiple selection.
7313  *
7314  * PARAMETER(S):
7315  * [I] HWND : window handle
7316  * [I] INT  : index
7317  *
7318  * RETURN:
7319  * Index number or -1 if there is no selection mark.
7320  */
7321 static LRESULT LISTVIEW_SetSelectionMark(HWND hwnd, INT nIndex)
7322 {
7323   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7324   INT nOldIndex = infoPtr->nSelectionMark;
7325
7326   infoPtr->nSelectionMark = nIndex;
7327
7328   return nOldIndex;
7329 }
7330
7331 /***
7332  * DESCRIPTION:
7333  * Sets the text background color.
7334  * 
7335  * PARAMETER(S):
7336  * [I] HWND : window handle
7337  * [I] COLORREF : text background color
7338  *
7339  * RETURN:
7340  *   SUCCESS : TRUE
7341  *   FAILURE : FALSE
7342  */
7343 static LRESULT LISTVIEW_SetTextBkColor(HWND hwnd, COLORREF clrTextBk)
7344 {
7345   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7346
7347   infoPtr->clrTextBk = clrTextBk;
7348   InvalidateRect(hwnd, NULL, TRUE);
7349
7350   return TRUE;
7351 }
7352
7353 /***
7354  * DESCRIPTION:
7355  * Sets the text foreground color.
7356  * 
7357  * PARAMETER(S):
7358  * [I] HWND : window handle
7359  * [I] COLORREF : text color 
7360  *
7361  * RETURN:
7362  *   SUCCESS : TRUE
7363  *   FAILURE : FALSE
7364  */
7365 static LRESULT LISTVIEW_SetTextColor (HWND hwnd, COLORREF clrText)
7366 {
7367   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7368
7369   infoPtr->clrText = clrText;
7370   InvalidateRect(hwnd, NULL, TRUE);
7371
7372   return TRUE;
7373 }
7374
7375 /* LISTVIEW_SetToolTips */
7376 /* LISTVIEW_SetUnicodeFormat */
7377 /* LISTVIEW_SetWorkAreas */
7378
7379 /***
7380  * DESCRIPTION:
7381  * Callback internally used by LISTVIEW_SortItems()
7382  * 
7383  * PARAMETER(S):
7384  * [I] LPVOID : first LISTVIEW_ITEM to compare
7385  * [I] LPVOID : second LISTVIEW_ITEM to compare
7386  * [I] LPARAM : HWND of control
7387  *
7388  * RETURN:
7389  *   if first comes before second : negative
7390  *   if first comes after second : positive
7391  *   if first and second are equivalent : zero
7392  */
7393 static INT WINAPI LISTVIEW_CallBackCompare( 
7394   LPVOID first, 
7395   LPVOID second, 
7396   LPARAM lParam)
7397 {
7398   /* Forward the call to the client defined callback */
7399   INT rv;
7400   HWND hwnd = (HWND)lParam;
7401   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7402   HDPA  hdpa_first = (HDPA) first;
7403   HDPA  hdpa_second = (HDPA) second;
7404   LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( hdpa_first, 0 );
7405   LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( hdpa_second, 0 );
7406
7407   rv = (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
7408
7409   return rv;
7410 }
7411
7412 /***
7413  * DESCRIPTION:
7414  * Sorts the listview items.
7415  * 
7416  * PARAMETER(S):
7417  * [I] HWND : window handle
7418  * [I] WPARAM : application-defined value
7419  * [I] LPARAM : pointer to comparision callback
7420  *
7421  * RETURN:
7422  *   SUCCESS : TRUE
7423  *   FAILURE : FALSE
7424  */
7425 static LRESULT LISTVIEW_SortItems(HWND hwnd, WPARAM wParam, LPARAM lParam)
7426 {
7427     LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7428     int nCount, i;
7429     UINT lStyle = GetWindowLongA(hwnd, GWL_STYLE);
7430     HDPA hdpaSubItems=NULL;
7431     LISTVIEW_ITEM *pLVItem=NULL;
7432     LPVOID selectionMarkItem;
7433
7434     if (lStyle & LVS_OWNERDATA)
7435       return FALSE;
7436
7437     if (!infoPtr || !infoPtr->hdpaItems)
7438         return FALSE;
7439    
7440     nCount = GETITEMCOUNT(infoPtr);
7441     /* if there are 0 or 1 items, there is no need to sort */
7442     if (nCount < 2)
7443         return TRUE;
7444
7445     infoPtr->pfnCompare = (PFNLVCOMPARE)lParam;
7446     infoPtr->lParamSort = (LPARAM)wParam;
7447     DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, hwnd);
7448
7449     /* Adjust selections and indices so that they are the way they should 
7450      * be after the sort (otherwise, the list items move around, but 
7451      * whatever is at the item's previous original position will be 
7452      * selected instead)
7453      */
7454     selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
7455     for (i=0; i < nCount; i++)
7456     {
7457        hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
7458        pLVItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
7459
7460        if (pLVItem->state & LVIS_SELECTED)
7461           LISTVIEW_AddSelectionRange(hwnd, i, i);
7462        else
7463           LISTVIEW_RemoveSelectionRange(hwnd, i, i);
7464        if (pLVItem->state & LVIS_FOCUSED)
7465           infoPtr->nFocusedItem=i;
7466     }
7467     if (selectionMarkItem != NULL)
7468        infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
7469     /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7470
7471     /* align the items */
7472     LISTVIEW_AlignTop(hwnd);
7473
7474     /* refresh the display */
7475     InvalidateRect(hwnd, NULL, TRUE);
7476
7477     return TRUE;
7478 }
7479
7480 /* LISTVIEW_SubItemHitTest */
7481
7482 /***
7483  * DESCRIPTION:
7484  * Updates an items or rearranges the listview control.
7485  * 
7486  * PARAMETER(S):
7487  * [I] HWND : window handle
7488  * [I] INT : item index
7489  *
7490  * RETURN:
7491  *   SUCCESS : TRUE
7492  *   FAILURE : FALSE
7493  */
7494 static LRESULT LISTVIEW_Update(HWND hwnd, INT nItem)
7495 {
7496   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7497   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
7498   BOOL bResult = FALSE;
7499   RECT rc;
7500
7501   if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
7502   {
7503     bResult = TRUE;
7504
7505     /* rearrange with default alignment style */
7506     if ((lStyle & LVS_AUTOARRANGE) && (((lStyle & LVS_TYPEMASK) == LVS_ICON) ||
7507         ((lStyle & LVS_TYPEMASK)  == LVS_SMALLICON)))
7508     {
7509       ListView_Arrange(hwnd, 0);
7510     }
7511     else
7512     {
7513       /* get item bounding rectangle */
7514       ListView_GetItemRect(hwnd, nItem, &rc, LVIR_BOUNDS);
7515       InvalidateRect(hwnd, &rc, TRUE);
7516     }
7517   }
7518
7519   return bResult;
7520 }
7521
7522 /***
7523  * DESCRIPTION:
7524  * Creates the listview control.
7525  * 
7526  * PARAMETER(S):
7527  * [I] HWND : window handle
7528  *
7529  * RETURN:
7530  * Zero
7531  */
7532 static LRESULT LISTVIEW_Create(HWND hwnd, WPARAM wParam, LPARAM lParam)
7533 {
7534   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7535   LPCREATESTRUCTA lpcs = (LPCREATESTRUCTA)lParam;
7536   UINT uView = lpcs->style & LVS_TYPEMASK;
7537   LOGFONTA logFont;
7538
7539   /* initialize info pointer */
7540   ZeroMemory(infoPtr, sizeof(LISTVIEW_INFO));
7541
7542   /* determine the type of structures to use */
7543   infoPtr->notifyFormat = SendMessageA(GetParent(hwnd), WM_NOTIFYFORMAT, 
7544                                        (WPARAM)hwnd, (LPARAM)NF_QUERY);
7545   if (infoPtr->notifyFormat != NFR_ANSI)
7546   {
7547     FIXME("ANSI notify format is NOT used\n");
7548   }
7549   
7550   /* initialize color information  */
7551   infoPtr->clrBk = GetSysColor(COLOR_WINDOW);
7552   infoPtr->clrText = GetSysColor(COLOR_WINDOWTEXT);
7553   infoPtr->clrTextBk = CLR_DEFAULT;
7554
7555   /* set default values */
7556   infoPtr->uCallbackMask = 0;
7557   infoPtr->nFocusedItem = -1;
7558   infoPtr->nSelectionMark = -1;
7559   infoPtr->nHotItem = -1;
7560   infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
7561   infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
7562   ZeroMemory(&infoPtr->rcList, sizeof(RECT));
7563   infoPtr->hwndEdit = 0;
7564   infoPtr->pedititem = NULL;
7565   infoPtr->nEditLabelItem = -1;
7566
7567   /* get default font (icon title) */
7568   SystemParametersInfoA(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
7569   infoPtr->hDefaultFont = CreateFontIndirectA(&logFont);
7570   infoPtr->hFont = infoPtr->hDefaultFont;
7571   
7572   /* create header */
7573   infoPtr->hwndHeader = CreateWindowA(WC_HEADERA, (LPCSTR)NULL, 
7574                                       WS_CHILD | HDS_HORZ | HDS_BUTTONS, 
7575                                       0, 0, 0, 0, hwnd, (HMENU)0, 
7576                                       lpcs->hInstance, NULL);
7577
7578   /* set header font */
7579   SendMessageA(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, 
7580                (LPARAM)TRUE);
7581   
7582   if (uView == LVS_ICON)
7583   {
7584     infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
7585     infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
7586   }
7587   else if (uView == LVS_REPORT)
7588   {
7589     if (!(LVS_NOCOLUMNHEADER & lpcs->style))
7590     {
7591       ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
7592     }
7593     else
7594     {
7595       /* set HDS_HIDDEN flag to hide the header bar */
7596       SetWindowLongA(infoPtr->hwndHeader, GWL_STYLE, 
7597                     GetWindowLongA(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
7598     }
7599       
7600
7601     infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
7602     infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
7603   }
7604   else
7605   {
7606     infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
7607     infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
7608   }
7609
7610   /* display unsupported listview window styles */
7611   LISTVIEW_UnsupportedStyles(lpcs->style);
7612
7613   /* allocate memory for the data structure */
7614   infoPtr->hdpaItems = DPA_Create(10);
7615
7616   /* allocate memory for the selection ranges */
7617   infoPtr->hdpaSelectionRanges = DPA_Create(10);
7618
7619   /* initialize size of items */
7620   infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
7621   infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
7622
7623   /* initialize the hover time to -1(indicating the default system hover time) */
7624   infoPtr->dwHoverTime = -1;  
7625
7626   return 0;
7627 }
7628
7629 /***
7630  * DESCRIPTION:
7631  * Erases the background of the listview control.
7632  * 
7633  * PARAMETER(S):
7634  * [I] HWND : window handle
7635  * [I] WPARAM : device context handle
7636  * [I] LPARAM : not used
7637  * 
7638  * RETURN:
7639  *   SUCCESS : TRUE
7640  *   FAILURE : FALSE
7641  */
7642 static LRESULT LISTVIEW_EraseBackground(HWND hwnd, WPARAM wParam, 
7643                                         LPARAM lParam)
7644 {
7645   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7646   BOOL bResult;
7647
7648   if (infoPtr->clrBk == CLR_NONE) 
7649   {
7650     bResult = SendMessageA(GetParent(hwnd), WM_ERASEBKGND, wParam, lParam);
7651   }
7652   else 
7653   {
7654     RECT rc;
7655     HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk);
7656     GetClientRect(hwnd, &rc);
7657     FillRect((HDC)wParam, &rc, hBrush);
7658     DeleteObject(hBrush);
7659     bResult = TRUE;
7660   }
7661
7662   return bResult;
7663 }
7664
7665
7666 static void LISTVIEW_FillBackground(HWND hwnd, HDC hdc, LPRECT rc)
7667 {
7668   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7669
7670   if (infoPtr->clrBk != CLR_NONE) 
7671   {
7672     HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk);
7673     FillRect(hdc, rc, hBrush);
7674     DeleteObject(hBrush);
7675   }
7676 }
7677
7678 /***
7679  * DESCRIPTION:
7680  * Retrieves the listview control font.
7681  * 
7682  * PARAMETER(S):
7683  * [I] HWND : window handle
7684  *
7685  * RETURN:
7686  * Font handle.
7687  */
7688 static LRESULT LISTVIEW_GetFont(HWND hwnd)
7689 {
7690   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7691
7692   return infoPtr->hFont;
7693 }
7694
7695 /***
7696  * DESCRIPTION:
7697  * Performs vertical scrolling.
7698  * 
7699  * PARAMETER(S):
7700  * [I] HWND : window handle
7701  * [I] INT : scroll code
7702  * [I] SHORT : current scroll position if scroll code is SB_THUMBPOSITION 
7703  *             or SB_THUMBTRACK.
7704  * [I] HWND : scrollbar control window handle
7705  *
7706  * RETURN:
7707  * Zero
7708  */
7709 static LRESULT LISTVIEW_VScroll(HWND hwnd, INT nScrollCode, SHORT nCurrentPos,
7710                                 HWND hScrollWnd)
7711 {
7712   SCROLLINFO scrollInfo;
7713
7714   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7715   SendMessageA(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7716
7717   ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
7718   scrollInfo.cbSize = sizeof(SCROLLINFO);
7719   scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
7720
7721   if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
7722   {
7723     INT nOldScrollPos = scrollInfo.nPos;
7724     switch (nScrollCode)
7725     {
7726     case SB_LINEUP:
7727       if (scrollInfo.nPos > scrollInfo.nMin)
7728       {
7729         scrollInfo.nPos--;
7730       }
7731     break;
7732     
7733     case SB_LINEDOWN:
7734       if (scrollInfo.nPos < scrollInfo.nMax)
7735       {
7736         scrollInfo.nPos++;
7737       }
7738       break;
7739       
7740     case SB_PAGEUP:
7741       if (scrollInfo.nPos > scrollInfo.nMin)
7742       {
7743         if (scrollInfo.nPos >= scrollInfo.nPage)
7744         {
7745           scrollInfo.nPos -= scrollInfo.nPage;
7746         }
7747         else
7748         {
7749           scrollInfo.nPos = scrollInfo.nMin;
7750         }
7751       }
7752       break;
7753       
7754     case SB_PAGEDOWN:
7755       if (scrollInfo.nPos < scrollInfo.nMax)
7756       {
7757         if (scrollInfo.nPos <= scrollInfo.nMax - scrollInfo.nPage)
7758         {
7759           scrollInfo.nPos += scrollInfo.nPage;
7760         }
7761         else
7762         {
7763           scrollInfo.nPos = scrollInfo.nMax;
7764         }
7765       }
7766       break;
7767
7768     case SB_THUMBPOSITION:
7769     case SB_THUMBTRACK:
7770         scrollInfo.nPos = nCurrentPos;
7771         if (scrollInfo.nPos > scrollInfo.nMax)
7772             scrollInfo.nPos=scrollInfo.nMax;
7773
7774         if (scrollInfo.nPos < scrollInfo.nMin)
7775             scrollInfo.nPos=scrollInfo.nMin;
7776
7777       break;
7778     }
7779
7780     if (nOldScrollPos != scrollInfo.nPos)
7781     {
7782       scrollInfo.fMask = SIF_POS;
7783       SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
7784       InvalidateRect(hwnd, NULL, TRUE);
7785     }
7786   }
7787     
7788   return 0;
7789 }
7790
7791 /***
7792  * DESCRIPTION:
7793  * Performs horizontal scrolling.
7794  * 
7795  * PARAMETER(S):
7796  * [I] HWND : window handle
7797  * [I] INT : scroll code
7798  * [I] SHORT : current scroll position if scroll code is SB_THUMBPOSITION 
7799  *             or SB_THUMBTRACK.
7800  * [I] HWND : scrollbar control window handle
7801  *
7802  * RETURN:
7803  * Zero
7804  */
7805 static LRESULT LISTVIEW_HScroll(HWND hwnd, INT nScrollCode, SHORT nCurrentPos,
7806                                 HWND hScrollWnd)
7807 {
7808   SCROLLINFO scrollInfo;
7809
7810   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7811   SendMessageA(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7812
7813
7814   ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
7815   scrollInfo.cbSize = sizeof(SCROLLINFO);
7816   scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
7817  
7818   if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
7819   {
7820     INT nOldScrollPos = scrollInfo.nPos;
7821
7822     switch (nScrollCode)
7823     {
7824     case SB_LINELEFT:
7825       if (scrollInfo.nPos > scrollInfo.nMin)
7826       {
7827         scrollInfo.nPos--;
7828       }
7829       break;
7830     
7831     case SB_LINERIGHT:
7832       if (scrollInfo.nPos < scrollInfo.nMax)
7833       {
7834         scrollInfo.nPos++;
7835       }
7836       break;
7837       
7838     case SB_PAGELEFT:
7839       if (scrollInfo.nPos > scrollInfo.nMin)
7840       {
7841         if (scrollInfo.nPos >= scrollInfo.nPage)
7842         {
7843           scrollInfo.nPos -= scrollInfo.nPage;
7844         }
7845         else
7846         {
7847           scrollInfo.nPos = scrollInfo.nMin;
7848         }
7849       }
7850       break;
7851       
7852     case SB_PAGERIGHT:
7853       if (scrollInfo.nPos < scrollInfo.nMax)
7854       {
7855         if (scrollInfo.nPos <= scrollInfo.nMax - scrollInfo.nPage)
7856         {
7857           scrollInfo.nPos += scrollInfo.nPage;
7858         }
7859         else
7860         {
7861           scrollInfo.nPos = scrollInfo.nMax;
7862         }
7863       }
7864       break;
7865
7866     case SB_THUMBPOSITION:
7867     case SB_THUMBTRACK:
7868         scrollInfo.nPos = nCurrentPos;
7869
7870         if (scrollInfo.nPos > scrollInfo.nMax)
7871             scrollInfo.nPos=scrollInfo.nMax;
7872
7873         if (scrollInfo.nPos < scrollInfo.nMin)
7874             scrollInfo.nPos=scrollInfo.nMin;
7875       break;
7876     }
7877
7878     if (nOldScrollPos != scrollInfo.nPos)
7879     {
7880       UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
7881       scrollInfo.fMask = SIF_POS;
7882       SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
7883       if(uView == LVS_REPORT)
7884       {
7885           scrollInfo.fMask = SIF_POS;
7886           GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
7887           LISTVIEW_UpdateHeaderSize(hwnd, scrollInfo.nPos);
7888       }
7889       InvalidateRect(hwnd, NULL, TRUE);
7890     }
7891   }
7892     
7893   return 0;
7894 }
7895
7896 static LRESULT LISTVIEW_MouseWheel(HWND hwnd, INT wheelDelta)
7897 {
7898     INT gcWheelDelta = 0;
7899     UINT pulScrollLines = 3;
7900     SCROLLINFO scrollInfo;
7901
7902     UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
7903
7904     SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
7905     gcWheelDelta -= wheelDelta;
7906
7907     ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
7908     scrollInfo.cbSize = sizeof(SCROLLINFO);
7909     scrollInfo.fMask = SIF_POS | SIF_RANGE;
7910
7911     switch(uView)
7912     {
7913     case LVS_ICON:
7914     case LVS_SMALLICON:
7915        /*
7916         *  listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
7917         *  should be fixed in the future.
7918         */
7919         if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
7920             LISTVIEW_VScroll(hwnd, SB_THUMBPOSITION, scrollInfo.nPos + (gcWheelDelta < 0) ? 37 : -37, 0);
7921         break;
7922
7923     case LVS_REPORT:
7924         if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
7925         {
7926             if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
7927             {
7928                 int cLineScroll = min(LISTVIEW_GetCountPerColumn(hwnd), pulScrollLines);
7929                 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
7930                 LISTVIEW_VScroll(hwnd, SB_THUMBPOSITION, scrollInfo.nPos + cLineScroll, 0);
7931             }
7932         }
7933         break;
7934
7935     case LVS_LIST:
7936         LISTVIEW_HScroll(hwnd, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
7937         break;
7938     }
7939     return 0;
7940 }
7941
7942 /***
7943  * DESCRIPTION:
7944  * ??? 
7945  * 
7946  * PARAMETER(S):
7947  * [I] HWND : window handle
7948  * [I] INT : virtual key 
7949  * [I] LONG : key data
7950  *
7951  * RETURN:
7952  * Zero
7953  */
7954 static LRESULT LISTVIEW_KeyDown(HWND hwnd, INT nVirtualKey, LONG lKeyData)
7955 {
7956   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7957   INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
7958   HWND hwndParent = GetParent(hwnd);
7959   NMLVKEYDOWN nmKeyDown; 
7960   NMHDR nmh;
7961   INT nItem = -1;
7962   BOOL bRedraw = FALSE;
7963   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
7964   UINT uView =  lStyle & LVS_TYPEMASK;
7965
7966   /* send LVN_KEYDOWN notification */
7967   ZeroMemory(&nmKeyDown, sizeof(NMLVKEYDOWN));
7968   nmKeyDown.hdr.hwndFrom = hwnd;  
7969   nmKeyDown.hdr.idFrom = nCtrlId;  
7970   nmKeyDown.hdr.code = LVN_KEYDOWN;  
7971   nmKeyDown.wVKey = nVirtualKey; 
7972   nmKeyDown.flags = 0; 
7973   SendMessageA(hwndParent, WM_NOTIFY, (WPARAM)nCtrlId, (LPARAM)&nmKeyDown); 
7974   
7975   /* initialize */
7976   nmh.hwndFrom = hwnd;
7977   nmh.idFrom = nCtrlId;
7978
7979   switch (nVirtualKey)
7980   {
7981   case VK_RETURN:
7982     if ((GETITEMCOUNT(infoPtr) > 0) && (infoPtr->nFocusedItem != -1))
7983     {
7984       /* send NM_RETURN notification */
7985       nmh.code = NM_RETURN;
7986       ListView_Notify(hwndParent, nCtrlId, &nmh);
7987       
7988       /* send LVN_ITEMACTIVATE notification */
7989       nmh.code = LVN_ITEMACTIVATE;
7990       ListView_Notify(hwndParent, nCtrlId, &nmh);
7991     }
7992     break;
7993
7994   case VK_HOME:
7995     if (GETITEMCOUNT(infoPtr) > 0)
7996     {
7997       nItem = 0;
7998     }
7999     break;
8000
8001   case VK_END:
8002     if (GETITEMCOUNT(infoPtr) > 0)
8003     {
8004       nItem = GETITEMCOUNT(infoPtr) - 1;
8005     }
8006     break;
8007
8008   case VK_LEFT:
8009     nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_TOLEFT);
8010     break;
8011
8012   case VK_UP:
8013     nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_ABOVE);
8014     break;
8015     
8016   case VK_RIGHT:
8017     nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_TORIGHT);
8018     break;
8019
8020   case VK_DOWN:
8021     nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_BELOW);
8022     break;
8023
8024   case VK_PRIOR:
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 < 0) nItem = 0;
8035     break;
8036
8037   case VK_NEXT:
8038     if (uView == LVS_REPORT)
8039     {
8040       nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(hwnd);
8041     }
8042     else
8043     {
8044       nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(hwnd)
8045                                     * LISTVIEW_GetCountPerRow(hwnd);
8046     }
8047     if(nItem >= GETITEMCOUNT(infoPtr)) nItem = GETITEMCOUNT(infoPtr) - 1;
8048     break;
8049   }
8050
8051   if ((nItem != -1) && (nItem != infoPtr->nFocusedItem))
8052   {
8053     bRedraw = LISTVIEW_KeySelection(hwnd, nItem);
8054     if (bRedraw != FALSE)
8055     {
8056       /* refresh client area */
8057       UpdateWindow(hwnd);
8058     }
8059   }
8060
8061   return 0;
8062 }
8063
8064 /***
8065  * DESCRIPTION:
8066  * Kills the focus.
8067  * 
8068  * PARAMETER(S):
8069  * [I] HWND : window handle
8070  *
8071  * RETURN:
8072  * Zero
8073  */
8074 static LRESULT LISTVIEW_KillFocus(HWND hwnd)
8075 {
8076   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongA(hwnd, 0);
8077   INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
8078   NMHDR nmh;
8079   INT i,nTop,nBottom;
8080   RECT rcItem;
8081   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
8082   UINT uView =  lStyle & LVS_TYPEMASK;
8083  
8084   TRACE("(hwnd=%x)\n", hwnd);
8085
8086   /* send NM_KILLFOCUS notification */
8087   nmh.hwndFrom = hwnd;
8088   nmh.idFrom = nCtrlId;
8089   nmh.code = NM_KILLFOCUS;
8090   ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
8091
8092   /* set window focus flag */
8093   infoPtr->bFocus = FALSE;
8094
8095   /* NEED drawing optimization ; redraw the selected items */
8096   if (uView & LVS_REPORT)
8097   { 
8098     nTop = LISTVIEW_GetTopIndex(hwnd);
8099     nBottom = nTop + 
8100               LISTVIEW_GetCountPerColumn(hwnd) + 1;
8101   }
8102   else
8103   {
8104     nTop = 0;
8105     nBottom = GETITEMCOUNT(infoPtr);
8106   }
8107   for (i = nTop; i<nBottom; i++)
8108   {
8109     if (LISTVIEW_IsSelected(hwnd,i))
8110     {
8111       rcItem.left = LVIR_BOUNDS;
8112       LISTVIEW_GetItemRect(hwnd, i, &rcItem);
8113       InvalidateRect(hwnd, &rcItem, FALSE);
8114     }
8115   }
8116
8117   return 0;
8118 }
8119
8120 /***
8121  * DESCRIPTION:
8122  * Processes double click messages (left mouse button).
8123  * 
8124  * PARAMETER(S):
8125  * [I] HWND : window handle
8126  * [I] WORD : key flag
8127  * [I] WORD : x coordinate
8128  * [I] WORD : y coordinate
8129  *
8130  * RETURN:
8131  * Zero
8132  */
8133 static LRESULT LISTVIEW_LButtonDblClk(HWND hwnd, WORD wKey, WORD wPosX, 
8134                                       WORD wPosY)
8135 {
8136   LONG nCtrlId = GetWindowLongA(hwnd, GWL_ID);
8137   LVHITTESTINFO htInfo;
8138   NMHDR nmh;
8139   NMLISTVIEW nmlv;
8140   INT ret;
8141
8142   TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8143
8144   htInfo.pt.x = wPosX;
8145   htInfo.pt.y = wPosY;
8146
8147   /* send NM_DBLCLK notification */
8148   ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8149   nmlv.hdr.hwndFrom = hwnd;
8150   nmlv.hdr.idFrom = nCtrlId;
8151   nmlv.hdr.code = NM_DBLCLK;
8152   ret = LISTVIEW_HitTestItem(hwnd, &htInfo, TRUE);
8153   if (ret != -1)
8154   {
8155     nmlv.iItem = htInfo.iItem;
8156     nmlv.iSubItem = htInfo.iSubItem;
8157   }
8158   else
8159   {
8160     nmlv.iItem = -1;
8161     nmlv.iSubItem = 0;
8162   }  
8163   nmlv.ptAction.x = wPosX;
8164   nmlv.ptAction.y = wPosY;
8165   ListView_LVNotify(GetParent(hwnd), nCtrlId, &nmlv);
8166
8167
8168   /* To send the LVN_ITEMACTIVATE, it must be on an Item */
8169   if(ret != -1)
8170   {
8171     /* send LVN_ITEMACTIVATE notification */
8172     nmh.hwndFrom = hwnd;
8173     nmh.idFrom = nCtrlId;
8174     nmh.code = LVN_ITEMACTIVATE;
8175     ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
8176   }
8177
8178   return 0;
8179 }
8180
8181 /***
8182  * DESCRIPTION:
8183  * Processes mouse down messages (left mouse button).
8184  * 
8185  * PARAMETER(S):
8186  * [I] HWND : window handle
8187  * [I] WORD : key flag
8188  * [I] WORD : x coordinate
8189  * [I] WORD : y coordinate
8190  *
8191  * RETURN:
8192  * Zero
8193  */
8194 static LRESULT LISTVIEW_LButtonDown(HWND hwnd, WORD wKey, WORD wPosX, 
8195                                     WORD wPosY)
8196 {
8197   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8198   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
8199   INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
8200   static BOOL bGroupSelect = TRUE;
8201   POINT ptPosition;
8202   NMHDR nmh;
8203   INT nItem;
8204
8205   TRACE("(hwnd=%x, key=%hu, X=%hu, Y=%hu)\n", hwnd, wKey, wPosX, 
8206         wPosY);
8207
8208   /* send NM_RELEASEDCAPTURE notification */ 
8209   nmh.hwndFrom = hwnd;
8210   nmh.idFrom = nCtrlId;
8211   nmh.code = NM_RELEASEDCAPTURE;
8212   ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
8213  
8214   if (infoPtr->bFocus == FALSE)
8215   {
8216     SetFocus(hwnd);
8217   }
8218
8219   /* set left button down flag */
8220   infoPtr->bLButtonDown = TRUE;
8221   
8222   ptPosition.x = wPosX;
8223   ptPosition.y = wPosY;
8224   nItem = LISTVIEW_MouseSelection(hwnd, ptPosition);
8225   if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
8226   {
8227     if (lStyle & LVS_SINGLESEL)
8228     {
8229       if ((ListView_GetItemState(hwnd, nItem, LVIS_SELECTED) & LVIS_SELECTED)
8230           && infoPtr->nEditLabelItem == -1) 
8231       {
8232           infoPtr->nEditLabelItem = nItem;
8233       }
8234       else
8235       {
8236         LISTVIEW_SetSelection(hwnd, nItem);
8237       }
8238     }
8239     else
8240     {
8241       if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
8242       {
8243         if (bGroupSelect != FALSE)
8244         {
8245           LISTVIEW_AddGroupSelection(hwnd, nItem);
8246         }
8247         else
8248         {
8249           LISTVIEW_AddSelection(hwnd, nItem);
8250         }
8251       }
8252       else if (wKey & MK_CONTROL)
8253       {
8254         bGroupSelect = LISTVIEW_ToggleSelection(hwnd, nItem);
8255       }
8256       else  if (wKey & MK_SHIFT)
8257       {
8258         LISTVIEW_SetGroupSelection(hwnd, nItem);
8259       }
8260       else
8261       {
8262         BOOL was_selected =
8263             (ListView_GetItemState(hwnd, nItem, LVIS_SELECTED) & LVIS_SELECTED);
8264
8265         /* set selection (clears other pre-existing selections) */
8266         LISTVIEW_SetSelection(hwnd, nItem);
8267
8268         if (was_selected && infoPtr->nEditLabelItem == -1)
8269         {
8270           infoPtr->nEditLabelItem = nItem;
8271         }
8272       }
8273     }
8274   }
8275   else
8276   {
8277     /* remove all selections */
8278     LISTVIEW_RemoveAllSelections(hwnd);
8279   }
8280
8281   /* redraw if we could have possibly selected something */
8282   if(!GETITEMCOUNT(infoPtr)) InvalidateRect(hwnd, NULL, TRUE);
8283
8284   return 0;
8285 }
8286
8287 /***
8288  * DESCRIPTION:
8289  * Processes mouse up messages (left mouse button).
8290  * 
8291  * PARAMETER(S):
8292  * [I] HWND : window handle
8293  * [I] WORD : key flag
8294  * [I] WORD : x coordinate
8295  * [I] WORD : y coordinate
8296  *
8297  * RETURN:
8298  * Zero
8299  */
8300 static LRESULT LISTVIEW_LButtonUp(HWND hwnd, WORD wKey, WORD wPosX, 
8301                                   WORD wPosY)
8302 {
8303   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8304
8305   TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8306
8307   if (infoPtr->bLButtonDown != FALSE) 
8308   {
8309     INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
8310     NMLISTVIEW nmlv;
8311     LVHITTESTINFO lvHitTestInfo;
8312     INT ret;
8313
8314     lvHitTestInfo.pt.x = wPosX;
8315     lvHitTestInfo.pt.y = wPosY;
8316
8317   /* send NM_CLICK notification */
8318     ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8319     nmlv.hdr.hwndFrom = hwnd;
8320     nmlv.hdr.idFrom = nCtrlId;
8321     nmlv.hdr.code = NM_CLICK;
8322     ret = LISTVIEW_HitTestItem(hwnd, &lvHitTestInfo, TRUE);
8323     if (ret != -1)
8324     {
8325         nmlv.iItem = lvHitTestInfo.iItem;
8326         nmlv.iSubItem = lvHitTestInfo.iSubItem;
8327     }
8328     else
8329     {
8330         nmlv.iItem = -1;
8331         nmlv.iSubItem = 0;
8332     }
8333     nmlv.ptAction.x = wPosX;
8334     nmlv.ptAction.y = wPosY;
8335     ListView_LVNotify(GetParent(hwnd), nCtrlId, &nmlv);
8336
8337
8338     /* set left button flag */
8339     infoPtr->bLButtonDown = FALSE;
8340
8341     if(infoPtr->nEditLabelItem != -1)
8342     {
8343       if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem)
8344       {
8345         LISTVIEW_EditLabelA(hwnd, lvHitTestInfo.iItem);
8346       }
8347       infoPtr->nEditLabelItem = -1;
8348     }
8349   }
8350
8351   return 0;
8352 }
8353
8354 /***
8355  * DESCRIPTION:
8356  * Creates the listview control (called before WM_CREATE).
8357  * 
8358  * PARAMETER(S):
8359  * [I] HWND : window handle
8360  * [I] WPARAM : unhandled 
8361  * [I] LPARAM : widow creation info
8362  * 
8363  * RETURN:
8364  * Zero
8365  */
8366 static LRESULT LISTVIEW_NCCreate(HWND hwnd, WPARAM wParam, LPARAM lParam)
8367 {
8368   LISTVIEW_INFO *infoPtr;
8369
8370   TRACE("(hwnd=%x,wParam=%x,lParam=%lx)\n", hwnd, wParam, lParam);
8371
8372   /* allocate memory for info structure */
8373   infoPtr = (LISTVIEW_INFO *)COMCTL32_Alloc(sizeof(LISTVIEW_INFO));
8374   SetWindowLongA(hwnd, 0, (LONG)infoPtr);
8375   if (infoPtr == NULL) 
8376   {
8377     ERR("could not allocate info memory!\n");
8378     return 0;
8379   }
8380
8381   if ((LISTVIEW_INFO *)GetWindowLongA(hwnd, 0) != infoPtr) 
8382   {
8383     ERR("pointer assignment error!\n");
8384     return 0;
8385   }
8386
8387   return DefWindowProcA(hwnd, WM_NCCREATE, wParam, lParam);
8388 }
8389
8390 /***
8391  * DESCRIPTION:
8392  * Destroys the listview control (called after WM_DESTROY).
8393  * 
8394  * PARAMETER(S):
8395  * [I] HWND : window handle
8396  * 
8397  * RETURN:
8398  * Zero
8399  */
8400 static LRESULT LISTVIEW_NCDestroy(HWND hwnd)
8401 {
8402   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8403
8404   TRACE("(hwnd=%x)\n", hwnd);
8405
8406   /* delete all items */
8407   LISTVIEW_DeleteAllItems(hwnd);
8408
8409   /* destroy data structure */
8410   DPA_Destroy(infoPtr->hdpaItems);
8411   DPA_Destroy(infoPtr->hdpaSelectionRanges);
8412
8413   /* destroy font */
8414   infoPtr->hFont = (HFONT)0;
8415   if (infoPtr->hDefaultFont)
8416   {
8417     DeleteObject(infoPtr->hDefaultFont);
8418   }
8419
8420   /* free listview info pointer*/
8421   COMCTL32_Free(infoPtr);
8422
8423   SetWindowLongA(hwnd, 0, 0);
8424   return 0;
8425 }
8426
8427 /***
8428  * DESCRIPTION:
8429  * Handles notifications from children.
8430  * 
8431  * PARAMETER(S):
8432  * [I] HWND : window handle
8433  * [I] INT : control identifier
8434  * [I] LPNMHDR : notification information
8435  * 
8436  * RETURN:
8437  * Zero
8438  */
8439 static LRESULT LISTVIEW_Notify(HWND hwnd, INT nCtrlId, LPNMHDR lpnmh)
8440 {
8441   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8442   
8443   if (lpnmh->hwndFrom == infoPtr->hwndHeader) 
8444   {
8445     /* handle notification from header control */
8446     if (lpnmh->code == HDN_ENDTRACKA)
8447     {
8448       infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8449       InvalidateRect(hwnd, NULL, TRUE);
8450     }
8451     else if(lpnmh->code ==  HDN_ITEMCLICKA)
8452     {
8453         /* Handle sorting by Header Column */
8454         NMLISTVIEW nmlv;
8455         LPNMHEADERA pnmHeader = (LPNMHEADERA) lpnmh;
8456         LONG lCtrlId = GetWindowLongA(hwnd, GWL_ID);
8457
8458         ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8459         nmlv.hdr.hwndFrom = hwnd;
8460         nmlv.hdr.idFrom = lCtrlId;
8461         nmlv.hdr.code = LVN_COLUMNCLICK;
8462         nmlv.iItem = -1;
8463         nmlv.iSubItem = pnmHeader->iItem;
8464         
8465         ListView_LVNotify(GetParent(hwnd),lCtrlId, &nmlv);
8466
8467     }
8468     else if(lpnmh->code == NM_RELEASEDCAPTURE)
8469     {
8470       /* Idealy this should be done in HDN_ENDTRACKA
8471        * but since SetItemBounds in Header.c is called after
8472        * the notification is sent, it is neccessary to handle the
8473        * update of the scroll bar here (Header.c works fine as it is,
8474        * no need to disturb it)
8475        */
8476       infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8477       LISTVIEW_UpdateScroll(hwnd);
8478       InvalidateRect(hwnd, NULL, TRUE);
8479     }
8480
8481   }
8482
8483   return 0;
8484 }
8485
8486 /***
8487  * DESCRIPTION:
8488  * Determines the type of structure to use.
8489  * 
8490  * PARAMETER(S):
8491  * [I] HWND : window handle of the sender
8492  * [I] HWND : listview window handle 
8493  * [I] INT : command specifying the nature of the WM_NOTIFYFORMAT
8494  *
8495  * RETURN:
8496  * Zero
8497  */
8498 static LRESULT LISTVIEW_NotifyFormat(HWND hwndFrom, HWND hwnd, INT nCommand)
8499 {
8500   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8501
8502   if (nCommand == NF_REQUERY)
8503   {
8504     /* determine the type of structure to use */
8505     infoPtr->notifyFormat = SendMessageA(hwndFrom, WM_NOTIFYFORMAT, 
8506                                          (WPARAM)hwnd, (LPARAM)NF_QUERY);
8507     if (infoPtr->notifyFormat == NFR_UNICODE)
8508     {
8509       FIXME("NO support for unicode structures\n");
8510     }
8511   }
8512
8513   return 0;
8514 }
8515
8516 /***
8517  * DESCRIPTION:
8518  * Paints/Repaints the listview control.
8519  * 
8520  * PARAMETER(S):
8521  * [I] HWND : window handle
8522  * [I] HDC : device context handle
8523  *
8524  * RETURN:
8525  * Zero
8526  */
8527 static LRESULT LISTVIEW_Paint(HWND hwnd, HDC hdc)
8528 {
8529   PAINTSTRUCT ps;
8530
8531    TRACE("(hwnd=%x,hdc=%x)\n", hwnd, hdc);
8532
8533   if (hdc == 0)
8534   {
8535     hdc = BeginPaint(hwnd, &ps);
8536     LISTVIEW_Refresh(hwnd, hdc);
8537     EndPaint(hwnd, &ps);
8538   }
8539   else
8540   {
8541     LISTVIEW_Refresh(hwnd, hdc);
8542   }
8543
8544   return 0;
8545 }
8546
8547 /***
8548  * DESCRIPTION:
8549  * Processes double click messages (right mouse button).
8550  * 
8551  * PARAMETER(S):
8552  * [I] HWND : window handle
8553  * [I] WORD : key flag
8554  * [I] WORD : x coordinate
8555  * [I] WORD : y coordinate
8556  *
8557  * RETURN:
8558  * Zero
8559  */
8560 static LRESULT LISTVIEW_RButtonDblClk(HWND hwnd, WORD wKey, WORD wPosX, 
8561                                       WORD wPosY)
8562 {
8563   INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
8564   NMHDR nmh;
8565
8566   TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8567
8568   /* send NM_RELEASEDCAPTURE notification */ 
8569   nmh.hwndFrom = hwnd;
8570   nmh.idFrom = nCtrlId;
8571   nmh.code = NM_RELEASEDCAPTURE;
8572   ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
8573
8574   /* send NM_RDBLCLK notification */
8575   nmh.code = NM_RDBLCLK;
8576   ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
8577
8578   return 0;
8579 }
8580
8581 /***
8582  * DESCRIPTION:
8583  * Processes mouse down messages (right mouse button).
8584  * 
8585  * PARAMETER(S):
8586  * [I] HWND : window handle
8587  * [I] WORD : key flag
8588  * [I] WORD : x coordinate
8589  * [I] WORD : y coordinate
8590  *
8591  * RETURN:
8592  * Zero
8593  */
8594 static LRESULT LISTVIEW_RButtonDown(HWND hwnd, WORD wKey, WORD wPosX, 
8595                                     WORD wPosY)
8596 {
8597   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8598   INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
8599   POINT ptPosition;
8600   NMHDR nmh;
8601   INT nItem;
8602
8603   TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8604
8605   /* send NM_RELEASEDCAPTURE notification */
8606   nmh.hwndFrom = hwnd;
8607   nmh.idFrom = nCtrlId;
8608   nmh.code = NM_RELEASEDCAPTURE;
8609   ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
8610  
8611   /* make sure the listview control window has the focus */
8612   if (infoPtr->bFocus == FALSE)
8613   {
8614     SetFocus(hwnd);
8615   }
8616
8617   /* set right button down flag */
8618   infoPtr->bRButtonDown = TRUE;
8619
8620   /* determine the index of the selected item */
8621   ptPosition.x = wPosX;
8622   ptPosition.y = wPosY;
8623   nItem = LISTVIEW_MouseSelection(hwnd, ptPosition);
8624   if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
8625   {
8626     LISTVIEW_SetItemFocus(hwnd,nItem);
8627     if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
8628         !LISTVIEW_IsSelected(hwnd,nItem))
8629     {
8630       LISTVIEW_SetSelection(hwnd, nItem);
8631     }
8632   }
8633   else
8634   {
8635     LISTVIEW_RemoveAllSelections(hwnd);
8636   }
8637   
8638   return 0;
8639 }
8640
8641 /***
8642  * DESCRIPTION:
8643  * Processes mouse up messages (right mouse button).
8644  * 
8645  * PARAMETER(S):
8646  * [I] HWND : window handle
8647  * [I] WORD : key flag
8648  * [I] WORD : x coordinate
8649  * [I] WORD : y coordinate
8650  *
8651  * RETURN:
8652  * Zero
8653  */
8654 static LRESULT LISTVIEW_RButtonUp(HWND hwnd, WORD wKey, WORD wPosX, 
8655                                   WORD wPosY)
8656 {
8657   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8658   INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
8659
8660   TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8661
8662   if (infoPtr->bRButtonDown != FALSE) 
8663   {
8664     NMLISTVIEW nmlv;
8665     LVHITTESTINFO lvHitTestInfo;
8666     POINT pt;
8667     INT ret;
8668
8669     lvHitTestInfo.pt.x = wPosX;
8670     lvHitTestInfo.pt.y = wPosY;
8671
8672     /* Send NM_RClICK notification */
8673     ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8674     nmlv.hdr.hwndFrom = hwnd;
8675     nmlv.hdr.idFrom = nCtrlId;
8676     nmlv.hdr.code = NM_RCLICK;
8677     ret = LISTVIEW_HitTestItem(hwnd, &lvHitTestInfo, TRUE);
8678     if (ret != -1)
8679     {
8680         nmlv.iItem = lvHitTestInfo.iItem;
8681         nmlv.iSubItem = lvHitTestInfo.iSubItem;
8682     }
8683     else
8684     {
8685         nmlv.iItem = -1;
8686         nmlv.iSubItem = 0;
8687     }
8688     nmlv.ptAction.x = wPosX;
8689     nmlv.ptAction.y = wPosY;
8690     ListView_LVNotify(GetParent(hwnd), nCtrlId, &nmlv);
8691
8692     pt.x = wPosX;
8693     pt.y = wPosY;
8694
8695     /* set button flag */
8696     infoPtr->bRButtonDown = FALSE;
8697     
8698     /* Change to screen coordinate for WM_CONTEXTMENU */
8699     ClientToScreen(hwnd, &pt);
8700     
8701     /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
8702     SendMessageA( hwnd, WM_CONTEXTMENU, (WPARAM) hwnd, MAKELPARAM(pt.x, pt.y));
8703   }
8704   
8705   return 0;
8706 }
8707
8708 /***
8709  * DESCRIPTION:
8710  * Sets the focus.  
8711  * 
8712  * PARAMETER(S):
8713  * [I] HWND : window handle
8714  * [I] HWND : window handle of previously focused window
8715  *
8716  * RETURN:
8717  * Zero
8718  */
8719 static LRESULT LISTVIEW_SetFocus(HWND hwnd, HWND hwndLoseFocus)
8720 {
8721   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8722   INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
8723   NMHDR nmh;
8724
8725   TRACE("(hwnd=%x, hwndLoseFocus=%x)\n", hwnd, hwndLoseFocus);
8726
8727   /* send NM_SETFOCUS notification */
8728   nmh.hwndFrom = hwnd;
8729   nmh.idFrom = nCtrlId;
8730   nmh.code = NM_SETFOCUS;
8731   ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
8732
8733   /* set window focus flag */
8734   infoPtr->bFocus = TRUE;
8735
8736   UpdateWindow(hwnd);
8737
8738   return 0;
8739 }
8740
8741 /***
8742  * DESCRIPTION:
8743  * Sets the font.  
8744  * 
8745  * PARAMETER(S):
8746  * [I] HWND : window handle
8747  * [I] HFONT : font handle
8748  * [I] WORD : redraw flag
8749  *
8750  * RETURN:
8751  * Zero
8752  */
8753 static LRESULT LISTVIEW_SetFont(HWND hwnd, HFONT hFont, WORD fRedraw)
8754 {
8755   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8756   UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8757
8758   TRACE("(hwnd=%x,hfont=%x,redraw=%hu)\n", hwnd, hFont, fRedraw);
8759
8760   if (hFont == 0)
8761   {
8762     infoPtr->hFont = infoPtr->hDefaultFont;
8763   }
8764   else
8765   {
8766     infoPtr->hFont = hFont;
8767   }
8768
8769   if (uView == LVS_REPORT)
8770   {
8771     /* set header font */
8772     SendMessageA(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, 
8773                    MAKELPARAM(fRedraw, 0));
8774   }
8775
8776   /* invalidate listview control client area */
8777   InvalidateRect(hwnd, NULL, TRUE);
8778   
8779   if (fRedraw != FALSE)
8780   {
8781     UpdateWindow(hwnd);
8782   }
8783
8784   return 0;
8785 }
8786
8787 /***
8788  * DESCRIPTION:
8789  * Message handling for WM_SETREDRAW.  
8790  * For the Listview, it invalidates the entire window (the doc specifies otherwise)
8791  * 
8792  * PARAMETER(S):
8793  * [I] HWND   : window handle
8794  * [I] bRedraw: state of redraw flag
8795  *
8796  * RETURN:
8797  * DefWinProc return value
8798  */
8799 static LRESULT LISTVIEW_SetRedraw(HWND hwnd, BOOL bRedraw)
8800 {
8801     LRESULT lResult;
8802     lResult = DefWindowProcA(hwnd, WM_SETREDRAW, bRedraw, 0);
8803     if(bRedraw)
8804     {
8805         RedrawWindow(hwnd, NULL, 0, 
8806             RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ALLCHILDREN | RDW_ERASENOW);
8807     }
8808     return lResult;
8809 }
8810
8811 /***
8812  * DESCRIPTION:
8813  * Resizes the listview control. This function processes WM_SIZE
8814  * messages.  At this time, the width and height are not used.
8815  * 
8816  * PARAMETER(S):
8817  * [I] HWND : window handle
8818  * [I] WORD : new width
8819  * [I] WORD : new height
8820  *
8821  * RETURN:
8822  * Zero
8823  */
8824 static LRESULT LISTVIEW_Size(HWND hwnd, int Width, int Height)
8825 {
8826   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE); 
8827   UINT uView = lStyle & LVS_TYPEMASK;
8828
8829   TRACE("(hwnd=%x, width=%d, height=%d)\n",hwnd, Width, Height);
8830
8831   LISTVIEW_UpdateSize(hwnd);
8832
8833   if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
8834   {
8835     if (lStyle & LVS_ALIGNLEFT)
8836     {
8837       LISTVIEW_AlignLeft(hwnd);
8838     }
8839     else
8840     {
8841       LISTVIEW_AlignTop(hwnd);
8842     }
8843   }
8844
8845   LISTVIEW_UpdateScroll(hwnd);
8846   
8847   /* invalidate client area + erase background */
8848   InvalidateRect(hwnd, NULL, TRUE);
8849
8850   return 0;
8851 }
8852
8853 /***
8854  * DESCRIPTION:
8855  * Sets the size information.
8856  * 
8857  * PARAMETER(S):
8858  * [I] HWND : window handle
8859  *
8860  * RETURN:
8861  * Zero
8862  */
8863 static VOID LISTVIEW_UpdateSize(HWND hwnd)
8864 {
8865   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8866   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
8867   UINT uView = lStyle & LVS_TYPEMASK;
8868   RECT rcList;
8869   
8870   GetClientRect(hwnd, &rcList);
8871   infoPtr->rcList.left = 0;
8872   infoPtr->rcList.right = max(rcList.right - rcList.left, 1);
8873   infoPtr->rcList.top = 0;
8874   infoPtr->rcList.bottom = max(rcList.bottom - rcList.top, 1);
8875      
8876   if (uView == LVS_LIST)
8877   {
8878     if (lStyle & WS_HSCROLL)
8879     {
8880       INT nHScrollHeight = GetSystemMetrics(SM_CYHSCROLL);
8881       if (infoPtr->rcList.bottom > nHScrollHeight)
8882       { 
8883         infoPtr->rcList.bottom -= nHScrollHeight;
8884       }
8885     }
8886   }
8887   else if (uView == LVS_REPORT)
8888   {
8889     HDLAYOUT hl;
8890     WINDOWPOS wp;
8891
8892     hl.prc = &rcList;
8893     hl.pwpos = &wp;
8894     Header_Layout(infoPtr->hwndHeader, &hl);
8895
8896     SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8897
8898     if (!(LVS_NOCOLUMNHEADER & lStyle))
8899     {
8900       infoPtr->rcList.top = max(wp.cy, 0);
8901     }
8902   }
8903 }
8904
8905 /***
8906  * DESCRIPTION:
8907  * Processes WM_STYLECHANGED messages. 
8908  * 
8909  * PARAMETER(S):
8910  * [I] HWND : window handle
8911  * [I] WPARAM : window style type (normal or extended)
8912  * [I] LPSTYLESTRUCT : window style information
8913  *
8914  * RETURN:
8915  * Zero
8916  */
8917 static INT LISTVIEW_StyleChanged(HWND hwnd, WPARAM wStyleType, 
8918                                  LPSTYLESTRUCT lpss)
8919 {
8920   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8921   UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
8922   UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
8923   RECT rcList = infoPtr->rcList;
8924
8925   TRACE("(hwnd=%x, styletype=%x, stylestruct=%p)\n", 
8926         hwnd, wStyleType, lpss);
8927
8928   if (wStyleType == GWL_STYLE)
8929   {
8930     if (uOldView == LVS_REPORT)
8931     {
8932       ShowWindow(infoPtr->hwndHeader, SW_HIDE);
8933     }
8934  
8935     if ((lpss->styleOld & WS_HSCROLL) != 0)
8936     {
8937        ShowScrollBar(hwnd, SB_HORZ, FALSE);
8938     }
8939  
8940     if ((lpss->styleOld & WS_VSCROLL) != 0)
8941     {
8942        ShowScrollBar(hwnd, SB_VERT, FALSE);
8943     }
8944  
8945     if (uNewView == LVS_ICON)
8946     {
8947       infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
8948       infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
8949       infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8950       infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
8951       if (lpss->styleNew & LVS_ALIGNLEFT)
8952       {
8953         LISTVIEW_AlignLeft(hwnd);
8954       }
8955       else
8956       {
8957         LISTVIEW_AlignTop(hwnd);
8958       }
8959     }
8960     else if (uNewView == LVS_REPORT)
8961     {
8962       HDLAYOUT hl;
8963       WINDOWPOS wp;
8964
8965       hl.prc = &rcList;
8966       hl.pwpos = &wp;
8967       Header_Layout(infoPtr->hwndHeader, &hl);
8968       SetWindowPos(infoPtr->hwndHeader, hwnd, wp.x, wp.y, wp.cx, wp.cy, 
8969                    wp.flags);
8970       if (!(LVS_NOCOLUMNHEADER & lpss->styleNew))
8971         ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
8972       
8973       infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8974       infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8975       infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8976       infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
8977     }
8978     else if (uNewView == LVS_LIST)
8979     {
8980       infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8981       infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8982       infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8983       infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
8984     }
8985     else
8986     {
8987       infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8988       infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8989       infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8990       infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
8991       if (lpss->styleNew & LVS_ALIGNLEFT)
8992       {
8993         LISTVIEW_AlignLeft(hwnd);
8994       }
8995       else
8996       {
8997         LISTVIEW_AlignTop(hwnd);
8998       }
8999     }
9000
9001     /* update the size of the client area */
9002     LISTVIEW_UpdateSize(hwnd);
9003
9004     /* add scrollbars if needed */
9005     LISTVIEW_UpdateScroll(hwnd);
9006     
9007     /* invalidate client area + erase background */
9008     InvalidateRect(hwnd, NULL, TRUE);
9009
9010     /* print the list of unsupported window styles */
9011     LISTVIEW_UnsupportedStyles(lpss->styleNew);
9012   }
9013
9014   /* If they change the view and we have an active edit control 
9015      we will need to kill the control since the redraw will
9016      misplace the edit control.
9017    */
9018   if (infoPtr->hwndEdit &&
9019         ((uNewView & (LVS_ICON|LVS_LIST|LVS_SMALLICON)) !=
9020         ((LVS_ICON|LVS_LIST|LVS_SMALLICON) & uOldView)))
9021   {
9022      SendMessageA(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
9023   }
9024
9025   return 0;
9026 }
9027
9028 /***
9029  * DESCRIPTION:
9030  * Window procedure of the listview control.
9031  *
9032  */
9033 static LRESULT WINAPI LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam,
9034                                    LPARAM lParam)
9035 {
9036   TRACE("hwnd=%x uMsg=%x wParam=%x lParam=%lx\n", hwnd, uMsg, wParam, lParam);
9037   if (!GetWindowLongA(hwnd, 0) && (uMsg != WM_NCCREATE))
9038     return DefWindowProcA( hwnd, uMsg, wParam, lParam );
9039   switch (uMsg)
9040   {
9041   case LVM_APPROXIMATEVIEWRECT: 
9042     return LISTVIEW_ApproximateViewRect(hwnd, (INT)wParam, 
9043                                         LOWORD(lParam), HIWORD(lParam));
9044   case LVM_ARRANGE: 
9045     return LISTVIEW_Arrange(hwnd, (INT)wParam);
9046
9047 /* case LVM_CREATEDRAGIMAGE: */
9048
9049   case LVM_DELETEALLITEMS:
9050     return LISTVIEW_DeleteAllItems(hwnd);
9051
9052   case LVM_DELETECOLUMN:
9053     return LISTVIEW_DeleteColumn(hwnd, (INT)wParam);
9054
9055   case LVM_DELETEITEM:
9056     return LISTVIEW_DeleteItem(hwnd, (INT)wParam);
9057
9058   case LVM_EDITLABELW:
9059   case LVM_EDITLABELA:
9060     return LISTVIEW_EditLabelA(hwnd, (INT)wParam);
9061
9062   case LVM_ENSUREVISIBLE:
9063     return LISTVIEW_EnsureVisible(hwnd, (INT)wParam, (BOOL)lParam);
9064
9065   case LVM_FINDITEMA:
9066     return LISTVIEW_FindItem(hwnd, (INT)wParam, (LPLVFINDINFO)lParam);
9067
9068   case LVM_GETBKCOLOR:
9069     return LISTVIEW_GetBkColor(hwnd);
9070
9071 /*      case LVM_GETBKIMAGE: */
9072
9073   case LVM_GETCALLBACKMASK:
9074     return LISTVIEW_GetCallbackMask(hwnd);
9075
9076   case LVM_GETCOLUMNA:
9077     return LISTVIEW_GetColumnA(hwnd, (INT)wParam, (LPLVCOLUMNA)lParam);
9078
9079 /*      case LVM_GETCOLUMNW: */
9080
9081   case LVM_GETCOLUMNORDERARRAY:
9082     return LISTVIEW_GetColumnOrderArray(hwnd, (INT)wParam, (LPINT)lParam);
9083
9084   case LVM_GETCOLUMNWIDTH:
9085     return LISTVIEW_GetColumnWidth(hwnd, (INT)wParam);
9086
9087   case LVM_GETCOUNTPERPAGE:
9088     return LISTVIEW_GetCountPerPage(hwnd);
9089
9090   case LVM_GETEDITCONTROL:
9091     return LISTVIEW_GetEditControl(hwnd);
9092
9093   case LVM_GETEXTENDEDLISTVIEWSTYLE:
9094     return LISTVIEW_GetExtendedListViewStyle(hwnd);
9095
9096   case LVM_GETHEADER:
9097     return LISTVIEW_GetHeader(hwnd);
9098
9099 /*      case LVM_GETHOTCURSOR: */
9100
9101   case LVM_GETHOTITEM:
9102     return LISTVIEW_GetHotItem(hwnd);
9103
9104   case LVM_GETHOVERTIME:
9105     return LISTVIEW_GetHoverTime(hwnd);
9106
9107   case LVM_GETIMAGELIST:
9108     return LISTVIEW_GetImageList(hwnd, (INT)wParam);
9109
9110 /*      case LVM_GETISEARCHSTRING: */
9111
9112   case LVM_GETITEMA:
9113     return LISTVIEW_GetItemA(hwnd, (LPLVITEMA)lParam, FALSE);
9114
9115 /*      case LVM_GETITEMW: */
9116
9117   case LVM_GETITEMCOUNT:
9118     return LISTVIEW_GetItemCount(hwnd);
9119
9120   case LVM_GETITEMPOSITION:
9121     return LISTVIEW_GetItemPosition(hwnd, (INT)wParam, (LPPOINT)lParam);
9122
9123   case LVM_GETITEMRECT: 
9124     return LISTVIEW_GetItemRect(hwnd, (INT)wParam, (LPRECT)lParam);
9125
9126   case LVM_GETITEMSPACING: 
9127     return LISTVIEW_GetItemSpacing(hwnd, (BOOL)wParam);
9128
9129   case LVM_GETITEMSTATE: 
9130     return LISTVIEW_GetItemState(hwnd, (INT)wParam, (UINT)lParam);
9131     
9132   case LVM_GETITEMTEXTA:
9133     LISTVIEW_GetItemTextA(hwnd, (INT)wParam, (LPLVITEMA)lParam);
9134     break;
9135
9136 /*      case LVM_GETITEMTEXTW: */
9137
9138   case LVM_GETNEXTITEM:
9139     return LISTVIEW_GetNextItem(hwnd, (INT)wParam, LOWORD(lParam));
9140
9141 /*      case LVM_GETNUMBEROFWORKAREAS: */
9142
9143   case LVM_GETORIGIN:
9144     return LISTVIEW_GetOrigin(hwnd, (LPPOINT)lParam);
9145
9146   case LVM_GETSELECTEDCOUNT:
9147     return LISTVIEW_GetSelectedCount(hwnd);
9148
9149   case LVM_GETSELECTIONMARK: 
9150     return LISTVIEW_GetSelectionMark(hwnd);
9151
9152   case LVM_GETSTRINGWIDTHA:
9153     return LISTVIEW_GetStringWidthA (hwnd, (LPCSTR)lParam);
9154
9155 /*      case LVM_GETSTRINGWIDTHW: */
9156 /*      case LVM_GETSUBITEMRECT: */
9157
9158   case LVM_GETTEXTBKCOLOR:
9159     return LISTVIEW_GetTextBkColor(hwnd);
9160
9161   case LVM_GETTEXTCOLOR:
9162     return LISTVIEW_GetTextColor(hwnd);
9163
9164 /*      case LVM_GETTOOLTIPS: */
9165
9166   case LVM_GETTOPINDEX:
9167     return LISTVIEW_GetTopIndex(hwnd);
9168
9169 /*      case LVM_GETUNICODEFORMAT: */
9170
9171   case LVM_GETVIEWRECT:
9172     return LISTVIEW_GetViewRect(hwnd, (LPRECT)lParam);
9173
9174 /*      case LVM_GETWORKAREAS: */
9175
9176   case LVM_HITTEST:
9177     return LISTVIEW_HitTest(hwnd, (LPLVHITTESTINFO)lParam);
9178
9179   case LVM_INSERTCOLUMNA:
9180     return LISTVIEW_InsertColumnA(hwnd, (INT)wParam, (LPLVCOLUMNA)lParam);
9181
9182   case LVM_INSERTCOLUMNW:
9183     return LISTVIEW_InsertColumnW(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam);
9184
9185   case LVM_INSERTITEMA:
9186     return LISTVIEW_InsertItemA(hwnd, (LPLVITEMA)lParam);
9187
9188   case LVM_INSERTITEMW:
9189     return LISTVIEW_InsertItemW(hwnd, (LPLVITEMW)lParam);
9190
9191   case LVM_REDRAWITEMS:
9192     return LISTVIEW_RedrawItems(hwnd, (INT)wParam, (INT)lParam);
9193
9194 /*   case LVM_SCROLL:  */
9195 /*     return LISTVIEW_Scroll(hwnd, (INT)wParam, (INT)lParam); */
9196
9197   case LVM_SETBKCOLOR:
9198     return LISTVIEW_SetBkColor(hwnd, (COLORREF)lParam);
9199
9200 /*      case LVM_SETBKIMAGE: */
9201
9202   case LVM_SETCALLBACKMASK:
9203     return LISTVIEW_SetCallbackMask(hwnd, (UINT)wParam);
9204
9205   case LVM_SETCOLUMNA:
9206     return LISTVIEW_SetColumnA(hwnd, (INT)wParam, (LPLVCOLUMNA)lParam);
9207
9208   case LVM_SETCOLUMNW:
9209     FIXME("Unimplemented msg LVM_SETCOLUMNW\n");
9210     return 0;
9211
9212   case LVM_SETCOLUMNORDERARRAY:
9213     return LISTVIEW_SetColumnOrderArray(hwnd, (INT)wParam, (LPINT)lParam);
9214
9215   case LVM_SETCOLUMNWIDTH:
9216     return LISTVIEW_SetColumnWidth(hwnd, (INT)wParam, SLOWORD(lParam));
9217
9218   case LVM_SETEXTENDEDLISTVIEWSTYLE:
9219     return LISTVIEW_SetExtendedListViewStyle(hwnd, (DWORD)wParam, (DWORD)lParam);
9220
9221 /*      case LVM_SETHOTCURSOR: */
9222
9223   case LVM_SETHOTITEM:
9224     return LISTVIEW_SetHotItem(hwnd, (INT)wParam);
9225
9226   case LVM_SETHOVERTIME:
9227     return LISTVIEW_SetHoverTime(hwnd, (DWORD)wParam);
9228
9229 /*      case LVM_SETICONSPACING: */
9230         
9231   case LVM_SETIMAGELIST:
9232     return LISTVIEW_SetImageList(hwnd, (INT)wParam, (HIMAGELIST)lParam);
9233
9234   case LVM_SETITEMA:
9235     return LISTVIEW_SetItemA(hwnd, (LPLVITEMA)lParam);
9236
9237 /*      case LVM_SETITEMW: */
9238
9239   case LVM_SETITEMCOUNT: 
9240     return LISTVIEW_SetItemCount(hwnd, (INT)wParam, (DWORD)lParam);
9241     
9242   case LVM_SETITEMPOSITION:
9243     return LISTVIEW_SetItemPosition(hwnd, (INT)wParam, (INT)LOWORD(lParam),
9244                                     (INT)HIWORD(lParam));
9245
9246   case LVM_SETITEMPOSITION32:
9247     return LISTVIEW_SetItemPosition(hwnd, (INT)wParam, ((POINT*)lParam)->x,
9248                                     ((POINT*)lParam)->y);
9249
9250   case LVM_SETITEMSTATE:
9251     return LISTVIEW_SetItemState(hwnd, (INT)wParam, (LPLVITEMA)lParam);
9252
9253   case LVM_SETITEMTEXTA:
9254     return LISTVIEW_SetItemTextA(hwnd, (INT)wParam, (LPLVITEMA)lParam);
9255
9256 /*      case LVM_SETITEMTEXTW: */
9257
9258   case LVM_SETSELECTIONMARK:
9259     return LISTVIEW_SetSelectionMark(hwnd, (INT)lParam);
9260
9261   case LVM_SETTEXTBKCOLOR:
9262     return LISTVIEW_SetTextBkColor(hwnd, (COLORREF)lParam);
9263
9264   case LVM_SETTEXTCOLOR:
9265     return LISTVIEW_SetTextColor(hwnd, (COLORREF)lParam);
9266
9267 /*      case LVM_SETTOOLTIPS: */
9268 /*      case LVM_SETUNICODEFORMAT: */
9269 /*      case LVM_SETWORKAREAS: */
9270
9271   case LVM_SORTITEMS:
9272     return LISTVIEW_SortItems(hwnd, wParam, lParam);
9273
9274 /*      case LVM_SUBITEMHITTEST: */
9275
9276   case LVM_UPDATE: 
9277     return LISTVIEW_Update(hwnd, (INT)wParam);
9278
9279   case WM_CHAR:
9280     return LISTVIEW_ProcessLetterKeys( hwnd, wParam, lParam );
9281
9282   case WM_COMMAND:
9283     return LISTVIEW_Command(hwnd, wParam, lParam);
9284
9285   case WM_CREATE:
9286     return LISTVIEW_Create(hwnd, wParam, lParam);
9287     
9288   case WM_ERASEBKGND:
9289     return LISTVIEW_EraseBackground(hwnd, wParam, lParam);
9290
9291   case WM_GETDLGCODE:
9292     return DLGC_WANTCHARS | DLGC_WANTARROWS;
9293
9294   case WM_GETFONT:
9295     return LISTVIEW_GetFont(hwnd);
9296
9297   case WM_HSCROLL:
9298     return LISTVIEW_HScroll(hwnd, (INT)LOWORD(wParam), 
9299                             (INT)HIWORD(wParam), (HWND)lParam);
9300
9301   case WM_KEYDOWN:
9302     return LISTVIEW_KeyDown(hwnd, (INT)wParam, (LONG)lParam);
9303
9304   case WM_KILLFOCUS:
9305     return LISTVIEW_KillFocus(hwnd);
9306
9307   case WM_LBUTTONDBLCLK:
9308     return LISTVIEW_LButtonDblClk(hwnd, (WORD)wParam, LOWORD(lParam), 
9309                                 HIWORD(lParam));
9310     
9311   case WM_LBUTTONDOWN:
9312     return LISTVIEW_LButtonDown(hwnd, (WORD)wParam, LOWORD(lParam), 
9313                                 HIWORD(lParam));
9314   case WM_LBUTTONUP:
9315     return LISTVIEW_LButtonUp(hwnd, (WORD)wParam, LOWORD(lParam), 
9316                               HIWORD(lParam));
9317   case WM_MOUSEMOVE:
9318     return LISTVIEW_MouseMove (hwnd, wParam, lParam);
9319
9320   case WM_MOUSEHOVER:
9321     return LISTVIEW_MouseHover(hwnd, wParam, lParam);
9322
9323   case WM_NCCREATE:
9324     return LISTVIEW_NCCreate(hwnd, wParam, lParam);
9325
9326   case WM_NCDESTROY:
9327     return LISTVIEW_NCDestroy(hwnd);
9328
9329   case WM_NOTIFY:
9330     return LISTVIEW_Notify(hwnd, (INT)wParam, (LPNMHDR)lParam);
9331
9332   case WM_NOTIFYFORMAT:
9333     return LISTVIEW_NotifyFormat(hwnd, (HWND)wParam, (INT)lParam);
9334
9335   case WM_PAINT: 
9336     return LISTVIEW_Paint(hwnd, (HDC)wParam); 
9337
9338   case WM_RBUTTONDBLCLK:
9339     return LISTVIEW_RButtonDblClk(hwnd, (WORD)wParam, LOWORD(lParam), 
9340                                   HIWORD(lParam));
9341
9342   case WM_RBUTTONDOWN:
9343     return LISTVIEW_RButtonDown(hwnd, (WORD)wParam, LOWORD(lParam), 
9344                                 HIWORD(lParam));
9345
9346   case WM_RBUTTONUP:
9347     return LISTVIEW_RButtonUp(hwnd, (WORD)wParam, LOWORD(lParam), 
9348                               HIWORD(lParam));
9349
9350   case WM_SETFOCUS:
9351     return LISTVIEW_SetFocus(hwnd, (HWND)wParam);
9352
9353   case WM_SETFONT:
9354     return LISTVIEW_SetFont(hwnd, (HFONT)wParam, (WORD)lParam);
9355
9356   case WM_SETREDRAW: 
9357     return LISTVIEW_SetRedraw(hwnd, (BOOL)wParam);
9358
9359   case WM_SIZE:
9360     return LISTVIEW_Size(hwnd, (int)SLOWORD(lParam), (int)SHIWORD(lParam));
9361
9362   case WM_STYLECHANGED:
9363     return LISTVIEW_StyleChanged(hwnd, wParam, (LPSTYLESTRUCT)lParam);
9364
9365 /*      case WM_TIMER: */
9366
9367   case WM_VSCROLL:
9368     return LISTVIEW_VScroll(hwnd, (INT)LOWORD(wParam), 
9369                             (INT)HIWORD(wParam), (HWND)lParam);
9370
9371   case WM_MOUSEWHEEL:
9372       if (wParam & (MK_SHIFT | MK_CONTROL))
9373           return DefWindowProcA( hwnd, uMsg, wParam, lParam );
9374       return LISTVIEW_MouseWheel(hwnd, (short int)HIWORD(wParam));/*    case WM_WINDOWPOSCHANGED: */
9375
9376 /*      case WM_WININICHANGE: */
9377
9378   default:
9379     if (uMsg >= WM_USER)
9380     {
9381       ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, 
9382           lParam);
9383     }
9384
9385     /* call default window procedure */
9386     return DefWindowProcA(hwnd, uMsg, wParam, lParam);
9387   }
9388
9389   return 0;
9390 }
9391
9392 /***
9393  * DESCRIPTION:
9394  * Registers the window class.
9395  * 
9396  * PARAMETER(S):
9397  * None
9398  *
9399  * RETURN:
9400  * None
9401  */
9402 VOID LISTVIEW_Register(void)
9403 {
9404   WNDCLASSA wndClass;
9405
9406     ZeroMemory(&wndClass, sizeof(WNDCLASSA));
9407     wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
9408     wndClass.lpfnWndProc = (WNDPROC)LISTVIEW_WindowProc;
9409     wndClass.cbClsExtra = 0;
9410     wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
9411     wndClass.hCursor = LoadCursorA(0, IDC_ARROWA);
9412     wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
9413     wndClass.lpszClassName = WC_LISTVIEWA;
9414     RegisterClassA(&wndClass);
9415 }
9416
9417 /***
9418  * DESCRIPTION:
9419  * Unregisters the window class.
9420  * 
9421  * PARAMETER(S):
9422  * None
9423  *
9424  * RETURN:
9425  * None
9426  */
9427 VOID LISTVIEW_Unregister(void)
9428 {
9429     UnregisterClassA(WC_LISTVIEWA, (HINSTANCE)NULL);
9430 }
9431
9432 /***
9433  * DESCRIPTION:
9434  * Handle any WM_COMMAND messages
9435  * 
9436  * PARAMETER(S):
9437  *
9438  * RETURN:
9439  */
9440 static LRESULT LISTVIEW_Command(HWND hwnd, WPARAM wParam, LPARAM lParam)
9441 {
9442     switch (HIWORD(wParam))
9443     {
9444         case EN_UPDATE:
9445         {
9446             /* 
9447              * Adjust the edit window size 
9448              */
9449             char buffer[1024];
9450             LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
9451             HDC           hdc      = GetDC(infoPtr->hwndEdit);
9452             HFONT         hFont, hOldFont = 0;
9453             RECT          rect;
9454             SIZE          sz;
9455             int           len;
9456
9457             len = GetWindowTextA(infoPtr->hwndEdit, buffer, 1023);
9458             GetWindowRect(infoPtr->hwndEdit, &rect);
9459
9460             /* Select font to get the right dimension of the string */
9461             hFont = SendMessageA(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
9462             if(hFont != 0)
9463             {
9464                 hOldFont = SelectObject(hdc, hFont);
9465             }
9466
9467             if (GetTextExtentPoint32A(hdc, buffer, strlen(buffer), &sz))
9468             {
9469                 TEXTMETRICA textMetric;
9470
9471                 /* Add Extra spacing for the next character */
9472                 GetTextMetricsA(hdc, &textMetric);
9473                 sz.cx += (textMetric.tmMaxCharWidth * 2);
9474
9475                 SetWindowPos ( 
9476                     infoPtr->hwndEdit,
9477                     HWND_TOP, 
9478                     0, 
9479                     0,
9480                     sz.cx,
9481                     rect.bottom - rect.top,
9482                     SWP_DRAWFRAME|SWP_NOMOVE);
9483             }
9484             if(hFont != 0)
9485             {
9486                 SelectObject(hdc, hOldFont);
9487             }
9488
9489             ReleaseDC(hwnd, hdc);
9490
9491             break;
9492         }
9493
9494         default:
9495           return SendMessageA (GetParent (hwnd), WM_COMMAND, wParam, lParam);
9496     }
9497
9498     return 0;
9499 }
9500
9501
9502 /***
9503  * DESCRIPTION:
9504  * Subclassed edit control windproc function
9505  *
9506  * PARAMETER(S):
9507  *
9508  * RETURN:
9509  */
9510 LRESULT CALLBACK EditLblWndProc(HWND hwnd, UINT uMsg, 
9511         WPARAM wParam, LPARAM lParam)
9512 {
9513     BOOL cancel = FALSE;
9514     LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(GetParent(hwnd), 0);
9515     EDITLABEL_ITEM *einfo = infoPtr->pedititem;
9516     static BOOL bIgnoreKillFocus = FALSE;
9517     switch (uMsg)
9518     {
9519         case WM_GETDLGCODE:
9520           return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
9521                         
9522         case WM_KILLFOCUS:
9523             if(bIgnoreKillFocus)
9524             {
9525                 return TRUE;
9526             }
9527             break;
9528
9529         case WM_DESTROY:
9530         {
9531             WNDPROC editProc = einfo->EditWndProc;
9532             SetWindowLongA(hwnd, GWL_WNDPROC, (LONG)editProc);
9533             COMCTL32_Free(einfo);
9534             infoPtr->pedititem = NULL;
9535             return CallWindowProcA(editProc, hwnd, uMsg, wParam, lParam);
9536         }
9537
9538         case WM_KEYDOWN:
9539             if (VK_ESCAPE == (INT)wParam)
9540             {
9541                 cancel = TRUE;
9542                 break;
9543
9544             }
9545             else if (VK_RETURN == (INT)wParam)
9546                 break;
9547
9548         default:
9549             return CallWindowProcA(einfo->EditWndProc, hwnd, 
9550                         uMsg, wParam, lParam);
9551     }
9552
9553     if (einfo->EditLblCb)
9554     {
9555         char *buffer  = NULL;
9556         
9557
9558         if (!cancel)
9559         {
9560             int len = 1 + GetWindowTextLengthA(hwnd);
9561
9562             if (len > 1)
9563             {
9564                 if (NULL != (buffer = (char *)COMCTL32_Alloc(len*sizeof(char))))
9565                 {
9566                     GetWindowTextA(hwnd, buffer, len);
9567                 }
9568             }
9569         }
9570         /* Processing LVN_ENDLABELEDIT message could kill the focus       */
9571         /* eg. Using a messagebox                                         */
9572         bIgnoreKillFocus = TRUE;
9573         einfo->EditLblCb(GetParent(hwnd), buffer, einfo->param);
9574
9575         if (buffer)
9576             COMCTL32_Free(buffer);
9577
9578         einfo->EditLblCb = NULL;
9579         bIgnoreKillFocus = FALSE;
9580     }
9581
9582     SendMessageA(hwnd, WM_CLOSE, 0, 0);
9583     return TRUE;
9584 }
9585
9586
9587 /***
9588  * DESCRIPTION:
9589  * Creates a subclassed edit cotrol
9590  *
9591  * PARAMETER(S):
9592  *
9593  * RETURN:
9594  */
9595 HWND CreateEditLabel(LPCSTR text, DWORD style, INT x, INT y, 
9596         INT width, INT height, HWND parent, HINSTANCE hinst, 
9597         EditlblCallback EditLblCb, DWORD param)
9598 {
9599     HWND hedit;
9600     SIZE sz;
9601     HDC hdc;
9602     HDC hOldFont=0;
9603     TEXTMETRICA textMetric;
9604     LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(parent, 0);
9605
9606     if (NULL == (infoPtr->pedititem = COMCTL32_Alloc(sizeof(EDITLABEL_ITEM))))
9607         return 0;
9608
9609     style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|WS_BORDER;
9610     hdc = GetDC(parent);
9611
9612     /* Select the font to get appropriate metric dimensions */
9613     if(infoPtr->hFont != 0)
9614     {
9615         hOldFont = SelectObject(hdc, infoPtr->hFont);
9616     }
9617
9618     /*Get String Lenght in pixels */
9619     GetTextExtentPoint32A(hdc, text, strlen(text), &sz);
9620
9621     /*Add Extra spacing for the next character */
9622     GetTextMetricsA(hdc, &textMetric);
9623     sz.cx += (textMetric.tmMaxCharWidth * 2);
9624
9625     if(infoPtr->hFont != 0)
9626     {
9627         SelectObject(hdc, hOldFont);
9628     }
9629
9630     ReleaseDC(parent, hdc);
9631     if (!(hedit = CreateWindowA("Edit", text, style, x, y, sz.cx, height, 
9632                     parent, 0, hinst, 0)))
9633     {
9634         COMCTL32_Free(infoPtr->pedititem);
9635         return 0;
9636     }
9637
9638     infoPtr->pedititem->param = param;
9639     infoPtr->pedititem->EditLblCb = EditLblCb;
9640     infoPtr->pedititem->EditWndProc = (WNDPROC)SetWindowLongA(hedit, 
9641           GWL_WNDPROC, (LONG) EditLblWndProc);
9642
9643     SendMessageA(hedit, WM_SETFONT, infoPtr->hFont, FALSE);
9644
9645     return hedit;
9646 }