Implemented VK_PRIOR and VK_NEXT processing (merged from Corel tree).
[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               LISTVIEW_AddSelectionRange(hwnd,lpLVItem->iItem,lpLVItem->iItem);
2553             else
2554               LISTVIEW_RemoveSelectionRange(hwnd,lpLVItem->iItem,
2555                                             lpLVItem->iItem);
2556           }
2557
2558           nmlv.hdr.code = LVN_ITEMCHANGED;
2559
2560           ListView_LVNotify(GetParent(hwnd), lCtrlId, &nmlv);
2561
2562           rcItem.left = LVIR_BOUNDS;
2563           LISTVIEW_GetItemRect(hwnd, lpLVItem->iItem, &rcItem);
2564           InvalidateRect(hwnd, &rcItem, TRUE);
2565         }
2566       }
2567       return TRUE;
2568     }
2569     return FALSE;
2570   }
2571
2572   if (lpLVItem != NULL)
2573   {
2574     if (lpLVItem->iSubItem == 0)
2575     {
2576       hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2577       if (hdpaSubItems != NULL && hdpaSubItems != (HDPA)-1)
2578       {
2579         lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, lpLVItem->iSubItem);
2580         if (lpItem != NULL)
2581         {
2582           ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
2583           nmlv.hdr.hwndFrom = hwnd;
2584           nmlv.hdr.idFrom = lCtrlId;
2585           nmlv.hdr.code = LVN_ITEMCHANGING;
2586           nmlv.lParam = lpItem->lParam;
2587           uChanged = LISTVIEW_GetItemChanges(lpItem, lpLVItem);
2588           if (uChanged != 0)
2589           {
2590             if (uChanged & LVIF_STATE)
2591             {
2592               nmlv.uNewState = lpLVItem->state & lpLVItem->stateMask;
2593               nmlv.uOldState = lpItem->state & lpLVItem->stateMask;
2594
2595               if (nmlv.uNewState & LVIS_SELECTED)
2596               {
2597                 /*
2598                  * This is redundant if called through SetSelection
2599                  *
2600                  * however is required if the used directly calls SetItem
2601                  * to set the selection.
2602                  */
2603                 if (lStyle & LVS_SINGLESEL)
2604                 {
2605                   LISTVIEW_RemoveAllSelections(hwnd);
2606                 }
2607
2608                 LISTVIEW_AddSelectionRange(hwnd,lpLVItem->iItem,
2609                                              lpLVItem->iItem);
2610               }
2611               else if (lpLVItem->stateMask & LVIS_SELECTED)
2612               {
2613                 LISTVIEW_RemoveSelectionRange(hwnd,lpLVItem->iItem,
2614                                               lpLVItem->iItem);
2615               }
2616               if (nmlv.uNewState & LVIS_FOCUSED)
2617               {
2618                 /*
2619                  * This is a fun hoop to jump to try to catch if
2620                  * the user is calling us directly to call focus or if
2621                  * this function is being called as a result of a 
2622                  * SetItemFocus call. 
2623                  */
2624                 if (infoPtr->nFocusedItem >= 0)
2625                   LISTVIEW_SetItemFocus(hwnd, lpLVItem->iItem);
2626               }           
2627             }
2628             
2629             nmlv.uChanged = uChanged;
2630             nmlv.iItem = lpLVItem->iItem;
2631             nmlv.lParam = lpItem->lParam;
2632             /* send LVN_ITEMCHANGING notification */
2633             ListView_LVNotify(GetParent(hwnd), lCtrlId, &nmlv);
2634
2635             /* copy information */
2636             bResult = LISTVIEW_InitItem(hwnd, lpItem, lpLVItem);
2637
2638             /* if LVS_LIST or LVS_SMALLICON, update the width of the items
2639                based on the width of the items text */
2640             if((uView == LVS_LIST) || (uView == LVS_SMALLICON))
2641             {
2642               item_width = LISTVIEW_GetStringWidthA(hwnd, lpItem->pszText);
2643
2644               if(item_width > infoPtr->nItemWidth)
2645                   infoPtr->nItemWidth = item_width;
2646             }
2647
2648             /* send LVN_ITEMCHANGED notification */
2649             nmlv.hdr.code = LVN_ITEMCHANGED;
2650             ListView_LVNotify(GetParent(hwnd), lCtrlId, &nmlv);
2651           }
2652           else
2653           {
2654             bResult = TRUE;
2655           }
2656           
2657           if (uChanged)
2658           {
2659             rcItem.left = LVIR_BOUNDS;
2660             LISTVIEW_GetItemRect(hwnd, lpLVItem->iItem, &rcItem);
2661             InvalidateRect(hwnd, &rcItem, TRUE);
2662           }
2663         }
2664       }
2665     }
2666   }
2667
2668   return bResult;
2669 }
2670
2671 /***
2672  * DESCRIPTION:
2673  * Sets subitem attributes.
2674  * 
2675  * PARAMETER(S):
2676  * [I] HWND : window handle
2677  * [I] LPLVITEM : new subitem atttributes 
2678  *
2679  * RETURN:
2680  *   SUCCESS : TRUE
2681  *   FAILURE : FALSE
2682  */
2683 static BOOL LISTVIEW_SetSubItem(HWND hwnd, LPLVITEMA lpLVItem)
2684 {
2685   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
2686   BOOL bResult = FALSE;
2687   HDPA hdpaSubItems;
2688   LISTVIEW_SUBITEM *lpSubItem;
2689   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2690   RECT rcItem;
2691
2692   if (lStyle & LVS_OWNERDATA)
2693     return FALSE;
2694
2695   if (lpLVItem != NULL)
2696   {
2697     if (lpLVItem->iSubItem > 0)
2698     {
2699       hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2700       if (hdpaSubItems != NULL)
2701       {
2702         /* set subitem only if column is present */
2703         if (Header_GetItemCount(infoPtr->hwndHeader) > lpLVItem->iSubItem)
2704         {
2705           lpSubItem = LISTVIEW_GetSubItem(hdpaSubItems, lpLVItem->iSubItem);
2706           if (lpSubItem != NULL)
2707           {
2708             bResult = LISTVIEW_InitSubItem(hwnd, lpSubItem, lpLVItem);
2709           }
2710           else
2711           {
2712             bResult = LISTVIEW_AddSubItem(hwnd, lpLVItem);
2713           }
2714           
2715           rcItem.left = LVIR_BOUNDS;
2716           LISTVIEW_GetItemRect(hwnd, lpLVItem->iItem, &rcItem);
2717           InvalidateRect(hwnd, &rcItem, FALSE);
2718         } 
2719       }
2720     }
2721   }
2722
2723   return bResult;
2724 }
2725
2726 /***
2727  * DESCRIPTION:
2728  * Retrieves the index of the item at coordinate (0, 0) of the client area.
2729  * 
2730  * PARAMETER(S):
2731  * [I] HWND : window handle
2732  *
2733  * RETURN:
2734  * item index
2735  */
2736 static INT LISTVIEW_GetTopIndex(HWND hwnd)
2737 {
2738   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2739   UINT uView = lStyle & LVS_TYPEMASK;
2740   INT nItem = 0;
2741   SCROLLINFO scrollInfo;
2742
2743   ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
2744   scrollInfo.cbSize = sizeof(SCROLLINFO);
2745   scrollInfo.fMask = SIF_POS;
2746   
2747   if (uView == LVS_LIST)
2748   {
2749     if (lStyle & WS_HSCROLL)
2750     {
2751       if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
2752       {
2753         nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(hwnd);
2754       }
2755     }
2756   }
2757   else if (uView == LVS_REPORT)
2758   {
2759     if (lStyle & WS_VSCROLL)
2760     {
2761       if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
2762       {
2763         nItem = scrollInfo.nPos;
2764       }
2765     }
2766   }
2767   
2768   return nItem;
2769 }
2770
2771 /***
2772  * DESCRIPTION:
2773  * Draws a subitem.
2774  * 
2775  * PARAMETER(S):
2776  * [I] HWND : window handle
2777  * [I] HDC : device context handle
2778  * [I] INT : item index
2779  * [I] INT : subitem index
2780  * [I] RECT * : clipping rectangle
2781  *
2782  * RETURN:
2783  * None
2784  */
2785 static VOID LISTVIEW_DrawSubItem(HWND hwnd, HDC hdc, INT nItem, INT nSubItem, 
2786                                  RECT rcItem, BOOL Selected)
2787 {
2788   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0); 
2789   CHAR szDispText[DISP_TEXT_SIZE];
2790   LVITEMA lvItem;
2791   UINT textoutOptions = ETO_CLIPPED | ETO_OPAQUE;
2792   RECT rcTemp;
2793
2794   TRACE("(hwnd=%x, hdc=%x, nItem=%d, nSubItem=%d)\n", hwnd, hdc,
2795         nItem, nSubItem);
2796
2797   /* get information needed for drawing the item */
2798   ZeroMemory(&lvItem, sizeof(LVITEMA));
2799   lvItem.mask = LVIF_TEXT;
2800   lvItem.iItem = nItem;
2801   lvItem.iSubItem = nSubItem;
2802   lvItem.cchTextMax = DISP_TEXT_SIZE;
2803   lvItem.pszText = szDispText;
2804   LISTVIEW_GetItemA(hwnd, &lvItem, TRUE);
2805
2806   /* redraw the background of the item */
2807   rcTemp = rcItem;
2808   if(infoPtr->nColumnCount == (nSubItem + 1))
2809     rcTemp.right = infoPtr->rcList.right;
2810   else
2811     rcTemp.right+=WIDTH_PADDING;
2812
2813   LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
2814
2815   /* set item colors */
2816   if (ListView_GetItemState(hwnd,nItem,LVIS_SELECTED) && Selected)
2817   {
2818     if (infoPtr->bFocus)
2819     {
2820       SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
2821       SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
2822     }
2823     else
2824     {
2825       SetBkColor(hdc, GetSysColor(COLOR_3DFACE));
2826       SetTextColor(hdc, GetSysColor(COLOR_BTNTEXT));
2827     }
2828   }
2829   else
2830   {
2831     if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
2832     {
2833        SetBkMode(hdc, TRANSPARENT);
2834        textoutOptions &= ~ETO_OPAQUE;
2835     }
2836     else
2837     {
2838       SetBkMode(hdc, OPAQUE);
2839       SetBkColor(hdc, infoPtr->clrTextBk);
2840     }
2841
2842     SetTextColor(hdc, infoPtr->clrText);
2843   }
2844
2845   ExtTextOutA(hdc, rcItem.left, rcItem.top, textoutOptions, 
2846               &rcItem, lvItem.pszText, lstrlenA(lvItem.pszText), NULL);
2847
2848   if (Selected)
2849   {
2850     /* fill in the gap */
2851     RECT rec;
2852     if (nSubItem < Header_GetItemCount(infoPtr->hwndHeader)-1)
2853     {
2854       CopyRect(&rec,&rcItem);
2855       rec.left = rec.right;
2856       rec.right = rec.left+REPORT_MARGINX;
2857       ExtTextOutA(hdc, rec.left , rec.top, textoutOptions,
2858         &rec, NULL, 0, NULL);
2859     }
2860     CopyRect(&rec,&rcItem);
2861     rec.right = rec.left;
2862     rec.left = rec.left - REPORT_MARGINX;
2863     ExtTextOutA(hdc, rec.left , rec.top, textoutOptions,
2864     &rec, NULL, 0, NULL);
2865   }
2866 }
2867
2868
2869 /***
2870  * DESCRIPTION:
2871  * Draws an item.
2872  * 
2873  * PARAMETER(S):
2874  * [I] HWND : window handle
2875  * [I] HDC : device context handle
2876  * [I] INT : item index
2877  * [I] RECT * : clipping rectangle
2878  *
2879  * RETURN:
2880  * None
2881  */
2882 static VOID LISTVIEW_DrawItem(HWND hwnd, HDC hdc, INT nItem, RECT rcItem, BOOL FullSelect, RECT* SuggestedFocus)
2883 {
2884   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0); 
2885   CHAR szDispText[DISP_TEXT_SIZE];
2886   INT nLabelWidth;
2887   LVITEMA lvItem;
2888   INT nMixMode;
2889   DWORD dwBkColor;
2890   DWORD dwTextColor,dwTextX;
2891   BOOL bImage = FALSE;
2892   INT   iBkMode = -1;
2893   UINT  textoutOptions = ETO_OPAQUE | ETO_CLIPPED;
2894   RECT rcTemp;
2895
2896   TRACE("(hwnd=%x, hdc=%x, nItem=%d)\n", hwnd, hdc, nItem);
2897
2898
2899   /* get information needed for drawing the item */
2900   ZeroMemory(&lvItem, sizeof(LVITEMA));
2901   lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE | LVIF_INDENT;
2902   lvItem.stateMask = LVIS_SELECTED |  LVIS_STATEIMAGEMASK;
2903   lvItem.iItem = nItem;
2904   lvItem.iSubItem = 0;
2905   lvItem.cchTextMax = DISP_TEXT_SIZE;
2906   lvItem.pszText = szDispText;
2907   LISTVIEW_GetItemA(hwnd, &lvItem, TRUE);
2908
2909   /* redraw the background of the item */
2910   rcTemp = rcItem;
2911   if(infoPtr->nColumnCount == (nItem + 1))
2912     rcTemp.right = infoPtr->rcList.right;
2913   else
2914     rcTemp.right+=WIDTH_PADDING;
2915
2916   LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
2917
2918   /* do indent */
2919   if (lvItem.iIndent>0 && infoPtr->iconSize.cx > 0)
2920   {
2921     rcItem.left += infoPtr->iconSize.cx * lvItem.iIndent; 
2922
2923     if (SuggestedFocus)
2924       SuggestedFocus->left += infoPtr->iconSize.cx * lvItem.iIndent;
2925   } 
2926
2927   /* state icons */
2928   if (infoPtr->himlState != NULL)
2929   {
2930      UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12; 
2931      if (uStateImage > 0)
2932      {
2933        ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, rcItem.left, 
2934                       rcItem.top, ILD_NORMAL);
2935      }
2936  
2937      rcItem.left += infoPtr->iconSize.cx; 
2938      if (SuggestedFocus)
2939        SuggestedFocus->left += infoPtr->iconSize.cx;
2940      bImage = TRUE;
2941   }
2942   
2943   /* small icons */
2944   if (infoPtr->himlSmall != NULL)
2945   {
2946     if ((lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus != FALSE) &&
2947         (lvItem.iImage>=0))
2948     {
2949       ImageList_SetBkColor(infoPtr->himlSmall, CLR_NONE);
2950       ImageList_Draw(infoPtr->himlSmall, lvItem.iImage, hdc, rcItem.left, 
2951                      rcItem.top, ILD_SELECTED);
2952     }
2953     else if (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_NORMAL);
2958     }
2959     
2960     rcItem.left += infoPtr->iconSize.cx; 
2961
2962     if (SuggestedFocus)
2963       SuggestedFocus->left += infoPtr->iconSize.cx;
2964     bImage = TRUE;
2965   }
2966
2967   /* Don't bother painting item being edited */
2968   if (infoPtr->hwndEdit && lvItem.state & LVIS_FOCUSED && !FullSelect)
2969       return;
2970
2971   if ((lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus != FALSE))
2972   {
2973     /* set item colors */ 
2974     dwBkColor = SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
2975     dwTextColor = SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
2976     /* set raster mode */
2977     nMixMode = SetROP2(hdc, R2_XORPEN);
2978   }
2979   else if ((GetWindowLongA(hwnd, GWL_STYLE) & LVS_SHOWSELALWAYS) && 
2980            (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus == FALSE))
2981   {
2982     dwBkColor = SetBkColor(hdc, GetSysColor(COLOR_3DFACE));
2983     dwTextColor = SetTextColor(hdc, GetSysColor(COLOR_BTNTEXT));
2984     /* set raster mode */
2985     nMixMode = SetROP2(hdc, R2_COPYPEN);
2986   }
2987   else
2988   {
2989     /* set item colors */
2990     if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
2991     {
2992       dwBkColor = GetBkColor(hdc);
2993       iBkMode = SetBkMode(hdc, TRANSPARENT);
2994       textoutOptions &= ~ETO_OPAQUE;
2995     }
2996     else
2997     {
2998       dwBkColor = SetBkColor(hdc, infoPtr->clrTextBk);
2999       iBkMode = SetBkMode(hdc, OPAQUE);
3000     }
3001
3002     dwTextColor = SetTextColor(hdc, infoPtr->clrText);
3003     /* set raster mode */
3004     nMixMode = SetROP2(hdc, R2_COPYPEN);
3005   }
3006   
3007   nLabelWidth = ListView_GetStringWidthA(hwnd, lvItem.pszText);
3008   if (rcItem.left + nLabelWidth < rcItem.right)
3009   {
3010     if (!FullSelect)
3011       rcItem.right = rcItem.left + nLabelWidth + TRAILING_PADDING;
3012     if (bImage)
3013       rcItem.right += IMAGE_PADDING;
3014   }
3015   
3016   /* draw label */  
3017   dwTextX = rcItem.left + 1;
3018   if (bImage)
3019     dwTextX += IMAGE_PADDING;
3020
3021   if (lvItem.pszText)
3022     ExtTextOutA(hdc, dwTextX, rcItem.top, textoutOptions,
3023                 &rcItem, lvItem.pszText, lstrlenA(lvItem.pszText), NULL);
3024
3025   if ((FullSelect)&&(Header_GetItemCount(infoPtr->hwndHeader) > 1))
3026   {
3027     /* fill in the gap */
3028     RECT rec;
3029     CopyRect(&rec,&rcItem);
3030     rec.left = rec.right;
3031     rec.right = rec.left+REPORT_MARGINX;
3032     ExtTextOutA(hdc, rec.left , rec.top, textoutOptions, 
3033     &rec, NULL, 0, NULL);
3034   }
3035   
3036   if (!FullSelect)
3037       CopyRect(SuggestedFocus,&rcItem);
3038
3039   if (nMixMode != 0)
3040   {
3041     SetROP2(hdc, R2_COPYPEN);
3042     SetBkColor(hdc, dwBkColor);
3043     SetTextColor(hdc, dwTextColor);
3044     if (iBkMode != -1)
3045       SetBkMode(hdc, iBkMode);
3046   }
3047 }
3048
3049 /***
3050  * DESCRIPTION:
3051  * Draws an item when in large icon display mode.
3052  * 
3053  * PARAMETER(S):
3054  * [I] HWND : window handle
3055  * [I] HDC : device context handle
3056  * [I] LISTVIEW_ITEM * : item
3057  * [I] INT : item index
3058  * [I] RECT * : clipping rectangle
3059  *
3060  * RETURN:
3061  * None
3062  */
3063 static VOID LISTVIEW_DrawLargeItem(HWND hwnd, HDC hdc, INT nItem, RECT rcItem,
3064                                    RECT *SuggestedFocus)
3065 {
3066   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0); 
3067   CHAR szDispText[DISP_TEXT_SIZE];
3068   INT nDrawPosX = rcItem.left;
3069   INT nLabelWidth, rcWidth;
3070   TEXTMETRICA tm;
3071   LVITEMA lvItem;
3072   UINT textoutOptions = ETO_CLIPPED | ETO_OPAQUE;
3073   RECT rcTemp;
3074
3075   TRACE("(hwnd=%x, hdc=%x, nItem=%d, left=%d, top=%d, right=%d, \
3076 bottom=%d)\n", hwnd, hdc, nItem, rcItem.left, rcItem.top, rcItem.right, 
3077         rcItem.bottom);
3078
3079   /* get information needed for drawing the item */
3080   ZeroMemory(&lvItem, sizeof(LVITEMA));
3081   lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE;
3082   lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3083   lvItem.iItem = nItem;
3084   lvItem.iSubItem = 0;
3085   lvItem.cchTextMax = DISP_TEXT_SIZE;
3086   lvItem.pszText = szDispText;
3087   LISTVIEW_GetItemA(hwnd, &lvItem, TRUE);
3088
3089   /* redraw the background of the item */
3090   rcTemp = rcItem;
3091   if(infoPtr->nColumnCount == (nItem + 1))
3092     rcTemp.right = infoPtr->rcList.right;
3093   else
3094     rcTemp.right+=WIDTH_PADDING;
3095
3096   LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3097
3098   if (lvItem.state & LVIS_SELECTED)
3099   {
3100     /* set item colors */ 
3101     SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
3102     SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
3103     /* set raster mode */
3104     SetROP2(hdc, R2_XORPEN);
3105   }
3106   else
3107   {
3108     /* set item colors */
3109     if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
3110     {
3111        SetBkMode(hdc, TRANSPARENT);
3112        textoutOptions &= ~ETO_OPAQUE;
3113     }
3114     else
3115     {
3116       SetBkMode(hdc, OPAQUE);
3117       SetBkColor(hdc, infoPtr->clrTextBk);
3118     }
3119
3120     SetTextColor(hdc, infoPtr->clrText);
3121     /* set raster mode */
3122     SetROP2(hdc, R2_COPYPEN);
3123   }
3124
3125   if (infoPtr->himlNormal != NULL)
3126   {
3127     rcItem.top += ICON_TOP_PADDING;
3128     nDrawPosX += (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
3129     if ((lvItem.state & LVIS_SELECTED) && (lvItem.iImage>=0))
3130     {
3131       ImageList_Draw(infoPtr->himlNormal, lvItem.iImage, hdc, nDrawPosX, 
3132                      rcItem.top, ILD_SELECTED);
3133     }
3134     else if (lvItem.iImage>=0)
3135     {
3136       ImageList_Draw(infoPtr->himlNormal, lvItem.iImage, hdc, nDrawPosX, 
3137                      rcItem.top, ILD_NORMAL);
3138     }
3139   }
3140
3141   /* Don't bother painting item being edited */
3142   if (infoPtr->hwndEdit && lvItem.state & LVIS_FOCUSED)
3143     return;
3144
3145   InflateRect(&rcItem, -(2*CAPTION_BORDER), 0);
3146   rcItem.top += infoPtr->iconSize.cy + ICON_BOTTOM_PADDING; 
3147   nLabelWidth = ListView_GetStringWidthA(hwnd, lvItem.pszText);
3148   GetTextMetricsA(hdc, &tm);
3149
3150   /* append an ellipse ('...') if the caption won't fit in the rect */
3151   rcWidth = max(0, rcItem.right - rcItem.left);
3152   if (nLabelWidth > rcWidth)
3153   {
3154       INT i, len, eos, nCharsFit;
3155       /* give or take a couple, how many average sized chars would fit? */
3156       nCharsFit = tm.tmAveCharWidth > 0 ? (rcWidth/tm.tmAveCharWidth)+2 : 0; 
3157       /* place the ellipse accordingly, without overrunning the buffer */
3158       len = strlen(szDispText);
3159       eos = min((nCharsFit > 1 && nCharsFit < len) ? nCharsFit+3 : len+2,
3160                 sizeof(szDispText)-1);
3161       
3162       nLabelWidth = ListView_GetStringWidthA(hwnd, szDispText);
3163       while ((nLabelWidth > rcWidth) && (eos > 3))
3164       {
3165          for (i = 1; i < 4; i++)
3166             szDispText[eos-i] = '.'; 
3167          /* shift the ellipse one char to the left for each iteration */
3168          szDispText[eos--] = '\0'; 
3169          nLabelWidth = ListView_GetStringWidthA(hwnd, szDispText);
3170       }
3171   }
3172
3173   InflateRect(&rcItem, 2*CAPTION_BORDER, 0);
3174   nDrawPosX = infoPtr->iconSpacing.cx - 2*CAPTION_BORDER - nLabelWidth;
3175   if (nDrawPosX > 1)
3176   {
3177     rcItem.left += nDrawPosX / 2;
3178     rcItem.right = rcItem.left + nLabelWidth + 2*CAPTION_BORDER;
3179   }
3180   else
3181   {
3182     rcItem.left += 1;
3183     rcItem.right = rcItem.left + infoPtr->iconSpacing.cx - 1;
3184   }
3185
3186   /* draw label */  
3187   rcItem.bottom = rcItem.top + tm.tmHeight + HEIGHT_PADDING; 
3188
3189   ExtTextOutA(hdc, rcItem.left + CAPTION_BORDER, rcItem.top, textoutOptions, 
3190               &rcItem, lvItem.pszText, lstrlenA(lvItem.pszText), NULL);
3191         
3192
3193   CopyRect(SuggestedFocus,&rcItem);
3194 }
3195
3196 /***
3197  * DESCRIPTION:
3198  * Draws listview items when in report display mode.
3199  * 
3200  * PARAMETER(S):
3201  * [I] HWND : window handle
3202  * [I] HDC : device context handle 
3203  *
3204  * RETURN:
3205  * None
3206  */
3207 static VOID LISTVIEW_RefreshReport(HWND hwnd, HDC hdc, DWORD cdmode)
3208 {
3209   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd,0);
3210   SCROLLINFO scrollInfo;
3211   INT nDrawPosY = infoPtr->rcList.top;
3212   INT nColumnCount;
3213   RECT rcItem, rcTemp;
3214   INT  j;
3215   INT nItem;
3216   INT nLast;
3217   BOOL FullSelected;
3218   DWORD cditemmode = CDRF_DODEFAULT;
3219   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
3220
3221   ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
3222   scrollInfo.cbSize = sizeof(SCROLLINFO);
3223   scrollInfo.fMask = SIF_POS;
3224
3225   nItem = ListView_GetTopIndex(hwnd);
3226
3227   /* add 1 for displaying a partial item at the bottom */
3228   nLast = nItem + LISTVIEW_GetCountPerColumn(hwnd) + 1;
3229   nLast = min(nLast, GETITEMCOUNT(infoPtr));
3230
3231   /* send cache hint notification */
3232   if (GetWindowLongA(hwnd,GWL_STYLE) & LVS_OWNERDATA)
3233   {
3234     NMLVCACHEHINT nmlv;
3235
3236     nmlv.hdr.hwndFrom = hwnd;
3237     nmlv.hdr.idFrom = GetWindowLongA(hwnd,GWL_ID);
3238     nmlv.hdr.code = LVN_ODCACHEHINT;
3239     nmlv.iFrom = nItem;
3240     nmlv.iTo   = nLast;
3241
3242     SendMessageA(GetParent(hwnd), WM_NOTIFY, (WPARAM)nmlv.hdr.idFrom,
3243                  (LPARAM)&nmlv);
3244   }
3245
3246   nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
3247   infoPtr->nColumnCount = nColumnCount; /* update nColumnCount */
3248   FullSelected = infoPtr->dwExStyle & LVS_EX_FULLROWSELECT;
3249
3250   /* clear the background of any part of the control that doesn't contain items */
3251   SubtractRect(&rcTemp, &infoPtr->rcList, &infoPtr->rcView);
3252   LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3253
3254   /* nothing to draw */
3255   if(GETITEMCOUNT(infoPtr) == 0)
3256     return;
3257
3258   for (; nItem < nLast; nItem++)
3259   {
3260     RECT SuggestedFocusRect;
3261
3262     /* Do Owner Draw */
3263     if (lStyle & LVS_OWNERDRAWFIXED)
3264     {
3265         UINT uID = GetWindowLongA( hwnd, GWL_ID);
3266         DRAWITEMSTRUCT dis;
3267         LVITEMA item;
3268         RECT br;
3269
3270         TRACE("Owner Drawn\n");    
3271         dis.CtlType = ODT_LISTVIEW;
3272         dis.CtlID = uID;
3273         dis.itemID = nItem;
3274         dis.itemAction = ODA_DRAWENTIRE;
3275         dis.itemState = 0;
3276        
3277         if (LISTVIEW_IsSelected(hwnd,nItem)) dis.itemState|=ODS_SELECTED;
3278         if (nItem==infoPtr->nFocusedItem)   dis.itemState|=ODS_FOCUS;
3279
3280         dis.hwndItem = hwnd;
3281         dis.hDC = hdc;
3282
3283         Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
3284
3285         dis.rcItem.left =0; 
3286         dis.rcItem.right = max(dis.rcItem.left, br.right);
3287         dis.rcItem.top = nDrawPosY;
3288         dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3289        
3290         ZeroMemory(&item,sizeof(LVITEMA));
3291         item.iItem = nItem;
3292         item.mask = LVIF_PARAM;
3293         ListView_GetItemA(hwnd,&item);
3294
3295         dis.itemData = item.lParam;
3296
3297         if (SendMessageA(GetParent(hwnd),WM_DRAWITEM,(WPARAM)uID,(LPARAM)&dis))
3298         {
3299           nDrawPosY += infoPtr->nItemHeight;
3300           continue;
3301         }
3302     }
3303
3304     if (FullSelected)
3305     {
3306       RECT ir,br;
3307
3308       Header_GetItemRect(infoPtr->hwndHeader, 0, &ir);
3309       Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
3310
3311       ir.left += REPORT_MARGINX;
3312       ir.right = max(ir.left, br.right - REPORT_MARGINX);
3313       ir.top = nDrawPosY;
3314       ir.bottom = ir.top + infoPtr->nItemHeight;
3315       
3316       CopyRect(&SuggestedFocusRect,&ir);
3317     }
3318
3319     for (j = 0; j < nColumnCount; j++)
3320     {
3321       if (cdmode & CDRF_NOTIFYITEMDRAW)
3322         cditemmode = LISTVIEW_SendCustomDrawItemNotify (hwnd, hdc, nItem, j,
3323                                                       CDDS_ITEMPREPAINT);
3324       if (cditemmode & CDRF_SKIPDEFAULT)
3325         continue;
3326
3327       Header_GetItemRect(infoPtr->hwndHeader, j, &rcItem);
3328
3329       rcItem.left += REPORT_MARGINX;
3330       rcItem.right = max(rcItem.left, rcItem.right - REPORT_MARGINX);
3331       rcItem.top = nDrawPosY;
3332       rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3333
3334       /* Offset the Scroll Bar Pos */
3335       if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE) 
3336       {
3337         rcItem.left -= (scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE);
3338         rcItem.right -= (scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE);
3339       }
3340
3341       if (j == 0)
3342       {
3343         LISTVIEW_DrawItem(hwnd, hdc, nItem, rcItem, FullSelected,
3344                           &SuggestedFocusRect); 
3345       }
3346       else 
3347       {
3348         LISTVIEW_DrawSubItem(hwnd, hdc, nItem, j, rcItem, 
3349                               FullSelected);
3350       }
3351
3352       if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3353         LISTVIEW_SendCustomDrawItemNotify(hwnd, hdc, nItem, 0, 
3354                                       CDDS_ITEMPOSTPAINT);
3355     }
3356     /*
3357      * Draw Focus Rect
3358      */
3359     if (LISTVIEW_GetItemState(hwnd,nItem,LVIS_FOCUSED) && infoPtr->bFocus)
3360     {
3361       BOOL rop=FALSE;
3362       if (FullSelected && LISTVIEW_GetItemState(hwnd,nItem,LVIS_SELECTED))
3363         rop = SetROP2(hdc, R2_XORPEN);
3364
3365       Rectangle(hdc, SuggestedFocusRect.left, SuggestedFocusRect.top, 
3366                 SuggestedFocusRect.right,SuggestedFocusRect.bottom); 
3367
3368       if (rop)
3369         SetROP2(hdc, R2_COPYPEN);
3370     }
3371     nDrawPosY += infoPtr->nItemHeight;
3372   }
3373 }
3374
3375 /***
3376  * DESCRIPTION:
3377  * Retrieves the number of items that can fit vertically in the client area.
3378  * 
3379  * PARAMETER(S):
3380  * [I] HWND : window handle
3381  *
3382  * RETURN:
3383  * Number of items per row.
3384  */
3385 static INT LISTVIEW_GetCountPerRow(HWND hwnd)
3386 {
3387   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd,0);
3388   UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3389   INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
3390   INT nCountPerRow = 1;
3391
3392   if (nListWidth > 0)
3393   {
3394     if (uView == LVS_REPORT)
3395     {
3396       nCountPerRow = 1;
3397     }
3398     else
3399     {
3400       nCountPerRow =  nListWidth / infoPtr->nItemWidth;
3401       if (nCountPerRow == 0)
3402       {
3403         nCountPerRow = 1;
3404       }
3405     }
3406   }
3407
3408   return nCountPerRow;
3409 }
3410
3411 /***
3412  * DESCRIPTION:
3413  * Retrieves the number of items that can fit horizontally in the client 
3414  * area.
3415  * 
3416  * PARAMETER(S):
3417  * [I] HWND : window handle
3418  *
3419  * RETURN:
3420  * Number of items per column.
3421  */
3422 static INT LISTVIEW_GetCountPerColumn(HWND hwnd)
3423 {
3424   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd,0);
3425   INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3426   INT nCountPerColumn = 1;
3427
3428   if (nListHeight > 0)
3429   {
3430     nCountPerColumn =  nListHeight / infoPtr->nItemHeight;
3431     if (nCountPerColumn == 0)
3432     {
3433       nCountPerColumn = 1;
3434     }
3435   }
3436
3437   return nCountPerColumn;
3438 }
3439
3440 /***
3441  * DESCRIPTION:
3442  * Retrieves the number of columns needed to display all the items when in 
3443  * list display mode.
3444  * 
3445  * PARAMETER(S):
3446  * [I] HWND : window handle
3447  *
3448  * RETURN:
3449  * Number of columns.
3450  */
3451 static INT LISTVIEW_GetColumnCount(HWND hwnd)
3452 {
3453   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
3454   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
3455   INT nColumnCount = 0;
3456
3457   if ((lStyle & LVS_TYPEMASK) == LVS_LIST)
3458   {
3459     if (infoPtr->rcList.right % infoPtr->nItemWidth == 0)
3460     {
3461       nColumnCount = infoPtr->rcList.right / infoPtr->nItemWidth;
3462     }
3463     else
3464     {
3465       nColumnCount = infoPtr->rcList.right / infoPtr->nItemWidth + 1;
3466     }
3467   }
3468
3469   return nColumnCount;
3470 }  
3471   
3472
3473 /***
3474  * DESCRIPTION:
3475  * Draws listview items when in list display mode.
3476  * 
3477  * PARAMETER(S):
3478  * [I] HWND : window handle
3479  * [I] HDC : device context handle 
3480  *
3481  * RETURN:
3482  * None
3483  */
3484 static VOID LISTVIEW_RefreshList(HWND hwnd, HDC hdc, DWORD cdmode)
3485 {
3486   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
3487   RECT rcItem, FocusRect, rcTemp;
3488   INT i, j;
3489   INT nItem;
3490   INT nColumnCount;
3491   INT nCountPerColumn;
3492   INT nItemWidth = infoPtr->nItemWidth;
3493   INT nItemHeight = infoPtr->nItemHeight;
3494   DWORD cditemmode = CDRF_DODEFAULT;
3495
3496   /* get number of fully visible columns */
3497   nColumnCount = LISTVIEW_GetColumnCount(hwnd);
3498   infoPtr->nColumnCount = nColumnCount;
3499   nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
3500   nItem = ListView_GetTopIndex(hwnd);
3501
3502   /* paint the background of the control that doesn't contain any items */
3503   SubtractRect(&rcTemp, &infoPtr->rcList, &infoPtr->rcView);
3504   LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3505
3506   /* nothing to draw, return here */
3507   if(GETITEMCOUNT(infoPtr) == 0)
3508     return;
3509
3510   for (i = 0; i < nColumnCount; i++)
3511   {
3512     for (j = 0; j < nCountPerColumn; j++, nItem++)
3513     {
3514       if (nItem >= GETITEMCOUNT(infoPtr))
3515         return;
3516
3517       if (cdmode & CDRF_NOTIFYITEMDRAW)
3518         cditemmode = LISTVIEW_SendCustomDrawItemNotify (hwnd, hdc, nItem, 0,
3519                                                       CDDS_ITEMPREPAINT);
3520       if (cditemmode & CDRF_SKIPDEFAULT)
3521         continue;
3522
3523       rcItem.top = j * nItemHeight;
3524       rcItem.left = i * nItemWidth;
3525       rcItem.bottom = rcItem.top + nItemHeight;
3526       rcItem.right = rcItem.left + nItemWidth;
3527       LISTVIEW_DrawItem(hwnd, hdc, nItem, rcItem, FALSE, &FocusRect);
3528       /*
3529        * Draw Focus Rect
3530        */
3531       if (LISTVIEW_GetItemState(hwnd,nItem,LVIS_FOCUSED) && infoPtr->bFocus)
3532       Rectangle(hdc, FocusRect.left, FocusRect.top, 
3533                 FocusRect.right,FocusRect.bottom);
3534
3535       if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3536         LISTVIEW_SendCustomDrawItemNotify(hwnd, hdc, nItem, 0, 
3537                                           CDDS_ITEMPOSTPAINT);
3538  
3539     }
3540   }
3541 }
3542
3543 /***
3544  * DESCRIPTION:
3545  * Draws listview items when in icon or small icon display mode.
3546  * 
3547  * PARAMETER(S):
3548  * [I] HWND : window handle
3549  * [I] HDC : device context handle 
3550  *
3551  * RETURN:
3552  * None
3553  */
3554 static VOID LISTVIEW_RefreshIcon(HWND hwnd, HDC hdc, BOOL bSmall, DWORD cdmode)
3555 {
3556   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
3557   POINT ptPosition;
3558   POINT ptOrigin;
3559   RECT rcItem, SuggestedFocus, rcTemp;
3560   INT i;
3561   DWORD cditemmode = CDRF_DODEFAULT;
3562
3563   infoPtr->nColumnCount = 1; /* set this to an arbitrary value to prevent */
3564                              /* DrawItem from erasing the incorrect background area */
3565
3566   /* paint the background of the control that doesn't contain any items */
3567   SubtractRect(&rcTemp, &infoPtr->rcList, &infoPtr->rcView);
3568   LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3569
3570   /* nothing to draw, return here */
3571   if(GETITEMCOUNT(infoPtr) == 0)
3572     return;
3573
3574   LISTVIEW_GetOrigin(hwnd, &ptOrigin);
3575   for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
3576   {
3577     if (cdmode & CDRF_NOTIFYITEMDRAW)
3578       cditemmode = LISTVIEW_SendCustomDrawItemNotify (hwnd, hdc, i, 0,
3579                                                       CDDS_ITEMPREPAINT);
3580     if (cditemmode & CDRF_SKIPDEFAULT)
3581         continue;
3582
3583     LISTVIEW_GetItemPosition(hwnd, i, &ptPosition);
3584     ptPosition.x += ptOrigin.x;
3585     ptPosition.y += ptOrigin.y;
3586       
3587     if (ptPosition.y + infoPtr->nItemHeight > infoPtr->rcList.top)
3588     {
3589       if (ptPosition.x + infoPtr->nItemWidth > infoPtr->rcList.left)
3590       {
3591         if (ptPosition.y < infoPtr->rcList.bottom)
3592         {
3593           if (ptPosition.x < infoPtr->rcList.right)
3594           {
3595             rcItem.top = ptPosition.y;
3596             rcItem.left = ptPosition.x;
3597             rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3598             rcItem.right = rcItem.left + infoPtr->nItemWidth;
3599             if (bSmall == FALSE)
3600             {
3601               LISTVIEW_DrawLargeItem(hwnd, hdc, i, rcItem, &SuggestedFocus);
3602             }
3603             else
3604             {
3605               LISTVIEW_DrawItem(hwnd, hdc, i, rcItem, FALSE, &SuggestedFocus);
3606             }
3607             /*
3608              * Draw Focus Rect
3609              */
3610             if (LISTVIEW_GetItemState(hwnd,i,LVIS_FOCUSED) && 
3611                 infoPtr->bFocus)
3612               Rectangle(hdc, SuggestedFocus.left, SuggestedFocus.top, 
3613                         SuggestedFocus.right,SuggestedFocus.bottom); 
3614           }
3615         }
3616       }
3617     }
3618     if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3619         LISTVIEW_SendCustomDrawItemNotify(hwnd, hdc, i, 0, 
3620                                           CDDS_ITEMPOSTPAINT);
3621   }
3622 }
3623
3624 /***
3625  * DESCRIPTION:
3626  * Draws listview items.
3627  * 
3628  * PARAMETER(S):
3629  * [I] HWND : window handle
3630  * [I] HDC : device context handle 
3631  *
3632  * RETURN:
3633  * NoneX
3634  */
3635 static VOID LISTVIEW_Refresh(HWND hwnd, HDC hdc)
3636 {
3637   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
3638   UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3639   HFONT hOldFont;
3640   HPEN hPen, hOldPen;
3641   DWORD cdmode;
3642   RECT rect;
3643
3644   GetClientRect(hwnd, &rect);
3645   cdmode = LISTVIEW_SendCustomDrawNotify(hwnd,CDDS_PREPAINT,hdc,rect);
3646
3647   if (cdmode == CDRF_SKIPDEFAULT) return;
3648
3649   /* select font */
3650   hOldFont = SelectObject(hdc, infoPtr->hFont);
3651
3652   /* select the dotted pen (for drawing the focus box) */
3653   hPen = CreatePen(PS_ALTERNATE, 1, 0);
3654   hOldPen = SelectObject(hdc, hPen);
3655
3656   /* select transparent brush (for drawing the focus box) */
3657   SelectObject(hdc, GetStockObject(NULL_BRUSH)); 
3658
3659   if (uView == LVS_LIST)
3660   {
3661     LISTVIEW_RefreshList(hwnd, hdc, cdmode); 
3662   }
3663   else if (uView == LVS_REPORT)
3664   {
3665     LISTVIEW_RefreshReport(hwnd, hdc, cdmode);
3666   }
3667   else if (uView == LVS_SMALLICON)
3668   {
3669     LISTVIEW_RefreshIcon(hwnd, hdc, TRUE, cdmode);
3670   }
3671   else if (uView == LVS_ICON)
3672   {
3673     LISTVIEW_RefreshIcon(hwnd, hdc, FALSE, cdmode);
3674   }
3675
3676   /* unselect objects */
3677   SelectObject(hdc, hOldFont);
3678   SelectObject(hdc, hOldPen);
3679   
3680   /* delete pen */
3681   DeleteObject(hPen);
3682  
3683   if (cdmode & CDRF_NOTIFYPOSTPAINT)
3684       LISTVIEW_SendCustomDrawNotify(hwnd, CDDS_POSTPAINT, hdc, rect);
3685 }
3686
3687
3688 /***
3689  * DESCRIPTION:
3690  * Calculates the approximate width and height of a given number of items.
3691  * 
3692  * PARAMETER(S):
3693  * [I] HWND : window handle
3694  * [I] INT : number of items
3695  * [I] INT : width
3696  * [I] INT : height
3697  *
3698  * RETURN:
3699  * Returns a DWORD. The width in the low word and the height in high word.
3700  */
3701 static LRESULT LISTVIEW_ApproximateViewRect(HWND hwnd, INT nItemCount, 
3702                                             WORD wWidth, WORD wHeight)
3703 {
3704   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
3705   UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3706   INT nItemCountPerColumn = 1;
3707   INT nColumnCount = 0;
3708   DWORD dwViewRect = 0;
3709
3710   if (nItemCount == -1)
3711   {
3712     nItemCount = GETITEMCOUNT(infoPtr);
3713   }
3714
3715   if (uView == LVS_LIST)
3716   {
3717     if (wHeight == 0xFFFF)
3718     {
3719       /* use current height */
3720       wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3721     }
3722
3723     if (wHeight < infoPtr->nItemHeight)
3724     {
3725       wHeight = infoPtr->nItemHeight;
3726     }
3727
3728     if (nItemCount > 0)
3729     {
3730       if (infoPtr->nItemHeight > 0)
3731       {
3732         nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
3733         if (nItemCountPerColumn == 0)
3734         {
3735           nItemCountPerColumn = 1;
3736         }
3737         
3738         if (nItemCount % nItemCountPerColumn != 0)
3739         {
3740           nColumnCount = nItemCount / nItemCountPerColumn;
3741         }
3742         else
3743         {
3744           nColumnCount = nItemCount / nItemCountPerColumn + 1;
3745         }
3746       }
3747     }
3748
3749     /* Microsoft padding magic */
3750     wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
3751     wWidth = nColumnCount * infoPtr->nItemWidth + 2;
3752
3753     dwViewRect = MAKELONG(wWidth, wHeight);
3754   }
3755   else if (uView == LVS_REPORT)
3756   {
3757     /* TO DO */
3758   }
3759   else if (uView == LVS_SMALLICON)
3760   {
3761     /* TO DO */
3762   }
3763   else if (uView == LVS_ICON)
3764   {
3765     /* TO DO */
3766   }
3767  
3768   return dwViewRect;
3769 }
3770
3771 /***
3772  * DESCRIPTION:
3773  * Arranges listview items in icon display mode.
3774  * 
3775  * PARAMETER(S):
3776  * [I] HWND : window handle
3777  * [I] INT : alignment code
3778  *
3779  * RETURN:
3780  *   SUCCESS : TRUE
3781  *   FAILURE : FALSE
3782  */
3783 static LRESULT LISTVIEW_Arrange(HWND hwnd, INT nAlignCode)
3784 {
3785   UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK; 
3786   BOOL bResult = FALSE;
3787
3788   if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
3789   {
3790     switch (nAlignCode)
3791     {
3792     case LVA_ALIGNLEFT:
3793       /* TO DO */
3794       break;
3795     case LVA_ALIGNTOP:
3796       /* TO DO */
3797       break;
3798     case LVA_DEFAULT:
3799       /* TO DO */
3800       break;
3801     case LVA_SNAPTOGRID:
3802       /* TO DO */
3803       break;
3804     }
3805   }
3806
3807   return bResult;
3808 }
3809
3810 /* << LISTVIEW_CreateDragImage >> */
3811
3812
3813 /***
3814  * DESCRIPTION:
3815  * Removes all listview items and subitems.
3816  * 
3817  * PARAMETER(S):
3818  * [I] HWND : window handle
3819  *
3820  * RETURN:
3821  *   SUCCESS : TRUE
3822  *   FAILURE : FALSE
3823  */
3824 static LRESULT LISTVIEW_DeleteAllItems(HWND hwnd)
3825 {
3826   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
3827   LONG lCtrlId = GetWindowLongA(hwnd, GWL_ID);
3828   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
3829   UINT uView = lStyle & LVS_TYPEMASK;
3830   LISTVIEW_ITEM *lpItem;
3831   LISTVIEW_SUBITEM *lpSubItem;
3832   NMLISTVIEW nmlv;
3833   BOOL bSuppress;
3834   BOOL bResult = FALSE;
3835   INT i;
3836   INT j;
3837   HDPA hdpaSubItems;
3838
3839   TRACE("(hwnd=%x,)\n", hwnd);
3840   LISTVIEW_RemoveAllSelections(hwnd);
3841
3842   if (lStyle & LVS_OWNERDATA)
3843   {
3844     infoPtr->hdpaItems->nItemCount = 0;
3845     InvalidateRect(hwnd, NULL, TRUE);
3846     return TRUE;
3847   }
3848
3849   if (GETITEMCOUNT(infoPtr) > 0)
3850   {
3851     /* initialize memory */
3852     ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3853     
3854     /* send LVN_DELETEALLITEMS notification */
3855     nmlv.hdr.hwndFrom = hwnd;
3856     nmlv.hdr.idFrom = lCtrlId;
3857     nmlv.hdr.code = LVN_DELETEALLITEMS;
3858     nmlv.iItem = -1;
3859
3860     /* verify if subsequent LVN_DELETEITEM notifications should be 
3861        suppressed */
3862     bSuppress = ListView_LVNotify(GetParent(hwnd), lCtrlId, &nmlv);
3863
3864     for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
3865     {
3866       hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
3867       if (hdpaSubItems != NULL)
3868       {
3869         for (j = 1; j < hdpaSubItems->nItemCount; j++)
3870         {
3871           lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, j);
3872           if (lpSubItem != NULL)
3873           {
3874             /* free subitem string */
3875             if ((lpSubItem->pszText != NULL) && 
3876                 (lpSubItem->pszText != LPSTR_TEXTCALLBACKA))
3877             {
3878               COMCTL32_Free(lpSubItem->pszText);
3879             }
3880         
3881             /* free subitem */
3882             COMCTL32_Free(lpSubItem);
3883           }    
3884         }
3885
3886         lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
3887         if (lpItem != NULL)
3888         {
3889           if (bSuppress == FALSE)
3890           {
3891             /* send LVN_DELETEITEM notification */
3892             nmlv.hdr.code = LVN_DELETEITEM;
3893             nmlv.iItem = i;
3894             nmlv.lParam = lpItem->lParam;
3895             ListView_LVNotify(GetParent(hwnd), lCtrlId, &nmlv);
3896           }
3897
3898           /* free item string */
3899           if ((lpItem->pszText != NULL) && 
3900               (lpItem->pszText != LPSTR_TEXTCALLBACKA))
3901           {
3902             COMCTL32_Free(lpItem->pszText);
3903           }
3904       
3905           /* free item */
3906           COMCTL32_Free(lpItem);
3907         }
3908         
3909         DPA_Destroy(hdpaSubItems);
3910       }
3911     }
3912
3913     /* reinitialize listview memory */
3914     bResult = DPA_DeleteAllPtrs(infoPtr->hdpaItems);
3915     
3916     /* align items (set position of each item) */
3917     if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
3918     {
3919       if (lStyle & LVS_ALIGNLEFT)
3920       {
3921         LISTVIEW_AlignLeft(hwnd);
3922       }
3923       else
3924       {
3925         LISTVIEW_AlignTop(hwnd);
3926       }
3927     }
3928     
3929     LISTVIEW_UpdateScroll(hwnd);
3930
3931     /* invalidate client area (optimization needed) */
3932     InvalidateRect(hwnd, NULL, TRUE);
3933   }
3934   
3935   return bResult;
3936 }
3937
3938 /***
3939  * DESCRIPTION:
3940  * Removes a column from the listview control.
3941  * 
3942  * PARAMETER(S):
3943  * [I] HWND : window handle
3944  * [I] INT : column index
3945  *
3946  * RETURN:
3947  *   SUCCESS : TRUE
3948  *   FAILURE : FALSE
3949  */
3950 static LRESULT LISTVIEW_DeleteColumn(HWND hwnd, INT nColumn)
3951 {
3952   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
3953   UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3954   UINT uOwnerData = GetWindowLongA(hwnd, GWL_STYLE) & LVS_OWNERDATA;
3955   BOOL bResult = FALSE;
3956
3957   if (Header_DeleteItem(infoPtr->hwndHeader, nColumn) != FALSE)
3958   {
3959     if (!uOwnerData)
3960       bResult = LISTVIEW_RemoveColumn(infoPtr->hdpaItems, nColumn);
3961
3962     /* Need to reset the item width when deleting a column */
3963     infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
3964
3965     /* reset scroll parameters */
3966     if (uView == LVS_REPORT)
3967     {
3968       /* update scrollbar(s) */
3969       LISTVIEW_UpdateScroll(hwnd);
3970
3971       /* refresh client area */
3972       InvalidateRect(hwnd, NULL, FALSE);
3973     }
3974   }
3975
3976   return bResult;
3977 }
3978
3979 /***
3980  * DESCRIPTION:
3981  * Removes an item from the listview control.
3982  * 
3983  * PARAMETER(S):
3984  * [I] HWND : window handle
3985  * [I] INT : item index  
3986  *
3987  * RETURN:
3988  *   SUCCESS : TRUE
3989  *   FAILURE : FALSE
3990  */
3991 static LRESULT LISTVIEW_DeleteItem(HWND hwnd, INT nItem)
3992 {
3993   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
3994   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
3995   UINT uView = lStyle & LVS_TYPEMASK;
3996   LONG lCtrlId = GetWindowLongA(hwnd, GWL_ID);
3997   NMLISTVIEW nmlv;
3998   BOOL bResult = FALSE;
3999   HDPA hdpaSubItems;
4000   LISTVIEW_ITEM *lpItem;
4001   LISTVIEW_SUBITEM *lpSubItem;
4002   INT i;
4003   LVITEMA item;
4004
4005   TRACE("(hwnd=%x,nItem=%d)\n", hwnd, nItem);
4006
4007   /* remove it from the selection range */
4008   ZeroMemory(&item,sizeof(LVITEMA));
4009   item.stateMask = LVIS_SELECTED;
4010   LISTVIEW_SetItemState(hwnd,nItem,&item);
4011
4012   LISTVIEW_ShiftSelections(hwnd,nItem,-1);
4013
4014   if (lStyle & LVS_OWNERDATA)
4015   {
4016     infoPtr->hdpaItems->nItemCount --;
4017     InvalidateRect(hwnd, NULL, TRUE);
4018     return TRUE;
4019   }
4020
4021   if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
4022   {
4023     /* initialize memory */
4024     ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4025
4026     hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
4027     if (hdpaSubItems != NULL)
4028     {
4029       for (i = 1; i < hdpaSubItems->nItemCount; i++)
4030       {
4031         lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
4032         if (lpSubItem != NULL)
4033         {
4034           /* free item string */
4035           if ((lpSubItem->pszText != NULL) && 
4036               (lpSubItem->pszText != LPSTR_TEXTCALLBACKA))
4037           {
4038             COMCTL32_Free(lpSubItem->pszText);
4039           }
4040         
4041           /* free item */
4042           COMCTL32_Free(lpSubItem);
4043         }    
4044       }
4045   
4046       lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
4047       if (lpItem != NULL)
4048       {
4049         /* send LVN_DELETEITEM notification */
4050         nmlv.hdr.hwndFrom = hwnd;
4051         nmlv.hdr.idFrom = lCtrlId;
4052         nmlv.hdr.code = LVN_DELETEITEM;
4053         nmlv.iItem = nItem;
4054         nmlv.lParam = lpItem->lParam;
4055         SendMessageA(GetParent(hwnd), WM_NOTIFY, (WPARAM)lCtrlId, 
4056                      (LPARAM)&nmlv);
4057
4058         /* free item string */
4059         if ((lpItem->pszText != NULL) && 
4060             (lpItem->pszText != LPSTR_TEXTCALLBACKA))
4061         {
4062           COMCTL32_Free(lpItem->pszText);
4063         }
4064       
4065         /* free item */
4066         COMCTL32_Free(lpItem);
4067       }
4068     
4069       bResult = DPA_Destroy(hdpaSubItems);
4070     }
4071
4072     /* align items (set position of each item) */
4073     if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4074     {
4075       if (lStyle & LVS_ALIGNLEFT)
4076       {
4077         LISTVIEW_AlignLeft(hwnd);
4078       }
4079       else
4080       {
4081         LISTVIEW_AlignTop(hwnd);
4082       }
4083     }
4084
4085     /* If this item had focus change focus to next or previous item */
4086     if (GETITEMCOUNT(infoPtr) > 0)
4087     {
4088        int sItem = nItem < GETITEMCOUNT(infoPtr) ? nItem : nItem - 1;
4089        if (infoPtr->nFocusedItem == nItem)
4090            LISTVIEW_SetItemFocus(hwnd, sItem);
4091     }
4092     else
4093           infoPtr->nFocusedItem = -1;
4094
4095     LISTVIEW_UpdateScroll(hwnd);
4096
4097     /* refresh client area */
4098     InvalidateRect(hwnd, NULL, TRUE);
4099   }
4100   
4101   return bResult;
4102 }
4103
4104
4105 /***
4106  * DESCRIPTION:
4107  * Return edit control handle of current edit label
4108  *
4109  * PARAMETER(S):
4110  * [I] HWND : window handle
4111  *
4112  * RETURN:
4113  *   SUCCESS : HWND
4114  *   FAILURE : 0
4115  */
4116 static LRESULT LISTVIEW_GetEditControl(HWND hwnd)
4117 {
4118   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4119   return infoPtr->hwndEdit;
4120 }
4121
4122
4123 /***
4124  * DESCRIPTION:
4125  * Callback implementation for editlabel control
4126  *
4127  * PARAMETER(S):
4128  * [I] HWND : window handle
4129  * [I] LPSTR : modified text
4130  * [I] DWORD : item index
4131  *
4132  * RETURN:
4133  *   SUCCESS : TRUE
4134  *   FAILURE : FALSE
4135  */
4136
4137 static BOOL LISTVIEW_EndEditLabel(HWND hwnd, LPSTR pszText, DWORD nItem)
4138 {
4139   NMLVDISPINFOA dispInfo;
4140   LISTVIEW_ITEM *lpItem;
4141   INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
4142   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4143   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
4144   HDPA hdpaSubItems;
4145   BOOL bUpdateItemText;
4146   LISTVIEW_ITEM lvItemRef;
4147   LVITEMA item;
4148
4149   ZeroMemory(&dispInfo, sizeof(NMLVDISPINFOA));
4150  
4151   if (!(lStyle & LVS_OWNERDATA))
4152   {
4153     if (NULL == (hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)))
4154           return FALSE;
4155
4156     if (NULL == (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)))
4157           return FALSE;
4158   }
4159   else
4160   {
4161     ZeroMemory(&lvItemRef,sizeof(LISTVIEW_ITEM));
4162     ZeroMemory(&item,sizeof(LVITEMA));
4163     item.iItem = nItem;
4164     item.iSubItem = 0;
4165     item.mask = LVIF_PARAM | LVIF_STATE;
4166     ListView_GetItemA(hwnd,&item);
4167     lvItemRef.state = item.state;
4168     lvItemRef.iImage = item.iImage;
4169     lvItemRef.lParam = item.lParam;
4170     lpItem = &lvItemRef;
4171   }
4172
4173   dispInfo.hdr.hwndFrom = hwnd;
4174   dispInfo.hdr.idFrom = nCtrlId;
4175   dispInfo.hdr.code = LVN_ENDLABELEDITA;
4176   dispInfo.item.mask = 0;
4177   dispInfo.item.iItem = nItem;
4178   dispInfo.item.state = lpItem->state;
4179   dispInfo.item.stateMask = 0;
4180   dispInfo.item.pszText = pszText;
4181   dispInfo.item.cchTextMax = pszText ? strlen(pszText) : 0;
4182   dispInfo.item.iImage = lpItem->iImage;
4183   dispInfo.item.lParam = lpItem->lParam;
4184   infoPtr->hwndEdit = 0;
4185
4186   bUpdateItemText = ListView_Notify(GetParent(hwnd), nCtrlId, &dispInfo);
4187
4188   /* Do we need to update the Item Text */
4189   if(bUpdateItemText)
4190   {
4191     if ((lpItem->pszText != LPSTR_TEXTCALLBACKA)&&(!(lStyle & LVS_OWNERDATA)))
4192     {
4193         Str_SetPtrA(&lpItem->pszText, pszText);
4194     }
4195   }
4196
4197   return TRUE;
4198 }
4199
4200 /***
4201  * DESCRIPTION:
4202  * Begin in place editing of specified list view item
4203  *
4204  * PARAMETER(S):
4205  * [I] HWND : window handle
4206  * [I] INT : item index
4207  *
4208  * RETURN:
4209  *   SUCCESS : TRUE
4210  *   FAILURE : FALSE
4211  */
4212
4213 static HWND LISTVIEW_EditLabelA(HWND hwnd, INT nItem)
4214 {
4215   NMLVDISPINFOA dispInfo;
4216   RECT rect;
4217   LISTVIEW_ITEM *lpItem;
4218   HWND hedit; 
4219   HINSTANCE hinst = GetWindowLongA(hwnd, GWL_HINSTANCE);
4220   INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
4221   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4222   HDPA hdpaSubItems;
4223   CHAR szDispText[DISP_TEXT_SIZE];
4224   LVITEMA lvItem,item;
4225   LISTVIEW_ITEM lvItemRef;
4226   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
4227  
4228   if (~GetWindowLongA(hwnd, GWL_STYLE) & LVS_EDITLABELS)
4229       return FALSE;
4230
4231   /* Is the EditBox still there, if so remove it */
4232   if(infoPtr->hwndEdit != 0)
4233   {
4234       SetFocus(hwnd);
4235   }
4236
4237   LISTVIEW_SetSelection(hwnd, nItem);
4238   LISTVIEW_SetItemFocus(hwnd, nItem);
4239
4240   ZeroMemory(&dispInfo, sizeof(NMLVDISPINFOA));
4241   if (!(lStyle & LVS_OWNERDATA))
4242   {
4243     if (NULL == (hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)))
4244           return 0;
4245
4246     if (NULL == (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)))
4247           return 0;
4248   } 
4249   else
4250   {
4251     ZeroMemory(&lvItemRef,sizeof(LISTVIEW_ITEM));
4252     ZeroMemory(&item,sizeof(LVITEMA));
4253     item.iItem = nItem;
4254     item.iSubItem = 0;
4255     item.mask = LVIF_PARAM | LVIF_STATE;
4256     ListView_GetItemA(hwnd,&item);
4257     lvItemRef.iImage = item.iImage;
4258     lvItemRef.state = item.state;
4259     lvItemRef.lParam = item.lParam;
4260     lpItem = &lvItemRef;
4261   }
4262
4263   /* get information needed for drawing the item */
4264   ZeroMemory(&lvItem, sizeof(LVITEMA));
4265   lvItem.mask = LVIF_TEXT;
4266   lvItem.iItem = nItem;
4267   lvItem.iSubItem = 0;
4268   lvItem.cchTextMax = DISP_TEXT_SIZE;
4269   lvItem.pszText = szDispText;
4270   ListView_GetItemA(hwnd, &lvItem);
4271
4272   dispInfo.hdr.hwndFrom = hwnd;
4273   dispInfo.hdr.idFrom = nCtrlId;
4274   dispInfo.hdr.code = LVN_BEGINLABELEDITA;
4275   dispInfo.item.mask = 0;
4276   dispInfo.item.iItem = nItem;
4277   dispInfo.item.state = lpItem->state;
4278   dispInfo.item.stateMask = 0;
4279   dispInfo.item.pszText = lvItem.pszText;
4280   dispInfo.item.cchTextMax = strlen(lvItem.pszText);
4281   dispInfo.item.iImage = lpItem->iImage;
4282   dispInfo.item.lParam = lpItem->lParam;
4283
4284   if (ListView_LVNotify(GetParent(hwnd), nCtrlId, &dispInfo))
4285           return 0;
4286
4287   rect.left = LVIR_LABEL;
4288   if (!LISTVIEW_GetItemRect(hwnd, nItem, &rect))
4289           return 0;
4290   
4291   if (!(hedit = CreateEditLabel(szDispText , WS_VISIBLE, 
4292                  rect.left-2, rect.top-1, 0, 
4293                  rect.bottom - rect.top+2, 
4294                  hwnd, hinst, LISTVIEW_EndEditLabel, nItem)))
4295          return 0;
4296
4297   infoPtr->hwndEdit = hedit;
4298   SetFocus(hedit); 
4299   SendMessageA(hedit, EM_SETSEL, 0, -1);
4300
4301   return hedit;
4302 }
4303
4304
4305 /***
4306  * DESCRIPTION:
4307  * Ensures the specified item is visible, scrolling into view if necessary.
4308  * 
4309  * PARAMETER(S):
4310  * [I] HWND : window handle
4311  * [I] INT : item index
4312  * [I] BOOL : partially or entirely visible
4313  *
4314  * RETURN:
4315  *   SUCCESS : TRUE
4316  *   FAILURE : FALSE
4317  */
4318 static BOOL LISTVIEW_EnsureVisible(HWND hwnd, INT nItem, BOOL bPartial)
4319 {
4320   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4321   UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
4322   INT nScrollPosHeight = 0;
4323   INT nScrollPosWidth = 0;
4324   SCROLLINFO scrollInfo;
4325   RECT rcItem;
4326   BOOL bRedraw = FALSE;
4327
4328   ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
4329   scrollInfo.cbSize = sizeof(SCROLLINFO);
4330   scrollInfo.fMask = SIF_POS;
4331
4332   /* ALWAYS bPartial == FALSE, FOR NOW! */
4333
4334   rcItem.left = LVIR_BOUNDS;
4335   if (LISTVIEW_GetItemRect(hwnd, nItem, &rcItem) != FALSE)
4336   {
4337     if (rcItem.left < infoPtr->rcList.left)
4338     {
4339       if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
4340       {
4341         /* scroll left */
4342         bRedraw = TRUE;
4343         if (uView == LVS_LIST)
4344         {
4345           nScrollPosWidth = infoPtr->nItemWidth;
4346           rcItem.left += infoPtr->rcList.left;
4347         }
4348         else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4349         {
4350           nScrollPosWidth = LISTVIEW_SCROLL_DIV_SIZE;
4351           rcItem.left += infoPtr->rcList.left;
4352         }
4353         
4354         /* When in LVS_REPORT view, the scroll position should 
4355            not be updated. */
4356         if (nScrollPosWidth != 0)
4357         {
4358           if (rcItem.left % nScrollPosWidth == 0)
4359           {
4360             scrollInfo.nPos += rcItem.left / nScrollPosWidth;
4361           }
4362           else
4363           {
4364             scrollInfo.nPos += rcItem.left / nScrollPosWidth - 1;
4365           }
4366           
4367           SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
4368         }
4369       }
4370     }
4371     else if (rcItem.right > infoPtr->rcList.right)
4372     {
4373       if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
4374       {
4375         /* scroll right */
4376         bRedraw = TRUE;
4377         if (uView == LVS_LIST)
4378         {
4379           rcItem.right -= infoPtr->rcList.right;
4380           nScrollPosWidth = infoPtr->nItemWidth;
4381         }
4382         else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4383         {
4384           rcItem.right -= infoPtr->rcList.right;
4385           nScrollPosWidth = LISTVIEW_SCROLL_DIV_SIZE;
4386         }
4387
4388         /* When in LVS_REPORT view, the scroll position should 
4389            not be updated. */
4390         if (nScrollPosWidth != 0)
4391         {
4392           if (rcItem.right % nScrollPosWidth == 0)
4393           {
4394             scrollInfo.nPos += rcItem.right / nScrollPosWidth;
4395           }
4396           else
4397           {
4398             scrollInfo.nPos += rcItem.right / nScrollPosWidth + 1;
4399           }
4400
4401           SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
4402         }
4403       }
4404     }
4405     
4406     if (rcItem.top < infoPtr->rcList.top)
4407     {
4408       /* scroll up */
4409       bRedraw = TRUE;
4410       if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
4411       {
4412         if (uView == LVS_REPORT)
4413         {
4414           rcItem.top -= infoPtr->rcList.top; 
4415           nScrollPosHeight = infoPtr->nItemHeight;
4416         }
4417         else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4418         {
4419           nScrollPosHeight = LISTVIEW_SCROLL_DIV_SIZE;
4420           rcItem.top += infoPtr->rcList.top;
4421         }
4422
4423         if (rcItem.top % nScrollPosHeight == 0)
4424         {
4425           scrollInfo.nPos += rcItem.top / nScrollPosHeight;
4426         }
4427         else
4428         {
4429           scrollInfo.nPos += rcItem.top / nScrollPosHeight - 1;
4430         }
4431         
4432         SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
4433       }
4434     }
4435     else if (rcItem.bottom > infoPtr->rcList.bottom)
4436     {
4437       /* scroll down */
4438       bRedraw = TRUE;
4439       if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
4440       {
4441         if (uView == LVS_REPORT)
4442         {
4443           rcItem.bottom -= infoPtr->rcList.bottom;
4444           nScrollPosHeight = infoPtr->nItemHeight;
4445         }
4446         else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4447         {
4448           nScrollPosHeight = LISTVIEW_SCROLL_DIV_SIZE;
4449           rcItem.bottom -= infoPtr->rcList.bottom;
4450         }
4451
4452         if (rcItem.bottom % nScrollPosHeight == 0)
4453         {
4454           scrollInfo.nPos += rcItem.bottom / nScrollPosHeight;
4455         }
4456         else
4457         {
4458           scrollInfo.nPos += rcItem.bottom / nScrollPosHeight + 1;
4459         }
4460         
4461         SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
4462       }
4463     }
4464   }
4465  
4466   if(bRedraw)
4467     InvalidateRect(hwnd,NULL,TRUE); 
4468   return TRUE;
4469 }
4470
4471 /***
4472  * DESCRIPTION:
4473  * Retrieves the nearest item, given a position and a direction.
4474  * 
4475  * PARAMETER(S):
4476  * [I] HWND : window handle
4477  * [I] POINT : start position
4478  * [I] UINT : direction
4479  *
4480  * RETURN:
4481  * Item index if successdful, -1 otherwise.
4482  */
4483 static INT LISTVIEW_GetNearestItem(HWND hwnd, POINT pt, UINT vkDirection)
4484 {
4485   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4486   LVHITTESTINFO lvHitTestInfo;
4487   INT nItem = -1;
4488   RECT rcView;
4489
4490   if (LISTVIEW_GetViewRect(hwnd, &rcView) != FALSE)
4491   {
4492     ZeroMemory(&lvHitTestInfo, sizeof(LVHITTESTINFO));
4493     LISTVIEW_GetOrigin(hwnd, &lvHitTestInfo.pt);
4494     lvHitTestInfo.pt.x += pt.x;
4495     lvHitTestInfo.pt.y += pt.y;
4496     
4497     do
4498     { 
4499       if (vkDirection == VK_DOWN)
4500       {
4501         lvHitTestInfo.pt.y += infoPtr->nItemHeight;
4502       }      
4503       else if (vkDirection == VK_UP)
4504       {
4505         lvHitTestInfo.pt.y -= infoPtr->nItemHeight;
4506       }
4507       else if (vkDirection == VK_LEFT)
4508       {
4509         lvHitTestInfo.pt.x -= infoPtr->nItemWidth;
4510       }
4511       else if (vkDirection == VK_RIGHT)
4512       {
4513         lvHitTestInfo.pt.x += infoPtr->nItemWidth;
4514       }
4515
4516       if (PtInRect(&rcView, lvHitTestInfo.pt) == FALSE)
4517       {
4518         return -1;
4519       }
4520       else
4521       {
4522         nItem = LISTVIEW_HitTestItem(hwnd, &lvHitTestInfo, TRUE);
4523       }
4524
4525     }
4526     while (nItem == -1);
4527   }
4528
4529   return nItem;
4530 }  
4531
4532 /***
4533  * DESCRIPTION:
4534  * Searches for an item with specific characteristics.
4535  * 
4536  * PARAMETER(S):
4537  * [I] HWND : window handle
4538  * [I] INT : base item index
4539  * [I] LPLVFINDINFO : item information to look for
4540  * 
4541  * RETURN:
4542  *   SUCCESS : index of item
4543  *   FAILURE : -1
4544  */
4545 static LRESULT LISTVIEW_FindItem(HWND hwnd, INT nStart, 
4546                                  LPLVFINDINFO lpFindInfo)
4547 {
4548   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4549   POINT ptItem;
4550   CHAR szDispText[DISP_TEXT_SIZE];
4551   LVITEMA lvItem;
4552   BOOL bWrap = FALSE;
4553   INT nItem = nStart;
4554   INT nLast = GETITEMCOUNT(infoPtr);
4555
4556   if ((nItem >= -1) && (lpFindInfo != NULL))
4557   {
4558     ZeroMemory(&lvItem, sizeof(LVITEMA));
4559
4560     if (lpFindInfo->flags & LVFI_PARAM)
4561     {
4562       lvItem.mask |= LVIF_PARAM;
4563     }
4564     
4565     if (lpFindInfo->flags & LVFI_STRING)
4566     {
4567       lvItem.mask |= LVIF_TEXT;
4568       lvItem.pszText = szDispText;
4569       lvItem.cchTextMax = DISP_TEXT_SIZE;
4570     }
4571
4572     if (lpFindInfo->flags & LVFI_PARTIAL)
4573     {
4574       lvItem.mask |= LVIF_TEXT;
4575       lvItem.pszText = szDispText;
4576       lvItem.cchTextMax = DISP_TEXT_SIZE;
4577     }
4578
4579     if (lpFindInfo->flags & LVFI_WRAP)
4580     {
4581       bWrap = TRUE;
4582     }
4583
4584     if (lpFindInfo->flags & LVFI_NEARESTXY)
4585     {
4586       ptItem.x = lpFindInfo->pt.x;
4587       ptItem.y = lpFindInfo->pt.y;
4588     }
4589
4590     while (1)
4591     {
4592       while (nItem < nLast)
4593       {
4594         if (lpFindInfo->flags & LVFI_NEARESTXY)
4595         {
4596           nItem = LISTVIEW_GetNearestItem(hwnd, ptItem, 
4597                                           lpFindInfo->vkDirection);
4598           if (nItem != -1)
4599           {
4600             /* get position of the new item index */
4601             if (ListView_GetItemPosition(hwnd, nItem, &ptItem) == FALSE)
4602               return -1;
4603           }
4604           else
4605             return -1;
4606         }
4607         else
4608         {
4609           nItem++;
4610         }
4611         
4612         lvItem.iItem = nItem;
4613         lvItem.iSubItem = 0;
4614         if (LISTVIEW_GetItemA(hwnd, &lvItem, TRUE) != FALSE)
4615         {
4616           if (lvItem.mask & LVIF_TEXT)
4617           {
4618             if (lpFindInfo->flags & LVFI_PARTIAL)
4619             {
4620               if (strstr(lvItem.pszText, lpFindInfo->psz) == NULL)
4621                 continue;
4622             }
4623             else
4624             {
4625               if (strcmp(lvItem.pszText, lpFindInfo->psz) != 0)
4626                 continue;
4627             }
4628           }
4629           
4630           if (lvItem.mask & LVIF_PARAM)
4631           {
4632             if (lpFindInfo->lParam != lvItem.lParam)
4633               continue;
4634           }
4635           
4636           return nItem;
4637         }
4638       }
4639
4640       if (bWrap != FALSE)
4641       {
4642         nItem = -1;
4643         nLast = nStart + 1;
4644         bWrap = FALSE;
4645       }
4646       else
4647       {
4648         return -1;
4649       }       
4650     }
4651   }
4652
4653  return -1; 
4654 }
4655
4656 /***
4657  * DESCRIPTION:
4658  * Retrieves the background color of the listview control.
4659  * 
4660  * PARAMETER(S):
4661  * [I] HWND : window handle
4662  *
4663  * RETURN:
4664  * COLORREF associated with the background.
4665  */
4666 static LRESULT LISTVIEW_GetBkColor(HWND hwnd)
4667 {
4668   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4669
4670   return infoPtr->clrBk;
4671 }
4672
4673 /***
4674  * DESCRIPTION:
4675  * Retrieves the background image of the listview control.
4676  * 
4677  * PARAMETER(S):
4678  * [I] HWND : window handle
4679  * [O] LPLVMKBIMAGE : background image attributes
4680  *
4681  * RETURN:
4682  *   SUCCESS : TRUE
4683  *   FAILURE : FALSE
4684  */
4685 /* static LRESULT LISTVIEW_GetBkImage(HWND hwnd, LPLVBKIMAGE lpBkImage)   */
4686 /* {   */
4687 /*   FIXME (listview, "empty stub!\n"); */
4688 /*   return FALSE;   */
4689 /* }   */
4690
4691 /***
4692  * DESCRIPTION:
4693  * Retrieves the callback mask.
4694  * 
4695  * PARAMETER(S):
4696  * [I] HWND : window handle
4697  *
4698  * RETURN:
4699  *  Value of mask
4700  */
4701 static UINT LISTVIEW_GetCallbackMask(HWND hwnd)
4702 {
4703   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4704
4705   return infoPtr->uCallbackMask;
4706 }
4707
4708 /***
4709  * DESCRIPTION:
4710  * Retrieves column attributes.
4711  * 
4712  * PARAMETER(S):
4713  * [I] HWND : window handle
4714  * [I] INT :  column index
4715  * [IO] LPLVCOLUMNA : column information
4716  *
4717  * RETURN:
4718  *   SUCCESS : TRUE
4719  *   FAILURE : FALSE
4720  */
4721 static LRESULT LISTVIEW_GetColumnA(HWND hwnd, INT nItem, LPLVCOLUMNA lpColumn)
4722 {
4723   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4724   HDITEMA hdi;
4725   BOOL bResult = FALSE;
4726   
4727   if (lpColumn != NULL)
4728   {
4729     /* initialize memory */
4730     ZeroMemory(&hdi, sizeof(HDITEMA));
4731     
4732     if (lpColumn->mask & LVCF_FMT)
4733     {
4734       hdi.mask |= HDI_FORMAT;
4735     }
4736
4737     if (lpColumn->mask & LVCF_WIDTH)
4738     {
4739       hdi.mask |= HDI_WIDTH;
4740     }
4741
4742     if (lpColumn->mask & LVCF_TEXT)
4743     {
4744       hdi.mask |= HDI_TEXT;
4745       hdi.cchTextMax = lpColumn->cchTextMax;
4746       hdi.pszText    = lpColumn->pszText;
4747     }
4748
4749     if (lpColumn->mask & LVCF_IMAGE)
4750     {
4751       hdi.mask |= HDI_IMAGE;
4752     }
4753
4754     if (lpColumn->mask & LVCF_ORDER)
4755     {
4756       hdi.mask |= HDI_ORDER;
4757     }
4758
4759     bResult = Header_GetItemA(infoPtr->hwndHeader, nItem, &hdi);
4760     if (bResult != FALSE)
4761     {
4762       if (lpColumn->mask & LVCF_FMT) 
4763       {
4764         lpColumn->fmt = 0;
4765
4766         if (hdi.fmt & HDF_LEFT)
4767         {
4768           lpColumn->fmt |= LVCFMT_LEFT;
4769         }
4770         else if (hdi.fmt & HDF_RIGHT)
4771         {
4772           lpColumn->fmt |= LVCFMT_RIGHT;
4773         }
4774         else if (hdi.fmt & HDF_CENTER)
4775         {
4776           lpColumn->fmt |= LVCFMT_CENTER;
4777         }
4778
4779         if (hdi.fmt & HDF_IMAGE)
4780         {
4781           lpColumn->fmt |= LVCFMT_COL_HAS_IMAGES;
4782         }
4783
4784         if (hdi.fmt & HDF_BITMAP_ON_RIGHT)
4785         {
4786           lpColumn->fmt |= LVCFMT_BITMAP_ON_RIGHT;
4787         }
4788       }
4789
4790       if (lpColumn->mask & LVCF_WIDTH)
4791       {
4792         lpColumn->cx = hdi.cxy;
4793       }
4794       
4795       if (lpColumn->mask & LVCF_IMAGE)
4796       {
4797         lpColumn->iImage = hdi.iImage;
4798       }
4799
4800       if (lpColumn->mask & LVCF_ORDER)
4801       {
4802         lpColumn->iOrder = hdi.iOrder;
4803       }
4804     }
4805   }
4806
4807   return bResult;
4808 }
4809
4810 /* LISTVIEW_GetColumnW */
4811
4812
4813 static LRESULT LISTVIEW_GetColumnOrderArray(HWND hwnd, INT iCount, LPINT lpiArray)
4814 {
4815 /*  LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0); */
4816     INT i;
4817
4818     if (!lpiArray)
4819         return FALSE;
4820
4821     /* little hack */
4822     for (i = 0; i < iCount; i++)
4823         lpiArray[i] = i;
4824
4825     return TRUE;
4826 }
4827
4828 /***
4829  * DESCRIPTION:
4830  * Retrieves the column width.
4831  * 
4832  * PARAMETER(S):
4833  * [I] HWND : window handle
4834  * [I] int : column index
4835  *
4836  * RETURN:
4837  *   SUCCESS : column width
4838  *   FAILURE : zero 
4839  */
4840 static LRESULT LISTVIEW_GetColumnWidth(HWND hwnd, INT nColumn)
4841 {
4842   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4843   UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
4844   INT nColumnWidth = 0;
4845   HDITEMA hdi;
4846
4847   if (uView == LVS_LIST)
4848   {
4849     nColumnWidth = infoPtr->nItemWidth;
4850   }
4851   else if (uView == LVS_REPORT)
4852   {
4853     /* get column width from header */
4854     ZeroMemory(&hdi, sizeof(HDITEMA));
4855     hdi.mask = HDI_WIDTH;
4856     if (Header_GetItemA(infoPtr->hwndHeader, nColumn, &hdi) != FALSE)
4857     {
4858       nColumnWidth = hdi.cxy;
4859     }
4860   }
4861
4862   return nColumnWidth;
4863 }
4864
4865 /***
4866  * DESCRIPTION:
4867  * In list or report display mode, retrieves the number of items that can fit 
4868  * vertically in the visible area. In icon or small icon display mode, 
4869  * retrieves the total number of visible items.
4870  * 
4871  * PARAMETER(S):
4872  * [I] HWND : window handle
4873  *
4874  * RETURN:
4875  * Number of fully visible items.
4876  */
4877 static LRESULT LISTVIEW_GetCountPerPage(HWND hwnd)
4878 {
4879   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4880   UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
4881   INT nItemCount = 0;
4882
4883   if (uView == LVS_LIST)
4884   {
4885     if (infoPtr->rcList.right > infoPtr->nItemWidth)
4886     {
4887       nItemCount = LISTVIEW_GetCountPerRow(hwnd) * 
4888                    LISTVIEW_GetCountPerColumn(hwnd);
4889     }
4890   }
4891   else if (uView == LVS_REPORT)
4892   {
4893     nItemCount = LISTVIEW_GetCountPerColumn(hwnd);
4894   }
4895   else
4896   {
4897     nItemCount = GETITEMCOUNT(infoPtr);
4898   }
4899
4900   return nItemCount;
4901 }
4902
4903 /* LISTVIEW_GetEditControl */
4904
4905 /***
4906  * DESCRIPTION:
4907  * Retrieves the extended listview style.
4908  *
4909  * PARAMETERS:
4910  * [I] HWND  : window handle
4911  *
4912  * RETURN:
4913  *   SUCCESS : previous style
4914  *   FAILURE : 0
4915  */
4916 static LRESULT LISTVIEW_GetExtendedListViewStyle(HWND hwnd)
4917 {
4918     LISTVIEW_INFO *infoPtr;
4919
4920     /* make sure we can get the listview info */
4921     if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0)))
4922         return (0);
4923
4924     return (infoPtr->dwExStyle);
4925 }
4926
4927 /***
4928  * DESCRIPTION:
4929  * Retrieves the handle to the header control.
4930  * 
4931  * PARAMETER(S):
4932  * [I] HWND : window handle
4933  *
4934  * RETURN:
4935  * Header handle.
4936  */
4937 static LRESULT LISTVIEW_GetHeader(HWND hwnd)
4938 {
4939   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4940
4941   return infoPtr->hwndHeader;
4942 }
4943
4944 /* LISTVIEW_GetHotCursor */
4945
4946 /***
4947  * DESCRIPTION:
4948  * Returns the time that the mouse cursor must hover over an item
4949  * before it is selected.
4950  *
4951  * PARAMETER(S):
4952  * [I] HWND : window handle
4953  *
4954  * RETURN:
4955  *   Returns the previously set hover time or (DWORD)-1 to indicate that the
4956  *   hover time is set to the default hover time.
4957  */
4958 static LRESULT LISTVIEW_GetHoverTime(HWND hwnd)
4959 {
4960   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4961
4962   return infoPtr->dwHoverTime;
4963 }
4964
4965 /***
4966  * DESCRIPTION:
4967  * Retrieves an image list handle.
4968  * 
4969  * PARAMETER(S):
4970  * [I] HWND : window handle
4971  * [I] INT : image list identifier 
4972  * 
4973  * RETURN:
4974  *   SUCCESS : image list handle
4975  *   FAILURE : NULL
4976  */
4977 static LRESULT LISTVIEW_GetImageList(HWND hwnd, INT nImageList)
4978 {
4979   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4980   HIMAGELIST himl = NULL;
4981
4982   switch (nImageList) 
4983   {
4984   case LVSIL_NORMAL:
4985     himl = infoPtr->himlNormal;
4986     break;
4987   case LVSIL_SMALL:
4988     himl = infoPtr->himlSmall;
4989     break;
4990   case LVSIL_STATE:
4991     himl = infoPtr->himlState;
4992     break;
4993   }
4994
4995   return (LRESULT)himl;
4996 }
4997
4998 /* LISTVIEW_GetISearchString */
4999
5000 /***
5001  * DESCRIPTION:
5002  * Retrieves item attributes.
5003  * 
5004  * PARAMETER(S):
5005  * [I] HWND : window handle
5006  * [IO] LPLVITEMA : item info
5007  * [I] internal : if true then we will use tricks that avoid copies
5008  *               but are not compatible with the regular interface
5009  * 
5010  * RETURN:
5011  *   SUCCESS : TRUE 
5012  *   FAILURE : FALSE
5013  */
5014 static LRESULT LISTVIEW_GetItemA(HWND hwnd, LPLVITEMA lpLVItem, BOOL internal)
5015 {
5016   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5017   LONG lCtrlId = GetWindowLongA(hwnd, GWL_ID);
5018   NMLVDISPINFOA dispInfo;
5019   LISTVIEW_SUBITEM *lpSubItem;
5020   LISTVIEW_ITEM *lpItem;
5021   INT* piImage;
5022   LPSTR* ppszText;
5023   HDPA hdpaSubItems;
5024   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
5025   /* In the following:
5026    * lpLVItem describes the information requested by the user
5027    * lpItem/lpSubItem is what we have
5028    * dispInfo is a structure we use to request the missing 
5029    *     information from the application
5030    */
5031
5032   TRACE("(hwnd=%x, lpLVItem=%p)\n", hwnd, lpLVItem);
5033
5034   if ((lpLVItem == NULL) ||
5035       (lpLVItem->iItem < 0) ||
5036       (lpLVItem->iItem >= GETITEMCOUNT(infoPtr))
5037      )
5038     return FALSE;
5039
5040   if (lStyle & LVS_OWNERDATA)
5041   {
5042     if (lpLVItem->mask & ~LVIF_STATE)
5043     {
5044       dispInfo.hdr.hwndFrom = hwnd;
5045       dispInfo.hdr.idFrom = lCtrlId;
5046       dispInfo.hdr.code = LVN_GETDISPINFOA;
5047       memcpy(&dispInfo.item,lpLVItem,sizeof(LVITEMA));
5048
5049       ListView_Notify(GetParent(hwnd), lCtrlId, &dispInfo);
5050       memcpy(lpLVItem,&dispInfo.item,sizeof(LVITEMA));
5051     }
5052
5053     if ((lpLVItem->mask & LVIF_STATE)&&(lpLVItem->iSubItem == 0))
5054     {
5055       lpLVItem->state = 0; 
5056       if (infoPtr->nFocusedItem == lpLVItem->iItem)
5057         lpLVItem->state |= LVIS_FOCUSED;
5058       if (LISTVIEW_IsSelected(hwnd,lpLVItem->iItem))
5059         lpLVItem->state |= LVIS_SELECTED;
5060     }
5061
5062     return TRUE;
5063   }
5064
5065
5066   hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
5067   if (hdpaSubItems == NULL)
5068     return FALSE;
5069
5070   lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
5071   if (lpItem == NULL)
5072     return FALSE;
5073
5074   ZeroMemory(&dispInfo, sizeof(NMLVDISPINFOA));
5075   if (lpLVItem->iSubItem == 0)
5076   {
5077     piImage=&lpItem->iImage;
5078     ppszText=&lpItem->pszText;
5079     if ((infoPtr->uCallbackMask != 0) && (lpLVItem->mask & LVIF_STATE))
5080     {        
5081       dispInfo.item.mask |= LVIF_STATE;
5082       dispInfo.item.stateMask = infoPtr->uCallbackMask; 
5083     }
5084   }
5085   else
5086   {
5087     lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
5088     if (lpSubItem != NULL)
5089     {
5090       piImage=&lpSubItem->iImage;
5091       ppszText=&lpSubItem->pszText;
5092     }
5093     else
5094     {
5095       piImage=NULL;
5096       ppszText=NULL;
5097     }
5098   }
5099
5100   if ((lpLVItem->mask & LVIF_IMAGE) &&
5101       ((piImage==NULL) || (*piImage == I_IMAGECALLBACK)))
5102   {
5103     dispInfo.item.mask |= LVIF_IMAGE;
5104   }
5105
5106   if ((lpLVItem->mask & LVIF_TEXT) &&
5107       ((ppszText==NULL) || (*ppszText == LPSTR_TEXTCALLBACKA)))
5108   {
5109     dispInfo.item.mask |= LVIF_TEXT;
5110     dispInfo.item.pszText = lpLVItem->pszText;
5111     dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5112   }
5113
5114   if (dispInfo.item.mask != 0)
5115   {
5116     /* We don't have all the requested info, query the application */
5117     dispInfo.hdr.hwndFrom = hwnd;
5118     dispInfo.hdr.idFrom = lCtrlId;
5119     dispInfo.hdr.code = LVN_GETDISPINFOA;
5120     dispInfo.item.iItem = lpLVItem->iItem;
5121     dispInfo.item.iSubItem = lpLVItem->iSubItem;
5122     dispInfo.item.lParam = lpItem->lParam;
5123     ListView_Notify(GetParent(hwnd), lCtrlId, &dispInfo);
5124   }
5125
5126   if (dispInfo.item.mask & LVIF_IMAGE)
5127   {
5128     lpLVItem->iImage = dispInfo.item.iImage;
5129   }
5130   else if (lpLVItem->mask & LVIF_IMAGE)
5131   {
5132     lpLVItem->iImage = *piImage;
5133   }
5134
5135   if (dispInfo.item.mask & LVIF_PARAM)
5136   {
5137     lpLVItem->lParam = dispInfo.item.lParam;
5138   }
5139   else if (lpLVItem->mask & LVIF_PARAM)
5140   {
5141     lpLVItem->lParam = lpItem->lParam;
5142   }
5143
5144   if (dispInfo.item.mask & LVIF_TEXT)
5145   {
5146     if ((dispInfo.item.mask & LVIF_DI_SETITEM) && (ppszText != NULL))
5147     {
5148       Str_SetPtrA(ppszText, dispInfo.item.pszText);
5149     }
5150     /* If lpLVItem->pszText==dispInfo.item.pszText a copy is unnecessary, but */
5151     /* some apps give a new pointer in ListView_Notify so we can't be sure.  */
5152     if (lpLVItem->pszText!=dispInfo.item.pszText) {
5153       lstrcpynA(lpLVItem->pszText, dispInfo.item.pszText, lpLVItem->cchTextMax);
5154     }
5155   }
5156   else if (lpLVItem->mask & LVIF_TEXT)
5157   {
5158     if (internal==TRUE)
5159     {
5160       lpLVItem->pszText=*ppszText;
5161     } else {
5162       lstrcpynA(lpLVItem->pszText, *ppszText, lpLVItem->cchTextMax);
5163     }
5164   }
5165
5166   if (lpLVItem->iSubItem == 0)
5167   {
5168     if (dispInfo.item.mask & LVIF_STATE)
5169     {
5170       lpLVItem->state = lpItem->state;
5171       lpLVItem->state &= ~dispInfo.item.stateMask;
5172       lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
5173
5174       lpLVItem->state &= ~LVIS_SELECTED;
5175       if ((dispInfo.item.stateMask & LVIS_SELECTED) && 
5176          (LISTVIEW_IsSelected(hwnd,dispInfo.item.iItem)))
5177         lpLVItem->state |= LVIS_SELECTED;
5178     }
5179     else if (lpLVItem->mask & LVIF_STATE)
5180     {
5181       lpLVItem->state = lpItem->state & lpLVItem->stateMask;
5182
5183       lpLVItem->state &= ~LVIS_SELECTED;
5184       if ((lpLVItem->stateMask & LVIS_SELECTED) && 
5185           (LISTVIEW_IsSelected(hwnd,lpLVItem->iItem)))
5186          lpLVItem->state |= LVIS_SELECTED;
5187     }
5188
5189     if (lpLVItem->mask & LVIF_PARAM)
5190     {
5191       lpLVItem->lParam = lpItem->lParam;
5192     }
5193
5194     if (lpLVItem->mask & LVIF_INDENT)
5195     {
5196       lpLVItem->iIndent = lpItem->iIndent;
5197     }
5198   }
5199
5200   return TRUE;
5201 }
5202
5203 /* LISTVIEW_GetItemW */
5204 /* LISTVIEW_GetHotCursor */
5205
5206 /***
5207  * DESCRIPTION:
5208  * Retrieves the index of the hot item.
5209  *
5210  * PARAMETERS:
5211  * [I] HWND  : window handle
5212  *
5213  * RETURN:
5214  *   SUCCESS : hot item index
5215  *   FAILURE : -1 (no hot item)
5216  */
5217 static LRESULT LISTVIEW_GetHotItem(HWND hwnd)
5218 {
5219     LISTVIEW_INFO *infoPtr;
5220
5221     /* make sure we can get the listview info */
5222     if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0)))
5223         return (-1);
5224
5225     return (infoPtr->nHotItem);
5226 }
5227
5228 /* LISTVIEW_GetHoverTime */
5229
5230 /***
5231  * DESCRIPTION:
5232  * Retrieves the number of items in the listview control.
5233  * 
5234  * PARAMETER(S):
5235  * [I] HWND : window handle
5236  * 
5237  * RETURN:
5238  * Number of items.
5239  */
5240 static LRESULT LISTVIEW_GetItemCount(HWND hwnd)
5241 {
5242   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5243
5244   return GETITEMCOUNT(infoPtr);
5245 }
5246
5247 /***
5248  * DESCRIPTION:
5249  * Retrieves the position (upper-left) of the listview control item.
5250  * 
5251  * PARAMETER(S):
5252  * [I] HWND : window handle
5253  * [I] INT : item index
5254  * [O] LPPOINT : coordinate information
5255  *
5256  * RETURN:
5257  *   SUCCESS : TRUE
5258  *   FAILURE : FALSE
5259  */
5260 static BOOL LISTVIEW_GetItemPosition(HWND hwnd, INT nItem, 
5261                                      LPPOINT lpptPosition)
5262 {
5263   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5264   UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5265   BOOL bResult = FALSE;
5266   HDPA hdpaSubItems;
5267   LISTVIEW_ITEM *lpItem;
5268   INT nCountPerColumn;
5269   INT nRow;
5270
5271   TRACE("(hwnd=%x,nItem=%d,lpptPosition=%p)\n", hwnd, nItem, 
5272         lpptPosition);
5273   
5274   if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) && 
5275       (lpptPosition != NULL))
5276   {
5277     if (uView == LVS_LIST)
5278     {
5279       bResult = TRUE;
5280       nItem = nItem - ListView_GetTopIndex(hwnd);
5281       nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
5282       if (nItem < 0)
5283       {
5284         nRow = nItem % nCountPerColumn;
5285         if (nRow == 0)
5286         {
5287           lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
5288           lpptPosition->y = 0;
5289         }
5290         else
5291         {
5292           lpptPosition->x = (nItem / nCountPerColumn -1) * infoPtr->nItemWidth;
5293           lpptPosition->y = (nRow + nCountPerColumn) * infoPtr->nItemHeight;  
5294         }
5295       }
5296       else
5297       {
5298         lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
5299         lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight;
5300       }
5301     }
5302     else if (uView == LVS_REPORT)
5303     {
5304       bResult = TRUE;
5305       lpptPosition->x = REPORT_MARGINX;
5306       lpptPosition->y = ((nItem - ListView_GetTopIndex(hwnd)) * 
5307                          infoPtr->nItemHeight) + infoPtr->rcList.top;
5308     }
5309     else
5310     {
5311       hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
5312       if (hdpaSubItems != NULL)
5313       {
5314         lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
5315         if (lpItem != NULL)
5316         {
5317           bResult = TRUE;
5318           lpptPosition->x = lpItem->ptPosition.x;
5319           lpptPosition->y = lpItem->ptPosition.y;
5320         }
5321       }
5322     }
5323   }
5324   return bResult;
5325 }
5326
5327 /***
5328  * DESCRIPTION:
5329  * Retrieves the bounding rectangle for a listview control item.
5330  * 
5331  * PARAMETER(S):
5332  * [I] HWND : window handle
5333  * [I] INT : item index
5334  * [IO] LPRECT : bounding rectangle coordinates
5335  * 
5336  * RETURN:
5337  *   SUCCESS : TRUE
5338  *   FAILURE : FALSE
5339  */
5340 static LRESULT LISTVIEW_GetItemRect(HWND hwnd, INT nItem, LPRECT lprc)
5341 {
5342   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5343   UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5344   BOOL bResult = FALSE;
5345   POINT ptOrigin;
5346   POINT ptItem;
5347   HDC hdc;
5348   HFONT hOldFont;
5349   INT nLeftPos;
5350   INT nLabelWidth;
5351   INT nIndent;
5352   TEXTMETRICA tm;
5353   LVITEMA lvItem;
5354
5355   TRACE("(hwnd=%x, nItem=%d, lprc=%p)\n", hwnd, nItem, lprc);
5356
5357   if (uView & LVS_REPORT)
5358   {
5359     ZeroMemory(&lvItem, sizeof(LVITEMA));
5360     lvItem.mask = LVIF_INDENT;
5361     lvItem.iItem = nItem;
5362     lvItem.iSubItem = 0;
5363     LISTVIEW_GetItemA(hwnd, &lvItem, TRUE);
5364
5365     /* do indent */
5366     if (lvItem.iIndent>0 && infoPtr->iconSize.cx > 0)
5367     {
5368       nIndent = infoPtr->iconSize.cx * lvItem.iIndent; 
5369     } 
5370     else
5371      nIndent = 0;
5372   }
5373   else
5374     nIndent = 0;
5375  
5376   if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) && (lprc != NULL))
5377   {
5378     if (ListView_GetItemPosition(hwnd, nItem, &ptItem) != FALSE)
5379     {
5380       switch(lprc->left)  
5381       {  
5382       case LVIR_ICON: 
5383         if (uView == LVS_ICON)
5384         {
5385           if (infoPtr->himlNormal != NULL)
5386           {
5387             if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5388             {
5389               bResult = TRUE;
5390               lprc->left = ptItem.x + ptOrigin.x;
5391               lprc->top = ptItem.y + ptOrigin.y;
5392               lprc->right = lprc->left + infoPtr->iconSize.cx;
5393               lprc->bottom = (lprc->top + infoPtr->iconSize.cy + 
5394                               ICON_BOTTOM_PADDING + ICON_TOP_PADDING);
5395             }
5396           }
5397         }
5398         else if (uView == LVS_SMALLICON)
5399         {
5400           if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5401           {
5402             bResult = TRUE;
5403             lprc->left = ptItem.x + ptOrigin.x;
5404             lprc->top = ptItem.y + ptOrigin.y;
5405             lprc->bottom = lprc->top + infoPtr->nItemHeight;
5406
5407             if (infoPtr->himlState != NULL)
5408               lprc->left += infoPtr->iconSize.cx;
5409               
5410             if (infoPtr->himlSmall != NULL)
5411               lprc->right = lprc->left + infoPtr->iconSize.cx;
5412             else
5413               lprc->right = lprc->left;
5414           }
5415         }
5416         else 
5417         {
5418           bResult = TRUE;
5419           lprc->left = ptItem.x;
5420           if (uView & LVS_REPORT)
5421             lprc->left += nIndent;
5422           lprc->top = ptItem.y;
5423           lprc->bottom = lprc->top + infoPtr->nItemHeight;
5424
5425           if (infoPtr->himlState != NULL)
5426           {
5427             lprc->left += infoPtr->iconSize.cx;
5428           }
5429             
5430           if (infoPtr->himlSmall != NULL)
5431           {
5432             lprc->right = lprc->left + infoPtr->iconSize.cx;
5433           }
5434           else
5435           {
5436             lprc->right = lprc->left;
5437           }
5438         }
5439         break;
5440
5441       case LVIR_LABEL: 
5442         if (uView == LVS_ICON)
5443         {
5444           if (infoPtr->himlNormal != NULL)
5445           {
5446             if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5447             {
5448               bResult = TRUE;
5449               lprc->left = ptItem.x + ptOrigin.x;
5450               lprc->top = (ptItem.y + ptOrigin.y + infoPtr->iconSize.cy +
5451                            ICON_BOTTOM_PADDING + ICON_TOP_PADDING);
5452               nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5453               if (infoPtr->iconSpacing.cx - nLabelWidth > 1)
5454               {
5455                 lprc->left += (infoPtr->iconSpacing.cx - nLabelWidth) / 2;
5456                 lprc->right = lprc->left + nLabelWidth;
5457               }
5458               else
5459               {
5460                 lprc->left += 1;
5461                 lprc->right = lprc->left + infoPtr->iconSpacing.cx - 1;
5462               }
5463             
5464               hdc = GetDC(hwnd);
5465               hOldFont = SelectObject(hdc, infoPtr->hFont);
5466               GetTextMetricsA(hdc, &tm);
5467               lprc->bottom = lprc->top + tm.tmHeight + HEIGHT_PADDING;
5468               SelectObject(hdc, hOldFont);
5469               ReleaseDC(hwnd, hdc);
5470             }              
5471           }
5472         }
5473         else if (uView == LVS_SMALLICON)
5474         {
5475           if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5476           {
5477             bResult = TRUE;
5478             nLeftPos = lprc->left = ptItem.x + ptOrigin.x; 
5479             lprc->top = ptItem.y + ptOrigin.y;
5480             lprc->bottom = lprc->top + infoPtr->nItemHeight;
5481             
5482             if (infoPtr->himlState != NULL)
5483             {
5484               lprc->left += infoPtr->iconSize.cx;
5485             }
5486             
5487             if (infoPtr->himlSmall != NULL)
5488             {
5489               lprc->left += infoPtr->iconSize.cx;
5490             }
5491             
5492             nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5493             nLabelWidth += TRAILING_PADDING;
5494             if (lprc->left + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5495             {
5496               lprc->right = lprc->left + nLabelWidth;
5497             }
5498             else
5499             {
5500               lprc->right = nLeftPos + infoPtr->nItemWidth;
5501             }
5502           }
5503         }
5504         else
5505         {
5506           bResult = TRUE;
5507           if (uView & LVS_REPORT)
5508             nLeftPos = lprc->left = ptItem.x + nIndent;
5509           else 
5510             nLeftPos = lprc->left = ptItem.x; 
5511           lprc->top = ptItem.y;
5512           lprc->bottom = lprc->top + infoPtr->nItemHeight;
5513
5514           if (infoPtr->himlState != NULL)
5515           {
5516             lprc->left += infoPtr->iconSize.cx;
5517           }
5518
5519           if (infoPtr->himlSmall != NULL)
5520           {
5521             lprc->left += infoPtr->iconSize.cx;
5522           }
5523
5524           nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5525           nLabelWidth += TRAILING_PADDING;
5526           if (infoPtr->himlSmall)
5527             nLabelWidth += IMAGE_PADDING;
5528           if (lprc->left + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5529           {
5530             lprc->right = lprc->left + nLabelWidth;
5531           }
5532           else
5533           {
5534             lprc->right = nLeftPos + infoPtr->nItemWidth;
5535           }
5536         }
5537         break;
5538
5539       case LVIR_BOUNDS:
5540         if (uView == LVS_ICON)
5541         {
5542           if (infoPtr->himlNormal != NULL)
5543           {
5544             if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5545             {
5546               bResult = TRUE;
5547               lprc->left = ptItem.x + ptOrigin.x;
5548               lprc->top = ptItem.y + ptOrigin.y; 
5549               lprc->right = lprc->left + infoPtr->iconSpacing.cx;
5550               lprc->bottom = lprc->top + infoPtr->iconSpacing.cy;
5551             }
5552           } 
5553         }
5554         else if (uView == LVS_SMALLICON)
5555         {
5556           if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5557           {
5558             bResult = TRUE;
5559             lprc->left = ptItem.x + ptOrigin.x; 
5560             lprc->right = lprc->left;
5561             lprc->top = ptItem.y + ptOrigin.y;
5562             lprc->bottom = lprc->top + infoPtr->nItemHeight;
5563             if (infoPtr->himlState != NULL)
5564               lprc->right += infoPtr->iconSize.cx;
5565             if (infoPtr->himlSmall != NULL)
5566               lprc->right += infoPtr->iconSize.cx;
5567
5568             nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5569             nLabelWidth += TRAILING_PADDING;
5570             if (infoPtr->himlSmall)
5571               nLabelWidth += IMAGE_PADDING;
5572             if (lprc->right + nLabelWidth < lprc->left + infoPtr->nItemWidth)
5573             {
5574               lprc->right += nLabelWidth;
5575             }
5576             else
5577             {
5578               lprc->right = lprc->left + infoPtr->nItemWidth;
5579             }
5580           }
5581         }
5582         else
5583         {
5584           bResult = TRUE;
5585           lprc->left = ptItem.x; 
5586           if (!(infoPtr->dwExStyle&LVS_EX_FULLROWSELECT) && uView&LVS_REPORT)
5587             lprc->left += nIndent;
5588           lprc->right = lprc->left;
5589           lprc->top = ptItem.y;
5590           lprc->bottom = lprc->top + infoPtr->nItemHeight;
5591
5592           if (infoPtr->dwExStyle & LVS_EX_FULLROWSELECT)
5593           {
5594             RECT br;
5595             int nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
5596             Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
5597
5598             lprc->right = max(lprc->left, br.right - REPORT_MARGINX);
5599           }
5600           else
5601           {
5602              if (infoPtr->himlState != NULL)
5603             {
5604               lprc->right += infoPtr->iconSize.cx;
5605             }
5606
5607             if (infoPtr->himlSmall != NULL)
5608             {
5609               lprc->right += infoPtr->iconSize.cx;
5610             }
5611
5612             nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5613             nLabelWidth += TRAILING_PADDING;
5614             if (lprc->right + nLabelWidth < lprc->left + infoPtr->nItemWidth)
5615             {
5616               lprc->right += nLabelWidth;
5617             }
5618             else
5619             {
5620               lprc->right = lprc->left + infoPtr->nItemWidth;
5621             }
5622           }
5623         }
5624         break;
5625         
5626       case LVIR_SELECTBOUNDS:
5627         if (uView == LVS_ICON)
5628         {
5629           if (infoPtr->himlNormal != NULL)
5630           {
5631             if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5632             {
5633               bResult = TRUE;
5634               lprc->left = ptItem.x + ptOrigin.x;
5635               lprc->top = ptItem.y + ptOrigin.y;
5636               lprc->right = lprc->left + infoPtr->iconSpacing.cx;
5637               lprc->bottom = lprc->top + infoPtr->iconSpacing.cy;
5638             }
5639           } 
5640         }
5641         else if (uView == LVS_SMALLICON)
5642         {
5643           if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5644           {
5645             bResult = TRUE;
5646             nLeftPos= lprc->left = ptItem.x + ptOrigin.x;  
5647             lprc->top = ptItem.y + ptOrigin.y;
5648             lprc->bottom = lprc->top + infoPtr->nItemHeight;
5649             
5650             if (infoPtr->himlState != NULL)
5651             {
5652               lprc->left += infoPtr->iconSize.cx;
5653             }
5654             
5655             lprc->right = lprc->left;
5656             
5657             if (infoPtr->himlSmall != NULL)
5658             {
5659               lprc->right += infoPtr->iconSize.cx;
5660             }
5661             
5662             nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5663             nLabelWidth += TRAILING_PADDING;
5664             if (lprc->right + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5665             {
5666               lprc->right += nLabelWidth;
5667             }
5668             else
5669             {
5670               lprc->right = nLeftPos + infoPtr->nItemWidth;
5671             }
5672           }
5673         }
5674         else
5675         {
5676           bResult = TRUE;
5677           if (!(infoPtr->dwExStyle&LVS_EX_FULLROWSELECT) && (uView&LVS_REPORT))
5678             nLeftPos = lprc->left = ptItem.x + nIndent; 
5679           else
5680             nLeftPos = lprc->left = ptItem.x; 
5681           lprc->top = ptItem.y;
5682           lprc->bottom = lprc->top + infoPtr->nItemHeight;
5683
5684           if (infoPtr->himlState != NULL)
5685           {
5686             lprc->left += infoPtr->iconSize.cx;
5687           }
5688           
5689           lprc->right = lprc->left;
5690         
5691           if (infoPtr->dwExStyle & LVS_EX_FULLROWSELECT)
5692           {
5693             RECT br;
5694             int nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
5695             Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
5696
5697             lprc->right = max(lprc->left, br.right - REPORT_MARGINX);
5698           }
5699           else
5700           {
5701             if (infoPtr->himlSmall != NULL)
5702             {
5703               lprc->right += infoPtr->iconSize.cx;
5704             }
5705
5706             nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5707             nLabelWidth += TRAILING_PADDING;
5708             if (infoPtr->himlSmall)
5709               nLabelWidth += IMAGE_PADDING;
5710             if (lprc->right + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5711             {
5712               lprc->right += nLabelWidth;
5713             }
5714             else
5715             {
5716               lprc->right = nLeftPos + infoPtr->nItemWidth;
5717             }
5718           }
5719         }
5720         break;
5721       }
5722     }
5723   }
5724   return bResult;
5725 }
5726
5727 /***
5728  * DESCRIPTION:
5729  * Retrieves the width of a label.
5730  * 
5731  * PARAMETER(S):
5732  * [I] HWND : window handle
5733  * 
5734  * RETURN:
5735  *   SUCCESS : string width (in pixels)
5736  *   FAILURE : zero
5737  */
5738 static INT LISTVIEW_GetLabelWidth(HWND hwnd, INT nItem)
5739 {
5740   CHAR szDispText[DISP_TEXT_SIZE];
5741   INT nLabelWidth = 0;
5742   LVITEMA lvItem;
5743
5744   TRACE("(hwnd=%x, nItem=%d)\n", hwnd, nItem);
5745
5746   ZeroMemory(&lvItem, sizeof(LVITEMA));
5747   lvItem.mask = LVIF_TEXT;
5748   lvItem.iItem = nItem;
5749   lvItem.cchTextMax = DISP_TEXT_SIZE;
5750   lvItem.pszText = szDispText;
5751   if (LISTVIEW_GetItemA(hwnd, &lvItem, TRUE) != FALSE)
5752   {
5753     nLabelWidth = ListView_GetStringWidthA(hwnd, lvItem.pszText); 
5754   }
5755     
5756   return nLabelWidth;
5757 }
5758
5759 /***
5760  * DESCRIPTION:
5761  * Retrieves the spacing between listview control items.
5762  * 
5763  * PARAMETER(S):
5764  * [I] HWND : window handle
5765  * [I] BOOL : flag for small or large icon 
5766  *
5767  * RETURN:
5768  * Horizontal + vertical spacing
5769  */
5770 static LRESULT LISTVIEW_GetItemSpacing(HWND hwnd, BOOL bSmall)
5771 {
5772   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5773   LONG lResult;
5774
5775   if (bSmall == FALSE)
5776   {
5777     lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
5778   }
5779   else
5780   {
5781     /* TODO: need to store width of smallicon item */
5782     lResult = MAKELONG(0, infoPtr->nItemHeight);
5783   }  
5784   
5785   return lResult;
5786 }
5787
5788 /***
5789  * DESCRIPTION:
5790  * Retrieves the state of a listview control item.
5791  * 
5792  * PARAMETER(S):
5793  * [I] HWND : window handle
5794  * [I] INT : item index
5795  * [I] UINT : state mask
5796  * 
5797  * RETURN:
5798  * State specified by the mask.
5799  */
5800 static LRESULT LISTVIEW_GetItemState(HWND hwnd, INT nItem, UINT uMask)
5801 {
5802   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5803   LVITEMA lvItem;
5804   UINT uState = 0;
5805
5806   if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
5807   {
5808     ZeroMemory(&lvItem, sizeof(LVITEMA));
5809     lvItem.iItem = nItem;
5810     lvItem.stateMask = uMask;
5811     lvItem.mask = LVIF_STATE;
5812     if (LISTVIEW_GetItemA(hwnd, &lvItem, TRUE) != FALSE)
5813     {
5814       uState = lvItem.state;
5815     }
5816   }
5817
5818   return uState;
5819 }
5820
5821 /***
5822  * DESCRIPTION:
5823  * Retrieves the text of a listview control item or subitem. 
5824  * 
5825  * PARAMETER(S):
5826  * [I] HWND : window handle
5827  * [I] INT : item index
5828  * [IO] LPLVITEMA : item information
5829  * 
5830  * RETURN:
5831  *   SUCCESS : string length
5832  *   FAILURE : 0
5833  */
5834 static LRESULT LISTVIEW_GetItemTextA(HWND hwnd, INT nItem, LPLVITEMA lpLVItem)
5835 {
5836   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5837   INT nLength = 0;
5838   
5839   if (lpLVItem != NULL)
5840   {
5841     if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
5842     {
5843       lpLVItem->mask = LVIF_TEXT;
5844       lpLVItem->iItem = nItem;
5845       if (LISTVIEW_GetItemA(hwnd, lpLVItem, FALSE) != FALSE)
5846       {
5847         nLength = lstrlenA(lpLVItem->pszText);
5848       }
5849     }
5850   }
5851
5852   return nLength;
5853 }
5854
5855 /***
5856  * DESCRIPTION:
5857  * Searches for an item based on properties + relationships.
5858  * 
5859  * PARAMETER(S):
5860  * [I] HWND : window handle
5861  * [I] INT : item index
5862  * [I] INT : relationship flag
5863  * 
5864  * RETURN:
5865  *   SUCCESS : item index
5866  *   FAILURE : -1
5867  */
5868 static LRESULT LISTVIEW_GetNextItem(HWND hwnd, INT nItem, UINT uFlags)
5869 {
5870   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5871   UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5872   UINT uMask = 0;
5873   LVFINDINFO lvFindInfo;
5874   INT nCountPerColumn;
5875   INT i;
5876   
5877   if ((nItem >= -1) && (nItem < GETITEMCOUNT(infoPtr)))
5878   { 
5879     ZeroMemory(&lvFindInfo, sizeof(LVFINDINFO));
5880
5881     if (uFlags & LVNI_CUT)
5882       uMask |= LVIS_CUT;
5883     
5884     if (uFlags & LVNI_DROPHILITED)
5885       uMask |= LVIS_DROPHILITED;
5886           
5887     if (uFlags & LVNI_FOCUSED)
5888       uMask |= LVIS_FOCUSED;
5889
5890     if (uFlags & LVNI_SELECTED)
5891       uMask |= LVIS_SELECTED;
5892
5893     if (uFlags & LVNI_ABOVE)
5894     {
5895       if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5896       {
5897         while (nItem >= 0)
5898         {
5899           nItem--;
5900           if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
5901             return nItem;
5902         }
5903       }
5904       else
5905       {
5906         lvFindInfo.flags = LVFI_NEARESTXY;
5907         lvFindInfo.vkDirection = VK_UP;
5908         ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
5909         while ((nItem = ListView_FindItem(hwnd, nItem, &lvFindInfo)) != -1)
5910         {
5911           if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
5912             return nItem;
5913         }
5914       }
5915     }
5916     else if (uFlags & LVNI_BELOW)
5917     {
5918       if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5919       {
5920         while (nItem < GETITEMCOUNT(infoPtr))
5921         {
5922           nItem++;
5923           if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
5924             return nItem;
5925         }
5926       }
5927       else
5928       {
5929         lvFindInfo.flags = LVFI_NEARESTXY;
5930         lvFindInfo.vkDirection = VK_DOWN;
5931         ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
5932         while ((nItem = ListView_FindItem(hwnd, nItem, &lvFindInfo)) != -1)
5933         {
5934           if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
5935             return nItem;
5936         }
5937       }
5938     }
5939     else if (uFlags & LVNI_TOLEFT)
5940     {
5941       if (uView == LVS_LIST)
5942       {
5943         nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
5944         while (nItem - nCountPerColumn >= 0)
5945         {
5946           nItem -= nCountPerColumn;
5947           if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
5948             return nItem;
5949         }
5950       }
5951       else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5952       {
5953         lvFindInfo.flags = LVFI_NEARESTXY;
5954         lvFindInfo.vkDirection = VK_LEFT;
5955         ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
5956         while ((nItem = ListView_FindItem(hwnd, nItem, &lvFindInfo)) != -1)
5957         {
5958           if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
5959             return nItem;
5960         }
5961       }
5962     }
5963     else if (uFlags & LVNI_TORIGHT)
5964     {
5965       if (uView == LVS_LIST)
5966       {
5967         nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
5968         while (nItem + nCountPerColumn < GETITEMCOUNT(infoPtr))
5969         {
5970           nItem += nCountPerColumn;
5971           if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
5972             return nItem;
5973         }
5974       }
5975       else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5976       {
5977         lvFindInfo.flags = LVFI_NEARESTXY;
5978         lvFindInfo.vkDirection = VK_RIGHT;
5979         ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
5980         while ((nItem = ListView_FindItem(hwnd, nItem, &lvFindInfo)) != -1)
5981         {
5982           if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
5983             return nItem;
5984         }
5985       }
5986     }
5987     else
5988     {
5989       nItem++;
5990
5991       /* search by index */
5992       for (i = nItem; i < GETITEMCOUNT(infoPtr); i++)
5993       {
5994         if ((ListView_GetItemState(hwnd, i, uMask) & uMask) == uMask)
5995           return i;
5996       }
5997     }
5998   }
5999
6000   return -1;
6001 }
6002
6003 /* LISTVIEW_GetNumberOfWorkAreas */
6004
6005 /***
6006  * DESCRIPTION:
6007  * Retrieves the origin coordinates when in icon or small icon display mode.
6008  * 
6009  * PARAMETER(S):
6010  * [I] HWND : window handle
6011  * [O] LPPOINT : coordinate information
6012  * 
6013  * RETURN:
6014  *   SUCCESS : TRUE
6015  *   FAILURE : FALSE
6016  */
6017 static LRESULT LISTVIEW_GetOrigin(HWND hwnd, LPPOINT lpptOrigin)
6018 {
6019   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
6020   UINT uView = lStyle & LVS_TYPEMASK;
6021   BOOL bResult = FALSE;
6022   
6023   TRACE("(hwnd=%x, lpptOrigin=%p)\n", hwnd, lpptOrigin);
6024
6025   if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6026   {
6027     SCROLLINFO scrollInfo;
6028     ZeroMemory(lpptOrigin, sizeof(POINT));
6029     ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
6030     scrollInfo.cbSize = sizeof(SCROLLINFO);
6031
6032     if (lStyle & WS_HSCROLL)
6033     {
6034       scrollInfo.fMask = SIF_POS;
6035       if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE) 
6036       {
6037         lpptOrigin->x = -scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE; 
6038       }
6039     }
6040
6041     if (lStyle & WS_VSCROLL)
6042     {
6043       scrollInfo.fMask = SIF_POS;
6044       if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
6045       {
6046         lpptOrigin->y = -scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE;
6047       }
6048     }
6049       
6050     bResult = TRUE;
6051   }
6052   
6053   return bResult;
6054 }
6055
6056 /***
6057  * DESCRIPTION:
6058  * Retrieves the number of items that are marked as selected.
6059  * 
6060  * PARAMETER(S):
6061  * [I] HWND : window handle
6062  * 
6063  * RETURN:
6064  * Number of items selected.
6065  */
6066 static LRESULT LISTVIEW_GetSelectedCount(HWND hwnd)
6067 {
6068 /* REDO THIS */
6069   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6070   INT nSelectedCount = 0;
6071   INT i;
6072
6073   for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
6074   {
6075     if (ListView_GetItemState(hwnd, i, LVIS_SELECTED) & LVIS_SELECTED)
6076     {
6077       nSelectedCount++;
6078     }
6079   }
6080   
6081   return nSelectedCount;
6082 }
6083
6084 /***
6085  * DESCRIPTION:
6086  * Retrieves item index that marks the start of a multiple selection.
6087  * 
6088  * PARAMETER(S):
6089  * [I] HWND : window handle
6090  * 
6091  * RETURN:
6092  * Index number or -1 if there is no selection mark.
6093  */
6094 static LRESULT LISTVIEW_GetSelectionMark(HWND hwnd)
6095 {
6096   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6097
6098   return infoPtr->nSelectionMark;
6099 }
6100
6101 /***
6102  * DESCRIPTION:
6103  * Retrieves the width of a string.
6104  * 
6105  * PARAMETER(S):
6106  * [I] HWND : window handle
6107  * 
6108  * RETURN:
6109  *   SUCCESS : string width (in pixels)
6110  *   FAILURE : zero
6111  */
6112 static LRESULT LISTVIEW_GetStringWidthA(HWND hwnd, LPCSTR lpszText)
6113 {
6114   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6115   HFONT hFont, hOldFont;
6116   SIZE stringSize;
6117   HDC hdc;
6118
6119   ZeroMemory(&stringSize, sizeof(SIZE));
6120   if (lpszText != NULL && lpszText != (LPCSTR)-1)
6121   {
6122     hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
6123     hdc = GetDC(hwnd);
6124     hOldFont = SelectObject(hdc, hFont);
6125     GetTextExtentPointA(hdc, lpszText, lstrlenA(lpszText), &stringSize);
6126     SelectObject(hdc, hOldFont);
6127     ReleaseDC(hwnd, hdc);
6128   }
6129
6130   return stringSize.cx;
6131 }
6132
6133 /***
6134  * DESCRIPTION:
6135  * Retrieves the text backgound color.
6136  * 
6137  * PARAMETER(S):
6138  * [I] HWND : window handle
6139  * 
6140  * RETURN:
6141  * COLORREF associated with the the background.
6142  */
6143 static LRESULT LISTVIEW_GetTextBkColor(HWND hwnd)
6144 {
6145   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongA(hwnd, 0);
6146
6147   return infoPtr->clrTextBk;
6148 }
6149
6150 /***
6151  * DESCRIPTION:
6152  * Retrieves the text color.
6153  * 
6154  * PARAMETER(S):
6155  * [I] HWND : window handle
6156  * 
6157  * RETURN:
6158  * COLORREF associated with the text.
6159  */
6160 static LRESULT LISTVIEW_GetTextColor(HWND hwnd)
6161 {
6162   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongA(hwnd, 0);
6163
6164   return infoPtr->clrText;
6165 }
6166
6167 /***
6168  * DESCRIPTION:
6169  * Determines which section of the item was selected (if any).
6170  * 
6171  * PARAMETER(S):
6172  * [I] HWND : window handle
6173  * [IO] LPLVHITTESTINFO : hit test information
6174  * [I] subitem : fill out iSubItem.
6175  *
6176  * RETURN:
6177  *   SUCCESS : item index
6178  *   FAILURE : -1
6179  */
6180 static INT LISTVIEW_HitTestItem(
6181   HWND hwnd, LPLVHITTESTINFO lpHitTestInfo, BOOL subitem
6182 ) {
6183   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6184   RECT rcItem;
6185   INT i,topindex,bottomindex;
6186   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
6187   UINT uView = lStyle & LVS_TYPEMASK;
6188
6189
6190   TRACE("(hwnd=%x, x=%ld, y=%ld)\n", hwnd, lpHitTestInfo->pt.x,
6191         lpHitTestInfo->pt.y);
6192
6193   topindex = ListView_GetTopIndex(hwnd);
6194   if (uView == LVS_REPORT)
6195   {
6196     bottomindex = topindex + LISTVIEW_GetCountPerColumn(hwnd) + 1;  
6197     bottomindex = min(bottomindex,GETITEMCOUNT(infoPtr));
6198   }
6199   else
6200   {
6201     bottomindex = GETITEMCOUNT(infoPtr);
6202   }
6203
6204   for (i = topindex; i < bottomindex; i++)
6205   {
6206     rcItem.left = LVIR_BOUNDS;
6207     if (LISTVIEW_GetItemRect(hwnd, i, &rcItem) != FALSE)
6208     {
6209       if (PtInRect(&rcItem, lpHitTestInfo->pt) != FALSE)
6210       {
6211         rcItem.left = LVIR_ICON;
6212         if (LISTVIEW_GetItemRect(hwnd, i, &rcItem) != FALSE)
6213         {
6214           if (PtInRect(&rcItem, lpHitTestInfo->pt) != FALSE)
6215           {
6216             lpHitTestInfo->flags = LVHT_ONITEMICON;
6217             lpHitTestInfo->iItem = i;
6218             if (subitem) lpHitTestInfo->iSubItem = 0;
6219             return i;
6220           }
6221         }
6222       
6223         rcItem.left = LVIR_LABEL;
6224         if (LISTVIEW_GetItemRect(hwnd, i, &rcItem) != FALSE)
6225         {
6226           if (PtInRect(&rcItem, lpHitTestInfo->pt) != FALSE)
6227           {
6228             lpHitTestInfo->flags = LVHT_ONITEMLABEL;
6229             lpHitTestInfo->iItem = i;
6230             if (subitem) lpHitTestInfo->iSubItem = 0;
6231             return i;
6232           }
6233         }
6234         
6235         lpHitTestInfo->flags = LVHT_ONITEMSTATEICON;
6236         lpHitTestInfo->iItem = i;
6237         if (subitem) lpHitTestInfo->iSubItem = 0;
6238         return i;
6239       }
6240     }
6241   }
6242      
6243   lpHitTestInfo->flags = LVHT_NOWHERE;
6244
6245   return -1;
6246 }
6247
6248 /***
6249  * DESCRIPTION:
6250  * Determines which listview item is located at the specified position.
6251  * 
6252  * PARAMETER(S):
6253  * [I] HWND : window handle
6254  * [IO} LPLVHITTESTINFO : hit test information
6255  *
6256  * RETURN:
6257  *   SUCCESS : item index
6258  *   FAILURE : -1
6259  */
6260 static LRESULT LISTVIEW_HitTest(HWND hwnd, LPLVHITTESTINFO lpHitTestInfo)
6261 {
6262   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6263   INT nItem = -1;
6264
6265   lpHitTestInfo->flags = 0;
6266
6267   if (infoPtr->rcList.left > lpHitTestInfo->pt.x)
6268   {
6269     lpHitTestInfo->flags = LVHT_TOLEFT;
6270   }
6271   else if (infoPtr->rcList.right < lpHitTestInfo->pt.x) 
6272   {
6273     lpHitTestInfo->flags = LVHT_TORIGHT;
6274   }
6275   if (infoPtr->rcList.top > lpHitTestInfo->pt.y)
6276   {
6277     lpHitTestInfo->flags |= LVHT_ABOVE;
6278   }
6279   else if (infoPtr->rcList.bottom < lpHitTestInfo->pt.y) 
6280   {
6281     lpHitTestInfo->flags |= LVHT_BELOW;
6282   }
6283
6284   if (lpHitTestInfo->flags == 0)
6285   {
6286     /* NOTE (mm 20001022): We must not allow iSubItem to be touched, for 
6287      * an app might pass only a structure with space up to iItem!
6288      * (MS Office 97 does that for instance in the file open dialog)
6289      */
6290     nItem = LISTVIEW_HitTestItem(hwnd, lpHitTestInfo, FALSE);
6291   }
6292   
6293   return nItem;
6294 }
6295
6296 /***
6297  * DESCRIPTION:
6298  * Inserts a new column.
6299  * 
6300  * PARAMETER(S):
6301  * [I] HWND : window handle
6302  * [I] INT : column index
6303  * [I] LPLVCOLUMNA : column information
6304  *
6305  * RETURN:
6306  *   SUCCESS : new column index
6307  *   FAILURE : -1
6308  */
6309 static LRESULT LISTVIEW_InsertColumnA(HWND hwnd, INT nColumn, 
6310                                       LPLVCOLUMNA lpColumn)
6311 {
6312   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6313   HDITEMA hdi;
6314   INT nNewColumn = -1;
6315
6316   TRACE("(hwnd=%x, nColumn=%d, lpColumn=%p)\n",hwnd, nColumn, 
6317         lpColumn);
6318
6319   if (lpColumn != NULL) 
6320   {
6321     /* initialize memory */
6322     ZeroMemory(&hdi, sizeof(HDITEMA));
6323
6324     if (lpColumn->mask & LVCF_FMT) 
6325     {
6326       /* format member is valid */
6327       hdi.mask |= HDI_FORMAT;
6328
6329       /* set text alignment (leftmost column must be left-aligned) */
6330       if (nColumn == 0)
6331       {
6332         hdi.fmt |= HDF_LEFT;
6333       }
6334       else
6335       {
6336         if (lpColumn->fmt & LVCFMT_LEFT)
6337         {
6338           hdi.fmt |= HDF_LEFT;
6339         }
6340         else if (lpColumn->fmt & LVCFMT_RIGHT)
6341         {
6342           hdi.fmt |= HDF_RIGHT;
6343         }
6344         else if (lpColumn->fmt & LVCFMT_CENTER)
6345         {
6346           hdi.fmt |= HDF_CENTER;
6347         }
6348       }
6349  
6350       if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6351       {
6352         hdi.fmt |= HDF_BITMAP_ON_RIGHT;
6353         /* ??? */
6354       }
6355
6356       if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6357       {
6358         /* ??? */
6359       }
6360       
6361       if (lpColumn->fmt & LVCFMT_IMAGE)
6362       {
6363         hdi.fmt |= HDF_IMAGE;
6364         hdi.iImage = I_IMAGECALLBACK;
6365       }
6366     }
6367
6368     if (lpColumn->mask & LVCF_WIDTH) 
6369     {
6370       hdi.mask |= HDI_WIDTH;
6371       hdi.cxy = lpColumn->cx;
6372     }
6373   
6374     if (lpColumn->mask & LVCF_TEXT) 
6375     {
6376       hdi.mask |= HDI_TEXT | HDI_FORMAT;
6377       hdi.pszText = lpColumn->pszText;
6378       hdi.cchTextMax = lstrlenA(lpColumn->pszText);
6379       hdi.fmt |= HDF_STRING;
6380     }
6381   
6382     if (lpColumn->mask & LVCF_IMAGE) 
6383     {
6384       hdi.mask |= HDI_IMAGE;
6385       hdi.iImage = lpColumn->iImage;
6386     }
6387
6388     if (lpColumn->mask & LVCF_ORDER) 
6389     {
6390       hdi.mask |= HDI_ORDER;
6391       hdi.iOrder = lpColumn->iOrder;
6392     }
6393
6394     /* insert item in header control */
6395     nNewColumn = SendMessageA(infoPtr->hwndHeader, HDM_INSERTITEMA,
6396                              (WPARAM)nColumn, (LPARAM)&hdi);
6397     
6398     /* Need to reset the item width when inserting a new column */
6399     infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
6400
6401     LISTVIEW_UpdateScroll(hwnd);
6402     InvalidateRect(hwnd, NULL, FALSE);
6403   }
6404
6405   return nNewColumn;
6406 }
6407
6408 static LRESULT LISTVIEW_InsertColumnW(HWND hwnd, INT nColumn, 
6409                                       LPLVCOLUMNW lpColumn)
6410 {
6411   LVCOLUMNA     lvca;
6412   LRESULT               lres;
6413       
6414   memcpy(&lvca,lpColumn,sizeof(lvca));
6415   if (lpColumn->mask & LVCF_TEXT)
6416     lvca.pszText = HEAP_strdupWtoA(GetProcessHeap(),0,lpColumn->pszText);
6417   lres = LISTVIEW_InsertColumnA(hwnd,nColumn,&lvca);
6418   if (lpColumn->mask & LVCF_TEXT)
6419     HeapFree(GetProcessHeap(),0,lvca.pszText);
6420   return lres;
6421 }
6422
6423 /* LISTVIEW_InsertCompare:  callback routine for comparing pszText members of the LV_ITEMS
6424    in a LISTVIEW on insert.  Passed to DPA_Sort in LISTVIEW_InsertItem.
6425    This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6426    and not during the processing of a LVM_SORTITEMS message. Applications should provide
6427    their own sort proc. when sending LVM_SORTITEMS.
6428 */
6429 /* Platform SDK:
6430     (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6431         if:
6432           LVS_SORTXXX must be specified, 
6433           LVS_OWNERDRAW is not set, 
6434           <item>.pszText is not LPSTR_TEXTCALLBACK.
6435
6436     (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices 
6437     are sorted based on item text..." 
6438 */
6439 static INT WINAPI LISTVIEW_InsertCompare(  LPVOID first, LPVOID second,  LPARAM lParam)
6440 {
6441   HDPA  hdpa_first = (HDPA) first;
6442   HDPA  hdpa_second = (HDPA) second;
6443   LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( hdpa_first, 0 );
6444   LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( hdpa_second, 0 );
6445   LONG lStyle = GetWindowLongA((HWND) lParam, GWL_STYLE);
6446   INT  cmpv = lstrcmpA( lv_first->pszText, lv_second->pszText );
6447   /* if we're sorting descending, negate the return value */
6448   return (lStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
6449 }
6450
6451 /***
6452  * nESCRIPTION:
6453  * Inserts a new item in the listview control.
6454  * 
6455  * PARAMETER(S):
6456  * [I] HWND : window handle
6457  * [I] LPLVITEMA : item information
6458  *
6459  * RETURN:
6460  *   SUCCESS : new item index
6461  *   FAILURE : -1
6462  */
6463 static LRESULT LISTVIEW_InsertItemA(HWND hwnd, LPLVITEMA lpLVItem)
6464 {
6465   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6466   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
6467   UINT uView = lStyle & LVS_TYPEMASK;
6468   LONG lCtrlId = GetWindowLongA(hwnd, GWL_ID);
6469   NMLISTVIEW nmlv;
6470   INT nItem = -1;
6471   HDPA hdpaSubItems;
6472   INT nItemWidth = 0;
6473   LISTVIEW_ITEM *lpItem = NULL;
6474
6475   TRACE("(hwnd=%x,lpLVItem=%p)\n", hwnd, lpLVItem);
6476
6477   if (lStyle & LVS_OWNERDATA)
6478   {
6479     nItem = infoPtr->hdpaItems->nItemCount;
6480     infoPtr->hdpaItems->nItemCount ++;
6481     return nItem;
6482   }
6483
6484   if (lpLVItem != NULL)
6485   {
6486     /* make sure it's not a subitem; cannot insert a subitem */
6487     if (lpLVItem->iSubItem == 0)
6488     {
6489       lpItem = (LISTVIEW_ITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_ITEM));
6490       if (lpItem != NULL)
6491       {
6492         ZeroMemory(lpItem, sizeof(LISTVIEW_ITEM));
6493         if (LISTVIEW_InitItem(hwnd, lpItem, lpLVItem) != FALSE)
6494         {
6495           /* insert item in listview control data structure */
6496           hdpaSubItems = DPA_Create(8);
6497           if (hdpaSubItems != NULL)
6498           {
6499             nItem = DPA_InsertPtr(hdpaSubItems, 0, lpItem);
6500             if (nItem != -1)
6501             {
6502               if ( ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
6503                       && !(lStyle & LVS_OWNERDRAWFIXED)
6504                       && (LPSTR_TEXTCALLBACKA != lpLVItem->pszText) )
6505               {
6506                 /* Insert the item in the proper sort order based on the pszText
6507                   member. See comments for LISTVIEW_InsertCompare() for greater detail */
6508                   nItem = DPA_InsertPtr( infoPtr->hdpaItems, 
6509                           GETITEMCOUNT( infoPtr ) + 1, hdpaSubItems );
6510                   DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, hwnd );
6511                   nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
6512               }
6513               else
6514               {
6515                 nItem = DPA_InsertPtr(infoPtr->hdpaItems, lpLVItem->iItem, 
6516                                     hdpaSubItems);
6517               }
6518               if (nItem != -1)
6519               {
6520                 LISTVIEW_ShiftSelections(hwnd,nItem,1);
6521
6522                 /* manage item focus */
6523                 if (lpLVItem->mask & LVIF_STATE)
6524                 {
6525                   lpItem->state &= ~(LVIS_FOCUSED|LVIS_SELECTED);
6526                   if (lpLVItem->stateMask & LVIS_SELECTED)
6527                   {
6528                     LISTVIEW_SetSelection(hwnd, nItem);
6529                   }
6530                   else if (lpLVItem->stateMask & LVIS_FOCUSED)
6531                   {
6532                     LISTVIEW_SetItemFocus(hwnd, nItem);
6533                   }           
6534                 }
6535                 
6536                 /* send LVN_INSERTITEM notification */
6537                 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6538                 nmlv.hdr.hwndFrom = hwnd;
6539                 nmlv.hdr.idFrom = lCtrlId;
6540                 nmlv.hdr.code = LVN_INSERTITEM;
6541                 nmlv.iItem = nItem;
6542                 nmlv.lParam = lpItem->lParam;;
6543                 ListView_LVNotify(GetParent(hwnd), lCtrlId, &nmlv);
6544                 
6545                 if ((uView == LVS_SMALLICON) || (uView == LVS_LIST))
6546                 {
6547                   nItemWidth = LISTVIEW_CalculateWidth(hwnd, lpLVItem->iItem); 
6548                   if (nItemWidth > infoPtr->nItemWidth)
6549                   {
6550                     infoPtr->nItemWidth = nItemWidth;
6551                   }
6552                 }
6553
6554                 /* align items (set position of each item) */
6555                 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6556                 {
6557                   if (lStyle & LVS_ALIGNLEFT)
6558                   {
6559                     LISTVIEW_AlignLeft(hwnd);
6560                   }
6561                   else
6562                   {
6563                     LISTVIEW_AlignTop(hwnd);
6564                   }
6565                 }
6566                 
6567                 LISTVIEW_UpdateScroll(hwnd);
6568                 /* refresh client area */
6569                 InvalidateRect(hwnd, NULL, FALSE);
6570               }
6571             }
6572           }
6573         }
6574       }
6575     }
6576   }
6577
6578   /* free memory if unsuccessful */
6579   if ((nItem == -1) && (lpItem != NULL))
6580   {
6581     COMCTL32_Free(lpItem);
6582   }
6583   
6584   return nItem;
6585 }
6586
6587 static LRESULT LISTVIEW_InsertItemW(HWND hwnd, LPLVITEMW lpLVItem) {
6588   LVITEMA lvia;
6589   LRESULT lres;
6590
6591   memcpy(&lvia,lpLVItem,sizeof(LVITEMA));
6592   if (lvia.mask & LVIF_TEXT) {
6593     if (lpLVItem->pszText == LPSTR_TEXTCALLBACKW)
6594       lvia.pszText = LPSTR_TEXTCALLBACKA;
6595     else
6596       lvia.pszText = HEAP_strdupWtoA(GetProcessHeap(),0,lpLVItem->pszText);
6597   }
6598   lres = LISTVIEW_InsertItemA(hwnd, &lvia);
6599   if (lvia.mask & LVIF_TEXT) {
6600     if (lpLVItem->pszText != LPSTR_TEXTCALLBACKW)
6601       HeapFree(GetProcessHeap(),0,lvia.pszText);
6602   }
6603   return lres;
6604 }
6605
6606 /* LISTVIEW_InsertItemW */
6607
6608 /***
6609  * DESCRIPTION:
6610  * Redraws a range of items.
6611  * 
6612  * PARAMETER(S):
6613  * [I] HWND : window handle
6614  * [I] INT : first item
6615  * [I] INT : last item
6616  *
6617  * RETURN:
6618  *   SUCCESS : TRUE
6619  *   FAILURE : FALSE
6620  */
6621 static LRESULT LISTVIEW_RedrawItems(HWND hwnd, INT nFirst, INT nLast)
6622 {
6623   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0); 
6624   BOOL bResult = FALSE;
6625   RECT rcItem;
6626
6627   if (nFirst <= nLast)
6628   {
6629     if ((nFirst >= 0) && (nFirst < GETITEMCOUNT(infoPtr)))
6630     {
6631       if ((nLast >= 0) && (nLast < GETITEMCOUNT(infoPtr)))
6632       {
6633           INT i;
6634           for (i = nFirst; i <= nLast; i++)
6635           {
6636                   rcItem.left = LVIR_BOUNDS;
6637                   LISTVIEW_GetItemRect(hwnd, i, &rcItem);
6638                   InvalidateRect(hwnd, &rcItem, TRUE);
6639           }
6640       }
6641     }
6642   }
6643
6644   return bResult;
6645 }
6646
6647 /* LISTVIEW_Scroll */
6648
6649 /***
6650  * DESCRIPTION:
6651  * Sets the background color.
6652  * 
6653  * PARAMETER(S):
6654  * [I] HWND : window handle
6655  * [I] COLORREF : background color
6656  *
6657  * RETURN:
6658  *   SUCCESS : TRUE
6659  *   FAILURE : FALSE
6660  */
6661 static LRESULT LISTVIEW_SetBkColor(HWND hwnd, COLORREF clrBk)
6662 {
6663   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6664
6665   infoPtr->clrBk = clrBk;
6666   InvalidateRect(hwnd, NULL, TRUE);
6667   
6668   return TRUE;
6669 }
6670
6671 /* LISTVIEW_SetBkImage */
6672
6673 /***
6674  * DESCRIPTION:
6675  * Sets the callback mask. This mask will be used when the parent
6676  * window stores state information (some or all).
6677  * 
6678  * PARAMETER(S):
6679  * [I] HWND : window handle
6680  * [I] UINT : state mask
6681  *
6682  * RETURN:
6683  *   SUCCESS : TRUE
6684  *   FAILURE : FALSE
6685  */
6686 static BOOL LISTVIEW_SetCallbackMask(HWND hwnd, UINT uMask)
6687 {
6688   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6689
6690   infoPtr->uCallbackMask = uMask;
6691
6692   return TRUE;
6693 }
6694
6695 /***
6696  * DESCRIPTION:
6697  * Sets the attributes of a header item.
6698  * 
6699  * PARAMETER(S):
6700  * [I] HWND : window handle
6701  * [I] INT : column index
6702  * [I] LPLVCOLUMNA : column attributes
6703  *
6704  * RETURN:
6705  *   SUCCESS : TRUE
6706  *   FAILURE : FALSE
6707  */
6708 static LRESULT LISTVIEW_SetColumnA(HWND hwnd, INT nColumn, 
6709                                    LPLVCOLUMNA lpColumn)
6710 {
6711   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6712   BOOL bResult = FALSE;
6713   HDITEMA hdi, hdiget;
6714
6715   if ((lpColumn != NULL) && (nColumn >= 0) && 
6716       (nColumn < Header_GetItemCount(infoPtr->hwndHeader)))
6717   {
6718     /* initialize memory */
6719     ZeroMemory(&hdi, sizeof(HDITEMA));
6720
6721     if (lpColumn->mask & LVCF_FMT) 
6722     {
6723       /* format member is valid */
6724       hdi.mask |= HDI_FORMAT;
6725
6726       /* get current format first */
6727       hdiget.mask = HDI_FORMAT;
6728       if (Header_GetItemA(infoPtr->hwndHeader, nColumn, &hdiget))
6729               /* preserve HDF_STRING if present */
6730               hdi.fmt = hdiget.fmt & HDF_STRING;
6731
6732       /* set text alignment (leftmost column must be left-aligned) */
6733       if (nColumn == 0)
6734       {
6735         hdi.fmt |= HDF_LEFT;
6736       }
6737       else
6738       {
6739         if (lpColumn->fmt & LVCFMT_LEFT)
6740         {
6741           hdi.fmt |= HDF_LEFT;
6742         }
6743         else if (lpColumn->fmt & LVCFMT_RIGHT)
6744         {
6745           hdi.fmt |= HDF_RIGHT;
6746         }
6747         else if (lpColumn->fmt & LVCFMT_CENTER)
6748         {
6749           hdi.fmt |= HDF_CENTER;
6750         }
6751       }
6752       
6753       if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6754       {
6755         hdi.fmt |= HDF_BITMAP_ON_RIGHT;
6756       }
6757
6758       if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6759       {
6760         hdi.fmt |= HDF_IMAGE;
6761       }
6762       
6763       if (lpColumn->fmt & LVCFMT_IMAGE)
6764       {
6765         hdi.fmt |= HDF_IMAGE;
6766         hdi.iImage = I_IMAGECALLBACK;
6767       }
6768     }
6769
6770     if (lpColumn->mask & LVCF_WIDTH) 
6771     {
6772       hdi.mask |= HDI_WIDTH;
6773       hdi.cxy = lpColumn->cx;
6774     }
6775     
6776     if (lpColumn->mask & LVCF_TEXT) 
6777     {
6778       hdi.mask |= HDI_TEXT | HDI_FORMAT;
6779       hdi.pszText = lpColumn->pszText;
6780       hdi.cchTextMax = lstrlenA(lpColumn->pszText);
6781       hdi.fmt |= HDF_STRING;
6782     }
6783   
6784     if (lpColumn->mask & LVCF_IMAGE) 
6785     {
6786       hdi.mask |= HDI_IMAGE;
6787       hdi.iImage = lpColumn->iImage;
6788     }
6789
6790     if (lpColumn->mask & LVCF_ORDER) 
6791     {
6792       hdi.mask |= HDI_ORDER;
6793       hdi.iOrder = lpColumn->iOrder;
6794     }
6795
6796     /* set header item attributes */
6797     bResult = Header_SetItemA(infoPtr->hwndHeader, nColumn, &hdi);
6798   }
6799   
6800   return bResult;
6801 }
6802
6803 /* LISTVIEW_SetColumnW */
6804
6805 /***
6806  * DESCRIPTION:
6807  * Sets the column order array
6808  *
6809  * PARAMETERS:
6810  * [I] HWND : window handle
6811  * [I] INT : number of elements in column order array
6812  * [I] INT : pointer to column order array
6813  *
6814  * RETURN:
6815  *   SUCCESS : TRUE
6816  *   FAILURE : FALSE
6817  */
6818 static LRESULT LISTVIEW_SetColumnOrderArray(HWND hwnd, INT iCount, LPINT lpiArray)
6819 {
6820 /*  LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0); */
6821
6822     FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
6823
6824     if (!lpiArray)
6825         return FALSE;
6826
6827     return TRUE;
6828 }
6829
6830
6831 /***
6832  * DESCRIPTION:
6833  * Sets the width of a column
6834  *
6835  * PARAMETERS:
6836  * [I] HWND : window handle
6837  * [I] INT : column index
6838  * [I] INT : column width
6839  *
6840  * RETURN:
6841  *   SUCCESS : TRUE
6842  *   FAILURE : FALSE
6843  */
6844 static LRESULT LISTVIEW_SetColumnWidth(HWND hwnd, INT iCol, INT cx)
6845 {
6846     LISTVIEW_INFO *infoPtr;
6847     HDITEMA hdi;
6848     LRESULT lret;
6849     LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
6850     UINT uView = lStyle & LVS_TYPEMASK; 
6851     HDC hdc;
6852     HFONT header_font;
6853     HFONT old_font;
6854     SIZE size;
6855     CHAR text_buffer[DISP_TEXT_SIZE];
6856     INT header_item_count;
6857     INT item_index;
6858     RECT rcHeader;
6859
6860
6861     /* make sure we can get the listview info */
6862     if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0)))
6863       return (FALSE);
6864
6865     if (!infoPtr->hwndHeader) /* make sure we have a header */
6866       return (FALSE);
6867
6868     /* set column width only if in report or list mode */
6869     if ((uView != LVS_REPORT) && (uView != LVS_LIST))
6870       return (FALSE);            
6871
6872     /* take care of invalid cx values */
6873     if((uView == LVS_REPORT) && (cx < -2))
6874       cx = LVSCW_AUTOSIZE;
6875     else if (uView == LVS_LIST && (cx < 1))
6876       return FALSE;
6877  
6878     /* resize all columns if in LVS_LIST mode */
6879     if(uView == LVS_LIST) {
6880       infoPtr->nItemWidth = cx;
6881       InvalidateRect(hwnd, NULL, TRUE); /* force redraw of the listview */
6882       return TRUE;
6883     }
6884
6885     /* autosize based on listview items width */
6886     if(cx == LVSCW_AUTOSIZE)
6887     {
6888       /* set the width of the header to the width of the widest item */
6889       for(item_index = 0; item_index < GETITEMCOUNT(infoPtr); item_index++)
6890       {
6891         if(cx < LISTVIEW_GetLabelWidth(hwnd, item_index))
6892           cx = LISTVIEW_GetLabelWidth(hwnd, item_index);
6893       } 
6894     } /* autosize based on listview header width */
6895     else if(cx == LVSCW_AUTOSIZE_USEHEADER)
6896     {
6897       header_item_count = Header_GetItemCount(infoPtr->hwndHeader);
6898  
6899       /* if iCol is the last column make it fill the remainder of the controls width */
6900       if(iCol == (header_item_count - 1)) {
6901         /* get the width of every item except the current one */
6902         hdi.mask = HDI_WIDTH;
6903         cx = 0;
6904         
6905         for(item_index = 0; item_index < (header_item_count - 1); item_index++) {
6906           Header_GetItemA(infoPtr->hwndHeader, item_index, (LPARAM)(&hdi));
6907           cx+=hdi.cxy;
6908         }
6909  
6910         /* retrieve the layout of the header */
6911         GetWindowRect(infoPtr->hwndHeader, &rcHeader);
6912
6913         cx = (rcHeader.right - rcHeader.left) - cx;
6914       }                                  
6915       else
6916       {
6917         /* retrieve header font */
6918         header_font = SendMessageA(infoPtr->hwndHeader, WM_GETFONT, 0L, 0L);
6919  
6920         /* retrieve header text */
6921         hdi.mask = HDI_TEXT;
6922         hdi.cchTextMax = sizeof(text_buffer);
6923         hdi.pszText = text_buffer;             
6924     
6925         Header_GetItemA(infoPtr->hwndHeader, iCol, (LPARAM)(&hdi));
6926  
6927         /* determine the width of the text in the header */
6928         hdc = GetDC(hwnd);
6929         old_font = SelectObject(hdc, header_font); /* select the font into hdc */
6930
6931         GetTextExtentPoint32A(hdc, text_buffer, strlen(text_buffer), &size);
6932  
6933         SelectObject(hdc, old_font); /* restore the old font */    
6934         ReleaseDC(hwnd, hdc);
6935  
6936         /* set the width of this column to the width of the text */
6937         cx = size.cx;
6938       }
6939   }
6940
6941   /* call header to update the column change */
6942   hdi.mask = HDI_WIDTH;                          
6943
6944   hdi.cxy = cx;
6945   lret = Header_SetItemA(infoPtr->hwndHeader, (WPARAM)iCol, (LPARAM)&hdi);
6946  
6947   InvalidateRect(hwnd, NULL, TRUE); /* force redraw of the listview */
6948  
6949   return lret;
6950 }
6951
6952 /***
6953  * DESCRIPTION:
6954  * Sets the extended listview style.
6955  *
6956  * PARAMETERS:
6957  * [I] HWND  : window handle
6958  * [I] DWORD : mask
6959  * [I] DWORD : style
6960  *
6961  * RETURN:
6962  *   SUCCESS : previous style
6963  *   FAILURE : 0
6964  */
6965 static LRESULT LISTVIEW_SetExtendedListViewStyle(HWND hwnd, DWORD dwMask, DWORD dwStyle)
6966 {
6967     LISTVIEW_INFO *infoPtr;
6968     DWORD dwOldStyle;
6969
6970     /* make sure we can get the listview info */
6971     if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0)))
6972         return (0);
6973
6974     /* store previous style */
6975     dwOldStyle = infoPtr->dwExStyle;
6976
6977     /* set new style */
6978     if (dwMask)
6979       infoPtr->dwExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
6980     else
6981       infoPtr->dwExStyle = dwStyle;
6982
6983     return (dwOldStyle);
6984 }
6985
6986 /* LISTVIEW_SetHotCursor */
6987
6988 /***
6989  * DESCRIPTION:
6990  * Sets the hot item index.
6991  *
6992  * PARAMETERS:
6993  * [I] HWND  : window handle
6994  * [I] INT   : index
6995  *
6996  * RETURN:
6997  *   SUCCESS : previous hot item index
6998  *   FAILURE : -1 (no hot item)
6999  */
7000 static LRESULT LISTVIEW_SetHotItem(HWND hwnd, INT iIndex)
7001 {
7002     LISTVIEW_INFO *infoPtr;
7003     INT iOldIndex;
7004
7005     /* make sure we can get the listview info */
7006     if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0)))
7007         return (-1);
7008
7009     /* store previous index */
7010     iOldIndex = infoPtr->nHotItem;
7011
7012     /* set new style */
7013     infoPtr->nHotItem = iIndex;
7014
7015     return (iOldIndex);
7016 }
7017
7018 /***
7019  * DESCRIPTION:
7020  * Sets the amount of time the cursor must hover over an item before it is selected.
7021  *
7022  * PARAMETER(S):
7023  * [I] HWND : window handle
7024  * [I] DWORD : dwHoverTime, if -1 the hover time is set to the default
7025  *
7026  * RETURN:
7027  * Returns the previous hover time
7028  */
7029 static LRESULT LISTVIEW_SetHoverTime(HWND hwnd, DWORD dwHoverTime)
7030 {
7031   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7032   DWORD oldHoverTime = infoPtr->dwHoverTime;
7033
7034   infoPtr->dwHoverTime = dwHoverTime;
7035
7036   return oldHoverTime;
7037 }
7038
7039 /* LISTVIEW_SetIconSpacing */
7040
7041 /***
7042  * DESCRIPTION:
7043  * Sets image lists.
7044  * 
7045  * PARAMETER(S):
7046  * [I] HWND : window handle
7047  * [I] INT : image list type  
7048  * [I] HIMAGELIST : image list handle
7049  *
7050  * RETURN:
7051  *   SUCCESS : old image list
7052  *   FAILURE : NULL
7053  */
7054 static LRESULT LISTVIEW_SetImageList(HWND hwnd, INT nType, HIMAGELIST himl)
7055 {
7056   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7057   HIMAGELIST himlOld = 0;
7058
7059   switch (nType) 
7060   {
7061   case LVSIL_NORMAL:
7062     himlOld = infoPtr->himlNormal;
7063     infoPtr->himlNormal = himl;
7064     break;
7065
7066   case LVSIL_SMALL:
7067     himlOld = infoPtr->himlSmall;
7068     infoPtr->himlSmall = himl;
7069     break;
7070
7071   case LVSIL_STATE:
7072     himlOld = infoPtr->himlState;
7073     infoPtr->himlState = himl;
7074     ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
7075     break;
7076   }
7077
7078   infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
7079
7080   return (LRESULT)himlOld;
7081 }
7082
7083
7084 /***
7085  * DESCRIPTION:
7086  * Sets the attributes of an item.
7087  * 
7088  * PARAMETER(S):
7089  * [I] HWND : window handle
7090  * [I] LPLVITEM : item information 
7091  *
7092  * RETURN:
7093  *   SUCCESS : TRUE
7094  *   FAILURE : FALSE
7095  */
7096 static LRESULT LISTVIEW_SetItemA(HWND hwnd, LPLVITEMA lpLVItem)
7097 {
7098   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7099   BOOL bResult = FALSE;
7100
7101   if (lpLVItem != NULL)
7102   {
7103     if ((lpLVItem->iItem >= 0) && (lpLVItem->iItem < GETITEMCOUNT(infoPtr)))
7104     {
7105       if (lpLVItem->iSubItem == 0)
7106       {
7107         bResult = LISTVIEW_SetItem(hwnd, lpLVItem);
7108       }
7109       else
7110       {
7111         bResult = LISTVIEW_SetSubItem(hwnd, lpLVItem);
7112       }
7113     }
7114   }
7115
7116
7117   return bResult;
7118 }
7119
7120 /* LISTVIEW_SetItemW  */
7121
7122 /***
7123  * DESCRIPTION:
7124  * Preallocates memory.
7125  * 
7126  * PARAMETER(S):
7127  * [I] HWND : window handle
7128  * [I] INT   : item count (projected number of items)
7129  * [I] DWORD : update flags
7130  *
7131  * RETURN:
7132  *   SUCCESS : TRUE
7133  *   FAILURE : FALSE
7134  */
7135 static BOOL LISTVIEW_SetItemCount(HWND hwnd, INT nItems, DWORD dwFlags)
7136 {
7137   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongA(hwnd, 0);
7138
7139   if (GetWindowLongA(hwnd, GWL_STYLE) & LVS_OWNERDATA)
7140   {
7141       int precount,topvisible;
7142       TRACE("LVS_OWNERDATA is set!\n");
7143
7144       /*
7145        * Internally remove all the selections. 
7146        */
7147       do
7148       {
7149         LISTVIEW_SELECTION *selection;
7150         selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,0);
7151         if (selection)
7152             LISTVIEW_RemoveSelectionRange(hwnd,selection->lower,
7153                                           selection->upper);
7154       }
7155       while (infoPtr->hdpaSelectionRanges->nItemCount>0);
7156  
7157       precount = infoPtr->hdpaItems->nItemCount;
7158       topvisible = ListView_GetTopIndex(hwnd) +
7159                    LISTVIEW_GetCountPerColumn(hwnd) + 1;
7160
7161       infoPtr->hdpaItems->nItemCount = nItems;
7162
7163       LISTVIEW_UpdateSize(hwnd);
7164       LISTVIEW_UpdateScroll(hwnd);
7165       if (min(precount,infoPtr->hdpaItems->nItemCount)<topvisible) 
7166         InvalidateRect(hwnd, NULL, TRUE);
7167   }
7168   else
7169   {
7170    if (nItems == 0)
7171     return LISTVIEW_DeleteAllItems (hwnd);
7172
7173    if (nItems > GETITEMCOUNT(infoPtr))
7174     {
7175       /* append items */
7176       FIXME("append items\n");
7177
7178     }
7179     else if (nItems < GETITEMCOUNT(infoPtr))
7180     {
7181       /* remove items */
7182       while(nItems < GETITEMCOUNT(infoPtr)) {
7183         LISTVIEW_DeleteItem(hwnd, GETITEMCOUNT(infoPtr) - 1);
7184       }
7185     }
7186   }
7187
7188   return TRUE;
7189 }
7190
7191 /***
7192  * DESCRIPTION:
7193  * Sets the position of an item.
7194  * 
7195  * PARAMETER(S):
7196  * [I] HWND : window handle
7197  * [I] INT : item index
7198  * [I] LONG : x coordinate
7199  * [I] LONG : y coordinate
7200  *
7201  * RETURN:
7202  *   SUCCESS : TRUE
7203  *   FAILURE : FALSE
7204  */
7205 static BOOL LISTVIEW_SetItemPosition(HWND hwnd, INT nItem,
7206                                      LONG nPosX, LONG nPosY)
7207 {
7208   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongA(hwnd, 0);
7209   UINT lStyle = GetWindowLongA(hwnd, GWL_STYLE);
7210   UINT uView = lStyle & LVS_TYPEMASK;
7211   LISTVIEW_ITEM *lpItem;
7212   HDPA hdpaSubItems;
7213   BOOL bResult = FALSE;
7214
7215   TRACE("(hwnd=%x,nItem=%d,X=%ld,Y=%ld)\n", hwnd, nItem, nPosX, nPosY);
7216   
7217   if (lStyle & LVS_OWNERDATA)
7218     return FALSE;
7219
7220   if ((nItem >= 0) || (nItem < GETITEMCOUNT(infoPtr)))
7221   {
7222     if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
7223     {
7224       hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
7225       if (hdpaSubItems != NULL)
7226       {
7227         lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
7228         if (lpItem != NULL)
7229         {
7230           bResult = TRUE;
7231           lpItem->ptPosition.x = nPosX;
7232           lpItem->ptPosition.y = nPosY;
7233         }
7234       }
7235     }
7236   }
7237
7238   return bResult;
7239 }
7240
7241 /***
7242  * DESCRIPTION:
7243  * Sets the state of one or many items.
7244  * 
7245  * PARAMETER(S):
7246  * [I] HWND : window handle
7247  * [I]INT : item index
7248  * [I] LPLVITEM : item or subitem info
7249  *
7250  * RETURN:
7251  *   SUCCESS : TRUE
7252  *   FAILURE : FALSE
7253  */
7254 static LRESULT LISTVIEW_SetItemState(HWND hwnd, INT nItem, LPLVITEMA lpLVItem)
7255 {
7256   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7257   BOOL bResult = FALSE;
7258   LVITEMA lvItem;
7259   INT i;
7260
7261   if (nItem == -1)
7262   {
7263     bResult = TRUE;
7264     ZeroMemory(&lvItem, sizeof(LVITEMA));
7265     lvItem.mask = LVIF_STATE;
7266     lvItem.state = lpLVItem->state;
7267     lvItem.stateMask = lpLVItem->stateMask ;
7268     
7269     /* apply to all items */
7270     for (i = 0; i< GETITEMCOUNT(infoPtr); i++)
7271     {
7272       lvItem.iItem = i;
7273       if (ListView_SetItemA(hwnd, &lvItem) == FALSE)
7274       {
7275         bResult = FALSE;
7276       }
7277     }
7278   }
7279   else
7280   {
7281     ZeroMemory(&lvItem, sizeof(LVITEMA));
7282     lvItem.mask = LVIF_STATE;
7283     lvItem.state = lpLVItem->state;
7284     lvItem.stateMask = lpLVItem->stateMask;
7285     lvItem.iItem = nItem;
7286     bResult = ListView_SetItemA(hwnd, &lvItem);
7287   }
7288
7289   return bResult;
7290 }
7291
7292 /***
7293  * DESCRIPTION:
7294  * Sets the text of an item or subitem.
7295  * 
7296  * PARAMETER(S):
7297  * [I] HWND : window handle
7298  * [I] INT : item index
7299  * [I] LPLVITEMA : item or subitem info
7300  *
7301  * RETURN:
7302  *   SUCCESS : TRUE
7303  *   FAILURE : FALSE
7304  */
7305 static BOOL LISTVIEW_SetItemTextA(HWND hwnd, INT nItem, LPLVITEMA lpLVItem)
7306 {
7307   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7308   BOOL bResult = FALSE;
7309   LVITEMA lvItem;
7310
7311   if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
7312   {
7313     ZeroMemory(&lvItem, sizeof(LVITEMA));
7314     lvItem.mask = LVIF_TEXT;
7315     lvItem.pszText = lpLVItem->pszText;
7316     lvItem.iItem = nItem;
7317     lvItem.iSubItem = lpLVItem->iSubItem;
7318     bResult = ListView_SetItemA(hwnd, &lvItem);
7319   }
7320   
7321   return bResult;
7322 }
7323
7324 /* LISTVIEW_SetItemTextW */
7325
7326 /***
7327  * DESCRIPTION:
7328  * Set item index that marks the start of a multiple selection.
7329  *
7330  * PARAMETER(S):
7331  * [I] HWND : window handle
7332  * [I] INT  : index
7333  *
7334  * RETURN:
7335  * Index number or -1 if there is no selection mark.
7336  */
7337 static LRESULT LISTVIEW_SetSelectionMark(HWND hwnd, INT nIndex)
7338 {
7339   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7340   INT nOldIndex = infoPtr->nSelectionMark;
7341
7342   infoPtr->nSelectionMark = nIndex;
7343
7344   return nOldIndex;
7345 }
7346
7347 /***
7348  * DESCRIPTION:
7349  * Sets the text background color.
7350  * 
7351  * PARAMETER(S):
7352  * [I] HWND : window handle
7353  * [I] COLORREF : text background color
7354  *
7355  * RETURN:
7356  *   SUCCESS : TRUE
7357  *   FAILURE : FALSE
7358  */
7359 static LRESULT LISTVIEW_SetTextBkColor(HWND hwnd, COLORREF clrTextBk)
7360 {
7361   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7362
7363   infoPtr->clrTextBk = clrTextBk;
7364   InvalidateRect(hwnd, NULL, TRUE);
7365
7366   return TRUE;
7367 }
7368
7369 /***
7370  * DESCRIPTION:
7371  * Sets the text foreground color.
7372  * 
7373  * PARAMETER(S):
7374  * [I] HWND : window handle
7375  * [I] COLORREF : text color 
7376  *
7377  * RETURN:
7378  *   SUCCESS : TRUE
7379  *   FAILURE : FALSE
7380  */
7381 static LRESULT LISTVIEW_SetTextColor (HWND hwnd, COLORREF clrText)
7382 {
7383   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7384
7385   infoPtr->clrText = clrText;
7386   InvalidateRect(hwnd, NULL, TRUE);
7387
7388   return TRUE;
7389 }
7390
7391 /* LISTVIEW_SetToolTips */
7392 /* LISTVIEW_SetUnicodeFormat */
7393 /* LISTVIEW_SetWorkAreas */
7394
7395 /***
7396  * DESCRIPTION:
7397  * Callback internally used by LISTVIEW_SortItems()
7398  * 
7399  * PARAMETER(S):
7400  * [I] LPVOID : first LISTVIEW_ITEM to compare
7401  * [I] LPVOID : second LISTVIEW_ITEM to compare
7402  * [I] LPARAM : HWND of control
7403  *
7404  * RETURN:
7405  *   if first comes before second : negative
7406  *   if first comes after second : positive
7407  *   if first and second are equivalent : zero
7408  */
7409 static INT WINAPI LISTVIEW_CallBackCompare( 
7410   LPVOID first, 
7411   LPVOID second, 
7412   LPARAM lParam)
7413 {
7414   /* Forward the call to the client defined callback */
7415   INT rv;
7416   HWND hwnd = (HWND)lParam;
7417   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7418   HDPA  hdpa_first = (HDPA) first;
7419   HDPA  hdpa_second = (HDPA) second;
7420   LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( hdpa_first, 0 );
7421   LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( hdpa_second, 0 );
7422
7423   rv = (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
7424
7425   return rv;
7426 }
7427
7428 /***
7429  * DESCRIPTION:
7430  * Sorts the listview items.
7431  * 
7432  * PARAMETER(S):
7433  * [I] HWND : window handle
7434  * [I] WPARAM : application-defined value
7435  * [I] LPARAM : pointer to comparision callback
7436  *
7437  * RETURN:
7438  *   SUCCESS : TRUE
7439  *   FAILURE : FALSE
7440  */
7441 static LRESULT LISTVIEW_SortItems(HWND hwnd, WPARAM wParam, LPARAM lParam)
7442 {
7443     LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7444     int nCount;
7445     UINT lStyle = GetWindowLongA(hwnd, GWL_STYLE);
7446
7447     if (lStyle & LVS_OWNERDATA)
7448       return FALSE;
7449
7450     if (!infoPtr || !infoPtr->hdpaItems)
7451         return FALSE;
7452    
7453     nCount = GETITEMCOUNT(infoPtr);
7454     /* if there are 0 or 1 items, there is no need to sort */
7455     if (nCount > 1)
7456     {
7457         infoPtr->pfnCompare = (PFNLVCOMPARE)lParam;
7458         infoPtr->lParamSort = (LPARAM)wParam;
7459         
7460         DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, hwnd);
7461     }
7462
7463     /* align the items */
7464     LISTVIEW_AlignTop(hwnd);
7465
7466     /* refresh the display */
7467     InvalidateRect(hwnd, NULL, TRUE);
7468
7469     return TRUE;
7470 }
7471
7472 /* LISTVIEW_SubItemHitTest */
7473
7474 /***
7475  * DESCRIPTION:
7476  * Updates an items or rearranges the listview control.
7477  * 
7478  * PARAMETER(S):
7479  * [I] HWND : window handle
7480  * [I] INT : item index
7481  *
7482  * RETURN:
7483  *   SUCCESS : TRUE
7484  *   FAILURE : FALSE
7485  */
7486 static LRESULT LISTVIEW_Update(HWND hwnd, INT nItem)
7487 {
7488   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7489   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
7490   BOOL bResult = FALSE;
7491   RECT rc;
7492
7493   if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
7494   {
7495     bResult = TRUE;
7496
7497     /* rearrange with default alignment style */
7498     if ((lStyle & LVS_AUTOARRANGE) && (((lStyle & LVS_TYPEMASK) == LVS_ICON) ||
7499         ((lStyle & LVS_TYPEMASK)  == LVS_SMALLICON)))
7500     {
7501       ListView_Arrange(hwnd, 0);
7502     }
7503     else
7504     {
7505       /* get item bounding rectangle */
7506       rc.left = LVIR_BOUNDS;
7507       ListView_GetItemRect(hwnd, nItem, &rc);
7508       InvalidateRect(hwnd, &rc, TRUE);
7509     }
7510   }
7511
7512   return bResult;
7513 }
7514
7515 /***
7516  * DESCRIPTION:
7517  * Creates the listview control.
7518  * 
7519  * PARAMETER(S):
7520  * [I] HWND : window handle
7521  *
7522  * RETURN:
7523  * Zero
7524  */
7525 static LRESULT LISTVIEW_Create(HWND hwnd, WPARAM wParam, LPARAM lParam)
7526 {
7527   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7528   LPCREATESTRUCTA lpcs = (LPCREATESTRUCTA)lParam;
7529   UINT uView = lpcs->style & LVS_TYPEMASK;
7530   LOGFONTA logFont;
7531
7532   /* initialize info pointer */
7533   ZeroMemory(infoPtr, sizeof(LISTVIEW_INFO));
7534
7535   /* determine the type of structures to use */
7536   infoPtr->notifyFormat = SendMessageA(GetParent(hwnd), WM_NOTIFYFORMAT, 
7537                                        (WPARAM)hwnd, (LPARAM)NF_QUERY);
7538   if (infoPtr->notifyFormat != NFR_ANSI)
7539   {
7540     FIXME("ANSI notify format is NOT used\n");
7541   }
7542   
7543   /* initialize color information  */
7544   infoPtr->clrBk = GetSysColor(COLOR_WINDOW);
7545   infoPtr->clrText = GetSysColor(COLOR_WINDOWTEXT);
7546   infoPtr->clrTextBk = CLR_DEFAULT;
7547
7548   /* set default values */
7549   infoPtr->uCallbackMask = 0;
7550   infoPtr->nFocusedItem = -1;
7551   infoPtr->nSelectionMark = -1;
7552   infoPtr->nHotItem = -1;
7553   infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
7554   infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
7555   ZeroMemory(&infoPtr->rcList, sizeof(RECT));
7556   infoPtr->hwndEdit = 0;
7557   infoPtr->pedititem = NULL;
7558   infoPtr->nEditLabelItem = -1;
7559
7560   /* get default font (icon title) */
7561   SystemParametersInfoA(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
7562   infoPtr->hDefaultFont = CreateFontIndirectA(&logFont);
7563   infoPtr->hFont = infoPtr->hDefaultFont;
7564   
7565   /* create header */
7566   infoPtr->hwndHeader = CreateWindowA(WC_HEADERA, (LPCSTR)NULL, 
7567                                       WS_CHILD | HDS_HORZ | HDS_BUTTONS, 
7568                                       0, 0, 0, 0, hwnd, (HMENU)0, 
7569                                       lpcs->hInstance, NULL);
7570
7571   /* set header font */
7572   SendMessageA(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, 
7573                (LPARAM)TRUE);
7574   
7575   if (uView == LVS_ICON)
7576   {
7577     infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
7578     infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
7579   }
7580   else if (uView == LVS_REPORT)
7581   {
7582     if (!(LVS_NOCOLUMNHEADER & lpcs->style))
7583     {
7584       ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
7585     }
7586     else
7587     {
7588       /* set HDS_HIDDEN flag to hide the header bar */
7589       SetWindowLongA(infoPtr->hwndHeader, GWL_STYLE, 
7590                     GetWindowLongA(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
7591     }
7592       
7593
7594     infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
7595     infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
7596   }
7597   else
7598   {
7599     infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
7600     infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
7601   }
7602
7603   /* display unsupported listview window styles */
7604   LISTVIEW_UnsupportedStyles(lpcs->style);
7605
7606   /* allocate memory for the data structure */
7607   infoPtr->hdpaItems = DPA_Create(10);
7608
7609   /* allocate memory for the selection ranges */
7610   infoPtr->hdpaSelectionRanges = DPA_Create(10);
7611
7612   /* initialize size of items */
7613   infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
7614   infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
7615
7616   /* initialize the hover time to -1(indicating the default system hover time) */
7617   infoPtr->dwHoverTime = -1;  
7618
7619   return 0;
7620 }
7621
7622 /***
7623  * DESCRIPTION:
7624  * Erases the background of the listview control.
7625  * 
7626  * PARAMETER(S):
7627  * [I] HWND : window handle
7628  * [I] WPARAM : device context handle
7629  * [I] LPARAM : not used
7630  * 
7631  * RETURN:
7632  *   SUCCESS : TRUE
7633  *   FAILURE : FALSE
7634  */
7635 static LRESULT LISTVIEW_EraseBackground(HWND hwnd, WPARAM wParam, 
7636                                         LPARAM lParam)
7637 {
7638   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7639   BOOL bResult;
7640
7641   if (infoPtr->clrBk == CLR_NONE) 
7642   {
7643     bResult = SendMessageA(GetParent(hwnd), WM_ERASEBKGND, wParam, lParam);
7644   }
7645   else 
7646   {
7647     RECT rc;
7648     HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk);
7649     GetClientRect(hwnd, &rc);
7650     FillRect((HDC)wParam, &rc, hBrush);
7651     DeleteObject(hBrush);
7652     bResult = TRUE;
7653   }
7654
7655   return bResult;
7656 }
7657
7658
7659 static void LISTVIEW_FillBackground(HWND hwnd, HDC hdc, LPRECT rc)
7660 {
7661   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7662
7663   if (infoPtr->clrBk != CLR_NONE) 
7664   {
7665     HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk);
7666     FillRect(hdc, rc, hBrush);
7667     DeleteObject(hBrush);
7668   }
7669 }
7670
7671 /***
7672  * DESCRIPTION:
7673  * Retrieves the listview control font.
7674  * 
7675  * PARAMETER(S):
7676  * [I] HWND : window handle
7677  *
7678  * RETURN:
7679  * Font handle.
7680  */
7681 static LRESULT LISTVIEW_GetFont(HWND hwnd)
7682 {
7683   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7684
7685   return infoPtr->hFont;
7686 }
7687
7688 /***
7689  * DESCRIPTION:
7690  * Performs vertical scrolling.
7691  * 
7692  * PARAMETER(S):
7693  * [I] HWND : window handle
7694  * [I] INT : scroll code
7695  * [I] SHORT : current scroll position if scroll code is SB_THIMBPOSITION 
7696  *             or SB_THUMBTRACK.
7697  * [I] HWND : scrollbar control window handle
7698  *
7699  * RETURN:
7700  * Zero
7701  */
7702 static LRESULT LISTVIEW_VScroll(HWND hwnd, INT nScrollCode, SHORT nCurrentPos,
7703                                 HWND hScrollWnd)
7704 {
7705   SCROLLINFO scrollInfo;
7706
7707   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7708   SendMessageA(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7709
7710   ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
7711   scrollInfo.cbSize = sizeof(SCROLLINFO);
7712   scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
7713
7714   if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
7715   {
7716     INT nOldScrollPos = scrollInfo.nPos;
7717     switch (nScrollCode)
7718     {
7719     case SB_LINEUP:
7720       if (scrollInfo.nPos > scrollInfo.nMin)
7721       {
7722         scrollInfo.nPos--;
7723       }
7724     break;
7725     
7726     case SB_LINEDOWN:
7727       if (scrollInfo.nPos < scrollInfo.nMax)
7728       {
7729         scrollInfo.nPos++;
7730       }
7731       break;
7732       
7733     case SB_PAGEUP:
7734       if (scrollInfo.nPos > scrollInfo.nMin)
7735       {
7736         if (scrollInfo.nPos >= scrollInfo.nPage)
7737         {
7738           scrollInfo.nPos -= scrollInfo.nPage;
7739         }
7740         else
7741         {
7742           scrollInfo.nPos = scrollInfo.nMin;
7743         }
7744       }
7745       break;
7746       
7747     case SB_PAGEDOWN:
7748       if (scrollInfo.nPos < scrollInfo.nMax)
7749       {
7750         if (scrollInfo.nPos <= scrollInfo.nMax - scrollInfo.nPage)
7751         {
7752           scrollInfo.nPos += scrollInfo.nPage;
7753         }
7754         else
7755         {
7756           scrollInfo.nPos = scrollInfo.nMax;
7757         }
7758       }
7759       break;
7760
7761     case SB_THUMBTRACK:
7762         scrollInfo.nPos = nCurrentPos;
7763         if (scrollInfo.nPos > scrollInfo.nMax)
7764             scrollInfo.nPos=scrollInfo.nMax;
7765
7766         if (scrollInfo.nPos < scrollInfo.nMin)
7767             scrollInfo.nPos=scrollInfo.nMin;
7768
7769       break;
7770     }
7771
7772     if (nOldScrollPos != scrollInfo.nPos)
7773     {
7774       scrollInfo.fMask = SIF_POS;
7775       SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
7776       InvalidateRect(hwnd, NULL, TRUE);
7777     }
7778   }
7779     
7780   return 0;
7781 }
7782
7783 /***
7784  * DESCRIPTION:
7785  * Performs horizontal scrolling.
7786  * 
7787  * PARAMETER(S):
7788  * [I] HWND : window handle
7789  * [I] INT : scroll code
7790  * [I] SHORT : current scroll position if scroll code is SB_THUMBPOSITION 
7791  *             or SB_THUMBTRACK.
7792  * [I] HWND : scrollbar control window handle
7793  *
7794  * RETURN:
7795  * Zero
7796  */
7797 static LRESULT LISTVIEW_HScroll(HWND hwnd, INT nScrollCode, SHORT nCurrentPos,
7798                                 HWND hScrollWnd)
7799 {
7800   SCROLLINFO scrollInfo;
7801
7802   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7803   SendMessageA(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7804
7805
7806   ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
7807   scrollInfo.cbSize = sizeof(SCROLLINFO);
7808   scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
7809  
7810   if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
7811   {
7812     INT nOldScrollPos = scrollInfo.nPos;
7813
7814     switch (nScrollCode)
7815     {
7816     case SB_LINELEFT:
7817       if (scrollInfo.nPos > scrollInfo.nMin)
7818       {
7819         scrollInfo.nPos--;
7820       }
7821       break;
7822     
7823     case SB_LINERIGHT:
7824       if (scrollInfo.nPos < scrollInfo.nMax)
7825       {
7826         scrollInfo.nPos++;
7827       }
7828       break;
7829       
7830     case SB_PAGELEFT:
7831       if (scrollInfo.nPos > scrollInfo.nMin)
7832       {
7833         if (scrollInfo.nPos >= scrollInfo.nPage)
7834         {
7835           scrollInfo.nPos -= scrollInfo.nPage;
7836         }
7837         else
7838         {
7839           scrollInfo.nPos = scrollInfo.nMin;
7840         }
7841       }
7842       break;
7843       
7844     case SB_PAGERIGHT:
7845       if (scrollInfo.nPos < scrollInfo.nMax)
7846       {
7847         if (scrollInfo.nPos <= scrollInfo.nMax - scrollInfo.nPage)
7848         {
7849           scrollInfo.nPos += scrollInfo.nPage;
7850         }
7851         else
7852         {
7853           scrollInfo.nPos = scrollInfo.nMax;
7854         }
7855       }
7856       break;
7857
7858     case SB_THUMBTRACK:
7859         scrollInfo.nPos = nCurrentPos;
7860
7861         if (scrollInfo.nPos > scrollInfo.nMax)
7862             scrollInfo.nPos=scrollInfo.nMax;
7863
7864         if (scrollInfo.nPos < scrollInfo.nMin)
7865             scrollInfo.nPos=scrollInfo.nMin;
7866       break;
7867     }
7868
7869     if (nOldScrollPos != scrollInfo.nPos)
7870     {
7871       UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
7872       scrollInfo.fMask = SIF_POS;
7873       SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
7874       if(uView == LVS_REPORT)
7875       {
7876           scrollInfo.fMask = SIF_POS;
7877           GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
7878           LISTVIEW_UpdateHeaderSize(hwnd, scrollInfo.nPos);
7879       }
7880       InvalidateRect(hwnd, NULL, TRUE);
7881     }
7882   }
7883     
7884   return 0;
7885 }
7886
7887 static LRESULT LISTVIEW_MouseWheel(HWND hwnd, INT wheelDelta)
7888 {
7889     INT gcWheelDelta = 0;
7890     UINT pulScrollLines = 3;
7891     SCROLLINFO scrollInfo;
7892
7893     UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
7894
7895     SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
7896     gcWheelDelta -= wheelDelta;
7897
7898     ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
7899     scrollInfo.cbSize = sizeof(SCROLLINFO);
7900     scrollInfo.fMask = SIF_POS | SIF_RANGE;
7901
7902     switch(uView)
7903     {
7904     case LVS_ICON:
7905     case LVS_SMALLICON:
7906        /*
7907         *  listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
7908         *  should be fixed in the future.
7909         */
7910         if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
7911             LISTVIEW_VScroll(hwnd, SB_THUMBPOSITION, scrollInfo.nPos + (gcWheelDelta < 0) ? 37 : -37, 0);
7912         break;
7913
7914     case LVS_REPORT:
7915         if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
7916         {
7917             if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
7918             {
7919                 int cLineScroll = min(LISTVIEW_GetCountPerColumn(hwnd), pulScrollLines);
7920                 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
7921                 LISTVIEW_VScroll(hwnd, SB_THUMBPOSITION, scrollInfo.nPos + cLineScroll, 0);
7922             }
7923         }
7924         break;
7925
7926     case LVS_LIST:
7927         LISTVIEW_HScroll(hwnd, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
7928         break;
7929     }
7930     return 0;
7931 }
7932
7933 /***
7934  * DESCRIPTION:
7935  * ??? 
7936  * 
7937  * PARAMETER(S):
7938  * [I] HWND : window handle
7939  * [I] INT : virtual key 
7940  * [I] LONG : key data
7941  *
7942  * RETURN:
7943  * Zero
7944  */
7945 static LRESULT LISTVIEW_KeyDown(HWND hwnd, INT nVirtualKey, LONG lKeyData)
7946 {
7947   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7948   INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
7949   HWND hwndParent = GetParent(hwnd);
7950   NMLVKEYDOWN nmKeyDown; 
7951   NMHDR nmh;
7952   INT nItem = -1;
7953   BOOL bRedraw = FALSE;
7954   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
7955   UINT uView =  lStyle & LVS_TYPEMASK;
7956
7957   /* send LVN_KEYDOWN notification */
7958   ZeroMemory(&nmKeyDown, sizeof(NMLVKEYDOWN));
7959   nmKeyDown.hdr.hwndFrom = hwnd;  
7960   nmKeyDown.hdr.idFrom = nCtrlId;  
7961   nmKeyDown.hdr.code = LVN_KEYDOWN;  
7962   nmKeyDown.wVKey = nVirtualKey; 
7963   nmKeyDown.flags = 0; 
7964   SendMessageA(hwndParent, WM_NOTIFY, (WPARAM)nCtrlId, (LPARAM)&nmKeyDown); 
7965   
7966   /* initialize */
7967   nmh.hwndFrom = hwnd;
7968   nmh.idFrom = nCtrlId;
7969
7970   switch (nVirtualKey)
7971   {
7972   case VK_RETURN:
7973     if ((GETITEMCOUNT(infoPtr) > 0) && (infoPtr->nFocusedItem != -1))
7974     {
7975       /* send NM_RETURN notification */
7976       nmh.code = NM_RETURN;
7977       ListView_Notify(hwndParent, nCtrlId, &nmh);
7978       
7979       /* send LVN_ITEMACTIVATE notification */
7980       nmh.code = LVN_ITEMACTIVATE;
7981       ListView_Notify(hwndParent, nCtrlId, &nmh);
7982     }
7983     break;
7984
7985   case VK_HOME:
7986     if (GETITEMCOUNT(infoPtr) > 0)
7987     {
7988       nItem = 0;
7989     }
7990     break;
7991
7992   case VK_END:
7993     if (GETITEMCOUNT(infoPtr) > 0)
7994     {
7995       nItem = GETITEMCOUNT(infoPtr) - 1;
7996     }
7997     break;
7998
7999   case VK_LEFT:
8000     nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_TOLEFT);
8001     break;
8002
8003   case VK_UP:
8004     nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_ABOVE);
8005     break;
8006     
8007   case VK_RIGHT:
8008     nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_TORIGHT);
8009     break;
8010
8011   case VK_DOWN:
8012     nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_BELOW);
8013     break;
8014
8015   case VK_PRIOR:
8016     if (uView == LVS_REPORT)
8017     {
8018       nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(hwnd);
8019     }
8020     else
8021     {
8022       nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(hwnd) 
8023                                     * LISTVIEW_GetCountPerRow(hwnd);
8024     }
8025     if(nItem < 0) nItem = 0;
8026     break;
8027
8028   case VK_NEXT:
8029     if (uView == LVS_REPORT)
8030     {
8031       nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(hwnd);
8032     }
8033     else
8034     {
8035       nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(hwnd)
8036                                     * LISTVIEW_GetCountPerRow(hwnd);
8037     }
8038     if(nItem >= GETITEMCOUNT(infoPtr)) nItem = GETITEMCOUNT(infoPtr) - 1;
8039     break;
8040   }
8041
8042   if ((nItem != -1) && (nItem != infoPtr->nFocusedItem))
8043   {
8044     bRedraw = LISTVIEW_KeySelection(hwnd, nItem);
8045     if (bRedraw != FALSE)
8046     {
8047       /* refresh client area */
8048       UpdateWindow(hwnd);
8049     }
8050   }
8051
8052   return 0;
8053 }
8054
8055 /***
8056  * DESCRIPTION:
8057  * Kills the focus.
8058  * 
8059  * PARAMETER(S):
8060  * [I] HWND : window handle
8061  *
8062  * RETURN:
8063  * Zero
8064  */
8065 static LRESULT LISTVIEW_KillFocus(HWND hwnd)
8066 {
8067   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongA(hwnd, 0);
8068   INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
8069   NMHDR nmh;
8070   INT i,nTop,nBottom;
8071   RECT rcItem;
8072   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
8073   UINT uView =  lStyle & LVS_TYPEMASK;
8074  
8075   TRACE("(hwnd=%x)\n", hwnd);
8076
8077   /* send NM_KILLFOCUS notification */
8078   nmh.hwndFrom = hwnd;
8079   nmh.idFrom = nCtrlId;
8080   nmh.code = NM_KILLFOCUS;
8081   ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
8082
8083   /* set window focus flag */
8084   infoPtr->bFocus = FALSE;
8085
8086   /* NEED drawing optimization ; redraw the selected items */
8087   if (uView & LVS_REPORT)
8088   { 
8089     nTop = LISTVIEW_GetTopIndex(hwnd);
8090     nBottom = nTop + 
8091               LISTVIEW_GetCountPerColumn(hwnd) + 1;
8092   }
8093   else
8094   {
8095     nTop = 0;
8096     nBottom = GETITEMCOUNT(infoPtr);
8097   }
8098   for (i = nTop; i<nBottom; i++)
8099   {
8100     if (LISTVIEW_IsSelected(hwnd,i))
8101     {
8102       rcItem.left = LVIR_BOUNDS;
8103       LISTVIEW_GetItemRect(hwnd, i, &rcItem);
8104       InvalidateRect(hwnd, &rcItem, FALSE);
8105     }
8106   }
8107
8108   return 0;
8109 }
8110
8111 /***
8112  * DESCRIPTION:
8113  * Processes double click messages (left mouse button).
8114  * 
8115  * PARAMETER(S):
8116  * [I] HWND : window handle
8117  * [I] WORD : key flag
8118  * [I] WORD : x coordinate
8119  * [I] WORD : y coordinate
8120  *
8121  * RETURN:
8122  * Zero
8123  */
8124 static LRESULT LISTVIEW_LButtonDblClk(HWND hwnd, WORD wKey, WORD wPosX, 
8125                                       WORD wPosY)
8126 {
8127   LONG nCtrlId = GetWindowLongA(hwnd, GWL_ID);
8128   LVHITTESTINFO htInfo;
8129   NMHDR nmh;
8130   NMLISTVIEW nmlv;
8131   INT ret;
8132
8133   TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8134
8135   htInfo.pt.x = wPosX;
8136   htInfo.pt.y = wPosY;
8137
8138   /* send NM_DBLCLK notification */
8139   ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8140   nmlv.hdr.hwndFrom = hwnd;
8141   nmlv.hdr.idFrom = nCtrlId;
8142   nmlv.hdr.code = NM_DBLCLK;
8143   ret = LISTVIEW_HitTestItem(hwnd, &htInfo, TRUE);
8144   if (ret != -1)
8145   {
8146     nmlv.iItem = htInfo.iItem;
8147     nmlv.iSubItem = htInfo.iSubItem;
8148   }
8149   else
8150   {
8151     nmlv.iItem = -1;
8152     nmlv.iSubItem = 0;
8153   }  
8154   nmlv.ptAction.x = wPosX;
8155   nmlv.ptAction.y = wPosY;
8156   ListView_LVNotify(GetParent(hwnd), nCtrlId, &nmlv);
8157
8158
8159   /* To send the LVN_ITEMACTIVATE, it must be on an Item */
8160   if(ret != -1)
8161   {
8162     /* send LVN_ITEMACTIVATE notification */
8163     nmh.hwndFrom = hwnd;
8164     nmh.idFrom = nCtrlId;
8165     nmh.code = LVN_ITEMACTIVATE;
8166     ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
8167   }
8168
8169   return 0;
8170 }
8171
8172 /***
8173  * DESCRIPTION:
8174  * Processes mouse down messages (left mouse button).
8175  * 
8176  * PARAMETER(S):
8177  * [I] HWND : window handle
8178  * [I] WORD : key flag
8179  * [I] WORD : x coordinate
8180  * [I] WORD : y coordinate
8181  *
8182  * RETURN:
8183  * Zero
8184  */
8185 static LRESULT LISTVIEW_LButtonDown(HWND hwnd, WORD wKey, WORD wPosX, 
8186                                     WORD wPosY)
8187 {
8188   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8189   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
8190   INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
8191   static BOOL bGroupSelect = TRUE;
8192   POINT ptPosition;
8193   NMHDR nmh;
8194   INT nItem;
8195
8196   TRACE("(hwnd=%x, key=%hu, X=%hu, Y=%hu)\n", hwnd, wKey, wPosX, 
8197         wPosY);
8198
8199   /* send NM_RELEASEDCAPTURE notification */ 
8200   nmh.hwndFrom = hwnd;
8201   nmh.idFrom = nCtrlId;
8202   nmh.code = NM_RELEASEDCAPTURE;
8203   ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
8204  
8205   if (infoPtr->bFocus == FALSE)
8206   {
8207     SetFocus(hwnd);
8208   }
8209
8210   /* set left button down flag */
8211   infoPtr->bLButtonDown = TRUE;
8212   
8213   ptPosition.x = wPosX;
8214   ptPosition.y = wPosY;
8215   nItem = LISTVIEW_MouseSelection(hwnd, ptPosition);
8216   if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
8217   {
8218     if (lStyle & LVS_SINGLESEL)
8219     {
8220       if ((ListView_GetItemState(hwnd, nItem, LVIS_SELECTED) & LVIS_SELECTED)
8221           && infoPtr->nEditLabelItem == -1) 
8222       {
8223           infoPtr->nEditLabelItem = nItem;
8224       }
8225       else
8226       {
8227         LISTVIEW_SetSelection(hwnd, nItem);
8228       }
8229     }
8230     else
8231     {
8232       if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
8233       {
8234         if (bGroupSelect != FALSE)
8235         {
8236           LISTVIEW_AddGroupSelection(hwnd, nItem);
8237         }
8238         else
8239         {
8240           LISTVIEW_AddSelection(hwnd, nItem);
8241         }
8242       }
8243       else if (wKey & MK_CONTROL)
8244       {
8245         bGroupSelect = LISTVIEW_ToggleSelection(hwnd, nItem);
8246       }
8247       else  if (wKey & MK_SHIFT)
8248       {
8249         LISTVIEW_SetGroupSelection(hwnd, nItem);
8250       }
8251       else
8252       {
8253         BOOL was_selected =
8254             (ListView_GetItemState(hwnd, nItem, LVIS_SELECTED) & LVIS_SELECTED);
8255
8256         /* set selection (clears other pre-existing selections) */
8257         LISTVIEW_SetSelection(hwnd, nItem);
8258
8259         if (was_selected && infoPtr->nEditLabelItem == -1)
8260         {
8261           infoPtr->nEditLabelItem = nItem;
8262         }
8263       }
8264     }
8265   }
8266   else
8267   {
8268     /* remove all selections */
8269     LISTVIEW_RemoveAllSelections(hwnd);
8270   }
8271
8272   /* redraw if we could have possibly selected something */
8273   if(!GETITEMCOUNT(infoPtr)) InvalidateRect(hwnd, NULL, TRUE);
8274
8275   return 0;
8276 }
8277
8278 /***
8279  * DESCRIPTION:
8280  * Processes mouse up messages (left mouse button).
8281  * 
8282  * PARAMETER(S):
8283  * [I] HWND : window handle
8284  * [I] WORD : key flag
8285  * [I] WORD : x coordinate
8286  * [I] WORD : y coordinate
8287  *
8288  * RETURN:
8289  * Zero
8290  */
8291 static LRESULT LISTVIEW_LButtonUp(HWND hwnd, WORD wKey, WORD wPosX, 
8292                                   WORD wPosY)
8293 {
8294   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8295
8296   TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8297
8298   if (infoPtr->bLButtonDown != FALSE) 
8299   {
8300     INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
8301     NMLISTVIEW nmlv;
8302     LVHITTESTINFO lvHitTestInfo;
8303     INT ret;
8304
8305     lvHitTestInfo.pt.x = wPosX;
8306     lvHitTestInfo.pt.y = wPosY;
8307
8308   /* send NM_CLICK notification */
8309     ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8310     nmlv.hdr.hwndFrom = hwnd;
8311     nmlv.hdr.idFrom = nCtrlId;
8312     nmlv.hdr.code = NM_CLICK;
8313     ret = LISTVIEW_HitTestItem(hwnd, &lvHitTestInfo, TRUE);
8314     if (ret != -1)
8315     {
8316         nmlv.iItem = lvHitTestInfo.iItem;
8317         nmlv.iSubItem = lvHitTestInfo.iSubItem;
8318     }
8319     else
8320     {
8321         nmlv.iItem = -1;
8322         nmlv.iSubItem = 0;
8323     }
8324     nmlv.ptAction.x = wPosX;
8325     nmlv.ptAction.y = wPosY;
8326     ListView_LVNotify(GetParent(hwnd), nCtrlId, &nmlv);
8327
8328
8329     /* set left button flag */
8330     infoPtr->bLButtonDown = FALSE;
8331
8332     if(infoPtr->nEditLabelItem != -1)
8333     {
8334       if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem)
8335       {
8336         LISTVIEW_EditLabelA(hwnd, lvHitTestInfo.iItem);
8337       }
8338       infoPtr->nEditLabelItem = -1;
8339     }
8340   }
8341
8342   return 0;
8343 }
8344
8345 /***
8346  * DESCRIPTION:
8347  * Creates the listview control (called before WM_CREATE).
8348  * 
8349  * PARAMETER(S):
8350  * [I] HWND : window handle
8351  * [I] WPARAM : unhandled 
8352  * [I] LPARAM : widow creation info
8353  * 
8354  * RETURN:
8355  * Zero
8356  */
8357 static LRESULT LISTVIEW_NCCreate(HWND hwnd, WPARAM wParam, LPARAM lParam)
8358 {
8359   LISTVIEW_INFO *infoPtr;
8360
8361   TRACE("(hwnd=%x,wParam=%x,lParam=%lx)\n", hwnd, wParam, lParam);
8362
8363   /* allocate memory for info structure */
8364   infoPtr = (LISTVIEW_INFO *)COMCTL32_Alloc(sizeof(LISTVIEW_INFO));
8365   SetWindowLongA(hwnd, 0, (LONG)infoPtr);
8366   if (infoPtr == NULL) 
8367   {
8368     ERR("could not allocate info memory!\n");
8369     return 0;
8370   }
8371
8372   if ((LISTVIEW_INFO *)GetWindowLongA(hwnd, 0) != infoPtr) 
8373   {
8374     ERR("pointer assignment error!\n");
8375     return 0;
8376   }
8377
8378   return DefWindowProcA(hwnd, WM_NCCREATE, wParam, lParam);
8379 }
8380
8381 /***
8382  * DESCRIPTION:
8383  * Destroys the listview control (called after WM_DESTROY).
8384  * 
8385  * PARAMETER(S):
8386  * [I] HWND : window handle
8387  * 
8388  * RETURN:
8389  * Zero
8390  */
8391 static LRESULT LISTVIEW_NCDestroy(HWND hwnd)
8392 {
8393   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8394
8395   TRACE("(hwnd=%x)\n", hwnd);
8396
8397   /* delete all items */
8398   LISTVIEW_DeleteAllItems(hwnd);
8399
8400   /* destroy data structure */
8401   DPA_Destroy(infoPtr->hdpaItems);
8402   DPA_Destroy(infoPtr->hdpaSelectionRanges);
8403
8404   /* destroy font */
8405   infoPtr->hFont = (HFONT)0;
8406   if (infoPtr->hDefaultFont)
8407   {
8408     DeleteObject(infoPtr->hDefaultFont);
8409   }
8410
8411   /* free listview info pointer*/
8412   COMCTL32_Free(infoPtr);
8413
8414   SetWindowLongA(hwnd, 0, 0);
8415   return 0;
8416 }
8417
8418 /***
8419  * DESCRIPTION:
8420  * Handles notifications from children.
8421  * 
8422  * PARAMETER(S):
8423  * [I] HWND : window handle
8424  * [I] INT : control identifier
8425  * [I] LPNMHDR : notification information
8426  * 
8427  * RETURN:
8428  * Zero
8429  */
8430 static LRESULT LISTVIEW_Notify(HWND hwnd, INT nCtrlId, LPNMHDR lpnmh)
8431 {
8432   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8433   
8434   if (lpnmh->hwndFrom == infoPtr->hwndHeader) 
8435   {
8436     /* handle notification from header control */
8437     if (lpnmh->code == HDN_ENDTRACKA)
8438     {
8439       infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8440       InvalidateRect(hwnd, NULL, TRUE);
8441     }
8442     else if(lpnmh->code ==  HDN_ITEMCLICKA)
8443     {
8444         /* Handle sorting by Header Column */
8445         NMLISTVIEW nmlv;
8446         LPNMHEADERA pnmHeader = (LPNMHEADERA) lpnmh;
8447         LONG lCtrlId = GetWindowLongA(hwnd, GWL_ID);
8448
8449         ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8450         nmlv.hdr.hwndFrom = hwnd;
8451         nmlv.hdr.idFrom = lCtrlId;
8452         nmlv.hdr.code = LVN_COLUMNCLICK;
8453         nmlv.iItem = -1;
8454         nmlv.iSubItem = pnmHeader->iItem;
8455         
8456         ListView_LVNotify(GetParent(hwnd),lCtrlId, &nmlv);
8457
8458     }
8459     else if(lpnmh->code == NM_RELEASEDCAPTURE)
8460     {
8461       /* Idealy this should be done in HDN_ENDTRACKA
8462        * but since SetItemBounds in Header.c is called after
8463        * the notification is sent, it is neccessary to handle the
8464        * update of the scroll bar here (Header.c works fine as it is,
8465        * no need to disturb it)
8466        */
8467       infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8468       LISTVIEW_UpdateScroll(hwnd);
8469       InvalidateRect(hwnd, NULL, TRUE);
8470     }
8471
8472   }
8473
8474   return 0;
8475 }
8476
8477 /***
8478  * DESCRIPTION:
8479  * Determines the type of structure to use.
8480  * 
8481  * PARAMETER(S):
8482  * [I] HWND : window handle of the sender
8483  * [I] HWND : listview window handle 
8484  * [I] INT : command specifying the nature of the WM_NOTIFYFORMAT
8485  *
8486  * RETURN:
8487  * Zero
8488  */
8489 static LRESULT LISTVIEW_NotifyFormat(HWND hwndFrom, HWND hwnd, INT nCommand)
8490 {
8491   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8492
8493   if (nCommand == NF_REQUERY)
8494   {
8495     /* determine the type of structure to use */
8496     infoPtr->notifyFormat = SendMessageA(hwndFrom, WM_NOTIFYFORMAT, 
8497                                          (WPARAM)hwnd, (LPARAM)NF_QUERY);
8498     if (infoPtr->notifyFormat == NFR_UNICODE)
8499     {
8500       FIXME("NO support for unicode structures");
8501     }
8502   }
8503
8504   return 0;
8505 }
8506
8507 /***
8508  * DESCRIPTION:
8509  * Paints/Repaints the listview control.
8510  * 
8511  * PARAMETER(S):
8512  * [I] HWND : window handle
8513  * [I] HDC : device context handle
8514  *
8515  * RETURN:
8516  * Zero
8517  */
8518 static LRESULT LISTVIEW_Paint(HWND hwnd, HDC hdc)
8519 {
8520   PAINTSTRUCT ps;
8521
8522    TRACE("(hwnd=%x,hdc=%x)\n", hwnd, hdc);
8523
8524   if (hdc == 0)
8525   {
8526     hdc = BeginPaint(hwnd, &ps);
8527     LISTVIEW_Refresh(hwnd, hdc);
8528     EndPaint(hwnd, &ps);
8529   }
8530   else
8531   {
8532     LISTVIEW_Refresh(hwnd, hdc);
8533   }
8534
8535   return 0;
8536 }
8537
8538 /***
8539  * DESCRIPTION:
8540  * Processes double click messages (right mouse button).
8541  * 
8542  * PARAMETER(S):
8543  * [I] HWND : window handle
8544  * [I] WORD : key flag
8545  * [I] WORD : x coordinate
8546  * [I] WORD : y coordinate
8547  *
8548  * RETURN:
8549  * Zero
8550  */
8551 static LRESULT LISTVIEW_RButtonDblClk(HWND hwnd, WORD wKey, WORD wPosX, 
8552                                       WORD wPosY)
8553 {
8554   INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
8555   NMHDR nmh;
8556
8557   TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8558
8559   /* send NM_RELEASEDCAPTURE notification */ 
8560   nmh.hwndFrom = hwnd;
8561   nmh.idFrom = nCtrlId;
8562   nmh.code = NM_RELEASEDCAPTURE;
8563   ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
8564
8565   /* send NM_RDBLCLK notification */
8566   nmh.code = NM_RDBLCLK;
8567   ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
8568
8569   return 0;
8570 }
8571
8572 /***
8573  * DESCRIPTION:
8574  * Processes mouse down messages (right mouse button).
8575  * 
8576  * PARAMETER(S):
8577  * [I] HWND : window handle
8578  * [I] WORD : key flag
8579  * [I] WORD : x coordinate
8580  * [I] WORD : y coordinate
8581  *
8582  * RETURN:
8583  * Zero
8584  */
8585 static LRESULT LISTVIEW_RButtonDown(HWND hwnd, WORD wKey, WORD wPosX, 
8586                                     WORD wPosY)
8587 {
8588   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8589   INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
8590   POINT ptPosition;
8591   NMHDR nmh;
8592   INT nItem;
8593
8594   TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8595
8596   /* send NM_RELEASEDCAPTURE notification */
8597   nmh.hwndFrom = hwnd;
8598   nmh.idFrom = nCtrlId;
8599   nmh.code = NM_RELEASEDCAPTURE;
8600   ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
8601  
8602   /* make sure the listview control window has the focus */
8603   if (infoPtr->bFocus == FALSE)
8604   {
8605     SetFocus(hwnd);
8606   }
8607
8608   /* set right button down flag */
8609   infoPtr->bRButtonDown = TRUE;
8610
8611   /* determine the index of the selected item */
8612   ptPosition.x = wPosX;
8613   ptPosition.y = wPosY;
8614   nItem = LISTVIEW_MouseSelection(hwnd, ptPosition);
8615   if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
8616   {
8617     if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)))
8618     {
8619       LISTVIEW_SetSelection(hwnd, nItem);
8620     }
8621   }
8622   else
8623   {
8624     LISTVIEW_RemoveAllSelections(hwnd);
8625   }
8626   
8627   return 0;
8628 }
8629
8630 /***
8631  * DESCRIPTION:
8632  * Processes mouse up messages (right mouse button).
8633  * 
8634  * PARAMETER(S):
8635  * [I] HWND : window handle
8636  * [I] WORD : key flag
8637  * [I] WORD : x coordinate
8638  * [I] WORD : y coordinate
8639  *
8640  * RETURN:
8641  * Zero
8642  */
8643 static LRESULT LISTVIEW_RButtonUp(HWND hwnd, WORD wKey, WORD wPosX, 
8644                                   WORD wPosY)
8645 {
8646   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8647   INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
8648
8649   TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8650
8651   if (infoPtr->bRButtonDown != FALSE) 
8652   {
8653     NMLISTVIEW nmlv;
8654     LVHITTESTINFO lvHitTestInfo;
8655     POINT pt;
8656     INT ret;
8657
8658     lvHitTestInfo.pt.x = wPosX;
8659     lvHitTestInfo.pt.y = wPosY;
8660
8661     /* Send NM_RClICK notification */
8662     ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8663     nmlv.hdr.hwndFrom = hwnd;
8664     nmlv.hdr.idFrom = nCtrlId;
8665     nmlv.hdr.code = NM_RCLICK;
8666     ret = LISTVIEW_HitTestItem(hwnd, &lvHitTestInfo, TRUE);
8667     if (ret != -1)
8668     {
8669         nmlv.iItem = lvHitTestInfo.iItem;
8670         nmlv.iSubItem = lvHitTestInfo.iSubItem;
8671     }
8672     else
8673     {
8674         nmlv.iItem = -1;
8675         nmlv.iSubItem = 0;
8676     }
8677     nmlv.ptAction.x = wPosX;
8678     nmlv.ptAction.y = wPosY;
8679     ListView_LVNotify(GetParent(hwnd), nCtrlId, &nmlv);
8680
8681     pt.x = wPosX;
8682     pt.y = wPosY;
8683
8684     /* set button flag */
8685     infoPtr->bRButtonDown = FALSE;
8686     
8687     /* Change to screen coordinate for WM_CONTEXTMENU */
8688     ClientToScreen(hwnd, &pt);
8689     
8690     /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
8691     SendMessageA( hwnd, WM_CONTEXTMENU, (WPARAM) hwnd, MAKELPARAM(pt.x, pt.y));
8692   }
8693   
8694   return 0;
8695 }
8696
8697 /***
8698  * DESCRIPTION:
8699  * Sets the focus.  
8700  * 
8701  * PARAMETER(S):
8702  * [I] HWND : window handle
8703  * [I] HWND : window handle of previously focused window
8704  *
8705  * RETURN:
8706  * Zero
8707  */
8708 static LRESULT LISTVIEW_SetFocus(HWND hwnd, HWND hwndLoseFocus)
8709 {
8710   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8711   INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
8712   NMHDR nmh;
8713
8714   TRACE("(hwnd=%x, hwndLoseFocus=%x)\n", hwnd, hwndLoseFocus);
8715
8716   /* send NM_SETFOCUS notification */
8717   nmh.hwndFrom = hwnd;
8718   nmh.idFrom = nCtrlId;
8719   nmh.code = NM_SETFOCUS;
8720   ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
8721
8722   /* set window focus flag */
8723   infoPtr->bFocus = TRUE;
8724
8725   UpdateWindow(hwnd);
8726
8727   return 0;
8728 }
8729
8730 /***
8731  * DESCRIPTION:
8732  * Sets the font.  
8733  * 
8734  * PARAMETER(S):
8735  * [I] HWND : window handle
8736  * [I] HFONT : font handle
8737  * [I] WORD : redraw flag
8738  *
8739  * RETURN:
8740  * Zero
8741  */
8742 static LRESULT LISTVIEW_SetFont(HWND hwnd, HFONT hFont, WORD fRedraw)
8743 {
8744   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8745   UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8746
8747   TRACE("(hwnd=%x,hfont=%x,redraw=%hu)\n", hwnd, hFont, fRedraw);
8748
8749   if (hFont == 0)
8750   {
8751     infoPtr->hFont = infoPtr->hDefaultFont;
8752   }
8753   else
8754   {
8755     infoPtr->hFont = hFont;
8756   }
8757
8758   if (uView == LVS_REPORT)
8759   {
8760     /* set header font */
8761     SendMessageA(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, 
8762                    MAKELPARAM(fRedraw, 0));
8763   }
8764
8765   /* invalidate listview control client area */
8766   InvalidateRect(hwnd, NULL, TRUE);
8767   
8768   if (fRedraw != FALSE)
8769   {
8770     UpdateWindow(hwnd);
8771   }
8772
8773   return 0;
8774 }
8775
8776 /***
8777  * DESCRIPTION:
8778  * Message handling for WM_SETREDRAW.  
8779  * For the Listview, it invalidates the entire window (the doc specifies otherwise)
8780  * 
8781  * PARAMETER(S):
8782  * [I] HWND   : window handle
8783  * [I] bRedraw: state of redraw flag
8784  *
8785  * RETURN:
8786  * DefWinProc return value
8787  */
8788 static LRESULT LISTVIEW_SetRedraw(HWND hwnd, BOOL bRedraw)
8789 {
8790     LRESULT lResult;
8791     lResult = DefWindowProcA(hwnd, WM_SETREDRAW, bRedraw, 0);
8792     if(bRedraw)
8793     {
8794         RedrawWindow(hwnd, NULL, 0, 
8795             RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ALLCHILDREN | RDW_ERASENOW);
8796     }
8797     return lResult;
8798 }
8799
8800 /***
8801  * DESCRIPTION:
8802  * Resizes the listview control. This function processes WM_SIZE
8803  * messages.  At this time, the width and height are not used.
8804  * 
8805  * PARAMETER(S):
8806  * [I] HWND : window handle
8807  * [I] WORD : new width
8808  * [I] WORD : new height
8809  *
8810  * RETURN:
8811  * Zero
8812  */
8813 static LRESULT LISTVIEW_Size(HWND hwnd, int Width, int Height)
8814 {
8815   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE); 
8816   UINT uView = lStyle & LVS_TYPEMASK;
8817
8818   TRACE("(hwnd=%x, width=%d, height=%d)\n",hwnd, Width, Height);
8819
8820   LISTVIEW_UpdateSize(hwnd);
8821
8822   if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
8823   {
8824     if (lStyle & LVS_ALIGNLEFT)
8825     {
8826       LISTVIEW_AlignLeft(hwnd);
8827     }
8828     else
8829     {
8830       LISTVIEW_AlignTop(hwnd);
8831     }
8832   }
8833
8834   LISTVIEW_UpdateScroll(hwnd);
8835   
8836   /* invalidate client area + erase background */
8837   InvalidateRect(hwnd, NULL, TRUE);
8838
8839   return 0;
8840 }
8841
8842 /***
8843  * DESCRIPTION:
8844  * Sets the size information.
8845  * 
8846  * PARAMETER(S):
8847  * [I] HWND : window handle
8848  *
8849  * RETURN:
8850  * Zero
8851  */
8852 static VOID LISTVIEW_UpdateSize(HWND hwnd)
8853 {
8854   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8855   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
8856   UINT uView = lStyle & LVS_TYPEMASK;
8857   RECT rcList;
8858   
8859   GetClientRect(hwnd, &rcList);
8860   infoPtr->rcList.left = 0;
8861   infoPtr->rcList.right = max(rcList.right - rcList.left, 1);
8862   infoPtr->rcList.top = 0;
8863   infoPtr->rcList.bottom = max(rcList.bottom - rcList.top, 1);
8864      
8865   if (uView == LVS_LIST)
8866   {
8867     if ((lStyle & WS_HSCROLL) == 0)
8868     {
8869       INT nHScrollHeight = GetSystemMetrics(SM_CYHSCROLL);
8870       if (infoPtr->rcList.bottom > nHScrollHeight)
8871       { 
8872         infoPtr->rcList.bottom -= nHScrollHeight;
8873       }
8874     }
8875   }
8876   else if (uView == LVS_REPORT)
8877   {
8878     HDLAYOUT hl;
8879     WINDOWPOS wp;
8880
8881     hl.prc = &rcList;
8882     hl.pwpos = &wp;
8883     Header_Layout(infoPtr->hwndHeader, &hl);
8884
8885     SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8886
8887     if (!(LVS_NOCOLUMNHEADER & lStyle))
8888     {
8889       infoPtr->rcList.top = max(wp.cy, 0);
8890     }
8891   }
8892 }
8893
8894 /***
8895  * DESCRIPTION:
8896  * Processes WM_STYLECHANGED messages. 
8897  * 
8898  * PARAMETER(S):
8899  * [I] HWND : window handle
8900  * [I] WPARAM : window style type (normal or extended)
8901  * [I] LPSTYLESTRUCT : window style information
8902  *
8903  * RETURN:
8904  * Zero
8905  */
8906 static INT LISTVIEW_StyleChanged(HWND hwnd, WPARAM wStyleType, 
8907                                  LPSTYLESTRUCT lpss)
8908 {
8909   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8910   UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
8911   UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
8912   RECT rcList = infoPtr->rcList;
8913
8914   TRACE("(hwnd=%x, styletype=%x, stylestruct=%p)\n", 
8915         hwnd, wStyleType, lpss);
8916
8917   if (wStyleType == GWL_STYLE)
8918   {
8919     if (uOldView == LVS_REPORT)
8920     {
8921       ShowWindow(infoPtr->hwndHeader, SW_HIDE);
8922     }
8923  
8924     if ((lpss->styleOld & WS_HSCROLL) != 0)
8925     {
8926        ShowScrollBar(hwnd, SB_HORZ, FALSE);
8927     }
8928  
8929     if ((lpss->styleOld & WS_VSCROLL) != 0)
8930     {
8931        ShowScrollBar(hwnd, SB_VERT, FALSE);
8932     }
8933  
8934     if (uNewView == LVS_ICON)
8935     {
8936       infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
8937       infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
8938       infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8939       infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
8940       if (lpss->styleNew & LVS_ALIGNLEFT)
8941       {
8942         LISTVIEW_AlignLeft(hwnd);
8943       }
8944       else
8945       {
8946         LISTVIEW_AlignTop(hwnd);
8947       }
8948     }
8949     else if (uNewView == LVS_REPORT)
8950     {
8951       HDLAYOUT hl;
8952       WINDOWPOS wp;
8953
8954       hl.prc = &rcList;
8955       hl.pwpos = &wp;
8956       Header_Layout(infoPtr->hwndHeader, &hl);
8957       SetWindowPos(infoPtr->hwndHeader, hwnd, wp.x, wp.y, wp.cx, wp.cy, 
8958                    wp.flags);
8959       if (!(LVS_NOCOLUMNHEADER & lpss->styleNew))
8960         ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
8961       
8962       infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8963       infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8964       infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8965       infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
8966     }
8967     else if (uNewView == LVS_LIST)
8968     {
8969       infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8970       infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8971       infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8972       infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
8973     }
8974     else
8975     {
8976       infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8977       infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8978       infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8979       infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
8980       if (lpss->styleNew & LVS_ALIGNLEFT)
8981       {
8982         LISTVIEW_AlignLeft(hwnd);
8983       }
8984       else
8985       {
8986         LISTVIEW_AlignTop(hwnd);
8987       }
8988     }
8989
8990     /* update the size of the client area */
8991     LISTVIEW_UpdateSize(hwnd);
8992
8993     /* add scrollbars if needed */
8994     LISTVIEW_UpdateScroll(hwnd);
8995     
8996     /* invalidate client area + erase background */
8997     InvalidateRect(hwnd, NULL, TRUE);
8998
8999     /* print the list of unsupported window styles */
9000     LISTVIEW_UnsupportedStyles(lpss->styleNew);
9001   }
9002
9003   /* If they change the view and we have an active edit control 
9004      we will need to kill the control since the redraw will
9005      misplace the edit control.
9006    */
9007   if (infoPtr->hwndEdit &&
9008         ((uNewView & (LVS_ICON|LVS_LIST|LVS_SMALLICON)) !=
9009         ((LVS_ICON|LVS_LIST|LVS_SMALLICON) & uOldView)))
9010   {
9011      SendMessageA(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
9012   }
9013
9014   return 0;
9015 }
9016
9017 /***
9018  * DESCRIPTION:
9019  * Window procedure of the listview control.
9020  *
9021  */
9022 static LRESULT WINAPI LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam,
9023                                    LPARAM lParam)
9024 {
9025   TRACE("hwnd=%x uMsg=%x wParam=%x lParam=%lx\n", hwnd, uMsg, wParam, lParam);
9026   if (!GetWindowLongA(hwnd, 0) && (uMsg != WM_NCCREATE))
9027     return DefWindowProcA( hwnd, uMsg, wParam, lParam );
9028   switch (uMsg)
9029   {
9030   case LVM_APPROXIMATEVIEWRECT: 
9031     return LISTVIEW_ApproximateViewRect(hwnd, (INT)wParam, 
9032                                         LOWORD(lParam), HIWORD(lParam));
9033   case LVM_ARRANGE: 
9034     return LISTVIEW_Arrange(hwnd, (INT)wParam);
9035
9036 /* case LVM_CREATEDRAGIMAGE: */
9037
9038   case LVM_DELETEALLITEMS:
9039     return LISTVIEW_DeleteAllItems(hwnd);
9040
9041   case LVM_DELETECOLUMN:
9042     return LISTVIEW_DeleteColumn(hwnd, (INT)wParam);
9043
9044   case LVM_DELETEITEM:
9045     return LISTVIEW_DeleteItem(hwnd, (INT)wParam);
9046
9047   case LVM_EDITLABELW:
9048   case LVM_EDITLABELA:
9049     return LISTVIEW_EditLabelA(hwnd, (INT)wParam);
9050
9051   case LVM_ENSUREVISIBLE:
9052     return LISTVIEW_EnsureVisible(hwnd, (INT)wParam, (BOOL)lParam);
9053
9054   case LVM_FINDITEMA:
9055     return LISTVIEW_FindItem(hwnd, (INT)wParam, (LPLVFINDINFO)lParam);
9056
9057   case LVM_GETBKCOLOR:
9058     return LISTVIEW_GetBkColor(hwnd);
9059
9060 /*      case LVM_GETBKIMAGE: */
9061
9062   case LVM_GETCALLBACKMASK:
9063     return LISTVIEW_GetCallbackMask(hwnd);
9064
9065   case LVM_GETCOLUMNA:
9066     return LISTVIEW_GetColumnA(hwnd, (INT)wParam, (LPLVCOLUMNA)lParam);
9067
9068 /*      case LVM_GETCOLUMNW: */
9069
9070   case LVM_GETCOLUMNORDERARRAY:
9071     return LISTVIEW_GetColumnOrderArray(hwnd, (INT)wParam, (LPINT)lParam);
9072
9073   case LVM_GETCOLUMNWIDTH:
9074     return LISTVIEW_GetColumnWidth(hwnd, (INT)wParam);
9075
9076   case LVM_GETCOUNTPERPAGE:
9077     return LISTVIEW_GetCountPerPage(hwnd);
9078
9079   case LVM_GETEDITCONTROL:
9080     return LISTVIEW_GetEditControl(hwnd);
9081
9082   case LVM_GETEXTENDEDLISTVIEWSTYLE:
9083     return LISTVIEW_GetExtendedListViewStyle(hwnd);
9084
9085   case LVM_GETHEADER:
9086     return LISTVIEW_GetHeader(hwnd);
9087
9088 /*      case LVM_GETHOTCURSOR: */
9089
9090   case LVM_GETHOTITEM:
9091     return LISTVIEW_GetHotItem(hwnd);
9092
9093   case LVM_GETHOVERTIME:
9094     return LISTVIEW_GetHoverTime(hwnd);
9095
9096   case LVM_GETIMAGELIST:
9097     return LISTVIEW_GetImageList(hwnd, (INT)wParam);
9098
9099 /*      case LVM_GETISEARCHSTRING: */
9100
9101   case LVM_GETITEMA:
9102     return LISTVIEW_GetItemA(hwnd, (LPLVITEMA)lParam, FALSE);
9103
9104 /*      case LVM_GETITEMW: */
9105
9106   case LVM_GETITEMCOUNT:
9107     return LISTVIEW_GetItemCount(hwnd);
9108
9109   case LVM_GETITEMPOSITION:
9110     return LISTVIEW_GetItemPosition(hwnd, (INT)wParam, (LPPOINT)lParam);
9111
9112   case LVM_GETITEMRECT: 
9113     return LISTVIEW_GetItemRect(hwnd, (INT)wParam, (LPRECT)lParam);
9114
9115   case LVM_GETITEMSPACING: 
9116     return LISTVIEW_GetItemSpacing(hwnd, (BOOL)wParam);
9117
9118   case LVM_GETITEMSTATE: 
9119     return LISTVIEW_GetItemState(hwnd, (INT)wParam, (UINT)lParam);
9120     
9121   case LVM_GETITEMTEXTA:
9122     LISTVIEW_GetItemTextA(hwnd, (INT)wParam, (LPLVITEMA)lParam);
9123     break;
9124
9125 /*      case LVM_GETITEMTEXTW: */
9126
9127   case LVM_GETNEXTITEM:
9128     return LISTVIEW_GetNextItem(hwnd, (INT)wParam, LOWORD(lParam));
9129
9130 /*      case LVM_GETNUMBEROFWORKAREAS: */
9131
9132   case LVM_GETORIGIN:
9133     return LISTVIEW_GetOrigin(hwnd, (LPPOINT)lParam);
9134
9135   case LVM_GETSELECTEDCOUNT:
9136     return LISTVIEW_GetSelectedCount(hwnd);
9137
9138   case LVM_GETSELECTIONMARK: 
9139     return LISTVIEW_GetSelectionMark(hwnd);
9140
9141   case LVM_GETSTRINGWIDTHA:
9142     return LISTVIEW_GetStringWidthA (hwnd, (LPCSTR)lParam);
9143
9144 /*      case LVM_GETSTRINGWIDTHW: */
9145 /*      case LVM_GETSUBITEMRECT: */
9146
9147   case LVM_GETTEXTBKCOLOR:
9148     return LISTVIEW_GetTextBkColor(hwnd);
9149
9150   case LVM_GETTEXTCOLOR:
9151     return LISTVIEW_GetTextColor(hwnd);
9152
9153 /*      case LVM_GETTOOLTIPS: */
9154
9155   case LVM_GETTOPINDEX:
9156     return LISTVIEW_GetTopIndex(hwnd);
9157
9158 /*      case LVM_GETUNICODEFORMAT: */
9159
9160   case LVM_GETVIEWRECT:
9161     return LISTVIEW_GetViewRect(hwnd, (LPRECT)lParam);
9162
9163 /*      case LVM_GETWORKAREAS: */
9164
9165   case LVM_HITTEST:
9166     return LISTVIEW_HitTest(hwnd, (LPLVHITTESTINFO)lParam);
9167
9168   case LVM_INSERTCOLUMNA:
9169     return LISTVIEW_InsertColumnA(hwnd, (INT)wParam, (LPLVCOLUMNA)lParam);
9170
9171   case LVM_INSERTCOLUMNW:
9172     return LISTVIEW_InsertColumnW(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam);
9173
9174   case LVM_INSERTITEMA:
9175     return LISTVIEW_InsertItemA(hwnd, (LPLVITEMA)lParam);
9176
9177   case LVM_INSERTITEMW:
9178     return LISTVIEW_InsertItemW(hwnd, (LPLVITEMW)lParam);
9179
9180   case LVM_REDRAWITEMS:
9181     return LISTVIEW_RedrawItems(hwnd, (INT)wParam, (INT)lParam);
9182
9183 /*   case LVM_SCROLL:  */
9184 /*     return LISTVIEW_Scroll(hwnd, (INT)wParam, (INT)lParam); */
9185
9186   case LVM_SETBKCOLOR:
9187     return LISTVIEW_SetBkColor(hwnd, (COLORREF)lParam);
9188
9189 /*      case LVM_SETBKIMAGE: */
9190
9191   case LVM_SETCALLBACKMASK:
9192     return LISTVIEW_SetCallbackMask(hwnd, (UINT)wParam);
9193
9194   case LVM_SETCOLUMNA:
9195     return LISTVIEW_SetColumnA(hwnd, (INT)wParam, (LPLVCOLUMNA)lParam);
9196
9197   case LVM_SETCOLUMNW:
9198     FIXME("Unimplemented msg LVM_SETCOLUMNW\n");
9199     return 0;
9200
9201   case LVM_SETCOLUMNORDERARRAY:
9202     return LISTVIEW_SetColumnOrderArray(hwnd, (INT)wParam, (LPINT)lParam);
9203
9204   case LVM_SETCOLUMNWIDTH:
9205     return LISTVIEW_SetColumnWidth(hwnd, (INT)wParam, SLOWORD(lParam));
9206
9207   case LVM_SETEXTENDEDLISTVIEWSTYLE:
9208     return LISTVIEW_SetExtendedListViewStyle(hwnd, (DWORD)wParam, (DWORD)lParam);
9209
9210 /*      case LVM_SETHOTCURSOR: */
9211
9212   case LVM_SETHOTITEM:
9213     return LISTVIEW_SetHotItem(hwnd, (INT)wParam);
9214
9215   case LVM_SETHOVERTIME:
9216     return LISTVIEW_SetHoverTime(hwnd, (DWORD)wParam);
9217
9218 /*      case LVM_SETICONSPACING: */
9219         
9220   case LVM_SETIMAGELIST:
9221     return LISTVIEW_SetImageList(hwnd, (INT)wParam, (HIMAGELIST)lParam);
9222
9223   case LVM_SETITEMA:
9224     return LISTVIEW_SetItemA(hwnd, (LPLVITEMA)lParam);
9225
9226 /*      case LVM_SETITEMW: */
9227
9228   case LVM_SETITEMCOUNT: 
9229     return LISTVIEW_SetItemCount(hwnd, (INT)wParam, (DWORD)lParam);
9230     
9231   case LVM_SETITEMPOSITION:
9232     return LISTVIEW_SetItemPosition(hwnd, (INT)wParam, (INT)LOWORD(lParam),
9233                                     (INT)HIWORD(lParam));
9234
9235   case LVM_SETITEMPOSITION32:
9236     return LISTVIEW_SetItemPosition(hwnd, (INT)wParam, ((POINT*)lParam)->x,
9237                                     ((POINT*)lParam)->y);
9238
9239   case LVM_SETITEMSTATE:
9240     return LISTVIEW_SetItemState(hwnd, (INT)wParam, (LPLVITEMA)lParam);
9241
9242   case LVM_SETITEMTEXTA:
9243     return LISTVIEW_SetItemTextA(hwnd, (INT)wParam, (LPLVITEMA)lParam);
9244
9245 /*      case LVM_SETITEMTEXTW: */
9246
9247   case LVM_SETSELECTIONMARK:
9248     return LISTVIEW_SetSelectionMark(hwnd, (INT)lParam);
9249
9250   case LVM_SETTEXTBKCOLOR:
9251     return LISTVIEW_SetTextBkColor(hwnd, (COLORREF)lParam);
9252
9253   case LVM_SETTEXTCOLOR:
9254     return LISTVIEW_SetTextColor(hwnd, (COLORREF)lParam);
9255
9256 /*      case LVM_SETTOOLTIPS: */
9257 /*      case LVM_SETUNICODEFORMAT: */
9258 /*      case LVM_SETWORKAREAS: */
9259
9260   case LVM_SORTITEMS:
9261     return LISTVIEW_SortItems(hwnd, wParam, lParam);
9262
9263 /*      case LVM_SUBITEMHITTEST: */
9264
9265   case LVM_UPDATE: 
9266     return LISTVIEW_Update(hwnd, (INT)wParam);
9267
9268 /*      case WM_CHAR: */
9269   case WM_CHAR:
9270     return LISTVIEW_ProcessLetterKeys( hwnd, wParam, lParam );
9271   
9272   case WM_COMMAND:
9273     return LISTVIEW_Command(hwnd, wParam, lParam);
9274
9275   case WM_CREATE:
9276     return LISTVIEW_Create(hwnd, wParam, lParam);
9277     
9278   case WM_ERASEBKGND:
9279     return LISTVIEW_EraseBackground(hwnd, wParam, lParam);
9280
9281   case WM_GETDLGCODE:
9282     return DLGC_WANTCHARS | DLGC_WANTARROWS;
9283
9284   case WM_GETFONT:
9285     return LISTVIEW_GetFont(hwnd);
9286
9287   case WM_HSCROLL:
9288     return LISTVIEW_HScroll(hwnd, (INT)LOWORD(wParam), 
9289                             (INT)HIWORD(wParam), (HWND)lParam);
9290
9291   case WM_KEYDOWN:
9292     return LISTVIEW_KeyDown(hwnd, (INT)wParam, (LONG)lParam);
9293
9294   case WM_KILLFOCUS:
9295     return LISTVIEW_KillFocus(hwnd);
9296
9297   case WM_LBUTTONDBLCLK:
9298     return LISTVIEW_LButtonDblClk(hwnd, (WORD)wParam, LOWORD(lParam), 
9299                                 HIWORD(lParam));
9300     
9301   case WM_LBUTTONDOWN:
9302     return LISTVIEW_LButtonDown(hwnd, (WORD)wParam, LOWORD(lParam), 
9303                                 HIWORD(lParam));
9304   case WM_LBUTTONUP:
9305     return LISTVIEW_LButtonUp(hwnd, (WORD)wParam, LOWORD(lParam), 
9306                               HIWORD(lParam));
9307   case WM_MOUSEMOVE:
9308     return LISTVIEW_MouseMove (hwnd, wParam, lParam);
9309
9310   case WM_MOUSEHOVER:
9311     return LISTVIEW_MouseHover(hwnd, wParam, lParam);
9312
9313   case WM_NCCREATE:
9314     return LISTVIEW_NCCreate(hwnd, wParam, lParam);
9315
9316   case WM_NCDESTROY:
9317     return LISTVIEW_NCDestroy(hwnd);
9318
9319   case WM_NOTIFY:
9320     return LISTVIEW_Notify(hwnd, (INT)wParam, (LPNMHDR)lParam);
9321
9322   case WM_NOTIFYFORMAT:
9323     return LISTVIEW_NotifyFormat(hwnd, (HWND)wParam, (INT)lParam);
9324
9325   case WM_PAINT: 
9326     return LISTVIEW_Paint(hwnd, (HDC)wParam); 
9327
9328   case WM_RBUTTONDBLCLK:
9329     return LISTVIEW_RButtonDblClk(hwnd, (WORD)wParam, LOWORD(lParam), 
9330                                   HIWORD(lParam));
9331
9332   case WM_RBUTTONDOWN:
9333     return LISTVIEW_RButtonDown(hwnd, (WORD)wParam, LOWORD(lParam), 
9334                                 HIWORD(lParam));
9335
9336   case WM_RBUTTONUP:
9337     return LISTVIEW_RButtonUp(hwnd, (WORD)wParam, LOWORD(lParam), 
9338                               HIWORD(lParam));
9339
9340   case WM_SETFOCUS:
9341     return LISTVIEW_SetFocus(hwnd, (HWND)wParam);
9342
9343   case WM_SETFONT:
9344     return LISTVIEW_SetFont(hwnd, (HFONT)wParam, (WORD)lParam);
9345
9346   case WM_SETREDRAW: 
9347     return LISTVIEW_SetRedraw(hwnd, (BOOL)wParam);
9348
9349   case WM_SIZE:
9350     return LISTVIEW_Size(hwnd, (int)SLOWORD(lParam), (int)SHIWORD(lParam));
9351
9352   case WM_STYLECHANGED:
9353     return LISTVIEW_StyleChanged(hwnd, wParam, (LPSTYLESTRUCT)lParam);
9354
9355 /*      case WM_TIMER: */
9356
9357   case WM_VSCROLL:
9358     return LISTVIEW_VScroll(hwnd, (INT)LOWORD(wParam), 
9359                             (INT)HIWORD(wParam), (HWND)lParam);
9360
9361   case WM_MOUSEWHEEL:
9362       if (wParam & (MK_SHIFT | MK_CONTROL))
9363           return DefWindowProcA( hwnd, uMsg, wParam, lParam );
9364       return LISTVIEW_MouseWheel(hwnd, (short int)HIWORD(wParam));/*    case WM_WINDOWPOSCHANGED: */
9365
9366 /*      case WM_WININICHANGE: */
9367
9368   default:
9369     if (uMsg >= WM_USER)
9370     {
9371       ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, 
9372           lParam);
9373     }
9374
9375     /* call default window procedure */
9376     return DefWindowProcA(hwnd, uMsg, wParam, lParam);
9377   }
9378
9379   return 0;
9380 }
9381
9382 /***
9383  * DESCRIPTION:
9384  * Registers the window class.
9385  * 
9386  * PARAMETER(S):
9387  * None
9388  *
9389  * RETURN:
9390  * None
9391  */
9392 VOID LISTVIEW_Register(void)
9393 {
9394   WNDCLASSA wndClass;
9395
9396     ZeroMemory(&wndClass, sizeof(WNDCLASSA));
9397     wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
9398     wndClass.lpfnWndProc = (WNDPROC)LISTVIEW_WindowProc;
9399     wndClass.cbClsExtra = 0;
9400     wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
9401     wndClass.hCursor = LoadCursorA(0, IDC_ARROWA);
9402     wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
9403     wndClass.lpszClassName = WC_LISTVIEWA;
9404     RegisterClassA(&wndClass);
9405 }
9406
9407 /***
9408  * DESCRIPTION:
9409  * Unregisters the window class.
9410  * 
9411  * PARAMETER(S):
9412  * None
9413  *
9414  * RETURN:
9415  * None
9416  */
9417 VOID LISTVIEW_Unregister(void)
9418 {
9419     UnregisterClassA(WC_LISTVIEWA, (HINSTANCE)NULL);
9420 }
9421
9422 /***
9423  * DESCRIPTION:
9424  * Handle any WM_COMMAND messages
9425  * 
9426  * PARAMETER(S):
9427  *
9428  * RETURN:
9429  */
9430 static LRESULT LISTVIEW_Command(HWND hwnd, WPARAM wParam, LPARAM lParam)
9431 {
9432     switch (HIWORD(wParam))
9433     {
9434         case EN_UPDATE:
9435         {
9436             /* 
9437              * Adjust the edit window size 
9438              */
9439             char buffer[1024];
9440             LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
9441             HDC           hdc      = GetDC(infoPtr->hwndEdit);
9442             HFONT         hFont, hOldFont = 0;
9443             RECT          rect;
9444             SIZE          sz;
9445             int           len;
9446
9447             len = GetWindowTextA(infoPtr->hwndEdit, buffer, 1023);
9448             GetWindowRect(infoPtr->hwndEdit, &rect);
9449
9450             /* Select font to get the right dimension of the string */
9451             hFont = SendMessageA(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
9452             if(hFont != 0)
9453             {
9454                 hOldFont = SelectObject(hdc, hFont);
9455             }
9456
9457             if (GetTextExtentPoint32A(hdc, buffer, strlen(buffer), &sz))
9458             {
9459                 TEXTMETRICA textMetric;
9460
9461                 /* Add Extra spacing for the next character */
9462                 GetTextMetricsA(hdc, &textMetric);
9463                 sz.cx += (textMetric.tmMaxCharWidth * 2);
9464
9465                 SetWindowPos ( 
9466                     infoPtr->hwndEdit,
9467                     HWND_TOP, 
9468                     0, 
9469                     0,
9470                     sz.cx,
9471                     rect.bottom - rect.top,
9472                     SWP_DRAWFRAME|SWP_NOMOVE);
9473             }
9474             if(hFont != 0)
9475             {
9476                 SelectObject(hdc, hOldFont);
9477             }
9478
9479             ReleaseDC(hwnd, hdc);
9480
9481             break;
9482         }
9483
9484         default:
9485           return SendMessageA (GetParent (hwnd), WM_COMMAND, wParam, lParam);
9486     }
9487
9488     return 0;
9489 }
9490
9491
9492 /***
9493  * DESCRIPTION:
9494  * Subclassed edit control windproc function
9495  *
9496  * PARAMETER(S):
9497  *
9498  * RETURN:
9499  */
9500 LRESULT CALLBACK EditLblWndProc(HWND hwnd, UINT uMsg, 
9501         WPARAM wParam, LPARAM lParam)
9502 {
9503     BOOL cancel = FALSE;
9504     LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(GetParent(hwnd), 0);
9505     EDITLABEL_ITEM *einfo = infoPtr->pedititem;
9506     static BOOL bIgnoreKillFocus = FALSE;
9507     switch (uMsg)
9508     {
9509         case WM_GETDLGCODE:
9510           return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
9511                         
9512         case WM_KILLFOCUS:
9513             if(bIgnoreKillFocus)
9514             {
9515                 return TRUE;
9516             }
9517             break;
9518
9519         case WM_DESTROY:
9520         {
9521             WNDPROC editProc = einfo->EditWndProc;
9522             SetWindowLongA(hwnd, GWL_WNDPROC, (LONG)editProc);
9523             COMCTL32_Free(einfo);
9524             infoPtr->pedititem = NULL;
9525             return CallWindowProcA(editProc, hwnd, uMsg, wParam, lParam);
9526         }
9527
9528         case WM_KEYDOWN:
9529             if (VK_ESCAPE == (INT)wParam)
9530             {
9531                 cancel = TRUE;
9532                 break;
9533
9534             }
9535             else if (VK_RETURN == (INT)wParam)
9536                 break;
9537
9538         default:
9539             return CallWindowProcA(einfo->EditWndProc, hwnd, 
9540                         uMsg, wParam, lParam);
9541     }
9542
9543     if (einfo->EditLblCb)
9544     {
9545         char *buffer  = NULL;
9546         
9547
9548         if (!cancel)
9549         {
9550             int len = 1 + GetWindowTextLengthA(hwnd);
9551
9552             if (len > 1)
9553             {
9554                 if (NULL != (buffer = (char *)COMCTL32_Alloc(len*sizeof(char))))
9555                 {
9556                     GetWindowTextA(hwnd, buffer, len);
9557                 }
9558             }
9559         }
9560         /* Processing LVN_ENDLABELEDIT message could kill the focus       */
9561         /* eg. Using a messagebox                                         */
9562         bIgnoreKillFocus = TRUE;
9563         einfo->EditLblCb(GetParent(hwnd), buffer, einfo->param);
9564
9565         if (buffer)
9566             COMCTL32_Free(buffer);
9567
9568         einfo->EditLblCb = NULL;
9569         bIgnoreKillFocus = FALSE;
9570     }
9571
9572     SendMessageA(hwnd, WM_CLOSE, 0, 0);
9573     return TRUE;
9574 }
9575
9576
9577 /***
9578  * DESCRIPTION:
9579  * Creates a subclassed edit cotrol
9580  *
9581  * PARAMETER(S):
9582  *
9583  * RETURN:
9584  */
9585 HWND CreateEditLabel(LPCSTR text, DWORD style, INT x, INT y, 
9586         INT width, INT height, HWND parent, HINSTANCE hinst, 
9587         EditlblCallback EditLblCb, DWORD param)
9588 {
9589     HWND hedit;
9590     SIZE sz;
9591     HDC hdc;
9592     HDC hOldFont=0;
9593     TEXTMETRICA textMetric;
9594     LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(parent, 0);
9595
9596     if (NULL == (infoPtr->pedititem = COMCTL32_Alloc(sizeof(EDITLABEL_ITEM))))
9597         return 0;
9598
9599     style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|WS_BORDER;
9600     hdc = GetDC(parent);
9601
9602     /* Select the font to get appropriate metric dimensions */
9603     if(infoPtr->hFont != 0)
9604     {
9605         hOldFont = SelectObject(hdc, infoPtr->hFont);
9606     }
9607
9608     /*Get String Lenght in pixels */
9609     GetTextExtentPoint32A(hdc, text, strlen(text), &sz);
9610
9611     /*Add Extra spacing for the next character */
9612     GetTextMetricsA(hdc, &textMetric);
9613     sz.cx += (textMetric.tmMaxCharWidth * 2);
9614
9615     if(infoPtr->hFont != 0)
9616     {
9617         SelectObject(hdc, hOldFont);
9618     }
9619
9620     ReleaseDC(parent, hdc);
9621     if (!(hedit = CreateWindowA("Edit", text, style, x, y, sz.cx, height, 
9622                     parent, 0, hinst, 0)))
9623     {
9624         COMCTL32_Free(infoPtr->pedititem);
9625         return 0;
9626     }
9627
9628     infoPtr->pedititem->param = param;
9629     infoPtr->pedititem->EditLblCb = EditLblCb;
9630     infoPtr->pedititem->EditWndProc = (WNDPROC)SetWindowLongA(hedit, 
9631           GWL_WNDPROC, (LONG) EditLblWndProc);
9632
9633     SendMessageA(hedit, WM_SETFONT, infoPtr->hFont, FALSE);
9634
9635     return hedit;
9636 }