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