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