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