[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-2011 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 int res;
564
565 n = min(min(n, lstrlenW(s1)), lstrlenW(s2));
566 res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
567 return res ? res - sizeof(WCHAR) : res;
568 }
569
570 /******** Debugging functions *****************************************/
571
572 static inline LPCSTR debugtext_t(LPCWSTR text, BOOL isW)
573 {
574 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
575 return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
576 }
577
578 static inline LPCSTR debugtext_tn(LPCWSTR text, BOOL isW, INT n)
579 {
580 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
581 n = min(textlenT(text, isW), n);
582 return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
583 }
584
585 static char* debug_getbuf(void)
586 {
587 static int index = 0;
588 static char buffers[DEBUG_BUFFERS][DEBUG_BUFFER_SIZE];
589 return buffers[index++ % DEBUG_BUFFERS];
590 }
591
592 static inline const char* debugrange(const RANGE *lprng)
593 {
594 if (!lprng) return "(null)";
595 return wine_dbg_sprintf("[%d, %d]", lprng->lower, lprng->upper);
596 }
597
598 static const char* debugscrollinfo(const SCROLLINFO *pScrollInfo)
599 {
600 char* buf = debug_getbuf(), *text = buf;
601 int len, size = DEBUG_BUFFER_SIZE;
602
603 if (pScrollInfo == NULL) return "(null)";
604 len = snprintf(buf, size, "{cbSize=%d, ", pScrollInfo->cbSize);
605 if (len == -1) goto end; buf += len; size -= len;
606 if (pScrollInfo->fMask & SIF_RANGE)
607 len = snprintf(buf, size, "nMin=%d, nMax=%d, ", pScrollInfo->nMin, pScrollInfo->nMax);
608 else len = 0;
609 if (len == -1) goto end; buf += len; size -= len;
610 if (pScrollInfo->fMask & SIF_PAGE)
611 len = snprintf(buf, size, "nPage=%u, ", pScrollInfo->nPage);
612 else len = 0;
613 if (len == -1) goto end; buf += len; size -= len;
614 if (pScrollInfo->fMask & SIF_POS)
615 len = snprintf(buf, size, "nPos=%d, ", pScrollInfo->nPos);
616 else len = 0;
617 if (len == -1) goto end; buf += len; size -= len;
618 if (pScrollInfo->fMask & SIF_TRACKPOS)
619 len = snprintf(buf, size, "nTrackPos=%d, ", pScrollInfo->nTrackPos);
620 else len = 0;
621 if (len == -1) goto end; buf += len;
622 goto undo;
623 end:
624 buf = text + strlen(text);
625 undo:
626 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
627 return text;
628 }
629
630 static const char* debugnmlistview(const NMLISTVIEW *plvnm)
631 {
632 if (!plvnm) return "(null)";
633 return wine_dbg_sprintf("iItem=%d, iSubItem=%d, uNewState=0x%x,"
634 " uOldState=0x%x, uChanged=0x%x, ptAction=%s, lParam=%ld",
635 plvnm->iItem, plvnm->iSubItem, plvnm->uNewState, plvnm->uOldState,
636 plvnm->uChanged, wine_dbgstr_point(&plvnm->ptAction), plvnm->lParam);
637 }
638
639 static const char* debuglvitem_t(const LVITEMW *lpLVItem, BOOL isW)
640 {
641 char* buf = debug_getbuf(), *text = buf;
642 int len, size = DEBUG_BUFFER_SIZE;
643
644 if (lpLVItem == NULL) return "(null)";
645 len = snprintf(buf, size, "{iItem=%d, iSubItem=%d, ", lpLVItem->iItem, lpLVItem->iSubItem);
646 if (len == -1) goto end; buf += len; size -= len;
647 if (lpLVItem->mask & LVIF_STATE)
648 len = snprintf(buf, size, "state=%x, stateMask=%x, ", lpLVItem->state, lpLVItem->stateMask);
649 else len = 0;
650 if (len == -1) goto end; buf += len; size -= len;
651 if (lpLVItem->mask & LVIF_TEXT)
652 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem->pszText, isW, 80), lpLVItem->cchTextMax);
653 else len = 0;
654 if (len == -1) goto end; buf += len; size -= len;
655 if (lpLVItem->mask & LVIF_IMAGE)
656 len = snprintf(buf, size, "iImage=%d, ", lpLVItem->iImage);
657 else len = 0;
658 if (len == -1) goto end; buf += len; size -= len;
659 if (lpLVItem->mask & LVIF_PARAM)
660 len = snprintf(buf, size, "lParam=%lx, ", lpLVItem->lParam);
661 else len = 0;
662 if (len == -1) goto end; buf += len; size -= len;
663 if (lpLVItem->mask & LVIF_INDENT)
664 len = snprintf(buf, size, "iIndent=%d, ", lpLVItem->iIndent);
665 else len = 0;
666 if (len == -1) goto end; buf += len;
667 goto undo;
668 end:
669 buf = text + strlen(text);
670 undo:
671 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
672 return text;
673 }
674
675 static const char* debuglvcolumn_t(const LVCOLUMNW *lpColumn, BOOL isW)
676 {
677 char* buf = debug_getbuf(), *text = buf;
678 int len, size = DEBUG_BUFFER_SIZE;
679
680 if (lpColumn == NULL) return "(null)";
681 len = snprintf(buf, size, "{");
682 if (len == -1) goto end; buf += len; size -= len;
683 if (lpColumn->mask & LVCF_SUBITEM)
684 len = snprintf(buf, size, "iSubItem=%d, ", lpColumn->iSubItem);
685 else len = 0;
686 if (len == -1) goto end; buf += len; size -= len;
687 if (lpColumn->mask & LVCF_FMT)
688 len = snprintf(buf, size, "fmt=%x, ", lpColumn->fmt);
689 else len = 0;
690 if (len == -1) goto end; buf += len; size -= len;
691 if (lpColumn->mask & LVCF_WIDTH)
692 len = snprintf(buf, size, "cx=%d, ", lpColumn->cx);
693 else len = 0;
694 if (len == -1) goto end; buf += len; size -= len;
695 if (lpColumn->mask & LVCF_TEXT)
696 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn->pszText, isW, 80), lpColumn->cchTextMax);
697 else len = 0;
698 if (len == -1) goto end; buf += len; size -= len;
699 if (lpColumn->mask & LVCF_IMAGE)
700 len = snprintf(buf, size, "iImage=%d, ", lpColumn->iImage);
701 else len = 0;
702 if (len == -1) goto end; buf += len; size -= len;
703 if (lpColumn->mask & LVCF_ORDER)
704 len = snprintf(buf, size, "iOrder=%d, ", lpColumn->iOrder);
705 else len = 0;
706 if (len == -1) goto end; buf += len;
707 goto undo;
708 end:
709 buf = text + strlen(text);
710 undo:
711 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
712 return text;
713 }
714
715 static const char* debuglvhittestinfo(const LVHITTESTINFO *lpht)
716 {
717 if (!lpht) return "(null)";
718
719 return wine_dbg_sprintf("{pt=%s, flags=0x%x, iItem=%d, iSubItem=%d}",
720 wine_dbgstr_point(&lpht->pt), lpht->flags, lpht->iItem, lpht->iSubItem);
721 }
722
723 /* Return the corresponding text for a given scroll value */
724 static inline LPCSTR debugscrollcode(int nScrollCode)
725 {
726 switch(nScrollCode)
727 {
728 case SB_LINELEFT: return "SB_LINELEFT";
729 case SB_LINERIGHT: return "SB_LINERIGHT";
730 case SB_PAGELEFT: return "SB_PAGELEFT";
731 case SB_PAGERIGHT: return "SB_PAGERIGHT";
732 case SB_THUMBPOSITION: return "SB_THUMBPOSITION";
733 case SB_THUMBTRACK: return "SB_THUMBTRACK";
734 case SB_ENDSCROLL: return "SB_ENDSCROLL";
735 case SB_INTERNAL: return "SB_INTERNAL";
736 default: return "unknown";
737 }
738 }
739
740
741 /******** Notification functions ************************************/
742
743 static int get_ansi_notification(UINT unicodeNotificationCode)
744 {
745 switch (unicodeNotificationCode)
746 {
747 case LVN_BEGINLABELEDITA:
748 case LVN_BEGINLABELEDITW: return LVN_BEGINLABELEDITA;
749 case LVN_ENDLABELEDITA:
750 case LVN_ENDLABELEDITW: return LVN_ENDLABELEDITA;
751 case LVN_GETDISPINFOA:
752 case LVN_GETDISPINFOW: return LVN_GETDISPINFOA;
753 case LVN_SETDISPINFOA:
754 case LVN_SETDISPINFOW: return LVN_SETDISPINFOA;
755 case LVN_ODFINDITEMA:
756 case LVN_ODFINDITEMW: return LVN_ODFINDITEMA;
757 case LVN_GETINFOTIPA:
758 case LVN_GETINFOTIPW: return LVN_GETINFOTIPA;
759 /* header forwards */
760 case HDN_TRACKA:
761 case HDN_TRACKW: return HDN_TRACKA;
762 case HDN_ENDTRACKA:
763 case HDN_ENDTRACKW: return HDN_ENDTRACKA;
764 case HDN_BEGINDRAG: return HDN_BEGINDRAG;
765 case HDN_ENDDRAG: return HDN_ENDDRAG;
766 case HDN_ITEMCHANGINGA:
767 case HDN_ITEMCHANGINGW: return HDN_ITEMCHANGINGA;
768 case HDN_ITEMCHANGEDA:
769 case HDN_ITEMCHANGEDW: return HDN_ITEMCHANGEDA;
770 case HDN_ITEMCLICKA:
771 case HDN_ITEMCLICKW: return HDN_ITEMCLICKA;
772 case HDN_DIVIDERDBLCLICKA:
773 case HDN_DIVIDERDBLCLICKW: return HDN_DIVIDERDBLCLICKA;
774 default: break;
775 }
776 FIXME("unknown notification %x\n", unicodeNotificationCode);
777 return unicodeNotificationCode;
778 }
779
780 /* forwards header notifications to listview parent */
781 static LRESULT notify_forward_header(const LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
782 {
783 NMHEADERA nmhA;
784 HDITEMA hditema;
785 HD_TEXTFILTERA textfilter;
786 LPSTR text = NULL, filter = NULL;
787 LRESULT ret;
788
789 /* on unicode format exit earlier */
790 if (infoPtr->notifyFormat == NFR_UNICODE)
791 return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, lpnmh->hdr.idFrom,
792 (LPARAM)lpnmh);
793
794 /* header always supplies unicode notifications,
795 all we have to do is to convert strings to ANSI */
796 nmhA = *(const NMHEADERA*)lpnmh;
797 if (lpnmh->pitem)
798 {
799 hditema = *(HDITEMA*)lpnmh->pitem;
800 nmhA.pitem = &hditema;
801 /* convert item text */
802 if (lpnmh->pitem->mask & HDI_TEXT)
803 {
804 hditema.pszText = NULL;
805 Str_SetPtrWtoA(&hditema.pszText, lpnmh->pitem->pszText);
806 text = hditema.pszText;
807 }
808 /* convert filter text */
809 if ((lpnmh->pitem->mask & HDI_FILTER) && (lpnmh->pitem->type == HDFT_ISSTRING) &&
810 lpnmh->pitem->pvFilter)
811 {
812 hditema.pvFilter = &textfilter;
813 textfilter = *(HD_TEXTFILTERA*)(lpnmh->pitem->pvFilter);
814 textfilter.pszText = NULL;
815 Str_SetPtrWtoA(&textfilter.pszText, ((HD_TEXTFILTERW*)lpnmh->pitem->pvFilter)->pszText);
816 filter = textfilter.pszText;
817 }
818 }
819 nmhA.hdr.code = get_ansi_notification(lpnmh->hdr.code);
820
821 ret = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, nmhA.hdr.idFrom,
822 (LPARAM)&nmhA);
823
824 /* cleanup */
825 Free(text);
826 Free(filter);
827
828 return ret;
829 }
830
831 static LRESULT notify_hdr(const LISTVIEW_INFO *infoPtr, INT code, LPNMHDR pnmh)
832 {
833 LRESULT result;
834
835 TRACE("(code=%d)\n", code);
836
837 pnmh->hwndFrom = infoPtr->hwndSelf;
838 pnmh->idFrom = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
839 pnmh->code = code;
840 result = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, pnmh->idFrom, (LPARAM)pnmh);
841
842 TRACE(" <= %ld\n", result);
843
844 return result;
845 }
846
847 static inline BOOL notify(const LISTVIEW_INFO *infoPtr, INT code)
848 {
849 NMHDR nmh;
850 HWND hwnd = infoPtr->hwndSelf;
851 notify_hdr(infoPtr, code, &nmh);
852 return IsWindow(hwnd);
853 }
854
855 static inline void notify_itemactivate(const LISTVIEW_INFO *infoPtr, const LVHITTESTINFO *htInfo)
856 {
857 NMITEMACTIVATE nmia;
858 LVITEMW item;
859
860 if (htInfo) {
861 nmia.uNewState = 0;
862 nmia.uOldState = 0;
863 nmia.uChanged = 0;
864 nmia.uKeyFlags = 0;
865
866 item.mask = LVIF_PARAM|LVIF_STATE;
867 item.iItem = htInfo->iItem;
868 item.iSubItem = 0;
869 item.stateMask = (UINT)-1;
870 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) {
871 nmia.lParam = item.lParam;
872 nmia.uOldState = item.state;
873 nmia.uNewState = item.state | LVIS_ACTIVATING;
874 nmia.uChanged = LVIF_STATE;
875 }
876
877 nmia.iItem = htInfo->iItem;
878 nmia.iSubItem = htInfo->iSubItem;
879 nmia.ptAction = htInfo->pt;
880
881 if (GetKeyState(VK_SHIFT) & 0x8000) nmia.uKeyFlags |= LVKF_SHIFT;
882 if (GetKeyState(VK_CONTROL) & 0x8000) nmia.uKeyFlags |= LVKF_CONTROL;
883 if (GetKeyState(VK_MENU) & 0x8000) nmia.uKeyFlags |= LVKF_ALT;
884 }
885 notify_hdr(infoPtr, LVN_ITEMACTIVATE, (LPNMHDR)&nmia);
886 }
887
888 static inline LRESULT notify_listview(const LISTVIEW_INFO *infoPtr, INT code, LPNMLISTVIEW plvnm)
889 {
890 TRACE("(code=%d, plvnm=%s)\n", code, debugnmlistview(plvnm));
891 return notify_hdr(infoPtr, code, (LPNMHDR)plvnm);
892 }
893
894 static BOOL notify_click(const LISTVIEW_INFO *infoPtr, INT code, const LVHITTESTINFO *lvht)
895 {
896 NMITEMACTIVATE nmia;
897 LVITEMW item;
898 HWND hwnd = infoPtr->hwndSelf;
899
900 TRACE("code=%d, lvht=%s\n", code, debuglvhittestinfo(lvht));
901 ZeroMemory(&nmia, sizeof(nmia));
902 nmia.iItem = lvht->iItem;
903 nmia.iSubItem = lvht->iSubItem;
904 nmia.ptAction = lvht->pt;
905 item.mask = LVIF_PARAM;
906 item.iItem = lvht->iItem;
907 item.iSubItem = 0;
908 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmia.lParam = item.lParam;
909 notify_hdr(infoPtr, code, (LPNMHDR)&nmia);
910 return IsWindow(hwnd);
911 }
912
913 static BOOL notify_deleteitem(const LISTVIEW_INFO *infoPtr, INT nItem)
914 {
915 NMLISTVIEW nmlv;
916 LVITEMW item;
917 HWND hwnd = infoPtr->hwndSelf;
918
919 ZeroMemory(&nmlv, sizeof (NMLISTVIEW));
920 nmlv.iItem = nItem;
921 item.mask = LVIF_PARAM;
922 item.iItem = nItem;
923 item.iSubItem = 0;
924 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
925 notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
926 return IsWindow(hwnd);
927 }
928
929 /*
930 Send notification. depends on dispinfoW having same
931 structure as dispinfoA.
932 infoPtr : listview struct
933 code : *Unicode* notification code
934 pdi : dispinfo structure (can be unicode or ansi)
935 isW : TRUE if dispinfo is Unicode
936 */
937 static BOOL notify_dispinfoT(const LISTVIEW_INFO *infoPtr, UINT code, LPNMLVDISPINFOW pdi, BOOL isW)
938 {
939 INT length = 0, ret_length;
940 LPWSTR buffer = NULL, ret_text;
941 BOOL return_ansi = FALSE;
942 BOOL return_unicode = FALSE;
943 BOOL ret;
944
945 if ((pdi->item.mask & LVIF_TEXT) && is_text(pdi->item.pszText))
946 {
947 return_unicode = ( isW && infoPtr->notifyFormat == NFR_ANSI);
948 return_ansi = (!isW && infoPtr->notifyFormat == NFR_UNICODE);
949 }
950
951 ret_length = pdi->item.cchTextMax;
952 ret_text = pdi->item.pszText;
953
954 if (return_unicode || return_ansi)
955 {
956 if (code != LVN_GETDISPINFOW)
957 {
958 length = return_ansi ?
959 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0):
960 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
961 }
962 else
963 {
964 length = pdi->item.cchTextMax;
965 *pdi->item.pszText = 0; /* make sure we don't process garbage */
966 }
967
968 buffer = Alloc( (return_ansi ? sizeof(WCHAR) : sizeof(CHAR)) * length);
969 if (!buffer) return FALSE;
970
971 if (return_ansi)
972 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1,
973 buffer, length);
974 else
975 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) buffer,
976 length, NULL, NULL);
977
978 pdi->item.pszText = buffer;
979 pdi->item.cchTextMax = length;
980 }
981
982 if (infoPtr->notifyFormat == NFR_ANSI)
983 code = get_ansi_notification(code);
984
985 TRACE(" pdi->item=%s\n", debuglvitem_t(&pdi->item, infoPtr->notifyFormat != NFR_ANSI));
986 ret = notify_hdr(infoPtr, code, &pdi->hdr);
987 TRACE(" resulting code=%d\n", pdi->hdr.code);
988
989 if (return_ansi || return_unicode)
990 {
991 if (return_ansi && (pdi->hdr.code == LVN_GETDISPINFOA))
992 {
993 strcpy((char*)ret_text, (char*)pdi->item.pszText);
994 }
995 else if (return_unicode && (pdi->hdr.code == LVN_GETDISPINFOW))
996 {
997 strcpyW(ret_text, pdi->item.pszText);
998 }
999 else if (return_ansi) /* note : pointer can be changed by app ! */
1000 {
1001 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) ret_text,
1002 ret_length, NULL, NULL);
1003 }
1004 else
1005 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
1006 ret_text, ret_length);
1007
1008 pdi->item.pszText = ret_text; /* restores our buffer */
1009 pdi->item.cchTextMax = ret_length;
1010
1011 Free(buffer);
1012 return ret;
1013 }
1014
1015 /* if dipsinfo holder changed notification code then convert */
1016 if (!isW && (pdi->hdr.code == LVN_GETDISPINFOW) && (pdi->item.mask & LVIF_TEXT))
1017 {
1018 length = WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
1019
1020 buffer = Alloc(length * sizeof(CHAR));
1021 if (!buffer) return FALSE;
1022
1023 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) buffer,
1024 ret_length, NULL, NULL);
1025
1026 strcpy((LPSTR)pdi->item.pszText, (LPSTR)buffer);
1027 Free(buffer);
1028 }
1029
1030 return ret;
1031 }
1032
1033 static void customdraw_fill(NMLVCUSTOMDRAW *lpnmlvcd, const LISTVIEW_INFO *infoPtr, HDC hdc,
1034 const RECT *rcBounds, const LVITEMW *lplvItem)
1035 {
1036 ZeroMemory(lpnmlvcd, sizeof(NMLVCUSTOMDRAW));
1037 lpnmlvcd->nmcd.hdc = hdc;
1038 lpnmlvcd->nmcd.rc = *rcBounds;
1039 lpnmlvcd->clrTextBk = infoPtr->clrTextBk;
1040 lpnmlvcd->clrText = infoPtr->clrText;
1041 if (!lplvItem) return;
1042 lpnmlvcd->nmcd.dwItemSpec = lplvItem->iItem + 1;
1043 lpnmlvcd->iSubItem = lplvItem->iSubItem;
1044 if (lplvItem->state & LVIS_SELECTED) lpnmlvcd->nmcd.uItemState |= CDIS_SELECTED;
1045 if (lplvItem->state & LVIS_FOCUSED) lpnmlvcd->nmcd.uItemState |= CDIS_FOCUS;
1046 if (lplvItem->iItem == infoPtr->nHotItem) lpnmlvcd->nmcd.uItemState |= CDIS_HOT;
1047 lpnmlvcd->nmcd.lItemlParam = lplvItem->lParam;
1048 }
1049
1050 static inline DWORD notify_customdraw (const LISTVIEW_INFO *infoPtr, DWORD dwDrawStage, NMLVCUSTOMDRAW *lpnmlvcd)
1051 {
1052 BOOL isForItem = (lpnmlvcd->nmcd.dwItemSpec != 0);
1053 DWORD result;
1054
1055 lpnmlvcd->nmcd.dwDrawStage = dwDrawStage;
1056 if (isForItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_ITEM;
1057 if (lpnmlvcd->iSubItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_SUBITEM;
1058 if (isForItem) lpnmlvcd->nmcd.dwItemSpec--;
1059 result = notify_hdr(infoPtr, NM_CUSTOMDRAW, &lpnmlvcd->nmcd.hdr);
1060 if (isForItem) lpnmlvcd->nmcd.dwItemSpec++;
1061 return result;
1062 }
1063
1064 static void prepaint_setup (const LISTVIEW_INFO *infoPtr, HDC hdc, NMLVCUSTOMDRAW *lpnmlvcd, BOOL SubItem)
1065 {
1066 if (lpnmlvcd->clrTextBk == CLR_DEFAULT)
1067 lpnmlvcd->clrTextBk = comctl32_color.clrWindow;
1068 if (lpnmlvcd->clrText == CLR_DEFAULT)
1069 lpnmlvcd->clrText = comctl32_color.clrWindowText;
1070
1071 /* apparently, for selected items, we have to override the returned values */
1072 if (!SubItem)
1073 {
1074 if (lpnmlvcd->nmcd.uItemState & CDIS_SELECTED)
1075 {
1076 if (infoPtr->bFocus)
1077 {
1078 lpnmlvcd->clrTextBk = comctl32_color.clrHighlight;
1079 lpnmlvcd->clrText = comctl32_color.clrHighlightText;
1080 }
1081 else if (infoPtr->dwStyle & LVS_SHOWSELALWAYS)
1082 {
1083 lpnmlvcd->clrTextBk = comctl32_color.clr3dFace;
1084 lpnmlvcd->clrText = comctl32_color.clrBtnText;
1085 }
1086 }
1087 }
1088
1089 /* Set the text attributes */
1090 if (lpnmlvcd->clrTextBk != CLR_NONE)
1091 {
1092 SetBkMode(hdc, OPAQUE);
1093 SetBkColor(hdc,lpnmlvcd->clrTextBk);
1094 }
1095 else
1096 SetBkMode(hdc, TRANSPARENT);
1097 SetTextColor(hdc, lpnmlvcd->clrText);
1098 }
1099
1100 static inline DWORD notify_postpaint (const LISTVIEW_INFO *infoPtr, NMLVCUSTOMDRAW *lpnmlvcd)
1101 {
1102 return notify_customdraw(infoPtr, CDDS_POSTPAINT, lpnmlvcd);
1103 }
1104
1105 /* returns TRUE when repaint needed, FALSE otherwise */
1106 static BOOL notify_measureitem(LISTVIEW_INFO *infoPtr)
1107 {
1108 MEASUREITEMSTRUCT mis;
1109 mis.CtlType = ODT_LISTVIEW;
1110 mis.CtlID = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
1111 mis.itemID = -1;
1112 mis.itemWidth = 0;
1113 mis.itemData = 0;
1114 mis.itemHeight= infoPtr->nItemHeight;
1115 SendMessageW(infoPtr->hwndNotify, WM_MEASUREITEM, mis.CtlID, (LPARAM)&mis);
1116 if (infoPtr->nItemHeight != max(mis.itemHeight, 1))
1117 {
1118 infoPtr->nMeasureItemHeight = infoPtr->nItemHeight = max(mis.itemHeight, 1);
1119 return TRUE;
1120 }
1121 return FALSE;
1122 }
1123
1124 /******** Item iterator functions **********************************/
1125
1126 static RANGES ranges_create(int count);
1127 static void ranges_destroy(RANGES ranges);
1128 static BOOL ranges_add(RANGES ranges, RANGE range);
1129 static BOOL ranges_del(RANGES ranges, RANGE range);
1130 static void ranges_dump(RANGES ranges);
1131
1132 static inline BOOL ranges_additem(RANGES ranges, INT nItem)
1133 {
1134 RANGE range = { nItem, nItem + 1 };
1135
1136 return ranges_add(ranges, range);
1137 }
1138
1139 static inline BOOL ranges_delitem(RANGES ranges, INT nItem)
1140 {
1141 RANGE range = { nItem, nItem + 1 };
1142
1143 return ranges_del(ranges, range);
1144 }
1145
1146 /***
1147 * ITERATOR DOCUMENTATION
1148 *
1149 * The iterator functions allow for easy, and convenient iteration
1150 * over items of interest in the list. Typically, you create a
1151 * iterator, use it, and destroy it, as such:
1152 * ITERATOR i;
1153 *
1154 * iterator_xxxitems(&i, ...);
1155 * while (iterator_{prev,next}(&i)
1156 * {
1157 * //code which uses i.nItem
1158 * }
1159 * iterator_destroy(&i);
1160 *
1161 * where xxx is either: framed, or visible.
1162 * Note that it is important that the code destroys the iterator
1163 * after it's done with it, as the creation of the iterator may
1164 * allocate memory, which thus needs to be freed.
1165 *
1166 * You can iterate both forwards, and backwards through the list,
1167 * by using iterator_next or iterator_prev respectively.
1168 *
1169 * Lower numbered items are draw on top of higher number items in
1170 * LVS_ICON, and LVS_SMALLICON (which are the only modes where
1171 * items may overlap). So, to test items, you should use
1172 * iterator_next
1173 * which lists the items top to bottom (in Z-order).
1174 * For drawing items, you should use
1175 * iterator_prev
1176 * which lists the items bottom to top (in Z-order).
1177 * If you keep iterating over the items after the end-of-items
1178 * marker (-1) is returned, the iterator will start from the
1179 * beginning. Typically, you don't need to test for -1,
1180 * because iterator_{next,prev} will return TRUE if more items
1181 * are to be iterated over, or FALSE otherwise.
1182 *
1183 * Note: the iterator is defined to be bidirectional. That is,
1184 * any number of prev followed by any number of next, or
1185 * five versa, should leave the iterator at the same item:
1186 * prev * n, next * n = next * n, prev * n
1187 *
1188 * The iterator has a notion of an out-of-order, special item,
1189 * which sits at the start of the list. This is used in
1190 * LVS_ICON, and LVS_SMALLICON mode to handle the focused item,
1191 * which needs to be first, as it may overlap other items.
1192 *
1193 * The code is a bit messy because we have:
1194 * - a special item to deal with
1195 * - simple range, or composite range
1196 * - empty range.
1197 * If you find bugs, or want to add features, please make sure you
1198 * always check/modify *both* iterator_prev, and iterator_next.
1199 */
1200
1201 /****
1202 * This function iterates through the items in increasing order,
1203 * but prefixed by the special item, then -1. That is:
1204 * special, 1, 2, 3, ..., n, -1.
1205 * Each item is listed only once.
1206 */
1207 static inline BOOL iterator_next(ITERATOR* i)
1208 {
1209 if (i->nItem == -1)
1210 {
1211 i->nItem = i->nSpecial;
1212 if (i->nItem != -1) return TRUE;
1213 }
1214 if (i->nItem == i->nSpecial)
1215 {
1216 if (i->ranges) i->index = 0;
1217 goto pickarange;
1218 }
1219
1220 i->nItem++;
1221 testitem:
1222 if (i->nItem == i->nSpecial) i->nItem++;
1223 if (i->nItem < i->range.upper) return TRUE;
1224
1225 pickarange:
1226 if (i->ranges)
1227 {
1228 if (i->index < DPA_GetPtrCount(i->ranges->hdpa))
1229 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, i->index++);
1230 else goto end;
1231 }
1232 else if (i->nItem >= i->range.upper) goto end;
1233
1234 i->nItem = i->range.lower;
1235 if (i->nItem >= 0) goto testitem;
1236 end:
1237 i->nItem = -1;
1238 return FALSE;
1239 }
1240
1241 /****
1242 * This function iterates through the items in decreasing order,
1243 * followed by the special item, then -1. That is:
1244 * n, n-1, ..., 3, 2, 1, special, -1.
1245 * Each item is listed only once.
1246 */
1247 static inline BOOL iterator_prev(ITERATOR* i)
1248 {
1249 BOOL start = FALSE;
1250
1251 if (i->nItem == -1)
1252 {
1253 start = TRUE;
1254 if (i->ranges) i->index = DPA_GetPtrCount(i->ranges->hdpa);
1255 goto pickarange;
1256 }
1257 if (i->nItem == i->nSpecial)
1258 {
1259 i->nItem = -1;
1260 return FALSE;
1261 }
1262
1263 testitem:
1264 i->nItem--;
1265 if (i->nItem == i->nSpecial) i->nItem--;
1266 if (i->nItem >= i->range.lower) return TRUE;
1267
1268 pickarange:
1269 if (i->ranges)
1270 {
1271 if (i->index > 0)
1272 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, --i->index);
1273 else goto end;
1274 }
1275 else if (!start && i->nItem < i->range.lower) goto end;
1276
1277 i->nItem = i->range.upper;
1278 if (i->nItem > 0) goto testitem;
1279 end:
1280 return (i->nItem = i->nSpecial) != -1;
1281 }
1282
1283 static RANGE iterator_range(const ITERATOR *i)
1284 {
1285 RANGE range;
1286
1287 if (!i->ranges) return i->range;
1288
1289 if (DPA_GetPtrCount(i->ranges->hdpa) > 0)
1290 {
1291 range.lower = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, 0)).lower;
1292 range.upper = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, DPA_GetPtrCount(i->ranges->hdpa) - 1)).upper;
1293 }
1294 else range.lower = range.upper = 0;
1295
1296 return range;
1297 }
1298
1299 /***
1300 * Releases resources associated with this ierator.
1301 */
1302 static inline void iterator_destroy(const ITERATOR *i)
1303 {
1304 ranges_destroy(i->ranges);
1305 }
1306
1307 /***
1308 * Create an empty iterator.
1309 */
1310 static inline BOOL iterator_empty(ITERATOR* i)
1311 {
1312 ZeroMemory(i, sizeof(*i));
1313 i->nItem = i->nSpecial = i->range.lower = i->range.upper = -1;
1314 return TRUE;
1315 }
1316
1317 /***
1318 * Create an iterator over a range.
1319 */
1320 static inline BOOL iterator_rangeitems(ITERATOR* i, RANGE range)
1321 {
1322 iterator_empty(i);
1323 i->range = range;
1324 return TRUE;
1325 }
1326
1327 /***
1328 * Create an iterator over a bunch of ranges.
1329 * Please note that the iterator will take ownership of the ranges,
1330 * and will free them upon destruction.
1331 */
1332 static inline BOOL iterator_rangesitems(ITERATOR* i, RANGES ranges)
1333 {
1334 iterator_empty(i);
1335 i->ranges = ranges;
1336 return TRUE;
1337 }
1338
1339 /***
1340 * Creates an iterator over the items which intersect frame.
1341 * Uses absolute coordinates rather than compensating for the current offset.
1342 */
1343 static BOOL iterator_frameditems_absolute(ITERATOR* i, const LISTVIEW_INFO* infoPtr, const RECT *frame)
1344 {
1345 RECT rcItem, rcTemp;
1346
1347 /* in case we fail, we want to return an empty iterator */
1348 if (!iterator_empty(i)) return FALSE;
1349
1350 TRACE("(frame=%s)\n", wine_dbgstr_rect(frame));
1351
1352 if (infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON)
1353 {
1354 INT nItem;
1355
1356 if (infoPtr->uView == LV_VIEW_ICON && infoPtr->nFocusedItem != -1)
1357 {
1358 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcItem);
1359 if (IntersectRect(&rcTemp, &rcItem, frame))
1360 i->nSpecial = infoPtr->nFocusedItem;
1361 }
1362 if (!(iterator_rangesitems(i, ranges_create(50)))) return FALSE;
1363 /* to do better here, we need to have PosX, and PosY sorted */
1364 TRACE("building icon ranges:\n");
1365 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
1366 {
1367 rcItem.left = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1368 rcItem.top = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1369 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1370 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1371 if (IntersectRect(&rcTemp, &rcItem, frame))
1372 ranges_additem(i->ranges, nItem);
1373 }
1374 return TRUE;
1375 }
1376 else if (infoPtr->uView == LV_VIEW_DETAILS)
1377 {
1378 RANGE range;
1379
1380 if (frame->left >= infoPtr->nItemWidth) return TRUE;
1381 if (frame->top >= infoPtr->nItemHeight * infoPtr->nItemCount) return TRUE;
1382
1383 range.lower = max(frame->top / infoPtr->nItemHeight, 0);
1384 range.upper = min((frame->bottom - 1) / infoPtr->nItemHeight, infoPtr->nItemCount - 1) + 1;
1385 if (range.upper <= range.lower) return TRUE;
1386 if (!iterator_rangeitems(i, range)) return FALSE;
1387 TRACE(" report=%s\n", debugrange(&i->range));
1388 }
1389 else
1390 {
1391 INT nPerCol = max((infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight, 1);
1392 INT nFirstRow = max(frame->top / infoPtr->nItemHeight, 0);
1393 INT nLastRow = min((frame->bottom - 1) / infoPtr->nItemHeight, nPerCol - 1);
1394 INT nFirstCol;
1395 INT nLastCol;
1396 INT lower;
1397 RANGE item_range;
1398 INT nCol;
1399
1400 if (infoPtr->nItemWidth)
1401 {
1402 nFirstCol = max(frame->left / infoPtr->nItemWidth, 0);
1403 nLastCol = min((frame->right - 1) / infoPtr->nItemWidth, (infoPtr->nItemCount + nPerCol - 1) / nPerCol);
1404 }
1405 else
1406 {
1407 nFirstCol = max(frame->left, 0);
1408 nLastCol = min(frame->right - 1, (infoPtr->nItemCount + nPerCol - 1) / nPerCol);
1409 }
1410
1411 lower = nFirstCol * nPerCol + nFirstRow;
1412
1413 TRACE("nPerCol=%d, nFirstRow=%d, nLastRow=%d, nFirstCol=%d, nLastCol=%d, lower=%d\n",
1414 nPerCol, nFirstRow, nLastRow, nFirstCol, nLastCol, lower);
1415
1416 if (nLastCol < nFirstCol || nLastRow < nFirstRow) return TRUE;
1417
1418 if (!(iterator_rangesitems(i, ranges_create(nLastCol - nFirstCol + 1)))) return FALSE;
1419 TRACE("building list ranges:\n");
1420 for (nCol = nFirstCol; nCol <= nLastCol; nCol++)
1421 {
1422 item_range.lower = nCol * nPerCol + nFirstRow;
1423 if(item_range.lower >= infoPtr->nItemCount) break;
1424 item_range.upper = min(nCol * nPerCol + nLastRow + 1, infoPtr->nItemCount);
1425 TRACE(" list=%s\n", debugrange(&item_range));
1426 ranges_add(i->ranges, item_range);
1427 }
1428 }
1429
1430 return TRUE;
1431 }
1432
1433 /***
1434 * Creates an iterator over the items which intersect lprc.
1435 */
1436 static BOOL iterator_frameditems(ITERATOR* i, const LISTVIEW_INFO* infoPtr, const RECT *lprc)
1437 {
1438 RECT frame = *lprc;
1439 POINT Origin;
1440
1441 TRACE("(lprc=%s)\n", wine_dbgstr_rect(lprc));
1442
1443 LISTVIEW_GetOrigin(infoPtr, &Origin);
1444 OffsetRect(&frame, -Origin.x, -Origin.y);
1445
1446 return iterator_frameditems_absolute(i, infoPtr, &frame);
1447 }
1448
1449 /***
1450 * Creates an iterator over the items which intersect the visible region of hdc.
1451 */
1452 static BOOL iterator_visibleitems(ITERATOR *i, const LISTVIEW_INFO *infoPtr, HDC hdc)
1453 {
1454 POINT Origin, Position;
1455 RECT rcItem, rcClip;
1456 INT rgntype;
1457
1458 rgntype = GetClipBox(hdc, &rcClip);
1459 if (rgntype == NULLREGION) return iterator_empty(i);
1460 if (!iterator_frameditems(i, infoPtr, &rcClip)) return FALSE;
1461 if (rgntype == SIMPLEREGION) return TRUE;
1462
1463 /* first deal with the special item */
1464 if (i->nSpecial != -1)
1465 {
1466 LISTVIEW_GetItemBox(infoPtr, i->nSpecial, &rcItem);
1467 if (!RectVisible(hdc, &rcItem)) i->nSpecial = -1;
1468 }
1469
1470 /* if we can't deal with the region, we'll just go with the simple range */
1471 LISTVIEW_GetOrigin(infoPtr, &Origin);
1472 TRACE("building visible range:\n");
1473 if (!i->ranges && i->range.lower < i->range.upper)
1474 {
1475 if (!(i->ranges = ranges_create(50))) return TRUE;
1476 if (!ranges_add(i->ranges, i->range))
1477 {
1478 ranges_destroy(i->ranges);
1479 i->ranges = 0;
1480 return TRUE;
1481 }
1482 }
1483
1484 /* now delete the invisible items from the list */
1485 while(iterator_next(i))
1486 {
1487 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
1488 rcItem.left = (infoPtr->uView == LV_VIEW_DETAILS) ? Origin.x : Position.x + Origin.x;
1489 rcItem.top = Position.y + Origin.y;
1490 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1491 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1492 if (!RectVisible(hdc, &rcItem))
1493 ranges_delitem(i->ranges, i->nItem);
1494 }
1495 /* the iterator should restart on the next iterator_next */
1496 TRACE("done\n");
1497
1498 return TRUE;
1499 }
1500
1501 /******** Misc helper functions ************************************/
1502
1503 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
1504 WPARAM wParam, LPARAM lParam, BOOL isW)
1505 {
1506 if (isW) return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
1507 else return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
1508 }
1509
1510 static inline BOOL is_autoarrange(const LISTVIEW_INFO *infoPtr)
1511 {
1512 return ((infoPtr->dwStyle & LVS_AUTOARRANGE) || infoPtr->bAutoarrange) &&
1513 (infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON);
1514 }
1515
1516 static void toggle_checkbox_state(LISTVIEW_INFO *infoPtr, INT nItem)
1517 {
1518 DWORD state = STATEIMAGEINDEX(LISTVIEW_GetItemState(infoPtr, nItem, LVIS_STATEIMAGEMASK));
1519 if(state == 1 || state == 2)
1520 {
1521 LVITEMW lvitem;
1522 state ^= 3;
1523 lvitem.state = INDEXTOSTATEIMAGEMASK(state);
1524 lvitem.stateMask = LVIS_STATEIMAGEMASK;
1525 LISTVIEW_SetItemState(infoPtr, nItem, &lvitem);
1526 }
1527 }
1528
1529 /* this should be called after window style got updated,
1530 it used to reset view state to match current window style */
1531 static inline void map_style_view(LISTVIEW_INFO *infoPtr)
1532 {
1533 switch (infoPtr->dwStyle & LVS_TYPEMASK)
1534 {
1535 case LVS_ICON:
1536 infoPtr->uView = LV_VIEW_ICON;
1537 break;
1538 case LVS_REPORT:
1539 infoPtr->uView = LV_VIEW_DETAILS;
1540 break;
1541 case LVS_SMALLICON:
1542 infoPtr->uView = LV_VIEW_SMALLICON;
1543 break;
1544 case LVS_LIST:
1545 infoPtr->uView = LV_VIEW_LIST;
1546 }
1547 }
1548
1549 /* computes next item id value */
1550 static DWORD get_next_itemid(const LISTVIEW_INFO *infoPtr)
1551 {
1552 INT count = DPA_GetPtrCount(infoPtr->hdpaItemIds);
1553
1554 if (count > 0)
1555 {
1556 ITEM_ID *lpID = DPA_GetPtr(infoPtr->hdpaItemIds, count - 1);
1557 return lpID->id + 1;
1558 }
1559 return 0;
1560 }
1561
1562 /******** Internal API functions ************************************/
1563
1564 static inline COLUMN_INFO * LISTVIEW_GetColumnInfo(const LISTVIEW_INFO *infoPtr, INT nSubItem)
1565 {
1566 static COLUMN_INFO mainItem;
1567
1568 if (nSubItem == 0 && DPA_GetPtrCount(infoPtr->hdpaColumns) == 0) return &mainItem;
1569 assert (nSubItem >= 0 && nSubItem < DPA_GetPtrCount(infoPtr->hdpaColumns));
1570
1571 /* update cached column rectangles */
1572 if (infoPtr->colRectsDirty)
1573 {
1574 COLUMN_INFO *info;
1575 LISTVIEW_INFO *Ptr = (LISTVIEW_INFO*)infoPtr;
1576 INT i;
1577
1578 for (i = 0; i < DPA_GetPtrCount(infoPtr->hdpaColumns); i++) {
1579 info = DPA_GetPtr(infoPtr->hdpaColumns, i);
1580 SendMessageW(infoPtr->hwndHeader, HDM_GETITEMRECT, i, (LPARAM)&info->rcHeader);
1581 }
1582 Ptr->colRectsDirty = FALSE;
1583 }
1584
1585 return DPA_GetPtr(infoPtr->hdpaColumns, nSubItem);
1586 }
1587
1588 static INT LISTVIEW_CreateHeader(LISTVIEW_INFO *infoPtr)
1589 {
1590 DWORD dFlags = WS_CHILD | HDS_HORZ | HDS_FULLDRAG | HDS_DRAGDROP;
1591 HINSTANCE hInst;
1592
1593 if (infoPtr->hwndHeader) return 0;
1594
1595 TRACE("Creating header for list %p\n", infoPtr->hwndSelf);
1596
1597 /* setup creation flags */
1598 dFlags |= (LVS_NOSORTHEADER & infoPtr->dwStyle) ? 0 : HDS_BUTTONS;
1599 dFlags |= (LVS_NOCOLUMNHEADER & infoPtr->dwStyle) ? HDS_HIDDEN : 0;
1600
1601 hInst = (HINSTANCE)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_HINSTANCE);
1602
1603 /* create header */
1604 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, NULL, dFlags,
1605 0, 0, 0, 0, infoPtr->hwndSelf, NULL, hInst, NULL);
1606 if (!infoPtr->hwndHeader) return -1;
1607
1608 /* set header unicode format */
1609 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT, TRUE, 0);
1610
1611 /* set header font */
1612 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, TRUE);
1613
1614 LISTVIEW_UpdateSize(infoPtr);
1615
1616 return 0;
1617 }
1618
1619 static inline void LISTVIEW_GetHeaderRect(const LISTVIEW_INFO *infoPtr, INT nSubItem, LPRECT lprc)
1620 {
1621 *lprc = LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->rcHeader;
1622 }
1623
1624 static inline BOOL LISTVIEW_IsHeaderEnabled(const LISTVIEW_INFO *infoPtr)
1625 {
1626 return (infoPtr->uView == LV_VIEW_DETAILS ||
1627 infoPtr->dwLvExStyle & LVS_EX_HEADERINALLVIEWS) &&
1628 !(infoPtr->dwStyle & LVS_NOCOLUMNHEADER);
1629 }
1630
1631 static inline BOOL LISTVIEW_GetItemW(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem)
1632 {
1633 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
1634 }
1635
1636 /* used to handle collapse main item column case */
1637 static inline BOOL LISTVIEW_DrawFocusRect(const LISTVIEW_INFO *infoPtr, HDC hdc)
1638 {
1639 BOOL Ret = FALSE;
1640
1641 if (infoPtr->rcFocus.left < infoPtr->rcFocus.right)
1642 {
1643 DWORD dwOldBkColor, dwOldTextColor;
1644
1645 dwOldBkColor = SetBkColor(hdc, RGB(255, 255, 255));
1646 dwOldTextColor = SetBkColor(hdc, RGB(0, 0, 0));
1647 Ret = DrawFocusRect(hdc, &infoPtr->rcFocus);
1648 SetBkColor(hdc, dwOldBkColor);
1649 SetBkColor(hdc, dwOldTextColor);
1650 }
1651 return Ret;
1652 }
1653
1654 /* Listview invalidation functions: use _only_ these functions to invalidate */
1655
1656 static inline BOOL is_redrawing(const LISTVIEW_INFO *infoPtr)
1657 {
1658 return infoPtr->bRedraw;
1659 }
1660
1661 static inline void LISTVIEW_InvalidateRect(const LISTVIEW_INFO *infoPtr, const RECT* rect)
1662 {
1663 if(!is_redrawing(infoPtr)) return;
1664 TRACE(" invalidating rect=%s\n", wine_dbgstr_rect(rect));
1665 InvalidateRect(infoPtr->hwndSelf, rect, TRUE);
1666 }
1667
1668 static inline void LISTVIEW_InvalidateItem(const LISTVIEW_INFO *infoPtr, INT nItem)
1669 {
1670 RECT rcBox;
1671
1672 if(!is_redrawing(infoPtr)) return;
1673 LISTVIEW_GetItemBox(infoPtr, nItem, &rcBox);
1674 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1675 }
1676
1677 static inline void LISTVIEW_InvalidateSubItem(const LISTVIEW_INFO *infoPtr, INT nItem, INT nSubItem)
1678 {
1679 POINT Origin, Position;
1680 RECT rcBox;
1681
1682 if(!is_redrawing(infoPtr)) return;
1683 assert (infoPtr->uView == LV_VIEW_DETAILS);
1684 LISTVIEW_GetOrigin(infoPtr, &Origin);
1685 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
1686 LISTVIEW_GetHeaderRect(infoPtr, nSubItem, &rcBox);
1687 rcBox.top = 0;
1688 rcBox.bottom = infoPtr->nItemHeight;
1689 OffsetRect(&rcBox, Origin.x + Position.x, Origin.y + Position.y);
1690 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1691 }
1692
1693 static inline void LISTVIEW_InvalidateList(const LISTVIEW_INFO *infoPtr)
1694 {
1695 LISTVIEW_InvalidateRect(infoPtr, NULL);
1696 }
1697
1698 static inline void LISTVIEW_InvalidateColumn(const LISTVIEW_INFO *infoPtr, INT nColumn)
1699 {
1700 RECT rcCol;
1701
1702 if(!is_redrawing(infoPtr)) return;
1703 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
1704 rcCol.top = infoPtr->rcList.top;
1705 rcCol.bottom = infoPtr->rcList.bottom;
1706 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
1707 }
1708
1709 /***
1710 * DESCRIPTION:
1711 * Retrieves the number of items that can fit vertically in the client area.
1712 *
1713 * PARAMETER(S):
1714 * [I] infoPtr : valid pointer to the listview structure
1715 *
1716 * RETURN:
1717 * Number of items per row.
1718 */
1719 static inline INT LISTVIEW_GetCountPerRow(const LISTVIEW_INFO *infoPtr)
1720 {
1721 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1722
1723 return max(nListWidth/(infoPtr->nItemWidth ? infoPtr->nItemWidth : 1), 1);
1724 }
1725
1726 /***
1727 * DESCRIPTION:
1728 * Retrieves the number of items that can fit horizontally in the client
1729 * area.
1730 *
1731 * PARAMETER(S):
1732 * [I] infoPtr : valid pointer to the listview structure
1733 *
1734 * RETURN:
1735 * Number of items per column.
1736 */
1737 static inline INT LISTVIEW_GetCountPerColumn(const LISTVIEW_INFO *infoPtr)
1738 {
1739 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1740
1741 return max(nListHeight / infoPtr->nItemHeight, 1);
1742 }
1743
1744
1745 /*************************************************************************
1746 * LISTVIEW_ProcessLetterKeys
1747 *
1748 * Processes keyboard messages generated by pressing the letter keys
1749 * on the keyboard.
1750 * What this does is perform a case insensitive search from the
1751 * current position with the following quirks:
1752 * - If two chars or more are pressed in quick succession we search
1753 * for the corresponding string (e.g. 'abc').
1754 * - If there is a delay we wipe away the current search string and
1755 * restart with just that char.
1756 * - If the user keeps pressing the same character, whether slowly or
1757 * fast, so that the search string is entirely composed of this
1758 * character ('aaaaa' for instance), then we search for first item
1759 * that starting with that character.
1760 * - If the user types the above character in quick succession, then
1761 * we must also search for the corresponding string ('aaaaa'), and
1762 * go to that string if there is a match.
1763 *
1764 * PARAMETERS
1765 * [I] hwnd : handle to the window
1766 * [I] charCode : the character code, the actual character
1767 * [I] keyData : key data
1768 *
1769 * RETURNS
1770 *
1771 * Zero.
1772 *
1773 * BUGS
1774 *
1775 * - The current implementation has a list of characters it will
1776 * accept and it ignores everything else. In particular it will
1777 * ignore accentuated characters which seems to match what
1778 * Windows does. But I'm not sure it makes sense to follow
1779 * Windows there.
1780 * - We don't sound a beep when the search fails.
1781 *
1782 * SEE ALSO
1783 *
1784 * TREEVIEW_ProcessLetterKeys
1785 */
1786 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData)
1787 {
1788 WCHAR buffer[MAX_PATH];
1789 INT endidx, startidx;
1790 DWORD prevTime;
1791 LVITEMW item;
1792 INT nItem;
1793 INT diff;
1794
1795 /* simple parameter checking */
1796 if (!charCode || !keyData || infoPtr->nItemCount == 0) return 0;
1797
1798 /* only allow the valid WM_CHARs through */
1799 if (!isalnumW(charCode) &&
1800 charCode != '.' && charCode != '`' && charCode != '!' &&
1801 charCode != '@' && charCode != '#' && charCode != '$' &&
1802 charCode != '%' && charCode != '^' && charCode != '&' &&
1803 charCode != '*' && charCode != '(' && charCode != ')' &&
1804 charCode != '-' && charCode != '_' && charCode != '+' &&
1805 charCode != '=' && charCode != '\\'&& charCode != ']' &&
1806 charCode != '}' && charCode != '[' && charCode != '{' &&
1807 charCode != '/' && charCode != '?' && charCode != '>' &&
1808 charCode != '<' && charCode != ',' && charCode != '~')
1809 return 0;
1810
1811 /* update the search parameters */
1812 prevTime = infoPtr->lastKeyPressTimestamp;
1813 infoPtr->lastKeyPressTimestamp = GetTickCount();
1814 diff = infoPtr->lastKeyPressTimestamp - prevTime;
1815
1816 if (diff >= 0 && diff < KEY_DELAY)
1817 {
1818 if (infoPtr->nSearchParamLength < MAX_PATH - 1)
1819 infoPtr->szSearchParam[infoPtr->nSearchParamLength++] = charCode;
1820
1821 if (infoPtr->charCode != charCode)
1822 infoPtr->charCode = charCode = 0;
1823 }
1824 else
1825 {
1826 infoPtr->charCode = charCode;
1827 infoPtr->szSearchParam[0] = charCode;
1828 infoPtr->nSearchParamLength = 1;
1829 }
1830
1831 /* and search from the current position */
1832 nItem = -1;
1833 endidx = infoPtr->nItemCount;
1834
1835 /* should start from next after focused item, so next item that matches
1836 will be selected, if there isn't any and focused matches it will be selected
1837 on second search stage from beginning of the list */
1838 if (infoPtr->nFocusedItem >= 0 && infoPtr->nItemCount > 1)
1839 startidx = infoPtr->nFocusedItem + 1;
1840 else
1841 startidx = 0;
1842
1843 /* let application handle this for virtual listview */
1844 if (infoPtr->dwStyle & LVS_OWNERDATA)
1845 {
1846 NMLVFINDITEMW nmlv;
1847
1848 memset(&nmlv.lvfi, 0, sizeof(nmlv.lvfi));
1849 nmlv.lvfi.flags = (LVFI_WRAP | LVFI_PARTIAL);
1850 nmlv.lvfi.psz = infoPtr->szSearchParam;
1851 nmlv.iStart = startidx;
1852
1853 infoPtr->szSearchParam[infoPtr->nSearchParamLength] = 0;
1854
1855 nItem = notify_hdr(infoPtr, LVN_ODFINDITEMW, (LPNMHDR)&nmlv.hdr);
1856 }
1857 else
1858 {
1859 INT i = startidx;
1860
1861 /* first search in [startidx, endidx), on failure continue in [0, startidx) */
1862 while (1)
1863 {
1864 /* start from first item if not found with >= startidx */
1865 if (i == infoPtr->nItemCount && startidx > 0)
1866 {
1867 endidx = startidx;
1868 startidx = 0;
1869 }
1870
1871 for (i = startidx; i < endidx; i++)
1872 {
1873 /* retrieve text */
1874 item.mask = LVIF_TEXT;
1875 item.iItem = i;
1876 item.iSubItem = 0;
1877 item.pszText = buffer;
1878 item.cchTextMax = MAX_PATH;
1879 if (!LISTVIEW_GetItemW(infoPtr, &item)) return 0;
1880
1881 if (lstrncmpiW(item.pszText, infoPtr->szSearchParam, infoPtr->nSearchParamLength) == 0)
1882 {
1883 nItem = i;
1884 break;
1885 }
1886 else if (nItem == -1 && lstrncmpiW(item.pszText, infoPtr->szSearchParam, 1) == 0)
1887 {
1888 /* this would work but we must keep looking for a longer match */
1889 nItem = i;
1890 }
1891 }
1892
1893 if ( nItem != -1 || /* found something */
1894 endidx != infoPtr->nItemCount || /* second search done */
1895 (startidx == 0 && endidx == infoPtr->nItemCount) /* full range for first search */ )
1896 break;
1897 };
1898 }
1899
1900 if (nItem != -1)
1901 LISTVIEW_KeySelection(infoPtr, nItem, FALSE);
1902
1903 return 0;
1904 }
1905
1906 /*************************************************************************
1907 * LISTVIEW_UpdateHeaderSize [Internal]
1908 *
1909 * Function to resize the header control
1910 *
1911 * PARAMS
1912 * [I] hwnd : handle to a window
1913 * [I] nNewScrollPos : scroll pos to set
1914 *
1915 * RETURNS
1916 * None.
1917 */
1918 static void LISTVIEW_UpdateHeaderSize(const LISTVIEW_INFO *infoPtr, INT nNewScrollPos)
1919 {
1920 RECT winRect;
1921 POINT point[2];
1922
1923 TRACE("nNewScrollPos=%d\n", nNewScrollPos);
1924
1925 if (!infoPtr->hwndHeader) return;
1926
1927 GetWindowRect(infoPtr->hwndHeader, &winRect);
1928 point[0].x = winRect.left;
1929 point[0].y = winRect.top;
1930 point[1].x = winRect.right;
1931 point[1].y = winRect.bottom;
1932
1933 MapWindowPoints(HWND_DESKTOP, infoPtr->hwndSelf, point, 2);
1934 point[0].x = -nNewScrollPos;
1935 point[1].x += nNewScrollPos;
1936
1937 SetWindowPos(infoPtr->hwndHeader,0,
1938 point[0].x,point[0].y,point[1].x,point[1].y,
1939 (infoPtr->dwStyle & LVS_NOCOLUMNHEADER) ? SWP_HIDEWINDOW : SWP_SHOWWINDOW |
1940 SWP_NOZORDER | SWP_NOACTIVATE);
1941 }
1942
1943 /***
1944 * DESCRIPTION:
1945 * Update the scrollbars. This functions should be called whenever
1946 * the content, size or view changes.
1947 *
1948 * PARAMETER(S):
1949 * [I] infoPtr : valid pointer to the listview structure
1950 *
1951 * RETURN:
1952 * None
1953 */
1954 static void LISTVIEW_UpdateScroll(const LISTVIEW_INFO *infoPtr)
1955 {
1956 SCROLLINFO horzInfo, vertInfo;
1957 INT dx, dy;
1958
1959 if ((infoPtr->dwStyle & LVS_NOSCROLL) || !is_redrawing(infoPtr)) return;
1960
1961 ZeroMemory(&horzInfo, sizeof(SCROLLINFO));
1962 horzInfo.cbSize = sizeof(SCROLLINFO);
1963 horzInfo.nPage = infoPtr->rcList.right - infoPtr->rcList.left;
1964
1965 /* for now, we'll set info.nMax to the _count_, and adjust it later */
1966 if (infoPtr->uView == LV_VIEW_LIST)
1967 {
1968 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
1969 horzInfo.nMax = (infoPtr->nItemCount + nPerCol - 1) / nPerCol;
1970
1971 /* scroll by at least one column per page */
1972 if(horzInfo.nPage < infoPtr->nItemWidth)
1973 horzInfo.nPage = infoPtr->nItemWidth;
1974
1975 if (infoPtr->nItemWidth)
1976 horzInfo.nPage /= infoPtr->nItemWidth;
1977 }
1978 else if (infoPtr->uView == LV_VIEW_DETAILS)
1979 {
1980 horzInfo.nMax = infoPtr->nItemWidth;
1981 }
1982 else /* LV_VIEW_ICON, or LV_VIEW_SMALLICON */
1983 {
1984 RECT rcView;
1985
1986 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) horzInfo.nMax = rcView.right - rcView.left;
1987 }
1988
1989 if (LISTVIEW_IsHeaderEnabled(infoPtr))
1990 {
1991 if (DPA_GetPtrCount(infoPtr->hdpaColumns))
1992 {
1993 RECT rcHeader;
1994 INT index;
1995
1996 index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX,
1997 DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, 0);
1998
1999 LISTVIEW_GetHeaderRect(infoPtr, index, &rcHeader);
2000 horzInfo.nMax = rcHeader.right;
2001 TRACE("horzInfo.nMax=%d\n", horzInfo.nMax);
2002 }
2003 }
2004
2005 horzInfo.fMask = SIF_RANGE | SIF_PAGE;
2006 horzInfo.nMax = max(horzInfo.nMax - 1, 0);
2007 dx = GetScrollPos(infoPtr->hwndSelf, SB_HORZ);
2008 dx -= SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo, TRUE);
2009 TRACE("horzInfo=%s\n", debugscrollinfo(&horzInfo));
2010
2011 /* Setting the horizontal scroll can change the listview size
2012 * (and potentially everything else) so we need to recompute
2013 * everything again for the vertical scroll
2014 */
2015
2016 ZeroMemory(&vertInfo, sizeof(SCROLLINFO));
2017 vertInfo.cbSize = sizeof(SCROLLINFO);
2018 vertInfo.nPage = infoPtr->rcList.bottom - infoPtr->rcList.top;
2019
2020 if (infoPtr->uView == LV_VIEW_DETAILS)
2021 {
2022 vertInfo.nMax = infoPtr->nItemCount;
2023
2024 /* scroll by at least one page */
2025 if(vertInfo.nPage < infoPtr->nItemHeight)
2026 vertInfo.nPage = infoPtr->nItemHeight;
2027
2028 if (infoPtr->nItemHeight > 0)
2029 vertInfo.nPage /= infoPtr->nItemHeight;
2030 }
2031 else if (infoPtr->uView != LV_VIEW_LIST) /* LV_VIEW_ICON, or LV_VIEW_SMALLICON */
2032 {
2033 RECT rcView;
2034
2035 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) vertInfo.nMax = rcView.bottom - rcView.top;
2036 }
2037
2038 vertInfo.fMask = SIF_RANGE | SIF_PAGE;
2039 vertInfo.nMax = max(vertInfo.nMax - 1, 0);
2040 dy = GetScrollPos(infoPtr->hwndSelf, SB_VERT);
2041 dy -= SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &vertInfo, TRUE);
2042 TRACE("vertInfo=%s\n", debugscrollinfo(&vertInfo));
2043
2044 /* Change of the range may have changed the scroll pos. If so move the content */
2045 if (dx != 0 || dy != 0)
2046 {
2047 RECT listRect;
2048 listRect = infoPtr->rcList;
2049 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &listRect, &listRect, 0, 0,
2050 SW_ERASE | SW_INVALIDATE);
2051 }
2052
2053 /* Update the Header Control */
2054 if (infoPtr->hwndHeader)
2055 {
2056 horzInfo.fMask = SIF_POS;
2057 GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo);
2058 LISTVIEW_UpdateHeaderSize(infoPtr, horzInfo.nPos);
2059 }
2060 }
2061
2062
2063 /***
2064 * DESCRIPTION:
2065 * Shows/hides the focus rectangle.
2066 *
2067 * PARAMETER(S):
2068 * [I] infoPtr : valid pointer to the listview structure
2069 * [I] fShow : TRUE to show the focus, FALSE to hide it.
2070 *
2071 * RETURN:
2072 * None
2073 */
2074 static void LISTVIEW_ShowFocusRect(const LISTVIEW_INFO *infoPtr, BOOL fShow)
2075 {
2076 HDC hdc;
2077
2078 TRACE("fShow=%d, nItem=%d\n", fShow, infoPtr->nFocusedItem);
2079
2080 if (infoPtr->nFocusedItem < 0) return;
2081
2082 /* we need some gymnastics in ICON mode to handle large items */
2083 if (infoPtr->uView == LV_VIEW_ICON)
2084 {
2085 RECT rcBox;
2086
2087 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcBox);
2088 if ((rcBox.bottom - rcBox.top) > infoPtr->nItemHeight)
2089 {
2090 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
2091 return;
2092 }
2093 }
2094
2095 if (!(hdc = GetDC(infoPtr->hwndSelf))) return;
2096
2097 /* for some reason, owner draw should work only in report mode */
2098 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (infoPtr->uView == LV_VIEW_DETAILS))
2099 {
2100 DRAWITEMSTRUCT dis;
2101 LVITEMW item;
2102
2103 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2104 HFONT hOldFont = SelectObject(hdc, hFont);
2105
2106 item.iItem = infoPtr->nFocusedItem;
2107 item.iSubItem = 0;
2108 item.mask = LVIF_PARAM;
2109 if (!LISTVIEW_GetItemW(infoPtr, &item)) goto done;
2110
2111 ZeroMemory(&dis, sizeof(dis));
2112 dis.CtlType = ODT_LISTVIEW;
2113 dis.CtlID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
2114 dis.itemID = item.iItem;
2115 dis.itemAction = ODA_FOCUS;
2116 if (fShow) dis.itemState |= ODS_FOCUS;
2117 dis.hwndItem = infoPtr->hwndSelf;
2118 dis.hDC = hdc;
2119 LISTVIEW_GetItemBox(infoPtr, dis.itemID, &dis.rcItem);
2120 dis.itemData = item.lParam;
2121
2122 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
2123
2124 SelectObject(hdc, hOldFont);
2125 }
2126 else
2127 {
2128 LISTVIEW_DrawFocusRect(infoPtr, hdc);
2129 }
2130 done:
2131 ReleaseDC(infoPtr->hwndSelf, hdc);
2132 }
2133
2134 /***
2135 * Invalidates all visible selected items.
2136 */
2137 static void LISTVIEW_InvalidateSelectedItems(const LISTVIEW_INFO *infoPtr)
2138 {
2139 ITERATOR i;
2140
2141 iterator_frameditems(&i, infoPtr, &infoPtr->rcList);
2142 while(iterator_next(&i))
2143 {
2144 if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED))
2145 LISTVIEW_InvalidateItem(infoPtr, i.nItem);
2146 }
2147 iterator_destroy(&i);
2148 }
2149
2150
2151 /***
2152 * DESCRIPTION: [INTERNAL]
2153 * Computes an item's (left,top) corner, relative to rcView.
2154 * That is, the position has NOT been made relative to the Origin.
2155 * This is deliberate, to avoid computing the Origin over, and
2156 * over again, when this function is called in a loop. Instead,
2157 * one can factor the computation of the Origin before the loop,
2158 * and offset the value returned by this function, on every iteration.
2159 *
2160 * PARAMETER(S):
2161 * [I] infoPtr : valid pointer to the listview structure
2162 * [I] nItem : item number
2163 * [O] lpptOrig : item top, left corner
2164 *
2165 * RETURN:
2166 * None.
2167 */
2168 static void LISTVIEW_GetItemOrigin(const LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
2169 {
2170 assert(nItem >= 0 && nItem < infoPtr->nItemCount);
2171
2172 if ((infoPtr->uView == LV_VIEW_SMALLICON) || (infoPtr->uView == LV_VIEW_ICON))
2173 {
2174 lpptPosition->x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
2175 lpptPosition->y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
2176 }
2177 else if (infoPtr->uView == LV_VIEW_LIST)
2178 {
2179 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
2180 lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
2181 lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight;
2182 }
2183 else /* LV_VIEW_DETAILS */
2184 {
2185 lpptPosition->x = REPORT_MARGINX;
2186 /* item is always at zero indexed column */
2187 if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0)
2188 lpptPosition->x += LISTVIEW_GetColumnInfo(infoPtr, 0)->rcHeader.left;
2189 lpptPosition->y = nItem * infoPtr->nItemHeight;
2190 }
2191 }
2192
2193 /***
2194 * DESCRIPTION: [INTERNAL]
2195 * Compute the rectangles of an item. This is to localize all
2196 * the computations in one place. If you are not interested in some
2197 * of these values, simply pass in a NULL -- the function is smart
2198 * enough to compute only what's necessary. The function computes
2199 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
2200 * one, the BOX rectangle. This rectangle is very cheap to compute,
2201 * and is guaranteed to contain all the other rectangles. Computing
2202 * the ICON rect is also cheap, but all the others are potentially
2203 * expensive. This gives an easy and effective optimization when
2204 * searching (like point inclusion, or rectangle intersection):
2205 * first test against the BOX, and if TRUE, test against the desired
2206 * rectangle.
2207 * If the function does not have all the necessary information
2208 * to computed the requested rectangles, will crash with a
2209 * failed assertion. This is done so we catch all programming
2210 * errors, given that the function is called only from our code.
2211 *
2212 * We have the following 'special' meanings for a few fields:
2213 * * If LVIS_FOCUSED is set, we assume the item has the focus
2214 * This is important in ICON mode, where it might get a larger
2215 * then usual rectangle
2216 *
2217 * Please note that subitem support works only in REPORT mode.
2218 *
2219 * PARAMETER(S):
2220 * [I] infoPtr : valid pointer to the listview structure
2221 * [I] lpLVItem : item to compute the measures for
2222 * [O] lprcBox : ptr to Box rectangle
2223 * Same as LVM_GETITEMRECT with LVIR_BOUNDS
2224 * [0] lprcSelectBox : ptr to select box rectangle
2225 * Same as LVM_GETITEMRECT with LVIR_SELECTEDBOUNDS
2226 * [O] lprcIcon : ptr to Icon rectangle
2227 * Same as LVM_GETITEMRECT with LVIR_ICON
2228 * [O] lprcStateIcon: ptr to State Icon rectangle
2229 * [O] lprcLabel : ptr to Label rectangle
2230 * Same as LVM_GETITEMRECT with LVIR_LABEL
2231 *
2232 * RETURN:
2233 * None.
2234 */
2235 static void LISTVIEW_GetItemMetrics(const LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem,
2236 LPRECT lprcBox, LPRECT lprcSelectBox,
2237 LPRECT lprcIcon, LPRECT lprcStateIcon, LPRECT lprcLabel)
2238 {
2239 BOOL doSelectBox = FALSE, doIcon = FALSE, doLabel = FALSE, oversizedBox = FALSE;
2240 RECT Box, SelectBox, Icon, Label;
2241 COLUMN_INFO *lpColumnInfo = NULL;
2242 SIZE labelSize = { 0, 0 };
2243
2244 TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem, TRUE));
2245
2246 /* Be smart and try to figure out the minimum we have to do */
2247 if (lpLVItem->iSubItem) assert(infoPtr->uView == LV_VIEW_DETAILS);
2248 if (infoPtr->uView == LV_VIEW_ICON && (lprcBox || lprcLabel))
2249 {
2250 assert((lpLVItem->mask & LVIF_STATE) && (lpLVItem->stateMask & LVIS_FOCUSED));
2251 if (lpLVItem->state & LVIS_FOCUSED) oversizedBox = doLabel = TRUE;
2252 }
2253 if (lprcSelectBox) doSelectBox = TRUE;
2254 if (lprcLabel) doLabel = TRUE;
2255 if (doLabel || lprcIcon || lprcStateIcon) doIcon = TRUE;
2256 if (doSelectBox)
2257 {
2258 doIcon = TRUE;
2259 doLabel = TRUE;
2260 }
2261
2262 /************************************************************/
2263 /* compute the box rectangle (it should be cheap to do) */
2264 /************************************************************/
2265 if (lpLVItem->iSubItem || infoPtr->uView == LV_VIEW_DETAILS)
2266 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpLVItem->iSubItem);
2267
2268 if (lpLVItem->iSubItem)
2269 {
2270 Box = lpColumnInfo->rcHeader;
2271 }
2272 else
2273 {
2274 Box.left = 0;
2275 Box.right = infoPtr->nItemWidth;
2276 }
2277 Box.top = 0;
2278 Box.bottom = infoPtr->nItemHeight;
2279
2280 /******************************************************************/
2281 /* compute ICON bounding box (ala LVM_GETITEMRECT) and STATEICON */
2282 /******************************************************************/
2283 if (doIcon)
2284 {
2285 LONG state_width = 0;
2286
2287 if (infoPtr->himlState && lpLVItem->iSubItem == 0)
2288 state_width = infoPtr->iconStateSize.cx;
2289
2290 if (infoPtr->uView == LV_VIEW_ICON)
2291 {
2292 Icon.left = Box.left + state_width;
2293 if (infoPtr->himlNormal)
2294 Icon.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx - state_width) / 2;
2295 Icon.top = Box.top + ICON_TOP_PADDING;
2296 Icon.right = Icon.left;
2297 Icon.bottom = Icon.top;
2298 if (infoPtr->himlNormal)
2299 {
2300 Icon.right += infoPtr->iconSize.cx;
2301 Icon.bottom += infoPtr->iconSize.cy;
2302 }
2303 }
2304 else /* LV_VIEW_SMALLICON, LV_VIEW_LIST or LV_VIEW_DETAILS */
2305 {
2306 Icon.left = Box.left + state_width;
2307
2308 if (infoPtr->uView == LV_VIEW_DETAILS && lpLVItem->iSubItem == 0)
2309 {
2310 /* we need the indent in report mode */
2311 assert(lpLVItem->mask & LVIF_INDENT);
2312 Icon.left += infoPtr->iconSize.cx * lpLVItem->iIndent + REPORT_MARGINX;
2313 }
2314
2315 Icon.top = Box.top;
2316 Icon.right = Icon.left;
2317 if (infoPtr->himlSmall &&
2318 (!lpColumnInfo || lpLVItem->iSubItem == 0 || (lpColumnInfo->fmt & LVCFMT_IMAGE) ||
2319 ((infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES) && lpLVItem->iImage != I_IMAGECALLBACK)))
2320 Icon.right += infoPtr->iconSize.cx;
2321 Icon.bottom = Icon.top + infoPtr->iconSize.cy;
2322 }
2323 if(lprcIcon) *lprcIcon = Icon;
2324 TRACE(" - icon=%s\n", wine_dbgstr_rect(&Icon));
2325
2326 /* TODO: is this correct? */
2327 if (lprcStateIcon)
2328 {
2329 lprcStateIcon->left = Icon.left - state_width;
2330 lprcStateIcon->right = Icon.left;
2331 lprcStateIcon->top = Icon.top;
2332 lprcStateIcon->bottom = lprcStateIcon->top + infoPtr->iconSize.cy;
2333 TRACE(" - state icon=%s\n", wine_dbgstr_rect(lprcStateIcon));
2334 }
2335 }
2336 else Icon.right = 0;
2337
2338 /************************************************************/
2339 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
2340 /************************************************************/
2341 if (doLabel)
2342 {
2343 /* calculate how far to the right can the label stretch */
2344 Label.right = Box.right;
2345 if (infoPtr->uView == LV_VIEW_DETAILS)
2346 {
2347 if (lpLVItem->iSubItem == 0)
2348 {
2349 /* we need a zero based rect here */
2350 Label = lpColumnInfo->rcHeader;
2351 OffsetRect(&Label, -Label.left, 0);
2352 }
2353 }
2354
2355 if (lpLVItem->iSubItem || ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && infoPtr->uView == LV_VIEW_DETAILS))
2356 {
2357 labelSize.cx = infoPtr->nItemWidth;
2358 labelSize.cy = infoPtr->nItemHeight;
2359 goto calc_label;
2360 }
2361
2362 /* we need the text in non owner draw mode */
2363 assert(lpLVItem->mask & LVIF_TEXT);
2364 if (is_text(lpLVItem->pszText))
2365 {
2366 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2367 HDC hdc = GetDC(infoPtr->hwndSelf);
2368 HFONT hOldFont = SelectObject(hdc, hFont);
2369 UINT uFormat;
2370 RECT rcText;
2371
2372 /* compute rough rectangle where the label will go */
2373 SetRectEmpty(&rcText);
2374 rcText.right = infoPtr->nItemWidth - TRAILING_LABEL_PADDING;
2375 rcText.bottom = infoPtr->nItemHeight;
2376 if (infoPtr->uView == LV_VIEW_ICON)
2377 rcText.bottom -= ICON_TOP_PADDING + infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2378
2379 /* now figure out the flags */
2380 if (infoPtr->uView == LV_VIEW_ICON)
2381 uFormat = oversizedBox ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS;
2382 else
2383 uFormat = LV_SL_DT_FLAGS;
2384
2385 DrawTextW (hdc, lpLVItem->pszText, -1, &rcText, uFormat | DT_CALCRECT);
2386
2387 if (rcText.right != rcText.left)
2388 labelSize.cx = min(rcText.right - rcText.left + TRAILING_LABEL_PADDING, infoPtr->nItemWidth);
2389
2390 labelSize.cy = rcText.bottom - rcText.top;
2391
2392 SelectObject(hdc, hOldFont);
2393 ReleaseDC(infoPtr->hwndSelf, hdc);
2394 }
2395
2396 calc_label:
2397 if (infoPtr->uView == LV_VIEW_ICON)
2398 {
2399 Label.left = Box.left + (infoPtr->nItemWidth - labelSize.cx) / 2;
2400 Label.top = Box.top + ICON_TOP_PADDING_HITABLE +
2401 infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2402 Label.right = Label.left + labelSize.cx;
2403 Label.bottom = Label.top + infoPtr->nItemHeight;
2404 if (!oversizedBox && labelSize.cy > infoPtr->ntmHeight)
2405 {
2406 labelSize.cy = min(Box.bottom - Label.top, labelSize.cy);
2407 labelSize.cy /= infoPtr->ntmHeight;
2408 labelSize.cy = max(labelSize.cy, 1);
2409 labelSize.cy *= infoPtr->ntmHeight;
2410 }
2411 Label.bottom = Label.top + labelSize.cy + HEIGHT_PADDING;
2412 }
2413 else if (infoPtr->uView == LV_VIEW_DETAILS)
2414 {
2415 Label.left = Icon.right;
2416 Label.top = Box.top;
2417 Label.right = lpLVItem->iSubItem ? lpColumnInfo->rcHeader.right :
2418 lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left;
2419 Label.bottom = Label.top + infoPtr->nItemHeight;
2420 }
2421 else /* LV_VIEW_SMALLICON or LV_VIEW_LIST */
2422 {
2423 Label.left = Icon.right;
2424 Label.top = Box.top;
2425 Label.right = min(Label.left + labelSize.cx, Label.right);
2426 Label.bottom = Label.top + infoPtr->nItemHeight;
2427 }
2428
2429 if (lprcLabel) *lprcLabel = Label;
2430 TRACE(" - label=%s\n", wine_dbgstr_rect(&Label));
2431 }
2432
2433 /************************************************************/
2434 /* compute SELECT bounding box */
2435 /************************************************************/
2436 if (doSelectBox)
2437 {
2438 if (infoPtr->uView == LV_VIEW_DETAILS)
2439 {
2440 SelectBox.left = Icon.left;
2441 SelectBox.top = Box.top;
2442 SelectBox.bottom = Box.bottom;
2443
2444 if (labelSize.cx)
2445 SelectBox.right = min(Label.left + labelSize.cx, Label.right);
2446 else
2447 SelectBox.right = min(Label.left + MAX_EMPTYTEXT_SELECT_WIDTH, Label.right);
2448 }
2449 else
2450 {
2451 UnionRect(&SelectBox, &Icon, &Label);
2452 }
2453 if (lprcSelectBox) *lprcSelectBox = SelectBox;
2454 TRACE(" - select box=%s\n", wine_dbgstr_rect(&SelectBox));
2455 }
2456
2457 /* Fix the Box if necessary */
2458 if (lprcBox)
2459 {
2460 if (oversizedBox) UnionRect(lprcBox, &Box, &Label);
2461 else *lprcBox = Box;
2462 }
2463 TRACE(" - box=%s\n", wine_dbgstr_rect(&Box));
2464 }
2465
2466 /***
2467 * DESCRIPTION: [INTERNAL]
2468 *
2469 * PARAMETER(S):
2470 * [I] infoPtr : valid pointer to the listview structure
2471 * [I] nItem : item number
2472 * [O] lprcBox : ptr to Box rectangle
2473 *
2474 * RETURN:
2475 * None.
2476 */
2477 static void LISTVIEW_GetItemBox(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprcBox)
2478 {
2479 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
2480 POINT Position, Origin;
2481 LVITEMW lvItem;
2482
2483 LISTVIEW_GetOrigin(infoPtr, &Origin);
2484 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
2485
2486 /* Be smart and try to figure out the minimum we have to do */
2487 lvItem.mask = 0;
2488 if (infoPtr->uView == LV_VIEW_ICON && infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
2489 lvItem.mask |= LVIF_TEXT;
2490 lvItem.iItem = nItem;
2491 lvItem.iSubItem = 0;
2492 lvItem.pszText = szDispText;
2493 lvItem.cchTextMax = DISP_TEXT_SIZE;
2494 if (lvItem.mask) LISTVIEW_GetItemW(infoPtr, &lvItem);
2495 if (infoPtr->uView == LV_VIEW_ICON)
2496 {
2497 lvItem.mask |= LVIF_STATE;
2498 lvItem.stateMask = LVIS_FOCUSED;
2499 lvItem.state = (lvItem.mask & LVIF_TEXT ? LVIS_FOCUSED : 0);
2500 }
2501 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprcBox, 0, 0, 0, 0);
2502
2503 if (infoPtr->uView == LV_VIEW_DETAILS && infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT &&
2504 SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX, 0, 0))
2505 {
2506 OffsetRect(lprcBox, Origin.x, Position.y + Origin.y);
2507 }
2508 else
2509 OffsetRect(lprcBox, Position.x + Origin.x, Position.y + Origin.y);
2510 }
2511
2512 /* LISTVIEW_MapIdToIndex helper */
2513 static INT CALLBACK MapIdSearchCompare(LPVOID p1, LPVOID p2, LPARAM lParam)
2514 {
2515 ITEM_ID *id1 = (ITEM_ID*)p1;
2516 ITEM_ID *id2 = (ITEM_ID*)p2;
2517
2518 if (id1->id == id2->id) return 0;
2519
2520 return (id1->id < id2->id) ? -1 : 1;
2521 }
2522
2523 /***
2524 * DESCRIPTION:
2525 * Returns the item index for id specified.
2526 *
2527 * PARAMETER(S):
2528 * [I] infoPtr : valid pointer to the listview structure
2529 * [I] iID : item id to get index for
2530 *
2531 * RETURN:
2532 * Item index, or -1 on failure.
2533 */
2534 static INT LISTVIEW_MapIdToIndex(const LISTVIEW_INFO *infoPtr, UINT iID)
2535 {
2536 ITEM_ID ID;
2537 INT index;
2538
2539 TRACE("iID=%d\n", iID);
2540
2541 if (infoPtr->dwStyle & LVS_OWNERDATA) return -1;
2542 if (infoPtr->nItemCount == 0) return -1;
2543
2544 ID.id = iID;
2545 index = DPA_Search(infoPtr->hdpaItemIds, &ID, -1, &MapIdSearchCompare, 0, DPAS_SORTED);
2546
2547 if (index != -1)
2548 {
2549 ITEM_ID *lpID = DPA_GetPtr(infoPtr->hdpaItemIds, index);
2550 return DPA_GetPtrIndex(infoPtr->hdpaItems, lpID->item);
2551 }
2552
2553 return -1;
2554 }
2555
2556 /***
2557 * DESCRIPTION:
2558 * Returns the item id for index given.
2559 *
2560 * PARAMETER(S):
2561 * [I] infoPtr : valid pointer to the listview structure
2562 * [I] iItem : item index to get id for
2563 *
2564 * RETURN:
2565 * Item id.
2566 */
2567 static DWORD LISTVIEW_MapIndexToId(const LISTVIEW_INFO *infoPtr, INT iItem)
2568 {
2569 ITEM_INFO *lpItem;
2570 HDPA hdpaSubItems;
2571
2572 TRACE("iItem=%d\n", iItem);
2573
2574 if (infoPtr->dwStyle & LVS_OWNERDATA) return -1;
2575 if (iItem < 0 || iItem >= infoPtr->nItemCount) return -1;
2576
2577 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, iItem);
2578 lpItem = DPA_GetPtr(hdpaSubItems, 0);
2579
2580 return lpItem->id->id;
2581 }
2582
2583 /***
2584 * DESCRIPTION:
2585 * Returns the current icon position, and advances it along the top.
2586 * The returned position is not offset by Origin.
2587 *
2588 * PARAMETER(S):
2589 * [I] infoPtr : valid pointer to the listview structure
2590 * [O] lpPos : will get the current icon position
2591 *
2592 * RETURN:
2593 * None
2594 */
2595 static void LISTVIEW_NextIconPosTop(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2596 {
2597 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
2598
2599 *lpPos = infoPtr->currIconPos;
2600
2601 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2602 if (infoPtr->currIconPos.x + infoPtr->nItemWidth <= nListWidth) return;
2603
2604 infoPtr->currIconPos.x = 0;
2605 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2606 }
2607
2608
2609 /***
2610 * DESCRIPTION:
2611 * Returns the current icon position, and advances it down the left edge.
2612 * The returned position is not offset by Origin.
2613 *
2614 * PARAMETER(S):
2615 * [I] infoPtr : valid pointer to the listview structure
2616 * [O] lpPos : will get the current icon position
2617 *
2618 * RETURN:
2619 * None
2620 */
2621 static void LISTVIEW_NextIconPosLeft(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2622 {
2623 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
2624
2625 *lpPos = infoPtr->currIconPos;
2626
2627 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2628 if (infoPtr->currIconPos.y + infoPtr->nItemHeight <= nListHeight) return;
2629
2630 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2631 infoPtr->currIconPos.y = 0;
2632 }
2633
2634
2635 /***
2636 * DESCRIPTION:
2637 * Moves an icon to the specified position.
2638 * It takes care of invalidating the item, etc.
2639 *
2640 * PARAMETER(S):
2641 * [I] infoPtr : valid pointer to the listview structure
2642 * [I] nItem : the item to move
2643 * [I] lpPos : the new icon position
2644 * [I] isNew : flags the item as being new
2645 *
2646 * RETURN:
2647 * Success: TRUE
2648 * Failure: FALSE
2649 */
2650 static BOOL LISTVIEW_MoveIconTo(const LISTVIEW_INFO *infoPtr, INT nItem, const POINT *lppt, BOOL isNew)
2651 {
2652 POINT old;
2653
2654 if (!isNew)
2655 {
2656 old.x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
2657 old.y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
2658
2659 if (lppt->x == old.x && lppt->y == old.y) return TRUE;
2660 LISTVIEW_InvalidateItem(infoPtr, nItem);
2661 }
2662
2663 /* Allocating a POINTER for every item is too resource intensive,
2664 * so we'll keep the (x,y) in different arrays */
2665 if (!DPA_SetPtr(infoPtr->hdpaPosX, nItem, (void *)(LONG_PTR)lppt->x)) return FALSE;
2666 if (!DPA_SetPtr(infoPtr->hdpaPosY, nItem, (void *)(LONG_PTR)lppt->y)) return FALSE;
2667
2668 LISTVIEW_InvalidateItem(infoPtr, nItem);
2669
2670 return TRUE;
2671 }
2672
2673 /***
2674 * DESCRIPTION:
2675 * Arranges listview items in icon display mode.
2676 *
2677 * PARAMETER(S):
2678 * [I] infoPtr : valid pointer to the listview structure
2679 * [I] nAlignCode : alignment code
2680 *
2681 * RETURN:
2682 * SUCCESS : TRUE
2683 * FAILURE : FALSE
2684 */
2685 static BOOL LISTVIEW_Arrange(LISTVIEW_INFO *infoPtr, INT nAlignCode)
2686 {
2687 void (*next_pos)(LISTVIEW_INFO *, LPPOINT);
2688 POINT pos;
2689 INT i;
2690
2691 if (infoPtr->uView != LV_VIEW_ICON && infoPtr->uView != LV_VIEW_SMALLICON) return FALSE;
2692
2693 TRACE("nAlignCode=%d\n", nAlignCode);
2694
2695 if (nAlignCode == LVA_DEFAULT)
2696 {
2697 if (infoPtr->dwStyle & LVS_ALIGNLEFT) nAlignCode = LVA_ALIGNLEFT;
2698 else nAlignCode = LVA_ALIGNTOP;
2699 }
2700
2701 switch (nAlignCode)
2702 {
2703 case LVA_ALIGNLEFT: next_pos = LISTVIEW_NextIconPosLeft; break;
2704 case LVA_ALIGNTOP: next_pos = LISTVIEW_NextIconPosTop; break;
2705 case LVA_SNAPTOGRID: next_pos = LISTVIEW_NextIconPosTop; break; /* FIXME */
2706 default: return FALSE;
2707 }
2708
2709 infoPtr->bAutoarrange = TRUE;
2710 infoPtr->currIconPos.x = infoPtr->currIconPos.y = 0;
2711 for (i = 0; i < infoPtr->nItemCount; i++)
2712 {
2713 next_pos(infoPtr, &pos);
2714 LISTVIEW_MoveIconTo(infoPtr, i, &pos, FALSE);
2715 }
2716
2717 return TRUE;
2718 }
2719
2720 /***
2721 * DESCRIPTION:
2722 * Retrieves the bounding rectangle of all the items, not offset by Origin.
2723 * For LVS_REPORT always returns empty rectangle.
2724 *
2725 * PARAMETER(S):
2726 * [I] infoPtr : valid pointer to the listview structure
2727 * [O] lprcView : bounding rectangle
2728 *
2729 * RETURN:
2730 * SUCCESS : TRUE
2731 * FAILURE : FALSE
2732 */
2733 static void LISTVIEW_GetAreaRect(const LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2734 {
2735 INT i, x, y;
2736
2737 SetRectEmpty(lprcView);
2738
2739 switch (infoPtr->uView)
2740 {
2741 case LV_VIEW_ICON:
2742 case LV_VIEW_SMALLICON:
2743 for (i = 0; i < infoPtr->nItemCount; i++)
2744 {
2745 x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, i);
2746 y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, i);
2747 lprcView->right = max(lprcView->right, x);
2748 lprcView->bottom = max(lprcView->bottom, y);
2749 }
2750 if (infoPtr->nItemCount > 0)
2751 {
2752 lprcView->right += infoPtr->nItemWidth;
2753 lprcView->bottom += infoPtr->nItemHeight;
2754 }
2755 break;
2756
2757 case LV_VIEW_LIST:
2758 y = LISTVIEW_GetCountPerColumn(infoPtr);
2759 x = infoPtr->nItemCount / y;
2760 if (infoPtr->nItemCount % y) x++;
2761 lprcView->right = x * infoPtr->nItemWidth;
2762 lprcView->bottom = y * infoPtr->nItemHeight;
2763 break;
2764 }
2765 }
2766
2767 /***
2768 * DESCRIPTION:
2769 * Retrieves the bounding rectangle of all the items.
2770 *
2771 * PARAMETER(S):
2772 * [I] infoPtr : valid pointer to the listview structure
2773 * [O] lprcView : bounding rectangle
2774 *
2775 * RETURN:
2776 * SUCCESS : TRUE
2777 * FAILURE : FALSE
2778 */
2779 static BOOL LISTVIEW_GetViewRect(const LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2780 {
2781 POINT ptOrigin;
2782
2783 TRACE("(lprcView=%p)\n", lprcView);
2784
2785 if (!lprcView) return FALSE;
2786
2787 LISTVIEW_GetAreaRect(infoPtr, lprcView);
2788
2789 if (infoPtr->uView != LV_VIEW_DETAILS)
2790 {
2791 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
2792 OffsetRect(lprcView, ptOrigin.x, ptOrigin.y);
2793 }
2794
2795 TRACE("lprcView=%s\n", wine_dbgstr_rect(lprcView));
2796
2797 return TRUE;
2798 }
2799
2800 /***
2801 * DESCRIPTION:
2802 * Retrieves the subitem pointer associated with the subitem index.
2803 *
2804 * PARAMETER(S):
2805 * [I] hdpaSubItems : DPA handle for a specific item
2806 * [I] nSubItem : index of subitem
2807 *
2808 * RETURN:
2809 * SUCCESS : subitem pointer
2810 * FAILURE : NULL
2811 */
2812 static SUBITEM_INFO* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems, INT nSubItem)
2813 {
2814 SUBITEM_INFO *lpSubItem;
2815 INT i;
2816
2817 /* we should binary search here if need be */
2818 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
2819 {
2820 lpSubItem = DPA_GetPtr(hdpaSubItems, i);
2821 if (lpSubItem->iSubItem == nSubItem)
2822 return lpSubItem;
2823 }
2824
2825 return NULL;
2826 }
2827
2828
2829 /***
2830 * DESCRIPTION:
2831 * Calculates the desired item width.
2832 *
2833 * PARAMETER(S):
2834 * [I] infoPtr : valid pointer to the listview structure
2835 *
2836 * RETURN:
2837 * The desired item width.
2838 */
2839 static INT LISTVIEW_CalculateItemWidth(const LISTVIEW_INFO *infoPtr)
2840 {
2841 INT nItemWidth = 0;
2842
2843 TRACE("uView=%d\n", infoPtr->uView);
2844
2845 if (infoPtr->uView == LV_VIEW_ICON)
2846 nItemWidth = infoPtr->iconSpacing.cx;
2847 else if (infoPtr->uView == LV_VIEW_DETAILS)
2848 {
2849 if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0)
2850 {
2851 RECT rcHeader;
2852 INT index;
2853
2854 index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX,
2855 DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, 0);
2856
2857 LISTVIEW_GetHeaderRect(infoPtr, index, &rcHeader);
2858 nItemWidth = rcHeader.right;
2859 }
2860 }
2861 else /* LV_VIEW_SMALLICON, or LV_VIEW_LIST */
2862 {
2863 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
2864 LVITEMW lvItem;
2865 INT i;
2866
2867 lvItem.mask = LVIF_TEXT;
2868 lvItem.iSubItem = 0;
2869
2870 for (i = 0; i < infoPtr->nItemCount; i++)
2871 {
2872 lvItem.iItem = i;
2873 lvItem.pszText = szDispText;
2874 lvItem.cchTextMax = DISP_TEXT_SIZE;
2875 if (LISTVIEW_GetItemW(infoPtr, &lvItem))
2876 nItemWidth = max(LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE),
2877 nItemWidth);
2878 }
2879
2880 if (infoPtr->himlSmall) nItemWidth += infoPtr->iconSize.cx;
2881 if (infoPtr->himlState) nItemWidth += infoPtr->iconStateSize.cx;
2882
2883 nItemWidth = max(DEFAULT_COLUMN_WIDTH, nItemWidth + WIDTH_PADDING);
2884 }
2885
2886 return nItemWidth;
2887 }
2888
2889 /***
2890 * DESCRIPTION:
2891 * Calculates the desired item height.
2892 *
2893 * PARAMETER(S):
2894 * [I] infoPtr : valid pointer to the listview structure
2895 *
2896 * RETURN:
2897 * The desired item height.
2898 */
2899 static INT LISTVIEW_CalculateItemHeight(const LISTVIEW_INFO *infoPtr)
2900 {
2901 INT nItemHeight;
2902
2903 TRACE("uView=%d\n", infoPtr->uView);
2904
2905 if (infoPtr->uView == LV_VIEW_ICON)
2906 nItemHeight = infoPtr->iconSpacing.cy;
2907 else
2908 {
2909 nItemHeight = infoPtr->ntmHeight;
2910 if (infoPtr->uView == LV_VIEW_DETAILS && infoPtr->dwLvExStyle & LVS_EX_GRIDLINES)
2911 nItemHeight++;
2912 if (infoPtr->himlState)
2913 nItemHeight = max(nItemHeight, infoPtr->iconStateSize.cy);
2914 if (infoPtr->himlSmall)
2915 nItemHeight = max(nItemHeight, infoPtr->iconSize.cy);
2916 if (infoPtr->himlState || infoPtr->himlSmall)
2917 nItemHeight += HEIGHT_PADDING;
2918 if (infoPtr->nMeasureItemHeight > 0)
2919 nItemHeight = infoPtr->nMeasureItemHeight;
2920 }
2921
2922 return max(nItemHeight, 1);
2923 }
2924
2925 /***
2926 * DESCRIPTION:
2927 * Updates the width, and height of an item.
2928 *
2929 * PARAMETER(S):
2930 * [I] infoPtr : valid pointer to the listview structure
2931 *
2932 * RETURN:
2933 * None.
2934 */
2935 static inline void LISTVIEW_UpdateItemSize(LISTVIEW_INFO *infoPtr)
2936 {
2937 infoPtr->nItemWidth = LISTVIEW_CalculateItemWidth(infoPtr);
2938 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
2939 }
2940
2941
2942 /***
2943 * DESCRIPTION:
2944 * Retrieves and saves important text metrics info for the current
2945 * Listview font.
2946 *
2947 * PARAMETER(S):
2948 * [I] infoPtr : valid pointer to the listview structure
2949 *
2950 */
2951 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO *infoPtr)
2952 {
2953 HDC hdc = GetDC(infoPtr->hwndSelf);
2954 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2955 HFONT hOldFont = SelectObject(hdc, hFont);
2956 TEXTMETRICW tm;
2957 SIZE sz;
2958
2959 if (GetTextMetricsW(hdc, &tm))
2960 {
2961 infoPtr->ntmHeight = tm.tmHeight;
2962 infoPtr->ntmMaxCharWidth = tm.tmMaxCharWidth;
2963 }
2964
2965 if (GetTextExtentPoint32A(hdc, "...", 3, &sz))
2966 infoPtr->nEllipsisWidth = sz.cx;
2967
2968 SelectObject(hdc, hOldFont);
2969 ReleaseDC(infoPtr->hwndSelf, hdc);
2970
2971 TRACE("tmHeight=%d\n", infoPtr->ntmHeight);
2972 }
2973
2974 /***
2975 * DESCRIPTION:
2976 * A compare function for ranges
2977 *
2978 * PARAMETER(S)
2979 * [I] range1 : pointer to range 1;
2980 * [I] range2 : pointer to range 2;
2981 * [I] flags : flags
2982 *
2983 * RETURNS:
2984 * > 0 : if range 1 > range 2
2985 * < 0 : if range 2 > range 1
2986 * = 0 : if range intersects range 2
2987 */
2988 static INT CALLBACK ranges_cmp(LPVOID range1, LPVOID range2, LPARAM flags)
2989 {
2990 INT cmp;
2991
2992 if (((RANGE*)range1)->upper <= ((RANGE*)range2)->lower)
2993 cmp = -1;
2994 else if (((RANGE*)range2)->upper <= ((RANGE*)range1)->lower)
2995 cmp = 1;
2996 else
2997 cmp = 0;
2998
2999 TRACE("range1=%s, range2=%s, cmp=%d\n", debugrange(range1), debugrange(range2), cmp);
3000
3001 return cmp;
3002 }
3003
3004 #define ranges_check(ranges, desc) if (TRACE_ON(listview)) ranges_assert(ranges, desc, __FUNCTION__, __LINE__)
3005
3006 static void ranges_assert(RANGES ranges, LPCSTR desc, const char *func, int line)
3007 {
3008 INT i;
3009 RANGE *prev, *curr;
3010
3011 TRACE("*** Checking %s:%d:%s ***\n", func, line, desc);
3012 assert (ranges);
3013 assert (DPA_GetPtrCount(ranges->hdpa) >= 0);
3014 ranges_dump(ranges);
3015 if (DPA_GetPtrCount(ranges->hdpa) > 0)
3016 {
3017 prev = DPA_GetPtr(ranges->hdpa, 0);
3018 assert (prev->lower >= 0 && prev->lower < prev->upper);
3019 for (i = 1; i < DPA_GetPtrCount(ranges->hdpa); i++)
3020 {
3021 curr = DPA_GetPtr(ranges->hdpa, i);
3022 assert (prev->upper <= curr->lower);
3023 assert (curr->lower < curr->upper);
3024 prev = curr;
3025 }
3026 }
3027 TRACE("--- Done checking---\n");
3028 }
3029
3030 static RANGES ranges_create(int count)
3031 {
3032 RANGES ranges = Alloc(sizeof(struct tagRANGES));
3033 if (!ranges) return NULL;
3034 ranges->hdpa = DPA_Create(count);
3035 if (ranges->hdpa) return ranges;
3036 Free(ranges);
3037 return NULL;
3038 }
3039
3040 static void ranges_clear(RANGES ranges)
3041 {
3042 INT i;
3043
3044 for(i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
3045 Free(DPA_GetPtr(ranges->hdpa, i));
3046 DPA_DeleteAllPtrs(ranges->hdpa);
3047 }
3048
3049
3050 static void ranges_destroy(RANGES ranges)
3051 {
3052 if (!ranges) return;
3053 ranges_clear(ranges);
3054 DPA_Destroy(ranges->hdpa);
3055 Free(ranges);
3056 }
3057
3058 static RANGES ranges_clone(RANGES ranges)
3059 {
3060 RANGES clone;
3061 INT i;
3062
3063 if (!(clone = ranges_create(DPA_GetPtrCount(ranges->hdpa)))) goto fail;
3064
3065 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
3066 {
3067 RANGE *newrng = Alloc(sizeof(RANGE));
3068 if (!newrng) goto fail;
3069 *newrng = *((RANGE*)DPA_GetPtr(ranges->hdpa, i));
3070 DPA_SetPtr(clone->hdpa, i, newrng);
3071 }
3072 return clone;
3073
3074 fail:
3075 TRACE ("clone failed\n");
3076 ranges_destroy(clone);
3077 return NULL;
3078 }
3079
3080 static RANGES ranges_diff(RANGES ranges, RANGES sub)
3081 {
3082 INT i;
3083
3084 for (i = 0; i < DPA_GetPtrCount(sub->hdpa); i++)
3085 ranges_del(ranges, *((RANGE *)DPA_GetPtr(sub->hdpa, i)));
3086
3087 return ranges;
3088 }
3089
3090 static void ranges_dump(RANGES ranges)
3091 {
3092 INT i;
3093
3094 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
3095 TRACE(" %s\n", debugrange