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