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