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