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