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