xref: /reactos/dll/win32/comctl32/listview.c (revision 0707475f)
1 /*
2  * Listview control
3  *
4  * Copyright 1998, 1999 Eric Kohl
5  * Copyright 1999 Luc Tourangeau
6  * Copyright 2000 Jason Mawdsley
7  * Copyright 2001 CodeWeavers Inc.
8  * Copyright 2002 Dimitrie O. Paun
9  * Copyright 2009-2015 Nikolay Sivov
10  * Copyright 2009 Owen Rudge for CodeWeavers
11  * Copyright 2012-2013 Daniel Jelinski
12  *
13  * This library is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU Lesser General Public
15  * License as published by the Free Software Foundation; either
16  * version 2.1 of the License, or (at your option) any later version.
17  *
18  * This library is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21  * Lesser General Public License for more details.
22  *
23  * You should have received a copy of the GNU Lesser General Public
24  * License along with this library; if not, write to the Free Software
25  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26  *
27  * TODO:
28  *
29  * Default Message Processing
30  *   -- WM_CREATE: create the icon and small icon image lists at this point only if
31  *      the LVS_SHAREIMAGELISTS style is not specified.
32  *   -- WM_WINDOWPOSCHANGED: arrange the list items if the current view is icon
33  *      or small icon and the LVS_AUTOARRANGE style is specified.
34  *   -- WM_TIMER
35  *   -- WM_WININICHANGE
36  *
37  * Features
38  *   -- Hot item handling, mouse hovering
39  *   -- Workareas support
40  *   -- Tilemode support
41  *   -- Groups support
42  *
43  * Bugs
44  *   -- Expand large item in ICON mode when the cursor is flying over the icon or text.
45  *   -- Support CustomDraw options for _WIN32_IE >= 0x560 (see NMLVCUSTOMDRAW docs).
46  *   -- LVA_SNAPTOGRID not implemented
47  *   -- LISTVIEW_ApproximateViewRect partially implemented
48  *   -- LISTVIEW_StyleChanged doesn't handle some changes too well
49  *
50  * Speedups
51  *   -- LISTVIEW_GetNextItem needs to be rewritten. It is currently
52  *      linear in the number of items in the list, and this is
53  *      unacceptable for large lists.
54  *   -- if list is sorted by item text LISTVIEW_InsertItemT could use
55  *      binary search to calculate item index (e.g. DPA_Search()).
56  *      This requires sorted state to be reliably tracked in item modifiers.
57  *   -- we should keep an ordered array of coordinates in iconic mode.
58  *      This would allow framing items (iterator_frameditems),
59  *      and finding the nearest item (LVFI_NEARESTXY) a lot more efficiently.
60  *
61  * Flags
62  *   -- LVIF_COLUMNS
63  *   -- LVIF_GROUPID
64  *
65  * States
66  *   -- LVIS_ACTIVATING (not currently supported by comctl32.dll version 6.0)
67  *   -- LVIS_DROPHILITED
68  *
69  * Styles
70  *   -- LVS_NOLABELWRAP
71  *   -- LVS_NOSCROLL (see Q137520)
72  *   -- LVS_ALIGNTOP
73  *
74  * Extended Styles
75  *   -- LVS_EX_BORDERSELECT
76  *   -- LVS_EX_FLATSB
77  *   -- LVS_EX_INFOTIP
78  *   -- LVS_EX_LABELTIP
79  *   -- LVS_EX_MULTIWORKAREAS
80  *   -- LVS_EX_REGIONAL
81  *   -- LVS_EX_SIMPLESELECT
82  *   -- LVS_EX_TWOCLICKACTIVATE
83  *   -- LVS_EX_UNDERLINECOLD
84  *   -- LVS_EX_UNDERLINEHOT
85  *
86  * Notifications:
87  *   -- LVN_BEGINSCROLL, LVN_ENDSCROLL
88  *   -- LVN_GETINFOTIP
89  *   -- LVN_HOTTRACK
90  *   -- LVN_SETDISPINFO
91  *
92  * Messages:
93  *   -- LVM_ENABLEGROUPVIEW
94  *   -- LVM_GETBKIMAGE, LVM_SETBKIMAGE
95  *   -- LVM_GETGROUPINFO, LVM_SETGROUPINFO
96  *   -- LVM_GETGROUPMETRICS, LVM_SETGROUPMETRICS
97  *   -- LVM_GETINSERTMARK, LVM_SETINSERTMARK
98  *   -- LVM_GETINSERTMARKCOLOR, LVM_SETINSERTMARKCOLOR
99  *   -- LVM_GETINSERTMARKRECT
100  *   -- LVM_GETNUMBEROFWORKAREAS
101  *   -- LVM_GETOUTLINECOLOR, LVM_SETOUTLINECOLOR
102  *   -- LVM_GETSELECTEDCOLUMN, LVM_SETSELECTEDCOLUMN
103  *   -- LVM_GETISEARCHSTRINGW, LVM_GETISEARCHSTRINGA
104  *   -- LVM_GETTILEINFO, LVM_SETTILEINFO
105  *   -- LVM_GETTILEVIEWINFO, LVM_SETTILEVIEWINFO
106  *   -- LVM_GETWORKAREAS, LVM_SETWORKAREAS
107  *   -- LVM_HASGROUP, LVM_INSERTGROUP, LVM_REMOVEGROUP, LVM_REMOVEALLGROUPS
108  *   -- LVM_INSERTGROUPSORTED
109  *   -- LVM_INSERTMARKHITTEST
110  *   -- LVM_ISGROUPVIEWENABLED
111  *   -- LVM_MOVEGROUP
112  *   -- LVM_MOVEITEMTOGROUP
113  *   -- LVM_SETINFOTIP
114  *   -- LVM_SETTILEWIDTH
115  *   -- LVM_SORTGROUPS
116  *
117  * Macros:
118  *   -- ListView_GetHoverTime, ListView_SetHoverTime
119  *   -- ListView_GetISearchString
120  *   -- ListView_GetNumberOfWorkAreas
121  *   -- ListView_GetWorkAreas, ListView_SetWorkAreas
122  *
123  * Functions:
124  *   -- LVGroupComparE
125  */
126 
127 #include <assert.h>
128 #include <ctype.h>
129 #include <string.h>
130 #include <stdlib.h>
131 #include <stdarg.h>
132 #include <stdio.h>
133 
134 #include "windef.h"
135 #include "winbase.h"
136 #include "winnt.h"
137 #include "wingdi.h"
138 #include "winuser.h"
139 #include "winnls.h"
140 #include "commctrl.h"
141 #include "comctl32.h"
142 #include "uxtheme.h"
143 
144 #include "wine/debug.h"
145 
146 WINE_DEFAULT_DEBUG_CHANNEL(listview);
147 
148 typedef struct tagCOLUMN_INFO
149 {
150   RECT rcHeader;	/* tracks the header's rectangle */
151   INT fmt;		/* same as LVCOLUMN.fmt */
152   INT cxMin;
153 } COLUMN_INFO;
154 
155 typedef struct tagITEMHDR
156 {
157   LPWSTR pszText;
158   INT iImage;
159 } ITEMHDR, *LPITEMHDR;
160 
161 typedef struct tagSUBITEM_INFO
162 {
163   ITEMHDR hdr;
164   INT iSubItem;
165 } SUBITEM_INFO;
166 
167 typedef struct tagITEM_ID ITEM_ID;
168 
169 typedef struct tagITEM_INFO
170 {
171   ITEMHDR hdr;
172   UINT state;
173   LPARAM lParam;
174   INT iIndent;
175   ITEM_ID *id;
176 } ITEM_INFO;
177 
178 struct tagITEM_ID
179 {
180   UINT id;   /* item id */
181   HDPA item; /* link to item data */
182 };
183 
184 typedef struct tagRANGE
185 {
186   INT lower;
187   INT upper;
188 } RANGE;
189 
190 typedef struct tagRANGES
191 {
192   HDPA hdpa;
193 } *RANGES;
194 
195 typedef struct tagITERATOR
196 {
197   INT nItem;
198   INT nSpecial;
199   RANGE range;
200   RANGES ranges;
201   INT index;
202 } ITERATOR;
203 
204 typedef struct tagDELAYED_ITEM_EDIT
205 {
206   BOOL fEnabled;
207   INT iItem;
208 } DELAYED_ITEM_EDIT;
209 
210 enum notification_mask
211 {
212   NOTIFY_MASK_ITEM_CHANGE = 0x1,
213   NOTIFY_MASK_END_LABEL_EDIT = 0x2,
214   NOTIFY_MASK_UNMASK_ALL = 0xffffffff
215 };
216 
217 typedef struct tagLISTVIEW_INFO
218 {
219   /* control window */
220   HWND hwndSelf;
221   RECT rcList;                 /* This rectangle is really the window
222 				* client rectangle possibly reduced by the
223 				* horizontal scroll bar and/or header - see
224 				* LISTVIEW_UpdateSize. This rectangle offset
225 				* by the LISTVIEW_GetOrigin value is in
226 				* client coordinates   */
227 
228   /* notification window */
229   SHORT notifyFormat;
230   HWND hwndNotify;
231   DWORD notify_mask;
232   UINT uCallbackMask;
233 
234   /* tooltips */
235   HWND hwndToolTip;
236 
237   /* items */
238   INT nItemCount;		/* the number of items in the list */
239   HDPA hdpaItems;               /* array ITEM_INFO pointers */
240   HDPA hdpaItemIds;             /* array of ITEM_ID pointers */
241   HDPA hdpaPosX;		/* maintains the (X, Y) coordinates of the */
242   HDPA hdpaPosY;		/* items in LVS_ICON, and LVS_SMALLICON modes */
243   RANGES selectionRanges;
244   INT nSelectionMark;           /* item to start next multiselection from */
245   INT nHotItem;
246 
247   /* columns */
248   HDPA hdpaColumns;		/* array of COLUMN_INFO pointers */
249   BOOL colRectsDirty;		/* trigger column rectangles requery from header */
250 
251   /* item metrics */
252   BOOL bNoItemMetrics;		/* flags if item metrics are not yet computed */
253   INT nItemHeight;
254   INT nItemWidth;
255 
256   /* sorting */
257   PFNLVCOMPARE pfnCompare;      /* sorting callback pointer */
258   LPARAM lParamSort;
259 
260   /* style */
261   DWORD dwStyle;		/* the cached window GWL_STYLE */
262   DWORD dwLvExStyle;		/* extended listview style */
263   DWORD uView;			/* current view available through LVM_[G,S]ETVIEW */
264 
265   /* edit item */
266   HWND hwndEdit;
267   WNDPROC EditWndProc;
268   INT nEditLabelItem;
269   DELAYED_ITEM_EDIT itemEdit;   /* Pointer to this structure will be the timer ID */
270 
271   /* icons */
272   HIMAGELIST himlNormal;
273   HIMAGELIST himlSmall;
274   HIMAGELIST himlState;
275   SIZE iconSize;
276   BOOL autoSpacing;
277   SIZE iconSpacing;
278   SIZE iconStateSize;
279   POINT currIconPos;        /* this is the position next icon will be placed */
280 
281   /* header */
282   HWND hwndHeader;
283   INT xTrackLine;           /* The x coefficient of the track line or -1 if none */
284 
285   /* marquee selection */
286   BOOL bMarqueeSelect;      /* marquee selection/highlight underway */
287   BOOL bScrolling;
288   RECT marqueeRect;         /* absolute coordinates of marquee selection */
289   RECT marqueeDrawRect;     /* relative coordinates for drawing marquee */
290   POINT marqueeOrigin;      /* absolute coordinates of marquee click origin */
291 
292   /* focus drawing */
293   BOOL bFocus;              /* control has focus */
294   INT nFocusedItem;
295   RECT rcFocus;             /* focus bounds */
296 
297   /* colors */
298   HBRUSH hBkBrush;
299   COLORREF clrBk;
300   COLORREF clrText;
301   COLORREF clrTextBk;
302 #ifdef __REACTOS__
303   BOOL bDefaultBkColor;
304 #endif
305 
306   /* font */
307   HFONT hDefaultFont;
308   HFONT hFont;
309   INT ntmHeight;            /* Some cached metrics of the font used */
310   INT ntmMaxCharWidth;      /* by the listview to draw items */
311   INT nEllipsisWidth;
312 
313   /* mouse operation */
314   BOOL bLButtonDown;
315   BOOL bDragging;
316   POINT ptClickPos;         /* point where the user clicked */
317   INT nLButtonDownItem;     /* tracks item to reset multiselection on WM_LBUTTONUP */
318   DWORD dwHoverTime;
319   HCURSOR hHotCursor;
320   INT cWheelRemainder;
321 
322   /* keyboard operation */
323   DWORD lastKeyPressTimestamp;
324   WPARAM charCode;
325   INT nSearchParamLength;
326   WCHAR szSearchParam[ MAX_PATH ];
327 
328   /* painting */
329   BOOL bIsDrawing;         /* Drawing in progress */
330   INT nMeasureItemHeight;  /* WM_MEASUREITEM result */
331   BOOL redraw;             /* WM_SETREDRAW switch */
332 
333   /* misc */
334   DWORD iVersion;          /* CCM_[G,S]ETVERSION */
335 } LISTVIEW_INFO;
336 
337 /*
338  * constants
339  */
340 /* How many we debug buffer to allocate */
341 #define DEBUG_BUFFERS 20
342 /* The size of a single debug buffer */
343 #define DEBUG_BUFFER_SIZE 256
344 
345 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
346 #define SB_INTERNAL      -1
347 
348 /* maximum size of a label */
349 #define DISP_TEXT_SIZE 260
350 
351 /* padding for items in list and small icon display modes */
352 #define WIDTH_PADDING 12
353 
354 /* padding for items in list, report and small icon display modes */
355 #define HEIGHT_PADDING 1
356 
357 /* offset of items in report display mode */
358 #define REPORT_MARGINX 2
359 
360 /* padding for icon in large icon display mode
361  *   ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
362  *                                 that HITTEST will see.
363  *   ICON_TOP_PADDING_HITABLE - spacing between above and icon.
364  *   ICON_TOP_PADDING - sum of the two above.
365  *   ICON_BOTTOM_PADDING - between bottom of icon and top of text
366  *   LABEL_HOR_PADDING - between text and sides of box
367  *   LABEL_VERT_PADDING - between bottom of text and end of box
368  *
369  *   ICON_LR_PADDING - additional width above icon size.
370  *   ICON_LR_HALF - half of the above value
371  */
372 #define ICON_TOP_PADDING_NOTHITABLE  2
373 #define ICON_TOP_PADDING_HITABLE     2
374 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
375 #define ICON_BOTTOM_PADDING          4
376 #define LABEL_HOR_PADDING            5
377 #define LABEL_VERT_PADDING           7
378 #define ICON_LR_PADDING              16
379 #define ICON_LR_HALF                 (ICON_LR_PADDING/2)
380 
381 /* default label width for items in list and small icon display modes */
382 #define DEFAULT_LABEL_WIDTH 40
383 /* maximum select rectangle width for empty text item in LV_VIEW_DETAILS */
384 #define MAX_EMPTYTEXT_SELECT_WIDTH 80
385 
386 /* default column width for items in list display mode */
387 #define DEFAULT_COLUMN_WIDTH 128
388 
389 /* Size of "line" scroll for V & H scrolls */
390 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
391 
392 /* Padding between image and label */
393 #define IMAGE_PADDING  2
394 
395 /* Padding behind the label */
396 #define TRAILING_LABEL_PADDING  12
397 #define TRAILING_HEADER_PADDING  11
398 
399 /* Border for the icon caption */
400 #define CAPTION_BORDER  2
401 
402 /* Standard DrawText flags */
403 #define LV_ML_DT_FLAGS  (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
404 #define LV_FL_DT_FLAGS  (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_NOCLIP)
405 #define LV_SL_DT_FLAGS  (DT_VCENTER | DT_NOPREFIX | DT_EDITCONTROL | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
406 
407 /* Image index from state */
408 #define STATEIMAGEINDEX(x) (((x) & LVIS_STATEIMAGEMASK) >> 12)
409 
410 /* The time in milliseconds to reset the search in the list */
411 #define KEY_DELAY       450
412 
413 /* Dump the LISTVIEW_INFO structure to the debug channel */
414 #define LISTVIEW_DUMP(iP) do { \
415   TRACE("hwndSelf=%p, clrBk=0x%06x, clrText=0x%06x, clrTextBk=0x%06x, ItemHeight=%d, ItemWidth=%d, Style=0x%08x\n", \
416         iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
417         iP->nItemHeight, iP->nItemWidth, iP->dwStyle); \
418   TRACE("hwndSelf=%p, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08x, Focus=%d\n", \
419         iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
420         iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, iP->bFocus ); \
421   TRACE("hwndSelf=%p, ntmH=%d, icSz.cx=%d, icSz.cy=%d, icSp.cx=%d, icSp.cy=%d, notifyFmt=%d\n", \
422         iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
423         iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
424   TRACE("hwndSelf=%p, rcList=%s\n", iP->hwndSelf, wine_dbgstr_rect(&iP->rcList)); \
425 } while(0)
426 
427 static const WCHAR themeClass[] = {'L','i','s','t','V','i','e','w',0};
428 
429 /*
430  * forward declarations
431  */
432 static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *, LPLVITEMW, BOOL);
433 static void LISTVIEW_GetItemBox(const LISTVIEW_INFO *, INT, LPRECT);
434 static void LISTVIEW_GetItemOrigin(const LISTVIEW_INFO *, INT, LPPOINT);
435 static BOOL LISTVIEW_GetItemPosition(const LISTVIEW_INFO *, INT, LPPOINT);
436 static BOOL LISTVIEW_GetItemRect(const LISTVIEW_INFO *, INT, LPRECT);
437 static void LISTVIEW_GetOrigin(const LISTVIEW_INFO *, LPPOINT);
438 static BOOL LISTVIEW_GetViewRect(const LISTVIEW_INFO *, LPRECT);
439 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *);
440 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *, WPARAM, LPARAM);
441 static INT LISTVIEW_GetStringWidthT(const LISTVIEW_INFO *, LPCWSTR, BOOL);
442 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *, INT, BOOL);
443 static UINT LISTVIEW_GetItemState(const LISTVIEW_INFO *, INT, UINT);
444 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *, INT, const LVITEMW *);
445 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *, INT, INT);
446 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *, INT, INT);
447 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *, INT, BOOL);
448 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *, INT, HIMAGELIST);
449 static INT LISTVIEW_HitTest(const LISTVIEW_INFO *, LPLVHITTESTINFO, BOOL, BOOL);
450 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *, BOOL, BOOL);
451 static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *, INT, INT);
452 
453 /******** Text handling functions *************************************/
454 
455 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
456  * text string. The string may be ANSI or Unicode, in which case
457  * the boolean isW tells us the type of the string.
458  *
459  * The name of the function tell what type of strings it expects:
460  *   W: Unicode, T: ANSI/Unicode - function of isW
461  */
462 
is_text(LPCWSTR text)463 static inline BOOL is_text(LPCWSTR text)
464 {
465     return text != NULL && text != LPSTR_TEXTCALLBACKW;
466 }
467 
textlenT(LPCWSTR text,BOOL isW)468 static inline int textlenT(LPCWSTR text, BOOL isW)
469 {
470     return !is_text(text) ? 0 :
471 	   isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
472 }
473 
textcpynT(LPWSTR dest,BOOL isDestW,LPCWSTR src,BOOL isSrcW,INT max)474 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
475 {
476     if (isDestW)
477 	if (isSrcW) lstrcpynW(dest, src, max);
478 	else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
479     else
480 	if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
481 	else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
482 }
483 
textdupTtoW(LPCWSTR text,BOOL isW)484 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
485 {
486     LPWSTR wstr = (LPWSTR)text;
487 
488     if (!isW && is_text(text))
489     {
490 	INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
491 	wstr = Alloc(len * sizeof(WCHAR));
492 	if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
493     }
494     TRACE("   wstr=%s\n", text == LPSTR_TEXTCALLBACKW ?  "(callback)" : debugstr_w(wstr));
495     return wstr;
496 }
497 
textfreeT(LPWSTR wstr,BOOL isW)498 static inline void textfreeT(LPWSTR wstr, BOOL isW)
499 {
500     if (!isW && is_text(wstr)) Free (wstr);
501 }
502 
503 /*
504  * dest is a pointer to a Unicode string
505  * src is a pointer to a string (Unicode if isW, ANSI if !isW)
506  */
textsetptrT(LPWSTR * dest,LPCWSTR src,BOOL isW)507 static BOOL textsetptrT(LPWSTR *dest, LPCWSTR src, BOOL isW)
508 {
509     BOOL bResult = TRUE;
510 
511     if (src == LPSTR_TEXTCALLBACKW)
512     {
513 	if (is_text(*dest)) Free(*dest);
514 	*dest = LPSTR_TEXTCALLBACKW;
515     }
516     else
517     {
518 	LPWSTR pszText = textdupTtoW(src, isW);
519 	if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
520 	bResult = Str_SetPtrW(dest, pszText);
521 	textfreeT(pszText, isW);
522     }
523     return bResult;
524 }
525 
526 /*
527  * compares a Unicode to a Unicode/ANSI text string
528  */
textcmpWT(LPCWSTR aw,LPCWSTR bt,BOOL isW)529 static inline int textcmpWT(LPCWSTR aw, LPCWSTR bt, BOOL isW)
530 {
531     if (!aw) return bt ? -1 : 0;
532     if (!bt) return 1;
533     if (aw == LPSTR_TEXTCALLBACKW)
534 	return bt == LPSTR_TEXTCALLBACKW ? 1 : -1;
535     if (bt != LPSTR_TEXTCALLBACKW)
536     {
537 	LPWSTR bw = textdupTtoW(bt, isW);
538 	int r = bw ? lstrcmpW(aw, bw) : 1;
539 	textfreeT(bw, isW);
540 	return r;
541     }
542 
543     return 1;
544 }
545 
546 /******** Debugging functions *****************************************/
547 
debugtext_t(LPCWSTR text,BOOL isW)548 static inline LPCSTR debugtext_t(LPCWSTR text, BOOL isW)
549 {
550     if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
551     return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
552 }
553 
debugtext_tn(LPCWSTR text,BOOL isW,INT n)554 static inline LPCSTR debugtext_tn(LPCWSTR text, BOOL isW, INT n)
555 {
556     if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
557     n = min(textlenT(text, isW), n);
558     return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
559 }
560 
debug_getbuf(void)561 static char* debug_getbuf(void)
562 {
563     static int index = 0;
564     static char buffers[DEBUG_BUFFERS][DEBUG_BUFFER_SIZE];
565     return buffers[index++ % DEBUG_BUFFERS];
566 }
567 
debugrange(const RANGE * lprng)568 static inline const char* debugrange(const RANGE *lprng)
569 {
570     if (!lprng) return "(null)";
571     return wine_dbg_sprintf("[%d, %d]", lprng->lower, lprng->upper);
572 }
573 
debugscrollinfo(const SCROLLINFO * pScrollInfo)574 static const char* debugscrollinfo(const SCROLLINFO *pScrollInfo)
575 {
576     char* buf = debug_getbuf(), *text = buf;
577     int len, size = DEBUG_BUFFER_SIZE;
578 
579     if (pScrollInfo == NULL) return "(null)";
580     len = snprintf(buf, size, "{cbSize=%u, ", pScrollInfo->cbSize);
581     if (len == -1) goto end;
582     buf += len; size -= len;
583     if (pScrollInfo->fMask & SIF_RANGE)
584 	len = snprintf(buf, size, "nMin=%d, nMax=%d, ", pScrollInfo->nMin, pScrollInfo->nMax);
585     else len = 0;
586     if (len == -1) goto end;
587     buf += len; size -= len;
588     if (pScrollInfo->fMask & SIF_PAGE)
589 	len = snprintf(buf, size, "nPage=%u, ", pScrollInfo->nPage);
590     else len = 0;
591     if (len == -1) goto end;
592     buf += len; size -= len;
593     if (pScrollInfo->fMask & SIF_POS)
594 	len = snprintf(buf, size, "nPos=%d, ", pScrollInfo->nPos);
595     else len = 0;
596     if (len == -1) goto end;
597     buf += len; size -= len;
598     if (pScrollInfo->fMask & SIF_TRACKPOS)
599 	len = snprintf(buf, size, "nTrackPos=%d, ", pScrollInfo->nTrackPos);
600     else len = 0;
601     if (len == -1) goto end;
602     buf += len;
603     goto undo;
604 end:
605     buf = text + strlen(text);
606 undo:
607     if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
608     return text;
609 }
610 
debugnmlistview(const NMLISTVIEW * plvnm)611 static const char* debugnmlistview(const NMLISTVIEW *plvnm)
612 {
613     if (!plvnm) return "(null)";
614     return wine_dbg_sprintf("iItem=%d, iSubItem=%d, uNewState=0x%x,"
615 	         " uOldState=0x%x, uChanged=0x%x, ptAction=%s, lParam=%ld",
616 	         plvnm->iItem, plvnm->iSubItem, plvnm->uNewState, plvnm->uOldState,
617 		 plvnm->uChanged, wine_dbgstr_point(&plvnm->ptAction), plvnm->lParam);
618 }
619 
debuglvitem_t(const LVITEMW * lpLVItem,BOOL isW)620 static const char* debuglvitem_t(const LVITEMW *lpLVItem, BOOL isW)
621 {
622     char* buf = debug_getbuf(), *text = buf;
623     int len, size = DEBUG_BUFFER_SIZE;
624 
625     if (lpLVItem == NULL) return "(null)";
626     len = snprintf(buf, size, "{iItem=%d, iSubItem=%d, ", lpLVItem->iItem, lpLVItem->iSubItem);
627     if (len == -1) goto end;
628     buf += len; size -= len;
629     if (lpLVItem->mask & LVIF_STATE)
630 	len = snprintf(buf, size, "state=%x, stateMask=%x, ", lpLVItem->state, lpLVItem->stateMask);
631     else len = 0;
632     if (len == -1) goto end;
633     buf += len; size -= len;
634     if (lpLVItem->mask & LVIF_TEXT)
635 	len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem->pszText, isW, 80), lpLVItem->cchTextMax);
636     else len = 0;
637     if (len == -1) goto end;
638     buf += len; size -= len;
639     if (lpLVItem->mask & LVIF_IMAGE)
640 	len = snprintf(buf, size, "iImage=%d, ", lpLVItem->iImage);
641     else len = 0;
642     if (len == -1) goto end;
643     buf += len; size -= len;
644     if (lpLVItem->mask & LVIF_PARAM)
645 	len = snprintf(buf, size, "lParam=%lx, ", lpLVItem->lParam);
646     else len = 0;
647     if (len == -1) goto end;
648     buf += len; size -= len;
649     if (lpLVItem->mask & LVIF_INDENT)
650 	len = snprintf(buf, size, "iIndent=%d, ", lpLVItem->iIndent);
651     else len = 0;
652     if (len == -1) goto end;
653     buf += len;
654     goto undo;
655 end:
656     buf = text + strlen(text);
657 undo:
658     if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
659     return text;
660 }
661 
debuglvcolumn_t(const LVCOLUMNW * lpColumn,BOOL isW)662 static const char* debuglvcolumn_t(const LVCOLUMNW *lpColumn, BOOL isW)
663 {
664     char* buf = debug_getbuf(), *text = buf;
665     int len, size = DEBUG_BUFFER_SIZE;
666 
667     if (lpColumn == NULL) return "(null)";
668     len = snprintf(buf, size, "{");
669     if (len == -1) goto end;
670     buf += len; size -= len;
671     if (lpColumn->mask & LVCF_SUBITEM)
672 	len = snprintf(buf, size, "iSubItem=%d, ",  lpColumn->iSubItem);
673     else len = 0;
674     if (len == -1) goto end;
675     buf += len; size -= len;
676     if (lpColumn->mask & LVCF_FMT)
677 	len = snprintf(buf, size, "fmt=%x, ", lpColumn->fmt);
678     else len = 0;
679     if (len == -1) goto end;
680     buf += len; size -= len;
681     if (lpColumn->mask & LVCF_WIDTH)
682 	len = snprintf(buf, size, "cx=%d, ", lpColumn->cx);
683     else len = 0;
684     if (len == -1) goto end;
685     buf += len; size -= len;
686     if (lpColumn->mask & LVCF_TEXT)
687 	len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn->pszText, isW, 80), lpColumn->cchTextMax);
688     else len = 0;
689     if (len == -1) goto end;
690     buf += len; size -= len;
691     if (lpColumn->mask & LVCF_IMAGE)
692 	len = snprintf(buf, size, "iImage=%d, ", lpColumn->iImage);
693     else len = 0;
694     if (len == -1) goto end;
695     buf += len; size -= len;
696     if (lpColumn->mask & LVCF_ORDER)
697 	len = snprintf(buf, size, "iOrder=%d, ", lpColumn->iOrder);
698     else len = 0;
699     if (len == -1) goto end;
700     buf += len;
701     goto undo;
702 end:
703     buf = text + strlen(text);
704 undo:
705     if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
706     return text;
707 }
708 
debuglvhittestinfo(const LVHITTESTINFO * lpht)709 static const char* debuglvhittestinfo(const LVHITTESTINFO *lpht)
710 {
711     if (!lpht) return "(null)";
712 
713     return wine_dbg_sprintf("{pt=%s, flags=0x%x, iItem=%d, iSubItem=%d}",
714 		 wine_dbgstr_point(&lpht->pt), lpht->flags, lpht->iItem, lpht->iSubItem);
715 }
716 
717 /* Return the corresponding text for a given scroll value */
debugscrollcode(int nScrollCode)718 static inline LPCSTR debugscrollcode(int nScrollCode)
719 {
720   switch(nScrollCode)
721   {
722   case SB_LINELEFT: return "SB_LINELEFT";
723   case SB_LINERIGHT: return "SB_LINERIGHT";
724   case SB_PAGELEFT: return "SB_PAGELEFT";
725   case SB_PAGERIGHT: return "SB_PAGERIGHT";
726   case SB_THUMBPOSITION: return "SB_THUMBPOSITION";
727   case SB_THUMBTRACK: return "SB_THUMBTRACK";
728   case SB_ENDSCROLL: return "SB_ENDSCROLL";
729   case SB_INTERNAL: return "SB_INTERNAL";
730   default: return "unknown";
731   }
732 }
733 
734 
735 /******** Notification functions ************************************/
736 
get_ansi_notification(UINT unicodeNotificationCode)737 static int get_ansi_notification(UINT unicodeNotificationCode)
738 {
739     switch (unicodeNotificationCode)
740     {
741     case LVN_BEGINLABELEDITA:
742     case LVN_BEGINLABELEDITW: return LVN_BEGINLABELEDITA;
743     case LVN_ENDLABELEDITA:
744     case LVN_ENDLABELEDITW: return LVN_ENDLABELEDITA;
745     case LVN_GETDISPINFOA:
746     case LVN_GETDISPINFOW: return LVN_GETDISPINFOA;
747     case LVN_SETDISPINFOA:
748     case LVN_SETDISPINFOW: return LVN_SETDISPINFOA;
749     case LVN_ODFINDITEMA:
750     case LVN_ODFINDITEMW: return LVN_ODFINDITEMA;
751     case LVN_GETINFOTIPA:
752     case LVN_GETINFOTIPW: return LVN_GETINFOTIPA;
753     /* header forwards */
754     case HDN_TRACKA:
755     case HDN_TRACKW: return HDN_TRACKA;
756     case HDN_ENDTRACKA:
757     case HDN_ENDTRACKW: return HDN_ENDTRACKA;
758     case HDN_BEGINDRAG: return HDN_BEGINDRAG;
759     case HDN_ENDDRAG: return HDN_ENDDRAG;
760     case HDN_ITEMCHANGINGA:
761     case HDN_ITEMCHANGINGW: return HDN_ITEMCHANGINGA;
762     case HDN_ITEMCHANGEDA:
763     case HDN_ITEMCHANGEDW: return HDN_ITEMCHANGEDA;
764     case HDN_ITEMCLICKA:
765     case HDN_ITEMCLICKW: return HDN_ITEMCLICKA;
766     case HDN_DIVIDERDBLCLICKA:
767     case HDN_DIVIDERDBLCLICKW: return HDN_DIVIDERDBLCLICKA;
768     default: break;
769     }
770     FIXME("unknown notification %x\n", unicodeNotificationCode);
771     return unicodeNotificationCode;
772 }
773 
774 /* forwards header notifications to listview parent */
notify_forward_header(const LISTVIEW_INFO * infoPtr,NMHEADERW * lpnmhW)775 static LRESULT notify_forward_header(const LISTVIEW_INFO *infoPtr, NMHEADERW *lpnmhW)
776 {
777     LPCWSTR text = NULL, filter = NULL;
778     LRESULT ret;
779     NMHEADERA *lpnmh = (NMHEADERA*) lpnmhW;
780 
781     /* on unicode format exit earlier */
782     if (infoPtr->notifyFormat == NFR_UNICODE)
783         return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, lpnmh->hdr.idFrom,
784                             (LPARAM)lpnmh);
785 
786     /* header always supplies unicode notifications,
787        all we have to do is to convert strings to ANSI */
788     if (lpnmh->pitem)
789     {
790         /* convert item text */
791         if (lpnmh->pitem->mask & HDI_TEXT)
792         {
793             text = (LPCWSTR)lpnmh->pitem->pszText;
794             lpnmh->pitem->pszText = NULL;
795             Str_SetPtrWtoA(&lpnmh->pitem->pszText, text);
796         }
797         /* convert filter text */
798         if ((lpnmh->pitem->mask & HDI_FILTER) && (lpnmh->pitem->type == HDFT_ISSTRING) &&
799              lpnmh->pitem->pvFilter)
800         {
801             filter = (LPCWSTR)((HD_TEXTFILTERA*)lpnmh->pitem->pvFilter)->pszText;
802             ((HD_TEXTFILTERA*)lpnmh->pitem->pvFilter)->pszText = NULL;
803             Str_SetPtrWtoA(&((HD_TEXTFILTERA*)lpnmh->pitem->pvFilter)->pszText, filter);
804         }
805     }
806     lpnmh->hdr.code = get_ansi_notification(lpnmh->hdr.code);
807 
808     ret = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, lpnmh->hdr.idFrom,
809                        (LPARAM)lpnmh);
810 
811     /* cleanup */
812     if(text)
813     {
814         Free(lpnmh->pitem->pszText);
815         lpnmh->pitem->pszText = (LPSTR)text;
816     }
817     if(filter)
818     {
819         Free(((HD_TEXTFILTERA*)lpnmh->pitem->pvFilter)->pszText);
820         ((HD_TEXTFILTERA*)lpnmh->pitem->pvFilter)->pszText = (LPSTR)filter;
821     }
822 
823     return ret;
824 }
825 
notify_hdr(const LISTVIEW_INFO * infoPtr,INT code,LPNMHDR pnmh)826 static LRESULT notify_hdr(const LISTVIEW_INFO *infoPtr, INT code, LPNMHDR pnmh)
827 {
828     LRESULT result;
829 
830     TRACE("(code=%d)\n", code);
831 
832     pnmh->hwndFrom = infoPtr->hwndSelf;
833     pnmh->idFrom = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
834     pnmh->code = code;
835     result = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, pnmh->idFrom, (LPARAM)pnmh);
836 
837     TRACE("  <= %ld\n", result);
838 
839     return result;
840 }
841 
notify(const LISTVIEW_INFO * infoPtr,INT code)842 static inline BOOL notify(const LISTVIEW_INFO *infoPtr, INT code)
843 {
844     NMHDR nmh;
845     HWND hwnd = infoPtr->hwndSelf;
846     notify_hdr(infoPtr, code, &nmh);
847     return IsWindow(hwnd);
848 }
849 
notify_itemactivate(const LISTVIEW_INFO * infoPtr,const LVHITTESTINFO * htInfo)850 static inline void notify_itemactivate(const LISTVIEW_INFO *infoPtr, const LVHITTESTINFO *htInfo)
851 {
852     NMITEMACTIVATE nmia;
853     LVITEMW item;
854 
855     nmia.uNewState = 0;
856     nmia.uOldState = 0;
857     nmia.uChanged  = 0;
858     nmia.uKeyFlags = 0;
859 
860     item.mask = LVIF_PARAM|LVIF_STATE;
861     item.iItem = htInfo->iItem;
862     item.iSubItem = 0;
863     item.stateMask = (UINT)-1;
864     if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) {
865         nmia.lParam = item.lParam;
866         nmia.uOldState = item.state;
867         nmia.uNewState = item.state | LVIS_ACTIVATING;
868         nmia.uChanged  = LVIF_STATE;
869     }
870 
871     nmia.iItem = htInfo->iItem;
872     nmia.iSubItem = htInfo->iSubItem;
873     nmia.ptAction = htInfo->pt;
874 
875     if (GetKeyState(VK_SHIFT) & 0x8000) nmia.uKeyFlags |= LVKF_SHIFT;
876     if (GetKeyState(VK_CONTROL) & 0x8000) nmia.uKeyFlags |= LVKF_CONTROL;
877     if (GetKeyState(VK_MENU) & 0x8000) nmia.uKeyFlags |= LVKF_ALT;
878 
879     notify_hdr(infoPtr, LVN_ITEMACTIVATE, (LPNMHDR)&nmia);
880 }
881 
notify_listview(const LISTVIEW_INFO * infoPtr,INT code,LPNMLISTVIEW plvnm)882 static inline LRESULT notify_listview(const LISTVIEW_INFO *infoPtr, INT code, LPNMLISTVIEW plvnm)
883 {
884     TRACE("(code=%d, plvnm=%s)\n", code, debugnmlistview(plvnm));
885     return notify_hdr(infoPtr, code, (LPNMHDR)plvnm);
886 }
887 
888 /* Handles NM_DBLCLK, NM_CLICK, NM_RDBLCLK, NM_RCLICK. Only NM_RCLICK return value is used. */
notify_click(const LISTVIEW_INFO * infoPtr,INT code,const LVHITTESTINFO * lvht)889 static BOOL notify_click(const LISTVIEW_INFO *infoPtr, INT code, const LVHITTESTINFO *lvht)
890 {
891     NMITEMACTIVATE nmia;
892     LVITEMW item;
893     HWND hwnd = infoPtr->hwndSelf;
894     LRESULT ret;
895 
896     TRACE("code=%d, lvht=%s\n", code, debuglvhittestinfo(lvht));
897     ZeroMemory(&nmia, sizeof(nmia));
898     nmia.iItem = lvht->iItem;
899     nmia.iSubItem = lvht->iSubItem;
900     nmia.ptAction = lvht->pt;
901     item.mask = LVIF_PARAM;
902     item.iItem = lvht->iItem;
903     item.iSubItem = 0;
904     if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmia.lParam = item.lParam;
905     ret = notify_hdr(infoPtr, code, (NMHDR*)&nmia);
906     return IsWindow(hwnd) && (code == NM_RCLICK ? !ret : TRUE);
907 }
908 
notify_deleteitem(const LISTVIEW_INFO * infoPtr,INT nItem)909 static BOOL notify_deleteitem(const LISTVIEW_INFO *infoPtr, INT nItem)
910 {
911     NMLISTVIEW nmlv;
912     LVITEMW item;
913     HWND hwnd = infoPtr->hwndSelf;
914 
915     ZeroMemory(&nmlv, sizeof (NMLISTVIEW));
916     nmlv.iItem = nItem;
917     item.mask = LVIF_PARAM;
918     item.iItem = nItem;
919     item.iSubItem = 0;
920     if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
921     notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
922     return IsWindow(hwnd);
923 }
924 
925 /*
926   Send notification. depends on dispinfoW having same
927   structure as dispinfoA.
928   infoPtr : listview struct
929   code : *Unicode* notification code
930   pdi : dispinfo structure (can be unicode or ansi)
931   isW : TRUE if dispinfo is Unicode
932 */
notify_dispinfoT(const LISTVIEW_INFO * infoPtr,UINT code,LPNMLVDISPINFOW pdi,BOOL isW)933 static BOOL notify_dispinfoT(const LISTVIEW_INFO *infoPtr, UINT code, LPNMLVDISPINFOW pdi, BOOL isW)
934 {
935     INT length = 0, ret_length;
936     LPWSTR buffer = NULL, ret_text;
937     BOOL return_ansi = FALSE;
938     BOOL return_unicode = FALSE;
939     BOOL ret;
940 
941     if ((pdi->item.mask & LVIF_TEXT) && is_text(pdi->item.pszText))
942     {
943 	return_unicode = ( isW && infoPtr->notifyFormat == NFR_ANSI);
944 	return_ansi    = (!isW && infoPtr->notifyFormat == NFR_UNICODE);
945     }
946 
947     ret_length = pdi->item.cchTextMax;
948     ret_text = pdi->item.pszText;
949 
950     if (return_unicode || return_ansi)
951     {
952         if (code != LVN_GETDISPINFOW)
953         {
954             length = return_ansi ?
955        		MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0):
956        		WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
957         }
958         else
959         {
960             length = pdi->item.cchTextMax;
961             *pdi->item.pszText = 0; /* make sure we don't process garbage */
962         }
963 
964         buffer = Alloc( (return_ansi ? sizeof(WCHAR) : sizeof(CHAR)) * length);
965         if (!buffer) return FALSE;
966 
967         if (return_ansi)
968             MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1,
969 	                        buffer, length);
970         else
971             WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) buffer,
972 	                        length, NULL, NULL);
973 
974         pdi->item.pszText = buffer;
975         pdi->item.cchTextMax = length;
976     }
977 
978     if (infoPtr->notifyFormat == NFR_ANSI)
979         code = get_ansi_notification(code);
980 
981     TRACE(" pdi->item=%s\n", debuglvitem_t(&pdi->item, infoPtr->notifyFormat != NFR_ANSI));
982     ret = notify_hdr(infoPtr, code, &pdi->hdr);
983     TRACE(" resulting code=%d\n", pdi->hdr.code);
984 
985     if (return_ansi || return_unicode)
986     {
987         if (return_ansi && (pdi->hdr.code == LVN_GETDISPINFOA))
988         {
989             strcpy((char*)ret_text, (char*)pdi->item.pszText);
990         }
991         else if (return_unicode && (pdi->hdr.code == LVN_GETDISPINFOW))
992         {
993             lstrcpyW(ret_text, pdi->item.pszText);
994         }
995         else if (return_ansi) /* note : pointer can be changed by app ! */
996         {
997 	    WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) ret_text,
998                 ret_length, NULL, NULL);
999         }
1000         else
1001             MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
1002                 ret_text, ret_length);
1003 
1004         pdi->item.pszText = ret_text; /* restores our buffer */
1005         pdi->item.cchTextMax = ret_length;
1006 
1007         Free(buffer);
1008         return ret;
1009     }
1010 
1011     /* if dispinfo holder changed notification code then convert */
1012     if (!isW && (pdi->hdr.code == LVN_GETDISPINFOW) && (pdi->item.mask & LVIF_TEXT))
1013     {
1014         length = WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
1015 
1016         buffer = Alloc(length * sizeof(CHAR));
1017         if (!buffer) return FALSE;
1018 
1019         WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) buffer,
1020                 ret_length, NULL, NULL);
1021 
1022         strcpy((LPSTR)pdi->item.pszText, (LPSTR)buffer);
1023         Free(buffer);
1024     }
1025 
1026     return ret;
1027 }
1028 
customdraw_fill(NMLVCUSTOMDRAW * lpnmlvcd,const LISTVIEW_INFO * infoPtr,HDC hdc,const RECT * rcBounds,const LVITEMW * lplvItem)1029 static void customdraw_fill(NMLVCUSTOMDRAW *lpnmlvcd, const LISTVIEW_INFO *infoPtr, HDC hdc,
1030 			    const RECT *rcBounds, const LVITEMW *lplvItem)
1031 {
1032     ZeroMemory(lpnmlvcd, sizeof(NMLVCUSTOMDRAW));
1033     lpnmlvcd->nmcd.hdc = hdc;
1034     lpnmlvcd->nmcd.rc = *rcBounds;
1035     lpnmlvcd->clrTextBk = infoPtr->clrTextBk;
1036     lpnmlvcd->clrText   = infoPtr->clrText;
1037     if (!lplvItem) return;
1038     lpnmlvcd->nmcd.dwItemSpec = lplvItem->iItem + 1;
1039     lpnmlvcd->iSubItem = lplvItem->iSubItem;
1040     if (lplvItem->state & LVIS_SELECTED) lpnmlvcd->nmcd.uItemState |= CDIS_SELECTED;
1041     if (lplvItem->state & LVIS_FOCUSED) lpnmlvcd->nmcd.uItemState |= CDIS_FOCUS;
1042     if (lplvItem->iItem == infoPtr->nHotItem) lpnmlvcd->nmcd.uItemState |= CDIS_HOT;
1043     lpnmlvcd->nmcd.lItemlParam = lplvItem->lParam;
1044 }
1045 
notify_customdraw(const LISTVIEW_INFO * infoPtr,DWORD dwDrawStage,NMLVCUSTOMDRAW * lpnmlvcd)1046 static inline DWORD notify_customdraw (const LISTVIEW_INFO *infoPtr, DWORD dwDrawStage, NMLVCUSTOMDRAW *lpnmlvcd)
1047 {
1048     BOOL isForItem = (lpnmlvcd->nmcd.dwItemSpec != 0);
1049     DWORD result;
1050 
1051     lpnmlvcd->nmcd.dwDrawStage = dwDrawStage;
1052     if (isForItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_ITEM;
1053     if (lpnmlvcd->iSubItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_SUBITEM;
1054     if (isForItem) lpnmlvcd->nmcd.dwItemSpec--;
1055     result = notify_hdr(infoPtr, NM_CUSTOMDRAW, &lpnmlvcd->nmcd.hdr);
1056     if (isForItem) lpnmlvcd->nmcd.dwItemSpec++;
1057     return result;
1058 }
1059 
prepaint_setup(const LISTVIEW_INFO * infoPtr,HDC hdc,NMLVCUSTOMDRAW * lpnmlvcd,BOOL SubItem)1060 static void prepaint_setup (const LISTVIEW_INFO *infoPtr, HDC hdc, NMLVCUSTOMDRAW *lpnmlvcd, BOOL SubItem)
1061 {
1062     COLORREF backcolor, textcolor;
1063 
1064     /* apparently, for selected items, we have to override the returned values */
1065     if (!SubItem || (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
1066     {
1067         if (lpnmlvcd->nmcd.uItemState & CDIS_SELECTED)
1068         {
1069             if (infoPtr->bFocus)
1070             {
1071                 lpnmlvcd->clrTextBk = comctl32_color.clrHighlight;
1072                 lpnmlvcd->clrText   = comctl32_color.clrHighlightText;
1073             }
1074             else if (infoPtr->dwStyle & LVS_SHOWSELALWAYS)
1075             {
1076                 lpnmlvcd->clrTextBk = comctl32_color.clr3dFace;
1077                 lpnmlvcd->clrText   = comctl32_color.clrBtnText;
1078             }
1079         }
1080     }
1081 
1082     backcolor = lpnmlvcd->clrTextBk;
1083     textcolor = lpnmlvcd->clrText;
1084 
1085     if (backcolor == CLR_DEFAULT)
1086         backcolor = comctl32_color.clrWindow;
1087     if (textcolor == CLR_DEFAULT)
1088         textcolor = comctl32_color.clrWindowText;
1089 
1090     /* Set the text attributes */
1091     if (backcolor != CLR_NONE)
1092     {
1093 	SetBkMode(hdc, OPAQUE);
1094 	SetBkColor(hdc, backcolor);
1095     }
1096     else
1097 	SetBkMode(hdc, TRANSPARENT);
1098     SetTextColor(hdc, textcolor);
1099 }
1100 
notify_postpaint(const LISTVIEW_INFO * infoPtr,NMLVCUSTOMDRAW * lpnmlvcd)1101 static inline DWORD notify_postpaint (const LISTVIEW_INFO *infoPtr, NMLVCUSTOMDRAW *lpnmlvcd)
1102 {
1103     return notify_customdraw(infoPtr, CDDS_POSTPAINT, lpnmlvcd);
1104 }
1105 
1106 /* returns TRUE when repaint needed, FALSE otherwise */
notify_measureitem(LISTVIEW_INFO * infoPtr)1107 static BOOL notify_measureitem(LISTVIEW_INFO *infoPtr)
1108 {
1109     MEASUREITEMSTRUCT mis;
1110     mis.CtlType = ODT_LISTVIEW;
1111     mis.CtlID = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
1112     mis.itemID = -1;
1113     mis.itemWidth = 0;
1114     mis.itemData = 0;
1115     mis.itemHeight= infoPtr->nItemHeight;
1116     SendMessageW(infoPtr->hwndNotify, WM_MEASUREITEM, mis.CtlID, (LPARAM)&mis);
1117     if (infoPtr->nItemHeight != max(mis.itemHeight, 1))
1118     {
1119         infoPtr->nMeasureItemHeight = infoPtr->nItemHeight = max(mis.itemHeight, 1);
1120         return TRUE;
1121     }
1122     return FALSE;
1123 }
1124 
1125 /******** Item iterator functions **********************************/
1126 
1127 static RANGES ranges_create(int count);
1128 static void ranges_destroy(RANGES ranges);
1129 static BOOL ranges_add(RANGES ranges, RANGE range);
1130 static BOOL ranges_del(RANGES ranges, RANGE range);
1131 static void ranges_dump(RANGES ranges);
1132 
ranges_additem(RANGES ranges,INT nItem)1133 static inline BOOL ranges_additem(RANGES ranges, INT nItem)
1134 {
1135     RANGE range = { nItem, nItem + 1 };
1136 
1137     return ranges_add(ranges, range);
1138 }
1139 
ranges_delitem(RANGES ranges,INT nItem)1140 static inline BOOL ranges_delitem(RANGES ranges, INT nItem)
1141 {
1142     RANGE range = { nItem, nItem + 1 };
1143 
1144     return ranges_del(ranges, range);
1145 }
1146 
1147 /***
1148  * ITERATOR DOCUMENTATION
1149  *
1150  * The iterator functions allow for easy, and convenient iteration
1151  * over items of interest in the list. Typically, you create an
1152  * iterator, use it, and destroy it, as such:
1153  *   ITERATOR i;
1154  *
1155  *   iterator_xxxitems(&i, ...);
1156  *   while (iterator_{prev,next}(&i)
1157  *   {
1158  *       //code which uses i.nItem
1159  *   }
1160  *   iterator_destroy(&i);
1161  *
1162  *   where xxx is either: framed, or visible.
1163  * Note that it is important that the code destroys the iterator
1164  * after it's done with it, as the creation of the iterator may
1165  * allocate memory, which thus needs to be freed.
1166  *
1167  * You can iterate both forwards, and backwards through the list,
1168  * by using iterator_next or iterator_prev respectively.
1169  *
1170  * Lower numbered items are draw on top of higher number items in
1171  * LVS_ICON, and LVS_SMALLICON (which are the only modes where
1172  * items may overlap). So, to test items, you should use
1173  *    iterator_next
1174  * which lists the items top to bottom (in Z-order).
1175  * For drawing items, you should use
1176  *    iterator_prev
1177  * which lists the items bottom to top (in Z-order).
1178  * If you keep iterating over the items after the end-of-items
1179  * marker (-1) is returned, the iterator will start from the
1180  * beginning. Typically, you don't need to test for -1,
1181  * because iterator_{next,prev} will return TRUE if more items
1182  * are to be iterated over, or FALSE otherwise.
1183  *
1184  * Note: the iterator is defined to be bidirectional. That is,
1185  *       any number of prev followed by any number of next, or
1186  *       five versa, should leave the iterator at the same item:
1187  *           prev * n, next * n = next * n, prev * n
1188  *
1189  * The iterator has a notion of an out-of-order, special item,
1190  * which sits at the start of the list. This is used in
1191  * LVS_ICON, and LVS_SMALLICON mode to handle the focused item,
1192  * which needs to be first, as it may overlap other items.
1193  *
1194  * The code is a bit messy because we have:
1195  *   - a special item to deal with
1196  *   - simple range, or composite range
1197  *   - empty range.
1198  * If you find bugs, or want to add features, please make sure you
1199  * always check/modify *both* iterator_prev, and iterator_next.
1200  */
1201 
1202 /****
1203  * This function iterates through the items in increasing order,
1204  * but prefixed by the special item, then -1. That is:
1205  *    special, 1, 2, 3, ..., n, -1.
1206  * Each item is listed only once.
1207  */
iterator_next(ITERATOR * i)1208 static inline BOOL iterator_next(ITERATOR* i)
1209 {
1210     if (i->nItem == -1)
1211     {
1212 	i->nItem = i->nSpecial;
1213 	if (i->nItem != -1) return TRUE;
1214     }
1215     if (i->nItem == i->nSpecial)
1216     {
1217 	if (i->ranges) i->index = 0;
1218 	goto pickarange;
1219     }
1220 
1221     i->nItem++;
1222 testitem:
1223     if (i->nItem == i->nSpecial) i->nItem++;
1224     if (i->nItem < i->range.upper) return TRUE;
1225 
1226 pickarange:
1227     if (i->ranges)
1228     {
1229 	if (i->index < DPA_GetPtrCount(i->ranges->hdpa))
1230 	    i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, i->index++);
1231 	else goto end;
1232     }
1233     else if (i->nItem >= i->range.upper) goto end;
1234 
1235     i->nItem = i->range.lower;
1236     if (i->nItem >= 0) goto testitem;
1237 end:
1238     i->nItem = -1;
1239     return FALSE;
1240 }
1241 
1242 /****
1243  * This function iterates through the items in decreasing order,
1244  * followed by the special item, then -1. That is:
1245  *    n, n-1, ..., 3, 2, 1, special, -1.
1246  * Each item is listed only once.
1247  */
iterator_prev(ITERATOR * i)1248 static inline BOOL iterator_prev(ITERATOR* i)
1249 {
1250     BOOL start = FALSE;
1251 
1252     if (i->nItem == -1)
1253     {
1254 	start = TRUE;
1255 	if (i->ranges) i->index = DPA_GetPtrCount(i->ranges->hdpa);
1256 	goto pickarange;
1257     }
1258     if (i->nItem == i->nSpecial)
1259     {
1260 	i->nItem = -1;
1261 	return FALSE;
1262     }
1263 
1264 testitem:
1265     i->nItem--;
1266     if (i->nItem == i->nSpecial) i->nItem--;
1267     if (i->nItem >= i->range.lower) return TRUE;
1268 
1269 pickarange:
1270     if (i->ranges)
1271     {
1272 	if (i->index > 0)
1273 	    i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, --i->index);
1274 	else goto end;
1275     }
1276     else if (!start && i->nItem < i->range.lower) goto end;
1277 
1278     i->nItem = i->range.upper;
1279     if (i->nItem > 0) goto testitem;
1280 end:
1281     return (i->nItem = i->nSpecial) != -1;
1282 }
1283 
iterator_range(const ITERATOR * i)1284 static RANGE iterator_range(const ITERATOR *i)
1285 {
1286     RANGE range;
1287 
1288     if (!i->ranges) return i->range;
1289 
1290     if (DPA_GetPtrCount(i->ranges->hdpa) > 0)
1291     {
1292         range.lower = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, 0)).lower;
1293         range.upper = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, DPA_GetPtrCount(i->ranges->hdpa) - 1)).upper;
1294     }
1295     else range.lower = range.upper = 0;
1296 
1297     return range;
1298 }
1299 
1300 /***
1301  * Releases resources associated with this iterator.
1302  */
iterator_destroy(const ITERATOR * i)1303 static inline void iterator_destroy(const ITERATOR *i)
1304 {
1305     ranges_destroy(i->ranges);
1306 }
1307 
1308 /***
1309  * Create an empty iterator.
1310  */
iterator_empty(ITERATOR * i)1311 static inline void iterator_empty(ITERATOR* i)
1312 {
1313     ZeroMemory(i, sizeof(*i));
1314     i->nItem = i->nSpecial = i->range.lower = i->range.upper = -1;
1315 }
1316 
1317 /***
1318  * Create an iterator over a range.
1319  */
iterator_rangeitems(ITERATOR * i,RANGE range)1320 static inline void iterator_rangeitems(ITERATOR* i, RANGE range)
1321 {
1322     iterator_empty(i);
1323     i->range = range;
1324 }
1325 
1326 /***
1327  * Create an iterator over a bunch of ranges.
1328  * Please note that the iterator will take ownership of the ranges,
1329  * and will free them upon destruction.
1330  */
iterator_rangesitems(ITERATOR * i,RANGES ranges)1331 static inline void iterator_rangesitems(ITERATOR* i, RANGES ranges)
1332 {
1333     iterator_empty(i);
1334     i->ranges = ranges;
1335 }
1336 
1337 /***
1338  * Creates an iterator over the items which intersect frame.
1339  * Uses absolute coordinates rather than compensating for the current offset.
1340  */
iterator_frameditems_absolute(ITERATOR * i,const LISTVIEW_INFO * infoPtr,const RECT * frame)1341 static BOOL iterator_frameditems_absolute(ITERATOR* i, const LISTVIEW_INFO* infoPtr, const RECT *frame)
1342 {
1343     RECT rcItem, rcTemp;
1344     RANGES ranges;
1345 
1346     TRACE("(frame=%s)\n", wine_dbgstr_rect(frame));
1347 
1348     /* in case we fail, we want to return an empty iterator */
1349     iterator_empty(i);
1350 
1351     if (infoPtr->nItemCount == 0)
1352         return TRUE;
1353 
1354     if (infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON)
1355     {
1356 	INT nItem;
1357 
1358 	if (infoPtr->uView == LV_VIEW_ICON && infoPtr->nFocusedItem != -1)
1359 	{
1360 	    LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcItem);
1361 	    if (IntersectRect(&rcTemp, &rcItem, frame))
1362 		i->nSpecial = infoPtr->nFocusedItem;
1363 	}
1364 	if (!(ranges = ranges_create(50))) return FALSE;
1365 	iterator_rangesitems(i, ranges);
1366 	/* to do better here, we need to have PosX, and PosY sorted */
1367 	TRACE("building icon ranges:\n");
1368 	for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
1369 	{
1370             rcItem.left = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1371 	    rcItem.top = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1372 	    rcItem.right = rcItem.left + infoPtr->nItemWidth;
1373 	    rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1374 	    if (IntersectRect(&rcTemp, &rcItem, frame))
1375 		ranges_additem(i->ranges, nItem);
1376 	}
1377 	return TRUE;
1378     }
1379     else if (infoPtr->uView == LV_VIEW_DETAILS)
1380     {
1381 	RANGE range;
1382 
1383 	if (frame->left >= infoPtr->nItemWidth) return TRUE;
1384 	if (frame->top >= infoPtr->nItemHeight * infoPtr->nItemCount) return TRUE;
1385 
1386 	range.lower = max(frame->top / infoPtr->nItemHeight, 0);
1387 	range.upper = min((frame->bottom - 1) / infoPtr->nItemHeight, infoPtr->nItemCount - 1) + 1;
1388 	if (range.upper <= range.lower) return TRUE;
1389 	iterator_rangeitems(i, range);
1390 	TRACE("    report=%s\n", debugrange(&i->range));
1391     }
1392     else
1393     {
1394 	INT nPerCol = max((infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight, 1);
1395 	INT nFirstRow = max(frame->top / infoPtr->nItemHeight, 0);
1396 	INT nLastRow = min((frame->bottom - 1) / infoPtr->nItemHeight, nPerCol - 1);
1397 	INT nFirstCol;
1398 	INT nLastCol;
1399 	INT lower;
1400 	RANGE item_range;
1401 	INT nCol;
1402 
1403 	if (infoPtr->nItemWidth)
1404 	{
1405 	    nFirstCol = max(frame->left / infoPtr->nItemWidth, 0);
1406             nLastCol  = min((frame->right - 1) / infoPtr->nItemWidth, (infoPtr->nItemCount + nPerCol - 1) / nPerCol);
1407 	}
1408 	else
1409 	{
1410 	    nFirstCol = max(frame->left, 0);
1411             nLastCol  = min(frame->right - 1, (infoPtr->nItemCount + nPerCol - 1) / nPerCol);
1412 	}
1413 
1414 	lower = nFirstCol * nPerCol + nFirstRow;
1415 
1416 	TRACE("nPerCol=%d, nFirstRow=%d, nLastRow=%d, nFirstCol=%d, nLastCol=%d, lower=%d\n",
1417 	      nPerCol, nFirstRow, nLastRow, nFirstCol, nLastCol, lower);
1418 
1419 	if (nLastCol < nFirstCol || nLastRow < nFirstRow) return TRUE;
1420 
1421 	if (!(ranges = ranges_create(nLastCol - nFirstCol + 1))) return FALSE;
1422 	iterator_rangesitems(i, ranges);
1423 	TRACE("building list ranges:\n");
1424 	for (nCol = nFirstCol; nCol <= nLastCol; nCol++)
1425 	{
1426 	    item_range.lower = nCol * nPerCol + nFirstRow;
1427 	    if(item_range.lower >= infoPtr->nItemCount) break;
1428 	    item_range.upper = min(nCol * nPerCol + nLastRow + 1, infoPtr->nItemCount);
1429 	    TRACE("   list=%s\n", debugrange(&item_range));
1430 	    ranges_add(i->ranges, item_range);
1431 	}
1432     }
1433 
1434     return TRUE;
1435 }
1436 
1437 /***
1438  * Creates an iterator over the items which intersect lprc.
1439  */
iterator_frameditems(ITERATOR * i,const LISTVIEW_INFO * infoPtr,const RECT * lprc)1440 static BOOL iterator_frameditems(ITERATOR* i, const LISTVIEW_INFO* infoPtr, const RECT *lprc)
1441 {
1442     RECT frame = *lprc;
1443     POINT Origin;
1444 
1445     TRACE("(lprc=%s)\n", wine_dbgstr_rect(lprc));
1446 
1447     LISTVIEW_GetOrigin(infoPtr, &Origin);
1448     OffsetRect(&frame, -Origin.x, -Origin.y);
1449 
1450     return iterator_frameditems_absolute(i, infoPtr, &frame);
1451 }
1452 
1453 /***
1454  * Creates an iterator over the items which intersect the visible region of hdc.
1455  */
iterator_visibleitems(ITERATOR * i,const LISTVIEW_INFO * infoPtr,HDC hdc)1456 static BOOL iterator_visibleitems(ITERATOR *i, const LISTVIEW_INFO *infoPtr, HDC  hdc)
1457 {
1458     POINT Origin, Position;
1459     RECT rcItem, rcClip;
1460     INT rgntype;
1461 
1462     rgntype = GetClipBox(hdc, &rcClip);
1463     if (rgntype == NULLREGION)
1464     {
1465         iterator_empty(i);
1466         return TRUE;
1467     }
1468     if (!iterator_frameditems(i, infoPtr, &rcClip)) return FALSE;
1469     if (rgntype == SIMPLEREGION) return TRUE;
1470 
1471     /* first deal with the special item */
1472     if (i->nSpecial != -1)
1473     {
1474 	LISTVIEW_GetItemBox(infoPtr, i->nSpecial, &rcItem);
1475 	if (!RectVisible(hdc, &rcItem)) i->nSpecial = -1;
1476     }
1477 
1478     /* if we can't deal with the region, we'll just go with the simple range */
1479     LISTVIEW_GetOrigin(infoPtr, &Origin);
1480     TRACE("building visible range:\n");
1481     if (!i->ranges && i->range.lower < i->range.upper)
1482     {
1483 	if (!(i->ranges = ranges_create(50))) return TRUE;
1484 	if (!ranges_add(i->ranges, i->range))
1485         {
1486 	    ranges_destroy(i->ranges);
1487 	    i->ranges = 0;
1488 	    return TRUE;
1489         }
1490     }
1491 
1492     /* now delete the invisible items from the list */
1493     while(iterator_next(i))
1494     {
1495 	LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
1496 	rcItem.left = (infoPtr->uView == LV_VIEW_DETAILS) ? Origin.x : Position.x + Origin.x;
1497 	rcItem.top = Position.y + Origin.y;
1498 	rcItem.right = rcItem.left + infoPtr->nItemWidth;
1499 	rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1500 	if (!RectVisible(hdc, &rcItem))
1501 	    ranges_delitem(i->ranges, i->nItem);
1502     }
1503     /* the iterator should restart on the next iterator_next */
1504     TRACE("done\n");
1505 
1506     return TRUE;
1507 }
1508 
1509 /* Remove common elements from two iterators */
1510 /* Passed iterators have to point on the first elements */
iterator_remove_common_items(ITERATOR * iter1,ITERATOR * iter2)1511 static BOOL iterator_remove_common_items(ITERATOR *iter1, ITERATOR *iter2)
1512 {
1513     if(!iter1->ranges || !iter2->ranges) {
1514         int lower, upper;
1515 
1516         if(iter1->ranges || iter2->ranges ||
1517                 (iter1->range.lower<iter2->range.lower && iter1->range.upper>iter2->range.upper) ||
1518                 (iter1->range.lower>iter2->range.lower && iter1->range.upper<iter2->range.upper)) {
1519             ERR("result is not a one range iterator\n");
1520             return FALSE;
1521         }
1522 
1523         if(iter1->range.lower==-1 || iter2->range.lower==-1)
1524             return TRUE;
1525 
1526         lower = iter1->range.lower;
1527         upper = iter1->range.upper;
1528 
1529         if(lower < iter2->range.lower)
1530             iter1->range.upper = iter2->range.lower;
1531         else if(upper > iter2->range.upper)
1532             iter1->range.lower = iter2->range.upper;
1533         else
1534             iter1->range.lower = iter1->range.upper = -1;
1535 
1536         if(iter2->range.lower < lower)
1537             iter2->range.upper = lower;
1538         else if(iter2->range.upper > upper)
1539             iter2->range.lower = upper;
1540         else
1541             iter2->range.lower = iter2->range.upper = -1;
1542 
1543         return TRUE;
1544     }
1545 
1546     iterator_next(iter1);
1547     iterator_next(iter2);
1548 
1549     while(1) {
1550         if(iter1->nItem==-1 || iter2->nItem==-1)
1551             break;
1552 
1553         if(iter1->nItem == iter2->nItem) {
1554             int delete = iter1->nItem;
1555 
1556             iterator_prev(iter1);
1557             iterator_prev(iter2);
1558             ranges_delitem(iter1->ranges, delete);
1559             ranges_delitem(iter2->ranges, delete);
1560             iterator_next(iter1);
1561             iterator_next(iter2);
1562         } else if(iter1->nItem > iter2->nItem)
1563             iterator_next(iter2);
1564         else
1565             iterator_next(iter1);
1566     }
1567 
1568     iter1->nItem = iter1->range.lower = iter1->range.upper = -1;
1569     iter2->nItem = iter2->range.lower = iter2->range.upper = -1;
1570     return TRUE;
1571 }
1572 
1573 /******** Misc helper functions ************************************/
1574 
CallWindowProcT(WNDPROC proc,HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam,BOOL isW)1575 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
1576 		                      WPARAM wParam, LPARAM lParam, BOOL isW)
1577 {
1578     if (isW) return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
1579     else return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
1580 }
1581 
is_autoarrange(const LISTVIEW_INFO * infoPtr)1582 static inline BOOL is_autoarrange(const LISTVIEW_INFO *infoPtr)
1583 {
1584     return (infoPtr->dwStyle & LVS_AUTOARRANGE) &&
1585         (infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON);
1586 }
1587 
toggle_checkbox_state(LISTVIEW_INFO * infoPtr,INT nItem)1588 static void toggle_checkbox_state(LISTVIEW_INFO *infoPtr, INT nItem)
1589 {
1590     DWORD state = STATEIMAGEINDEX(LISTVIEW_GetItemState(infoPtr, nItem, LVIS_STATEIMAGEMASK));
1591     if(state == 1 || state == 2)
1592     {
1593         LVITEMW lvitem;
1594         state ^= 3;
1595         lvitem.state = INDEXTOSTATEIMAGEMASK(state);
1596         lvitem.stateMask = LVIS_STATEIMAGEMASK;
1597         LISTVIEW_SetItemState(infoPtr, nItem, &lvitem);
1598     }
1599 }
1600 
1601 /* this should be called after window style got updated,
1602    it used to reset view state to match current window style */
map_style_view(LISTVIEW_INFO * infoPtr)1603 static inline void map_style_view(LISTVIEW_INFO *infoPtr)
1604 {
1605     switch (infoPtr->dwStyle & LVS_TYPEMASK)
1606     {
1607     case LVS_ICON:
1608         infoPtr->uView = LV_VIEW_ICON;
1609         break;
1610     case LVS_REPORT:
1611         infoPtr->uView = LV_VIEW_DETAILS;
1612         break;
1613     case LVS_SMALLICON:
1614         infoPtr->uView = LV_VIEW_SMALLICON;
1615         break;
1616     case LVS_LIST:
1617         infoPtr->uView = LV_VIEW_LIST;
1618     }
1619 }
1620 
1621 /* computes next item id value */
get_next_itemid(const LISTVIEW_INFO * infoPtr)1622 static DWORD get_next_itemid(const LISTVIEW_INFO *infoPtr)
1623 {
1624     INT count = DPA_GetPtrCount(infoPtr->hdpaItemIds);
1625 
1626     if (count > 0)
1627     {
1628         ITEM_ID *lpID = DPA_GetPtr(infoPtr->hdpaItemIds, count - 1);
1629         return lpID->id + 1;
1630     }
1631     return 0;
1632 }
1633 
1634 /******** Internal API functions ************************************/
1635 
LISTVIEW_GetColumnInfo(const LISTVIEW_INFO * infoPtr,INT nSubItem)1636 static inline COLUMN_INFO * LISTVIEW_GetColumnInfo(const LISTVIEW_INFO *infoPtr, INT nSubItem)
1637 {
1638     static COLUMN_INFO mainItem;
1639 
1640     if (nSubItem == 0 && DPA_GetPtrCount(infoPtr->hdpaColumns) == 0) return &mainItem;
1641     assert (nSubItem >= 0 && nSubItem < DPA_GetPtrCount(infoPtr->hdpaColumns));
1642 
1643     /* update cached column rectangles */
1644     if (infoPtr->colRectsDirty)
1645     {
1646         COLUMN_INFO *info;
1647         LISTVIEW_INFO *Ptr = (LISTVIEW_INFO*)infoPtr;
1648         INT i;
1649 
1650         for (i = 0; i < DPA_GetPtrCount(infoPtr->hdpaColumns); i++) {
1651             info = DPA_GetPtr(infoPtr->hdpaColumns, i);
1652             SendMessageW(infoPtr->hwndHeader, HDM_GETITEMRECT, i, (LPARAM)&info->rcHeader);
1653         }
1654         Ptr->colRectsDirty = FALSE;
1655     }
1656 
1657     return DPA_GetPtr(infoPtr->hdpaColumns, nSubItem);
1658 }
1659 
LISTVIEW_CreateHeader(LISTVIEW_INFO * infoPtr)1660 static INT LISTVIEW_CreateHeader(LISTVIEW_INFO *infoPtr)
1661 {
1662     DWORD dFlags = WS_CHILD | HDS_HORZ | HDS_FULLDRAG | HDS_DRAGDROP;
1663     HINSTANCE hInst;
1664 
1665     if (infoPtr->hwndHeader) return 0;
1666 
1667     TRACE("Creating header for list %p\n", infoPtr->hwndSelf);
1668 
1669     /* setup creation flags */
1670     dFlags |= (LVS_NOSORTHEADER & infoPtr->dwStyle) ? 0 : HDS_BUTTONS;
1671     dFlags |= (LVS_NOCOLUMNHEADER & infoPtr->dwStyle) ? HDS_HIDDEN : 0;
1672 
1673     hInst = (HINSTANCE)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_HINSTANCE);
1674 
1675     /* create header */
1676     infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, NULL, dFlags,
1677       0, 0, 0, 0, infoPtr->hwndSelf, NULL, hInst, NULL);
1678     if (!infoPtr->hwndHeader) return -1;
1679 
1680     /* set header unicode format */
1681     SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT, TRUE, 0);
1682 
1683     /* set header font */
1684     SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, TRUE);
1685 
1686     /* set header image list */
1687     if (infoPtr->himlSmall)
1688         SendMessageW(infoPtr->hwndHeader, HDM_SETIMAGELIST, 0, (LPARAM)infoPtr->himlSmall);
1689 
1690     LISTVIEW_UpdateSize(infoPtr);
1691 
1692     return 0;
1693 }
1694 
LISTVIEW_GetHeaderRect(const LISTVIEW_INFO * infoPtr,INT nSubItem,LPRECT lprc)1695 static inline void LISTVIEW_GetHeaderRect(const LISTVIEW_INFO *infoPtr, INT nSubItem, LPRECT lprc)
1696 {
1697     *lprc = LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->rcHeader;
1698 }
1699 
LISTVIEW_IsHeaderEnabled(const LISTVIEW_INFO * infoPtr)1700 static inline BOOL LISTVIEW_IsHeaderEnabled(const LISTVIEW_INFO *infoPtr)
1701 {
1702     return (infoPtr->uView == LV_VIEW_DETAILS ||
1703             infoPtr->dwLvExStyle & LVS_EX_HEADERINALLVIEWS) &&
1704           !(infoPtr->dwStyle & LVS_NOCOLUMNHEADER);
1705 }
1706 
LISTVIEW_GetItemW(const LISTVIEW_INFO * infoPtr,LPLVITEMW lpLVItem)1707 static inline BOOL LISTVIEW_GetItemW(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem)
1708 {
1709     return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
1710 }
1711 
1712 /* used to handle collapse main item column case */
LISTVIEW_DrawFocusRect(const LISTVIEW_INFO * infoPtr,HDC hdc)1713 static inline BOOL LISTVIEW_DrawFocusRect(const LISTVIEW_INFO *infoPtr, HDC hdc)
1714 {
1715 #ifdef __REACTOS__
1716     BOOL Ret = FALSE;
1717 
1718     if (infoPtr->rcFocus.left < infoPtr->rcFocus.right)
1719     {
1720         DWORD dwOldBkColor, dwOldTextColor;
1721 
1722         dwOldBkColor = SetBkColor(hdc, RGB(255, 255, 255));
1723         dwOldTextColor = SetBkColor(hdc, RGB(0, 0, 0));
1724         Ret = DrawFocusRect(hdc, &infoPtr->rcFocus);
1725         SetBkColor(hdc, dwOldBkColor);
1726         SetBkColor(hdc, dwOldTextColor);
1727     }
1728     return Ret;
1729 #else
1730     return (infoPtr->rcFocus.left < infoPtr->rcFocus.right) ?
1731             DrawFocusRect(hdc, &infoPtr->rcFocus) : FALSE;
1732 #endif
1733 }
1734 
1735 /* Listview invalidation functions: use _only_ these functions to invalidate */
1736 
is_redrawing(const LISTVIEW_INFO * infoPtr)1737 static inline BOOL is_redrawing(const LISTVIEW_INFO *infoPtr)
1738 {
1739     return infoPtr->redraw;
1740 }
1741 
LISTVIEW_InvalidateRect(const LISTVIEW_INFO * infoPtr,const RECT * rect)1742 static inline void LISTVIEW_InvalidateRect(const LISTVIEW_INFO *infoPtr, const RECT* rect)
1743 {
1744     if(!is_redrawing(infoPtr)) return;
1745     TRACE(" invalidating rect=%s\n", wine_dbgstr_rect(rect));
1746     InvalidateRect(infoPtr->hwndSelf, rect, TRUE);
1747 }
1748 
LISTVIEW_InvalidateItem(const LISTVIEW_INFO * infoPtr,INT nItem)1749 static inline void LISTVIEW_InvalidateItem(const LISTVIEW_INFO *infoPtr, INT nItem)
1750 {
1751     RECT rcBox;
1752 
1753     if (!is_redrawing(infoPtr) || nItem < 0 || nItem >= infoPtr->nItemCount)
1754         return;
1755 
1756     LISTVIEW_GetItemBox(infoPtr, nItem, &rcBox);
1757     LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1758 }
1759 
LISTVIEW_InvalidateSubItem(const LISTVIEW_INFO * infoPtr,INT nItem,INT nSubItem)1760 static inline void LISTVIEW_InvalidateSubItem(const LISTVIEW_INFO *infoPtr, INT nItem, INT nSubItem)
1761 {
1762     POINT Origin, Position;
1763     RECT rcBox;
1764 
1765     if(!is_redrawing(infoPtr)) return;
1766     assert (infoPtr->uView == LV_VIEW_DETAILS);
1767     LISTVIEW_GetOrigin(infoPtr, &Origin);
1768     LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
1769     LISTVIEW_GetHeaderRect(infoPtr, nSubItem, &rcBox);
1770     rcBox.top = 0;
1771     rcBox.bottom = infoPtr->nItemHeight;
1772     OffsetRect(&rcBox, Origin.x, Origin.y + Position.y);
1773     LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1774 }
1775 
LISTVIEW_InvalidateList(const LISTVIEW_INFO * infoPtr)1776 static inline void LISTVIEW_InvalidateList(const LISTVIEW_INFO *infoPtr)
1777 {
1778     LISTVIEW_InvalidateRect(infoPtr, NULL);
1779 }
1780 
LISTVIEW_InvalidateColumn(const LISTVIEW_INFO * infoPtr,INT nColumn)1781 static inline void LISTVIEW_InvalidateColumn(const LISTVIEW_INFO *infoPtr, INT nColumn)
1782 {
1783     RECT rcCol;
1784 
1785     if(!is_redrawing(infoPtr)) return;
1786     LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
1787     rcCol.top = infoPtr->rcList.top;
1788     rcCol.bottom = infoPtr->rcList.bottom;
1789     LISTVIEW_InvalidateRect(infoPtr, &rcCol);
1790 }
1791 
1792 /***
1793  * DESCRIPTION:
1794  * Retrieves the number of items that can fit vertically in the client area.
1795  *
1796  * PARAMETER(S):
1797  * [I] infoPtr : valid pointer to the listview structure
1798  *
1799  * RETURN:
1800  * Number of items per row.
1801  */
LISTVIEW_GetCountPerRow(const LISTVIEW_INFO * infoPtr)1802 static inline INT LISTVIEW_GetCountPerRow(const LISTVIEW_INFO *infoPtr)
1803 {
1804     INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1805 
1806     return max(nListWidth/(infoPtr->nItemWidth ? infoPtr->nItemWidth : 1), 1);
1807 }
1808 
1809 /***
1810  * DESCRIPTION:
1811  * Retrieves the number of items that can fit horizontally in the client
1812  * area.
1813  *
1814  * PARAMETER(S):
1815  * [I] infoPtr : valid pointer to the listview structure
1816  *
1817  * RETURN:
1818  * Number of items per column.
1819  */
LISTVIEW_GetCountPerColumn(const LISTVIEW_INFO * infoPtr)1820 static inline INT LISTVIEW_GetCountPerColumn(const LISTVIEW_INFO *infoPtr)
1821 {
1822     INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1823 
1824     return infoPtr->nItemHeight ? max(nListHeight / infoPtr->nItemHeight, 1) : 0;
1825 }
1826 
1827 
1828 /*************************************************************************
1829  *		LISTVIEW_ProcessLetterKeys
1830  *
1831  *  Processes keyboard messages generated by pressing the letter keys
1832  *  on the keyboard.
1833  *  What this does is perform a case insensitive search from the
1834  *  current position with the following quirks:
1835  *  - If two chars or more are pressed in quick succession we search
1836  *    for the corresponding string (e.g. 'abc').
1837  *  - If there is a delay we wipe away the current search string and
1838  *    restart with just that char.
1839  *  - If the user keeps pressing the same character, whether slowly or
1840  *    fast, so that the search string is entirely composed of this
1841  *    character ('aaaaa' for instance), then we search for first item
1842  *    that starting with that character.
1843  *  - If the user types the above character in quick succession, then
1844  *    we must also search for the corresponding string ('aaaaa'), and
1845  *    go to that string if there is a match.
1846  *
1847  * PARAMETERS
1848  *   [I] hwnd : handle to the window
1849  *   [I] charCode : the character code, the actual character
1850  *   [I] keyData : key data
1851  *
1852  * RETURNS
1853  *
1854  *  Zero.
1855  *
1856  * BUGS
1857  *
1858  *  - The current implementation has a list of characters it will
1859  *    accept and it ignores everything else. In particular it will
1860  *    ignore accentuated characters which seems to match what
1861  *    Windows does. But I'm not sure it makes sense to follow
1862  *    Windows there.
1863  *  - We don't sound a beep when the search fails.
1864  *
1865  * SEE ALSO
1866  *
1867  *  TREEVIEW_ProcessLetterKeys
1868  */
LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO * infoPtr,WPARAM charCode,LPARAM keyData)1869 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData)
1870 {
1871     WCHAR buffer[MAX_PATH];
1872     DWORD prevTime;
1873     LVITEMW item;
1874     int startidx;
1875     INT nItem;
1876     INT diff;
1877 
1878     /* simple parameter checking */
1879     if (!charCode || !keyData || infoPtr->nItemCount == 0) return 0;
1880 
1881     /* only allow the valid WM_CHARs through */
1882     if (!iswalnum(charCode) &&
1883         charCode != '.' && charCode != '`' && charCode != '!' &&
1884         charCode != '@' && charCode != '#' && charCode != '$' &&
1885         charCode != '%' && charCode != '^' && charCode != '&' &&
1886         charCode != '*' && charCode != '(' && charCode != ')' &&
1887         charCode != '-' && charCode != '_' && charCode != '+' &&
1888         charCode != '=' && charCode != '\\'&& charCode != ']' &&
1889         charCode != '}' && charCode != '[' && charCode != '{' &&
1890         charCode != '/' && charCode != '?' && charCode != '>' &&
1891         charCode != '<' && charCode != ',' && charCode != '~')
1892         return 0;
1893 
1894     /* update the search parameters */
1895     prevTime = infoPtr->lastKeyPressTimestamp;
1896     infoPtr->lastKeyPressTimestamp = GetTickCount();
1897     diff = infoPtr->lastKeyPressTimestamp - prevTime;
1898 
1899     if (diff >= 0 && diff < KEY_DELAY)
1900     {
1901         if (infoPtr->nSearchParamLength < MAX_PATH - 1)
1902             infoPtr->szSearchParam[infoPtr->nSearchParamLength++] = charCode;
1903 
1904         if (infoPtr->charCode != charCode)
1905             infoPtr->charCode = charCode = 0;
1906     }
1907     else
1908     {
1909         infoPtr->charCode = charCode;
1910         infoPtr->szSearchParam[0] = charCode;
1911         infoPtr->nSearchParamLength = 1;
1912     }
1913 
1914     /* should start from next after focused item, so next item that matches
1915        will be selected, if there isn't any and focused matches it will be selected
1916        on second search stage from beginning of the list */
1917     if (infoPtr->nFocusedItem >= 0 && infoPtr->nItemCount > 1)
1918     {
1919         /* with some accumulated search data available start with current focus, otherwise
1920            it's excluded from search */
1921         startidx = infoPtr->nSearchParamLength > 1 ? infoPtr->nFocusedItem : infoPtr->nFocusedItem + 1;
1922         if (startidx == infoPtr->nItemCount) startidx = 0;
1923     }
1924     else
1925         startidx = 0;
1926 
1927     /* let application handle this for virtual listview */
1928     if (infoPtr->dwStyle & LVS_OWNERDATA)
1929     {
1930         NMLVFINDITEMW nmlv;
1931 
1932         memset(&nmlv.lvfi, 0, sizeof(nmlv.lvfi));
1933         nmlv.lvfi.flags = (LVFI_WRAP | LVFI_PARTIAL);
1934         nmlv.lvfi.psz = infoPtr->szSearchParam;
1935         nmlv.iStart = startidx;
1936 
1937         infoPtr->szSearchParam[infoPtr->nSearchParamLength] = 0;
1938 
1939         nItem = notify_hdr(infoPtr, LVN_ODFINDITEMW, (LPNMHDR)&nmlv.hdr);
1940     }
1941     else
1942     {
1943         int i = startidx, endidx;
1944 
1945         /* and search from the current position */
1946         nItem = -1;
1947         endidx = infoPtr->nItemCount;
1948 
1949         /* first search in [startidx, endidx), on failure continue in [0, startidx) */
1950         while (1)
1951         {
1952             /* start from first item if not found with >= startidx */
1953             if (i == infoPtr->nItemCount && startidx > 0)
1954             {
1955                 endidx = startidx;
1956                 startidx = 0;
1957             }
1958 
1959             for (i = startidx; i < endidx; i++)
1960             {
1961                 /* retrieve text */
1962                 item.mask = LVIF_TEXT;
1963                 item.iItem = i;
1964                 item.iSubItem = 0;
1965                 item.pszText = buffer;
1966                 item.cchTextMax = MAX_PATH;
1967                 if (!LISTVIEW_GetItemW(infoPtr, &item)) return 0;
1968 
1969                 if (!wcsnicmp(item.pszText, infoPtr->szSearchParam, infoPtr->nSearchParamLength))
1970                 {
1971                     nItem = i;
1972                     break;
1973                 }
1974                 /* this is used to find first char match when search string is not available yet,
1975                    otherwise every WM_CHAR will search to next item by first char, ignoring that we're
1976                    already waiting for user to complete a string */
1977                 else if (nItem == -1 && infoPtr->nSearchParamLength == 1 && !wcsnicmp(item.pszText, infoPtr->szSearchParam, 1))
1978                 {
1979                     /* this would work but we must keep looking for a longer match */
1980                     nItem = i;
1981                 }
1982             }
1983 
1984             if ( nItem != -1 || /* found something */
1985                  endidx != infoPtr->nItemCount || /* second search done */
1986                 (startidx == 0 && endidx == infoPtr->nItemCount) /* full range for first search */ )
1987                 break;
1988         };
1989     }
1990 
1991     if (nItem != -1)
1992         LISTVIEW_KeySelection(infoPtr, nItem, FALSE);
1993 
1994     return 0;
1995 }
1996 
1997 /*************************************************************************
1998  * LISTVIEW_UpdateHeaderSize [Internal]
1999  *
2000  * Function to resize the header control
2001  *
2002  * PARAMS
2003  * [I]  hwnd : handle to a window
2004  * [I]  nNewScrollPos : scroll pos to set
2005  *
2006  * RETURNS
2007  * None.
2008  */
LISTVIEW_UpdateHeaderSize(const LISTVIEW_INFO * infoPtr,INT nNewScrollPos)2009 static void LISTVIEW_UpdateHeaderSize(const LISTVIEW_INFO *infoPtr, INT nNewScrollPos)
2010 {
2011     RECT winRect;
2012     POINT point[2];
2013 
2014     TRACE("nNewScrollPos=%d\n", nNewScrollPos);
2015 
2016     if (!infoPtr->hwndHeader)  return;
2017 
2018     GetWindowRect(infoPtr->hwndHeader, &winRect);
2019     point[0].x = winRect.left;
2020     point[0].y = winRect.top;
2021     point[1].x = winRect.right;
2022     point[1].y = winRect.bottom;
2023 
2024     MapWindowPoints(HWND_DESKTOP, infoPtr->hwndSelf, point, 2);
2025     point[0].x = -nNewScrollPos;
2026     point[1].x += nNewScrollPos;
2027 
2028     SetWindowPos(infoPtr->hwndHeader,0,
2029         point[0].x,point[0].y,point[1].x,point[1].y,
2030         (infoPtr->dwStyle & LVS_NOCOLUMNHEADER) ? SWP_HIDEWINDOW : SWP_SHOWWINDOW |
2031         SWP_NOZORDER | SWP_NOACTIVATE);
2032 }
2033 
LISTVIEW_UpdateHScroll(LISTVIEW_INFO * infoPtr)2034 static INT LISTVIEW_UpdateHScroll(LISTVIEW_INFO *infoPtr)
2035 {
2036     SCROLLINFO horzInfo;
2037     INT dx;
2038 
2039     ZeroMemory(&horzInfo, sizeof(SCROLLINFO));
2040     horzInfo.cbSize = sizeof(SCROLLINFO);
2041     horzInfo.nPage = infoPtr->rcList.right - infoPtr->rcList.left;
2042 
2043     /* for now, we'll set info.nMax to the _count_, and adjust it later */
2044     if (infoPtr->uView == LV_VIEW_LIST)
2045     {
2046 	INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
2047 	horzInfo.nMax = (infoPtr->nItemCount + nPerCol - 1) / nPerCol;
2048 
2049 	/* scroll by at least one column per page */
2050 	if(horzInfo.nPage < infoPtr->nItemWidth)
2051 		horzInfo.nPage = infoPtr->nItemWidth;
2052 
2053 	if (infoPtr->nItemWidth)
2054 	    horzInfo.nPage /= infoPtr->nItemWidth;
2055     }
2056     else if (infoPtr->uView == LV_VIEW_DETAILS)
2057     {
2058 	horzInfo.nMax = infoPtr->nItemWidth;
2059     }
2060     else /* LV_VIEW_ICON, or LV_VIEW_SMALLICON */
2061     {
2062 	RECT rcView;
2063 
2064 	if (LISTVIEW_GetViewRect(infoPtr, &rcView)) horzInfo.nMax = rcView.right - rcView.left;
2065     }
2066 
2067     if (LISTVIEW_IsHeaderEnabled(infoPtr))
2068     {
2069 	if (DPA_GetPtrCount(infoPtr->hdpaColumns))
2070 	{
2071 	    RECT rcHeader;
2072 	    INT index;
2073 
2074 	    index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX,
2075                                  DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, 0);
2076 
2077 	    LISTVIEW_GetHeaderRect(infoPtr, index, &rcHeader);
2078 	    horzInfo.nMax = rcHeader.right;
2079 	    TRACE("horzInfo.nMax=%d\n", horzInfo.nMax);
2080 	}
2081     }
2082 
2083     horzInfo.fMask = SIF_RANGE | SIF_PAGE;
2084     horzInfo.nMax = max(horzInfo.nMax - 1, 0);
2085 #ifdef __REACTOS__ /* CORE-16466 part 1 of 4 */
2086     horzInfo.nMax = (horzInfo.nPage == 0 ? 0 : horzInfo.nMax);
2087 #endif
2088     dx = GetScrollPos(infoPtr->hwndSelf, SB_HORZ);
2089     dx -= SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo, TRUE);
2090     TRACE("horzInfo=%s\n", debugscrollinfo(&horzInfo));
2091 
2092     /* Update the Header Control */
2093     if (infoPtr->hwndHeader)
2094     {
2095 	horzInfo.fMask = SIF_POS;
2096 	GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo);
2097 	LISTVIEW_UpdateHeaderSize(infoPtr, horzInfo.nPos);
2098     }
2099 
2100     LISTVIEW_UpdateSize(infoPtr);
2101     return dx;
2102 }
2103 
LISTVIEW_UpdateVScroll(LISTVIEW_INFO * infoPtr)2104 static INT LISTVIEW_UpdateVScroll(LISTVIEW_INFO *infoPtr)
2105 {
2106     SCROLLINFO vertInfo;
2107     INT dy;
2108 
2109     ZeroMemory(&vertInfo, sizeof(SCROLLINFO));
2110     vertInfo.cbSize = sizeof(SCROLLINFO);
2111 #ifdef __REACTOS__ /* CORE-16466 part 2 of 4 */
2112     vertInfo.nPage = max(0, infoPtr->rcList.bottom - infoPtr->rcList.top);
2113 #else
2114     vertInfo.nPage = infoPtr->rcList.bottom - infoPtr->rcList.top;
2115 #endif
2116 
2117     if (infoPtr->uView == LV_VIEW_DETAILS)
2118     {
2119 #ifdef __REACTOS__ /* CORE-16466 part 3a of 4 */
2120       if (vertInfo.nPage != 0)
2121       {
2122 #endif
2123 	vertInfo.nMax = infoPtr->nItemCount;
2124 
2125 	/* scroll by at least one page */
2126 	if(vertInfo.nPage < infoPtr->nItemHeight)
2127 	  vertInfo.nPage = infoPtr->nItemHeight;
2128 
2129         if (infoPtr->nItemHeight > 0)
2130             vertInfo.nPage /= infoPtr->nItemHeight;
2131 #ifdef __REACTOS__ /* CORE-16466 part 3b of 4 */
2132       }
2133 #endif
2134     }
2135     else if (infoPtr->uView != LV_VIEW_LIST) /* LV_VIEW_ICON, or LV_VIEW_SMALLICON */
2136     {
2137 	RECT rcView;
2138 
2139 	if (LISTVIEW_GetViewRect(infoPtr, &rcView)) vertInfo.nMax = rcView.bottom - rcView.top;
2140     }
2141 
2142     vertInfo.fMask = SIF_RANGE | SIF_PAGE;
2143     vertInfo.nMax = max(vertInfo.nMax - 1, 0);
2144 #ifdef __REACTOS__ /* CORE-16466 part 4 of 4 */
2145     vertInfo.nMax = (vertInfo.nPage == 0 ? 0 : vertInfo.nMax);
2146 #endif
2147     dy = GetScrollPos(infoPtr->hwndSelf, SB_VERT);
2148     dy -= SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &vertInfo, TRUE);
2149     TRACE("vertInfo=%s\n", debugscrollinfo(&vertInfo));
2150 
2151     LISTVIEW_UpdateSize(infoPtr);
2152     return dy;
2153 }
2154 
2155 /***
2156  * DESCRIPTION:
2157  * Update the scrollbars. This function should be called whenever
2158  * the content, size or view changes.
2159  *
2160  * PARAMETER(S):
2161  * [I] infoPtr : valid pointer to the listview structure
2162  *
2163  * RETURN:
2164  * None
2165  */
LISTVIEW_UpdateScroll(LISTVIEW_INFO * infoPtr)2166 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *infoPtr)
2167 {
2168     INT dx, dy, pass;
2169 
2170     if ((infoPtr->dwStyle & LVS_NOSCROLL) || !is_redrawing(infoPtr)) return;
2171 
2172     /* Setting the horizontal scroll can change the listview size
2173      * (and potentially everything else) so we need to recompute
2174      * everything again for the vertical scroll and vice-versa
2175      */
2176     for (dx = 0, dy = 0, pass = 0; pass <= 1; pass++)
2177     {
2178         dx += LISTVIEW_UpdateHScroll(infoPtr);
2179         dy += LISTVIEW_UpdateVScroll(infoPtr);
2180     }
2181 
2182     /* Change of the range may have changed the scroll pos. If so move the content */
2183     if (dx != 0 || dy != 0)
2184     {
2185         RECT listRect;
2186         listRect = infoPtr->rcList;
2187         ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &listRect, &listRect, 0, 0,
2188             SW_ERASE | SW_INVALIDATE);
2189     }
2190 }
2191 
2192 
2193 /***
2194  * DESCRIPTION:
2195  * Shows/hides the focus rectangle.
2196  *
2197  * PARAMETER(S):
2198  * [I] infoPtr : valid pointer to the listview structure
2199  * [I] fShow : TRUE to show the focus, FALSE to hide it.
2200  *
2201  * RETURN:
2202  * None
2203  */
LISTVIEW_ShowFocusRect(const LISTVIEW_INFO * infoPtr,BOOL fShow)2204 static void LISTVIEW_ShowFocusRect(const LISTVIEW_INFO *infoPtr, BOOL fShow)
2205 {
2206     HDC hdc;
2207 
2208     TRACE("fShow=%d, nItem=%d\n", fShow, infoPtr->nFocusedItem);
2209 
2210     if (infoPtr->nFocusedItem < 0) return;
2211 
2212     /* we need some gymnastics in ICON mode to handle large items */
2213     if (infoPtr->uView == LV_VIEW_ICON)
2214     {
2215 	RECT rcBox;
2216 
2217 	LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcBox);
2218 	if ((rcBox.bottom - rcBox.top) > infoPtr->nItemHeight)
2219 	{
2220 	    LISTVIEW_InvalidateRect(infoPtr, &rcBox);
2221 	    return;
2222 	}
2223     }
2224 
2225     if (!(hdc = GetDC(infoPtr->hwndSelf))) return;
2226 
2227     /* for some reason, owner draw should work only in report mode */
2228     if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (infoPtr->uView == LV_VIEW_DETAILS))
2229     {
2230 	DRAWITEMSTRUCT dis;
2231 	LVITEMW item;
2232 
2233 	HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2234 	HFONT hOldFont = SelectObject(hdc, hFont);
2235 
2236         item.iItem = infoPtr->nFocusedItem;
2237 	item.iSubItem = 0;
2238         item.mask = LVIF_PARAM;
2239 	if (!LISTVIEW_GetItemW(infoPtr, &item)) goto done;
2240 
2241 	ZeroMemory(&dis, sizeof(dis));
2242 	dis.CtlType = ODT_LISTVIEW;
2243 	dis.CtlID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
2244 	dis.itemID = item.iItem;
2245 	dis.itemAction = ODA_FOCUS;
2246 	if (fShow) dis.itemState |= ODS_FOCUS;
2247 	dis.hwndItem = infoPtr->hwndSelf;
2248 	dis.hDC = hdc;
2249 	LISTVIEW_GetItemBox(infoPtr, dis.itemID, &dis.rcItem);
2250 	dis.itemData = item.lParam;
2251 
2252 	SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
2253 
2254 	SelectObject(hdc, hOldFont);
2255     }
2256     else
2257         LISTVIEW_InvalidateItem(infoPtr, infoPtr->nFocusedItem);
2258 
2259 done:
2260     ReleaseDC(infoPtr->hwndSelf, hdc);
2261 }
2262 
2263 /***
2264  * Invalidates all visible selected items.
2265  */
LISTVIEW_InvalidateSelectedItems(const LISTVIEW_INFO * infoPtr)2266 static void LISTVIEW_InvalidateSelectedItems(const LISTVIEW_INFO *infoPtr)
2267 {
2268     ITERATOR i;
2269 
2270     iterator_frameditems(&i, infoPtr, &infoPtr->rcList);
2271     while(iterator_next(&i))
2272     {
2273 #ifdef __REACTOS__
2274 	if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED | LVIS_CUT))
2275 #else
2276 	if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED))
2277 #endif
2278 	    LISTVIEW_InvalidateItem(infoPtr, i.nItem);
2279     }
2280     iterator_destroy(&i);
2281 }
2282 
2283 
2284 /***
2285  * DESCRIPTION:            [INTERNAL]
2286  * Computes an item's (left,top) corner, relative to rcView.
2287  * That is, the position has NOT been made relative to the Origin.
2288  * This is deliberate, to avoid computing the Origin over, and
2289  * over again, when this function is called in a loop. Instead,
2290  * one can factor the computation of the Origin before the loop,
2291  * and offset the value returned by this function, on every iteration.
2292  *
2293  * PARAMETER(S):
2294  * [I] infoPtr : valid pointer to the listview structure
2295  * [I] nItem  : item number
2296  * [O] lpptOrig : item top, left corner
2297  *
2298  * RETURN:
2299  *   None.
2300  */
LISTVIEW_GetItemOrigin(const LISTVIEW_INFO * infoPtr,INT nItem,LPPOINT lpptPosition)2301 static void LISTVIEW_GetItemOrigin(const LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
2302 {
2303     assert(nItem >= 0 && nItem < infoPtr->nItemCount);
2304 
2305     if ((infoPtr->uView == LV_VIEW_SMALLICON) || (infoPtr->uView == LV_VIEW_ICON))
2306     {
2307 	lpptPosition->x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
2308 	lpptPosition->y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
2309     }
2310     else if (infoPtr->uView == LV_VIEW_LIST)
2311     {
2312         INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
2313 	lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
2314 	lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight;
2315     }
2316     else /* LV_VIEW_DETAILS */
2317     {
2318 	lpptPosition->x = REPORT_MARGINX;
2319 	/* item is always at zero indexed column */
2320 	if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0)
2321 	    lpptPosition->x += LISTVIEW_GetColumnInfo(infoPtr, 0)->rcHeader.left;
2322 	lpptPosition->y = nItem * infoPtr->nItemHeight;
2323     }
2324 }
2325 
2326 /***
2327  * DESCRIPTION:            [INTERNAL]
2328  * Compute the rectangles of an item.  This is to localize all
2329  * the computations in one place. If you are not interested in some
2330  * of these values, simply pass in a NULL -- the function is smart
2331  * enough to compute only what's necessary. The function computes
2332  * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
2333  * one, the BOX rectangle. This rectangle is very cheap to compute,
2334  * and is guaranteed to contain all the other rectangles. Computing
2335  * the ICON rect is also cheap, but all the others are potentially
2336  * expensive. This gives an easy and effective optimization when
2337  * searching (like point inclusion, or rectangle intersection):
2338  * first test against the BOX, and if TRUE, test against the desired
2339  * rectangle.
2340  * If the function does not have all the necessary information
2341  * to computed the requested rectangles, will crash with a
2342  * failed assertion. This is done so we catch all programming
2343  * errors, given that the function is called only from our code.
2344  *
2345  * We have the following 'special' meanings for a few fields:
2346  *   * If LVIS_FOCUSED is set, we assume the item has the focus
2347  *     This is important in ICON mode, where it might get a larger
2348  *     then usual rectangle
2349  *
2350  * Please note that subitem support works only in REPORT mode.
2351  *
2352  * PARAMETER(S):
2353  * [I] infoPtr : valid pointer to the listview structure
2354  * [I] lpLVItem : item to compute the measures for
2355  * [O] lprcBox : ptr to Box rectangle
2356  *                Same as LVM_GETITEMRECT with LVIR_BOUNDS
2357  * [0] lprcSelectBox : ptr to select box rectangle
2358  *  		  Same as LVM_GETITEMRECT with LVIR_SELECTEDBOUNDS
2359  * [O] lprcIcon : ptr to Icon rectangle
2360  *                Same as LVM_GETITEMRECT with LVIR_ICON
2361  * [O] lprcStateIcon: ptr to State Icon rectangle
2362  * [O] lprcLabel : ptr to Label rectangle
2363  *                Same as LVM_GETITEMRECT with LVIR_LABEL
2364  *
2365  * RETURN:
2366  *   None.
2367  */
LISTVIEW_GetItemMetrics(const LISTVIEW_INFO * infoPtr,const LVITEMW * lpLVItem,LPRECT lprcBox,LPRECT lprcSelectBox,LPRECT lprcIcon,LPRECT lprcStateIcon,LPRECT lprcLabel)2368 static void LISTVIEW_GetItemMetrics(const LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem,
2369 				    LPRECT lprcBox, LPRECT lprcSelectBox,
2370 				    LPRECT lprcIcon, LPRECT lprcStateIcon, LPRECT lprcLabel)
2371 {
2372     BOOL doSelectBox = FALSE, doIcon = FALSE, doLabel = FALSE, oversizedBox = FALSE;
2373     RECT Box, SelectBox, Icon, Label;
2374     COLUMN_INFO *lpColumnInfo = NULL;
2375     SIZE labelSize = { 0, 0 };
2376 
2377     TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem, TRUE));
2378 
2379     /* Be smart and try to figure out the minimum we have to do */
2380     if (lpLVItem->iSubItem) assert(infoPtr->uView == LV_VIEW_DETAILS);
2381     if (infoPtr->uView == LV_VIEW_ICON && (lprcBox || lprcLabel))
2382     {
2383 	assert((lpLVItem->mask & LVIF_STATE) && (lpLVItem->stateMask & LVIS_FOCUSED));
2384 	if (lpLVItem->state & LVIS_FOCUSED) oversizedBox = doLabel = TRUE;
2385     }
2386     if (lprcSelectBox) doSelectBox = TRUE;
2387     if (lprcLabel) doLabel = TRUE;
2388     if (doLabel || lprcIcon || lprcStateIcon) doIcon = TRUE;
2389     if (doSelectBox)
2390     {
2391         doIcon = TRUE;
2392         doLabel = TRUE;
2393     }
2394 
2395     /************************************************************/
2396     /* compute the box rectangle (it should be cheap to do)     */
2397     /************************************************************/
2398     if (lpLVItem->iSubItem || infoPtr->uView == LV_VIEW_DETAILS)
2399 	lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpLVItem->iSubItem);
2400 
2401     if (lpLVItem->iSubItem)
2402     {
2403 	Box = lpColumnInfo->rcHeader;
2404     }
2405     else
2406     {
2407 	Box.left = 0;
2408 	Box.right = infoPtr->nItemWidth;
2409     }
2410     Box.top = 0;
2411     Box.bottom = infoPtr->nItemHeight;
2412 
2413     /******************************************************************/
2414     /* compute ICON bounding box (ala LVM_GETITEMRECT) and STATEICON  */
2415     /******************************************************************/
2416     if (doIcon)
2417     {
2418 	LONG state_width = 0;
2419 
2420 	if (infoPtr->himlState && lpLVItem->iSubItem == 0)
2421 	    state_width = infoPtr->iconStateSize.cx;
2422 
2423 	if (infoPtr->uView == LV_VIEW_ICON)
2424 	{
2425 	    Icon.left   = Box.left + state_width;
2426 	    if (infoPtr->himlNormal)
2427 		Icon.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx - state_width) / 2;
2428 	    Icon.top    = Box.top + ICON_TOP_PADDING;
2429 	    Icon.right  = Icon.left;
2430 	    Icon.bottom = Icon.top;
2431 	    if (infoPtr->himlNormal)
2432 	    {
2433 		Icon.right  += infoPtr->iconSize.cx;
2434 		Icon.bottom += infoPtr->iconSize.cy;
2435 	    }
2436 	}
2437 	else /* LV_VIEW_SMALLICON, LV_VIEW_LIST or LV_VIEW_DETAILS */
2438 	{
2439 	    Icon.left   = Box.left + state_width;
2440 
2441 	    if (infoPtr->uView == LV_VIEW_DETAILS && lpLVItem->iSubItem == 0)
2442 	    {
2443 		/* we need the indent in report mode */
2444 		assert(lpLVItem->mask & LVIF_INDENT);
2445 		Icon.left += infoPtr->iconSize.cx * lpLVItem->iIndent + REPORT_MARGINX;
2446 	    }
2447 
2448 	    Icon.top    = Box.top;
2449 	    Icon.right  = Icon.left;
2450 	    if (infoPtr->himlSmall &&
2451                 (!lpColumnInfo || lpLVItem->iSubItem == 0 ||
2452                  ((infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES) && lpLVItem->iImage != I_IMAGECALLBACK)))
2453 		Icon.right += infoPtr->iconSize.cx;
2454 	    Icon.bottom = Icon.top + infoPtr->iconSize.cy;
2455 	}
2456 	if(lprcIcon) *lprcIcon = Icon;
2457 	TRACE("    - icon=%s\n", wine_dbgstr_rect(&Icon));
2458 
2459         /* TODO: is this correct? */
2460         if (lprcStateIcon)
2461         {
2462             lprcStateIcon->left = Icon.left - state_width;
2463             lprcStateIcon->right = Icon.left;
2464             lprcStateIcon->top = Icon.top;
2465             lprcStateIcon->bottom = lprcStateIcon->top + infoPtr->iconSize.cy;
2466             TRACE("    - state icon=%s\n", wine_dbgstr_rect(lprcStateIcon));
2467         }
2468      }
2469      else Icon.right = 0;
2470 
2471     /************************************************************/
2472     /* compute LABEL bounding box (ala LVM_GETITEMRECT)         */
2473     /************************************************************/
2474     if (doLabel)
2475     {
2476 	/* calculate how far to the right can the label stretch */
2477 	Label.right = Box.right;
2478 	if (infoPtr->uView == LV_VIEW_DETAILS)
2479 	{
2480 	    if (lpLVItem->iSubItem == 0)
2481 	    {
2482 		/* we need a zero based rect here */
2483 		Label = lpColumnInfo->rcHeader;
2484 		OffsetRect(&Label, -Label.left, 0);
2485 	    }
2486 	}
2487 
2488 	if (lpLVItem->iSubItem || ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && infoPtr->uView == LV_VIEW_DETAILS))
2489 	{
2490 	   labelSize.cx = infoPtr->nItemWidth;
2491 	   labelSize.cy = infoPtr->nItemHeight;
2492 	   goto calc_label;
2493 	}
2494 
2495 	/* we need the text in non owner draw mode */
2496 	assert(lpLVItem->mask & LVIF_TEXT);
2497 	if (is_text(lpLVItem->pszText))
2498         {
2499     	    HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2500     	    HDC hdc = GetDC(infoPtr->hwndSelf);
2501     	    HFONT hOldFont = SelectObject(hdc, hFont);
2502 	    UINT uFormat;
2503 	    RECT rcText;
2504 
2505 	    /* compute rough rectangle where the label will go */
2506 	    SetRectEmpty(&rcText);
2507 	    rcText.right = infoPtr->nItemWidth - TRAILING_LABEL_PADDING;
2508 	    rcText.bottom = infoPtr->nItemHeight;
2509 	    if (infoPtr->uView == LV_VIEW_ICON)
2510 		rcText.bottom -= ICON_TOP_PADDING + infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2511 
2512 	    /* now figure out the flags */
2513 	    if (infoPtr->uView == LV_VIEW_ICON)
2514 		uFormat = oversizedBox ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS;
2515 	    else
2516 		uFormat = LV_SL_DT_FLAGS;
2517 
2518     	    DrawTextW (hdc, lpLVItem->pszText, -1, &rcText, uFormat | DT_CALCRECT);
2519 
2520 	    if (rcText.right != rcText.left)
2521 	        labelSize.cx = min(rcText.right - rcText.left + TRAILING_LABEL_PADDING, infoPtr->nItemWidth);
2522 
2523 	    labelSize.cy = rcText.bottom - rcText.top;
2524 
2525     	    SelectObject(hdc, hOldFont);
2526     	    ReleaseDC(infoPtr->hwndSelf, hdc);
2527 	}
2528 
2529 calc_label:
2530 	if (infoPtr->uView == LV_VIEW_ICON)
2531 	{
2532 	    Label.left = Box.left + (infoPtr->nItemWidth - labelSize.cx) / 2;
2533 	    Label.top  = Box.top + ICON_TOP_PADDING_HITABLE +
2534 		         infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2535 	    Label.right = Label.left + labelSize.cx;
2536 	    Label.bottom = Label.top + infoPtr->nItemHeight;
2537 	    if (!oversizedBox && labelSize.cy > infoPtr->ntmHeight)
2538 	    {
2539 		labelSize.cy = min(Box.bottom - Label.top, labelSize.cy);
2540 		labelSize.cy /= infoPtr->ntmHeight;
2541 		labelSize.cy = max(labelSize.cy, 1);
2542 		labelSize.cy *= infoPtr->ntmHeight;
2543 	     }
2544 	     Label.bottom = Label.top + labelSize.cy + HEIGHT_PADDING;
2545 	}
2546 	else if (infoPtr->uView == LV_VIEW_DETAILS)
2547 	{
2548 	    Label.left = Icon.right;
2549 	    Label.top = Box.top;
2550 	    Label.right = lpLVItem->iSubItem ? lpColumnInfo->rcHeader.right :
2551 			  lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left;
2552 	    Label.bottom = Label.top + infoPtr->nItemHeight;
2553 	}
2554 	else /* LV_VIEW_SMALLICON or LV_VIEW_LIST */
2555 	{
2556 	    Label.left = Icon.right;
2557 	    Label.top = Box.top;
2558 	    Label.right = min(Label.left + labelSize.cx, Label.right);
2559 	    Label.bottom = Label.top + infoPtr->nItemHeight;
2560 	}
2561 
2562 	if (lprcLabel) *lprcLabel = Label;
2563 	TRACE("    - label=%s\n", wine_dbgstr_rect(&Label));
2564     }
2565 
2566     /************************************************************/
2567     /* compute SELECT bounding box                              */
2568     /************************************************************/
2569     if (doSelectBox)
2570     {
2571 	if (infoPtr->uView == LV_VIEW_DETAILS)
2572 	{
2573 	    SelectBox.left = Icon.left;
2574 	    SelectBox.top = Box.top;
2575 	    SelectBox.bottom = Box.bottom;
2576 
2577 	    if (labelSize.cx)
2578 	        SelectBox.right = min(Label.left + labelSize.cx, Label.right);
2579 	    else
2580 	        SelectBox.right = min(Label.left + MAX_EMPTYTEXT_SELECT_WIDTH, Label.right);
2581 	}
2582 	else
2583 	{
2584 	    UnionRect(&SelectBox, &Icon, &Label);
2585 	}
2586 	if (lprcSelectBox) *lprcSelectBox = SelectBox;
2587 	TRACE("    - select box=%s\n", wine_dbgstr_rect(&SelectBox));
2588     }
2589 
2590     /* Fix the Box if necessary */
2591     if (lprcBox)
2592     {
2593 	if (oversizedBox) UnionRect(lprcBox, &Box, &Label);
2594 	else *lprcBox = Box;
2595     }
2596     TRACE("    - box=%s\n", wine_dbgstr_rect(&Box));
2597 }
2598 
2599 /***
2600  * DESCRIPTION:            [INTERNAL]
2601  *
2602  * PARAMETER(S):
2603  * [I] infoPtr : valid pointer to the listview structure
2604  * [I] nItem : item number
2605  * [O] lprcBox : ptr to Box rectangle
2606  *
2607  * RETURN:
2608  *   None.
2609  */
LISTVIEW_GetItemBox(const LISTVIEW_INFO * infoPtr,INT nItem,LPRECT lprcBox)2610 static void LISTVIEW_GetItemBox(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprcBox)
2611 {
2612     WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
2613     POINT Position, Origin;
2614     LVITEMW lvItem;
2615 
2616     LISTVIEW_GetOrigin(infoPtr, &Origin);
2617     LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
2618 
2619     /* Be smart and try to figure out the minimum we have to do */
2620     lvItem.mask = 0;
2621     if (infoPtr->uView == LV_VIEW_ICON && infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
2622 	lvItem.mask |= LVIF_TEXT;
2623     lvItem.iItem = nItem;
2624     lvItem.iSubItem = 0;
2625     lvItem.pszText = szDispText;
2626     lvItem.cchTextMax = DISP_TEXT_SIZE;
2627     if (lvItem.mask) LISTVIEW_GetItemW(infoPtr, &lvItem);
2628     if (infoPtr->uView == LV_VIEW_ICON)
2629     {
2630 	lvItem.mask |= LVIF_STATE;
2631 	lvItem.stateMask = LVIS_FOCUSED;
2632 	lvItem.state = (lvItem.mask & LVIF_TEXT ? LVIS_FOCUSED : 0);
2633     }
2634     LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprcBox, 0, 0, 0, 0);
2635 
2636     if (infoPtr->uView == LV_VIEW_DETAILS && infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT &&
2637         SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX, 0, 0))
2638     {
2639         OffsetRect(lprcBox, Origin.x, Position.y + Origin.y);
2640     }
2641     else
2642         OffsetRect(lprcBox, Position.x + Origin.x, Position.y + Origin.y);
2643 }
2644 
2645 /* LISTVIEW_MapIdToIndex helper */
MapIdSearchCompare(LPVOID p1,LPVOID p2,LPARAM lParam)2646 static INT CALLBACK MapIdSearchCompare(LPVOID p1, LPVOID p2, LPARAM lParam)
2647 {
2648     ITEM_ID *id1 = (ITEM_ID*)p1;
2649     ITEM_ID *id2 = (ITEM_ID*)p2;
2650 
2651     if (id1->id == id2->id) return 0;
2652 
2653     return (id1->id < id2->id) ? -1 : 1;
2654 }
2655 
2656 /***
2657  * DESCRIPTION:
2658  * Returns the item index for id specified.
2659  *
2660  * PARAMETER(S):
2661  * [I] infoPtr : valid pointer to the listview structure
2662  * [I] iID : item id to get index for
2663  *
2664  * RETURN:
2665  * Item index, or -1 on failure.
2666  */
LISTVIEW_MapIdToIndex(const LISTVIEW_INFO * infoPtr,UINT iID)2667 static INT LISTVIEW_MapIdToIndex(const LISTVIEW_INFO *infoPtr, UINT iID)
2668 {
2669     ITEM_ID ID;
2670     INT index;
2671 
2672     TRACE("iID=%d\n", iID);
2673 
2674     if (infoPtr->dwStyle & LVS_OWNERDATA) return -1;
2675     if (infoPtr->nItemCount == 0) return -1;
2676 
2677     ID.id = iID;
2678     index = DPA_Search(infoPtr->hdpaItemIds, &ID, -1, MapIdSearchCompare, 0, DPAS_SORTED);
2679 
2680     if (index != -1)
2681     {
2682         ITEM_ID *lpID = DPA_GetPtr(infoPtr->hdpaItemIds, index);
2683         return DPA_GetPtrIndex(infoPtr->hdpaItems, lpID->item);
2684     }
2685 
2686     return -1;
2687 }
2688 
2689 /***
2690  * DESCRIPTION:
2691  * Returns the item id for index given.
2692  *
2693  * PARAMETER(S):
2694  * [I] infoPtr : valid pointer to the listview structure
2695  * [I] iItem : item index to get id for
2696  *
2697  * RETURN:
2698  * Item id.
2699  */
LISTVIEW_MapIndexToId(const LISTVIEW_INFO * infoPtr,INT iItem)2700 static DWORD LISTVIEW_MapIndexToId(const LISTVIEW_INFO *infoPtr, INT iItem)
2701 {
2702     ITEM_INFO *lpItem;
2703     HDPA hdpaSubItems;
2704 
2705     TRACE("iItem=%d\n", iItem);
2706 
2707     if (infoPtr->dwStyle & LVS_OWNERDATA) return -1;
2708     if (iItem < 0 || iItem >= infoPtr->nItemCount) return -1;
2709 
2710     hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, iItem);
2711     lpItem = DPA_GetPtr(hdpaSubItems, 0);
2712 
2713     return lpItem->id->id;
2714 }
2715 
2716 /***
2717  * DESCRIPTION:
2718  * Returns the current icon position, and advances it along the top.
2719  * The returned position is not offset by Origin.
2720  *
2721  * PARAMETER(S):
2722  * [I] infoPtr : valid pointer to the listview structure
2723  * [O] lpPos : will get the current icon position
2724  * [I] nItem : item id to get position for
2725  *
2726  * RETURN:
2727  * None
2728  */
2729 #ifdef __REACTOS__
LISTVIEW_NextIconPosTop(LISTVIEW_INFO * infoPtr,LPPOINT lpPos,INT nItem)2730 static void LISTVIEW_NextIconPosTop(LISTVIEW_INFO *infoPtr, LPPOINT lpPos, INT nItem)
2731 #else
2732 static void LISTVIEW_NextIconPosTop(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2733 #endif
2734 {
2735     INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
2736 
2737     *lpPos = infoPtr->currIconPos;
2738 
2739     infoPtr->currIconPos.x += infoPtr->nItemWidth;
2740     if (infoPtr->currIconPos.x + infoPtr->nItemWidth <= nListWidth) return;
2741 
2742     infoPtr->currIconPos.x  = 0;
2743     infoPtr->currIconPos.y += infoPtr->nItemHeight;
2744 }
2745 
2746 
2747 /***
2748  * DESCRIPTION:
2749  * Returns the current icon position, and advances it down the left edge.
2750  * The returned position is not offset by Origin.
2751  *
2752  * PARAMETER(S):
2753  * [I] infoPtr : valid pointer to the listview structure
2754  * [O] lpPos : will get the current icon position
2755  * [I] nItem : item id to get position for
2756  *
2757  * RETURN:
2758  * None
2759  */
2760 #ifdef __REACTOS__
LISTVIEW_NextIconPosLeft(LISTVIEW_INFO * infoPtr,LPPOINT lpPos,INT nItem)2761 static void LISTVIEW_NextIconPosLeft(LISTVIEW_INFO *infoPtr, LPPOINT lpPos, INT nItem)
2762 #else
2763 static void LISTVIEW_NextIconPosLeft(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2764 #endif
2765 {
2766     INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
2767 
2768     *lpPos = infoPtr->currIconPos;
2769 
2770     infoPtr->currIconPos.y += infoPtr->nItemHeight;
2771     if (infoPtr->currIconPos.y + infoPtr->nItemHeight <= nListHeight) return;
2772 
2773     infoPtr->currIconPos.x += infoPtr->nItemWidth;
2774     infoPtr->currIconPos.y  = 0;
2775 }
2776 
2777 
2778 #ifdef __REACTOS__
2779 /***
2780  * DESCRIPTION:
2781  * Returns the grid position closest to the already placed icon.
2782  * The returned position is not offset by Origin.
2783  *
2784  * PARAMETER(S):
2785  * [I] infoPtr : valid pointer to the listview structure
2786  * [O] lpPos : will get the current icon position
2787  * [I] nItem : item id to get position for
2788  *
2789  * RETURN:
2790  * None
2791  */
LISTVIEW_NextIconPosSnap(LISTVIEW_INFO * infoPtr,LPPOINT lpPos,INT nItem)2792 static void LISTVIEW_NextIconPosSnap(LISTVIEW_INFO *infoPtr, LPPOINT lpPos, INT nItem)
2793 {
2794     INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
2795     INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
2796     INT nMaxColumns = nListWidth / infoPtr->nItemWidth;
2797     INT nMaxRows = nListHeight / infoPtr->nItemHeight;
2798     POINT oldPosition;
2799 
2800     // get the existing x and y position and then snap to the closest grid square
2801     oldPosition.x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
2802     oldPosition.y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
2803 
2804     // FIXME: This could should deal with multiple icons in the same grid square
2805     // equivalent of max(0, round(oldPosition / itemSize) * itemSize), but without need for 'round' function
2806     (*lpPos).x = max(0, oldPosition.x + (infoPtr->nItemWidth >> 1) - (oldPosition.x + (infoPtr->nItemWidth >> 1)) % infoPtr->nItemWidth);
2807     (*lpPos).y = max(0, oldPosition.y + (infoPtr->nItemHeight >> 1) - (oldPosition.y + (infoPtr->nItemHeight >> 1)) % infoPtr->nItemHeight);
2808 
2809     // deal with any icons that have gone out of range
2810     if ((*lpPos).x > nListWidth) (*lpPos).x = nMaxColumns * infoPtr->nItemWidth;
2811     if ((*lpPos).y > nListHeight) (*lpPos).y = nMaxRows * infoPtr->nItemHeight;
2812 }
2813 #endif
2814 
2815 
2816 /***
2817  * DESCRIPTION:
2818  * Moves an icon to the specified position.
2819  * It takes care of invalidating the item, etc.
2820  *
2821  * PARAMETER(S):
2822  * [I] infoPtr : valid pointer to the listview structure
2823  * [I] nItem : the item to move
2824  * [I] lpPos : the new icon position
2825  * [I] isNew : flags the item as being new
2826  *
2827  * RETURN:
2828  *   Success: TRUE
2829  *   Failure: FALSE
2830  */
LISTVIEW_MoveIconTo(const LISTVIEW_INFO * infoPtr,INT nItem,const POINT * lppt,BOOL isNew)2831 static BOOL LISTVIEW_MoveIconTo(const LISTVIEW_INFO *infoPtr, INT nItem, const POINT *lppt, BOOL isNew)
2832 {
2833     POINT old;
2834 
2835     if (!isNew)
2836     {
2837         old.x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
2838         old.y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
2839 
2840         if (lppt->x == old.x && lppt->y == old.y) return TRUE;
2841 	LISTVIEW_InvalidateItem(infoPtr, nItem);
2842     }
2843 
2844     /* Allocating a POINTER for every item is too resource intensive,
2845      * so we'll keep the (x,y) in different arrays */
2846     if (!DPA_SetPtr(infoPtr->hdpaPosX, nItem, (void *)(LONG_PTR)lppt->x)) return FALSE;
2847     if (!DPA_SetPtr(infoPtr->hdpaPosY, nItem, (void *)(LONG_PTR)lppt->y)) return FALSE;
2848 
2849     LISTVIEW_InvalidateItem(infoPtr, nItem);
2850 
2851     return TRUE;
2852 }
2853 
2854 /***
2855  * DESCRIPTION:
2856  * Arranges listview items in icon display mode.
2857  *
2858  * PARAMETER(S):
2859  * [I] infoPtr : valid pointer to the listview structure
2860  * [I] nAlignCode : alignment code
2861  *
2862  * RETURN:
2863  *   SUCCESS : TRUE
2864  *   FAILURE : FALSE
2865  */
LISTVIEW_Arrange(LISTVIEW_INFO * infoPtr,INT nAlignCode)2866 static BOOL LISTVIEW_Arrange(LISTVIEW_INFO *infoPtr, INT nAlignCode)
2867 {
2868 #ifdef __REACTOS__
2869     void (*next_pos)(LISTVIEW_INFO *, LPPOINT, INT);
2870 #else
2871     void (*next_pos)(LISTVIEW_INFO *, LPPOINT);
2872 #endif
2873     POINT pos;
2874     INT i;
2875 
2876     if (infoPtr->uView != LV_VIEW_ICON && infoPtr->uView != LV_VIEW_SMALLICON) return FALSE;
2877 
2878     TRACE("nAlignCode=%d\n", nAlignCode);
2879 
2880     if (nAlignCode == LVA_DEFAULT)
2881     {
2882 	if (infoPtr->dwStyle & LVS_ALIGNLEFT) nAlignCode = LVA_ALIGNLEFT;
2883         else nAlignCode = LVA_ALIGNTOP;
2884     }
2885 
2886     switch (nAlignCode)
2887     {
2888     case LVA_ALIGNLEFT:  next_pos = LISTVIEW_NextIconPosLeft; break;
2889     case LVA_ALIGNTOP:   next_pos = LISTVIEW_NextIconPosTop;  break;
2890 #ifdef __REACTOS__
2891     case LVA_SNAPTOGRID: next_pos = LISTVIEW_NextIconPosSnap; break;
2892 #else
2893     case LVA_SNAPTOGRID: next_pos = LISTVIEW_NextIconPosTop;  break; /* FIXME */
2894 #endif
2895     default: return FALSE;
2896     }
2897 
2898     infoPtr->currIconPos.x = infoPtr->currIconPos.y = 0;
2899     for (i = 0; i < infoPtr->nItemCount; i++)
2900     {
2901 #ifdef __REACTOS__
2902     next_pos(infoPtr, &pos, i);
2903 #else
2904     next_pos(infoPtr, &pos);
2905 #endif
2906 	LISTVIEW_MoveIconTo(infoPtr, i, &pos, FALSE);
2907     }
2908 
2909     return TRUE;
2910 }
2911 
2912 /***
2913  * DESCRIPTION:
2914  * Retrieves the bounding rectangle of all the items, not offset by Origin.
2915  * For LVS_REPORT always returns empty rectangle.
2916  *
2917  * PARAMETER(S):
2918  * [I] infoPtr : valid pointer to the listview structure
2919  * [O] lprcView : bounding rectangle
2920  *
2921  * RETURN:
2922  *   SUCCESS : TRUE
2923  *   FAILURE : FALSE
2924  */
LISTVIEW_GetAreaRect(const LISTVIEW_INFO * infoPtr,LPRECT lprcView)2925 static void LISTVIEW_GetAreaRect(const LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2926 {
2927     INT i, x, y;
2928 
2929     SetRectEmpty(lprcView);
2930 
2931     switch (infoPtr->uView)
2932     {
2933     case LV_VIEW_ICON:
2934     case LV_VIEW_SMALLICON:
2935 	for (i = 0; i < infoPtr->nItemCount; i++)
2936 	{
2937 	    x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, i);
2938             y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, i);
2939 	    lprcView->right = max(lprcView->right, x);
2940 	    lprcView->bottom = max(lprcView->bottom, y);
2941 	}
2942 	if (infoPtr->nItemCount > 0)
2943 	{
2944 	    lprcView->right += infoPtr->nItemWidth;
2945 	    lprcView->bottom += infoPtr->nItemHeight;
2946 	}
2947 	break;
2948 
2949     case LV_VIEW_LIST:
2950 	y = LISTVIEW_GetCountPerColumn(infoPtr);
2951 	x = infoPtr->nItemCount / y;
2952 	if (infoPtr->nItemCount % y) x++;
2953 	lprcView->right = x * infoPtr->nItemWidth;
2954 	lprcView->bottom = y * infoPtr->nItemHeight;
2955 	break;
2956     }
2957 }
2958 
2959 /***
2960  * DESCRIPTION:
2961  * Retrieves the bounding rectangle of all the items.
2962  *
2963  * PARAMETER(S):
2964  * [I] infoPtr : valid pointer to the listview structure
2965  * [O] lprcView : bounding rectangle
2966  *
2967  * RETURN:
2968  *   SUCCESS : TRUE
2969  *   FAILURE : FALSE
2970  */
LISTVIEW_GetViewRect(const LISTVIEW_INFO * infoPtr,LPRECT lprcView)2971 static BOOL LISTVIEW_GetViewRect(const LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2972 {
2973     POINT ptOrigin;
2974 
2975     TRACE("(lprcView=%p)\n", lprcView);
2976 
2977     if (!lprcView) return FALSE;
2978 
2979     LISTVIEW_GetAreaRect(infoPtr, lprcView);
2980 
2981     if (infoPtr->uView != LV_VIEW_DETAILS)
2982     {
2983         LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
2984         OffsetRect(lprcView, ptOrigin.x, ptOrigin.y);
2985     }
2986 
2987     TRACE("lprcView=%s\n", wine_dbgstr_rect(lprcView));
2988 
2989     return TRUE;
2990 }
2991 
2992 /***
2993  * DESCRIPTION:
2994  * Retrieves the subitem pointer associated with the subitem index.
2995  *
2996  * PARAMETER(S):
2997  * [I] hdpaSubItems : DPA handle for a specific item
2998  * [I] nSubItem : index of subitem
2999  *
3000  * RETURN:
3001  *   SUCCESS : subitem pointer
3002  *   FAILURE : NULL
3003  */
LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems,INT nSubItem)3004 static SUBITEM_INFO* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems, INT nSubItem)
3005 {
3006     SUBITEM_INFO *lpSubItem;
3007     INT i;
3008 
3009     /* we should binary search here if need be */
3010     for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
3011     {
3012         lpSubItem = DPA_GetPtr(hdpaSubItems, i);
3013 	if (lpSubItem->iSubItem == nSubItem)
3014 	    return lpSubItem;
3015     }
3016 
3017     return NULL;
3018 }
3019 
3020 
3021 /***
3022  * DESCRIPTION:
3023  * Calculates the desired item width.
3024  *
3025  * PARAMETER(S):
3026  * [I] infoPtr : valid pointer to the listview structure
3027  *
3028  * RETURN:
3029  *  The desired item width.
3030  */
LISTVIEW_CalculateItemWidth(const LISTVIEW_INFO * infoPtr)3031 static INT LISTVIEW_CalculateItemWidth(const LISTVIEW_INFO *infoPtr)
3032 {
3033     INT nItemWidth = 0;
3034 
3035     TRACE("uView=%d\n", infoPtr->uView);
3036 
3037     if (infoPtr->uView == LV_VIEW_ICON)
3038 	nItemWidth = infoPtr->iconSpacing.cx;
3039     else if (infoPtr->uView == LV_VIEW_DETAILS)
3040     {
3041 	if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0)
3042 	{
3043 	    RECT rcHeader;
3044 	    INT index;
3045 
3046 	    index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX,
3047                                  DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, 0);
3048 
3049 	    LISTVIEW_GetHeaderRect(infoPtr, index, &rcHeader);
3050             nItemWidth = rcHeader.right;
3051 	}
3052     }
3053     else /* LV_VIEW_SMALLICON, or LV_VIEW_LIST */
3054     {
3055 	WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3056 	LVITEMW lvItem;
3057 	INT i;
3058 
3059 	lvItem.mask = LVIF_TEXT;
3060 	lvItem.iSubItem = 0;
3061 
3062 	for (i = 0; i < infoPtr->nItemCount; i++)
3063 	{
3064 	    lvItem.iItem = i;
3065 	    lvItem.pszText = szDispText;
3066 	    lvItem.cchTextMax = DISP_TEXT_SIZE;
3067 	    if (LISTVIEW_GetItemW(infoPtr, &lvItem))
3068 		nItemWidth = max(LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE),
3069 				 nItemWidth);
3070 	}
3071 
3072         if (infoPtr->himlSmall) nItemWidth += infoPtr->iconSize.cx;
3073         if (infoPtr->himlState) nItemWidth += infoPtr->iconStateSize.cx;
3074 
3075         nItemWidth = max(DEFAULT_COLUMN_WIDTH, nItemWidth + WIDTH_PADDING);
3076     }
3077 
3078     return nItemWidth;
3079 }
3080 
3081 /***
3082  * DESCRIPTION:
3083  * Calculates the desired item height.
3084  *
3085  * PARAMETER(S):
3086  * [I] infoPtr : valid pointer to the listview structure
3087  *
3088  * RETURN:
3089  *  The desired item height.
3090  */
LISTVIEW_CalculateItemHeight(const LISTVIEW_INFO * infoPtr)3091 static INT LISTVIEW_CalculateItemHeight(const LISTVIEW_INFO *infoPtr)
3092 {
3093     INT nItemHeight;
3094 
3095     TRACE("uView=%d\n", infoPtr->uView);
3096 
3097     if (infoPtr->uView == LV_VIEW_ICON)
3098 	nItemHeight = infoPtr->iconSpacing.cy;
3099     else
3100     {
3101 	nItemHeight = infoPtr->ntmHeight;
3102 	if (infoPtr->himlState)
3103 	    nItemHeight = max(nItemHeight, infoPtr->iconStateSize.cy);
3104 	if (infoPtr->himlSmall)
3105 	    nItemHeight = max(nItemHeight, infoPtr->iconSize.cy);
3106 	nItemHeight += HEIGHT_PADDING;
3107     if (infoPtr->nMeasureItemHeight > 0)
3108         nItemHeight = infoPtr->nMeasureItemHeight;
3109     }
3110 
3111     return max(nItemHeight, 1);
3112 }
3113 
3114 /***
3115  * DESCRIPTION:
3116  * Updates the width, and height of an item.
3117  *
3118  * PARAMETER(S):
3119  * [I] infoPtr : valid pointer to the listview structure
3120  *
3121  * RETURN:
3122  *  None.
3123  */
LISTVIEW_UpdateItemSize(LISTVIEW_INFO * infoPtr)3124 static inline void LISTVIEW_UpdateItemSize(LISTVIEW_INFO *infoPtr)
3125 {
3126     infoPtr->nItemWidth = LISTVIEW_CalculateItemWidth(infoPtr);
3127     infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
3128 }
3129 
3130 
3131 /***
3132  * DESCRIPTION:
3133  * Retrieves and saves important text metrics info for the current
3134  * Listview font.
3135  *
3136  * PARAMETER(S):
3137  * [I] infoPtr : valid pointer to the listview structure
3138  *
3139  */
LISTVIEW_SaveTextMetrics(LISTVIEW_INFO * infoPtr)3140 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO *infoPtr)
3141 {
3142     HDC hdc = GetDC(infoPtr->hwndSelf);
3143     HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
3144     HFONT hOldFont = SelectObject(hdc, hFont);
3145     TEXTMETRICW tm;
3146     SIZE sz;
3147 
3148     if (GetTextMetricsW(hdc, &tm))
3149     {
3150 	infoPtr->ntmHeight = tm.tmHeight;
3151 	infoPtr->ntmMaxCharWidth = tm.tmMaxCharWidth;
3152     }
3153 
3154     if (GetTextExtentPoint32A(hdc, "...", 3, &sz))
3155 	infoPtr->nEllipsisWidth = sz.cx;
3156 
3157     SelectObject(hdc, hOldFont);
3158     ReleaseDC(infoPtr->hwndSelf, hdc);
3159 
3160     TRACE("tmHeight=%d\n", infoPtr->ntmHeight);
3161 }
3162 
3163 /***
3164  * DESCRIPTION:
3165  * A compare function for ranges
3166  *
3167  * PARAMETER(S)
3168  * [I] range1 : pointer to range 1;
3169  * [I] range2 : pointer to range 2;
3170  * [I] flags : flags
3171  *
3172  * RETURNS:
3173  * > 0 : if range 1 > range 2
3174  * < 0 : if range 2 > range 1
3175  * = 0 : if range intersects range 2
3176  */
ranges_cmp(LPVOID range1,LPVOID range2,LPARAM flags)3177 static INT CALLBACK ranges_cmp(LPVOID range1, LPVOID range2, LPARAM flags)
3178 {
3179     INT cmp;
3180 
3181     if (((RANGE*)range1)->upper <= ((RANGE*)range2)->lower)
3182 	cmp = -1;
3183     else if (((RANGE*)range2)->upper <= ((RANGE*)range1)->lower)
3184 	cmp = 1;
3185     else
3186 	cmp = 0;
3187 
3188     TRACE("range1=%s, range2=%s, cmp=%d\n", debugrange(range1), debugrange(range2), cmp);
3189 
3190     return cmp;
3191 }
3192 
3193 #define ranges_check(ranges, desc) if (TRACE_ON(listview)) ranges_assert(ranges, desc, __FILE__, __LINE__)
3194 
ranges_assert(RANGES ranges,LPCSTR desc,const char * file,int line)3195 static void ranges_assert(RANGES ranges, LPCSTR desc, const char *file, int line)
3196 {
3197     INT i;
3198     RANGE *prev, *curr;
3199 
3200     TRACE("*** Checking %s:%d:%s ***\n", file, line, desc);
3201     assert (ranges);
3202     assert (DPA_GetPtrCount(ranges->hdpa) >= 0);
3203     ranges_dump(ranges);
3204     if (DPA_GetPtrCount(ranges->hdpa) > 0)
3205     {
3206 	prev = DPA_GetPtr(ranges->hdpa, 0);
3207 	assert (prev->lower >= 0 && prev->lower < prev->upper);
3208 	for (i = 1; i < DPA_GetPtrCount(ranges->hdpa); i++)
3209 	{
3210 	    curr = DPA_GetPtr(ranges->hdpa, i);
3211 	    assert (prev->upper <= curr->lower);
3212 	    assert (curr->lower < curr->upper);
3213 	    prev = curr;
3214 	}
3215     }
3216     TRACE("--- Done checking---\n");
3217 }
3218 
ranges_create(int count)3219 static RANGES ranges_create(int count)
3220 {
3221     RANGES ranges = Alloc(sizeof(struct tagRANGES));
3222     if (!ranges) return NULL;
3223     ranges->hdpa = DPA_Create(count);
3224     if (ranges->hdpa) return ranges;
3225     Free(ranges);
3226     return NULL;
3227 }
3228 
ranges_clear(RANGES ranges)3229 static void ranges_clear(RANGES ranges)
3230 {
3231     INT i;
3232 
3233     for(i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
3234 	Free(DPA_GetPtr(ranges->hdpa, i));
3235     DPA_DeleteAllPtrs(ranges->hdpa);
3236 }
3237 
3238 
ranges_destroy(RANGES ranges)3239 static void ranges_destroy(RANGES ranges)
3240 {
3241     if (!ranges) return;
3242     ranges_clear(ranges);
3243     DPA_Destroy(ranges->hdpa);
3244     Free(ranges);
3245 }
3246 
ranges_clone(RANGES ranges)3247 static RANGES ranges_clone(RANGES ranges)
3248 {
3249     RANGES clone;
3250     INT i;
3251 
3252 #ifdef __REACTOS__
3253     if (!ranges || !ranges->hdpa)
3254     {
3255         /*
3256          * If a ExplorerBand tree rename operation is completed by left-clicking in
3257          * DefView, the navigation to the newly named item causes the ListView in DefView
3258          * to call LISTVIEW_DeselectAllSkipItems during ListView destruction.
3259         */
3260         return NULL;
3261     }
3262 #endif
3263     if (!(clone = ranges_create(DPA_GetPtrCount(ranges->hdpa)))) goto fail;
3264 
3265     for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
3266     {
3267         RANGE *newrng = Alloc(sizeof(RANGE));
3268 	if (!newrng) goto fail;
3269 	*newrng = *((RANGE*)DPA_GetPtr(ranges->hdpa, i));
3270         if (!DPA_SetPtr(clone->hdpa, i, newrng))
3271         {
3272             Free(newrng);
3273             goto fail;
3274         }
3275     }
3276     return clone;
3277 
3278 fail:
3279     TRACE ("clone failed\n");
3280     ranges_destroy(clone);
3281     return NULL;
3282 }
3283 
ranges_diff(RANGES ranges,RANGES sub)3284 static RANGES ranges_diff(RANGES ranges, RANGES sub)
3285 {
3286     INT i;
3287 
3288     for (i = 0; i < DPA_GetPtrCount(sub->hdpa); i++)
3289 	ranges_del(ranges, *((RANGE *)DPA_GetPtr(sub->hdpa, i)));
3290 
3291     return ranges;
3292 }
3293 
ranges_dump(RANGES ranges)3294 static void ranges_dump(RANGES ranges)
3295 {
3296     INT i;
3297 
3298     for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
3299     	TRACE("   %s\n", debugrange(DPA_GetPtr(ranges->hdpa, i)));
3300 }
3301 
ranges_contain(RANGES ranges,INT nItem)3302 static inline BOOL ranges_contain(RANGES ranges, INT nItem)
3303 {
3304     RANGE srchrng = { nItem, nItem + 1 };
3305 
3306     TRACE("(nItem=%d)\n", nItem);
3307     ranges_check(ranges, "before contain");
3308     return DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED) != -1;
3309 }
3310 
ranges_itemcount(RANGES ranges)3311 static INT ranges_itemcount(RANGES ranges)
3312 {
3313     INT i, count = 0;
3314 
3315     for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
3316     {
3317 	RANGE *sel = DPA_GetPtr(ranges->hdpa, i);
3318 	count += sel->upper - sel->lower;
3319     }
3320 
3321     return count;
3322 }
3323 
ranges_shift(RANGES ranges,INT nItem,INT delta,INT nUpper)3324 static BOOL ranges_shift(RANGES ranges, INT nItem, INT delta, INT nUpper)
3325 {
3326     RANGE srchrng = { nItem, nItem + 1 }, *chkrng;
3327     INT index;
3328 
3329     index = DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
3330     if (index == -1) return TRUE;
3331 
3332     for (; index < DPA_GetPtrCount(ranges->hdpa); index++)
3333     {
3334 	chkrng = DPA_GetPtr(ranges->hdpa, index);
3335     	if (chkrng->lower >= nItem)
3336 	    chkrng->lower = max(min(chkrng->lower + delta, nUpper - 1), 0);
3337         if (chkrng->upper > nItem)
3338 	    chkrng->upper = max(min(chkrng->upper + delta, nUpper), 0);
3339     }
3340     return TRUE;
3341 }
3342 
ranges_add(RANGES ranges,RANGE range)3343 static BOOL ranges_add(RANGES ranges, RANGE range)
3344 {
3345     RANGE srchrgn;
3346     INT index;
3347 
3348     TRACE("(%s)\n", debugrange(&range));
3349     ranges_check(ranges, "before add");
3350 
3351     /* try find overlapping regions first */
3352     srchrgn.lower = range.lower - 1;
3353     srchrgn.upper = range.upper + 1;
3354     index = DPA_Search(ranges->hdpa, &srchrgn, 0, ranges_cmp, 0, DPAS_SORTED);
3355 
3356     if (index == -1)
3357     {
3358 	RANGE *newrgn;
3359 
3360 	TRACE("Adding new range\n");
3361 
3362 	/* create the brand new range to insert */
3363         newrgn = Alloc(sizeof(RANGE));
3364 	if(!newrgn) goto fail;
3365 	*newrgn = range;
3366 
3367 	/* figure out where to insert it */
3368 	index = DPA_Search(ranges->hdpa, newrgn, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
3369 	TRACE("index=%d\n", index);
3370 	if (index == -1) index = 0;
3371 
3372 	/* and get it over with */
3373 	if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
3374 	{
3375 	    Free(newrgn);
3376 	    goto fail;
3377 	}
3378     }
3379     else
3380     {
3381 	RANGE *chkrgn, *mrgrgn;
3382 	INT fromindex, mergeindex;
3383 
3384 	chkrgn = DPA_GetPtr(ranges->hdpa, index);
3385 	TRACE("Merge with %s @%d\n", debugrange(chkrgn), index);
3386 
3387 	chkrgn->lower = min(range.lower, chkrgn->lower);
3388 	chkrgn->upper = max(range.upper, chkrgn->upper);
3389 
3390 	TRACE("New range %s @%d\n", debugrange(chkrgn), index);
3391 
3392         /* merge now common ranges */
3393 	fromindex = 0;
3394 	srchrgn.lower = chkrgn->lower - 1;
3395 	srchrgn.upper = chkrgn->upper + 1;
3396 
3397 	do
3398 	{
3399 	    mergeindex = DPA_Search(ranges->hdpa, &srchrgn, fromindex, ranges_cmp, 0, 0);
3400 	    if (mergeindex == -1) break;
3401 	    if (mergeindex == index)
3402 	    {
3403 		fromindex = index + 1;
3404 		continue;
3405 	    }
3406 
3407 	    TRACE("Merge with index %i\n", mergeindex);
3408 
3409 	    mrgrgn = DPA_GetPtr(ranges->hdpa, mergeindex);
3410 	    chkrgn->lower = min(chkrgn->lower, mrgrgn->lower);
3411 	    chkrgn->upper = max(chkrgn->upper, mrgrgn->upper);
3412 	    Free(mrgrgn);
3413 	    DPA_DeletePtr(ranges->hdpa, mergeindex);
3414 	    if (mergeindex < index) index --;
3415 	} while(1);
3416     }
3417 
3418     ranges_check(ranges, "after add");
3419     return TRUE;
3420 
3421 fail:
3422     ranges_check(ranges, "failed add");
3423     return FALSE;
3424 }
3425 
ranges_del(RANGES ranges,RANGE range)3426 static BOOL ranges_del(RANGES ranges, RANGE range)
3427 {
3428     RANGE *chkrgn;
3429     INT index;
3430 
3431     TRACE("(%s)\n", debugrange(&range));
3432     ranges_check(ranges, "before del");
3433 
3434     /* we don't use DPAS_SORTED here, since we need *
3435      * to find the first overlapping range          */
3436     index = DPA_Search(ranges->hdpa, &range, 0, ranges_cmp, 0, 0);
3437     while(index != -1)
3438     {
3439 	chkrgn = DPA_GetPtr(ranges->hdpa, index);
3440 
3441 	TRACE("Matches range %s @%d\n", debugrange(chkrgn), index);
3442 
3443 	/* case 1: Same range */
3444 	if ( (chkrgn->upper == range.upper) &&
3445 	     (chkrgn->lower == range.lower) )
3446 	{
3447 	    DPA_DeletePtr(ranges->hdpa, index);
3448 	    Free(chkrgn);
3449 	    break;
3450 	}
3451 	/* case 2: engulf */
3452 	else if ( (chkrgn->upper <= range.upper) &&
3453 		  (chkrgn->lower >= range.lower) )
3454 	{
3455 	    DPA_DeletePtr(ranges->hdpa, index);
3456 	    Free(chkrgn);
3457 	}
3458 	/* case 3: overlap upper */
3459 	else if ( (chkrgn->upper <= range.upper) &&
3460 		  (chkrgn->lower < range.lower) )
3461 	{
3462 	    chkrgn->upper = range.lower;
3463 	}
3464 	/* case 4: overlap lower */
3465 	else if ( (chkrgn->upper > range.upper) &&
3466 		  (chkrgn->lower >= range.lower) )
3467 	{
3468 	    chkrgn->lower = range.upper;
3469 	    break;
3470 	}
3471 	/* case 5: fully internal */
3472 	else
3473 	{
3474 	    RANGE *newrgn;
3475 
3476 	    if (!(newrgn = Alloc(sizeof(RANGE)))) goto fail;
3477 	    newrgn->lower = chkrgn->lower;
3478 	    newrgn->upper = range.lower;
3479 	    chkrgn->lower = range.upper;
3480 	    if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
3481 	    {
3482 		Free(newrgn);
3483 		goto fail;
3484 	    }
3485 	    break;
3486 	}
3487 
3488 	index = DPA_Search(ranges->hdpa, &range, index, ranges_cmp, 0, 0);
3489     }
3490 
3491     ranges_check(ranges, "after del");
3492     return TRUE;
3493 
3494 fail:
3495     ranges_check(ranges, "failed del");
3496     return FALSE;
3497 }
3498 
3499 /***
3500 * DESCRIPTION:
3501 * Removes all selection ranges
3502 *
3503 * Parameters(s):
3504 * [I] infoPtr : valid pointer to the listview structure
3505 * [I] toSkip : item range to skip removing the selection
3506 *
3507 * RETURNS:
3508 *   SUCCESS : TRUE
3509 *   FAILURE : FALSE
3510 */
LISTVIEW_DeselectAllSkipItems(LISTVIEW_INFO * infoPtr,RANGES toSkip)3511 static BOOL LISTVIEW_DeselectAllSkipItems(LISTVIEW_INFO *infoPtr, RANGES toSkip)
3512 {
3513     LVITEMW lvItem;
3514     ITERATOR i;
3515     RANGES clone;
3516 
3517     TRACE("()\n");
3518 
3519     lvItem.state = 0;
3520     lvItem.stateMask = LVIS_SELECTED;
3521 
3522     /* need to clone the DPA because callbacks can change it */
3523     if (!(clone = ranges_clone(infoPtr->selectionRanges))) return FALSE;
3524     iterator_rangesitems(&i, ranges_diff(clone, toSkip));
3525     while(iterator_next(&i))
3526 	LISTVIEW_SetItemState(infoPtr, i.nItem, &lvItem);
3527     /* note that the iterator destructor will free the cloned range */
3528     iterator_destroy(&i);
3529 
3530     return TRUE;
3531 }
3532 
LISTVIEW_DeselectAllSkipItem(LISTVIEW_INFO * infoPtr,INT nItem)3533 static inline BOOL LISTVIEW_DeselectAllSkipItem(LISTVIEW_INFO *infoPtr, INT nItem)
3534 {
3535     RANGES toSkip;
3536 
3537     if (!(toSkip = ranges_create(1))) return FALSE;
3538     if (nItem != -1) ranges_additem(toSkip, nItem);
3539     LISTVIEW_DeselectAllSkipItems(infoPtr, toSkip);
3540     ranges_destroy(toSkip);
3541     return TRUE;
3542 }
3543 
LISTVIEW_DeselectAll(LISTVIEW_INFO * infoPtr)3544 static inline BOOL LISTVIEW_DeselectAll(LISTVIEW_INFO *infoPtr)
3545 {
3546     return LISTVIEW_DeselectAllSkipItem(infoPtr, -1);
3547 }
3548 
3549 /***
3550  * DESCRIPTION:
3551  * Retrieves the number of items that are marked as selected.
3552  *
3553  * PARAMETER(S):
3554  * [I] infoPtr : valid pointer to the listview structure
3555  *
3556  * RETURN:
3557  * Number of items selected.
3558  */
LISTVIEW_GetSelectedCount(const LISTVIEW_INFO * infoPtr)3559 static INT LISTVIEW_GetSelectedCount(const LISTVIEW_INFO *infoPtr)
3560 {
3561     INT nSelectedCount = 0;
3562 
3563     if (infoPtr->uCallbackMask & LVIS_SELECTED)
3564     {
3565         INT i;
3566 	for (i = 0; i < infoPtr->nItemCount; i++)
3567   	{
3568 	    if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
3569 		nSelectedCount++;
3570 	}
3571     }
3572     else
3573 	nSelectedCount = ranges_itemcount(infoPtr->selectionRanges);
3574 
3575     TRACE("nSelectedCount=%d\n", nSelectedCount);
3576     return nSelectedCount;
3577 }
3578 
3579 /***
3580  * DESCRIPTION:
3581  * Manages the item focus.
3582  *
3583  * PARAMETER(S):
3584  * [I] infoPtr : valid pointer to the listview structure
3585  * [I] nItem : item index
3586  *
3587  * RETURN:
3588  *   TRUE : focused item changed
3589  *   FALSE : focused item has NOT changed
3590  */
LISTVIEW_SetItemFocus(LISTVIEW_INFO * infoPtr,INT nItem)3591 static inline BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem)
3592 {
3593     INT oldFocus = infoPtr->nFocusedItem;
3594     LVITEMW lvItem;
3595 
3596     if (nItem == infoPtr->nFocusedItem) return FALSE;
3597 
3598     lvItem.state =  nItem == -1 ? 0 : LVIS_FOCUSED;
3599     lvItem.stateMask = LVIS_FOCUSED;
3600     LISTVIEW_SetItemState(infoPtr, nItem == -1 ? infoPtr->nFocusedItem : nItem, &lvItem);
3601 
3602     return oldFocus != infoPtr->nFocusedItem;
3603 }
3604 
shift_item(const LISTVIEW_INFO * infoPtr,INT nShiftItem,INT nItem,INT direction)3605 static INT shift_item(const LISTVIEW_INFO *infoPtr, INT nShiftItem, INT nItem, INT direction)
3606 {
3607     if (nShiftItem < nItem) return nShiftItem;
3608 
3609     if (nShiftItem > nItem) return nShiftItem + direction;
3610 
3611     if (direction > 0) return nShiftItem + direction;
3612 
3613     return min(nShiftItem, infoPtr->nItemCount - 1);
3614 }
3615 
3616 /* This function updates focus index.
3617 
3618 Parameters:
3619    focus : current focus index
3620    item : index of item to be added/removed
3621    direction : add/remove flag
3622 */
LISTVIEW_ShiftFocus(LISTVIEW_INFO * infoPtr,INT focus,INT item,INT direction)3623 static void LISTVIEW_ShiftFocus(LISTVIEW_INFO *infoPtr, INT focus, INT item, INT direction)
3624 {
3625     DWORD old_mask = infoPtr->notify_mask & NOTIFY_MASK_ITEM_CHANGE;
3626 
3627     infoPtr->notify_mask &= ~NOTIFY_MASK_ITEM_CHANGE;
3628     focus = shift_item(infoPtr, focus, item, direction);
3629     if (focus != infoPtr->nFocusedItem)
3630         LISTVIEW_SetItemFocus(infoPtr, focus);
3631     infoPtr->notify_mask |= old_mask;
3632 }
3633 
3634 /**
3635 * DESCRIPTION:
3636 * Updates the various indices after an item has been inserted or deleted.
3637 *
3638 * PARAMETER(S):
3639 * [I] infoPtr : valid pointer to the listview structure
3640 * [I] nItem : item index
3641 * [I] direction : Direction of shift, +1 or -1.
3642 *
3643 * RETURN:
3644 * None
3645 */
LISTVIEW_ShiftIndices(LISTVIEW_INFO * infoPtr,INT nItem,INT direction)3646 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction)
3647 {
3648     TRACE("Shifting %i, %i steps\n", nItem, direction);
3649 
3650     ranges_shift(infoPtr->selectionRanges, nItem, direction, infoPtr->nItemCount);
3651     assert(abs(direction) == 1);
3652     infoPtr->nSelectionMark = shift_item(infoPtr, infoPtr->nSelectionMark, nItem, direction);
3653 
3654     /* But we are not supposed to modify nHotItem! */
3655 }
3656 
3657 /**
3658  * DESCRIPTION:
3659  * Adds a block of selections.
3660  *
3661  * PARAMETER(S):
3662  * [I] infoPtr : valid pointer to the listview structure
3663  * [I] nItem : item index
3664  *
3665  * RETURN:
3666  * Whether the window is still valid.
3667  */
LISTVIEW_AddGroupSelection(LISTVIEW_INFO * infoPtr,INT nItem)3668 static BOOL LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3669 {
3670     INT nFirst = min(infoPtr->nSelectionMark, nItem);
3671     INT nLast = max(infoPtr->nSelectionMark, nItem);
3672     HWND hwndSelf = infoPtr->hwndSelf;
3673     NMLVODSTATECHANGE nmlv;
3674     DWORD old_mask;
3675     LVITEMW item;
3676     INT i;
3677 
3678     /* Temporarily disable change notification
3679      * If the control is LVS_OWNERDATA, we need to send
3680      * only one LVN_ODSTATECHANGED notification.
3681      * See MSDN documentation for LVN_ITEMCHANGED.
3682      */
3683     old_mask = infoPtr->notify_mask & NOTIFY_MASK_ITEM_CHANGE;
3684     if (infoPtr->dwStyle & LVS_OWNERDATA)
3685         infoPtr->notify_mask &= ~NOTIFY_MASK_ITEM_CHANGE;
3686 
3687     if (nFirst == -1) nFirst = nItem;
3688 
3689     item.state = LVIS_SELECTED;
3690     item.stateMask = LVIS_SELECTED;
3691 
3692     for (i = nFirst; i <= nLast; i++)
3693 	LISTVIEW_SetItemState(infoPtr,i,&item);
3694 
3695     ZeroMemory(&nmlv, sizeof(nmlv));
3696     nmlv.iFrom = nFirst;
3697     nmlv.iTo = nLast;
3698     nmlv.uOldState = 0;
3699     nmlv.uNewState = item.state;
3700 
3701     notify_hdr(infoPtr, LVN_ODSTATECHANGED, (LPNMHDR)&nmlv);
3702     if (!IsWindow(hwndSelf))
3703         return FALSE;
3704     infoPtr->notify_mask |= old_mask;
3705     return TRUE;
3706 }
3707 
3708 
3709 /***
3710  * DESCRIPTION:
3711  * Sets a single group selection.
3712  *
3713  * PARAMETER(S):
3714  * [I] infoPtr : valid pointer to the listview structure
3715  * [I] nItem : item index
3716  *
3717  * RETURN:
3718  * None
3719  */
LISTVIEW_SetGroupSelection(LISTVIEW_INFO * infoPtr,INT nItem)3720 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3721 {
3722     RANGES selection;
3723     DWORD old_mask;
3724     LVITEMW item;
3725     ITERATOR i;
3726 
3727     if (!(selection = ranges_create(100))) return;
3728 
3729     item.state = LVIS_SELECTED;
3730     item.stateMask = LVIS_SELECTED;
3731 
3732     if ((infoPtr->uView == LV_VIEW_LIST) || (infoPtr->uView == LV_VIEW_DETAILS))
3733     {
3734 	if (infoPtr->nSelectionMark == -1)
3735 	{
3736 	    infoPtr->nSelectionMark = nItem;
3737 	    ranges_additem(selection, nItem);
3738 	}
3739 	else
3740 	{
3741 	    RANGE sel;
3742 
3743 	    sel.lower = min(infoPtr->nSelectionMark, nItem);
3744 	    sel.upper = max(infoPtr->nSelectionMark, nItem) + 1;
3745 	    ranges_add(selection, sel);
3746 	}
3747     }
3748     else
3749     {
3750 	RECT rcItem, rcSel, rcSelMark;
3751 	POINT ptItem;
3752 
3753 	rcItem.left = LVIR_BOUNDS;
3754 	if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) {
3755 	     ranges_destroy (selection);
3756 	     return;
3757 	}
3758 	rcSelMark.left = LVIR_BOUNDS;
3759 	if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) {
3760 	     ranges_destroy (selection);
3761 	     return;
3762 	}
3763 	UnionRect(&rcSel, &rcItem, &rcSelMark);
3764 	iterator_frameditems(&i, infoPtr, &rcSel);
3765 	while(iterator_next(&i))
3766 	{
3767 	    LISTVIEW_GetItemPosition(infoPtr, i.nItem, &ptItem);
3768 	    if (PtInRect(&rcSel, ptItem)) ranges_additem(selection, i.nItem);
3769 	}
3770 	iterator_destroy(&i);
3771     }
3772 
3773     /* disable per item notifications on LVS_OWNERDATA style
3774        FIXME: single LVN_ODSTATECHANGED should be used */
3775     old_mask = infoPtr->notify_mask & NOTIFY_MASK_ITEM_CHANGE;
3776     if (infoPtr->dwStyle & LVS_OWNERDATA)
3777         infoPtr->notify_mask &= ~NOTIFY_MASK_ITEM_CHANGE;
3778 
3779     LISTVIEW_DeselectAllSkipItems(infoPtr, selection);
3780 
3781 
3782     iterator_rangesitems(&i, selection);
3783     while(iterator_next(&i))
3784 	LISTVIEW_SetItemState(infoPtr, i.nItem, &item);
3785     /* this will also destroy the selection */
3786     iterator_destroy(&i);
3787 
3788     infoPtr->notify_mask |= old_mask;
3789     LISTVIEW_SetItemFocus(infoPtr, nItem);
3790 }
3791 
3792 /***
3793  * DESCRIPTION:
3794  * Sets a single selection.
3795  *
3796  * PARAMETER(S):
3797  * [I] infoPtr : valid pointer to the listview structure
3798  * [I] nItem : item index
3799  *
3800  * RETURN:
3801  * None
3802  */
LISTVIEW_SetSelection(LISTVIEW_INFO * infoPtr,INT nItem)3803 static void LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3804 {
3805     LVITEMW lvItem;
3806 
3807     TRACE("nItem=%d\n", nItem);
3808 
3809     LISTVIEW_DeselectAllSkipItem(infoPtr, nItem);
3810 
3811     lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
3812     lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
3813     LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3814 
3815     infoPtr->nSelectionMark = nItem;
3816 }
3817 
3818 /***
3819  * DESCRIPTION:
3820  * Set selection(s) with keyboard.
3821  *
3822  * PARAMETER(S):
3823  * [I] infoPtr : valid pointer to the listview structure
3824  * [I] nItem : item index
3825  * [I] space : VK_SPACE code sent
3826  *
3827  * RETURN:
3828  *   SUCCESS : TRUE (needs to be repainted)
3829  *   FAILURE : FALSE (nothing has changed)
3830  */
LISTVIEW_KeySelection(LISTVIEW_INFO * infoPtr,INT nItem,BOOL space)3831 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem, BOOL space)
3832 {
3833   /* FIXME: pass in the state */
3834   WORD wShift = GetKeyState(VK_SHIFT) & 0x8000;
3835   WORD wCtrl = GetKeyState(VK_CONTROL) & 0x8000;
3836   BOOL bResult = FALSE;
3837 
3838   TRACE("nItem=%d, wShift=%d, wCtrl=%d\n", nItem, wShift, wCtrl);
3839   if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
3840   {
3841     bResult = TRUE;
3842 
3843     if (infoPtr->dwStyle & LVS_SINGLESEL || (wShift == 0 && wCtrl == 0))
3844       LISTVIEW_SetSelection(infoPtr, nItem);
3845     else
3846     {
3847       if (wShift)
3848         LISTVIEW_SetGroupSelection(infoPtr, nItem);
3849       else if (wCtrl)
3850       {
3851         LVITEMW lvItem;
3852         lvItem.state = ~LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3853         lvItem.stateMask = LVIS_SELECTED;
3854         if (space)
3855         {
3856             LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3857             if (lvItem.state & LVIS_SELECTED)
3858                 infoPtr->nSelectionMark = nItem;
3859         }
3860         bResult = LISTVIEW_SetItemFocus(infoPtr, nItem);
3861       }
3862     }
3863     LISTVIEW_EnsureVisible(infoPtr, nItem, FALSE);
3864   }
3865 
3866   UpdateWindow(infoPtr->hwndSelf); /* update client area */
3867   return bResult;
3868 }
3869 
LISTVIEW_GetItemAtPt(const LISTVIEW_INFO * infoPtr,LPLVITEMW lpLVItem,POINT pt)3870 static BOOL LISTVIEW_GetItemAtPt(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, POINT pt)
3871 {
3872     LVHITTESTINFO lvHitTestInfo;
3873 
3874     ZeroMemory(&lvHitTestInfo, sizeof(lvHitTestInfo));
3875     lvHitTestInfo.pt.x = pt.x;
3876     lvHitTestInfo.pt.y = pt.y;
3877 
3878     LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
3879 
3880     lpLVItem->mask = LVIF_PARAM;
3881     lpLVItem->iItem = lvHitTestInfo.iItem;
3882     lpLVItem->iSubItem = 0;
3883 
3884     return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
3885 }
3886 
LISTVIEW_IsHotTracking(const LISTVIEW_INFO * infoPtr)3887 static inline BOOL LISTVIEW_IsHotTracking(const LISTVIEW_INFO *infoPtr)
3888 {
3889     return ((infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) ||
3890             (infoPtr->dwLvExStyle & LVS_EX_ONECLICKACTIVATE) ||
3891             (infoPtr->dwLvExStyle & LVS_EX_TWOCLICKACTIVATE));
3892 }
3893 
3894 /***
3895  * DESCRIPTION:
3896  * Called when the mouse is being actively tracked and has hovered for a specified
3897  * amount of time
3898  *
3899  * PARAMETER(S):
3900  * [I] infoPtr : valid pointer to the listview structure
3901  * [I] fwKeys : key indicator
3902  * [I] x,y : mouse position
3903  *
3904  * RETURN:
3905  *   0 if the message was processed, non-zero if there was an error
3906  *
3907  * INFO:
3908  * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
3909  * over the item for a certain period of time.
3910  *
3911  */
LISTVIEW_MouseHover(LISTVIEW_INFO * infoPtr,INT x,INT y)3912 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, INT x, INT y)
3913 {
3914     NMHDR hdr;
3915 
3916     if (notify_hdr(infoPtr, NM_HOVER, &hdr)) return 0;
3917 
3918     if (LISTVIEW_IsHotTracking(infoPtr))
3919     {
3920         LVITEMW item;
3921         POINT pt;
3922 
3923         pt.x = x;
3924         pt.y = y;
3925 
3926         if (LISTVIEW_GetItemAtPt(infoPtr, &item, pt))
3927             LISTVIEW_SetSelection(infoPtr, item.iItem);
3928 
3929         SetFocus(infoPtr->hwndSelf);
3930     }
3931 
3932     return 0;
3933 }
3934 
3935 #define SCROLL_LEFT   0x1
3936 #define SCROLL_RIGHT  0x2
3937 #define SCROLL_UP     0x4
3938 #define SCROLL_DOWN   0x8
3939 
3940 /***
3941  * DESCRIPTION:
3942  * Utility routine to draw and highlight items within a marquee selection rectangle.
3943  *
3944  * PARAMETER(S):
3945  * [I] infoPtr     : valid pointer to the listview structure
3946  * [I] coords_orig : original co-ordinates of the cursor
3947  * [I] coords_offs : offsetted coordinates of the cursor
3948  * [I] offset      : offset amount
3949  * [I] scroll      : Bitmask of which directions we should scroll, if at all
3950  *
3951  * RETURN:
3952  *   None.
3953  */
LISTVIEW_MarqueeHighlight(LISTVIEW_INFO * infoPtr,const POINT * coords_orig,INT scroll)3954 static void LISTVIEW_MarqueeHighlight(LISTVIEW_INFO *infoPtr, const POINT *coords_orig,
3955                                       INT scroll)
3956 {
3957     BOOL controlDown = FALSE;
3958     LVITEMW item;
3959     ITERATOR old_elems, new_elems;
3960     RECT rect;
3961     POINT coords_offs, offset;
3962 
3963     /* Ensure coordinates are within client bounds */
3964     coords_offs.x = max(min(coords_orig->x, infoPtr->rcList.right), 0);
3965     coords_offs.y = max(min(coords_orig->y, infoPtr->rcList.bottom), 0);
3966 
3967     /* Get offset */
3968     LISTVIEW_GetOrigin(infoPtr, &offset);
3969 
3970     /* Offset coordinates by the appropriate amount */
3971     coords_offs.x -= offset.x;
3972     coords_offs.y -= offset.y;
3973 
3974     if (coords_offs.x > infoPtr->marqueeOrigin.x)
3975     {
3976         rect.left = infoPtr->marqueeOrigin.x;
3977         rect.right = coords_offs.x;
3978     }
3979     else
3980     {
3981         rect.left = coords_offs.x;
3982         rect.right = infoPtr->marqueeOrigin.x;
3983     }
3984 
3985     if (coords_offs.y > infoPtr->marqueeOrigin.y)
3986     {
3987         rect.top = infoPtr->marqueeOrigin.y;
3988         rect.bottom = coords_offs.y;
3989     }
3990     else
3991     {
3992         rect.top = coords_offs.y;
3993         rect.bottom = infoPtr->marqueeOrigin.y;
3994     }
3995 
3996     /* Cancel out the old marquee rectangle and draw the new one */
3997     LISTVIEW_InvalidateRect(infoPtr, &infoPtr->marqueeDrawRect);
3998 
3999     /* Scroll by the appropriate distance if applicable - speed up scrolling as
4000        the cursor is further away */
4001 
4002     if ((scroll & SCROLL_LEFT) && (coords_orig->x <= 0))
4003         LISTVIEW_Scroll(infoPtr, coords_orig->x, 0);
4004 
4005     if ((scroll & SCROLL_RIGHT) && (coords_orig->x >= infoPtr->rcList.right))
4006         LISTVIEW_Scroll(infoPtr, (coords_orig->x - infoPtr->rcList.right), 0);
4007 
4008     if ((scroll & SCROLL_UP) && (coords_orig->y <= 0))
4009         LISTVIEW_Scroll(infoPtr, 0, coords_orig->y);
4010 
4011     if ((scroll & SCROLL_DOWN) && (coords_orig->y >= infoPtr->rcList.bottom))
4012         LISTVIEW_Scroll(infoPtr, 0, (coords_orig->y - infoPtr->rcList.bottom));
4013 
4014     iterator_frameditems_absolute(&old_elems, infoPtr, &infoPtr->marqueeRect);
4015 
4016     infoPtr->marqueeRect = rect;
4017     infoPtr->marqueeDrawRect = rect;
4018     OffsetRect(&infoPtr->marqueeDrawRect, offset.x, offset.y);
4019 
4020     iterator_frameditems_absolute(&new_elems, infoPtr, &infoPtr->marqueeRect);
4021     iterator_remove_common_items(&old_elems, &new_elems);
4022 
4023     /* Iterate over no longer selected items */
4024     while (iterator_next(&old_elems))
4025     {
4026         if (old_elems.nItem > -1)
4027         {
4028             if (LISTVIEW_GetItemState(infoPtr, old_elems.nItem, LVIS_SELECTED) == LVIS_SELECTED)
4029                 item.state = 0;
4030             else
4031                 item.state = LVIS_SELECTED;
4032 
4033             item.stateMask = LVIS_SELECTED;
4034 
4035             LISTVIEW_SetItemState(infoPtr, old_elems.nItem, &item);
4036         }
4037     }
4038     iterator_destroy(&old_elems);
4039 
4040 
4041     /* Iterate over newly selected items */
4042     if (GetKeyState(VK_CONTROL) & 0x8000)
4043         controlDown = TRUE;
4044 
4045     while (iterator_next(&new_elems))
4046     {
4047         if (new_elems.nItem > -1)
4048         {
4049             /* If CTRL is pressed, invert. If not, always select the item. */
4050             if ((controlDown) && (LISTVIEW_GetItemState(infoPtr, new_elems.nItem, LVIS_SELECTED)))
4051                 item.state = 0;
4052             else
4053                 item.state = LVIS_SELECTED;
4054 
4055             item.stateMask = LVIS_SELECTED;
4056 
4057             LISTVIEW_SetItemState(infoPtr, new_elems.nItem, &item);
4058         }
4059     }
4060     iterator_destroy(&new_elems);
4061 
4062     LISTVIEW_InvalidateRect(infoPtr, &infoPtr->marqueeDrawRect);
4063 }
4064 
4065 /***
4066  * DESCRIPTION:
4067  * Called when we are in a marquee selection that involves scrolling the listview (ie,
4068  * the cursor is outside the bounds of the client area). This is a TIMERPROC.
4069  *
4070  * PARAMETER(S):
4071  * [I] hwnd : Handle to the listview
4072  * [I] uMsg : WM_TIMER (ignored)
4073  * [I] idEvent : The timer ID interpreted as a pointer to a LISTVIEW_INFO struct
4074  * [I] dwTimer : The elapsed time (ignored)
4075  *
4076  * RETURN:
4077  *   None.
4078  */
LISTVIEW_ScrollTimer(HWND hWnd,UINT uMsg,UINT_PTR idEvent,DWORD dwTime)4079 static VOID CALLBACK LISTVIEW_ScrollTimer(HWND hWnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
4080 {
4081     LISTVIEW_INFO *infoPtr;
4082     SCROLLINFO scrollInfo;
4083     POINT coords;
4084     INT scroll = 0;
4085 
4086     infoPtr = (LISTVIEW_INFO *) idEvent;
4087 
4088     if (!infoPtr)
4089         return;
4090 
4091     /* Get the current cursor position and convert to client coordinates */
4092     GetCursorPos(&coords);
4093     ScreenToClient(hWnd, &coords);
4094 
4095     scrollInfo.cbSize = sizeof(SCROLLINFO);
4096     scrollInfo.fMask = SIF_ALL;
4097 
4098     /* Work out in which directions we can scroll */
4099     if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
4100     {
4101         if (scrollInfo.nPos != scrollInfo.nMin)
4102             scroll |= SCROLL_UP;
4103 
4104         if (((scrollInfo.nPage + scrollInfo.nPos) - 1) != scrollInfo.nMax)
4105             scroll |= SCROLL_DOWN;
4106     }
4107 
4108     if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
4109     {
4110         if (scrollInfo.nPos != scrollInfo.nMin)
4111             scroll |= SCROLL_LEFT;
4112 
4113         if (((scrollInfo.nPage + scrollInfo.nPos) - 1) != scrollInfo.nMax)
4114             scroll |= SCROLL_RIGHT;
4115     }
4116 
4117     if (((coords.x <= 0) && (scroll & SCROLL_LEFT)) ||
4118         ((coords.y <= 0) && (scroll & SCROLL_UP))   ||
4119         ((coords.x >= infoPtr->rcList.right) && (scroll & SCROLL_RIGHT)) ||
4120         ((coords.y >= infoPtr->rcList.bottom) && (scroll & SCROLL_DOWN)))
4121     {
4122         LISTVIEW_MarqueeHighlight(infoPtr, &coords, scroll);
4123     }
4124 }
4125 
4126 /***
4127  * DESCRIPTION:
4128  * Called whenever WM_MOUSEMOVE is received.
4129  *
4130  * PARAMETER(S):
4131  * [I] infoPtr : valid pointer to the listview structure
4132  * [I] fwKeys : key indicator
4133  * [I] x,y : mouse position
4134  *
4135  * RETURN:
4136  *   0 if the message is processed, non-zero if there was an error
4137  */
LISTVIEW_MouseMove(LISTVIEW_INFO * infoPtr,WORD fwKeys,INT x,INT y)4138 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, INT x, INT y)
4139 {
4140     LVHITTESTINFO ht;
4141     RECT rect;
4142     POINT pt;
4143 
4144     pt.x = x;
4145     pt.y = y;
4146 
4147     if (!(fwKeys & MK_LBUTTON))
4148         infoPtr->bLButtonDown = FALSE;
4149 
4150     if (infoPtr->bLButtonDown)
4151     {
4152         rect.left = rect.right = infoPtr->ptClickPos.x;
4153         rect.top = rect.bottom = infoPtr->ptClickPos.y;
4154 
4155         InflateRect(&rect, GetSystemMetrics(SM_CXDRAG), GetSystemMetrics(SM_CYDRAG));
4156 
4157         if (infoPtr->bMarqueeSelect)
4158         {
4159             /* Enable the timer if we're going outside our bounds, in case the user doesn't
4160                move the mouse again */
4161 
4162             if ((x <= 0) || (y <= 0) || (x >= infoPtr->rcList.right) ||
4163                 (y >= infoPtr->rcList.bottom))
4164             {
4165                 if (!infoPtr->bScrolling)
4166                 {
4167                     infoPtr->bScrolling = TRUE;
4168                     SetTimer(infoPtr->hwndSelf, (UINT_PTR) infoPtr, 1, LISTVIEW_ScrollTimer);
4169                 }
4170             }
4171             else
4172             {
4173                 infoPtr->bScrolling = FALSE;
4174                 KillTimer(infoPtr->hwndSelf, (UINT_PTR) infoPtr);
4175             }
4176 
4177             LISTVIEW_MarqueeHighlight(infoPtr, &pt, 0);
4178             return 0;
4179         }
4180 
4181         ht.pt = pt;
4182         LISTVIEW_HitTest(infoPtr, &ht, TRUE, TRUE);
4183 
4184         /* reset item marker */
4185         if (infoPtr->nLButtonDownItem != ht.iItem)
4186             infoPtr->nLButtonDownItem = -1;
4187 
4188         if (!PtInRect(&rect, pt))
4189         {
4190             /* this path covers the following:
4191                1. WM_LBUTTONDOWN over selected item (sets focus on it)
4192                2. change focus with keys
4193                3. move mouse over item from step 1 selects it and moves focus on it */
4194             if (infoPtr->nLButtonDownItem != -1 &&
4195                !LISTVIEW_GetItemState(infoPtr, infoPtr->nLButtonDownItem, LVIS_SELECTED))
4196             {
4197                 LVITEMW lvItem;
4198 
4199                 lvItem.state =  LVIS_FOCUSED | LVIS_SELECTED;
4200                 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
4201 
4202                 LISTVIEW_SetItemState(infoPtr, infoPtr->nLButtonDownItem, &lvItem);
4203                 infoPtr->nLButtonDownItem = -1;
4204             }
4205 
4206             if (!infoPtr->bDragging)
4207             {
4208                 ht.pt = infoPtr->ptClickPos;
4209                 LISTVIEW_HitTest(infoPtr, &ht, TRUE, TRUE);
4210 
4211                 /* If the click is outside the range of an item, begin a
4212                    highlight. If not, begin an item drag. */
4213                 if (ht.iItem == -1)
4214                 {
4215                     NMHDR hdr;
4216 
4217                     /* If we're allowing multiple selections, send notification.
4218                        If return value is non-zero, cancel. */
4219                     if (!(infoPtr->dwStyle & LVS_SINGLESEL) && (notify_hdr(infoPtr, LVN_MARQUEEBEGIN, &hdr) == 0))
4220                     {
4221                         /* Store the absolute coordinates of the click */
4222                         POINT offset;
4223                         LISTVIEW_GetOrigin(infoPtr, &offset);
4224 
4225                         infoPtr->marqueeOrigin.x = infoPtr->ptClickPos.x - offset.x;
4226                         infoPtr->marqueeOrigin.y = infoPtr->ptClickPos.y - offset.y;
4227 
4228                         /* Begin selection and capture mouse */
4229                         infoPtr->bMarqueeSelect = TRUE;
4230                         infoPtr->marqueeRect = rect;
4231                         SetCapture(infoPtr->hwndSelf);
4232                     }
4233                 }
4234                 else
4235                 {
4236                     NMLISTVIEW nmlv;
4237 
4238                     ZeroMemory(&nmlv, sizeof(nmlv));
4239                     nmlv.iItem = ht.iItem;
4240                     nmlv.ptAction = infoPtr->ptClickPos;
4241 
4242                     notify_listview(infoPtr, LVN_BEGINDRAG, &nmlv);
4243                     infoPtr->bDragging = TRUE;
4244                 }
4245             }
4246 
4247             return 0;
4248         }
4249     }
4250 
4251     /* see if we are supposed to be tracking mouse hovering */
4252     if (LISTVIEW_IsHotTracking(infoPtr)) {
4253         TRACKMOUSEEVENT trackinfo;
4254         DWORD flags;
4255 
4256         trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
4257         trackinfo.dwFlags = TME_QUERY;
4258 
4259         /* see if we are already tracking this hwnd */
4260         _TrackMouseEvent(&trackinfo);
4261 
4262         flags = TME_LEAVE;
4263         if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)
4264             flags |= TME_HOVER;
4265 
4266         if((trackinfo.dwFlags & flags) != flags || trackinfo.hwndTrack != infoPtr->hwndSelf) {
4267             trackinfo.dwFlags     = flags;
4268             trackinfo.dwHoverTime = infoPtr->dwHoverTime;
4269             trackinfo.hwndTrack   = infoPtr->hwndSelf;
4270 
4271             /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
4272             _TrackMouseEvent(&trackinfo);
4273         }
4274     }
4275 
4276     return 0;
4277 }
4278 
4279 
4280 /***
4281  * Tests whether the item is assignable to a list with style lStyle
4282  */
is_assignable_item(const LVITEMW * lpLVItem,LONG lStyle)4283 static inline BOOL is_assignable_item(const LVITEMW *lpLVItem, LONG lStyle)
4284 {
4285     if ( (lpLVItem->mask & LVIF_TEXT) &&
4286 	(lpLVItem->pszText == LPSTR_TEXTCALLBACKW) &&
4287 	(lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE;
4288 
4289     return TRUE;
4290 }
4291 
4292 
4293 /***
4294  * DESCRIPTION:
4295  * Helper for LISTVIEW_SetItemT and LISTVIEW_InsertItemT: sets item attributes.
4296  *
4297  * PARAMETER(S):
4298  * [I] infoPtr : valid pointer to the listview structure
4299  * [I] lpLVItem : valid pointer to new item attributes
4300  * [I] isNew : the item being set is being inserted
4301  * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
4302  * [O] bChanged : will be set to TRUE if the item really changed
4303  *
4304  * RETURN:
4305  *   SUCCESS : TRUE
4306  *   FAILURE : FALSE
4307  */
set_main_item(LISTVIEW_INFO * infoPtr,const LVITEMW * lpLVItem,BOOL isNew,BOOL isW,BOOL * bChanged)4308 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isNew, BOOL isW, BOOL *bChanged)
4309 {
4310     ITEM_INFO *lpItem;
4311     NMLISTVIEW nmlv;
4312     UINT uChanged = 0;
4313     LVITEMW item;
4314     /* stateMask is ignored for LVM_INSERTITEM */
4315     UINT stateMask = isNew ? ~0 : lpLVItem->stateMask;
4316 
4317     TRACE("()\n");
4318 
4319     assert(lpLVItem->iItem >= 0 && lpLVItem->iItem < infoPtr->nItemCount);
4320 
4321     if (lpLVItem->mask == 0) return TRUE;
4322 
4323     if (infoPtr->dwStyle & LVS_OWNERDATA)
4324     {
4325 	/* a virtual listview only stores selection and focus */
4326 	if (lpLVItem->mask & ~LVIF_STATE)
4327 	    return FALSE;
4328 	lpItem = NULL;
4329     }
4330     else
4331     {
4332         HDPA hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
4333         lpItem = DPA_GetPtr(hdpaSubItems, 0);
4334 	assert (lpItem);
4335     }
4336 
4337     /* we need to get the lParam and state of the item */
4338     item.iItem = lpLVItem->iItem;
4339     item.iSubItem = lpLVItem->iSubItem;
4340     item.mask = LVIF_STATE | LVIF_PARAM;
4341     item.stateMask = (infoPtr->dwStyle & LVS_OWNERDATA) ? LVIS_FOCUSED | LVIS_SELECTED : ~0;
4342 
4343     item.state = 0;
4344     item.lParam = 0;
4345     if (!isNew && !LISTVIEW_GetItemW(infoPtr, &item)) return FALSE;
4346 
4347     TRACE("oldState=%x, newState=%x\n", item.state, lpLVItem->state);
4348     /* determine what fields will change */
4349     if ((lpLVItem->mask & LVIF_STATE) && ((item.state ^ lpLVItem->state) & stateMask & ~infoPtr->uCallbackMask))
4350 	uChanged |= LVIF_STATE;
4351 
4352     if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage))
4353 	uChanged |= LVIF_IMAGE;
4354 
4355     if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam))
4356 	uChanged |= LVIF_PARAM;
4357 
4358     if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent))
4359 	uChanged |= LVIF_INDENT;
4360 
4361     if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
4362 	uChanged |= LVIF_TEXT;
4363 
4364     TRACE("change mask=0x%x\n", uChanged);
4365 
4366     memset(&nmlv, 0, sizeof(NMLISTVIEW));
4367     nmlv.iItem = lpLVItem->iItem;
4368     if (lpLVItem->mask & LVIF_STATE)
4369     {
4370         nmlv.uNewState = (item.state & ~stateMask) | (lpLVItem->state & stateMask);
4371         nmlv.uOldState = item.state;
4372     }
4373     nmlv.uChanged = uChanged ? uChanged : lpLVItem->mask;
4374     nmlv.lParam = item.lParam;
4375 
4376     /* Send LVN_ITEMCHANGING notification, if the item is not being inserted
4377        and we are _NOT_ virtual (LVS_OWNERDATA), and change notifications
4378        are enabled. Even nothing really changed we still need to send this,
4379        in this case uChanged mask is just set to passed item mask. */
4380     if (lpItem && !isNew && (infoPtr->notify_mask & NOTIFY_MASK_ITEM_CHANGE))
4381     {
4382       HWND hwndSelf = infoPtr->hwndSelf;
4383 
4384       if (notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv))
4385 	return FALSE;
4386       if (!IsWindow(hwndSelf))
4387 	return FALSE;
4388     }
4389 
4390     /* When item is inserted we need to shift existing focus index if new item has lower index. */
4391     if (isNew && (stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED) &&
4392         /* this means we won't hit a focus change path later */
4393         ((uChanged & LVIF_STATE) == 0 || (!(lpLVItem->state & LVIS_FOCUSED) && (infoPtr->nFocusedItem != lpLVItem->iItem))))
4394     {
4395         if (infoPtr->nFocusedItem != -1 && (lpLVItem->iItem <= infoPtr->nFocusedItem))
4396             infoPtr->nFocusedItem++;
4397     }
4398 
4399     if (!uChanged) return TRUE;
4400     *bChanged = TRUE;
4401 
4402     /* copy information */
4403     if (lpLVItem->mask & LVIF_TEXT)
4404         textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
4405 
4406     if (lpLVItem->mask & LVIF_IMAGE)
4407 	lpItem->hdr.iImage = lpLVItem->iImage;
4408 
4409     if (lpLVItem->mask & LVIF_PARAM)
4410 	lpItem->lParam = lpLVItem->lParam;
4411 
4412     if (lpLVItem->mask & LVIF_INDENT)
4413 	lpItem->iIndent = lpLVItem->iIndent;
4414 
4415     if (uChanged & LVIF_STATE)
4416     {
4417 	if (lpItem && (stateMask & ~infoPtr->uCallbackMask))
4418 	{
4419 	    lpItem->state &= ~stateMask;
4420 	    lpItem->state |= (lpLVItem->state & stateMask);
4421 	}
4422 	if (lpLVItem->state & stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED)
4423 	{
4424 	    if (infoPtr->dwStyle & LVS_SINGLESEL) LISTVIEW_DeselectAllSkipItem(infoPtr, lpLVItem->iItem);
4425 	    ranges_additem(infoPtr->selectionRanges, lpLVItem->iItem);
4426 	}
4427 	else if (stateMask & LVIS_SELECTED)
4428 	{
4429 	    ranges_delitem(infoPtr->selectionRanges, lpLVItem->iItem);
4430 	}
4431 	/* If we are asked to change focus, and we manage it, do it.
4432            It's important to have all new item data stored at this point,
4433            because changing existing focus could result in a redrawing operation,
4434            which in turn could ask for disp data, application should see all data
4435            for inserted item when processing LVN_GETDISPINFO.
4436 
4437            The way this works application will see nested item change notifications -
4438            changed item notifications interrupted by ones from item losing focus. */
4439 	if (stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
4440 	{
4441 	    if (lpLVItem->state & LVIS_FOCUSED)
4442 	    {
4443 		/* update selection mark */
4444 		if (infoPtr->nFocusedItem == -1 && infoPtr->nSelectionMark == -1)
4445 		    infoPtr->nSelectionMark = lpLVItem->iItem;
4446 
4447 		if (infoPtr->nFocusedItem != -1)
4448 		{
4449 		    /* remove current focus */
4450 		    item.mask  = LVIF_STATE;
4451 		    item.state = 0;
4452 		    item.stateMask = LVIS_FOCUSED;
4453 
4454 		    /* recurse with redrawing an item */
4455 		    LISTVIEW_SetItemState(infoPtr, infoPtr->nFocusedItem, &item);
4456 		}
4457 
4458 		infoPtr->nFocusedItem = lpLVItem->iItem;
4459 	        LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, infoPtr->uView == LV_VIEW_LIST);
4460 	    }
4461 	    else if (infoPtr->nFocusedItem == lpLVItem->iItem)
4462 	    {
4463 	        infoPtr->nFocusedItem = -1;
4464 	    }
4465 	}
4466     }
4467 
4468     /* if we're inserting the item, we're done */
4469     if (isNew) return TRUE;
4470 
4471     /* send LVN_ITEMCHANGED notification */
4472     if (lpLVItem->mask & LVIF_PARAM) nmlv.lParam = lpLVItem->lParam;
4473     if (infoPtr->notify_mask & NOTIFY_MASK_ITEM_CHANGE)
4474         notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
4475 
4476     return TRUE;
4477 }
4478 
4479 /***
4480  * DESCRIPTION:
4481  * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes.
4482  *
4483  * PARAMETER(S):
4484  * [I] infoPtr : valid pointer to the listview structure
4485  * [I] lpLVItem : valid pointer to new subitem attributes
4486  * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
4487  * [O] bChanged : will be set to TRUE if the item really changed
4488  *
4489  * RETURN:
4490  *   SUCCESS : TRUE
4491  *   FAILURE : FALSE
4492  */
set_sub_item(const LISTVIEW_INFO * infoPtr,const LVITEMW * lpLVItem,BOOL isW,BOOL * bChanged)4493 static BOOL set_sub_item(const LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW, BOOL *bChanged)
4494 {
4495     HDPA hdpaSubItems;
4496     SUBITEM_INFO *lpSubItem;
4497 
4498     /* we do not support subitems for virtual listviews */
4499     if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
4500 
4501     /* set subitem only if column is present */
4502     if (lpLVItem->iSubItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
4503 
4504     /* First do some sanity checks */
4505     /* The LVIF_STATE flag is valid for subitems, but does not appear to be
4506        particularly useful. We currently do not actually do anything with
4507        the flag on subitems.
4508     */
4509     if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE | LVIF_STATE | LVIF_DI_SETITEM)) return FALSE;
4510     if (!(lpLVItem->mask & (LVIF_TEXT | LVIF_IMAGE | LVIF_STATE))) return TRUE;
4511 
4512     /* get the subitem structure, and create it if not there */
4513     hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
4514     assert (hdpaSubItems);
4515 
4516     lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
4517     if (!lpSubItem)
4518     {
4519 	SUBITEM_INFO *tmpSubItem;
4520 	INT i;
4521 
4522 	lpSubItem = Alloc(sizeof(SUBITEM_INFO));
4523 	if (!lpSubItem) return FALSE;
4524 	/* we could binary search here, if need be...*/
4525   	for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
4526   	{
4527             tmpSubItem = DPA_GetPtr(hdpaSubItems, i);
4528 	    if (tmpSubItem->iSubItem > lpLVItem->iSubItem) break;
4529   	}
4530 	if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1)
4531 	{
4532 	    Free(lpSubItem);
4533 	    return FALSE;
4534 	}
4535         lpSubItem->iSubItem = lpLVItem->iSubItem;
4536         lpSubItem->hdr.iImage = I_IMAGECALLBACK;
4537 	*bChanged = TRUE;
4538     }
4539 
4540     if ((lpLVItem->mask & LVIF_IMAGE) && (lpSubItem->hdr.iImage != lpLVItem->iImage))
4541     {
4542         lpSubItem->hdr.iImage = lpLVItem->iImage;
4543         *bChanged = TRUE;
4544     }
4545 
4546     if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpSubItem->hdr.pszText, lpLVItem->pszText, isW))
4547     {
4548         textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW);
4549         *bChanged = TRUE;
4550     }
4551 
4552     return TRUE;
4553 }
4554 
4555 /***
4556  * DESCRIPTION:
4557  * Sets item attributes.
4558  *
4559  * PARAMETER(S):
4560  * [I] infoPtr : valid pointer to the listview structure
4561  * [I] lpLVItem : new item attributes
4562  * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
4563  *
4564  * RETURN:
4565  *   SUCCESS : TRUE
4566  *   FAILURE : FALSE
4567  */
LISTVIEW_SetItemT(LISTVIEW_INFO * infoPtr,LVITEMW * lpLVItem,BOOL isW)4568 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, LVITEMW *lpLVItem, BOOL isW)
4569 {
4570     HWND hwndSelf = infoPtr->hwndSelf;
4571     LPWSTR pszText = NULL;
4572     BOOL bResult, bChanged = FALSE;
4573     RECT oldItemArea;
4574 
4575     TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
4576 
4577     if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
4578 	return FALSE;
4579 
4580     /* Store old item area */
4581     LISTVIEW_GetItemBox(infoPtr, lpLVItem->iItem, &oldItemArea);
4582 
4583     /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
4584     if ((lpLVItem->mask & LVIF_TEXT) && is_text(lpLVItem->pszText))
4585     {
4586 	pszText = lpLVItem->pszText;
4587 	lpLVItem->pszText = textdupTtoW(lpLVItem->pszText, isW);
4588     }
4589 
4590     /* actually set the fields */
4591     if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE;
4592 
4593     if (lpLVItem->iSubItem)
4594 	bResult = set_sub_item(infoPtr, lpLVItem, TRUE, &bChanged);
4595     else
4596 	bResult = set_main_item(infoPtr, lpLVItem, FALSE, TRUE, &bChanged);
4597     if (!IsWindow(hwndSelf))
4598 	return FALSE;
4599 
4600     /* redraw item, if necessary */
4601     if (bChanged && !infoPtr->bIsDrawing)
4602     {
4603 	/* this little optimization eliminates some nasty flicker */
4604 	if ( infoPtr->uView == LV_VIEW_DETAILS && !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) &&
4605 	     !(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) &&
4606              lpLVItem->iSubItem > 0 && lpLVItem->iSubItem <= DPA_GetPtrCount(infoPtr->hdpaColumns) )
4607 	    LISTVIEW_InvalidateSubItem(infoPtr, lpLVItem->iItem, lpLVItem->iSubItem);
4608 	else
4609         {
4610             LISTVIEW_InvalidateRect(infoPtr, &oldItemArea);
4611 	    LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem);
4612         }
4613     }
4614     /* restore text */
4615     if (pszText)
4616     {
4617 	textfreeT(lpLVItem->pszText, isW);
4618 	lpLVItem->pszText = pszText;
4619     }
4620 
4621     return bResult;
4622 }
4623 
4624 /***
4625  * DESCRIPTION:
4626  * Retrieves the index of the item at coordinate (0, 0) of the client area.
4627  *
4628  * PARAMETER(S):
4629  * [I] infoPtr : valid pointer to the listview structure
4630  *
4631  * RETURN:
4632  * item index
4633  */
LISTVIEW_GetTopIndex(const LISTVIEW_INFO * infoPtr)4634 static INT LISTVIEW_GetTopIndex(const LISTVIEW_INFO *infoPtr)
4635 {
4636     INT nItem = 0;
4637     SCROLLINFO scrollInfo;
4638 
4639     scrollInfo.cbSize = sizeof(SCROLLINFO);
4640     scrollInfo.fMask = SIF_POS;
4641 
4642     if (infoPtr->uView == LV_VIEW_LIST)
4643     {
4644 	if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
4645 	    nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
4646     }
4647     else if (infoPtr->uView == LV_VIEW_DETAILS)
4648     {
4649 	if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
4650 	    nItem = scrollInfo.nPos;
4651     }
4652     else
4653     {
4654 	if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
4655 	    nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight);
4656     }
4657 
4658     TRACE("nItem=%d\n", nItem);
4659 
4660     return nItem;
4661 }
4662 
4663 
4664 /***
4665  * DESCRIPTION:
4666  * Erases the background of the given rectangle
4667  *
4668  * PARAMETER(S):
4669  * [I] infoPtr : valid pointer to the listview structure
4670  * [I] hdc : device context handle
4671  * [I] lprcBox : clipping rectangle
4672  *
4673  * RETURN:
4674  *   Success: TRUE
4675  *   Failure: FALSE
4676  */
LISTVIEW_FillBkgnd(const LISTVIEW_INFO * infoPtr,HDC hdc,const RECT * lprcBox)4677 static inline BOOL LISTVIEW_FillBkgnd(const LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *lprcBox)
4678 {
4679     if (!infoPtr->hBkBrush) return FALSE;
4680 
4681     TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc, wine_dbgstr_rect(lprcBox), infoPtr->hBkBrush);
4682 
4683     return FillRect(hdc, lprcBox, infoPtr->hBkBrush);
4684 }
4685 
4686 /* Draw main item or subitem */
LISTVIEW_DrawItemPart(LISTVIEW_INFO * infoPtr,LVITEMW * item,const NMLVCUSTOMDRAW * nmlvcd,const POINT * pos)4687 static void LISTVIEW_DrawItemPart(LISTVIEW_INFO *infoPtr, LVITEMW *item, const NMLVCUSTOMDRAW *nmlvcd, const POINT *pos)
4688 {
4689     RECT rcSelect, rcLabel, rcBox, rcStateIcon, rcIcon;
4690     const RECT *background;
4691     HIMAGELIST himl;
4692     UINT format;
4693     RECT *focus;
4694 
4695     /* now check if we need to update the focus rectangle */
4696     focus = infoPtr->bFocus && (item->state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
4697     if (!focus) item->state &= ~LVIS_FOCUSED;
4698 
4699     LISTVIEW_GetItemMetrics(infoPtr, item, &rcBox, &rcSelect, &rcIcon, &rcStateIcon, &rcLabel);
4700     OffsetRect(&rcBox, pos->x, pos->y);
4701     OffsetRect(&rcSelect, pos->x, pos->y);
4702     OffsetRect(&rcIcon, pos->x, pos->y);
4703     OffsetRect(&rcStateIcon, pos->x, pos->y);
4704     OffsetRect(&rcLabel, pos->x, pos->y);
4705     TRACE("%d: box=%s, select=%s, icon=%s. label=%s\n", item->iSubItem,
4706         wine_dbgstr_rect(&rcBox), wine_dbgstr_rect(&rcSelect),
4707         wine_dbgstr_rect(&rcIcon), wine_dbgstr_rect(&rcLabel));
4708 
4709     /* FIXME: temporary hack */
4710     rcSelect.left = rcLabel.left;
4711 
4712     if (infoPtr->uView == LV_VIEW_DETAILS && item->iSubItem == 0)
4713     {
4714         if (!(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
4715             OffsetRect(&rcSelect, LISTVIEW_GetColumnInfo(infoPtr, 0)->rcHeader.left, 0);
4716         OffsetRect(&rcIcon, LISTVIEW_GetColumnInfo(infoPtr, 0)->rcHeader.left, 0);
4717         OffsetRect(&rcStateIcon, LISTVIEW_GetColumnInfo(infoPtr, 0)->rcHeader.left, 0);
4718         OffsetRect(&rcLabel, LISTVIEW_GetColumnInfo(infoPtr, 0)->rcHeader.left, 0);
4719     }
4720 
4721     /* in icon mode, the label rect is really what we want to draw the
4722      * background for */
4723     /* in detail mode, we want to paint background for label rect when
4724      * item is not selected or listview has full row select; otherwise paint
4725      * background for text only */
4726     if ( infoPtr->uView == LV_VIEW_ICON ||
4727         (infoPtr->uView == LV_VIEW_DETAILS && (!(item->state & LVIS_SELECTED) ||
4728         (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))))
4729         background = &rcLabel;
4730     else
4731         background = &rcSelect;
4732 
4733     if (nmlvcd->clrTextBk != CLR_NONE)
4734         ExtTextOutW(nmlvcd->nmcd.hdc, background->left, background->top, ETO_OPAQUE, background, NULL, 0, NULL);
4735 
4736     if (item->state & LVIS_FOCUSED)
4737     {
4738         if (infoPtr->uView == LV_VIEW_DETAILS)
4739         {
4740             if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
4741             {
4742                 /* we have to update left focus bound too if item isn't in leftmost column
4743 	           and reduce right box bound */
4744                 if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0)
4745                 {
4746                     INT leftmost;
4747 
4748                     if ((leftmost = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX, 0, 0)))
4749                     {
4750                         INT Originx = pos->x - LISTVIEW_GetColumnInfo(infoPtr, leftmost)->rcHeader.left;
4751                         INT rightmost = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX,
4752                             DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, 0);
4753 
4754                         rcBox.right   = LISTVIEW_GetColumnInfo(infoPtr, rightmost)->rcHeader.right + Originx;
4755                         rcSelect.left = LISTVIEW_GetColumnInfo(infoPtr, leftmost)->rcHeader.left + Originx;
4756                     }
4757                 }
4758                 rcSelect.right = rcBox.right;
4759             }
4760             infoPtr->rcFocus = rcSelect;
4761         }
4762         else
4763             infoPtr->rcFocus = rcLabel;
4764     }
4765 
4766     /* state icons */
4767     if (infoPtr->himlState && STATEIMAGEINDEX(item->state) && (item->iSubItem == 0))
4768     {
4769         UINT stateimage = STATEIMAGEINDEX(item->state);
4770         if (stateimage)
4771 	{
4772 	     TRACE("stateimage=%d\n", stateimage);
4773 	     ImageList_Draw(infoPtr->himlState, stateimage-1, nmlvcd->nmcd.hdc, rcStateIcon.left, rcStateIcon.top, ILD_NORMAL);
4774 	}
4775     }
4776 
4777     /* item icons */
4778     himl = (infoPtr->uView == LV_VIEW_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
4779     if (himl && item->iImage >= 0 && !IsRectEmpty(&rcIcon))
4780     {
4781         UINT style;
4782 
4783         TRACE("iImage=%d\n", item->iImage);
4784 
4785         if (item->state & (LVIS_SELECTED | LVIS_CUT) && infoPtr->bFocus)
4786             style = ILD_SELECTED;
4787         else
4788             style = ILD_NORMAL;
4789 
4790         ImageList_DrawEx(himl, item->iImage, nmlvcd->nmcd.hdc, rcIcon.left, rcIcon.top,
4791                          rcIcon.right - rcIcon.left, rcIcon.bottom - rcIcon.top, infoPtr->clrBk,
4792                          item->state & LVIS_CUT ? RGB(255, 255, 255) : CLR_DEFAULT,
4793                          style | (item->state & LVIS_OVERLAYMASK));
4794     }
4795 
4796     /* Don't bother painting item being edited */
4797     if (infoPtr->hwndEdit && item->iItem == infoPtr->nEditLabelItem && item->iSubItem == 0) return;
4798 
4799     /* figure out the text drawing flags */
4800     format = (infoPtr->uView == LV_VIEW_ICON ? (focus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS) : LV_SL_DT_FLAGS);
4801     if (infoPtr->uView == LV_VIEW_ICON)
4802 	format = (focus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS);
4803     else if (item->iSubItem)
4804     {
4805 	switch (LISTVIEW_GetColumnInfo(infoPtr, item->iSubItem)->fmt & LVCFMT_JUSTIFYMASK)
4806 	{
4807 	case LVCFMT_RIGHT:  format |= DT_RIGHT;  break;
4808 	case LVCFMT_CENTER: format |= DT_CENTER; break;
4809 	default:            format |= DT_LEFT;
4810 	}
4811     }
4812     if (!(format & (DT_RIGHT | DT_CENTER)))
4813     {
4814         if (himl && item->iImage >= 0 && !IsRectEmpty(&rcIcon)) rcLabel.left += IMAGE_PADDING;
4815         else rcLabel.left += LABEL_HOR_PADDING;
4816     }
4817     else if (format & DT_RIGHT) rcLabel.right -= LABEL_HOR_PADDING;
4818 
4819     /* for GRIDLINES reduce the bottom so the text formats correctly */
4820     if (infoPtr->uView == LV_VIEW_DETAILS && infoPtr->dwLvExStyle & LVS_EX_GRIDLINES)
4821         rcLabel.bottom--;
4822 
4823 #ifdef __REACTOS__
4824     if ((!(item->state & LVIS_SELECTED) || !infoPtr->bFocus) && (infoPtr->dwLvExStyle & LVS_EX_TRANSPARENTSHADOWTEXT))
4825         DrawShadowText(nmlvcd->nmcd.hdc, item->pszText, -1, &rcLabel, format, RGB(255, 255, 255), RGB(0, 0, 0), 2, 2);
4826     else
4827 #endif
4828         DrawTextW(nmlvcd->nmcd.hdc, item->pszText, -1, &rcLabel, format);
4829 }
4830 
4831 /***
4832  * DESCRIPTION:
4833  * Draws an item.
4834  *
4835  * PARAMETER(S):
4836  * [I] infoPtr : valid pointer to the listview structure
4837  * [I] hdc : device context handle
4838  * [I] nItem : item index
4839  * [I] nSubItem : subitem index
4840  * [I] pos : item position in client coordinates
4841  * [I] cdmode : custom draw mode
4842  *
4843  * RETURN:
4844  *   Success: TRUE
4845  *   Failure: FALSE
4846  */
LISTVIEW_DrawItem(LISTVIEW_INFO * infoPtr,HDC hdc,INT nItem,ITERATOR * subitems,POINT pos,DWORD cdmode)4847 static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, ITERATOR *subitems, POINT pos, DWORD cdmode)
4848 {
4849     WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4850     static WCHAR callbackW[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 };
4851     DWORD cdsubitemmode = CDRF_DODEFAULT;
4852     RECT *focus, rcBox;
4853     NMLVCUSTOMDRAW nmlvcd;
4854     LVITEMW lvItem;
4855 
4856     TRACE("(hdc=%p, nItem=%d, subitems=%p, pos=%s)\n", hdc, nItem, subitems, wine_dbgstr_point(&pos));
4857 
4858     /* get information needed for drawing the item */
4859     lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM | LVIF_STATE;
4860     if (infoPtr->uView == LV_VIEW_DETAILS) lvItem.mask |= LVIF_INDENT;
4861     lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK | LVIS_CUT | LVIS_OVERLAYMASK;
4862     lvItem.iItem = nItem;
4863     lvItem.iSubItem = 0;
4864     lvItem.state = 0;
4865     lvItem.lParam = 0;
4866     lvItem.cchTextMax = DISP_TEXT_SIZE;
4867     lvItem.pszText = szDispText;
4868     if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
4869     if (lvItem.pszText == LPSTR_TEXTCALLBACKW) lvItem.pszText = callbackW;
4870     TRACE("   lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
4871 
4872     /* now check if we need to update the focus rectangle */
4873     focus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
4874     if (!focus) lvItem.state &= ~LVIS_FOCUSED;
4875 
4876     LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, NULL, NULL, NULL, NULL);
4877     OffsetRect(&rcBox, pos.x, pos.y);
4878 
4879     /* Full custom draw stage sequence looks like this:
4880 
4881        LV_VIEW_DETAILS:
4882 
4883        - CDDS_ITEMPREPAINT
4884        - CDDS_ITEMPREPAINT|CDDS_SUBITEM   | => sent n times, where n is number of subitems,
4885          CDDS_ITEMPOSTPAINT|CDDS_SUBITEM  |    including item itself
4886        - CDDS_ITEMPOSTPAINT
4887 
4888        other styles:
4889 
4890        - CDDS_ITEMPREPAINT
4891        - CDDS_ITEMPOSTPAINT
4892     */
4893 
4894     /* fill in the custom draw structure */
4895     customdraw_fill(&nmlvcd, infoPtr, hdc, &rcBox, &lvItem);
4896     if (cdmode & CDRF_NOTIFYITEMDRAW)
4897         cdsubitemmode = notify_customdraw(infoPtr, CDDS_ITEMPREPAINT, &nmlvcd);
4898     if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
4899 
4900     if (subitems)
4901     {
4902         while (iterator_next(subitems))
4903         {
4904             DWORD subitemstage = CDRF_DODEFAULT;
4905             NMLVCUSTOMDRAW temp_nmlvcd;
4906 
4907             /* We need to query for each subitem, item's data (subitem == 0) is already here at this point */
4908             if (subitems->nItem)
4909             {
4910                 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM | LVIF_INDENT;
4911                 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK | LVIS_CUT | LVIS_OVERLAYMASK;
4912                 lvItem.iItem = nItem;
4913                 lvItem.iSubItem = subitems->nItem;
4914                 lvItem.state = 0;
4915                 lvItem.lParam = 0;
4916                 lvItem.cchTextMax = DISP_TEXT_SIZE;
4917                 lvItem.pszText = szDispText;
4918                 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
4919                 if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
4920 	            lvItem.state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
4921                 if (lvItem.pszText == LPSTR_TEXTCALLBACKW) lvItem.pszText = callbackW;
4922                 TRACE("   lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
4923 
4924                 /* update custom draw data */
4925                 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &nmlvcd.nmcd.rc, NULL, NULL, NULL, NULL);
4926                 OffsetRect(&nmlvcd.nmcd.rc, pos.x, pos.y);
4927                 nmlvcd.iSubItem = subitems->nItem;
4928             }
4929 
4930             if (cdsubitemmode & CDRF_NOTIFYSUBITEMDRAW)
4931                 subitemstage = notify_customdraw(infoPtr, CDDS_SUBITEM | CDDS_ITEMPREPAINT, &nmlvcd);
4932 
4933             /*
4934              * A selection should neither affect the colors in the post paint notification nor
4935              * affect the colors of the next drawn subitem. Copy the structure to prevent this.
4936              */
4937             temp_nmlvcd = nmlvcd;
4938             prepaint_setup(infoPtr, hdc, &temp_nmlvcd, subitems->nItem);
4939 
4940             if (!(subitemstage & CDRF_SKIPDEFAULT))
4941                 LISTVIEW_DrawItemPart(infoPtr, &lvItem, &temp_nmlvcd, &pos);
4942 
4943             if (subitemstage & CDRF_NOTIFYPOSTPAINT)
4944                 subitemstage = notify_customdraw(infoPtr, CDDS_SUBITEM | CDDS_ITEMPOSTPAINT, &nmlvcd);
4945         }
4946     }
4947     else
4948     {
4949         prepaint_setup(infoPtr, hdc, &nmlvcd, FALSE);
4950         LISTVIEW_DrawItemPart(infoPtr, &lvItem, &nmlvcd, &pos);
4951     }
4952 
4953 postpaint:
4954     if (cdsubitemmode & CDRF_NOTIFYPOSTPAINT)
4955     {
4956         nmlvcd.iSubItem = 0;
4957         notify_customdraw(infoPtr, CDDS_ITEMPOSTPAINT, &nmlvcd);
4958     }
4959 
4960     return TRUE;
4961 }
4962 
4963 /***
4964  * DESCRIPTION:
4965  * Draws listview items when in owner draw mode.
4966  *
4967  * PARAMETER(S):
4968  * [I] infoPtr : valid pointer to the listview structure
4969  * [I] hdc : device context handle
4970  *
4971  * RETURN:
4972  * None
4973  */
LISTVIEW_RefreshOwnerDraw(const LISTVIEW_INFO * infoPtr,ITERATOR * i,HDC hdc,DWORD cdmode)4974 static void LISTVIEW_RefreshOwnerDraw(const LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
4975 {
4976     UINT uID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
4977     DWORD cditemmode = CDRF_DODEFAULT;
4978     NMLVCUSTOMDRAW nmlvcd;
4979     POINT Origin, Position;
4980     DRAWITEMSTRUCT dis;
4981     LVITEMW item;
4982 
4983     TRACE("()\n");
4984 
4985     ZeroMemory(&dis, sizeof(dis));
4986 
4987     /* Get scroll info once before loop */
4988     LISTVIEW_GetOrigin(infoPtr, &Origin);
4989 
4990     /* iterate through the invalidated rows */
4991     while(iterator_next(i))
4992     {
4993 	item.iItem = i->nItem;
4994 	item.iSubItem = 0;
4995 	item.mask = LVIF_PARAM | LVIF_STATE;
4996 	item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
4997 	if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
4998 
4999 	dis.CtlType = ODT_LISTVIEW;
5000 	dis.CtlID = uID;
5001 	dis.itemID = item.iItem;
5002 	dis.itemAction = ODA_DRAWENTIRE;
5003 	dis.itemState = 0;
5004 	if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
5005 	if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS;
5006 	dis.hwndItem = infoPtr->hwndSelf;
5007 	dis.hDC = hdc;
5008 	LISTVIEW_GetItemOrigin(infoPtr, dis.itemID, &Position);
5009 	dis.rcItem.left = Position.x + Origin.x;
5010 	dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth;
5011 	dis.rcItem.top = Position.y + Origin.y;
5012 	dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
5013 	dis.itemData = item.lParam;
5014 
5015 	TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), wine_dbgstr_rect(&dis.rcItem));
5016 
5017     /*
5018      * Even if we do not send the CDRF_NOTIFYITEMDRAW we need to fill the nmlvcd
5019      * structure for the rest. of the paint cycle
5020      */
5021 	customdraw_fill(&nmlvcd, infoPtr, hdc, &dis.rcItem, &item);
5022 	if (cdmode & CDRF_NOTIFYITEMDRAW)
5023             cditemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
5024 
5025 	if (!(cditemmode & CDRF_SKIPDEFAULT))
5026 	{
5027             prepaint_setup (infoPtr, hdc, &nmlvcd, FALSE);
5028 	    SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
5029 	}
5030 
5031     	if (cditemmode & CDRF_NOTIFYPOSTPAINT)
5032             notify_postpaint(infoPtr, &nmlvcd);
5033     }
5034 }
5035 
5036 /***
5037  * DESCRIPTION:
5038  * Draws listview items when in report display mode.
5039  *
5040  * PARAMETER(S):
5041  * [I] infoPtr : valid pointer to the listview structure
5042  * [I] hdc : device context handle
5043  * [I] cdmode : custom draw mode
5044  *
5045  * RETURN:
5046  * None
5047  */
LISTVIEW_RefreshReport(LISTVIEW_INFO * infoPtr,ITERATOR * i,HDC hdc,DWORD cdmode)5048 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
5049 {
5050     INT rgntype;
5051     RECT rcClip, rcItem;
5052     POINT Origin;
5053     RANGES colRanges;
5054     INT col;
5055     ITERATOR j;
5056 
5057     TRACE("()\n");
5058 
5059     /* figure out what to draw */
5060     rgntype = GetClipBox(hdc, &rcClip);
5061     if (rgntype == NULLREGION) return;
5062 
5063     /* Get scroll info once before loop */
5064     LISTVIEW_GetOrigin(infoPtr, &Origin);
5065 
5066     colRanges = ranges_create(DPA_GetPtrCount(infoPtr->hdpaColumns));
5067 
5068     /* narrow down the columns we need to paint */
5069     for(col = 0; col < DPA_GetPtrCount(infoPtr->hdpaColumns); col++)
5070     {
5071 	INT index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX, col, 0);
5072 
5073 	LISTVIEW_GetHeaderRect(infoPtr, index, &rcItem);
5074 	if ((rcItem.right + Origin.x >= rcClip.left) && (rcItem.left + Origin.x < rcClip.right))
5075 	    ranges_additem(colRanges, index);
5076     }
5077     iterator_rangesitems(&j, colRanges);
5078 
5079     /* in full row select, we _have_ to draw the main item */
5080     if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
5081 	j.nSpecial = 0;
5082 
5083     /* iterate through the invalidated rows */
5084     while(iterator_next(i))
5085     {
5086         RANGES subitems;
5087         POINT Position;
5088         ITERATOR k;
5089 
5090         SelectObject(hdc, infoPtr->hFont);
5091 	LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
5092         Position.x = Origin.x;
5093 	Position.y += Origin.y;
5094 
5095         subitems = ranges_create(DPA_GetPtrCount(infoPtr->hdpaColumns));
5096 
5097 	/* iterate through the invalidated columns */
5098 	while(iterator_next(&j))
5099 	{
5100 	    LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
5101 
5102 	    if (rgntype == COMPLEXREGION && !((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && j.nItem == 0))
5103 	    {
5104 		rcItem.top = 0;
5105 	        rcItem.bottom = infoPtr->nItemHeight;
5106 		OffsetRect(&rcItem, Origin.x, Position.y);
5107 		if (!RectVisible(hdc, &rcItem)) continue;
5108 	    }
5109 
5110             ranges_additem(subitems, j.nItem);
5111 	}
5112 
5113         iterator_rangesitems(&k, subitems);
5114         LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, &k, Position, cdmode);
5115         iterator_destroy(&k);
5116     }
5117     iterator_destroy(&j);
5118 }
5119 
5120 /***
5121  * DESCRIPTION:
5122  * Draws the gridlines if necessary when in report display mode.
5123  *
5124  * PARAMETER(S):
5125  * [I] infoPtr : valid pointer to the listview structure
5126  * [I] hdc : device context handle
5127  *
5128  * RETURN:
5129  * None
5130  */
LISTVIEW_RefreshReportGrid(LISTVIEW_INFO * infoPtr,HDC hdc)5131 static void LISTVIEW_RefreshReportGrid(LISTVIEW_INFO *infoPtr, HDC hdc)
5132 {
5133     INT rgntype;
5134     INT y, itemheight;
5135     INT col, index;
5136     HPEN hPen, hOldPen;
5137     RECT rcClip, rcItem = {0};
5138     POINT Origin;
5139     RANGES colRanges;
5140     ITERATOR j;
5141     BOOL rmost = FALSE;
5142 
5143     TRACE("()\n");
5144 
5145     /* figure out what to draw */
5146     rgntype = GetClipBox(hdc, &rcClip);
5147     if (rgntype == NULLREGION) return;
5148 
5149     /* Get scroll info once before loop */
5150     LISTVIEW_GetOrigin(infoPtr, &Origin);
5151 
5152     colRanges = ranges_create(DPA_GetPtrCount(infoPtr->hdpaColumns));
5153 
5154     /* narrow down the columns we need to paint */
5155     for(col = 0; col < DPA_GetPtrCount(infoPtr->hdpaColumns); col++)
5156     {
5157         index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX, col, 0);
5158 
5159         LISTVIEW_GetHeaderRect(infoPtr, index, &rcItem);
5160         if ((rcItem.right + Origin.x >= rcClip.left) && (rcItem.left + Origin.x < rcClip.right))
5161             ranges_additem(colRanges, index);
5162     }
5163 
5164     /* is right most vertical line visible? */
5165     if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0)
5166     {
5167         index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, 0);
5168         LISTVIEW_GetHeaderRect(infoPtr, index, &rcItem);
5169         rmost = (rcItem.right + Origin.x < rcClip.right);
5170     }
5171 
5172     if ((hPen = CreatePen( PS_SOLID, 1, comctl32_color.clr3dFace )))
5173     {
5174         hOldPen = SelectObject ( hdc, hPen );
5175 
5176         /* draw the vertical lines for the columns */
5177         iterator_rangesitems(&j, colRanges);
5178         while(iterator_next(&j))
5179         {
5180             LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
5181             if (rcItem.left == 0) continue; /* skip leftmost column */
5182             rcItem.left += Origin.x;
5183             rcItem.right += Origin.x;
5184             rcItem.top = infoPtr->rcList.top;
5185             rcItem.bottom = infoPtr->rcList.bottom;
5186             TRACE("vert col=%d, rcItem=%s\n", j.nItem, wine_dbgstr_rect(&rcItem));
5187             MoveToEx (hdc, rcItem.left, rcItem.top, NULL);
5188             LineTo (hdc, rcItem.left, rcItem.bottom);
5189         }
5190         iterator_destroy(&j);
5191         /* draw rightmost grid line if visible */
5192         if (rmost)
5193         {
5194             index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX,
5195                                  DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, 0);
5196             LISTVIEW_GetHeaderRect(infoPtr, index, &rcItem);
5197 
5198             rcItem.right += Origin.x;
5199 
5200             MoveToEx (hdc, rcItem.right, infoPtr->rcList.top, NULL);
5201             LineTo (hdc, rcItem.right, infoPtr->rcList.bottom);
5202         }
5203 
5204         /* draw the horizontal lines for the rows */
5205         itemheight =  LISTVIEW_CalculateItemHeight(infoPtr);
5206         rcItem.left   = infoPtr->rcList.left;
5207         rcItem.right  = infoPtr->rcList.right;
5208         for(y = Origin.y > 1 ? Origin.y - 1 : itemheight - 1 + Origin.y % itemheight; y<=infoPtr->rcList.bottom; y+=itemheight)
5209         {
5210             rcItem.bottom = rcItem.top = y;
5211             TRACE("horz rcItem=%s\n", wine_dbgstr_rect(&rcItem));
5212             MoveToEx (hdc, rcItem.left, rcItem.top, NULL);
5213             LineTo (hdc, rcItem.right, rcItem.top);
5214         }
5215 
5216         SelectObject( hdc, hOldPen );
5217         DeleteObject( hPen );
5218     }
5219     else
5220         ranges_destroy(colRanges);
5221 }
5222 
5223 /***
5224  * DESCRIPTION:
5225  * Draws listview items when in list display mode.
5226  *
5227  * PARAMETER(S):
5228  * [I] infoPtr : valid pointer to the listview structure
5229  * [I] hdc : device context handle
5230  * [I] cdmode : custom draw mode
5231  *
5232  * RETURN:
5233  * None
5234  */
LISTVIEW_RefreshList(LISTVIEW_INFO * infoPtr,ITERATOR * i,HDC hdc,DWORD cdmode)5235 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
5236 {
5237     POINT Origin, Position;
5238 
5239     /* Get scroll info once before loop */
5240     LISTVIEW_GetOrigin(infoPtr, &Origin);
5241 
5242     while(iterator_prev(i))
5243     {
5244         SelectObject(hdc, infoPtr->hFont);
5245 	LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
5246 	Position.x += Origin.x;
5247 	Position.y += Origin.y;
5248 
5249         LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, NULL, Position, cdmode);
5250     }
5251 }
5252 
5253 
5254 /***
5255  * DESCRIPTION:
5256  * Draws listview items.
5257  *
5258  * PARAMETER(S):
5259  * [I] infoPtr : valid pointer to the listview structure
5260  * [I] hdc : device context handle
5261  * [I] prcErase : rect to be erased before refresh (may be NULL)
5262  *
5263  * RETURN:
5264  * NoneX
5265  */
LISTVIEW_Refresh(LISTVIEW_INFO * infoPtr,HDC hdc,const RECT * prcErase)5266 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *prcErase)
5267 {
5268     COLORREF oldTextColor = 0, oldBkColor = 0;
5269     NMLVCUSTOMDRAW nmlvcd;
5270     HFONT hOldFont = 0;
5271     DWORD cdmode;
5272     INT oldBkMode = 0;
5273     RECT rcClient;
5274     ITERATOR i;
5275     HDC hdcOrig = hdc;
5276     HBITMAP hbmp = NULL;
5277     RANGE range;
5278 
5279     LISTVIEW_DUMP(infoPtr);
5280 
5281     if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) {
5282         TRACE("double buffering\n");
5283 
5284         hdc = CreateCompatibleDC(hdcOrig);
5285         if (!hdc) {
5286             ERR("Failed to create DC for backbuffer\n");
5287             return;
5288         }
5289         hbmp = CreateCompatibleBitmap(hdcOrig, infoPtr->rcList.right,
5290                                       infoPtr->rcList.bottom);
5291         if (!hbmp) {
5292             ERR("Failed to create bitmap for backbuffer\n");
5293             DeleteDC(hdc);
5294             return;
5295         }
5296 
5297         SelectObject(hdc, hbmp);
5298         SelectObject(hdc, infoPtr->hFont);
5299 
5300         if(GetClipBox(hdcOrig, &rcClient))
5301             IntersectClipRect(hdc, rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);
5302     } else {
5303         /* Save dc values we're gonna trash while drawing
5304          * FIXME: Should be done in LISTVIEW_DrawItem() */
5305         hOldFont = SelectObject(hdc, infoPtr->hFont);
5306         oldBkMode = GetBkMode(hdc);
5307         oldBkColor = GetBkColor(hdc);
5308         oldTextColor = GetTextColor(hdc);
5309     }
5310 
5311     infoPtr->bIsDrawing = TRUE;
5312 
5313     if (prcErase) {
5314         LISTVIEW_FillBkgnd(infoPtr, hdc, prcErase);
5315     } else if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) {
5316         /* If no erasing was done (usually because RedrawWindow was called
5317          * with RDW_INVALIDATE only) we need to copy the old contents into
5318          * the backbuffer before continuing. */
5319         BitBlt(hdc, infoPtr->rcList.left, infoPtr->rcList.top,
5320                infoPtr->rcList.right - infoPtr->rcList.left,
5321                infoPtr->rcList.bottom - infoPtr->rcList.top,
5322                hdcOrig, infoPtr->rcList.left, infoPtr->rcList.top, SRCCOPY);
5323     }
5324 
5325     GetClientRect(infoPtr->hwndSelf, &rcClient);
5326     customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient, 0);
5327     cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
5328     if (cdmode & CDRF_SKIPDEFAULT) goto enddraw;
5329 
5330     /* nothing to draw */
5331     if(infoPtr->nItemCount == 0) goto enddraw;
5332 
5333     /* figure out what we need to draw */
5334     iterator_visibleitems(&i, infoPtr, hdc);
5335     range = iterator_range(&i);
5336 
5337     /* send cache hint notification */
5338     if (infoPtr->dwStyle & LVS_OWNERDATA)
5339     {
5340 	NMLVCACHEHINT nmlv;
5341 
5342     	ZeroMemory(&nmlv, sizeof(NMLVCACHEHINT));
5343     	nmlv.iFrom = range.lower;
5344     	nmlv.iTo   = range.upper - 1;
5345     	notify_hdr(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
5346     }
5347 
5348     if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (infoPtr->uView == LV_VIEW_DETAILS))
5349 	LISTVIEW_RefreshOwnerDraw(infoPtr, &i, hdc, cdmode);
5350     else
5351     {
5352 	if (infoPtr->uView == LV_VIEW_DETAILS)
5353             LISTVIEW_RefreshReport(infoPtr, &i, hdc, cdmode);
5354 	else /* LV_VIEW_LIST, LV_VIEW_ICON or LV_VIEW_SMALLICON */
5355 	    LISTVIEW_RefreshList(infoPtr, &i, hdc, cdmode);
5356 
5357 	/* if we have a focus rect and it's visible, draw it */
5358 	if (infoPtr->bFocus && range.lower <= infoPtr->nFocusedItem &&
5359                         (range.upper - 1) >= infoPtr->nFocusedItem)
5360 	    LISTVIEW_DrawFocusRect(infoPtr, hdc);
5361     }
5362     iterator_destroy(&i);
5363 
5364 enddraw:
5365     /* For LVS_EX_GRIDLINES go and draw lines */
5366     /*  This includes the case where there were *no* items */
5367     if ((infoPtr->uView == LV_VIEW_DETAILS) && infoPtr->dwLvExStyle & LVS_EX_GRIDLINES)
5368         LISTVIEW_RefreshReportGrid(infoPtr, hdc);
5369 
5370     /* Draw marquee rectangle if appropriate */
5371     if (infoPtr->bMarqueeSelect)
5372 #ifdef __REACTOS__
5373     {
5374         SetBkColor(hdc, RGB(255, 255, 255));
5375         SetTextColor(hdc, RGB(0, 0, 0));
5376         DrawFocusRect(hdc, &infoPtr->marqueeDrawRect);
5377     }
5378 #else
5379         DrawFocusRect(hdc, &infoPtr->marqueeDrawRect);
5380 #endif
5381 
5382     if (cdmode & CDRF_NOTIFYPOSTPAINT)
5383 	notify_postpaint(infoPtr, &nmlvcd);
5384 
5385     if(hbmp) {
5386         BitBlt(hdcOrig, infoPtr->rcList.left, infoPtr->rcList.top,
5387                infoPtr->rcList.right - infoPtr->rcList.left,
5388                infoPtr->rcList.bottom - infoPtr->rcList.top,
5389                hdc, infoPtr->rcList.left, infoPtr->rcList.top, SRCCOPY);
5390 
5391         DeleteObject(hbmp);
5392         DeleteDC(hdc);
5393     } else {
5394         SelectObject(hdc, hOldFont);
5395         SetBkMode(hdc, oldBkMode);
5396         SetBkColor(hdc, oldBkColor);
5397         SetTextColor(hdc, oldTextColor);
5398     }
5399 
5400     infoPtr->bIsDrawing = FALSE;
5401 }
5402 
5403 
5404 /***
5405  * DESCRIPTION:
5406  * Calculates the approximate width and height of a given number of items.
5407  *
5408  * PARAMETER(S):
5409  * [I] infoPtr : valid pointer to the listview structure
5410  * [I] nItemCount : number of items
5411  * [I] wWidth : width
5412  * [I] wHeight : height
5413  *
5414  * RETURN:
5415  * Returns a DWORD. The width in the low word and the height in high word.
5416  */
LISTVIEW_ApproximateViewRect(const LISTVIEW_INFO * infoPtr,INT nItemCount,WORD wWidth,WORD wHeight)5417 static DWORD LISTVIEW_ApproximateViewRect(const LISTVIEW_INFO *infoPtr, INT nItemCount,
5418                                             WORD wWidth, WORD wHeight)
5419 {
5420   DWORD dwViewRect = 0;
5421 
5422   if (nItemCount == -1)
5423     nItemCount = infoPtr->nItemCount;
5424 
5425   if (infoPtr->uView == LV_VIEW_LIST)
5426   {
5427     INT nItemCountPerColumn = 1;
5428     INT nColumnCount = 0;
5429 
5430     if (wHeight == 0xFFFF)
5431     {
5432       /* use current height */
5433       wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
5434     }
5435 
5436     if (wHeight < infoPtr->nItemHeight)
5437       wHeight = infoPtr->nItemHeight;
5438 
5439     if (nItemCount > 0)
5440     {
5441       if (infoPtr->nItemHeight > 0)
5442       {
5443         nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
5444         if (nItemCountPerColumn == 0)
5445           nItemCountPerColumn = 1;
5446 
5447         if (nItemCount % nItemCountPerColumn != 0)
5448           nColumnCount = nItemCount / nItemCountPerColumn;
5449         else
5450           nColumnCount = nItemCount / nItemCountPerColumn + 1;
5451       }
5452     }
5453 
5454     /* Microsoft padding magic */
5455     wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
5456     wWidth = nColumnCount * infoPtr->nItemWidth + 2;
5457 
5458     dwViewRect = MAKELONG(wWidth, wHeight);
5459   }
5460   else if (infoPtr->uView == LV_VIEW_DETAILS)
5461   {
5462     RECT rcBox;
5463 
5464     if (infoPtr->nItemCount > 0)
5465     {
5466       LISTVIEW_GetItemBox(infoPtr, 0, &rcBox);
5467       wWidth = rcBox.right - rcBox.left;
5468       wHeight = (rcBox.bottom - rcBox.top) * nItemCount;
5469     }
5470     else
5471     {
5472       /* use current height and width */
5473       if (wHeight == 0xffff)
5474           wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
5475       if (wWidth == 0xffff)
5476           wWidth = infoPtr->rcList.right - infoPtr->rcList.left;
5477     }
5478 
5479     dwViewRect = MAKELONG(wWidth, wHeight);
5480   }
5481   else if (infoPtr->uView == LV_VIEW_ICON)
5482   {
5483     UINT rows,cols;
5484     UINT nItemWidth;
5485     UINT nItemHeight;
5486 
5487     nItemWidth = infoPtr->iconSpacing.cx;
5488     nItemHeight = infoPtr->iconSpacing.cy;
5489 
5490     if (wWidth == 0xffff)
5491       wWidth = infoPtr->rcList.right - infoPtr->rcList.left;
5492 
5493     if (wWidth < nItemWidth)
5494       wWidth = nItemWidth;
5495 
5496     cols = wWidth / nItemWidth;
5497     if (cols > nItemCount)
5498       cols = nItemCount;
5499     if (cols < 1)
5500         cols = 1;
5501 
5502     if (nItemCount)
5503     {
5504       rows = nItemCount / cols;
5505       if (nItemCount % cols)
5506         rows++;
5507     }
5508     else
5509       rows = 0;
5510 
5511     wHeight = (nItemHeight * rows)+2;
5512     wWidth = (nItemWidth * cols)+2;
5513 
5514     dwViewRect = MAKELONG(wWidth, wHeight);
5515   }
5516   else if (infoPtr->uView == LV_VIEW_SMALLICON)
5517     FIXME("uView == LV_VIEW_SMALLICON: not implemented\n");
5518 
5519   return dwViewRect;
5520 }
5521 
5522 /***
5523  * DESCRIPTION:
5524  * Cancel edit label with saving item text.
5525  *
5526  * PARAMETER(S):
5527  * [I] infoPtr : valid pointer to the listview structure
5528  *
5529  * RETURN:
5530  * Always returns TRUE.
5531  */
LISTVIEW_CancelEditLabel(LISTVIEW_INFO * infoPtr)5532 static LRESULT LISTVIEW_CancelEditLabel(LISTVIEW_INFO *infoPtr)
5533 {
5534     if (infoPtr->hwndEdit)
5535     {
5536         /* handle value will be lost after LISTVIEW_EndEditLabelT */
5537         HWND edit = infoPtr->hwndEdit;
5538 
5539         LISTVIEW_EndEditLabelT(infoPtr, TRUE, IsWindowUnicode(infoPtr->hwndEdit));
5540         SendMessageW(edit, WM_CLOSE, 0, 0);
5541     }
5542 
5543     return TRUE;
5544 }
5545 
5546 /***
5547  * DESCRIPTION:
5548  * Create a drag image list for the specified item.
5549  *
5550  * PARAMETER(S):
5551  * [I] infoPtr : valid pointer to the listview structure
5552  * [I] iItem   : index of item
5553  * [O] lppt    : Upper-left corner of the image
5554  *
5555  * RETURN:
5556  * Returns a handle to the image list if successful, NULL otherwise.
5557  */
LISTVIEW_CreateDragImage(LISTVIEW_INFO * infoPtr,INT iItem,LPPOINT lppt)5558 static HIMAGELIST LISTVIEW_CreateDragImage(LISTVIEW_INFO *infoPtr, INT iItem, LPPOINT lppt)
5559 {
5560     RECT rcItem;
5561     SIZE size;
5562     POINT pos;
5563     HDC hdc, hdcOrig;
5564     HBITMAP hbmp, hOldbmp;
5565     HFONT hOldFont;
5566     HIMAGELIST dragList = 0;
5567     TRACE("iItem=%d Count=%d\n", iItem, infoPtr->nItemCount);
5568 
5569     if (iItem < 0 || iItem >= infoPtr->nItemCount || !lppt)
5570         return 0;
5571 
5572     rcItem.left = LVIR_BOUNDS;
5573     if (!LISTVIEW_GetItemRect(infoPtr, iItem, &rcItem))
5574         return 0;
5575 
5576     lppt->x = rcItem.left;
5577     lppt->y = rcItem.top;
5578 
5579     size.cx = rcItem.right - rcItem.left;
5580     size.cy = rcItem.bottom - rcItem.top;
5581 
5582     hdcOrig = GetDC(infoPtr->hwndSelf);
5583     hdc = CreateCompatibleDC(hdcOrig);
5584     hbmp = CreateCompatibleBitmap(hdcOrig, size.cx, size.cy);
5585     hOldbmp = SelectObject(hdc, hbmp);
5586     hOldFont = SelectObject(hdc, infoPtr->hFont);
5587 
5588     SetRect(&rcItem, 0, 0, size.cx, size.cy);
5589     FillRect(hdc, &rcItem, infoPtr->hBkBrush);
5590 
5591     pos.x = pos.y = 0;
5592     if (LISTVIEW_DrawItem(infoPtr, hdc, iItem, NULL, pos, CDRF_DODEFAULT))
5593     {
5594         dragList = ImageList_Create(size.cx, size.cy, ILC_COLOR, 10, 10);
5595         SelectObject(hdc, hOldbmp);
5596         ImageList_Add(dragList, hbmp, 0);
5597     }
5598     else
5599         SelectObject(hdc, hOldbmp);
5600 
5601     SelectObject(hdc, hOldFont);
5602     DeleteObject(hbmp);
5603     DeleteDC(hdc);
5604     ReleaseDC(infoPtr->hwndSelf, hdcOrig);
5605 
5606     TRACE("ret=%p\n", dragList);
5607 
5608     return dragList;
5609 }
5610 
5611 
5612 /***
5613  * DESCRIPTION:
5614  * Removes all listview items and subitems.
5615  *
5616  * PARAMETER(S):
5617  * [I] infoPtr : valid pointer to the listview structure
5618  *
5619  * RETURN:
5620  *   SUCCESS : TRUE
5621  *   FAILURE : FALSE
5622  */
LISTVIEW_DeleteAllItems(LISTVIEW_INFO * infoPtr,BOOL destroy)5623 static BOOL LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr, BOOL destroy)
5624 {
5625     HDPA hdpaSubItems = NULL;
5626     BOOL suppress = FALSE;
5627     ITEMHDR *hdrItem;
5628     ITEM_INFO *lpItem;
5629     ITEM_ID *lpID;
5630     INT i, j;
5631 
5632     TRACE("()\n");
5633 
5634     /* we do it directly, to avoid notifications */
5635     ranges_clear(infoPtr->selectionRanges);
5636     infoPtr->nSelectionMark = -1;
5637     infoPtr->nFocusedItem = -1;
5638     SetRectEmpty(&infoPtr->rcFocus);
5639     /* But we are supposed to leave nHotItem as is! */
5640 
5641     /* send LVN_DELETEALLITEMS notification */
5642     if (!(infoPtr->dwStyle & LVS_OWNERDATA) || !destroy)
5643     {
5644         NMLISTVIEW nmlv;
5645 
5646         memset(&nmlv, 0, sizeof(NMLISTVIEW));
5647         nmlv.iItem = -1;
5648         suppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
5649     }
5650 
5651     for (i = infoPtr->nItemCount - 1; i >= 0; i--)
5652     {
5653 	if (!(infoPtr->dwStyle & LVS_OWNERDATA))
5654 	{
5655 	    /* send LVN_DELETEITEM notification, if not suppressed
5656 	       and if it is not a virtual listview */
5657 	    if (!suppress) notify_deleteitem(infoPtr, i);
5658 	    hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, i);
5659 	    lpItem = DPA_GetPtr(hdpaSubItems, 0);
5660 	    /* free id struct */
5661 	    j = DPA_GetPtrIndex(infoPtr->hdpaItemIds, lpItem->id);
5662 	    lpID = DPA_GetPtr(infoPtr->hdpaItemIds, j);
5663 	    DPA_DeletePtr(infoPtr->hdpaItemIds, j);
5664 	    Free(lpID);
5665 	    /* both item and subitem start with ITEMHDR header */
5666 	    for (j = 0; j < DPA_GetPtrCount(hdpaSubItems); j++)
5667 	    {
5668 	        hdrItem = DPA_GetPtr(hdpaSubItems, j);
5669 		if (is_text(hdrItem->pszText)) Free(hdrItem->pszText);
5670 		Free(hdrItem);
5671 	    }
5672 	    DPA_Destroy(hdpaSubItems);
5673 	    DPA_DeletePtr(infoPtr->hdpaItems, i);
5674 	}
5675 	DPA_DeletePtr(infoPtr->hdpaPosX, i);
5676 	DPA_DeletePtr(infoPtr->hdpaPosY, i);
5677 	infoPtr->nItemCount --;
5678     }
5679 
5680     if (!destroy)
5681     {
5682         LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
5683         LISTVIEW_UpdateScroll(infoPtr);
5684     }
5685     LISTVIEW_InvalidateList(infoPtr);
5686 
5687     return TRUE;
5688 }
5689 
5690 /***
5691  * DESCRIPTION:
5692  * Scrolls, and updates the columns, when a column is changing width.
5693  *
5694  * PARAMETER(S):
5695  * [I] infoPtr : valid pointer to the listview structure
5696  * [I] nColumn : column to scroll
5697  * [I] dx : amount of scroll, in pixels
5698  *
5699  * RETURN:
5700  *   None.
5701  */
LISTVIEW_ScrollColumns(LISTVIEW_INFO * infoPtr,INT nColumn,INT dx)5702 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO *infoPtr, INT nColumn, INT dx)
5703 {
5704     COLUMN_INFO *lpColumnInfo;
5705     RECT rcOld, rcCol;
5706     POINT ptOrigin;
5707     INT nCol;
5708     HDITEMW hdi;
5709 
5710     if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) < 1) return;
5711     lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1));
5712     rcCol = lpColumnInfo->rcHeader;
5713     if (nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns))
5714 	rcCol.left = rcCol.right;
5715 
5716     /* adjust the other columns */
5717     hdi.mask = HDI_ORDER;
5718     if (SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, nColumn, (LPARAM)&hdi))
5719     {
5720 	INT nOrder = hdi.iOrder;
5721 	for (nCol = 0; nCol < DPA_GetPtrCount(infoPtr->hdpaColumns); nCol++)
5722 	{
5723 	    hdi.mask = HDI_ORDER;
5724 	    SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, nCol, (LPARAM)&hdi);
5725 	    if (hdi.iOrder >= nOrder) {
5726 		lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nCol);
5727 		lpColumnInfo->rcHeader.left  += dx;
5728 		lpColumnInfo->rcHeader.right += dx;
5729 	    }
5730 	}
5731     }
5732 
5733     /* do not update screen if not in report mode */
5734     if (!is_redrawing(infoPtr) || infoPtr->uView != LV_VIEW_DETAILS) return;
5735 
5736     /* Need to reset the item width when inserting a new column */
5737     infoPtr->nItemWidth += dx;
5738 
5739     LISTVIEW_UpdateScroll(infoPtr);
5740     LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
5741 
5742     /* scroll to cover the deleted column, and invalidate for redraw */
5743     rcOld = infoPtr->rcList;
5744     rcOld.left = ptOrigin.x + rcCol.left + dx;
5745     ScrollWindowEx(infoPtr->hwndSelf, dx, 0, &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
5746 }
5747 
5748 /***
5749  * DESCRIPTION:
5750  * Removes a column from the listview control.
5751  *
5752  * PARAMETER(S):
5753  * [I] infoPtr : valid pointer to the listview structure
5754  * [I] nColumn : column index
5755  *
5756  * RETURN:
5757  *   SUCCESS : TRUE
5758  *   FAILURE : FALSE
5759  */
LISTVIEW_DeleteColumn(LISTVIEW_INFO * infoPtr,INT nColumn)5760 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
5761 {
5762     RECT rcCol;
5763 
5764     TRACE("nColumn=%d\n", nColumn);
5765 
5766     if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns))
5767         return FALSE;
5768 
5769     /* While the MSDN specifically says that column zero should not be deleted,
5770        what actually happens is that the column itself is deleted but no items or subitems
5771        are removed.
5772      */
5773 
5774     LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
5775 
5776     if (!SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nColumn, 0))
5777 	return FALSE;
5778 
5779     Free(DPA_GetPtr(infoPtr->hdpaColumns, nColumn));
5780     DPA_DeletePtr(infoPtr->hdpaColumns, nColumn);
5781 
5782     if (!(infoPtr->dwStyle & LVS_OWNERDATA) && nColumn)
5783     {
5784 	SUBITEM_INFO *lpSubItem, *lpDelItem;
5785 	HDPA hdpaSubItems;
5786 	INT nItem, nSubItem, i;
5787 
5788 	for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
5789 	{
5790             hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, nItem);
5791 	    nSubItem = 0;
5792 	    lpDelItem = 0;
5793 	    for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
5794 	    {
5795                 lpSubItem = DPA_GetPtr(hdpaSubItems, i);
5796 		if (lpSubItem->iSubItem == nColumn)
5797 		{
5798 		    nSubItem = i;
5799 		    lpDelItem = lpSubItem;
5800 		}
5801 		else if (lpSubItem->iSubItem > nColumn)
5802 		{
5803 		    lpSubItem->iSubItem--;
5804 		}
5805 	    }
5806 
5807 	    /* if we found our subitem, zap it */
5808 	    if (nSubItem > 0)
5809 	    {
5810 		/* free string */
5811 		if (is_text(lpDelItem->hdr.pszText))
5812 		    Free(lpDelItem->hdr.pszText);
5813 
5814 		/* free item */
5815 		Free(lpDelItem);
5816 
5817 		/* free dpa memory */
5818 		DPA_DeletePtr(hdpaSubItems, nSubItem);
5819     	    }
5820 	}
5821     }
5822 
5823     /* update the other column info */
5824     if(DPA_GetPtrCount(infoPtr->hdpaColumns) == 0)
5825         LISTVIEW_InvalidateList(infoPtr);
5826     else
5827         LISTVIEW_ScrollColumns(infoPtr, nColumn, -(rcCol.right - rcCol.left));
5828     LISTVIEW_UpdateItemSize(infoPtr);
5829 
5830     return TRUE;
5831 }
5832 
5833 /***
5834  * DESCRIPTION:
5835  * Invalidates the listview after an item's insertion or deletion.
5836  *
5837  * PARAMETER(S):
5838  * [I] infoPtr : valid pointer to the listview structure
5839  * [I] nItem : item index
5840  * [I] dir : -1 if deleting, 1 if inserting
5841  *
5842  * RETURN:
5843  *   None
5844  */
LISTVIEW_ScrollOnInsert(LISTVIEW_INFO * infoPtr,INT nItem,INT dir)5845 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO *infoPtr, INT nItem, INT dir)
5846 {
5847     INT nPerCol, nItemCol, nItemRow;
5848     RECT rcScroll;
5849     POINT Origin;
5850 
5851     /* if we don't refresh, what's the point of scrolling? */
5852     if (!is_redrawing(infoPtr)) return;
5853 
5854     assert (abs(dir) == 1);
5855 
5856     /* arrange icons if autoarrange is on */
5857     if (is_autoarrange(infoPtr))
5858     {
5859 	BOOL arrange = TRUE;
5860 	if (dir < 0 && nItem >= infoPtr->nItemCount) arrange = FALSE;
5861 	if (dir > 0 && nItem == infoPtr->nItemCount - 1) arrange = FALSE;
5862 	if (arrange) LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
5863     }
5864 
5865     /* scrollbars need updating */
5866     LISTVIEW_UpdateScroll(infoPtr);
5867 
5868     /* figure out the item's position */
5869     if (infoPtr->uView == LV_VIEW_DETAILS)
5870 	nPerCol = infoPtr->nItemCount + 1;
5871     else if (infoPtr->uView == LV_VIEW_LIST)
5872 	nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
5873     else /* LV_VIEW_ICON, or LV_VIEW_SMALLICON */
5874 	return;
5875 
5876     nItemCol = nItem / nPerCol;
5877     nItemRow = nItem % nPerCol;
5878     LISTVIEW_GetOrigin(infoPtr, &Origin);
5879 
5880     /* move the items below up a slot */
5881     rcScroll.left = nItemCol * infoPtr->nItemWidth;
5882     rcScroll.top = nItemRow * infoPtr->nItemHeight;
5883     rcScroll.right = rcScroll.left + infoPtr->nItemWidth;
5884     rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
5885     OffsetRect(&rcScroll, Origin.x, Origin.y);
5886     TRACE("rcScroll=%s, dx=%d\n", wine_dbgstr_rect(&rcScroll), dir * infoPtr->nItemHeight);
5887     if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
5888     {
5889 	TRACE("Invalidating rcScroll=%s, rcList=%s\n", wine_dbgstr_rect(&rcScroll), wine_dbgstr_rect(&infoPtr->rcList));
5890 	InvalidateRect(infoPtr->hwndSelf, &rcScroll, TRUE);
5891     }
5892 
5893     /* report has only that column, so we're done */
5894     if (infoPtr->uView == LV_VIEW_DETAILS) return;
5895 
5896     /* now for LISTs, we have to deal with the columns to the right */
5897     SetRect(&rcScroll, (nItemCol + 1) * infoPtr->nItemWidth, 0,
5898             (infoPtr->nItemCount / nPerCol + 1) * infoPtr->nItemWidth,
5899             nPerCol * infoPtr->nItemHeight);
5900     OffsetRect(&rcScroll, Origin.x, Origin.y);
5901     if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
5902 	InvalidateRect(infoPtr->hwndSelf, &rcScroll, TRUE);
5903 }
5904 
5905 /***
5906  * DESCRIPTION:
5907  * Removes an item from the listview control.
5908  *
5909  * PARAMETER(S):
5910  * [I] infoPtr : valid pointer to the listview structure
5911  * [I] nItem : item index
5912  *
5913  * RETURN:
5914  *   SUCCESS : TRUE
5915  *   FAILURE : FALSE
5916  */
LISTVIEW_DeleteItem(LISTVIEW_INFO * infoPtr,INT nItem)5917 static BOOL LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
5918 {
5919     LVITEMW item;
5920     const BOOL is_icon = (infoPtr->uView == LV_VIEW_SMALLICON || infoPtr->uView == LV_VIEW_ICON);
5921     INT focus = infoPtr->nFocusedItem;
5922 
5923     TRACE("(nItem=%d)\n", nItem);
5924 
5925     if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5926 
5927     /* remove selection, and focus */
5928     item.state = 0;
5929     item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
5930     LISTVIEW_SetItemState(infoPtr, nItem, &item);
5931 
5932     /* send LVN_DELETEITEM notification. */
5933     if (!notify_deleteitem(infoPtr, nItem)) return FALSE;
5934 
5935     /* we need to do this here, because we'll be deleting stuff */
5936     if (is_icon)
5937 	LISTVIEW_InvalidateItem(infoPtr, nItem);
5938 
5939     if (!(infoPtr->dwStyle & LVS_OWNERDATA))
5940     {
5941         HDPA hdpaSubItems;
5942 	ITEMHDR *hdrItem;
5943 	ITEM_INFO *lpItem;
5944 	ITEM_ID *lpID;
5945 	INT i;
5946 
5947 	hdpaSubItems = DPA_DeletePtr(infoPtr->hdpaItems, nItem);
5948 	lpItem = DPA_GetPtr(hdpaSubItems, 0);
5949 
5950 	/* free id struct */
5951 	i = DPA_GetPtrIndex(infoPtr->hdpaItemIds, lpItem->id);
5952 	lpID = DPA_GetPtr(infoPtr->hdpaItemIds, i);
5953 	DPA_DeletePtr(infoPtr->hdpaItemIds, i);
5954 	Free(lpID);
5955 	for (i = 0; i < DPA_GetPtrCount(hdpaSubItems); i++)
5956     	{
5957             hdrItem = DPA_GetPtr(hdpaSubItems, i);
5958 	    if (is_text(hdrItem->pszText)) Free(hdrItem->pszText);
5959             Free(hdrItem);
5960         }
5961         DPA_Destroy(hdpaSubItems);
5962     }
5963 
5964     if (is_icon)
5965     {
5966 	DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
5967 	DPA_DeletePtr(infoPtr->hdpaPosY, nItem);
5968     }
5969 
5970     infoPtr->nItemCount--;
5971     LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
5972     LISTVIEW_ShiftFocus(infoPtr, focus, nItem, -1);
5973 
5974     /* now is the invalidation fun */
5975     if (!is_icon)
5976         LISTVIEW_ScrollOnInsert(infoPtr, nItem, -1);
5977     return TRUE;
5978 }
5979 
5980 
5981 /***
5982  * DESCRIPTION:
5983  * Callback implementation for editlabel control
5984  *
5985  * PARAMETER(S):
5986  * [I] infoPtr : valid pointer to the listview structure
5987  * [I] storeText : store edit box text as item text
5988  * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
5989  *
5990  * RETURN:
5991  *   SUCCESS : TRUE
5992  *   FAILURE : FALSE
5993  */
LISTVIEW_EndEditLabelT(LISTVIEW_INFO * infoPtr,BOOL storeText,BOOL isW)5994 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, BOOL storeText, BOOL isW)
5995 {
5996     HWND hwndSelf = infoPtr->hwndSelf;
5997     WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
5998     NMLVDISPINFOW dispInfo;
5999     INT editedItem = infoPtr->nEditLabelItem;
6000     BOOL same;
6001     WCHAR *pszText = NULL;
6002     BOOL res;
6003 
6004     if (storeText)
6005     {
6006         DWORD len = isW ? GetWindowTextLengthW(infoPtr->hwndEdit) : GetWindowTextLengthA(infoPtr->hwndEdit);
6007 
6008         if (len++)
6009         {
6010             if (!(pszText = Alloc(len * (isW ? sizeof(WCHAR) : sizeof(CHAR)))))
6011                 return FALSE;
6012 
6013             if (isW)
6014                 GetWindowTextW(infoPtr->hwndEdit, pszText, len);
6015             else
6016                 GetWindowTextA(infoPtr->hwndEdit, (CHAR*)pszText, len);
6017         }
6018     }
6019 
6020     TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
6021 
6022     ZeroMemory(&dispInfo, sizeof(dispInfo));
6023     dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
6024     dispInfo.item.iItem = editedItem;
6025     dispInfo.item.iSubItem = 0;
6026     dispInfo.item.stateMask = ~0;
6027     dispInfo.item.pszText = szDispText;
6028     dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
6029     if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW))
6030     {
6031        res = FALSE;
6032        goto cleanup;
6033     }
6034 
6035     if (isW)
6036         same = (lstrcmpW(dispInfo.item.pszText, pszText) == 0);
6037     else
6038     {
6039         LPWSTR tmp = textdupTtoW(pszText, FALSE);
6040         same = (lstrcmpW(dispInfo.item.pszText, tmp) == 0);
6041         textfreeT(tmp, FALSE);
6042     }
6043 
6044     /* add the text from the edit in */
6045     dispInfo.item.mask |= LVIF_TEXT;
6046     dispInfo.item.pszText = same ? NULL : pszText;
6047     dispInfo.item.cchTextMax = textlenT(dispInfo.item.pszText, isW);
6048 
6049     infoPtr->notify_mask &= ~NOTIFY_MASK_END_LABEL_EDIT;
6050 
6051     /* Do we need to update the Item Text */
6052     res = notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW);
6053 
6054     infoPtr->notify_mask |= NOTIFY_MASK_END_LABEL_EDIT;
6055 
6056     infoPtr->nEditLabelItem = -1;
6057     infoPtr->hwndEdit = 0;
6058 
6059     if (!res) goto cleanup;
6060 
6061     if (!IsWindow(hwndSelf))
6062     {
6063 	res = FALSE;
6064 	goto cleanup;
6065     }
6066     if (!pszText) return TRUE;
6067     if (same)
6068     {
6069         res = TRUE;
6070         goto cleanup;
6071     }
6072 
6073     if (!(infoPtr->dwStyle & LVS_OWNERDATA))
6074     {
6075         HDPA hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, editedItem);
6076         ITEM_INFO* lpItem = DPA_GetPtr(hdpaSubItems, 0);
6077         if (lpItem && lpItem->hdr.pszText == LPSTR_TEXTCALLBACKW)
6078         {
6079             LISTVIEW_InvalidateItem(infoPtr, editedItem);
6080             res = TRUE;
6081             goto cleanup;
6082         }
6083     }
6084 
6085     ZeroMemory(&dispInfo, sizeof(dispInfo));
6086     dispInfo.item.mask = LVIF_TEXT;
6087     dispInfo.item.iItem = editedItem;
6088     dispInfo.item.iSubItem = 0;
6089     dispInfo.item.pszText = pszText;
6090     dispInfo.item.cchTextMax = textlenT(pszText, isW);
6091     res = LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
6092 
6093 cleanup:
6094     Free(pszText);
6095 
6096     return res;
6097 }
6098 
6099 /***
6100  * DESCRIPTION:
6101  * Subclassed edit control windproc function
6102  *
6103  * PARAMETER(S):
6104  * [I] hwnd : the edit window handle
6105  * [I] uMsg : the message that is to be processed
6106  * [I] wParam : first message parameter
6107  * [I] lParam : second message parameter
6108  * [I] isW : TRUE if input is Unicode
6109  *
6110  * RETURN:
6111  *   Zero.
6112  */
EditLblWndProcT(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam,BOOL isW)6113 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL isW)
6114 {
6115     LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(GetParent(hwnd), 0);
6116     BOOL save = TRUE;
6117 
6118     TRACE("(hwnd=%p, uMsg=%x, wParam=%lx, lParam=%lx, isW=%d)\n",
6119 	  hwnd, uMsg, wParam, lParam, isW);
6120 
6121     switch (uMsg)
6122     {
6123 	case WM_GETDLGCODE:
6124 	  return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
6125 
6126 	case WM_DESTROY:
6127 	{
6128 	    WNDPROC editProc = infoPtr->EditWndProc;
6129 	    infoPtr->EditWndProc = 0;
6130 	    SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (DWORD_PTR)editProc);
6131 	    return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
6132 	}
6133 
6134 	case WM_KEYDOWN:
6135 	    if (VK_ESCAPE == (INT)wParam)
6136 	    {
6137 		save = FALSE;
6138                 break;
6139 	    }
6140 	    else if (VK_RETURN == (INT)wParam)
6141 		break;
6142 
6143 	default:
6144 	    return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
6145     }
6146 
6147     /* kill the edit */
6148     if (infoPtr->hwndEdit)
6149 	LISTVIEW_EndEditLabelT(infoPtr, save, isW);
6150 
6151     SendMessageW(hwnd, WM_CLOSE, 0, 0);
6152     return 0;
6153 }
6154 
6155 /***
6156  * DESCRIPTION:
6157  * Subclassed edit control Unicode windproc function
6158  *
6159  * PARAMETER(S):
6160  * [I] hwnd : the edit window handle
6161  * [I] uMsg : the message that is to be processed
6162  * [I] wParam : first message parameter
6163  * [I] lParam : second message parameter
6164  *
6165  * RETURN:
6166  */
EditLblWndProcW(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)6167 static LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
6168 {
6169     return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
6170 }
6171 
6172 /***
6173  * DESCRIPTION:
6174  * Subclassed edit control ANSI windproc function
6175  *
6176  * PARAMETER(S):
6177  * [I] hwnd : the edit window handle
6178  * [I] uMsg : the message that is to be processed
6179  * [I] wParam : first message parameter
6180  * [I] lParam : second message parameter
6181  *
6182  * RETURN:
6183  */
EditLblWndProcA(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)6184 static LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
6185 {
6186     return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
6187 }
6188 
6189 /***
6190  * DESCRIPTION:
6191  * Creates a subclassed edit control
6192  *
6193  * PARAMETER(S):
6194  * [I] infoPtr : valid pointer to the listview structure
6195  * [I] text : initial text for the edit
6196  * [I] style : the window style
6197  * [I] isW : TRUE if input is Unicode
6198  *
6199  * RETURN:
6200  */
CreateEditLabelT(LISTVIEW_INFO * infoPtr,LPCWSTR text,BOOL isW)6201 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, BOOL isW)
6202 {
6203     static const DWORD style = WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|ES_AUTOHSCROLL|WS_BORDER|WS_VISIBLE;
6204     HINSTANCE hinst = (HINSTANCE)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_HINSTANCE);
6205     HWND hedit;
6206 
6207     TRACE("(%p, text=%s, isW=%d)\n", infoPtr, debugtext_t(text, isW), isW);
6208 
6209     /* window will be resized and positioned after LVN_BEGINLABELEDIT */
6210     if (isW)
6211 	hedit = CreateWindowW(WC_EDITW, text, style, 0, 0, 0, 0, infoPtr->hwndSelf, 0, hinst, 0);
6212     else
6213 	hedit = CreateWindowA(WC_EDITA, (LPCSTR)text, style, 0, 0, 0, 0, infoPtr->hwndSelf, 0, hinst, 0);
6214 
6215     if (!hedit) return 0;
6216 
6217     infoPtr->EditWndProc = (WNDPROC)
6218 	(isW ? SetWindowLongPtrW(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcW) :
6219                SetWindowLongPtrA(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcA) );
6220 
6221     SendMessageW(hedit, WM_SETFONT, (WPARAM)infoPtr->hFont, FALSE);
6222     SendMessageW(hedit, EM_SETLIMITTEXT, DISP_TEXT_SIZE-1, 0);
6223 
6224     return hedit;
6225 }
6226 
6227 /***
6228  * DESCRIPTION:
6229  * Begin in place editing of specified list view item
6230  *
6231  * PARAMETER(S):
6232  * [I] infoPtr : valid pointer to the listview structure
6233  * [I] nItem : item index
6234  * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
6235  *
6236  * RETURN:
6237  *   SUCCESS : TRUE
6238  *   FAILURE : FALSE
6239  */
LISTVIEW_EditLabelT(LISTVIEW_INFO * infoPtr,INT nItem,BOOL isW)6240 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
6241 {
6242     WCHAR disptextW[DISP_TEXT_SIZE] = { 0 };
6243     HWND hwndSelf = infoPtr->hwndSelf;
6244     NMLVDISPINFOW dispInfo;
6245     HFONT hOldFont = NULL;
6246     TEXTMETRICW tm;
6247     RECT rect;
6248     SIZE sz;
6249     HDC hdc;
6250 
6251     TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
6252 
6253     if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
6254 
6255     /* remove existing edit box */
6256     if (infoPtr->hwndEdit)
6257     {
6258         SetFocus(infoPtr->hwndSelf);
6259         infoPtr->hwndEdit = 0;
6260     }
6261 
6262     if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
6263 
6264     infoPtr->nEditLabelItem = nItem;
6265 
6266     LISTVIEW_SetSelection(infoPtr, nItem);
6267     LISTVIEW_SetItemFocus(infoPtr, nItem);
6268     LISTVIEW_InvalidateItem(infoPtr, nItem);
6269 
6270     rect.left = LVIR_LABEL;
6271     if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
6272 
6273     ZeroMemory(&dispInfo, sizeof(dispInfo));
6274     dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
6275     dispInfo.item.iItem = nItem;
6276     dispInfo.item.iSubItem = 0;
6277     dispInfo.item.stateMask = ~0;
6278     dispInfo.item.pszText = disptextW;
6279     dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
6280     if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
6281 
6282     infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, isW);
6283     if (!infoPtr->hwndEdit) return 0;
6284 
6285     if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
6286     {
6287 	if (!IsWindow(hwndSelf))
6288 	    return 0;
6289 	SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
6290 	infoPtr->hwndEdit = 0;
6291 	return 0;
6292     }
6293 
6294     TRACE("disp text=%s\n", debugtext_t(dispInfo.item.pszText, isW));
6295 
6296     /* position and display edit box */
6297     hdc = GetDC(infoPtr->hwndSelf);
6298 
6299     /* select the font to get appropriate metric dimensions */
6300     if (infoPtr->hFont)
6301         hOldFont = SelectObject(hdc, infoPtr->hFont);
6302 
6303     /* use real edit box content, it could be altered during LVN_BEGINLABELEDIT notification */
6304     GetWindowTextW(infoPtr->hwndEdit, disptextW, DISP_TEXT_SIZE);
6305     TRACE("edit box text=%s\n", debugstr_w(disptextW));
6306 
6307     /* get string length in pixels */
6308     GetTextExtentPoint32W(hdc, disptextW, lstrlenW(disptextW), &sz);
6309 
6310     /* add extra spacing for the next character */
6311     GetTextMetricsW(hdc, &tm);
6312     sz.cx += tm.tmMaxCharWidth * 2;
6313 
6314     if (infoPtr->hFont)
6315         SelectObject(hdc, hOldFont);
6316 
6317     ReleaseDC(infoPtr->hwndSelf, hdc);
6318 
6319     sz.cy = rect.bottom - rect.top + 2;
6320     rect.left -= 2;
6321     rect.top  -= 1;
6322     TRACE("moving edit=(%d,%d)-(%d,%d)\n", rect.left, rect.top, sz.cx, sz.cy);
6323     MoveWindow(infoPtr->hwndEdit, rect.left, rect.top, sz.cx, sz.cy, FALSE);
6324     ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
6325     SetFocus(infoPtr->hwndEdit);
6326     SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
6327     return infoPtr->hwndEdit;
6328 }
6329 
6330 
6331 /***
6332  * DESCRIPTION:
6333  * Ensures the specified item is visible, scrolling into view if necessary.
6334  *
6335  * PARAMETER(S):
6336  * [I] infoPtr : valid pointer to the listview structure
6337  * [I] nItem : item index
6338  * [I] bPartial : partially or entirely visible
6339  *
6340  * RETURN:
6341  *   SUCCESS : TRUE
6342  *   FAILURE : FALSE
6343  */
LISTVIEW_EnsureVisible(LISTVIEW_INFO * infoPtr,INT nItem,BOOL bPartial)6344 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
6345 {
6346     INT nScrollPosHeight = 0;
6347     INT nScrollPosWidth = 0;
6348     INT nHorzAdjust = 0;
6349     INT nVertAdjust = 0;
6350     INT nHorzDiff = 0;
6351     INT nVertDiff = 0;
6352     RECT rcItem, rcTemp;
6353 
6354     rcItem.left = LVIR_BOUNDS;
6355     if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
6356 
6357     if (bPartial && IntersectRect(&rcTemp, &infoPtr->rcList, &rcItem)) return TRUE;
6358 
6359     if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
6360     {
6361         /* scroll left/right, but in LV_VIEW_DETAILS mode */
6362         if (infoPtr->uView == LV_VIEW_LIST)
6363             nScrollPosWidth = infoPtr->nItemWidth;
6364         else if ((infoPtr->uView == LV_VIEW_SMALLICON) || (infoPtr->uView == LV_VIEW_ICON))
6365             nScrollPosWidth = 1;
6366 
6367 	if (rcItem.left < infoPtr->rcList.left)
6368 	{
6369 	    nHorzAdjust = -1;
6370 	    if (infoPtr->uView != LV_VIEW_DETAILS) nHorzDiff = rcItem.left - infoPtr->rcList.left;
6371 	}
6372 	else
6373 	{
6374 	    nHorzAdjust = 1;
6375 	    if (infoPtr->uView != LV_VIEW_DETAILS) nHorzDiff = rcItem.right - infoPtr->rcList.right;
6376 	}
6377     }
6378 
6379     if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
6380     {
6381 	/* scroll up/down, but not in LVS_LIST mode */
6382         if (infoPtr->uView == LV_VIEW_DETAILS)
6383             nScrollPosHeight = infoPtr->nItemHeight;
6384         else if ((infoPtr->uView == LV_VIEW_ICON) || (infoPtr->uView == LV_VIEW_SMALLICON))
6385             nScrollPosHeight = 1;
6386 
6387 	if (rcItem.top < infoPtr->rcList.top)
6388 	{
6389 	    nVertAdjust = -1;
6390 	    if (infoPtr->uView != LV_VIEW_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top;
6391 	}
6392 	else
6393 	{
6394 	    nVertAdjust = 1;
6395 	    if (infoPtr->uView != LV_VIEW_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
6396 	}
6397     }
6398 
6399     if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
6400 
6401     if (nScrollPosWidth)
6402     {
6403 	INT diff = nHorzDiff / nScrollPosWidth;
6404 	if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust;
6405 	LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff);
6406     }
6407 
6408     if (nScrollPosHeight)
6409     {
6410 	INT diff = nVertDiff / nScrollPosHeight;
6411 	if (nVertDiff % nScrollPosHeight) diff += nVertAdjust;
6412 	LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff);
6413     }
6414 
6415     return TRUE;
6416 }
6417 
6418 /***
6419  * DESCRIPTION:
6420  * Searches for an item with specific characteristics.
6421  *
6422  * PARAMETER(S):
6423  * [I] hwnd : window handle
6424  * [I] nStart : base item index
6425  * [I] lpFindInfo : item information to look for
6426  *
6427  * RETURN:
6428  *   SUCCESS : index of item
6429  *   FAILURE : -1
6430  */
LISTVIEW_FindItemW(const LISTVIEW_INFO * infoPtr,INT nStart,const LVFINDINFOW * lpFindInfo)6431 static INT LISTVIEW_FindItemW(const LISTVIEW_INFO *infoPtr, INT nStart,
6432                               const LVFINDINFOW *lpFindInfo)
6433 {
6434     WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
6435     BOOL bWrap = FALSE, bNearest = FALSE;
6436     INT nItem = nStart + 1, nLast = infoPtr->nItemCount, nNearestItem = -1;
6437     ULONG xdist, ydist, dist, mindist = 0x7fffffff;
6438     POINT Position, Destination;
6439     LVITEMW lvItem;
6440 
6441     /* Search in virtual listviews should be done by application, not by
6442        listview control, so we just send LVN_ODFINDITEMW and return the result */
6443     if (infoPtr->dwStyle & LVS_OWNERDATA)
6444     {
6445         NMLVFINDITEMW nmlv;
6446 
6447         nmlv.iStart = nStart;
6448         nmlv.lvfi = *lpFindInfo;
6449         return notify_hdr(infoPtr, LVN_ODFINDITEMW, (LPNMHDR)&nmlv.hdr);
6450     }
6451 
6452     if (!lpFindInfo || nItem < 0) return -1;
6453 
6454     lvItem.mask = 0;
6455     if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL | LVFI_SUBSTRING))
6456     {
6457         lvItem.mask |= LVIF_TEXT;
6458         lvItem.pszText = szDispText;
6459         lvItem.cchTextMax = DISP_TEXT_SIZE;
6460     }
6461 
6462     if (lpFindInfo->flags & LVFI_WRAP)
6463         bWrap = TRUE;
6464 
6465     if ((lpFindInfo->flags & LVFI_NEARESTXY) &&
6466 	(infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON))
6467     {
6468 	POINT Origin;
6469 	RECT rcArea;
6470 
6471         LISTVIEW_GetOrigin(infoPtr, &Origin);
6472 	Destination.x = lpFindInfo->pt.x - Origin.x;
6473 	Destination.y = lpFindInfo->pt.y - Origin.y;
6474 	switch(lpFindInfo->vkDirection)
6475 	{
6476 	case VK_DOWN:  Destination.y += infoPtr->nItemHeight; break;
6477 	case VK_UP:    Destination.y -= infoPtr->nItemHeight; break;
6478 	case VK_RIGHT: Destination.x += infoPtr->nItemWidth; break;
6479 	case VK_LEFT:  Destination.x -= infoPtr->nItemWidth; break;
6480 	case VK_HOME:  Destination.x = Destination.y = 0; break;
6481 	case VK_NEXT:  Destination.y += infoPtr->rcList.bottom - infoPtr->rcList.top; break;
6482 	case VK_PRIOR: Destination.y -= infoPtr->rcList.bottom - infoPtr->rcList.top; break;
6483 	case VK_END:
6484 	    LISTVIEW_GetAreaRect(infoPtr, &rcArea);
6485 	    Destination.x = rcArea.right;
6486 	    Destination.y = rcArea.bottom;
6487 	    break;
6488 	default: ERR("Unknown vkDirection=%d\n", lpFindInfo->vkDirection);
6489 	}
6490 	bNearest = TRUE;
6491     }
6492     else Destination.x = Destination.y = 0;
6493 
6494     /* if LVFI_PARAM is specified, all other flags are ignored */
6495     if (lpFindInfo->flags & LVFI_PARAM)
6496     {
6497         lvItem.mask |= LVIF_PARAM;
6498 	bNearest = FALSE;
6499 	lvItem.mask &= ~LVIF_TEXT;
6500     }
6501 
6502     nItem = bNearest ? -1 : nStart + 1;
6503 
6504 again:
6505     for (; nItem < nLast; nItem++)
6506     {
6507         lvItem.iItem = nItem;
6508         lvItem.iSubItem = 0;
6509         lvItem.pszText = szDispText;
6510         if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
6511 
6512 	if (lvItem.mask & LVIF_PARAM)
6513         {
6514             if (lpFindInfo->lParam == lvItem.lParam)
6515                 return nItem;
6516             else
6517                 continue;
6518         }
6519 
6520         if (lvItem.mask & LVIF_TEXT)
6521 	{
6522             if (lpFindInfo->flags & (LVFI_PARTIAL | LVFI_SUBSTRING))
6523             {
6524 		WCHAR *p = wcsstr(lvItem.pszText, lpFindInfo->psz);
6525 		if (!p || p != lvItem.pszText) continue;
6526             }
6527             else
6528             {
6529             	if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0) continue;
6530             }
6531 	}
6532 
6533         if (!bNearest) return nItem;
6534 
6535 	/* This is very inefficient. To do a good job here,
6536 	 * we need a sorted array of (x,y) item positions */
6537 	LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
6538 
6539 	/* compute the distance^2 to the destination */
6540 	xdist = Destination.x - Position.x;
6541 	ydist = Destination.y - Position.y;
6542 	dist = xdist * xdist + ydist * ydist;
6543 
6544 	/* remember the distance, and item if it's closer */
6545 	if (dist < mindist)
6546 	{
6547 	    mindist = dist;
6548 	    nNearestItem = nItem;
6549 	}
6550     }
6551 
6552     if (bWrap)
6553     {
6554         nItem = 0;
6555         nLast = min(nStart + 1, infoPtr->nItemCount);
6556         bWrap = FALSE;
6557 	goto again;
6558     }
6559 
6560     return nNearestItem;
6561 }
6562 
6563 /***
6564  * DESCRIPTION:
6565  * Searches for an item with specific characteristics.
6566  *
6567  * PARAMETER(S):
6568  * [I] hwnd : window handle
6569  * [I] nStart : base item index
6570  * [I] lpFindInfo : item information to look for
6571  *
6572  * RETURN:
6573  *   SUCCESS : index of item
6574  *   FAILURE : -1
6575  */
LISTVIEW_FindItemA(const LISTVIEW_INFO * infoPtr,INT nStart,const LVFINDINFOA * lpFindInfo)6576 static INT LISTVIEW_FindItemA(const LISTVIEW_INFO *infoPtr, INT nStart,
6577                               const LVFINDINFOA *lpFindInfo)
6578 {
6579     LVFINDINFOW fiw;
6580     INT res;
6581     LPWSTR strW = NULL;
6582 
6583     memcpy(&fiw, lpFindInfo, sizeof(fiw));
6584     if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL | LVFI_SUBSTRING))
6585         fiw.psz = strW = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
6586     res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
6587     textfreeT(strW, FALSE);
6588     return res;
6589 }
6590 
6591 /***
6592  * DESCRIPTION:
6593  * Retrieves column attributes.
6594  *
6595  * PARAMETER(S):
6596  * [I] infoPtr : valid pointer to the listview structure
6597  * [I] nColumn :  column index
6598  * [IO] lpColumn : column information
6599  * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
6600  *           otherwise it is in fact a LPLVCOLUMNA
6601  *
6602  * RETURN:
6603  *   SUCCESS : TRUE
6604  *   FAILURE : FALSE
6605  */
LISTVIEW_GetColumnT(const LISTVIEW_INFO * infoPtr,INT nColumn,LPLVCOLUMNW lpColumn,BOOL isW)6606 static BOOL LISTVIEW_GetColumnT(const LISTVIEW_INFO *infoPtr, INT nColumn, LPLVCOLUMNW lpColumn, BOOL isW)
6607 {
6608     COLUMN_INFO *lpColumnInfo;
6609     HDITEMW hdi;
6610 
6611     if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6612     lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
6613 
6614     /* initialize memory */
6615     ZeroMemory(&hdi, sizeof(hdi));
6616 
6617     if (lpColumn->mask & LVCF_TEXT)
6618     {
6619         hdi.mask |= HDI_TEXT;
6620         hdi.pszText = lpColumn->pszText;
6621         hdi.cchTextMax = lpColumn->cchTextMax;
6622     }
6623 
6624     if (lpColumn->mask & LVCF_IMAGE)
6625         hdi.mask |= HDI_IMAGE;
6626 
6627     if (lpColumn->mask & LVCF_ORDER)
6628         hdi.mask |= HDI_ORDER;
6629 
6630     if (lpColumn->mask & LVCF_SUBITEM)
6631         hdi.mask |= HDI_LPARAM;
6632 
6633     if (!SendMessageW(infoPtr->hwndHeader, isW ? HDM_GETITEMW : HDM_GETITEMA, nColumn, (LPARAM)&hdi)) return FALSE;
6634 
6635     if (lpColumn->mask & LVCF_FMT)
6636 	lpColumn->fmt = lpColumnInfo->fmt;
6637 
6638     if (lpColumn->mask & LVCF_WIDTH)
6639         lpColumn->cx = lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left;
6640 
6641     if (lpColumn->mask & LVCF_IMAGE)
6642 	lpColumn->iImage = hdi.iImage;
6643 
6644     if (lpColumn->mask & LVCF_ORDER)
6645 	lpColumn->iOrder = hdi.iOrder;
6646 
6647     if (lpColumn->mask & LVCF_SUBITEM)
6648 	lpColumn->iSubItem = hdi.lParam;
6649 
6650     if (lpColumn->mask & LVCF_MINWIDTH)
6651 	lpColumn->cxMin = lpColumnInfo->cxMin;
6652 
6653     return TRUE;
6654 }
6655 
LISTVIEW_GetColumnOrderArray(const LISTVIEW_INFO * infoPtr,INT iCount,LPINT lpiArray)6656 static inline BOOL LISTVIEW_GetColumnOrderArray(const LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
6657 {
6658     if (!infoPtr->hwndHeader) return FALSE;
6659     return SendMessageW(infoPtr->hwndHeader, HDM_GETORDERARRAY, iCount, (LPARAM)lpiArray);
6660 }
6661 
6662 /***
6663  * DESCRIPTION:
6664  * Retrieves the column width.
6665  *
6666  * PARAMETER(S):
6667  * [I] infoPtr : valid pointer to the listview structure
6668  * [I] int : column index
6669  *
6670  * RETURN:
6671  *   SUCCESS : column width
6672  *   FAILURE : zero
6673  */
LISTVIEW_GetColumnWidth(const LISTVIEW_INFO * infoPtr,INT nColumn)6674 static INT LISTVIEW_GetColumnWidth(const LISTVIEW_INFO *infoPtr, INT nColumn)
6675 {
6676     INT nColumnWidth = 0;
6677     HDITEMW hdItem;
6678 
6679     TRACE("nColumn=%d\n", nColumn);
6680 
6681     /* we have a 'column' in LIST and REPORT mode only */
6682     switch(infoPtr->uView)
6683     {
6684     case LV_VIEW_LIST:
6685 	nColumnWidth = infoPtr->nItemWidth;
6686 	break;
6687     case LV_VIEW_DETAILS:
6688 	/* We are not using LISTVIEW_GetHeaderRect as this data is updated only after a HDN_ITEMCHANGED.
6689 	 * There is an application that subclasses the listview, calls LVM_GETCOLUMNWIDTH in the
6690 	 * HDN_ITEMCHANGED handler and goes into infinite recursion if it receives old data.
6691 	 */
6692 	hdItem.mask = HDI_WIDTH;
6693 	if (!SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, nColumn, (LPARAM)&hdItem))
6694 	{
6695 	    WARN("(%p): HDM_GETITEMW failed for item %d\n", infoPtr->hwndSelf, nColumn);
6696 	    return 0;
6697 	}
6698 	nColumnWidth = hdItem.cxy;
6699 	break;
6700     }
6701 
6702     TRACE("nColumnWidth=%d\n", nColumnWidth);
6703     return nColumnWidth;
6704 }
6705 
6706 /***
6707  * DESCRIPTION:
6708  * In list or report display mode, retrieves the number of items that can fit
6709  * vertically in the visible area. In icon or small icon display mode,
6710  * retrieves the total number of visible items.
6711  *
6712  * PARAMETER(S):
6713  * [I] infoPtr : valid pointer to the listview structure
6714  *
6715  * RETURN:
6716  * Number of fully visible items.
6717  */
LISTVIEW_GetCountPerPage(const LISTVIEW_INFO * infoPtr)6718 static INT LISTVIEW_GetCountPerPage(const LISTVIEW_INFO *infoPtr)
6719 {
6720     switch (infoPtr->uView)
6721     {
6722     case LV_VIEW_ICON:
6723     case LV_VIEW_SMALLICON:
6724 	return infoPtr->nItemCount;
6725     case LV_VIEW_DETAILS:
6726 	return LISTVIEW_GetCountPerColumn(infoPtr);
6727     case LV_VIEW_LIST:
6728 	return LISTVIEW_GetCountPerRow(infoPtr) * LISTVIEW_GetCountPerColumn(infoPtr);
6729     }
6730     assert(FALSE);
6731     return 0;
6732 }
6733 
6734 /***
6735  * DESCRIPTION:
6736  * Retrieves an image list handle.
6737  *
6738  * PARAMETER(S):
6739  * [I] infoPtr : valid pointer to the listview structure
6740  * [I] nImageList : image list identifier
6741  *
6742  * RETURN:
6743  *   SUCCESS : image list handle
6744  *   FAILURE : NULL
6745  */
LISTVIEW_GetImageList(const LISTVIEW_INFO * infoPtr,INT nImageList)6746 static HIMAGELIST LISTVIEW_GetImageList(const LISTVIEW_INFO *infoPtr, INT nImageList)
6747 {
6748     switch (nImageList)
6749     {
6750     case LVSIL_NORMAL: return infoPtr->himlNormal;
6751     case LVSIL_SMALL:  return infoPtr->himlSmall;
6752     case LVSIL_STATE:  return infoPtr->himlState;
6753     case LVSIL_GROUPHEADER:
6754         FIXME("LVSIL_GROUPHEADER not supported\n");
6755         break;
6756     default:
6757         WARN("got unknown imagelist index - %d\n", nImageList);
6758     }
6759     return NULL;
6760 }
6761 
6762 /* LISTVIEW_GetISearchString */
6763 
6764 /***
6765  * DESCRIPTION:
6766  * Retrieves item attributes.
6767  *
6768  * PARAMETER(S):
6769  * [I] hwnd : window handle
6770  * [IO] lpLVItem : item info
6771  * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
6772  *           if FALSE, then lpLVItem is a LPLVITEMA.
6773  *
6774  * NOTE:
6775  *   This is the internal 'GetItem' interface -- it tries to
6776  *   be smart and avoid text copies, if possible, by modifying
6777  *   lpLVItem->pszText to point to the text string. Please note
6778  *   that this is not always possible (e.g. OWNERDATA), so on
6779  *   entry you *must* supply valid values for pszText, and cchTextMax.
6780  *   The only difference to the documented interface is that upon
6781  *   return, you should use *only* the lpLVItem->pszText, rather than
6782  *   the buffer pointer you provided on input. Most code already does
6783  *   that, so it's not a problem.
6784  *   For the two cases when the text must be copied (that is,
6785  *   for LVM_GETITEM, and LVM_GETITEMTEXT), use LISTVIEW_GetItemExtT.
6786  *
6787  * RETURN:
6788  *   SUCCESS : TRUE
6789  *   FAILURE : FALSE
6790  */
LISTVIEW_GetItemT(const LISTVIEW_INFO * infoPtr,LPLVITEMW lpLVItem,BOOL isW)6791 static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
6792 {
6793     ITEMHDR callbackHdr = { LPSTR_TEXTCALLBACKW, I_IMAGECALLBACK };
6794     BOOL is_subitem_invalid = FALSE;
6795     NMLVDISPINFOW dispInfo;
6796     ITEM_INFO *lpItem;
6797     ITEMHDR* pItemHdr;
6798     HDPA hdpaSubItems;
6799 
6800     TRACE("(item=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
6801 
6802     if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
6803 	return FALSE;
6804 
6805     if (lpLVItem->mask == 0) return TRUE;
6806     TRACE("mask=%x\n", lpLVItem->mask);
6807 
6808     if (lpLVItem->iSubItem && (lpLVItem->mask & LVIF_STATE))
6809         lpLVItem->state = 0;
6810 
6811     /* a quick optimization if all we're asked is the focus state
6812      * these queries are worth optimising since they are common,
6813      * and can be answered in constant time, without the heavy accesses */
6814     if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIS_FOCUSED) &&
6815 	 !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
6816     {
6817         lpLVItem->state = 0;
6818         if (infoPtr->nFocusedItem == lpLVItem->iItem && !lpLVItem->iSubItem)
6819             lpLVItem->state |= LVIS_FOCUSED;
6820         return TRUE;
6821     }
6822 
6823     ZeroMemory(&dispInfo, sizeof(dispInfo));
6824 
6825     /* if the app stores all the data, handle it separately */
6826     if (infoPtr->dwStyle & LVS_OWNERDATA)
6827     {
6828 	dispInfo.item.state = 0;
6829 
6830 	/* apparently, we should not callback for lParam in LVS_OWNERDATA */
6831 	if ((lpLVItem->mask & ~(LVIF_STATE | LVIF_PARAM)) ||
6832 	   ((lpLVItem->mask & LVIF_STATE) && (infoPtr->uCallbackMask & lpLVItem->stateMask)))
6833 	{
6834 	    UINT mask = lpLVItem->mask;
6835 
6836 	    /* NOTE: copy only fields which we _know_ are initialized, some apps
6837 	     *       depend on the uninitialized fields being 0 */
6838 	    dispInfo.item.mask = lpLVItem->mask & ~LVIF_PARAM;
6839 	    dispInfo.item.iItem = lpLVItem->iItem;
6840 	    dispInfo.item.iSubItem = lpLVItem->iSubItem;
6841 	    if (lpLVItem->mask & LVIF_TEXT)
6842 	    {
6843 		if (lpLVItem->mask & LVIF_NORECOMPUTE)
6844 		    /* reset mask */
6845 		    dispInfo.item.mask &= ~(LVIF_TEXT | LVIF_NORECOMPUTE);
6846 		else
6847 		{
6848 		    dispInfo.item.pszText = lpLVItem->pszText;
6849 		    dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
6850 		}
6851 	    }
6852 	    if (lpLVItem->mask & LVIF_STATE)
6853 	        dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
6854 	    /* could be zeroed on LVIF_NORECOMPUTE case */
6855 	    if (dispInfo.item.mask)
6856 	    {
6857 	        notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
6858 	        dispInfo.item.stateMask = lpLVItem->stateMask;
6859 	        if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
6860 	        {
6861 	            /* full size structure expected - _WIN32IE >= 0x560 */
6862 	            *lpLVItem = dispInfo.item;
6863 	        }
6864 	        else if (lpLVItem->mask & LVIF_INDENT)
6865 	        {
6866 	            /* indent member expected - _WIN32IE >= 0x300 */
6867 	            memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iGroupId ));
6868 	        }
6869 	        else
6870 	        {
6871 	            /* minimal structure expected */
6872 	            memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iIndent ));
6873 	        }
6874 	        lpLVItem->mask = mask;
6875 	        TRACE("   getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
6876 	    }
6877 	}
6878 
6879 	/* make sure lParam is zeroed out */
6880 	if (lpLVItem->mask & LVIF_PARAM) lpLVItem->lParam = 0;
6881 
6882 	/* callback marked pointer required here */
6883 	if ((lpLVItem->mask & LVIF_TEXT) && (lpLVItem->mask & LVIF_NORECOMPUTE))
6884 	    lpLVItem->pszText = LPSTR_TEXTCALLBACKW;
6885 
6886 	/* we store only a little state, so if we're not asked, we're done */
6887 	if (!(lpLVItem->mask & LVIF_STATE) || lpLVItem->iSubItem) return TRUE;
6888 
6889 	/* if focus is handled by us, report it */
6890 	if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
6891 	{
6892 	    lpLVItem->state &= ~LVIS_FOCUSED;
6893 	    if (infoPtr->nFocusedItem == lpLVItem->iItem)
6894 	        lpLVItem->state |= LVIS_FOCUSED;
6895         }
6896 
6897 	/* and do the same for selection, if we handle it */
6898 	if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
6899 	{
6900 	    lpLVItem->state &= ~LVIS_SELECTED;
6901 	    if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
6902 		lpLVItem->state |= LVIS_SELECTED;
6903 	}
6904 
6905 	return TRUE;
6906     }
6907 
6908     /* find the item and subitem structures before we proceed */
6909     hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
6910     lpItem = DPA_GetPtr(hdpaSubItems, 0);
6911     assert (lpItem);
6912 
6913     if (lpLVItem->iSubItem)
6914     {
6915         SUBITEM_INFO *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
6916         if (lpSubItem)
6917             pItemHdr = &lpSubItem->hdr;
6918         else
6919         {
6920             pItemHdr = &callbackHdr;
6921             is_subitem_invalid = TRUE;
6922         }
6923     }
6924     else
6925 	pItemHdr = &lpItem->hdr;
6926 
6927     /* Do we need to query the state from the app? */
6928     if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && (!lpLVItem->iSubItem || is_subitem_invalid))
6929     {
6930 	dispInfo.item.mask |= LVIF_STATE;
6931 	dispInfo.item.stateMask = infoPtr->uCallbackMask;
6932     }
6933 
6934     /* Do we need to enquire about the image? */
6935     if ((lpLVItem->mask & LVIF_IMAGE) && pItemHdr->iImage == I_IMAGECALLBACK &&
6936         (!lpLVItem->iSubItem || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES)))
6937     {
6938 	dispInfo.item.mask |= LVIF_IMAGE;
6939         dispInfo.item.iImage = I_IMAGECALLBACK;
6940     }
6941 
6942     /* Only items support indentation */
6943     if ((lpLVItem->mask & LVIF_INDENT) && lpItem->iIndent == I_INDENTCALLBACK && !lpLVItem->iSubItem)
6944     {
6945         dispInfo.item.mask |= LVIF_INDENT;
6946         dispInfo.item.iIndent = I_INDENTCALLBACK;
6947     }
6948 
6949     /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
6950     if ((lpLVItem->mask & LVIF_TEXT) && !(lpLVItem->mask & LVIF_NORECOMPUTE) &&
6951         !is_text(pItemHdr->pszText))
6952     {
6953 	dispInfo.item.mask |= LVIF_TEXT;
6954 	dispInfo.item.pszText = lpLVItem->pszText;
6955 	dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
6956 	if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
6957 	    *dispInfo.item.pszText = '\0';
6958     }
6959 
6960     /* If we don't have all the requested info, query the application */
6961     if (dispInfo.item.mask)
6962     {
6963 	dispInfo.item.iItem = lpLVItem->iItem;
6964 	dispInfo.item.iSubItem = lpLVItem->iSubItem;
6965 	dispInfo.item.lParam = lpItem->lParam;
6966 	notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
6967 	TRACE("   getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
6968     }
6969 
6970     /* we should not store values for subitems */
6971     if (lpLVItem->iSubItem) dispInfo.item.mask &= ~LVIF_DI_SETITEM;
6972 
6973     /* Now, handle the iImage field */
6974     if (dispInfo.item.mask & LVIF_IMAGE)
6975     {
6976 	lpLVItem->iImage = dispInfo.item.iImage;
6977 	if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->iImage == I_IMAGECALLBACK)
6978 	    pItemHdr->iImage = dispInfo.item.iImage;
6979     }
6980     else if (lpLVItem->mask & LVIF_IMAGE)
6981     {
6982         if (!lpLVItem->iSubItem || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES))
6983             lpLVItem->iImage = pItemHdr->iImage;
6984         else
6985             lpLVItem->iImage = 0;
6986     }
6987 
6988     /* The pszText field */
6989     if (dispInfo.item.mask & LVIF_TEXT)
6990     {
6991 	if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
6992 	    textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
6993 
6994 	lpLVItem->pszText = dispInfo.item.pszText;
6995     }
6996     else if (lpLVItem->mask & LVIF_TEXT)
6997     {
6998 	/* if LVN_GETDISPINFO's disabled with LVIF_NORECOMPUTE return callback placeholder */
6999 	if (isW || !is_text(pItemHdr->pszText)) lpLVItem->pszText = pItemHdr->pszText;
7000 	else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
7001     }
7002 
7003     /* Next is the lParam field */
7004     if (dispInfo.item.mask & LVIF_PARAM)
7005     {
7006 	lpLVItem->lParam = dispInfo.item.lParam;
7007 	if ((dispInfo.item.mask & LVIF_DI_SETITEM))
7008 	    lpItem->lParam = dispInfo.item.lParam;
7009     }
7010     else if (lpLVItem->mask & LVIF_PARAM)
7011 	lpLVItem->lParam = lpItem->lParam;
7012 
7013     /* if this is a subitem, we're done */
7014     if (lpLVItem->iSubItem) return TRUE;
7015 
7016     /* ... the state field (this one is different due to uCallbackmask) */
7017     if (lpLVItem->mask & LVIF_STATE)
7018     {
7019 	lpLVItem->state = lpItem->state & lpLVItem->stateMask;
7020 	if (dispInfo.item.mask & LVIF_STATE)
7021 	{
7022 	    lpLVItem->state &= ~dispInfo.item.stateMask;
7023 	    lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
7024 	}
7025 	if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
7026 	{
7027 	    lpLVItem->state &= ~LVIS_FOCUSED;
7028 	    if (infoPtr->nFocusedItem == lpLVItem->iItem)
7029 	        lpLVItem->state |= LVIS_FOCUSED;
7030         }
7031 	if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
7032 	{
7033 	    lpLVItem->state &= ~LVIS_SELECTED;
7034 	    if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
7035 		lpLVItem->state |= LVIS_SELECTED;
7036 	}
7037     }
7038 
7039     /* and last, but not least, the indent field */
7040     if (dispInfo.item.mask & LVIF_INDENT)
7041     {
7042 	lpLVItem->iIndent = dispInfo.item.iIndent;
7043 	if ((dispInfo.item.mask & LVIF_DI_SETITEM) && lpItem->iIndent == I_INDENTCALLBACK)
7044 	    lpItem->iIndent = dispInfo.item.iIndent;
7045     }
7046     else if (lpLVItem->mask & LVIF_INDENT)
7047     {
7048         lpLVItem->iIndent = lpItem->iIndent;
7049     }
7050 
7051     return TRUE;
7052 }
7053 
7054 /***
7055  * DESCRIPTION:
7056  * Retrieves item attributes.
7057  *
7058  * PARAMETER(S):
7059  * [I] hwnd : window handle
7060  * [IO] lpLVItem : item info
7061  * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
7062  *           if FALSE, then lpLVItem is a LPLVITEMA.
7063  *
7064  * NOTE:
7065  *   This is the external 'GetItem' interface -- it properly copies
7066  *   the text in the provided buffer.
7067  *
7068  * RETURN:
7069  *   SUCCESS : TRUE
7070  *   FAILURE : FALSE
7071  */
LISTVIEW_GetItemExtT(const LISTVIEW_INFO * infoPtr,LPLVITEMW lpLVItem,BOOL isW)7072 static BOOL LISTVIEW_GetItemExtT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
7073 {
7074     LPWSTR pszText;
7075     BOOL bResult;
7076 
7077     if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
7078 	return FALSE;
7079 
7080     pszText = lpLVItem->pszText;
7081     bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
7082     if (bResult && (lpLVItem->mask & LVIF_TEXT) && lpLVItem->pszText != pszText)
7083     {
7084 	if (lpLVItem->pszText != LPSTR_TEXTCALLBACKW)
7085 	    textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
7086 	else
7087 	    pszText = LPSTR_TEXTCALLBACKW;
7088     }
7089     lpLVItem->pszText = pszText;
7090 
7091     return bResult;
7092 }
7093 
7094 
7095 /***
7096  * DESCRIPTION:
7097  * Retrieves the position (upper-left) of the listview control item.
7098  * Note that for LVS_ICON style, the upper-left is that of the icon
7099  * and not the bounding box.
7100  *
7101  * PARAMETER(S):
7102  * [I] infoPtr : valid pointer to the listview structure
7103  * [I] nItem : item index
7104  * [O] lpptPosition : coordinate information
7105  *
7106  * RETURN:
7107  *   SUCCESS : TRUE
7108  *   FAILURE : FALSE
7109  */
LISTVIEW_GetItemPosition(const LISTVIEW_INFO * infoPtr,INT nItem,LPPOINT lpptPosition)7110 static BOOL LISTVIEW_GetItemPosition(const LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
7111 {
7112     POINT Origin;
7113 
7114     TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
7115 
7116     if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
7117 
7118     LISTVIEW_GetOrigin(infoPtr, &Origin);
7119     LISTVIEW_GetItemOrigin(infoPtr, nItem, lpptPosition);
7120 
7121     if (infoPtr->uView == LV_VIEW_ICON)
7122     {
7123         lpptPosition->x += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
7124         lpptPosition->y += ICON_TOP_PADDING;
7125     }
7126     lpptPosition->x += Origin.x;
7127     lpptPosition->y += Origin.y;
7128 
7129     TRACE ("  lpptPosition=%s\n", wine_dbgstr_point(lpptPosition));
7130     return TRUE;
7131 }
7132 
7133 
7134 /***
7135  * DESCRIPTION:
7136  * Retrieves the bounding rectangle for a listview control item.
7137  *
7138  * PARAMETER(S):
7139  * [I] infoPtr : valid pointer to the listview structure
7140  * [I] nItem : item index
7141  * [IO] lprc : bounding rectangle coordinates
7142  *     lprc->left specifies the portion of the item for which the bounding
7143  *     rectangle will be retrieved.
7144  *
7145  *     LVIR_BOUNDS Returns the bounding rectangle of the entire item,
7146  *        including the icon and label.
7147  *         *
7148  *         * For LVS_ICON
7149  *         * Experiment shows that native control returns:
7150  *         *  width = min (48, length of text line)
7151  *         *    .left = position.x - (width - iconsize.cx)/2
7152  *         *    .right = .left + width
7153  *         *  height = #lines of text * ntmHeight + icon height + 8
7154  *         *    .top = position.y - 2
7155  *         *    .bottom = .top + height
7156  *         *  separation between items .y = itemSpacing.cy - height
7157  *         *                           .x = itemSpacing.cx - width
7158  *     LVIR_ICON Returns the bounding rectangle of the icon or small icon.
7159  *         *
7160  *         * For LVS_ICON
7161  *         * Experiment shows that native control returns:
7162  *         *  width = iconSize.cx + 16
7163  *         *    .left = position.x - (width - iconsize.cx)/2
7164  *         *    .right = .left + width
7165  *         *  height = iconSize.cy + 4
7166  *         *    .top = position.y - 2
7167  *         *    .bottom = .top + height
7168  *         *  separation between items .y = itemSpacing.cy - height
7169  *         *                           .x = itemSpacing.cx - width
7170  *     LVIR_LABEL Returns the bounding rectangle of the item text.
7171  *         *
7172  *         * For LVS_ICON
7173  *         * Experiment shows that native control returns:
7174  *         *  width = text length
7175  *         *    .left = position.x - width/2
7176  *         *    .right = .left + width
7177  *         *  height = ntmH * linecount + 2
7178  *         *    .top = position.y + iconSize.cy + 6
7179  *         *    .bottom = .top + height
7180  *         *  separation between items .y = itemSpacing.cy - height
7181  *         *                           .x = itemSpacing.cx - width
7182  *     LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
7183  *	rectangles, but excludes columns in report view.
7184  *
7185  * RETURN:
7186  *   SUCCESS : TRUE
7187  *   FAILURE : FALSE
7188  *
7189  * NOTES
7190  *   Note that the bounding rectangle of the label in the LVS_ICON view depends
7191  *   upon whether the window has the focus currently and on whether the item
7192  *   is the one with the focus.  Ensure that the control's record of which
7193  *   item has the focus agrees with the items' records.
7194  */
LISTVIEW_GetItemRect(const LISTVIEW_INFO * infoPtr,INT nItem,LPRECT lprc)7195 static BOOL LISTVIEW_GetItemRect(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
7196 {
7197     WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
7198     BOOL doLabel = TRUE, oversizedBox = FALSE;
7199     POINT Position, Origin;
7200     LVITEMW lvItem;
7201     LONG mode;
7202 
7203     TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
7204 
7205     if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
7206 
7207     LISTVIEW_GetOrigin(infoPtr, &Origin);
7208     LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
7209 
7210     /* Be smart and try to figure out the minimum we have to do */
7211     if (lprc->left == LVIR_ICON) doLabel = FALSE;
7212     if (infoPtr->uView == LV_VIEW_DETAILS && lprc->left == LVIR_BOUNDS) doLabel = FALSE;
7213     if (infoPtr->uView == LV_VIEW_ICON && lprc->left != LVIR_ICON &&
7214 	infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
7215 	oversizedBox = TRUE;
7216 
7217     /* get what we need from the item before hand, so we make
7218      * only one request. This can speed up things, if data
7219      * is stored on the app side */
7220     lvItem.mask = 0;
7221     if (infoPtr->uView == LV_VIEW_DETAILS) lvItem.mask |= LVIF_INDENT;
7222     if (doLabel) lvItem.mask |= LVIF_TEXT;
7223     lvItem.iItem = nItem;
7224     lvItem.iSubItem = 0;
7225     lvItem.pszText = szDispText;
7226     lvItem.cchTextMax = DISP_TEXT_SIZE;
7227     if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
7228     /* we got the state already up, simulate it here, to avoid a reget */
7229     if (infoPtr->uView == LV_VIEW_ICON && (lprc->left != LVIR_ICON))
7230     {
7231 	lvItem.mask |= LVIF_STATE;
7232 	lvItem.stateMask = LVIS_FOCUSED;
7233 	lvItem.state = (oversizedBox ? LVIS_FOCUSED : 0);
7234     }
7235 
7236     if (infoPtr->uView == LV_VIEW_DETAILS && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && lprc->left == LVIR_SELECTBOUNDS)
7237 	lprc->left = LVIR_BOUNDS;
7238 
7239     mode = lprc->left;
7240     switch(lprc->left)
7241     {
7242     case LVIR_ICON:
7243 	LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL, NULL);
7244         break;
7245 
7246     case LVIR_LABEL:
7247 	LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, NULL, NULL, lprc);
7248         break;
7249 
7250     case LVIR_BOUNDS:
7251 	LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL, NULL);
7252         break;
7253 
7254     case LVIR_SELECTBOUNDS:
7255 	LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, lprc, NULL, NULL, NULL);
7256         break;
7257 
7258     default:
7259 	WARN("Unknown value: %d\n", lprc->left);
7260 	return FALSE;
7261     }
7262 
7263     if (infoPtr->uView == LV_VIEW_DETAILS)
7264     {
7265 	if (mode != LVIR_BOUNDS)
7266 	    OffsetRect(lprc, Origin.x + LISTVIEW_GetColumnInfo(infoPtr, 0)->rcHeader.left,
7267 	                     Position.y + Origin.y);
7268 	else
7269 	    OffsetRect(lprc, Origin.x, Position.y + Origin.y);
7270     }
7271     else
7272         OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
7273 
7274     TRACE(" rect=%s\n", wine_dbgstr_rect(lprc));
7275 
7276     return TRUE;
7277 }
7278 
7279 /***
7280  * DESCRIPTION:
7281  * Retrieves the spacing between listview control items.
7282  *
7283  * PARAMETER(S):
7284  * [I] infoPtr : valid pointer to the listview structure
7285  * [IO] lprc : rectangle to receive the output
7286  *             on input, lprc->top = nSubItem
7287  *                       lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
7288  *
7289  * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
7290  *       not only those of the first column.
7291  *
7292  * RETURN:
7293  *     TRUE: success
7294  *     FALSE: failure
7295  */
LISTVIEW_GetSubItemRect(const LISTVIEW_INFO * infoPtr,INT item,LPRECT lprc)7296 static BOOL LISTVIEW_GetSubItemRect(const LISTVIEW_INFO *infoPtr, INT item, LPRECT lprc)
7297 {
7298     RECT rect = { 0, 0, 0, 0 };
7299     POINT origin;
7300     INT y;
7301 
7302     if (!lprc) return FALSE;
7303 
7304     TRACE("(item=%d, subitem=%d, type=%d)\n", item, lprc->top, lprc->left);
7305     /* Subitem of '0' means item itself, and this works for all control view modes */
7306     if (lprc->top == 0)
7307         return LISTVIEW_GetItemRect(infoPtr, item, lprc);
7308 
7309     if (infoPtr->uView != LV_VIEW_DETAILS) return FALSE;
7310 
7311     LISTVIEW_GetOrigin(infoPtr, &origin);
7312     /* this works for any item index, no matter if it exists or not */
7313     y = item * infoPtr->nItemHeight + origin.y;
7314 
7315     if (infoPtr->hwndHeader && SendMessageW(infoPtr->hwndHeader, HDM_GETITEMRECT, lprc->top, (LPARAM)&rect))
7316     {
7317         rect.top = 0;
7318         rect.bottom = infoPtr->nItemHeight;
7319     }
7320     else
7321     {
7322         /* Native implementation is broken for this case and garbage is left for left and right fields,
7323            we zero them to get predictable output */
7324         lprc->left = lprc->right = lprc->top = 0;
7325         lprc->bottom = infoPtr->nItemHeight;
7326         OffsetRect(lprc, origin.x, y);
7327         TRACE("return rect %s\n", wine_dbgstr_rect(lprc));
7328         return TRUE;
7329     }
7330 
7331     switch (lprc->left)
7332     {
7333     case LVIR_ICON:
7334     {
7335         /* it doesn't matter if main item actually has an icon, if imagelist is set icon width is returned */
7336         if (infoPtr->himlSmall)
7337             rect.right = rect.left + infoPtr->iconSize.cx;
7338         else
7339             rect.right = rect.left;
7340 
7341         rect.bottom = rect.top + infoPtr->iconSize.cy;
7342         break;
7343     }
7344     case LVIR_LABEL:
7345     case LVIR_BOUNDS:
7346         break;
7347 
7348     default:
7349 	ERR("Unknown bounds=%d\n", lprc->left);
7350 	return FALSE;
7351     }
7352 
7353     OffsetRect(&rect, origin.x, y);
7354     *lprc = rect;
7355     TRACE("return rect %s\n", wine_dbgstr_rect(lprc));
7356 
7357     return TRUE;
7358 }
7359 
7360 /***
7361  * DESCRIPTION:
7362  * Retrieves the spacing between listview control items.
7363  *
7364  * PARAMETER(S):
7365  * [I] infoPtr : valid pointer to the listview structure
7366  * [I] bSmall : flag for small or large icon
7367  *
7368  * RETURN:
7369  * Horizontal + vertical spacing
7370  */
LISTVIEW_GetItemSpacing(const LISTVIEW_INFO * infoPtr,BOOL bSmall)7371 static LONG LISTVIEW_GetItemSpacing(const LISTVIEW_INFO *infoPtr, BOOL bSmall)
7372 {
7373   LONG lResult;
7374 
7375   if (!bSmall)
7376   {
7377     lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
7378   }
7379   else
7380   {
7381     if (infoPtr->uView == LV_VIEW_ICON)
7382       lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
7383     else
7384       lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
7385   }
7386   return lResult;
7387 }
7388 
7389 /***
7390  * DESCRIPTION:
7391  * Retrieves the state of a listview control item.
7392  *
7393  * PARAMETER(S):
7394  * [I] infoPtr : valid pointer to the listview structure
7395  * [I] nItem : item index
7396  * [I] uMask : state mask
7397  *
7398  * RETURN:
7399  * State specified by the mask.
7400  */
LISTVIEW_GetItemState(const LISTVIEW_INFO * infoPtr,INT nItem,UINT uMask)7401 static UINT LISTVIEW_GetItemState(const LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
7402 {
7403     LVITEMW lvItem;
7404 
7405     if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
7406 
7407     lvItem.iItem = nItem;
7408     lvItem.iSubItem = 0;
7409     lvItem.mask = LVIF_STATE;
7410     lvItem.stateMask = uMask;
7411     if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
7412 
7413     return lvItem.state & uMask;
7414 }
7415 
7416 /***
7417  * DESCRIPTION:
7418  * Retrieves the text of a listview control item or subitem.
7419  *
7420  * PARAMETER(S):
7421  * [I] hwnd : window handle
7422  * [I] nItem : item index
7423  * [IO] lpLVItem : item information
7424  * [I] isW :  TRUE if lpLVItem is Unicode
7425  *
7426  * RETURN:
7427  *   SUCCESS : string length
7428  *   FAILURE : 0
7429  */
LISTVIEW_GetItemTextT(const LISTVIEW_INFO * infoPtr,INT nItem,LPLVITEMW lpLVItem,BOOL isW)7430 static INT LISTVIEW_GetItemTextT(const LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
7431 {
7432     if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
7433 
7434     lpLVItem->mask = LVIF_TEXT;
7435     lpLVItem->iItem = nItem;
7436     if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
7437 
7438     return textlenT(lpLVItem->pszText, isW);
7439 }
7440 
7441 /***
7442  * DESCRIPTION:
7443  * Searches for an item based on properties + relationships.
7444  *
7445  * PARAMETER(S):
7446  * [I] infoPtr : valid pointer to the listview structure
7447  * [I] nItem : item index
7448  * [I] uFlags : relationship flag
7449  *
7450  * RETURN:
7451  *   SUCCESS : item index
7452  *   FAILURE : -1
7453  */
LISTVIEW_GetNextItem(const LISTVIEW_INFO * infoPtr,INT nItem,UINT uFlags)7454 static INT LISTVIEW_GetNextItem(const LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
7455 {
7456     UINT uMask = 0;
7457     LVFINDINFOW lvFindInfo;
7458     INT nCountPerColumn;
7459 #ifndef __REACTOS__
7460     INT nCountPerRow;
7461 #endif
7462     INT i;
7463 
7464     TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
7465     if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
7466 
7467     ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
7468 
7469     if (uFlags & LVNI_CUT)
7470       uMask |= LVIS_CUT;
7471 
7472     if (uFlags & LVNI_DROPHILITED)
7473       uMask |= LVIS_DROPHILITED;
7474 
7475     if (uFlags & LVNI_FOCUSED)
7476       uMask |= LVIS_FOCUSED;
7477 
7478     if (uFlags & LVNI_SELECTED)
7479       uMask |= LVIS_SELECTED;
7480 
7481     /* if we're asked for the focused item, that's only one,
7482      * so it's worth optimizing */
7483     if (uFlags & LVNI_FOCUSED)
7484     {
7485 	if ((LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) != uMask) return -1;
7486 	return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
7487     }
7488 
7489     if (uFlags & LVNI_ABOVE)
7490     {
7491       if ((infoPtr->uView == LV_VIEW_LIST) || (infoPtr->uView == LV_VIEW_DETAILS))
7492       {
7493         while (nItem >= 0)
7494         {
7495           nItem--;
7496           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7497             return nItem;
7498         }
7499       }
7500       else
7501       {
7502 #ifndef __REACTOS__
7503         /* Special case for autoarrange - move 'til the top of a list */
7504         if (is_autoarrange(infoPtr))
7505         {
7506           nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
7507           while (nItem - nCountPerRow >= 0)
7508           {
7509             nItem -= nCountPerRow;
7510             if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7511               return nItem;
7512           }
7513           return -1;
7514         }
7515 #endif
7516         lvFindInfo.flags = LVFI_NEARESTXY;
7517         lvFindInfo.vkDirection = VK_UP;
7518         LISTVIEW_GetItemPosition(infoPtr, nItem, &lvFindInfo.pt);
7519         while ((nItem = LISTVIEW_FindItemW(infoPtr, nItem, &lvFindInfo)) != -1)
7520         {
7521           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7522             return nItem;
7523         }
7524       }
7525     }
7526     else if (uFlags & LVNI_BELOW)
7527     {
7528       if ((infoPtr->uView == LV_VIEW_LIST) || (infoPtr->uView == LV_VIEW_DETAILS))
7529       {
7530         while (nItem < infoPtr->nItemCount)
7531         {
7532           nItem++;
7533           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7534             return nItem;
7535         }
7536       }
7537       else
7538       {
7539 #ifndef __REACTOS__
7540         /* Special case for autoarrange - move 'til the bottom of a list */
7541         if (is_autoarrange(infoPtr))
7542         {
7543           nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
7544           while (nItem + nCountPerRow < infoPtr->nItemCount )
7545           {
7546             nItem += nCountPerRow;
7547             if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7548               return nItem;
7549           }
7550           return -1;
7551         }
7552 #endif
7553         lvFindInfo.flags = LVFI_NEARESTXY;
7554         lvFindInfo.vkDirection = VK_DOWN;
7555         LISTVIEW_GetItemPosition(infoPtr, nItem, &lvFindInfo.pt);
7556         while ((nItem = LISTVIEW_FindItemW(infoPtr, nItem, &lvFindInfo)) != -1)
7557         {
7558           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7559             return nItem;
7560         }
7561       }
7562     }
7563     else if (uFlags & LVNI_TOLEFT)
7564     {
7565       if (infoPtr->uView == LV_VIEW_LIST)
7566       {
7567         nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
7568         while (nItem - nCountPerColumn >= 0)
7569         {
7570           nItem -= nCountPerColumn;
7571           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7572             return nItem;
7573         }
7574       }
7575       else if ((infoPtr->uView == LV_VIEW_SMALLICON) || (infoPtr->uView == LV_VIEW_ICON))
7576       {
7577 #ifndef __REACTOS__
7578         /* Special case for autoarrange - move 'til the beginning of a row */
7579         if (is_autoarrange(infoPtr))
7580         {
7581           nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
7582           while (nItem % nCountPerRow > 0)
7583           {
7584             nItem --;
7585             if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7586               return nItem;
7587           }
7588           return -1;
7589         }
7590 #endif
7591         lvFindInfo.flags = LVFI_NEARESTXY;
7592         lvFindInfo.vkDirection = VK_LEFT;
7593         LISTVIEW_GetItemPosition(infoPtr, nItem, &lvFindInfo.pt);
7594         while ((nItem = LISTVIEW_FindItemW(infoPtr, nItem, &lvFindInfo)) != -1)
7595         {
7596           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7597             return nItem;
7598         }
7599       }
7600     }
7601     else if (uFlags & LVNI_TORIGHT)
7602     {
7603       if (infoPtr->uView == LV_VIEW_LIST)
7604       {
7605         nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
7606         while (nItem + nCountPerColumn < infoPtr->nItemCount)
7607         {
7608           nItem += nCountPerColumn;
7609           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7610             return nItem;
7611         }
7612       }
7613       else if ((infoPtr->uView == LV_VIEW_SMALLICON) || (infoPtr->uView == LV_VIEW_ICON))
7614       {
7615 #ifndef __REACTOS__
7616         /* Special case for autoarrange - move 'til the end of a row */
7617         if (is_autoarrange(infoPtr))
7618         {
7619           nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
7620           while (nItem % nCountPerRow < nCountPerRow - 1 )
7621           {
7622             nItem ++;
7623             if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7624               return nItem;
7625           }
7626           return -1;
7627         }
7628 #endif
7629         lvFindInfo.flags = LVFI_NEARESTXY;
7630         lvFindInfo.vkDirection = VK_RIGHT;
7631         LISTVIEW_GetItemPosition(infoPtr, nItem, &lvFindInfo.pt);
7632         while ((nItem = LISTVIEW_FindItemW(infoPtr, nItem, &lvFindInfo)) != -1)
7633         {
7634           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7635             return nItem;
7636         }
7637       }
7638     }
7639     else
7640     {
7641       nItem++;
7642 
7643       /* search by index */
7644       for (i = nItem; i < infoPtr->nItemCount; i++)
7645       {
7646         if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
7647           return i;
7648       }
7649     }
7650 
7651     return -1;
7652 }
7653 
7654 /* LISTVIEW_GetNumberOfWorkAreas */
7655 
7656 /***
7657  * DESCRIPTION:
7658  * Retrieves the origin coordinates when in icon or small icon display mode.
7659  *
7660  * PARAMETER(S):
7661  * [I] infoPtr : valid pointer to the listview structure
7662  * [O] lpptOrigin : coordinate information
7663  *
7664  * RETURN:
7665  *   None.
7666  */
LISTVIEW_GetOrigin(const LISTVIEW_INFO * infoPtr,LPPOINT lpptOrigin)7667 static void LISTVIEW_GetOrigin(const LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
7668 {
7669     INT nHorzPos = 0, nVertPos = 0;
7670     SCROLLINFO scrollInfo;
7671 
7672     scrollInfo.cbSize = sizeof(SCROLLINFO);
7673     scrollInfo.fMask = SIF_POS;
7674 
7675     if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
7676 	nHorzPos = scrollInfo.nPos;
7677     if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
7678 	nVertPos = scrollInfo.nPos;
7679 
7680     TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
7681 
7682     lpptOrigin->x = infoPtr->rcList.left;
7683     lpptOrigin->y = infoPtr->rcList.top;
7684     if (infoPtr->uView == LV_VIEW_LIST)
7685 	nHorzPos *= infoPtr->nItemWidth;
7686     else if (infoPtr->uView == LV_VIEW_DETAILS)
7687 	nVertPos *= infoPtr->nItemHeight;
7688 
7689     lpptOrigin->x -= nHorzPos;
7690     lpptOrigin->y -= nVertPos;
7691 
7692     TRACE(" origin=%s\n", wine_dbgstr_point(lpptOrigin));
7693 }
7694 
7695 /***
7696  * DESCRIPTION:
7697  * Retrieves the width of a string.
7698  *
7699  * PARAMETER(S):
7700  * [I] hwnd : window handle
7701  * [I] lpszText : text string to process
7702  * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
7703  *
7704  * RETURN:
7705  *   SUCCESS : string width (in pixels)
7706  *   FAILURE : zero
7707  */
LISTVIEW_GetStringWidthT(const LISTVIEW_INFO * infoPtr,LPCWSTR lpszText,BOOL isW)7708 static INT LISTVIEW_GetStringWidthT(const LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
7709 {
7710     SIZE stringSize;
7711 
7712     stringSize.cx = 0;
7713     if (is_text(lpszText))
7714     {
7715     	HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
7716     	HDC hdc = GetDC(infoPtr->hwndSelf);
7717     	HFONT hOldFont = SelectObject(hdc, hFont);
7718 
7719     	if (isW)
7720   	    GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
7721     	else
7722   	    GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
7723     	SelectObject(hdc, hOldFont);
7724     	ReleaseDC(infoPtr->hwndSelf, hdc);
7725     }
7726     return stringSize.cx;
7727 }
7728 
7729 /***
7730  * DESCRIPTION:
7731  * Determines which listview item is located at the specified position.
7732  *
7733  * PARAMETER(S):
7734  * [I] infoPtr : valid pointer to the listview structure
7735  * [IO] lpht : hit test information
7736  * [I] subitem : fill out iSubItem.
7737  * [I] select : return the index only if the hit selects the item
7738  *
7739  * NOTE:
7740  * (mm 20001022): We must not allow iSubItem to be touched, for
7741  * an app might pass only a structure with space up to iItem!
7742  * (MS Office 97 does that for instance in the file open dialog)
7743  *
7744  * RETURN:
7745  *   SUCCESS : item index
7746  *   FAILURE : -1
7747  */
LISTVIEW_HitTest(const LISTVIEW_INFO * infoPtr,LPLVHITTESTINFO lpht,BOOL subitem,BOOL select)7748 static INT LISTVIEW_HitTest(const LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select)
7749 {
7750     WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
7751     RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch;
7752     POINT Origin, Position, opt;
7753     BOOL is_fullrow;
7754     LVITEMW lvItem;
7755     ITERATOR i;
7756     INT iItem;
7757 
7758     TRACE("(pt=%s, subitem=%d, select=%d)\n", wine_dbgstr_point(&lpht->pt), subitem, select);
7759 
7760     lpht->flags = 0;
7761     lpht->iItem = -1;
7762     if (subitem) lpht->iSubItem = 0;
7763 
7764     LISTVIEW_GetOrigin(infoPtr, &Origin);
7765 
7766     /* set whole list relation flags */
7767     if (subitem && infoPtr->uView == LV_VIEW_DETAILS)
7768     {
7769         /* LVM_SUBITEMHITTEST checks left bound of possible client area */
7770         if (infoPtr->rcList.left > lpht->pt.x && Origin.x < lpht->pt.x)
7771 	    lpht->flags |= LVHT_TOLEFT;
7772 
7773 	if (lpht->pt.y < infoPtr->rcList.top && lpht->pt.y >= 0)
7774 	    opt.y = lpht->pt.y + infoPtr->rcList.top;
7775 	else
7776 	    opt.y = lpht->pt.y;
7777 
7778 	if (infoPtr->rcList.bottom < opt.y)
7779 	    lpht->flags |= LVHT_BELOW;
7780     }
7781     else
7782     {
7783 	if (infoPtr->rcList.left > lpht->pt.x)
7784 	    lpht->flags |= LVHT_TOLEFT;
7785 	else if (infoPtr->rcList.right < lpht->pt.x)
7786 	    lpht->flags |= LVHT_TORIGHT;
7787 
7788 	if (infoPtr->rcList.top > lpht->pt.y)
7789 	    lpht->flags |= LVHT_ABOVE;
7790 	else if (infoPtr->rcList.bottom < lpht->pt.y)
7791 	    lpht->flags |= LVHT_BELOW;
7792     }
7793 
7794     /* even if item is invalid try to find subitem */
7795     if (infoPtr->uView == LV_VIEW_DETAILS && subitem)
7796     {
7797 	RECT *pRect;
7798 	INT j;
7799 
7800 	opt.x = lpht->pt.x - Origin.x;
7801 
7802 	lpht->iSubItem = -1;
7803 	for (j = 0; j < DPA_GetPtrCount(infoPtr->hdpaColumns); j++)
7804 	{
7805 	    pRect = &LISTVIEW_GetColumnInfo(infoPtr, j)->rcHeader;
7806 
7807 	    if ((opt.x >= pRect->left) && (opt.x < pRect->right))
7808 	    {
7809 		lpht->iSubItem = j;
7810 		break;
7811 	    }
7812 	}
7813 	TRACE("lpht->iSubItem=%d\n", lpht->iSubItem);
7814 
7815 	/* if we're outside horizontal columns bounds there's nothing to test further */
7816 	if (lpht->iSubItem == -1)
7817 	{
7818 	    lpht->iItem = -1;
7819 	    lpht->flags = LVHT_NOWHERE;
7820 	    return -1;
7821 	}
7822     }
7823 
7824     TRACE("lpht->flags=0x%x\n", lpht->flags);
7825     if (lpht->flags) return -1;
7826 
7827     lpht->flags |= LVHT_NOWHERE;
7828 
7829     /* first deal with the large items */
7830     rcSearch.left = lpht->pt.x;
7831     rcSearch.top = lpht->pt.y;
7832     rcSearch.right = rcSearch.left + 1;
7833     rcSearch.bottom = rcSearch.top + 1;
7834 
7835     iterator_frameditems(&i, infoPtr, &rcSearch);
7836     iterator_next(&i); /* go to first item in the sequence */
7837     iItem = i.nItem;
7838     iterator_destroy(&i);
7839 
7840     TRACE("lpht->iItem=%d\n", iItem);
7841     if (iItem == -1) return -1;
7842 
7843     lvItem.mask = LVIF_STATE | LVIF_TEXT;
7844     if (infoPtr->uView == LV_VIEW_DETAILS) lvItem.mask |= LVIF_INDENT;
7845     lvItem.stateMask = LVIS_STATEIMAGEMASK;
7846     if (infoPtr->uView == LV_VIEW_ICON) lvItem.stateMask |= LVIS_FOCUSED;
7847     lvItem.iItem = iItem;
7848     lvItem.iSubItem = subitem ? lpht->iSubItem : 0;
7849     lvItem.pszText = szDispText;
7850     lvItem.cchTextMax = DISP_TEXT_SIZE;
7851     if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1;
7852     if (!infoPtr->bFocus) lvItem.state &= ~LVIS_FOCUSED;
7853 
7854     LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, NULL, &rcIcon, &rcState, &rcLabel);
7855     LISTVIEW_GetItemOrigin(infoPtr, iItem, &Position);
7856     opt.x = lpht->pt.x - Position.x - Origin.x;
7857 
7858     if (lpht->pt.y < infoPtr->rcList.top && lpht->pt.y >= 0)
7859 	opt.y = lpht->pt.y - Position.y - Origin.y + infoPtr->rcList.top;
7860     else
7861 	opt.y = lpht->pt.y - Position.y - Origin.y;
7862 
7863     if (infoPtr->uView == LV_VIEW_DETAILS)
7864     {
7865 	rcBounds = rcBox;
7866 	if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
7867 	    opt.x = lpht->pt.x - Origin.x;
7868     }
7869     else
7870     {
7871         UnionRect(&rcBounds, &rcIcon, &rcLabel);
7872         UnionRect(&rcBounds, &rcBounds, &rcState);
7873     }
7874     TRACE("rcBounds=%s\n", wine_dbgstr_rect(&rcBounds));
7875     if (!PtInRect(&rcBounds, opt)) return -1;
7876 
7877     /* That's a special case - row rectangle is used as item rectangle and
7878        returned flags contain all item parts. */
7879     is_fullrow = (infoPtr->uView == LV_VIEW_DETAILS) && ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) || (infoPtr->dwStyle & LVS_OWNERDRAWFIXED));
7880 
7881     if (PtInRect(&rcIcon, opt))
7882 	lpht->flags |= LVHT_ONITEMICON;
7883     else if (PtInRect(&rcLabel, opt))
7884 	lpht->flags |= LVHT_ONITEMLABEL;
7885     else if (infoPtr->himlState && PtInRect(&rcState, opt))
7886 	lpht->flags |= LVHT_ONITEMSTATEICON;
7887     if (is_fullrow && !(lpht->flags & LVHT_ONITEM))
7888     {
7889 	lpht->flags = LVHT_ONITEM | LVHT_ABOVE;
7890     }
7891     if (lpht->flags & LVHT_ONITEM)
7892 	lpht->flags &= ~LVHT_NOWHERE;
7893     TRACE("lpht->flags=0x%x\n", lpht->flags);
7894 
7895     if (select && !is_fullrow)
7896     {
7897         if (infoPtr->uView == LV_VIEW_DETAILS)
7898         {
7899             /* get main item bounds */
7900             lvItem.iSubItem = 0;
7901             LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, NULL, &rcIcon, &rcState, &rcLabel);
7902             UnionRect(&rcBounds, &rcIcon, &rcLabel);
7903             UnionRect(&rcBounds, &rcBounds, &rcState);
7904         }
7905         if (!PtInRect(&rcBounds, opt)) iItem = -1;
7906     }
7907     return lpht->iItem = iItem;
7908 }
7909 
7910 /***
7911  * DESCRIPTION:
7912  * Inserts a new item in the listview control.
7913  *
7914  * PARAMETER(S):
7915  * [I] infoPtr : valid pointer to the listview structure
7916  * [I] lpLVItem : item information
7917  * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
7918  *
7919  * RETURN:
7920  *   SUCCESS : new item index
7921  *   FAILURE : -1
7922  */
LISTVIEW_InsertItemT(LISTVIEW_INFO * infoPtr,const LVITEMW * lpLVItem,BOOL isW)7923 static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
7924 {
7925     INT nItem;
7926     HDPA hdpaSubItems;
7927     NMLISTVIEW nmlv;
7928     ITEM_INFO *lpItem;
7929     ITEM_ID *lpID;
7930     BOOL is_sorted, has_changed;
7931     LVITEMW item;
7932     HWND hwndSelf = infoPtr->hwndSelf;
7933 
7934     TRACE("(item=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
7935 
7936     if (infoPtr->dwStyle & LVS_OWNERDATA) return infoPtr->nItemCount++;
7937 
7938     /* make sure it's an item, and not a subitem; cannot insert a subitem */
7939     if (!lpLVItem || lpLVItem->iSubItem) return -1;
7940 
7941     if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return -1;
7942 
7943     if (!(lpItem = Alloc(sizeof(ITEM_INFO)))) return -1;
7944 
7945     /* insert item in listview control data structure */
7946     if ( !(hdpaSubItems = DPA_Create(8)) ) goto fail;
7947     if ( !DPA_SetPtr(hdpaSubItems, 0, lpItem) ) assert (FALSE);
7948 
7949     /* link with id struct */
7950     if (!(lpID = Alloc(sizeof(ITEM_ID)))) goto fail;
7951     lpItem->id = lpID;
7952     lpID->item = hdpaSubItems;
7953     lpID->id = get_next_itemid(infoPtr);
7954     if ( DPA_InsertPtr(infoPtr->hdpaItemIds, infoPtr->nItemCount, lpID) == -1) goto fail;
7955 
7956     is_sorted = (infoPtr->dwStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
7957 	        !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
7958 
7959     if (lpLVItem->iItem < 0 && !is_sorted) return -1;
7960 
7961     /* calculate new item index */
7962     if (is_sorted)
7963     {
7964         HDPA hItem;
7965         ITEM_INFO *item_s;
7966         INT i = 0, cmpv;
7967         WCHAR *textW;
7968 
7969         textW = textdupTtoW(lpLVItem->pszText, isW);
7970 
7971         while (i < infoPtr->nItemCount)
7972         {
7973             hItem  = DPA_GetPtr( infoPtr->hdpaItems, i);
7974             item_s = DPA_GetPtr(hItem, 0);
7975 
7976             cmpv = textcmpWT(item_s->hdr.pszText, textW, TRUE);
7977             if (infoPtr->dwStyle & LVS_SORTDESCENDING) cmpv *= -1;
7978 
7979             if (cmpv >= 0) break;
7980             i++;
7981         }
7982 
7983         textfreeT(textW, isW);
7984 
7985         nItem = i;
7986     }
7987     else
7988         nItem = min(lpLVItem->iItem, infoPtr->nItemCount);
7989 
7990     TRACE("inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem);
7991     nItem = DPA_InsertPtr( infoPtr->hdpaItems, nItem, hdpaSubItems );
7992     if (nItem == -1) goto fail;
7993     infoPtr->nItemCount++;
7994 
7995     /* shift indices first so they don't get tangled */
7996     LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
7997 
7998     /* set the item attributes */
7999     if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
8000     {
8001         /* full size structure expected - _WIN32IE >= 0x560 */
8002         item = *lpLVItem;
8003     }
8004     else if (lpLVItem->mask & LVIF_INDENT)
8005     {
8006         /* indent member expected - _WIN32IE >= 0x300 */
8007         memcpy(&item, lpLVItem, offsetof( LVITEMW, iGroupId ));
8008     }
8009     else
8010     {
8011         /* minimal structure expected */
8012         memcpy(&item, lpLVItem, offsetof( LVITEMW, iIndent ));
8013     }
8014     item.iItem = nItem;
8015     if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
8016     {
8017         if (item.mask & LVIF_STATE)
8018         {
8019             item.stateMask |= LVIS_STATEIMAGEMASK;
8020             item.state &= ~LVIS_STATEIMAGEMASK;
8021             item.state |= INDEXTOSTATEIMAGEMASK(1);
8022         }
8023         else
8024         {
8025             item.mask |= LVIF_STATE;
8026             item.stateMask = LVIS_STATEIMAGEMASK;
8027             item.state = INDEXTOSTATEIMAGEMASK(1);
8028         }
8029     }
8030 
8031     if (!set_main_item(infoPtr, &item, TRUE, isW, &has_changed)) goto undo;
8032 
8033     /* make room for the position, if we are in the right mode */
8034     if ((infoPtr->uView == LV_VIEW_SMALLICON) || (infoPtr->uView == LV_VIEW_ICON))
8035     {
8036         if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
8037 	    goto undo;
8038         if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
8039 	{
8040 	    DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
8041 	    goto undo;
8042 	}
8043     }
8044 
8045     /* send LVN_INSERTITEM notification */
8046     memset(&nmlv, 0, sizeof(NMLISTVIEW));
8047     nmlv.iItem = nItem;
8048     nmlv.lParam = lpItem->lParam;
8049     notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
8050     if (!IsWindow(hwndSelf))
8051 	return -1;
8052 
8053     /* align items (set position of each item) */
8054     if (infoPtr->uView == LV_VIEW_SMALLICON || infoPtr->uView == LV_VIEW_ICON)
8055     {
8056 	POINT pt;
8057 
8058 #ifdef __REACTOS__
8059 	if (infoPtr->dwStyle & LVS_ALIGNLEFT)
8060 	    LISTVIEW_NextIconPosLeft(infoPtr, &pt, nItem);
8061         else
8062 	    LISTVIEW_NextIconPosTop(infoPtr, &pt, nItem);
8063 #else
8064     if (infoPtr->dwStyle & LVS_ALIGNLEFT)
8065 	    LISTVIEW_NextIconPosLeft(infoPtr, &pt);
8066         else
8067 	    LISTVIEW_NextIconPosTop(infoPtr, &pt);
8068 #endif
8069 
8070 	LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, TRUE);
8071     }
8072 
8073     /* now is the invalidation fun */
8074     LISTVIEW_ScrollOnInsert(infoPtr, nItem, 1);
8075     return nItem;
8076 
8077 undo:
8078     LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
8079     LISTVIEW_ShiftFocus(infoPtr, infoPtr->nFocusedItem, nItem, -1);
8080     DPA_DeletePtr(infoPtr->hdpaItems, nItem);
8081     infoPtr->nItemCount--;
8082 fail:
8083     DPA_DeletePtr(hdpaSubItems, 0);
8084     DPA_Destroy (hdpaSubItems);
8085     Free (lpItem);
8086     return -1;
8087 }
8088 
8089 /***
8090  * DESCRIPTION:
8091  * Checks item visibility.
8092  *
8093  * PARAMETER(S):
8094  * [I] infoPtr : valid pointer to the listview structure
8095  * [I] nFirst : item index to check for
8096  *
8097  * RETURN:
8098  *   Item visible : TRUE
8099  *   Item invisible or failure : FALSE
8100  */
LISTVIEW_IsItemVisible(const LISTVIEW_INFO * infoPtr,INT nItem)8101 static BOOL LISTVIEW_IsItemVisible(const LISTVIEW_INFO *infoPtr, INT nItem)
8102 {
8103     POINT Origin, Position;
8104     RECT rcItem;
8105     HDC hdc;
8106     BOOL ret;
8107 
8108     TRACE("nItem=%d\n", nItem);
8109 
8110     if (nItem < 0 || nItem >= DPA_GetPtrCount(infoPtr->hdpaItems)) return FALSE;
8111 
8112     LISTVIEW_GetOrigin(infoPtr, &Origin);
8113     LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
8114     rcItem.left = Position.x + Origin.x;
8115     rcItem.top  = Position.y + Origin.y;
8116     rcItem.right  = rcItem.left + infoPtr->nItemWidth;
8117     rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
8118 
8119     hdc = GetDC(infoPtr->hwndSelf);
8120     if (!hdc) return FALSE;
8121     ret = RectVisible(hdc, &rcItem);
8122     ReleaseDC(infoPtr->hwndSelf, hdc);
8123 
8124     return ret;
8125 }
8126 
8127 /***
8128  * DESCRIPTION:
8129  * Redraws a range of items.
8130  *
8131  * PARAMETER(S):
8132  * [I] infoPtr : valid pointer to the listview structure
8133  * [I] nFirst : first item
8134  * [I] nLast : last item
8135  *
8136  * RETURN:
8137  *   SUCCESS : TRUE
8138  *   FAILURE : FALSE
8139  */
LISTVIEW_RedrawItems(const LISTVIEW_INFO * infoPtr,INT nFirst,INT nLast)8140 static BOOL LISTVIEW_RedrawItems(const LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
8141 {
8142     INT i;
8143 
8144     for (i = max(nFirst, 0); i <= min(nLast, infoPtr->nItemCount - 1); i++)
8145 	LISTVIEW_InvalidateItem(infoPtr, i);
8146 
8147     return TRUE;
8148 }
8149 
8150 /***
8151  * DESCRIPTION:
8152  * Scroll the content of a listview.
8153  *
8154  * PARAMETER(S):
8155  * [I] infoPtr : valid pointer to the listview structure
8156  * [I] dx : horizontal scroll amount in pixels
8157  * [I] dy : vertical scroll amount in pixels
8158  *
8159  * RETURN:
8160  *   SUCCESS : TRUE
8161  *   FAILURE : FALSE
8162  *
8163  * COMMENTS:
8164  *  If the control is in report view (LV_VIEW_DETAILS) the control can
8165  *  be scrolled only in line increments. "dy" will be rounded to the
8166  *  nearest number of pixels that are a whole line. Ex: if line height
8167  *  is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
8168  *  is passed, then the scroll will be 0.  (per MSDN 7/2002)
8169  */
LISTVIEW_Scroll(LISTVIEW_INFO * infoPtr,INT dx,INT dy)8170 static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
8171 {
8172     switch(infoPtr->uView) {
8173     case LV_VIEW_DETAILS:
8174 	dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
8175         dy /= infoPtr->nItemHeight;
8176 	break;
8177     case LV_VIEW_LIST:
8178     	if (dy != 0) return FALSE;
8179 	break;
8180     default: /* icon */
8181 	break;
8182     }
8183 
8184     if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx);
8185     if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy);
8186 
8187     return TRUE;
8188 }
8189 
8190 /***
8191  * DESCRIPTION:
8192  * Sets the background color.
8193  *
8194  * PARAMETER(S):
8195  * [I] infoPtr : valid pointer to the listview structure
8196  * [I] color   : background color
8197  *
8198  * RETURN:
8199  *   SUCCESS : TRUE
8200  *   FAILURE : FALSE
8201  */
LISTVIEW_SetBkColor(LISTVIEW_INFO * infoPtr,COLORREF color)8202 static BOOL LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF color)
8203 {
8204     TRACE("(color=%x)\n", color);
8205 
8206 #ifdef __REACTOS__
8207     infoPtr->bDefaultBkColor = FALSE;
8208 #endif
8209     if(infoPtr->clrBk != color) {
8210 	if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
8211 	infoPtr->clrBk = color;
8212 	if (color == CLR_NONE)
8213 	    infoPtr->hBkBrush = (HBRUSH)GetClassLongPtrW(infoPtr->hwndSelf, GCLP_HBRBACKGROUND);
8214 	else
8215 	{
8216 	    infoPtr->hBkBrush = CreateSolidBrush(color);
8217 	    infoPtr->dwLvExStyle &= ~LVS_EX_TRANSPARENTBKGND;
8218 	}
8219     }
8220 
8221     return TRUE;
8222 }
8223 
8224 /* LISTVIEW_SetBkImage */
8225 
8226 /*** Helper for {Insert,Set}ColumnT *only* */
column_fill_hditem(const LISTVIEW_INFO * infoPtr,HDITEMW * lphdi,INT nColumn,const LVCOLUMNW * lpColumn,BOOL isW)8227 static void column_fill_hditem(const LISTVIEW_INFO *infoPtr, HDITEMW *lphdi, INT nColumn,
8228                                const LVCOLUMNW *lpColumn, BOOL isW)
8229 {
8230     if (lpColumn->mask & LVCF_FMT)
8231     {
8232 	/* format member is valid */
8233 	lphdi->mask |= HDI_FORMAT;
8234 
8235 	/* set text alignment (leftmost column must be left-aligned) */
8236         if (nColumn == 0 || (lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
8237             lphdi->fmt |= HDF_LEFT;
8238         else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_RIGHT)
8239             lphdi->fmt |= HDF_RIGHT;
8240         else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_CENTER)
8241             lphdi->fmt |= HDF_CENTER;
8242 
8243         if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
8244             lphdi->fmt |= HDF_BITMAP_ON_RIGHT;
8245 
8246         if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
8247         {
8248             lphdi->fmt |= HDF_IMAGE;
8249             lphdi->iImage = I_IMAGECALLBACK;
8250         }
8251 
8252         if (lpColumn->fmt & LVCFMT_FIXED_WIDTH)
8253             lphdi->fmt |= HDF_FIXEDWIDTH;
8254     }
8255 
8256     if (lpColumn->mask & LVCF_WIDTH)
8257     {
8258         lphdi->mask |= HDI_WIDTH;
8259         if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
8260         {
8261             /* make it fill the remainder of the controls width */
8262             RECT rcHeader;
8263             INT item_index;
8264 
8265             for(item_index = 0; item_index < (nColumn - 1); item_index++)
8266 	    {
8267             	LISTVIEW_GetHeaderRect(infoPtr, item_index, &rcHeader);
8268 		lphdi->cxy += rcHeader.right - rcHeader.left;
8269 	    }
8270 
8271             /* retrieve the layout of the header */
8272             GetClientRect(infoPtr->hwndSelf, &rcHeader);
8273             TRACE("start cxy=%d rcHeader=%s\n", lphdi->cxy, wine_dbgstr_rect(&rcHeader));
8274 
8275             lphdi->cxy = (rcHeader.right - rcHeader.left) - lphdi->cxy;
8276         }
8277         else
8278             lphdi->cxy = lpColumn->cx;
8279     }
8280 
8281     if (lpColumn->mask & LVCF_TEXT)
8282     {
8283         lphdi->mask |= HDI_TEXT | HDI_FORMAT;
8284         lphdi->fmt |= HDF_STRING;
8285         lphdi->pszText = lpColumn->pszText;
8286         lphdi->cchTextMax = textlenT(lpColumn->pszText, isW);
8287     }
8288 
8289     if (lpColumn->mask & LVCF_IMAGE)
8290     {
8291         lphdi->mask |= HDI_IMAGE;
8292         lphdi->iImage = lpColumn->iImage;
8293     }
8294 
8295     if (lpColumn->mask & LVCF_ORDER)
8296     {
8297 	lphdi->mask |= HDI_ORDER;
8298 	lphdi->iOrder = lpColumn->iOrder;
8299     }
8300 }
8301 
8302 
8303 /***
8304  * DESCRIPTION:
8305  * Inserts a new column.
8306  *
8307  * PARAMETER(S):
8308  * [I] infoPtr : valid pointer to the listview structure
8309  * [I] nColumn : column index
8310  * [I] lpColumn : column information
8311  * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
8312  *
8313  * RETURN:
8314  *   SUCCESS : new column index
8315  *   FAILURE : -1
8316  */
LISTVIEW_InsertColumnT(LISTVIEW_INFO * infoPtr,INT nColumn,const LVCOLUMNW * lpColumn,BOOL isW)8317 static INT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
8318                                   const LVCOLUMNW *lpColumn, BOOL isW)
8319 {
8320     COLUMN_INFO *lpColumnInfo;
8321     INT nNewColumn;
8322     HDITEMW hdi;
8323 
8324     TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
8325 
8326     if (!lpColumn || nColumn < 0) return -1;
8327     nColumn = min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns));
8328 
8329     ZeroMemory(&hdi, sizeof(HDITEMW));
8330     column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
8331 
8332     /*
8333      * A mask not including LVCF_WIDTH turns into a mask of width, width 10
8334      * (can be seen in SPY) otherwise column never gets added.
8335      */
8336     if (!(lpColumn->mask & LVCF_WIDTH)) {
8337         hdi.mask |= HDI_WIDTH;
8338         hdi.cxy = 10;
8339     }
8340 
8341     /*
8342      * when the iSubItem is available Windows copies it to the header lParam. It seems
8343      * to happen only in LVM_INSERTCOLUMN - not in LVM_SETCOLUMN
8344      */
8345     if (lpColumn->mask & LVCF_SUBITEM)
8346     {
8347         hdi.mask |= HDI_LPARAM;
8348         hdi.lParam = lpColumn->iSubItem;
8349     }
8350 
8351     /* create header if not present */
8352     LISTVIEW_CreateHeader(infoPtr);
8353     if (!(LVS_NOCOLUMNHEADER & infoPtr->dwStyle) &&
8354          (infoPtr->uView == LV_VIEW_DETAILS) && (WS_VISIBLE & infoPtr->dwStyle))
8355     {
8356         ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
8357     }
8358 
8359     /* insert item in header control */
8360     nNewColumn = SendMessageW(infoPtr->hwndHeader,
8361 		              isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
8362                               nColumn, (LPARAM)&hdi);
8363     if (nNewColumn == -1) return -1;
8364     if (nNewColumn != nColumn) ERR("nColumn=%d, nNewColumn=%d\n", nColumn, nNewColumn);
8365 
8366     /* create our own column info */
8367     if (!(lpColumnInfo = Alloc(sizeof(COLUMN_INFO)))) goto fail;
8368     if (DPA_InsertPtr(infoPtr->hdpaColumns, nNewColumn, lpColumnInfo) == -1) goto fail;
8369 
8370     if (lpColumn->mask & LVCF_FMT) lpColumnInfo->fmt = lpColumn->fmt;
8371     if (lpColumn->mask & LVCF_MINWIDTH) lpColumnInfo->cxMin = lpColumn->cxMin;
8372     if (!SendMessageW(infoPtr->hwndHeader, HDM_GETITEMRECT, nNewColumn, (LPARAM)&lpColumnInfo->rcHeader))
8373         goto fail;
8374 
8375     /* now we have to actually adjust the data */
8376     if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
8377     {
8378 	SUBITEM_INFO *lpSubItem;
8379 	HDPA hdpaSubItems;
8380 	INT nItem, i;
8381 	LVITEMW item;
8382 	BOOL changed;
8383 
8384 	item.iSubItem = nNewColumn;
8385 	item.mask = LVIF_TEXT | LVIF_IMAGE;
8386 	item.iImage = I_IMAGECALLBACK;
8387 	item.pszText = LPSTR_TEXTCALLBACKW;
8388 
8389 	for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
8390 	{
8391             hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, nItem);
8392 	    for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
8393 	    {
8394                 lpSubItem = DPA_GetPtr(hdpaSubItems, i);
8395 		if (lpSubItem->iSubItem >= nNewColumn)
8396 		    lpSubItem->iSubItem++;
8397 	    }
8398 
8399 	    /* add new subitem for each item */
8400 	    item.iItem = nItem;
8401 	    set_sub_item(infoPtr, &item, isW, &changed);
8402 	}
8403     }
8404 
8405     /* make space for the new column */
8406     LISTVIEW_ScrollColumns(infoPtr, nNewColumn + 1, lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
8407     LISTVIEW_UpdateItemSize(infoPtr);
8408 
8409     return nNewColumn;
8410 
8411 fail:
8412     if (nNewColumn != -1) SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nNewColumn, 0);
8413     if (lpColumnInfo)
8414     {
8415 	DPA_DeletePtr(infoPtr->hdpaColumns, nNewColumn);
8416 	Free(lpColumnInfo);
8417     }
8418     return -1;
8419 }
8420 
8421 /***
8422  * DESCRIPTION:
8423  * Sets the attributes of a header item.
8424  *
8425  * PARAMETER(S):
8426  * [I] infoPtr : valid pointer to the listview structure
8427  * [I] nColumn : column index
8428  * [I] lpColumn : column attributes
8429  * [I] isW: if TRUE, then lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
8430  *
8431  * RETURN:
8432  *   SUCCESS : TRUE
8433  *   FAILURE : FALSE
8434  */
LISTVIEW_SetColumnT(const LISTVIEW_INFO * infoPtr,INT nColumn,const LVCOLUMNW * lpColumn,BOOL isW)8435 static BOOL LISTVIEW_SetColumnT(const LISTVIEW_INFO *infoPtr, INT nColumn,
8436                                 const LVCOLUMNW *lpColumn, BOOL isW)
8437 {
8438     HDITEMW hdi, hdiget;
8439     BOOL bResult;
8440 
8441     TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
8442 
8443     if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
8444 
8445     ZeroMemory(&hdi, sizeof(HDITEMW));
8446     if (lpColumn->mask & LVCF_FMT)
8447     {
8448         hdi.mask |= HDI_FORMAT;
8449         hdiget.mask = HDI_FORMAT;
8450         if (SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, nColumn, (LPARAM)&hdiget))
8451 	    hdi.fmt = hdiget.fmt & HDF_STRING;
8452     }
8453     column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
8454 
8455     /* set header item attributes */
8456     bResult = SendMessageW(infoPtr->hwndHeader, isW ? HDM_SETITEMW : HDM_SETITEMA, nColumn, (LPARAM)&hdi);
8457     if (!bResult) return FALSE;
8458 
8459     if (lpColumn->mask & LVCF_FMT)
8460     {
8461 	COLUMN_INFO *lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
8462 	INT oldFmt = lpColumnInfo->fmt;
8463 
8464 	lpColumnInfo->fmt = lpColumn->fmt;
8465 	if ((oldFmt ^ lpColumn->fmt) & (LVCFMT_JUSTIFYMASK | LVCFMT_IMAGE))
8466 	{
8467 	    if (infoPtr->uView == LV_VIEW_DETAILS) LISTVIEW_InvalidateColumn(infoPtr, nColumn);
8468 	}
8469     }
8470 
8471     if (lpColumn->mask & LVCF_MINWIDTH)
8472 	LISTVIEW_GetColumnInfo(infoPtr, nColumn)->cxMin = lpColumn->cxMin;
8473 
8474     return TRUE;
8475 }
8476 
8477 /***
8478  * DESCRIPTION:
8479  * Sets the column order array
8480  *
8481  * PARAMETERS:
8482  * [I] infoPtr : valid pointer to the listview structure
8483  * [I] iCount : number of elements in column order array
8484  * [I] lpiArray : pointer to column order array
8485  *
8486  * RETURN:
8487  *   SUCCESS : TRUE
8488  *   FAILURE : FALSE
8489  */
LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO * infoPtr,INT iCount,const INT * lpiArray)8490 static BOOL LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, const INT *lpiArray)
8491 {
8492     if (!infoPtr->hwndHeader) return FALSE;
8493     infoPtr->colRectsDirty = TRUE;
8494     return SendMessageW(infoPtr->hwndHeader, HDM_SETORDERARRAY, iCount, (LPARAM)lpiArray);
8495 }
8496 
8497 /***
8498  * DESCRIPTION:
8499  * Sets the width of a column
8500  *
8501  * PARAMETERS:
8502  * [I] infoPtr : valid pointer to the listview structure
8503  * [I] nColumn : column index
8504  * [I] cx : column width
8505  *
8506  * RETURN:
8507  *   SUCCESS : TRUE
8508  *   FAILURE : FALSE
8509  */
LISTVIEW_SetColumnWidth(LISTVIEW_INFO * infoPtr,INT nColumn,INT cx)8510 static BOOL LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn, INT cx)
8511 {
8512     WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
8513     INT max_cx = 0;
8514     HDITEMW hdi;
8515 
8516     TRACE("(nColumn=%d, cx=%d)\n", nColumn, cx);
8517 
8518     /* set column width only if in report or list mode */
8519     if (infoPtr->uView != LV_VIEW_DETAILS && infoPtr->uView != LV_VIEW_LIST) return FALSE;
8520 
8521     /* take care of invalid cx values - LVSCW_AUTOSIZE_* values are negative,
8522        with _USEHEADER being the lowest */
8523     if (infoPtr->uView == LV_VIEW_DETAILS && cx < LVSCW_AUTOSIZE_USEHEADER) cx = LVSCW_AUTOSIZE;
8524     else if (infoPtr->uView == LV_VIEW_LIST && cx <= 0) return FALSE;
8525 
8526     /* resize all columns if in LV_VIEW_LIST mode */
8527     if(infoPtr->uView == LV_VIEW_LIST)
8528     {
8529 	infoPtr->nItemWidth = cx;
8530 	LISTVIEW_InvalidateList(infoPtr);
8531 	return TRUE;
8532     }
8533 
8534     if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
8535 
8536     if (cx == LVSCW_AUTOSIZE || (cx == LVSCW_AUTOSIZE_USEHEADER && nColumn < DPA_GetPtrCount(infoPtr->hdpaColumns) -1))
8537     {
8538 	INT nLabelWidth;
8539 	LVITEMW lvItem;
8540 
8541 	lvItem.mask = LVIF_TEXT;
8542 	lvItem.iItem = 0;
8543 	lvItem.iSubItem = nColumn;
8544 	lvItem.cchTextMax = DISP_TEXT_SIZE;
8545 	for (; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
8546 	{
8547             lvItem.pszText = szDispText;
8548 	    if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
8549 	    nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
8550 	    if (max_cx < nLabelWidth) max_cx = nLabelWidth;
8551 	}
8552 	if (infoPtr->himlSmall && (nColumn == 0 || (LISTVIEW_GetColumnInfo(infoPtr, nColumn)->fmt & LVCFMT_IMAGE)))
8553 	    max_cx += infoPtr->iconSize.cx;
8554 	max_cx += TRAILING_LABEL_PADDING;
8555         if (nColumn == 0 && (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES))
8556             max_cx += GetSystemMetrics(SM_CXSMICON);
8557     }
8558 
8559     /* autosize based on listview items width */
8560     if(cx == LVSCW_AUTOSIZE)
8561 	cx = max_cx;
8562     else if(cx == LVSCW_AUTOSIZE_USEHEADER)
8563     {
8564 	/* if iCol is the last column make it fill the remainder of the controls width */
8565         if(nColumn == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1)
8566 	{
8567 	    RECT rcHeader;
8568 	    POINT Origin;
8569 
8570 	    LISTVIEW_GetOrigin(infoPtr, &Origin);
8571 	    LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
8572 
8573 	    cx = infoPtr->rcList.right - Origin.x - rcHeader.left;
8574 	}
8575 	else
8576 	{
8577             /* Despite what the MS docs say, if this is not the last
8578                column, then MS resizes the column to the width of the
8579                largest text string in the column, including headers
8580                and items. This is different from LVSCW_AUTOSIZE in that
8581 	       LVSCW_AUTOSIZE ignores the header string length. */
8582 	    cx = 0;
8583 
8584 	    /* retrieve header text */
8585 	    hdi.mask = HDI_TEXT|HDI_FORMAT|HDI_IMAGE|HDI_BITMAP;
8586 	    hdi.cchTextMax = DISP_TEXT_SIZE;
8587 	    hdi.pszText = szDispText;
8588 	    if (SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, nColumn, (LPARAM)&hdi))
8589 	    {
8590 		HDC hdc = GetDC(infoPtr->hwndSelf);
8591 		HFONT old_font = SelectObject(hdc, (HFONT)SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0, 0));
8592 		HIMAGELIST himl = (HIMAGELIST)SendMessageW(infoPtr->hwndHeader, HDM_GETIMAGELIST, 0, 0);
8593 		INT bitmap_margin = 0;
8594 		SIZE size;
8595 
8596 		if (GetTextExtentPoint32W(hdc, hdi.pszText, lstrlenW(hdi.pszText), &size))
8597 		    cx = size.cx + TRAILING_HEADER_PADDING;
8598 
8599 		if (hdi.fmt & (HDF_IMAGE|HDF_BITMAP))
8600 		    bitmap_margin = SendMessageW(infoPtr->hwndHeader, HDM_GETBITMAPMARGIN, 0, 0);
8601 
8602 		if ((hdi.fmt & HDF_IMAGE) && himl)
8603 		{
8604 		    INT icon_cx, icon_cy;
8605 
8606 		    if (!ImageList_GetIconSize(himl, &icon_cx, &icon_cy))
8607 		        cx += icon_cx + 2*bitmap_margin;
8608 		}
8609 		else if (hdi.fmt & HDF_BITMAP)
8610 		{
8611 		    BITMAP bmp;
8612 
8613 		    GetObjectW(hdi.hbm, sizeof(BITMAP), &bmp);
8614 		    cx += bmp.bmWidth + 2*bitmap_margin;
8615 		}
8616 
8617 		SelectObject(hdc, old_font);
8618 		ReleaseDC(infoPtr->hwndSelf, hdc);
8619 	    }
8620 	    cx = max (cx, max_cx);
8621 	}
8622     }
8623 
8624     if (cx < 0) return FALSE;
8625 
8626     /* call header to update the column change */
8627     hdi.mask = HDI_WIDTH;
8628     hdi.cxy = max(cx, LISTVIEW_GetColumnInfo(infoPtr, nColumn)->cxMin);
8629     TRACE("hdi.cxy=%d\n", hdi.cxy);
8630     return SendMessageW(infoPtr->hwndHeader, HDM_SETITEMW, nColumn, (LPARAM)&hdi);
8631 }
8632 
8633 /***
8634  * Creates the checkbox imagelist.  Helper for LISTVIEW_SetExtendedListViewStyle
8635  *
8636  */
LISTVIEW_CreateCheckBoxIL(const LISTVIEW_INFO * infoPtr)8637 static HIMAGELIST LISTVIEW_CreateCheckBoxIL(const LISTVIEW_INFO *infoPtr)
8638 {
8639     HDC hdc_wnd, hdc;
8640     HBITMAP hbm_im, hbm_mask, hbm_orig;
8641     RECT rc;
8642     HBRUSH hbr_white = GetStockObject(WHITE_BRUSH);
8643     HBRUSH hbr_black = GetStockObject(BLACK_BRUSH);
8644     HIMAGELIST himl;
8645 
8646     himl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
8647                             ILC_COLOR | ILC_MASK, 2, 2);
8648     hdc_wnd = GetDC(infoPtr->hwndSelf);
8649     hdc = CreateCompatibleDC(hdc_wnd);
8650     hbm_im = CreateCompatibleBitmap(hdc_wnd, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
8651     hbm_mask = CreateBitmap(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 1, 1, NULL);
8652     ReleaseDC(infoPtr->hwndSelf, hdc_wnd);
8653 
8654     SetRect(&rc, 0, 0, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
8655     hbm_orig = SelectObject(hdc, hbm_mask);
8656     FillRect(hdc, &rc, hbr_white);
8657     InflateRect(&rc, -2, -2);
8658     FillRect(hdc, &rc, hbr_black);
8659 
8660     SelectObject(hdc, hbm_im);
8661     DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO);
8662     SelectObject(hdc, hbm_orig);
8663     ImageList_Add(himl, hbm_im, hbm_mask);
8664 
8665     SelectObject(hdc, hbm_im);
8666     DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO | DFCS_CHECKED);
8667     SelectObject(hdc, hbm_orig);
8668     ImageList_Add(himl, hbm_im, hbm_mask);
8669 
8670     DeleteObject(hbm_mask);
8671     DeleteObject(hbm_im);
8672     DeleteDC(hdc);
8673 
8674     return himl;
8675 }
8676 
8677 /***
8678  * DESCRIPTION:
8679  * Sets the extended listview style.
8680  *
8681  * PARAMETERS:
8682  * [I] infoPtr : valid pointer to the listview structure
8683  * [I] dwMask : mask
8684  * [I] dwStyle : style
8685  *
8686  * RETURN:
8687  *   SUCCESS : previous style
8688  *   FAILURE : 0
8689  */
LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO * infoPtr,DWORD mask,DWORD ex_style)8690 static DWORD LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD mask, DWORD ex_style)
8691 {
8692     DWORD old_ex_style = infoPtr->dwLvExStyle;
8693 
8694     TRACE("mask=0x%08x, ex_style=0x%08x\n", mask, ex_style);
8695 
8696     /* set new style */
8697     if (mask)
8698 	infoPtr->dwLvExStyle = (old_ex_style & ~mask) | (ex_style & mask);
8699     else
8700 	infoPtr->dwLvExStyle = ex_style;
8701 
8702     if((infoPtr->dwLvExStyle ^ old_ex_style) & LVS_EX_CHECKBOXES)
8703     {
8704         HIMAGELIST himl = 0;
8705         if(infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
8706         {
8707             LVITEMW item;
8708             item.mask = LVIF_STATE;
8709             item.stateMask = LVIS_STATEIMAGEMASK;
8710             item.state = INDEXTOSTATEIMAGEMASK(1);
8711             LISTVIEW_SetItemState(infoPtr, -1, &item);
8712 
8713             himl = LISTVIEW_CreateCheckBoxIL(infoPtr);
8714             if(!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS))
8715                 ImageList_Destroy(infoPtr->himlState);
8716         }
8717         himl = LISTVIEW_SetImageList(infoPtr, LVSIL_STATE, himl);
8718         /*   checkbox list replaces previous custom list or... */
8719         if(((infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) &&
8720            !(infoPtr->dwStyle & LVS_SHAREIMAGELISTS)) ||
8721             /* ...previous was checkbox list */
8722             (old_ex_style & LVS_EX_CHECKBOXES))
8723             ImageList_Destroy(himl);
8724     }
8725 
8726     if((infoPtr->dwLvExStyle ^ old_ex_style) & LVS_EX_HEADERDRAGDROP)
8727     {
8728         DWORD style;
8729 
8730         /* if not already created */
8731         LISTVIEW_CreateHeader(infoPtr);
8732 
8733         style = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE);
8734         if (infoPtr->dwLvExStyle & LVS_EX_HEADERDRAGDROP)
8735             style |= HDS_DRAGDROP;
8736         else
8737             style &= ~HDS_DRAGDROP;
8738         SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, style);
8739     }
8740 
8741     /* GRIDLINES adds decoration at top so changes sizes */
8742     if((infoPtr->dwLvExStyle ^ old_ex_style) & LVS_EX_GRIDLINES)
8743     {
8744         LISTVIEW_CreateHeader(infoPtr);
8745         LISTVIEW_UpdateSize(infoPtr);
8746     }
8747 
8748     if((infoPtr->dwLvExStyle ^ old_ex_style) & LVS_EX_FULLROWSELECT)
8749     {
8750         LISTVIEW_CreateHeader(infoPtr);
8751     }
8752 
8753     if((infoPtr->dwLvExStyle ^ old_ex_style) & LVS_EX_TRANSPARENTBKGND)
8754     {
8755         if (infoPtr->dwLvExStyle & LVS_EX_TRANSPARENTBKGND)
8756             LISTVIEW_SetBkColor(infoPtr, CLR_NONE);
8757     }
8758 
8759     if((infoPtr->dwLvExStyle ^ old_ex_style) & LVS_EX_HEADERINALLVIEWS)
8760     {
8761         if (infoPtr->dwLvExStyle & LVS_EX_HEADERINALLVIEWS)
8762             LISTVIEW_CreateHeader(infoPtr);
8763         else
8764             ShowWindow(infoPtr->hwndHeader, SW_HIDE);
8765         LISTVIEW_UpdateSize(infoPtr);
8766         LISTVIEW_UpdateScroll(infoPtr);
8767     }
8768 
8769 #ifdef __REACTOS__
8770     if ((infoPtr->dwLvExStyle & LVS_EX_SNAPTOGRID) > (old_ex_style & LVS_EX_SNAPTOGRID))
8771     {
8772         LISTVIEW_Arrange(infoPtr, LVA_SNAPTOGRID);
8773     }
8774 #endif
8775 
8776     LISTVIEW_InvalidateList(infoPtr);
8777     return old_ex_style;
8778 }
8779 
8780 /***
8781  * DESCRIPTION:
8782  * Sets the new hot cursor used during hot tracking and hover selection.
8783  *
8784  * PARAMETER(S):
8785  * [I] infoPtr : valid pointer to the listview structure
8786  * [I] hCursor : the new hot cursor handle
8787  *
8788  * RETURN:
8789  * Returns the previous hot cursor
8790  */
LISTVIEW_SetHotCursor(LISTVIEW_INFO * infoPtr,HCURSOR hCursor)8791 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
8792 {
8793     HCURSOR oldCursor = infoPtr->hHotCursor;
8794 
8795     infoPtr->hHotCursor = hCursor;
8796 
8797     return oldCursor;
8798 }
8799 
8800 
8801 /***
8802  * DESCRIPTION:
8803  * Sets the hot item index.
8804  *
8805  * PARAMETERS:
8806  * [I] infoPtr : valid pointer to the listview structure
8807  * [I] iIndex : index
8808  *
8809  * RETURN:
8810  *   SUCCESS : previous hot item index
8811  *   FAILURE : -1 (no hot item)
8812  */
LISTVIEW_SetHotItem(LISTVIEW_INFO * infoPtr,INT iIndex)8813 static INT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
8814 {
8815     INT iOldIndex = infoPtr->nHotItem;
8816 
8817     infoPtr->nHotItem = iIndex;
8818 
8819     return iOldIndex;
8820 }
8821 
8822 
8823 /***
8824  * DESCRIPTION:
8825  * Sets the amount of time the cursor must hover over an item before it is selected.
8826  *
8827  * PARAMETER(S):
8828  * [I] infoPtr : valid pointer to the listview structure
8829  * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
8830  *
8831  * RETURN:
8832  * Returns the previous hover time
8833  */
LISTVIEW_SetHoverTime(LISTVIEW_INFO * infoPtr,DWORD dwHoverTime)8834 static DWORD LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
8835 {
8836     DWORD oldHoverTime = infoPtr->dwHoverTime;
8837 
8838     infoPtr->dwHoverTime = dwHoverTime;
8839 
8840     return oldHoverTime;
8841 }
8842 
8843 /***
8844  * DESCRIPTION:
8845  * Sets spacing for icons of LVS_ICON style.
8846  *
8847  * PARAMETER(S):
8848  * [I] infoPtr : valid pointer to the listview structure
8849  * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
8850  * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
8851  *
8852  * RETURN:
8853  *   MAKELONG(oldcx, oldcy)
8854  */
LISTVIEW_SetIconSpacing(LISTVIEW_INFO * infoPtr,INT cx,INT cy)8855 static DWORD LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, INT cx, INT cy)
8856 {
8857     INT iconWidth = 0, iconHeight = 0;
8858     DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
8859 
8860     TRACE("requested=(%d,%d)\n", cx, cy);
8861 
8862     /* set to defaults, if instructed to */
8863     if (cx == -1 && cy == -1)
8864     {
8865         infoPtr->autoSpacing = TRUE;
8866         if (infoPtr->himlNormal)
8867             ImageList_GetIconSize(infoPtr->himlNormal, &iconWidth, &iconHeight);
8868         cx = GetSystemMetrics(SM_CXICONSPACING) - GetSystemMetrics(SM_CXICON) + iconWidth;
8869         cy = GetSystemMetrics(SM_CYICONSPACING) - GetSystemMetrics(SM_CYICON) + iconHeight;
8870     }
8871     else
8872         infoPtr->autoSpacing = FALSE;
8873 
8874     /* if 0 then keep width */
8875     if (cx != 0)
8876         infoPtr->iconSpacing.cx = cx;
8877 
8878     /* if 0 then keep height */
8879     if (cy != 0)
8880         infoPtr->iconSpacing.cy = cy;
8881 
8882     TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%d,%d), ntmH=%d\n",
8883           LOWORD(oldspacing), HIWORD(oldspacing), infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy,
8884 	  infoPtr->iconSize.cx, infoPtr->iconSize.cy,
8885 	  infoPtr->ntmHeight);
8886 
8887     /* these depend on the iconSpacing */
8888     LISTVIEW_UpdateItemSize(infoPtr);
8889 
8890     return oldspacing;
8891 }
8892 
set_icon_size(SIZE * size,HIMAGELIST himl,BOOL is_small)8893 static inline void set_icon_size(SIZE *size, HIMAGELIST himl, BOOL is_small)
8894 {
8895     INT cx, cy;
8896 
8897     if (himl && ImageList_GetIconSize(himl, &cx, &cy))
8898     {
8899 	size->cx = cx;
8900 	size->cy = cy;
8901     }
8902     else
8903     {
8904 	size->cx = GetSystemMetrics(is_small ? SM_CXSMICON : SM_CXICON);
8905 	size->cy = GetSystemMetrics(is_small ? SM_CYSMICON : SM_CYICON);
8906     }
8907 }
8908 
8909 /***
8910  * DESCRIPTION:
8911  * Sets image lists.
8912  *
8913  * PARAMETER(S):
8914  * [I] infoPtr : valid pointer to the listview structure
8915  * [I] nType : image list type
8916  * [I] himl : image list handle
8917  *
8918  * RETURN:
8919  *   SUCCESS : old image list
8920  *   FAILURE : NULL
8921  */
LISTVIEW_SetImageList(LISTVIEW_INFO * infoPtr,INT nType,HIMAGELIST himl)8922 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
8923 {
8924     INT oldHeight = infoPtr->nItemHeight;
8925     HIMAGELIST himlOld = 0;
8926 
8927     TRACE("(nType=%d, himl=%p)\n", nType, himl);
8928 
8929     switch (nType)
8930     {
8931     case LVSIL_NORMAL:
8932         himlOld = infoPtr->himlNormal;
8933         infoPtr->himlNormal = himl;
8934         if (infoPtr->uView == LV_VIEW_ICON) set_icon_size(&infoPtr->iconSize, himl, FALSE);
8935         if (infoPtr->autoSpacing)
8936             LISTVIEW_SetIconSpacing(infoPtr, -1, -1);
8937     break;
8938 
8939     case LVSIL_SMALL:
8940         himlOld = infoPtr->himlSmall;
8941         infoPtr->himlSmall = himl;
8942         if (infoPtr->uView != LV_VIEW_ICON) set_icon_size(&infoPtr->iconSize, himl, TRUE);
8943         if (infoPtr->hwndHeader)
8944             SendMessageW(infoPtr->hwndHeader, HDM_SETIMAGELIST, 0, (LPARAM)himl);
8945     break;
8946 
8947     case LVSIL_STATE:
8948         himlOld = infoPtr->himlState;
8949         infoPtr->himlState = himl;
8950         set_icon_size(&infoPtr->iconStateSize, himl, TRUE);
8951         ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
8952     break;
8953 
8954     default:
8955         ERR("Unknown icon type=%d\n", nType);
8956 	return NULL;
8957     }
8958 
8959     infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
8960     if (infoPtr->nItemHeight != oldHeight)
8961         LISTVIEW_UpdateScroll(infoPtr);
8962 
8963     return himlOld;
8964 }
8965 
8966 /***
8967  * DESCRIPTION:
8968  * Preallocates memory (does *not* set the actual count of items !)
8969  *
8970  * PARAMETER(S):
8971  * [I] infoPtr : valid pointer to the listview structure
8972  * [I] nItems : item count (projected number of items to allocate)
8973  * [I] dwFlags : update flags
8974  *
8975  * RETURN:
8976  *   SUCCESS : TRUE
8977  *   FAILURE : FALSE
8978  */
LISTVIEW_SetItemCount(LISTVIEW_INFO * infoPtr,INT nItems,DWORD dwFlags)8979 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
8980 {
8981     TRACE("(nItems=%d, dwFlags=%x)\n", nItems, dwFlags);
8982 
8983     if (infoPtr->dwStyle & LVS_OWNERDATA)
8984     {
8985 	INT nOldCount = infoPtr->nItemCount;
8986 	infoPtr->nItemCount = nItems;
8987 
8988 	if (nItems < nOldCount)
8989 	{
8990 	    RANGE range = { nItems, nOldCount };
8991 	    ranges_del(infoPtr->selectionRanges, range);
8992 	    if (infoPtr->nFocusedItem >= nItems)
8993 	    {
8994 		LISTVIEW_SetItemFocus(infoPtr, -1);
8995                 infoPtr->nFocusedItem = -1;
8996 		SetRectEmpty(&infoPtr->rcFocus);
8997 	    }
8998 	}
8999 
9000 	LISTVIEW_UpdateScroll(infoPtr);
9001 
9002 	/* the flags are valid only in ownerdata report and list modes */
9003 	if (infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON) dwFlags = 0;
9004 
9005 	if (!(dwFlags & LVSICF_NOSCROLL) && infoPtr->nFocusedItem != -1)
9006 	    LISTVIEW_EnsureVisible(infoPtr, infoPtr->nFocusedItem, FALSE);
9007 
9008 	if (!(dwFlags & LVSICF_NOINVALIDATEALL))
9009 	    LISTVIEW_InvalidateList(infoPtr);
9010 	else
9011 	{
9012 	    INT nFrom, nTo;
9013 	    POINT Origin;
9014 	    RECT rcErase;
9015 
9016 	    LISTVIEW_GetOrigin(infoPtr, &Origin);
9017     	    nFrom = min(nOldCount, nItems);
9018 	    nTo = max(nOldCount, nItems);
9019 
9020 	    if (infoPtr->uView == LV_VIEW_DETAILS)
9021 	    {
9022                 SetRect(&rcErase, 0, nFrom * infoPtr->nItemHeight, infoPtr->nItemWidth,
9023                         nTo * infoPtr->nItemHeight);
9024 		OffsetRect(&rcErase, Origin.x, Origin.y);
9025 		if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
9026 		    LISTVIEW_InvalidateRect(infoPtr, &rcErase);
9027 	    }
9028 	    else /* LV_VIEW_LIST */
9029 	    {
9030 		INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
9031 
9032 		rcErase.left = (nFrom / nPerCol) * infoPtr->nItemWidth;
9033 		rcErase.top = (nFrom % nPerCol) * infoPtr->nItemHeight;
9034 		rcErase.right = rcErase.left + infoPtr->nItemWidth;
9035 		rcErase.bottom = nPerCol * infoPtr->nItemHeight;
9036 		OffsetRect(&rcErase, Origin.x, Origin.y);
9037 		if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
9038 		    LISTVIEW_InvalidateRect(infoPtr, &rcErase);
9039 
9040 		rcErase.left = (nFrom / nPerCol + 1) * infoPtr->nItemWidth;
9041 		rcErase.top = 0;
9042 		rcErase.right = (nTo / nPerCol + 1) * infoPtr->nItemWidth;
9043 		rcErase.bottom = nPerCol * infoPtr->nItemHeight;
9044 		OffsetRect(&rcErase, Origin.x, Origin.y);
9045 		if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
9046 		    LISTVIEW_InvalidateRect(infoPtr, &rcErase);
9047 	    }
9048 	}
9049     }
9050     else
9051     {
9052 	/* According to MSDN for non-LVS_OWNERDATA this is just
9053 	 * a performance issue. The control allocates its internal
9054 	 * data structures for the number of items specified. It
9055 	 * cuts down on the number of memory allocations. Therefore
9056 	 * we will just issue a WARN here
9057 	 */
9058 	WARN("for non-ownerdata performance option not implemented.\n");
9059     }
9060 
9061     return TRUE;
9062 }
9063 
9064 /***
9065  * DESCRIPTION:
9066  * Sets the position of an item.
9067  *
9068  * PARAMETER(S):
9069  * [I] infoPtr : valid pointer to the listview structure
9070  * [I] nItem : item index
9071  * [I] pt : coordinate
9072  *
9073  * RETURN:
9074  *   SUCCESS : TRUE
9075  *   FAILURE : FALSE
9076  */
LISTVIEW_SetItemPosition(LISTVIEW_INFO * infoPtr,INT nItem,const POINT * pt)9077 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, const POINT *pt)
9078 {
9079     POINT Origin, Pt;
9080 
9081     TRACE("(nItem=%d, pt=%s)\n", nItem, wine_dbgstr_point(pt));
9082 
9083     if (!pt || nItem < 0 || nItem >= infoPtr->nItemCount ||
9084 	!(infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON)) return FALSE;
9085 
9086 #ifdef __REACTOS__
9087     /* FIXME:  This should really call snap to grid if auto-arrange is enabled
9088        and limit the size of the grid to nItemCount elements */
9089     if (is_autoarrange(infoPtr)) return FALSE;
9090 #endif
9091 
9092     Pt = *pt;
9093     LISTVIEW_GetOrigin(infoPtr, &Origin);
9094 
9095     /* This point value seems to be an undocumented feature.
9096      * The best guess is that it means either at the origin,
9097      * or at true beginning of the list. I will assume the origin. */
9098     if ((Pt.x == -1) && (Pt.y == -1))
9099 	Pt = Origin;
9100 
9101     if (infoPtr->uView == LV_VIEW_ICON)
9102     {
9103 	Pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
9104 	Pt.y -= ICON_TOP_PADDING;
9105     }
9106     Pt.x -= Origin.x;
9107     Pt.y -= Origin.y;
9108 
9109 #ifdef __REACTOS__
9110     if (infoPtr->dwLvExStyle & LVS_EX_SNAPTOGRID)
9111     {
9112         Pt.x = max(0, Pt.x + (infoPtr->nItemWidth >> 1) - (Pt.x + (infoPtr->nItemWidth >> 1)) % infoPtr->nItemWidth);
9113         Pt.y = max(0, Pt.y + (infoPtr->nItemHeight >> 1) - (Pt.y + (infoPtr->nItemHeight >> 1)) % infoPtr->nItemHeight);
9114     }
9115 #endif
9116 
9117     return LISTVIEW_MoveIconTo(infoPtr, nItem, &Pt, FALSE);
9118 }
9119 
9120 /***
9121  * DESCRIPTION:
9122  * Sets the state of one or many items.
9123  *
9124  * PARAMETER(S):
9125  * [I] infoPtr : valid pointer to the listview structure
9126  * [I] nItem : item index
9127  * [I] item  : item or subitem info
9128  *
9129  * RETURN:
9130  *   SUCCESS : TRUE
9131  *   FAILURE : FALSE
9132  */
LISTVIEW_SetItemState(LISTVIEW_INFO * infoPtr,INT nItem,const LVITEMW * item)9133 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *item)
9134 {
9135     BOOL ret = TRUE;
9136     LVITEMW lvItem;
9137 
9138     if (!item) return FALSE;
9139 
9140     lvItem.iItem = nItem;
9141     lvItem.iSubItem = 0;
9142     lvItem.mask = LVIF_STATE;
9143     lvItem.state = item->state;
9144     lvItem.stateMask = item->stateMask;
9145     TRACE("item=%s\n", debuglvitem_t(&lvItem, TRUE));
9146 
9147     if (nItem == -1)
9148     {
9149         UINT oldstate = 0;
9150         DWORD old_mask;
9151 
9152         /* special case optimization for recurring attempt to deselect all */
9153         if (lvItem.state == 0 && lvItem.stateMask == LVIS_SELECTED && !LISTVIEW_GetSelectedCount(infoPtr))
9154             return TRUE;
9155 
9156 	/* select all isn't allowed in LVS_SINGLESEL */
9157 	if ((lvItem.state & lvItem.stateMask & LVIS_SELECTED) && (infoPtr->dwStyle & LVS_SINGLESEL))
9158 	    return FALSE;
9159 
9160 	/* focus all isn't allowed */
9161 	if (lvItem.state & lvItem.stateMask & LVIS_FOCUSED) return FALSE;
9162 
9163         old_mask = infoPtr->notify_mask & NOTIFY_MASK_ITEM_CHANGE;
9164         if (infoPtr->dwStyle & LVS_OWNERDATA)
9165         {
9166             infoPtr->notify_mask &= ~NOTIFY_MASK_ITEM_CHANGE;
9167             if (!(lvItem.state & LVIS_SELECTED) && LISTVIEW_GetSelectedCount(infoPtr))
9168                 oldstate |= LVIS_SELECTED;
9169             if (infoPtr->nFocusedItem != -1) oldstate |= LVIS_FOCUSED;
9170         }
9171 
9172     	/* apply to all items */
9173     	for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
9174 	    if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) ret = FALSE;
9175 
9176         if (infoPtr->dwStyle & LVS_OWNERDATA)
9177         {
9178             NMLISTVIEW nmlv;
9179 
9180             infoPtr->notify_mask |= old_mask;
9181 
9182             nmlv.iItem = -1;
9183             nmlv.iSubItem = 0;
9184             nmlv.uNewState = lvItem.state & lvItem.stateMask;
9185             nmlv.uOldState = oldstate & lvItem.stateMask;
9186             nmlv.uChanged = LVIF_STATE;
9187             nmlv.ptAction.x = nmlv.ptAction.y = 0;
9188             nmlv.lParam = 0;
9189 
9190             notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
9191         }
9192     }
9193     else
9194 	ret = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
9195 
9196     return ret;
9197 }
9198 
9199 /***
9200  * DESCRIPTION:
9201  * Sets the text of an item or subitem.
9202  *
9203  * PARAMETER(S):
9204  * [I] hwnd : window handle
9205  * [I] nItem : item index
9206  * [I] lpLVItem : item or subitem info
9207  * [I] isW : TRUE if input is Unicode
9208  *
9209  * RETURN:
9210  *   SUCCESS : TRUE
9211  *   FAILURE : FALSE
9212  */
LISTVIEW_SetItemTextT(LISTVIEW_INFO * infoPtr,INT nItem,const LVITEMW * lpLVItem,BOOL isW)9213 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem, BOOL isW)
9214 {
9215     LVITEMW lvItem;
9216 
9217     if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
9218     if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
9219 
9220     lvItem.iItem = nItem;
9221     lvItem.iSubItem = lpLVItem->iSubItem;
9222     lvItem.mask = LVIF_TEXT;
9223     lvItem.pszText = lpLVItem->pszText;
9224     lvItem.cchTextMax = lpLVItem->cchTextMax;
9225 
9226     TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
9227 
9228     return LISTVIEW_SetItemT(infoPtr, &lvItem, isW);
9229 }
9230 
9231 /***
9232  * DESCRIPTION:
9233  * Set item index that marks the start of a multiple selection.
9234  *
9235  * PARAMETER(S):
9236  * [I] infoPtr : valid pointer to the listview structure
9237  * [I] nIndex : index
9238  *
9239  * RETURN:
9240  * Index number or -1 if there is no selection mark.
9241  */
LISTVIEW_SetSelectionMark(LISTVIEW_INFO * infoPtr,INT nIndex)9242 static INT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
9243 {
9244   INT nOldIndex = infoPtr->nSelectionMark;
9245 
9246   TRACE("(nIndex=%d)\n", nIndex);
9247 
9248   if (nIndex >= -1 && nIndex < infoPtr->nItemCount)
9249     infoPtr->nSelectionMark = nIndex;
9250 
9251   return nOldIndex;
9252 }
9253 
9254 /***
9255  * DESCRIPTION:
9256  * Sets the text background color.
9257  *
9258  * PARAMETER(S):
9259  * [I] infoPtr : valid pointer to the listview structure
9260  * [I] color   : text background color
9261  *
9262  * RETURN:
9263  *   SUCCESS : TRUE
9264  *   FAILURE : FALSE
9265  */
LISTVIEW_SetTextBkColor(LISTVIEW_INFO * infoPtr,COLORREF color)9266 static BOOL LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF color)
9267 {
9268     TRACE("(color=%x)\n", color);
9269 
9270     infoPtr->clrTextBk = color;
9271     return TRUE;
9272 }
9273 
9274 /***
9275  * DESCRIPTION:
9276  * Sets the text foreground color.
9277  *
9278  * PARAMETER(S):
9279  * [I] infoPtr : valid pointer to the listview structure
9280  * [I] color   : text color
9281  *
9282  * RETURN:
9283  *   SUCCESS : TRUE
9284  *   FAILURE : FALSE
9285  */
LISTVIEW_SetTextColor(LISTVIEW_INFO * infoPtr,COLORREF color)9286 static BOOL LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF color)
9287 {
9288     TRACE("(color=%x)\n", color);
9289 
9290     infoPtr->clrText = color;
9291     return TRUE;
9292 }
9293 
9294 /***
9295  * DESCRIPTION:
9296  * Sets new ToolTip window to ListView control.
9297  *
9298  * PARAMETER(S):
9299  * [I] infoPtr        : valid pointer to the listview structure
9300  * [I] hwndNewToolTip : handle to new ToolTip
9301  *
9302  * RETURN:
9303  *   old tool tip
9304  */
LISTVIEW_SetToolTips(LISTVIEW_INFO * infoPtr,HWND hwndNewToolTip)9305 static HWND LISTVIEW_SetToolTips( LISTVIEW_INFO *infoPtr, HWND hwndNewToolTip)
9306 {
9307   HWND hwndOldToolTip = infoPtr->hwndToolTip;
9308   infoPtr->hwndToolTip = hwndNewToolTip;
9309   return hwndOldToolTip;
9310 }
9311 
9312 /*
9313  * DESCRIPTION:
9314  *   sets the Unicode character format flag for the control
9315  * PARAMETER(S):
9316  *    [I] infoPtr         :valid pointer to the listview structure
9317  *    [I] fUnicode        :true to switch to UNICODE false to switch to ANSI
9318  *
9319  * RETURN:
9320  *    Old Unicode Format
9321  */
LISTVIEW_SetUnicodeFormat(LISTVIEW_INFO * infoPtr,BOOL unicode)9322 static BOOL LISTVIEW_SetUnicodeFormat( LISTVIEW_INFO *infoPtr, BOOL unicode)
9323 {
9324   SHORT rc = infoPtr->notifyFormat;
9325   infoPtr->notifyFormat = (unicode) ? NFR_UNICODE : NFR_ANSI;
9326   return rc == NFR_UNICODE;
9327 }
9328 
9329 /*
9330  * DESCRIPTION:
9331  *   sets the control view mode
9332  * PARAMETER(S):
9333  *    [I] infoPtr         :valid pointer to the listview structure
9334  *    [I] nView           :new view mode value
9335  *
9336  * RETURN:
9337  *    SUCCESS:  1
9338  *    FAILURE: -1
9339  */
LISTVIEW_SetView(LISTVIEW_INFO * infoPtr,DWORD nView)9340 static INT LISTVIEW_SetView(LISTVIEW_INFO *infoPtr, DWORD nView)
9341 {
9342   HIMAGELIST himl;
9343 
9344   if (infoPtr->uView == nView) return 1;
9345 
9346   if ((INT)nView < 0 || nView > LV_VIEW_MAX) return -1;
9347   if (nView == LV_VIEW_TILE)
9348   {
9349       FIXME("View LV_VIEW_TILE unimplemented\n");
9350       return -1;
9351   }
9352 
9353   infoPtr->uView = nView;
9354 
9355   SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
9356   ShowWindow(infoPtr->hwndHeader, SW_HIDE);
9357 
9358   ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
9359   SetRectEmpty(&infoPtr->rcFocus);
9360 
9361   himl = (nView == LV_VIEW_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
9362   set_icon_size(&infoPtr->iconSize, himl, nView != LV_VIEW_ICON);
9363 
9364   switch (nView)
9365   {
9366   case LV_VIEW_ICON:
9367   case LV_VIEW_SMALLICON:
9368       LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9369       break;
9370   case LV_VIEW_DETAILS:
9371   {
9372       HDLAYOUT hl;
9373       WINDOWPOS wp;
9374 
9375       LISTVIEW_CreateHeader( infoPtr );
9376 
9377       hl.prc = &infoPtr->rcList;
9378       hl.pwpos = &wp;
9379       SendMessageW(infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl);
9380       SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy,
9381                    wp.flags | ((infoPtr->dwStyle & LVS_NOCOLUMNHEADER) ? SWP_HIDEWINDOW : SWP_SHOWWINDOW));
9382       break;
9383   }
9384   case LV_VIEW_LIST:
9385       break;
9386   }
9387 
9388   LISTVIEW_UpdateItemSize(infoPtr);
9389   LISTVIEW_UpdateSize(infoPtr);
9390   LISTVIEW_UpdateScroll(infoPtr);
9391   LISTVIEW_InvalidateList(infoPtr);
9392 
9393   TRACE("nView=%d\n", nView);
9394 
9395   return 1;
9396 }
9397 
9398 /* LISTVIEW_SetWorkAreas */
9399 
9400 /***
9401  * DESCRIPTION:
9402  * Callback internally used by LISTVIEW_SortItems() in response of LVM_SORTITEMS
9403  *
9404  * PARAMETER(S):
9405  * [I] first : pointer to first ITEM_INFO to compare
9406  * [I] second : pointer to second ITEM_INFO to compare
9407  * [I] lParam : HWND of control
9408  *
9409  * RETURN:
9410  *   if first comes before second : negative
9411  *   if first comes after second : positive
9412  *   if first and second are equivalent : zero
9413  */
LISTVIEW_CallBackCompare(LPVOID first,LPVOID second,LPARAM lParam)9414 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
9415 {
9416   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam;
9417   ITEM_INFO* lv_first = DPA_GetPtr( first, 0 );
9418   ITEM_INFO* lv_second = DPA_GetPtr( second, 0 );
9419 
9420   /* Forward the call to the client defined callback */
9421   return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
9422 }
9423 
9424 /***
9425  * DESCRIPTION:
9426  * Callback internally used by LISTVIEW_SortItems() in response of LVM_SORTITEMSEX
9427  *
9428  * PARAMETER(S):
9429  * [I] first : pointer to first ITEM_INFO to compare
9430  * [I] second : pointer to second ITEM_INFO to compare
9431  * [I] lParam : HWND of control
9432  *
9433  * RETURN:
9434  *   if first comes before second : negative
9435  *   if first comes after second : positive
9436  *   if first and second are equivalent : zero
9437  */
LISTVIEW_CallBackCompareEx(LPVOID first,LPVOID second,LPARAM lParam)9438 static INT WINAPI LISTVIEW_CallBackCompareEx(LPVOID first, LPVOID second, LPARAM lParam)
9439 {
9440   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam;
9441   INT first_idx  = DPA_GetPtrIndex( infoPtr->hdpaItems, first  );
9442   INT second_idx = DPA_GetPtrIndex( infoPtr->hdpaItems, second );
9443 
9444   /* Forward the call to the client defined callback */
9445   return (infoPtr->pfnCompare)( first_idx, second_idx, infoPtr->lParamSort );
9446 }
9447 
9448 /***
9449  * DESCRIPTION:
9450  * Sorts the listview items.
9451  *
9452  * PARAMETER(S):
9453  * [I] infoPtr : valid pointer to the listview structure
9454  * [I] pfnCompare : application-defined value
9455  * [I] lParamSort : pointer to comparison callback
9456  * [I] IsEx : TRUE when LVM_SORTITEMSEX used
9457  *
9458  * RETURN:
9459  *   SUCCESS : TRUE
9460  *   FAILURE : FALSE
9461  */
LISTVIEW_SortItems(LISTVIEW_INFO * infoPtr,PFNLVCOMPARE pfnCompare,LPARAM lParamSort,BOOL IsEx)9462 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare,
9463                                LPARAM lParamSort, BOOL IsEx)
9464 {
9465     HDPA hdpaSubItems;
9466     ITEM_INFO *lpItem;
9467     LPVOID selectionMarkItem = NULL;
9468     LPVOID focusedItem = NULL;
9469     int i;
9470 
9471     TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
9472 
9473     if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
9474 
9475     if (!pfnCompare) return FALSE;
9476     if (!infoPtr->hdpaItems) return FALSE;
9477 
9478     /* if there are 0 or 1 items, there is no need to sort */
9479     if (infoPtr->nItemCount < 2) return TRUE;
9480 
9481     /* clear selection */
9482     ranges_clear(infoPtr->selectionRanges);
9483 
9484     /* save selection mark and focused item */
9485     if (infoPtr->nSelectionMark >= 0)
9486         selectionMarkItem = DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark);
9487     if (infoPtr->nFocusedItem >= 0)
9488         focusedItem = DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
9489 
9490     infoPtr->pfnCompare = pfnCompare;
9491     infoPtr->lParamSort = lParamSort;
9492     if (IsEx)
9493         DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompareEx, (LPARAM)infoPtr);
9494     else
9495         DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr);
9496 
9497     /* restore selection ranges */
9498     for (i=0; i < infoPtr->nItemCount; i++)
9499     {
9500         hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, i);
9501         lpItem = DPA_GetPtr(hdpaSubItems, 0);
9502 
9503 	if (lpItem->state & LVIS_SELECTED)
9504 	    ranges_additem(infoPtr->selectionRanges, i);
9505     }
9506     /* restore selection mark and focused item */
9507     infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
9508     infoPtr->nFocusedItem   = DPA_GetPtrIndex(infoPtr->hdpaItems, focusedItem);
9509 
9510     /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
9511 
9512     /* refresh the display */
9513     LISTVIEW_InvalidateList(infoPtr);
9514     return TRUE;
9515 }
9516 
9517 /***
9518  * DESCRIPTION:
9519  * Update theme handle after a theme change.
9520  *
9521  * PARAMETER(S):
9522  * [I] infoPtr : valid pointer to the listview structure
9523  *
9524  * RETURN:
9525  *   SUCCESS : 0
9526  *   FAILURE : something else
9527  */
LISTVIEW_ThemeChanged(const LISTVIEW_INFO * infoPtr)9528 static LRESULT LISTVIEW_ThemeChanged(const LISTVIEW_INFO *infoPtr)
9529 {
9530     HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
9531     CloseThemeData(theme);
9532     OpenThemeData(infoPtr->hwndSelf, themeClass);
9533     return 0;
9534 }
9535 
9536 /***
9537  * DESCRIPTION:
9538  * Updates an items or rearranges the listview control.
9539  *
9540  * PARAMETER(S):
9541  * [I] infoPtr : valid pointer to the listview structure
9542  * [I] nItem : item index
9543  *
9544  * RETURN:
9545  *   SUCCESS : TRUE
9546  *   FAILURE : FALSE
9547  */
LISTVIEW_Update(LISTVIEW_INFO * infoPtr,INT nItem)9548 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
9549 {
9550     TRACE("(nItem=%d)\n", nItem);
9551 
9552     if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
9553 
9554     /* rearrange with default alignment style */
9555     if (is_autoarrange(infoPtr))
9556 	LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9557     else
9558 	LISTVIEW_InvalidateItem(infoPtr, nItem);
9559 
9560     return TRUE;
9561 }
9562 
9563 /***
9564  * DESCRIPTION:
9565  * Draw the track line at the place defined in the infoPtr structure.
9566  * The line is drawn with a XOR pen so drawing the line for the second time
9567  * in the same place erases the line.
9568  *
9569  * PARAMETER(S):
9570  * [I] infoPtr : valid pointer to the listview structure
9571  *
9572  * RETURN:
9573  *   SUCCESS : TRUE
9574  *   FAILURE : FALSE
9575  */
LISTVIEW_DrawTrackLine(const LISTVIEW_INFO * infoPtr)9576 static BOOL LISTVIEW_DrawTrackLine(const LISTVIEW_INFO *infoPtr)
9577 {
9578     HDC hdc;
9579 
9580     if (infoPtr->xTrackLine == -1)
9581         return FALSE;
9582 
9583     if (!(hdc = GetDC(infoPtr->hwndSelf)))
9584         return FALSE;
9585     PatBlt( hdc, infoPtr->xTrackLine, infoPtr->rcList.top,
9586             1, infoPtr->rcList.bottom - infoPtr->rcList.top, DSTINVERT );
9587     ReleaseDC(infoPtr->hwndSelf, hdc);
9588     return TRUE;
9589 }
9590 
9591 /***
9592  * DESCRIPTION:
9593  * Called when an edit control should be displayed. This function is called after
9594  * we are sure that there was a single click - not a double click (this is a TIMERPROC).
9595  *
9596  * PARAMETER(S):
9597  * [I] hwnd : Handle to the listview
9598  * [I] uMsg : WM_TIMER (ignored)
9599  * [I] idEvent : The timer ID interpreted as a pointer to a DELAYED_EDIT_ITEM struct
9600  * [I] dwTimer : The elapsed time (ignored)
9601  *
9602  * RETURN:
9603  *   None.
9604  */
LISTVIEW_DelayedEditItem(HWND hwnd,UINT uMsg,UINT_PTR idEvent,DWORD dwTime)9605 static VOID CALLBACK LISTVIEW_DelayedEditItem(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
9606 {
9607     DELAYED_ITEM_EDIT *editItem = (DELAYED_ITEM_EDIT *)idEvent;
9608     LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
9609 
9610     KillTimer(hwnd, idEvent);
9611     editItem->fEnabled = FALSE;
9612     /* check if the item is still selected */
9613     if (infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, editItem->iItem, LVIS_SELECTED))
9614         LISTVIEW_EditLabelT(infoPtr, editItem->iItem, TRUE);
9615 }
9616 
9617 /***
9618  * DESCRIPTION:
9619  * Creates the listview control - the WM_NCCREATE phase.
9620  *
9621  * PARAMETER(S):
9622  * [I] hwnd : window handle
9623  * [I] lpcs : the create parameters
9624  *
9625  * RETURN:
9626  *   Success: TRUE
9627  *   Failure: FALSE
9628  */
LISTVIEW_NCCreate(HWND hwnd,WPARAM wParam,const CREATESTRUCTW * lpcs)9629 static LRESULT LISTVIEW_NCCreate(HWND hwnd, WPARAM wParam, const CREATESTRUCTW *lpcs)
9630 {
9631   LISTVIEW_INFO *infoPtr;
9632   LOGFONTW logFont;
9633 
9634   TRACE("(lpcs=%p)\n", lpcs);
9635 
9636   /* initialize info pointer */
9637   infoPtr = Alloc(sizeof(LISTVIEW_INFO));
9638   if (!infoPtr) return FALSE;
9639 
9640   SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr);
9641 
9642   infoPtr->hwndSelf = hwnd;
9643   infoPtr->dwStyle = lpcs->style;    /* Note: may be changed in WM_CREATE */
9644   map_style_view(infoPtr);
9645   /* determine the type of structures to use */
9646   infoPtr->hwndNotify = lpcs->hwndParent;
9647   /* infoPtr->notifyFormat will be filled in WM_CREATE */
9648 
9649   /* initialize color information  */
9650   infoPtr->clrBk = CLR_NONE;
9651   infoPtr->clrText = CLR_DEFAULT;
9652   infoPtr->clrTextBk = CLR_DEFAULT;
9653   LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
9654 #ifdef __REACTOS__
9655   infoPtr->bDefaultBkColor = TRUE;
9656 #endif
9657 
9658   /* set default values */
9659   infoPtr->nFocusedItem = -1;
9660   infoPtr->nSelectionMark = -1;
9661   infoPtr->nHotItem = -1;
9662   infoPtr->redraw = TRUE;
9663   infoPtr->bNoItemMetrics = TRUE;
9664   infoPtr->notify_mask = NOTIFY_MASK_UNMASK_ALL;
9665   infoPtr->autoSpacing = TRUE;
9666   infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING) - GetSystemMetrics(SM_CXICON);
9667   infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING) - GetSystemMetrics(SM_CYICON);
9668   infoPtr->nEditLabelItem = -1;
9669   infoPtr->nLButtonDownItem = -1;
9670   infoPtr->dwHoverTime = HOVER_DEFAULT; /* default system hover time */
9671   infoPtr->cWheelRemainder = 0;
9672   infoPtr->nMeasureItemHeight = 0;
9673   infoPtr->xTrackLine = -1;  /* no track line */
9674   infoPtr->itemEdit.fEnabled = FALSE;
9675   infoPtr->iVersion = COMCTL32_VERSION;
9676   infoPtr->colRectsDirty = FALSE;
9677 
9678   /* get default font (icon title) */
9679   SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
9680   infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
9681   infoPtr->hFont = infoPtr->hDefaultFont;
9682   LISTVIEW_SaveTextMetrics(infoPtr);
9683 
9684   /* allocate memory for the data structure */
9685   if (!(infoPtr->selectionRanges = ranges_create(10))) goto fail;
9686   if (!(infoPtr->hdpaItems = DPA_Create(10))) goto fail;
9687   if (!(infoPtr->hdpaItemIds = DPA_Create(10))) goto fail;
9688   if (!(infoPtr->hdpaPosX  = DPA_Create(10))) goto fail;
9689   if (!(infoPtr->hdpaPosY  = DPA_Create(10))) goto fail;
9690   if (!(infoPtr->hdpaColumns = DPA_Create(10))) goto fail;
9691 
9692   return DefWindowProcW(hwnd, WM_NCCREATE, wParam, (LPARAM)lpcs);
9693 
9694 fail:
9695     DestroyWindow(infoPtr->hwndHeader);
9696     ranges_destroy(infoPtr->selectionRanges);
9697     DPA_Destroy(infoPtr->hdpaItems);
9698     DPA_Destroy(infoPtr->hdpaItemIds);
9699     DPA_Destroy(infoPtr->hdpaPosX);
9700     DPA_Destroy(infoPtr->hdpaPosY);
9701     DPA_Destroy(infoPtr->hdpaColumns);
9702     Free(infoPtr);
9703     return FALSE;
9704 }
9705 
9706 /***
9707  * DESCRIPTION:
9708  * Creates the listview control - the WM_CREATE phase. Most of the data is
9709  * already set up in LISTVIEW_NCCreate
9710  *
9711  * PARAMETER(S):
9712  * [I] hwnd : window handle
9713  * [I] lpcs : the create parameters
9714  *
9715  * RETURN:
9716  *   Success: 0
9717  *   Failure: -1
9718  */
LISTVIEW_Create(HWND hwnd,const CREATESTRUCTW * lpcs)9719 static LRESULT LISTVIEW_Create(HWND hwnd, const CREATESTRUCTW *lpcs)
9720 {
9721   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
9722 
9723   TRACE("(lpcs=%p, style=0x%08x)\n", lpcs, lpcs->style);
9724 
9725   infoPtr->dwStyle = lpcs->style;
9726   map_style_view(infoPtr);
9727 
9728   infoPtr->notifyFormat = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT,
9729                                        (WPARAM)infoPtr->hwndSelf, NF_QUERY);
9730   /* on error defaulting to ANSI notifications */
9731   if (infoPtr->notifyFormat == 0) infoPtr->notifyFormat = NFR_ANSI;
9732   TRACE("notify format=%d\n", infoPtr->notifyFormat);
9733 
9734   if ((infoPtr->uView == LV_VIEW_DETAILS) && (lpcs->style & WS_VISIBLE))
9735   {
9736     if (LISTVIEW_CreateHeader(infoPtr) < 0)  return -1;
9737   }
9738   else
9739     infoPtr->hwndHeader = 0;
9740 
9741   /* init item size to avoid division by 0 */
9742   LISTVIEW_UpdateItemSize (infoPtr);
9743   LISTVIEW_UpdateSize (infoPtr);
9744 
9745   if (infoPtr->uView == LV_VIEW_DETAILS)
9746   {
9747     if (!(LVS_NOCOLUMNHEADER & lpcs->style) && (WS_VISIBLE & lpcs->style))
9748     {
9749       ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
9750     }
9751     LISTVIEW_UpdateScroll(infoPtr);
9752     /* send WM_MEASUREITEM notification */
9753     if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED) notify_measureitem(infoPtr);
9754   }
9755 
9756   OpenThemeData(hwnd, themeClass);
9757 
9758   /* initialize the icon sizes */
9759   set_icon_size(&infoPtr->iconSize, infoPtr->himlNormal, infoPtr->uView != LV_VIEW_ICON);
9760   set_icon_size(&infoPtr->iconStateSize, infoPtr->himlState, TRUE);
9761   return 0;
9762 }
9763 
9764 /***
9765  * DESCRIPTION:
9766  * Destroys the listview control.
9767  *
9768  * PARAMETER(S):
9769  * [I] infoPtr : valid pointer to the listview structure
9770  *
9771  * RETURN:
9772  *   Success: 0
9773  *   Failure: -1
9774  */
LISTVIEW_Destroy(LISTVIEW_INFO * infoPtr)9775 static LRESULT LISTVIEW_Destroy(LISTVIEW_INFO *infoPtr)
9776 {
9777     HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
9778     CloseThemeData(theme);
9779 
9780     /* delete all items */
9781     LISTVIEW_DeleteAllItems(infoPtr, TRUE);
9782 
9783     return 0;
9784 }
9785 
9786 /***
9787  * DESCRIPTION:
9788  * Enables the listview control.
9789  *
9790  * PARAMETER(S):
9791  * [I] infoPtr : valid pointer to the listview structure
9792  * [I] bEnable : specifies whether to enable or disable the window
9793  *
9794  * RETURN:
9795  *   SUCCESS : TRUE
9796  *   FAILURE : FALSE
9797  */
LISTVIEW_Enable(const LISTVIEW_INFO * infoPtr)9798 static BOOL LISTVIEW_Enable(const LISTVIEW_INFO *infoPtr)
9799 {
9800     if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED)
9801         InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
9802     return TRUE;
9803 }
9804 
9805 /***
9806  * DESCRIPTION:
9807  * Erases the background of the listview control.
9808  *
9809  * PARAMETER(S):
9810  * [I] infoPtr : valid pointer to the listview structure
9811  * [I] hdc : device context handle
9812  *
9813  * RETURN:
9814  *   SUCCESS : TRUE
9815  *   FAILURE : FALSE
9816  */
LISTVIEW_EraseBkgnd(const LISTVIEW_INFO * infoPtr,HDC hdc)9817 static inline BOOL LISTVIEW_EraseBkgnd(const LISTVIEW_INFO *infoPtr, HDC hdc)
9818 {
9819     RECT rc;
9820 
9821     TRACE("(hdc=%p)\n", hdc);
9822 
9823     if (!GetClipBox(hdc, &rc)) return FALSE;
9824 
9825     if (infoPtr->clrBk == CLR_NONE)
9826     {
9827         if (infoPtr->dwLvExStyle & LVS_EX_TRANSPARENTBKGND)
9828             return SendMessageW(infoPtr->hwndNotify, WM_PRINTCLIENT,
9829                                 (WPARAM)hdc, PRF_ERASEBKGND);
9830         else
9831             return SendMessageW(infoPtr->hwndNotify, WM_ERASEBKGND, (WPARAM)hdc, 0);
9832     }
9833 
9834     /* for double buffered controls we need to do this during refresh */
9835     if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) return FALSE;
9836 
9837     return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
9838 }
9839 
9840 
9841 /***
9842  * DESCRIPTION:
9843  * Helper function for LISTVIEW_[HV]Scroll *only*.
9844  * Performs vertical/horizontal scrolling by a give amount.
9845  *
9846  * PARAMETER(S):
9847  * [I] infoPtr : valid pointer to the listview structure
9848  * [I] dx : amount of horizontal scroll
9849  * [I] dy : amount of vertical scroll
9850  */
scroll_list(LISTVIEW_INFO * infoPtr,INT dx,INT dy)9851 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
9852 {
9853     /* now we can scroll the list */
9854     ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList,
9855 		   &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
9856     /* if we have focus, adjust rect */
9857     OffsetRect(&infoPtr->rcFocus, dx, dy);
9858     UpdateWindow(infoPtr->hwndSelf);
9859 }
9860 
9861 /***
9862  * DESCRIPTION:
9863  * Performs vertical scrolling.
9864  *
9865  * PARAMETER(S):
9866  * [I] infoPtr : valid pointer to the listview structure
9867  * [I] nScrollCode : scroll code
9868  * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
9869  * [I] hScrollWnd  : scrollbar control window handle
9870  *
9871  * RETURN:
9872  * Zero
9873  *
9874  * NOTES:
9875  *   SB_LINEUP/SB_LINEDOWN:
9876  *        for LVS_ICON, LVS_SMALLICON is 37 by experiment
9877  *        for LVS_REPORT is 1 line
9878  *        for LVS_LIST cannot occur
9879  *
9880  */
LISTVIEW_VScroll(LISTVIEW_INFO * infoPtr,INT nScrollCode,INT nScrollDiff)9881 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
9882 				INT nScrollDiff)
9883 {
9884     INT nOldScrollPos, nNewScrollPos;
9885     SCROLLINFO scrollInfo;
9886     BOOL is_an_icon;
9887 
9888     TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
9889 	debugscrollcode(nScrollCode), nScrollDiff);
9890 
9891     if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
9892 
9893     scrollInfo.cbSize = sizeof(SCROLLINFO);
9894     scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
9895 
9896     is_an_icon = ((infoPtr->uView == LV_VIEW_ICON) || (infoPtr->uView == LV_VIEW_SMALLICON));
9897 
9898     if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
9899 
9900     nOldScrollPos = scrollInfo.nPos;
9901     switch (nScrollCode)
9902     {
9903     case SB_INTERNAL:
9904         break;
9905 
9906     case SB_LINEUP:
9907 	nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
9908         break;
9909 
9910     case SB_LINEDOWN:
9911 	nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
9912         break;
9913 
9914     case SB_PAGEUP:
9915 	nScrollDiff = -scrollInfo.nPage;
9916         break;
9917 
9918     case SB_PAGEDOWN:
9919 	nScrollDiff = scrollInfo.nPage;
9920         break;
9921 
9922     case SB_THUMBPOSITION:
9923     case SB_THUMBTRACK:
9924 	nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
9925         break;
9926 
9927     default:
9928 	nScrollDiff = 0;
9929     }
9930 
9931     /* quit right away if pos isn't changing */
9932     if (nScrollDiff == 0) return 0;
9933 
9934     /* calculate new position, and handle overflows */
9935     nNewScrollPos = scrollInfo.nPos + nScrollDiff;
9936     if (nScrollDiff > 0) {
9937 	if (nNewScrollPos < nOldScrollPos ||
9938 	    nNewScrollPos > scrollInfo.nMax)
9939 	    nNewScrollPos = scrollInfo.nMax;
9940     } else {
9941 	if (nNewScrollPos > nOldScrollPos ||
9942 	    nNewScrollPos < scrollInfo.nMin)
9943 	    nNewScrollPos = scrollInfo.nMin;
9944     }
9945 
9946     /* set the new position, and reread in case it changed */
9947     scrollInfo.fMask = SIF_POS;
9948     scrollInfo.nPos = nNewScrollPos;
9949     nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
9950 
9951     /* carry on only if it really changed */
9952     if (nNewScrollPos == nOldScrollPos) return 0;
9953 
9954     /* now adjust to client coordinates */
9955     nScrollDiff = nOldScrollPos - nNewScrollPos;
9956     if (infoPtr->uView == LV_VIEW_DETAILS) nScrollDiff *= infoPtr->nItemHeight;
9957 
9958     /* and scroll the window */
9959     scroll_list(infoPtr, 0, nScrollDiff);
9960 
9961     return 0;
9962 }
9963 
9964 /***
9965  * DESCRIPTION:
9966  * Performs horizontal scrolling.
9967  *
9968  * PARAMETER(S):
9969  * [I] infoPtr : valid pointer to the listview structure
9970  * [I] nScrollCode : scroll code
9971  * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
9972  * [I] hScrollWnd  : scrollbar control window handle
9973  *
9974  * RETURN:
9975  * Zero
9976  *
9977  * NOTES:
9978  *   SB_LINELEFT/SB_LINERIGHT:
9979  *        for LVS_ICON, LVS_SMALLICON  1 pixel
9980  *        for LVS_REPORT is 1 pixel
9981  *        for LVS_LIST  is 1 column --> which is a 1 because the
9982  *                                      scroll is based on columns not pixels
9983  *
9984  */
LISTVIEW_HScroll(LISTVIEW_INFO * infoPtr,INT nScrollCode,INT nScrollDiff)9985 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
9986                                 INT nScrollDiff)
9987 {
9988     INT nOldScrollPos, nNewScrollPos;
9989     SCROLLINFO scrollInfo;
9990     BOOL is_an_icon;
9991 
9992     TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
9993 	debugscrollcode(nScrollCode), nScrollDiff);
9994 
9995     if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
9996 
9997     scrollInfo.cbSize = sizeof(SCROLLINFO);
9998     scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
9999 
10000     is_an_icon = ((infoPtr->uView == LV_VIEW_ICON) || (infoPtr->uView == LV_VIEW_SMALLICON));
10001 
10002     if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
10003 
10004     nOldScrollPos = scrollInfo.nPos;
10005 
10006     switch (nScrollCode)
10007     {
10008     case SB_INTERNAL:
10009         break;
10010 
10011     case SB_LINELEFT:
10012 	nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
10013         break;
10014 
10015     case SB_LINERIGHT:
10016 	nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
10017         break;
10018 
10019     case SB_PAGELEFT:
10020 	nScrollDiff = -scrollInfo.nPage;
10021         break;
10022 
10023     case SB_PAGERIGHT:
10024 	nScrollDiff = scrollInfo.nPage;
10025         break;
10026 
10027     case SB_THUMBPOSITION:
10028     case SB_THUMBTRACK:
10029 	nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
10030 	break;
10031 
10032     default:
10033 	nScrollDiff = 0;
10034     }
10035 
10036     /* quit right away if pos isn't changing */
10037     if (nScrollDiff == 0) return 0;
10038 
10039     /* calculate new position, and handle overflows */
10040     nNewScrollPos = scrollInfo.nPos + nScrollDiff;
10041     if (nScrollDiff > 0) {
10042 	if (nNewScrollPos < nOldScrollPos ||
10043 	    nNewScrollPos > scrollInfo.nMax)
10044 	    nNewScrollPos = scrollInfo.nMax;
10045     } else {
10046 	if (nNewScrollPos > nOldScrollPos ||
10047 	    nNewScrollPos < scrollInfo.nMin)
10048 	    nNewScrollPos = scrollInfo.nMin;
10049     }
10050 
10051     /* set the new position, and reread in case it changed */
10052     scrollInfo.fMask = SIF_POS;
10053     scrollInfo.nPos = nNewScrollPos;
10054     nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
10055 
10056     /* carry on only if it really changed */
10057     if (nNewScrollPos == nOldScrollPos) return 0;
10058 
10059     LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
10060 
10061     /* now adjust to client coordinates */
10062     nScrollDiff = nOldScrollPos - nNewScrollPos;
10063     if (infoPtr->uView == LV_VIEW_LIST) nScrollDiff *= infoPtr->nItemWidth;
10064 
10065     /* and scroll the window */
10066     scroll_list(infoPtr, nScrollDiff, 0);
10067 
10068     return 0;
10069 }
10070 
LISTVIEW_MouseWheel(LISTVIEW_INFO * infoPtr,INT wheelDelta)10071 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
10072 {
10073     INT pulScrollLines = 3;
10074 
10075     TRACE("(wheelDelta=%d)\n", wheelDelta);
10076 
10077     switch(infoPtr->uView)
10078     {
10079     case LV_VIEW_ICON:
10080     case LV_VIEW_SMALLICON:
10081        /*
10082         *  listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
10083         *  should be fixed in the future.
10084         */
10085         LISTVIEW_VScroll(infoPtr, SB_INTERNAL, (wheelDelta > 0) ?
10086                 -LISTVIEW_SCROLL_ICON_LINE_SIZE : LISTVIEW_SCROLL_ICON_LINE_SIZE);
10087         break;
10088 
10089     case LV_VIEW_DETAILS:
10090         SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
10091 
10092         /* if scrolling changes direction, ignore left overs */
10093         if ((wheelDelta < 0 && infoPtr->cWheelRemainder < 0) ||
10094             (wheelDelta > 0 && infoPtr->cWheelRemainder > 0))
10095             infoPtr->cWheelRemainder += wheelDelta;
10096         else
10097             infoPtr->cWheelRemainder = wheelDelta;
10098         if (infoPtr->cWheelRemainder && pulScrollLines)
10099         {
10100             int cLineScroll;
10101             pulScrollLines = min((UINT)LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
10102             cLineScroll = pulScrollLines * infoPtr->cWheelRemainder / WHEEL_DELTA;
10103             infoPtr->cWheelRemainder -= WHEEL_DELTA * cLineScroll / pulScrollLines;
10104             LISTVIEW_VScroll(infoPtr, SB_INTERNAL, -cLineScroll);
10105         }
10106         break;
10107 
10108     case LV_VIEW_LIST:
10109         LISTVIEW_HScroll(infoPtr, (wheelDelta > 0) ? SB_LINELEFT : SB_LINERIGHT, 0);
10110         break;
10111     }
10112     return 0;
10113 }
10114 
10115 /***
10116  * DESCRIPTION:
10117  * ???
10118  *
10119  * PARAMETER(S):
10120  * [I] infoPtr : valid pointer to the listview structure
10121  * [I] nVirtualKey : virtual key
10122  * [I] lKeyData : key data
10123  *
10124  * RETURN:
10125  * Zero
10126  */
LISTVIEW_KeyDown(LISTVIEW_INFO * infoPtr,INT nVirtualKey,LONG lKeyData)10127 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
10128 {
10129   HWND hwndSelf = infoPtr->hwndSelf;
10130   INT nItem = -1;
10131   NMLVKEYDOWN nmKeyDown;
10132 
10133   TRACE("(nVirtualKey=%d, lKeyData=%d)\n", nVirtualKey, lKeyData);
10134 
10135   /* send LVN_KEYDOWN notification */
10136   nmKeyDown.wVKey = nVirtualKey;
10137   nmKeyDown.flags = 0;
10138   notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
10139   if (!IsWindow(hwndSelf))
10140     return 0;
10141 
10142   switch (nVirtualKey)
10143   {
10144   case VK_SPACE:
10145     nItem = infoPtr->nFocusedItem;
10146     if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
10147         toggle_checkbox_state(infoPtr, infoPtr->nFocusedItem);
10148     break;
10149 
10150   case VK_RETURN:
10151     if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
10152     {
10153         if (!notify(infoPtr, NM_RETURN)) return 0;
10154         if (!notify(infoPtr, LVN_ITEMACTIVATE)) return 0;
10155     }
10156     break;
10157 
10158   case VK_HOME:
10159     if (infoPtr->nItemCount > 0)
10160       nItem = 0;
10161     break;
10162 
10163   case VK_END:
10164     if (infoPtr->nItemCount > 0)
10165       nItem = infoPtr->nItemCount - 1;
10166     break;
10167 
10168   case VK_LEFT:
10169     nItem = LISTVIEW_GetNextItem(infoPtr, infoPtr->nFocusedItem, LVNI_TOLEFT);
10170     break;
10171 
10172   case VK_UP:
10173     nItem = LISTVIEW_GetNextItem(infoPtr, infoPtr->nFocusedItem, LVNI_ABOVE);
10174     break;
10175 
10176   case VK_RIGHT:
10177     nItem = LISTVIEW_GetNextItem(infoPtr, infoPtr->nFocusedItem, LVNI_TORIGHT);
10178     break;
10179 
10180   case VK_DOWN:
10181     nItem = LISTVIEW_GetNextItem(infoPtr, infoPtr->nFocusedItem, LVNI_BELOW);
10182     break;
10183 
10184   case VK_PRIOR:
10185     if (infoPtr->uView == LV_VIEW_DETAILS)
10186     {
10187       INT topidx = LISTVIEW_GetTopIndex(infoPtr);
10188       if (infoPtr->nFocusedItem == topidx)
10189         nItem = topidx - LISTVIEW_GetCountPerColumn(infoPtr) + 1;
10190       else
10191         nItem = topidx;
10192     }
10193     else
10194       nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
10195                                     * LISTVIEW_GetCountPerRow(infoPtr);
10196     if(nItem < 0) nItem = 0;
10197     break;
10198 
10199   case VK_NEXT:
10200     if (infoPtr->uView == LV_VIEW_DETAILS)
10201     {
10202       INT topidx = LISTVIEW_GetTopIndex(infoPtr);
10203       INT cnt = LISTVIEW_GetCountPerColumn(infoPtr);
10204       if (infoPtr->nFocusedItem == topidx + cnt - 1)
10205         nItem = infoPtr->nFocusedItem + cnt - 1;
10206       else
10207         nItem = topidx + cnt - 1;
10208     }
10209     else
10210       nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
10211                                     * LISTVIEW_GetCountPerRow(infoPtr);
10212     if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
10213     break;
10214   }
10215 
10216   if ((nItem != -1) && (nItem != infoPtr->nFocusedItem || nVirtualKey == VK_SPACE))
10217       LISTVIEW_KeySelection(infoPtr, nItem, nVirtualKey == VK_SPACE);
10218 
10219   return 0;
10220 }
10221 
10222 /***
10223  * DESCRIPTION:
10224  * Kills the focus.
10225  *
10226  * PARAMETER(S):
10227  * [I] infoPtr : valid pointer to the listview structure
10228  *
10229  * RETURN:
10230  * Zero
10231  */
LISTVIEW_KillFocus(LISTVIEW_INFO * infoPtr)10232 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
10233 {
10234     TRACE("()\n");
10235 
10236     /* drop any left over scroll amount */
10237     infoPtr->cWheelRemainder = 0;
10238 
10239     /* if we did not have the focus, there's nothing more to do */
10240     if (!infoPtr->bFocus) return 0;
10241 
10242     /* send NM_KILLFOCUS notification */
10243     if (!notify(infoPtr, NM_KILLFOCUS)) return 0;
10244 
10245     /* if we have a focus rectangle, get rid of it */
10246     LISTVIEW_ShowFocusRect(infoPtr, FALSE);
10247 
10248     /* if have a marquee selection, stop it */
10249     if (infoPtr->bMarqueeSelect)
10250     {
10251         /* Remove the marquee rectangle and release our mouse capture */
10252         LISTVIEW_InvalidateRect(infoPtr, &infoPtr->marqueeRect);
10253         ReleaseCapture();
10254 
10255         SetRectEmpty(&infoPtr->marqueeRect);
10256 
10257         infoPtr->bMarqueeSelect = FALSE;
10258         infoPtr->bScrolling = FALSE;
10259         KillTimer(infoPtr->hwndSelf, (UINT_PTR) infoPtr);
10260     }
10261 
10262     /* set window focus flag */
10263     infoPtr->bFocus = FALSE;
10264 
10265     /* invalidate the selected items before resetting focus flag */
10266     LISTVIEW_InvalidateSelectedItems(infoPtr);
10267 
10268     return 0;
10269 }
10270 
10271 /***
10272  * DESCRIPTION:
10273  * Processes double click messages (left mouse button).
10274  *
10275  * PARAMETER(S):
10276  * [I] infoPtr : valid pointer to the listview structure
10277  * [I] wKey : key flag
10278  * [I] x,y : mouse coordinate
10279  *
10280  * RETURN:
10281  * Zero
10282  */
LISTVIEW_LButtonDblClk(LISTVIEW_INFO * infoPtr,WORD wKey,INT x,INT y)10283 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
10284 {
10285     LVHITTESTINFO htInfo;
10286 
10287     TRACE("(key=%hu, X=%u, Y=%u)\n", wKey, x, y);
10288 
10289     /* Cancel the item edition if any */
10290     if (infoPtr->itemEdit.fEnabled)
10291     {
10292       KillTimer(infoPtr->hwndSelf, (UINT_PTR)&infoPtr->itemEdit);
10293       infoPtr->itemEdit.fEnabled = FALSE;
10294     }
10295 
10296     /* send NM_RELEASEDCAPTURE notification */
10297     if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
10298 
10299     htInfo.pt.x = x;
10300     htInfo.pt.y = y;
10301 
10302     /* send NM_DBLCLK notification */
10303     LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
10304     if (!notify_click(infoPtr, NM_DBLCLK, &htInfo)) return 0;
10305 
10306     /* To send the LVN_ITEMACTIVATE, it must be on an Item */
10307     if(htInfo.iItem != -1) notify_itemactivate(infoPtr,&htInfo);
10308 
10309     return 0;
10310 }
10311 
LISTVIEW_TrackMouse(const LISTVIEW_INFO * infoPtr,POINT pt)10312 static LRESULT LISTVIEW_TrackMouse(const LISTVIEW_INFO *infoPtr, POINT pt)
10313 {
10314     MSG msg;
10315     RECT r;
10316 
10317     r.top = r.bottom = pt.y;
10318     r.left = r.right = pt.x;
10319 
10320     InflateRect(&r, GetSystemMetrics(SM_CXDRAG), GetSystemMetrics(SM_CYDRAG));
10321 
10322     SetCapture(infoPtr->hwndSelf);
10323 
10324     while (1)
10325     {
10326 	if (PeekMessageW(&msg, 0, 0, 0, PM_REMOVE | PM_NOYIELD))
10327 	{
10328 	    if (msg.message == WM_MOUSEMOVE)
10329 	    {
10330 		pt.x = (short)LOWORD(msg.lParam);
10331 		pt.y = (short)HIWORD(msg.lParam);
10332 		if (PtInRect(&r, pt))
10333 		    continue;
10334 		else
10335 		{
10336 		    ReleaseCapture();
10337 		    return 1;
10338 		}
10339 	    }
10340 	    else if (msg.message >= WM_LBUTTONDOWN &&
10341 		     msg.message <= WM_RBUTTONDBLCLK)
10342 	    {
10343 		break;
10344 	    }
10345 
10346 	    DispatchMessageW(&msg);
10347 	}
10348 
10349 	if (GetCapture() != infoPtr->hwndSelf)
10350 	    return 0;
10351     }
10352 
10353     ReleaseCapture();
10354     return 0;
10355 }
10356 
10357 
10358 /***
10359  * DESCRIPTION:
10360  * Processes mouse down messages (left mouse button).
10361  *
10362  * PARAMETERS:
10363  *   infoPtr  [I ] valid pointer to the listview structure
10364  *   wKey     [I ] key flag
10365  *   x,y      [I ] mouse coordinate
10366  *
10367  * RETURN:
10368  *   Zero
10369  */
LISTVIEW_LButtonDown(LISTVIEW_INFO * infoPtr,WORD wKey,INT x,INT y)10370 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
10371 {
10372   LVHITTESTINFO lvHitTestInfo;
10373   static BOOL bGroupSelect = TRUE;
10374   POINT pt = { x, y };
10375   INT nItem;
10376 
10377   TRACE("(key=%hu, X=%u, Y=%u)\n", wKey, x, y);
10378 
10379   /* send NM_RELEASEDCAPTURE notification */
10380   if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
10381 
10382   /* set left button down flag and record the click position */
10383   infoPtr->bLButtonDown = TRUE;
10384   infoPtr->ptClickPos = pt;
10385   infoPtr->bDragging = FALSE;
10386   infoPtr->bMarqueeSelect = FALSE;
10387   infoPtr->bScrolling = FALSE;
10388 
10389   lvHitTestInfo.pt.x = x;
10390   lvHitTestInfo.pt.y = y;
10391 
10392   nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
10393   TRACE("at %s, nItem=%d\n", wine_dbgstr_point(&pt), nItem);
10394   if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
10395   {
10396     if ((infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) && (lvHitTestInfo.flags & LVHT_ONITEMSTATEICON))
10397     {
10398         notify_click(infoPtr, NM_CLICK, &lvHitTestInfo);
10399         toggle_checkbox_state(infoPtr, nItem);
10400         infoPtr->bLButtonDown = FALSE;
10401         return 0;
10402     }
10403 
10404     if (infoPtr->dwStyle & LVS_SINGLESEL)
10405     {
10406       if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
10407         infoPtr->nEditLabelItem = nItem;
10408       else
10409         LISTVIEW_SetSelection(infoPtr, nItem);
10410     }
10411     else
10412     {
10413       if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
10414       {
10415         if (bGroupSelect)
10416 	{
10417           if (!LISTVIEW_AddGroupSelection(infoPtr, nItem)) return 0;
10418     	  LISTVIEW_SetItemFocus(infoPtr, nItem);
10419           infoPtr->nSelectionMark = nItem;
10420 	}
10421         else
10422 	{
10423           LVITEMW item;
10424 
10425 	  item.state = LVIS_SELECTED | LVIS_FOCUSED;
10426 	  item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
10427 
10428 	  LISTVIEW_SetItemState(infoPtr,nItem,&item);
10429 	  infoPtr->nSelectionMark = nItem;
10430 	}
10431       }
10432       else if (wKey & MK_CONTROL)
10433       {
10434         LVITEMW item;
10435 
10436 	bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
10437 
10438 	item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
10439         item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
10440 	LISTVIEW_SetItemState(infoPtr, nItem, &item);
10441         infoPtr->nSelectionMark = nItem;
10442       }
10443       else  if (wKey & MK_SHIFT)
10444       {
10445         LISTVIEW_SetGroupSelection(infoPtr, nItem);
10446       }
10447       else
10448       {
10449 	if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
10450 	{
10451 	  infoPtr->nEditLabelItem = nItem;
10452 	  infoPtr->nLButtonDownItem = nItem;
10453 
10454           LISTVIEW_SetItemFocus(infoPtr, nItem);
10455 	}
10456 	else
10457 	  /* set selection (clears other pre-existing selections) */
10458 	  LISTVIEW_SetSelection(infoPtr, nItem);
10459       }
10460     }
10461 
10462     if (!infoPtr->bFocus)
10463         SetFocus(infoPtr->hwndSelf);
10464 
10465     if (infoPtr->dwLvExStyle & LVS_EX_ONECLICKACTIVATE)
10466         if(lvHitTestInfo.iItem != -1) notify_itemactivate(infoPtr,&lvHitTestInfo);
10467   }
10468   else
10469   {
10470     if (!infoPtr->bFocus)
10471         SetFocus(infoPtr->hwndSelf);
10472 
10473     /* remove all selections */
10474     if (!(wKey & MK_CONTROL) && !(wKey & MK_SHIFT))
10475         LISTVIEW_DeselectAll(infoPtr);
10476     ReleaseCapture();
10477   }
10478 
10479   return 0;
10480 }
10481 
10482 /***
10483  * DESCRIPTION:
10484  * Processes mouse up messages (left mouse button).
10485  *
10486  * PARAMETERS:
10487  *   infoPtr [I ] valid pointer to the listview structure
10488  *   wKey    [I ] key flag
10489  *   x,y     [I ] mouse coordinate
10490  *
10491  * RETURN:
10492  *   Zero
10493  */
LISTVIEW_LButtonUp(LISTVIEW_INFO * infoPtr,WORD wKey,INT x,INT y)10494 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
10495 {
10496     LVHITTESTINFO lvHitTestInfo;
10497 
10498     TRACE("(key=%hu, X=%u, Y=%u)\n", wKey, x, y);
10499 
10500     if (!infoPtr->bLButtonDown) return 0;
10501 
10502     lvHitTestInfo.pt.x = x;
10503     lvHitTestInfo.pt.y = y;
10504 
10505     /* send NM_CLICK notification */
10506     LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
10507     if (!notify_click(infoPtr, NM_CLICK, &lvHitTestInfo)) return 0;
10508 
10509     /* set left button flag */
10510     infoPtr->bLButtonDown = FALSE;
10511 
10512     /* set a single selection, reset others */
10513     if(lvHitTestInfo.iItem == infoPtr->nLButtonDownItem && lvHitTestInfo.iItem != -1)
10514         LISTVIEW_SetSelection(infoPtr, infoPtr->nLButtonDownItem);
10515     infoPtr->nLButtonDownItem = -1;
10516 
10517     if (infoPtr->bDragging || infoPtr->bMarqueeSelect)
10518     {
10519         /* Remove the marquee rectangle and release our mouse capture */
10520         if (infoPtr->bMarqueeSelect)
10521         {
10522             LISTVIEW_InvalidateRect(infoPtr, &infoPtr->marqueeDrawRect);
10523             ReleaseCapture();
10524         }
10525 
10526         SetRectEmpty(&infoPtr->marqueeRect);
10527         SetRectEmpty(&infoPtr->marqueeDrawRect);
10528 
10529         infoPtr->bDragging = FALSE;
10530         infoPtr->bMarqueeSelect = FALSE;
10531         infoPtr->bScrolling = FALSE;
10532 
10533         KillTimer(infoPtr->hwndSelf, (UINT_PTR) infoPtr);
10534         return 0;
10535     }
10536 
10537     /* if we clicked on a selected item, edit the label */
10538     if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
10539     {
10540         /* we want to make sure the user doesn't want to do a double click. So we will
10541          * delay the edit. WM_LBUTTONDBLCLICK will cancel the timer
10542          */
10543         infoPtr->itemEdit.fEnabled = TRUE;
10544         infoPtr->itemEdit.iItem = lvHitTestInfo.iItem;
10545         SetTimer(infoPtr->hwndSelf,
10546             (UINT_PTR)&infoPtr->itemEdit,
10547             GetDoubleClickTime(),
10548             LISTVIEW_DelayedEditItem);
10549     }
10550 
10551     return 0;
10552 }
10553 
10554 /***
10555  * DESCRIPTION:
10556  * Destroys the listview control (called after WM_DESTROY).
10557  *
10558  * PARAMETER(S):
10559  * [I] infoPtr : valid pointer to the listview structure
10560  *
10561  * RETURN:
10562  * Zero
10563  */
LISTVIEW_NCDestroy(LISTVIEW_INFO * infoPtr)10564 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
10565 {
10566   INT i;
10567 
10568   TRACE("()\n");
10569 
10570   /* destroy data structure */
10571   DPA_Destroy(infoPtr->hdpaItems);
10572   DPA_Destroy(infoPtr->hdpaItemIds);
10573   DPA_Destroy(infoPtr->hdpaPosX);
10574   DPA_Destroy(infoPtr->hdpaPosY);
10575   /* columns */
10576   for (i = 0; i < DPA_GetPtrCount(infoPtr->hdpaColumns); i++)
10577       Free(DPA_GetPtr(infoPtr->hdpaColumns, i));
10578   DPA_Destroy(infoPtr->hdpaColumns);
10579   ranges_destroy(infoPtr->selectionRanges);
10580 #ifdef __REACTOS__
10581   infoPtr->selectionRanges = NULL; /* See note in ranges_clone */
10582 #endif
10583 
10584   /* destroy image lists */
10585   if (!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS))
10586   {
10587       ImageList_Destroy(infoPtr->himlNormal);
10588       ImageList_Destroy(infoPtr->himlSmall);
10589       ImageList_Destroy(infoPtr->himlState);
10590   }
10591 
10592   /* destroy font, bkgnd brush */
10593   infoPtr->hFont = 0;
10594   if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
10595   if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
10596 
10597   SetWindowLongPtrW(infoPtr->hwndSelf, 0, 0);
10598 
10599   /* free listview info pointer*/
10600   Free(infoPtr);
10601 
10602   return 0;
10603 }
10604 
10605 /***
10606  * DESCRIPTION:
10607  * Handles notifications.
10608  *
10609  * PARAMETER(S):
10610  * [I] infoPtr : valid pointer to the listview structure
10611  * [I] lpnmhdr : notification information
10612  *
10613  * RETURN:
10614  * Zero
10615  */
LISTVIEW_Notify(LISTVIEW_INFO * infoPtr,NMHDR * lpnmhdr)10616 static LRESULT LISTVIEW_Notify(LISTVIEW_INFO *infoPtr, NMHDR *lpnmhdr)
10617 {
10618     NMHEADERW *lpnmh;
10619 
10620     TRACE("(lpnmhdr=%p)\n", lpnmhdr);
10621 
10622     if (!lpnmhdr || lpnmhdr->hwndFrom != infoPtr->hwndHeader) return 0;
10623 
10624     /* remember: HDN_LAST < HDN_FIRST */
10625     if (lpnmhdr->code > HDN_FIRST || lpnmhdr->code < HDN_LAST) return 0;
10626     lpnmh = (NMHEADERW *)lpnmhdr;
10627 
10628     if (lpnmh->iItem < 0 || lpnmh->iItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
10629 
10630     switch (lpnmhdr->code)
10631     {
10632 	case HDN_TRACKW:
10633 	case HDN_TRACKA:
10634 	{
10635 	    COLUMN_INFO *lpColumnInfo;
10636 	    POINT ptOrigin;
10637 	    INT x;
10638 
10639 	    if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
10640 		break;
10641 
10642             /* remove the old line (if any) */
10643             LISTVIEW_DrawTrackLine(infoPtr);
10644 
10645             /* compute & draw the new line */
10646             lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
10647             x = lpColumnInfo->rcHeader.left + lpnmh->pitem->cxy;
10648             LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
10649             infoPtr->xTrackLine = x + ptOrigin.x;
10650             LISTVIEW_DrawTrackLine(infoPtr);
10651             return notify_forward_header(infoPtr, lpnmh);
10652 	}
10653 
10654 	case HDN_ENDTRACKA:
10655 	case HDN_ENDTRACKW:
10656 	    /* remove the track line (if any) */
10657 	    LISTVIEW_DrawTrackLine(infoPtr);
10658 	    infoPtr->xTrackLine = -1;
10659             return notify_forward_header(infoPtr, lpnmh);
10660 
10661         case HDN_BEGINDRAG:
10662             if ((infoPtr->dwLvExStyle & LVS_EX_HEADERDRAGDROP) == 0) return 1;
10663             return notify_forward_header(infoPtr, lpnmh);
10664 
10665         case HDN_ENDDRAG:
10666             infoPtr->colRectsDirty = TRUE;
10667             LISTVIEW_InvalidateList(infoPtr);
10668             return notify_forward_header(infoPtr, lpnmh);
10669 
10670 	case HDN_ITEMCHANGEDW:
10671 	case HDN_ITEMCHANGEDA:
10672 	{
10673 	    COLUMN_INFO *lpColumnInfo;
10674 	    HDITEMW hdi;
10675 	    INT dx, cxy;
10676 
10677 	    if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
10678 	    {
10679 		hdi.mask = HDI_WIDTH;
10680 		if (!SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, lpnmh->iItem, (LPARAM)&hdi)) return 0;
10681 		cxy = hdi.cxy;
10682 	    }
10683 	    else
10684 		cxy = lpnmh->pitem->cxy;
10685 
10686 	    /* determine how much we change since the last know position */
10687 	    lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
10688 	    dx = cxy - (lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
10689 	    if (dx != 0)
10690 	    {
10691 		lpColumnInfo->rcHeader.right += dx;
10692 
10693 		hdi.mask = HDI_ORDER;
10694 		SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, lpnmh->iItem, (LPARAM)&hdi);
10695 
10696 		/* not the rightmost one */
10697 		if (hdi.iOrder + 1 < DPA_GetPtrCount(infoPtr->hdpaColumns))
10698 		{
10699 		    INT nIndex = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX,
10700 					      hdi.iOrder + 1, 0);
10701 		    LISTVIEW_ScrollColumns(infoPtr, nIndex, dx);
10702 		}
10703 		else
10704 		{
10705 		    /* only needs to update the scrolls */
10706 		    infoPtr->nItemWidth += dx;
10707 		    LISTVIEW_UpdateScroll(infoPtr);
10708 		}
10709 		LISTVIEW_UpdateItemSize(infoPtr);
10710 		if (infoPtr->uView == LV_VIEW_DETAILS && is_redrawing(infoPtr))
10711 		{
10712 		    POINT ptOrigin;
10713 		    RECT rcCol = lpColumnInfo->rcHeader;
10714 
10715 		    LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
10716 		    OffsetRect(&rcCol, ptOrigin.x, 0);
10717 
10718 		    rcCol.top = infoPtr->rcList.top;
10719 		    rcCol.bottom = infoPtr->rcList.bottom;
10720 
10721 		    /* resizing left-aligned columns leaves most of the left side untouched */
10722 		    if ((lpColumnInfo->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
10723 		    {
10724 			INT nMaxDirty = infoPtr->nEllipsisWidth + infoPtr->ntmMaxCharWidth;
10725 			if (dx > 0)
10726 			    nMaxDirty += dx;
10727 			rcCol.left = max (rcCol.left, rcCol.right - nMaxDirty);
10728 		    }
10729 
10730 		    /* when shrinking the last column clear the now unused field */
10731 		    if (hdi.iOrder == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1)
10732 		    {
10733 		        RECT right;
10734 
10735 		        rcCol.right -= dx;
10736 
10737 		        /* deal with right from rightmost column area */
10738 		        right.left = rcCol.right;
10739 		        right.top  = rcCol.top;
10740 		        right.bottom = rcCol.bottom;
10741 		        right.right = infoPtr->rcList.right;
10742 
10743 		        LISTVIEW_InvalidateRect(infoPtr, &right);
10744 		    }
10745 
10746 		    LISTVIEW_InvalidateRect(infoPtr, &rcCol);
10747 		}
10748 	    }
10749 	    break;
10750         }
10751 
10752 	case HDN_ITEMCLICKW:
10753 	case HDN_ITEMCLICKA:
10754 	{
10755             /* Handle sorting by Header Column */
10756             NMLISTVIEW nmlv;
10757 
10758             ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
10759             nmlv.iItem = -1;
10760             nmlv.iSubItem = lpnmh->iItem;
10761             notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
10762             return notify_forward_header(infoPtr, lpnmh);
10763         }
10764 
10765 	case HDN_DIVIDERDBLCLICKW:
10766 	case HDN_DIVIDERDBLCLICKA:
10767             /* FIXME: for LVS_EX_HEADERINALLVIEWS and not LV_VIEW_DETAILS
10768                       we should use LVSCW_AUTOSIZE_USEHEADER, helper rework or
10769                       split needed for that */
10770             LISTVIEW_SetColumnWidth(infoPtr, lpnmh->iItem, LVSCW_AUTOSIZE);
10771             return notify_forward_header(infoPtr, lpnmh);
10772     }
10773     return 0;
10774 }
10775 
10776 /***
10777  * DESCRIPTION:
10778  * Paint non-client area of control.
10779  *
10780  * PARAMETER(S):
10781  * [I] infoPtr : valid pointer to the listview structureof the sender
10782  * [I] region : update region
10783  *
10784  * RETURN:
10785  *  TRUE  - frame was painted
10786  *  FALSE - call default window proc
10787  */
LISTVIEW_NCPaint(const LISTVIEW_INFO * infoPtr,HRGN region)10788 static BOOL LISTVIEW_NCPaint(const LISTVIEW_INFO *infoPtr, HRGN region)
10789 {
10790     HTHEME theme = GetWindowTheme (infoPtr->hwndSelf);
10791     HDC dc;
10792     RECT r;
10793     HRGN cliprgn;
10794     int cxEdge = GetSystemMetrics (SM_CXEDGE),
10795         cyEdge = GetSystemMetrics (SM_CYEDGE);
10796 
10797     if (!theme)
10798        return DefWindowProcW (infoPtr->hwndSelf, WM_NCPAINT, (WPARAM)region, 0);
10799 
10800     GetWindowRect(infoPtr->hwndSelf, &r);
10801 
10802     cliprgn = CreateRectRgn (r.left + cxEdge, r.top + cyEdge,
10803         r.right - cxEdge, r.bottom - cyEdge);
10804     if (region != (HRGN)1)
10805         CombineRgn (cliprgn, cliprgn, region, RGN_AND);
10806     OffsetRect(&r, -r.left, -r.top);
10807 
10808 #ifdef __REACTOS__ /* r73789 */
10809     dc = GetWindowDC(infoPtr->hwndSelf);
10810     /* Exclude client part */
10811     ExcludeClipRect(dc, r.left + cxEdge, r.top + cyEdge,
10812         r.right - cxEdge, r.bottom -cyEdge);
10813 #else
10814     dc = GetDCEx(infoPtr->hwndSelf, region, DCX_WINDOW|DCX_INTERSECTRGN);
10815     OffsetRect(&r, -r.left, -r.top);
10816 #endif
10817 
10818     if (IsThemeBackgroundPartiallyTransparent (theme, 0, 0))
10819         DrawThemeParentBackground(infoPtr->hwndSelf, dc, &r);
10820     DrawThemeBackground (theme, dc, 0, 0, &r, 0);
10821     ReleaseDC(infoPtr->hwndSelf, dc);
10822 
10823     /* Call default proc to get the scrollbars etc. painted */
10824     DefWindowProcW (infoPtr->hwndSelf, WM_NCPAINT, (WPARAM)cliprgn, 0);
10825     DeleteObject(cliprgn);
10826 
10827     return FALSE;
10828 }
10829 
10830 /***
10831  * DESCRIPTION:
10832  * Determines the type of structure to use.
10833  *
10834  * PARAMETER(S):
10835  * [I] infoPtr : valid pointer to the listview structureof the sender
10836  * [I] hwndFrom : listview window handle
10837  * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
10838  *
10839  * RETURN:
10840  * Zero
10841  */
LISTVIEW_NotifyFormat(LISTVIEW_INFO * infoPtr,HWND hwndFrom,INT nCommand)10842 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
10843 {
10844     TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom, nCommand);
10845 
10846     if (nCommand == NF_REQUERY)
10847         infoPtr->notifyFormat = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY);
10848 
10849     return infoPtr->notifyFormat;
10850 }
10851 
10852 /***
10853  * DESCRIPTION:
10854  * Paints/Repaints the listview control. Internal use.
10855  *
10856  * PARAMETER(S):
10857  * [I] infoPtr : valid pointer to the listview structure
10858  * [I] hdc : device context handle
10859  *
10860  * RETURN:
10861  * Zero
10862  */
LISTVIEW_Paint(LISTVIEW_INFO * infoPtr,HDC hdc)10863 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
10864 {
10865     TRACE("(hdc=%p)\n", hdc);
10866 
10867     if (infoPtr->bNoItemMetrics && infoPtr->nItemCount)
10868     {
10869 	infoPtr->bNoItemMetrics = FALSE;
10870 	LISTVIEW_UpdateItemSize(infoPtr);
10871 	if (infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON)
10872 	    LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
10873 	LISTVIEW_UpdateScroll(infoPtr);
10874     }
10875 
10876     if (infoPtr->hwndHeader)  UpdateWindow(infoPtr->hwndHeader);
10877 
10878     if (hdc)
10879         LISTVIEW_Refresh(infoPtr, hdc, NULL);
10880     else
10881     {
10882 	PAINTSTRUCT ps;
10883 
10884 	hdc = BeginPaint(infoPtr->hwndSelf, &ps);
10885 	if (!hdc) return 1;
10886 	LISTVIEW_Refresh(infoPtr, hdc, ps.fErase ? &ps.rcPaint : NULL);
10887 	EndPaint(infoPtr->hwndSelf, &ps);
10888     }
10889 
10890     return 0;
10891 }
10892 
10893 /***
10894  * DESCRIPTION:
10895  * Paints/Repaints the listview control, WM_PAINT handler.
10896  *
10897  * PARAMETER(S):
10898  * [I] infoPtr : valid pointer to the listview structure
10899  * [I] hdc : device context handle
10900  *
10901  * RETURN:
10902  * Zero
10903  */
LISTVIEW_WMPaint(LISTVIEW_INFO * infoPtr,HDC hdc)10904 static inline LRESULT LISTVIEW_WMPaint(LISTVIEW_INFO *infoPtr, HDC hdc)
10905 {
10906     TRACE("(hdc=%p)\n", hdc);
10907 
10908     if (!is_redrawing(infoPtr))
10909         return DefWindowProcW (infoPtr->hwndSelf, WM_PAINT, (WPARAM)hdc, 0);
10910 
10911     return LISTVIEW_Paint(infoPtr, hdc);
10912 }
10913 
10914 /***
10915  * DESCRIPTION:
10916  * Paints/Repaints the listview control.
10917  *
10918  * PARAMETER(S):
10919  * [I] infoPtr : valid pointer to the listview structure
10920  * [I] hdc : device context handle
10921  * [I] options : drawing options
10922  *
10923  * RETURN:
10924  * Zero
10925  */
LISTVIEW_PrintClient(LISTVIEW_INFO * infoPtr,HDC hdc,DWORD options)10926 static LRESULT LISTVIEW_PrintClient(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD options)
10927 {
10928     if ((options & PRF_CHECKVISIBLE) && !IsWindowVisible(infoPtr->hwndSelf))
10929         return 0;
10930 
10931     if (options & ~(PRF_ERASEBKGND|PRF_CLIENT))
10932         FIXME("(hdc=%p options=0x%08x) partial stub\n", hdc, options);
10933 
10934     if (options & PRF_ERASEBKGND)
10935         LISTVIEW_EraseBkgnd(infoPtr, hdc);
10936 
10937     if (options & PRF_CLIENT)
10938         LISTVIEW_Paint(infoPtr, hdc);
10939 
10940     return 0;
10941 }
10942 
10943 
10944 /***
10945  * DESCRIPTION:
10946  * Processes double click messages (right mouse button).
10947  *
10948  * PARAMETER(S):
10949  * [I] infoPtr : valid pointer to the listview structure
10950  * [I] wKey : key flag
10951  * [I] x,y : mouse coordinate
10952  *
10953  * RETURN:
10954  * Zero
10955  */
LISTVIEW_RButtonDblClk(const LISTVIEW_INFO * infoPtr,WORD wKey,INT x,INT y)10956 static LRESULT LISTVIEW_RButtonDblClk(const LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
10957 {
10958     LVHITTESTINFO lvHitTestInfo;
10959 
10960     TRACE("(key=%hu,X=%u,Y=%u)\n", wKey, x, y);
10961 
10962     /* send NM_RELEASEDCAPTURE notification */
10963     if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
10964 
10965     /* send NM_RDBLCLK notification */
10966     lvHitTestInfo.pt.x = x;
10967     lvHitTestInfo.pt.y = y;
10968     LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
10969     notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
10970 
10971     return 0;
10972 }
10973 
10974 /***
10975  * DESCRIPTION:
10976  * Processes WM_RBUTTONDOWN message and corresponding drag operation.
10977  *
10978  * PARAMETER(S):
10979  * [I] infoPtr : valid pointer to the listview structure
10980  * [I] wKey : key flag
10981  * [I] x, y : mouse coordinate
10982  *
10983  * RETURN:
10984  * Zero
10985  */
LISTVIEW_RButtonDown(LISTVIEW_INFO * infoPtr,WORD wKey,INT x,INT y)10986 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
10987 {
10988     LVHITTESTINFO ht;
10989     INT item;
10990 
10991     TRACE("(key=%hu, x=%d, y=%d)\n", wKey, x, y);
10992 
10993     /* send NM_RELEASEDCAPTURE notification */
10994     if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
10995 
10996     /* determine the index of the selected item */
10997     ht.pt.x = x;
10998     ht.pt.y = y;
10999     item = LISTVIEW_HitTest(infoPtr, &ht, TRUE, TRUE);
11000 
11001     /* make sure the listview control window has the focus */
11002     if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
11003 
11004     if ((item >= 0) && (item < infoPtr->nItemCount))
11005     {
11006 	LISTVIEW_SetItemFocus(infoPtr, item);
11007 	if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
11008             !LISTVIEW_GetItemState(infoPtr, item, LVIS_SELECTED))
11009 	    LISTVIEW_SetSelection(infoPtr, item);
11010     }
11011     else
11012 	LISTVIEW_DeselectAll(infoPtr);
11013 
11014     if (LISTVIEW_TrackMouse(infoPtr, ht.pt))
11015     {
11016 	if (ht.iItem != -1)
11017 	{
11018             NMLISTVIEW nmlv;
11019 
11020             memset(&nmlv, 0, sizeof(nmlv));
11021             nmlv.iItem = ht.iItem;
11022             nmlv.ptAction = ht.pt;
11023 
11024             notify_listview(infoPtr, LVN_BEGINRDRAG, &nmlv);
11025 	}
11026     }
11027     else
11028     {
11029 	SetFocus(infoPtr->hwndSelf);
11030 
11031         ht.pt.x = x;
11032         ht.pt.y = y;
11033         LISTVIEW_HitTest(infoPtr, &ht, TRUE, FALSE);
11034 
11035 	if (notify_click(infoPtr, NM_RCLICK, &ht))
11036 	{
11037 	    /* Send a WM_CONTEXTMENU message in response to the WM_RBUTTONUP */
11038 	    SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
11039 		(WPARAM)infoPtr->hwndSelf, (LPARAM)GetMessagePos());
11040 	}
11041     }
11042 
11043     return 0;
11044 }
11045 
11046 /***
11047  * DESCRIPTION:
11048  * Sets the cursor.
11049  *
11050  * PARAMETER(S):
11051  * [I] infoPtr : valid pointer to the listview structure
11052  * [I] hwnd : window handle of window containing the cursor
11053  * [I] nHittest : hit-test code
11054  * [I] wMouseMsg : ideintifier of the mouse message
11055  *
11056  * RETURN:
11057  * TRUE if cursor is set
11058  * FALSE otherwise
11059  */
LISTVIEW_SetCursor(const LISTVIEW_INFO * infoPtr,WPARAM wParam,LPARAM lParam)11060 static BOOL LISTVIEW_SetCursor(const LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
11061 {
11062     LVHITTESTINFO lvHitTestInfo;
11063 
11064     if (!LISTVIEW_IsHotTracking(infoPtr)) goto forward;
11065 
11066     if (!infoPtr->hHotCursor) goto forward;
11067 
11068     GetCursorPos(&lvHitTestInfo.pt);
11069     if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) goto forward;
11070 
11071     SetCursor(infoPtr->hHotCursor);
11072 
11073     return TRUE;
11074 
11075 forward:
11076 
11077     return DefWindowProcW(infoPtr->hwndSelf, WM_SETCURSOR, wParam, lParam);
11078 }
11079 
11080 /***
11081  * DESCRIPTION:
11082  * Sets the focus.
11083  *
11084  * PARAMETER(S):
11085  * [I] infoPtr : valid pointer to the listview structure
11086  * [I] hwndLoseFocus : handle of previously focused window
11087  *
11088  * RETURN:
11089  * Zero
11090  */
LISTVIEW_SetFocus(LISTVIEW_INFO * infoPtr,HWND hwndLoseFocus)11091 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
11092 {
11093     TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus);
11094 
11095     /* if we have the focus already, there's nothing to do */
11096     if (infoPtr->bFocus) return 0;
11097 
11098     /* send NM_SETFOCUS notification */
11099     if (!notify(infoPtr, NM_SETFOCUS)) return 0;
11100 
11101     /* set window focus flag */
11102     infoPtr->bFocus = TRUE;
11103 
11104     /* put the focus rect back on */
11105     LISTVIEW_ShowFocusRect(infoPtr, TRUE);
11106 
11107     /* redraw all visible selected items */
11108     LISTVIEW_InvalidateSelectedItems(infoPtr);
11109 
11110     return 0;
11111 }
11112 
11113 /***
11114  * DESCRIPTION:
11115  * Sets the font.
11116  *
11117  * PARAMETER(S):
11118  * [I] infoPtr : valid pointer to the listview structure
11119  * [I] fRedraw : font handle
11120  * [I] fRedraw : redraw flag
11121  *
11122  * RETURN:
11123  * Zero
11124  */
LISTVIEW_SetFont(LISTVIEW_INFO * infoPtr,HFONT hFont,WORD fRedraw)11125 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
11126 {
11127     HFONT oldFont = infoPtr->hFont;
11128     INT oldHeight = infoPtr->nItemHeight;
11129 
11130     TRACE("(hfont=%p,redraw=%hu)\n", hFont, fRedraw);
11131 
11132     infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
11133     if (infoPtr->hFont == oldFont) return 0;
11134 
11135     LISTVIEW_SaveTextMetrics(infoPtr);
11136 
11137     infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
11138 
11139     if (infoPtr->uView == LV_VIEW_DETAILS)
11140     {
11141 	SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
11142         LISTVIEW_UpdateSize(infoPtr);
11143         LISTVIEW_UpdateScroll(infoPtr);
11144     }
11145     else if (infoPtr->nItemHeight != oldHeight)
11146         LISTVIEW_UpdateScroll(infoPtr);
11147 
11148     if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
11149 
11150     return 0;
11151 }
11152 
11153 /***
11154  * DESCRIPTION:
11155  * Message handling for WM_SETREDRAW.
11156  * For the Listview, it invalidates the entire window (the doc specifies otherwise)
11157  *
11158  * PARAMETER(S):
11159  * [I] infoPtr : valid pointer to the listview structure
11160  * [I] redraw: state of redraw flag
11161  *
11162  * RETURN:
11163  *     Zero.
11164  */
LISTVIEW_SetRedraw(LISTVIEW_INFO * infoPtr,BOOL redraw)11165 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL redraw)
11166 {
11167     TRACE("old=%d, new=%d\n", infoPtr->redraw, redraw);
11168 
11169     if (infoPtr->redraw == !!redraw)
11170         return 0;
11171 
11172     if (!(infoPtr->redraw = !!redraw))
11173         return 0;
11174 
11175     if (is_autoarrange(infoPtr))
11176 	LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
11177     LISTVIEW_UpdateScroll(infoPtr);
11178 
11179     /* despite what the WM_SETREDRAW docs says, apps expect us
11180      * to invalidate the listview here... stupid! */
11181     LISTVIEW_InvalidateList(infoPtr);
11182 
11183     return 0;
11184 }
11185 
11186 /***
11187  * DESCRIPTION:
11188  * Resizes the listview control. This function processes WM_SIZE
11189  * messages.  At this time, the width and height are not used.
11190  *
11191  * PARAMETER(S):
11192  * [I] infoPtr : valid pointer to the listview structure
11193  * [I] Width : new width
11194  * [I] Height : new height
11195  *
11196  * RETURN:
11197  * Zero
11198  */
LISTVIEW_Size(LISTVIEW_INFO * infoPtr,int Width,int Height)11199 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
11200 {
11201     RECT rcOld = infoPtr->rcList;
11202 
11203     TRACE("(width=%d, height=%d)\n", Width, Height);
11204 
11205     LISTVIEW_UpdateSize(infoPtr);
11206     if (EqualRect(&rcOld, &infoPtr->rcList)) return 0;
11207 
11208     /* do not bother with display related stuff if we're not redrawing */
11209     if (!is_redrawing(infoPtr)) return 0;
11210 
11211     if (is_autoarrange(infoPtr))
11212 	LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
11213 
11214     LISTVIEW_UpdateScroll(infoPtr);
11215 
11216     /* refresh all only for lists whose height changed significantly */
11217     if ((infoPtr->uView == LV_VIEW_LIST) &&
11218 	(rcOld.bottom - rcOld.top) / infoPtr->nItemHeight !=
11219 	(infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight)
11220 	LISTVIEW_InvalidateList(infoPtr);
11221 
11222   return 0;
11223 }
11224 
11225 /***
11226  * DESCRIPTION:
11227  * Sets the size information.
11228  *
11229  * PARAMETER(S):
11230  * [I] infoPtr : valid pointer to the listview structure
11231  *
11232  * RETURN:
11233  *  None
11234  */
LISTVIEW_UpdateSize(LISTVIEW_INFO * infoPtr)11235 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
11236 {
11237     TRACE("uView=%d, rcList(old)=%s\n", infoPtr->uView, wine_dbgstr_rect(&infoPtr->rcList));
11238 
11239     GetClientRect(infoPtr->hwndSelf, &infoPtr->rcList);
11240 
11241     if (infoPtr->uView == LV_VIEW_LIST)
11242     {
11243 	/* Apparently the "LIST" style is supposed to have the same
11244 	 * number of items in a column even if there is no scroll bar.
11245 	 * Since if a scroll bar already exists then the bottom is already
11246 	 * reduced, only reduce if the scroll bar does not currently exist.
11247 	 * The "2" is there to mimic the native control. I think it may be
11248 	 * related to either padding or edges.  (GLA 7/2002)
11249 	 */
11250 	if (!(GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_HSCROLL))
11251 	    infoPtr->rcList.bottom -= GetSystemMetrics(SM_CYHSCROLL);
11252         infoPtr->rcList.bottom = max (infoPtr->rcList.bottom - 2, 0);
11253     }
11254 
11255     /* When ListView control is created invisible, header isn't created right away. */
11256     if (infoPtr->hwndHeader)
11257     {
11258         POINT origin;
11259         WINDOWPOS wp;
11260         HDLAYOUT hl;
11261         RECT rect;
11262 
11263         LISTVIEW_GetOrigin(infoPtr, &origin);
11264 
11265         rect = infoPtr->rcList;
11266         rect.left += origin.x;
11267 
11268         hl.prc = &rect;
11269 	hl.pwpos = &wp;
11270 	SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
11271 	TRACE("  wp.flags=0x%08x, wp=%d,%d (%dx%d)\n", wp.flags, wp.x, wp.y, wp.cx, wp.cy);
11272 
11273 	if (LISTVIEW_IsHeaderEnabled(infoPtr))
11274 	    wp.flags |= SWP_SHOWWINDOW;
11275 	else
11276 	{
11277 	    wp.flags |= SWP_HIDEWINDOW;
11278 	    wp.cy = 0;
11279 	}
11280 
11281 	SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
11282 	TRACE("  after SWP wp=%d,%d (%dx%d)\n", wp.x, wp.y, wp.cx, wp.cy);
11283 
11284 	infoPtr->rcList.top = max(wp.cy, 0);
11285     }
11286     /* extra padding for grid */
11287     if (infoPtr->uView == LV_VIEW_DETAILS && infoPtr->dwLvExStyle & LVS_EX_GRIDLINES)
11288 	infoPtr->rcList.top += 2;
11289 
11290     TRACE("  rcList=%s\n", wine_dbgstr_rect(&infoPtr->rcList));
11291 }
11292 
11293 /***
11294  * DESCRIPTION:
11295  * Processes WM_STYLECHANGED messages.
11296  *
11297  * PARAMETER(S):
11298  * [I] infoPtr : valid pointer to the listview structure
11299  * [I] wStyleType : window style type (normal or extended)
11300  * [I] lpss : window style information
11301  *
11302  * RETURN:
11303  * Zero
11304  */
LISTVIEW_StyleChanged(LISTVIEW_INFO * infoPtr,WPARAM wStyleType,const STYLESTRUCT * lpss)11305 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
11306                                  const STYLESTRUCT *lpss)
11307 {
11308     UINT uNewView, uOldView;
11309     UINT style;
11310 
11311     TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
11312           wStyleType, lpss->styleOld, lpss->styleNew);
11313 
11314     if (wStyleType != GWL_STYLE || lpss->styleNew == infoPtr->dwStyle) return 0;
11315 
11316     infoPtr->dwStyle = lpss->styleNew;
11317 
11318     if (((lpss->styleOld & WS_HSCROLL) != 0)&&
11319         ((lpss->styleNew & WS_HSCROLL) == 0))
11320        ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
11321 
11322     if (((lpss->styleOld & WS_VSCROLL) != 0)&&
11323         ((lpss->styleNew & WS_VSCROLL) == 0))
11324        ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
11325 
11326     uNewView = lpss->styleNew & LVS_TYPEMASK;
11327     uOldView = lpss->styleOld & LVS_TYPEMASK;
11328 
11329     if (uNewView != uOldView)
11330     {
11331     	HIMAGELIST himl;
11332 
11333         /* LVM_SETVIEW doesn't change window style bits within LVS_TYPEMASK,
11334            changing style updates current view only when view bits change. */
11335         map_style_view(infoPtr);
11336         SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
11337     	ShowWindow(infoPtr->hwndHeader, SW_HIDE);
11338 
11339         ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
11340         SetRectEmpty(&infoPtr->rcFocus);
11341 
11342         himl = (uNewView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
11343         set_icon_size(&infoPtr->iconSize, himl, uNewView != LVS_ICON);
11344 
11345         if (uNewView == LVS_REPORT)
11346         {
11347             HDLAYOUT hl;
11348             WINDOWPOS wp;
11349 
11350             LISTVIEW_CreateHeader( infoPtr );
11351 
11352             hl.prc = &infoPtr->rcList;
11353             hl.pwpos = &wp;
11354             SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
11355             SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy,
11356                     wp.flags | ((infoPtr->dwStyle & LVS_NOCOLUMNHEADER)
11357                         ? SWP_HIDEWINDOW : SWP_SHOWWINDOW));
11358         }
11359 
11360 	LISTVIEW_UpdateItemSize(infoPtr);
11361     }
11362 
11363     if (uNewView == LVS_REPORT || infoPtr->dwLvExStyle & LVS_EX_HEADERINALLVIEWS)
11364     {
11365         if ((lpss->styleOld ^ lpss->styleNew) & LVS_NOCOLUMNHEADER)
11366         {
11367             if (lpss->styleNew & LVS_NOCOLUMNHEADER)
11368             {
11369                 /* Turn off the header control */
11370                 style = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE);
11371                 TRACE("Hide header control, was 0x%08x\n", style);
11372                 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, style | HDS_HIDDEN);
11373             } else {
11374                 /* Turn on the header control */
11375                 if ((style = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE)) & HDS_HIDDEN)
11376                 {
11377                     TRACE("Show header control, was 0x%08x\n", style);
11378                     SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, (style & ~HDS_HIDDEN) | WS_VISIBLE);
11379                 }
11380             }
11381         }
11382     }
11383 
11384     if ( (uNewView == LVS_ICON || uNewView == LVS_SMALLICON) &&
11385 	 (uNewView != uOldView || ((lpss->styleNew ^ lpss->styleOld) & LVS_ALIGNMASK)) )
11386 	 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
11387 
11388     /* update the size of the client area */
11389     LISTVIEW_UpdateSize(infoPtr);
11390 
11391     /* add scrollbars if needed */
11392     LISTVIEW_UpdateScroll(infoPtr);
11393 
11394     /* invalidate client area + erase background */
11395     LISTVIEW_InvalidateList(infoPtr);
11396 
11397     return 0;
11398 }
11399 
11400 /***
11401  * DESCRIPTION:
11402  * Processes WM_STYLECHANGING messages.
11403  *
11404  * PARAMETER(S):
11405  * [I] wStyleType : window style type (normal or extended)
11406  * [I0] lpss : window style information
11407  *
11408  * RETURN:
11409  * Zero
11410  */
LISTVIEW_StyleChanging(WPARAM wStyleType,STYLESTRUCT * lpss)11411 static INT LISTVIEW_StyleChanging(WPARAM wStyleType,
11412                                   STYLESTRUCT *lpss)
11413 {
11414     TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
11415           wStyleType, lpss->styleOld, lpss->styleNew);
11416 
11417     /* don't forward LVS_OWNERDATA only if not already set to */
11418     if ((lpss->styleNew ^ lpss->styleOld) & LVS_OWNERDATA)
11419     {
11420         if (lpss->styleOld & LVS_OWNERDATA)
11421             lpss->styleNew |= LVS_OWNERDATA;
11422         else
11423             lpss->styleNew &= ~LVS_OWNERDATA;
11424     }
11425 
11426     return 0;
11427 }
11428 
11429 /***
11430  * DESCRIPTION:
11431  * Processes WM_SHOWWINDOW messages.
11432  *
11433  * PARAMETER(S):
11434  * [I] infoPtr : valid pointer to the listview structure
11435  * [I] bShown  : window is being shown (FALSE when hidden)
11436  * [I] iStatus : window show status
11437  *
11438  * RETURN:
11439  * Zero
11440  */
LISTVIEW_ShowWindow(LISTVIEW_INFO * infoPtr,WPARAM bShown,LPARAM iStatus)11441 static LRESULT LISTVIEW_ShowWindow(LISTVIEW_INFO *infoPtr, WPARAM bShown, LPARAM iStatus)
11442 {
11443   /* header delayed creation */
11444   if ((infoPtr->uView == LV_VIEW_DETAILS) && bShown)
11445   {
11446     LISTVIEW_CreateHeader(infoPtr);
11447 
11448     if (!(LVS_NOCOLUMNHEADER & infoPtr->dwStyle))
11449       ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
11450   }
11451 
11452   return DefWindowProcW(infoPtr->hwndSelf, WM_SHOWWINDOW, bShown, iStatus);
11453 }
11454 
11455 /***
11456  * DESCRIPTION:
11457  * Processes CCM_GETVERSION messages.
11458  *
11459  * PARAMETER(S):
11460  * [I] infoPtr : valid pointer to the listview structure
11461  *
11462  * RETURN:
11463  * Current version
11464  */
LISTVIEW_GetVersion(const LISTVIEW_INFO * infoPtr)11465 static inline LRESULT LISTVIEW_GetVersion(const LISTVIEW_INFO *infoPtr)
11466 {
11467   return infoPtr->iVersion;
11468 }
11469 
11470 /***
11471  * DESCRIPTION:
11472  * Processes CCM_SETVERSION messages.
11473  *
11474  * PARAMETER(S):
11475  * [I] infoPtr  : valid pointer to the listview structure
11476  * [I] iVersion : version to be set
11477  *
11478  * RETURN:
11479  * -1 when requested version is greater than DLL version;
11480  * previous version otherwise
11481  */
LISTVIEW_SetVersion(LISTVIEW_INFO * infoPtr,DWORD iVersion)11482 static LRESULT LISTVIEW_SetVersion(LISTVIEW_INFO *infoPtr, DWORD iVersion)
11483 {
11484   INT iOldVersion = infoPtr->iVersion;
11485 
11486   if (iVersion > COMCTL32_VERSION)
11487     return -1;
11488 
11489   infoPtr->iVersion = iVersion;
11490 
11491   TRACE("new version %d\n", iVersion);
11492 
11493   return iOldVersion;
11494 }
11495 
11496 /***
11497  * DESCRIPTION:
11498  * Window procedure of the listview control.
11499  *
11500  */
11501 static LRESULT WINAPI
LISTVIEW_WindowProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)11502 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
11503 {
11504   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
11505 
11506   TRACE("(hwnd=%p uMsg=%x wParam=%lx lParam=%lx)\n", hwnd, uMsg, wParam, lParam);
11507 
11508   if (!infoPtr && (uMsg != WM_NCCREATE))
11509     return DefWindowProcW(hwnd, uMsg, wParam, lParam);
11510 
11511   switch (uMsg)
11512   {
11513   case LVM_APPROXIMATEVIEWRECT:
11514     return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
11515                                         LOWORD(lParam), HIWORD(lParam));
11516   case LVM_ARRANGE:
11517     return LISTVIEW_Arrange(infoPtr, (INT)wParam);
11518 
11519   case LVM_CANCELEDITLABEL:
11520     return LISTVIEW_CancelEditLabel(infoPtr);
11521 
11522   case LVM_CREATEDRAGIMAGE:
11523     return (LRESULT)LISTVIEW_CreateDragImage(infoPtr, (INT)wParam, (LPPOINT)lParam);
11524 
11525   case LVM_DELETEALLITEMS:
11526     return LISTVIEW_DeleteAllItems(infoPtr, FALSE);
11527 
11528   case LVM_DELETECOLUMN:
11529     return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
11530 
11531   case LVM_DELETEITEM:
11532     return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
11533 
11534   case LVM_EDITLABELA:
11535   case LVM_EDITLABELW:
11536     return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam,
11537                                         uMsg == LVM_EDITLABELW);
11538   /* case LVM_ENABLEGROUPVIEW: */
11539 
11540   case LVM_ENSUREVISIBLE:
11541     return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
11542 
11543   case LVM_FINDITEMW:
11544     return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
11545 
11546   case LVM_FINDITEMA:
11547     return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
11548 
11549   case LVM_GETBKCOLOR:
11550     return infoPtr->clrBk;
11551 
11552   /* case LVM_GETBKIMAGE: */
11553 
11554   case LVM_GETCALLBACKMASK:
11555     return infoPtr->uCallbackMask;
11556 
11557   case LVM_GETCOLUMNA:
11558   case LVM_GETCOLUMNW:
11559     return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam,
11560                                uMsg == LVM_GETCOLUMNW);
11561 
11562   case LVM_GETCOLUMNORDERARRAY:
11563     return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
11564 
11565   case LVM_GETCOLUMNWIDTH:
11566     return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
11567 
11568   case LVM_GETCOUNTPERPAGE:
11569     return LISTVIEW_GetCountPerPage(infoPtr);
11570 
11571   case LVM_GETEDITCONTROL:
11572     return (LRESULT)infoPtr->hwndEdit;
11573 
11574   case LVM_GETEXTENDEDLISTVIEWSTYLE:
11575     return infoPtr->dwLvExStyle;
11576 
11577   /* case LVM_GETGROUPINFO: */
11578 
11579   /* case LVM_GETGROUPMETRICS: */
11580 
11581   case LVM_GETHEADER:
11582     return (LRESULT)infoPtr->hwndHeader;
11583 
11584   case LVM_GETHOTCURSOR:
11585     return (LRESULT)infoPtr->hHotCursor;
11586 
11587   case LVM_GETHOTITEM:
11588     return infoPtr->nHotItem;
11589 
11590   case LVM_GETHOVERTIME:
11591     return infoPtr->dwHoverTime;
11592 
11593   case LVM_GETIMAGELIST:
11594     return (LRESULT)LISTVIEW_GetImageList(infoPtr, (INT)wParam);
11595 
11596   /* case LVM_GETINSERTMARK: */
11597 
11598   /* case LVM_GETINSERTMARKCOLOR: */
11599 
11600   /* case LVM_GETINSERTMARKRECT: */
11601 
11602   case LVM_GETISEARCHSTRINGA:
11603   case LVM_GETISEARCHSTRINGW:
11604     FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
11605     return FALSE;
11606 
11607   case LVM_GETITEMA:
11608   case LVM_GETITEMW:
11609     return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, uMsg == LVM_GETITEMW);
11610 
11611   case LVM_GETITEMCOUNT:
11612     return infoPtr->nItemCount;
11613 
11614   case LVM_GETITEMPOSITION:
11615     return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
11616 
11617   case LVM_GETITEMRECT:
11618     return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
11619 
11620   case LVM_GETITEMSPACING:
11621     return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
11622 
11623   case LVM_GETITEMSTATE:
11624     return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
11625 
11626   case LVM_GETITEMTEXTA:
11627   case LVM_GETITEMTEXTW:
11628     return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam,
11629                                  uMsg == LVM_GETITEMTEXTW);
11630 
11631   case LVM_GETNEXTITEM:
11632     return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
11633 
11634   case LVM_GETNUMBEROFWORKAREAS:
11635     FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
11636     return 1;
11637 
11638   case LVM_GETORIGIN:
11639     if (!lParam) return FALSE;
11640     if (infoPtr->uView == LV_VIEW_DETAILS ||
11641         infoPtr->uView == LV_VIEW_LIST) return FALSE;
11642     LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
11643     return TRUE;
11644 
11645   /* case LVM_GETOUTLINECOLOR: */
11646 
11647   /* case LVM_GETSELECTEDCOLUMN: */
11648 
11649   case LVM_GETSELECTEDCOUNT:
11650     return LISTVIEW_GetSelectedCount(infoPtr);
11651 
11652   case LVM_GETSELECTIONMARK:
11653     return infoPtr->nSelectionMark;
11654 
11655   case LVM_GETSTRINGWIDTHA:
11656   case LVM_GETSTRINGWIDTHW:
11657     return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam,
11658                                     uMsg == LVM_GETSTRINGWIDTHW);
11659 
11660   case LVM_GETSUBITEMRECT:
11661     return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
11662 
11663   case LVM_GETTEXTBKCOLOR:
11664     return infoPtr->clrTextBk;
11665 
11666   case LVM_GETTEXTCOLOR:
11667     return infoPtr->clrText;
11668 
11669   /* case LVM_GETTILEINFO: */
11670 
11671   /* case LVM_GETTILEVIEWINFO: */
11672 
11673   case LVM_GETTOOLTIPS:
11674     if( !infoPtr->hwndToolTip )
11675         infoPtr->hwndToolTip = COMCTL32_CreateToolTip( hwnd );
11676     return (LRESULT)infoPtr->hwndToolTip;
11677 
11678   case LVM_GETTOPINDEX:
11679     return LISTVIEW_GetTopIndex(infoPtr);
11680 
11681   case LVM_GETUNICODEFORMAT:
11682     return (infoPtr->notifyFormat == NFR_UNICODE);
11683 
11684   case LVM_GETVIEW:
11685     return infoPtr->uView;
11686 
11687   case LVM_GETVIEWRECT:
11688     return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
11689 
11690   case LVM_GETWORKAREAS:
11691     FIXME("LVM_GETWORKAREAS: unimplemented\n");
11692     return FALSE;
11693 
11694   /* case LVM_HASGROUP: */
11695 
11696   case LVM_HITTEST:
11697     return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, TRUE);
11698 
11699   case LVM_INSERTCOLUMNA:
11700   case LVM_INSERTCOLUMNW:
11701     return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam,
11702                                   uMsg == LVM_INSERTCOLUMNW);
11703 
11704   /* case LVM_INSERTGROUP: */
11705 
11706   /* case LVM_INSERTGROUPSORTED: */
11707 
11708   case LVM_INSERTITEMA:
11709   case LVM_INSERTITEMW:
11710     return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, uMsg == LVM_INSERTITEMW);
11711 
11712   /* case LVM_INSERTMARKHITTEST: */
11713 
11714   /* case LVM_ISGROUPVIEWENABLED: */
11715 
11716   case LVM_ISITEMVISIBLE:
11717     return LISTVIEW_IsItemVisible(infoPtr, (INT)wParam);
11718 
11719   case LVM_MAPIDTOINDEX:
11720     return LISTVIEW_MapIdToIndex(infoPtr, (UINT)wParam);
11721 
11722   case LVM_MAPINDEXTOID:
11723     return LISTVIEW_MapIndexToId(infoPtr, (INT)wParam);
11724 
11725   /* case LVM_MOVEGROUP: */
11726 
11727   /* case LVM_MOVEITEMTOGROUP: */
11728 
11729   case LVM_REDRAWITEMS:
11730     return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
11731 
11732   /* case LVM_REMOVEALLGROUPS: */
11733 
11734   /* case LVM_REMOVEGROUP: */
11735 
11736   case LVM_SCROLL:
11737     return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
11738 
11739   case LVM_SETBKCOLOR:
11740     return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
11741 
11742   /* case LVM_SETBKIMAGE: */
11743 
11744   case LVM_SETCALLBACKMASK:
11745     infoPtr->uCallbackMask = (UINT)wParam;
11746     return TRUE;
11747 
11748   case LVM_SETCOLUMNA:
11749   case LVM_SETCOLUMNW:
11750     return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam,
11751                                uMsg == LVM_SETCOLUMNW);
11752 
11753   case LVM_SETCOLUMNORDERARRAY:
11754     return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
11755 
11756   case LVM_SETCOLUMNWIDTH:
11757     return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, (short)LOWORD(lParam));
11758 
11759   case LVM_SETEXTENDEDLISTVIEWSTYLE:
11760     return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
11761 
11762   /* case LVM_SETGROUPINFO: */
11763 
11764   /* case LVM_SETGROUPMETRICS: */
11765 
11766   case LVM_SETHOTCURSOR:
11767     return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
11768 
11769   case LVM_SETHOTITEM:
11770     return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
11771 
11772   case LVM_SETHOVERTIME:
11773     return LISTVIEW_SetHoverTime(infoPtr, (DWORD)lParam);
11774 
11775   case LVM_SETICONSPACING:
11776     if(lParam == -1)
11777         return LISTVIEW_SetIconSpacing(infoPtr, -1, -1);
11778     return LISTVIEW_SetIconSpacing(infoPtr, LOWORD(lParam), HIWORD(lParam));
11779 
11780   case LVM_SETIMAGELIST:
11781     return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
11782 
11783   /* case LVM_SETINFOTIP: */
11784 
11785   /* case LVM_SETINSERTMARK: */
11786 
11787   /* case LVM_SETINSERTMARKCOLOR: */
11788 
11789   case LVM_SETITEMA:
11790   case LVM_SETITEMW:
11791     {
11792 	if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
11793 	return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, (uMsg == LVM_SETITEMW));
11794     }
11795 
11796   case LVM_SETITEMCOUNT:
11797     return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
11798 
11799   case LVM_SETITEMPOSITION:
11800     {
11801 	POINT pt;
11802         pt.x = (short)LOWORD(lParam);
11803         pt.y = (short)HIWORD(lParam);
11804         return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, &pt);
11805     }
11806 
11807   case LVM_SETITEMPOSITION32:
11808     return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, (POINT*)lParam);
11809 
11810   case LVM_SETITEMSTATE:
11811     return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
11812 
11813   case LVM_SETITEMTEXTA:
11814   case LVM_SETITEMTEXTW:
11815     return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam,
11816                                  uMsg == LVM_SETITEMTEXTW);
11817 
11818   /* case LVM_SETOUTLINECOLOR: */
11819 
11820   /* case LVM_SETSELECTEDCOLUMN: */
11821 
11822   case LVM_SETSELECTIONMARK:
11823     return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
11824 
11825   case LVM_SETTEXTBKCOLOR:
11826     return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
11827 
11828   case LVM_SETTEXTCOLOR:
11829     return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
11830 
11831   /* case LVM_SETTILEINFO: */
11832 
11833   /* case LVM_SETTILEVIEWINFO: */
11834 
11835   /* case LVM_SETTILEWIDTH: */
11836 
11837   case LVM_SETTOOLTIPS:
11838     return (LRESULT)LISTVIEW_SetToolTips(infoPtr, (HWND)lParam);
11839 
11840   case LVM_SETUNICODEFORMAT:
11841     return LISTVIEW_SetUnicodeFormat(infoPtr, wParam);
11842 
11843   case LVM_SETVIEW:
11844     return LISTVIEW_SetView(infoPtr, wParam);
11845 
11846   /* case LVM_SETWORKAREAS: */
11847 
11848   /* case LVM_SORTGROUPS: */
11849 
11850   case LVM_SORTITEMS:
11851   case LVM_SORTITEMSEX:
11852     return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, wParam,
11853                               uMsg == LVM_SORTITEMSEX);
11854   case LVM_SUBITEMHITTEST:
11855     return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
11856 
11857   case LVM_UPDATE:
11858     return LISTVIEW_Update(infoPtr, (INT)wParam);
11859 
11860   case CCM_GETVERSION:
11861     return LISTVIEW_GetVersion(infoPtr);
11862 
11863   case CCM_SETVERSION:
11864     return LISTVIEW_SetVersion(infoPtr, wParam);
11865 
11866   case WM_CHAR:
11867     return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
11868 
11869   case WM_COMMAND:
11870     return LISTVIEW_Command(infoPtr, wParam, lParam);
11871 
11872   case WM_NCCREATE:
11873     return LISTVIEW_NCCreate(hwnd, wParam, (LPCREATESTRUCTW)lParam);
11874 
11875   case WM_CREATE:
11876     return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
11877 
11878   case WM_DESTROY:
11879     return LISTVIEW_Destroy(infoPtr);
11880 
11881   case WM_ENABLE:
11882     return LISTVIEW_Enable(infoPtr);
11883 
11884   case WM_ERASEBKGND:
11885     return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
11886 
11887   case WM_GETDLGCODE:
11888     return DLGC_WANTCHARS | DLGC_WANTARROWS;
11889 
11890   case WM_GETFONT:
11891     return (LRESULT)infoPtr->hFont;
11892 
11893   case WM_HSCROLL:
11894     return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0);
11895 
11896   case WM_KEYDOWN:
11897     return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
11898 
11899   case WM_KILLFOCUS:
11900     return LISTVIEW_KillFocus(infoPtr);
11901 
11902   case WM_LBUTTONDBLCLK:
11903     return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
11904 
11905   case WM_LBUTTONDOWN:
11906     return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
11907 
11908   case WM_LBUTTONUP:
11909     return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
11910 
11911   case WM_MOUSEMOVE:
11912     return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
11913 
11914   case WM_MOUSEHOVER:
11915     return LISTVIEW_MouseHover(infoPtr, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
11916 
11917   case WM_NCDESTROY:
11918     return LISTVIEW_NCDestroy(infoPtr);
11919 
11920   case WM_NCPAINT:
11921     return LISTVIEW_NCPaint(infoPtr, (HRGN)wParam);
11922 
11923   case WM_NOTIFY:
11924     return LISTVIEW_Notify(infoPtr, (LPNMHDR)lParam);
11925 
11926   case WM_NOTIFYFORMAT:
11927     return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
11928 
11929   case WM_PRINTCLIENT:
11930     return LISTVIEW_PrintClient(infoPtr, (HDC)wParam, (DWORD)lParam);
11931 
11932   case WM_PAINT:
11933     return LISTVIEW_WMPaint(infoPtr, (HDC)wParam);
11934 
11935   case WM_RBUTTONDBLCLK:
11936     return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
11937 
11938   case WM_RBUTTONDOWN:
11939     return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
11940 
11941   case WM_SETCURSOR:
11942     return LISTVIEW_SetCursor(infoPtr, wParam, lParam);
11943 
11944   case WM_SETFOCUS:
11945     return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
11946 
11947   case WM_SETFONT:
11948     return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
11949 
11950   case WM_SETREDRAW:
11951     return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
11952 
11953   case WM_SHOWWINDOW:
11954     return LISTVIEW_ShowWindow(infoPtr, wParam, lParam);
11955 
11956   case WM_STYLECHANGED:
11957     return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
11958 
11959   case WM_STYLECHANGING:
11960     return LISTVIEW_StyleChanging(wParam, (LPSTYLESTRUCT)lParam);
11961 
11962   case WM_SYSCOLORCHANGE:
11963     COMCTL32_RefreshSysColors();
11964 #ifdef __REACTOS__
11965     if (infoPtr->bDefaultBkColor)
11966     {
11967         LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
11968         infoPtr->bDefaultBkColor = TRUE;
11969         LISTVIEW_InvalidateList(infoPtr);
11970     }
11971 #endif
11972     return 0;
11973 
11974 /*	case WM_TIMER: */
11975   case WM_THEMECHANGED:
11976     return LISTVIEW_ThemeChanged(infoPtr);
11977 
11978   case WM_VSCROLL:
11979     return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0);
11980 
11981   case WM_MOUSEWHEEL:
11982       if (wParam & (MK_SHIFT | MK_CONTROL))
11983           return DefWindowProcW(hwnd, uMsg, wParam, lParam);
11984       return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
11985 
11986   case WM_WINDOWPOSCHANGED:
11987       if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE))
11988       {
11989           SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
11990                        SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
11991 
11992           if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (infoPtr->uView == LV_VIEW_DETAILS))
11993           {
11994               if (notify_measureitem(infoPtr)) LISTVIEW_InvalidateList(infoPtr);
11995           }
11996           LISTVIEW_Size(infoPtr, ((WINDOWPOS *)lParam)->cx, ((WINDOWPOS *)lParam)->cy);
11997       }
11998       return DefWindowProcW(hwnd, uMsg, wParam, lParam);
11999 
12000 /*	case WM_WININICHANGE: */
12001 
12002   default:
12003     if ((uMsg >= WM_USER) && (uMsg < WM_APP) && !COMCTL32_IsReflectedMessage(uMsg))
12004       ERR("unknown msg %04x wp=%08lx lp=%08lx\n", uMsg, wParam, lParam);
12005 
12006     return DefWindowProcW(hwnd, uMsg, wParam, lParam);
12007   }
12008 
12009 }
12010 
12011 /***
12012  * DESCRIPTION:
12013  * Registers the window class.
12014  *
12015  * PARAMETER(S):
12016  * None
12017  *
12018  * RETURN:
12019  * None
12020  */
LISTVIEW_Register(void)12021 void LISTVIEW_Register(void)
12022 {
12023     WNDCLASSW wndClass;
12024 
12025     ZeroMemory(&wndClass, sizeof(WNDCLASSW));
12026     wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
12027     wndClass.lpfnWndProc = LISTVIEW_WindowProc;
12028     wndClass.cbClsExtra = 0;
12029     wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
12030     wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
12031     wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
12032     wndClass.lpszClassName = WC_LISTVIEWW;
12033     RegisterClassW(&wndClass);
12034 }
12035 
12036 /***
12037  * DESCRIPTION:
12038  * Unregisters the window class.
12039  *
12040  * PARAMETER(S):
12041  * None
12042  *
12043  * RETURN:
12044  * None
12045  */
LISTVIEW_Unregister(void)12046 void LISTVIEW_Unregister(void)
12047 {
12048     UnregisterClassW(WC_LISTVIEWW, NULL);
12049 }
12050 
12051 /***
12052  * DESCRIPTION:
12053  * Handle any WM_COMMAND messages
12054  *
12055  * PARAMETER(S):
12056  * [I] infoPtr : valid pointer to the listview structure
12057  * [I] wParam : the first message parameter
12058  * [I] lParam : the second message parameter
12059  *
12060  * RETURN:
12061  *   Zero.
12062  */
LISTVIEW_Command(LISTVIEW_INFO * infoPtr,WPARAM wParam,LPARAM lParam)12063 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
12064 {
12065 
12066     TRACE("(%p %x %x %lx)\n", infoPtr, HIWORD(wParam), LOWORD(wParam), lParam);
12067 
12068     if (!infoPtr->hwndEdit) return 0;
12069 
12070     switch (HIWORD(wParam))
12071     {
12072 	case EN_UPDATE:
12073 	{
12074 	    /*
12075 	     * Adjust the edit window size
12076 	     */
12077 	    WCHAR buffer[1024];
12078 	    HDC           hdc = GetDC(infoPtr->hwndEdit);
12079             HFONT         hFont, hOldFont = 0;
12080 	    RECT	  rect;
12081 	    SIZE	  sz;
12082 
12083 	    if (!infoPtr->hwndEdit || !hdc) return 0;
12084 	    GetWindowTextW(infoPtr->hwndEdit, buffer, ARRAY_SIZE(buffer));
12085 	    GetWindowRect(infoPtr->hwndEdit, &rect);
12086 
12087             /* Select font to get the right dimension of the string */
12088             hFont = (HFONT)SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
12089             if (hFont)
12090             {
12091                 hOldFont = SelectObject(hdc, hFont);
12092             }
12093 
12094 	    if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
12095 	    {
12096                 TEXTMETRICW textMetric;
12097 
12098                 /* Add Extra spacing for the next character */
12099                 GetTextMetricsW(hdc, &textMetric);
12100                 sz.cx += (textMetric.tmMaxCharWidth * 2);
12101 
12102 		SetWindowPos(infoPtr->hwndEdit, NULL, 0, 0, sz.cx,
12103 		    rect.bottom - rect.top, SWP_DRAWFRAME | SWP_NOMOVE | SWP_NOZORDER);
12104 	    }
12105             if (hFont)
12106                 SelectObject(hdc, hOldFont);
12107 
12108 	    ReleaseDC(infoPtr->hwndEdit, hdc);
12109 
12110 	    break;
12111 	}
12112 	case EN_KILLFOCUS:
12113 	{
12114             if (infoPtr->notify_mask & NOTIFY_MASK_END_LABEL_EDIT)
12115                 LISTVIEW_CancelEditLabel(infoPtr);
12116             break;
12117 	}
12118 
12119 	default:
12120 	  return SendMessageW (infoPtr->hwndNotify, WM_COMMAND, wParam, lParam);
12121     }
12122 
12123     return 0;
12124 }
12125