Implement WM_SETREDRAW properly.
[wine/dcerpc.git] / dlls / comctl32 / listview.c
blob0d40806436db7b327ceac7e86305ac93d0da3eda
1 /*
2 * Listview control
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
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.
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.
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
24 * NOTES
25 * Listview control implementation.
27 * TODO:
28 * -- Drawing optimizations.
29 * -- Hot item handling.
31 * Notifications:
32 * LISTVIEW_Notify : most notifications from children (editbox and header)
34 * Data structure:
35 * LISTVIEW_SetItemCount : not completed for non OWNERDATA
37 * Advanced functionality:
38 * LISTVIEW_GetNumberOfWorkAreas : not implemented
39 * LISTVIEW_GetISearchString : not implemented
40 * LISTVIEW_GetBkImage : not implemented
41 * LISTVIEW_SetBkImage : not implemented
42 * LISTVIEW_GetColumnOrderArray : simple hack only
43 * LISTVIEW_SetColumnOrderArray : simple hack only
44 * LISTVIEW_Arrange : empty stub
45 * LISTVIEW_ApproximateViewRect : incomplete
46 * LISTVIEW_Update : not completed
48 * Known differences in message stream from native control (not known if
49 * these differences cause problems):
50 * LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
51 * LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
52 * WM_PAINT does LVN_GETDISPINFO in item order 0->n, native does n->0.
53 * WM_SETREDRAW(True) native does LVN_GETDISPINFO for all items and
54 * does *not* invoke DefWindowProc
55 * WM_CREATE does not issue WM_QUERYUISTATE and associated registry
56 * processing for "USEDOUBLECLICKTIME".
60 #include "config.h"
61 #include "wine/port.h"
63 #include <ctype.h>
64 #include <string.h>
65 #include <stdlib.h>
66 #include <stdio.h>
68 #include "winbase.h"
69 #include "winnt.h"
70 #include "heap.h"
71 #include "commctrl.h"
72 #include "comctl32.h"
73 #include "wine/debug.h"
75 WINE_DEFAULT_DEBUG_CHANNEL(listview);
77 typedef struct tagCOLUMNCACHE
79 RECT rc;
80 UINT align;
81 } COLUMNCACHE, *LPCOLUMNCACHE;
83 typedef struct tagITEMHDR
85 LPWSTR pszText;
86 INT iImage;
87 } ITEMHDR, *LPITEMHDR;
89 typedef struct tagLISTVIEW_SUBITEM
91 ITEMHDR hdr;
92 INT iSubItem;
93 } LISTVIEW_SUBITEM;
95 typedef struct tagLISTVIEW_ITEM
97 ITEMHDR hdr;
98 UINT state;
99 LPARAM lParam;
100 INT iIndent;
101 BOOL valid;
102 } LISTVIEW_ITEM;
104 typedef struct tagRANGE
106 INT lower;
107 INT upper;
108 } RANGE;
110 typedef struct tagITERATOR
112 INT nItem;
113 RANGE range;
114 HDPA ranges;
115 INT index;
116 } ITERATOR;
118 typedef struct tagLISTVIEW_INFO
120 HWND hwndSelf;
121 HBRUSH hBkBrush;
122 COLORREF clrBk;
123 COLORREF clrText;
124 COLORREF clrTextBk;
125 HIMAGELIST himlNormal;
126 HIMAGELIST himlSmall;
127 HIMAGELIST himlState;
128 BOOL bLButtonDown;
129 BOOL bRButtonDown;
130 INT nItemHeight;
131 INT nItemWidth;
132 HDPA hdpaSelectionRanges;
133 INT nSelectionMark;
134 BOOL bRemovingAllSelections;
135 INT nHotItem;
136 SHORT notifyFormat;
137 RECT rcList; /* This rectangle is really the window
138 * client rectangle possibly reduced by the
139 * horizontal scroll bar and/or header - see
140 * LISTVIEW_UpdateSize. This rectangle offset
141 * by the LISTVIEW_GetOrigin value is in
142 * client coordinates */
143 RECT rcView; /* This rectangle contains all items -
144 * contructed in LISTVIEW_AlignTop and
145 * LISTVIEW_AlignLeft */
146 SIZE iconSize;
147 SIZE iconSpacing;
148 SIZE iconStateSize;
149 UINT uCallbackMask;
150 HWND hwndHeader;
151 HFONT hDefaultFont;
152 HCURSOR hHotCursor;
153 HFONT hFont;
154 INT ntmHeight; /* from GetTextMetrics from above font */
155 INT ntmAveCharWidth; /* from GetTextMetrics from above font */
156 BOOL bRedraw;
157 BOOL bFocus;
158 INT nFocusedItem;
159 RECT rcFocus;
160 DWORD dwStyle; /* the cached window GWL_STYLE */
161 DWORD dwLvExStyle; /* extended listview style */
162 INT nItemCount;
163 HDPA hdpaItems;
164 HDPA hdpaPosX; /* maintains the (X, Y) coordinates of the */
165 HDPA hdpaPosY; /* items in LVS_ICON, and LVS_SMALLICON modes */
166 PFNLVCOMPARE pfnCompare;
167 LPARAM lParamSort;
168 HWND hwndEdit;
169 BOOL bEditing;
170 WNDPROC EditWndProc;
171 INT nEditLabelItem;
172 DWORD dwHoverTime;
174 DWORD lastKeyPressTimestamp;
175 WPARAM charCode;
176 INT nSearchParamLength;
177 WCHAR szSearchParam[ MAX_PATH ];
178 BOOL bIsDrawing;
179 } LISTVIEW_INFO;
182 * constants
184 /* How many we debug buffer to allocate */
185 #define DEBUG_BUFFERS 20
186 /* The size of a single debug bbuffer */
187 #define DEBUG_BUFFER_SIZE 256
189 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
190 #define SB_INTERNAL -1
192 /* maximum size of a label */
193 #define DISP_TEXT_SIZE 512
195 /* padding for items in list and small icon display modes */
196 #define WIDTH_PADDING 12
198 /* padding for items in list, report and small icon display modes */
199 #define HEIGHT_PADDING 1
201 /* offset of items in report display mode */
202 #define REPORT_MARGINX 2
204 /* padding for icon in large icon display mode
205 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
206 * that HITTEST will see.
207 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
208 * ICON_TOP_PADDING - sum of the two above.
209 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
210 * LABEL_VERT_PADDING - between bottom of text and end of box
212 * ICON_LR_PADDING - additional width above icon size.
213 * ICON_LR_HALF - half of the above value
215 #define ICON_TOP_PADDING_NOTHITABLE 2
216 #define ICON_TOP_PADDING_HITABLE 2
217 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
218 #define ICON_BOTTOM_PADDING 4
219 #define LABEL_VERT_PADDING 7
220 #define ICON_LR_PADDING 16
221 #define ICON_LR_HALF (ICON_LR_PADDING/2)
223 /* default label width for items in list and small icon display modes */
224 #define DEFAULT_LABEL_WIDTH 40
226 /* default column width for items in list display mode */
227 #define DEFAULT_COLUMN_WIDTH 128
229 /* Size of "line" scroll for V & H scrolls */
230 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
232 /* Padding betwen image and label */
233 #define IMAGE_PADDING 2
235 /* Padding behind the label */
236 #define TRAILING_PADDING 5
238 /* Border for the icon caption */
239 #define CAPTION_BORDER 2
241 /* Standard DrawText flags */
242 #define LV_ML_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
243 #define LV_FL_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_NOCLIP)
244 #define LV_SL_DT_FLAGS (DT_TOP | DT_EDITCONTROL | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
246 /* The time in milisecods to reset the search in the list */
247 #define KEY_DELAY 450
249 /* Dump the LISTVIEW_INFO structure to the debug channel */
250 #define LISTVIEW_DUMP(iP) do { \
251 TRACE("hwndSelf=%08x, clrBk=0x%06lx, clrText=0x%06lx, clrTextBk=0x%06lx, ItemHeight=%d, ItemWidth=%d, Style=0x%08lx\n", \
252 iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
253 iP->nItemHeight, iP->nItemWidth, infoPtr->dwStyle); \
254 TRACE("hwndSelf=%08x, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08lx, Focus=%s\n", \
255 iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
256 iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, \
257 (iP->bFocus) ? "true" : "false"); \
258 TRACE("hwndSelf=%08x, ntmH=%d, icSz.cx=%ld, icSz.cy=%ld, icSp.cx=%ld, icSp.cy=%ld, notifyFmt=%d\n", \
259 iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
260 iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
261 TRACE("hwndSelf=%08x, rcList=(%d,%d)-(%d,%d), rcView=(%d,%d)-(%d,%d)\n", \
262 iP->hwndSelf, \
263 iP->rcList.left, iP->rcList.top, iP->rcList.right, iP->rcList.bottom, \
264 iP->rcView.left, iP->rcView.top, iP->rcView.right, iP->rcView.bottom); \
265 } while(0)
269 * forward declarations
271 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL);
272 static BOOL LISTVIEW_GetItemMeasures(LISTVIEW_INFO *, INT, LPRECT, LPRECT, LPRECT, LPRECT);
273 static void LISTVIEW_AlignLeft(LISTVIEW_INFO *);
274 static void LISTVIEW_AlignTop(LISTVIEW_INFO *);
275 static void LISTVIEW_AddGroupSelection(LISTVIEW_INFO *, INT);
276 static INT LISTVIEW_GetItemHeight(LISTVIEW_INFO *);
277 static BOOL LISTVIEW_GetItemListOrigin(LISTVIEW_INFO *, INT, LPPOINT);
278 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *, INT, LPPOINT);
279 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *, INT, LPRECT);
280 static INT LISTVIEW_CalculateMaxWidth(LISTVIEW_INFO *);
281 static BOOL LISTVIEW_GetSubItemRect(LISTVIEW_INFO *, INT, LPRECT);
282 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *, INT);
283 static LRESULT LISTVIEW_GetColumnWidth(LISTVIEW_INFO *, INT);
284 static BOOL LISTVIEW_GetOrigin(LISTVIEW_INFO *, LPPOINT);
285 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *, LPRECT);
286 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *, INT);
287 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL);
288 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *, INT, POINT);
289 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *);
290 static void LISTVIEW_SetSelection(LISTVIEW_INFO *, INT);
291 static BOOL LISTVIEW_UpdateSize(LISTVIEW_INFO *);
292 static void LISTVIEW_UnsupportedStyles(LONG);
293 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *, INT, BOOL);
294 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *, WPARAM, LPARAM);
295 static LRESULT LISTVIEW_SortItems(LISTVIEW_INFO *, PFNLVCOMPARE, LPARAM);
296 static LRESULT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *, LPCWSTR, BOOL);
297 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *, INT);
298 static LRESULT LISTVIEW_GetItemState(LISTVIEW_INFO *, INT, UINT);
299 static LRESULT LISTVIEW_SetItemState(LISTVIEW_INFO *, INT, LPLVITEMW);
300 static LRESULT LISTVIEW_GetColumnT(LISTVIEW_INFO *, INT, LPLVCOLUMNW, BOOL);
301 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *, INT, INT, HWND);
302 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *, INT, INT, HWND);
303 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *);
304 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *, INT, BOOL);
305 static HWND CreateEditLabelT(LISTVIEW_INFO *, LPCWSTR, DWORD, INT, INT, INT, INT, BOOL);
307 /******** Text handling functions *************************************/
309 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
310 * text string. The string may be ANSI or Unicode, in which case
311 * the boolean isW tells us the type of the string.
313 * The name of the function tell what type of strings it expects:
314 * W: Unicode, T: ANSI/Unicode - function of isW
317 static inline BOOL is_textW(LPCWSTR text)
319 return text != NULL && text != LPSTR_TEXTCALLBACKW;
322 static inline BOOL is_textT(LPCWSTR text, BOOL isW)
324 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
325 return is_textW(text);
328 static inline int textlenT(LPCWSTR text, BOOL isW)
330 return !is_textT(text, isW) ? 0 :
331 isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
334 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
336 if (isDestW)
337 if (isSrcW) lstrcpynW(dest, src, max);
338 else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
339 else
340 if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
341 else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
344 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
346 LPWSTR wstr = (LPWSTR)text;
348 if (!isW && is_textT(text, isW))
350 INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
351 wstr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
352 if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
354 TRACE(" wstr=%s\n", text == LPSTR_TEXTCALLBACKW ? "(callback)" : debugstr_w(wstr));
355 return wstr;
358 static inline void textfreeT(LPWSTR wstr, BOOL isW)
360 if (!isW && is_textT(wstr, isW)) HeapFree(GetProcessHeap(), 0, wstr);
364 * dest is a pointer to a Unicode string
365 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
367 static BOOL textsetptrT(LPWSTR *dest, LPWSTR src, BOOL isW)
369 BOOL bResult = TRUE;
371 if (src == LPSTR_TEXTCALLBACKW)
373 if (is_textW(*dest)) COMCTL32_Free(*dest);
374 *dest = LPSTR_TEXTCALLBACKW;
376 else
378 LPWSTR pszText = textdupTtoW(src, isW);
379 if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
380 bResult = Str_SetPtrW(dest, pszText);
381 textfreeT(pszText, isW);
383 return bResult;
387 * compares a Unicode to a Unicode/ANSI text string
389 static inline int textcmpWT(LPWSTR aw, LPWSTR bt, BOOL isW)
391 if (!aw) return bt ? -1 : 0;
392 if (!bt) return aw ? 1 : 0;
393 if (aw == LPSTR_TEXTCALLBACKW)
394 return bt == LPSTR_TEXTCALLBACKW ? 0 : -1;
395 if (bt != LPSTR_TEXTCALLBACKW)
397 LPWSTR bw = textdupTtoW(bt, isW);
398 int r = bw ? lstrcmpW(aw, bw) : 1;
399 textfreeT(bw, isW);
400 return r;
403 return 1;
406 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
408 int res;
410 n = min(min(n, strlenW(s1)), strlenW(s2));
411 res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
412 return res ? res - sizeof(WCHAR) : res;
415 /******** Debugging functions *****************************************/
417 static inline LPCSTR debugtext_t(LPCWSTR text, BOOL isW)
419 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
420 return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
423 static inline LPCSTR debugtext_tn(LPCWSTR text, BOOL isW, INT n)
425 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
426 n = min(textlenT(text, isW), n);
427 return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
430 static char* debug_getbuf()
432 static int index = 0;
433 static char buffers[DEBUG_BUFFERS][DEBUG_BUFFER_SIZE];
434 return buffers[index++ % DEBUG_BUFFERS];
437 static inline char* debugpoint(const POINT* lppt)
439 if (lppt)
441 char* buf = debug_getbuf();
442 snprintf(buf, DEBUG_BUFFER_SIZE, "(%ld, %ld)", lppt->x, lppt->y);
443 return buf;
444 } else return "(null)";
447 static inline char* debugrect(const RECT* rect)
449 if (rect)
451 char* buf = debug_getbuf();
452 snprintf(buf, DEBUG_BUFFER_SIZE, "[(%d, %d);(%d, %d)]",
453 rect->left, rect->top, rect->right, rect->bottom);
454 return buf;
455 } else return "(null)";
458 static char* debuglvitem_t(LPLVITEMW lpLVItem, BOOL isW)
460 char* buf = debug_getbuf(), *text = buf;
461 int len, size = DEBUG_BUFFER_SIZE;
463 if (lpLVItem == NULL) return "(null)";
464 len = snprintf(buf, size, "{iItem=%d, iSubItem=%d, ", lpLVItem->iItem, lpLVItem->iSubItem);
465 if (len == -1) goto end; buf += len; size -= len;
466 if (lpLVItem->mask & LVIF_STATE)
467 len = snprintf(buf, size, "state=%x, stateMask=%x, ", lpLVItem->state, lpLVItem->stateMask);
468 else len = 0;
469 if (len == -1) goto end; buf += len; size -= len;
470 if (lpLVItem->mask & LVIF_TEXT)
471 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem->pszText, isW, 80), lpLVItem->cchTextMax);
472 else len = 0;
473 if (len == -1) goto end; buf += len; size -= len;
474 if (lpLVItem->mask & LVIF_IMAGE)
475 len = snprintf(buf, size, "iImage=%d, ", lpLVItem->iImage);
476 else len = 0;
477 if (len == -1) goto end; buf += len; size -= len;
478 if (lpLVItem->mask & LVIF_PARAM)
479 len = snprintf(buf, size, "lParam=%lx, ", lpLVItem->lParam);
480 else len = 0;
481 if (len == -1) goto end; buf += len; size -= len;
482 if (lpLVItem->mask & LVIF_INDENT)
483 len = snprintf(buf, size, "iIndent=%d, ", lpLVItem->iIndent);
484 else len = 0;
485 if (len == -1) goto end; buf += len; size -= len;
486 goto undo;
487 end:
488 buf = text + strlen(text);
489 undo:
490 if (buf - text > 2) buf[-2] = '}'; buf[-1] = 0;
491 return text;
494 static char* debuglvcolumn_t(LPLVCOLUMNW lpColumn, BOOL isW)
496 char* buf = debug_getbuf(), *text = buf;
497 int len, size = DEBUG_BUFFER_SIZE;
499 if (lpColumn == NULL) return "(null)";
500 len = snprintf(buf, size, "{");
501 if (len == -1) goto end; buf += len; size -= len;
502 if (lpColumn->mask & LVCF_SUBITEM)
503 len = snprintf(buf, size, "iSubItem=%d, ", lpColumn->iSubItem);
504 else len = 0;
505 if (len == -1) goto end; buf += len; size -= len;
506 if (lpColumn->mask & LVCF_FMT)
507 len = snprintf(buf, size, "fmt=%x, ", lpColumn->fmt);
508 else len = 0;
509 if (len == -1) goto end; buf += len; size -= len;
510 if (lpColumn->mask & LVCF_WIDTH)
511 len = snprintf(buf, size, "cx=%d, ", lpColumn->cx);
512 else len = 0;
513 if (len == -1) goto end; buf += len; size -= len;
514 if (lpColumn->mask & LVCF_TEXT)
515 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn->pszText, isW, 80), lpColumn->cchTextMax);
516 else len = 0;
517 if (len == -1) goto end; buf += len; size -= len;
518 if (lpColumn->mask & LVCF_IMAGE)
519 len = snprintf(buf, size, "iImage=%d, ", lpColumn->iImage);
520 else len = 0;
521 if (len == -1) goto end; buf += len; size -= len;
522 if (lpColumn->mask & LVCF_ORDER)
523 len = snprintf(buf, size, "iOrder=%d, ", lpColumn->iOrder);
524 else len = 0;
525 if (len == -1) goto end; buf += len; size -= len;
526 goto undo;
527 end:
528 buf = text + strlen(text);
529 undo:
530 if (buf - text > 2) buf[-2] = '}'; buf[-1] = 0;
531 return text;
535 /******** Notification functions i************************************/
537 static inline BOOL notify_hdr(LISTVIEW_INFO *infoPtr, INT code, LPNMHDR pnmh)
539 pnmh->hwndFrom = infoPtr->hwndSelf;
540 pnmh->idFrom = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
541 pnmh->code = code;
542 return (BOOL)SendMessageW(GetParent(infoPtr->hwndSelf), WM_NOTIFY,
543 (WPARAM)pnmh->idFrom, (LPARAM)pnmh);
546 static inline BOOL notify(LISTVIEW_INFO *infoPtr, INT code)
548 NMHDR nmh;
549 return notify_hdr(infoPtr, code, &nmh);
552 static inline void notify_itemactivate(LISTVIEW_INFO *infoPtr)
554 notify(infoPtr, LVN_ITEMACTIVATE);
557 static inline BOOL notify_listview(LISTVIEW_INFO *infoPtr, INT code, LPNMLISTVIEW plvnm)
559 return notify_hdr(infoPtr, code, (LPNMHDR)plvnm);
562 static BOOL notify_click(LISTVIEW_INFO *infoPtr, INT code, LVHITTESTINFO *lvht)
564 NMLISTVIEW nmlv;
566 ZeroMemory(&nmlv, sizeof(nmlv));
567 nmlv.iItem = lvht->iItem;
568 nmlv.iSubItem = lvht->iSubItem;
569 nmlv.ptAction = lvht->pt;
570 return notify_listview(infoPtr, NM_RCLICK, &nmlv);
573 static int get_ansi_notification(INT unicodeNotificationCode)
575 switch (unicodeNotificationCode)
577 case LVN_BEGINLABELEDITW: return LVN_BEGINLABELEDITA;
578 case LVN_ENDLABELEDITW: return LVN_ENDLABELEDITA;
579 case LVN_GETDISPINFOW: return LVN_GETDISPINFOA;
580 case LVN_SETDISPINFOW: return LVN_SETDISPINFOA;
581 case LVN_ODFINDITEMW: return LVN_ODFINDITEMA;
582 case LVN_GETINFOTIPW: return LVN_GETINFOTIPA;
584 ERR("unknown notification %x\n", unicodeNotificationCode);
585 return unicodeNotificationCode;
589 Send notification. depends on dispinfoW having same
590 structure as dispinfoA.
591 infoPtr : listview struct
592 notificationCode : *Unicode* notification code
593 pdi : dispinfo structure (can be unicode or ansi)
594 isW : TRUE if dispinfo is Unicode
596 static BOOL notify_dispinfoT(LISTVIEW_INFO *infoPtr, INT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
598 BOOL bResult = FALSE;
599 BOOL convertToAnsi = FALSE, convertToUnicode = FALSE;
600 INT realNotifCode;
601 INT cchTempBufMax = 0, savCchTextMax = 0;
602 LPWSTR pszTempBuf = NULL, savPszText = NULL;
604 if ((pdi->item.mask & LVIF_TEXT) && is_textT(pdi->item.pszText, isW))
606 convertToAnsi = (isW && infoPtr->notifyFormat == NFR_ANSI);
607 convertToUnicode = (!isW && infoPtr->notifyFormat == NFR_UNICODE);
610 if (convertToAnsi || convertToUnicode)
612 if (notificationCode != LVN_GETDISPINFOW)
614 cchTempBufMax = convertToUnicode ?
615 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0):
616 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
618 else
620 cchTempBufMax = pdi->item.cchTextMax;
621 *pdi->item.pszText = 0; /* make sure we don't process garbage */
624 pszTempBuf = HeapAlloc(GetProcessHeap(), 0,
625 (convertToUnicode ? sizeof(WCHAR) : sizeof(CHAR)) * cchTempBufMax);
626 if (!pszTempBuf) return FALSE;
627 if (convertToUnicode)
628 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1,
629 pszTempBuf, cchTempBufMax);
630 else
631 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) pszTempBuf,
632 cchTempBufMax, NULL, NULL);
633 savCchTextMax = pdi->item.cchTextMax;
634 savPszText = pdi->item.pszText;
635 pdi->item.pszText = pszTempBuf;
636 pdi->item.cchTextMax = cchTempBufMax;
639 if (infoPtr->notifyFormat == NFR_ANSI)
640 realNotifCode = get_ansi_notification(notificationCode);
641 else
642 realNotifCode = notificationCode;
643 bResult = notify_hdr(infoPtr, realNotifCode, (LPNMHDR)pdi);
645 if (convertToUnicode || convertToAnsi)
647 if (convertToUnicode) /* note : pointer can be changed by app ! */
648 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) savPszText,
649 savCchTextMax, NULL, NULL);
650 else
651 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
652 savPszText, savCchTextMax);
653 pdi->item.pszText = savPszText; /* restores our buffer */
654 pdi->item.cchTextMax = savCchTextMax;
655 HeapFree(GetProcessHeap(), 0, pszTempBuf);
657 return bResult;
660 static inline void notify_odcachehint(LISTVIEW_INFO *infoPtr, RANGE range)
662 NMLVCACHEHINT nmlv;
664 nmlv.iFrom = range.lower;
665 nmlv.iTo = range.upper;
666 notify_hdr(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
669 static BOOL notify_customdraw (LISTVIEW_INFO *infoPtr, DWORD dwDrawStage, HDC hdc, RECT rc)
671 NMLVCUSTOMDRAW nmlvcd;
673 TRACE("(dwDrawStage=%lx, hdc=%x, rc=?)\n", dwDrawStage, hdc);
675 nmlvcd.nmcd.dwDrawStage = dwDrawStage;
676 nmlvcd.nmcd.hdc = hdc;
677 nmlvcd.nmcd.rc = rc;
678 nmlvcd.nmcd.dwItemSpec = 0;
679 nmlvcd.nmcd.uItemState = 0;
680 nmlvcd.nmcd.lItemlParam = 0;
681 nmlvcd.clrText = infoPtr->clrText;
682 nmlvcd.clrTextBk = infoPtr->clrBk;
684 return (BOOL)notify_hdr(infoPtr, NM_CUSTOMDRAW, &nmlvcd.nmcd.hdr);
687 /* FIXME: we should inline this where it's called somehow
688 * I think we need to pass in the structure
690 static BOOL notify_customdrawitem (LISTVIEW_INFO *infoPtr, HDC hdc, UINT iItem, UINT iSubItem, UINT uItemDrawState)
692 NMLVCUSTOMDRAW nmlvcd;
693 UINT uItemState;
694 RECT itemRect;
695 LVITEMW item;
696 BOOL bReturn;
698 item.iItem = iItem;
699 item.iSubItem = 0;
700 item.mask = LVIF_PARAM;
701 if (!LISTVIEW_GetItemT(infoPtr, &item, TRUE)) return FALSE;
703 uItemState = 0;
705 if (LISTVIEW_GetItemState(infoPtr, iItem, LVIS_SELECTED)) uItemState |= CDIS_SELECTED;
706 if (LISTVIEW_GetItemState(infoPtr, iItem, LVIS_FOCUSED)) uItemState |= CDIS_FOCUS;
707 if (iItem == infoPtr->nHotItem) uItemState |= CDIS_HOT;
709 itemRect.left = LVIR_BOUNDS;
710 LISTVIEW_GetItemRect(infoPtr, iItem, &itemRect);
712 nmlvcd.nmcd.dwDrawStage = CDDS_ITEM | uItemDrawState;
713 nmlvcd.nmcd.hdc = hdc;
714 nmlvcd.nmcd.rc = itemRect;
715 nmlvcd.nmcd.dwItemSpec = iItem;
716 nmlvcd.nmcd.uItemState = uItemState;
717 nmlvcd.nmcd.lItemlParam = item.lParam;
718 nmlvcd.clrText = infoPtr->clrText;
719 nmlvcd.clrTextBk = infoPtr->clrBk;
720 nmlvcd.iSubItem = iSubItem;
722 TRACE("drawstage=%lx hdc=%x item=%lx, itemstate=%x, lItemlParam=%lx\n",
723 nmlvcd.nmcd.dwDrawStage, nmlvcd.nmcd.hdc, nmlvcd.nmcd.dwItemSpec,
724 nmlvcd.nmcd.uItemState, nmlvcd.nmcd.lItemlParam);
726 bReturn = notify_hdr(infoPtr, NM_CUSTOMDRAW, &nmlvcd.nmcd.hdr);
728 infoPtr->clrText = nmlvcd.clrText;
729 infoPtr->clrBk = nmlvcd.clrTextBk;
731 return bReturn;
734 /******** Item iterator functions **********************************/
736 static BOOL ranges_add(HDPA ranges, RANGE range);
737 static BOOL ranges_del(HDPA ranges, RANGE range);
738 static void ranges_dump(HDPA ranges);
740 static inline BOOL iterator_next(ITERATOR* i)
742 if (i->nItem == -1)
744 if (i->ranges) goto pickarange;
745 return (i->nItem = i->range.lower) != -1;
747 i->nItem++;
748 if (i->nItem <= i->range.upper)
749 return TRUE;
751 pickarange:
752 if (i->ranges && i->index < i->ranges->nItemCount)
754 i->range = *(RANGE*)DPA_GetPtr(i->ranges, i->index++);
755 return (i->nItem = i->range.lower) != -1;
757 i->nItem = i->range.lower = i->range.upper = -1;
758 return FALSE;
761 static RANGE iterator_range(ITERATOR* i)
763 RANGE range;
765 if (!i->ranges) return i->range;
767 range.lower = (*(RANGE*)DPA_GetPtr(i->ranges, 0)).lower;
768 range.upper = (*(RANGE*)DPA_GetPtr(i->ranges, i->ranges->nItemCount - 1)).upper;
769 return range;
772 static inline void iterator_destroy(ITERATOR* i)
774 if (i->ranges) DPA_Destroy(i->ranges);
777 static inline BOOL iterator_empty(ITERATOR* i)
779 ZeroMemory(i, sizeof(*i));
780 i->nItem = i->range.lower = i->range.upper = -1;
781 return TRUE;
784 static BOOL iterator_frameditems(ITERATOR* i, LISTVIEW_INFO* infoPtr, const RECT* lprc)
786 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
787 INT lower, upper;
788 RECT frame = *lprc;
789 POINT Origin;
791 /* in case we fail, we want to return an empty iterator */
792 if (!iterator_empty(i)) return FALSE;
794 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return FALSE;
796 OffsetRect(&frame, -Origin.x, -Origin.y);
798 if (uView == LVS_ICON || uView == LVS_SMALLICON)
800 /* FIXME: we got to do better then this */
801 i->range.lower = 0;
802 i->range.upper = infoPtr->nItemCount - 1;
803 return TRUE;
805 else if (uView == LVS_REPORT)
807 if (frame.left >= infoPtr->nItemWidth) return TRUE;
808 if (frame.top >= infoPtr->nItemHeight * infoPtr->nItemCount) return TRUE;
810 lower = frame.top / infoPtr->nItemHeight;
811 upper = (frame.bottom - 1) / infoPtr->nItemHeight;
813 else
815 INT nPerCol = max((infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight, 1);
816 if (frame.top >= infoPtr->nItemHeight * nPerCol) return TRUE;
817 lower = (frame.left / infoPtr->nItemWidth) * nPerCol + frame.top / infoPtr->nItemHeight;
818 upper = ((frame.right - 1) / infoPtr->nItemWidth) * nPerCol + (frame.bottom - 1) / infoPtr->nItemHeight;
821 if (upper < 0 || lower >= infoPtr->nItemCount) return TRUE;
822 lower = max(lower, 0);
823 upper = min(upper, infoPtr->nItemCount - 1);
824 if (upper < lower) return TRUE;
825 i->range.lower = lower;
826 i->range.upper = upper;
828 return TRUE;
831 static BOOL iterator_clippeditems(ITERATOR* i, LISTVIEW_INFO *infoPtr, HDC hdc)
833 POINT Origin, Position;
834 RECT rcItem, rcClip;
835 INT nItem, rgntype;
837 rgntype = GetClipBox(hdc, &rcClip);
838 if (rgntype == NULLREGION) return iterator_empty(i);
839 if (!iterator_frameditems(i, infoPtr, &rcClip)) return FALSE;
840 if (rgntype == SIMPLEREGION) return TRUE;
842 /* if we can't deal with the region, we'll just go with the simple range */
843 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return TRUE;
844 if (!(i->ranges = DPA_Create(10))) return TRUE;
845 if (!ranges_add(i->ranges, i->range))
847 DPA_Destroy(i->ranges);
848 i->ranges = 0;
849 return TRUE;
852 /* no delete the invisible items from the list */
853 for (nItem = i->range.lower; nItem <= i->range.upper; nItem++)
855 if (!LISTVIEW_GetItemListOrigin(infoPtr, nItem, &Position)) continue;
856 rcItem.left = Position.x + Origin.x;
857 rcItem.top = Position.y + Origin.y;
858 rcItem.right = rcItem.left + infoPtr->nItemWidth;
859 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
860 if (!RectVisible(hdc, &rcItem))
862 RANGE item_range = { nItem, nItem };
863 ranges_del(i->ranges, item_range);
867 return TRUE;
870 static inline BOOL iterator_visibleitems(ITERATOR* i, LISTVIEW_INFO *infoPtr)
872 return iterator_frameditems(i, infoPtr, &infoPtr->rcList);
875 /******** Misc helper functions ************************************/
877 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
878 WPARAM wParam, LPARAM lParam, BOOL isW)
880 if (isW) return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
881 else return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
884 /******** Internal API functions ************************************/
886 /* The Invalidate* are macros, so we preserve the caller location */
887 #define LISTVIEW_InvalidateRect(infoPtr, rect) while(infoPtr->bRedraw) { \
888 TRACE(" invalidating rect=%s\n", debugrect(rect)); \
889 InvalidateRect(infoPtr->hwndSelf, rect, TRUE); \
890 break;\
893 #define LISTVIEW_InvalidateItem(infoPtr, nItem) do { \
894 RECT rcItem; \
895 rcItem.left = LVIR_BOUNDS; \
896 if(LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) \
897 LISTVIEW_InvalidateRect(infoPtr, &rcItem); \
898 } while (0)
900 #define LISTVIEW_InvalidateList(infoPtr)\
901 LISTVIEW_InvalidateRect(infoPtr, NULL)
903 static inline BOOL LISTVIEW_GetItemW(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem)
905 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
908 static inline int LISTVIEW_GetType(LISTVIEW_INFO *infoPtr)
910 return infoPtr->dwStyle & LVS_TYPEMASK;
913 /***
914 * DESCRIPTION:
915 * Retrieves the number of items that can fit vertically in the client area.
917 * PARAMETER(S):
918 * [I] infoPtr : valid pointer to the listview structure
920 * RETURN:
921 * Number of items per row.
923 static inline INT LISTVIEW_GetCountPerRow(LISTVIEW_INFO *infoPtr)
925 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
927 return max(nListWidth/infoPtr->nItemWidth, 1);
930 /***
931 * DESCRIPTION:
932 * Retrieves the number of items that can fit horizontally in the client
933 * area.
935 * PARAMETER(S):
936 * [I] infoPtr : valid pointer to the listview structure
938 * RETURN:
939 * Number of items per column.
941 static inline INT LISTVIEW_GetCountPerColumn(LISTVIEW_INFO *infoPtr)
943 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
945 return max(nListHeight / infoPtr->nItemHeight, 1);
949 /*************************************************************************
950 * LISTVIEW_ProcessLetterKeys
952 * Processes keyboard messages generated by pressing the letter keys
953 * on the keyboard.
954 * What this does is perform a case insensitive search from the
955 * current position with the following quirks:
956 * - If two chars or more are pressed in quick succession we search
957 * for the corresponding string (e.g. 'abc').
958 * - If there is a delay we wipe away the current search string and
959 * restart with just that char.
960 * - If the user keeps pressing the same character, whether slowly or
961 * fast, so that the search string is entirely composed of this
962 * character ('aaaaa' for instance), then we search for first item
963 * that starting with that character.
964 * - If the user types the above character in quick succession, then
965 * we must also search for the corresponding string ('aaaaa'), and
966 * go to that string if there is a match.
968 * PARAMETERS
969 * [I] hwnd : handle to the window
970 * [I] charCode : the character code, the actual character
971 * [I] keyData : key data
973 * RETURNS
975 * Zero.
977 * BUGS
979 * - The current implementation has a list of characters it will
980 * accept and it ignores averything else. In particular it will
981 * ignore accentuated characters which seems to match what
982 * Windows does. But I'm not sure it makes sense to follow
983 * Windows there.
984 * - We don't sound a beep when the search fails.
986 * SEE ALSO
988 * TREEVIEW_ProcessLetterKeys
990 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData)
992 INT nItem;
993 INT endidx,idx;
994 LVITEMW item;
995 WCHAR buffer[MAX_PATH];
996 DWORD lastKeyPressTimestamp = infoPtr->lastKeyPressTimestamp;
998 /* simple parameter checking */
999 if (!charCode || !keyData) return 0;
1001 /* only allow the valid WM_CHARs through */
1002 if (!isalnum(charCode) &&
1003 charCode != '.' && charCode != '`' && charCode != '!' &&
1004 charCode != '@' && charCode != '#' && charCode != '$' &&
1005 charCode != '%' && charCode != '^' && charCode != '&' &&
1006 charCode != '*' && charCode != '(' && charCode != ')' &&
1007 charCode != '-' && charCode != '_' && charCode != '+' &&
1008 charCode != '=' && charCode != '\\'&& charCode != ']' &&
1009 charCode != '}' && charCode != '[' && charCode != '{' &&
1010 charCode != '/' && charCode != '?' && charCode != '>' &&
1011 charCode != '<' && charCode != ',' && charCode != '~')
1012 return 0;
1014 /* if there's one item or less, there is no where to go */
1015 if (infoPtr->nItemCount <= 1) return 0;
1017 /* update the search parameters */
1018 infoPtr->lastKeyPressTimestamp = GetTickCount();
1019 if (infoPtr->lastKeyPressTimestamp - lastKeyPressTimestamp < KEY_DELAY) {
1020 if (infoPtr->nSearchParamLength < MAX_PATH)
1021 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
1022 if (infoPtr->charCode != charCode)
1023 infoPtr->charCode = charCode = 0;
1024 } else {
1025 infoPtr->charCode=charCode;
1026 infoPtr->szSearchParam[0]=charCode;
1027 infoPtr->nSearchParamLength=1;
1028 /* Redundant with the 1 char string */
1029 charCode=0;
1032 /* and search from the current position */
1033 nItem=-1;
1034 if (infoPtr->nFocusedItem >= 0) {
1035 endidx=infoPtr->nFocusedItem;
1036 idx=endidx;
1037 /* if looking for single character match,
1038 * then we must always move forward
1040 if (infoPtr->nSearchParamLength == 1)
1041 idx++;
1042 } else {
1043 endidx=infoPtr->nItemCount;
1044 idx=0;
1046 do {
1047 if (idx == infoPtr->nItemCount) {
1048 if (endidx == infoPtr->nItemCount || endidx == 0)
1049 break;
1050 idx=0;
1053 /* get item */
1054 item.mask = LVIF_TEXT;
1055 item.iItem = idx;
1056 item.iSubItem = 0;
1057 item.pszText = buffer;
1058 item.cchTextMax = MAX_PATH;
1059 if (!LISTVIEW_GetItemW(infoPtr, &item)) return 0;
1061 /* check for a match */
1062 if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
1063 nItem=idx;
1064 break;
1065 } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
1066 (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
1067 /* This would work but we must keep looking for a longer match */
1068 nItem=idx;
1070 idx++;
1071 } while (idx != endidx);
1073 if (nItem != -1)
1074 LISTVIEW_KeySelection(infoPtr, nItem);
1076 return 0;
1079 /*************************************************************************
1080 * LISTVIEW_UpdateHeaderSize [Internal]
1082 * Function to resize the header control
1084 * PARAMS
1085 * hwnd [I] handle to a window
1086 * nNewScrollPos [I] Scroll Pos to Set
1088 * RETURNS
1089 * nothing
1091 * NOTES
1093 static void LISTVIEW_UpdateHeaderSize(LISTVIEW_INFO *infoPtr, INT nNewScrollPos)
1095 RECT winRect;
1096 POINT point[2];
1098 TRACE("nNewScrollPos=%d\n", nNewScrollPos);
1100 GetWindowRect(infoPtr->hwndHeader, &winRect);
1101 point[0].x = winRect.left;
1102 point[0].y = winRect.top;
1103 point[1].x = winRect.right;
1104 point[1].y = winRect.bottom;
1106 MapWindowPoints(HWND_DESKTOP, infoPtr->hwndSelf, point, 2);
1107 point[0].x = -nNewScrollPos;
1108 point[1].x += nNewScrollPos;
1110 SetWindowPos(infoPtr->hwndHeader,0,
1111 point[0].x,point[0].y,point[1].x,point[1].y,
1112 SWP_NOZORDER | SWP_NOACTIVATE);
1115 /***
1116 * DESCRIPTION:
1117 * Update the scrollbars. This functions should be called whenever
1118 * the content, size or view changes.
1120 * PARAMETER(S):
1121 * [I] infoPtr : valid pointer to the listview structure
1123 * RETURN:
1124 * None
1126 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *infoPtr)
1128 LONG lStyle = infoPtr->dwStyle;
1129 UINT uView = lStyle & LVS_TYPEMASK;
1130 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1131 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1132 SCROLLINFO scrollInfo;
1134 if (lStyle & LVS_NOSCROLL) return;
1136 scrollInfo.cbSize = sizeof(SCROLLINFO);
1138 if (uView == LVS_LIST)
1140 /* update horizontal scrollbar */
1141 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
1142 INT nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
1144 TRACE("items=%d, perColumn=%d, perRow=%d\n",
1145 infoPtr->nItemCount, nCountPerColumn, nCountPerRow);
1147 scrollInfo.nMin = 0;
1148 scrollInfo.nMax = infoPtr->nItemCount / nCountPerColumn;
1149 if((infoPtr->nItemCount % nCountPerColumn) == 0)
1150 scrollInfo.nMax--;
1151 if (scrollInfo.nMax < 0) scrollInfo.nMax = 0;
1152 scrollInfo.nPos = ListView_GetTopIndex(infoPtr->hwndSelf) / nCountPerColumn;
1153 scrollInfo.nPage = nCountPerRow;
1154 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
1155 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
1156 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
1158 else if (uView == LVS_REPORT)
1160 BOOL test;
1162 /* update vertical scrollbar */
1163 scrollInfo.nMin = 0;
1164 scrollInfo.nMax = infoPtr->nItemCount - 1;
1165 scrollInfo.nPos = ListView_GetTopIndex(infoPtr->hwndSelf);
1166 scrollInfo.nPage = LISTVIEW_GetCountPerColumn(infoPtr);
1167 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
1168 test = (scrollInfo.nMin >= scrollInfo.nMax - max((INT)scrollInfo.nPage - 1, 0));
1169 TRACE("LVS_REPORT Vert. nMax=%d, nPage=%d, test=%d\n",
1170 scrollInfo.nMax, scrollInfo.nPage, test);
1171 SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
1172 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, (test) ? FALSE : TRUE);
1174 /* update horizontal scrollbar */
1175 nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1176 scrollInfo.fMask = SIF_POS;
1177 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)
1178 || infoPtr->nItemCount == 0)
1180 scrollInfo.nPos = 0;
1182 scrollInfo.nMin = 0;
1183 scrollInfo.nMax = max(infoPtr->nItemWidth, 0)-1;
1184 scrollInfo.nPage = nListWidth;
1185 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE ;
1186 test = (scrollInfo.nMin >= scrollInfo.nMax - max((INT)scrollInfo.nPage - 1, 0));
1187 TRACE("LVS_REPORT Horz. nMax=%d, nPage=%d, test=%d\n",
1188 scrollInfo.nMax, scrollInfo.nPage, test);
1189 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
1190 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, (test) ? FALSE : TRUE);
1192 /* Update the Header Control */
1193 scrollInfo.fMask = SIF_POS;
1194 GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo);
1195 LISTVIEW_UpdateHeaderSize(infoPtr, scrollInfo.nPos);
1198 else
1200 RECT rcView;
1202 if (LISTVIEW_GetViewRect(infoPtr, &rcView))
1204 INT nViewWidth = rcView.right - rcView.left;
1205 INT nViewHeight = rcView.bottom - rcView.top;
1207 /* Update Horizontal Scrollbar */
1208 scrollInfo.fMask = SIF_POS;
1209 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)
1210 || infoPtr->nItemCount == 0)
1212 scrollInfo.nPos = 0;
1214 scrollInfo.nMin = 0;
1215 scrollInfo.nMax = max(nViewWidth, 0)-1;
1216 scrollInfo.nPage = nListWidth;
1217 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
1218 TRACE("LVS_ICON/SMALLICON Horz.\n");
1219 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
1221 /* Update Vertical Scrollbar */
1222 nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1223 scrollInfo.fMask = SIF_POS;
1224 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)
1225 || infoPtr->nItemCount == 0)
1227 scrollInfo.nPos = 0;
1229 scrollInfo.nMin = 0;
1230 scrollInfo.nMax = max(nViewHeight,0)-1;
1231 scrollInfo.nPage = nListHeight;
1232 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
1233 TRACE("LVS_ICON/SMALLICON Vert.\n");
1234 SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
1240 /***
1241 * DESCRIPTION:
1242 * Shows/hides the focus rectangle.
1244 * PARAMETER(S):
1245 * [I] infoPtr : valid pointer to the listview structure
1246 * [I] fShow : TRUE to show the focus, FALSE to hide it.
1248 * RETURN:
1249 * None
1251 static void LISTVIEW_ShowFocusRect(LISTVIEW_INFO *infoPtr, BOOL fShow)
1253 HDC hdc;
1255 TRACE("fShow=%d, nItem=%d\n", fShow, infoPtr->nFocusedItem);
1257 if (infoPtr->nFocusedItem < 0) return;
1259 /* we need some gymnastics in ICON mode to handle large items */
1260 if ( (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON )
1262 RECT rcBox;
1264 if (!LISTVIEW_GetItemMeasures(infoPtr, infoPtr->nFocusedItem, &rcBox, 0, 0, 0))
1265 return;
1266 if ((rcBox.bottom - rcBox.top) > infoPtr->nItemHeight)
1268 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1269 return;
1273 if (!(hdc = GetDC(infoPtr->hwndSelf))) return;
1275 if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED)
1277 DRAWITEMSTRUCT dis;
1278 LVITEMW item;
1280 item.iItem = infoPtr->nFocusedItem;
1281 item.iSubItem = 0;
1282 item.mask = LVIF_PARAM;
1283 if (!LISTVIEW_GetItemW(infoPtr, &item)) goto done;
1285 ZeroMemory(&dis, sizeof(dis));
1286 dis.CtlType = ODT_LISTVIEW;
1287 dis.CtlID = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
1288 dis.itemID = item.iItem;
1289 dis.itemAction = ODA_FOCUS;
1290 if (fShow) dis.itemState |= ODS_FOCUS;
1291 dis.hwndItem = infoPtr->hwndSelf;
1292 dis.hDC = hdc;
1293 if (!LISTVIEW_GetItemMeasures(infoPtr, dis.itemID, &dis.rcItem, 0, 0, 0)) goto done;
1294 dis.itemData = item.lParam;
1296 SendMessageW(GetParent(infoPtr->hwndSelf), WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
1298 else
1300 DrawFocusRect(hdc, &infoPtr->rcFocus);
1302 done:
1303 ReleaseDC(infoPtr->hwndSelf, hdc);
1306 /***
1307 * Invalidates all visible selected items.
1309 static void LISTVIEW_InvalidateSelectedItems(LISTVIEW_INFO *infoPtr)
1311 ITERATOR i;
1313 iterator_visibleitems(&i, infoPtr);
1314 while(iterator_next(&i))
1316 if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED))
1317 LISTVIEW_InvalidateItem(infoPtr, i.nItem);
1319 iterator_destroy(&i);
1323 /***
1324 * DESCRIPTION:
1325 * Prints a message for unsupported window styles.
1326 * A kind of TODO list for window styles.
1328 * PARAMETER(S):
1329 * [I] LONG : window style
1331 * RETURN:
1332 * None
1334 static void LISTVIEW_UnsupportedStyles(LONG lStyle)
1336 if ((LVS_TYPESTYLEMASK & lStyle) == LVS_NOSCROLL)
1337 FIXME(" LVS_NOSCROLL\n");
1339 if (lStyle & LVS_NOLABELWRAP)
1340 FIXME(" LVS_NOLABELWRAP\n");
1342 if (lStyle & LVS_SORTASCENDING)
1343 FIXME(" LVS_SORTASCENDING\n");
1345 if (lStyle & LVS_SORTDESCENDING)
1346 FIXME(" LVS_SORTDESCENDING\n");
1350 /***
1351 * DESCRIPTION: [INTERNAL]
1352 * Computes an item's (left,top) corner, relative to rcView.
1353 * That is, the position has NOT been made relative to the Origin.
1354 * This is deliberate, to avoid computing the Origin over, and
1355 * over again, when this function is call in a loop. Instead,
1356 * one ca factor the computation of the Origin before the loop,
1357 * and offset the value retured by this function, on every iteration.
1359 * PARAMETER(S):
1360 * [I] infoPtr : valid pointer to the listview structure
1361 * [I] nItem : item number
1362 * [O] lpptOrig : item top, left corner
1364 * RETURN:
1365 * TRUE if computations OK
1366 * FALSE otherwise
1368 static BOOL LISTVIEW_GetItemListOrigin(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
1370 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1372 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1374 lpptPosition->x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1375 lpptPosition->y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1377 else if (uView == LVS_LIST)
1379 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
1380 lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
1381 lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight;
1383 else /* LVS_REPORT */
1385 lpptPosition->x = REPORT_MARGINX;
1386 lpptPosition->y = nItem * infoPtr->nItemHeight;
1389 return TRUE;
1392 /***
1393 * DESCRIPTION: [INTERNAL]
1394 * Compute the rectangles of an item. This is to localize all
1395 * the computations in one place. If you are not interested in some
1396 * of these values, simply pass in a NULL -- the fucntion is smart
1397 * enough to compute only what's necessary. The function computes
1398 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
1399 * one, the BOX rectangle. This rectangle is very cheap to compute,
1400 * and is guaranteed to contain all the other rectangles. Computing
1401 * the ICON rect is also cheap, but all the others are potentaily
1402 * expensive. This gives an easy and effective optimization when
1403 * searching (like point inclusion, or rectangle intersection):
1404 * first test against the BOX, and if TRUE, test agains the desired
1405 * rectangle. These optimizations are coded in:
1406 * LISTVIEW_PtInRect, and LISTVIEW_IntersectRect
1407 * use them wherever is appropriate.
1409 * PARAMETER(S):
1410 * [I] infoPtr : valid pointer to the listview structure
1411 * [I] nItem : item number
1412 * [O] lprcBox : ptr to Box rectangle
1413 * The internal LVIR_BOX rectangle
1414 * [O] lprcBounds : ptr to Bounds rectangle
1415 * Same as LVM_GETITEMRECT with LVIR_BOUNDS
1416 * [O] lprcIcon : ptr to Icon rectangle
1417 * Same as LVM_GETITEMRECT with LVIR_ICON
1418 * [O] lprcLabel : ptr to Label rectangle
1419 * Same as LVM_GETITEMRECT with LVIR_LABEL
1421 * RETURN:
1422 * TRUE if computations OK
1423 * FALSE otherwise
1425 static BOOL LISTVIEW_GetItemMeasures(LISTVIEW_INFO *infoPtr, INT nItem,
1426 LPRECT lprcBox, LPRECT lprcBounds,
1427 LPRECT lprcIcon, LPRECT lprcLabel)
1429 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1430 BOOL doIcon = FALSE, doLabel = FALSE, oversizedBox = FALSE;
1431 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
1432 RECT Box, Icon, Label;
1433 POINT Position, Origin;
1434 LVITEMW lvItem;
1436 /* Be smart and try to figure out the minimum we have to do */
1437 if (lprcBounds)
1439 if (uView == LVS_REPORT) doIcon = TRUE;
1440 else doLabel = TRUE;
1442 if (uView == LVS_ICON && infoPtr->bFocus &&
1443 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) &&
1444 LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
1445 oversizedBox = doLabel = TRUE;
1446 if (lprcLabel) doLabel = TRUE;
1447 if (doLabel || lprcIcon) doIcon = TRUE;
1449 /* get what we need from the item before hand, so we make
1450 * only one request. This can speed up things, if data
1451 * is stored on the app side */
1452 if (doLabel || (doIcon && uView == LVS_REPORT))
1454 lvItem.mask = 0;
1455 if (doIcon) lvItem.mask |= LVIF_INDENT;
1456 if (doLabel) lvItem.mask |= LVIF_TEXT;
1457 lvItem.iItem = nItem;
1458 lvItem.iSubItem = 0;
1459 lvItem.pszText = szDispText;
1460 lvItem.cchTextMax = DISP_TEXT_SIZE;
1461 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
1464 /************************************************************/
1465 /* compute the box rectangle (it should be cheap to do) */
1466 /************************************************************/
1467 if (!LISTVIEW_GetItemListOrigin(infoPtr, nItem, &Position)) return FALSE;
1468 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return FALSE;
1469 Box.left = Position.x + Origin.x;
1470 Box.top = Position.y + Origin.y;
1471 Box.right = Box.left + infoPtr->nItemWidth;
1472 Box.bottom = Box.top + infoPtr->nItemHeight;
1474 /************************************************************/
1475 /* compute ICON bounding box (ala LVM_GETITEMRECT) */
1476 /************************************************************/
1477 if (doIcon)
1479 if (uView == LVS_ICON)
1481 Icon.left = Box.left;
1482 if (infoPtr->himlNormal)
1483 Icon.left += (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2 - ICON_LR_HALF;
1484 Icon.top = Box.top;
1485 Icon.right = Icon.left;
1486 Icon.bottom = Icon.top;
1487 if (infoPtr->himlNormal)
1489 Icon.right += infoPtr->iconSize.cx + ICON_LR_PADDING;
1490 Icon.bottom += infoPtr->iconSize.cy + ICON_TOP_PADDING;
1493 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
1495 /* do indent */
1496 Icon.left = Box.left;
1497 if (uView == LVS_REPORT) Icon.left += infoPtr->iconSize.cx * lvItem.iIndent;
1498 if (infoPtr->himlState) Icon.left += infoPtr->iconStateSize.cx;
1499 Icon.top = Box.top;
1500 Icon.right = Icon.left;
1501 if (infoPtr->himlSmall) Icon.right += infoPtr->iconSize.cx;
1502 Icon.bottom = Icon.top + infoPtr->nItemHeight;
1504 if(lprcIcon) *lprcIcon = Icon;
1505 TRACE("hwnd=%x, item=%d, icon=%s\n", infoPtr->hwndSelf, nItem, debugrect(&Icon));
1508 /************************************************************/
1509 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
1510 /************************************************************/
1511 if (doLabel)
1513 SIZE labelSize = { 0, 0 };
1515 if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED)
1517 labelSize.cx = infoPtr->nItemWidth;
1518 labelSize.cy = infoPtr->nItemHeight;
1520 else if (is_textT(lvItem.pszText, TRUE))
1522 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
1523 HDC hdc = GetDC(infoPtr->hwndSelf);
1524 HFONT hOldFont = SelectObject(hdc, hFont);
1525 UINT uFormat;
1526 RECT rcText;
1528 /* compute rough rectangle where the label will go */
1529 SetRectEmpty(&rcText);
1530 rcText.right = infoPtr->nItemWidth - TRAILING_PADDING;
1531 rcText.bottom = infoPtr->nItemHeight;
1532 if (uView == LVS_ICON)
1533 rcText.bottom -= ICON_TOP_PADDING + infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
1535 /* now figure out the flags */
1536 if (uView == LVS_ICON)
1537 uFormat = oversizedBox ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS;
1538 else
1539 uFormat = LV_SL_DT_FLAGS;
1541 DrawTextW (hdc, lvItem.pszText, -1, &rcText, uFormat | DT_CALCRECT);
1543 labelSize.cx = min(rcText.right - rcText.left + TRAILING_PADDING, infoPtr->nItemWidth);
1544 labelSize.cy = rcText.bottom - rcText.top;
1546 SelectObject(hdc, hOldFont);
1547 ReleaseDC(infoPtr->hwndSelf, hdc);
1550 if (uView == LVS_ICON)
1552 Label.left = Box.left + (infoPtr->iconSpacing.cx - labelSize.cx) / 2;
1553 Label.top = Box.top + ICON_TOP_PADDING_HITABLE +
1554 infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
1555 Label.right = Label.left + labelSize.cx;
1556 if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED)
1557 Label.bottom = Label.top + infoPtr->nItemHeight;
1558 else
1560 if (!oversizedBox && labelSize.cy > infoPtr->ntmHeight)
1562 labelSize.cy = min(Box.bottom - Label.top, labelSize.cy);
1563 labelSize.cy /= infoPtr->ntmHeight;
1564 labelSize.cy = max(labelSize.cy, 1);
1565 labelSize.cy *= infoPtr->ntmHeight;
1567 Label.bottom = Label.top + labelSize.cy + HEIGHT_PADDING;
1570 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
1572 Label.left = Icon.right;
1573 Label.top = Box.top;
1574 Label.right = Label.left + labelSize.cx;
1575 if (infoPtr->himlSmall) Label.right += IMAGE_PADDING;
1576 if (Label.right > Box.right) Label.right = Box.right;
1577 Label.bottom = Label.top + infoPtr->nItemHeight;
1580 if (lprcLabel) *lprcLabel = Label;
1581 TRACE("hwnd=%x, item=%d, label=%s\n", infoPtr->hwndSelf, nItem, debugrect(&Label));
1584 /***********************************************************/
1585 /* compute bounds box for the item (ala LVM_GETITEMRECT) */
1586 /***********************************************************/
1587 if (lprcBounds)
1589 if (uView == LVS_REPORT)
1591 lprcBounds->left = infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT ? Box.left : Icon.left;
1592 lprcBounds->top = Box.top;
1593 lprcBounds->right = min(lprcBounds->left + infoPtr->nItemWidth, Box.right) - REPORT_MARGINX;
1594 if (lprcBounds->right < lprcBounds->left) lprcBounds->right = lprcBounds->left;
1595 lprcBounds->bottom = lprcBounds->top + infoPtr->nItemHeight;
1597 else
1598 UnionRect(lprcBounds, &Icon, &Label);
1599 TRACE("hwnd=%x, item=%d, bounds=%s\n", infoPtr->hwndSelf, nItem, debugrect(lprcBounds));
1602 if (oversizedBox) UnionRect(lprcBox, &Box, &Label);
1603 else if (lprcBox) *lprcBox = Box;
1604 TRACE("hwnd=%x, item=%d, box=%s\n", infoPtr->hwndSelf, nItem, debugrect(&Box));
1606 return TRUE;
1609 /***
1610 * DESCRIPTION:
1611 * Aligns the items with the top edge of the window.
1613 * PARAMETER(S):
1614 * [I] infoPtr : valid pointer to the listview structure
1616 * RETURN:
1617 * None
1619 static void LISTVIEW_AlignTop(LISTVIEW_INFO *infoPtr)
1621 UINT uView = LISTVIEW_GetType(infoPtr);
1622 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1623 POINT ptItem;
1624 RECT rcView;
1625 INT i, off_x=0, off_y=0;
1627 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1629 /* Since SetItemPosition uses upper-left of icon, and for
1630 style=LVS_ICON the icon is not left adjusted, get the offset */
1631 if (uView == LVS_ICON)
1633 off_y = ICON_TOP_PADDING;
1634 off_x = (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
1636 ptItem.x = off_x;
1637 ptItem.y = off_y;
1638 ZeroMemory(&rcView, sizeof(RECT));
1639 TRACE("Icon off.x=%d, off.y=%d, left=%d, right=%d\n",
1640 off_x, off_y,
1641 infoPtr->rcList.left, infoPtr->rcList.right);
1643 if (nListWidth > infoPtr->nItemWidth)
1645 for (i = 0; i < infoPtr->nItemCount; i++)
1647 if ((ptItem.x-off_x) + infoPtr->nItemWidth > nListWidth)
1649 ptItem.x = off_x;
1650 ptItem.y += infoPtr->nItemHeight;
1653 LISTVIEW_SetItemPosition(infoPtr, i, ptItem);
1654 ptItem.x += infoPtr->nItemWidth;
1655 rcView.right = max(rcView.right, ptItem.x);
1658 rcView.right -= off_x;
1659 rcView.bottom = (ptItem.y-off_y) + infoPtr->nItemHeight;
1661 else
1663 for (i = 0; i < infoPtr->nItemCount; i++)
1665 LISTVIEW_SetItemPosition(infoPtr, i, ptItem);
1666 ptItem.y += infoPtr->nItemHeight;
1669 rcView.right = infoPtr->nItemWidth;
1670 rcView.bottom = ptItem.y-off_y;
1673 infoPtr->rcView = rcView;
1677 /***
1678 * DESCRIPTION:
1679 * Aligns the items with the left edge of the window.
1681 * PARAMETER(S):
1682 * [I] infoPtr : valid pointer to the listview structure
1684 * RETURN:
1685 * None
1687 static void LISTVIEW_AlignLeft(LISTVIEW_INFO *infoPtr)
1689 UINT uView = LISTVIEW_GetType(infoPtr);
1690 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1691 POINT ptItem;
1692 RECT rcView;
1693 INT i, off_x=0, off_y=0;
1695 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1697 /* Since SetItemPosition uses upper-left of icon, and for
1698 style=LVS_ICON the icon is not left adjusted, get the offset */
1699 if (uView == LVS_ICON)
1701 off_y = ICON_TOP_PADDING;
1702 off_x = (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
1704 ptItem.x = off_x;
1705 ptItem.y = off_y;
1706 ZeroMemory(&rcView, sizeof(RECT));
1707 TRACE("Icon off.x=%d, off.y=%d\n", off_x, off_y);
1709 if (nListHeight > infoPtr->nItemHeight)
1711 for (i = 0; i < infoPtr->nItemCount; i++)
1713 if (ptItem.y + infoPtr->nItemHeight > nListHeight)
1715 ptItem.y = off_y;
1716 ptItem.x += infoPtr->nItemWidth;
1719 LISTVIEW_SetItemPosition(infoPtr, i, ptItem);
1720 ptItem.y += infoPtr->nItemHeight;
1721 rcView.bottom = max(rcView.bottom, ptItem.y);
1724 rcView.right = ptItem.x + infoPtr->nItemWidth;
1726 else
1728 for (i = 0; i < infoPtr->nItemCount; i++)
1730 LISTVIEW_SetItemPosition(infoPtr, i, ptItem);
1731 ptItem.x += infoPtr->nItemWidth;
1734 rcView.bottom = infoPtr->nItemHeight;
1735 rcView.right = ptItem.x;
1738 infoPtr->rcView = rcView;
1743 /***
1744 * DESCRIPTION:
1745 * Retrieves the bounding rectangle of all the items.
1747 * PARAMETER(S):
1748 * [I] infoPtr : valid pointer to the listview structure
1749 * [O] lprcView : bounding rectangle
1751 * RETURN:
1752 * SUCCESS : TRUE
1753 * FAILURE : FALSE
1755 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
1757 POINT ptOrigin;
1759 TRACE("(lprcView=%p)\n", lprcView);
1761 if (!lprcView) return FALSE;
1763 if (!LISTVIEW_GetOrigin(infoPtr, &ptOrigin)) return FALSE;
1765 *lprcView = infoPtr->rcView;
1766 OffsetRect(lprcView, ptOrigin.x, ptOrigin.y);
1768 TRACE("lprcView=%s\n", debugrect(lprcView));
1770 return TRUE;
1773 /***
1774 * DESCRIPTION:
1775 * Retrieves the subitem pointer associated with the subitem index.
1777 * PARAMETER(S):
1778 * [I] HDPA : DPA handle for a specific item
1779 * [I] INT : index of subitem
1781 * RETURN:
1782 * SUCCESS : subitem pointer
1783 * FAILURE : NULL
1785 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems,
1786 INT nSubItem)
1788 LISTVIEW_SUBITEM *lpSubItem;
1789 INT i;
1791 /* we should binary search here if need be */
1792 for (i = 1; i < hdpaSubItems->nItemCount; i++)
1794 lpSubItem = (LISTVIEW_SUBITEM *) DPA_GetPtr(hdpaSubItems, i);
1795 if (lpSubItem && (lpSubItem->iSubItem == nSubItem))
1796 return lpSubItem;
1799 return NULL;
1803 /***
1804 * DESCRIPTION:
1805 * Calculates the width of a specific item.
1807 * PARAMETER(S):
1808 * [I] infoPtr : valid pointer to the listview structure
1809 * [I] nItem : item to calculate width, or -1 for max of all
1811 * RETURN:
1812 * Returns the width of an item width an item.
1814 static INT LISTVIEW_CalculateItemWidth(LISTVIEW_INFO *infoPtr, INT nItem)
1816 UINT uView = LISTVIEW_GetType(infoPtr);
1817 INT nItemWidth = 0, i;
1819 if (uView == LVS_ICON)
1820 nItemWidth = infoPtr->iconSpacing.cx;
1821 else if (uView == LVS_REPORT)
1823 INT nHeaderItemCount;
1824 RECT rcHeaderItem;
1826 /* calculate width of header */
1827 nHeaderItemCount = Header_GetItemCount(infoPtr->hwndHeader);
1828 for (i = 0; i < nHeaderItemCount; i++)
1829 if (Header_GetItemRect(infoPtr->hwndHeader, i, &rcHeaderItem))
1830 nItemWidth += (rcHeaderItem.right - rcHeaderItem.left);
1832 else
1834 INT nLabelWidth;
1836 if (infoPtr->nItemCount == 0) return DEFAULT_COLUMN_WIDTH;
1838 /* get width of string */
1839 if (nItem == -1)
1841 for (i = 0; i < infoPtr->nItemCount; i++)
1843 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, i);
1844 nItemWidth = max(nItemWidth, nLabelWidth);
1847 else
1848 nItemWidth = LISTVIEW_GetLabelWidth(infoPtr, nItem);
1849 if (!nItemWidth) return DEFAULT_COLUMN_WIDTH;
1850 nItemWidth += WIDTH_PADDING;
1851 if (infoPtr->himlSmall) nItemWidth += infoPtr->iconSize.cx;
1852 if (infoPtr->himlState) nItemWidth += infoPtr->iconStateSize.cx;
1853 if (nItem == -1) nItemWidth = max(DEFAULT_COLUMN_WIDTH, nItemWidth);
1856 return max(nItemWidth, 1);
1859 /***
1860 * DESCRIPTION:
1861 * Calculates the max width of any item in the list.
1863 * PARAMETER(S):
1864 * [I] infoPtr : valid pointer to the listview structure
1865 * [I] LONG : window style
1867 * RETURN:
1868 * Returns item width.
1870 static inline INT LISTVIEW_CalculateMaxWidth(LISTVIEW_INFO *infoPtr)
1872 return LISTVIEW_CalculateItemWidth(infoPtr, -1);
1875 /***
1876 * DESCRIPTION:
1877 * Retrieves and saves important text metrics info for the current
1878 * Listview font.
1880 * PARAMETER(S):
1881 * [I] infoPtr : valid pointer to the listview structure
1884 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO *infoPtr)
1886 TEXTMETRICW tm;
1887 HDC hdc = GetDC(infoPtr->hwndSelf);
1888 HFONT hOldFont = SelectObject(hdc, infoPtr->hFont);
1889 INT oldHeight, oldACW;
1891 GetTextMetricsW(hdc, &tm);
1893 oldHeight = infoPtr->ntmHeight;
1894 oldACW = infoPtr->ntmAveCharWidth;
1895 infoPtr->ntmHeight = tm.tmHeight;
1896 infoPtr->ntmAveCharWidth = tm.tmAveCharWidth;
1898 SelectObject(hdc, hOldFont);
1899 ReleaseDC(infoPtr->hwndSelf, hdc);
1900 TRACE("tmHeight old=%d,new=%d; tmAveCharWidth old=%d,new=%d\n",
1901 oldHeight, infoPtr->ntmHeight, oldACW, infoPtr->ntmAveCharWidth);
1905 /***
1906 * DESCRIPTION:
1907 * Calculates the height of an item.
1909 * PARAMETER(S):
1910 * [I] infoPtr : valid pointer to the listview structure
1912 * RETURN:
1913 * Returns item height.
1915 static INT LISTVIEW_GetItemHeight(LISTVIEW_INFO *infoPtr)
1917 INT nItemHeight;
1919 if (LISTVIEW_GetType(infoPtr) == LVS_ICON)
1920 nItemHeight = infoPtr->iconSpacing.cy;
1921 else
1923 nItemHeight = infoPtr->ntmHeight;
1924 if (infoPtr->himlState)
1925 nItemHeight = max(nItemHeight, infoPtr->iconStateSize.cy);
1926 if (infoPtr->himlSmall)
1927 nItemHeight = max(nItemHeight, infoPtr->iconSize.cy);
1928 if (infoPtr->himlState || infoPtr->himlSmall)
1929 nItemHeight += HEIGHT_PADDING;
1931 return nItemHeight;
1934 #if 0
1935 static void LISTVIEW_PrintSelectionRanges(LISTVIEW_INFO *infoPtr)
1937 ERR("Selections are:\n");
1938 ranges_dump(infoPtr->hdpaSelectionRanges);
1940 #endif
1942 /***
1943 * DESCRIPTION:
1944 * A compare function for ranges
1946 * PARAMETER(S)
1947 * [I] range1 : pointer to range 1;
1948 * [I] range2 : pointer to range 2;
1949 * [I] flags : flags
1951 * RETURNS:
1952 * >0 : if Item 1 > Item 2
1953 * <0 : if Item 2 > Item 1
1954 * 0 : if Item 1 == Item 2
1956 static INT CALLBACK ranges_cmp(LPVOID range1, LPVOID range2, LPARAM flags)
1958 if (((RANGE*)range1)->upper < ((RANGE*)range2)->lower)
1959 return -1;
1960 if (((RANGE*)range2)->upper < ((RANGE*)range1)->lower)
1961 return 1;
1962 return 0;
1965 static void ranges_dump(HDPA ranges)
1967 INT i;
1969 for (i = 0; i < ranges->nItemCount; i++)
1971 RANGE *selection = DPA_GetPtr(ranges, i);
1972 ERR(" [%d - %d]\n", selection->lower, selection->upper);
1976 static inline BOOL ranges_contain(HDPA ranges, INT nItem)
1978 RANGE srchrng = { nItem, nItem };
1980 return DPA_Search(ranges, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED) != -1;
1983 static BOOL ranges_shift(HDPA ranges, INT nItem, INT delta)
1985 RANGE srchrng, *chkrng;
1986 INT index;
1988 srchrng.upper = nItem;
1989 srchrng.lower = nItem;
1991 index = DPA_Search(ranges, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
1992 if (index == -1) return TRUE;
1994 for (;index < ranges->nItemCount; index++)
1996 chkrng = DPA_GetPtr(ranges, index);
1997 if (chkrng->lower >= nItem && chkrng->lower + delta >= 0)
1998 chkrng->lower += delta;
1999 if (chkrng->upper >= nItem && chkrng->upper + delta >= 0)
2000 chkrng->upper += delta;
2002 return TRUE;
2005 static BOOL ranges_add(HDPA ranges, RANGE range)
2007 RANGE srchrgn;
2008 INT index;
2010 TRACE("range=(%i - %i)\n", range.lower, range.upper);
2011 if (TRACE_ON(listview)) ranges_dump(ranges);
2013 /* try find overlapping regions first */
2014 srchrgn.lower = range.lower - 1;
2015 srchrgn.upper = range.upper + 1;
2016 index = DPA_Search(ranges, &srchrgn, 0, ranges_cmp, 0, 0);
2018 if (index == -1)
2020 RANGE *newrgn;
2022 /* create the brand new range to insert */
2023 newrgn = (RANGE *)COMCTL32_Alloc(sizeof(RANGE));
2024 if(!newrgn) return FALSE;
2025 *newrgn = range;
2027 /* figure out where to insert it */
2028 index = DPA_Search(ranges, newrgn, 0, ranges_cmp, 0, DPAS_INSERTAFTER);
2029 if (index == -1) index = 0;
2031 /* and get it over with */
2032 DPA_InsertPtr(ranges, index, newrgn);
2034 else
2036 RANGE *chkrgn, *mrgrgn;
2037 INT fromindex, mergeindex;
2039 chkrgn = DPA_GetPtr(ranges, index);
2040 if (!chkrgn) return FALSE;
2041 TRACE("Merge with index %i (%d - %d)\n",
2042 index, chkrgn->lower, chkrgn->upper);
2044 chkrgn->lower = min(range.lower, chkrgn->lower);
2045 chkrgn->upper = max(range.upper, chkrgn->upper);
2047 TRACE("New range %i (%d - %d)\n",
2048 index, chkrgn->lower, chkrgn->upper);
2050 /* merge now common anges */
2051 fromindex = 0;
2052 srchrgn.lower = chkrgn->lower - 1;
2053 srchrgn.upper = chkrgn->upper + 1;
2057 mergeindex = DPA_Search(ranges, &srchrgn, fromindex, ranges_cmp, 0, 0);
2058 if (mergeindex == -1) break;
2059 if (mergeindex == index)
2061 fromindex = index + 1;
2062 continue;
2065 TRACE("Merge with index %i\n", mergeindex);
2067 mrgrgn = DPA_GetPtr(ranges, mergeindex);
2068 if (!mrgrgn) return FALSE;
2070 chkrgn->lower = min(chkrgn->lower, mrgrgn->lower);
2071 chkrgn->upper = max(chkrgn->upper, mrgrgn->upper);
2072 COMCTL32_Free(mrgrgn);
2073 DPA_DeletePtr(ranges, mergeindex);
2074 if (mergeindex < index) index --;
2075 } while(1);
2078 return TRUE;
2081 static BOOL ranges_del(HDPA ranges, RANGE range)
2083 RANGE remrgn, tmprgn, *chkrgn;
2084 BOOL done = FALSE;
2085 INT index;
2087 TRACE("range: (%d - %d)\n", range.lower, range.upper);
2089 remrgn = range;
2092 index = DPA_Search(ranges, &remrgn, 0, ranges_cmp, 0, 0);
2093 if (index == -1) return TRUE;
2095 chkrgn = DPA_GetPtr(ranges, index);
2096 if (!chkrgn) return FALSE;
2098 TRACE("Matches range index %i (%d - %d)\n",
2099 index, chkrgn->lower, chkrgn->upper);
2101 /* case 1: Same range */
2102 if ( (chkrgn->upper == remrgn.upper) &&
2103 (chkrgn->lower == remrgn.lower) )
2105 DPA_DeletePtr(ranges, index);
2106 done = TRUE;
2108 /* case 2: engulf */
2109 else if ( (chkrgn->upper <= remrgn.upper) &&
2110 (chkrgn->lower >= remrgn.lower) )
2112 DPA_DeletePtr(ranges, index);
2114 /* case 3: overlap upper */
2115 else if ( (chkrgn->upper <= remrgn.upper) &&
2116 (chkrgn->lower < remrgn.lower) )
2118 chkrgn->upper = remrgn.lower - 1;
2120 /* case 4: overlap lower */
2121 else if ( (chkrgn->upper > remrgn.upper) &&
2122 (chkrgn->lower >= remrgn.lower) )
2124 chkrgn->lower = remrgn.upper + 1;
2126 /* case 5: fully internal */
2127 else
2129 RANGE *newrgn = (RANGE *)COMCTL32_Alloc(sizeof(RANGE));
2130 if (!newrgn) return FALSE;
2131 tmprgn = *chkrgn;
2132 newrgn->lower = chkrgn->lower;
2133 newrgn->upper = remrgn.lower - 1;
2134 chkrgn->lower = remrgn.upper + 1;
2135 DPA_InsertPtr(ranges, index, newrgn);
2136 chkrgn = &tmprgn;
2139 while(!done);
2141 return TRUE;
2144 /***
2145 * Helper function for LISTVIEW_RemoveSelectionRange, and LISTVIEW_SetItem.
2147 static BOOL remove_selection_range(LISTVIEW_INFO *infoPtr, INT lower, INT upper, BOOL adj_sel_only)
2149 RANGE range = { lower, upper };
2150 LVITEMW lvItem;
2151 INT i;
2153 if (!ranges_del(infoPtr->hdpaSelectionRanges, range)) return FALSE;
2154 if (adj_sel_only) return TRUE;
2156 /* reset the selection on items */
2157 lvItem.state = 0;
2158 lvItem.stateMask = LVIS_SELECTED;
2159 for(i = lower; i <= upper; i++)
2160 LISTVIEW_SetItemState(infoPtr, i, &lvItem);
2162 return TRUE;
2164 /***
2165 * Helper function for LISTVIEW_AddSelectionRange, and LISTVIEW_SetItem.
2167 static BOOL add_selection_range(LISTVIEW_INFO *infoPtr, INT lower, INT upper, BOOL adj_sel_only)
2169 RANGE range = { lower, upper };
2170 LVITEMW lvItem;
2171 INT i;
2173 if (!ranges_add(infoPtr->hdpaSelectionRanges, range)) return FALSE;
2174 if (adj_sel_only) return TRUE;
2176 /* set the selection on items */
2177 lvItem.state = LVIS_SELECTED;
2178 lvItem.stateMask = LVIS_SELECTED;
2179 for(i = lower; i <= upper; i++)
2180 LISTVIEW_SetItemState(infoPtr, i, &lvItem);
2182 return TRUE;
2186 * DESCRIPTION:
2187 * Adds a selection range.
2189 * PARAMETER(S):
2190 * [I] infoPtr : valid pointer to the listview structure
2191 * [I] lower : lower item index
2192 * [I] upper : upper item index
2194 * RETURN:
2195 * Success: TRUE
2196 * Failure: FALSE
2198 static inline BOOL LISTVIEW_AddSelectionRange(LISTVIEW_INFO *infoPtr, INT lower, INT upper)
2200 return add_selection_range(infoPtr, lower, upper, FALSE);
2203 /***
2204 * DESCRIPTION:
2205 * Removes a range selections.
2207 * PARAMETER(S):
2208 * [I] infoPtr : valid pointer to the listview structure
2209 * [I] lower : lower item index
2210 * [I] upper : upper item index
2212 * RETURN:
2213 * Success: TRUE
2214 * Failure: FALSE
2216 static inline BOOL LISTVIEW_RemoveSelectionRange(LISTVIEW_INFO *infoPtr, INT lower, INT upper)
2218 return remove_selection_range(infoPtr, lower, upper, FALSE);
2221 /***
2222 * DESCRIPTION:
2223 * Removes all selection ranges
2225 * Parameters(s):
2226 * [I] infoPtr : valid pointer to the listview structure
2228 * RETURNS:
2229 * SUCCESS : TRUE
2230 * FAILURE : TRUE
2232 static LRESULT LISTVIEW_RemoveAllSelections(LISTVIEW_INFO *infoPtr)
2234 RANGE *sel;
2236 if (infoPtr->bRemovingAllSelections) return TRUE;
2238 infoPtr->bRemovingAllSelections = TRUE;
2240 TRACE("()\n");
2244 sel = DPA_GetPtr(infoPtr->hdpaSelectionRanges, 0);
2245 if (sel) LISTVIEW_RemoveSelectionRange(infoPtr, sel->lower, sel->upper);
2247 while (infoPtr->hdpaSelectionRanges->nItemCount > 0);
2249 infoPtr->bRemovingAllSelections = FALSE;
2251 return TRUE;
2254 /***
2255 * DESCRIPTION:
2256 * Retrieves the number of items that are marked as selected.
2258 * PARAMETER(S):
2259 * [I] infoPtr : valid pointer to the listview structure
2261 * RETURN:
2262 * Number of items selected.
2264 static INT LISTVIEW_GetSelectedCount(LISTVIEW_INFO *infoPtr)
2266 INT i, nSelectedCount = 0;
2268 if (infoPtr->uCallbackMask & LVIS_SELECTED)
2270 INT i;
2271 for (i = 0; i < infoPtr->nItemCount; i++)
2273 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
2274 nSelectedCount++;
2277 else
2279 for (i = 0; i < infoPtr->hdpaSelectionRanges->nItemCount; i++)
2281 RANGE *sel = DPA_GetPtr(infoPtr->hdpaSelectionRanges, i);
2282 nSelectedCount += sel->upper - sel->lower + 1;
2286 TRACE("nSelectedCount=%d\n", nSelectedCount);
2287 return nSelectedCount;
2290 /***
2291 * DESCRIPTION:
2292 * Manages the item focus.
2294 * PARAMETER(S):
2295 * [I] infoPtr : valid pointer to the listview structure
2296 * [I] INT : item index
2298 * RETURN:
2299 * TRUE : focused item changed
2300 * FALSE : focused item has NOT changed
2302 static inline BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem)
2304 INT oldFocus = infoPtr->nFocusedItem;
2305 LVITEMW lvItem;
2307 lvItem.state = LVIS_FOCUSED;
2308 lvItem.stateMask = LVIS_FOCUSED;
2309 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
2311 return oldFocus != infoPtr->nFocusedItem;
2315 * DESCRIPTION:
2316 * Updates the various indices after an item has been inserted or deleted.
2318 * PARAMETER(S):
2319 * [I] infoPtr : valid pointer to the listview structure
2320 * [I] nItem : item index
2321 * [I] direction : Direction of shift, +1 or -1.
2323 * RETURN:
2324 * None
2326 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction)
2328 TRACE("Shifting %iu, %i steps\n", nItem, direction);
2330 ranges_shift(infoPtr->hdpaSelectionRanges, nItem, direction);
2332 /* Note that the following will fail if direction != +1 and -1 */
2333 if (infoPtr->nSelectionMark > nItem)
2334 infoPtr->nSelectionMark += direction;
2335 else if (infoPtr->nSelectionMark == nItem)
2337 if (direction > 0)
2338 infoPtr->nSelectionMark += direction;
2339 else if (infoPtr->nSelectionMark >= infoPtr->nItemCount)
2340 infoPtr->nSelectionMark = infoPtr->nItemCount - 1;
2343 if (infoPtr->nFocusedItem > nItem)
2344 infoPtr->nFocusedItem += direction;
2345 else if (infoPtr->nFocusedItem == nItem)
2347 if (direction > 0)
2348 infoPtr->nFocusedItem += direction;
2349 else
2351 if (infoPtr->nFocusedItem >= infoPtr->nItemCount)
2352 infoPtr->nFocusedItem = infoPtr->nItemCount - 1;
2353 if (infoPtr->nFocusedItem >= 0)
2354 LISTVIEW_SetItemFocus(infoPtr, infoPtr->nFocusedItem);
2357 /* But we are not supposed to modify nHotItem! */
2362 * DESCRIPTION:
2363 * Adds a block of selections.
2365 * PARAMETER(S):
2366 * [I] infoPtr : valid pointer to the listview structure
2367 * [I] INT : item index
2369 * RETURN:
2370 * None
2372 static void LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2374 INT nFirst = min(infoPtr->nSelectionMark, nItem);
2375 INT nLast = max(infoPtr->nSelectionMark, nItem);
2376 INT i;
2377 LVITEMW item;
2379 if (nFirst == -1)
2380 nFirst = nItem;
2382 item.state = LVIS_SELECTED;
2383 item.stateMask = LVIS_SELECTED;
2385 /* FIXME: this is not correct LVS_OWNERDATA
2386 * See docu for LVN_ITEMCHANGED. Is there something similar for
2387 * RemoveGroupSelection (is there such a thing?)?
2389 for (i = nFirst; i <= nLast; i++)
2390 LISTVIEW_SetItemState(infoPtr,i,&item);
2392 LISTVIEW_SetItemFocus(infoPtr, nItem);
2393 infoPtr->nSelectionMark = nItem;
2397 /***
2398 * DESCRIPTION:
2399 * Sets a single group selection.
2401 * PARAMETER(S):
2402 * [I] infoPtr : valid pointer to the listview structure
2403 * [I] INT : item index
2405 * RETURN:
2406 * None
2408 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2410 UINT uView = LISTVIEW_GetType(infoPtr);
2411 INT i;
2412 LVITEMW item;
2413 POINT ptItem;
2414 RECT rcSel;
2416 LISTVIEW_RemoveAllSelections(infoPtr);
2418 item.state = LVIS_SELECTED;
2419 item.stateMask = LVIS_SELECTED;
2421 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
2423 INT nFirst, nLast;
2425 if (infoPtr->nSelectionMark == -1)
2426 infoPtr->nSelectionMark = nFirst = nLast = nItem;
2427 else
2429 nFirst = min(infoPtr->nSelectionMark, nItem);
2430 nLast = max(infoPtr->nSelectionMark, nItem);
2432 for (i = nFirst; i <= nLast; i++)
2433 LISTVIEW_SetItemState(infoPtr, i, &item);
2435 else
2437 RECT rcItem, rcSelMark;
2438 ITERATOR i;
2440 rcItem.left = LVIR_BOUNDS;
2441 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return;
2442 rcSelMark.left = LVIR_BOUNDS;
2443 if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) return;
2444 UnionRect(&rcSel, &rcItem, &rcSelMark);
2445 iterator_frameditems(&i, infoPtr, &rcSel);
2446 while(iterator_next(&i))
2448 LISTVIEW_GetItemPosition(infoPtr, i.nItem, &ptItem);
2449 if (PtInRect(&rcSel, ptItem))
2450 LISTVIEW_SetItemState(infoPtr, i.nItem, &item);
2452 iterator_destroy(&i);
2455 LISTVIEW_SetItemFocus(infoPtr, nItem);
2458 /***
2459 * DESCRIPTION:
2460 * Sets a single selection.
2462 * PARAMETER(S):
2463 * [I] infoPtr : valid pointer to the listview structure
2464 * [I] INT : item index
2466 * RETURN:
2467 * None
2469 static void LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2471 LVITEMW lvItem;
2473 TRACE("nItem=%d\n", nItem);
2475 LISTVIEW_RemoveAllSelections(infoPtr);
2477 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
2478 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
2479 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
2481 infoPtr->nSelectionMark = nItem;
2484 /***
2485 * DESCRIPTION:
2486 * Set selection(s) with keyboard.
2488 * PARAMETER(S):
2489 * [I] infoPtr : valid pointer to the listview structure
2490 * [I] INT : item index
2492 * RETURN:
2493 * SUCCESS : TRUE (needs to be repainted)
2494 * FAILURE : FALSE (nothing has changed)
2496 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem)
2498 /* FIXME: pass in the state */
2499 LONG lStyle = infoPtr->dwStyle;
2500 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
2501 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
2502 BOOL bResult = FALSE;
2504 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
2506 if (lStyle & LVS_SINGLESEL)
2508 bResult = TRUE;
2509 LISTVIEW_SetSelection(infoPtr, nItem);
2511 else
2513 if (wShift)
2515 bResult = TRUE;
2516 LISTVIEW_SetGroupSelection(infoPtr, nItem);
2518 else if (wCtrl)
2520 bResult = LISTVIEW_SetItemFocus(infoPtr, nItem);
2522 else
2524 bResult = TRUE;
2525 LISTVIEW_SetSelection(infoPtr, nItem);
2528 ListView_EnsureVisible(infoPtr->hwndSelf, nItem, FALSE);
2531 UpdateWindow(infoPtr->hwndSelf); /* update client area */
2532 return bResult;
2536 /***
2537 * DESCRIPTION:
2538 * Called when the mouse is being actively tracked and has hovered for a specified
2539 * amount of time
2541 * PARAMETER(S):
2542 * [I] infoPtr : valid pointer to the listview structure
2543 * [I] fwKeys : key indicator
2544 * [I] pts : mouse position
2546 * RETURN:
2547 * 0 if the message was processed, non-zero if there was an error
2549 * INFO:
2550 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
2551 * over the item for a certain period of time.
2554 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, WORD fwKyes, POINTS pts)
2556 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)
2557 /* FIXME: select the item!!! */
2558 /*LISTVIEW_GetItemAtPt(infoPtr, pt)*/;
2560 return 0;
2563 /***
2564 * DESCRIPTION:
2565 * Called whenever WM_MOUSEMOVE is received.
2567 * PARAMETER(S):
2568 * [I] infoPtr : valid pointer to the listview structure
2569 * [I] fwKeys : key indicator
2570 * [I] pts : mouse position
2572 * RETURN:
2573 * 0 if the message is processed, non-zero if there was an error
2575 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, POINTS pts)
2577 TRACKMOUSEEVENT trackinfo;
2579 /* see if we are supposed to be tracking mouse hovering */
2580 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) {
2581 /* fill in the trackinfo struct */
2582 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
2583 trackinfo.dwFlags = TME_QUERY;
2584 trackinfo.hwndTrack = infoPtr->hwndSelf;
2585 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
2587 /* see if we are already tracking this hwnd */
2588 _TrackMouseEvent(&trackinfo);
2590 if(!(trackinfo.dwFlags & TME_HOVER)) {
2591 trackinfo.dwFlags = TME_HOVER;
2593 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
2594 _TrackMouseEvent(&trackinfo);
2598 return 0;
2602 /***
2603 * Tests wheather the item is assignable to a list with style lStyle
2605 static inline BOOL is_assignable_item(LPLVITEMW lpLVItem, LONG lStyle)
2607 if ( (lpLVItem->mask & LVIF_TEXT) &&
2608 (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) &&
2609 (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE;
2611 return TRUE;
2614 /***
2615 * DESCRIPTION:
2616 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
2618 * PARAMETER(S):
2619 * [I] infoPtr : valid pointer to the listview structure
2620 * [I] lpLVItem : valid pointer to new item atttributes
2621 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2623 * RETURN:
2624 * SUCCESS : TRUE
2625 * FAILURE : FALSE
2627 static BOOL set_owner_item(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
2629 LONG lStyle = infoPtr->dwStyle;
2630 NMLISTVIEW nmlv;
2631 INT oldState;
2633 /* a virtual listview stores only the state for the main item */
2634 if (lpLVItem->iSubItem || !(lpLVItem->mask & LVIF_STATE)) return FALSE;
2636 oldState = LISTVIEW_GetItemState(infoPtr, lpLVItem->iItem, LVIS_FOCUSED | LVIS_SELECTED);
2637 TRACE("oldState=%x, newState=%x, uCallbackMask=%x\n",
2638 oldState, lpLVItem->state, infoPtr->uCallbackMask);
2640 /* we're done if we don't need to change anything we handle */
2641 if ( !((oldState ^ lpLVItem->state) & lpLVItem->stateMask &
2642 ~infoPtr->uCallbackMask & (LVIS_FOCUSED | LVIS_SELECTED))) return TRUE;
2645 * As per MSDN LVN_ITEMCHANGING notifications are _NOT_ sent for
2646 * by LVS_OWERNDATA list controls
2649 /* if we handle the focus, and we're asked to change it, do it now */
2650 if ( lpLVItem->stateMask & LVIS_FOCUSED )
2652 if (lpLVItem->state & LVIS_FOCUSED)
2653 infoPtr->nFocusedItem = lpLVItem->iItem;
2654 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
2655 infoPtr->nFocusedItem = -1;
2658 /* and the selection is the only other state a virtual list may hold */
2659 if (lpLVItem->stateMask & LVIS_SELECTED)
2661 if (lpLVItem->state & LVIS_SELECTED)
2663 if (lStyle & LVS_SINGLESEL) LISTVIEW_RemoveAllSelections(infoPtr);
2664 add_selection_range(infoPtr, lpLVItem->iItem, lpLVItem->iItem, TRUE);
2666 else
2667 remove_selection_range(infoPtr, lpLVItem->iItem, lpLVItem->iItem, TRUE);
2670 /* notify the parent now that things have changed */
2671 ZeroMemory(&nmlv, sizeof(nmlv));
2672 nmlv.iItem = lpLVItem->iItem;
2673 nmlv.uNewState = lpLVItem->state;
2674 nmlv.uOldState = oldState;
2675 nmlv.uChanged = LVIF_STATE;
2676 notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
2678 return TRUE;
2681 /***
2682 * DESCRIPTION:
2683 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
2685 * PARAMETER(S):
2686 * [I] infoPtr : valid pointer to the listview structure
2687 * [I] lpLVItem : valid pointer to new item atttributes
2688 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2690 * RETURN:
2691 * SUCCESS : TRUE
2692 * FAILURE : FALSE
2694 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
2696 LONG lStyle = infoPtr->dwStyle;
2697 UINT uView = lStyle & LVS_TYPEMASK;
2698 HDPA hdpaSubItems;
2699 LISTVIEW_ITEM *lpItem;
2700 NMLISTVIEW nmlv;
2701 UINT uChanged = 0;
2703 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2704 if (!hdpaSubItems && hdpaSubItems != (HDPA)-1) return FALSE;
2706 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
2707 if (!lpItem) return FALSE;
2709 /* determine what fields will change */
2710 if ((lpLVItem->mask & LVIF_STATE) &&
2711 ((lpItem->state ^ lpLVItem->state) & lpLVItem->stateMask))
2712 uChanged |= LVIF_STATE;
2714 if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage))
2715 uChanged |= LVIF_IMAGE;
2717 if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam))
2718 uChanged |= LVIF_PARAM;
2720 if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent))
2721 uChanged |= LVIF_INDENT;
2723 if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
2724 uChanged |= LVIF_TEXT;
2726 if (!uChanged) return TRUE;
2728 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
2729 nmlv.iItem = lpLVItem->iItem;
2730 nmlv.uNewState = lpLVItem->state & lpLVItem->stateMask;
2731 nmlv.uOldState = lpItem->state & lpLVItem->stateMask;
2732 nmlv.uChanged = uChanged;
2733 nmlv.lParam = lpItem->lParam;
2735 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
2736 if(lpItem->valid && notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv))
2737 return FALSE;
2739 /* copy information */
2740 if (lpLVItem->mask & LVIF_TEXT)
2741 textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
2743 if (lpLVItem->mask & LVIF_IMAGE)
2744 lpItem->hdr.iImage = lpLVItem->iImage;
2746 if (lpLVItem->mask & LVIF_PARAM)
2747 lpItem->lParam = lpLVItem->lParam;
2749 if (lpLVItem->mask & LVIF_INDENT)
2750 lpItem->iIndent = lpLVItem->iIndent;
2752 if (uChanged & LVIF_STATE)
2754 lpItem->state &= ~lpLVItem->stateMask;
2755 lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
2756 if (nmlv.uNewState & LVIS_SELECTED)
2758 if (lStyle & LVS_SINGLESEL) LISTVIEW_RemoveAllSelections(infoPtr);
2759 add_selection_range(infoPtr, lpLVItem->iItem, lpLVItem->iItem, TRUE);
2761 else if (lpLVItem->stateMask & LVIS_SELECTED)
2762 remove_selection_range(infoPtr, lpLVItem->iItem, lpLVItem->iItem, TRUE);
2764 /* if we are asked to change focus, and we manage it, do it */
2765 if (nmlv.uNewState & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
2767 if (lpLVItem->state & LVIS_FOCUSED)
2769 infoPtr->nFocusedItem = lpLVItem->iItem;
2770 LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, FALSE);
2772 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
2773 infoPtr->nFocusedItem = -1;
2777 /* if LVS_LIST or LVS_SMALLICON, update the width of the items */
2778 if((uChanged & LVIF_TEXT) && ((uView == LVS_LIST) || (uView == LVS_SMALLICON)))
2780 int item_width = LISTVIEW_CalculateItemWidth(infoPtr, lpLVItem->iItem);
2781 if(item_width > infoPtr->nItemWidth) infoPtr->nItemWidth = item_width;
2784 /* if we're inserting the item, we're done */
2785 if (!lpItem->valid) return TRUE;
2787 /* send LVN_ITEMCHANGED notification */
2788 nmlv.lParam = lpItem->lParam;
2789 notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
2791 return TRUE;
2794 /***
2795 * DESCRIPTION:
2796 * Helper for LISTVIEW_SetItemT *only*: sets subitem attributes.
2798 * PARAMETER(S):
2799 * [I] infoPtr : valid pointer to the listview structure
2800 * [I] lpLVItem : valid pointer to new subitem atttributes
2801 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2803 * RETURN:
2804 * SUCCESS : TRUE
2805 * FAILURE : FALSE
2807 static BOOL set_sub_item(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
2809 HDPA hdpaSubItems;
2810 LISTVIEW_SUBITEM *lpSubItem;
2811 BOOL bModified = FALSE;
2813 /* set subitem only if column is present */
2814 if (Header_GetItemCount(infoPtr->hwndHeader) <= lpLVItem->iSubItem)
2815 return FALSE;
2817 /* First do some sanity checks */
2818 if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE)) return FALSE;
2819 if (!(lpLVItem->mask & (LVIF_TEXT | LVIF_IMAGE))) return TRUE;
2821 /* get the subitem structure, and create it if not there */
2822 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2823 if (!hdpaSubItems) return FALSE;
2825 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
2826 if (!lpSubItem)
2828 LISTVIEW_SUBITEM *tmpSubItem;
2829 INT i;
2831 lpSubItem = (LISTVIEW_SUBITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM));
2832 if (!lpSubItem) return FALSE;
2833 /* we could binary search here, if need be...*/
2834 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2836 tmpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2837 if (tmpSubItem && tmpSubItem->iSubItem > lpLVItem->iSubItem) break;
2839 if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1)
2841 COMCTL32_Free(lpSubItem);
2842 return FALSE;
2844 lpSubItem->iSubItem = lpLVItem->iSubItem;
2845 bModified = TRUE;
2848 if (lpLVItem->mask & LVIF_IMAGE)
2849 if (lpSubItem->hdr.iImage != lpLVItem->iImage)
2851 lpSubItem->hdr.iImage = lpLVItem->iImage;
2852 bModified = TRUE;
2855 if (lpLVItem->mask & LVIF_TEXT)
2856 if (lpSubItem->hdr.pszText != lpLVItem->pszText)
2858 textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW);
2859 bModified = TRUE;
2862 if (bModified && !infoPtr->bIsDrawing)
2864 RECT rect;
2866 rect.left = LVIR_BOUNDS;
2867 rect.top = lpLVItem->iSubItem;
2868 /* GetSubItemRect will fail in non-report mode, so there's
2869 * gonna be no invalidation then, yay! */
2870 if (LISTVIEW_GetSubItemRect(infoPtr, lpLVItem->iItem, &rect))
2871 LISTVIEW_InvalidateRect(infoPtr, &rect);
2874 return TRUE;
2877 /***
2878 * DESCRIPTION:
2879 * Sets item attributes.
2881 * PARAMETER(S):
2882 * [I] infoPtr : valid pointer to the listview structure
2883 * [I] LPLVITEM : new item atttributes
2884 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2886 * RETURN:
2887 * SUCCESS : TRUE
2888 * FAILURE : FALSE
2890 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
2892 INT nOldFocus = infoPtr->nFocusedItem;
2893 LPWSTR pszText = NULL;
2894 BOOL bResult;
2896 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
2898 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
2899 return FALSE;
2901 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
2902 if ((lpLVItem->mask & LVIF_TEXT) && is_textW(lpLVItem->pszText))
2904 pszText = lpLVItem->pszText;
2905 lpLVItem->pszText = textdupTtoW(lpLVItem->pszText, isW);
2908 /* actually set the fields */
2909 if (infoPtr->dwStyle & LVS_OWNERDATA)
2910 bResult = set_owner_item(infoPtr, lpLVItem, TRUE);
2911 else
2913 /* sanity checks first */
2914 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE;
2916 if (lpLVItem->iSubItem)
2917 bResult = set_sub_item(infoPtr, lpLVItem, TRUE);
2918 else
2919 bResult = set_main_item(infoPtr, lpLVItem, TRUE);
2922 /* redraw item, if necessary */
2923 if (bResult && !infoPtr->bIsDrawing && lpLVItem->iSubItem == 0)
2925 if (nOldFocus != infoPtr->nFocusedItem && infoPtr->bFocus)
2926 LISTVIEW_InvalidateRect(infoPtr, &infoPtr->rcFocus);
2928 /* this little optimization eliminates some nasty flicker */
2929 if ( (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT &&
2930 !(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) &&
2931 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED))
2933 RECT rect;
2935 rect.left = LVIR_BOUNDS;
2936 rect.top = 0;
2937 if (LISTVIEW_GetSubItemRect(infoPtr, lpLVItem->iItem, &rect))
2938 LISTVIEW_InvalidateRect(infoPtr, &rect);
2940 else
2941 LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem);
2943 /* restore text */
2944 if (pszText)
2946 textfreeT(lpLVItem->pszText, isW);
2947 lpLVItem->pszText = pszText;
2950 return bResult;
2953 /***
2954 * DESCRIPTION:
2955 * Retrieves the index of the item at coordinate (0, 0) of the client area.
2957 * PARAMETER(S):
2958 * [I] infoPtr : valid pointer to the listview structure
2960 * RETURN:
2961 * item index
2963 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *infoPtr)
2965 LONG lStyle = infoPtr->dwStyle;
2966 UINT uView = lStyle & LVS_TYPEMASK;
2967 INT nItem = 0;
2968 SCROLLINFO scrollInfo;
2970 scrollInfo.cbSize = sizeof(SCROLLINFO);
2971 scrollInfo.fMask = SIF_POS;
2973 if (uView == LVS_LIST)
2975 if ((lStyle & WS_HSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
2976 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
2978 else if (uView == LVS_REPORT)
2980 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
2981 nItem = scrollInfo.nPos;
2983 else
2985 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
2986 nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight);
2989 TRACE("nItem=%d\n", nItem);
2991 return nItem;
2994 /* used by the drawing code */
2995 typedef struct tagTEXTATTR
2997 int bkMode;
2998 COLORREF bkColor;
2999 COLORREF fgColor;
3000 } TEXTATTR;
3002 /* helper function for the drawing code */
3003 static inline void set_text_attr(HDC hdc, TEXTATTR *ta)
3005 ta->bkMode = SetBkMode(hdc, ta->bkMode);
3006 ta->bkColor = SetBkColor(hdc, ta->bkColor);
3007 ta->fgColor = SetTextColor(hdc, ta->fgColor);
3010 /* helper function for the drawing code */
3011 static void select_text_attr(LISTVIEW_INFO *infoPtr, HDC hdc, BOOL isSelected, TEXTATTR *ta)
3013 ta->bkMode = OPAQUE;
3015 if (isSelected && infoPtr->bFocus)
3017 ta->bkColor = comctl32_color.clrHighlight;
3018 ta->fgColor = comctl32_color.clrHighlightText;
3020 else if (isSelected && (infoPtr->dwStyle & LVS_SHOWSELALWAYS))
3022 ta->bkColor = comctl32_color.clr3dFace;
3023 ta->fgColor = comctl32_color.clrBtnText;
3025 else if ( (infoPtr->clrTextBk != CLR_DEFAULT) && (infoPtr->clrTextBk != CLR_NONE) )
3027 ta->bkColor = infoPtr->clrTextBk;
3028 ta->fgColor = infoPtr->clrText;
3030 else
3032 ta->bkMode = TRANSPARENT;
3033 ta->bkColor = GetBkColor(hdc);
3034 ta->fgColor = infoPtr->clrText;
3037 set_text_attr(hdc, ta);
3040 /***
3041 * DESCRIPTION:
3042 * Erases the background of the given rectangle
3044 * PARAMETER(S):
3045 * [I] infoPtr : valid pointer to the listview structure
3046 * [I] hdc : device context handle
3047 * [I] lprcBox : clipping rectangle
3049 * RETURN:
3050 * Success: TRUE
3051 * Failure: FALSE
3053 static inline BOOL LISTVIEW_FillBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT* lprcBox)
3055 if (!infoPtr->hBkBrush) return FALSE;
3057 TRACE("(hdc=%x, lprcBox=%s, hBkBrush=%x)\n", hdc, debugrect(lprcBox), infoPtr->hBkBrush);
3059 return FillRect(hdc, lprcBox, infoPtr->hBkBrush);
3062 /***
3063 * DESCRIPTION:
3064 * Draws a subitem.
3066 * PARAMETER(S):
3067 * [I] infoPtr : valid pointer to the listview structure
3068 * [I] HDC : device context handle
3069 * [I] INT : item index
3070 * [I] INT : subitem index
3071 * [I] RECT * : clipping rectangle
3073 * RETURN:
3074 * Success: TRUE
3075 * Failure: FALSE
3077 static BOOL LISTVIEW_DrawSubItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem,
3078 INT nSubItem, RECT rcItem, UINT align)
3080 WCHAR szDispText[DISP_TEXT_SIZE];
3081 LVITEMW lvItem;
3083 TRACE("(hdc=%x, nItem=%d, nSubItem=%d, rcItem=%s)\n",
3084 hdc, nItem, nSubItem, debugrect(&rcItem));
3086 /* get information needed for drawing the item */
3087 lvItem.mask = LVIF_TEXT | LVIF_IMAGE;
3088 lvItem.iItem = nItem;
3089 lvItem.iSubItem = nSubItem;
3090 lvItem.cchTextMax = DISP_TEXT_SIZE;
3091 lvItem.pszText = szDispText;
3092 *lvItem.pszText = '\0';
3093 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
3095 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3097 if (lvItem.iImage) FIXME("Draw the image for the subitem\n");
3099 DrawTextW(hdc, lvItem.pszText, -1, &rcItem, LV_SL_DT_FLAGS | align);
3101 return TRUE;
3105 /***
3106 * DESCRIPTION:
3107 * Draws an item.
3109 * PARAMETER(S):
3110 * [I] infoPtr : valid pointer to the listview structure
3111 * [I] hdc : device context handle
3112 * [I] nItem : item index
3113 * [I] rcItem : item rectangle
3115 * RETURN:
3116 * TRUE: if item is focused
3117 * FALSE: otherwise
3119 static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, RECT rcItem)
3121 WCHAR szDispText[DISP_TEXT_SIZE];
3122 INT nLabelWidth, imagePadding = 0;
3123 RECT* lprcFocus, rcOrig = rcItem;
3124 LVITEMW lvItem;
3125 TEXTATTR ta;
3127 TRACE("(hdc=%x, nItem=%d)\n", hdc, nItem);
3129 /* get information needed for drawing the item */
3130 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE | LVIF_INDENT;
3131 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
3132 lvItem.iItem = nItem;
3133 lvItem.iSubItem = 0;
3134 lvItem.cchTextMax = DISP_TEXT_SIZE;
3135 lvItem.pszText = szDispText;
3136 *lvItem.pszText = '\0';
3137 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
3138 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3140 /* now check if we need to update the focus rectangle */
3141 lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
3142 if (lprcFocus) SetRectEmpty(lprcFocus);
3144 /* do indent */
3145 rcItem.left += infoPtr->iconSize.cx * lvItem.iIndent;
3147 /* state icons */
3148 if (infoPtr->himlState)
3150 UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12;
3151 if (uStateImage)
3153 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc,
3154 rcItem.left, rcItem.top, ILD_NORMAL);
3156 rcItem.left += infoPtr->iconStateSize.cx;
3157 imagePadding = IMAGE_PADDING;
3160 /* small icons */
3161 if (infoPtr->himlSmall)
3163 if (lvItem.iImage >= 0)
3165 UINT mode = (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus) ?
3166 ILD_SELECTED : ILD_NORMAL;
3167 ImageList_SetBkColor(infoPtr->himlSmall, CLR_NONE);
3168 ImageList_Draw(infoPtr->himlSmall, lvItem.iImage, hdc,
3169 rcItem.left, rcItem.top, mode);
3171 rcItem.left += infoPtr->iconSize.cx;
3172 imagePadding = IMAGE_PADDING;
3175 /* Don't bother painting item being edited */
3176 if (infoPtr->bEditing && lprcFocus)
3177 return FALSE;
3179 select_text_attr(infoPtr, hdc, lvItem.state & LVIS_SELECTED, &ta);
3181 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
3182 rcItem.left += imagePadding;
3183 rcItem.right = rcItem.left + nLabelWidth + TRAILING_PADDING;
3184 if (rcItem.right > rcOrig.right) rcItem.right = rcOrig.right;
3186 if (lvItem.pszText)
3188 TRACE("drawing text=%s, in rect=%s\n", debugstr_w(lvItem.pszText), debugrect(&rcItem));
3189 if(lprcFocus) *lprcFocus = rcItem;
3190 if (lvItem.state & LVIS_SELECTED)
3191 ExtTextOutW(hdc, rcItem.left, rcItem.top, ETO_OPAQUE, &rcItem, 0, 0, 0);
3192 DrawTextW(hdc, lvItem.pszText, -1, &rcItem, LV_SL_DT_FLAGS | DT_CENTER);
3195 set_text_attr(hdc, &ta);
3196 return lprcFocus != NULL;
3199 /***
3200 * DESCRIPTION:
3201 * Draws an item when in large icon display mode.
3203 * PARAMETER(S):
3204 * [I] infoPtr : valid pointer to the listview structure
3205 * [I] hdc : device context handle
3206 * [I] nItem : item index
3207 * [I] rcItem : clipping rectangle
3209 * RETURN:
3210 * TRUE: if item is focused
3211 * FALSE: otherwise
3213 static BOOL LISTVIEW_DrawLargeItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, RECT rcItem)
3215 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3216 LVITEMW lvItem;
3217 UINT uFormat;
3218 RECT rcIcon, rcFocus, rcLabel, *lprcFocus;
3220 TRACE("(hdc=%x, nItem=%d, rcItem=%s)\n", hdc, nItem, debugrect(&rcItem));
3222 /* get information needed for drawing the item */
3223 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE;
3224 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
3225 lvItem.iItem = nItem;
3226 lvItem.iSubItem = 0;
3227 lvItem.cchTextMax = DISP_TEXT_SIZE;
3228 lvItem.pszText = szDispText;
3229 *lvItem.pszText = '\0';
3230 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
3231 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3233 /* now check if we need to update the focus rectangle */
3234 lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
3236 if (!LISTVIEW_GetItemMeasures(infoPtr, nItem, NULL, NULL, &rcIcon, &rcLabel)) return FALSE;
3238 /* Set the item to the boundary box for now */
3239 TRACE("rcIcon=%s, rcLabel=%s\n", debugrect(&rcIcon), debugrect(&rcLabel));
3241 /* Figure out text colours etc. depending on state
3242 * At least the following states exist; there may be more.
3243 * Many items may be selected
3244 * At most one item may have the focus
3245 * The application may not actually be active currently
3246 * 1. The item is not selected in any way
3247 * 2. The cursor is flying over the icon or text and the text is being
3248 * expanded because it is not fully displayed currently.
3249 * 3. The item is selected and is focussed, i.e. the user has not clicked
3250 * in the blank area of the window, and the window (or application?)
3251 * still has the focus.
3252 * 4. As 3 except that a different window has the focus
3253 * 5. The item is the selected item of all the items, but the user has
3254 * clicked somewhere else on the window.
3255 * Only a few of these are handled currently. In particular 2 is not yet
3256 * handled since we do not support the functionality currently (or at least
3257 * we didn't when I wrote this)
3260 if (lvItem.state & LVIS_SELECTED)
3262 /* set item colors */
3263 SetBkColor(hdc, comctl32_color.clrHighlight);
3264 SetTextColor(hdc, comctl32_color.clrHighlightText);
3265 SetBkMode (hdc, OPAQUE);
3266 /* set raster mode */
3267 SetROP2(hdc, R2_XORPEN);
3268 /* When exactly is it in XOR? while being dragged? */
3270 else
3272 /* set item colors */
3273 if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
3275 SetBkMode(hdc, TRANSPARENT);
3277 else
3279 SetBkMode(hdc, OPAQUE);
3280 SetBkColor(hdc, infoPtr->clrTextBk);
3283 SetTextColor(hdc, infoPtr->clrText);
3284 /* set raster mode */
3285 SetROP2(hdc, R2_COPYPEN);
3288 /* In cases 2,3 and 5 (see above) the full text is displayed, with word
3289 * wrapping and long words split.
3290 * In cases 1 and 4 only a portion of the text is displayed with word
3291 * wrapping and both word and end ellipsis. (I don't yet know about path
3292 * ellipsis)
3294 uFormat = lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS;
3296 /* state icons */
3297 if (infoPtr->himlState != NULL)
3299 UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12;
3300 INT x, y;
3302 x = rcIcon.left - infoPtr->iconStateSize.cx + 10;
3303 y = rcIcon.top + infoPtr->iconSize.cy - infoPtr->iconStateSize.cy + 4;
3304 if (uStateImage > 0)
3305 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, x, y, ILD_NORMAL);
3308 /* draw the icon */
3309 if (infoPtr->himlNormal != NULL)
3311 if (lvItem.iImage >= 0)
3312 ImageList_Draw (infoPtr->himlNormal, lvItem.iImage, hdc,
3313 rcIcon.left + ICON_LR_HALF, rcIcon.top + ICON_TOP_PADDING,
3314 (lvItem.state & LVIS_SELECTED) ? ILD_SELECTED : ILD_NORMAL);
3317 /* Draw the text below the icon */
3319 /* Don't bother painting item being edited */
3320 if ((infoPtr->bEditing && lprcFocus) || !lvItem.pszText || !lstrlenW(lvItem.pszText))
3322 if(lprcFocus) SetRectEmpty(lprcFocus);
3323 return FALSE;
3326 /* draw label */
3328 /* I am sure of most of the uFormat values. However I am not sure about
3329 * whether we need or do not need the following:
3330 * DT_EXTERNALLEADING, DT_INTERNAL, DT_CALCRECT, DT_NOFULLWIDTHCHARBREAK,
3331 * DT_PATH_ELLIPSIS, DT_RTLREADING,
3332 * We certainly do not need
3333 * DT_BOTTOM, DT_VCENTER, DT_MODIFYSTRING, DT_LEFT, DT_RIGHT, DT_PREFIXONLY,
3334 * DT_SINGLELINE, DT_TABSTOP, DT_EXPANDTABS
3337 /* If the text is being drawn without clipping (i.e. the full text) then we
3338 * need to jump through a few hoops to ensure that it all gets displayed and
3339 * that the background is complete
3341 rcFocus = rcLabel; /* save for focus */
3342 if (lvItem.state & LVIS_SELECTED)
3343 ExtTextOutW(hdc, rcLabel.left, rcLabel.top, ETO_OPAQUE, &rcLabel, 0, 0, 0);
3344 /* else ? What if we are losing the focus? will we not get a complete
3345 * background?
3348 DrawTextW (hdc, lvItem.pszText, -1, &rcLabel, uFormat);
3349 TRACE("text at rcLabel=%s is %s\n", debugrect(&rcLabel), debugstr_w(lvItem.pszText));
3351 if(lprcFocus) CopyRect(lprcFocus, &rcFocus);
3353 return lprcFocus != NULL;
3356 /***
3357 * DESCRIPTION:
3358 * Draws listview items when in owner draw mode.
3360 * PARAMETER(S):
3361 * [I] infoPtr : valid pointer to the listview structure
3362 * [I] hdc : device context handle
3364 * RETURN:
3365 * None
3367 static void LISTVIEW_RefreshOwnerDraw(LISTVIEW_INFO *infoPtr, HDC hdc)
3369 UINT uID = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
3370 HWND hwndParent = GetParent(infoPtr->hwndSelf);
3371 POINT Origin, Position;
3372 DRAWITEMSTRUCT dis;
3373 LVITEMW item;
3374 ITERATOR i;
3376 TRACE("()\n");
3378 ZeroMemory(&dis, sizeof(dis));
3380 /* Get scroll info once before loop */
3381 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return;
3383 /* figure out what we need to draw */
3384 iterator_clippeditems(&i, infoPtr, hdc);
3386 /* send cache hint notification */
3387 if (infoPtr->dwStyle & LVS_OWNERDATA)
3388 notify_odcachehint(infoPtr, iterator_range(&i));
3390 /* iterate through the invalidated rows */
3391 while(iterator_next(&i))
3393 item.iItem = i.nItem;
3394 item.iSubItem = 0;
3395 item.mask = LVIF_PARAM | LVIF_STATE;
3396 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3397 if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
3399 dis.CtlType = ODT_LISTVIEW;
3400 dis.CtlID = uID;
3401 dis.itemID = item.iItem;
3402 dis.itemAction = ODA_DRAWENTIRE;
3403 dis.itemState = 0;
3404 if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
3405 if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS;
3406 dis.hwndItem = infoPtr->hwndSelf;
3407 dis.hDC = hdc;
3408 if (!LISTVIEW_GetItemListOrigin(infoPtr, dis.itemID, &Position)) continue;
3409 dis.rcItem.left = Position.x + Origin.x;
3410 dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth;
3411 dis.rcItem.top = Position.y + Origin.y;
3412 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3413 dis.itemData = item.lParam;
3415 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), debugrect(&dis.rcItem));
3416 SendMessageW(hwndParent, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
3418 iterator_destroy(&i);
3421 /***
3422 * DESCRIPTION:
3423 * Draws listview items when in report display mode.
3425 * PARAMETER(S):
3426 * [I] infoPtr : valid pointer to the listview structure
3427 * [I] hdc : device context handle
3428 * [I] cdmode : custom draw mode
3430 * RETURN:
3431 * None
3433 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD cdmode)
3435 INT rgntype, nDrawPosY, j;
3436 INT nColumnCount, nFirstCol, nLastCol;
3437 RECT rcItem, rcClip, rcFullSelect;
3438 BOOL bFullSelected, isFocused;
3439 DWORD cditemmode = CDRF_DODEFAULT;
3440 TEXTATTR tmpTa, oldTa;
3441 COLUMNCACHE *lpCols;
3442 LVCOLUMNW lvColumn;
3443 LVITEMW item;
3444 POINT ptOrig;
3445 ITERATOR i;
3447 TRACE("()\n");
3449 /* figure out what to draw */
3450 rgntype = GetClipBox(hdc, &rcClip);
3451 if (rgntype == NULLREGION) return;
3453 /* cache column info */
3454 nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
3455 lpCols = COMCTL32_Alloc(nColumnCount * sizeof(COLUMNCACHE));
3456 if (!lpCols) return;
3457 for (j = 0; j < nColumnCount; j++)
3459 Header_GetItemRect(infoPtr->hwndHeader, j, &lpCols[j].rc);
3460 TRACE("lpCols[%d].rc=%s\n", j, debugrect(&lpCols[j].rc));
3463 /* Get scroll info once before loop */
3464 if (!LISTVIEW_GetOrigin(infoPtr, &ptOrig)) return;
3466 /* we now narrow the columns as well */
3467 nLastCol = nColumnCount - 1;
3468 for(nFirstCol = 0; nFirstCol < nColumnCount; nFirstCol++)
3469 if (lpCols[nFirstCol].rc.right + ptOrig.x >= rcClip.left) break;
3470 for(nLastCol = nColumnCount - 1; nLastCol >= 0; nLastCol--)
3471 if (lpCols[nLastCol].rc.left + ptOrig.x < rcClip.right) break;
3473 /* cache the per-column information before we start drawing */
3474 for (j = nFirstCol; j <= nLastCol; j++)
3476 lvColumn.mask = LVCF_FMT;
3477 LISTVIEW_GetColumnT(infoPtr, j, &lvColumn, TRUE);
3478 TRACE("lvColumn=%s\n", debuglvcolumn_t(&lvColumn, TRUE));
3479 lpCols[j].align = DT_LEFT;
3480 if (lvColumn.fmt & LVCFMT_RIGHT)
3481 lpCols[j].align = DT_RIGHT;
3482 else if (lvColumn.fmt & LVCFMT_CENTER)
3483 lpCols[j].align = DT_CENTER;
3486 /* save dc values we're gonna trash while drawing */
3487 oldTa.bkMode = GetBkMode(hdc);
3488 oldTa.bkColor = GetBkColor(hdc);
3489 oldTa.fgColor = GetTextColor(hdc);
3491 /* figure out what we need to draw */
3492 iterator_clippeditems(&i, infoPtr, hdc);
3494 /* a last few bits before we start drawing */
3495 bFullSelected = infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT;
3496 TRACE("Colums=(%di - %d)\n", nFirstCol, nLastCol);
3498 /* send cache hint notification */
3499 if (infoPtr->dwStyle & LVS_OWNERDATA)
3500 notify_odcachehint(infoPtr, iterator_range(&i));
3502 /* iterate through the invalidated rows */
3503 while(iterator_next(&i))
3505 nDrawPosY = i.nItem * infoPtr->nItemHeight;
3507 /* compute the full select rectangle, if needed */
3508 if (bFullSelected)
3510 item.mask = LVIF_IMAGE | LVIF_STATE | LVIF_INDENT;
3511 item.stateMask = LVIS_SELECTED;
3512 item.iItem = i.nItem;
3513 item.iSubItem = 0;
3514 if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
3516 rcFullSelect.left = lpCols[0].rc.left + REPORT_MARGINX +
3517 infoPtr->iconSize.cx * item.iIndent +
3518 (infoPtr->himlSmall ? infoPtr->iconSize.cx : 0);
3519 rcFullSelect.right = max(rcFullSelect.left, lpCols[nColumnCount - 1].rc.right - REPORT_MARGINX);
3520 rcFullSelect.top = nDrawPosY;
3521 rcFullSelect.bottom = rcFullSelect.top + infoPtr->nItemHeight;
3522 OffsetRect(&rcFullSelect, ptOrig.x, ptOrig.y);
3525 /* draw the background of the selection rectangle, if need be */
3526 select_text_attr(infoPtr, hdc, bFullSelected && (item.state & LVIS_SELECTED), &tmpTa);
3527 if (bFullSelected && (item.state & LVIS_SELECTED))
3528 ExtTextOutW(hdc, rcFullSelect.left, rcFullSelect.top, ETO_OPAQUE, &rcFullSelect, 0, 0, 0);
3530 /* iterate through the invalidated columns */
3531 isFocused = FALSE;
3532 for (j = nFirstCol; j <= nLastCol; j++)
3534 rcItem = lpCols[j].rc;
3535 rcItem.left += REPORT_MARGINX;
3536 rcItem.right = max(rcItem.left, rcItem.right - REPORT_MARGINX);
3537 rcItem.top = nDrawPosY;
3538 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3540 /* Offset the Scroll Bar Pos */
3541 OffsetRect(&rcItem, ptOrig.x, ptOrig.y);
3543 if (rgntype == COMPLEXREGION && !RectVisible(hdc, &rcItem)) continue;
3545 if (cdmode & CDRF_NOTIFYITEMDRAW)
3546 cditemmode = notify_customdrawitem (infoPtr, hdc, i.nItem, j, CDDS_ITEMPREPAINT);
3547 if (cditemmode & CDRF_SKIPDEFAULT) continue;
3549 if (j == 0)
3550 isFocused = LISTVIEW_DrawItem(infoPtr, hdc, i.nItem, rcItem);
3551 else
3552 LISTVIEW_DrawSubItem(infoPtr, hdc, i.nItem, j, rcItem, lpCols[j].align);
3554 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3555 notify_customdrawitem(infoPtr, hdc, i.nItem, 0, CDDS_ITEMPOSTPAINT);
3558 /* Adjust focus if we have it, and we are in full select */
3559 if (bFullSelected && isFocused) infoPtr->rcFocus = rcFullSelect;
3561 iterator_destroy(&i);
3563 /* cleanup the mess */
3564 set_text_attr(hdc, &oldTa);
3565 COMCTL32_Free(lpCols);
3568 /***
3569 * DESCRIPTION:
3570 * Draws listview items when in list display mode.
3572 * PARAMETER(S):
3573 * [I] infoPtr : valid pointer to the listview structure
3574 * [I] hdc : device context handle
3575 * [I] cdmode : custom draw mode
3577 * RETURN:
3578 * None
3580 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD cdmode)
3582 DWORD cditemmode = CDRF_DODEFAULT;
3583 POINT Origin, Position;
3584 RECT rcItem;
3585 ITERATOR i;
3587 /* Get scroll info once before loop */
3588 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return;
3590 /* figure out what we need to draw */
3591 iterator_clippeditems(&i, infoPtr, hdc);
3593 while(iterator_next(&i))
3595 if (cdmode & CDRF_NOTIFYITEMDRAW)
3596 cditemmode = notify_customdrawitem (infoPtr, hdc, i.nItem, 0, CDDS_ITEMPREPAINT);
3597 if (cditemmode & CDRF_SKIPDEFAULT) continue;
3599 if (!LISTVIEW_GetItemListOrigin(infoPtr, i.nItem, &Position)) continue;
3600 rcItem.left = Position.x;
3601 rcItem.top = Position.y;
3602 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3603 rcItem.right = rcItem.left + infoPtr->nItemWidth;
3604 OffsetRect(&rcItem, Origin.x, Origin.y);
3606 LISTVIEW_DrawItem(infoPtr, hdc, i.nItem, rcItem);
3608 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3609 notify_customdrawitem(infoPtr, hdc, i.nItem, 0, CDDS_ITEMPOSTPAINT);
3612 iterator_destroy(&i);
3615 /***
3616 * DESCRIPTION:
3617 * Draws listview items when in icon or small icon display mode.
3619 * PARAMETER(S):
3620 * [I] infoPtr : valid pointer to the listview structure
3621 * [I] hdc : device context handle
3622 * [I] cdmode : custom draw mode
3624 * RETURN:
3625 * None
3627 static void LISTVIEW_RefreshIcon(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD cdmode)
3629 DWORD cditemmode = CDRF_DODEFAULT;
3630 POINT Origin, Position;
3631 RECT rcItem;
3632 ITERATOR i;
3634 /* Get scroll info once before loop */
3635 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return;
3637 /* figure out what we need to draw */
3638 iterator_clippeditems(&i, infoPtr, hdc);
3640 while(iterator_next(&i))
3642 if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_FOCUSED))
3643 continue;
3645 if (!LISTVIEW_GetItemListOrigin(infoPtr, i.nItem, &Position)) continue;
3646 rcItem.left = Position.x;
3647 rcItem.top = Position.y;
3648 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3649 rcItem.right = rcItem.left + infoPtr->nItemWidth;
3650 OffsetRect(&rcItem, Origin.x, Origin.y);
3652 if (cdmode & CDRF_NOTIFYITEMDRAW)
3653 cditemmode = notify_customdrawitem (infoPtr, hdc, i.nItem, 0, CDDS_ITEMPREPAINT);
3654 if (cditemmode & CDRF_SKIPDEFAULT) continue;
3656 LISTVIEW_DrawLargeItem(infoPtr, hdc, i.nItem, rcItem);
3658 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3659 notify_customdrawitem(infoPtr, hdc, i.nItem, 0, CDDS_ITEMPOSTPAINT);
3661 iterator_destroy(&i);
3663 /* draw the focused item last, in case it's oversized */
3664 if (!LISTVIEW_GetItemMeasures(infoPtr, infoPtr->nFocusedItem, &rcItem, 0, 0, 0)) return;
3665 if (!RectVisible(hdc, &rcItem)) return;
3667 if (cdmode & CDRF_NOTIFYITEMDRAW)
3668 cditemmode = notify_customdrawitem (infoPtr, hdc, infoPtr->nFocusedItem, 0, CDDS_ITEMPREPAINT);
3669 if (cditemmode & CDRF_SKIPDEFAULT)
3670 return;
3672 LISTVIEW_DrawLargeItem(infoPtr, hdc, infoPtr->nFocusedItem, rcItem);
3674 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3675 notify_customdrawitem(infoPtr, hdc, i.nItem, 0, CDDS_ITEMPOSTPAINT);
3678 /***
3679 * DESCRIPTION:
3680 * Draws listview items.
3682 * PARAMETER(S):
3683 * [I] infoPtr : valid pointer to the listview structure
3684 * [I] HDC : device context handle
3686 * RETURN:
3687 * NoneX
3689 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc)
3691 UINT uView = LISTVIEW_GetType(infoPtr);
3692 HFONT hOldFont;
3693 DWORD cdmode;
3694 RECT rcClient;
3696 LISTVIEW_DUMP(infoPtr);
3698 GetClientRect(infoPtr->hwndSelf, &rcClient);
3700 cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, hdc, rcClient);
3701 if (cdmode == CDRF_SKIPDEFAULT) return;
3703 infoPtr->bIsDrawing = TRUE;
3705 /* nothing to draw */
3706 if(infoPtr->nItemCount == 0) goto enddraw;
3708 /* select font */
3709 hOldFont = SelectObject(hdc, infoPtr->hFont);
3711 if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED)
3712 LISTVIEW_RefreshOwnerDraw(infoPtr, hdc);
3713 else if (uView == LVS_ICON)
3714 LISTVIEW_RefreshIcon(infoPtr, hdc, cdmode);
3715 else if (uView == LVS_REPORT)
3716 LISTVIEW_RefreshReport(infoPtr, hdc, cdmode);
3717 else /* LVS_LIST or LVS_SMALLICON */
3718 LISTVIEW_RefreshList(infoPtr, hdc, cdmode);
3720 /* if we have a focus rect, draw it */
3721 if (infoPtr->bFocus && !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED))
3722 DrawFocusRect(hdc, &infoPtr->rcFocus);
3724 /* unselect objects */
3725 SelectObject(hdc, hOldFont);
3727 enddraw:
3728 if (cdmode & CDRF_NOTIFYPOSTPAINT)
3729 notify_customdraw(infoPtr, CDDS_POSTPAINT, hdc, rcClient);
3731 infoPtr->bIsDrawing = FALSE;
3735 /***
3736 * DESCRIPTION:
3737 * Calculates the approximate width and height of a given number of items.
3739 * PARAMETER(S):
3740 * [I] infoPtr : valid pointer to the listview structure
3741 * [I] INT : number of items
3742 * [I] INT : width
3743 * [I] INT : height
3745 * RETURN:
3746 * Returns a DWORD. The width in the low word and the height in high word.
3748 static LRESULT LISTVIEW_ApproximateViewRect(LISTVIEW_INFO *infoPtr, INT nItemCount,
3749 WORD wWidth, WORD wHeight)
3751 UINT uView = LISTVIEW_GetType(infoPtr);
3752 INT nItemCountPerColumn = 1;
3753 INT nColumnCount = 0;
3754 DWORD dwViewRect = 0;
3756 if (nItemCount == -1)
3757 nItemCount = infoPtr->nItemCount;
3759 if (uView == LVS_LIST)
3761 if (wHeight == 0xFFFF)
3763 /* use current height */
3764 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3767 if (wHeight < infoPtr->nItemHeight)
3768 wHeight = infoPtr->nItemHeight;
3770 if (nItemCount > 0)
3772 if (infoPtr->nItemHeight > 0)
3774 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
3775 if (nItemCountPerColumn == 0)
3776 nItemCountPerColumn = 1;
3778 if (nItemCount % nItemCountPerColumn != 0)
3779 nColumnCount = nItemCount / nItemCountPerColumn;
3780 else
3781 nColumnCount = nItemCount / nItemCountPerColumn + 1;
3785 /* Microsoft padding magic */
3786 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
3787 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
3789 dwViewRect = MAKELONG(wWidth, wHeight);
3791 else if (uView == LVS_REPORT)
3792 FIXME("uView == LVS_REPORT: not implemented\n");
3793 else if (uView == LVS_SMALLICON)
3794 FIXME("uView == LVS_SMALLICON: not implemented\n");
3795 else if (uView == LVS_ICON)
3796 FIXME("uView == LVS_ICON: not implemented\n");
3798 return dwViewRect;
3801 /***
3802 * DESCRIPTION:
3803 * Arranges listview items in icon display mode.
3805 * PARAMETER(S):
3806 * [I] infoPtr : valid pointer to the listview structure
3807 * [I] INT : alignment code
3809 * RETURN:
3810 * SUCCESS : TRUE
3811 * FAILURE : FALSE
3813 static LRESULT LISTVIEW_Arrange(LISTVIEW_INFO *infoPtr, INT nAlignCode)
3815 UINT uView = LISTVIEW_GetType(infoPtr);
3816 BOOL bResult = FALSE;
3818 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
3820 switch (nAlignCode)
3822 case LVA_ALIGNLEFT:
3823 FIXME("nAlignCode=LVA_ALIGNLEFT: not implemented\n");
3824 break;
3825 case LVA_ALIGNTOP:
3826 FIXME("nAlignCode=LVA_ALIGNTOP: not implemented\n");
3827 break;
3828 case LVA_DEFAULT:
3829 FIXME("nAlignCode=LVA_DEFAULT: not implemented\n");
3830 break;
3831 case LVA_SNAPTOGRID:
3832 FIXME("nAlignCode=LVA_SNAPTOGRID: not implemented\n");
3833 break;
3837 return bResult;
3840 /* << LISTVIEW_CreateDragImage >> */
3843 /***
3844 * DESCRIPTION:
3845 * Removes all listview items and subitems.
3847 * PARAMETER(S):
3848 * [I] infoPtr : valid pointer to the listview structure
3850 * RETURN:
3851 * SUCCESS : TRUE
3852 * FAILURE : FALSE
3854 static LRESULT LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr)
3856 LONG lStyle = infoPtr->dwStyle;
3857 UINT uView = lStyle & LVS_TYPEMASK;
3858 LISTVIEW_ITEM *lpItem;
3859 LISTVIEW_SUBITEM *lpSubItem;
3860 NMLISTVIEW nmlv;
3861 BOOL bSuppress;
3862 BOOL bResult = FALSE;
3863 HDPA hdpaSubItems;
3865 TRACE("()\n");
3867 LISTVIEW_RemoveAllSelections(infoPtr);
3868 infoPtr->nSelectionMark=-1;
3869 infoPtr->nFocusedItem=-1;
3870 SetRectEmpty(&infoPtr->rcFocus);
3871 /* But we are supposed to leave nHotItem as is! */
3873 if (lStyle & LVS_OWNERDATA)
3875 infoPtr->nItemCount = 0;
3876 LISTVIEW_InvalidateList(infoPtr);
3877 return TRUE;
3880 if (infoPtr->nItemCount > 0)
3882 INT i, j;
3884 /* send LVN_DELETEALLITEMS notification */
3885 /* verify if subsequent LVN_DELETEITEM notifications should be
3886 suppressed */
3887 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3888 nmlv.iItem = -1;
3889 bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
3891 for (i = 0; i < infoPtr->nItemCount; i++)
3893 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
3894 if (hdpaSubItems != NULL)
3896 for (j = 1; j < hdpaSubItems->nItemCount; j++)
3898 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, j);
3899 if (lpSubItem != NULL)
3901 /* free subitem string */
3902 if (is_textW(lpSubItem->hdr.pszText))
3903 COMCTL32_Free(lpSubItem->hdr.pszText);
3905 /* free subitem */
3906 COMCTL32_Free(lpSubItem);
3910 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
3911 if (lpItem != NULL)
3913 if (!bSuppress)
3915 /* send LVN_DELETEITEM notification */
3916 nmlv.iItem = i;
3917 nmlv.lParam = lpItem->lParam;
3918 notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
3921 /* free item string */
3922 if (is_textW(lpItem->hdr.pszText))
3923 COMCTL32_Free(lpItem->hdr.pszText);
3925 /* free item */
3926 COMCTL32_Free(lpItem);
3929 DPA_Destroy(hdpaSubItems);
3933 /* reinitialize listview memory */
3934 bResult = DPA_DeleteAllPtrs(infoPtr->hdpaItems);
3935 infoPtr->nItemCount = 0;
3936 DPA_DeleteAllPtrs(infoPtr->hdpaPosX);
3937 DPA_DeleteAllPtrs(infoPtr->hdpaPosY);
3939 /* align items (set position of each item) */
3940 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
3942 if (lStyle & LVS_ALIGNLEFT)
3944 LISTVIEW_AlignLeft(infoPtr);
3946 else
3948 LISTVIEW_AlignTop(infoPtr);
3952 LISTVIEW_UpdateScroll(infoPtr);
3954 LISTVIEW_InvalidateList(infoPtr);
3957 return bResult;
3960 /***
3961 * DESCRIPTION:
3962 * Removes a column from the listview control.
3964 * PARAMETER(S):
3965 * [I] infoPtr : valid pointer to the listview structure
3966 * [I] INT : column index
3968 * RETURN:
3969 * SUCCESS : TRUE
3970 * FAILURE : FALSE
3972 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
3974 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3975 RECT rcCol, rcOld;
3977 TRACE("nColumn=%d\n", nColumn);
3979 if (nColumn <= 0) return FALSE;
3981 if (uView == LVS_REPORT)
3983 if (!Header_GetItemRect(infoPtr->hwndHeader, nColumn, &rcCol))
3984 return FALSE;
3986 if (!Header_DeleteItem(infoPtr->hwndHeader, nColumn))
3987 return FALSE;
3990 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
3992 LISTVIEW_SUBITEM *lpSubItem, *lpDelItem;
3993 HDPA hdpaSubItems;
3994 INT nItem, nSubItem, i;
3996 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
3998 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
3999 if (!hdpaSubItems) continue;
4000 nSubItem = 0;
4001 lpDelItem = 0;
4002 for (i = 1; i < hdpaSubItems->nItemCount; i++)
4004 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
4005 if (!lpSubItem) break;
4006 if (lpSubItem->iSubItem == nColumn)
4008 nSubItem = i;
4009 lpDelItem = lpSubItem;
4011 else if (lpSubItem->iSubItem > nColumn)
4013 lpSubItem->iSubItem--;
4017 /* if we found our subitem, zapp it */
4018 if (nSubItem > 0)
4020 /* free string */
4021 if (is_textW(lpDelItem->hdr.pszText))
4022 COMCTL32_Free(lpDelItem->hdr.pszText);
4024 /* free item */
4025 COMCTL32_Free(lpDelItem);
4027 /* free dpa memory */
4028 DPA_DeletePtr(hdpaSubItems, nSubItem);
4033 /* we need to worry about display issues in report mode only */
4034 if (uView != LVS_REPORT) return TRUE;
4036 /* if we have a focus, must first erase the focus rect */
4037 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, FALSE);
4039 /* Need to reset the item width when deleting a column */
4040 infoPtr->nItemWidth -= rcCol.right - rcCol.left;
4042 /* update scrollbar(s) */
4043 LISTVIEW_UpdateScroll(infoPtr);
4045 /* scroll to cover the deleted column, and invalidate for redraw */
4046 rcOld = infoPtr->rcList;
4047 rcOld.left = rcCol.left;
4048 ScrollWindowEx(infoPtr->hwndSelf, -(rcCol.right - rcCol.left), 0,
4049 &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
4051 /* we can restore focus now */
4052 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, TRUE);
4054 return TRUE;
4057 /***
4058 * DESCRIPTION:
4059 * Removes an item from the listview control.
4061 * PARAMETER(S):
4062 * [I] infoPtr : valid pointer to the listview structure
4063 * [I] INT : item index
4065 * RETURN:
4066 * SUCCESS : TRUE
4067 * FAILURE : FALSE
4069 static LRESULT LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
4071 LONG lStyle = infoPtr->dwStyle;
4072 UINT uView = lStyle & LVS_TYPEMASK;
4073 LONG lCtrlId = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
4074 NMLISTVIEW nmlv;
4075 BOOL bResult = FALSE;
4076 HDPA hdpaSubItems;
4077 LISTVIEW_ITEM *lpItem;
4078 LISTVIEW_SUBITEM *lpSubItem;
4079 INT i;
4080 LVITEMW item;
4082 TRACE("(nItem=%d)\n", nItem);
4085 /* First, send LVN_DELETEITEM notification. */
4086 memset(&nmlv, 0, sizeof (NMLISTVIEW));
4087 nmlv.hdr.hwndFrom = infoPtr->hwndSelf;
4088 nmlv.hdr.idFrom = lCtrlId;
4089 nmlv.hdr.code = LVN_DELETEITEM;
4090 nmlv.iItem = nItem;
4091 SendMessageW((infoPtr->hwndSelf), WM_NOTIFY, (WPARAM)lCtrlId,
4092 (LPARAM)&nmlv);
4094 if (nItem == infoPtr->nFocusedItem)
4096 infoPtr->nFocusedItem = -1;
4097 SetRectEmpty(&infoPtr->rcFocus);
4100 /* remove it from the selection range */
4101 item.state = LVIS_SELECTED;
4102 item.stateMask = LVIS_SELECTED;
4103 LISTVIEW_SetItemState(infoPtr,nItem,&item);
4105 if (lStyle & LVS_OWNERDATA)
4107 infoPtr->nItemCount--;
4108 LISTVIEW_InvalidateList(infoPtr); /*FIXME: optimize */
4109 return TRUE;
4112 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
4114 /* initialize memory */
4115 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4117 hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
4118 if (hdpaSubItems != NULL)
4120 infoPtr->nItemCount--;
4121 for (i = 1; i < hdpaSubItems->nItemCount; i++)
4123 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
4124 if (lpSubItem != NULL)
4126 /* free item string */
4127 if (is_textW(lpSubItem->hdr.pszText))
4128 COMCTL32_Free(lpSubItem->hdr.pszText);
4130 /* free item */
4131 COMCTL32_Free(lpSubItem);
4135 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
4136 if (lpItem != NULL)
4138 /* free item string */
4139 if (is_textW(lpItem->hdr.pszText))
4140 COMCTL32_Free(lpItem->hdr.pszText);
4142 /* free item */
4143 COMCTL32_Free(lpItem);
4146 bResult = DPA_Destroy(hdpaSubItems);
4147 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
4148 DPA_DeletePtr(infoPtr->hdpaPosY, nItem);
4151 LISTVIEW_ShiftIndices(infoPtr,nItem,-1);
4153 /* align items (set position of each item) */
4154 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4156 if (lStyle & LVS_ALIGNLEFT)
4157 LISTVIEW_AlignLeft(infoPtr);
4158 else
4159 LISTVIEW_AlignTop(infoPtr);
4162 LISTVIEW_UpdateScroll(infoPtr);
4164 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
4167 return bResult;
4171 /***
4172 * DESCRIPTION:
4173 * Callback implementation for editlabel control
4175 * PARAMETER(S):
4176 * [I] infoPtr : valid pointer to the listview structure
4177 * [I] pszText : modified text
4178 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4180 * RETURN:
4181 * SUCCESS : TRUE
4182 * FAILURE : FALSE
4184 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, LPWSTR pszText, BOOL isW)
4186 NMLVDISPINFOW dispInfo;
4188 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
4190 infoPtr->bEditing = FALSE;
4192 ZeroMemory(&dispInfo, sizeof(dispInfo));
4193 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE;
4194 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4195 dispInfo.item.iSubItem = 0;
4196 dispInfo.item.stateMask = ~0;
4197 if (!LISTVIEW_GetItemW(infoPtr, &dispInfo.item)) return FALSE;
4198 dispInfo.item.pszText = pszText;
4199 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4201 /* Do we need to update the Item Text */
4202 if (!notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW)) return FALSE;
4203 if (!pszText) return TRUE;
4205 ZeroMemory(&dispInfo, sizeof(dispInfo));
4206 dispInfo.item.mask = LVIF_TEXT;
4207 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4208 dispInfo.item.iSubItem = 0;
4209 dispInfo.item.pszText = pszText;
4210 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4211 return LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
4214 /***
4215 * DESCRIPTION:
4216 * Begin in place editing of specified list view item
4218 * PARAMETER(S):
4219 * [I] infoPtr : valid pointer to the listview structure
4220 * [I] INT : item index
4221 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4223 * RETURN:
4224 * SUCCESS : TRUE
4225 * FAILURE : FALSE
4227 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
4229 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
4230 NMLVDISPINFOW dispInfo;
4231 RECT rect;
4233 TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
4235 if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
4237 infoPtr->nEditLabelItem = nItem;
4239 /* Is the EditBox still there, if so remove it */
4240 if(infoPtr->hwndEdit != 0)
4242 SetFocus(infoPtr->hwndSelf);
4243 infoPtr->hwndEdit = 0;
4246 LISTVIEW_SetSelection(infoPtr, nItem);
4247 LISTVIEW_SetItemFocus(infoPtr, nItem);
4249 rect.left = LVIR_LABEL;
4250 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
4252 ZeroMemory(&dispInfo, sizeof(dispInfo));
4253 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
4254 dispInfo.item.iItem = nItem;
4255 dispInfo.item.iSubItem = 0;
4256 dispInfo.item.stateMask = ~0;
4257 dispInfo.item.pszText = szDispText;
4258 dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
4259 if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
4261 infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, WS_VISIBLE,
4262 rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, isW);
4263 if (!infoPtr->hwndEdit) return 0;
4265 if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
4267 SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
4268 infoPtr->hwndEdit = 0;
4269 return 0;
4272 infoPtr->bEditing = TRUE;
4273 ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
4274 SetFocus(infoPtr->hwndEdit);
4275 SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
4276 return infoPtr->hwndEdit;
4280 /***
4281 * DESCRIPTION:
4282 * Ensures the specified item is visible, scrolling into view if necessary.
4284 * PARAMETER(S):
4285 * [I] infoPtr : valid pointer to the listview structure
4286 * [I] nItem : item index
4287 * [I] bPartial : partially or entirely visible
4289 * RETURN:
4290 * SUCCESS : TRUE
4291 * FAILURE : FALSE
4293 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
4295 UINT uView = LISTVIEW_GetType(infoPtr);
4296 INT nScrollPosHeight = 0;
4297 INT nScrollPosWidth = 0;
4298 INT nHorzAdjust = 0;
4299 INT nVertAdjust = 0;
4300 INT nHorzDiff = 0;
4301 INT nVertDiff = 0;
4302 RECT rcItem;
4304 /* FIXME: ALWAYS bPartial == FALSE, FOR NOW! */
4306 rcItem.left = LVIR_BOUNDS;
4307 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
4309 if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
4311 /* scroll left/right, but in LVS_REPORT mode */
4312 if (uView == LVS_LIST)
4313 nScrollPosWidth = infoPtr->nItemWidth;
4314 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4315 nScrollPosWidth = 1;
4317 if (rcItem.left < infoPtr->rcList.left)
4319 nHorzAdjust = -1;
4320 if (uView != LVS_REPORT) nHorzDiff = rcItem.left - infoPtr->rcList.left;
4322 else
4324 nHorzAdjust = 1;
4325 if (uView != LVS_REPORT) nHorzDiff = rcItem.right - infoPtr->rcList.right;
4329 if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
4331 /* scroll up/down, but not in LVS_LIST mode */
4332 if (uView == LVS_REPORT)
4333 nScrollPosHeight = infoPtr->nItemHeight;
4334 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4335 nScrollPosHeight = 1;
4337 if (rcItem.top < infoPtr->rcList.top)
4339 nVertAdjust = -1;
4340 if (uView != LVS_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top;
4342 else
4344 nVertAdjust = 1;
4345 if (uView != LVS_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
4349 if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
4351 if (nScrollPosWidth)
4353 INT diff = nHorzDiff / nScrollPosWidth;
4354 if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust;
4355 LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff, 0);
4358 if (nScrollPosHeight)
4360 INT diff = nVertDiff / nScrollPosHeight;
4361 if (nVertDiff % nScrollPosHeight) diff += nVertAdjust;
4362 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff, 0);
4365 return TRUE;
4368 /***
4369 * DESCRIPTION:
4370 * Searches for an item with specific characteristics.
4372 * PARAMETER(S):
4373 * [I] hwnd : window handle
4374 * [I] nStart : base item index
4375 * [I] lpFindInfo : item information to look for
4377 * RETURN:
4378 * SUCCESS : index of item
4379 * FAILURE : -1
4381 static LRESULT LISTVIEW_FindItemW(LISTVIEW_INFO *infoPtr, INT nStart,
4382 LPLVFINDINFOW lpFindInfo)
4384 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4385 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4386 BOOL bWrap = FALSE, bNearest = FALSE;
4387 INT nItem = nStart + 1, nLast = infoPtr->nItemCount, nNearestItem = -1;
4388 ULONG xdist, ydist, dist, mindist = 0x7fffffff;
4389 POINT Position, Destination;
4390 LVITEMW lvItem;
4392 if (!lpFindInfo || nItem < 0) return -1;
4394 lvItem.mask = 0;
4395 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
4397 lvItem.mask |= LVIF_TEXT;
4398 lvItem.pszText = szDispText;
4399 lvItem.cchTextMax = DISP_TEXT_SIZE;
4402 if (lpFindInfo->flags & LVFI_WRAP)
4403 bWrap = TRUE;
4405 if ((lpFindInfo->flags & LVFI_NEARESTXY) &&
4406 (uView == LVS_ICON || uView ==LVS_SMALLICON))
4408 POINT Origin;
4410 FIXME("LVFI_NEARESTXY is slow.\n");
4411 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return -1;
4412 Destination.x = lpFindInfo->pt.x - Origin.x;
4413 Destination.y = lpFindInfo->pt.y - Origin.y;
4414 switch(lpFindInfo->vkDirection)
4416 case VK_DOWN: Destination.y += infoPtr->nItemHeight; break;
4417 case VK_UP: Destination.y -= infoPtr->nItemHeight; break;
4418 case VK_RIGHT: Destination.x += infoPtr->nItemWidth; break;
4419 case VK_LEFT: Destination.x -= infoPtr->nItemWidth; break;
4420 case VK_HOME: Destination.x = Destination.y = 0; break;
4421 case VK_END: Destination.x = infoPtr->rcView.right; Destination.y = infoPtr->rcView.bottom; break;
4422 case VK_NEXT: Destination.y += infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4423 case VK_PRIOR: Destination.y -= infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4424 default: FIXME("Unknown vkDirection=%d\n", lpFindInfo->vkDirection);
4426 bNearest = TRUE;
4429 /* if LVFI_PARAM is specified, all other flags are ignored */
4430 if (lpFindInfo->flags & LVFI_PARAM)
4432 lvItem.mask |= LVIF_PARAM;
4433 bNearest = FALSE;
4434 lvItem.mask &= ~LVIF_TEXT;
4437 again:
4438 for (; nItem < nLast; nItem++)
4440 lvItem.iItem = nItem;
4441 lvItem.iSubItem = 0;
4442 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
4444 if (lvItem.mask & LVIF_PARAM && lpFindInfo->lParam == lvItem.lParam)
4445 return nItem;
4447 if (lvItem.mask & LVIF_TEXT)
4449 if (lpFindInfo->flags & LVFI_PARTIAL)
4451 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL) continue;
4453 else
4455 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0) continue;
4459 if (!bNearest) return nItem;
4461 /* This is very inefficient. To do a good job here,
4462 * we need a sorted array of (x,y) item positions */
4463 if (!LISTVIEW_GetItemListOrigin(infoPtr, nItem, &Position)) continue;
4465 /* compute the distance^2 to the destination */
4466 xdist = Destination.x - Position.x;
4467 ydist = Destination.y - Position.y;
4468 dist = xdist * xdist + ydist * ydist;
4470 /* remember the distance, and item if it's closer */
4471 if (dist < mindist)
4473 mindist = dist;
4474 nNearestItem = nItem;
4478 if (bWrap)
4480 nItem = 0;
4481 nLast = min(nStart + 1, infoPtr->nItemCount);
4482 bWrap = FALSE;
4483 goto again;
4486 return nNearestItem;
4489 /***
4490 * DESCRIPTION:
4491 * Searches for an item with specific characteristics.
4493 * PARAMETER(S):
4494 * [I] hwnd : window handle
4495 * [I] nStart : base item index
4496 * [I] lpFindInfo : item information to look for
4498 * RETURN:
4499 * SUCCESS : index of item
4500 * FAILURE : -1
4502 static LRESULT LISTVIEW_FindItemA(LISTVIEW_INFO *infoPtr, INT nStart,
4503 LPLVFINDINFOA lpFindInfo)
4505 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
4506 LVFINDINFOW fiw;
4507 LRESULT res;
4509 memcpy(&fiw, lpFindInfo, sizeof(fiw));
4510 if (hasText) fiw.psz = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
4511 res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
4512 if (hasText) textfreeT((LPWSTR)fiw.psz, FALSE);
4513 return res;
4516 /***
4517 * DESCRIPTION:
4518 * Retrieves the background image of the listview control.
4520 * PARAMETER(S):
4521 * [I] infoPtr : valid pointer to the listview structure
4522 * [O] LPLVMKBIMAGE : background image attributes
4524 * RETURN:
4525 * SUCCESS : TRUE
4526 * FAILURE : FALSE
4528 /* static LRESULT LISTVIEW_GetBkImage(LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
4529 /* { */
4530 /* FIXME (listview, "empty stub!\n"); */
4531 /* return FALSE; */
4532 /* } */
4534 /***
4535 * DESCRIPTION:
4536 * Retrieves column attributes.
4538 * PARAMETER(S):
4539 * [I] infoPtr : valid pointer to the listview structure
4540 * [I] INT : column index
4541 * [IO] LPLVCOLUMNW : column information
4542 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
4543 * otherwise it is in fact a LPLVCOLUMNA
4545 * RETURN:
4546 * SUCCESS : TRUE
4547 * FAILURE : FALSE
4549 static LRESULT LISTVIEW_GetColumnT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVCOLUMNW lpColumn, BOOL isW)
4551 HDITEMW hdi;
4552 BOOL bResult = FALSE;
4554 if (lpColumn != NULL)
4557 /* initialize memory */
4558 ZeroMemory(&hdi, sizeof(hdi));
4560 if (lpColumn->mask & LVCF_FMT)
4561 hdi.mask |= HDI_FORMAT;
4563 if (lpColumn->mask & LVCF_WIDTH)
4564 hdi.mask |= HDI_WIDTH;
4566 if (lpColumn->mask & LVCF_TEXT)
4568 hdi.mask |= HDI_TEXT;
4569 hdi.cchTextMax = lpColumn->cchTextMax;
4570 hdi.pszText = lpColumn->pszText;
4573 if (lpColumn->mask & LVCF_IMAGE)
4574 hdi.mask |= HDI_IMAGE;
4576 if (lpColumn->mask & LVCF_ORDER)
4577 hdi.mask |= HDI_ORDER;
4579 if (isW)
4580 bResult = Header_GetItemW(infoPtr->hwndHeader, nItem, &hdi);
4581 else
4582 bResult = Header_GetItemA(infoPtr->hwndHeader, nItem, &hdi);
4584 if (bResult)
4586 if (lpColumn->mask & LVCF_FMT)
4588 lpColumn->fmt = 0;
4590 if (hdi.fmt & HDF_LEFT)
4591 lpColumn->fmt |= LVCFMT_LEFT;
4592 else if (hdi.fmt & HDF_RIGHT)
4593 lpColumn->fmt |= LVCFMT_RIGHT;
4594 else if (hdi.fmt & HDF_CENTER)
4595 lpColumn->fmt |= LVCFMT_CENTER;
4597 if (hdi.fmt & HDF_IMAGE)
4598 lpColumn->fmt |= LVCFMT_COL_HAS_IMAGES;
4600 if (hdi.fmt & HDF_BITMAP_ON_RIGHT)
4601 lpColumn->fmt |= LVCFMT_BITMAP_ON_RIGHT;
4604 if (lpColumn->mask & LVCF_WIDTH)
4605 lpColumn->cx = hdi.cxy;
4607 if (lpColumn->mask & LVCF_IMAGE)
4608 lpColumn->iImage = hdi.iImage;
4610 if (lpColumn->mask & LVCF_ORDER)
4611 lpColumn->iOrder = hdi.iOrder;
4613 TRACE("(col=%d, lpColumn=%s, isW=%d)\n",
4614 nItem, debuglvcolumn_t(lpColumn, isW), isW);
4619 return bResult;
4623 static LRESULT LISTVIEW_GetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
4625 INT i;
4627 if (!lpiArray)
4628 return FALSE;
4630 /* FIXME: little hack */
4631 for (i = 0; i < iCount; i++)
4632 lpiArray[i] = i;
4634 return TRUE;
4637 /***
4638 * DESCRIPTION:
4639 * Retrieves the column width.
4641 * PARAMETER(S):
4642 * [I] infoPtr : valid pointer to the listview structure
4643 * [I] int : column index
4645 * RETURN:
4646 * SUCCESS : column width
4647 * FAILURE : zero
4649 static LRESULT LISTVIEW_GetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn)
4651 INT nColumnWidth = 0;
4652 HDITEMW hdi;
4654 TRACE("nColumn=%d\n", nColumn);
4656 switch(LISTVIEW_GetType(infoPtr))
4658 case LVS_LIST:
4659 nColumnWidth = infoPtr->nItemWidth;
4660 break;
4661 case LVS_REPORT:
4662 hdi.mask = HDI_WIDTH;
4663 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdi))
4664 nColumnWidth = hdi.cxy;
4665 break;
4666 default:
4667 /* we don't have a 'column' in [SMALL]ICON mode */
4670 TRACE("nColumnWidth=%d\n", nColumnWidth);
4671 return nColumnWidth;
4674 /***
4675 * DESCRIPTION:
4676 * In list or report display mode, retrieves the number of items that can fit
4677 * vertically in the visible area. In icon or small icon display mode,
4678 * retrieves the total number of visible items.
4680 * PARAMETER(S):
4681 * [I] infoPtr : valid pointer to the listview structure
4683 * RETURN:
4684 * Number of fully visible items.
4686 static LRESULT LISTVIEW_GetCountPerPage(LISTVIEW_INFO *infoPtr)
4688 UINT uView = LISTVIEW_GetType(infoPtr);
4689 INT nItemCount = 0;
4691 if (uView == LVS_LIST)
4693 if (infoPtr->rcList.right > infoPtr->nItemWidth)
4695 nItemCount = LISTVIEW_GetCountPerRow(infoPtr) *
4696 LISTVIEW_GetCountPerColumn(infoPtr);
4699 else if (uView == LVS_REPORT)
4701 nItemCount = LISTVIEW_GetCountPerColumn(infoPtr);
4703 else
4705 nItemCount = infoPtr->nItemCount;
4708 return nItemCount;
4712 /***
4713 * DESCRIPTION:
4714 * Retrieves an image list handle.
4716 * PARAMETER(S):
4717 * [I] infoPtr : valid pointer to the listview structure
4718 * [I] nImageList : image list identifier
4720 * RETURN:
4721 * SUCCESS : image list handle
4722 * FAILURE : NULL
4724 static LRESULT LISTVIEW_GetImageList(LISTVIEW_INFO *infoPtr, INT nImageList)
4726 HIMAGELIST himl = NULL;
4728 switch (nImageList)
4730 case LVSIL_NORMAL:
4731 himl = infoPtr->himlNormal;
4732 break;
4733 case LVSIL_SMALL:
4734 himl = infoPtr->himlSmall;
4735 break;
4736 case LVSIL_STATE:
4737 himl = infoPtr->himlState;
4738 break;
4741 return (LRESULT)himl;
4744 /* LISTVIEW_GetISearchString */
4746 /***
4747 * DESCRIPTION:
4748 * Retrieves item attributes.
4750 * PARAMETER(S):
4751 * [I] hwnd : window handle
4752 * [IO] lpLVItem : item info
4753 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
4754 * if FALSE, the lpLVItem is a LPLVITEMA.
4756 * NOTE:
4757 * This is the internal 'GetItem' interface -- it tries to
4758 * be smart, and avoids text copies, if possible, by modifing
4759 * lpLVItem->pszText to point to the text string. Please note
4760 * that this is not always possible (e.g. OWNERDATA), so on
4761 * entry you *must* supply valid values for pszText, and cchTextMax.
4762 * The only difference to the documented interface is that upon
4763 * return, you should use *only* the lpLVItem->pszText, rather than
4764 * the buffer pointer you provided on input. Most code already does
4765 * that, so it's not a problem.
4766 * For the two cases when the text must be copied (that is,
4767 * for LVM_GETITEM, and LVMGETITEMTEXT), use LISTVIEW_GetItemExtT.
4769 * RETURN:
4770 * SUCCESS : TRUE
4771 * FAILURE : FALSE
4773 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
4775 NMLVDISPINFOW dispInfo;
4776 LISTVIEW_ITEM *lpItem;
4777 ITEMHDR* pItemHdr;
4778 HDPA hdpaSubItems;
4780 /* In the following:
4781 * lpLVItem describes the information requested by the user
4782 * lpItem is what we have
4783 * dispInfo is a structure we use to request the missing
4784 * information from the application
4787 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
4789 if (!lpLVItem || (lpLVItem->iItem < 0) ||
4790 (lpLVItem->iItem >= infoPtr->nItemCount))
4791 return FALSE;
4793 /* a quick optimization if all we're asked is the focus state
4794 * these queries are worth optimising since they are common,
4795 * and can be answered in constant time, without the heavy accesses */
4796 if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIF_STATE) &&
4797 !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
4799 lpLVItem->state = 0;
4800 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4801 lpLVItem->state |= LVIS_FOCUSED;
4802 return TRUE;
4805 ZeroMemory(&dispInfo, sizeof(dispInfo));
4807 /* if the app stores all the data, handle it separately */
4808 if (infoPtr->dwStyle & LVS_OWNERDATA)
4810 dispInfo.item.state = 0;
4812 /* if we need to callback, do it now */
4813 if ((lpLVItem->mask & ~LVIF_STATE) || infoPtr->uCallbackMask)
4815 /* NOTE: copy only fields which we _know_ are initialized, some apps
4816 * depend on the uninitialized fields being 0 */
4817 dispInfo.item.mask = lpLVItem->mask;
4818 dispInfo.item.iItem = lpLVItem->iItem;
4819 dispInfo.item.iSubItem = lpLVItem->iSubItem;
4820 if (lpLVItem->mask & LVIF_TEXT)
4822 dispInfo.item.pszText = lpLVItem->pszText;
4823 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
4825 if (lpLVItem->mask & LVIF_STATE)
4826 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
4827 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
4828 dispInfo.item.stateMask = lpLVItem->stateMask;
4829 *lpLVItem = dispInfo.item;
4830 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
4833 /* we store only a little state, so if we're not asked, we're done */
4834 if (!(lpLVItem->mask & LVIF_STATE) || lpLVItem->iSubItem) return TRUE;
4836 /* if focus is handled by us, report it */
4837 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
4839 lpLVItem->state &= ~LVIS_FOCUSED;
4840 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4841 lpLVItem->state |= LVIS_FOCUSED;
4844 /* and do the same for selection, if we handle it */
4845 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
4847 lpLVItem->state &= ~LVIS_SELECTED;
4848 if (ranges_contain(infoPtr->hdpaSelectionRanges, lpLVItem->iItem))
4849 lpLVItem->state |= LVIS_SELECTED;
4852 return TRUE;
4855 /* find the item and subitem structures before we proceed */
4856 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
4857 if (hdpaSubItems == NULL) return FALSE;
4859 if ( !(lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)) )
4860 return FALSE;
4862 if (lpLVItem->iSubItem)
4864 LISTVIEW_SUBITEM *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
4865 if(!lpSubItem) return FALSE;
4866 pItemHdr = &lpSubItem->hdr;
4868 else
4869 pItemHdr = &lpItem->hdr;
4871 /* Do we need to query the state from the app? */
4872 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && lpLVItem->iSubItem == 0)
4874 dispInfo.item.mask |= LVIF_STATE;
4875 dispInfo.item.stateMask = infoPtr->uCallbackMask;
4878 /* Do we need to enquire about the image? */
4879 if ((lpLVItem->mask & LVIF_IMAGE) && (pItemHdr->iImage==I_IMAGECALLBACK))
4880 dispInfo.item.mask |= LVIF_IMAGE;
4882 /* Do we need to enquire about the text? */
4883 if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(pItemHdr->pszText))
4885 dispInfo.item.mask |= LVIF_TEXT;
4886 dispInfo.item.pszText = lpLVItem->pszText;
4887 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
4888 if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
4889 *dispInfo.item.pszText = '\0';
4892 /* If we don't have all the requested info, query the application */
4893 if (dispInfo.item.mask != 0)
4895 dispInfo.item.iItem = lpLVItem->iItem;
4896 dispInfo.item.iSubItem = lpLVItem->iSubItem;
4897 dispInfo.item.lParam = lpItem->lParam;
4898 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
4899 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
4902 /* Now, handle the iImage field */
4903 if (dispInfo.item.mask & LVIF_IMAGE)
4905 lpLVItem->iImage = dispInfo.item.iImage;
4906 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && (pItemHdr->iImage==I_IMAGECALLBACK))
4907 pItemHdr->iImage = dispInfo.item.iImage;
4909 else if (lpLVItem->mask & LVIF_IMAGE)
4910 lpLVItem->iImage = pItemHdr->iImage;
4912 /* The pszText field */
4913 if (dispInfo.item.mask & LVIF_TEXT)
4915 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
4916 textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
4918 lpLVItem->pszText = dispInfo.item.pszText;
4920 else if (lpLVItem->mask & LVIF_TEXT)
4922 if (isW) lpLVItem->pszText = pItemHdr->pszText;
4923 else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
4926 /* if this is a subitem, we're done*/
4927 if (lpLVItem->iSubItem) return TRUE;
4929 /* Next is the lParam field */
4930 if (dispInfo.item.mask & LVIF_PARAM)
4932 lpLVItem->lParam = dispInfo.item.lParam;
4933 if ((dispInfo.item.mask & LVIF_DI_SETITEM))
4934 lpItem->lParam = dispInfo.item.lParam;
4936 else if (lpLVItem->mask & LVIF_PARAM)
4937 lpLVItem->lParam = lpItem->lParam;
4939 /* ... the state field (this one is different due to uCallbackmask) */
4940 if (lpLVItem->mask & LVIF_STATE)
4942 lpLVItem->state = lpItem->state;
4943 if (dispInfo.item.mask & LVIF_STATE)
4945 lpLVItem->state &= ~dispInfo.item.stateMask;
4946 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
4948 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
4950 lpLVItem->state &= ~LVIS_FOCUSED;
4951 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4952 lpLVItem->state |= LVIS_FOCUSED;
4954 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
4956 lpLVItem->state &= ~LVIS_SELECTED;
4957 if (ranges_contain(infoPtr->hdpaSelectionRanges, lpLVItem->iItem))
4958 lpLVItem->state |= LVIS_SELECTED;
4962 /* and last, but not least, the indent field */
4963 if (lpLVItem->mask & LVIF_INDENT)
4964 lpLVItem->iIndent = lpItem->iIndent;
4966 return TRUE;
4969 /***
4970 * DESCRIPTION:
4971 * Retrieves item attributes.
4973 * PARAMETER(S):
4974 * [I] hwnd : window handle
4975 * [IO] lpLVItem : item info
4976 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
4977 * if FALSE, the lpLVItem is a LPLVITEMA.
4979 * NOTE:
4980 * This is the external 'GetItem' interface -- it properly copies
4981 * the text in the provided buffer.
4983 * RETURN:
4984 * SUCCESS : TRUE
4985 * FAILURE : FALSE
4987 static BOOL LISTVIEW_GetItemExtT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
4989 LPWSTR pszText;
4990 BOOL bResult;
4992 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
4993 return FALSE;
4995 pszText = lpLVItem->pszText;
4996 bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
4997 if (bResult && lpLVItem->pszText != pszText)
4998 textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
4999 lpLVItem->pszText = pszText;
5001 return bResult;
5005 /***
5006 * DESCRIPTION:
5007 * Retrieves the position (upper-left) of the listview control item.
5008 * Note that for LVS_ICON style, the upper-left is that of the icon
5009 * and not the bounding box.
5011 * PARAMETER(S):
5012 * [I] infoPtr : valid pointer to the listview structure
5013 * [I] nItem : item index
5014 * [O] lpptPosition : coordinate information
5016 * RETURN:
5017 * SUCCESS : TRUE
5018 * FAILURE : FALSE
5020 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
5022 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5023 POINT Origin;
5025 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
5027 if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5028 if (!LISTVIEW_GetItemListOrigin(infoPtr, nItem, lpptPosition)) return FALSE;
5029 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return FALSE;
5031 if (uView == LVS_ICON)
5033 lpptPosition->x += (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
5034 lpptPosition->y += ICON_TOP_PADDING;
5036 lpptPosition->x += Origin.x;
5037 lpptPosition->y += Origin.y;
5039 TRACE (" lpptPosition=%s\n", debugpoint(lpptPosition));
5040 return TRUE;
5044 /***
5045 * DESCRIPTION:
5046 * Retrieves the bounding rectangle for a listview control item.
5048 * PARAMETER(S):
5049 * [I] infoPtr : valid pointer to the listview structure
5050 * [I] nItem : item index
5051 * [IO] lprc : bounding rectangle coordinates
5052 * lprc->left specifies the portion of the item for which the bounding
5053 * rectangle will be retrieved.
5055 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5056 * including the icon and label.
5058 * * For LVS_ICON
5059 * * Experiment shows that native control returns:
5060 * * width = min (48, length of text line)
5061 * * .left = position.x - (width - iconsize.cx)/2
5062 * * .right = .left + width
5063 * * height = #lines of text * ntmHeight + icon height + 8
5064 * * .top = position.y - 2
5065 * * .bottom = .top + height
5066 * * separation between items .y = itemSpacing.cy - height
5067 * * .x = itemSpacing.cx - width
5068 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5070 * * For LVS_ICON
5071 * * Experiment shows that native control returns:
5072 * * width = iconSize.cx + 16
5073 * * .left = position.x - (width - iconsize.cx)/2
5074 * * .right = .left + width
5075 * * height = iconSize.cy + 4
5076 * * .top = position.y - 2
5077 * * .bottom = .top + height
5078 * * separation between items .y = itemSpacing.cy - height
5079 * * .x = itemSpacing.cx - width
5080 * LVIR_LABEL Returns the bounding rectangle of the item text.
5082 * * For LVS_ICON
5083 * * Experiment shows that native control returns:
5084 * * width = text length
5085 * * .left = position.x - width/2
5086 * * .right = .left + width
5087 * * height = ntmH * linecount + 2
5088 * * .top = position.y + iconSize.cy + 6
5089 * * .bottom = .top + height
5090 * * separation between items .y = itemSpacing.cy - height
5091 * * .x = itemSpacing.cx - width
5092 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5093 * rectangles, but excludes columns in report view.
5095 * RETURN:
5096 * SUCCESS : TRUE
5097 * FAILURE : FALSE
5099 * NOTES
5100 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5101 * upon whether the window has the focus currently and on whether the item
5102 * is the one with the focus. Ensure that the control's record of which
5103 * item has the focus agrees with the items' records.
5105 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5107 RECT label_rect;
5109 TRACE("(hwnd=%x, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
5111 if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5113 switch(lprc->left)
5115 case LVIR_ICON:
5116 if (!LISTVIEW_GetItemMeasures(infoPtr, nItem, NULL, NULL, lprc, NULL)) return FALSE;
5117 break;
5119 case LVIR_LABEL:
5120 if (!LISTVIEW_GetItemMeasures(infoPtr, nItem, NULL, NULL, NULL, lprc)) return FALSE;
5121 break;
5123 case LVIR_BOUNDS:
5124 if (!LISTVIEW_GetItemMeasures(infoPtr, nItem, NULL, lprc, NULL, NULL)) return FALSE;
5125 break;
5127 case LVIR_SELECTBOUNDS:
5128 if (!LISTVIEW_GetItemMeasures(infoPtr, nItem, NULL, lprc, NULL, &label_rect)) return FALSE;
5129 if ( (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT &&
5130 !(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) )
5131 lprc->right = label_rect.right;
5132 break;
5134 default:
5135 WARN("Unknown value: %d\n", lprc->left);
5136 return FALSE;
5139 TRACE(" rect=%s\n", debugrect(lprc));
5141 return TRUE;
5144 /***
5145 * DESCRIPTION:
5146 * Retrieves the spacing between listview control items.
5148 * PARAMETER(S):
5149 * [I] infoPtr : valid pointer to the listview structure
5150 * [IO] lprc : rectangle to receive the output
5151 * on input, lprc->top = nSubItem
5152 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
5154 * NOTE: this call is succeeds only for REPORT style listviews.
5155 * Because we can calculate things much faster in report mode,
5156 * we're gonna do the calculations inline here, instead of
5157 * calling functions that do heavy lifting.
5159 * RETURN:
5160 * TRUE: success
5161 * FALSE: failure
5163 static BOOL LISTVIEW_GetSubItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5165 POINT ptPosition;
5166 INT nSubItem, flags;
5168 if (!lprc || LISTVIEW_GetType(infoPtr) != LVS_REPORT) return FALSE;
5170 nSubItem = lprc->top;
5171 flags = lprc->left;
5173 TRACE("(nItem=%d, nSubItem=%d)\n", nItem, nSubItem);
5175 if (!Header_GetItemRect(infoPtr->hwndHeader, nSubItem, lprc)) return FALSE;
5176 if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &ptPosition)) return FALSE;
5177 lprc->top = ptPosition.y;
5178 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5180 switch(flags)
5182 case LVIR_ICON:
5183 FIXME("Unimplemented LVIR_ICON\n");
5184 return FALSE;
5185 case LVIR_LABEL:
5186 case LVIR_BOUNDS:
5187 /* nothing to do here, we're done */
5188 break;
5189 default:
5190 ERR("Unknown bounds=%d\n", lprc->left);
5191 return FALSE;
5193 return TRUE;
5197 /***
5198 * DESCRIPTION:
5199 * Retrieves the width of a label.
5201 * PARAMETER(S):
5202 * [I] infoPtr : valid pointer to the listview structure
5204 * RETURN:
5205 * SUCCESS : string width (in pixels)
5206 * FAILURE : zero
5208 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *infoPtr, INT nItem)
5210 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5211 LVITEMW lvItem;
5213 TRACE("(nItem=%d)\n", nItem);
5215 lvItem.mask = LVIF_TEXT;
5216 lvItem.iItem = nItem;
5217 lvItem.iSubItem = 0;
5218 lvItem.pszText = szDispText;
5219 lvItem.cchTextMax = DISP_TEXT_SIZE;
5220 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5222 return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
5225 /***
5226 * DESCRIPTION:
5227 * Retrieves the spacing between listview control items.
5229 * PARAMETER(S):
5230 * [I] infoPtr : valid pointer to the listview structure
5231 * [I] BOOL : flag for small or large icon
5233 * RETURN:
5234 * Horizontal + vertical spacing
5236 static LRESULT LISTVIEW_GetItemSpacing(LISTVIEW_INFO *infoPtr, BOOL bSmall)
5238 LONG lResult;
5240 if (!bSmall)
5242 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
5244 else
5246 if (LISTVIEW_GetType(infoPtr) == LVS_ICON)
5247 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
5248 else
5249 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
5251 return lResult;
5254 /***
5255 * DESCRIPTION:
5256 * Retrieves the state of a listview control item.
5258 * PARAMETER(S):
5259 * [I] infoPtr : valid pointer to the listview structure
5260 * [I] nItem : item index
5261 * [I] uMask : state mask
5263 * RETURN:
5264 * State specified by the mask.
5266 static LRESULT LISTVIEW_GetItemState(LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
5268 LVITEMW lvItem;
5270 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5272 lvItem.iItem = nItem;
5273 lvItem.iSubItem = 0;
5274 lvItem.mask = LVIF_STATE;
5275 lvItem.stateMask = uMask;
5276 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5278 return lvItem.state & uMask;
5281 /***
5282 * DESCRIPTION:
5283 * Retrieves the text of a listview control item or subitem.
5285 * PARAMETER(S):
5286 * [I] hwnd : window handle
5287 * [I] nItem : item index
5288 * [IO] lpLVItem : item information
5289 * [I] isW : TRUE if lpLVItem is Unicode
5291 * RETURN:
5292 * SUCCESS : string length
5293 * FAILURE : 0
5295 static LRESULT LISTVIEW_GetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
5297 if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5299 lpLVItem->mask = LVIF_TEXT;
5300 lpLVItem->iItem = nItem;
5301 if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
5303 return textlenT(lpLVItem->pszText, isW);
5306 /***
5307 * DESCRIPTION:
5308 * Searches for an item based on properties + relationships.
5310 * PARAMETER(S):
5311 * [I] infoPtr : valid pointer to the listview structure
5312 * [I] nItem : item index
5313 * [I] uFlags : relationship flag
5315 * FIXME:
5316 * This function is very, very inefficient! Needs work.
5318 * RETURN:
5319 * SUCCESS : item index
5320 * FAILURE : -1
5322 static LRESULT LISTVIEW_GetNextItem(LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
5324 UINT uView = LISTVIEW_GetType(infoPtr);
5325 UINT uMask = 0;
5326 LVFINDINFOW lvFindInfo;
5327 INT nCountPerColumn;
5328 INT i;
5330 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
5331 if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
5333 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
5335 if (uFlags & LVNI_CUT)
5336 uMask |= LVIS_CUT;
5338 if (uFlags & LVNI_DROPHILITED)
5339 uMask |= LVIS_DROPHILITED;
5341 if (uFlags & LVNI_FOCUSED)
5342 uMask |= LVIS_FOCUSED;
5344 if (uFlags & LVNI_SELECTED)
5345 uMask |= LVIS_SELECTED;
5347 /* if we're asked for the focused item, that's only one,
5348 * so it's worth optimizing */
5349 if (uFlags & LVNI_FOCUSED)
5351 if (!(LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) == uMask) return -1;
5352 return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
5355 if (uFlags & LVNI_ABOVE)
5357 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5359 while (nItem >= 0)
5361 nItem--;
5362 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5363 return nItem;
5366 else
5368 lvFindInfo.flags = LVFI_NEARESTXY;
5369 lvFindInfo.vkDirection = VK_UP;
5370 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5371 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5373 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5374 return nItem;
5378 else if (uFlags & LVNI_BELOW)
5380 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5382 while (nItem < infoPtr->nItemCount)
5384 nItem++;
5385 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5386 return nItem;
5389 else
5391 lvFindInfo.flags = LVFI_NEARESTXY;
5392 lvFindInfo.vkDirection = VK_DOWN;
5393 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5394 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5396 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5397 return nItem;
5401 else if (uFlags & LVNI_TOLEFT)
5403 if (uView == LVS_LIST)
5405 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5406 while (nItem - nCountPerColumn >= 0)
5408 nItem -= nCountPerColumn;
5409 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5410 return nItem;
5413 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5415 lvFindInfo.flags = LVFI_NEARESTXY;
5416 lvFindInfo.vkDirection = VK_LEFT;
5417 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5418 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5420 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5421 return nItem;
5425 else if (uFlags & LVNI_TORIGHT)
5427 if (uView == LVS_LIST)
5429 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5430 while (nItem + nCountPerColumn < infoPtr->nItemCount)
5432 nItem += nCountPerColumn;
5433 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5434 return nItem;
5437 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5439 lvFindInfo.flags = LVFI_NEARESTXY;
5440 lvFindInfo.vkDirection = VK_RIGHT;
5441 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5442 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5444 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5445 return nItem;
5449 else
5451 nItem++;
5453 /* search by index */
5454 for (i = nItem; i < infoPtr->nItemCount; i++)
5456 if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
5457 return i;
5461 return -1;
5464 /* LISTVIEW_GetNumberOfWorkAreas */
5466 /***
5467 * DESCRIPTION:
5468 * Retrieves the origin coordinates when in icon or small icon display mode.
5470 * PARAMETER(S):
5471 * [I] infoPtr : valid pointer to the listview structure
5472 * [O] lpptOrigin : coordinate information
5474 * RETURN:
5475 * SUCCESS : TRUE
5476 * FAILURE : FALSE
5478 static BOOL LISTVIEW_GetOrigin(LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
5480 DWORD lStyle = infoPtr->dwStyle;
5481 UINT uView = lStyle & LVS_TYPEMASK;
5482 INT nHorzPos = 0, nVertPos = 0;
5483 SCROLLINFO scrollInfo;
5485 if (!lpptOrigin) return FALSE;
5487 scrollInfo.cbSize = sizeof(SCROLLINFO);
5488 scrollInfo.fMask = SIF_POS;
5490 if ((lStyle & WS_HSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
5491 nHorzPos = scrollInfo.nPos;
5492 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
5493 nVertPos = scrollInfo.nPos;
5495 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
5497 lpptOrigin->x = infoPtr->rcList.left;
5498 lpptOrigin->y = infoPtr->rcList.top;
5499 if (uView == LVS_LIST)
5500 nHorzPos *= infoPtr->nItemWidth;
5501 else if (uView == LVS_REPORT)
5502 nVertPos *= infoPtr->nItemHeight;
5504 lpptOrigin->x -= nHorzPos;
5505 lpptOrigin->y -= nVertPos;
5507 TRACE(" origin=%s\n", debugpoint(lpptOrigin));
5509 return TRUE;
5512 /***
5513 * DESCRIPTION:
5514 * Retrieves the width of a string.
5516 * PARAMETER(S):
5517 * [I] hwnd : window handle
5518 * [I] lpszText : text string to process
5519 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
5521 * RETURN:
5522 * SUCCESS : string width (in pixels)
5523 * FAILURE : zero
5525 static LRESULT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
5527 SIZE stringSize;
5529 stringSize.cx = 0;
5530 if (is_textT(lpszText, isW))
5532 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
5533 HDC hdc = GetDC(infoPtr->hwndSelf);
5534 HFONT hOldFont = SelectObject(hdc, hFont);
5536 if (isW)
5537 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
5538 else
5539 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
5540 SelectObject(hdc, hOldFont);
5541 ReleaseDC(infoPtr->hwndSelf, hdc);
5543 return stringSize.cx;
5546 /***
5547 * DESCRIPTION:
5548 * Determines which listview item is located at the specified position.
5550 * PARAMETER(S):
5551 * [I] infoPtr : valid pointer to the listview structure
5552 * [IO] lpht : hit test information
5553 * [I] subitem : fill out iSubItem.
5554 * [I] select : return the index only if the hit selects the item
5556 * NOTE:
5557 * (mm 20001022): We must not allow iSubItem to be touched, for
5558 * an app might pass only a structure with space up to iItem!
5559 * (MS Office 97 does that for instance in the file open dialog)
5561 * RETURN:
5562 * SUCCESS : item index
5563 * FAILURE : -1
5565 static LRESULT LISTVIEW_HitTest(LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select)
5567 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5568 RECT rcBounds, rcIcon, rcLabel;
5570 TRACE("(pt=%s, subitem=%d, select=%d)\n", debugpoint(&lpht->pt), subitem, select);
5572 lpht->flags = 0;
5573 lpht->iItem = -1;
5574 if (subitem) lpht->iSubItem = 0;
5576 if (infoPtr->rcList.left > lpht->pt.x)
5577 lpht->flags |= LVHT_TOLEFT;
5578 else if (infoPtr->rcList.right < lpht->pt.x)
5579 lpht->flags |= LVHT_TORIGHT;
5581 if (infoPtr->rcList.top > lpht->pt.y)
5582 lpht->flags |= LVHT_ABOVE;
5583 else if (infoPtr->rcList.bottom < lpht->pt.y)
5584 lpht->flags |= LVHT_BELOW;
5586 if (lpht->flags) return -1;
5588 lpht->flags |= LVHT_NOWHERE;
5590 /* first deal with the large items */
5591 if (uView == LVS_ICON && (infoPtr->dwStyle & LVS_OWNERDRAWFIXED) &&
5592 PtInRect (&infoPtr->rcFocus, lpht->pt))
5594 lpht->iItem = infoPtr->nFocusedItem;
5596 else
5598 if (uView == LVS_ICON || uView == LVS_SMALLICON)
5600 RECT rcSearch;
5601 ITERATOR i;
5603 rcSearch.left = lpht->pt.x - infoPtr->nItemWidth;
5604 rcSearch.top = lpht->pt.y - infoPtr->nItemHeight;
5605 rcSearch.right = lpht->pt.x + 1;
5606 rcSearch.bottom = lpht->pt.y + 1;
5608 iterator_frameditems(&i, infoPtr, &rcSearch);
5609 while(iterator_next(&i))
5611 if (!LISTVIEW_GetItemMeasures(infoPtr, i.nItem, &rcBounds, 0, 0, 0)) continue;
5612 if (PtInRect(&rcBounds, lpht->pt)) break;
5614 lpht->iItem = i.nItem;
5615 iterator_destroy(&i);
5617 else
5619 POINT Origin, Position;
5620 INT nPerCol = (uView == LVS_REPORT) ? infoPtr->nItemCount : LISTVIEW_GetCountPerColumn(infoPtr);
5622 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return -1;
5623 Position.x = lpht->pt.x - Origin.x;
5624 Position.y = lpht->pt.y - Origin.y;
5625 TRACE("Position=%s, nPerCol=%d, nItemHeight=%d, nColHeight=%d\n",
5626 debugpoint(&Position), nPerCol, infoPtr->nItemHeight, nPerCol * infoPtr->nItemHeight);
5628 if (Position.y < nPerCol * infoPtr->nItemHeight)
5630 lpht->iItem = (Position.x / infoPtr->nItemWidth) * nPerCol + (Position.y / infoPtr->nItemHeight);
5631 TRACE("iItem=%d\n", lpht->iItem);
5632 if (lpht->iItem < 0 || lpht->iItem >= infoPtr->nItemCount) lpht->iItem = -1;
5637 if (lpht->iItem == -1) return -1;
5639 if (!LISTVIEW_GetItemMeasures(infoPtr, lpht->iItem, 0, &rcBounds, &rcIcon, &rcLabel)) return -1;
5641 if (!PtInRect(&rcBounds, lpht->pt)) return -1;
5643 if (PtInRect(&rcIcon, lpht->pt))
5644 lpht->flags |= LVHT_ONITEMICON;
5645 else if (PtInRect(&rcLabel, lpht->pt))
5646 lpht->flags |= LVHT_ONITEMLABEL;
5647 else if (infoPtr->himlState)
5649 /* FIXME: move this to GetItemMeasures */
5650 LVITEMW lvItem;
5652 lvItem.mask = LVIF_STATE;
5653 lvItem.stateMask = LVIS_STATEIMAGEMASK;
5654 lvItem.iItem = lpht->iItem;
5655 lvItem.iSubItem = 0;
5656 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1;
5658 UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12;
5659 RECT rcState;
5661 if (uView == LVS_ICON)
5663 rcState.left = rcIcon.left - infoPtr->iconStateSize.cx + 10;
5664 rcState.top = rcIcon.top + infoPtr->iconSize.cy - infoPtr->iconStateSize.cy + 4;
5666 else
5668 rcState.left = rcIcon.left - infoPtr->iconStateSize.cx - IMAGE_PADDING;
5669 rcState.top = rcIcon.top;
5671 rcState.right = rcState.left + infoPtr->iconStateSize.cx;
5672 rcState.bottom = rcState.top + infoPtr->iconStateSize.cy;
5674 if (uStateImage > 0 && PtInRect(&rcState, lpht->pt))
5675 lpht->flags |= LVHT_ONITEMSTATEICON;
5678 if (lpht->flags & LVHT_ONITEM)
5679 lpht->flags &= ~LVHT_NOWHERE;
5681 if (uView == LVS_REPORT && lpht->iItem != -1 && subitem)
5683 INT j, nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
5684 rcBounds.right = rcBounds.left;
5685 for (j = 0; j < nColumnCount; j++)
5687 rcBounds.left = rcBounds.right;
5688 rcBounds.right += LISTVIEW_GetColumnWidth(infoPtr, j);
5689 if (PtInRect(&rcBounds, lpht->pt))
5691 lpht->iSubItem = j;
5692 break;
5697 if (!select || lpht->iItem == -1) return lpht->iItem;
5699 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)) return lpht->iItem;
5701 return lpht->flags & (LVHT_ONITEMICON | LVHT_ONITEMLABEL) ? lpht->iItem : -1;
5705 /***
5706 * DESCRIPTION:
5707 * Inserts a new column.
5709 * PARAMETER(S):
5710 * [I] infoPtr : valid pointer to the listview structure
5711 * [I] INT : column index
5712 * [I] LPLVCOLUMNW : column information
5714 * RETURN:
5715 * SUCCESS : new column index
5716 * FAILURE : -1
5718 static LRESULT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
5719 LPLVCOLUMNW lpColumn, BOOL isW)
5721 RECT rcOld, rcCol;
5722 INT nNewColumn;
5723 HDITEMW hdi;
5725 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
5727 if (!lpColumn) return -1;
5729 hdi.mask = hdi.fmt = 0;
5730 if (lpColumn->mask & LVCF_FMT)
5732 /* format member is valid */
5733 hdi.mask |= HDI_FORMAT;
5735 /* set text alignment (leftmost column must be left-aligned) */
5736 if (nColumn == 0 || lpColumn->fmt & LVCFMT_LEFT)
5737 hdi.fmt |= HDF_LEFT;
5738 else if (lpColumn->fmt & LVCFMT_RIGHT)
5739 hdi.fmt |= HDF_RIGHT;
5740 else if (lpColumn->fmt & LVCFMT_CENTER)
5741 hdi.fmt |= HDF_CENTER;
5743 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
5744 hdi.fmt |= HDF_BITMAP_ON_RIGHT;
5746 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
5748 hdi.fmt |= HDF_IMAGE;
5749 hdi.iImage = I_IMAGECALLBACK;
5752 if (lpColumn->fmt & LVCFMT_IMAGE)
5753 ; /* FIXME: enable images for *(sub)items* this column */
5756 if (lpColumn->mask & LVCF_WIDTH)
5758 hdi.mask |= HDI_WIDTH;
5759 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
5761 /* make it fill the remainder of the controls width */
5762 HDITEMW hdit;
5763 RECT rcHeader;
5764 INT item_index;
5766 /* get the width of every item except the current one */
5767 hdit.mask = HDI_WIDTH;
5768 hdi.cxy = 0;
5770 for(item_index = 0; item_index < (nColumn - 1); item_index++)
5771 if (Header_GetItemW(infoPtr->hwndHeader, item_index, (LPARAM)(&hdit)))
5772 hdi.cxy += hdit.cxy;
5774 /* retrieve the layout of the header */
5775 GetClientRect(infoPtr->hwndSelf, &rcHeader);
5776 TRACE("start cxy=%d rcHeader=%s\n", hdi.cxy, debugrect(&rcHeader));
5778 hdi.cxy = (rcHeader.right - rcHeader.left) - hdi.cxy;
5780 else
5781 hdi.cxy = lpColumn->cx;
5784 if (lpColumn->mask & LVCF_TEXT)
5786 hdi.mask |= HDI_TEXT | HDI_FORMAT;
5787 hdi.fmt |= HDF_STRING;
5788 hdi.pszText = lpColumn->pszText;
5789 hdi.cchTextMax = textlenT(lpColumn->pszText, isW);
5792 if (lpColumn->mask & LVCF_IMAGE)
5794 hdi.mask |= HDI_IMAGE;
5795 hdi.iImage = lpColumn->iImage;
5798 if (lpColumn->mask & LVCF_ORDER)
5800 hdi.mask |= HDI_ORDER;
5801 hdi.iOrder = lpColumn->iOrder;
5804 /* insert item in header control */
5805 nNewColumn = SendMessageW(infoPtr->hwndHeader,
5806 isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
5807 (WPARAM)nColumn, (LPARAM)&hdi);
5808 if (nNewColumn == -1) return -1;
5809 if (!Header_GetItemRect(infoPtr->hwndHeader, nNewColumn, &rcCol)) return -1;
5811 /* now we have to actually adjust the data */
5812 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
5814 LISTVIEW_SUBITEM *lpSubItem, *lpMainItem, **lpNewItems = 0;
5815 HDPA hdpaSubItems;
5816 INT nItem, i;
5818 /* preallocate memory, so we can fail gracefully */
5819 if (nNewColumn == 0)
5821 lpNewItems = COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM *) * infoPtr->nItemCount);
5822 if (!lpNewItems) return -1;
5823 for (i = 0; i < infoPtr->nItemCount; i++)
5824 if (!(lpNewItems[i] = COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM)))) break;
5825 if (i != infoPtr->nItemCount)
5827 for(; i >=0; i--) COMCTL32_Free(lpNewItems[i]);
5828 COMCTL32_Free(lpNewItems);
5829 return -1;
5833 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
5835 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
5836 if (!hdpaSubItems) continue;
5837 for (i = 1; i < hdpaSubItems->nItemCount; i++)
5839 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
5840 if (!lpSubItem) break;
5841 if (lpSubItem->iSubItem >= nNewColumn)
5842 lpSubItem->iSubItem++;
5845 /* if we found our subitem, zapp it */
5846 if (nNewColumn == 0)
5848 lpMainItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, 0);
5849 lpSubItem = lpNewItems[nItem];
5850 lpSubItem->hdr = lpMainItem->hdr;
5851 lpSubItem->iSubItem = 1;
5852 ZeroMemory(&lpMainItem->hdr, sizeof(lpMainItem->hdr));
5853 lpMainItem->iSubItem = 0;
5854 DPA_InsertPtr(hdpaSubItems, 1, lpSubItem);
5858 COMCTL32_Free(lpNewItems);
5861 /* we don't have to worry abiut display issues in non-report mode */
5862 if ((infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return nNewColumn;
5864 /* if we have a focus, must first erase the focus rect */
5865 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, FALSE);
5867 /* Need to reset the item width when inserting a new column */
5868 infoPtr->nItemWidth += rcCol.right - rcCol.left;
5870 LISTVIEW_UpdateScroll(infoPtr);
5872 /* scroll to cover the deleted column, and invalidate for redraw */
5873 rcOld = infoPtr->rcList;
5874 rcOld.left = rcCol.left;
5875 ScrollWindowEx(infoPtr->hwndSelf, rcCol.right - rcCol.left, 0,
5876 &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
5878 /* we can restore focus now */
5879 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, TRUE);
5881 return nNewColumn;
5884 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
5885 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
5886 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
5887 and not during the processing of a LVM_SORTITEMS message. Applications should provide
5888 their own sort proc. when sending LVM_SORTITEMS.
5890 /* Platform SDK:
5891 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
5893 LVS_SORTXXX must be specified,
5894 LVS_OWNERDRAW is not set,
5895 <item>.pszText is not LPSTR_TEXTCALLBACK.
5897 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
5898 are sorted based on item text..."
5900 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
5902 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 );
5903 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 );
5904 INT cmpv = textcmpWT(lv_first->hdr.pszText, lv_second->hdr.pszText, TRUE);
5906 /* if we're sorting descending, negate the return value */
5907 return (((LISTVIEW_INFO *)lParam)->dwStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
5910 /***
5911 * nESCRIPTION:
5912 * Inserts a new item in the listview control.
5914 * PARAMETER(S):
5915 * [I] infoPtr : valid pointer to the listview structure
5916 * [I] lpLVItem : item information
5917 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
5919 * RETURN:
5920 * SUCCESS : new item index
5921 * FAILURE : -1
5923 static LRESULT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5925 LONG lStyle = infoPtr->dwStyle;
5926 UINT uView = lStyle & LVS_TYPEMASK;
5927 INT nItem = -1;
5928 HDPA hdpaSubItems;
5929 NMLISTVIEW nmlv;
5930 LISTVIEW_ITEM *lpItem;
5931 BOOL is_sorted;
5933 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
5935 if (lStyle & LVS_OWNERDATA)
5937 nItem = infoPtr->nItemCount;
5938 infoPtr->nItemCount++;
5939 return nItem;
5942 /* make sure it's an item, and not a subitem; cannot insert a subitem */
5943 if (!lpLVItem || lpLVItem->iSubItem) return -1;
5945 if (!is_assignable_item(lpLVItem, lStyle)) return -1;
5947 if ( !(lpItem = (LISTVIEW_ITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_ITEM))) )
5948 return -1;
5950 /* insert item in listview control data structure */
5951 if ( (hdpaSubItems = DPA_Create(8)) )
5952 nItem = DPA_InsertPtr(hdpaSubItems, 0, lpItem);
5953 if (nItem == -1) goto fail;
5955 is_sorted = (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
5956 !(lStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
5958 nItem = DPA_InsertPtr( infoPtr->hdpaItems,
5959 is_sorted ? infoPtr->nItemCount + 1 : lpLVItem->iItem,
5960 hdpaSubItems );
5961 if (nItem == -1) goto fail;
5962 infoPtr->nItemCount++;
5964 if (!LISTVIEW_SetItemT(infoPtr, lpLVItem, isW))
5965 goto undo;
5967 /* if we're sorted, sort the list, and update the index */
5968 if (is_sorted)
5970 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, (LPARAM)infoPtr );
5971 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
5972 if (nItem == -1)
5974 ERR("We can't find the item we just inserted, possible memory corruption.");
5975 /* we can't remove it from the list if we can't find it, so just fail */
5976 /* we don't deallocate memory here, as it will probably cause more problems */
5977 return -1;
5981 /* make room for the position, if we are in the right mode */
5982 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5984 if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
5985 goto undo;
5986 if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
5988 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
5989 goto undo;
5993 /* Add the subitem list to the items array. Do this last in case we go to
5994 * fail during the above.
5996 LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
5998 lpItem->valid = TRUE;
6000 /* send LVN_INSERTITEM notification */
6001 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6002 nmlv.iItem = nItem;
6003 nmlv.lParam = lpItem->lParam;
6004 notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
6006 /* align items (set position of each item) */
6007 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6009 if (lStyle & LVS_ALIGNLEFT) LISTVIEW_AlignLeft(infoPtr);
6010 else LISTVIEW_AlignTop(infoPtr);
6013 LISTVIEW_UpdateScroll(infoPtr);
6015 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
6017 TRACE(" <- %d\n", nItem);
6018 return nItem;
6020 undo:
6021 DPA_DeletePtr(infoPtr->hdpaItems, nItem);
6022 infoPtr->nItemCount--;
6023 fail:
6024 DPA_DeletePtr(hdpaSubItems, 0);
6025 DPA_Destroy (hdpaSubItems);
6026 COMCTL32_Free (lpItem);
6027 return -1;
6030 /***
6031 * DESCRIPTION:
6032 * Redraws a range of items.
6034 * PARAMETER(S):
6035 * [I] infoPtr : valid pointer to the listview structure
6036 * [I] INT : first item
6037 * [I] INT : last item
6039 * RETURN:
6040 * SUCCESS : TRUE
6041 * FAILURE : FALSE
6043 static LRESULT LISTVIEW_RedrawItems(LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
6045 INT i;
6047 if (nLast < nFirst || min(nFirst, nLast) < 0 ||
6048 max(nFirst, nLast) >= infoPtr->nItemCount)
6049 return FALSE;
6051 for (i = nFirst; i <= nLast; i++)
6052 LISTVIEW_InvalidateItem(infoPtr, i);
6054 return TRUE;
6057 /***
6058 * DESCRIPTION:
6059 * Scroll the content of a listview.
6061 * PARAMETER(S):
6062 * [I] infoPtr : valid pointer to the listview structure
6063 * [I] INT : horizontal scroll amount in pixels
6064 * [I] INT : vertical scroll amount in pixels
6066 * RETURN:
6067 * SUCCESS : TRUE
6068 * FAILURE : FALSE
6070 * COMMENTS:
6071 * If the control is in report mode (LVS_REPORT) the control can
6072 * be scrolled only in line increments. "dy" will be rounded to the
6073 * nearest number of pixels that are a whole line. Ex: if line height
6074 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6075 * is passed the the scroll will be 0. (per MSDN 7/2002)
6077 * For: (per experimentaion with native control and CSpy ListView)
6078 * LVS_ICON dy=1 = 1 pixel (vertical only)
6079 * dx ignored
6080 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
6081 * dx ignored
6082 * LVS_LIST dx=1 = 1 column (horizontal only)
6083 * but will only scroll 1 column per message
6084 * no matter what the value.
6085 * dy must be 0 or FALSE returned.
6086 * LVS_REPORT dx=1 = 1 pixel
6087 * dy= see above
6090 static LRESULT LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
6092 switch(LISTVIEW_GetType(infoPtr)) {
6093 case LVS_REPORT:
6094 dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
6095 dy /= infoPtr->nItemHeight;
6096 break;
6097 case LVS_LIST:
6098 if (dy != 0) return FALSE;
6099 break;
6100 default: /* icon */
6101 dx = 0;
6102 break;
6105 if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
6106 if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
6108 return TRUE;
6111 /***
6112 * DESCRIPTION:
6113 * Sets the background color.
6115 * PARAMETER(S):
6116 * [I] infoPtr : valid pointer to the listview structure
6117 * [I] COLORREF : background color
6119 * RETURN:
6120 * SUCCESS : TRUE
6121 * FAILURE : FALSE
6123 static LRESULT LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
6125 TRACE("(clrBk=%lx)\n", clrBk);
6127 if(infoPtr->clrBk != clrBk) {
6128 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
6129 infoPtr->clrBk = clrBk;
6130 if (clrBk == CLR_NONE)
6131 infoPtr->hBkBrush = GetClassLongW(infoPtr->hwndSelf, GCL_HBRBACKGROUND);
6132 else
6133 infoPtr->hBkBrush = CreateSolidBrush(clrBk);
6134 LISTVIEW_InvalidateList(infoPtr);
6137 return TRUE;
6140 /* LISTVIEW_SetBkImage */
6142 /***
6143 * DESCRIPTION:
6144 * Sets the attributes of a header item.
6146 * PARAMETER(S):
6147 * [I] infoPtr : valid pointer to the listview structure
6148 * [I] INT : column index
6149 * [I] LPLVCOLUMNW : column attributes
6150 * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW,
6151 * otherwise it is in fact a LPLVCOLUMNA
6153 * RETURN:
6154 * SUCCESS : TRUE
6155 * FAILURE : FALSE
6157 static LRESULT LISTVIEW_SetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6158 LPLVCOLUMNW lpColumn, BOOL isW)
6160 BOOL bResult = FALSE;
6161 HDITEMW hdi, hdiget;
6163 if ((lpColumn != NULL) && (nColumn >= 0) &&
6164 (nColumn < Header_GetItemCount(infoPtr->hwndHeader)))
6166 /* initialize memory */
6167 ZeroMemory(&hdi, sizeof(hdi));
6169 if (lpColumn->mask & LVCF_FMT)
6171 /* format member is valid */
6172 hdi.mask |= HDI_FORMAT;
6174 /* get current format first */
6175 hdiget.mask = HDI_FORMAT;
6176 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
6177 /* preserve HDF_STRING if present */
6178 hdi.fmt = hdiget.fmt & HDF_STRING;
6180 /* set text alignment (leftmost column must be left-aligned) */
6181 if (nColumn == 0)
6183 hdi.fmt |= HDF_LEFT;
6185 else
6187 if (lpColumn->fmt & LVCFMT_LEFT)
6188 hdi.fmt |= HDF_LEFT;
6189 else if (lpColumn->fmt & LVCFMT_RIGHT)
6190 hdi.fmt |= HDF_RIGHT;
6191 else if (lpColumn->fmt & LVCFMT_CENTER)
6192 hdi.fmt |= HDF_CENTER;
6195 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6196 hdi.fmt |= HDF_BITMAP_ON_RIGHT;
6198 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6199 hdi.fmt |= HDF_IMAGE;
6201 if (lpColumn->fmt & LVCFMT_IMAGE)
6203 hdi.fmt |= HDF_IMAGE;
6204 hdi.iImage = I_IMAGECALLBACK;
6208 if (lpColumn->mask & LVCF_WIDTH)
6210 hdi.mask |= HDI_WIDTH;
6211 hdi.cxy = lpColumn->cx;
6214 if (lpColumn->mask & LVCF_TEXT)
6216 hdi.mask |= HDI_TEXT | HDI_FORMAT;
6217 hdi.pszText = lpColumn->pszText;
6218 hdi.cchTextMax = textlenT(lpColumn->pszText, isW);
6219 hdi.fmt |= HDF_STRING;
6222 if (lpColumn->mask & LVCF_IMAGE)
6224 hdi.mask |= HDI_IMAGE;
6225 hdi.iImage = lpColumn->iImage;
6228 if (lpColumn->mask & LVCF_ORDER)
6230 hdi.mask |= HDI_ORDER;
6231 hdi.iOrder = lpColumn->iOrder;
6234 /* set header item attributes */
6235 if (isW)
6236 bResult = Header_SetItemW(infoPtr->hwndHeader, nColumn, &hdi);
6237 else
6238 bResult = Header_SetItemA(infoPtr->hwndHeader, nColumn, &hdi);
6241 return bResult;
6244 /***
6245 * DESCRIPTION:
6246 * Sets the column order array
6248 * PARAMETERS:
6249 * [I] infoPtr : valid pointer to the listview structure
6250 * [I] INT : number of elements in column order array
6251 * [I] INT : pointer to column order array
6253 * RETURN:
6254 * SUCCESS : TRUE
6255 * FAILURE : FALSE
6257 static LRESULT LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
6259 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
6261 if (!lpiArray)
6262 return FALSE;
6264 return TRUE;
6268 /***
6269 * DESCRIPTION:
6270 * Sets the width of a column
6272 * PARAMETERS:
6273 * [I] infoPtr : valid pointer to the listview structure
6274 * [I] INT : column index
6275 * [I] INT : column width
6277 * RETURN:
6278 * SUCCESS : TRUE
6279 * FAILURE : FALSE
6281 static LRESULT LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT iCol, INT cx)
6283 HDITEMW hdi;
6284 LRESULT lret;
6285 LONG lStyle = infoPtr->dwStyle;
6286 UINT uView = lStyle & LVS_TYPEMASK;
6287 HDC hdc;
6288 HFONT header_font;
6289 HFONT old_font;
6290 SIZE size;
6291 WCHAR text_buffer[DISP_TEXT_SIZE];
6292 INT header_item_count;
6293 INT item_index;
6294 INT nLabelWidth;
6295 RECT rcHeader;
6296 LVITEMW lvItem;
6297 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
6299 if (!infoPtr->hwndHeader) /* make sure we have a header */
6300 return (FALSE);
6302 /* set column width only if in report or list mode */
6303 if ((uView != LVS_REPORT) && (uView != LVS_LIST))
6304 return (FALSE);
6306 TRACE("(iCol=%d, cx=%d\n", iCol, cx);
6308 /* take care of invalid cx values */
6309 if((uView == LVS_REPORT) && (cx < -2))
6310 cx = LVSCW_AUTOSIZE;
6311 else if (uView == LVS_LIST && (cx < 1))
6312 return FALSE;
6314 /* resize all columns if in LVS_LIST mode */
6315 if(uView == LVS_LIST) {
6316 infoPtr->nItemWidth = cx;
6317 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
6318 return TRUE;
6321 /* autosize based on listview items width */
6322 if(cx == LVSCW_AUTOSIZE)
6324 /* set the width of the column to the width of the widest item */
6325 if (iCol == 0 || uView == LVS_LIST)
6327 cx = 0;
6328 for(item_index = 0; item_index < infoPtr->nItemCount; item_index++)
6330 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, item_index);
6331 cx = (nLabelWidth>cx)?nLabelWidth:cx;
6333 if (infoPtr->himlSmall)
6334 cx += infoPtr->iconSize.cx + IMAGE_PADDING;
6336 else
6338 lvItem.iSubItem = iCol;
6339 lvItem.mask = LVIF_TEXT;
6340 lvItem.pszText = szDispText;
6341 lvItem.cchTextMax = DISP_TEXT_SIZE;
6342 cx = 0;
6343 for(item_index = 0; item_index < infoPtr->nItemCount; item_index++)
6345 lvItem.iItem = item_index;
6346 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
6347 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6348 cx = (nLabelWidth>cx)?nLabelWidth:cx;
6351 cx += TRAILING_PADDING;
6352 } /* autosize based on listview header width */
6353 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
6355 header_item_count = Header_GetItemCount(infoPtr->hwndHeader);
6357 /* if iCol is the last column make it fill the remainder of the controls width */
6358 if(iCol == (header_item_count - 1)) {
6359 /* get the width of every item except the current one */
6360 hdi.mask = HDI_WIDTH;
6361 cx = 0;
6363 for(item_index = 0; item_index < (header_item_count - 1); item_index++) {
6364 Header_GetItemW(infoPtr->hwndHeader, item_index, (LPARAM)(&hdi));
6365 cx+=hdi.cxy;
6368 /* retrieve the layout of the header */
6369 GetWindowRect(infoPtr->hwndHeader, &rcHeader);
6371 cx = (rcHeader.right - rcHeader.left) - cx;
6373 else
6375 /* Despite what the MS docs say, if this is not the last
6376 column, then MS resizes the column to the width of the
6377 largest text string in the column, including headers
6378 and items. This is different from LVSCW_AUTOSIZE in that
6379 LVSCW_AUTOSIZE ignores the header string length.
6382 /* retrieve header font */
6383 header_font = SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0L, 0L);
6385 /* retrieve header text */
6386 hdi.mask = HDI_TEXT;
6387 hdi.cchTextMax = sizeof(text_buffer)/sizeof(text_buffer[0]);
6388 hdi.pszText = text_buffer;
6390 Header_GetItemW(infoPtr->hwndHeader, iCol, (LPARAM)(&hdi));
6392 /* determine the width of the text in the header */
6393 hdc = GetDC(infoPtr->hwndSelf);
6394 old_font = SelectObject(hdc, header_font); /* select the font into hdc */
6396 GetTextExtentPoint32W(hdc, text_buffer, lstrlenW(text_buffer), &size);
6398 SelectObject(hdc, old_font); /* restore the old font */
6399 ReleaseDC(infoPtr->hwndSelf, hdc);
6401 lvItem.iSubItem = iCol;
6402 lvItem.mask = LVIF_TEXT;
6403 lvItem.pszText = szDispText;
6404 lvItem.cchTextMax = DISP_TEXT_SIZE;
6405 cx = size.cx;
6406 for(item_index = 0; item_index < infoPtr->nItemCount; item_index++)
6408 lvItem.iItem = item_index;
6409 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
6410 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6411 nLabelWidth += TRAILING_PADDING;
6412 /* While it is possible for subitems to have icons, even MS messes
6413 up the positioning, so I suspect no applications actually use
6414 them. */
6415 if (item_index == 0 && infoPtr->himlSmall)
6416 nLabelWidth += infoPtr->iconSize.cx + IMAGE_PADDING;
6417 cx = (nLabelWidth>cx)?nLabelWidth:cx;
6422 /* call header to update the column change */
6423 hdi.mask = HDI_WIDTH;
6425 hdi.cxy = cx;
6426 lret = Header_SetItemW(infoPtr->hwndHeader, (WPARAM)iCol, (LPARAM)&hdi);
6428 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
6430 return lret;
6433 /***
6434 * DESCRIPTION:
6435 * Sets the extended listview style.
6437 * PARAMETERS:
6438 * [I] infoPtr : valid pointer to the listview structure
6439 * [I] DWORD : mask
6440 * [I] DWORD : style
6442 * RETURN:
6443 * SUCCESS : previous style
6444 * FAILURE : 0
6446 static LRESULT LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwStyle)
6448 DWORD dwOldStyle = infoPtr->dwLvExStyle;
6450 /* set new style */
6451 if (dwMask)
6452 infoPtr->dwLvExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
6453 else
6454 infoPtr->dwLvExStyle = dwStyle;
6456 return dwOldStyle;
6459 /***
6460 * DESCRIPTION:
6461 * Sets the new hot cursor used during hot tracking and hover selection.
6463 * PARAMETER(S):
6464 * [I] infoPtr : valid pointer to the listview structure
6465 * [I} hCurosr : the new hot cursor handle
6467 * RETURN:
6468 * Returns the previous hot cursor
6470 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
6472 HCURSOR oldCursor = infoPtr->hHotCursor;
6473 infoPtr->hHotCursor = hCursor;
6474 return oldCursor;
6478 /***
6479 * DESCRIPTION:
6480 * Sets the hot item index.
6482 * PARAMETERS:
6483 * [I] infoPtr : valid pointer to the listview structure
6484 * [I] INT : index
6486 * RETURN:
6487 * SUCCESS : previous hot item index
6488 * FAILURE : -1 (no hot item)
6490 static LRESULT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
6492 INT iOldIndex = infoPtr->nHotItem;
6493 infoPtr->nHotItem = iIndex;
6494 return iOldIndex;
6498 /***
6499 * DESCRIPTION:
6500 * Sets the amount of time the cursor must hover over an item before it is selected.
6502 * PARAMETER(S):
6503 * [I] infoPtr : valid pointer to the listview structure
6504 * [I] DWORD : dwHoverTime, if -1 the hover time is set to the default
6506 * RETURN:
6507 * Returns the previous hover time
6509 static LRESULT LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
6511 DWORD oldHoverTime = infoPtr->dwHoverTime;
6512 infoPtr->dwHoverTime = dwHoverTime;
6513 return oldHoverTime;
6516 /***
6517 * DESCRIPTION:
6518 * Sets spacing for icons of LVS_ICON style.
6520 * PARAMETER(S):
6521 * [I] infoPtr : valid pointer to the listview structure
6522 * [I] DWORD : MAKELONG(cx, cy)
6524 * RETURN:
6525 * MAKELONG(oldcx, oldcy)
6527 static LRESULT LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, DWORD spacing)
6529 INT cy = HIWORD(spacing), cx = LOWORD(spacing);
6530 DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6531 LONG lStyle = infoPtr->dwStyle;
6532 UINT uView = lStyle & LVS_TYPEMASK;
6534 TRACE("requested=(%d,%d)\n", cx, cy);
6536 /* this is supported only for LVS_ICON style */
6537 if (uView != LVS_ICON) return oldspacing;
6539 /* set to defaults, if instructed to */
6540 if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
6541 if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
6543 /* if 0 then compute width
6544 * FIXME: Should scan each item and determine max width of
6545 * icon or label, then make that the width */
6546 if (cx == 0)
6547 cx = infoPtr->iconSpacing.cx;
6549 /* if 0 then compute height */
6550 if (cy == 0)
6551 cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
6552 ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
6555 infoPtr->iconSpacing.cx = cx;
6556 infoPtr->iconSpacing.cy = cy;
6558 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%ld,%ld), ntmH=%d\n",
6559 LOWORD(oldspacing), HIWORD(oldspacing), cx, cy,
6560 infoPtr->iconSize.cx, infoPtr->iconSize.cy,
6561 infoPtr->ntmHeight);
6563 /* these depend on the iconSpacing */
6564 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
6565 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
6567 return oldspacing;
6570 inline void update_icon_size(HIMAGELIST himl, SIZE *size)
6572 INT cx, cy;
6574 if (himl && ImageList_GetIconSize(himl, &cx, &cy))
6576 size->cx = cx;
6577 size->cy = cy;
6579 else
6580 size->cx = size->cy = 0;
6583 /***
6584 * DESCRIPTION:
6585 * Sets image lists.
6587 * PARAMETER(S):
6588 * [I] infoPtr : valid pointer to the listview structure
6589 * [I] INT : image list type
6590 * [I] HIMAGELIST : image list handle
6592 * RETURN:
6593 * SUCCESS : old image list
6594 * FAILURE : NULL
6596 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
6598 UINT uView = LISTVIEW_GetType(infoPtr);
6599 INT oldHeight = infoPtr->nItemHeight;
6600 HIMAGELIST himlOld = 0;
6602 switch (nType)
6604 case LVSIL_NORMAL:
6605 himlOld = infoPtr->himlNormal;
6606 infoPtr->himlNormal = himl;
6607 if (uView == LVS_ICON) update_icon_size(himl, &infoPtr->iconSize);
6608 LISTVIEW_SetIconSpacing(infoPtr, 0);
6609 break;
6611 case LVSIL_SMALL:
6612 himlOld = infoPtr->himlSmall;
6613 infoPtr->himlSmall = himl;
6614 if (uView != LVS_ICON) update_icon_size(himl, &infoPtr->iconSize);
6615 break;
6617 case LVSIL_STATE:
6618 himlOld = infoPtr->himlState;
6619 infoPtr->himlState = himl;
6620 update_icon_size(himl, &infoPtr->iconStateSize);
6621 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
6622 break;
6624 default:
6625 ERR("Unknown icon type=%d\n", nType);
6626 return NULL;
6629 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
6630 if (infoPtr->nItemHeight != oldHeight)
6631 LISTVIEW_UpdateScroll(infoPtr);
6633 return himlOld;
6636 /***
6637 * DESCRIPTION:
6638 * Preallocates memory (does *not* set the actual count of items !)
6640 * PARAMETER(S):
6641 * [I] infoPtr : valid pointer to the listview structure
6642 * [I] INT : item count (projected number of items to allocate)
6643 * [I] DWORD : update flags
6645 * RETURN:
6646 * SUCCESS : TRUE
6647 * FAILURE : FALSE
6649 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
6651 TRACE("(nItems=%d, dwFlags=%lx)\n", nItems, dwFlags);
6653 if (infoPtr->dwStyle & LVS_OWNERDATA)
6655 int precount,topvisible;
6657 TRACE("LVS_OWNERDATA is set!\n");
6658 if (dwFlags & (LVSICF_NOINVALIDATEALL | LVSICF_NOSCROLL))
6659 FIXME("flags %s %s not implemented\n",
6660 (dwFlags & LVSICF_NOINVALIDATEALL) ? "LVSICF_NOINVALIDATEALL"
6661 : "",
6662 (dwFlags & LVSICF_NOSCROLL) ? "LVSICF_NOSCROLL" : "");
6664 LISTVIEW_RemoveAllSelections(infoPtr);
6666 precount = infoPtr->nItemCount;
6667 topvisible = LISTVIEW_GetTopIndex(infoPtr) +
6668 LISTVIEW_GetCountPerColumn(infoPtr) + 1;
6670 infoPtr->nItemCount = nItems;
6671 infoPtr->nItemWidth = max(LISTVIEW_CalculateMaxWidth(infoPtr),
6672 DEFAULT_COLUMN_WIDTH);
6674 LISTVIEW_UpdateSize(infoPtr);
6675 LISTVIEW_UpdateScroll(infoPtr);
6677 if (min(precount,infoPtr->nItemCount) < topvisible)
6678 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
6680 else
6682 /* According to MSDN for non-LVS_OWNERDATA this is just
6683 * a performance issue. The control allocates its internal
6684 * data structures for the number of items specified. It
6685 * cuts down on the number of memory allocations. Therefore
6686 * we will just issue a WARN here
6688 WARN("for non-ownerdata performance option not implemented.\n");
6691 return TRUE;
6694 /***
6695 * DESCRIPTION:
6696 * Sets the position of an item.
6698 * PARAMETER(S):
6699 * [I] infoPtr : valid pointer to the listview structure
6700 * [I] nItem : item index
6701 * [I] pt : coordinate
6703 * RETURN:
6704 * SUCCESS : TRUE
6705 * FAILURE : FALSE
6707 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT pt)
6709 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6710 POINT old;
6712 TRACE("(nItem=%d, &pt=%s\n", nItem, debugpoint(&pt));
6714 if (nItem < 0 || nItem >= infoPtr->nItemCount ||
6715 !(uView == LVS_ICON || uView == LVS_SMALLICON)) return FALSE;
6717 /* This point value seems to be an undocumented feature.
6718 * The best guess is that it means either at the origin,
6719 * or at true beginning of the list. I will assume the origin. */
6720 if ((pt.x == -1) && (pt.y == -1))
6721 LISTVIEW_GetOrigin(infoPtr, &pt);
6722 else if (uView == LVS_ICON)
6724 pt.x -= (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
6725 pt.y -= ICON_TOP_PADDING;
6728 /* save the old position */
6729 old.x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
6730 old.y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
6732 /* Is the position changing? */
6733 if (pt.x == old.x && pt.y == old.y) return TRUE;
6735 /* FIXME: shouldn't we invalidate, as the item moved? */
6737 /* Allocating a POINTER for every item is too resource intensive,
6738 * so we'll keep the (x,y) in different arrays */
6739 if (DPA_SetPtr(infoPtr->hdpaPosX, nItem, (void *)pt.x) &&
6740 DPA_SetPtr(infoPtr->hdpaPosY, nItem, (void *)pt.y) )
6741 return TRUE;
6743 ERR("We should never fail here (nItem=%d, pt=%s), please report.\n",
6744 nItem, debugpoint(&pt));
6745 return FALSE;
6748 /***
6749 * DESCRIPTION:
6750 * Sets the state of one or many items.
6752 * PARAMETER(S):
6753 * [I] infoPtr : valid pointer to the listview structure
6754 * [I]INT : item index
6755 * [I] LPLVITEM : item or subitem info
6757 * RETURN:
6758 * SUCCESS : TRUE
6759 * FAILURE : FALSE
6761 static LRESULT LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem)
6763 BOOL bResult = TRUE;
6764 LVITEMW lvItem;
6766 lvItem.iItem = nItem;
6767 lvItem.iSubItem = 0;
6768 lvItem.mask = LVIF_STATE;
6769 lvItem.state = lpLVItem->state;
6770 lvItem.stateMask = lpLVItem->stateMask;
6771 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
6773 if (nItem == -1)
6775 /* apply to all items */
6776 for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
6777 if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
6779 else
6780 bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
6782 return bResult;
6785 /***
6786 * DESCRIPTION:
6787 * Sets the text of an item or subitem.
6789 * PARAMETER(S):
6790 * [I] hwnd : window handle
6791 * [I] nItem : item index
6792 * [I] lpLVItem : item or subitem info
6793 * [I] isW : TRUE if input is Unicode
6795 * RETURN:
6796 * SUCCESS : TRUE
6797 * FAILURE : FALSE
6799 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
6801 LVITEMW lvItem;
6803 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
6805 lvItem.iItem = nItem;
6806 lvItem.iSubItem = lpLVItem->iSubItem;
6807 lvItem.mask = LVIF_TEXT;
6808 lvItem.pszText = lpLVItem->pszText;
6809 lvItem.cchTextMax = lpLVItem->cchTextMax;
6811 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
6813 return LISTVIEW_SetItemT(infoPtr, &lvItem, isW);
6816 /***
6817 * DESCRIPTION:
6818 * Set item index that marks the start of a multiple selection.
6820 * PARAMETER(S):
6821 * [I] infoPtr : valid pointer to the listview structure
6822 * [I] INT : index
6824 * RETURN:
6825 * Index number or -1 if there is no selection mark.
6827 static LRESULT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
6829 INT nOldIndex = infoPtr->nSelectionMark;
6831 TRACE("(nIndex=%d)\n", nIndex);
6833 infoPtr->nSelectionMark = nIndex;
6835 return nOldIndex;
6838 /***
6839 * DESCRIPTION:
6840 * Sets the text background color.
6842 * PARAMETER(S):
6843 * [I] infoPtr : valid pointer to the listview structure
6844 * [I] COLORREF : text background color
6846 * RETURN:
6847 * SUCCESS : TRUE
6848 * FAILURE : FALSE
6850 static LRESULT LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
6852 TRACE("(clrTextBk=%lx)\n", clrTextBk);
6854 if (infoPtr->clrTextBk != clrTextBk)
6856 infoPtr->clrTextBk = clrTextBk;
6857 LISTVIEW_InvalidateList(infoPtr);
6860 return TRUE;
6863 /***
6864 * DESCRIPTION:
6865 * Sets the text foreground color.
6867 * PARAMETER(S):
6868 * [I] infoPtr : valid pointer to the listview structure
6869 * [I] COLORREF : text color
6871 * RETURN:
6872 * SUCCESS : TRUE
6873 * FAILURE : FALSE
6875 static LRESULT LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
6877 TRACE("(clrText=%lx)\n", clrText);
6879 if (infoPtr->clrText != clrText)
6881 infoPtr->clrText = clrText;
6882 LISTVIEW_InvalidateList(infoPtr);
6885 return TRUE;
6888 /* LISTVIEW_SetToolTips */
6889 /* LISTVIEW_SetUnicodeFormat */
6890 /* LISTVIEW_SetWorkAreas */
6892 /***
6893 * DESCRIPTION:
6894 * Callback internally used by LISTVIEW_SortItems()
6896 * PARAMETER(S):
6897 * [I] LPVOID : first LISTVIEW_ITEM to compare
6898 * [I] LPVOID : second LISTVIEW_ITEM to compare
6899 * [I] LPARAM : HWND of control
6901 * RETURN:
6902 * if first comes before second : negative
6903 * if first comes after second : positive
6904 * if first and second are equivalent : zero
6906 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
6908 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW((HWND)lParam, 0);
6909 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 );
6910 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 );
6912 /* Forward the call to the client defined callback */
6913 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
6916 /***
6917 * DESCRIPTION:
6918 * Sorts the listview items.
6920 * PARAMETER(S):
6921 * [I] infoPtr : valid pointer to the listview structure
6922 * [I] WPARAM : application-defined value
6923 * [I] LPARAM : pointer to comparision callback
6925 * RETURN:
6926 * SUCCESS : TRUE
6927 * FAILURE : FALSE
6929 static LRESULT LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
6931 UINT lStyle = infoPtr->dwStyle;
6932 HDPA hdpaSubItems;
6933 LISTVIEW_ITEM *lpItem;
6934 LPVOID selectionMarkItem;
6935 int i;
6937 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
6939 if (lStyle & LVS_OWNERDATA) return FALSE;
6941 if (!infoPtr->hdpaItems) return FALSE;
6943 /* if there are 0 or 1 items, there is no need to sort */
6944 if (infoPtr->nItemCount < 2) return TRUE;
6946 if (infoPtr->nFocusedItem >= 0)
6948 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
6949 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
6950 if (lpItem) lpItem->state |= LVIS_FOCUSED;
6953 infoPtr->pfnCompare = pfnCompare;
6954 infoPtr->lParamSort = lParamSort;
6955 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr->hwndSelf);
6957 /* Adjust selections and indices so that they are the way they should
6958 * be after the sort (otherwise, the list items move around, but
6959 * whatever is at the item's previous original position will be
6960 * selected instead)
6961 * FIXME: can't this be made more efficient?
6963 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
6964 for (i=0; i < infoPtr->nItemCount; i++)
6966 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
6967 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
6969 if (lpItem->state & LVIS_SELECTED)
6970 LISTVIEW_AddSelectionRange(infoPtr, i, i);
6971 else
6972 LISTVIEW_RemoveSelectionRange(infoPtr, i, i);
6973 if (lpItem->state & LVIS_FOCUSED)
6975 infoPtr->nFocusedItem = i;
6976 lpItem->state &= ~LVIS_FOCUSED;
6979 if (selectionMarkItem != NULL)
6980 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
6981 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
6983 /* align the items */
6984 LISTVIEW_AlignTop(infoPtr);
6986 /* refresh the display */
6987 LISTVIEW_InvalidateList(infoPtr); /* FIXME: display should not change for [SMALL]ICON view */
6989 return TRUE;
6992 /***
6993 * DESCRIPTION:
6994 * Updates an items or rearranges the listview control.
6996 * PARAMETER(S):
6997 * [I] infoPtr : valid pointer to the listview structure
6998 * [I] INT : item index
7000 * RETURN:
7001 * SUCCESS : TRUE
7002 * FAILURE : FALSE
7004 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
7006 LONG lStyle = infoPtr->dwStyle;
7007 UINT uView = lStyle & LVS_TYPEMASK;
7009 TRACE("(nItem=%d)\n", nItem);
7011 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
7013 /* rearrange with default alignment style */
7014 if ((lStyle & LVS_AUTOARRANGE) && ((uView == LVS_ICON) ||(uView == LVS_SMALLICON)))
7015 LISTVIEW_Arrange(infoPtr, 0);
7016 else
7017 LISTVIEW_InvalidateItem(infoPtr, nItem);
7019 return TRUE;
7023 /***
7024 * DESCRIPTION:
7025 * Creates the listview control.
7027 * PARAMETER(S):
7028 * [I] hwnd : window handle
7029 * [I] lpcs : the create parameters
7031 * RETURN:
7032 * Success: 0
7033 * Failure: -1
7035 static LRESULT LISTVIEW_Create(HWND hwnd, LPCREATESTRUCTW lpcs)
7037 LISTVIEW_INFO *infoPtr;
7038 UINT uView = lpcs->style & LVS_TYPEMASK;
7039 LOGFONTW logFont;
7041 TRACE("(lpcs=%p)\n", lpcs);
7043 /* initialize info pointer */
7044 infoPtr = (LISTVIEW_INFO *)COMCTL32_Alloc(sizeof(LISTVIEW_INFO));
7045 if (!infoPtr) return -1;
7047 SetWindowLongW(hwnd, 0, (LONG)infoPtr);
7049 infoPtr->hwndSelf = hwnd;
7050 infoPtr->dwStyle = lpcs->style;
7051 /* determine the type of structures to use */
7052 infoPtr->notifyFormat = SendMessageW(GetParent(infoPtr->hwndSelf), WM_NOTIFYFORMAT,
7053 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
7055 /* initialize color information */
7056 infoPtr->clrBk = CLR_NONE;
7057 infoPtr->clrText = comctl32_color.clrWindowText;
7058 infoPtr->clrTextBk = CLR_DEFAULT;
7059 LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
7061 /* set default values */
7062 infoPtr->nFocusedItem = -1;
7063 infoPtr->nSelectionMark = -1;
7064 infoPtr->nHotItem = -1;
7065 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
7066 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
7067 infoPtr->nEditLabelItem = -1;
7069 /* get default font (icon title) */
7070 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
7071 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
7072 infoPtr->hFont = infoPtr->hDefaultFont;
7073 LISTVIEW_SaveTextMetrics(infoPtr);
7075 /* create header */
7076 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, (LPCWSTR)NULL,
7077 WS_CHILD | HDS_HORZ | (DWORD)((LVS_NOSORTHEADER & lpcs->style)?0:HDS_BUTTONS),
7078 0, 0, 0, 0, hwnd, (HMENU)0,
7079 lpcs->hInstance, NULL);
7081 /* set header unicode format */
7082 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT,(WPARAM)TRUE,(LPARAM)NULL);
7084 /* set header font */
7085 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont,
7086 (LPARAM)TRUE);
7088 if (uView == LVS_ICON)
7090 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
7091 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
7093 else if (uView == LVS_REPORT)
7095 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
7097 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
7099 else
7101 /* set HDS_HIDDEN flag to hide the header bar */
7102 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
7103 GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
7107 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
7108 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
7110 else
7112 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
7113 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
7116 infoPtr->iconStateSize.cx = GetSystemMetrics(SM_CXSMICON);
7117 infoPtr->iconStateSize.cy = GetSystemMetrics(SM_CYSMICON);
7119 /* display unsupported listview window styles */
7120 LISTVIEW_UnsupportedStyles(lpcs->style);
7122 /* allocate memory for the data structure */
7123 infoPtr->hdpaItems = DPA_Create(10);
7124 infoPtr->hdpaPosX = DPA_Create(10);
7125 infoPtr->hdpaPosY = DPA_Create(10);
7127 /* allocate memory for the selection ranges */
7128 infoPtr->hdpaSelectionRanges = DPA_Create(10);
7130 /* initialize size of items */
7131 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
7132 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
7134 /* initialize the hover time to -1(indicating the default system hover time) */
7135 infoPtr->dwHoverTime = -1;
7137 return 0;
7140 /***
7141 * DESCRIPTION:
7142 * Erases the background of the listview control.
7144 * PARAMETER(S):
7145 * [I] infoPtr : valid pointer to the listview structure
7146 * [I] hdc : device context handle
7148 * RETURN:
7149 * SUCCESS : TRUE
7150 * FAILURE : FALSE
7152 static inline BOOL LISTVIEW_EraseBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc)
7154 RECT rc;
7156 TRACE("(hdc=%x)\n", hdc);
7158 if (!GetClipBox(hdc, &rc)) return FALSE;
7160 return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
7164 /***
7165 * DESCRIPTION:
7166 * Helper function for LISTVIEW_[HV]Scroll *only*.
7167 * Performs vertical/horizontal scrolling by a give amount.
7169 * PARAMETER(S):
7170 * [I] infoPtr : valid pointer to the listview structure
7171 * [I] dx : amount of horizontal scroll
7172 * [I] dy : amount of vertical scroll
7174 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
7176 /* now we can scroll the list */
7177 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList,
7178 &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
7179 /* if we have focus, adjust rect */
7180 OffsetRect(&infoPtr->rcFocus, dx, dy);
7181 UpdateWindow(infoPtr->hwndSelf);
7184 /***
7185 * DESCRIPTION:
7186 * Performs vertical scrolling.
7188 * PARAMETER(S):
7189 * [I] infoPtr : valid pointer to the listview structure
7190 * [I] nScrollCode : scroll code
7191 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7192 * [I] hScrollWnd : scrollbar control window handle
7194 * RETURN:
7195 * Zero
7197 * NOTES:
7198 * SB_LINEUP/SB_LINEDOWN:
7199 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
7200 * for LVS_REPORT is 1 line
7201 * for LVS_LIST cannot occur
7204 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7205 INT nScrollDiff, HWND hScrollWnd)
7207 UINT uView = LISTVIEW_GetType(infoPtr);
7208 INT nOldScrollPos, nNewScrollPos;
7209 SCROLLINFO scrollInfo;
7210 BOOL is_an_icon;
7212 TRACE("(nScrollCode=%d, nScrollDiff=%d)\n", nScrollCode, nScrollDiff);
7214 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7216 scrollInfo.cbSize = sizeof(SCROLLINFO);
7217 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7219 is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
7221 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
7223 nOldScrollPos = scrollInfo.nPos;
7224 switch (nScrollCode)
7226 case SB_INTERNAL:
7227 break;
7229 case SB_LINEUP:
7230 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
7231 break;
7233 case SB_LINEDOWN:
7234 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
7235 break;
7237 case SB_PAGEUP:
7238 nScrollDiff = -scrollInfo.nPage;
7239 break;
7241 case SB_PAGEDOWN:
7242 nScrollDiff = scrollInfo.nPage;
7243 break;
7245 case SB_THUMBPOSITION:
7246 case SB_THUMBTRACK:
7247 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7248 break;
7250 default:
7251 nScrollDiff = 0;
7254 /* quit right away if pos isn't changing */
7255 if (nScrollDiff == 0) return 0;
7257 /* calculate new position, and handle overflows */
7258 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7259 if (nScrollDiff > 0) {
7260 if (nNewScrollPos < nOldScrollPos ||
7261 nNewScrollPos > scrollInfo.nMax)
7262 nNewScrollPos = scrollInfo.nMax;
7263 } else {
7264 if (nNewScrollPos > nOldScrollPos ||
7265 nNewScrollPos < scrollInfo.nMin)
7266 nNewScrollPos = scrollInfo.nMin;
7269 /* set the new position, and reread in case it changed */
7270 scrollInfo.fMask = SIF_POS;
7271 scrollInfo.nPos = nNewScrollPos;
7272 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
7274 /* carry on only if it really changed */
7275 if (nNewScrollPos == nOldScrollPos) return 0;
7277 /* now adjust to client coordinates */
7278 nScrollDiff = nOldScrollPos - nNewScrollPos;
7279 if (uView == LVS_REPORT) nScrollDiff *= infoPtr->nItemHeight;
7281 /* and scroll the window */
7282 scroll_list(infoPtr, 0, nScrollDiff);
7284 return 0;
7287 /***
7288 * DESCRIPTION:
7289 * Performs horizontal scrolling.
7291 * PARAMETER(S):
7292 * [I] infoPtr : valid pointer to the listview structure
7293 * [I] nScrollCode : scroll code
7294 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7295 * [I] hScrollWnd : scrollbar control window handle
7297 * RETURN:
7298 * Zero
7300 * NOTES:
7301 * SB_LINELEFT/SB_LINERIGHT:
7302 * for LVS_ICON, LVS_SMALLICON 1 pixel
7303 * for LVS_REPORT is 1 pixel
7304 * for LVS_LIST is 1 column --> which is a 1 because the
7305 * scroll is based on columns not pixels
7308 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7309 INT nScrollDiff, HWND hScrollWnd)
7311 UINT uView = LISTVIEW_GetType(infoPtr);
7312 INT nOldScrollPos, nNewScrollPos;
7313 SCROLLINFO scrollInfo;
7315 TRACE("(nScrollCode=%d, nScrollDiff=%d)\n", nScrollCode, nScrollDiff);
7317 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7319 scrollInfo.cbSize = sizeof(SCROLLINFO);
7320 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7322 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
7324 nOldScrollPos = scrollInfo.nPos;
7326 switch (nScrollCode)
7328 case SB_INTERNAL:
7329 break;
7331 case SB_LINELEFT:
7332 nScrollDiff = -1;
7333 break;
7335 case SB_LINERIGHT:
7336 nScrollDiff = 1;
7337 break;
7339 case SB_PAGELEFT:
7340 nScrollDiff = -scrollInfo.nPage;
7341 break;
7343 case SB_PAGERIGHT:
7344 nScrollDiff = scrollInfo.nPage;
7345 break;
7347 case SB_THUMBPOSITION:
7348 case SB_THUMBTRACK:
7349 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7350 break;
7352 default:
7353 nScrollDiff = 0;
7356 /* quit right away if pos isn't changing */
7357 if (nScrollDiff == 0) return 0;
7359 /* calculate new position, and handle overflows */
7360 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7361 if (nScrollDiff > 0) {
7362 if (nNewScrollPos < nOldScrollPos ||
7363 nNewScrollPos > scrollInfo.nMax)
7364 nNewScrollPos = scrollInfo.nMax;
7365 } else {
7366 if (nNewScrollPos > nOldScrollPos ||
7367 nNewScrollPos < scrollInfo.nMin)
7368 nNewScrollPos = scrollInfo.nMin;
7371 /* set the new position, and reread in case it changed */
7372 scrollInfo.fMask = SIF_POS;
7373 scrollInfo.nPos = nNewScrollPos;
7374 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
7376 /* carry on only if it really changed */
7377 if (nNewScrollPos == nOldScrollPos) return 0;
7379 if(uView == LVS_REPORT)
7380 LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
7382 /* now adjust to client coordinates */
7383 nScrollDiff = nOldScrollPos - nNewScrollPos;
7384 if (uView == LVS_LIST) nScrollDiff *= infoPtr->nItemWidth;
7386 /* and scroll the window */
7387 scroll_list(infoPtr, nScrollDiff, 0);
7389 return 0;
7392 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
7394 UINT uView = LISTVIEW_GetType(infoPtr);
7395 INT gcWheelDelta = 0;
7396 UINT pulScrollLines = 3;
7397 SCROLLINFO scrollInfo;
7399 TRACE("(wheelDelta=%d)\n", wheelDelta);
7401 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
7402 gcWheelDelta -= wheelDelta;
7404 scrollInfo.cbSize = sizeof(SCROLLINFO);
7405 scrollInfo.fMask = SIF_POS;
7407 switch(uView)
7409 case LVS_ICON:
7410 case LVS_SMALLICON:
7412 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
7413 * should be fixed in the future.
7415 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
7416 LISTVIEW_VScroll(infoPtr, SB_THUMBPOSITION,
7417 scrollInfo.nPos + (gcWheelDelta < 0) ?
7418 LISTVIEW_SCROLL_ICON_LINE_SIZE :
7419 -LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
7420 break;
7422 case LVS_REPORT:
7423 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
7425 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
7427 int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
7428 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
7429 LISTVIEW_VScroll(infoPtr, SB_THUMBPOSITION, scrollInfo.nPos + cLineScroll, 0);
7432 break;
7434 case LVS_LIST:
7435 LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
7436 break;
7438 return 0;
7441 /***
7442 * DESCRIPTION:
7443 * ???
7445 * PARAMETER(S):
7446 * [I] infoPtr : valid pointer to the listview structure
7447 * [I] INT : virtual key
7448 * [I] LONG : key data
7450 * RETURN:
7451 * Zero
7453 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
7455 UINT uView = LISTVIEW_GetType(infoPtr);
7456 INT nItem = -1;
7457 NMLVKEYDOWN nmKeyDown;
7459 TRACE("(nVirtualKey=%d, lKeyData=%ld)\n", nVirtualKey, lKeyData);
7461 /* send LVN_KEYDOWN notification */
7462 nmKeyDown.wVKey = nVirtualKey;
7463 nmKeyDown.flags = 0;
7464 notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
7466 switch (nVirtualKey)
7468 case VK_RETURN:
7469 if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
7471 notify(infoPtr, NM_RETURN);
7472 notify(infoPtr, LVN_ITEMACTIVATE);
7474 break;
7476 case VK_HOME:
7477 if (infoPtr->nItemCount > 0)
7478 nItem = 0;
7479 break;
7481 case VK_END:
7482 if (infoPtr->nItemCount > 0)
7483 nItem = infoPtr->nItemCount - 1;
7484 break;
7486 case VK_LEFT:
7487 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TOLEFT);
7488 break;
7490 case VK_UP:
7491 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_ABOVE);
7492 break;
7494 case VK_RIGHT:
7495 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TORIGHT);
7496 break;
7498 case VK_DOWN:
7499 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_BELOW);
7500 break;
7502 case VK_PRIOR:
7503 if (uView == LVS_REPORT)
7504 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr);
7505 else
7506 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
7507 * LISTVIEW_GetCountPerRow(infoPtr);
7508 if(nItem < 0) nItem = 0;
7509 break;
7511 case VK_NEXT:
7512 if (uView == LVS_REPORT)
7513 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr);
7514 else
7515 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
7516 * LISTVIEW_GetCountPerRow(infoPtr);
7517 if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
7518 break;
7521 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem))
7522 LISTVIEW_KeySelection(infoPtr, nItem);
7524 return 0;
7527 /***
7528 * DESCRIPTION:
7529 * Kills the focus.
7531 * PARAMETER(S):
7532 * [I] infoPtr : valid pointer to the listview structure
7534 * RETURN:
7535 * Zero
7537 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
7539 TRACE("()\n");
7541 /* if we did not have the focus, there's nothing to do */
7542 if (!infoPtr->bFocus) return 0;
7544 /* send NM_KILLFOCUS notification */
7545 notify(infoPtr, NM_KILLFOCUS);
7547 /* if we have a focus rectagle, get rid of it */
7548 LISTVIEW_ShowFocusRect(infoPtr, FALSE);
7550 /* set window focus flag */
7551 infoPtr->bFocus = FALSE;
7553 /* invalidate the selected items before reseting focus flag */
7554 LISTVIEW_InvalidateSelectedItems(infoPtr);
7556 return 0;
7559 /***
7560 * DESCRIPTION:
7561 * Processes double click messages (left mouse button).
7563 * PARAMETER(S):
7564 * [I] infoPtr : valid pointer to the listview structure
7565 * [I] wKey : key flag
7566 * [I] pts : mouse coordinate
7568 * RETURN:
7569 * Zero
7571 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7573 LVHITTESTINFO htInfo;
7575 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7577 /* send NM_RELEASEDCAPTURE notification */
7578 notify(infoPtr, NM_RELEASEDCAPTURE);
7580 htInfo.pt.x = pts.x;
7581 htInfo.pt.y = pts.y;
7583 /* send NM_DBLCLK notification */
7584 LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
7585 notify_click(infoPtr, NM_DBLCLK, &htInfo);
7587 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
7588 if(htInfo.iItem != -1) notify(infoPtr, LVN_ITEMACTIVATE);
7590 return 0;
7593 /***
7594 * DESCRIPTION:
7595 * Processes mouse down messages (left mouse button).
7597 * PARAMETER(S):
7598 * [I] infoPtr : valid pointer to the listview structure
7599 * [I] wKey : key flag
7600 * [I] pts : mouse coordinate
7602 * RETURN:
7603 * Zero
7605 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7607 LVHITTESTINFO lvHitTestInfo;
7608 LONG lStyle = infoPtr->dwStyle;
7609 static BOOL bGroupSelect = TRUE;
7610 POINT pt = { pts.x, pts.y };
7611 INT nItem;
7613 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7615 /* send NM_RELEASEDCAPTURE notification */
7616 notify(infoPtr, NM_RELEASEDCAPTURE);
7618 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
7620 /* set left button down flag */
7621 infoPtr->bLButtonDown = TRUE;
7623 lvHitTestInfo.pt.x = pts.x;
7624 lvHitTestInfo.pt.y = pts.y;
7626 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
7627 TRACE("at %s, nItem=%d\n", debugpoint(&pt), nItem);
7628 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
7630 if (lStyle & LVS_SINGLESEL)
7632 if ((LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
7633 && infoPtr->nEditLabelItem == -1)
7634 infoPtr->nEditLabelItem = nItem;
7635 else
7636 LISTVIEW_SetSelection(infoPtr, nItem);
7638 else
7640 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
7642 if (bGroupSelect)
7643 LISTVIEW_AddGroupSelection(infoPtr, nItem);
7644 else
7646 LVITEMW item;
7648 item.state = LVIS_SELECTED | LVIS_FOCUSED;
7649 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
7651 LISTVIEW_SetItemState(infoPtr,nItem,&item);
7652 infoPtr->nSelectionMark = nItem;
7655 else if (wKey & MK_CONTROL)
7657 LVITEMW item;
7659 bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
7661 item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
7662 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
7663 LISTVIEW_SetItemState(infoPtr, nItem, &item);
7664 infoPtr->nSelectionMark = nItem;
7666 else if (wKey & MK_SHIFT)
7668 LISTVIEW_SetGroupSelection(infoPtr, nItem);
7670 else
7672 BOOL was_selected = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
7674 /* set selection (clears other pre-existing selections) */
7675 LISTVIEW_SetSelection(infoPtr, nItem);
7677 if (was_selected && infoPtr->nEditLabelItem == -1)
7678 infoPtr->nEditLabelItem = nItem;
7682 else
7684 /* remove all selections */
7685 LISTVIEW_RemoveAllSelections(infoPtr);
7688 return 0;
7691 /***
7692 * DESCRIPTION:
7693 * Processes mouse up messages (left mouse button).
7695 * PARAMETER(S):
7696 * [I] infoPtr : valid pointer to the listview structure
7697 * [I] wKey : key flag
7698 * [I] pts : mouse coordinate
7700 * RETURN:
7701 * Zero
7703 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7705 LVHITTESTINFO lvHitTestInfo;
7707 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7709 if (!infoPtr->bLButtonDown) return 0;
7711 lvHitTestInfo.pt.x = pts.x;
7712 lvHitTestInfo.pt.y = pts.y;
7714 /* send NM_CLICK notification */
7715 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
7716 notify_click(infoPtr, NM_CLICK, &lvHitTestInfo);
7718 /* set left button flag */
7719 infoPtr->bLButtonDown = FALSE;
7721 if(infoPtr->nEditLabelItem != -1)
7723 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem &&
7724 (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
7725 LISTVIEW_EditLabelT(infoPtr, lvHitTestInfo.iItem, TRUE);
7726 infoPtr->nEditLabelItem = -1;
7729 return 0;
7732 /***
7733 * DESCRIPTION:
7734 * Destroys the listview control (called after WM_DESTROY).
7736 * PARAMETER(S):
7737 * [I] infoPtr : valid pointer to the listview structure
7739 * RETURN:
7740 * Zero
7742 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
7744 LONG lStyle = infoPtr->dwStyle;
7746 TRACE("()\n");
7748 /* delete all items */
7749 LISTVIEW_DeleteAllItems(infoPtr);
7751 /* destroy data structure */
7752 DPA_Destroy(infoPtr->hdpaItems);
7753 DPA_Destroy(infoPtr->hdpaSelectionRanges);
7755 /* destroy image lists */
7756 if (!(lStyle & LVS_SHAREIMAGELISTS))
7758 /* FIXME: If the caller does a ImageList_Destroy and then we
7759 * do this code the area will be freed twice. Currently
7760 * this generates an "err:heap:HEAP_ValidateInUseArena
7761 * Heap xxxxxxxx: in-use arena yyyyyyyy next block
7762 * has PREV_FREE flag" sometimes.
7764 * We will leak the memory till we figure out how to fix
7766 if (infoPtr->himlNormal)
7767 ImageList_Destroy(infoPtr->himlNormal);
7768 if (infoPtr->himlSmall)
7769 ImageList_Destroy(infoPtr->himlSmall);
7770 if (infoPtr->himlState)
7771 ImageList_Destroy(infoPtr->himlState);
7774 /* destroy font, bkgnd brush */
7775 infoPtr->hFont = 0;
7776 if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
7777 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
7779 /* free listview info pointer*/
7780 COMCTL32_Free(infoPtr);
7782 SetWindowLongW(infoPtr->hwndSelf, 0, 0);
7783 return 0;
7786 /***
7787 * DESCRIPTION:
7788 * Handles notifications from children.
7790 * PARAMETER(S):
7791 * [I] infoPtr : valid pointer to the listview structure
7792 * [I] INT : control identifier
7793 * [I] LPNMHDR : notification information
7795 * RETURN:
7796 * Zero
7798 static LRESULT LISTVIEW_Notify(LISTVIEW_INFO *infoPtr, INT nCtrlId, LPNMHDR lpnmh)
7800 TRACE("(nCtrlId=%d, lpnmh=%p)\n", nCtrlId, lpnmh);
7802 if (lpnmh->hwndFrom == infoPtr->hwndHeader)
7804 /* handle notification from header control */
7805 if (lpnmh->code == HDN_ENDTRACKW)
7807 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
7808 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
7810 else if(lpnmh->code == HDN_ITEMCLICKW || lpnmh->code == HDN_ITEMCLICKA)
7812 /* Handle sorting by Header Column */
7813 NMLISTVIEW nmlv;
7815 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
7816 nmlv.iItem = -1;
7817 nmlv.iSubItem = ((LPNMHEADERW)lpnmh)->iItem;
7818 notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
7820 else if(lpnmh->code == NM_RELEASEDCAPTURE)
7822 /* Idealy this should be done in HDN_ENDTRACKA
7823 * but since SetItemBounds in Header.c is called after
7824 * the notification is sent, it is neccessary to handle the
7825 * update of the scroll bar here (Header.c works fine as it is,
7826 * no need to disturb it)
7828 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
7829 LISTVIEW_UpdateScroll(infoPtr);
7830 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
7835 return 0;
7838 /***
7839 * DESCRIPTION:
7840 * Determines the type of structure to use.
7842 * PARAMETER(S):
7843 * [I] infoPtr : valid pointer to the listview structureof the sender
7844 * [I] HWND : listview window handle
7845 * [I] INT : command specifying the nature of the WM_NOTIFYFORMAT
7847 * RETURN:
7848 * Zero
7850 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
7852 TRACE("(hwndFrom=%x, nCommand=%d)\n", hwndFrom, nCommand);
7854 if (nCommand == NF_REQUERY)
7855 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT,
7856 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
7857 return 0;
7860 /***
7861 * DESCRIPTION:
7862 * Paints/Repaints the listview control.
7864 * PARAMETER(S):
7865 * [I] infoPtr : valid pointer to the listview structure
7866 * [I] HDC : device context handle
7868 * RETURN:
7869 * Zero
7871 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
7873 TRACE("(hdc=%x)\n", hdc);
7875 if (hdc)
7876 LISTVIEW_Refresh(infoPtr, hdc);
7877 else
7879 PAINTSTRUCT ps;
7881 hdc = BeginPaint(infoPtr->hwndSelf, &ps);
7882 if (!hdc) return 1;
7883 if (ps.fErase) LISTVIEW_FillBkgnd(infoPtr, hdc, &ps.rcPaint);
7884 LISTVIEW_Refresh(infoPtr, hdc);
7885 EndPaint(infoPtr->hwndSelf, &ps);
7888 return 0;
7891 /***
7892 * DESCRIPTION:
7893 * Processes double click messages (right mouse button).
7895 * PARAMETER(S):
7896 * [I] infoPtr : valid pointer to the listview structure
7897 * [I] wKey : key flag
7898 * [I] pts : mouse coordinate
7900 * RETURN:
7901 * Zero
7903 static LRESULT LISTVIEW_RButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7905 LVHITTESTINFO lvHitTestInfo;
7907 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
7909 /* send NM_RELEASEDCAPTURE notification */
7910 notify(infoPtr, NM_RELEASEDCAPTURE);
7912 /* send NM_RDBLCLK notification */
7913 lvHitTestInfo.pt.x = pts.x;
7914 lvHitTestInfo.pt.y = pts.y;
7915 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
7916 notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
7918 return 0;
7921 /***
7922 * DESCRIPTION:
7923 * Processes mouse down messages (right mouse button).
7925 * PARAMETER(S):
7926 * [I] infoPtr : valid pointer to the listview structure
7927 * [I] wKey : key flag
7928 * [I] pts : mouse coordinate
7930 * RETURN:
7931 * Zero
7933 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7935 LVHITTESTINFO lvHitTestInfo;
7936 INT nItem;
7938 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
7940 /* send NM_RELEASEDCAPTURE notification */
7941 notify(infoPtr, NM_RELEASEDCAPTURE);
7943 /* make sure the listview control window has the focus */
7944 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
7946 /* set right button down flag */
7947 infoPtr->bRButtonDown = TRUE;
7949 /* determine the index of the selected item */
7950 lvHitTestInfo.pt.x = pts.x;
7951 lvHitTestInfo.pt.y = pts.y;
7952 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
7954 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
7956 LISTVIEW_SetItemFocus(infoPtr, nItem);
7957 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
7958 !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
7959 LISTVIEW_SetSelection(infoPtr, nItem);
7961 else
7963 LISTVIEW_RemoveAllSelections(infoPtr);
7966 return 0;
7969 /***
7970 * DESCRIPTION:
7971 * Processes mouse up messages (right mouse button).
7973 * PARAMETER(S):
7974 * [I] infoPtr : valid pointer to the listview structure
7975 * [I] wKey : key flag
7976 * [I] pts : mouse coordinate
7978 * RETURN:
7979 * Zero
7981 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7983 LVHITTESTINFO lvHitTestInfo;
7984 POINT pt;
7986 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
7988 if (!infoPtr->bRButtonDown) return 0;
7990 /* set button flag */
7991 infoPtr->bRButtonDown = FALSE;
7993 /* Send NM_RClICK notification */
7994 lvHitTestInfo.pt.x = pts.x;
7995 lvHitTestInfo.pt.y = pts.y;
7996 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
7997 notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo);
7999 /* Change to screen coordinate for WM_CONTEXTMENU */
8000 pt = lvHitTestInfo.pt;
8001 ClientToScreen(infoPtr->hwndSelf, &pt);
8003 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
8004 SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
8005 (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
8007 return 0;
8011 /***
8012 * DESCRIPTION:
8013 * Sets the cursor.
8015 * PARAMETER(S):
8016 * [I] infoPtr : valid pointer to the listview structure
8017 * [I] hwnd : window handle of window containing the cursor
8018 * [I] nHittest : hit-test code
8019 * [I] wMouseMsg : ideintifier of the mouse message
8021 * RETURN:
8022 * TRUE if cursor is set
8023 * FALSE otherwise
8025 static BOOL LISTVIEW_SetCursor(LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
8027 LVHITTESTINFO lvHitTestInfo;
8029 if(!(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)) return FALSE;
8031 if(!infoPtr->hHotCursor) return FALSE;
8033 GetCursorPos(&lvHitTestInfo.pt);
8034 if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) return FALSE;
8036 SetCursor(infoPtr->hHotCursor);
8038 return TRUE;
8041 /***
8042 * DESCRIPTION:
8043 * Sets the focus.
8045 * PARAMETER(S):
8046 * [I] infoPtr : valid pointer to the listview structure
8047 * [I] infoPtr : handle of previously focused window
8049 * RETURN:
8050 * Zero
8052 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
8054 TRACE("(hwndLoseFocus=%x)\n", hwndLoseFocus);
8056 /* if we have the focus already, there's nothing to do */
8057 if (infoPtr->bFocus) return 0;
8059 /* send NM_SETFOCUS notification */
8060 notify(infoPtr, NM_SETFOCUS);
8062 /* set window focus flag */
8063 infoPtr->bFocus = TRUE;
8065 /* put the focus rect back on */
8066 LISTVIEW_ShowFocusRect(infoPtr, TRUE);
8068 /* redraw all visible selected items */
8069 LISTVIEW_InvalidateSelectedItems(infoPtr);
8071 return 0;
8074 /***
8075 * DESCRIPTION:
8076 * Sets the font.
8078 * PARAMETER(S):
8079 * [I] infoPtr : valid pointer to the listview structure
8080 * [I] HFONT : font handle
8081 * [I] WORD : redraw flag
8083 * RETURN:
8084 * Zero
8086 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
8088 HFONT oldFont = infoPtr->hFont;
8090 TRACE("(hfont=%x,redraw=%hu)\n", hFont, fRedraw);
8092 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
8093 if (infoPtr->hFont == oldFont) return 0;
8095 LISTVIEW_SaveTextMetrics(infoPtr);
8097 if (LISTVIEW_GetType(infoPtr) == LVS_REPORT)
8098 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
8100 if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
8102 return 0;
8105 /***
8106 * DESCRIPTION:
8107 * Message handling for WM_SETREDRAW.
8108 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
8110 * PARAMETER(S):
8111 * [I] infoPtr : valid pointer to the listview structure
8112 * [I] bRedraw: state of redraw flag
8114 * RETURN:
8115 * DefWinProc return value
8117 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
8119 infoPtr->bRedraw = bRedraw;
8120 if(bRedraw)
8121 RedrawWindow(infoPtr->hwndSelf, NULL, 0,
8122 RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ALLCHILDREN | RDW_ERASENOW);
8123 return 0;
8126 /***
8127 * DESCRIPTION:
8128 * Resizes the listview control. This function processes WM_SIZE
8129 * messages. At this time, the width and height are not used.
8131 * PARAMETER(S):
8132 * [I] infoPtr : valid pointer to the listview structure
8133 * [I] WORD : new width
8134 * [I] WORD : new height
8136 * RETURN:
8137 * Zero
8139 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
8141 LONG lStyle = infoPtr->dwStyle;
8142 UINT uView = lStyle & LVS_TYPEMASK;
8144 TRACE("(width=%d, height=%d)\n", Width, Height);
8146 if (LISTVIEW_UpdateSize(infoPtr))
8148 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
8150 if (lStyle & LVS_ALIGNLEFT)
8151 LISTVIEW_AlignLeft(infoPtr);
8152 else
8153 LISTVIEW_AlignTop(infoPtr);
8156 LISTVIEW_UpdateScroll(infoPtr);
8158 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
8161 return 0;
8164 /***
8165 * DESCRIPTION:
8166 * Sets the size information.
8168 * PARAMETER(S):
8169 * [I] infoPtr : valid pointer to the listview structure
8171 * RETURN:
8172 * Zero if no size change
8173 * 1 of size changed
8175 static BOOL LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
8177 LONG lStyle = infoPtr->dwStyle;
8178 UINT uView = lStyle & LVS_TYPEMASK;
8179 RECT rcList;
8180 RECT rcOld;
8182 GetClientRect(infoPtr->hwndSelf, &rcList);
8183 CopyRect(&rcOld,&(infoPtr->rcList));
8184 infoPtr->rcList.left = 0;
8185 infoPtr->rcList.right = max(rcList.right - rcList.left, 1);
8186 infoPtr->rcList.top = 0;
8187 infoPtr->rcList.bottom = max(rcList.bottom - rcList.top, 1);
8189 if (uView == LVS_LIST)
8191 /* Apparently the "LIST" style is supposed to have the same
8192 * number of items in a column even if there is no scroll bar.
8193 * Since if a scroll bar already exists then the bottom is already
8194 * reduced, only reduce if the scroll bar does not currently exist.
8195 * The "2" is there to mimic the native control. I think it may be
8196 * related to either padding or edges. (GLA 7/2002)
8198 if (!(lStyle & WS_HSCROLL))
8200 INT nHScrollHeight = GetSystemMetrics(SM_CYHSCROLL);
8201 if (infoPtr->rcList.bottom > nHScrollHeight)
8202 infoPtr->rcList.bottom -= (nHScrollHeight + 2);
8204 else
8206 if (infoPtr->rcList.bottom > 2)
8207 infoPtr->rcList.bottom -= 2;
8210 else if (uView == LVS_REPORT)
8212 HDLAYOUT hl;
8213 WINDOWPOS wp;
8215 hl.prc = &rcList;
8216 hl.pwpos = &wp;
8217 Header_Layout(infoPtr->hwndHeader, &hl);
8219 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8221 if (!(LVS_NOCOLUMNHEADER & lStyle))
8222 infoPtr->rcList.top = max(wp.cy, 0);
8224 return (EqualRect(&rcOld,&(infoPtr->rcList)));
8227 /***
8228 * DESCRIPTION:
8229 * Processes WM_STYLECHANGED messages.
8231 * PARAMETER(S):
8232 * [I] infoPtr : valid pointer to the listview structure
8233 * [I] WPARAM : window style type (normal or extended)
8234 * [I] LPSTYLESTRUCT : window style information
8236 * RETURN:
8237 * Zero
8239 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
8240 LPSTYLESTRUCT lpss)
8242 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
8243 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
8244 RECT rcList = infoPtr->rcList;
8246 TRACE("(styletype=%x, styleOld=0x%08lx, styleNew=0x%08lx)\n",
8247 wStyleType, lpss->styleOld, lpss->styleNew);
8249 /* FIXME: if LVS_NOSORTHEADER changed, update header */
8251 if (wStyleType == GWL_STYLE)
8253 infoPtr->dwStyle = lpss->styleNew;
8255 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
8256 ((lpss->styleNew & WS_HSCROLL) == 0))
8257 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
8259 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
8260 ((lpss->styleNew & WS_VSCROLL) == 0))
8261 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
8263 /* If switching modes, then start with no scroll bars and then
8264 * decide.
8266 if (uNewView != uOldView)
8268 if (uOldView == LVS_REPORT)
8269 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
8271 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
8272 SetRectEmpty(&infoPtr->rcFocus);
8275 if (uNewView == LVS_ICON)
8277 INT oldcx, oldcy;
8279 /* First readjust the iconSize and if necessary the iconSpacing */
8280 oldcx = infoPtr->iconSize.cx;
8281 oldcy = infoPtr->iconSize.cy;
8282 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
8283 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
8284 if (infoPtr->himlNormal != NULL)
8286 INT cx, cy;
8287 ImageList_GetIconSize(infoPtr->himlNormal, &cx, &cy);
8288 infoPtr->iconSize.cx = cx;
8289 infoPtr->iconSize.cy = cy;
8291 if ((infoPtr->iconSize.cx != oldcx) || (infoPtr->iconSize.cy != oldcy))
8293 TRACE("icon old size=(%d,%d), new size=(%ld,%ld)\n",
8294 oldcx, oldcy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
8295 LISTVIEW_SetIconSpacing(infoPtr,0);
8298 /* Now update the full item width and height */
8299 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
8300 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
8301 if (lpss->styleNew & LVS_ALIGNLEFT)
8302 LISTVIEW_AlignLeft(infoPtr);
8303 else
8304 LISTVIEW_AlignTop(infoPtr);
8306 else if (uNewView == LVS_REPORT)
8308 HDLAYOUT hl;
8309 WINDOWPOS wp;
8311 hl.prc = &rcList;
8312 hl.pwpos = &wp;
8313 Header_Layout(infoPtr->hwndHeader, &hl);
8314 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy,
8315 wp.flags);
8316 if (!(LVS_NOCOLUMNHEADER & lpss->styleNew))
8317 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
8319 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8320 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8321 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
8322 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
8324 else if (uNewView == LVS_LIST)
8326 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8327 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8328 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
8329 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
8331 else
8333 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8334 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8335 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
8336 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
8337 if (lpss->styleNew & LVS_ALIGNLEFT)
8338 LISTVIEW_AlignLeft(infoPtr);
8339 else
8340 LISTVIEW_AlignTop(infoPtr);
8343 /* update the size of the client area */
8344 LISTVIEW_UpdateSize(infoPtr);
8346 /* add scrollbars if needed */
8347 LISTVIEW_UpdateScroll(infoPtr);
8349 /* invalidate client area + erase background */
8350 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
8352 /* print the list of unsupported window styles */
8353 LISTVIEW_UnsupportedStyles(lpss->styleNew);
8356 /* If they change the view and we have an active edit control
8357 we will need to kill the control since the redraw will
8358 misplace the edit control.
8360 if (infoPtr->bEditing &&
8361 ((uNewView & (LVS_ICON|LVS_LIST|LVS_SMALLICON)) !=
8362 ((LVS_ICON|LVS_LIST|LVS_SMALLICON) & uOldView)))
8364 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8367 return 0;
8370 /***
8371 * DESCRIPTION:
8372 * Window procedure of the listview control.
8375 static LRESULT WINAPI
8376 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
8378 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8380 TRACE("(uMsg=%x wParam=%x lParam=%lx)\n", uMsg, wParam, lParam);
8382 if (!infoPtr && (uMsg != WM_CREATE))
8383 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8385 if (infoPtr)
8387 infoPtr->dwStyle = GetWindowLongW(hwnd, GWL_STYLE);
8390 switch (uMsg)
8392 case LVM_APPROXIMATEVIEWRECT:
8393 return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
8394 LOWORD(lParam), HIWORD(lParam));
8395 case LVM_ARRANGE:
8396 return LISTVIEW_Arrange(infoPtr, (INT)wParam);
8398 /* case LVN_CANCELEDITLABEL */
8400 /* case LVM_CREATEDRAGIMAGE: */
8402 case LVM_DELETEALLITEMS:
8403 return LISTVIEW_DeleteAllItems(infoPtr);
8405 case LVM_DELETECOLUMN:
8406 return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
8408 case LVM_DELETEITEM:
8409 return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
8411 case LVM_EDITLABELW:
8412 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
8414 case LVM_EDITLABELA:
8415 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
8417 /* case LVN_ENABLEGROUPVIEW: */
8419 case LVM_ENSUREVISIBLE:
8420 return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
8422 case LVM_FINDITEMW:
8423 return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
8425 case LVM_FINDITEMA:
8426 return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
8428 case LVM_GETBKCOLOR:
8429 return infoPtr->clrBk;
8431 /* case LVM_GETBKIMAGE: */
8433 case LVM_GETCALLBACKMASK:
8434 return infoPtr->uCallbackMask;
8436 case LVM_GETCOLUMNA:
8437 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8439 case LVM_GETCOLUMNW:
8440 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8442 case LVM_GETCOLUMNORDERARRAY:
8443 return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
8445 case LVM_GETCOLUMNWIDTH:
8446 return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
8448 case LVM_GETCOUNTPERPAGE:
8449 return LISTVIEW_GetCountPerPage(infoPtr);
8451 case LVM_GETEDITCONTROL:
8452 return (LRESULT)infoPtr->hwndEdit;
8454 case LVM_GETEXTENDEDLISTVIEWSTYLE:
8455 return infoPtr->dwLvExStyle;
8457 case LVM_GETHEADER:
8458 return (LRESULT)infoPtr->hwndHeader;
8460 case LVM_GETHOTCURSOR:
8461 return (LRESULT)infoPtr->hHotCursor;
8463 case LVM_GETHOTITEM:
8464 return infoPtr->nHotItem;
8466 case LVM_GETHOVERTIME:
8467 return infoPtr->dwHoverTime;
8469 case LVM_GETIMAGELIST:
8470 return LISTVIEW_GetImageList(infoPtr, (INT)wParam);
8472 /* case LVN_GETINSERTMARK: */
8474 /* case LVN_GETINSERTMARKCOLOR: */
8476 /* case LVN_GETINSERTMARKRECT: */
8478 case LVM_GETISEARCHSTRINGA:
8479 case LVM_GETISEARCHSTRINGW:
8480 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
8481 return FALSE;
8483 case LVM_GETITEMA:
8484 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE);
8486 case LVM_GETITEMW:
8487 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE);
8489 case LVM_GETITEMCOUNT:
8490 return infoPtr->nItemCount;
8492 case LVM_GETITEMPOSITION:
8493 return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
8495 case LVM_GETITEMRECT:
8496 return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
8498 case LVM_GETITEMSPACING:
8499 return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
8501 case LVM_GETITEMSTATE:
8502 return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
8504 case LVM_GETITEMTEXTA:
8505 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
8507 case LVM_GETITEMTEXTW:
8508 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
8510 case LVM_GETNEXTITEM:
8511 return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
8513 case LVM_GETNUMBEROFWORKAREAS:
8514 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
8515 return 1;
8517 case LVM_GETORIGIN:
8518 return LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
8520 /* case LVN_GETOUTLINECOLOR: */
8522 /* case LVM_GETSELECTEDCOLUMN: */
8524 case LVM_GETSELECTEDCOUNT:
8525 return LISTVIEW_GetSelectedCount(infoPtr);
8527 case LVM_GETSELECTIONMARK:
8528 return infoPtr->nSelectionMark;
8530 case LVM_GETSTRINGWIDTHA:
8531 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
8533 case LVM_GETSTRINGWIDTHW:
8534 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
8536 case LVM_GETSUBITEMRECT:
8537 return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
8539 case LVM_GETTEXTBKCOLOR:
8540 return infoPtr->clrTextBk;
8542 case LVM_GETTEXTCOLOR:
8543 return infoPtr->clrText;
8545 /* case LVN_GETTILEINFO: */
8547 /* case LVN_GETTILEVIEWINFO: */
8549 case LVM_GETTOOLTIPS:
8550 FIXME("LVM_GETTOOLTIPS: unimplemented\n");
8551 return FALSE;
8553 case LVM_GETTOPINDEX:
8554 return LISTVIEW_GetTopIndex(infoPtr);
8556 /*case LVM_GETUNICODEFORMAT:
8557 FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
8558 return FALSE;*/
8560 case LVM_GETVIEWRECT:
8561 return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
8563 case LVM_GETWORKAREAS:
8564 FIXME("LVM_GETWORKAREAS: unimplemented\n");
8565 return FALSE;
8567 /* case LVN_HASGROUP: */
8569 case LVM_HITTEST:
8570 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, FALSE);
8572 case LVM_INSERTCOLUMNA:
8573 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8575 case LVM_INSERTCOLUMNW:
8576 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8578 /* case LVN_INSERTGROUP: */
8580 /* case LVN_INSERTGROUPSORTED: */
8582 case LVM_INSERTITEMA:
8583 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
8585 case LVM_INSERTITEMW:
8586 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
8588 /* case LVN_INSERTMARKHITTEST: */
8590 /* case LVN_ISGROUPVIEWENABLED: */
8592 /* case LVN_MAPIDTOINDEX: */
8594 /* case LVN_INEDXTOID: */
8596 /* case LVN_MOVEGROUP: */
8598 /* case LVN_MOVEITEMTOGROUP: */
8600 case LVM_REDRAWITEMS:
8601 return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
8603 /* case LVN_REMOVEALLGROUPS: */
8605 /* case LVN_REMOVEGROUP: */
8607 case LVM_SCROLL:
8608 return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
8610 case LVM_SETBKCOLOR:
8611 return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
8613 /* case LVM_SETBKIMAGE: */
8615 case LVM_SETCALLBACKMASK:
8616 infoPtr->uCallbackMask = (UINT)wParam;
8617 return TRUE;
8619 case LVM_SETCOLUMNA:
8620 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8622 case LVM_SETCOLUMNW:
8623 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8625 case LVM_SETCOLUMNORDERARRAY:
8626 return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
8628 case LVM_SETCOLUMNWIDTH:
8629 return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, SLOWORD(lParam));
8631 case LVM_SETEXTENDEDLISTVIEWSTYLE:
8632 return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
8634 /* case LVN_SETGROUPINFO: */
8636 /* case LVN_SETGROUPMETRICS: */
8638 case LVM_SETHOTCURSOR:
8639 return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
8641 case LVM_SETHOTITEM:
8642 return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
8644 case LVM_SETHOVERTIME:
8645 return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
8647 case LVM_SETICONSPACING:
8648 return LISTVIEW_SetIconSpacing(infoPtr, (DWORD)lParam);
8650 case LVM_SETIMAGELIST:
8651 return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
8653 /* case LVN_SETINFOTIP: */
8655 /* case LVN_SETINSERTMARK: */
8657 /* case LVN_SETINSERTMARKCOLOR: */
8659 case LVM_SETITEMA:
8660 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
8662 case LVM_SETITEMW:
8663 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
8665 case LVM_SETITEMCOUNT:
8666 return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
8668 case LVM_SETITEMPOSITION:
8670 POINT pt = { SLOWORD(lParam), SHIWORD(lParam) };
8671 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, pt);
8674 case LVM_SETITEMPOSITION32:
8675 if (lParam == 0) return FALSE;
8676 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, *((POINT*)lParam));
8678 case LVM_SETITEMSTATE:
8679 return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
8681 case LVM_SETITEMTEXTA:
8682 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
8684 case LVM_SETITEMTEXTW:
8685 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
8687 /* case LVN_SETOUTLINECOLOR: */
8689 /* case LVN_SETSELECTEDCOLUMN: */
8691 case LVM_SETSELECTIONMARK:
8692 return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
8694 case LVM_SETTEXTBKCOLOR:
8695 return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
8697 case LVM_SETTEXTCOLOR:
8698 return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
8700 /* case LVN_SETTILEINFO: */
8702 /* case LVN_SETTILEVIEWINFO: */
8704 /* case LVN_SETTILEWIDTH: */
8706 /* case LVM_SETTOOLTIPS: */
8708 /* case LVM_SETUNICODEFORMAT: */
8710 /* case LVN_SETVIEW: */
8712 /* case LVM_SETWORKAREAS: */
8714 /* case LVN_SORTGROUPS: */
8716 case LVM_SORTITEMS:
8717 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
8719 case LVM_SUBITEMHITTEST:
8720 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
8722 case LVM_UPDATE:
8723 return LISTVIEW_Update(infoPtr, (INT)wParam);
8725 case WM_CHAR:
8726 return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
8728 case WM_COMMAND:
8729 return LISTVIEW_Command(infoPtr, wParam, lParam);
8731 case WM_CREATE:
8732 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
8734 case WM_ERASEBKGND:
8735 return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
8737 case WM_GETDLGCODE:
8738 return DLGC_WANTCHARS | DLGC_WANTARROWS;
8740 case WM_GETFONT:
8741 return infoPtr->hFont;
8743 case WM_HSCROLL:
8744 return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
8746 case WM_KEYDOWN:
8747 return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
8749 case WM_KILLFOCUS:
8750 return LISTVIEW_KillFocus(infoPtr);
8752 case WM_LBUTTONDBLCLK:
8753 return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8755 case WM_LBUTTONDOWN:
8756 return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8758 case WM_LBUTTONUP:
8759 return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8761 case WM_MOUSEMOVE:
8762 return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8764 case WM_MOUSEHOVER:
8765 return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8767 case WM_NCDESTROY:
8768 return LISTVIEW_NCDestroy(infoPtr);
8770 case WM_NOTIFY:
8771 return LISTVIEW_Notify(infoPtr, (INT)wParam, (LPNMHDR)lParam);
8773 case WM_NOTIFYFORMAT:
8774 return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
8776 case WM_PAINT:
8777 return LISTVIEW_Paint(infoPtr, (HDC)wParam);
8779 case WM_RBUTTONDBLCLK:
8780 return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8782 case WM_RBUTTONDOWN:
8783 return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8785 case WM_RBUTTONUP:
8786 return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8788 case WM_SETCURSOR:
8789 if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
8790 return TRUE;
8791 goto fwd_msg;
8793 case WM_SETFOCUS:
8794 return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
8796 case WM_SETFONT:
8797 return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
8799 case WM_SETREDRAW:
8800 return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
8802 case WM_SIZE:
8803 return LISTVIEW_Size(infoPtr, (int)SLOWORD(lParam), (int)SHIWORD(lParam));
8805 case WM_STYLECHANGED:
8806 return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
8808 case WM_SYSCOLORCHANGE:
8809 COMCTL32_RefreshSysColors();
8810 return 0;
8812 /* case WM_TIMER: */
8814 case WM_VSCROLL:
8815 return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
8817 case WM_MOUSEWHEEL:
8818 if (wParam & (MK_SHIFT | MK_CONTROL))
8819 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8820 return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
8822 case WM_WINDOWPOSCHANGED:
8823 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE)) {
8824 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
8825 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
8826 LISTVIEW_UpdateSize(infoPtr);
8827 LISTVIEW_UpdateScroll(infoPtr);
8829 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8831 /* case WM_WININICHANGE: */
8833 default:
8834 if ((uMsg >= WM_USER) && (uMsg < WM_APP))
8835 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
8837 fwd_msg:
8838 /* call default window procedure */
8839 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8842 return 0;
8845 /***
8846 * DESCRIPTION:
8847 * Registers the window class.
8849 * PARAMETER(S):
8850 * None
8852 * RETURN:
8853 * None
8855 void LISTVIEW_Register(void)
8857 WNDCLASSW wndClass;
8859 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
8860 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
8861 wndClass.lpfnWndProc = (WNDPROC)LISTVIEW_WindowProc;
8862 wndClass.cbClsExtra = 0;
8863 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
8864 wndClass.hCursor = LoadCursorW(0, IDC_ARROWW);
8865 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
8866 wndClass.lpszClassName = WC_LISTVIEWW;
8867 RegisterClassW(&wndClass);
8870 /***
8871 * DESCRIPTION:
8872 * Unregisters the window class.
8874 * PARAMETER(S):
8875 * None
8877 * RETURN:
8878 * None
8880 void LISTVIEW_Unregister(void)
8882 UnregisterClassW(WC_LISTVIEWW, (HINSTANCE)NULL);
8885 /***
8886 * DESCRIPTION:
8887 * Handle any WM_COMMAND messages
8889 * PARAMETER(S):
8891 * RETURN:
8893 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
8895 switch (HIWORD(wParam))
8897 case EN_UPDATE:
8900 * Adjust the edit window size
8902 WCHAR buffer[1024];
8903 HDC hdc = GetDC(infoPtr->hwndEdit);
8904 HFONT hFont, hOldFont = 0;
8905 RECT rect;
8906 SIZE sz;
8907 int len;
8909 len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
8910 GetWindowRect(infoPtr->hwndEdit, &rect);
8912 /* Select font to get the right dimension of the string */
8913 hFont = SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
8914 if(hFont != 0)
8916 hOldFont = SelectObject(hdc, hFont);
8919 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
8921 TEXTMETRICW textMetric;
8923 /* Add Extra spacing for the next character */
8924 GetTextMetricsW(hdc, &textMetric);
8925 sz.cx += (textMetric.tmMaxCharWidth * 2);
8927 SetWindowPos (
8928 infoPtr->hwndEdit,
8929 HWND_TOP,
8932 sz.cx,
8933 rect.bottom - rect.top,
8934 SWP_DRAWFRAME|SWP_NOMOVE);
8936 if(hFont != 0)
8937 SelectObject(hdc, hOldFont);
8939 ReleaseDC(infoPtr->hwndSelf, hdc);
8941 break;
8944 default:
8945 return SendMessageW (GetParent (infoPtr->hwndSelf), WM_COMMAND, wParam, lParam);
8948 return 0;
8952 /***
8953 * DESCRIPTION:
8954 * Subclassed edit control windproc function
8956 * PARAMETER(S):
8958 * RETURN:
8960 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg,
8961 WPARAM wParam, LPARAM lParam, BOOL isW)
8963 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(GetParent(hwnd), 0);
8964 static BOOL bIgnoreKillFocus = FALSE;
8965 BOOL cancel = FALSE;
8967 TRACE("(hwnd=%x, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
8968 hwnd, uMsg, wParam, lParam, isW);
8970 switch (uMsg)
8972 case WM_GETDLGCODE:
8973 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
8975 case WM_KILLFOCUS:
8976 if(bIgnoreKillFocus) return TRUE;
8977 break;
8979 case WM_DESTROY:
8981 WNDPROC editProc = infoPtr->EditWndProc;
8982 infoPtr->EditWndProc = 0;
8983 SetWindowLongW(hwnd, GWL_WNDPROC, (LONG)editProc);
8984 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
8987 case WM_KEYDOWN:
8988 if (VK_ESCAPE == (INT)wParam)
8990 cancel = TRUE;
8991 break;
8993 else if (VK_RETURN == (INT)wParam)
8994 break;
8996 default:
8997 return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
9000 if (infoPtr->bEditing)
9002 LPWSTR buffer = NULL;
9004 if (!cancel)
9006 DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
9008 if (len)
9010 if ( (buffer = COMCTL32_Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
9012 if (isW) GetWindowTextW(hwnd, buffer, len+1);
9013 else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
9017 /* Processing LVN_ENDLABELEDIT message could kill the focus */
9018 /* eg. Using a messagebox */
9019 bIgnoreKillFocus = TRUE;
9020 LISTVIEW_EndEditLabelT(infoPtr, buffer, isW);
9022 if (buffer) COMCTL32_Free(buffer);
9024 bIgnoreKillFocus = FALSE;
9027 SendMessageW(hwnd, WM_CLOSE, 0, 0);
9028 return TRUE;
9031 /***
9032 * DESCRIPTION:
9033 * Subclassed edit control windproc function
9035 * PARAMETER(S):
9037 * RETURN:
9039 LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9041 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
9044 /***
9045 * DESCRIPTION:
9046 * Subclassed edit control windproc function
9048 * PARAMETER(S):
9050 * RETURN:
9052 LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9054 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
9057 /***
9058 * DESCRIPTION:
9059 * Creates a subclassed edit cotrol
9061 * PARAMETER(S):
9063 * RETURN:
9065 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style,
9066 INT x, INT y, INT width, INT height, BOOL isW)
9068 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
9069 HWND hedit;
9070 SIZE sz;
9071 HDC hdc;
9072 HDC hOldFont=0;
9073 TEXTMETRICW textMetric;
9074 HINSTANCE hinst = GetWindowLongW(infoPtr->hwndSelf, GWL_HINSTANCE);
9076 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
9078 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|WS_BORDER;
9079 hdc = GetDC(infoPtr->hwndSelf);
9081 /* Select the font to get appropriate metric dimensions */
9082 if(infoPtr->hFont != 0)
9083 hOldFont = SelectObject(hdc, infoPtr->hFont);
9085 /*Get String Lenght in pixels */
9086 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
9088 /*Add Extra spacing for the next character */
9089 GetTextMetricsW(hdc, &textMetric);
9090 sz.cx += (textMetric.tmMaxCharWidth * 2);
9092 if(infoPtr->hFont != 0)
9093 SelectObject(hdc, hOldFont);
9095 ReleaseDC(infoPtr->hwndSelf, hdc);
9096 if (isW)
9097 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9098 else
9099 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9101 if (!hedit) return 0;
9103 infoPtr->EditWndProc = (WNDPROC)
9104 (isW ? SetWindowLongW(hedit, GWL_WNDPROC, (LONG)EditLblWndProcW) :
9105 SetWindowLongA(hedit, GWL_WNDPROC, (LONG)EditLblWndProcA) );
9107 SendMessageW(hedit, WM_SETFONT, infoPtr->hFont, FALSE);
9109 return hedit;