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