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