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