Fixed some issues found by winapi_check.
[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 for non OWNERDATA
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_SetBkImage : not implemented
35  *   LISTVIEW_GetColumnOrderArray : simple hack only
36  *   LISTVIEW_SetColumnOrderArray : simple hack only
37  *   LISTVIEW_Arrange : empty stub
38  *   LISTVIEW_ApproximateViewRect : incomplete
39  *   LISTVIEW_Scroll : not implemented 
40  *   LISTVIEW_Update : not completed
41  */
42
43 #include <ctype.h>
44 #include <string.h>
45 #include <stdlib.h>
46
47 #include "winbase.h"
48 #include "heap.h"
49 #include "commctrl.h"
50 #include "debugtools.h"
51
52 DEFAULT_DEBUG_CHANNEL(listview);
53
54 /* Some definitions for inline edit control */    
55 typedef BOOL (*EditlblCallback)(HWND, LPSTR, DWORD);
56
57 typedef struct tagEDITLABEL_ITEM
58 {
59     WNDPROC EditWndProc;
60     DWORD param;
61     EditlblCallback EditLblCb;
62 } EDITLABEL_ITEM;
63
64 typedef struct tagLISTVIEW_SUBITEM
65 {
66     LPSTR pszText;
67     INT iImage;
68     INT iSubItem;
69
70 } LISTVIEW_SUBITEM;
71
72 typedef struct tagLISTVIEW_ITEM
73 {
74   UINT state;
75   LPSTR pszText;
76   INT iImage;
77   LPARAM lParam;
78   INT iIndent;
79   POINT ptPosition;
80
81 } LISTVIEW_ITEM;
82
83 typedef struct tagLISTVIEW_SELECTION
84 {
85   DWORD lower;
86   DWORD upper;
87 } LISTVIEW_SELECTION;
88
89 typedef struct tagLISTVIEW_INFO
90 {
91     COLORREF clrBk;
92     COLORREF clrText;
93     COLORREF clrTextBk;
94     HIMAGELIST himlNormal;
95     HIMAGELIST himlSmall;
96     HIMAGELIST himlState;
97     BOOL bLButtonDown;
98     BOOL bRButtonDown;
99     INT nFocusedItem;
100     HDPA hdpaSelectionRanges;
101     INT nItemHeight;
102     INT nItemWidth;
103     INT nSelectionMark;
104     INT nHotItem;
105     SHORT notifyFormat;
106     RECT rcList;
107     RECT rcView;
108     SIZE iconSize;
109     SIZE iconSpacing;
110     UINT uCallbackMask;
111     HWND hwndHeader;
112     HFONT hDefaultFont;
113     HFONT hFont;
114     BOOL bFocus;
115     DWORD dwExStyle;    /* extended listview style */
116     HDPA hdpaItems;
117     PFNLVCOMPARE pfnCompare;
118     LPARAM lParamSort;
119     HWND hwndEdit;
120     INT nEditLabelItem;
121     EDITLABEL_ITEM *pedititem;
122     DWORD dwHoverTime;
123     INT nColumnCount; /* the number of columns in this control */
124
125     WPARAM charCode; /* Added */
126     CHAR szSearchParam[ MAX_PATH ]; /* Added */
127     DWORD timeSinceLastKeyPress; /* Added */
128     INT nSearchParamLength; /* Added */
129 } LISTVIEW_INFO;
130
131 /*
132  * constants 
133  */
134
135 /* maximum size of a label */
136 #define DISP_TEXT_SIZE 512
137
138 /* padding for items in list and small icon display modes */
139 #define WIDTH_PADDING 12
140
141 /* padding for items in list, report and small icon display modes */
142 #define HEIGHT_PADDING 1
143
144 /* offset of items in report display mode */
145 #define REPORT_MARGINX 2
146
147 /* padding for icon in large icon display mode */
148 #define ICON_TOP_PADDING 2
149 #define ICON_BOTTOM_PADDING 2
150
151 /* padding for label in large icon display mode */
152 #define LABEL_VERT_OFFSET 2
153
154 /* default label width for items in list and small icon display modes */
155 #define DEFAULT_LABEL_WIDTH 40
156
157 /* default column width for items in list display mode */
158 #define DEFAULT_COLUMN_WIDTH 96 
159
160 /* Increment size of the horizontal scroll bar */
161 #define LISTVIEW_SCROLL_DIV_SIZE 10
162
163 /* Padding betwen image and label */
164 #define IMAGE_PADDING  2
165
166 /* Padding behind the label */
167 #define TRAILING_PADDING  5
168
169 /* Border for the icon caption */
170 #define CAPTION_BORDER  2
171 /* 
172  * macros
173  */
174 #define GETITEMCOUNT(infoPtr) ((infoPtr)->hdpaItems->nItemCount)
175 #define ListView_LVNotify(hwnd,lCtrlId,plvnm) \
176     (BOOL)SendMessageA((hwnd),WM_NOTIFY,(WPARAM)(INT)lCtrlId,(LPARAM)(LPNMLISTVIEW)(plvnm))
177 #define ListView_Notify(hwnd,lCtrlId,pnmh) \
178     (BOOL)SendMessageA((hwnd),WM_NOTIFY,(WPARAM)(INT)lCtrlId,(LPARAM)(LPNMHDR)(pnmh))
179 /* retrieve the number of items in the listview */
180 #define GETITEMCOUNT(infoPtr) ((infoPtr)->hdpaItems->nItemCount)
181
182 HWND CreateEditLabel(LPCSTR text, DWORD style, INT x, INT y, 
183         INT width, INT height, HWND parent, HINSTANCE hinst, 
184         EditlblCallback EditLblCb, DWORD param);
185  
186 /* 
187  * forward declarations 
188  */
189 static LRESULT LISTVIEW_GetItemA(HWND hwnd, LPLVITEMA lpLVItem, BOOL internal);
190 static INT LISTVIEW_HitTestItem(HWND, LPLVHITTESTINFO, BOOL);
191 static INT LISTVIEW_GetCountPerRow(HWND);
192 static INT LISTVIEW_GetCountPerColumn(HWND);
193 static VOID LISTVIEW_AlignLeft(HWND);
194 static VOID LISTVIEW_AlignTop(HWND);
195 static VOID LISTVIEW_AddGroupSelection(HWND, INT);
196 static VOID LISTVIEW_AddSelection(HWND, INT);
197 static BOOL LISTVIEW_AddSubItem(HWND, LPLVITEMA);
198 static INT LISTVIEW_FindInsertPosition(HDPA, INT);
199 static INT LISTVIEW_GetItemHeight(HWND);
200 static BOOL LISTVIEW_GetItemPosition(HWND, INT, LPPOINT);
201 static LRESULT LISTVIEW_GetItemRect(HWND, INT, LPRECT);
202 static INT LISTVIEW_GetItemWidth(HWND);
203 static INT LISTVIEW_GetLabelWidth(HWND, INT);
204 static LRESULT LISTVIEW_GetOrigin(HWND, LPPOINT);
205 static INT LISTVIEW_CalculateWidth(HWND hwnd, INT nItem);
206 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItem(HDPA, INT);
207 static LRESULT LISTVIEW_GetViewRect(HWND, LPRECT);
208 static BOOL LISTVIEW_InitItem(HWND, LISTVIEW_ITEM *, LPLVITEMA);
209 static BOOL LISTVIEW_InitSubItem(HWND, LISTVIEW_SUBITEM *, LPLVITEMA);
210 static LRESULT LISTVIEW_MouseSelection(HWND, POINT);
211 static BOOL LISTVIEW_RemoveColumn(HDPA, INT);
212 static BOOL LISTVIEW_RemoveSubItem(HDPA, INT);
213 static VOID LISTVIEW_SetGroupSelection(HWND, INT);
214 static BOOL LISTVIEW_SetItem(HWND, LPLVITEMA);
215 static BOOL LISTVIEW_SetItemFocus(HWND, INT);
216 static BOOL LISTVIEW_SetItemPosition(HWND, INT, LONG, LONG);
217 static VOID LISTVIEW_UpdateScroll(HWND);
218 static VOID LISTVIEW_SetSelection(HWND, INT);
219 static VOID LISTVIEW_UpdateSize(HWND);
220 static BOOL LISTVIEW_SetSubItem(HWND, LPLVITEMA);
221 static LRESULT LISTVIEW_SetViewRect(HWND, LPRECT);
222 static BOOL LISTVIEW_ToggleSelection(HWND, INT);
223 static VOID LISTVIEW_UnsupportedStyles(LONG lStyle);
224 static HWND LISTVIEW_EditLabelA(HWND hwnd, INT nItem);
225 static BOOL LISTVIEW_EndEditLabel(HWND hwnd, LPSTR pszText, DWORD nItem);
226 static LRESULT LISTVIEW_Command(HWND hwnd, WPARAM wParam, LPARAM lParam);
227 static LRESULT LISTVIEW_SortItems(HWND hwnd, WPARAM wParam, LPARAM lParam);
228 static LRESULT LISTVIEW_GetStringWidthA(HWND hwnd, LPCSTR lpszText);
229 static INT LISTVIEW_ProcessLetterKeys( HWND hwnd, WPARAM charCode, LPARAM keyData );
230 static BOOL LISTVIEW_KeySelection(HWND hwnd, INT nItem);
231 static LRESULT LISTVIEW_GetItemState(HWND hwnd, INT nItem, UINT uMask);
232 static LRESULT LISTVIEW_SetItemState(HWND hwnd, INT nItem, LPLVITEMA lpLVItem);
233 static BOOL LISTVIEW_IsSelected(HWND hwnd, INT nItem);
234 static VOID LISTVIEW_RemoveSelectionRange(HWND hwnd, INT lItem, INT uItem);
235 static void LISTVIEW_FillBackground(HWND hwnd, HDC hdc, LPRECT rc);
236
237 /******** Defines that LISTVIEW_ProcessLetterKeys uses ****************/
238 #define KEY_DELAY       900
239 #define LISTVIEW_InitLvItemStruct(item,idx,TEXT)  \
240                     ZeroMemory(&(item), sizeof(LVITEMA)); \
241                     (item).mask = LVIF_TEXT; \
242                     (item).iItem = (idx); \
243                     (item).iSubItem = 0; \
244                     (item).pszText = (TEXT); \
245                     (item).cchTextMax = MAX_PATH
246
247 static BOOL
248 LISTVIEW_SendCustomDrawNotify (HWND hwnd, DWORD dwDrawStage, HDC hdc,
249                                RECT rc)
250 {
251   LISTVIEW_INFO *infoPtr;
252   NMLVCUSTOMDRAW nmcdhdr;
253   LPNMCUSTOMDRAW nmcd;
254
255   TRACE("drawstage:%lx hdc:%x\n", dwDrawStage, hdc);
256
257   infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
258
259   nmcd= & nmcdhdr.nmcd;
260   nmcd->hdr.hwndFrom = hwnd;
261   nmcd->hdr.idFrom =  GetWindowLongA( hwnd, GWL_ID);
262   nmcd->hdr.code   = NM_CUSTOMDRAW;
263   nmcd->dwDrawStage= dwDrawStage;
264   nmcd->hdc        = hdc;
265   nmcd->rc.left    = rc.left;
266   nmcd->rc.right   = rc.right;
267   nmcd->rc.bottom  = rc.bottom;
268   nmcd->rc.top     = rc.top;
269   nmcd->dwItemSpec = 0;
270   nmcd->uItemState = 0;
271   nmcd->lItemlParam= 0;
272   nmcdhdr.clrText  = infoPtr->clrText;
273   nmcdhdr.clrTextBk= infoPtr->clrBk;
274
275   return (BOOL)SendMessageA (GetParent (hwnd), WM_NOTIFY,
276               (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmcdhdr);
277 }
278
279 static BOOL
280 LISTVIEW_SendCustomDrawItemNotify (HWND hwnd, HDC hdc,
281                                    UINT iItem, UINT iSubItem, 
282                                    UINT uItemDrawState)
283 {
284  LISTVIEW_INFO *infoPtr;
285  NMLVCUSTOMDRAW nmcdhdr;
286  LPNMCUSTOMDRAW nmcd;
287  DWORD dwDrawStage,dwItemSpec;
288  UINT uItemState;
289  INT retval;
290  RECT itemRect;
291  LVITEMA item;
292
293  infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
294
295  ZeroMemory(&item,sizeof(LVITEMA));
296  item.iItem = iItem;
297  item.mask = LVIF_PARAM;
298  ListView_GetItemA(hwnd,&item);
299
300  dwDrawStage=CDDS_ITEM | uItemDrawState;
301  dwItemSpec=iItem;
302  uItemState=0;
303
304  if (LISTVIEW_IsSelected(hwnd,iItem)) uItemState|=CDIS_SELECTED;
305  if (iItem==infoPtr->nFocusedItem)   uItemState|=CDIS_FOCUS;
306  if (iItem==infoPtr->nHotItem)       uItemState|=CDIS_HOT;
307
308  itemRect.left = LVIR_BOUNDS;
309  LISTVIEW_GetItemRect(hwnd, iItem, &itemRect);
310
311  nmcd= & nmcdhdr.nmcd;
312  nmcd->hdr.hwndFrom = hwnd;
313  nmcd->hdr.idFrom =  GetWindowLongA( hwnd, GWL_ID);
314  nmcd->hdr.code   = NM_CUSTOMDRAW;
315  nmcd->dwDrawStage= dwDrawStage;
316  nmcd->hdc                = hdc;
317  nmcd->rc.left    = itemRect.left;
318  nmcd->rc.right   = itemRect.right;
319  nmcd->rc.bottom  = itemRect.bottom;
320  nmcd->rc.top     = itemRect.top;
321  nmcd->dwItemSpec = dwItemSpec;
322  nmcd->uItemState = uItemState;
323  nmcd->lItemlParam= item.lParam;
324  nmcdhdr.clrText  = infoPtr->clrText;
325  nmcdhdr.clrTextBk= infoPtr->clrBk;
326  nmcdhdr.iSubItem   =iSubItem;
327
328  TRACE("drawstage:%lx hdc:%x item:%lx, itemstate:%x, lItemlParam:%lx\n",
329                   nmcd->dwDrawStage, nmcd->hdc, nmcd->dwItemSpec,
330           nmcd->uItemState, nmcd->lItemlParam);
331
332  retval=SendMessageA (GetParent (hwnd), WM_NOTIFY,
333                  (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmcdhdr);
334
335  infoPtr->clrText=nmcdhdr.clrText;
336  infoPtr->clrBk  =nmcdhdr.clrTextBk;
337  return (BOOL) retval;
338 }
339
340
341 /*************************************************************************
342 * DESCRIPTION:
343 *       Processes keyboard messages generated by pressing the letter keys on the keyboard.
344 *       Assumes the list is sorted alphabetically, without regard to case.
345 *
346 * PARAMETERS:
347 *       [I]   HWND: handle to the window
348 *       [I]   WPARAM: the character code, the actual character
349 *       [I]   LPARAM: key data
350 *
351 *
352 * RETURN:
353 *       Zero.
354 *
355 * TODO:
356 *       
357 *
358 */
359 static INT LISTVIEW_ProcessLetterKeys( HWND hwnd, WPARAM charCode, LPARAM keyData )
360 {
361     LISTVIEW_INFO *infoPtr = NULL;
362     INT nItem = -1;
363     BOOL bRedraw;
364     INT nSize = 0;
365     INT idx = 0;
366     BOOL bFoundMatchingFiles = FALSE;           
367     LVITEMA item;
368     CHAR TEXT[ MAX_PATH ];
369     CHAR szCharCode[ 2 ];
370     DWORD timeSinceLastKeyPress = 0;
371     
372     szCharCode[0] = charCode;
373     szCharCode[1] = 0;
374    
375     /* simple parameter checking  */
376     if ( !hwnd || !charCode || !keyData )
377         return 0;
378                                              
379     infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
380     
381     if ( !infoPtr )
382         return 0;
383     
384     /* only allow the valid WM_CHARs through */
385     if ( isalnum( 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           || charCode == '[' || charCode == '{' || charCode == '/' || charCode == '?'
391           || charCode == '>' || charCode == '<' || charCode == ',' || charCode == '~')
392     {
393         timeSinceLastKeyPress = GetTickCount();
394         
395         nSize = GETITEMCOUNT( infoPtr );
396         /* if there are 0 items, there is no where to go */
397         if ( nSize == 0 )
398             return 0;
399         /* 
400          * If the last charCode equals the current charCode then look
401          * to the next element in list to see if it matches the previous 
402          * charCode.
403          */
404         if ( infoPtr->charCode == charCode )
405         { 
406             if ( timeSinceLastKeyPress - infoPtr->timeSinceLastKeyPress < KEY_DELAY )
407             {    /* append new character to search string */
408                 strcat( infoPtr->szSearchParam, szCharCode );
409                 infoPtr->nSearchParamLength++;
410
411                 /* loop from start of list view */
412                 for( idx = infoPtr->nFocusedItem; idx < nSize; idx++ )
413                 {   /* get item */
414                     LISTVIEW_InitLvItemStruct( item, idx, TEXT );
415                     ListView_GetItemA( hwnd, &item );
416
417                     /* compare items */
418                     if ( strncasecmp( item.pszText, infoPtr->szSearchParam, 
419                                       infoPtr->nSearchParamLength ) == 0 )
420                     {
421                         nItem = idx;
422                         break;
423                     }
424                 }
425             }
426             else if ( infoPtr->timeSinceLastKeyPress > timeSinceLastKeyPress )
427             { /* The DWORD went over it's boundery?? Ergo assuming too slow??. */
428                 for ( idx = 0; idx < nSize; idx++ )
429                 {
430                     LISTVIEW_InitLvItemStruct( item, idx, TEXT );
431                     ListView_GetItemA( hwnd, &item );
432     
433                     if ( strncasecmp( &( item.pszText[ 0 ] ), szCharCode, 1 ) == 0 )
434                     {
435                         nItem = idx;
436                         break;
437                     }
438                 }
439                 strcpy( infoPtr->szSearchParam, szCharCode );
440                 infoPtr->nSearchParamLength = 1;
441             }
442             else
443             {   /* Save szCharCode for use in later searches */
444                 strcpy( infoPtr->szSearchParam, szCharCode );
445                 infoPtr->nSearchParamLength = 1;
446     
447                 LISTVIEW_InitLvItemStruct( item, infoPtr->nFocusedItem + 1, TEXT );
448                 ListView_GetItemA( hwnd, &item );
449     
450                 if ( strncasecmp( &( item.pszText[ 0 ] ), szCharCode, 1 ) == 0 )
451                     nItem = infoPtr->nFocusedItem + 1;
452                 else
453                 {   /*
454                      * Ok so there are no more folders that match
455                      * now we look for files.
456                      */   
457                     for ( idx = infoPtr->nFocusedItem + 1; idx < nSize; idx ++ )
458                     {
459                         LISTVIEW_InitLvItemStruct( item, idx, TEXT );
460                         ListView_GetItemA( hwnd, &item );
461                                 
462                         if ( strncasecmp( &( item.pszText[ 0 ] ), szCharCode, 1 ) == 0 )
463                         {
464                             nItem = idx;
465                             bFoundMatchingFiles = TRUE;
466                             break;
467                         }
468                     }
469                     if ( !bFoundMatchingFiles ) 
470                     {  /* go back to first instance */
471                         for ( idx = 0; idx < nSize; idx ++ )
472                         {
473                             LISTVIEW_InitLvItemStruct( item,idx, TEXT );
474                             ListView_GetItemA( hwnd, &item );
475                                     
476                             if ( strncasecmp( &( item.pszText[ 0 ] ), szCharCode, 1 ) == 0 )
477                             {
478                                 nItem = idx;
479                                 break;
480                             }
481                         }
482                     }
483                 }
484             }
485         } /*END: if ( infoPtr->charCode == charCode )*/
486         
487         else /* different keypressed */
488         {
489             /* could be that they are spelling the file/directory for us */
490             if ( timeSinceLastKeyPress - infoPtr->timeSinceLastKeyPress > KEY_DELAY )
491             {   /* 
492                  * Too slow, move to the first instance of the
493                  * charCode. 
494                  */
495                 for ( idx = 0; idx < nSize; idx++ )
496                 {
497                     LISTVIEW_InitLvItemStruct( item,idx, TEXT );
498                     ListView_GetItemA( hwnd, &item );
499     
500                     if ( strncasecmp( &( item.pszText[ 0 ] ), szCharCode, 1 ) == 0 )
501                     {   
502                         nItem = idx;
503                         break;
504                     }
505                 }
506                 strcpy( infoPtr->szSearchParam, szCharCode );
507                 infoPtr->nSearchParamLength = 1;
508             }
509             else if ( infoPtr->timeSinceLastKeyPress > timeSinceLastKeyPress )
510             {  /* The DWORD went over it's boundery?? Ergo assuming too slow??. */
511                 for ( idx = 0; idx < nSize; idx++ )
512                 {
513                     LISTVIEW_InitLvItemStruct( item,idx, TEXT );
514                     ListView_GetItemA( hwnd, &item );
515     
516                     if ( strncasecmp( &( item.pszText[ 0 ] ), szCharCode, 1 ) == 0 )
517                     {
518                         nItem = idx;
519                         break;
520                     }
521                 }
522                 strcpy( infoPtr->szSearchParam, szCharCode );
523                 infoPtr->nSearchParamLength = 1;
524             }
525             else /* Search for the string the user is typing */
526             {   
527                 /* append new character to search string */
528                 strcat( infoPtr->szSearchParam, szCharCode );
529                 infoPtr->nSearchParamLength++;
530
531                 /* loop from start of list view */
532                 for( idx = 0; idx < nSize; idx++ )
533                 {   /* get item */
534                     LISTVIEW_InitLvItemStruct( item, idx, TEXT );
535                     ListView_GetItemA( hwnd, &item );
536
537                     /* compare items */
538                     if ( strncasecmp( item.pszText, infoPtr->szSearchParam, 
539                                       infoPtr->nSearchParamLength ) == 0 )
540                     {
541                         nItem = idx;
542                         break;
543                     }
544                 }
545             }
546         }/*END: else */
547     }
548     else
549         return 0;
550     
551     bRedraw = LISTVIEW_KeySelection(hwnd, nItem );
552     if (bRedraw != FALSE)
553     {
554         /* refresh client area */
555         InvalidateRect(hwnd, NULL, TRUE);
556         UpdateWindow(hwnd);
557     }
558
559     /* Store the WM_CHAR for next time */
560     infoPtr->charCode = charCode;
561     
562     /* Store time */
563     infoPtr->timeSinceLastKeyPress = timeSinceLastKeyPress;
564     
565     return 0;
566
567 }
568
569 /*************************************************************************
570  * LISTVIEW_UpdateHeaderSize [Internal] 
571  *
572  * Function to resize the header control
573  *
574  * PARAMS
575  *     hwnd             [I] handle to a window
576  *     nNewScrollPos    [I] Scroll Pos to Set
577  *
578  * RETURNS
579  *     nothing
580  *
581  * NOTES
582  */
583 static VOID LISTVIEW_UpdateHeaderSize(HWND hwnd, INT nNewScrollPos)
584 {
585     LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
586     RECT winRect;
587     POINT point[2];
588
589     GetWindowRect(infoPtr->hwndHeader, &winRect);
590     point[0].x = winRect.left;
591     point[0].y = winRect.top;
592     point[1].x = winRect.right;
593     point[1].y = winRect.bottom;
594
595     MapWindowPoints(HWND_DESKTOP, hwnd, point, 2);
596     point[0].x = -(nNewScrollPos * LISTVIEW_SCROLL_DIV_SIZE);
597     point[1].x += (nNewScrollPos * LISTVIEW_SCROLL_DIV_SIZE);
598
599     SetWindowPos(infoPtr->hwndHeader,0,
600         point[0].x,point[0].y,point[1].x,point[1].y,
601         SWP_NOZORDER | SWP_NOACTIVATE);
602 }
603
604 /***
605  * DESCRIPTION:
606  * Update the scrollbars. This functions should be called whenever 
607  * the content, size or view changes.
608  * 
609  * PARAMETER(S):
610  * [I] HWND : window handle
611  *
612  * RETURN:
613  * None
614  */
615 static VOID LISTVIEW_UpdateScroll(HWND hwnd)
616 {
617   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0); 
618   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
619   UINT uView =  lStyle & LVS_TYPEMASK;
620   INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
621   INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
622   SCROLLINFO scrollInfo;
623
624   ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
625   scrollInfo.cbSize = sizeof(SCROLLINFO);
626
627   if (uView == LVS_LIST)
628   {
629     /* update horizontal scrollbar */
630
631     INT nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
632     INT nCountPerRow = LISTVIEW_GetCountPerRow(hwnd);
633     INT nNumOfItems = GETITEMCOUNT(infoPtr);
634
635     scrollInfo.nMax = nNumOfItems / nCountPerColumn;
636     if((nNumOfItems % nCountPerColumn) == 0)
637     {
638       scrollInfo.nMax--;
639     }
640     scrollInfo.nPos = ListView_GetTopIndex(hwnd) / nCountPerColumn;
641     scrollInfo.nPage = nCountPerRow;
642     scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
643     SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
644     ShowScrollBar(hwnd, SB_VERT, FALSE);
645   }
646   else if (uView == LVS_REPORT)
647   {
648     /* update vertical scrollbar */
649     scrollInfo.nMin = 0;
650     scrollInfo.nMax = GETITEMCOUNT(infoPtr) - 1;
651     scrollInfo.nPos = ListView_GetTopIndex(hwnd);
652     scrollInfo.nPage = LISTVIEW_GetCountPerColumn(hwnd);
653     scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
654     SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
655
656     /* update horizontal scrollbar */
657     nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
658     if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) == FALSE 
659        || GETITEMCOUNT(infoPtr) == 0)
660     {
661       scrollInfo.nPos = 0;
662     } 
663     scrollInfo.nMin = 0;
664     scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE  ; 
665     scrollInfo.nPage = nListWidth / LISTVIEW_SCROLL_DIV_SIZE;
666     scrollInfo.nMax = max(infoPtr->nItemWidth / LISTVIEW_SCROLL_DIV_SIZE, 0)-1;
667     SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE); 
668
669     /* Update the Header Control */
670     scrollInfo.fMask = SIF_POS;
671     GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
672     LISTVIEW_UpdateHeaderSize(hwnd, scrollInfo.nPos);
673
674   }
675   else
676   {
677     RECT rcView;
678
679     if (LISTVIEW_GetViewRect(hwnd, &rcView) != FALSE)
680     {
681       INT nViewWidth = rcView.right - rcView.left;
682       INT nViewHeight = rcView.bottom - rcView.top;
683
684       /* Update Horizontal Scrollbar */
685       scrollInfo.fMask = SIF_POS;
686       if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) == FALSE
687         || GETITEMCOUNT(infoPtr) == 0)
688       {
689         scrollInfo.nPos = 0;
690       }
691       scrollInfo.nMax = max(nViewWidth / LISTVIEW_SCROLL_DIV_SIZE, 0)-1;
692       scrollInfo.nMin = 0;
693       scrollInfo.nPage = nListWidth / LISTVIEW_SCROLL_DIV_SIZE;
694       scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
695       SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
696
697       /* Update Vertical Scrollbar */
698       nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
699       scrollInfo.fMask = SIF_POS;
700       if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) == FALSE
701         || GETITEMCOUNT(infoPtr) == 0)
702       {
703         scrollInfo.nPos = 0;
704       }
705       scrollInfo.nMax = max(nViewHeight / LISTVIEW_SCROLL_DIV_SIZE,0)-1;
706       scrollInfo.nMin = 0;
707       scrollInfo.nPage = nListHeight / LISTVIEW_SCROLL_DIV_SIZE;
708       scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
709       SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
710     }
711   }
712 }
713
714 /***
715  * DESCRIPTION:
716  * Prints a message for unsupported window styles.
717  * A kind of TODO list for window styles.
718  * 
719  * PARAMETER(S):
720  * [I] LONG : window style
721  *
722  * RETURN:
723  * None
724  */
725 static VOID LISTVIEW_UnsupportedStyles(LONG lStyle)
726 {
727   if ((LVS_TYPEMASK & lStyle) == LVS_EDITLABELS)
728   {
729     FIXME("  LVS_EDITLABELS\n");
730   }
731
732   if ((LVS_TYPEMASK & lStyle) == LVS_NOLABELWRAP)
733   {
734     FIXME("  LVS_NOLABELWRAP\n");
735   }
736
737   if ((LVS_TYPEMASK & lStyle) == LVS_NOSCROLL)
738   {
739     FIXME("  LVS_NOSCROLL\n");
740   }
741
742   if ((LVS_TYPEMASK & lStyle) == LVS_NOSORTHEADER)
743   {
744     FIXME("  LVS_NOSORTHEADER\n");
745   }
746
747   if ((LVS_TYPEMASK & lStyle) == LVS_OWNERDRAWFIXED)
748   {
749     FIXME("  LVS_OWNERDRAWFIXED\n");
750   }
751
752   if ((LVS_TYPEMASK & lStyle) == LVS_SHAREIMAGELISTS)
753   {
754     FIXME("  LVS_SHAREIMAGELISTS\n");
755   }
756
757   if ((LVS_TYPEMASK & lStyle) == LVS_SORTASCENDING)
758   {
759     FIXME("  LVS_SORTASCENDING\n");
760   }
761
762   if ((LVS_TYPEMASK & lStyle) == LVS_SORTDESCENDING)
763   {
764     FIXME("  LVS_SORTDESCENDING\n");
765   }
766 }
767
768 /***
769  * DESCRIPTION:
770  * Aligns the items with the top edge of the window.
771  * 
772  * PARAMETER(S):
773  * [I] HWND : window handle
774  *
775  * RETURN:
776  * None
777  */
778 static VOID LISTVIEW_AlignTop(HWND hwnd)
779 {
780   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
781   UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
782   INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
783   POINT ptItem;
784   RECT rcView;
785   INT i;
786   
787   if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
788   {
789     ZeroMemory(&ptItem, sizeof(POINT));
790     ZeroMemory(&rcView, sizeof(RECT));
791     
792     if (nListWidth > infoPtr->nItemWidth)
793     {
794       for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
795       {
796         if (ptItem.x + infoPtr->nItemWidth > nListWidth)
797         {
798           ptItem.x = 0;
799           ptItem.y += infoPtr->nItemHeight;
800         }
801         
802         ListView_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
803         ptItem.x += infoPtr->nItemWidth;
804         rcView.right = max(rcView.right, ptItem.x);
805       }
806
807       rcView.bottom = ptItem.y + infoPtr->nItemHeight;
808     }
809     else
810     {
811       for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
812       {
813         ListView_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
814         ptItem.y += infoPtr->nItemHeight;
815       }
816
817       rcView.right = infoPtr->nItemWidth;
818       rcView.bottom = ptItem.y;
819     }
820
821     LISTVIEW_SetViewRect(hwnd, &rcView);
822   }
823 }
824
825 /***
826  * DESCRIPTION:
827  * Aligns the items with the left edge of the window.
828  * 
829  * PARAMETER(S):
830  * [I] HWND : window handle
831  *
832  * RETURN:
833  * None
834  */
835 static VOID LISTVIEW_AlignLeft(HWND hwnd)
836 {
837   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
838   UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
839   INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
840   POINT ptItem;
841   RECT rcView;
842   INT i;
843   
844   if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
845   {
846     ZeroMemory(&ptItem, sizeof(POINT));
847     ZeroMemory(&rcView, sizeof(RECT));
848
849     if (nListHeight > infoPtr->nItemHeight)
850     {
851       for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
852       {
853         if (ptItem.y + infoPtr->nItemHeight > nListHeight)
854         {
855           ptItem.y = 0;
856           ptItem.x += infoPtr->nItemWidth;
857         }
858
859         ListView_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
860         ptItem.y += infoPtr->nItemHeight;
861         rcView.bottom = max(rcView.bottom, ptItem.y);
862       }
863
864       rcView.right = ptItem.x + infoPtr->nItemWidth;
865     }
866     else
867     {
868       for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
869       {
870         ListView_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
871         ptItem.x += infoPtr->nItemWidth;
872       }
873
874       rcView.bottom = infoPtr->nItemHeight;
875       rcView.right = ptItem.x;
876     }
877
878     LISTVIEW_SetViewRect(hwnd, &rcView);
879   }
880 }
881
882 /***
883  * DESCRIPTION:
884  * Set the bounding rectangle of all the items.
885  * 
886  * PARAMETER(S):
887  * [I] HWND : window handle
888  * [I] LPRECT : bounding rectangle
889  *
890  * RETURN:
891  *   SUCCESS : TRUE
892  *   FAILURE : FALSE
893  */
894 static LRESULT LISTVIEW_SetViewRect(HWND hwnd, LPRECT lprcView)
895 {
896   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongA(hwnd, 0);
897   BOOL bResult = FALSE;
898
899   TRACE("(hwnd=%x, left=%d, top=%d, right=%d, bottom=%d)\n", hwnd, 
900         lprcView->left, lprcView->top, lprcView->right, lprcView->bottom);
901   
902   if (lprcView != NULL)
903   {
904     bResult = TRUE;
905     infoPtr->rcView.left = lprcView->left;
906     infoPtr->rcView.top = lprcView->top;
907     infoPtr->rcView.right = lprcView->right;
908     infoPtr->rcView.bottom = lprcView->bottom;
909   }
910
911   return bResult;
912 }
913
914 /***
915  * DESCRIPTION:
916  * Retrieves the bounding rectangle of all the items.
917  * 
918  * PARAMETER(S):
919  * [I] HWND : window handle
920  * [O] LPRECT : bounding rectangle
921  *
922  * RETURN:
923  *   SUCCESS : TRUE
924  *   FAILURE : FALSE
925  */
926 static LRESULT LISTVIEW_GetViewRect(HWND hwnd, LPRECT lprcView)
927 {
928   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongA(hwnd, 0);
929   BOOL bResult = FALSE;
930   POINT ptOrigin;
931
932   TRACE("(hwnd=%x, lprcView=%p)\n", hwnd, lprcView);
933
934   if (lprcView != NULL)
935   {
936     bResult = LISTVIEW_GetOrigin(hwnd, &ptOrigin);
937     if (bResult != FALSE)
938     {
939       lprcView->left = infoPtr->rcView.left + ptOrigin.x;
940       lprcView->top = infoPtr->rcView.top + ptOrigin.y;
941       lprcView->right = infoPtr->rcView.right + ptOrigin.x;
942       lprcView->bottom = infoPtr->rcView.bottom + ptOrigin.y;
943     }
944
945     TRACE("(left=%d, top=%d, right=%d, bottom=%d)\n", 
946           lprcView->left, lprcView->top, lprcView->right, lprcView->bottom);
947   }
948
949   return bResult;
950 }
951
952 /***
953  * DESCRIPTION:
954  * Retrieves the subitem pointer associated with the subitem index.
955  * 
956  * PARAMETER(S):
957  * [I] HDPA : DPA handle for a specific item
958  * [I] INT : index of subitem
959  *
960  * RETURN:
961  *   SUCCESS : subitem pointer
962  *   FAILURE : NULL
963  */
964 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems, 
965                                                 INT nSubItem)
966 {
967   LISTVIEW_SUBITEM *lpSubItem;
968   INT i;
969
970   for (i = 1; i < hdpaSubItems->nItemCount; i++)
971   {
972     lpSubItem = (LISTVIEW_SUBITEM *) DPA_GetPtr(hdpaSubItems, i);
973     if (lpSubItem != NULL)
974     {
975       if (lpSubItem->iSubItem == nSubItem)
976       {
977         return lpSubItem;
978       }
979     }
980   }
981
982   return NULL;
983 }
984
985 /***
986  * DESCRIPTION:
987  * Calculates the width of an item.
988  * 
989  * PARAMETER(S):
990  * [I] HWND : window handle
991  * [I] LONG : window style
992  *
993  * RETURN:
994  * Returns item width.
995  */
996 static INT LISTVIEW_GetItemWidth(HWND hwnd)
997 {
998   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
999   LONG style = GetWindowLongA(hwnd, GWL_STYLE); 
1000   UINT uView = style & LVS_TYPEMASK; 
1001   INT nHeaderItemCount;
1002   RECT rcHeaderItem;
1003   INT nItemWidth = 0;
1004   INT nLabelWidth;
1005   INT i;
1006
1007   TRACE("(hwnd=%x)\n", hwnd);
1008
1009   if (uView == LVS_ICON)
1010   {
1011     nItemWidth = infoPtr->iconSpacing.cx;
1012   }
1013   else if (uView == LVS_REPORT)
1014   {
1015     /* calculate width of header */
1016     nHeaderItemCount = Header_GetItemCount(infoPtr->hwndHeader);
1017     for (i = 0; i < nHeaderItemCount; i++)
1018     {
1019       if (Header_GetItemRect(infoPtr->hwndHeader, i, &rcHeaderItem) != 0)
1020       {
1021         nItemWidth += (rcHeaderItem.right - rcHeaderItem.left);
1022       }
1023     }
1024   }
1025   else
1026   {
1027     for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1028     { 
1029       nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, i);
1030       nItemWidth = max(nItemWidth, nLabelWidth);
1031     }
1032     
1033     /* default label size */
1034     if (GETITEMCOUNT(infoPtr) == 0)
1035     {
1036       nItemWidth = DEFAULT_COLUMN_WIDTH;
1037     }
1038     else
1039     {
1040       if (nItemWidth == 0)
1041       {
1042         nItemWidth = DEFAULT_LABEL_WIDTH;
1043       }
1044       else
1045       {
1046         /* add padding */
1047         nItemWidth += WIDTH_PADDING;
1048       
1049         if (infoPtr->himlSmall != NULL)
1050         {
1051           nItemWidth += infoPtr->iconSize.cx;
1052         }
1053
1054         if (infoPtr->himlState != NULL)
1055         {
1056           nItemWidth += infoPtr->iconSize.cx;
1057         }
1058       }
1059     }
1060   }
1061   if(nItemWidth == 0)
1062   {
1063       /* nItemWidth Cannot be Zero */
1064       nItemWidth = 1;
1065   }
1066   return nItemWidth;
1067 }
1068
1069 /***
1070  * DESCRIPTION:
1071  * Calculates the width of a specific item.
1072  * 
1073  * PARAMETER(S):
1074  * [I] HWND : window handle
1075  * [I] LPSTR : string  
1076  *
1077  * RETURN:
1078  * Returns the width of an item width a specified string.
1079  */
1080 static INT LISTVIEW_CalculateWidth(HWND hwnd, INT nItem)
1081 {
1082   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1083   UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
1084   INT nHeaderItemCount;
1085   RECT rcHeaderItem;
1086   INT nItemWidth = 0;
1087   INT i;
1088
1089   TRACE("(hwnd=%x)\n", hwnd);
1090
1091   if (uView == LVS_ICON)
1092   {
1093     nItemWidth = infoPtr->iconSpacing.cx;
1094   }
1095   else if (uView == LVS_REPORT)
1096   {
1097     /* calculate width of header */
1098     nHeaderItemCount = Header_GetItemCount(infoPtr->hwndHeader);
1099     for (i = 0; i < nHeaderItemCount; i++)
1100     {
1101       if (Header_GetItemRect(infoPtr->hwndHeader, i, &rcHeaderItem) != 0)
1102       {
1103         nItemWidth += (rcHeaderItem.right - rcHeaderItem.left);
1104       }
1105     }
1106   }
1107   else
1108   {
1109     /* get width of string */
1110     nItemWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
1111
1112     /* default label size */
1113     if (GETITEMCOUNT(infoPtr) == 0)
1114     {
1115       nItemWidth = DEFAULT_COLUMN_WIDTH;
1116     }
1117     else
1118     {
1119       if (nItemWidth == 0)
1120       {
1121         nItemWidth = DEFAULT_LABEL_WIDTH;
1122       }
1123       else
1124       {
1125         /* add padding */
1126         nItemWidth += WIDTH_PADDING;
1127       
1128         if (infoPtr->himlSmall != NULL)
1129         {
1130           nItemWidth += infoPtr->iconSize.cx;
1131         }
1132
1133         if (infoPtr->himlState != NULL)
1134         {
1135           nItemWidth += infoPtr->iconSize.cx;
1136         }
1137       }
1138     }
1139   }
1140   
1141   return nItemWidth;
1142 }
1143
1144 /***
1145  * DESCRIPTION:
1146  * Calculates the height of an item.
1147  * 
1148  * PARAMETER(S):
1149  * [I] HWND : window handle
1150  * [I] LONG : window style
1151  *
1152  * RETURN:
1153  * Returns item height.
1154  */
1155 static INT LISTVIEW_GetItemHeight(HWND hwnd)
1156 {
1157   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1158   UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
1159   INT nItemHeight = 0;
1160
1161   if (uView == LVS_ICON)
1162   {
1163     nItemHeight = infoPtr->iconSpacing.cy;
1164   }
1165   else
1166   {
1167     TEXTMETRICA tm; 
1168     HDC hdc = GetDC(hwnd);
1169     HFONT hOldFont = SelectObject(hdc, infoPtr->hFont);
1170     GetTextMetricsA(hdc, &tm);
1171
1172     if(infoPtr->himlState || infoPtr->himlSmall)
1173       nItemHeight = max(tm.tmHeight, infoPtr->iconSize.cy) + HEIGHT_PADDING;
1174     else
1175       nItemHeight = tm.tmHeight;
1176
1177     SelectObject(hdc, hOldFont);
1178     ReleaseDC(hwnd, hdc);
1179   }
1180
1181   return nItemHeight;
1182 }
1183
1184
1185 static void LISTVIEW_PrintSelectionRanges(HWND hwnd)
1186 {
1187   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1188   LISTVIEW_SELECTION *selection;
1189   INT topSelection = infoPtr->hdpaSelectionRanges->nItemCount;
1190   INT i;
1191
1192   TRACE("Selections are:\n");
1193   for (i = 0; i < topSelection; i++)
1194   {
1195     selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,i);
1196     TRACE("     %lu - %lu\n",selection->lower,selection->upper);
1197   }
1198 }
1199
1200 /***
1201  * DESCRIPTION:
1202  * A compare function for selection ranges
1203  *
1204  *PARAMETER(S)
1205  * [I] LPVOID : Item 1;
1206  * [I] LPVOID : Item 2;
1207  * [I] LPARAM : flags
1208  *
1209  *RETURNS:
1210  * >0 : if Item 1 > Item 2
1211  * <0 : if Item 2 > Item 1
1212  * 0 : if Item 1 == Item 2
1213  */
1214 static INT CALLBACK LISTVIEW_CompareSelectionRanges(LPVOID range1, LPVOID range2, 
1215                                                     LPARAM flags)
1216 {
1217   int l1 = ((LISTVIEW_SELECTION*)(range1))->lower;
1218   int l2 = ((LISTVIEW_SELECTION*)(range2))->lower;
1219   int u1 = ((LISTVIEW_SELECTION*)(range1))->upper;
1220   int u2 = ((LISTVIEW_SELECTION*)(range2))->upper; 
1221   int rc=0;
1222
1223   if (u1 < l2)
1224     rc= -1;
1225  
1226   if (u2 < l1)
1227      rc= 1;
1228
1229   return rc;
1230 }
1231
1232 /**
1233 * DESCRIPTION:
1234 * Adds a selection range.
1235
1236 * PARAMETER(S):
1237 * [I] HWND : window handle
1238 * [I] INT : lower item index
1239 * [I] INT : upper item index 
1240 *
1241 * RETURN:
1242 * None
1243 */
1244 static VOID LISTVIEW_AddSelectionRange(HWND hwnd, INT lItem, INT uItem)
1245 {
1246  LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1247  LISTVIEW_SELECTION *selection;
1248  INT topSelection = infoPtr->hdpaSelectionRanges->nItemCount;
1249  BOOL lowerzero=FALSE;
1250
1251  selection = (LISTVIEW_SELECTION *)COMCTL32_Alloc(sizeof(LISTVIEW_SELECTION));
1252  selection->lower = lItem;
1253  selection->upper = uItem;
1254
1255  TRACE("Add range %i - %i\n",lItem,uItem);
1256  if (topSelection)
1257  {
1258    LISTVIEW_SELECTION *checkselection,*checkselection2;
1259    INT index,mergeindex;
1260
1261    /* find overlapping selections */
1262    /* we want to catch adjacent ranges so expand our range by 1 */
1263
1264    selection->upper++;
1265    if (selection->lower == 0)
1266      lowerzero = TRUE;
1267    else
1268      selection->lower--;
1269
1270    index = DPA_Search(infoPtr->hdpaSelectionRanges, selection, 0,
1271                       LISTVIEW_CompareSelectionRanges,
1272                       0,0);
1273    selection->upper --; 
1274    if (lowerzero)
1275      lowerzero=FALSE;
1276    else
1277      selection->lower ++;
1278
1279    if (index >=0)
1280    {
1281      checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,index);
1282      TRACE("Merge with index %i (%lu - %lu)\n",index,checkselection->lower,
1283            checkselection->upper);
1284      
1285      checkselection->lower = min(selection->lower,checkselection->lower);
1286      checkselection->upper = max(selection->upper,checkselection->upper);
1287     
1288      TRACE("New range (%lu - %lu)\n", checkselection->lower, 
1289            checkselection->upper);
1290
1291      COMCTL32_Free(selection);    
1292  
1293      /* merge now common selection ranges in the lower group*/
1294      do
1295      {
1296         checkselection->upper ++; 
1297         if (checkselection->lower == 0)
1298           lowerzero = TRUE;
1299         else
1300           checkselection->lower --;
1301
1302         TRACE("search lower range (%lu - %lu)\n", checkselection->lower, 
1303               checkselection->upper);
1304
1305         /* not sorted yet */
1306         mergeindex = DPA_Search(infoPtr->hdpaSelectionRanges, checkselection, 0,
1307                                 LISTVIEW_CompareSelectionRanges, 0,
1308                                 0);
1309
1310         checkselection->upper --; 
1311         if (lowerzero)
1312           lowerzero = FALSE;
1313         else
1314           checkselection->lower ++;
1315
1316         if (mergeindex >=0  && mergeindex != index)
1317         {
1318           TRACE("Merge with index %i\n",mergeindex);
1319           checkselection2 = DPA_GetPtr(infoPtr->hdpaSelectionRanges,
1320                                        mergeindex);
1321           checkselection->lower = min(checkselection->lower, 
1322                                       checkselection2->lower);
1323           checkselection->upper = max(checkselection->upper,
1324                                       checkselection2->upper);
1325           COMCTL32_Free(checkselection2);
1326           DPA_DeletePtr(infoPtr->hdpaSelectionRanges,mergeindex);
1327           index --;
1328         }
1329      }
1330      while (mergeindex > -1 && mergeindex <index);
1331
1332      /* merge now common selection ranges in the upper group*/
1333     do
1334     {
1335        checkselection->upper ++;
1336        if (checkselection->lower == 0)
1337          lowerzero = TRUE;
1338        else 
1339          checkselection->lower --;
1340
1341        TRACE("search upper range %i (%lu - %lu)\n",index, 
1342              checkselection->lower, checkselection->upper);
1343
1344        /* not sorted yet */
1345        mergeindex = DPA_Search(infoPtr->hdpaSelectionRanges, checkselection,
1346                                index+1,
1347                                LISTVIEW_CompareSelectionRanges, 0,
1348                                0);
1349
1350        checkselection->upper --; 
1351        if (lowerzero)
1352          lowerzero = FALSE;
1353        else
1354          checkselection->lower ++;
1355
1356        if (mergeindex >=0 && mergeindex !=index)
1357        {
1358          TRACE("Merge with index %i\n",mergeindex);
1359          checkselection2 = DPA_GetPtr(infoPtr->hdpaSelectionRanges,
1360                                       mergeindex);
1361          checkselection->lower = min(checkselection->lower, 
1362                                      checkselection2->lower);
1363          checkselection->upper = max(checkselection->upper,
1364                                      checkselection2->upper);
1365          COMCTL32_Free(checkselection2);
1366          DPA_DeletePtr(infoPtr->hdpaSelectionRanges,mergeindex);
1367        }
1368     }
1369     while (mergeindex > -1);
1370    }
1371    else
1372    {
1373
1374      index = DPA_Search(infoPtr->hdpaSelectionRanges, selection, 0,
1375                        LISTVIEW_CompareSelectionRanges, 0, 
1376                        DPAS_INSERTAFTER);
1377
1378      TRACE("Insert before index %i\n",index);
1379      if (index == -1)
1380        index = 0;
1381      DPA_InsertPtr(infoPtr->hdpaSelectionRanges,index,selection); 
1382    }
1383  } 
1384  else
1385  {
1386    DPA_InsertPtr(infoPtr->hdpaSelectionRanges,0,selection);
1387  }
1388  /*
1389   * Incase of error 
1390   */
1391  DPA_Sort(infoPtr->hdpaSelectionRanges,LISTVIEW_CompareSelectionRanges,0);
1392  LISTVIEW_PrintSelectionRanges(hwnd);
1393 }
1394
1395 /**
1396 * DESCRIPTION:
1397 * check if a specified index is selected.
1398
1399 * PARAMETER(S):
1400 * [I] HWND : window handle
1401 * [I] INT : item index 
1402 *
1403 * RETURN:
1404 * None
1405 */
1406 static BOOL LISTVIEW_IsSelected(HWND hwnd, INT nItem)
1407 {
1408   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1409   LISTVIEW_SELECTION selection;
1410   INT index;
1411
1412   selection.upper = nItem;
1413   selection.lower = nItem;
1414
1415   index = DPA_Search(infoPtr->hdpaSelectionRanges, &selection, 0,
1416                       LISTVIEW_CompareSelectionRanges,
1417                       0,DPAS_SORTED);
1418   if (index != -1)
1419     return TRUE;
1420   else
1421     return FALSE;
1422 }
1423
1424 /***
1425 * DESCRIPTION:
1426 * Removes all selection ranges
1427 *
1428 * Parameters(s):
1429 *   HWND: window handle
1430 *
1431 * RETURNS:
1432 *   SUCCESS : TRUE
1433 *   FAILURE : TRUE
1434 */
1435 static LRESULT LISTVIEW_RemoveAllSelections(HWND hwnd)
1436 {
1437   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1438   LISTVIEW_SELECTION *selection;
1439   INT i;
1440   LVITEMA item;
1441   
1442   TRACE("(0x%x)\n",hwnd);
1443
1444   ZeroMemory(&item,sizeof(LVITEMA));
1445   item.stateMask = LVIS_SELECTED;
1446
1447   do
1448   {
1449     selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,0);
1450     if (selection)
1451     {
1452       TRACE("Removing %lu to %lu\n",selection->lower, selection->upper);
1453       for (i = selection->lower; i<=selection->upper; i++)
1454         LISTVIEW_SetItemState(hwnd,i,&item);
1455       LISTVIEW_RemoveSelectionRange(hwnd,selection->lower,selection->upper);
1456     }
1457   } 
1458   while (infoPtr->hdpaSelectionRanges->nItemCount>0);
1459
1460   TRACE("done\n");
1461   return TRUE; 
1462 }
1463
1464 /**
1465 * DESCRIPTION:
1466 * Removes a range selections.
1467
1468 * PARAMETER(S):
1469 * [I] HWND : window handle
1470 * [I] INT : lower item index
1471 * [I] INT : upper item index 
1472 *
1473 * RETURN:
1474 * None
1475 */
1476 static VOID LISTVIEW_RemoveSelectionRange(HWND hwnd, INT lItem, INT uItem)
1477 {
1478   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1479   LISTVIEW_SELECTION removeselection,*checkselection;
1480   INT index;
1481
1482   removeselection.lower = lItem;
1483   removeselection.upper = uItem;
1484
1485   TRACE("Remove range %lu - %lu\n",removeselection.lower,removeselection.upper);
1486   LISTVIEW_PrintSelectionRanges(hwnd);
1487
1488   index = DPA_Search(infoPtr->hdpaSelectionRanges, &removeselection, 0,
1489                      LISTVIEW_CompareSelectionRanges,
1490                      0,0);
1491   
1492   if (index == -1)
1493     return;
1494
1495  
1496   checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,
1497                               index);
1498
1499   TRACE("Matches range index %i (%lu-%lu)\n",index,checkselection->lower,
1500         checkselection->upper);
1501
1502   /* case 1: Same */
1503   if ((checkselection->upper == removeselection.upper) && 
1504      (checkselection->lower == removeselection.lower))
1505   {
1506     DPA_DeletePtr(infoPtr->hdpaSelectionRanges,index);
1507     TRACE("Case 1\n");
1508   }
1509   /* case 2: engulf */
1510   else if (((checkselection->upper < removeselection.upper) && 
1511       (checkselection->lower > removeselection.lower))||
1512      ((checkselection->upper <= removeselection.upper) &&
1513       (checkselection->lower > removeselection.lower)) ||
1514      ((checkselection->upper < removeselection.upper) &&
1515       (checkselection->lower >= removeselection.lower)))
1516
1517   {
1518     DPA_DeletePtr(infoPtr->hdpaSelectionRanges,index);
1519     /* do it again because others may also get caught */
1520     TRACE("Case 2\n");
1521     LISTVIEW_RemoveSelectionRange(hwnd,lItem,uItem);
1522   }
1523   /* case 3: overlap upper */
1524   else if ((checkselection->upper < removeselection.upper) &&
1525       (checkselection->lower < removeselection.lower))
1526   {
1527     checkselection->upper = removeselection.lower - 1;
1528     TRACE("Case 3\n");
1529     LISTVIEW_RemoveSelectionRange(hwnd,lItem,uItem);
1530   }
1531   /* case 4: overlap lower */
1532   else if ((checkselection->upper > removeselection.upper) &&
1533       (checkselection->lower > removeselection.lower))
1534   {
1535     checkselection->lower = removeselection.upper + 1;
1536     TRACE("Case 4\n");
1537     LISTVIEW_RemoveSelectionRange(hwnd,lItem,uItem);
1538   }
1539   /* case 5: fully internal */
1540   else if (checkselection->upper == removeselection.upper)
1541     checkselection->upper = removeselection.lower - 1;
1542   else if (checkselection->lower == removeselection.lower)
1543     checkselection->lower = removeselection.upper + 1;
1544   else
1545   {
1546     /* bisect the range */
1547     LISTVIEW_SELECTION *newselection;
1548     
1549     newselection = (LISTVIEW_SELECTION *)
1550                           COMCTL32_Alloc(sizeof(LISTVIEW_SELECTION));
1551     newselection -> lower = checkselection->lower;
1552     newselection -> upper = removeselection.lower - 1;
1553     checkselection -> lower = removeselection.upper + 1;
1554     DPA_InsertPtr(infoPtr->hdpaSelectionRanges,index,newselection);
1555     TRACE("Case 5\n");
1556     DPA_Sort(infoPtr->hdpaSelectionRanges,LISTVIEW_CompareSelectionRanges,0);
1557   }
1558   LISTVIEW_PrintSelectionRanges(hwnd);
1559 }
1560
1561 /**
1562 * DESCRIPTION:
1563 * shifts all selection indexs starting with the indesx specified
1564 * in the direction specified.
1565
1566 * PARAMETER(S):
1567 * [I] HWND : window handle
1568 * [I] INT : item index 
1569 * [I] INT : amount and direction of shift
1570 *
1571 * RETURN:
1572 * None
1573 */
1574 static VOID LISTVIEW_ShiftSelections(HWND hwnd, INT nItem, INT direction)
1575 {
1576   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1577   LISTVIEW_SELECTION selection,*checkselection;
1578   INT index;
1579
1580   TRACE("Shifting %iu, %i steps\n",nItem,direction);
1581
1582   selection.upper = nItem;
1583   selection.lower = nItem;
1584
1585   index = DPA_Search(infoPtr->hdpaSelectionRanges, &selection, 0,
1586                      LISTVIEW_CompareSelectionRanges,
1587                      0,DPAS_SORTED|DPAS_INSERTAFTER);
1588
1589   while ((index < infoPtr->hdpaSelectionRanges->nItemCount)&&(index != -1)) 
1590   {
1591     checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,index);
1592     if ((checkselection->lower >= nItem)&&
1593        (checkselection->lower + direction >= 0))
1594         checkselection->lower += direction;
1595     if ((checkselection->upper >= nItem)&&
1596        (checkselection->upper + direction >=0))
1597         checkselection->upper += direction;
1598     index ++;
1599   }
1600 }
1601
1602
1603 /**
1604  * DESCRIPTION:
1605  * Adds a block of selections.
1606  * 
1607  * PARAMETER(S):
1608  * [I] HWND : window handle
1609  * [I] INT : item index 
1610  *
1611  * RETURN:
1612  * None
1613  */
1614 static VOID LISTVIEW_AddGroupSelection(HWND hwnd, INT nItem)
1615 {
1616   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1617   INT nFirst = min(infoPtr->nSelectionMark, nItem);
1618   INT nLast = max(infoPtr->nSelectionMark, nItem);
1619   INT i;
1620   LVITEMA item;
1621
1622   ZeroMemory(&item,sizeof(LVITEMA));
1623   item.stateMask = LVIS_SELECTED;
1624   item.state = LVIS_SELECTED;
1625
1626   for (i = nFirst; i <= nLast; i++);
1627   {
1628     LISTVIEW_SetItemState(hwnd,i,&item);
1629   }
1630
1631   LISTVIEW_SetItemFocus(hwnd, nItem);
1632   infoPtr->nSelectionMark = nItem;
1633 }
1634
1635
1636 /***
1637  * DESCRIPTION:
1638  * Adds a single selection.
1639  * 
1640  * PARAMETER(S):
1641  * [I] HWND : window handle
1642  * [I] INT : item index 
1643  *
1644  * RETURN:
1645  * None
1646  */
1647 static VOID LISTVIEW_AddSelection(HWND hwnd, INT nItem)
1648 {
1649   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1650   LVITEMA item;
1651
1652   ZeroMemory(&item,sizeof(LVITEMA));
1653   item.state = LVIS_SELECTED;
1654   item.stateMask = LVIS_SELECTED;
1655
1656   LISTVIEW_SetItemState(hwnd,nItem,&item);
1657
1658   LISTVIEW_SetItemFocus(hwnd, nItem);
1659   infoPtr->nSelectionMark = nItem;
1660 }
1661
1662 /***
1663  * DESCRIPTION:
1664  * Selects or unselects an item.
1665  * 
1666  * PARAMETER(S):
1667  * [I] HWND : window handle
1668  * [I] INT : item index 
1669  *
1670  * RETURN:
1671  *   SELECT: TRUE 
1672  *   UNSELECT : FALSE
1673  */
1674 static BOOL LISTVIEW_ToggleSelection(HWND hwnd, INT nItem)
1675 {
1676   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1677   BOOL bResult;
1678   LVITEMA item;
1679
1680   ZeroMemory(&item,sizeof(LVITEMA));
1681   item.stateMask = LVIS_SELECTED;
1682
1683   if (LISTVIEW_IsSelected(hwnd,nItem))
1684   {
1685  
1686     LISTVIEW_SetItemState(hwnd,nItem,&item);
1687     bResult = FALSE;
1688   }
1689   else
1690   {
1691     item.state = LVIS_SELECTED;
1692     LISTVIEW_SetItemState(hwnd,nItem,&item);
1693     bResult = TRUE;
1694   }
1695
1696   LISTVIEW_SetItemFocus(hwnd, nItem);
1697   infoPtr->nSelectionMark = nItem;
1698
1699   return bResult;
1700 }
1701
1702 /***
1703  * DESCRIPTION:
1704  * Selects items based on view coordinates. 
1705  * 
1706  * PARAMETER(S):
1707  * [I] HWND : window handle
1708  * [I] RECT : selection rectangle  
1709  *
1710  * RETURN:
1711  * None
1712  */
1713 static VOID LISTVIEW_SetSelectionRect(HWND hwnd, RECT rcSelRect)
1714 {
1715   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1716   POINT ptItem;
1717   INT i;
1718   LVITEMA item;
1719
1720   ZeroMemory(&item,sizeof(LVITEMA));
1721   item.stateMask = LVIS_SELECTED;
1722
1723   for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1724   {
1725     LISTVIEW_GetItemPosition(hwnd, i, &ptItem);
1726
1727     if (PtInRect(&rcSelRect, ptItem) != FALSE)
1728     {
1729       item.state = LVIS_SELECTED;
1730       LISTVIEW_SetItemState(hwnd,i,&item);
1731     }
1732     else
1733     {
1734       item.state = 0;
1735       LISTVIEW_SetItemState(hwnd,i,&item);
1736     }
1737   }
1738 }
1739
1740 /***
1741  * DESCRIPTION:
1742  * Sets a single group selection.
1743  * 
1744  * PARAMETER(S):
1745  * [I] HWND : window handle
1746  * [I] INT : item index 
1747  *
1748  * RETURN:
1749  * None 
1750  */
1751 static VOID LISTVIEW_SetGroupSelection(HWND hwnd, INT nItem)
1752 {
1753   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1754   UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
1755   LVITEMA item;
1756
1757   ZeroMemory(&item,sizeof(LVITEMA));
1758   item.stateMask = LVIS_SELECTED;
1759
1760   if ((uView == LVS_LIST) || (uView == LVS_REPORT))
1761   {
1762     INT i;
1763     INT nFirst = min(infoPtr->nSelectionMark, nItem);
1764     INT nLast = max(infoPtr->nSelectionMark, nItem);
1765
1766     for (i = 0; i <= GETITEMCOUNT(infoPtr); i++)
1767     {
1768       if ((i < nFirst) || (i > nLast))
1769       {
1770         item.state = 0;
1771         LISTVIEW_SetItemState(hwnd,i,&item);
1772       }
1773       else
1774       {
1775         item.state = LVIS_SELECTED;
1776         LISTVIEW_SetItemState(hwnd,i,&item);
1777       }
1778     }
1779   }
1780   else
1781   {
1782     POINT ptItem;
1783     POINT ptSelMark;
1784     RECT rcSel;
1785     LISTVIEW_GetItemPosition(hwnd, nItem, &ptItem);
1786     LISTVIEW_GetItemPosition(hwnd, infoPtr->nSelectionMark, &ptSelMark);
1787     rcSel.left = min(ptSelMark.x, ptItem.x);
1788     rcSel.top = min(ptSelMark.y, ptItem.y);
1789     rcSel.right = max(ptSelMark.x, ptItem.x) + infoPtr->nItemWidth;
1790     rcSel.bottom = max(ptSelMark.y, ptItem.y) + infoPtr->nItemHeight;
1791     LISTVIEW_SetSelectionRect(hwnd, rcSel);
1792   }
1793
1794   LISTVIEW_SetItemFocus(hwnd, nItem);
1795 }
1796
1797 /***
1798  * DESCRIPTION:
1799  * Manages the item focus.
1800  * 
1801  * PARAMETER(S):
1802  * [I] HWND : window handle
1803  * [I] INT : item index 
1804  *
1805  * RETURN:
1806  *   TRUE : focused item changed
1807  *   FALSE : focused item has NOT changed
1808  */
1809 static BOOL LISTVIEW_SetItemFocus(HWND hwnd, INT nItem)
1810 {
1811   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1812   BOOL bResult = FALSE;
1813   LVITEMA lvItem;
1814
1815   if (infoPtr->nFocusedItem != nItem)
1816   {
1817     if (infoPtr->nFocusedItem >= 0)
1818     {
1819       INT oldFocus = infoPtr->nFocusedItem;
1820       bResult = TRUE;
1821       infoPtr->nFocusedItem = -1;
1822       ZeroMemory(&lvItem, sizeof(LVITEMA));
1823       lvItem.stateMask = LVIS_FOCUSED;
1824       ListView_SetItemState(hwnd, oldFocus, &lvItem); 
1825
1826     }
1827
1828     lvItem.state =  LVIS_FOCUSED;
1829     lvItem.stateMask = LVIS_FOCUSED;
1830     ListView_SetItemState(hwnd, nItem, &lvItem);
1831
1832     infoPtr->nFocusedItem = nItem;
1833     ListView_EnsureVisible(hwnd, nItem, FALSE);
1834   }
1835   
1836   return bResult; 
1837 }
1838
1839 /***
1840  * DESCRIPTION:
1841  * Sets a single selection.
1842  * 
1843  * PARAMETER(S):
1844  * [I] HWND : window handle
1845  * [I] INT : item index 
1846  *
1847  * RETURN:
1848  * None
1849  */
1850 static VOID LISTVIEW_SetSelection(HWND hwnd, INT nItem)
1851 {
1852   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1853   LVITEMA lvItem;
1854
1855   ZeroMemory(&lvItem, sizeof(LVITEMA));
1856   lvItem.stateMask = LVIS_FOCUSED;
1857   ListView_SetItemState(hwnd, infoPtr->nFocusedItem, &lvItem); 
1858
1859   LISTVIEW_RemoveAllSelections(hwnd);
1860
1861   lvItem.state =   LVIS_FOCUSED|LVIS_SELECTED;
1862   lvItem.stateMask = LVIS_FOCUSED|LVIS_SELECTED;
1863   ListView_SetItemState(hwnd, nItem, &lvItem);
1864
1865   infoPtr->nFocusedItem = nItem;
1866   infoPtr->nSelectionMark = nItem;
1867 }
1868
1869 /***
1870  * DESCRIPTION:
1871  * Set selection(s) with keyboard.
1872  * 
1873  * PARAMETER(S):
1874  * [I] HWND : window handle
1875  * [I] INT : item index 
1876  *
1877  * RETURN:
1878  *   SUCCESS : TRUE (needs to be repainted)
1879  *   FAILURE : FALSE (nothing has changed)
1880  */
1881 static BOOL LISTVIEW_KeySelection(HWND hwnd, INT nItem)
1882 {
1883   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1884   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1885   WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
1886   WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
1887   BOOL bResult = FALSE;
1888
1889   if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
1890   {
1891     if (lStyle & LVS_SINGLESEL)
1892     {
1893       bResult = TRUE;
1894       LISTVIEW_SetSelection(hwnd, nItem); 
1895       ListView_EnsureVisible(hwnd, nItem, FALSE);
1896     }
1897     else
1898     {
1899       if (wShift)
1900       {
1901         bResult = TRUE;
1902         LISTVIEW_SetGroupSelection(hwnd, nItem); 
1903       }
1904       else if (wCtrl)
1905       {
1906         bResult = LISTVIEW_SetItemFocus(hwnd, nItem);
1907       }
1908       else
1909       {
1910         bResult = TRUE;
1911         LISTVIEW_SetSelection(hwnd, nItem); 
1912         ListView_EnsureVisible(hwnd, nItem, FALSE);
1913       }
1914     }
1915   }
1916
1917   return bResult;
1918 }
1919
1920 /***
1921  * DESCRIPTION:
1922  * Called when the mouse is being actively tracked and has hovered for a specified
1923  * amount of time
1924  *
1925  * PARAMETER(S):
1926  * [I] HWND : window handle
1927  * [I] wParam : key indicator
1928  * [I] lParam : mouse position
1929  *
1930  * RETURN:
1931  *   0 if the message was processed, non-zero if there was an error
1932  *
1933  * INFO:
1934  * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
1935  * over the item for a certain period of time.
1936  * 
1937  */
1938 static LRESULT LISTVIEW_MouseHover(HWND hwnd, WPARAM wParam, LPARAM lParam)
1939 {
1940   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1941   POINT pt;
1942
1943   pt.x = (INT)LOWORD(lParam);
1944   pt.y = (INT)HIWORD(lParam);
1945
1946   if(infoPtr->dwExStyle & LVS_EX_TRACKSELECT) {
1947     /* select the item under the cursor */
1948     LISTVIEW_MouseSelection(hwnd, pt);
1949   }
1950
1951   return 0;
1952 }
1953
1954 /***
1955  * DESCRIPTION:
1956  * Called whenever WM_MOUSEMOVE is recieved.
1957  *
1958  * PARAMETER(S):
1959  * [I] HWND : window handle
1960  * [I] wParam : key indicators
1961  * [I] lParam : cursor position
1962  *
1963  * RETURN:
1964  *   0 if the message is processed, non-zero if there was an error
1965  */
1966 static LRESULT LISTVIEW_MouseMove(HWND hwnd, WPARAM wParam, LPARAM lParam)
1967 {
1968   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1969   TRACKMOUSEEVENT trackinfo;
1970  
1971   /* see if we are supposed to be tracking mouse hovering */
1972   if(infoPtr->dwExStyle & LVS_EX_TRACKSELECT) {
1973      /* fill in the trackinfo struct */
1974      trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
1975      trackinfo.dwFlags = TME_QUERY;
1976      trackinfo.hwndTrack = hwnd;
1977      trackinfo.dwHoverTime = infoPtr->dwHoverTime;
1978
1979      /* see if we are already tracking this hwnd */
1980      _TrackMouseEvent(&trackinfo);
1981
1982      if(!(trackinfo.dwFlags & TME_HOVER)) {
1983        trackinfo.dwFlags = TME_HOVER;
1984
1985        /* call TRACKMOUSEEVENT so we recieve WM_MOUSEHOVER messages */
1986        _TrackMouseEvent(&trackinfo);
1987     }
1988   }
1989   
1990   return 0;
1991 }
1992
1993 /***
1994  * DESCRIPTION:
1995  * Selects an item based on coordinates.
1996  * 
1997  * PARAMETER(S):
1998  * [I] HWND : window handle
1999  * [I] POINT : mouse click ccordinates
2000  *
2001  * RETURN:
2002  *   SUCCESS : item index
2003  *   FAILURE : -1
2004  */
2005 static LRESULT LISTVIEW_MouseSelection(HWND hwnd, POINT pt)
2006 {
2007   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
2008   RECT rcItem;
2009   INT i,topindex,bottomindex;
2010   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2011   UINT uView = lStyle & LVS_TYPEMASK;
2012
2013   topindex = ListView_GetTopIndex(hwnd);
2014   if (uView == LVS_REPORT)
2015   {
2016     bottomindex = topindex + LISTVIEW_GetCountPerColumn(hwnd) + 1;  
2017     bottomindex = min(bottomindex,GETITEMCOUNT(infoPtr));
2018   }
2019   else
2020   {
2021     bottomindex = GETITEMCOUNT(infoPtr);
2022   }
2023
2024   for (i = topindex; i < bottomindex; i++) 
2025   {
2026     rcItem.left = LVIR_SELECTBOUNDS;
2027     if (LISTVIEW_GetItemRect(hwnd, i, &rcItem) == TRUE)
2028     {
2029       if (PtInRect(&rcItem, pt) != FALSE)
2030       {
2031         return i;
2032       }
2033     }
2034   }
2035
2036   return -1;
2037 }
2038
2039 /***
2040  * DESCRIPTION:
2041  * Removes a column.
2042  * 
2043  * PARAMETER(S):
2044  * [IO] HDPA : dynamic pointer array handle
2045  * [I] INT : column index (subitem index)
2046  *
2047  * RETURN:
2048  *   SUCCCESS : TRUE
2049  *   FAILURE : FALSE
2050  */
2051 static BOOL LISTVIEW_RemoveColumn(HDPA hdpaItems, INT nSubItem)
2052 {
2053   BOOL bResult = TRUE;
2054   HDPA hdpaSubItems;
2055   INT i;
2056
2057   for (i = 0; i < hdpaItems->nItemCount; i++)
2058   {
2059     hdpaSubItems = (HDPA)DPA_GetPtr(hdpaItems, i);
2060     if (hdpaSubItems != NULL)
2061     {
2062       if (LISTVIEW_RemoveSubItem(hdpaSubItems, nSubItem) == FALSE)
2063       {
2064         bResult = FALSE;
2065       }
2066     }
2067   }
2068     
2069   return bResult;
2070 }
2071
2072 /***
2073  * DESCRIPTION:
2074  * Removes a subitem at a given position.
2075  * 
2076  * PARAMETER(S):
2077  * [IO] HDPA : dynamic pointer array handle
2078  * [I] INT : subitem index
2079  *
2080  * RETURN:
2081  *   SUCCCESS : TRUE
2082  *   FAILURE : FALSE
2083  */
2084 static BOOL LISTVIEW_RemoveSubItem(HDPA hdpaSubItems, INT nSubItem)
2085 {
2086   LISTVIEW_SUBITEM *lpSubItem;
2087   INT i;
2088
2089   for (i = 1; i < hdpaSubItems->nItemCount; i++)
2090   {
2091     lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2092     if (lpSubItem != NULL)
2093     {
2094       if (lpSubItem->iSubItem == nSubItem)
2095       {
2096         /* free string */
2097         if ((lpSubItem->pszText != NULL) && 
2098             (lpSubItem->pszText != LPSTR_TEXTCALLBACKA))
2099         {
2100           COMCTL32_Free(lpSubItem->pszText);
2101         }
2102         
2103         /* free item */
2104         COMCTL32_Free(lpSubItem);
2105
2106         /* free dpa memory */
2107         if (DPA_DeletePtr(hdpaSubItems, i) == NULL)
2108         {
2109           return FALSE;
2110         }
2111       }
2112       else if (lpSubItem->iSubItem > nSubItem)
2113       {
2114         return TRUE;
2115       }
2116     }
2117   }    
2118   
2119   return TRUE;
2120 }
2121
2122 /***
2123  * DESCRIPTION:
2124  * Compares the item information.
2125  * 
2126  * PARAMETER(S):
2127  * [I] LISTVIEW_ITEM *: destination item 
2128  * [I] LPLVITEM : source item 
2129  *
2130  * RETURN:
2131  *   SUCCCESS : TRUE (EQUAL)
2132  *   FAILURE : FALSE (NOT EQUAL)
2133  */
2134 static UINT LISTVIEW_GetItemChanges(LISTVIEW_ITEM *lpItem, LPLVITEMA lpLVItem)
2135 {
2136   UINT uChanged = 0;
2137
2138   if ((lpItem != NULL) && (lpLVItem != NULL))
2139   {
2140     if (lpLVItem->mask & LVIF_STATE)
2141     {
2142       if ((lpItem->state & lpLVItem->stateMask) != 
2143           (lpLVItem->state & lpLVItem->stateMask))
2144       {
2145         uChanged |= LVIF_STATE; 
2146       }
2147     }
2148
2149     if (lpLVItem->mask & LVIF_IMAGE)
2150     {
2151       if (lpItem->iImage != lpLVItem->iImage)
2152       {
2153         uChanged |= LVIF_IMAGE; 
2154       }
2155     }
2156   
2157     if (lpLVItem->mask & LVIF_PARAM)
2158     {
2159       if (lpItem->lParam != lpLVItem->lParam)
2160       {
2161         uChanged |= LVIF_PARAM; 
2162       }
2163     }
2164     
2165     if (lpLVItem->mask & LVIF_INDENT)
2166     {
2167       if (lpItem->iIndent != lpLVItem->iIndent)
2168       {
2169         uChanged |= LVIF_INDENT; 
2170       }
2171     }
2172
2173     if (lpLVItem->mask & LVIF_TEXT) 
2174     {
2175       if (lpLVItem->pszText == LPSTR_TEXTCALLBACKA) 
2176       {
2177         if (lpItem->pszText != LPSTR_TEXTCALLBACKA)
2178         {
2179           uChanged |= LVIF_TEXT; 
2180         }
2181       }
2182       else
2183       {
2184         if (lpItem->pszText == LPSTR_TEXTCALLBACKA)
2185         {
2186           uChanged |= LVIF_TEXT; 
2187         }
2188         else
2189         {
2190           if (lpLVItem->pszText)
2191           {
2192             if (lpItem->pszText)
2193             {
2194               if (strcmp(lpLVItem->pszText, lpItem->pszText) != 0)
2195               {
2196                 uChanged |= LVIF_TEXT;
2197               }
2198             }
2199             else
2200             {
2201               uChanged |= LVIF_TEXT;
2202             }
2203           }
2204           else
2205           {
2206             if (lpItem->pszText)
2207             {
2208               uChanged |= LVIF_TEXT;
2209             }
2210           }
2211         }
2212       }
2213     }
2214   }
2215   return uChanged;
2216 }
2217
2218 /***
2219  * DESCRIPTION:
2220  * Initializes item attributes.
2221  * 
2222  * PARAMETER(S):
2223  * [I] HWND : window handle
2224  * [O] LISTVIEW_ITEM *: destination item 
2225  * [I] LPLVITEM : source item 
2226  *
2227  * RETURN:
2228  *   SUCCCESS : TRUE
2229  *   FAILURE : FALSE
2230  */
2231 static BOOL LISTVIEW_InitItem(HWND hwnd, LISTVIEW_ITEM *lpItem, 
2232                               LPLVITEMA lpLVItem)
2233 {
2234   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2235   BOOL bResult = FALSE;
2236
2237   if ((lpItem != NULL) && (lpLVItem != NULL))
2238   {
2239     bResult = TRUE;
2240     
2241     if (lpLVItem->mask & LVIF_STATE)
2242     {
2243       lpItem->state &= ~lpLVItem->stateMask;
2244       lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
2245     }
2246     
2247     if (lpLVItem->mask & LVIF_IMAGE)
2248     {
2249       lpItem->iImage = lpLVItem->iImage;
2250     }
2251   
2252     if (lpLVItem->mask & LVIF_PARAM)
2253     {
2254       lpItem->lParam = lpLVItem->lParam;
2255     }
2256     
2257     if (lpLVItem->mask & LVIF_INDENT)
2258     {
2259       lpItem->iIndent = lpLVItem->iIndent;
2260     }
2261
2262     if (lpLVItem->mask & LVIF_TEXT) 
2263     {
2264       if (lpLVItem->pszText == LPSTR_TEXTCALLBACKA) 
2265       {
2266         if ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
2267         {
2268           return FALSE;
2269         }
2270
2271         if ((lpItem->pszText != NULL) && 
2272             (lpItem->pszText != LPSTR_TEXTCALLBACKA))
2273         {
2274           COMCTL32_Free(lpItem->pszText);
2275         }
2276     
2277         lpItem->pszText = LPSTR_TEXTCALLBACKA;
2278       }
2279       else 
2280       {
2281         if (lpItem->pszText == LPSTR_TEXTCALLBACKA)
2282         {
2283           lpItem->pszText = NULL;
2284         }
2285         
2286         bResult = Str_SetPtrA(&lpItem->pszText, lpLVItem->pszText);
2287       }
2288     }
2289   }
2290
2291   return bResult;
2292 }
2293
2294 /***
2295  * DESCRIPTION:
2296  * Initializes subitem attributes.
2297  *
2298  * NOTE: The documentation specifies that the operation fails if the user
2299  * tries to set the indent of a subitem.
2300  *
2301  * PARAMETER(S):
2302  * [I] HWND : window handle
2303  * [O] LISTVIEW_SUBITEM *: destination subitem
2304  * [I] LPLVITEM : source subitem
2305  *
2306  * RETURN:
2307  *   SUCCCESS : TRUE
2308  *   FAILURE : FALSE
2309  */
2310 static BOOL LISTVIEW_InitSubItem(HWND hwnd, LISTVIEW_SUBITEM *lpSubItem, 
2311                                    LPLVITEMA lpLVItem)
2312 {
2313   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2314   BOOL bResult = FALSE;
2315   
2316   if ((lpSubItem != NULL) && (lpLVItem != NULL))
2317   {
2318     if (!(lpLVItem->mask & LVIF_INDENT))
2319     {
2320       bResult = TRUE;
2321
2322       lpSubItem->iSubItem = lpLVItem->iSubItem;
2323
2324       if (lpLVItem->mask & LVIF_IMAGE)
2325       {
2326         lpSubItem->iImage = lpLVItem->iImage;
2327       }
2328       
2329       if (lpLVItem->mask & LVIF_TEXT) 
2330       {
2331         if (lpLVItem->pszText == LPSTR_TEXTCALLBACKA) 
2332         {
2333           if ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
2334           {
2335             return FALSE;
2336           } 
2337
2338           if ((lpSubItem->pszText != NULL) && 
2339               (lpSubItem->pszText != LPSTR_TEXTCALLBACKA))
2340           {
2341             COMCTL32_Free(lpSubItem->pszText);
2342           }
2343     
2344           lpSubItem->pszText = LPSTR_TEXTCALLBACKA;
2345         }
2346         else 
2347         {
2348           if (lpSubItem->pszText == LPSTR_TEXTCALLBACKA)
2349           {
2350             lpSubItem->pszText = NULL;
2351           }
2352         
2353           bResult = Str_SetPtrA(&lpSubItem->pszText, lpLVItem->pszText);
2354         }
2355       }
2356     }
2357   }
2358
2359   return bResult;
2360 }
2361
2362 /***
2363  * DESCRIPTION:
2364  * Adds a subitem at a given position (column index).
2365  * 
2366  * PARAMETER(S):
2367  * [I] HWND : window handle
2368  * [I] LPLVITEM : new subitem atttributes 
2369  *
2370  * RETURN:
2371  *   SUCCESS : TRUE
2372  *   FAILURE : FALSE
2373  */
2374 static BOOL LISTVIEW_AddSubItem(HWND hwnd, LPLVITEMA lpLVItem)
2375 {
2376   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
2377   LISTVIEW_SUBITEM *lpSubItem = NULL;
2378   BOOL bResult = FALSE;
2379   HDPA hdpaSubItems;
2380   INT nPosition, nItem;
2381   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2382
2383   if (lStyle & LVS_OWNERDATA)
2384     return FALSE;
2385
2386   if (lpLVItem != NULL)
2387   {
2388     hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2389     if (hdpaSubItems != NULL)
2390     {
2391       lpSubItem = (LISTVIEW_SUBITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM));
2392       if (lpSubItem != NULL)
2393       {
2394         ZeroMemory(lpSubItem, sizeof(LISTVIEW_SUBITEM));
2395         if (LISTVIEW_InitSubItem(hwnd, lpSubItem, lpLVItem) != FALSE)
2396         {
2397           nPosition = LISTVIEW_FindInsertPosition(hdpaSubItems, 
2398                                                   lpSubItem->iSubItem);
2399           nItem = DPA_InsertPtr(hdpaSubItems, nPosition, lpSubItem);
2400           if (nItem != -1)
2401           {
2402             bResult = TRUE;
2403           }            
2404         }
2405       }
2406     }
2407   }
2408   
2409   /* cleanup if unsuccessful */   
2410   if ((bResult == FALSE) && (lpSubItem != NULL))
2411   {
2412     COMCTL32_Free(lpSubItem);
2413   }
2414   
2415   return bResult;
2416 }
2417
2418 /***
2419  * DESCRIPTION:
2420  * Finds the dpa insert position (array index).
2421  * 
2422  * PARAMETER(S):
2423  * [I] HWND : window handle
2424  * [I] INT : subitem index
2425  *
2426  * RETURN:
2427  *   SUCCESS : TRUE
2428  *   FAILURE : FALSE
2429  */
2430 static INT LISTVIEW_FindInsertPosition(HDPA hdpaSubItems, INT nSubItem)
2431 {
2432   LISTVIEW_SUBITEM *lpSubItem;
2433   INT i;
2434
2435   for (i = 1; i < hdpaSubItems->nItemCount; i++)
2436   {
2437     lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2438     if (lpSubItem != NULL)
2439     {
2440       if (lpSubItem->iSubItem > nSubItem)
2441       {
2442         return i;
2443       }
2444     } 
2445   }
2446
2447   return hdpaSubItems->nItemCount;
2448 }
2449
2450 /***
2451  * DESCRIPTION:
2452  * Retrieves a listview subitem at a given position (column index).
2453  * 
2454  * PARAMETER(S):
2455  * [I] HWND : window handle
2456  * [I] INT : subitem index
2457  *
2458  * RETURN:
2459  *   SUCCESS : TRUE
2460  *   FAILURE : FALSE
2461  */
2462 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItem(HDPA hdpaSubItems, INT nSubItem)
2463 {
2464   LISTVIEW_SUBITEM *lpSubItem;
2465   INT i;
2466
2467   for (i = 1; i < hdpaSubItems->nItemCount; i++)
2468   {
2469     lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2470     if (lpSubItem != NULL)
2471     {
2472       if (lpSubItem->iSubItem == nSubItem)
2473       {
2474         return lpSubItem;
2475       }
2476       else if (lpSubItem->iSubItem > nSubItem)
2477       {
2478         return NULL;
2479       }  
2480     }
2481   }
2482   
2483   return NULL;
2484 }
2485
2486 /***
2487  * DESCRIPTION:
2488  * Sets item attributes.
2489  * 
2490  * PARAMETER(S):
2491  * [I] HWND : window handle
2492  * [I] LPLVITEM : new item atttributes 
2493  *
2494  * RETURN:
2495  *   SUCCESS : TRUE
2496  *   FAILURE : FALSE
2497  */
2498 static BOOL LISTVIEW_SetItem(HWND hwnd, LPLVITEMA lpLVItem)
2499 {
2500   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
2501   BOOL bResult = FALSE;
2502   HDPA hdpaSubItems;
2503   LISTVIEW_ITEM *lpItem;
2504   NMLISTVIEW nmlv;
2505   LONG lCtrlId = GetWindowLongA(hwnd, GWL_ID);
2506   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2507   UINT uChanged;
2508   UINT uView = lStyle & LVS_TYPEMASK;
2509   INT item_width;
2510   RECT rcItem;
2511
2512   if (lStyle & LVS_OWNERDATA)
2513   {
2514     if ((lpLVItem->iSubItem == 0)&&(lpLVItem->mask == LVIF_STATE))
2515     {
2516       LVITEMA itm;
2517
2518       ZeroMemory(&itm,sizeof(LVITEMA));
2519       itm.mask = LVIF_STATE | LVIF_PARAM;
2520       itm.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
2521       itm.iItem = lpLVItem->iItem;
2522       itm.iSubItem = 0;
2523       ListView_GetItemA(hwnd,&itm);
2524       
2525
2526       ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
2527       nmlv.hdr.hwndFrom = hwnd;
2528       nmlv.hdr.idFrom = lCtrlId;
2529       nmlv.hdr.code = LVN_ITEMCHANGING;
2530       nmlv.uNewState = lpLVItem->state;
2531       nmlv.uOldState = itm.state;
2532       nmlv.uChanged = LVIF_STATE;
2533       nmlv.lParam = itm.lParam;
2534       nmlv.iItem = lpLVItem->iItem;
2535
2536       if ((itm.state & lpLVItem->stateMask) != 
2537           (lpLVItem->state & lpLVItem->stateMask))
2538       {
2539         /* send LVN_ITEMCHANGING notification */
2540         if (!ListView_LVNotify(GetParent(hwnd), lCtrlId, &nmlv))
2541         {
2542           if (lpLVItem->stateMask & LVIS_FOCUSED)
2543           {
2544             if (lpLVItem->state & LVIS_FOCUSED)
2545               infoPtr->nFocusedItem = lpLVItem->iItem;
2546             else if (infoPtr->nFocusedItem == lpLVItem->iItem)
2547               infoPtr->nFocusedItem = -1;
2548           }
2549           if (lpLVItem->stateMask & LVIS_SELECTED)
2550           {
2551             if (lpLVItem->state & LVIS_SELECTED)
2552             {
2553                 if (lStyle & LVS_SINGLESEL)
2554                 {
2555                   LISTVIEW_RemoveAllSelections(hwnd);
2556                 }
2557               LISTVIEW_AddSelectionRange(hwnd,lpLVItem->iItem,lpLVItem->iItem);
2558             }
2559             else
2560               LISTVIEW_RemoveSelectionRange(hwnd,lpLVItem->iItem,
2561                                             lpLVItem->iItem);
2562           }
2563
2564           nmlv.hdr.code = LVN_ITEMCHANGED;
2565
2566           ListView_LVNotify(GetParent(hwnd), lCtrlId, &nmlv);
2567
2568           rcItem.left = LVIR_BOUNDS;
2569           LISTVIEW_GetItemRect(hwnd, lpLVItem->iItem, &rcItem);
2570           InvalidateRect(hwnd, &rcItem, TRUE);
2571         }
2572       }
2573       return TRUE;
2574     }
2575     return FALSE;
2576   }
2577
2578   if (lpLVItem != NULL)
2579   {
2580     if (lpLVItem->iSubItem == 0)
2581     {
2582       hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2583       if (hdpaSubItems != NULL && hdpaSubItems != (HDPA)-1)
2584       {
2585         lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, lpLVItem->iSubItem);
2586         if (lpItem != NULL)
2587         {
2588           ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
2589           nmlv.hdr.hwndFrom = hwnd;
2590           nmlv.hdr.idFrom = lCtrlId;
2591           nmlv.hdr.code = LVN_ITEMCHANGING;
2592           nmlv.lParam = lpItem->lParam;
2593           uChanged = LISTVIEW_GetItemChanges(lpItem, lpLVItem);
2594           if (uChanged != 0)
2595           {
2596             if (uChanged & LVIF_STATE)
2597             {
2598               nmlv.uNewState = lpLVItem->state & lpLVItem->stateMask;
2599               nmlv.uOldState = lpItem->state & lpLVItem->stateMask;
2600
2601               if (nmlv.uNewState & LVIS_SELECTED)
2602               {
2603                 /*
2604                  * This is redundant if called through SetSelection
2605                  *
2606                  * however is required if the used directly calls SetItem
2607                  * to set the selection.
2608                  */
2609                 if (lStyle & LVS_SINGLESEL)
2610                 {
2611                   LISTVIEW_RemoveAllSelections(hwnd);
2612                 }
2613
2614                 LISTVIEW_AddSelectionRange(hwnd,lpLVItem->iItem,
2615                                              lpLVItem->iItem);
2616               }
2617               else if (lpLVItem->stateMask & LVIS_SELECTED)
2618               {
2619                 LISTVIEW_RemoveSelectionRange(hwnd,lpLVItem->iItem,
2620                                               lpLVItem->iItem);
2621               }
2622               if (nmlv.uNewState & LVIS_FOCUSED)
2623               {
2624                 /*
2625                  * This is a fun hoop to jump to try to catch if
2626                  * the user is calling us directly to call focus or if
2627                  * this function is being called as a result of a 
2628                  * SetItemFocus call. 
2629                  */
2630                 if (infoPtr->nFocusedItem >= 0)
2631                   LISTVIEW_SetItemFocus(hwnd, lpLVItem->iItem);
2632               }           
2633             }
2634             
2635             nmlv.uChanged = uChanged;
2636             nmlv.iItem = lpLVItem->iItem;
2637             nmlv.lParam = lpItem->lParam;
2638             /* send LVN_ITEMCHANGING notification */
2639             ListView_LVNotify(GetParent(hwnd), lCtrlId, &nmlv);
2640
2641             /* copy information */
2642             bResult = LISTVIEW_InitItem(hwnd, lpItem, lpLVItem);
2643
2644             /* if LVS_LIST or LVS_SMALLICON, update the width of the items
2645                based on the width of the items text */
2646             if((uView == LVS_LIST) || (uView == LVS_SMALLICON))
2647             {
2648               item_width = LISTVIEW_GetStringWidthA(hwnd, lpItem->pszText);
2649
2650               if(item_width > infoPtr->nItemWidth)
2651                   infoPtr->nItemWidth = item_width;
2652             }
2653
2654             /* send LVN_ITEMCHANGED notification */
2655             nmlv.hdr.code = LVN_ITEMCHANGED;
2656             ListView_LVNotify(GetParent(hwnd), lCtrlId, &nmlv);
2657           }
2658           else
2659           {
2660             bResult = TRUE;
2661           }
2662           
2663           if (uChanged)
2664           {
2665             rcItem.left = LVIR_BOUNDS;
2666             LISTVIEW_GetItemRect(hwnd, lpLVItem->iItem, &rcItem);
2667             InvalidateRect(hwnd, &rcItem, TRUE);
2668           }
2669         }
2670       }
2671     }
2672   }
2673
2674   return bResult;
2675 }
2676
2677 /***
2678  * DESCRIPTION:
2679  * Sets subitem attributes.
2680  * 
2681  * PARAMETER(S):
2682  * [I] HWND : window handle
2683  * [I] LPLVITEM : new subitem atttributes 
2684  *
2685  * RETURN:
2686  *   SUCCESS : TRUE
2687  *   FAILURE : FALSE
2688  */
2689 static BOOL LISTVIEW_SetSubItem(HWND hwnd, LPLVITEMA lpLVItem)
2690 {
2691   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
2692   BOOL bResult = FALSE;
2693   HDPA hdpaSubItems;
2694   LISTVIEW_SUBITEM *lpSubItem;
2695   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2696   RECT rcItem;
2697
2698   if (lStyle & LVS_OWNERDATA)
2699     return FALSE;
2700
2701   if (lpLVItem != NULL)
2702   {
2703     if (lpLVItem->iSubItem > 0)
2704     {
2705       hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2706       if (hdpaSubItems != NULL)
2707       {
2708         /* set subitem only if column is present */
2709         if (Header_GetItemCount(infoPtr->hwndHeader) > lpLVItem->iSubItem)
2710         {
2711           lpSubItem = LISTVIEW_GetSubItem(hdpaSubItems, lpLVItem->iSubItem);
2712           if (lpSubItem != NULL)
2713           {
2714             bResult = LISTVIEW_InitSubItem(hwnd, lpSubItem, lpLVItem);
2715           }
2716           else
2717           {
2718             bResult = LISTVIEW_AddSubItem(hwnd, lpLVItem);
2719           }
2720           
2721           rcItem.left = LVIR_BOUNDS;
2722           LISTVIEW_GetItemRect(hwnd, lpLVItem->iItem, &rcItem);
2723           InvalidateRect(hwnd, &rcItem, FALSE);
2724         } 
2725       }
2726     }
2727   }
2728
2729   return bResult;
2730 }
2731
2732 /***
2733  * DESCRIPTION:
2734  * Retrieves the index of the item at coordinate (0, 0) of the client area.
2735  * 
2736  * PARAMETER(S):
2737  * [I] HWND : window handle
2738  *
2739  * RETURN:
2740  * item index
2741  */
2742 static INT LISTVIEW_GetTopIndex(HWND hwnd)
2743 {
2744   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2745   UINT uView = lStyle & LVS_TYPEMASK;
2746   INT nItem = 0;
2747   SCROLLINFO scrollInfo;
2748
2749   ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
2750   scrollInfo.cbSize = sizeof(SCROLLINFO);
2751   scrollInfo.fMask = SIF_POS;
2752   
2753   if (uView == LVS_LIST)
2754   {
2755     if (lStyle & WS_HSCROLL)
2756     {
2757       if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
2758       {
2759         nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(hwnd);
2760       }
2761     }
2762   }
2763   else if (uView == LVS_REPORT)
2764   {
2765     if (lStyle & WS_VSCROLL)
2766     {
2767       if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
2768       {
2769         nItem = scrollInfo.nPos;
2770       }
2771     }
2772   }
2773   
2774   return nItem;
2775 }
2776
2777 /***
2778  * DESCRIPTION:
2779  * Draws a subitem.
2780  * 
2781  * PARAMETER(S):
2782  * [I] HWND : window handle
2783  * [I] HDC : device context handle
2784  * [I] INT : item index
2785  * [I] INT : subitem index
2786  * [I] RECT * : clipping rectangle
2787  *
2788  * RETURN:
2789  * None
2790  */
2791 static VOID LISTVIEW_DrawSubItem(HWND hwnd, HDC hdc, INT nItem, INT nSubItem, 
2792                                  RECT rcItem, BOOL Selected)
2793 {
2794   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0); 
2795   CHAR szDispText[DISP_TEXT_SIZE];
2796   LVITEMA lvItem;
2797   UINT textoutOptions = ETO_CLIPPED | ETO_OPAQUE;
2798   RECT rcTemp;
2799
2800   TRACE("(hwnd=%x, hdc=%x, nItem=%d, nSubItem=%d)\n", hwnd, hdc,
2801         nItem, nSubItem);
2802
2803   /* get information needed for drawing the item */
2804   ZeroMemory(&lvItem, sizeof(LVITEMA));
2805   lvItem.mask = LVIF_TEXT;
2806   lvItem.iItem = nItem;
2807   lvItem.iSubItem = nSubItem;
2808   lvItem.cchTextMax = DISP_TEXT_SIZE;
2809   lvItem.pszText = szDispText;
2810   LISTVIEW_GetItemA(hwnd, &lvItem, TRUE);
2811
2812   /* redraw the background of the item */
2813   rcTemp = rcItem;
2814   if(infoPtr->nColumnCount == (nSubItem + 1))
2815     rcTemp.right = infoPtr->rcList.right;
2816   else
2817     rcTemp.right+=WIDTH_PADDING;
2818
2819   LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
2820
2821   /* set item colors */
2822   if (ListView_GetItemState(hwnd,nItem,LVIS_SELECTED) && Selected)
2823   {
2824     if (infoPtr->bFocus)
2825     {
2826       SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
2827       SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
2828     }
2829     else
2830     {
2831       SetBkColor(hdc, GetSysColor(COLOR_3DFACE));
2832       SetTextColor(hdc, GetSysColor(COLOR_BTNTEXT));
2833     }
2834   }
2835   else
2836   {
2837     if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
2838     {
2839        SetBkMode(hdc, TRANSPARENT);
2840        textoutOptions &= ~ETO_OPAQUE;
2841     }
2842     else
2843     {
2844       SetBkMode(hdc, OPAQUE);
2845       SetBkColor(hdc, infoPtr->clrTextBk);
2846     }
2847
2848     SetTextColor(hdc, infoPtr->clrText);
2849   }
2850
2851   ExtTextOutA(hdc, rcItem.left, rcItem.top, textoutOptions, 
2852               &rcItem, lvItem.pszText, lstrlenA(lvItem.pszText), NULL);
2853
2854   if (Selected)
2855   {
2856     /* fill in the gap */
2857     RECT rec;
2858     if (nSubItem < Header_GetItemCount(infoPtr->hwndHeader)-1)
2859     {
2860       CopyRect(&rec,&rcItem);
2861       rec.left = rec.right;
2862       rec.right = rec.left+REPORT_MARGINX;
2863       ExtTextOutA(hdc, rec.left , rec.top, textoutOptions,
2864         &rec, NULL, 0, NULL);
2865     }
2866     CopyRect(&rec,&rcItem);
2867     rec.right = rec.left;
2868     rec.left = rec.left - REPORT_MARGINX;
2869     ExtTextOutA(hdc, rec.left , rec.top, textoutOptions,
2870     &rec, NULL, 0, NULL);
2871   }
2872 }
2873
2874
2875 /***
2876  * DESCRIPTION:
2877  * Draws an item.
2878  * 
2879  * PARAMETER(S):
2880  * [I] HWND : window handle
2881  * [I] HDC : device context handle
2882  * [I] INT : item index
2883  * [I] RECT * : clipping rectangle
2884  *
2885  * RETURN:
2886  * None
2887  */
2888 static VOID LISTVIEW_DrawItem(HWND hwnd, HDC hdc, INT nItem, RECT rcItem, BOOL FullSelect, RECT* SuggestedFocus)
2889 {
2890   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0); 
2891   CHAR szDispText[DISP_TEXT_SIZE];
2892   INT nLabelWidth;
2893   LVITEMA lvItem;
2894   INT nMixMode;
2895   DWORD dwBkColor;
2896   DWORD dwTextColor,dwTextX;
2897   BOOL bImage = FALSE;
2898   INT   iBkMode = -1;
2899   UINT  textoutOptions = ETO_OPAQUE | ETO_CLIPPED;
2900   RECT rcTemp;
2901
2902   TRACE("(hwnd=%x, hdc=%x, nItem=%d)\n", hwnd, hdc, nItem);
2903
2904
2905   /* get information needed for drawing the item */
2906   ZeroMemory(&lvItem, sizeof(LVITEMA));
2907   lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE | LVIF_INDENT;
2908   lvItem.stateMask = LVIS_SELECTED |  LVIS_STATEIMAGEMASK;
2909   lvItem.iItem = nItem;
2910   lvItem.iSubItem = 0;
2911   lvItem.cchTextMax = DISP_TEXT_SIZE;
2912   lvItem.pszText = szDispText;
2913   LISTVIEW_GetItemA(hwnd, &lvItem, TRUE);
2914
2915   /* redraw the background of the item */
2916   rcTemp = rcItem;
2917   if(infoPtr->nColumnCount == (nItem + 1))
2918     rcTemp.right = infoPtr->rcList.right;
2919   else
2920     rcTemp.right+=WIDTH_PADDING;
2921
2922   LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
2923
2924   /* do indent */
2925   if (lvItem.iIndent>0 && infoPtr->iconSize.cx > 0)
2926   {
2927     rcItem.left += infoPtr->iconSize.cx * lvItem.iIndent; 
2928
2929     if (SuggestedFocus)
2930       SuggestedFocus->left += infoPtr->iconSize.cx * lvItem.iIndent;
2931   } 
2932
2933   /* state icons */
2934   if (infoPtr->himlState != NULL)
2935   {
2936      UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12; 
2937      if (uStateImage > 0)
2938      {
2939        ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, rcItem.left, 
2940                       rcItem.top, ILD_NORMAL);
2941      }
2942  
2943      rcItem.left += infoPtr->iconSize.cx; 
2944      if (SuggestedFocus)
2945        SuggestedFocus->left += infoPtr->iconSize.cx;
2946      bImage = TRUE;
2947   }
2948   
2949   /* small icons */
2950   if (infoPtr->himlSmall != NULL)
2951   {
2952     if ((lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus != FALSE) &&
2953         (lvItem.iImage>=0))
2954     {
2955       ImageList_SetBkColor(infoPtr->himlSmall, CLR_NONE);
2956       ImageList_Draw(infoPtr->himlSmall, lvItem.iImage, hdc, rcItem.left, 
2957                      rcItem.top, ILD_SELECTED);
2958     }
2959     else if (lvItem.iImage>=0)
2960     {
2961       ImageList_SetBkColor(infoPtr->himlSmall, CLR_NONE);
2962       ImageList_Draw(infoPtr->himlSmall, lvItem.iImage, hdc, rcItem.left, 
2963                      rcItem.top, ILD_NORMAL);
2964     }
2965     
2966     rcItem.left += infoPtr->iconSize.cx; 
2967
2968     if (SuggestedFocus)
2969       SuggestedFocus->left += infoPtr->iconSize.cx;
2970     bImage = TRUE;
2971   }
2972
2973   /* Don't bother painting item being edited */
2974   if (infoPtr->hwndEdit && lvItem.state & LVIS_FOCUSED && !FullSelect)
2975       return;
2976
2977   if ((lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus != FALSE))
2978   {
2979     /* set item colors */ 
2980     dwBkColor = SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
2981     dwTextColor = SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
2982     /* set raster mode */
2983     nMixMode = SetROP2(hdc, R2_XORPEN);
2984   }
2985   else if ((GetWindowLongA(hwnd, GWL_STYLE) & LVS_SHOWSELALWAYS) && 
2986            (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus == FALSE))
2987   {
2988     dwBkColor = SetBkColor(hdc, GetSysColor(COLOR_3DFACE));
2989     dwTextColor = SetTextColor(hdc, GetSysColor(COLOR_BTNTEXT));
2990     /* set raster mode */
2991     nMixMode = SetROP2(hdc, R2_COPYPEN);
2992   }
2993   else
2994   {
2995     /* set item colors */
2996     if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
2997     {
2998       dwBkColor = GetBkColor(hdc);
2999       iBkMode = SetBkMode(hdc, TRANSPARENT);
3000       textoutOptions &= ~ETO_OPAQUE;
3001     }
3002     else
3003     {
3004       dwBkColor = SetBkColor(hdc, infoPtr->clrTextBk);
3005       iBkMode = SetBkMode(hdc, OPAQUE);
3006     }
3007
3008     dwTextColor = SetTextColor(hdc, infoPtr->clrText);
3009     /* set raster mode */
3010     nMixMode = SetROP2(hdc, R2_COPYPEN);
3011   }
3012   
3013   nLabelWidth = ListView_GetStringWidthA(hwnd, lvItem.pszText);
3014   if (rcItem.left + nLabelWidth < rcItem.right)
3015   {
3016     if (!FullSelect)
3017       rcItem.right = rcItem.left + nLabelWidth + TRAILING_PADDING;
3018     if (bImage)
3019       rcItem.right += IMAGE_PADDING;
3020   }
3021   
3022   /* draw label */  
3023   dwTextX = rcItem.left + 1;
3024   if (bImage)
3025     dwTextX += IMAGE_PADDING;
3026
3027   if (lvItem.pszText)
3028     ExtTextOutA(hdc, dwTextX, rcItem.top, textoutOptions,
3029                 &rcItem, lvItem.pszText, lstrlenA(lvItem.pszText), NULL);
3030
3031   if ((FullSelect)&&(Header_GetItemCount(infoPtr->hwndHeader) > 1))
3032   {
3033     /* fill in the gap */
3034     RECT rec;
3035     CopyRect(&rec,&rcItem);
3036     rec.left = rec.right;
3037     rec.right = rec.left+REPORT_MARGINX;
3038     ExtTextOutA(hdc, rec.left , rec.top, textoutOptions, 
3039     &rec, NULL, 0, NULL);
3040   }
3041   
3042   if (!FullSelect)
3043       CopyRect(SuggestedFocus,&rcItem);
3044
3045   if (nMixMode != 0)
3046   {
3047     SetROP2(hdc, R2_COPYPEN);
3048     SetBkColor(hdc, dwBkColor);
3049     SetTextColor(hdc, dwTextColor);
3050     if (iBkMode != -1)
3051       SetBkMode(hdc, iBkMode);
3052   }
3053 }
3054
3055 /***
3056  * DESCRIPTION:
3057  * Draws an item when in large icon display mode.
3058  * 
3059  * PARAMETER(S):
3060  * [I] HWND : window handle
3061  * [I] HDC : device context handle
3062  * [I] LISTVIEW_ITEM * : item
3063  * [I] INT : item index
3064  * [I] RECT * : clipping rectangle
3065  *
3066  * RETURN:
3067  * None
3068  */
3069 static VOID LISTVIEW_DrawLargeItem(HWND hwnd, HDC hdc, INT nItem, RECT rcItem,
3070                                    RECT *SuggestedFocus)
3071 {
3072   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0); 
3073   CHAR szDispText[DISP_TEXT_SIZE];
3074   INT nDrawPosX = rcItem.left;
3075   INT nLabelWidth, rcWidth;
3076   TEXTMETRICA tm;
3077   LVITEMA lvItem;
3078   UINT textoutOptions = ETO_CLIPPED | ETO_OPAQUE;
3079   RECT rcTemp;
3080
3081   TRACE("(hwnd=%x, hdc=%x, nItem=%d, left=%d, top=%d, right=%d, \
3082 bottom=%d)\n", hwnd, hdc, nItem, rcItem.left, rcItem.top, rcItem.right, 
3083         rcItem.bottom);
3084
3085   /* get information needed for drawing the item */
3086   ZeroMemory(&lvItem, sizeof(LVITEMA));
3087   lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE;
3088   lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3089   lvItem.iItem = nItem;
3090   lvItem.iSubItem = 0;
3091   lvItem.cchTextMax = DISP_TEXT_SIZE;
3092   lvItem.pszText = szDispText;
3093   LISTVIEW_GetItemA(hwnd, &lvItem, TRUE);
3094
3095   /* redraw the background of the item */
3096   rcTemp = rcItem;
3097   if(infoPtr->nColumnCount == (nItem + 1))
3098     rcTemp.right = infoPtr->rcList.right;
3099   else
3100     rcTemp.right+=WIDTH_PADDING;
3101
3102   LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3103
3104   if (lvItem.state & LVIS_SELECTED)
3105   {
3106     /* set item colors */ 
3107     SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
3108     SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
3109     /* set raster mode */
3110     SetROP2(hdc, R2_XORPEN);
3111   }
3112   else
3113   {
3114     /* set item colors */
3115     if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
3116     {
3117        SetBkMode(hdc, TRANSPARENT);
3118        textoutOptions &= ~ETO_OPAQUE;
3119     }
3120     else
3121     {
3122       SetBkMode(hdc, OPAQUE);
3123       SetBkColor(hdc, infoPtr->clrTextBk);
3124     }
3125
3126     SetTextColor(hdc, infoPtr->clrText);
3127     /* set raster mode */
3128     SetROP2(hdc, R2_COPYPEN);
3129   }
3130
3131   if (infoPtr->himlNormal != NULL)
3132   {
3133     rcItem.top += ICON_TOP_PADDING;
3134     nDrawPosX += (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
3135     if ((lvItem.state & LVIS_SELECTED) && (lvItem.iImage>=0))
3136     {
3137       ImageList_Draw(infoPtr->himlNormal, lvItem.iImage, hdc, nDrawPosX, 
3138                      rcItem.top, ILD_SELECTED);
3139     }
3140     else if (lvItem.iImage>=0)
3141     {
3142       ImageList_Draw(infoPtr->himlNormal, lvItem.iImage, hdc, nDrawPosX, 
3143                      rcItem.top, ILD_NORMAL);
3144     }
3145   }
3146
3147   /* Don't bother painting item being edited */
3148   if (infoPtr->hwndEdit && lvItem.state & LVIS_FOCUSED)
3149     return;
3150
3151   InflateRect(&rcItem, -(2*CAPTION_BORDER), 0);
3152   rcItem.top += infoPtr->iconSize.cy + ICON_BOTTOM_PADDING; 
3153   nLabelWidth = ListView_GetStringWidthA(hwnd, lvItem.pszText);
3154   GetTextMetricsA(hdc, &tm);
3155
3156   /* append an ellipse ('...') if the caption won't fit in the rect */
3157   rcWidth = max(0, rcItem.right - rcItem.left);
3158   if (nLabelWidth > rcWidth)
3159   {
3160       INT i, len, eos, nCharsFit;
3161       /* give or take a couple, how many average sized chars would fit? */
3162       nCharsFit = tm.tmAveCharWidth > 0 ? (rcWidth/tm.tmAveCharWidth)+2 : 0; 
3163       /* place the ellipse accordingly, without overrunning the buffer */
3164       len = strlen(szDispText);
3165       eos = min((nCharsFit > 1 && nCharsFit < len) ? nCharsFit+3 : len+2,
3166                 sizeof(szDispText)-1);
3167       
3168       nLabelWidth = ListView_GetStringWidthA(hwnd, szDispText);
3169       while ((nLabelWidth > rcWidth) && (eos > 3))
3170       {
3171          for (i = 1; i < 4; i++)
3172             szDispText[eos-i] = '.'; 
3173          /* shift the ellipse one char to the left for each iteration */
3174          szDispText[eos--] = '\0'; 
3175          nLabelWidth = ListView_GetStringWidthA(hwnd, szDispText);
3176       }
3177   }
3178
3179   InflateRect(&rcItem, 2*CAPTION_BORDER, 0);
3180   nDrawPosX = infoPtr->iconSpacing.cx - 2*CAPTION_BORDER - nLabelWidth;
3181   if (nDrawPosX > 1)
3182   {
3183     rcItem.left += nDrawPosX / 2;
3184     rcItem.right = rcItem.left + nLabelWidth + 2*CAPTION_BORDER;
3185   }
3186   else
3187   {
3188     rcItem.left += 1;
3189     rcItem.right = rcItem.left + infoPtr->iconSpacing.cx - 1;
3190   }
3191
3192   /* draw label */  
3193   rcItem.bottom = rcItem.top + tm.tmHeight + HEIGHT_PADDING; 
3194
3195   ExtTextOutA(hdc, rcItem.left + CAPTION_BORDER, rcItem.top, textoutOptions, 
3196               &rcItem, lvItem.pszText, lstrlenA(lvItem.pszText), NULL);
3197         
3198
3199   CopyRect(SuggestedFocus,&rcItem);
3200 }
3201
3202 /***
3203  * DESCRIPTION:
3204  * Draws listview items when in report display mode.
3205  * 
3206  * PARAMETER(S):
3207  * [I] HWND : window handle
3208  * [I] HDC : device context handle 
3209  *
3210  * RETURN:
3211  * None
3212  */
3213 static VOID LISTVIEW_RefreshReport(HWND hwnd, HDC hdc, DWORD cdmode)
3214 {
3215   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd,0);
3216   SCROLLINFO scrollInfo;
3217   INT nDrawPosY = infoPtr->rcList.top;
3218   INT nColumnCount;
3219   RECT rcItem, rcTemp;
3220   INT  j;
3221   INT nItem;
3222   INT nLast;
3223   BOOL FullSelected;
3224   DWORD cditemmode = CDRF_DODEFAULT;
3225   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
3226
3227   ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
3228   scrollInfo.cbSize = sizeof(SCROLLINFO);
3229   scrollInfo.fMask = SIF_POS;
3230
3231   nItem = ListView_GetTopIndex(hwnd);
3232
3233   /* add 1 for displaying a partial item at the bottom */
3234   nLast = nItem + LISTVIEW_GetCountPerColumn(hwnd) + 1;
3235   nLast = min(nLast, GETITEMCOUNT(infoPtr));
3236
3237   /* send cache hint notification */
3238   if (GetWindowLongA(hwnd,GWL_STYLE) & LVS_OWNERDATA)
3239   {
3240     NMLVCACHEHINT nmlv;
3241
3242     nmlv.hdr.hwndFrom = hwnd;
3243     nmlv.hdr.idFrom = GetWindowLongA(hwnd,GWL_ID);
3244     nmlv.hdr.code = LVN_ODCACHEHINT;
3245     nmlv.iFrom = nItem;
3246     nmlv.iTo   = nLast;
3247
3248     SendMessageA(GetParent(hwnd), WM_NOTIFY, (WPARAM)nmlv.hdr.idFrom,
3249                  (LPARAM)&nmlv);
3250   }
3251
3252   nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
3253   infoPtr->nColumnCount = nColumnCount; /* update nColumnCount */
3254   FullSelected = infoPtr->dwExStyle & LVS_EX_FULLROWSELECT;
3255
3256   /* clear the background of any part of the control that doesn't contain items */
3257   SubtractRect(&rcTemp, &infoPtr->rcList, &infoPtr->rcView);
3258   LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3259
3260   /* nothing to draw */
3261   if(GETITEMCOUNT(infoPtr) == 0)
3262     return;
3263
3264   for (; nItem < nLast; nItem++)
3265   {
3266     RECT SuggestedFocusRect;
3267
3268     /* Do Owner Draw */
3269     if (lStyle & LVS_OWNERDRAWFIXED)
3270     {
3271         UINT uID = GetWindowLongA( hwnd, GWL_ID);
3272         DRAWITEMSTRUCT dis;
3273         LVITEMA item;
3274         RECT br;
3275
3276         TRACE("Owner Drawn\n");    
3277         dis.CtlType = ODT_LISTVIEW;
3278         dis.CtlID = uID;
3279         dis.itemID = nItem;
3280         dis.itemAction = ODA_DRAWENTIRE;
3281         dis.itemState = 0;
3282        
3283         if (LISTVIEW_IsSelected(hwnd,nItem)) dis.itemState|=ODS_SELECTED;
3284         if (nItem==infoPtr->nFocusedItem)   dis.itemState|=ODS_FOCUS;
3285
3286         dis.hwndItem = hwnd;
3287         dis.hDC = hdc;
3288
3289         Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
3290
3291         dis.rcItem.left =0; 
3292         dis.rcItem.right = max(dis.rcItem.left, br.right);
3293         dis.rcItem.top = nDrawPosY;
3294         dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3295        
3296         ZeroMemory(&item,sizeof(LVITEMA));
3297         item.iItem = nItem;
3298         item.mask = LVIF_PARAM;
3299         ListView_GetItemA(hwnd,&item);
3300
3301         dis.itemData = item.lParam;
3302
3303         if (SendMessageA(GetParent(hwnd),WM_DRAWITEM,(WPARAM)uID,(LPARAM)&dis))
3304         {
3305           nDrawPosY += infoPtr->nItemHeight;
3306           continue;
3307         }
3308     }
3309
3310     if (FullSelected)
3311     {
3312       RECT ir,br;
3313
3314       Header_GetItemRect(infoPtr->hwndHeader, 0, &ir);
3315       Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
3316
3317       ir.left += REPORT_MARGINX;
3318       ir.right = max(ir.left, br.right - REPORT_MARGINX);
3319       ir.top = nDrawPosY;
3320       ir.bottom = ir.top + infoPtr->nItemHeight;
3321       
3322       CopyRect(&SuggestedFocusRect,&ir);
3323     }
3324
3325     for (j = 0; j < nColumnCount; j++)
3326     {
3327       if (cdmode & CDRF_NOTIFYITEMDRAW)
3328         cditemmode = LISTVIEW_SendCustomDrawItemNotify (hwnd, hdc, nItem, j,
3329                                                       CDDS_ITEMPREPAINT);
3330       if (cditemmode & CDRF_SKIPDEFAULT)
3331         continue;
3332
3333       Header_GetItemRect(infoPtr->hwndHeader, j, &rcItem);
3334
3335       rcItem.left += REPORT_MARGINX;
3336       rcItem.right = max(rcItem.left, rcItem.right - REPORT_MARGINX);
3337       rcItem.top = nDrawPosY;
3338       rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3339
3340       /* Offset the Scroll Bar Pos */
3341       if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE) 
3342       {
3343         rcItem.left -= (scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE);
3344         rcItem.right -= (scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE);
3345       }
3346
3347       if (j == 0)
3348       {
3349         LISTVIEW_DrawItem(hwnd, hdc, nItem, rcItem, FullSelected,
3350                           &SuggestedFocusRect); 
3351       }
3352       else 
3353       {
3354         LISTVIEW_DrawSubItem(hwnd, hdc, nItem, j, rcItem, 
3355                               FullSelected);
3356       }
3357
3358       if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3359         LISTVIEW_SendCustomDrawItemNotify(hwnd, hdc, nItem, 0, 
3360                                       CDDS_ITEMPOSTPAINT);
3361     }
3362     /*
3363      * Draw Focus Rect
3364      */
3365     if (LISTVIEW_GetItemState(hwnd,nItem,LVIS_FOCUSED) && infoPtr->bFocus)
3366     {
3367       BOOL rop=FALSE;
3368       if (FullSelected && LISTVIEW_GetItemState(hwnd,nItem,LVIS_SELECTED))
3369         rop = SetROP2(hdc, R2_XORPEN);
3370
3371       Rectangle(hdc, SuggestedFocusRect.left, SuggestedFocusRect.top, 
3372                 SuggestedFocusRect.right,SuggestedFocusRect.bottom); 
3373
3374       if (rop)
3375         SetROP2(hdc, R2_COPYPEN);
3376     }
3377     nDrawPosY += infoPtr->nItemHeight;
3378   }
3379 }
3380
3381 /***
3382  * DESCRIPTION:
3383  * Retrieves the number of items that can fit vertically in the client area.
3384  * 
3385  * PARAMETER(S):
3386  * [I] HWND : window handle
3387  *
3388  * RETURN:
3389  * Number of items per row.
3390  */
3391 static INT LISTVIEW_GetCountPerRow(HWND hwnd)
3392 {
3393   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd,0);
3394   UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3395   INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
3396   INT nCountPerRow = 1;
3397
3398   if (nListWidth > 0)
3399   {
3400     if (uView == LVS_REPORT)
3401     {
3402       nCountPerRow = 1;
3403     }
3404     else
3405     {
3406       nCountPerRow =  nListWidth / infoPtr->nItemWidth;
3407       if (nCountPerRow == 0)
3408       {
3409         nCountPerRow = 1;
3410       }
3411     }
3412   }
3413
3414   return nCountPerRow;
3415 }
3416
3417 /***
3418  * DESCRIPTION:
3419  * Retrieves the number of items that can fit horizontally in the client 
3420  * area.
3421  * 
3422  * PARAMETER(S):
3423  * [I] HWND : window handle
3424  *
3425  * RETURN:
3426  * Number of items per column.
3427  */
3428 static INT LISTVIEW_GetCountPerColumn(HWND hwnd)
3429 {
3430   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd,0);
3431   INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3432   INT nCountPerColumn = 1;
3433
3434   if (nListHeight > 0)
3435   {
3436     nCountPerColumn =  nListHeight / infoPtr->nItemHeight;
3437     if (nCountPerColumn == 0)
3438     {
3439       nCountPerColumn = 1;
3440     }
3441   }
3442
3443   return nCountPerColumn;
3444 }
3445
3446 /***
3447  * DESCRIPTION:
3448  * Retrieves the number of columns needed to display all the items when in 
3449  * list display mode.
3450  * 
3451  * PARAMETER(S):
3452  * [I] HWND : window handle
3453  *
3454  * RETURN:
3455  * Number of columns.
3456  */
3457 static INT LISTVIEW_GetColumnCount(HWND hwnd)
3458 {
3459   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
3460   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
3461   INT nColumnCount = 0;
3462
3463   if ((lStyle & LVS_TYPEMASK) == LVS_LIST)
3464   {
3465     if (infoPtr->rcList.right % infoPtr->nItemWidth == 0)
3466     {
3467       nColumnCount = infoPtr->rcList.right / infoPtr->nItemWidth;
3468     }
3469     else
3470     {
3471       nColumnCount = infoPtr->rcList.right / infoPtr->nItemWidth + 1;
3472     }
3473   }
3474
3475   return nColumnCount;
3476 }  
3477   
3478
3479 /***
3480  * DESCRIPTION:
3481  * Draws listview items when in list display mode.
3482  * 
3483  * PARAMETER(S):
3484  * [I] HWND : window handle
3485  * [I] HDC : device context handle 
3486  *
3487  * RETURN:
3488  * None
3489  */
3490 static VOID LISTVIEW_RefreshList(HWND hwnd, HDC hdc, DWORD cdmode)
3491 {
3492   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
3493   RECT rcItem, FocusRect, rcTemp;
3494   INT i, j;
3495   INT nItem;
3496   INT nColumnCount;
3497   INT nCountPerColumn;
3498   INT nItemWidth = infoPtr->nItemWidth;
3499   INT nItemHeight = infoPtr->nItemHeight;
3500   DWORD cditemmode = CDRF_DODEFAULT;
3501
3502   /* get number of fully visible columns */
3503   nColumnCount = LISTVIEW_GetColumnCount(hwnd);
3504   infoPtr->nColumnCount = nColumnCount;
3505   nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
3506   nItem = ListView_GetTopIndex(hwnd);
3507
3508   /* paint the background of the control that doesn't contain any items */
3509   SubtractRect(&rcTemp, &infoPtr->rcList, &infoPtr->rcView);
3510   LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3511
3512   /* nothing to draw, return here */
3513   if(GETITEMCOUNT(infoPtr) == 0)
3514     return;
3515
3516   for (i = 0; i < nColumnCount; i++)
3517   {
3518     for (j = 0; j < nCountPerColumn; j++, nItem++)
3519     {
3520       if (nItem >= GETITEMCOUNT(infoPtr))
3521         return;
3522
3523       if (cdmode & CDRF_NOTIFYITEMDRAW)
3524         cditemmode = LISTVIEW_SendCustomDrawItemNotify (hwnd, hdc, nItem, 0,
3525                                                       CDDS_ITEMPREPAINT);
3526       if (cditemmode & CDRF_SKIPDEFAULT)
3527         continue;
3528
3529       rcItem.top = j * nItemHeight;
3530       rcItem.left = i * nItemWidth;
3531       rcItem.bottom = rcItem.top + nItemHeight;
3532       rcItem.right = rcItem.left + nItemWidth;
3533       LISTVIEW_DrawItem(hwnd, hdc, nItem, rcItem, FALSE, &FocusRect);
3534       /*
3535        * Draw Focus Rect
3536        */
3537       if (LISTVIEW_GetItemState(hwnd,nItem,LVIS_FOCUSED) && infoPtr->bFocus)
3538       Rectangle(hdc, FocusRect.left, FocusRect.top, 
3539                 FocusRect.right,FocusRect.bottom);
3540
3541       if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3542         LISTVIEW_SendCustomDrawItemNotify(hwnd, hdc, nItem, 0, 
3543                                           CDDS_ITEMPOSTPAINT);
3544  
3545     }
3546   }
3547 }
3548
3549 /***
3550  * DESCRIPTION:
3551  * Draws listview items when in icon or small icon display mode.
3552  * 
3553  * PARAMETER(S):
3554  * [I] HWND : window handle
3555  * [I] HDC : device context handle 
3556  *
3557  * RETURN:
3558  * None
3559  */
3560 static VOID LISTVIEW_RefreshIcon(HWND hwnd, HDC hdc, BOOL bSmall, DWORD cdmode)
3561 {
3562   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
3563   POINT ptPosition;
3564   POINT ptOrigin;
3565   RECT rcItem, SuggestedFocus, rcTemp;
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   LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3575
3576   /* nothing to draw, return here */
3577   if(GETITEMCOUNT(infoPtr) == 0)
3578     return;
3579
3580   LISTVIEW_GetOrigin(hwnd, &ptOrigin);
3581   for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
3582   {
3583     if (cdmode & CDRF_NOTIFYITEMDRAW)
3584       cditemmode = LISTVIEW_SendCustomDrawItemNotify (hwnd, hdc, i, 0,
3585                                                       CDDS_ITEMPREPAINT);
3586     if (cditemmode & CDRF_SKIPDEFAULT)
3587         continue;
3588
3589     LISTVIEW_GetItemPosition(hwnd, i, &ptPosition);
3590     ptPosition.x += ptOrigin.x;
3591     ptPosition.y += ptOrigin.y;
3592       
3593     if (ptPosition.y + infoPtr->nItemHeight > infoPtr->rcList.top)
3594     {
3595       if (ptPosition.x + infoPtr->nItemWidth > infoPtr->rcList.left)
3596       {
3597         if (ptPosition.y < infoPtr->rcList.bottom)
3598         {
3599           if (ptPosition.x < infoPtr->rcList.right)
3600           {
3601             rcItem.top = ptPosition.y;
3602             rcItem.left = ptPosition.x;
3603             rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3604             rcItem.right = rcItem.left + infoPtr->nItemWidth;
3605             if (bSmall == FALSE)
3606             {
3607               LISTVIEW_DrawLargeItem(hwnd, hdc, i, rcItem, &SuggestedFocus);
3608             }
3609             else
3610             {
3611               LISTVIEW_DrawItem(hwnd, hdc, i, rcItem, FALSE, &SuggestedFocus);
3612             }
3613             /*
3614              * Draw Focus Rect
3615              */
3616             if (LISTVIEW_GetItemState(hwnd,i,LVIS_FOCUSED) && 
3617                 infoPtr->bFocus)
3618               Rectangle(hdc, SuggestedFocus.left, SuggestedFocus.top, 
3619                         SuggestedFocus.right,SuggestedFocus.bottom); 
3620           }
3621         }
3622       }
3623     }
3624     if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3625         LISTVIEW_SendCustomDrawItemNotify(hwnd, hdc, i, 0, 
3626                                           CDDS_ITEMPOSTPAINT);
3627   }
3628 }
3629
3630 /***
3631  * DESCRIPTION:
3632  * Draws listview items.
3633  * 
3634  * PARAMETER(S):
3635  * [I] HWND : window handle
3636  * [I] HDC : device context handle 
3637  *
3638  * RETURN:
3639  * NoneX
3640  */
3641 static VOID LISTVIEW_Refresh(HWND hwnd, HDC hdc)
3642 {
3643   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
3644   UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3645   HFONT hOldFont;
3646   HPEN hPen, hOldPen;
3647   DWORD cdmode;
3648   RECT rect;
3649
3650   GetClientRect(hwnd, &rect);
3651   cdmode = LISTVIEW_SendCustomDrawNotify(hwnd,CDDS_PREPAINT,hdc,rect);
3652
3653   if (cdmode == CDRF_SKIPDEFAULT) return;
3654
3655   /* select font */
3656   hOldFont = SelectObject(hdc, infoPtr->hFont);
3657
3658   /* select the dotted pen (for drawing the focus box) */
3659   hPen = CreatePen(PS_ALTERNATE, 1, 0);
3660   hOldPen = SelectObject(hdc, hPen);
3661
3662   /* select transparent brush (for drawing the focus box) */
3663   SelectObject(hdc, GetStockObject(NULL_BRUSH)); 
3664
3665   if (uView == LVS_LIST)
3666   {
3667     LISTVIEW_RefreshList(hwnd, hdc, cdmode); 
3668   }
3669   else if (uView == LVS_REPORT)
3670   {
3671     LISTVIEW_RefreshReport(hwnd, hdc, cdmode);
3672   }
3673   else if (uView == LVS_SMALLICON)
3674   {
3675     LISTVIEW_RefreshIcon(hwnd, hdc, TRUE, cdmode);
3676   }
3677   else if (uView == LVS_ICON)
3678   {
3679     LISTVIEW_RefreshIcon(hwnd, hdc, FALSE, cdmode);
3680   }
3681
3682   /* unselect objects */
3683   SelectObject(hdc, hOldFont);
3684   SelectObject(hdc, hOldPen);
3685   
3686   /* delete pen */
3687   DeleteObject(hPen);
3688  
3689   if (cdmode & CDRF_NOTIFYPOSTPAINT)
3690       LISTVIEW_SendCustomDrawNotify(hwnd, CDDS_POSTPAINT, hdc, rect);
3691 }
3692
3693
3694 /***
3695  * DESCRIPTION:
3696  * Calculates the approximate width and height of a given number of items.
3697  * 
3698  * PARAMETER(S):
3699  * [I] HWND : window handle
3700  * [I] INT : number of items
3701  * [I] INT : width
3702  * [I] INT : height
3703  *
3704  * RETURN:
3705  * Returns a DWORD. The width in the low word and the height in high word.
3706  */
3707 static LRESULT LISTVIEW_ApproximateViewRect(HWND hwnd, INT nItemCount, 
3708                                             WORD wWidth, WORD wHeight)
3709 {
3710   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
3711   UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3712   INT nItemCountPerColumn = 1;
3713   INT nColumnCount = 0;
3714   DWORD dwViewRect = 0;
3715
3716   if (nItemCount == -1)
3717   {
3718     nItemCount = GETITEMCOUNT(infoPtr);
3719   }
3720
3721   if (uView == LVS_LIST)
3722   {
3723     if (wHeight == 0xFFFF)
3724     {
3725       /* use current height */
3726       wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3727     }
3728
3729     if (wHeight < infoPtr->nItemHeight)
3730     {
3731       wHeight = infoPtr->nItemHeight;
3732     }
3733
3734     if (nItemCount > 0)
3735     {
3736       if (infoPtr->nItemHeight > 0)
3737       {
3738         nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
3739         if (nItemCountPerColumn == 0)
3740         {
3741           nItemCountPerColumn = 1;
3742         }
3743         
3744         if (nItemCount % nItemCountPerColumn != 0)
3745         {
3746           nColumnCount = nItemCount / nItemCountPerColumn;
3747         }
3748         else
3749         {
3750           nColumnCount = nItemCount / nItemCountPerColumn + 1;
3751         }
3752       }
3753     }
3754
3755     /* Microsoft padding magic */
3756     wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
3757     wWidth = nColumnCount * infoPtr->nItemWidth + 2;
3758
3759     dwViewRect = MAKELONG(wWidth, wHeight);
3760   }
3761   else if (uView == LVS_REPORT)
3762   {
3763     /* TO DO */
3764   }
3765   else if (uView == LVS_SMALLICON)
3766   {
3767     /* TO DO */
3768   }
3769   else if (uView == LVS_ICON)
3770   {
3771     /* TO DO */
3772   }
3773  
3774   return dwViewRect;
3775 }
3776
3777 /***
3778  * DESCRIPTION:
3779  * Arranges listview items in icon display mode.
3780  * 
3781  * PARAMETER(S):
3782  * [I] HWND : window handle
3783  * [I] INT : alignment code
3784  *
3785  * RETURN:
3786  *   SUCCESS : TRUE
3787  *   FAILURE : FALSE
3788  */
3789 static LRESULT LISTVIEW_Arrange(HWND hwnd, INT nAlignCode)
3790 {
3791   UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK; 
3792   BOOL bResult = FALSE;
3793
3794   if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
3795   {
3796     switch (nAlignCode)
3797     {
3798     case LVA_ALIGNLEFT:
3799       /* TO DO */
3800       break;
3801     case LVA_ALIGNTOP:
3802       /* TO DO */
3803       break;
3804     case LVA_DEFAULT:
3805       /* TO DO */
3806       break;
3807     case LVA_SNAPTOGRID:
3808       /* TO DO */
3809       break;
3810     }
3811   }
3812
3813   return bResult;
3814 }
3815
3816 /* << LISTVIEW_CreateDragImage >> */
3817
3818
3819 /***
3820  * DESCRIPTION:
3821  * Removes all listview items and subitems.
3822  * 
3823  * PARAMETER(S):
3824  * [I] HWND : window handle
3825  *
3826  * RETURN:
3827  *   SUCCESS : TRUE
3828  *   FAILURE : FALSE
3829  */
3830 static LRESULT LISTVIEW_DeleteAllItems(HWND hwnd)
3831 {
3832   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
3833   LONG lCtrlId = GetWindowLongA(hwnd, GWL_ID);
3834   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
3835   UINT uView = lStyle & LVS_TYPEMASK;
3836   LISTVIEW_ITEM *lpItem;
3837   LISTVIEW_SUBITEM *lpSubItem;
3838   NMLISTVIEW nmlv;
3839   BOOL bSuppress;
3840   BOOL bResult = FALSE;
3841   INT i;
3842   INT j;
3843   HDPA hdpaSubItems;
3844
3845   TRACE("(hwnd=%x,)\n", hwnd);
3846   LISTVIEW_RemoveAllSelections(hwnd);
3847
3848   if (lStyle & LVS_OWNERDATA)
3849   {
3850     infoPtr->hdpaItems->nItemCount = 0;
3851     InvalidateRect(hwnd, NULL, TRUE);
3852     return TRUE;
3853   }
3854
3855   if (GETITEMCOUNT(infoPtr) > 0)
3856   {
3857     /* initialize memory */
3858     ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3859     
3860     /* send LVN_DELETEALLITEMS notification */
3861     nmlv.hdr.hwndFrom = hwnd;
3862     nmlv.hdr.idFrom = lCtrlId;
3863     nmlv.hdr.code = LVN_DELETEALLITEMS;
3864     nmlv.iItem = -1;
3865
3866     /* verify if subsequent LVN_DELETEITEM notifications should be 
3867        suppressed */
3868     bSuppress = ListView_LVNotify(GetParent(hwnd), lCtrlId, &nmlv);
3869
3870     for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
3871     {
3872       hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
3873       if (hdpaSubItems != NULL)
3874       {
3875         for (j = 1; j < hdpaSubItems->nItemCount; j++)
3876         {
3877           lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, j);
3878           if (lpSubItem != NULL)
3879           {
3880             /* free subitem string */
3881             if ((lpSubItem->pszText != NULL) && 
3882                 (lpSubItem->pszText != LPSTR_TEXTCALLBACKA))
3883             {
3884               COMCTL32_Free(lpSubItem->pszText);
3885             }
3886         
3887             /* free subitem */
3888             COMCTL32_Free(lpSubItem);
3889           }    
3890         }
3891
3892         lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
3893         if (lpItem != NULL)
3894         {
3895           if (bSuppress == FALSE)
3896           {
3897             /* send LVN_DELETEITEM notification */
3898             nmlv.hdr.code = LVN_DELETEITEM;
3899             nmlv.iItem = i;
3900             nmlv.lParam = lpItem->lParam;
3901             ListView_LVNotify(GetParent(hwnd), lCtrlId, &nmlv);
3902           }
3903
3904           /* free item string */
3905           if ((lpItem->pszText != NULL) && 
3906               (lpItem->pszText != LPSTR_TEXTCALLBACKA))
3907           {
3908             COMCTL32_Free(lpItem->pszText);
3909           }
3910       
3911           /* free item */
3912           COMCTL32_Free(lpItem);
3913         }
3914         
3915         DPA_Destroy(hdpaSubItems);
3916       }
3917     }
3918
3919     /* reinitialize listview memory */
3920     bResult = DPA_DeleteAllPtrs(infoPtr->hdpaItems);
3921     
3922     /* align items (set position of each item) */
3923     if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
3924     {
3925       if (lStyle & LVS_ALIGNLEFT)
3926       {
3927         LISTVIEW_AlignLeft(hwnd);
3928       }
3929       else
3930       {
3931         LISTVIEW_AlignTop(hwnd);
3932       }
3933     }
3934     
3935     LISTVIEW_UpdateScroll(hwnd);
3936
3937     /* invalidate client area (optimization needed) */
3938     InvalidateRect(hwnd, NULL, TRUE);
3939   }
3940   
3941   return bResult;
3942 }
3943
3944 /***
3945  * DESCRIPTION:
3946  * Removes a column from the listview control.
3947  * 
3948  * PARAMETER(S):
3949  * [I] HWND : window handle
3950  * [I] INT : column index
3951  *
3952  * RETURN:
3953  *   SUCCESS : TRUE
3954  *   FAILURE : FALSE
3955  */
3956 static LRESULT LISTVIEW_DeleteColumn(HWND hwnd, INT nColumn)
3957 {
3958   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
3959   UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3960   UINT uOwnerData = GetWindowLongA(hwnd, GWL_STYLE) & LVS_OWNERDATA;
3961   BOOL bResult = FALSE;
3962
3963   if (Header_DeleteItem(infoPtr->hwndHeader, nColumn) != FALSE)
3964   {
3965     if (!uOwnerData)
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 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, TRUE);
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  * [I] subitem : fill out iSubItem.
6181  *
6182  * RETURN:
6183  *   SUCCESS : item index
6184  *   FAILURE : -1
6185  */
6186 static INT LISTVIEW_HitTestItem(
6187   HWND hwnd, LPLVHITTESTINFO lpHitTestInfo, BOOL subitem
6188 ) {
6189   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6190   RECT rcItem;
6191   INT i,topindex,bottomindex;
6192   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
6193   UINT uView = lStyle & LVS_TYPEMASK;
6194
6195
6196   TRACE("(hwnd=%x, x=%ld, y=%ld)\n", hwnd, lpHitTestInfo->pt.x,
6197         lpHitTestInfo->pt.y);
6198
6199   topindex = ListView_GetTopIndex(hwnd);
6200   if (uView == LVS_REPORT)
6201   {
6202     bottomindex = topindex + LISTVIEW_GetCountPerColumn(hwnd) + 1;  
6203     bottomindex = min(bottomindex,GETITEMCOUNT(infoPtr));
6204   }
6205   else
6206   {
6207     bottomindex = GETITEMCOUNT(infoPtr);
6208   }
6209
6210   for (i = topindex; i < bottomindex; i++)
6211   {
6212     rcItem.left = LVIR_BOUNDS;
6213     if (LISTVIEW_GetItemRect(hwnd, i, &rcItem) != FALSE)
6214     {
6215       if (PtInRect(&rcItem, lpHitTestInfo->pt) != FALSE)
6216       {
6217         rcItem.left = LVIR_ICON;
6218         if (LISTVIEW_GetItemRect(hwnd, i, &rcItem) != FALSE)
6219         {
6220           if (PtInRect(&rcItem, lpHitTestInfo->pt) != FALSE)
6221           {
6222             lpHitTestInfo->flags = LVHT_ONITEMICON;
6223             lpHitTestInfo->iItem = i;
6224             if (subitem) lpHitTestInfo->iSubItem = 0;
6225             return i;
6226           }
6227         }
6228       
6229         rcItem.left = LVIR_LABEL;
6230         if (LISTVIEW_GetItemRect(hwnd, i, &rcItem) != FALSE)
6231         {
6232           if (PtInRect(&rcItem, lpHitTestInfo->pt) != FALSE)
6233           {
6234             lpHitTestInfo->flags = LVHT_ONITEMLABEL;
6235             lpHitTestInfo->iItem = i;
6236             if (subitem) lpHitTestInfo->iSubItem = 0;
6237             return i;
6238           }
6239         }
6240         
6241         lpHitTestInfo->flags = LVHT_ONITEMSTATEICON;
6242         lpHitTestInfo->iItem = i;
6243         if (subitem) lpHitTestInfo->iSubItem = 0;
6244         return i;
6245       }
6246     }
6247   }
6248      
6249   lpHitTestInfo->flags = LVHT_NOWHERE;
6250
6251   return -1;
6252 }
6253
6254 /***
6255  * DESCRIPTION:
6256  * Determines which listview item is located at the specified position.
6257  * 
6258  * PARAMETER(S):
6259  * [I] HWND : window handle
6260  * [IO} LPLVHITTESTINFO : hit test information
6261  *
6262  * RETURN:
6263  *   SUCCESS : item index
6264  *   FAILURE : -1
6265  */
6266 static LRESULT LISTVIEW_HitTest(HWND hwnd, LPLVHITTESTINFO lpHitTestInfo)
6267 {
6268   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6269   INT nItem = -1;
6270
6271   lpHitTestInfo->flags = 0;
6272
6273   if (infoPtr->rcList.left > lpHitTestInfo->pt.x)
6274   {
6275     lpHitTestInfo->flags = LVHT_TOLEFT;
6276   }
6277   else if (infoPtr->rcList.right < lpHitTestInfo->pt.x) 
6278   {
6279     lpHitTestInfo->flags = LVHT_TORIGHT;
6280   }
6281   if (infoPtr->rcList.top > lpHitTestInfo->pt.y)
6282   {
6283     lpHitTestInfo->flags |= LVHT_ABOVE;
6284   }
6285   else if (infoPtr->rcList.bottom < lpHitTestInfo->pt.y) 
6286   {
6287     lpHitTestInfo->flags |= LVHT_BELOW;
6288   }
6289
6290   if (lpHitTestInfo->flags == 0)
6291   {
6292     /* NOTE (mm 20001022): We must not allow iSubItem to be touched, for 
6293      * an app might pass only a structure with space up to iItem!
6294      * (MS Office 97 does that for instance in the file open dialog)
6295      */
6296     nItem = LISTVIEW_HitTestItem(hwnd, lpHitTestInfo, FALSE);
6297   }
6298   
6299   return nItem;
6300 }
6301
6302 /***
6303  * DESCRIPTION:
6304  * Inserts a new column.
6305  * 
6306  * PARAMETER(S):
6307  * [I] HWND : window handle
6308  * [I] INT : column index
6309  * [I] LPLVCOLUMNA : column information
6310  *
6311  * RETURN:
6312  *   SUCCESS : new column index
6313  *   FAILURE : -1
6314  */
6315 static LRESULT LISTVIEW_InsertColumnA(HWND hwnd, INT nColumn, 
6316                                       LPLVCOLUMNA lpColumn)
6317 {
6318   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6319   HDITEMA hdi;
6320   INT nNewColumn = -1;
6321
6322   TRACE("(hwnd=%x, nColumn=%d, lpColumn=%p)\n",hwnd, nColumn, 
6323         lpColumn);
6324
6325   if (lpColumn != NULL) 
6326   {
6327     /* initialize memory */
6328     ZeroMemory(&hdi, sizeof(HDITEMA));
6329
6330     if (lpColumn->mask & LVCF_FMT) 
6331     {
6332       /* format member is valid */
6333       hdi.mask |= HDI_FORMAT;
6334
6335       /* set text alignment (leftmost column must be left-aligned) */
6336       if (nColumn == 0)
6337       {
6338         hdi.fmt |= HDF_LEFT;
6339       }
6340       else
6341       {
6342         if (lpColumn->fmt & LVCFMT_LEFT)
6343         {
6344           hdi.fmt |= HDF_LEFT;
6345         }
6346         else if (lpColumn->fmt & LVCFMT_RIGHT)
6347         {
6348           hdi.fmt |= HDF_RIGHT;
6349         }
6350         else if (lpColumn->fmt & LVCFMT_CENTER)
6351         {
6352           hdi.fmt |= HDF_CENTER;
6353         }
6354       }
6355  
6356       if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6357       {
6358         hdi.fmt |= HDF_BITMAP_ON_RIGHT;
6359         /* ??? */
6360       }
6361
6362       if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6363       {
6364         /* ??? */
6365       }
6366       
6367       if (lpColumn->fmt & LVCFMT_IMAGE)
6368       {
6369         hdi.fmt |= HDF_IMAGE;
6370         hdi.iImage = I_IMAGECALLBACK;
6371       }
6372     }
6373
6374     if (lpColumn->mask & LVCF_WIDTH) 
6375     {
6376       hdi.mask |= HDI_WIDTH;
6377       hdi.cxy = lpColumn->cx;
6378     }
6379   
6380     if (lpColumn->mask & LVCF_TEXT) 
6381     {
6382       hdi.mask |= HDI_TEXT | HDI_FORMAT;
6383       hdi.pszText = lpColumn->pszText;
6384       hdi.cchTextMax = lstrlenA(lpColumn->pszText);
6385       hdi.fmt |= HDF_STRING;
6386     }
6387   
6388     if (lpColumn->mask & LVCF_IMAGE) 
6389     {
6390       hdi.mask |= HDI_IMAGE;
6391       hdi.iImage = lpColumn->iImage;
6392     }
6393
6394     if (lpColumn->mask & LVCF_ORDER) 
6395     {
6396       hdi.mask |= HDI_ORDER;
6397       hdi.iOrder = lpColumn->iOrder;
6398     }
6399
6400     /* insert item in header control */
6401     nNewColumn = SendMessageA(infoPtr->hwndHeader, HDM_INSERTITEMA,
6402                              (WPARAM)nColumn, (LPARAM)&hdi);
6403     
6404     /* Need to reset the item width when inserting a new column */
6405     infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
6406
6407     LISTVIEW_UpdateScroll(hwnd);
6408     InvalidateRect(hwnd, NULL, FALSE);
6409   }
6410
6411   return nNewColumn;
6412 }
6413
6414 static LRESULT LISTVIEW_InsertColumnW(HWND hwnd, INT nColumn, 
6415                                       LPLVCOLUMNW lpColumn)
6416 {
6417   LVCOLUMNA     lvca;
6418   LRESULT               lres;
6419       
6420   memcpy(&lvca,lpColumn,sizeof(lvca));
6421   if (lpColumn->mask & LVCF_TEXT)
6422     lvca.pszText = HEAP_strdupWtoA(GetProcessHeap(),0,lpColumn->pszText);
6423   lres = LISTVIEW_InsertColumnA(hwnd,nColumn,&lvca);
6424   if (lpColumn->mask & LVCF_TEXT)
6425     HeapFree(GetProcessHeap(),0,lvca.pszText);
6426   return lres;
6427 }
6428
6429 /* LISTVIEW_InsertCompare:  callback routine for comparing pszText members of the LV_ITEMS
6430    in a LISTVIEW on insert.  Passed to DPA_Sort in LISTVIEW_InsertItem.
6431    This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6432    and not during the processing of a LVM_SORTITEMS message. Applications should provide
6433    their own sort proc. when sending LVM_SORTITEMS.
6434 */
6435 /* Platform SDK:
6436     (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6437         if:
6438           LVS_SORTXXX must be specified, 
6439           LVS_OWNERDRAW is not set, 
6440           <item>.pszText is not LPSTR_TEXTCALLBACK.
6441
6442     (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices 
6443     are sorted based on item text..." 
6444 */
6445 static INT WINAPI LISTVIEW_InsertCompare(  LPVOID first, LPVOID second,  LPARAM lParam)
6446 {
6447   HDPA  hdpa_first = (HDPA) first;
6448   HDPA  hdpa_second = (HDPA) second;
6449   LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( hdpa_first, 0 );
6450   LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( hdpa_second, 0 );
6451   LONG lStyle = GetWindowLongA((HWND) lParam, GWL_STYLE);
6452   INT  cmpv = lstrcmpA( lv_first->pszText, lv_second->pszText );
6453   /* if we're sorting descending, negate the return value */
6454   return (lStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
6455 }
6456
6457 /***
6458  * nESCRIPTION:
6459  * Inserts a new item in the listview control.
6460  * 
6461  * PARAMETER(S):
6462  * [I] HWND : window handle
6463  * [I] LPLVITEMA : item information
6464  *
6465  * RETURN:
6466  *   SUCCESS : new item index
6467  *   FAILURE : -1
6468  */
6469 static LRESULT LISTVIEW_InsertItemA(HWND hwnd, LPLVITEMA lpLVItem)
6470 {
6471   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6472   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
6473   UINT uView = lStyle & LVS_TYPEMASK;
6474   LONG lCtrlId = GetWindowLongA(hwnd, GWL_ID);
6475   NMLISTVIEW nmlv;
6476   INT nItem = -1;
6477   HDPA hdpaSubItems;
6478   INT nItemWidth = 0;
6479   LISTVIEW_ITEM *lpItem = NULL;
6480
6481   TRACE("(hwnd=%x,lpLVItem=%p)\n", hwnd, lpLVItem);
6482
6483   if (lStyle & LVS_OWNERDATA)
6484   {
6485     nItem = infoPtr->hdpaItems->nItemCount;
6486     infoPtr->hdpaItems->nItemCount ++;
6487     return nItem;
6488   }
6489
6490   if (lpLVItem != NULL)
6491   {
6492     /* make sure it's not a subitem; cannot insert a subitem */
6493     if (lpLVItem->iSubItem == 0)
6494     {
6495       lpItem = (LISTVIEW_ITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_ITEM));
6496       if (lpItem != NULL)
6497       {
6498         ZeroMemory(lpItem, sizeof(LISTVIEW_ITEM));
6499         if (LISTVIEW_InitItem(hwnd, lpItem, lpLVItem) != FALSE)
6500         {
6501           /* insert item in listview control data structure */
6502           hdpaSubItems = DPA_Create(8);
6503           if (hdpaSubItems != NULL)
6504           {
6505             nItem = DPA_InsertPtr(hdpaSubItems, 0, lpItem);
6506             if (nItem != -1)
6507             {
6508               if ( ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
6509                       && !(lStyle & LVS_OWNERDRAWFIXED)
6510                       && (LPSTR_TEXTCALLBACKA != lpLVItem->pszText) )
6511               {
6512                 /* Insert the item in the proper sort order based on the pszText
6513                   member. See comments for LISTVIEW_InsertCompare() for greater detail */
6514                   nItem = DPA_InsertPtr( infoPtr->hdpaItems, 
6515                           GETITEMCOUNT( infoPtr ) + 1, hdpaSubItems );
6516                   DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, hwnd );
6517                   nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
6518               }
6519               else
6520               {
6521                 nItem = DPA_InsertPtr(infoPtr->hdpaItems, lpLVItem->iItem, 
6522                                     hdpaSubItems);
6523               }
6524               if (nItem != -1)
6525               {
6526                 LISTVIEW_ShiftSelections(hwnd,nItem,1);
6527
6528                 /* manage item focus */
6529                 if (lpLVItem->mask & LVIF_STATE)
6530                 {
6531                   lpItem->state &= ~(LVIS_FOCUSED|LVIS_SELECTED);
6532                   if (lpLVItem->stateMask & LVIS_SELECTED)
6533                   {
6534                     LISTVIEW_SetSelection(hwnd, nItem);
6535                   }
6536                   else if (lpLVItem->stateMask & LVIS_FOCUSED)
6537                   {
6538                     LISTVIEW_SetItemFocus(hwnd, nItem);
6539                   }           
6540                 }
6541                 
6542                 /* send LVN_INSERTITEM notification */
6543                 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6544                 nmlv.hdr.hwndFrom = hwnd;
6545                 nmlv.hdr.idFrom = lCtrlId;
6546                 nmlv.hdr.code = LVN_INSERTITEM;
6547                 nmlv.iItem = nItem;
6548                 nmlv.lParam = lpItem->lParam;;
6549                 ListView_LVNotify(GetParent(hwnd), lCtrlId, &nmlv);
6550                 
6551                 if ((uView == LVS_SMALLICON) || (uView == LVS_LIST))
6552                 {
6553                   nItemWidth = LISTVIEW_CalculateWidth(hwnd, lpLVItem->iItem); 
6554                   if (nItemWidth > infoPtr->nItemWidth)
6555                   {
6556                     infoPtr->nItemWidth = nItemWidth;
6557                   }
6558                 }
6559
6560                 /* align items (set position of each item) */
6561                 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6562                 {
6563                   if (lStyle & LVS_ALIGNLEFT)
6564                   {
6565                     LISTVIEW_AlignLeft(hwnd);
6566                   }
6567                   else
6568                   {
6569                     LISTVIEW_AlignTop(hwnd);
6570                   }
6571                 }
6572                 
6573                 LISTVIEW_UpdateScroll(hwnd);
6574                 /* refresh client area */
6575                 InvalidateRect(hwnd, NULL, FALSE);
6576               }
6577             }
6578           }
6579         }
6580       }
6581     }
6582   }
6583
6584   /* free memory if unsuccessful */
6585   if ((nItem == -1) && (lpItem != NULL))
6586   {
6587     COMCTL32_Free(lpItem);
6588   }
6589   
6590   return nItem;
6591 }
6592
6593 static LRESULT LISTVIEW_InsertItemW(HWND hwnd, LPLVITEMW lpLVItem) {
6594   LVITEMA lvia;
6595   LRESULT lres;
6596
6597   memcpy(&lvia,lpLVItem,sizeof(LVITEMA));
6598   if (lvia.mask & LVIF_TEXT) {
6599     if (lpLVItem->pszText == LPSTR_TEXTCALLBACKW)
6600       lvia.pszText = LPSTR_TEXTCALLBACKA;
6601     else
6602       lvia.pszText = HEAP_strdupWtoA(GetProcessHeap(),0,lpLVItem->pszText);
6603   }
6604   lres = LISTVIEW_InsertItemA(hwnd, &lvia);
6605   if (lvia.mask & LVIF_TEXT) {
6606     if (lpLVItem->pszText != LPSTR_TEXTCALLBACKW)
6607       HeapFree(GetProcessHeap(),0,lvia.pszText);
6608   }
6609   return lres;
6610 }
6611
6612 /* LISTVIEW_InsertItemW */
6613
6614 /***
6615  * DESCRIPTION:
6616  * Redraws a range of items.
6617  * 
6618  * PARAMETER(S):
6619  * [I] HWND : window handle
6620  * [I] INT : first item
6621  * [I] INT : last item
6622  *
6623  * RETURN:
6624  *   SUCCESS : TRUE
6625  *   FAILURE : FALSE
6626  */
6627 static LRESULT LISTVIEW_RedrawItems(HWND hwnd, INT nFirst, INT nLast)
6628 {
6629   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0); 
6630   BOOL bResult = FALSE;
6631   RECT rcItem;
6632
6633   if (nFirst <= nLast)
6634   {
6635     if ((nFirst >= 0) && (nFirst < GETITEMCOUNT(infoPtr)))
6636     {
6637       if ((nLast >= 0) && (nLast < GETITEMCOUNT(infoPtr)))
6638       {
6639           INT i;
6640           for (i = nFirst; i <= nLast; i++)
6641           {
6642                   rcItem.left = LVIR_BOUNDS;
6643                   LISTVIEW_GetItemRect(hwnd, i, &rcItem);
6644                   InvalidateRect(hwnd, &rcItem, TRUE);
6645           }
6646       }
6647     }
6648   }
6649
6650   return bResult;
6651 }
6652
6653 /* LISTVIEW_Scroll */
6654
6655 /***
6656  * DESCRIPTION:
6657  * Sets the background color.
6658  * 
6659  * PARAMETER(S):
6660  * [I] HWND : window handle
6661  * [I] COLORREF : background color
6662  *
6663  * RETURN:
6664  *   SUCCESS : TRUE
6665  *   FAILURE : FALSE
6666  */
6667 static LRESULT LISTVIEW_SetBkColor(HWND hwnd, COLORREF clrBk)
6668 {
6669   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6670
6671   infoPtr->clrBk = clrBk;
6672   InvalidateRect(hwnd, NULL, TRUE);
6673   
6674   return TRUE;
6675 }
6676
6677 /* LISTVIEW_SetBkImage */
6678
6679 /***
6680  * DESCRIPTION:
6681  * Sets the callback mask. This mask will be used when the parent
6682  * window stores state information (some or all).
6683  * 
6684  * PARAMETER(S):
6685  * [I] HWND : window handle
6686  * [I] UINT : state mask
6687  *
6688  * RETURN:
6689  *   SUCCESS : TRUE
6690  *   FAILURE : FALSE
6691  */
6692 static BOOL LISTVIEW_SetCallbackMask(HWND hwnd, UINT uMask)
6693 {
6694   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6695
6696   infoPtr->uCallbackMask = uMask;
6697
6698   return TRUE;
6699 }
6700
6701 /***
6702  * DESCRIPTION:
6703  * Sets the attributes of a header item.
6704  * 
6705  * PARAMETER(S):
6706  * [I] HWND : window handle
6707  * [I] INT : column index
6708  * [I] LPLVCOLUMNA : column attributes
6709  *
6710  * RETURN:
6711  *   SUCCESS : TRUE
6712  *   FAILURE : FALSE
6713  */
6714 static LRESULT LISTVIEW_SetColumnA(HWND hwnd, INT nColumn, 
6715                                    LPLVCOLUMNA lpColumn)
6716 {
6717   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6718   BOOL bResult = FALSE;
6719   HDITEMA hdi, hdiget;
6720
6721   if ((lpColumn != NULL) && (nColumn >= 0) && 
6722       (nColumn < Header_GetItemCount(infoPtr->hwndHeader)))
6723   {
6724     /* initialize memory */
6725     ZeroMemory(&hdi, sizeof(HDITEMA));
6726
6727     if (lpColumn->mask & LVCF_FMT) 
6728     {
6729       /* format member is valid */
6730       hdi.mask |= HDI_FORMAT;
6731
6732       /* get current format first */
6733       hdiget.mask = HDI_FORMAT;
6734       if (Header_GetItemA(infoPtr->hwndHeader, nColumn, &hdiget))
6735               /* preserve HDF_STRING if present */
6736               hdi.fmt = hdiget.fmt & HDF_STRING;
6737
6738       /* set text alignment (leftmost column must be left-aligned) */
6739       if (nColumn == 0)
6740       {
6741         hdi.fmt |= HDF_LEFT;
6742       }
6743       else
6744       {
6745         if (lpColumn->fmt & LVCFMT_LEFT)
6746         {
6747           hdi.fmt |= HDF_LEFT;
6748         }
6749         else if (lpColumn->fmt & LVCFMT_RIGHT)
6750         {
6751           hdi.fmt |= HDF_RIGHT;
6752         }
6753         else if (lpColumn->fmt & LVCFMT_CENTER)
6754         {
6755           hdi.fmt |= HDF_CENTER;
6756         }
6757       }
6758       
6759       if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6760       {
6761         hdi.fmt |= HDF_BITMAP_ON_RIGHT;
6762       }
6763
6764       if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6765       {
6766         hdi.fmt |= HDF_IMAGE;
6767       }
6768       
6769       if (lpColumn->fmt & LVCFMT_IMAGE)
6770       {
6771         hdi.fmt |= HDF_IMAGE;
6772         hdi.iImage = I_IMAGECALLBACK;
6773       }
6774     }
6775
6776     if (lpColumn->mask & LVCF_WIDTH) 
6777     {
6778       hdi.mask |= HDI_WIDTH;
6779       hdi.cxy = lpColumn->cx;
6780     }
6781     
6782     if (lpColumn->mask & LVCF_TEXT) 
6783     {
6784       hdi.mask |= HDI_TEXT | HDI_FORMAT;
6785       hdi.pszText = lpColumn->pszText;
6786       hdi.cchTextMax = lstrlenA(lpColumn->pszText);
6787       hdi.fmt |= HDF_STRING;
6788     }
6789   
6790     if (lpColumn->mask & LVCF_IMAGE) 
6791     {
6792       hdi.mask |= HDI_IMAGE;
6793       hdi.iImage = lpColumn->iImage;
6794     }
6795
6796     if (lpColumn->mask & LVCF_ORDER) 
6797     {
6798       hdi.mask |= HDI_ORDER;
6799       hdi.iOrder = lpColumn->iOrder;
6800     }
6801
6802     /* set header item attributes */
6803     bResult = Header_SetItemA(infoPtr->hwndHeader, nColumn, &hdi);
6804   }
6805   
6806   return bResult;
6807 }
6808
6809 /* LISTVIEW_SetColumnW */
6810
6811 /***
6812  * DESCRIPTION:
6813  * Sets the column order array
6814  *
6815  * PARAMETERS:
6816  * [I] HWND : window handle
6817  * [I] INT : number of elements in column order array
6818  * [I] INT : pointer to column order array
6819  *
6820  * RETURN:
6821  *   SUCCESS : TRUE
6822  *   FAILURE : FALSE
6823  */
6824 static LRESULT LISTVIEW_SetColumnOrderArray(HWND hwnd, INT iCount, LPINT lpiArray)
6825 {
6826 /*  LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0); */
6827
6828     FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
6829
6830     if (!lpiArray)
6831         return FALSE;
6832
6833     return TRUE;
6834 }
6835
6836
6837 /***
6838  * DESCRIPTION:
6839  * Sets the width of a column
6840  *
6841  * PARAMETERS:
6842  * [I] HWND : window handle
6843  * [I] INT : column index
6844  * [I] INT : column width
6845  *
6846  * RETURN:
6847  *   SUCCESS : TRUE
6848  *   FAILURE : FALSE
6849  */
6850 static LRESULT LISTVIEW_SetColumnWidth(HWND hwnd, INT iCol, INT cx)
6851 {
6852     LISTVIEW_INFO *infoPtr;
6853     HDITEMA hdi;
6854     LRESULT lret;
6855     LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
6856     UINT uView = lStyle & LVS_TYPEMASK; 
6857     HDC hdc;
6858     HFONT header_font;
6859     HFONT old_font;
6860     SIZE size;
6861     CHAR text_buffer[DISP_TEXT_SIZE];
6862     INT header_item_count;
6863     INT item_index;
6864     RECT rcHeader;
6865
6866
6867     /* make sure we can get the listview info */
6868     if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0)))
6869       return (FALSE);
6870
6871     if (!infoPtr->hwndHeader) /* make sure we have a header */
6872       return (FALSE);
6873
6874     /* set column width only if in report or list mode */
6875     if ((uView != LVS_REPORT) && (uView != LVS_LIST))
6876       return (FALSE);            
6877
6878     /* take care of invalid cx values */
6879     if((uView == LVS_REPORT) && (cx < -2))
6880       cx = LVSCW_AUTOSIZE;
6881     else if (uView == LVS_LIST && (cx < 1))
6882       return FALSE;
6883  
6884     /* resize all columns if in LVS_LIST mode */
6885     if(uView == LVS_LIST) {
6886       infoPtr->nItemWidth = cx;
6887       InvalidateRect(hwnd, NULL, TRUE); /* force redraw of the listview */
6888       return TRUE;
6889     }
6890
6891     /* autosize based on listview items width */
6892     if(cx == LVSCW_AUTOSIZE)
6893     {
6894       /* set the width of the header to the width of the widest item */
6895       for(item_index = 0; item_index < GETITEMCOUNT(infoPtr); item_index++)
6896       {
6897         if(cx < LISTVIEW_GetLabelWidth(hwnd, item_index))
6898           cx = LISTVIEW_GetLabelWidth(hwnd, item_index);
6899       } 
6900     } /* autosize based on listview header width */
6901     else if(cx == LVSCW_AUTOSIZE_USEHEADER)
6902     {
6903       header_item_count = Header_GetItemCount(infoPtr->hwndHeader);
6904  
6905       /* if iCol is the last column make it fill the remainder of the controls width */
6906       if(iCol == (header_item_count - 1)) {
6907         /* get the width of every item except the current one */
6908         hdi.mask = HDI_WIDTH;
6909         cx = 0;
6910         
6911         for(item_index = 0; item_index < (header_item_count - 1); item_index++) {
6912           Header_GetItemA(infoPtr->hwndHeader, item_index, (LPARAM)(&hdi));
6913           cx+=hdi.cxy;
6914         }
6915  
6916         /* retrieve the layout of the header */
6917         GetWindowRect(infoPtr->hwndHeader, &rcHeader);
6918
6919         cx = (rcHeader.right - rcHeader.left) - cx;
6920       }                                  
6921       else
6922       {
6923         /* retrieve header font */
6924         header_font = SendMessageA(infoPtr->hwndHeader, WM_GETFONT, 0L, 0L);
6925  
6926         /* retrieve header text */
6927         hdi.mask = HDI_TEXT;
6928         hdi.cchTextMax = sizeof(text_buffer);
6929         hdi.pszText = text_buffer;             
6930     
6931         Header_GetItemA(infoPtr->hwndHeader, iCol, (LPARAM)(&hdi));
6932  
6933         /* determine the width of the text in the header */
6934         hdc = GetDC(hwnd);
6935         old_font = SelectObject(hdc, header_font); /* select the font into hdc */
6936
6937         GetTextExtentPoint32A(hdc, text_buffer, strlen(text_buffer), &size);
6938  
6939         SelectObject(hdc, old_font); /* restore the old font */    
6940         ReleaseDC(hwnd, hdc);
6941  
6942         /* set the width of this column to the width of the text */
6943         cx = size.cx;
6944       }
6945   }
6946
6947   /* call header to update the column change */
6948   hdi.mask = HDI_WIDTH;                          
6949
6950   hdi.cxy = cx;
6951   lret = Header_SetItemA(infoPtr->hwndHeader, (WPARAM)iCol, (LPARAM)&hdi);
6952  
6953   InvalidateRect(hwnd, NULL, TRUE); /* force redraw of the listview */
6954  
6955   return lret;
6956 }
6957
6958 /***
6959  * DESCRIPTION:
6960  * Sets the extended listview style.
6961  *
6962  * PARAMETERS:
6963  * [I] HWND  : window handle
6964  * [I] DWORD : mask
6965  * [I] DWORD : style
6966  *
6967  * RETURN:
6968  *   SUCCESS : previous style
6969  *   FAILURE : 0
6970  */
6971 static LRESULT LISTVIEW_SetExtendedListViewStyle(HWND hwnd, DWORD dwMask, DWORD dwStyle)
6972 {
6973     LISTVIEW_INFO *infoPtr;
6974     DWORD dwOldStyle;
6975
6976     /* make sure we can get the listview info */
6977     if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0)))
6978         return (0);
6979
6980     /* store previous style */
6981     dwOldStyle = infoPtr->dwExStyle;
6982
6983     /* set new style */
6984     if (dwMask)
6985       infoPtr->dwExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
6986     else
6987       infoPtr->dwExStyle = dwStyle;
6988
6989     return (dwOldStyle);
6990 }
6991
6992 /* LISTVIEW_SetHotCursor */
6993
6994 /***
6995  * DESCRIPTION:
6996  * Sets the hot item index.
6997  *
6998  * PARAMETERS:
6999  * [I] HWND  : window handle
7000  * [I] INT   : index
7001  *
7002  * RETURN:
7003  *   SUCCESS : previous hot item index
7004  *   FAILURE : -1 (no hot item)
7005  */
7006 static LRESULT LISTVIEW_SetHotItem(HWND hwnd, INT iIndex)
7007 {
7008     LISTVIEW_INFO *infoPtr;
7009     INT iOldIndex;
7010
7011     /* make sure we can get the listview info */
7012     if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0)))
7013         return (-1);
7014
7015     /* store previous index */
7016     iOldIndex = infoPtr->nHotItem;
7017
7018     /* set new style */
7019     infoPtr->nHotItem = iIndex;
7020
7021     return (iOldIndex);
7022 }
7023
7024 /***
7025  * DESCRIPTION:
7026  * Sets the amount of time the cursor must hover over an item before it is selected.
7027  *
7028  * PARAMETER(S):
7029  * [I] HWND : window handle
7030  * [I] DWORD : dwHoverTime, if -1 the hover time is set to the default
7031  *
7032  * RETURN:
7033  * Returns the previous hover time
7034  */
7035 static LRESULT LISTVIEW_SetHoverTime(HWND hwnd, DWORD dwHoverTime)
7036 {
7037   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7038   DWORD oldHoverTime = infoPtr->dwHoverTime;
7039
7040   infoPtr->dwHoverTime = dwHoverTime;
7041
7042   return oldHoverTime;
7043 }
7044
7045 /* LISTVIEW_SetIconSpacing */
7046
7047 /***
7048  * DESCRIPTION:
7049  * Sets image lists.
7050  * 
7051  * PARAMETER(S):
7052  * [I] HWND : window handle
7053  * [I] INT : image list type  
7054  * [I] HIMAGELIST : image list handle
7055  *
7056  * RETURN:
7057  *   SUCCESS : old image list
7058  *   FAILURE : NULL
7059  */
7060 static LRESULT LISTVIEW_SetImageList(HWND hwnd, INT nType, HIMAGELIST himl)
7061 {
7062   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7063   HIMAGELIST himlOld = 0;
7064
7065   switch (nType) 
7066   {
7067   case LVSIL_NORMAL:
7068     himlOld = infoPtr->himlNormal;
7069     infoPtr->himlNormal = himl;
7070     break;
7071
7072   case LVSIL_SMALL:
7073     himlOld = infoPtr->himlSmall;
7074     infoPtr->himlSmall = himl;
7075     break;
7076
7077   case LVSIL_STATE:
7078     himlOld = infoPtr->himlState;
7079     infoPtr->himlState = himl;
7080     ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
7081     break;
7082   }
7083
7084   infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
7085
7086   return (LRESULT)himlOld;
7087 }
7088
7089
7090 /***
7091  * DESCRIPTION:
7092  * Sets the attributes of an item.
7093  * 
7094  * PARAMETER(S):
7095  * [I] HWND : window handle
7096  * [I] LPLVITEM : item information 
7097  *
7098  * RETURN:
7099  *   SUCCESS : TRUE
7100  *   FAILURE : FALSE
7101  */
7102 static LRESULT LISTVIEW_SetItemA(HWND hwnd, LPLVITEMA lpLVItem)
7103 {
7104   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7105   BOOL bResult = FALSE;
7106
7107   if (lpLVItem != NULL)
7108   {
7109     if ((lpLVItem->iItem >= 0) && (lpLVItem->iItem < GETITEMCOUNT(infoPtr)))
7110     {
7111       if (lpLVItem->iSubItem == 0)
7112       {
7113         bResult = LISTVIEW_SetItem(hwnd, lpLVItem);
7114       }
7115       else
7116       {
7117         bResult = LISTVIEW_SetSubItem(hwnd, lpLVItem);
7118       }
7119     }
7120   }
7121
7122
7123   return bResult;
7124 }
7125
7126 /* LISTVIEW_SetItemW  */
7127
7128 /***
7129  * DESCRIPTION:
7130  * Preallocates memory.
7131  * 
7132  * PARAMETER(S):
7133  * [I] HWND : window handle
7134  * [I] INT   : item count (projected number of items)
7135  * [I] DWORD : update flags
7136  *
7137  * RETURN:
7138  *   SUCCESS : TRUE
7139  *   FAILURE : FALSE
7140  */
7141 static BOOL LISTVIEW_SetItemCount(HWND hwnd, INT nItems, DWORD dwFlags)
7142 {
7143   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongA(hwnd, 0);
7144
7145   if (GetWindowLongA(hwnd, GWL_STYLE) & LVS_OWNERDATA)
7146   {
7147       int precount,topvisible;
7148       TRACE("LVS_OWNERDATA is set!\n");
7149
7150       /*
7151        * Internally remove all the selections. 
7152        */
7153       do
7154       {
7155         LISTVIEW_SELECTION *selection;
7156         selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,0);
7157         if (selection)
7158             LISTVIEW_RemoveSelectionRange(hwnd,selection->lower,
7159                                           selection->upper);
7160       }
7161       while (infoPtr->hdpaSelectionRanges->nItemCount>0);
7162  
7163       precount = infoPtr->hdpaItems->nItemCount;
7164       topvisible = ListView_GetTopIndex(hwnd) +
7165                    LISTVIEW_GetCountPerColumn(hwnd) + 1;
7166
7167       infoPtr->hdpaItems->nItemCount = nItems;
7168
7169       LISTVIEW_UpdateSize(hwnd);
7170       LISTVIEW_UpdateScroll(hwnd);
7171       if (min(precount,infoPtr->hdpaItems->nItemCount)<topvisible) 
7172         InvalidateRect(hwnd, NULL, TRUE);
7173   }
7174   else
7175   {
7176    if (nItems == 0)
7177     return LISTVIEW_DeleteAllItems (hwnd);
7178
7179    if (nItems > GETITEMCOUNT(infoPtr))
7180     {
7181       /* append items */
7182       FIXME("append items\n");
7183
7184     }
7185     else if (nItems < GETITEMCOUNT(infoPtr))
7186     {
7187       /* remove items */
7188       while(nItems < GETITEMCOUNT(infoPtr)) {
7189         LISTVIEW_DeleteItem(hwnd, GETITEMCOUNT(infoPtr) - 1);
7190       }
7191     }
7192   }
7193
7194   return TRUE;
7195 }
7196
7197 /***
7198  * DESCRIPTION:
7199  * Sets the position of an item.
7200  * 
7201  * PARAMETER(S):
7202  * [I] HWND : window handle
7203  * [I] INT : item index
7204  * [I] LONG : x coordinate
7205  * [I] LONG : y coordinate
7206  *
7207  * RETURN:
7208  *   SUCCESS : TRUE
7209  *   FAILURE : FALSE
7210  */
7211 static BOOL LISTVIEW_SetItemPosition(HWND hwnd, INT nItem,
7212                                      LONG nPosX, LONG nPosY)
7213 {
7214   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongA(hwnd, 0);
7215   UINT lStyle = GetWindowLongA(hwnd, GWL_STYLE);
7216   UINT uView = lStyle & LVS_TYPEMASK;
7217   LISTVIEW_ITEM *lpItem;
7218   HDPA hdpaSubItems;
7219   BOOL bResult = FALSE;
7220
7221   TRACE("(hwnd=%x,nItem=%d,X=%ld,Y=%ld)\n", hwnd, nItem, nPosX, nPosY);
7222   
7223   if (lStyle & LVS_OWNERDATA)
7224     return FALSE;
7225
7226   if ((nItem >= 0) || (nItem < GETITEMCOUNT(infoPtr)))
7227   {
7228     if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
7229     {
7230       hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
7231       if (hdpaSubItems != NULL)
7232       {
7233         lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
7234         if (lpItem != NULL)
7235         {
7236           bResult = TRUE;
7237           lpItem->ptPosition.x = nPosX;
7238           lpItem->ptPosition.y = nPosY;
7239         }
7240       }
7241     }
7242   }
7243
7244   return bResult;
7245 }
7246
7247 /***
7248  * DESCRIPTION:
7249  * Sets the state of one or many items.
7250  * 
7251  * PARAMETER(S):
7252  * [I] HWND : window handle
7253  * [I]INT : item index
7254  * [I] LPLVITEM : item or subitem info
7255  *
7256  * RETURN:
7257  *   SUCCESS : TRUE
7258  *   FAILURE : FALSE
7259  */
7260 static LRESULT LISTVIEW_SetItemState(HWND hwnd, INT nItem, LPLVITEMA lpLVItem)
7261 {
7262   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7263   BOOL bResult = FALSE;
7264   LVITEMA lvItem;
7265   INT i;
7266
7267   if (nItem == -1)
7268   {
7269     bResult = TRUE;
7270     ZeroMemory(&lvItem, sizeof(LVITEMA));
7271     lvItem.mask = LVIF_STATE;
7272     lvItem.state = lpLVItem->state;
7273     lvItem.stateMask = lpLVItem->stateMask ;
7274     
7275     /* apply to all items */
7276     for (i = 0; i< GETITEMCOUNT(infoPtr); i++)
7277     {
7278       lvItem.iItem = i;
7279       if (ListView_SetItemA(hwnd, &lvItem) == FALSE)
7280       {
7281         bResult = FALSE;
7282       }
7283     }
7284   }
7285   else
7286   {
7287     ZeroMemory(&lvItem, sizeof(LVITEMA));
7288     lvItem.mask = LVIF_STATE;
7289     lvItem.state = lpLVItem->state;
7290     lvItem.stateMask = lpLVItem->stateMask;
7291     lvItem.iItem = nItem;
7292     bResult = ListView_SetItemA(hwnd, &lvItem);
7293   }
7294
7295   return bResult;
7296 }
7297
7298 /***
7299  * DESCRIPTION:
7300  * Sets the text of an item or subitem.
7301  * 
7302  * PARAMETER(S):
7303  * [I] HWND : window handle
7304  * [I] INT : item index
7305  * [I] LPLVITEMA : item or subitem info
7306  *
7307  * RETURN:
7308  *   SUCCESS : TRUE
7309  *   FAILURE : FALSE
7310  */
7311 static BOOL LISTVIEW_SetItemTextA(HWND hwnd, INT nItem, LPLVITEMA lpLVItem)
7312 {
7313   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7314   BOOL bResult = FALSE;
7315   LVITEMA lvItem;
7316
7317   if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
7318   {
7319     ZeroMemory(&lvItem, sizeof(LVITEMA));
7320     lvItem.mask = LVIF_TEXT;
7321     lvItem.pszText = lpLVItem->pszText;
7322     lvItem.iItem = nItem;
7323     lvItem.iSubItem = lpLVItem->iSubItem;
7324     bResult = ListView_SetItemA(hwnd, &lvItem);
7325   }
7326   
7327   return bResult;
7328 }
7329
7330 /* LISTVIEW_SetItemTextW */
7331
7332 /***
7333  * DESCRIPTION:
7334  * Set item index that marks the start of a multiple selection.
7335  *
7336  * PARAMETER(S):
7337  * [I] HWND : window handle
7338  * [I] INT  : index
7339  *
7340  * RETURN:
7341  * Index number or -1 if there is no selection mark.
7342  */
7343 static LRESULT LISTVIEW_SetSelectionMark(HWND hwnd, INT nIndex)
7344 {
7345   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7346   INT nOldIndex = infoPtr->nSelectionMark;
7347
7348   infoPtr->nSelectionMark = nIndex;
7349
7350   return nOldIndex;
7351 }
7352
7353 /***
7354  * DESCRIPTION:
7355  * Sets the text background color.
7356  * 
7357  * PARAMETER(S):
7358  * [I] HWND : window handle
7359  * [I] COLORREF : text background color
7360  *
7361  * RETURN:
7362  *   SUCCESS : TRUE
7363  *   FAILURE : FALSE
7364  */
7365 static LRESULT LISTVIEW_SetTextBkColor(HWND hwnd, COLORREF clrTextBk)
7366 {
7367   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7368
7369   infoPtr->clrTextBk = clrTextBk;
7370   InvalidateRect(hwnd, NULL, TRUE);
7371
7372   return TRUE;
7373 }
7374
7375 /***
7376  * DESCRIPTION:
7377  * Sets the text foreground color.
7378  * 
7379  * PARAMETER(S):
7380  * [I] HWND : window handle
7381  * [I] COLORREF : text color 
7382  *
7383  * RETURN:
7384  *   SUCCESS : TRUE
7385  *   FAILURE : FALSE
7386  */
7387 static LRESULT LISTVIEW_SetTextColor (HWND hwnd, COLORREF clrText)
7388 {
7389   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7390
7391   infoPtr->clrText = clrText;
7392   InvalidateRect(hwnd, NULL, TRUE);
7393
7394   return TRUE;
7395 }
7396
7397 /* LISTVIEW_SetToolTips */
7398 /* LISTVIEW_SetUnicodeFormat */
7399 /* LISTVIEW_SetWorkAreas */
7400
7401 /***
7402  * DESCRIPTION:
7403  * Callback internally used by LISTVIEW_SortItems()
7404  * 
7405  * PARAMETER(S):
7406  * [I] LPVOID : first LISTVIEW_ITEM to compare
7407  * [I] LPVOID : second LISTVIEW_ITEM to compare
7408  * [I] LPARAM : HWND of control
7409  *
7410  * RETURN:
7411  *   if first comes before second : negative
7412  *   if first comes after second : positive
7413  *   if first and second are equivalent : zero
7414  */
7415 static INT WINAPI LISTVIEW_CallBackCompare( 
7416   LPVOID first, 
7417   LPVOID second, 
7418   LPARAM lParam)
7419 {
7420   /* Forward the call to the client defined callback */
7421   INT rv;
7422   HWND hwnd = (HWND)lParam;
7423   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7424   HDPA  hdpa_first = (HDPA) first;
7425   HDPA  hdpa_second = (HDPA) second;
7426   LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( hdpa_first, 0 );
7427   LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( hdpa_second, 0 );
7428
7429   rv = (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
7430
7431   return rv;
7432 }
7433
7434 /***
7435  * DESCRIPTION:
7436  * Sorts the listview items.
7437  * 
7438  * PARAMETER(S):
7439  * [I] HWND : window handle
7440  * [I] WPARAM : application-defined value
7441  * [I] LPARAM : pointer to comparision callback
7442  *
7443  * RETURN:
7444  *   SUCCESS : TRUE
7445  *   FAILURE : FALSE
7446  */
7447 static LRESULT LISTVIEW_SortItems(HWND hwnd, WPARAM wParam, LPARAM lParam)
7448 {
7449     LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7450     int nCount;
7451     UINT lStyle = GetWindowLongA(hwnd, GWL_STYLE);
7452
7453     if (lStyle & LVS_OWNERDATA)
7454       return FALSE;
7455
7456     if (!infoPtr || !infoPtr->hdpaItems)
7457         return FALSE;
7458    
7459     nCount = GETITEMCOUNT(infoPtr);
7460     /* if there are 0 or 1 items, there is no need to sort */
7461     if (nCount > 1)
7462     {
7463         infoPtr->pfnCompare = (PFNLVCOMPARE)lParam;
7464         infoPtr->lParamSort = (LPARAM)wParam;
7465         
7466         DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, hwnd);
7467     }
7468
7469     /* align the items */
7470     LISTVIEW_AlignTop(hwnd);
7471
7472     /* refresh the display */
7473     InvalidateRect(hwnd, NULL, TRUE);
7474
7475     return TRUE;
7476 }
7477
7478 /* LISTVIEW_SubItemHitTest */
7479
7480 /***
7481  * DESCRIPTION:
7482  * Updates an items or rearranges the listview control.
7483  * 
7484  * PARAMETER(S):
7485  * [I] HWND : window handle
7486  * [I] INT : item index
7487  *
7488  * RETURN:
7489  *   SUCCESS : TRUE
7490  *   FAILURE : FALSE
7491  */
7492 static LRESULT LISTVIEW_Update(HWND hwnd, INT nItem)
7493 {
7494   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7495   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
7496   BOOL bResult = FALSE;
7497   RECT rc;
7498
7499   if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
7500   {
7501     bResult = TRUE;
7502
7503     /* rearrange with default alignment style */
7504     if ((lStyle & LVS_AUTOARRANGE) && (((lStyle & LVS_TYPEMASK) == LVS_ICON) ||
7505         ((lStyle & LVS_TYPEMASK)  == LVS_SMALLICON)))
7506     {
7507       ListView_Arrange(hwnd, 0);
7508     }
7509     else
7510     {
7511       /* get item bounding rectangle */
7512       rc.left = LVIR_BOUNDS;
7513       ListView_GetItemRect(hwnd, nItem, &rc);
7514       InvalidateRect(hwnd, &rc, TRUE);
7515     }
7516   }
7517
7518   return bResult;
7519 }
7520
7521 /***
7522  * DESCRIPTION:
7523  * Creates the listview control.
7524  * 
7525  * PARAMETER(S):
7526  * [I] HWND : window handle
7527  *
7528  * RETURN:
7529  * Zero
7530  */
7531 static LRESULT LISTVIEW_Create(HWND hwnd, WPARAM wParam, LPARAM lParam)
7532 {
7533   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7534   LPCREATESTRUCTA lpcs = (LPCREATESTRUCTA)lParam;
7535   UINT uView = lpcs->style & LVS_TYPEMASK;
7536   LOGFONTA logFont;
7537
7538   /* initialize info pointer */
7539   ZeroMemory(infoPtr, sizeof(LISTVIEW_INFO));
7540
7541   /* determine the type of structures to use */
7542   infoPtr->notifyFormat = SendMessageA(GetParent(hwnd), WM_NOTIFYFORMAT, 
7543                                        (WPARAM)hwnd, (LPARAM)NF_QUERY);
7544   if (infoPtr->notifyFormat != NFR_ANSI)
7545   {
7546     FIXME("ANSI notify format is NOT used\n");
7547   }
7548   
7549   /* initialize color information  */
7550   infoPtr->clrBk = GetSysColor(COLOR_WINDOW);
7551   infoPtr->clrText = GetSysColor(COLOR_WINDOWTEXT);
7552   infoPtr->clrTextBk = CLR_DEFAULT;
7553
7554   /* set default values */
7555   infoPtr->uCallbackMask = 0;
7556   infoPtr->nFocusedItem = -1;
7557   infoPtr->nSelectionMark = -1;
7558   infoPtr->nHotItem = -1;
7559   infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
7560   infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
7561   ZeroMemory(&infoPtr->rcList, sizeof(RECT));
7562   infoPtr->hwndEdit = 0;
7563   infoPtr->pedititem = NULL;
7564   infoPtr->nEditLabelItem = -1;
7565
7566   /* get default font (icon title) */
7567   SystemParametersInfoA(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
7568   infoPtr->hDefaultFont = CreateFontIndirectA(&logFont);
7569   infoPtr->hFont = infoPtr->hDefaultFont;
7570   
7571   /* create header */
7572   infoPtr->hwndHeader = CreateWindowA(WC_HEADERA, (LPCSTR)NULL, 
7573                                       WS_CHILD | HDS_HORZ | HDS_BUTTONS, 
7574                                       0, 0, 0, 0, hwnd, (HMENU)0, 
7575                                       lpcs->hInstance, NULL);
7576
7577   /* set header font */
7578   SendMessageA(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, 
7579                (LPARAM)TRUE);
7580   
7581   if (uView == LVS_ICON)
7582   {
7583     infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
7584     infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
7585   }
7586   else if (uView == LVS_REPORT)
7587   {
7588     if (!(LVS_NOCOLUMNHEADER & lpcs->style))
7589     {
7590       ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
7591     }
7592     else
7593     {
7594       /* set HDS_HIDDEN flag to hide the header bar */
7595       SetWindowLongA(infoPtr->hwndHeader, GWL_STYLE, 
7596                     GetWindowLongA(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
7597     }
7598       
7599
7600     infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
7601     infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
7602   }
7603   else
7604   {
7605     infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
7606     infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
7607   }
7608
7609   /* display unsupported listview window styles */
7610   LISTVIEW_UnsupportedStyles(lpcs->style);
7611
7612   /* allocate memory for the data structure */
7613   infoPtr->hdpaItems = DPA_Create(10);
7614
7615   /* allocate memory for the selection ranges */
7616   infoPtr->hdpaSelectionRanges = DPA_Create(10);
7617
7618   /* initialize size of items */
7619   infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
7620   infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
7621
7622   /* initialize the hover time to -1(indicating the default system hover time) */
7623   infoPtr->dwHoverTime = -1;  
7624
7625   return 0;
7626 }
7627
7628 /***
7629  * DESCRIPTION:
7630  * Erases the background of the listview control.
7631  * 
7632  * PARAMETER(S):
7633  * [I] HWND : window handle
7634  * [I] WPARAM : device context handle
7635  * [I] LPARAM : not used
7636  * 
7637  * RETURN:
7638  *   SUCCESS : TRUE
7639  *   FAILURE : FALSE
7640  */
7641 static LRESULT LISTVIEW_EraseBackground(HWND hwnd, WPARAM wParam, 
7642                                         LPARAM lParam)
7643 {
7644   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7645   BOOL bResult;
7646
7647   if (infoPtr->clrBk == CLR_NONE) 
7648   {
7649     bResult = SendMessageA(GetParent(hwnd), WM_ERASEBKGND, wParam, lParam);
7650   }
7651   else 
7652   {
7653     RECT rc;
7654     HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk);
7655     GetClientRect(hwnd, &rc);
7656     FillRect((HDC)wParam, &rc, hBrush);
7657     DeleteObject(hBrush);
7658     bResult = TRUE;
7659   }
7660
7661   return bResult;
7662 }
7663
7664
7665 static void LISTVIEW_FillBackground(HWND hwnd, HDC hdc, LPRECT rc)
7666 {
7667   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7668
7669   if (infoPtr->clrBk != CLR_NONE) 
7670   {
7671     HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk);
7672     FillRect(hdc, rc, hBrush);
7673     DeleteObject(hBrush);
7674   }
7675 }
7676
7677 /***
7678  * DESCRIPTION:
7679  * Retrieves the listview control font.
7680  * 
7681  * PARAMETER(S):
7682  * [I] HWND : window handle
7683  *
7684  * RETURN:
7685  * Font handle.
7686  */
7687 static LRESULT LISTVIEW_GetFont(HWND hwnd)
7688 {
7689   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7690
7691   return infoPtr->hFont;
7692 }
7693
7694 /***
7695  * DESCRIPTION:
7696  * Performs vertical scrolling.
7697  * 
7698  * PARAMETER(S):
7699  * [I] HWND : window handle
7700  * [I] INT : scroll code
7701  * [I] SHORT : current scroll position if scroll code is SB_THIMBPOSITION 
7702  *             or SB_THUMBTRACK.
7703  * [I] HWND : scrollbar control window handle
7704  *
7705  * RETURN:
7706  * Zero
7707  */
7708 static LRESULT LISTVIEW_VScroll(HWND hwnd, INT nScrollCode, SHORT nCurrentPos,
7709                                 HWND hScrollWnd)
7710 {
7711   SCROLLINFO scrollInfo;
7712
7713   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7714   SendMessageA(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7715
7716   ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
7717   scrollInfo.cbSize = sizeof(SCROLLINFO);
7718   scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
7719
7720   if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
7721   {
7722     INT nOldScrollPos = scrollInfo.nPos;
7723     switch (nScrollCode)
7724     {
7725     case SB_LINEUP:
7726       if (scrollInfo.nPos > scrollInfo.nMin)
7727       {
7728         scrollInfo.nPos--;
7729       }
7730     break;
7731     
7732     case SB_LINEDOWN:
7733       if (scrollInfo.nPos < scrollInfo.nMax)
7734       {
7735         scrollInfo.nPos++;
7736       }
7737       break;
7738       
7739     case SB_PAGEUP:
7740       if (scrollInfo.nPos > scrollInfo.nMin)
7741       {
7742         if (scrollInfo.nPos >= scrollInfo.nPage)
7743         {
7744           scrollInfo.nPos -= scrollInfo.nPage;
7745         }
7746         else
7747         {
7748           scrollInfo.nPos = scrollInfo.nMin;
7749         }
7750       }
7751       break;
7752       
7753     case SB_PAGEDOWN:
7754       if (scrollInfo.nPos < scrollInfo.nMax)
7755       {
7756         if (scrollInfo.nPos <= scrollInfo.nMax - scrollInfo.nPage)
7757         {
7758           scrollInfo.nPos += scrollInfo.nPage;
7759         }
7760         else
7761         {
7762           scrollInfo.nPos = scrollInfo.nMax;
7763         }
7764       }
7765       break;
7766
7767     case SB_THUMBTRACK:
7768         scrollInfo.nPos = nCurrentPos;
7769         if (scrollInfo.nPos > scrollInfo.nMax)
7770             scrollInfo.nPos=scrollInfo.nMax;
7771
7772         if (scrollInfo.nPos < scrollInfo.nMin)
7773             scrollInfo.nPos=scrollInfo.nMin;
7774
7775       break;
7776     }
7777
7778     if (nOldScrollPos != scrollInfo.nPos)
7779     {
7780       scrollInfo.fMask = SIF_POS;
7781       SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
7782       InvalidateRect(hwnd, NULL, TRUE);
7783     }
7784   }
7785     
7786   return 0;
7787 }
7788
7789 /***
7790  * DESCRIPTION:
7791  * Performs horizontal scrolling.
7792  * 
7793  * PARAMETER(S):
7794  * [I] HWND : window handle
7795  * [I] INT : scroll code
7796  * [I] SHORT : current scroll position if scroll code is SB_THUMBPOSITION 
7797  *             or SB_THUMBTRACK.
7798  * [I] HWND : scrollbar control window handle
7799  *
7800  * RETURN:
7801  * Zero
7802  */
7803 static LRESULT LISTVIEW_HScroll(HWND hwnd, INT nScrollCode, SHORT nCurrentPos,
7804                                 HWND hScrollWnd)
7805 {
7806   SCROLLINFO scrollInfo;
7807
7808   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7809   SendMessageA(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7810
7811
7812   ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
7813   scrollInfo.cbSize = sizeof(SCROLLINFO);
7814   scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
7815  
7816   if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
7817   {
7818     INT nOldScrollPos = scrollInfo.nPos;
7819
7820     switch (nScrollCode)
7821     {
7822     case SB_LINELEFT:
7823       if (scrollInfo.nPos > scrollInfo.nMin)
7824       {
7825         scrollInfo.nPos--;
7826       }
7827       break;
7828     
7829     case SB_LINERIGHT:
7830       if (scrollInfo.nPos < scrollInfo.nMax)
7831       {
7832         scrollInfo.nPos++;
7833       }
7834       break;
7835       
7836     case SB_PAGELEFT:
7837       if (scrollInfo.nPos > scrollInfo.nMin)
7838       {
7839         if (scrollInfo.nPos >= scrollInfo.nPage)
7840         {
7841           scrollInfo.nPos -= scrollInfo.nPage;
7842         }
7843         else
7844         {
7845           scrollInfo.nPos = scrollInfo.nMin;
7846         }
7847       }
7848       break;
7849       
7850     case SB_PAGERIGHT:
7851       if (scrollInfo.nPos < scrollInfo.nMax)
7852       {
7853         if (scrollInfo.nPos <= scrollInfo.nMax - scrollInfo.nPage)
7854         {
7855           scrollInfo.nPos += scrollInfo.nPage;
7856         }
7857         else
7858         {
7859           scrollInfo.nPos = scrollInfo.nMax;
7860         }
7861       }
7862       break;
7863
7864     case SB_THUMBTRACK:
7865         scrollInfo.nPos = nCurrentPos;
7866
7867         if (scrollInfo.nPos > scrollInfo.nMax)
7868             scrollInfo.nPos=scrollInfo.nMax;
7869
7870         if (scrollInfo.nPos < scrollInfo.nMin)
7871             scrollInfo.nPos=scrollInfo.nMin;
7872       break;
7873     }
7874
7875     if (nOldScrollPos != scrollInfo.nPos)
7876     {
7877       UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
7878       scrollInfo.fMask = SIF_POS;
7879       SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
7880       if(uView == LVS_REPORT)
7881       {
7882           scrollInfo.fMask = SIF_POS;
7883           GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
7884           LISTVIEW_UpdateHeaderSize(hwnd, scrollInfo.nPos);
7885       }
7886       InvalidateRect(hwnd, NULL, TRUE);
7887     }
7888   }
7889     
7890   return 0;
7891 }
7892
7893 static LRESULT LISTVIEW_MouseWheel(HWND hwnd, INT wheelDelta)
7894 {
7895     INT gcWheelDelta = 0;
7896     UINT pulScrollLines = 3;
7897     SCROLLINFO scrollInfo;
7898
7899     UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
7900
7901     SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
7902     gcWheelDelta -= wheelDelta;
7903
7904     ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
7905     scrollInfo.cbSize = sizeof(SCROLLINFO);
7906     scrollInfo.fMask = SIF_POS | SIF_RANGE;
7907
7908     switch(uView)
7909     {
7910     case LVS_ICON:
7911     case LVS_SMALLICON:
7912        /*
7913         *  listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
7914         *  should be fixed in the future.
7915         */
7916         if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
7917             LISTVIEW_VScroll(hwnd, SB_THUMBPOSITION, scrollInfo.nPos + (gcWheelDelta < 0) ? 37 : -37, 0);
7918         break;
7919
7920     case LVS_REPORT:
7921         if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
7922         {
7923             if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
7924             {
7925                 int cLineScroll = min(LISTVIEW_GetCountPerColumn(hwnd), pulScrollLines);
7926                 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
7927                 LISTVIEW_VScroll(hwnd, SB_THUMBPOSITION, scrollInfo.nPos + cLineScroll, 0);
7928             }
7929         }
7930         break;
7931
7932     case LVS_LIST:
7933         LISTVIEW_HScroll(hwnd, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
7934         break;
7935     }
7936     return 0;
7937 }
7938
7939 /***
7940  * DESCRIPTION:
7941  * ??? 
7942  * 
7943  * PARAMETER(S):
7944  * [I] HWND : window handle
7945  * [I] INT : virtual key 
7946  * [I] LONG : key data
7947  *
7948  * RETURN:
7949  * Zero
7950  */
7951 static LRESULT LISTVIEW_KeyDown(HWND hwnd, INT nVirtualKey, LONG lKeyData)
7952 {
7953   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7954   INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
7955   HWND hwndParent = GetParent(hwnd);
7956   NMLVKEYDOWN nmKeyDown; 
7957   NMHDR nmh;
7958   INT nItem = -1;
7959   BOOL bRedraw = FALSE;
7960   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
7961   UINT uView =  lStyle & LVS_TYPEMASK;
7962
7963   /* send LVN_KEYDOWN notification */
7964   ZeroMemory(&nmKeyDown, sizeof(NMLVKEYDOWN));
7965   nmKeyDown.hdr.hwndFrom = hwnd;  
7966   nmKeyDown.hdr.idFrom = nCtrlId;  
7967   nmKeyDown.hdr.code = LVN_KEYDOWN;  
7968   nmKeyDown.wVKey = nVirtualKey; 
7969   nmKeyDown.flags = 0; 
7970   SendMessageA(hwndParent, WM_NOTIFY, (WPARAM)nCtrlId, (LPARAM)&nmKeyDown); 
7971   
7972   /* initialize */
7973   nmh.hwndFrom = hwnd;
7974   nmh.idFrom = nCtrlId;
7975
7976   switch (nVirtualKey)
7977   {
7978   case VK_RETURN:
7979     if ((GETITEMCOUNT(infoPtr) > 0) && (infoPtr->nFocusedItem != -1))
7980     {
7981       /* send NM_RETURN notification */
7982       nmh.code = NM_RETURN;
7983       ListView_Notify(hwndParent, nCtrlId, &nmh);
7984       
7985       /* send LVN_ITEMACTIVATE notification */
7986       nmh.code = LVN_ITEMACTIVATE;
7987       ListView_Notify(hwndParent, nCtrlId, &nmh);
7988     }
7989     break;
7990
7991   case VK_HOME:
7992     if (GETITEMCOUNT(infoPtr) > 0)
7993     {
7994       nItem = 0;
7995     }
7996     break;
7997
7998   case VK_END:
7999     if (GETITEMCOUNT(infoPtr) > 0)
8000     {
8001       nItem = GETITEMCOUNT(infoPtr) - 1;
8002     }
8003     break;
8004
8005   case VK_LEFT:
8006     nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_TOLEFT);
8007     break;
8008
8009   case VK_UP:
8010     nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_ABOVE);
8011     break;
8012     
8013   case VK_RIGHT:
8014     nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_TORIGHT);
8015     break;
8016
8017   case VK_DOWN:
8018     nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_BELOW);
8019     break;
8020
8021   case VK_PRIOR:
8022     if (uView == LVS_REPORT)
8023     {
8024       nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(hwnd);
8025     }
8026     else
8027     {
8028       nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(hwnd) 
8029                                     * LISTVIEW_GetCountPerRow(hwnd);
8030     }
8031     if(nItem < 0) nItem = 0;
8032     break;
8033
8034   case VK_NEXT:
8035     if (uView == LVS_REPORT)
8036     {
8037       nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(hwnd);
8038     }
8039     else
8040     {
8041       nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(hwnd)
8042                                     * LISTVIEW_GetCountPerRow(hwnd);
8043     }
8044     if(nItem >= GETITEMCOUNT(infoPtr)) nItem = GETITEMCOUNT(infoPtr) - 1;
8045     break;
8046   }
8047
8048   if ((nItem != -1) && (nItem != infoPtr->nFocusedItem))
8049   {
8050     bRedraw = LISTVIEW_KeySelection(hwnd, nItem);
8051     if (bRedraw != FALSE)
8052     {
8053       /* refresh client area */
8054       UpdateWindow(hwnd);
8055     }
8056   }
8057
8058   return 0;
8059 }
8060
8061 /***
8062  * DESCRIPTION:
8063  * Kills the focus.
8064  * 
8065  * PARAMETER(S):
8066  * [I] HWND : window handle
8067  *
8068  * RETURN:
8069  * Zero
8070  */
8071 static LRESULT LISTVIEW_KillFocus(HWND hwnd)
8072 {
8073   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongA(hwnd, 0);
8074   INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
8075   NMHDR nmh;
8076   INT i,nTop,nBottom;
8077   RECT rcItem;
8078   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
8079   UINT uView =  lStyle & LVS_TYPEMASK;
8080  
8081   TRACE("(hwnd=%x)\n", hwnd);
8082
8083   /* send NM_KILLFOCUS notification */
8084   nmh.hwndFrom = hwnd;
8085   nmh.idFrom = nCtrlId;
8086   nmh.code = NM_KILLFOCUS;
8087   ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
8088
8089   /* set window focus flag */
8090   infoPtr->bFocus = FALSE;
8091
8092   /* NEED drawing optimization ; redraw the selected items */
8093   if (uView & LVS_REPORT)
8094   { 
8095     nTop = LISTVIEW_GetTopIndex(hwnd);
8096     nBottom = nTop + 
8097               LISTVIEW_GetCountPerColumn(hwnd) + 1;
8098   }
8099   else
8100   {
8101     nTop = 0;
8102     nBottom = GETITEMCOUNT(infoPtr);
8103   }
8104   for (i = nTop; i<nBottom; i++)
8105   {
8106     if (LISTVIEW_IsSelected(hwnd,i))
8107     {
8108       rcItem.left = LVIR_BOUNDS;
8109       LISTVIEW_GetItemRect(hwnd, i, &rcItem);
8110       InvalidateRect(hwnd, &rcItem, FALSE);
8111     }
8112   }
8113
8114   return 0;
8115 }
8116
8117 /***
8118  * DESCRIPTION:
8119  * Processes double click messages (left mouse button).
8120  * 
8121  * PARAMETER(S):
8122  * [I] HWND : window handle
8123  * [I] WORD : key flag
8124  * [I] WORD : x coordinate
8125  * [I] WORD : y coordinate
8126  *
8127  * RETURN:
8128  * Zero
8129  */
8130 static LRESULT LISTVIEW_LButtonDblClk(HWND hwnd, WORD wKey, WORD wPosX, 
8131                                       WORD wPosY)
8132 {
8133   LONG nCtrlId = GetWindowLongA(hwnd, GWL_ID);
8134   LVHITTESTINFO htInfo;
8135   NMHDR nmh;
8136   NMLISTVIEW nmlv;
8137   INT ret;
8138
8139   TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8140
8141   htInfo.pt.x = wPosX;
8142   htInfo.pt.y = wPosY;
8143
8144   /* send NM_DBLCLK notification */
8145   ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8146   nmlv.hdr.hwndFrom = hwnd;
8147   nmlv.hdr.idFrom = nCtrlId;
8148   nmlv.hdr.code = NM_DBLCLK;
8149   ret = LISTVIEW_HitTestItem(hwnd, &htInfo, TRUE);
8150   if (ret != -1)
8151   {
8152     nmlv.iItem = htInfo.iItem;
8153     nmlv.iSubItem = htInfo.iSubItem;
8154   }
8155   else
8156   {
8157     nmlv.iItem = -1;
8158     nmlv.iSubItem = 0;
8159   }  
8160   nmlv.ptAction.x = wPosX;
8161   nmlv.ptAction.y = wPosY;
8162   ListView_LVNotify(GetParent(hwnd), nCtrlId, &nmlv);
8163
8164
8165   /* To send the LVN_ITEMACTIVATE, it must be on an Item */
8166   if(ret != -1)
8167   {
8168     /* send LVN_ITEMACTIVATE notification */
8169     nmh.hwndFrom = hwnd;
8170     nmh.idFrom = nCtrlId;
8171     nmh.code = LVN_ITEMACTIVATE;
8172     ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
8173   }
8174
8175   return 0;
8176 }
8177
8178 /***
8179  * DESCRIPTION:
8180  * Processes mouse down messages (left mouse button).
8181  * 
8182  * PARAMETER(S):
8183  * [I] HWND : window handle
8184  * [I] WORD : key flag
8185  * [I] WORD : x coordinate
8186  * [I] WORD : y coordinate
8187  *
8188  * RETURN:
8189  * Zero
8190  */
8191 static LRESULT LISTVIEW_LButtonDown(HWND hwnd, WORD wKey, WORD wPosX, 
8192                                     WORD wPosY)
8193 {
8194   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8195   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
8196   INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
8197   static BOOL bGroupSelect = TRUE;
8198   POINT ptPosition;
8199   NMHDR nmh;
8200   INT nItem;
8201
8202   TRACE("(hwnd=%x, key=%hu, X=%hu, Y=%hu)\n", hwnd, wKey, wPosX, 
8203         wPosY);
8204
8205   /* send NM_RELEASEDCAPTURE notification */ 
8206   nmh.hwndFrom = hwnd;
8207   nmh.idFrom = nCtrlId;
8208   nmh.code = NM_RELEASEDCAPTURE;
8209   ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
8210  
8211   if (infoPtr->bFocus == FALSE)
8212   {
8213     SetFocus(hwnd);
8214   }
8215
8216   /* set left button down flag */
8217   infoPtr->bLButtonDown = TRUE;
8218   
8219   ptPosition.x = wPosX;
8220   ptPosition.y = wPosY;
8221   nItem = LISTVIEW_MouseSelection(hwnd, ptPosition);
8222   if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
8223   {
8224     if (lStyle & LVS_SINGLESEL)
8225     {
8226       if ((ListView_GetItemState(hwnd, nItem, LVIS_SELECTED) & LVIS_SELECTED)
8227           && infoPtr->nEditLabelItem == -1) 
8228       {
8229           infoPtr->nEditLabelItem = nItem;
8230       }
8231       else
8232       {
8233         LISTVIEW_SetSelection(hwnd, nItem);
8234       }
8235     }
8236     else
8237     {
8238       if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
8239       {
8240         if (bGroupSelect != FALSE)
8241         {
8242           LISTVIEW_AddGroupSelection(hwnd, nItem);
8243         }
8244         else
8245         {
8246           LISTVIEW_AddSelection(hwnd, nItem);
8247         }
8248       }
8249       else if (wKey & MK_CONTROL)
8250       {
8251         bGroupSelect = LISTVIEW_ToggleSelection(hwnd, nItem);
8252       }
8253       else  if (wKey & MK_SHIFT)
8254       {
8255         LISTVIEW_SetGroupSelection(hwnd, nItem);
8256       }
8257       else
8258       {
8259         BOOL was_selected =
8260             (ListView_GetItemState(hwnd, nItem, LVIS_SELECTED) & LVIS_SELECTED);
8261
8262         /* set selection (clears other pre-existing selections) */
8263         LISTVIEW_SetSelection(hwnd, nItem);
8264
8265         if (was_selected && infoPtr->nEditLabelItem == -1)
8266         {
8267           infoPtr->nEditLabelItem = nItem;
8268         }
8269       }
8270     }
8271   }
8272   else
8273   {
8274     /* remove all selections */
8275     LISTVIEW_RemoveAllSelections(hwnd);
8276   }
8277
8278   /* redraw if we could have possibly selected something */
8279   if(!GETITEMCOUNT(infoPtr)) InvalidateRect(hwnd, NULL, TRUE);
8280
8281   return 0;
8282 }
8283
8284 /***
8285  * DESCRIPTION:
8286  * Processes mouse up messages (left mouse button).
8287  * 
8288  * PARAMETER(S):
8289  * [I] HWND : window handle
8290  * [I] WORD : key flag
8291  * [I] WORD : x coordinate
8292  * [I] WORD : y coordinate
8293  *
8294  * RETURN:
8295  * Zero
8296  */
8297 static LRESULT LISTVIEW_LButtonUp(HWND hwnd, WORD wKey, WORD wPosX, 
8298                                   WORD wPosY)
8299 {
8300   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8301
8302   TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8303
8304   if (infoPtr->bLButtonDown != FALSE) 
8305   {
8306     INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
8307     NMLISTVIEW nmlv;
8308     LVHITTESTINFO lvHitTestInfo;
8309     INT ret;
8310
8311     lvHitTestInfo.pt.x = wPosX;
8312     lvHitTestInfo.pt.y = wPosY;
8313
8314   /* send NM_CLICK notification */
8315     ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8316     nmlv.hdr.hwndFrom = hwnd;
8317     nmlv.hdr.idFrom = nCtrlId;
8318     nmlv.hdr.code = NM_CLICK;
8319     ret = LISTVIEW_HitTestItem(hwnd, &lvHitTestInfo, TRUE);
8320     if (ret != -1)
8321     {
8322         nmlv.iItem = lvHitTestInfo.iItem;
8323         nmlv.iSubItem = lvHitTestInfo.iSubItem;
8324     }
8325     else
8326     {
8327         nmlv.iItem = -1;
8328         nmlv.iSubItem = 0;
8329     }
8330     nmlv.ptAction.x = wPosX;
8331     nmlv.ptAction.y = wPosY;
8332     ListView_LVNotify(GetParent(hwnd), nCtrlId, &nmlv);
8333
8334
8335     /* set left button flag */
8336     infoPtr->bLButtonDown = FALSE;
8337
8338     if(infoPtr->nEditLabelItem != -1)
8339     {
8340       if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem)
8341       {
8342         LISTVIEW_EditLabelA(hwnd, lvHitTestInfo.iItem);
8343       }
8344       infoPtr->nEditLabelItem = -1;
8345     }
8346   }
8347
8348   return 0;
8349 }
8350
8351 /***
8352  * DESCRIPTION:
8353  * Creates the listview control (called before WM_CREATE).
8354  * 
8355  * PARAMETER(S):
8356  * [I] HWND : window handle
8357  * [I] WPARAM : unhandled 
8358  * [I] LPARAM : widow creation info
8359  * 
8360  * RETURN:
8361  * Zero
8362  */
8363 static LRESULT LISTVIEW_NCCreate(HWND hwnd, WPARAM wParam, LPARAM lParam)
8364 {
8365   LISTVIEW_INFO *infoPtr;
8366
8367   TRACE("(hwnd=%x,wParam=%x,lParam=%lx)\n", hwnd, wParam, lParam);
8368
8369   /* allocate memory for info structure */
8370   infoPtr = (LISTVIEW_INFO *)COMCTL32_Alloc(sizeof(LISTVIEW_INFO));
8371   SetWindowLongA(hwnd, 0, (LONG)infoPtr);
8372   if (infoPtr == NULL) 
8373   {
8374     ERR("could not allocate info memory!\n");
8375     return 0;
8376   }
8377
8378   if ((LISTVIEW_INFO *)GetWindowLongA(hwnd, 0) != infoPtr) 
8379   {
8380     ERR("pointer assignment error!\n");
8381     return 0;
8382   }
8383
8384   return DefWindowProcA(hwnd, WM_NCCREATE, wParam, lParam);
8385 }
8386
8387 /***
8388  * DESCRIPTION:
8389  * Destroys the listview control (called after WM_DESTROY).
8390  * 
8391  * PARAMETER(S):
8392  * [I] HWND : window handle
8393  * 
8394  * RETURN:
8395  * Zero
8396  */
8397 static LRESULT LISTVIEW_NCDestroy(HWND hwnd)
8398 {
8399   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8400
8401   TRACE("(hwnd=%x)\n", hwnd);
8402
8403   /* delete all items */
8404   LISTVIEW_DeleteAllItems(hwnd);
8405
8406   /* destroy data structure */
8407   DPA_Destroy(infoPtr->hdpaItems);
8408   DPA_Destroy(infoPtr->hdpaSelectionRanges);
8409
8410   /* destroy font */
8411   infoPtr->hFont = (HFONT)0;
8412   if (infoPtr->hDefaultFont)
8413   {
8414     DeleteObject(infoPtr->hDefaultFont);
8415   }
8416
8417   /* free listview info pointer*/
8418   COMCTL32_Free(infoPtr);
8419
8420   SetWindowLongA(hwnd, 0, 0);
8421   return 0;
8422 }
8423
8424 /***
8425  * DESCRIPTION:
8426  * Handles notifications from children.
8427  * 
8428  * PARAMETER(S):
8429  * [I] HWND : window handle
8430  * [I] INT : control identifier
8431  * [I] LPNMHDR : notification information
8432  * 
8433  * RETURN:
8434  * Zero
8435  */
8436 static LRESULT LISTVIEW_Notify(HWND hwnd, INT nCtrlId, LPNMHDR lpnmh)
8437 {
8438   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8439   
8440   if (lpnmh->hwndFrom == infoPtr->hwndHeader) 
8441   {
8442     /* handle notification from header control */
8443     if (lpnmh->code == HDN_ENDTRACKA)
8444     {
8445       infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8446       InvalidateRect(hwnd, NULL, TRUE);
8447     }
8448     else if(lpnmh->code ==  HDN_ITEMCLICKA)
8449     {
8450         /* Handle sorting by Header Column */
8451         NMLISTVIEW nmlv;
8452         LPNMHEADERA pnmHeader = (LPNMHEADERA) lpnmh;
8453         LONG lCtrlId = GetWindowLongA(hwnd, GWL_ID);
8454
8455         ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8456         nmlv.hdr.hwndFrom = hwnd;
8457         nmlv.hdr.idFrom = lCtrlId;
8458         nmlv.hdr.code = LVN_COLUMNCLICK;
8459         nmlv.iItem = -1;
8460         nmlv.iSubItem = pnmHeader->iItem;
8461         
8462         ListView_LVNotify(GetParent(hwnd),lCtrlId, &nmlv);
8463
8464     }
8465     else if(lpnmh->code == NM_RELEASEDCAPTURE)
8466     {
8467       /* Idealy this should be done in HDN_ENDTRACKA
8468        * but since SetItemBounds in Header.c is called after
8469        * the notification is sent, it is neccessary to handle the
8470        * update of the scroll bar here (Header.c works fine as it is,
8471        * no need to disturb it)
8472        */
8473       infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8474       LISTVIEW_UpdateScroll(hwnd);
8475       InvalidateRect(hwnd, NULL, TRUE);
8476     }
8477
8478   }
8479
8480   return 0;
8481 }
8482
8483 /***
8484  * DESCRIPTION:
8485  * Determines the type of structure to use.
8486  * 
8487  * PARAMETER(S):
8488  * [I] HWND : window handle of the sender
8489  * [I] HWND : listview window handle 
8490  * [I] INT : command specifying the nature of the WM_NOTIFYFORMAT
8491  *
8492  * RETURN:
8493  * Zero
8494  */
8495 static LRESULT LISTVIEW_NotifyFormat(HWND hwndFrom, HWND hwnd, INT nCommand)
8496 {
8497   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8498
8499   if (nCommand == NF_REQUERY)
8500   {
8501     /* determine the type of structure to use */
8502     infoPtr->notifyFormat = SendMessageA(hwndFrom, WM_NOTIFYFORMAT, 
8503                                          (WPARAM)hwnd, (LPARAM)NF_QUERY);
8504     if (infoPtr->notifyFormat == NFR_UNICODE)
8505     {
8506       FIXME("NO support for unicode structures");
8507     }
8508   }
8509
8510   return 0;
8511 }
8512
8513 /***
8514  * DESCRIPTION:
8515  * Paints/Repaints the listview control.
8516  * 
8517  * PARAMETER(S):
8518  * [I] HWND : window handle
8519  * [I] HDC : device context handle
8520  *
8521  * RETURN:
8522  * Zero
8523  */
8524 static LRESULT LISTVIEW_Paint(HWND hwnd, HDC hdc)
8525 {
8526   PAINTSTRUCT ps;
8527
8528    TRACE("(hwnd=%x,hdc=%x)\n", hwnd, hdc);
8529
8530   if (hdc == 0)
8531   {
8532     hdc = BeginPaint(hwnd, &ps);
8533     LISTVIEW_Refresh(hwnd, hdc);
8534     EndPaint(hwnd, &ps);
8535   }
8536   else
8537   {
8538     LISTVIEW_Refresh(hwnd, hdc);
8539   }
8540
8541   return 0;
8542 }
8543
8544 /***
8545  * DESCRIPTION:
8546  * Processes double click messages (right mouse button).
8547  * 
8548  * PARAMETER(S):
8549  * [I] HWND : window handle
8550  * [I] WORD : key flag
8551  * [I] WORD : x coordinate
8552  * [I] WORD : y coordinate
8553  *
8554  * RETURN:
8555  * Zero
8556  */
8557 static LRESULT LISTVIEW_RButtonDblClk(HWND hwnd, WORD wKey, WORD wPosX, 
8558                                       WORD wPosY)
8559 {
8560   INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
8561   NMHDR nmh;
8562
8563   TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8564
8565   /* send NM_RELEASEDCAPTURE notification */ 
8566   nmh.hwndFrom = hwnd;
8567   nmh.idFrom = nCtrlId;
8568   nmh.code = NM_RELEASEDCAPTURE;
8569   ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
8570
8571   /* send NM_RDBLCLK notification */
8572   nmh.code = NM_RDBLCLK;
8573   ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
8574
8575   return 0;
8576 }
8577
8578 /***
8579  * DESCRIPTION:
8580  * Processes mouse down messages (right mouse button).
8581  * 
8582  * PARAMETER(S):
8583  * [I] HWND : window handle
8584  * [I] WORD : key flag
8585  * [I] WORD : x coordinate
8586  * [I] WORD : y coordinate
8587  *
8588  * RETURN:
8589  * Zero
8590  */
8591 static LRESULT LISTVIEW_RButtonDown(HWND hwnd, WORD wKey, WORD wPosX, 
8592                                     WORD wPosY)
8593 {
8594   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8595   INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
8596   POINT ptPosition;
8597   NMHDR nmh;
8598   INT nItem;
8599
8600   TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8601
8602   /* send NM_RELEASEDCAPTURE notification */
8603   nmh.hwndFrom = hwnd;
8604   nmh.idFrom = nCtrlId;
8605   nmh.code = NM_RELEASEDCAPTURE;
8606   ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
8607  
8608   /* make sure the listview control window has the focus */
8609   if (infoPtr->bFocus == FALSE)
8610   {
8611     SetFocus(hwnd);
8612   }
8613
8614   /* set right button down flag */
8615   infoPtr->bRButtonDown = TRUE;
8616
8617   /* determine the index of the selected item */
8618   ptPosition.x = wPosX;
8619   ptPosition.y = wPosY;
8620   nItem = LISTVIEW_MouseSelection(hwnd, ptPosition);
8621   if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
8622   {
8623     if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)))
8624     {
8625       LISTVIEW_SetSelection(hwnd, nItem);
8626     }
8627   }
8628   else
8629   {
8630     LISTVIEW_RemoveAllSelections(hwnd);
8631   }
8632   
8633   return 0;
8634 }
8635
8636 /***
8637  * DESCRIPTION:
8638  * Processes mouse up messages (right mouse button).
8639  * 
8640  * PARAMETER(S):
8641  * [I] HWND : window handle
8642  * [I] WORD : key flag
8643  * [I] WORD : x coordinate
8644  * [I] WORD : y coordinate
8645  *
8646  * RETURN:
8647  * Zero
8648  */
8649 static LRESULT LISTVIEW_RButtonUp(HWND hwnd, WORD wKey, WORD wPosX, 
8650                                   WORD wPosY)
8651 {
8652   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8653   INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
8654
8655   TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8656
8657   if (infoPtr->bRButtonDown != FALSE) 
8658   {
8659     NMLISTVIEW nmlv;
8660     LVHITTESTINFO lvHitTestInfo;
8661     POINT pt;
8662     INT ret;
8663
8664     lvHitTestInfo.pt.x = wPosX;
8665     lvHitTestInfo.pt.y = wPosY;
8666
8667     /* Send NM_RClICK notification */
8668     ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8669     nmlv.hdr.hwndFrom = hwnd;
8670     nmlv.hdr.idFrom = nCtrlId;
8671     nmlv.hdr.code = NM_RCLICK;
8672     ret = LISTVIEW_HitTestItem(hwnd, &lvHitTestInfo, TRUE);
8673     if (ret != -1)
8674     {
8675         nmlv.iItem = lvHitTestInfo.iItem;
8676         nmlv.iSubItem = lvHitTestInfo.iSubItem;
8677     }
8678     else
8679     {
8680         nmlv.iItem = -1;
8681         nmlv.iSubItem = 0;
8682     }
8683     nmlv.ptAction.x = wPosX;
8684     nmlv.ptAction.y = wPosY;
8685     ListView_LVNotify(GetParent(hwnd), nCtrlId, &nmlv);
8686
8687     pt.x = wPosX;
8688     pt.y = wPosY;
8689
8690     /* set button flag */
8691     infoPtr->bRButtonDown = FALSE;
8692     
8693     /* Change to screen coordinate for WM_CONTEXTMENU */
8694     ClientToScreen(hwnd, &pt);
8695     
8696     /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
8697     SendMessageA( hwnd, WM_CONTEXTMENU, (WPARAM) hwnd, MAKELPARAM(pt.x, pt.y));
8698   }
8699   
8700   return 0;
8701 }
8702
8703 /***
8704  * DESCRIPTION:
8705  * Sets the focus.  
8706  * 
8707  * PARAMETER(S):
8708  * [I] HWND : window handle
8709  * [I] HWND : window handle of previously focused window
8710  *
8711  * RETURN:
8712  * Zero
8713  */
8714 static LRESULT LISTVIEW_SetFocus(HWND hwnd, HWND hwndLoseFocus)
8715 {
8716   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8717   INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
8718   NMHDR nmh;
8719
8720   TRACE("(hwnd=%x, hwndLoseFocus=%x)\n", hwnd, hwndLoseFocus);
8721
8722   /* send NM_SETFOCUS notification */
8723   nmh.hwndFrom = hwnd;
8724   nmh.idFrom = nCtrlId;
8725   nmh.code = NM_SETFOCUS;
8726   ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
8727
8728   /* set window focus flag */
8729   infoPtr->bFocus = TRUE;
8730
8731   UpdateWindow(hwnd);
8732
8733   return 0;
8734 }
8735
8736 /***
8737  * DESCRIPTION:
8738  * Sets the font.  
8739  * 
8740  * PARAMETER(S):
8741  * [I] HWND : window handle
8742  * [I] HFONT : font handle
8743  * [I] WORD : redraw flag
8744  *
8745  * RETURN:
8746  * Zero
8747  */
8748 static LRESULT LISTVIEW_SetFont(HWND hwnd, HFONT hFont, WORD fRedraw)
8749 {
8750   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8751   UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8752
8753   TRACE("(hwnd=%x,hfont=%x,redraw=%hu)\n", hwnd, hFont, fRedraw);
8754
8755   if (hFont == 0)
8756   {
8757     infoPtr->hFont = infoPtr->hDefaultFont;
8758   }
8759   else
8760   {
8761     infoPtr->hFont = hFont;
8762   }
8763
8764   if (uView == LVS_REPORT)
8765   {
8766     /* set header font */
8767     SendMessageA(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, 
8768                    MAKELPARAM(fRedraw, 0));
8769   }
8770
8771   /* invalidate listview control client area */
8772   InvalidateRect(hwnd, NULL, TRUE);
8773   
8774   if (fRedraw != FALSE)
8775   {
8776     UpdateWindow(hwnd);
8777   }
8778
8779   return 0;
8780 }
8781
8782 /***
8783  * DESCRIPTION:
8784  * Message handling for WM_SETREDRAW.  
8785  * For the Listview, it invalidates the entire window (the doc specifies otherwise)
8786  * 
8787  * PARAMETER(S):
8788  * [I] HWND   : window handle
8789  * [I] bRedraw: state of redraw flag
8790  *
8791  * RETURN:
8792  * DefWinProc return value
8793  */
8794 static LRESULT LISTVIEW_SetRedraw(HWND hwnd, BOOL bRedraw)
8795 {
8796     LRESULT lResult;
8797     lResult = DefWindowProcA(hwnd, WM_SETREDRAW, bRedraw, 0);
8798     if(bRedraw)
8799     {
8800         RedrawWindow(hwnd, NULL, 0, 
8801             RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ALLCHILDREN | RDW_ERASENOW);
8802     }
8803     return lResult;
8804 }
8805
8806 /***
8807  * DESCRIPTION:
8808  * Resizes the listview control. This function processes WM_SIZE
8809  * messages.  At this time, the width and height are not used.
8810  * 
8811  * PARAMETER(S):
8812  * [I] HWND : window handle
8813  * [I] WORD : new width
8814  * [I] WORD : new height
8815  *
8816  * RETURN:
8817  * Zero
8818  */
8819 static LRESULT LISTVIEW_Size(HWND hwnd, int Width, int Height)
8820 {
8821   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE); 
8822   UINT uView = lStyle & LVS_TYPEMASK;
8823
8824   TRACE("(hwnd=%x, width=%d, height=%d)\n",hwnd, Width, Height);
8825
8826   LISTVIEW_UpdateSize(hwnd);
8827
8828   if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
8829   {
8830     if (lStyle & LVS_ALIGNLEFT)
8831     {
8832       LISTVIEW_AlignLeft(hwnd);
8833     }
8834     else
8835     {
8836       LISTVIEW_AlignTop(hwnd);
8837     }
8838   }
8839
8840   LISTVIEW_UpdateScroll(hwnd);
8841   
8842   /* invalidate client area + erase background */
8843   InvalidateRect(hwnd, NULL, TRUE);
8844
8845   return 0;
8846 }
8847
8848 /***
8849  * DESCRIPTION:
8850  * Sets the size information.
8851  * 
8852  * PARAMETER(S):
8853  * [I] HWND : window handle
8854  *
8855  * RETURN:
8856  * Zero
8857  */
8858 static VOID LISTVIEW_UpdateSize(HWND hwnd)
8859 {
8860   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8861   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
8862   UINT uView = lStyle & LVS_TYPEMASK;
8863   RECT rcList;
8864   
8865   GetClientRect(hwnd, &rcList);
8866   infoPtr->rcList.left = 0;
8867   infoPtr->rcList.right = max(rcList.right - rcList.left, 1);
8868   infoPtr->rcList.top = 0;
8869   infoPtr->rcList.bottom = max(rcList.bottom - rcList.top, 1);
8870      
8871   if (uView == LVS_LIST)
8872   {
8873     if ((lStyle & WS_HSCROLL) == 0)
8874     {
8875       INT nHScrollHeight = GetSystemMetrics(SM_CYHSCROLL);
8876       if (infoPtr->rcList.bottom > nHScrollHeight)
8877       { 
8878         infoPtr->rcList.bottom -= nHScrollHeight;
8879       }
8880     }
8881   }
8882   else if (uView == LVS_REPORT)
8883   {
8884     HDLAYOUT hl;
8885     WINDOWPOS wp;
8886
8887     hl.prc = &rcList;
8888     hl.pwpos = &wp;
8889     Header_Layout(infoPtr->hwndHeader, &hl);
8890
8891     SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8892
8893     if (!(LVS_NOCOLUMNHEADER & lStyle))
8894     {
8895       infoPtr->rcList.top = max(wp.cy, 0);
8896     }
8897   }
8898 }
8899
8900 /***
8901  * DESCRIPTION:
8902  * Processes WM_STYLECHANGED messages. 
8903  * 
8904  * PARAMETER(S):
8905  * [I] HWND : window handle
8906  * [I] WPARAM : window style type (normal or extended)
8907  * [I] LPSTYLESTRUCT : window style information
8908  *
8909  * RETURN:
8910  * Zero
8911  */
8912 static INT LISTVIEW_StyleChanged(HWND hwnd, WPARAM wStyleType, 
8913                                  LPSTYLESTRUCT lpss)
8914 {
8915   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8916   UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
8917   UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
8918   RECT rcList = infoPtr->rcList;
8919
8920   TRACE("(hwnd=%x, styletype=%x, stylestruct=%p)\n", 
8921         hwnd, wStyleType, lpss);
8922
8923   if (wStyleType == GWL_STYLE)
8924   {
8925     if (uOldView == LVS_REPORT)
8926     {
8927       ShowWindow(infoPtr->hwndHeader, SW_HIDE);
8928     }
8929  
8930     if ((lpss->styleOld & WS_HSCROLL) != 0)
8931     {
8932        ShowScrollBar(hwnd, SB_HORZ, FALSE);
8933     }
8934  
8935     if ((lpss->styleOld & WS_VSCROLL) != 0)
8936     {
8937        ShowScrollBar(hwnd, SB_VERT, FALSE);
8938     }
8939  
8940     if (uNewView == LVS_ICON)
8941     {
8942       infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
8943       infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
8944       infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8945       infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
8946       if (lpss->styleNew & LVS_ALIGNLEFT)
8947       {
8948         LISTVIEW_AlignLeft(hwnd);
8949       }
8950       else
8951       {
8952         LISTVIEW_AlignTop(hwnd);
8953       }
8954     }
8955     else if (uNewView == LVS_REPORT)
8956     {
8957       HDLAYOUT hl;
8958       WINDOWPOS wp;
8959
8960       hl.prc = &rcList;
8961       hl.pwpos = &wp;
8962       Header_Layout(infoPtr->hwndHeader, &hl);
8963       SetWindowPos(infoPtr->hwndHeader, hwnd, wp.x, wp.y, wp.cx, wp.cy, 
8964                    wp.flags);
8965       if (!(LVS_NOCOLUMNHEADER & lpss->styleNew))
8966         ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
8967       
8968       infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8969       infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8970       infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8971       infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
8972     }
8973     else if (uNewView == LVS_LIST)
8974     {
8975       infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8976       infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8977       infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8978       infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
8979     }
8980     else
8981     {
8982       infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8983       infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8984       infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8985       infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
8986       if (lpss->styleNew & LVS_ALIGNLEFT)
8987       {
8988         LISTVIEW_AlignLeft(hwnd);
8989       }
8990       else
8991       {
8992         LISTVIEW_AlignTop(hwnd);
8993       }
8994     }
8995
8996     /* update the size of the client area */
8997     LISTVIEW_UpdateSize(hwnd);
8998
8999     /* add scrollbars if needed */
9000     LISTVIEW_UpdateScroll(hwnd);
9001     
9002     /* invalidate client area + erase background */
9003     InvalidateRect(hwnd, NULL, TRUE);
9004
9005     /* print the list of unsupported window styles */
9006     LISTVIEW_UnsupportedStyles(lpss->styleNew);
9007   }
9008
9009   /* If they change the view and we have an active edit control 
9010      we will need to kill the control since the redraw will
9011      misplace the edit control.
9012    */
9013   if (infoPtr->hwndEdit &&
9014         ((uNewView & (LVS_ICON|LVS_LIST|LVS_SMALLICON)) !=
9015         ((LVS_ICON|LVS_LIST|LVS_SMALLICON) & uOldView)))
9016   {
9017      SendMessageA(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
9018   }
9019
9020   return 0;
9021 }
9022
9023 /***
9024  * DESCRIPTION:
9025  * Window procedure of the listview control.
9026  *
9027  */
9028 static LRESULT WINAPI LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam,
9029                                    LPARAM lParam)
9030 {
9031   TRACE("hwnd=%x uMsg=%x wParam=%x lParam=%lx\n", hwnd, uMsg, wParam, lParam);
9032   if (!GetWindowLongA(hwnd, 0) && (uMsg != WM_NCCREATE))
9033     return DefWindowProcA( hwnd, uMsg, wParam, lParam );
9034   switch (uMsg)
9035   {
9036   case LVM_APPROXIMATEVIEWRECT: 
9037     return LISTVIEW_ApproximateViewRect(hwnd, (INT)wParam, 
9038                                         LOWORD(lParam), HIWORD(lParam));
9039   case LVM_ARRANGE: 
9040     return LISTVIEW_Arrange(hwnd, (INT)wParam);
9041
9042 /* case LVM_CREATEDRAGIMAGE: */
9043
9044   case LVM_DELETEALLITEMS:
9045     return LISTVIEW_DeleteAllItems(hwnd);
9046
9047   case LVM_DELETECOLUMN:
9048     return LISTVIEW_DeleteColumn(hwnd, (INT)wParam);
9049
9050   case LVM_DELETEITEM:
9051     return LISTVIEW_DeleteItem(hwnd, (INT)wParam);
9052
9053   case LVM_EDITLABELW:
9054   case LVM_EDITLABELA:
9055     return LISTVIEW_EditLabelA(hwnd, (INT)wParam);
9056
9057   case LVM_ENSUREVISIBLE:
9058     return LISTVIEW_EnsureVisible(hwnd, (INT)wParam, (BOOL)lParam);
9059
9060   case LVM_FINDITEMA:
9061     return LISTVIEW_FindItem(hwnd, (INT)wParam, (LPLVFINDINFO)lParam);
9062
9063   case LVM_GETBKCOLOR:
9064     return LISTVIEW_GetBkColor(hwnd);
9065
9066 /*      case LVM_GETBKIMAGE: */
9067
9068   case LVM_GETCALLBACKMASK:
9069     return LISTVIEW_GetCallbackMask(hwnd);
9070
9071   case LVM_GETCOLUMNA:
9072     return LISTVIEW_GetColumnA(hwnd, (INT)wParam, (LPLVCOLUMNA)lParam);
9073
9074 /*      case LVM_GETCOLUMNW: */
9075
9076   case LVM_GETCOLUMNORDERARRAY:
9077     return LISTVIEW_GetColumnOrderArray(hwnd, (INT)wParam, (LPINT)lParam);
9078
9079   case LVM_GETCOLUMNWIDTH:
9080     return LISTVIEW_GetColumnWidth(hwnd, (INT)wParam);
9081
9082   case LVM_GETCOUNTPERPAGE:
9083     return LISTVIEW_GetCountPerPage(hwnd);
9084
9085   case LVM_GETEDITCONTROL:
9086     return LISTVIEW_GetEditControl(hwnd);
9087
9088   case LVM_GETEXTENDEDLISTVIEWSTYLE:
9089     return LISTVIEW_GetExtendedListViewStyle(hwnd);
9090
9091   case LVM_GETHEADER:
9092     return LISTVIEW_GetHeader(hwnd);
9093
9094 /*      case LVM_GETHOTCURSOR: */
9095
9096   case LVM_GETHOTITEM:
9097     return LISTVIEW_GetHotItem(hwnd);
9098
9099   case LVM_GETHOVERTIME:
9100     return LISTVIEW_GetHoverTime(hwnd);
9101
9102   case LVM_GETIMAGELIST:
9103     return LISTVIEW_GetImageList(hwnd, (INT)wParam);
9104
9105 /*      case LVM_GETISEARCHSTRING: */
9106
9107   case LVM_GETITEMA:
9108     return LISTVIEW_GetItemA(hwnd, (LPLVITEMA)lParam, FALSE);
9109
9110 /*      case LVM_GETITEMW: */
9111
9112   case LVM_GETITEMCOUNT:
9113     return LISTVIEW_GetItemCount(hwnd);
9114
9115   case LVM_GETITEMPOSITION:
9116     return LISTVIEW_GetItemPosition(hwnd, (INT)wParam, (LPPOINT)lParam);
9117
9118   case LVM_GETITEMRECT: 
9119     return LISTVIEW_GetItemRect(hwnd, (INT)wParam, (LPRECT)lParam);
9120
9121   case LVM_GETITEMSPACING: 
9122     return LISTVIEW_GetItemSpacing(hwnd, (BOOL)wParam);
9123
9124   case LVM_GETITEMSTATE: 
9125     return LISTVIEW_GetItemState(hwnd, (INT)wParam, (UINT)lParam);
9126     
9127   case LVM_GETITEMTEXTA:
9128     LISTVIEW_GetItemTextA(hwnd, (INT)wParam, (LPLVITEMA)lParam);
9129     break;
9130
9131 /*      case LVM_GETITEMTEXTW: */
9132
9133   case LVM_GETNEXTITEM:
9134     return LISTVIEW_GetNextItem(hwnd, (INT)wParam, LOWORD(lParam));
9135
9136 /*      case LVM_GETNUMBEROFWORKAREAS: */
9137
9138   case LVM_GETORIGIN:
9139     return LISTVIEW_GetOrigin(hwnd, (LPPOINT)lParam);
9140
9141   case LVM_GETSELECTEDCOUNT:
9142     return LISTVIEW_GetSelectedCount(hwnd);
9143
9144   case LVM_GETSELECTIONMARK: 
9145     return LISTVIEW_GetSelectionMark(hwnd);
9146
9147   case LVM_GETSTRINGWIDTHA:
9148     return LISTVIEW_GetStringWidthA (hwnd, (LPCSTR)lParam);
9149
9150 /*      case LVM_GETSTRINGWIDTHW: */
9151 /*      case LVM_GETSUBITEMRECT: */
9152
9153   case LVM_GETTEXTBKCOLOR:
9154     return LISTVIEW_GetTextBkColor(hwnd);
9155
9156   case LVM_GETTEXTCOLOR:
9157     return LISTVIEW_GetTextColor(hwnd);
9158
9159 /*      case LVM_GETTOOLTIPS: */
9160
9161   case LVM_GETTOPINDEX:
9162     return LISTVIEW_GetTopIndex(hwnd);
9163
9164 /*      case LVM_GETUNICODEFORMAT: */
9165
9166   case LVM_GETVIEWRECT:
9167     return LISTVIEW_GetViewRect(hwnd, (LPRECT)lParam);
9168
9169 /*      case LVM_GETWORKAREAS: */
9170
9171   case LVM_HITTEST:
9172     return LISTVIEW_HitTest(hwnd, (LPLVHITTESTINFO)lParam);
9173
9174   case LVM_INSERTCOLUMNA:
9175     return LISTVIEW_InsertColumnA(hwnd, (INT)wParam, (LPLVCOLUMNA)lParam);
9176
9177   case LVM_INSERTCOLUMNW:
9178     return LISTVIEW_InsertColumnW(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam);
9179
9180   case LVM_INSERTITEMA:
9181     return LISTVIEW_InsertItemA(hwnd, (LPLVITEMA)lParam);
9182
9183   case LVM_INSERTITEMW:
9184     return LISTVIEW_InsertItemW(hwnd, (LPLVITEMW)lParam);
9185
9186   case LVM_REDRAWITEMS:
9187     return LISTVIEW_RedrawItems(hwnd, (INT)wParam, (INT)lParam);
9188
9189 /*   case LVM_SCROLL:  */
9190 /*     return LISTVIEW_Scroll(hwnd, (INT)wParam, (INT)lParam); */
9191
9192   case LVM_SETBKCOLOR:
9193     return LISTVIEW_SetBkColor(hwnd, (COLORREF)lParam);
9194
9195 /*      case LVM_SETBKIMAGE: */
9196
9197   case LVM_SETCALLBACKMASK:
9198     return LISTVIEW_SetCallbackMask(hwnd, (UINT)wParam);
9199
9200   case LVM_SETCOLUMNA:
9201     return LISTVIEW_SetColumnA(hwnd, (INT)wParam, (LPLVCOLUMNA)lParam);
9202
9203   case LVM_SETCOLUMNW:
9204     FIXME("Unimplemented msg LVM_SETCOLUMNW\n");
9205     return 0;
9206
9207   case LVM_SETCOLUMNORDERARRAY:
9208     return LISTVIEW_SetColumnOrderArray(hwnd, (INT)wParam, (LPINT)lParam);
9209
9210   case LVM_SETCOLUMNWIDTH:
9211     return LISTVIEW_SetColumnWidth(hwnd, (INT)wParam, SLOWORD(lParam));
9212
9213   case LVM_SETEXTENDEDLISTVIEWSTYLE:
9214     return LISTVIEW_SetExtendedListViewStyle(hwnd, (DWORD)wParam, (DWORD)lParam);
9215
9216 /*      case LVM_SETHOTCURSOR: */
9217
9218   case LVM_SETHOTITEM:
9219     return LISTVIEW_SetHotItem(hwnd, (INT)wParam);
9220
9221   case LVM_SETHOVERTIME:
9222     return LISTVIEW_SetHoverTime(hwnd, (DWORD)wParam);
9223
9224 /*      case LVM_SETICONSPACING: */
9225         
9226   case LVM_SETIMAGELIST:
9227     return LISTVIEW_SetImageList(hwnd, (INT)wParam, (HIMAGELIST)lParam);
9228
9229   case LVM_SETITEMA:
9230     return LISTVIEW_SetItemA(hwnd, (LPLVITEMA)lParam);
9231
9232 /*      case LVM_SETITEMW: */
9233
9234   case LVM_SETITEMCOUNT: 
9235     return LISTVIEW_SetItemCount(hwnd, (INT)wParam, (DWORD)lParam);
9236     
9237   case LVM_SETITEMPOSITION:
9238     return LISTVIEW_SetItemPosition(hwnd, (INT)wParam, (INT)LOWORD(lParam),
9239                                     (INT)HIWORD(lParam));
9240
9241   case LVM_SETITEMPOSITION32:
9242     return LISTVIEW_SetItemPosition(hwnd, (INT)wParam, ((POINT*)lParam)->x,
9243                                     ((POINT*)lParam)->y);
9244
9245   case LVM_SETITEMSTATE:
9246     return LISTVIEW_SetItemState(hwnd, (INT)wParam, (LPLVITEMA)lParam);
9247
9248   case LVM_SETITEMTEXTA:
9249     return LISTVIEW_SetItemTextA(hwnd, (INT)wParam, (LPLVITEMA)lParam);
9250
9251 /*      case LVM_SETITEMTEXTW: */
9252
9253   case LVM_SETSELECTIONMARK:
9254     return LISTVIEW_SetSelectionMark(hwnd, (INT)lParam);
9255
9256   case LVM_SETTEXTBKCOLOR:
9257     return LISTVIEW_SetTextBkColor(hwnd, (COLORREF)lParam);
9258
9259   case LVM_SETTEXTCOLOR:
9260     return LISTVIEW_SetTextColor(hwnd, (COLORREF)lParam);
9261
9262 /*      case LVM_SETTOOLTIPS: */
9263 /*      case LVM_SETUNICODEFORMAT: */
9264 /*      case LVM_SETWORKAREAS: */
9265
9266   case LVM_SORTITEMS:
9267     return LISTVIEW_SortItems(hwnd, wParam, lParam);
9268
9269 /*      case LVM_SUBITEMHITTEST: */
9270
9271   case LVM_UPDATE: 
9272     return LISTVIEW_Update(hwnd, (INT)wParam);
9273
9274 /*      case WM_CHAR: */
9275   case WM_CHAR:
9276     return LISTVIEW_ProcessLetterKeys( hwnd, wParam, lParam );
9277   
9278   case WM_COMMAND:
9279     return LISTVIEW_Command(hwnd, wParam, lParam);
9280
9281   case WM_CREATE:
9282     return LISTVIEW_Create(hwnd, wParam, lParam);
9283     
9284   case WM_ERASEBKGND:
9285     return LISTVIEW_EraseBackground(hwnd, wParam, lParam);
9286
9287   case WM_GETDLGCODE:
9288     return DLGC_WANTCHARS | DLGC_WANTARROWS;
9289
9290   case WM_GETFONT:
9291     return LISTVIEW_GetFont(hwnd);
9292
9293   case WM_HSCROLL:
9294     return LISTVIEW_HScroll(hwnd, (INT)LOWORD(wParam), 
9295                             (INT)HIWORD(wParam), (HWND)lParam);
9296
9297   case WM_KEYDOWN:
9298     return LISTVIEW_KeyDown(hwnd, (INT)wParam, (LONG)lParam);
9299
9300   case WM_KILLFOCUS:
9301     return LISTVIEW_KillFocus(hwnd);
9302
9303   case WM_LBUTTONDBLCLK:
9304     return LISTVIEW_LButtonDblClk(hwnd, (WORD)wParam, LOWORD(lParam), 
9305                                 HIWORD(lParam));
9306     
9307   case WM_LBUTTONDOWN:
9308     return LISTVIEW_LButtonDown(hwnd, (WORD)wParam, LOWORD(lParam), 
9309                                 HIWORD(lParam));
9310   case WM_LBUTTONUP:
9311     return LISTVIEW_LButtonUp(hwnd, (WORD)wParam, LOWORD(lParam), 
9312                               HIWORD(lParam));
9313   case WM_MOUSEMOVE:
9314     return LISTVIEW_MouseMove (hwnd, wParam, lParam);
9315
9316   case WM_MOUSEHOVER:
9317     return LISTVIEW_MouseHover(hwnd, wParam, lParam);
9318
9319   case WM_NCCREATE:
9320     return LISTVIEW_NCCreate(hwnd, wParam, lParam);
9321
9322   case WM_NCDESTROY:
9323     return LISTVIEW_NCDestroy(hwnd);
9324
9325   case WM_NOTIFY:
9326     return LISTVIEW_Notify(hwnd, (INT)wParam, (LPNMHDR)lParam);
9327
9328   case WM_NOTIFYFORMAT:
9329     return LISTVIEW_NotifyFormat(hwnd, (HWND)wParam, (INT)lParam);
9330
9331   case WM_PAINT: 
9332     return LISTVIEW_Paint(hwnd, (HDC)wParam); 
9333
9334   case WM_RBUTTONDBLCLK:
9335     return LISTVIEW_RButtonDblClk(hwnd, (WORD)wParam, LOWORD(lParam), 
9336                                   HIWORD(lParam));
9337
9338   case WM_RBUTTONDOWN:
9339     return LISTVIEW_RButtonDown(hwnd, (WORD)wParam, LOWORD(lParam), 
9340                                 HIWORD(lParam));
9341
9342   case WM_RBUTTONUP:
9343     return LISTVIEW_RButtonUp(hwnd, (WORD)wParam, LOWORD(lParam), 
9344                               HIWORD(lParam));
9345
9346   case WM_SETFOCUS:
9347     return LISTVIEW_SetFocus(hwnd, (HWND)wParam);
9348
9349   case WM_SETFONT:
9350     return LISTVIEW_SetFont(hwnd, (HFONT)wParam, (WORD)lParam);
9351
9352   case WM_SETREDRAW: 
9353     return LISTVIEW_SetRedraw(hwnd, (BOOL)wParam);
9354
9355   case WM_SIZE:
9356     return LISTVIEW_Size(hwnd, (int)SLOWORD(lParam), (int)SHIWORD(lParam));
9357
9358   case WM_STYLECHANGED:
9359     return LISTVIEW_StyleChanged(hwnd, wParam, (LPSTYLESTRUCT)lParam);
9360
9361 /*      case WM_TIMER: */
9362
9363   case WM_VSCROLL:
9364     return LISTVIEW_VScroll(hwnd, (INT)LOWORD(wParam), 
9365                             (INT)HIWORD(wParam), (HWND)lParam);
9366
9367   case WM_MOUSEWHEEL:
9368       if (wParam & (MK_SHIFT | MK_CONTROL))
9369           return DefWindowProcA( hwnd, uMsg, wParam, lParam );
9370       return LISTVIEW_MouseWheel(hwnd, (short int)HIWORD(wParam));/*    case WM_WINDOWPOSCHANGED: */
9371
9372 /*      case WM_WININICHANGE: */
9373
9374   default:
9375     if (uMsg >= WM_USER)
9376     {
9377       ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, 
9378           lParam);
9379     }
9380
9381     /* call default window procedure */
9382     return DefWindowProcA(hwnd, uMsg, wParam, lParam);
9383   }
9384
9385   return 0;
9386 }
9387
9388 /***
9389  * DESCRIPTION:
9390  * Registers the window class.
9391  * 
9392  * PARAMETER(S):
9393  * None
9394  *
9395  * RETURN:
9396  * None
9397  */
9398 VOID LISTVIEW_Register(void)
9399 {
9400   WNDCLASSA wndClass;
9401
9402     ZeroMemory(&wndClass, sizeof(WNDCLASSA));
9403     wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
9404     wndClass.lpfnWndProc = (WNDPROC)LISTVIEW_WindowProc;
9405     wndClass.cbClsExtra = 0;
9406     wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
9407     wndClass.hCursor = LoadCursorA(0, IDC_ARROWA);
9408     wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
9409     wndClass.lpszClassName = WC_LISTVIEWA;
9410     RegisterClassA(&wndClass);
9411 }
9412
9413 /***
9414  * DESCRIPTION:
9415  * Unregisters the window class.
9416  * 
9417  * PARAMETER(S):
9418  * None
9419  *
9420  * RETURN:
9421  * None
9422  */
9423 VOID LISTVIEW_Unregister(void)
9424 {
9425     UnregisterClassA(WC_LISTVIEWA, (HINSTANCE)NULL);
9426 }
9427
9428 /***
9429  * DESCRIPTION:
9430  * Handle any WM_COMMAND messages
9431  * 
9432  * PARAMETER(S):
9433  *
9434  * RETURN:
9435  */
9436 static LRESULT LISTVIEW_Command(HWND hwnd, WPARAM wParam, LPARAM lParam)
9437 {
9438     switch (HIWORD(wParam))
9439     {
9440         case EN_UPDATE:
9441         {
9442             /* 
9443              * Adjust the edit window size 
9444              */
9445             char buffer[1024];
9446             LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
9447             HDC           hdc      = GetDC(infoPtr->hwndEdit);
9448             HFONT         hFont, hOldFont = 0;
9449             RECT          rect;
9450             SIZE          sz;
9451             int           len;
9452
9453             len = GetWindowTextA(infoPtr->hwndEdit, buffer, 1023);
9454             GetWindowRect(infoPtr->hwndEdit, &rect);
9455
9456             /* Select font to get the right dimension of the string */
9457             hFont = SendMessageA(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
9458             if(hFont != 0)
9459             {
9460                 hOldFont = SelectObject(hdc, hFont);
9461             }
9462
9463             if (GetTextExtentPoint32A(hdc, buffer, strlen(buffer), &sz))
9464             {
9465                 TEXTMETRICA textMetric;
9466
9467                 /* Add Extra spacing for the next character */
9468                 GetTextMetricsA(hdc, &textMetric);
9469                 sz.cx += (textMetric.tmMaxCharWidth * 2);
9470
9471                 SetWindowPos ( 
9472                     infoPtr->hwndEdit,
9473                     HWND_TOP, 
9474                     0, 
9475                     0,
9476                     sz.cx,
9477                     rect.bottom - rect.top,
9478                     SWP_DRAWFRAME|SWP_NOMOVE);
9479             }
9480             if(hFont != 0)
9481             {
9482                 SelectObject(hdc, hOldFont);
9483             }
9484
9485             ReleaseDC(hwnd, hdc);
9486
9487             break;
9488         }
9489
9490         default:
9491           return SendMessageA (GetParent (hwnd), WM_COMMAND, wParam, lParam);
9492     }
9493
9494     return 0;
9495 }
9496
9497
9498 /***
9499  * DESCRIPTION:
9500  * Subclassed edit control windproc function
9501  *
9502  * PARAMETER(S):
9503  *
9504  * RETURN:
9505  */
9506 LRESULT CALLBACK EditLblWndProc(HWND hwnd, UINT uMsg, 
9507         WPARAM wParam, LPARAM lParam)
9508 {
9509     BOOL cancel = FALSE;
9510     LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(GetParent(hwnd), 0);
9511     EDITLABEL_ITEM *einfo = infoPtr->pedititem;
9512     static BOOL bIgnoreKillFocus = FALSE;
9513     switch (uMsg)
9514     {
9515         case WM_GETDLGCODE:
9516           return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
9517                         
9518         case WM_KILLFOCUS:
9519             if(bIgnoreKillFocus)
9520             {
9521                 return TRUE;
9522             }
9523             break;
9524
9525         case WM_DESTROY:
9526         {
9527             WNDPROC editProc = einfo->EditWndProc;
9528             SetWindowLongA(hwnd, GWL_WNDPROC, (LONG)editProc);
9529             COMCTL32_Free(einfo);
9530             infoPtr->pedititem = NULL;
9531             return CallWindowProcA(editProc, hwnd, uMsg, wParam, lParam);
9532         }
9533
9534         case WM_KEYDOWN:
9535             if (VK_ESCAPE == (INT)wParam)
9536             {
9537                 cancel = TRUE;
9538                 break;
9539
9540             }
9541             else if (VK_RETURN == (INT)wParam)
9542                 break;
9543
9544         default:
9545             return CallWindowProcA(einfo->EditWndProc, hwnd, 
9546                         uMsg, wParam, lParam);
9547     }
9548
9549     if (einfo->EditLblCb)
9550     {
9551         char *buffer  = NULL;
9552         
9553
9554         if (!cancel)
9555         {
9556             int len = 1 + GetWindowTextLengthA(hwnd);
9557
9558             if (len > 1)
9559             {
9560                 if (NULL != (buffer = (char *)COMCTL32_Alloc(len*sizeof(char))))
9561                 {
9562                     GetWindowTextA(hwnd, buffer, len);
9563                 }
9564             }
9565         }
9566         /* Processing LVN_ENDLABELEDIT message could kill the focus       */
9567         /* eg. Using a messagebox                                         */
9568         bIgnoreKillFocus = TRUE;
9569         einfo->EditLblCb(GetParent(hwnd), buffer, einfo->param);
9570
9571         if (buffer)
9572             COMCTL32_Free(buffer);
9573
9574         einfo->EditLblCb = NULL;
9575         bIgnoreKillFocus = FALSE;
9576     }
9577
9578     SendMessageA(hwnd, WM_CLOSE, 0, 0);
9579     return TRUE;
9580 }
9581
9582
9583 /***
9584  * DESCRIPTION:
9585  * Creates a subclassed edit cotrol
9586  *
9587  * PARAMETER(S):
9588  *
9589  * RETURN:
9590  */
9591 HWND CreateEditLabel(LPCSTR text, DWORD style, INT x, INT y, 
9592         INT width, INT height, HWND parent, HINSTANCE hinst, 
9593         EditlblCallback EditLblCb, DWORD param)
9594 {
9595     HWND hedit;
9596     SIZE sz;
9597     HDC hdc;
9598     HDC hOldFont=0;
9599     TEXTMETRICA textMetric;
9600     LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(parent, 0);
9601
9602     if (NULL == (infoPtr->pedititem = COMCTL32_Alloc(sizeof(EDITLABEL_ITEM))))
9603         return 0;
9604
9605     style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|WS_BORDER;
9606     hdc = GetDC(parent);
9607
9608     /* Select the font to get appropriate metric dimensions */
9609     if(infoPtr->hFont != 0)
9610     {
9611         hOldFont = SelectObject(hdc, infoPtr->hFont);
9612     }
9613
9614     /*Get String Lenght in pixels */
9615     GetTextExtentPoint32A(hdc, text, strlen(text), &sz);
9616
9617     /*Add Extra spacing for the next character */
9618     GetTextMetricsA(hdc, &textMetric);
9619     sz.cx += (textMetric.tmMaxCharWidth * 2);
9620
9621     if(infoPtr->hFont != 0)
9622     {
9623         SelectObject(hdc, hOldFont);
9624     }
9625
9626     ReleaseDC(parent, hdc);
9627     if (!(hedit = CreateWindowA("Edit", text, style, x, y, sz.cx, height, 
9628                     parent, 0, hinst, 0)))
9629     {
9630         COMCTL32_Free(infoPtr->pedititem);
9631         return 0;
9632     }
9633
9634     infoPtr->pedititem->param = param;
9635     infoPtr->pedititem->EditLblCb = EditLblCb;
9636     infoPtr->pedititem->EditWndProc = (WNDPROC)SetWindowLongA(hedit, 
9637           GWL_WNDPROC, (LONG) EditLblWndProc);
9638
9639     SendMessageA(hedit, WM_SETFONT, infoPtr->hFont, FALSE);
9640
9641     return hedit;
9642 }