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
11 * Listview control implementation.
14 * 1. No horizontal scrolling when header is larger than the client area.
15 * 2. Drawing optimizations.
16 * 3. Hot item handling.
19 * LISTVIEW_Notify : most notifications from children (editbox and header)
22 * LISTVIEW_SetItemCount : not completed for non OWNERDATA
24 * Advanced functionality:
25 * LISTVIEW_GetNumberOfWorkAreas : not implemented
26 * LISTVIEW_GetHotCursor : not implemented
27 * LISTVIEW_GetISearchString : not implemented
28 * LISTVIEW_GetBkImage : not implemented
29 * LISTVIEW_SetBkImage : not implemented
30 * LISTVIEW_GetColumnOrderArray : simple hack only
31 * LISTVIEW_SetColumnOrderArray : simple hack only
32 * LISTVIEW_Arrange : empty stub
33 * LISTVIEW_ApproximateViewRect : incomplete
34 * LISTVIEW_Scroll : not implemented
35 * LISTVIEW_Update : not completed
37 * Known differences in message stream from native control (not known if
38 * these differences cause problems):
39 * LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
40 * LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
41 * WM_PAINT does LVN_GETDISPINFO in item order 0->n, native does n->0.
42 * WM_SETREDRAW(True) native does LVN_GETDISPINFO for all items and
43 * does *not* invoke DefWindowProc
44 * WM_CREATE does not issue WM_QUERYUISTATE and associated registry
45 * processing for "USEDOUBLECLICKTIME".
57 #include "debugtools.h"
59 DEFAULT_DEBUG_CHANNEL(listview);
61 /* Some definitions for inline edit control */
62 typedef BOOL (*EditlblCallbackW)(HWND, LPWSTR, DWORD);
63 typedef BOOL (*EditlblCallbackA)(HWND, LPWSTR, DWORD);
65 typedef struct tagLV_INTHIT
68 DWORD distance; /* distance to closest item */
69 INT iDistItem; /* item number that is closest */
70 } LV_INTHIT, *LPLV_INTHIT;
73 typedef struct tagEDITLABEL_ITEM
77 EditlblCallbackW EditLblCb;
80 typedef struct tagLISTVIEW_SUBITEM
87 typedef struct tagLISTVIEW_ITEM
98 typedef struct tagLISTVIEW_SELECTION
102 } LISTVIEW_SELECTION;
104 typedef struct tagLISTVIEW_INFO
110 HIMAGELIST himlNormal;
111 HIMAGELIST himlSmall;
112 HIMAGELIST himlState;
116 HDPA hdpaSelectionRanges;
130 INT ntmHeight; /* from GetTextMetrics from above font */
131 INT ntmAveCharWidth; /* from GetTextMetrics from above font */
133 DWORD dwExStyle; /* extended listview style */
135 PFNLVCOMPARE pfnCompare;
139 EDITLABEL_ITEM *pedititem;
141 INT nColumnCount; /* the number of columns in this control */
143 DWORD lastKeyPressTimestamp; /* Added */
144 WPARAM charCode; /* Added */
145 INT nSearchParamLength; /* Added */
146 WCHAR szSearchParam[ MAX_PATH ]; /* Added */
153 /* maximum size of a label */
154 #define DISP_TEXT_SIZE 512
156 /* padding for items in list and small icon display modes */
157 #define WIDTH_PADDING 12
159 /* padding for items in list, report and small icon display modes */
160 #define HEIGHT_PADDING 1
162 /* offset of items in report display mode */
163 #define REPORT_MARGINX 2
165 /* padding for icon in large icon display mode
166 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
167 * that HITTEST will see.
168 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
169 * ICON_TOP_PADDING - sum of the two above.
170 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
171 * LABEL_VERT_OFFSET - between bottom of text and end of box
173 #define ICON_TOP_PADDING_NOTHITABLE 2
174 #define ICON_TOP_PADDING_HITABLE 2
175 #define ICON_TOP_PADDING ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE
176 #define ICON_BOTTOM_PADDING 4
177 #define LABEL_VERT_OFFSET 10
179 /* default label width for items in list and small icon display modes */
180 #define DEFAULT_LABEL_WIDTH 40
182 /* default column width for items in list display mode */
183 #define DEFAULT_COLUMN_WIDTH 96
185 /* Increment size of the horizontal scroll bar */
186 #define LISTVIEW_SCROLL_DIV_SIZE 10
188 /* Padding betwen image and label */
189 #define IMAGE_PADDING 2
191 /* Padding behind the label */
192 #define TRAILING_PADDING 5
194 /* Border for the icon caption */
195 #define CAPTION_BORDER 2
199 /* retrieve the number of items in the listview */
200 #define GETITEMCOUNT(infoPtr) ((infoPtr)->hdpaItems->nItemCount)
201 #define HDM_INSERTITEMT(isW) ( (isW) ? HDM_INSERTITEMW : HDM_INSERTITEMA )
203 HWND CreateEditLabelT(LPCWSTR text, DWORD style, INT x, INT y,
204 INT width, INT height, HWND parent, HINSTANCE hinst,
205 EditlblCallbackW EditLblCb, DWORD param, BOOL isW);
208 * forward declarations
210 static LRESULT LISTVIEW_GetItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL internal, BOOL isW);
211 static INT LISTVIEW_SuperHitTestItem(HWND, LPLV_INTHIT, BOOL);
212 static INT LISTVIEW_HitTestItem(HWND, LPLVHITTESTINFO, BOOL);
213 static INT LISTVIEW_GetCountPerRow(HWND);
214 static INT LISTVIEW_GetCountPerColumn(HWND);
215 static VOID LISTVIEW_AlignLeft(HWND);
216 static VOID LISTVIEW_AlignTop(HWND);
217 static VOID LISTVIEW_AddGroupSelection(HWND, INT);
218 static VOID LISTVIEW_AddSelection(HWND, INT);
219 static BOOL LISTVIEW_AddSubItemT(HWND, LPLVITEMW, BOOL);
220 static INT LISTVIEW_FindInsertPosition(HDPA, INT);
221 static INT LISTVIEW_GetItemHeight(HWND);
222 static BOOL LISTVIEW_GetItemBoundBox(HWND, INT, LPRECT);
223 static BOOL LISTVIEW_GetItemPosition(HWND, INT, LPPOINT);
224 static LRESULT LISTVIEW_GetItemRect(HWND, INT, LPRECT);
225 static INT LISTVIEW_GetItemWidth(HWND);
226 static INT LISTVIEW_GetLabelWidth(HWND, INT);
227 static LRESULT LISTVIEW_GetOrigin(HWND, LPPOINT);
228 static INT LISTVIEW_CalculateWidth(HWND hwnd, INT nItem);
229 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItem(HDPA, INT);
230 static LRESULT LISTVIEW_GetViewRect(HWND, LPRECT);
231 static BOOL LISTVIEW_InitItemT(HWND, LISTVIEW_ITEM *, LPLVITEMW, BOOL);
232 static BOOL LISTVIEW_InitSubItemT(HWND, LISTVIEW_SUBITEM *, LPLVITEMW, BOOL);
233 static LRESULT LISTVIEW_MouseSelection(HWND, POINT);
234 static BOOL LISTVIEW_RemoveColumn(HDPA, INT);
235 static BOOL LISTVIEW_RemoveSubItem(HDPA, INT);
236 static VOID LISTVIEW_SetGroupSelection(HWND, INT);
237 static BOOL LISTVIEW_SetItemT(HWND, LPLVITEMW, BOOL);
238 static BOOL LISTVIEW_SetItemFocus(HWND, INT);
239 static BOOL LISTVIEW_SetItemPosition(HWND, INT, LONG, LONG);
240 static VOID LISTVIEW_UpdateScroll(HWND);
241 static VOID LISTVIEW_SetSelection(HWND, INT);
242 static VOID LISTVIEW_UpdateSize(HWND);
243 static BOOL LISTVIEW_SetSubItemT(HWND, LPLVITEMW, BOOL);
244 static LRESULT LISTVIEW_SetViewRect(HWND, LPRECT);
245 static BOOL LISTVIEW_ToggleSelection(HWND, INT);
246 static VOID LISTVIEW_UnsupportedStyles(LONG lStyle);
247 static HWND LISTVIEW_EditLabelT(HWND hwnd, INT nItem, BOOL isW);
248 static BOOL LISTVIEW_EndEditLabelW(HWND hwnd, LPWSTR pszText, DWORD nItem);
249 static BOOL LISTVIEW_EndEditLabelA(HWND hwnd, LPSTR pszText, DWORD nItem);
250 static LRESULT LISTVIEW_Command(HWND hwnd, WPARAM wParam, LPARAM lParam);
251 static LRESULT LISTVIEW_SortItems(HWND hwnd, PFNLVCOMPARE pfnCompare, LPARAM lParamSort);
252 static LRESULT LISTVIEW_GetStringWidthT(HWND hwnd, LPCWSTR lpszText, BOOL isW);
253 static INT LISTVIEW_ProcessLetterKeys( HWND hwnd, WPARAM charCode, LPARAM keyData );
254 static BOOL LISTVIEW_KeySelection(HWND hwnd, INT nItem);
255 static LRESULT LISTVIEW_GetItemState(HWND hwnd, INT nItem, UINT uMask);
256 static LRESULT LISTVIEW_SetItemState(HWND hwnd, INT nItem, LPLVITEMW lpLVItem);
257 static BOOL LISTVIEW_IsSelected(HWND hwnd, INT nItem);
258 static VOID LISTVIEW_RemoveSelectionRange(HWND hwnd, INT lItem, INT uItem);
259 static void LISTVIEW_FillBackground(HWND hwnd, HDC hdc, LPRECT rc);
260 static void ListView_UpdateLargeItemLabelRect (HWND hwnd, const LISTVIEW_INFO* infoPtr, int nItem, RECT *rect);
262 /******** Defines that LISTVIEW_ProcessLetterKeys uses ****************/
263 #define KEY_DELAY 450
265 #define COUNTOF(array) (sizeof(array)/sizeof(array[0]))
267 static inline BOOL is_textW(LPCWSTR text)
269 return text != NULL && text != LPSTR_TEXTCALLBACKW;
272 static inline BOOL is_textT(LPCWSTR text, BOOL isW)
274 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
275 return is_textW(text);
278 static inline int textlenT(LPCWSTR text, BOOL isW)
280 return !is_textT(text, isW) ? 0 :
281 isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
284 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
287 if (isSrcW) lstrcpynW(dest, src, max);
288 else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
290 if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
291 else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
294 static inline LPCSTR debugstr_t(LPCWSTR text, BOOL isW)
296 return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
299 static inline LPCSTR debugstr_tn(LPCWSTR text, BOOL isW, INT n)
301 return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
304 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
306 LPWSTR wstr = (LPWSTR)text;
308 TRACE("(text=%s, isW=%d)\n", debugstr_t(text, isW), isW);
311 INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
312 wstr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
313 if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
315 TRACE(" wstr=%s\n", debugstr_w(wstr));
319 static inline void textfreeT(LPWSTR wstr, BOOL isW)
321 if (!isW && wstr) HeapFree(GetProcessHeap(), 0, wstr);
325 * dest is a pointer to a Unicode string
326 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
328 static inline BOOL textsetptrT(LPWSTR *dest, LPWSTR src, BOOL isW)
330 LPWSTR pszText = textdupTtoW(src, isW);
332 if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
333 bResult = Str_SetPtrW(dest, pszText);
334 textfreeT(pszText, isW);
338 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
339 WPARAM wParam, LPARAM lParam, BOOL isW)
342 return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
344 return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
347 static inline BOOL notify(HWND self, INT code, LPNMHDR pnmh)
349 pnmh->hwndFrom = self;
350 pnmh->idFrom = GetWindowLongW(self, GWL_ID);
352 return (BOOL)SendMessageW(GetParent(self), WM_NOTIFY,
353 (WPARAM)pnmh->idFrom, (LPARAM)pnmh);
356 static inline BOOL hdr_notify(HWND self, INT code)
359 return notify(self, code, &nmh);
362 static inline BOOL listview_notify(HWND self, INT code, LPNMLISTVIEW plvnm)
364 return notify(self, code, (LPNMHDR)plvnm);
367 static int tabNotification[] = {
368 LVN_BEGINLABELEDITW, LVN_BEGINLABELEDITA,
369 LVN_ENDLABELEDITW, LVN_ENDLABELEDITA,
370 LVN_GETDISPINFOW, LVN_GETDISPINFOA,
371 LVN_SETDISPINFOW, LVN_SETDISPINFOA,
372 LVN_ODFINDITEMW, LVN_ODFINDITEMA,
373 LVN_GETINFOTIPW, LVN_GETINFOTIPA,
377 static int get_ansi_notification(INT unicodeNotificationCode)
379 int *pTabNotif = tabNotification;
380 while (*pTabNotif && (unicodeNotificationCode != *pTabNotif++));
381 if (*pTabNotif) return *pTabNotif;
382 ERR("unknown notification %x\n", unicodeNotificationCode);
383 return unicodeNotificationCode;
387 Send notification. depends on dispinfoW having same
388 structure as dispinfoA.
389 self : listview handle
390 notificationCode : *Unicode* notification code
391 pdi : dispinfo structure (can be unicode or ansi)
392 isW : TRUE if dispinfo is Unicode
394 static BOOL dispinfo_notifyT(HWND self, INT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
396 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(self, 0);
397 BOOL bResult = FALSE;
398 BOOL convertToAnsi = FALSE, convertToUnicode = FALSE;
400 INT cchTempBufMax = 0, savCchTextMax = 0;
401 LPWSTR pszTempBuf = NULL, savPszText = NULL;
403 TRACE("(self=%x, code=%x, pdi=%p, isW=%d)\n", self, notificationCode, pdi, isW);
404 TRACE(" notifyFormat=%s\n",
405 infoPtr->notifyFormat == NFR_UNICODE ? "NFR_UNICODE" :
406 infoPtr->notifyFormat == NFR_ANSI ? "NFR_ANSI" : "(not set)");
407 if (infoPtr->notifyFormat == NFR_ANSI)
408 realNotifCode = get_ansi_notification(notificationCode);
410 realNotifCode = notificationCode;
412 if (is_textT(pdi->item.pszText, isW))
414 if (isW && infoPtr->notifyFormat == NFR_ANSI)
415 convertToAnsi = TRUE;
416 if (!isW && infoPtr->notifyFormat == NFR_UNICODE)
417 convertToUnicode = TRUE;
420 if (convertToAnsi || convertToUnicode)
422 TRACE(" we have to convert the text to the correct format\n");
423 if (notificationCode != LVN_GETDISPINFOW)
424 { /* length of existing text */
425 cchTempBufMax = convertToUnicode ?
426 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0):
427 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
430 cchTempBufMax = pdi->item.cchTextMax;
432 pszTempBuf = HeapAlloc(GetProcessHeap(), 0,
433 (convertToUnicode ? sizeof(WCHAR) : sizeof(CHAR)) * cchTempBufMax);
434 if (!pszTempBuf) return FALSE;
435 if (convertToUnicode)
436 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1,
437 pszTempBuf, cchTempBufMax);
439 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) pszTempBuf,
440 cchTempBufMax, NULL, NULL);
441 TRACE(" text=%s\n", debugstr_t(pszTempBuf, convertToUnicode));
442 savCchTextMax = pdi->item.cchTextMax;
443 savPszText = pdi->item.pszText;
444 pdi->item.pszText = pszTempBuf;
445 pdi->item.cchTextMax = cchTempBufMax;
448 bResult = notify(self, realNotifCode, (LPNMHDR)pdi);
450 if (convertToUnicode || convertToAnsi)
451 { /* convert back result */
452 TRACE(" returned text=%s\n", debugstr_t(pdi->item.pszText, convertToUnicode));
453 if (convertToUnicode) /* note : pointer can be changed by app ! */
454 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) savPszText,
455 savCchTextMax, NULL, NULL);
457 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
458 savPszText, savCchTextMax);
459 pdi->item.pszText = savPszText; /* restores our buffer */
460 pdi->item.cchTextMax = savCchTextMax;
461 HeapFree(GetProcessHeap(), 0, pszTempBuf);
466 static inline LRESULT LISTVIEW_GetItemW(HWND hwnd, LPLVITEMW lpLVItem, BOOL internal)
468 return LISTVIEW_GetItemT(hwnd, lpLVItem, internal, TRUE);
471 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
475 n = min(min(n, strlenW(s1)), strlenW(s2));
476 res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
477 return res ? res - 2 : res;
480 static char* debuglvitem_t(LPLVITEMW lpLVItem, BOOL isW)
482 static int index = 0;
483 static char buffers[20][256];
484 char* buf = buffers[index++ % 20];
485 if (lpLVItem == NULL) return "(null)";
486 snprintf(buf, 256, "{mask=%x, iItem=%d, iSubItem=%d, state=%x, stateMask=%x,"
487 " pszText=%s, cchTextMax=%d, iImage=%d, lParam=%lx, iIndent=%d}",
488 lpLVItem->mask, lpLVItem->iItem, lpLVItem->iSubItem,
489 lpLVItem->state, lpLVItem->stateMask,
490 lpLVItem->pszText == LPSTR_TEXTCALLBACKW ? "(callback)" :
491 debugstr_tn(lpLVItem->pszText, isW, 80),
492 lpLVItem->cchTextMax, lpLVItem->iImage, lpLVItem->lParam,
497 static char* debuglvcolumn_t(LPLVCOLUMNW lpColumn, BOOL isW)
499 static int index = 0;
500 static char buffers[20][256];
501 char* buf = buffers[index++ % 20];
502 if (lpColumn == NULL) return "(null)";
503 snprintf(buf, 256, "{mask=%x, fmt=%x, cx=%d,"
504 " pszText=%s, cchTextMax=%d, iSubItem=%d}",
505 lpColumn->mask, lpColumn->fmt, lpColumn->cx,
506 lpColumn->mask & LVCF_TEXT ? lpColumn->pszText == LPSTR_TEXTCALLBACKW ? "(callback)" :
507 debugstr_tn(lpColumn->pszText, isW, 80): "",
508 lpColumn->mask & LVCF_TEXT ? lpColumn->cchTextMax: 0, lpColumn->iSubItem);
512 static void LISTVIEW_DumpListview(LISTVIEW_INFO *iP, INT line)
514 DWORD dwStyle = GetWindowLongW (iP->hwndSelf, GWL_STYLE);
515 TRACE("listview %08x at line %d, clrBk=0x%06lx, clrText=0x%06lx, clrTextBk=0x%06lx, ItemHeight=%d, ItemWidth=%d, Style=0x%08lx\n",
516 iP->hwndSelf, line, iP->clrBk, iP->clrText, iP->clrTextBk,
517 iP->nItemHeight, iP->nItemWidth, dwStyle);
518 TRACE("listview %08x at line %d, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08lx\n",
519 iP->hwndSelf, line, iP->himlNormal, iP->himlSmall, iP->himlState,
520 iP->nFocusedItem, iP->nHotItem, iP->dwExStyle);
524 LISTVIEW_SendCustomDrawNotify (HWND hwnd, DWORD dwDrawStage, HDC hdc,
527 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
528 NMLVCUSTOMDRAW nmcdhdr;
531 TRACE("(hwnd=%x, dwDrawStage=%lx, hdc=%x, rc=?)\n", hwnd, dwDrawStage, hdc);
533 nmcd= & nmcdhdr.nmcd;
534 nmcd->hdr.hwndFrom = hwnd;
535 nmcd->hdr.idFrom = GetWindowLongW( hwnd, GWL_ID);
536 nmcd->hdr.code = NM_CUSTOMDRAW;
537 nmcd->dwDrawStage= dwDrawStage;
539 nmcd->rc.left = rc.left;
540 nmcd->rc.right = rc.right;
541 nmcd->rc.bottom = rc.bottom;
542 nmcd->rc.top = rc.top;
543 nmcd->dwItemSpec = 0;
544 nmcd->uItemState = 0;
545 nmcd->lItemlParam= 0;
546 nmcdhdr.clrText = infoPtr->clrText;
547 nmcdhdr.clrTextBk= infoPtr->clrBk;
549 return (BOOL)SendMessageW (GetParent (hwnd), WM_NOTIFY,
550 (WPARAM) GetWindowLongW( hwnd, GWL_ID), (LPARAM)&nmcdhdr);
554 LISTVIEW_SendCustomDrawItemNotify (HWND hwnd, HDC hdc,
555 UINT iItem, UINT iSubItem,
558 LISTVIEW_INFO *infoPtr;
559 NMLVCUSTOMDRAW nmcdhdr;
561 DWORD dwDrawStage,dwItemSpec;
567 infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
569 ZeroMemory(&item,sizeof(item));
571 item.mask = LVIF_PARAM;
572 ListView_GetItemW(hwnd,&item);
574 dwDrawStage=CDDS_ITEM | uItemDrawState;
578 if (LISTVIEW_IsSelected(hwnd,iItem)) uItemState|=CDIS_SELECTED;
579 if (iItem==infoPtr->nFocusedItem) uItemState|=CDIS_FOCUS;
580 if (iItem==infoPtr->nHotItem) uItemState|=CDIS_HOT;
582 itemRect.left = LVIR_BOUNDS;
583 LISTVIEW_GetItemRect(hwnd, iItem, &itemRect);
585 nmcd= & nmcdhdr.nmcd;
586 nmcd->hdr.hwndFrom = hwnd;
587 nmcd->hdr.idFrom = GetWindowLongW( hwnd, GWL_ID);
588 nmcd->hdr.code = NM_CUSTOMDRAW;
589 nmcd->dwDrawStage= dwDrawStage;
591 nmcd->rc.left = itemRect.left;
592 nmcd->rc.right = itemRect.right;
593 nmcd->rc.bottom = itemRect.bottom;
594 nmcd->rc.top = itemRect.top;
595 nmcd->dwItemSpec = dwItemSpec;
596 nmcd->uItemState = uItemState;
597 nmcd->lItemlParam= item.lParam;
598 nmcdhdr.clrText = infoPtr->clrText;
599 nmcdhdr.clrTextBk= infoPtr->clrBk;
600 nmcdhdr.iSubItem =iSubItem;
602 TRACE("drawstage=%lx hdc=%x item=%lx, itemstate=%x, lItemlParam=%lx\n",
603 nmcd->dwDrawStage, nmcd->hdc, nmcd->dwItemSpec,
604 nmcd->uItemState, nmcd->lItemlParam);
606 retval=SendMessageW (GetParent (hwnd), WM_NOTIFY,
607 (WPARAM) GetWindowLongW( hwnd, GWL_ID), (LPARAM)&nmcdhdr);
609 infoPtr->clrText=nmcdhdr.clrText;
610 infoPtr->clrBk =nmcdhdr.clrTextBk;
611 return (BOOL) retval;
615 /*************************************************************************
616 * LISTVIEW_ProcessLetterKeys
618 * Processes keyboard messages generated by pressing the letter keys
620 * What this does is perform a case insensitive search from the
621 * current position with the following quirks:
622 * - If two chars or more are pressed in quick succession we search
623 * for the corresponding string (e.g. 'abc').
624 * - If there is a delay we wipe away the current search string and
625 * restart with just that char.
626 * - If the user keeps pressing the same character, whether slowly or
627 * fast, so that the search string is entirely composed of this
628 * character ('aaaaa' for instance), then we search for first item
629 * that starting with that character.
630 * - If the user types the above character in quick succession, then
631 * we must also search for the corresponding string ('aaaaa'), and
632 * go to that string if there is a match.
640 * - The current implementation has a list of characters it will
641 * accept and it ignores averything else. In particular it will
642 * ignore accentuated characters which seems to match what
643 * Windows does. But I'm not sure it makes sense to follow
645 * - We don't sound a beep when the search fails.
649 * TREEVIEW_ProcessLetterKeys
651 static INT LISTVIEW_ProcessLetterKeys(
652 HWND hwnd, /* handle to the window */
653 WPARAM charCode, /* the character code, the actual character */
654 LPARAM keyData /* key data */
657 LISTVIEW_INFO *infoPtr;
662 WCHAR buffer[MAX_PATH];
663 DWORD timestamp,elapsed;
665 /* simple parameter checking */
666 if (!hwnd || !charCode || !keyData)
669 infoPtr=(LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
673 /* only allow the valid WM_CHARs through */
674 if (!isalnum(charCode) &&
675 charCode != '.' && charCode != '`' && charCode != '!' &&
676 charCode != '@' && charCode != '#' && charCode != '$' &&
677 charCode != '%' && charCode != '^' && charCode != '&' &&
678 charCode != '*' && charCode != '(' && charCode != ')' &&
679 charCode != '-' && charCode != '_' && charCode != '+' &&
680 charCode != '=' && charCode != '\\'&& charCode != ']' &&
681 charCode != '}' && charCode != '[' && charCode != '{' &&
682 charCode != '/' && charCode != '?' && charCode != '>' &&
683 charCode != '<' && charCode != ',' && charCode != '~')
686 nSize=GETITEMCOUNT(infoPtr);
687 /* if there's one item or less, there is no where to go */
691 /* compute how much time elapsed since last keypress */
692 timestamp=GetTickCount();
693 if (timestamp > infoPtr->lastKeyPressTimestamp) {
694 elapsed=timestamp-infoPtr->lastKeyPressTimestamp;
696 elapsed=infoPtr->lastKeyPressTimestamp-timestamp;
699 /* update the search parameters */
700 infoPtr->lastKeyPressTimestamp=timestamp;
701 if (elapsed < KEY_DELAY) {
702 if (infoPtr->nSearchParamLength < COUNTOF(infoPtr->szSearchParam)) {
703 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
705 if (infoPtr->charCode != charCode) {
706 infoPtr->charCode=charCode=0;
709 infoPtr->charCode=charCode;
710 infoPtr->szSearchParam[0]=charCode;
711 infoPtr->nSearchParamLength=1;
712 /* Redundant with the 1 char string */
716 /* and search from the current position */
718 if (infoPtr->nFocusedItem >= 0) {
719 endidx=infoPtr->nFocusedItem;
721 /* if looking for single character match,
722 * then we must always move forward
724 if (infoPtr->nSearchParamLength == 1)
738 ZeroMemory(&item, sizeof(item));
739 item.mask = LVIF_TEXT;
742 item.pszText = buffer;
743 item.cchTextMax = COUNTOF(buffer);
744 ListView_GetItemW( hwnd, &item );
746 /* check for a match */
747 if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
750 } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
751 (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
752 /* This would work but we must keep looking for a longer match */
756 } while (idx != endidx);
759 if (LISTVIEW_KeySelection(hwnd, nItem) != FALSE) {
760 /* refresh client area */
761 InvalidateRect(hwnd, NULL, TRUE);
769 /*************************************************************************
770 * LISTVIEW_UpdateHeaderSize [Internal]
772 * Function to resize the header control
775 * hwnd [I] handle to a window
776 * nNewScrollPos [I] Scroll Pos to Set
783 static VOID LISTVIEW_UpdateHeaderSize(HWND hwnd, INT nNewScrollPos)
785 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
789 GetWindowRect(infoPtr->hwndHeader, &winRect);
790 point[0].x = winRect.left;
791 point[0].y = winRect.top;
792 point[1].x = winRect.right;
793 point[1].y = winRect.bottom;
795 MapWindowPoints(HWND_DESKTOP, hwnd, point, 2);
796 point[0].x = -(nNewScrollPos * LISTVIEW_SCROLL_DIV_SIZE);
797 point[1].x += (nNewScrollPos * LISTVIEW_SCROLL_DIV_SIZE);
799 SetWindowPos(infoPtr->hwndHeader,0,
800 point[0].x,point[0].y,point[1].x,point[1].y,
801 SWP_NOZORDER | SWP_NOACTIVATE);
806 * Update the scrollbars. This functions should be called whenever
807 * the content, size or view changes.
810 * [I] HWND : window handle
815 static VOID LISTVIEW_UpdateScroll(HWND hwnd)
817 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
818 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
819 UINT uView = lStyle & LVS_TYPEMASK;
820 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
821 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
822 SCROLLINFO scrollInfo;
824 if (lStyle & LVS_NOSCROLL) return;
826 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
827 scrollInfo.cbSize = sizeof(SCROLLINFO);
829 if (uView == LVS_LIST)
831 /* update horizontal scrollbar */
833 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
834 INT nCountPerRow = LISTVIEW_GetCountPerRow(hwnd);
835 INT nNumOfItems = GETITEMCOUNT(infoPtr);
837 scrollInfo.nMax = nNumOfItems / nCountPerColumn;
838 if((nNumOfItems % nCountPerColumn) == 0)
842 scrollInfo.nPos = ListView_GetTopIndex(hwnd) / nCountPerColumn;
843 scrollInfo.nPage = nCountPerRow;
844 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
845 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
846 ShowScrollBar(hwnd, SB_VERT, FALSE);
848 else if (uView == LVS_REPORT)
850 /* update vertical scrollbar */
852 scrollInfo.nMax = GETITEMCOUNT(infoPtr) - 1;
853 scrollInfo.nPos = ListView_GetTopIndex(hwnd);
854 scrollInfo.nPage = LISTVIEW_GetCountPerColumn(hwnd);
855 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
856 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
858 /* update horizontal scrollbar */
859 nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
860 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) == FALSE
861 || GETITEMCOUNT(infoPtr) == 0)
866 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE ;
867 scrollInfo.nPage = nListWidth / LISTVIEW_SCROLL_DIV_SIZE;
868 scrollInfo.nMax = max(infoPtr->nItemWidth / LISTVIEW_SCROLL_DIV_SIZE, 0)-1;
869 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
871 /* Update the Header Control */
872 scrollInfo.fMask = SIF_POS;
873 GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
874 LISTVIEW_UpdateHeaderSize(hwnd, scrollInfo.nPos);
881 if (LISTVIEW_GetViewRect(hwnd, &rcView) != FALSE)
883 INT nViewWidth = rcView.right - rcView.left;
884 INT nViewHeight = rcView.bottom - rcView.top;
886 /* Update Horizontal Scrollbar */
887 scrollInfo.fMask = SIF_POS;
888 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) == FALSE
889 || GETITEMCOUNT(infoPtr) == 0)
893 scrollInfo.nMax = max(nViewWidth / LISTVIEW_SCROLL_DIV_SIZE, 0)-1;
895 scrollInfo.nPage = nListWidth / LISTVIEW_SCROLL_DIV_SIZE;
896 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
897 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
899 /* Update Vertical Scrollbar */
900 nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
901 scrollInfo.fMask = SIF_POS;
902 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) == FALSE
903 || GETITEMCOUNT(infoPtr) == 0)
907 scrollInfo.nMax = max(nViewHeight / LISTVIEW_SCROLL_DIV_SIZE,0)-1;
909 scrollInfo.nPage = nListHeight / LISTVIEW_SCROLL_DIV_SIZE;
910 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
911 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
918 * Prints a message for unsupported window styles.
919 * A kind of TODO list for window styles.
922 * [I] LONG : window style
927 static VOID LISTVIEW_UnsupportedStyles(LONG lStyle)
929 if ((LVS_TYPESTYLEMASK & lStyle) == LVS_NOSCROLL)
930 FIXME(" LVS_NOSCROLL\n");
932 if ((LVS_TYPESTYLEMASK & lStyle) == LVS_NOSORTHEADER)
933 FIXME(" LVS_NOSORTHEADER\n");
935 if (lStyle & LVS_EDITLABELS)
936 FIXME(" LVS_EDITLABELS\n");
938 if (lStyle & LVS_NOLABELWRAP)
939 FIXME(" LVS_NOLABELWRAP\n");
941 if (lStyle & LVS_SHAREIMAGELISTS)
942 FIXME(" LVS_SHAREIMAGELISTS\n");
944 if (lStyle & LVS_SORTASCENDING)
945 FIXME(" LVS_SORTASCENDING\n");
947 if (lStyle & LVS_SORTDESCENDING)
948 FIXME(" LVS_SORTDESCENDING\n");
953 * Aligns the items with the top edge of the window.
956 * [I] HWND : window handle
961 static VOID LISTVIEW_AlignTop(HWND hwnd)
963 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
964 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
965 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
968 INT i, off_x=0, off_y=0;
970 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
972 /* Since SetItemPosition uses upper-left of icon, and for
973 style=LVS_ICON the icon is not left adjusted, get the offset */
974 if (uView == LVS_ICON)
976 off_y = ICON_TOP_PADDING;
977 off_x = (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
981 ZeroMemory(&rcView, sizeof(RECT));
983 if (nListWidth > infoPtr->nItemWidth)
985 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
987 if (ptItem.x + infoPtr->nItemWidth > nListWidth)
990 ptItem.y += infoPtr->nItemHeight;
993 LISTVIEW_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
994 ptItem.x += infoPtr->nItemWidth;
995 rcView.right = max(rcView.right, ptItem.x);
998 rcView.bottom = ptItem.y + infoPtr->nItemHeight;
1002 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1004 LISTVIEW_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
1005 ptItem.y += infoPtr->nItemHeight;
1008 rcView.right = infoPtr->nItemWidth;
1009 rcView.bottom = ptItem.y;
1012 LISTVIEW_SetViewRect(hwnd, &rcView);
1018 * Aligns the items with the left edge of the window.
1021 * [I] HWND : window handle
1026 static VOID LISTVIEW_AlignLeft(HWND hwnd)
1028 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1029 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
1030 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1033 INT i, off_x=0, off_y=0;
1035 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1037 /* Since SetItemPosition uses upper-left of icon, and for
1038 style=LVS_ICON the icon is not left adjusted, get the offset */
1039 if (uView == LVS_ICON)
1041 off_y = ICON_TOP_PADDING;
1042 off_x = (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
1046 ZeroMemory(&rcView, sizeof(RECT));
1048 if (nListHeight > infoPtr->nItemHeight)
1050 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1052 if (ptItem.y + infoPtr->nItemHeight > nListHeight)
1055 ptItem.x += infoPtr->nItemWidth;
1058 LISTVIEW_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
1059 ptItem.y += infoPtr->nItemHeight;
1060 rcView.bottom = max(rcView.bottom, ptItem.y);
1063 rcView.right = ptItem.x + infoPtr->nItemWidth;
1067 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1069 LISTVIEW_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
1070 ptItem.x += infoPtr->nItemWidth;
1073 rcView.bottom = infoPtr->nItemHeight;
1074 rcView.right = ptItem.x;
1077 LISTVIEW_SetViewRect(hwnd, &rcView);
1083 * Set the bounding rectangle of all the items.
1086 * [I] HWND : window handle
1087 * [I] LPRECT : bounding rectangle
1093 static LRESULT LISTVIEW_SetViewRect(HWND hwnd, LPRECT lprcView)
1095 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
1096 BOOL bResult = FALSE;
1098 TRACE("(hwnd=%x, left=%d, top=%d, right=%d, bottom=%d)\n", hwnd,
1099 lprcView->left, lprcView->top, lprcView->right, lprcView->bottom);
1101 if (lprcView != NULL)
1104 infoPtr->rcView.left = lprcView->left;
1105 infoPtr->rcView.top = lprcView->top;
1106 infoPtr->rcView.right = lprcView->right;
1107 infoPtr->rcView.bottom = lprcView->bottom;
1115 * Retrieves the bounding rectangle of all the items.
1118 * [I] HWND : window handle
1119 * [O] LPRECT : bounding rectangle
1125 static LRESULT LISTVIEW_GetViewRect(HWND hwnd, LPRECT lprcView)
1127 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
1128 BOOL bResult = FALSE;
1131 TRACE("(hwnd=%x, lprcView=%p)\n", hwnd, lprcView);
1133 if (lprcView != NULL)
1135 bResult = LISTVIEW_GetOrigin(hwnd, &ptOrigin);
1136 if (bResult != FALSE)
1138 lprcView->left = infoPtr->rcView.left + ptOrigin.x;
1139 lprcView->top = infoPtr->rcView.top + ptOrigin.y;
1140 lprcView->right = infoPtr->rcView.right + ptOrigin.x;
1141 lprcView->bottom = infoPtr->rcView.bottom + ptOrigin.y;
1144 TRACE("(left=%d, top=%d, right=%d, bottom=%d)\n",
1145 lprcView->left, lprcView->top, lprcView->right, lprcView->bottom);
1153 * Retrieves the subitem pointer associated with the subitem index.
1156 * [I] HDPA : DPA handle for a specific item
1157 * [I] INT : index of subitem
1160 * SUCCESS : subitem pointer
1163 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems,
1166 LISTVIEW_SUBITEM *lpSubItem;
1169 for (i = 1; i < hdpaSubItems->nItemCount; i++)
1171 lpSubItem = (LISTVIEW_SUBITEM *) DPA_GetPtr(hdpaSubItems, i);
1172 if (lpSubItem != NULL)
1174 if (lpSubItem->iSubItem == nSubItem)
1186 * Calculates the width of an item.
1189 * [I] HWND : window handle
1190 * [I] LONG : window style
1193 * Returns item width.
1195 static INT LISTVIEW_GetItemWidth(HWND hwnd)
1197 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1198 LONG style = GetWindowLongW(hwnd, GWL_STYLE);
1199 UINT uView = style & LVS_TYPEMASK;
1200 INT nHeaderItemCount;
1206 TRACE("(hwnd=%x)\n", hwnd);
1208 if (uView == LVS_ICON)
1210 nItemWidth = infoPtr->iconSpacing.cx;
1212 else if (uView == LVS_REPORT)
1214 /* calculate width of header */
1215 nHeaderItemCount = Header_GetItemCount(infoPtr->hwndHeader);
1216 for (i = 0; i < nHeaderItemCount; i++)
1218 if (Header_GetItemRect(infoPtr->hwndHeader, i, &rcHeaderItem) != 0)
1220 nItemWidth += (rcHeaderItem.right - rcHeaderItem.left);
1226 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1228 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, i);
1229 nItemWidth = max(nItemWidth, nLabelWidth);
1232 /* default label size */
1233 if (GETITEMCOUNT(infoPtr) == 0)
1235 nItemWidth = DEFAULT_COLUMN_WIDTH;
1239 if (nItemWidth == 0)
1241 nItemWidth = DEFAULT_LABEL_WIDTH;
1246 nItemWidth += WIDTH_PADDING;
1248 if (infoPtr->himlSmall != NULL)
1250 nItemWidth += infoPtr->iconSize.cx;
1253 if (infoPtr->himlState != NULL)
1255 nItemWidth += infoPtr->iconSize.cx;
1262 /* nItemWidth Cannot be Zero */
1270 * Calculates the width of a specific item.
1273 * [I] HWND : window handle
1274 * [I] LPSTR : string
1277 * Returns the width of an item width a specified string.
1279 static INT LISTVIEW_CalculateWidth(HWND hwnd, INT nItem)
1281 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1282 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
1283 INT nHeaderItemCount;
1288 TRACE("(hwnd=%x)\n", hwnd);
1290 if (uView == LVS_ICON)
1292 nItemWidth = infoPtr->iconSpacing.cx;
1294 else if (uView == LVS_REPORT)
1296 /* calculate width of header */
1297 nHeaderItemCount = Header_GetItemCount(infoPtr->hwndHeader);
1298 for (i = 0; i < nHeaderItemCount; i++)
1300 if (Header_GetItemRect(infoPtr->hwndHeader, i, &rcHeaderItem) != 0)
1302 nItemWidth += (rcHeaderItem.right - rcHeaderItem.left);
1308 /* get width of string */
1309 nItemWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
1311 /* default label size */
1312 if (GETITEMCOUNT(infoPtr) == 0)
1314 nItemWidth = DEFAULT_COLUMN_WIDTH;
1318 if (nItemWidth == 0)
1320 nItemWidth = DEFAULT_LABEL_WIDTH;
1325 nItemWidth += WIDTH_PADDING;
1327 if (infoPtr->himlSmall != NULL)
1329 nItemWidth += infoPtr->iconSize.cx;
1332 if (infoPtr->himlState != NULL)
1334 nItemWidth += infoPtr->iconSize.cx;
1345 * Retrieves and saves important text metrics info for the current
1349 * [I] HWND : window handle
1352 static VOID LISTVIEW_SaveTextMetrics(HWND hwnd)
1354 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1356 HDC hdc = GetDC(hwnd);
1357 HFONT hOldFont = SelectObject(hdc, infoPtr->hFont);
1358 INT oldHeight, oldACW;
1360 GetTextMetricsW(hdc, &tm);
1362 oldHeight = infoPtr->ntmHeight;
1363 oldACW = infoPtr->ntmAveCharWidth;
1364 infoPtr->ntmHeight = tm.tmHeight;
1365 infoPtr->ntmAveCharWidth = tm.tmAveCharWidth;
1367 SelectObject(hdc, hOldFont);
1368 ReleaseDC(hwnd, hdc);
1369 TRACE("tmHeight old=%d,new=%d; tmAveCharWidth old=%d,new=%d\n",
1370 oldHeight, infoPtr->ntmHeight, oldACW, infoPtr->ntmAveCharWidth);
1376 * Calculates the height of an item.
1379 * [I] HWND : window handle
1382 * Returns item height.
1384 static INT LISTVIEW_GetItemHeight(HWND hwnd)
1386 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1387 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
1388 INT nItemHeight = 0;
1390 if (uView == LVS_ICON)
1392 nItemHeight = infoPtr->iconSpacing.cy;
1396 if(infoPtr->himlState || infoPtr->himlSmall)
1397 nItemHeight = max(infoPtr->ntmHeight, infoPtr->iconSize.cy) + HEIGHT_PADDING;
1399 nItemHeight = infoPtr->ntmHeight;
1406 static void LISTVIEW_PrintSelectionRanges(HWND hwnd)
1408 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1409 LISTVIEW_SELECTION *selection;
1410 INT topSelection = infoPtr->hdpaSelectionRanges->nItemCount;
1413 TRACE("Selections are:\n");
1414 for (i = 0; i < topSelection; i++)
1416 selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,i);
1417 TRACE(" %lu - %lu\n",selection->lower,selection->upper);
1423 * A compare function for selection ranges
1426 * [I] LPVOID : Item 1;
1427 * [I] LPVOID : Item 2;
1428 * [I] LPARAM : flags
1431 * >0 : if Item 1 > Item 2
1432 * <0 : if Item 2 > Item 1
1433 * 0 : if Item 1 == Item 2
1435 static INT CALLBACK LISTVIEW_CompareSelectionRanges(LPVOID range1, LPVOID range2,
1438 int l1 = ((LISTVIEW_SELECTION*)(range1))->lower;
1439 int l2 = ((LISTVIEW_SELECTION*)(range2))->lower;
1440 int u1 = ((LISTVIEW_SELECTION*)(range1))->upper;
1441 int u2 = ((LISTVIEW_SELECTION*)(range2))->upper;
1455 * Adds a selection range.
1458 * [I] HWND : window handle
1459 * [I] INT : lower item index
1460 * [I] INT : upper item index
1465 static VOID LISTVIEW_AddSelectionRange(HWND hwnd, INT lItem, INT uItem)
1467 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1468 LISTVIEW_SELECTION *selection;
1469 INT topSelection = infoPtr->hdpaSelectionRanges->nItemCount;
1470 BOOL lowerzero=FALSE;
1472 selection = (LISTVIEW_SELECTION *)COMCTL32_Alloc(sizeof(LISTVIEW_SELECTION));
1473 selection->lower = lItem;
1474 selection->upper = uItem;
1476 TRACE("Add range %i - %i\n", lItem, uItem);
1479 LISTVIEW_SELECTION *checkselection,*checkselection2;
1480 INT index,mergeindex;
1482 /* find overlapping selections */
1483 /* we want to catch adjacent ranges so expand our range by 1 */
1486 if (selection->lower == 0)
1491 index = DPA_Search(infoPtr->hdpaSelectionRanges, selection, 0,
1492 LISTVIEW_CompareSelectionRanges,
1494 selection->upper --;
1498 selection->lower ++;
1502 checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,index);
1503 TRACE("Merge with index %i (%lu - %lu)\n",index,checkselection->lower,
1504 checkselection->upper);
1506 checkselection->lower = min(selection->lower,checkselection->lower);
1507 checkselection->upper = max(selection->upper,checkselection->upper);
1509 TRACE("New range (%lu - %lu)\n", checkselection->lower,
1510 checkselection->upper);
1512 COMCTL32_Free(selection);
1514 /* merge now common selection ranges in the lower group*/
1517 checkselection->upper ++;
1518 if (checkselection->lower == 0)
1521 checkselection->lower --;
1523 TRACE("search lower range (%lu - %lu)\n", checkselection->lower,
1524 checkselection->upper);
1526 /* not sorted yet */
1527 mergeindex = DPA_Search(infoPtr->hdpaSelectionRanges, checkselection, 0,
1528 LISTVIEW_CompareSelectionRanges, 0,
1531 checkselection->upper --;
1535 checkselection->lower ++;
1537 if (mergeindex >=0 && mergeindex != index)
1539 TRACE("Merge with index %i\n",mergeindex);
1540 checkselection2 = DPA_GetPtr(infoPtr->hdpaSelectionRanges,
1542 checkselection->lower = min(checkselection->lower,
1543 checkselection2->lower);
1544 checkselection->upper = max(checkselection->upper,
1545 checkselection2->upper);
1546 COMCTL32_Free(checkselection2);
1547 DPA_DeletePtr(infoPtr->hdpaSelectionRanges,mergeindex);
1551 while (mergeindex > -1 && mergeindex <index);
1553 /* merge now common selection ranges in the upper group*/
1556 checkselection->upper ++;
1557 if (checkselection->lower == 0)
1560 checkselection->lower --;
1562 TRACE("search upper range %i (%lu - %lu)\n",index,
1563 checkselection->lower, checkselection->upper);
1565 /* not sorted yet */
1566 mergeindex = DPA_Search(infoPtr->hdpaSelectionRanges, checkselection,
1568 LISTVIEW_CompareSelectionRanges, 0,
1571 checkselection->upper --;
1575 checkselection->lower ++;
1577 if (mergeindex >=0 && mergeindex !=index)
1579 TRACE("Merge with index %i\n",mergeindex);
1580 checkselection2 = DPA_GetPtr(infoPtr->hdpaSelectionRanges,
1582 checkselection->lower = min(checkselection->lower,
1583 checkselection2->lower);
1584 checkselection->upper = max(checkselection->upper,
1585 checkselection2->upper);
1586 COMCTL32_Free(checkselection2);
1587 DPA_DeletePtr(infoPtr->hdpaSelectionRanges,mergeindex);
1590 while (mergeindex > -1);
1595 index = DPA_Search(infoPtr->hdpaSelectionRanges, selection, 0,
1596 LISTVIEW_CompareSelectionRanges, 0,
1599 TRACE("Insert before index %i\n",index);
1602 DPA_InsertPtr(infoPtr->hdpaSelectionRanges,index,selection);
1607 DPA_InsertPtr(infoPtr->hdpaSelectionRanges,0,selection);
1612 DPA_Sort(infoPtr->hdpaSelectionRanges,LISTVIEW_CompareSelectionRanges,0);
1613 LISTVIEW_PrintSelectionRanges(hwnd);
1618 * check if a specified index is selected.
1621 * [I] HWND : window handle
1622 * [I] INT : item index
1627 static BOOL LISTVIEW_IsSelected(HWND hwnd, INT nItem)
1629 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1630 LISTVIEW_SELECTION selection;
1633 selection.upper = nItem;
1634 selection.lower = nItem;
1636 index = DPA_Search(infoPtr->hdpaSelectionRanges, &selection, 0,
1637 LISTVIEW_CompareSelectionRanges,
1647 * Removes all selection ranges
1650 * HWND: window handle
1656 static LRESULT LISTVIEW_RemoveAllSelections(HWND hwnd)
1658 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1659 LISTVIEW_SELECTION *selection;
1663 TRACE("(0x%x)\n",hwnd);
1665 ZeroMemory(&item,sizeof(item));
1666 item.stateMask = LVIS_SELECTED;
1670 selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,0);
1673 TRACE("Removing %lu to %lu\n",selection->lower, selection->upper);
1674 for (i = selection->lower; i<=selection->upper; i++)
1675 LISTVIEW_SetItemState(hwnd,i,&item);
1676 LISTVIEW_RemoveSelectionRange(hwnd,selection->lower,selection->upper);
1679 while (infoPtr->hdpaSelectionRanges->nItemCount>0);
1687 * Removes a range selections.
1690 * [I] HWND : window handle
1691 * [I] INT : lower item index
1692 * [I] INT : upper item index
1697 static VOID LISTVIEW_RemoveSelectionRange(HWND hwnd, INT lItem, INT uItem)
1699 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1700 LISTVIEW_SELECTION removeselection,*checkselection;
1703 removeselection.lower = lItem;
1704 removeselection.upper = uItem;
1706 TRACE("Remove range %lu - %lu\n",removeselection.lower,removeselection.upper);
1707 LISTVIEW_PrintSelectionRanges(hwnd);
1709 index = DPA_Search(infoPtr->hdpaSelectionRanges, &removeselection, 0,
1710 LISTVIEW_CompareSelectionRanges,
1717 checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,
1720 TRACE("Matches range index %i (%lu-%lu)\n",index,checkselection->lower,
1721 checkselection->upper);
1724 if ((checkselection->upper == removeselection.upper) &&
1725 (checkselection->lower == removeselection.lower))
1727 DPA_DeletePtr(infoPtr->hdpaSelectionRanges,index);
1730 /* case 2: engulf */
1731 else if (((checkselection->upper < removeselection.upper) &&
1732 (checkselection->lower > removeselection.lower))||
1733 ((checkselection->upper <= removeselection.upper) &&
1734 (checkselection->lower > removeselection.lower)) ||
1735 ((checkselection->upper < removeselection.upper) &&
1736 (checkselection->lower >= removeselection.lower)))
1739 DPA_DeletePtr(infoPtr->hdpaSelectionRanges,index);
1740 /* do it again because others may also get caught */
1742 LISTVIEW_RemoveSelectionRange(hwnd,lItem,uItem);
1744 /* case 3: overlap upper */
1745 else if ((checkselection->upper < removeselection.upper) &&
1746 (checkselection->lower < removeselection.lower))
1748 checkselection->upper = removeselection.lower - 1;
1750 LISTVIEW_RemoveSelectionRange(hwnd,lItem,uItem);
1752 /* case 4: overlap lower */
1753 else if ((checkselection->upper > removeselection.upper) &&
1754 (checkselection->lower > removeselection.lower))
1756 checkselection->lower = removeselection.upper + 1;
1758 LISTVIEW_RemoveSelectionRange(hwnd,lItem,uItem);
1760 /* case 5: fully internal */
1761 else if (checkselection->upper == removeselection.upper)
1762 checkselection->upper = removeselection.lower - 1;
1763 else if (checkselection->lower == removeselection.lower)
1764 checkselection->lower = removeselection.upper + 1;
1767 /* bisect the range */
1768 LISTVIEW_SELECTION *newselection;
1770 newselection = (LISTVIEW_SELECTION *)
1771 COMCTL32_Alloc(sizeof(LISTVIEW_SELECTION));
1772 newselection -> lower = checkselection->lower;
1773 newselection -> upper = removeselection.lower - 1;
1774 checkselection -> lower = removeselection.upper + 1;
1775 DPA_InsertPtr(infoPtr->hdpaSelectionRanges,index,newselection);
1777 DPA_Sort(infoPtr->hdpaSelectionRanges,LISTVIEW_CompareSelectionRanges,0);
1779 LISTVIEW_PrintSelectionRanges(hwnd);
1784 * Updates the various indices after an item has been inserted or deleted.
1787 * [I] HWND : window handle
1788 * [I] INT : item index
1789 * [I] INT : Direction of shift, +1 or -1.
1794 static VOID LISTVIEW_ShiftIndices(HWND hwnd, INT nItem, INT direction)
1796 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1797 LISTVIEW_SELECTION selection,*checkselection;
1800 TRACE("Shifting %iu, %i steps\n",nItem,direction);
1802 selection.upper = nItem;
1803 selection.lower = nItem;
1805 index = DPA_Search(infoPtr->hdpaSelectionRanges, &selection, 0,
1806 LISTVIEW_CompareSelectionRanges,
1807 0,DPAS_SORTED|DPAS_INSERTAFTER);
1809 while ((index < infoPtr->hdpaSelectionRanges->nItemCount)&&(index != -1))
1811 checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,index);
1812 if ((checkselection->lower >= nItem)&&
1813 (checkselection->lower + direction >= 0))
1814 checkselection->lower += direction;
1815 if ((checkselection->upper >= nItem)&&
1816 (checkselection->upper + direction >=0))
1817 checkselection->upper += direction;
1821 /* Note that the following will fail if direction != +1 and -1 */
1822 if (infoPtr->nSelectionMark > nItem)
1823 infoPtr->nSelectionMark += direction;
1824 else if (infoPtr->nSelectionMark == nItem)
1827 infoPtr->nSelectionMark += direction;
1828 else if (infoPtr->nSelectionMark >= GETITEMCOUNT(infoPtr))
1829 infoPtr->nSelectionMark = GETITEMCOUNT(infoPtr) - 1;
1832 if (infoPtr->nFocusedItem > nItem)
1833 infoPtr->nFocusedItem += direction;
1834 else if (infoPtr->nFocusedItem == nItem)
1837 infoPtr->nFocusedItem += direction;
1840 if (infoPtr->nFocusedItem >= GETITEMCOUNT(infoPtr))
1841 infoPtr->nFocusedItem = GETITEMCOUNT(infoPtr) - 1;
1842 if (infoPtr->nFocusedItem >= 0)
1843 LISTVIEW_SetItemFocus(hwnd, infoPtr->nFocusedItem);
1846 /* But we are not supposed to modify nHotItem! */
1852 * Adds a block of selections.
1855 * [I] HWND : window handle
1856 * [I] INT : item index
1861 static VOID LISTVIEW_AddGroupSelection(HWND hwnd, INT nItem)
1863 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1864 INT nFirst = min(infoPtr->nSelectionMark, nItem);
1865 INT nLast = max(infoPtr->nSelectionMark, nItem);
1872 ZeroMemory(&item,sizeof(item));
1873 item.stateMask = LVIS_SELECTED;
1874 item.state = LVIS_SELECTED;
1876 for (i = nFirst; i <= nLast; i++)
1877 LISTVIEW_SetItemState(hwnd,i,&item);
1879 LISTVIEW_SetItemFocus(hwnd, nItem);
1880 infoPtr->nSelectionMark = nItem;
1886 * Adds a single selection.
1889 * [I] HWND : window handle
1890 * [I] INT : item index
1895 static VOID LISTVIEW_AddSelection(HWND hwnd, INT nItem)
1897 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1900 ZeroMemory(&item,sizeof(item));
1901 item.state = LVIS_SELECTED;
1902 item.stateMask = LVIS_SELECTED;
1904 LISTVIEW_SetItemState(hwnd,nItem,&item);
1906 LISTVIEW_SetItemFocus(hwnd, nItem);
1907 infoPtr->nSelectionMark = nItem;
1912 * Selects or unselects an item.
1915 * [I] HWND : window handle
1916 * [I] INT : item index
1922 static BOOL LISTVIEW_ToggleSelection(HWND hwnd, INT nItem)
1924 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1928 ZeroMemory(&item,sizeof(item));
1929 item.stateMask = LVIS_SELECTED;
1931 if (LISTVIEW_IsSelected(hwnd,nItem))
1933 LISTVIEW_SetItemState(hwnd,nItem,&item);
1938 item.state = LVIS_SELECTED;
1939 LISTVIEW_SetItemState(hwnd,nItem,&item);
1943 LISTVIEW_SetItemFocus(hwnd, nItem);
1944 infoPtr->nSelectionMark = nItem;
1951 * Selects items based on view coordinates.
1954 * [I] HWND : window handle
1955 * [I] RECT : selection rectangle
1960 static VOID LISTVIEW_SetSelectionRect(HWND hwnd, RECT rcSelRect)
1962 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1967 ZeroMemory(&item,sizeof(item));
1968 item.stateMask = LVIS_SELECTED;
1970 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1972 LISTVIEW_GetItemPosition(hwnd, i, &ptItem);
1974 if (PtInRect(&rcSelRect, ptItem) != FALSE)
1975 item.state = LVIS_SELECTED;
1978 LISTVIEW_SetItemState(hwnd,i,&item);
1984 * Sets a single group selection.
1987 * [I] HWND : window handle
1988 * [I] INT : item index
1993 static VOID LISTVIEW_SetGroupSelection(HWND hwnd, INT nItem)
1995 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1996 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
1999 ZeroMemory(&item,sizeof(item));
2000 item.stateMask = LVIS_SELECTED;
2002 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
2007 if (infoPtr->nSelectionMark == -1)
2009 infoPtr->nSelectionMark = nFirst = nLast = nItem;
2013 nFirst = min(infoPtr->nSelectionMark, nItem);
2014 nLast = max(infoPtr->nSelectionMark, nItem);
2017 for (i = 0; i <= GETITEMCOUNT(infoPtr); i++)
2019 if ((i < nFirst) || (i > nLast))
2022 item.state = LVIS_SELECTED;
2023 LISTVIEW_SetItemState(hwnd,i,&item);
2031 LISTVIEW_GetItemBoundBox(hwnd, nItem, &rcItem);
2032 LISTVIEW_GetItemBoundBox(hwnd, infoPtr->nSelectionMark, &rcSelMark);
2033 rcSel.left = min(rcSelMark.left, rcItem.left);
2034 rcSel.top = min(rcSelMark.top, rcItem.top);
2035 rcSel.right = max(rcSelMark.right, rcItem.right);
2036 rcSel.bottom = max(rcSelMark.bottom, rcItem.bottom);
2037 LISTVIEW_SetSelectionRect(hwnd, rcSel);
2038 TRACE("item %d (%d,%d)-(%d,%d), mark %d (%d,%d)-(%d,%d), sel (%d,%d)-(%d,%d)\n",
2039 nItem, rcItem.left, rcItem.top, rcItem.right, rcItem.bottom,
2040 infoPtr->nSelectionMark,
2041 rcSelMark.left, rcSelMark.top, rcSelMark.right, rcSelMark.bottom,
2042 rcSel.left, rcSel.top, rcSel.right, rcSel.bottom);
2046 LISTVIEW_SetItemFocus(hwnd, nItem);
2051 * Manages the item focus.
2054 * [I] HWND : window handle
2055 * [I] INT : item index
2058 * TRUE : focused item changed
2059 * FALSE : focused item has NOT changed
2061 static BOOL LISTVIEW_SetItemFocus(HWND hwnd, INT nItem)
2063 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2064 BOOL bResult = FALSE;
2067 if (infoPtr->nFocusedItem != nItem)
2069 if (infoPtr->nFocusedItem >= 0)
2071 INT oldFocus = infoPtr->nFocusedItem;
2073 infoPtr->nFocusedItem = -1;
2074 ZeroMemory(&lvItem, sizeof(lvItem));
2075 lvItem.stateMask = LVIS_FOCUSED;
2076 ListView_SetItemState(hwnd, oldFocus, &lvItem);
2080 lvItem.state = LVIS_FOCUSED;
2081 lvItem.stateMask = LVIS_FOCUSED;
2082 ListView_SetItemState(hwnd, nItem, &lvItem);
2084 infoPtr->nFocusedItem = nItem;
2085 ListView_EnsureVisible(hwnd, nItem, FALSE);
2093 * Sets a single selection.
2096 * [I] HWND : window handle
2097 * [I] INT : item index
2102 static VOID LISTVIEW_SetSelection(HWND hwnd, INT nItem)
2104 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2107 ZeroMemory(&lvItem, sizeof(lvItem));
2108 lvItem.stateMask = LVIS_FOCUSED;
2109 ListView_SetItemState(hwnd, infoPtr->nFocusedItem, &lvItem);
2111 LISTVIEW_RemoveAllSelections(hwnd);
2113 lvItem.state = LVIS_FOCUSED|LVIS_SELECTED;
2114 lvItem.stateMask = LVIS_FOCUSED|LVIS_SELECTED;
2115 ListView_SetItemState(hwnd, nItem, &lvItem);
2117 infoPtr->nFocusedItem = nItem;
2118 infoPtr->nSelectionMark = nItem;
2123 * Set selection(s) with keyboard.
2126 * [I] HWND : window handle
2127 * [I] INT : item index
2130 * SUCCESS : TRUE (needs to be repainted)
2131 * FAILURE : FALSE (nothing has changed)
2133 static BOOL LISTVIEW_KeySelection(HWND hwnd, INT nItem)
2135 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2136 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2137 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
2138 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
2139 BOOL bResult = FALSE;
2141 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
2143 if (lStyle & LVS_SINGLESEL)
2146 LISTVIEW_SetSelection(hwnd, nItem);
2147 ListView_EnsureVisible(hwnd, nItem, FALSE);
2154 LISTVIEW_SetGroupSelection(hwnd, nItem);
2158 bResult = LISTVIEW_SetItemFocus(hwnd, nItem);
2163 LISTVIEW_SetSelection(hwnd, nItem);
2164 ListView_EnsureVisible(hwnd, nItem, FALSE);
2174 * Called when the mouse is being actively tracked and has hovered for a specified
2178 * [I] HWND : window handle
2179 * [I] wParam : key indicator
2180 * [I] lParam : mouse position
2183 * 0 if the message was processed, non-zero if there was an error
2186 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
2187 * over the item for a certain period of time.
2190 static LRESULT LISTVIEW_MouseHover(HWND hwnd, WPARAM wParam, LPARAM lParam)
2192 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2195 pt.x = (INT)LOWORD(lParam);
2196 pt.y = (INT)HIWORD(lParam);
2198 if(infoPtr->dwExStyle & LVS_EX_TRACKSELECT) {
2199 /* select the item under the cursor */
2200 LISTVIEW_MouseSelection(hwnd, pt);
2208 * Called whenever WM_MOUSEMOVE is received.
2211 * [I] HWND : window handle
2212 * [I] wParam : key indicators
2213 * [I] lParam : cursor position
2216 * 0 if the message is processed, non-zero if there was an error
2218 static LRESULT LISTVIEW_MouseMove(HWND hwnd, WPARAM wParam, LPARAM lParam)
2220 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2221 TRACKMOUSEEVENT trackinfo;
2223 /* see if we are supposed to be tracking mouse hovering */
2224 if(infoPtr->dwExStyle & LVS_EX_TRACKSELECT) {
2225 /* fill in the trackinfo struct */
2226 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
2227 trackinfo.dwFlags = TME_QUERY;
2228 trackinfo.hwndTrack = hwnd;
2229 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
2231 /* see if we are already tracking this hwnd */
2232 _TrackMouseEvent(&trackinfo);
2234 if(!(trackinfo.dwFlags & TME_HOVER)) {
2235 trackinfo.dwFlags = TME_HOVER;
2237 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
2238 _TrackMouseEvent(&trackinfo);
2247 * Selects an item based on coordinates.
2250 * [I] HWND : window handle
2251 * [I] POINT : mouse click ccordinates
2254 * SUCCESS : item index
2257 static LRESULT LISTVIEW_MouseSelection(HWND hwnd, POINT pt)
2259 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2261 INT i,topindex,bottomindex;
2262 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2263 UINT uView = lStyle & LVS_TYPEMASK;
2265 topindex = ListView_GetTopIndex(hwnd);
2266 if (uView == LVS_REPORT)
2268 bottomindex = topindex + LISTVIEW_GetCountPerColumn(hwnd) + 1;
2269 bottomindex = min(bottomindex,GETITEMCOUNT(infoPtr));
2273 bottomindex = GETITEMCOUNT(infoPtr);
2276 for (i = topindex; i < bottomindex; i++)
2278 rcItem.left = LVIR_SELECTBOUNDS;
2279 if (LISTVIEW_GetItemRect(hwnd, i, &rcItem) == TRUE)
2281 if (PtInRect(&rcItem, pt) != FALSE)
2296 * [IO] HDPA : dynamic pointer array handle
2297 * [I] INT : column index (subitem index)
2303 static BOOL LISTVIEW_RemoveColumn(HDPA hdpaItems, INT nSubItem)
2305 BOOL bResult = TRUE;
2309 for (i = 0; i < hdpaItems->nItemCount; i++)
2311 hdpaSubItems = (HDPA)DPA_GetPtr(hdpaItems, i);
2312 if (hdpaSubItems != NULL)
2314 if (LISTVIEW_RemoveSubItem(hdpaSubItems, nSubItem) == FALSE)
2326 * Removes a subitem at a given position.
2329 * [IO] HDPA : dynamic pointer array handle
2330 * [I] INT : subitem index
2336 static BOOL LISTVIEW_RemoveSubItem(HDPA hdpaSubItems, INT nSubItem)
2338 LISTVIEW_SUBITEM *lpSubItem;
2341 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2343 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2344 if (lpSubItem != NULL)
2346 if (lpSubItem->iSubItem == nSubItem)
2349 if (is_textW(lpSubItem->pszText))
2350 COMCTL32_Free(lpSubItem->pszText);
2353 COMCTL32_Free(lpSubItem);
2355 /* free dpa memory */
2356 if (DPA_DeletePtr(hdpaSubItems, i) == NULL)
2359 else if (lpSubItem->iSubItem > nSubItem)
2369 * Compares the item information.
2372 * [I] LISTVIEW_ITEM *: destination item
2373 * [I] LPLVITEM : source item
2374 * [I] isW : TRUE if lpLVItem is Unicode, FALSE it it's ANSI
2377 * SUCCCESS : TRUE (EQUAL)
2378 * FAILURE : FALSE (NOT EQUAL)
2380 static UINT LISTVIEW_GetItemChangesT(LISTVIEW_ITEM *lpItem, LPLVITEMW lpLVItem, BOOL isW)
2384 if ((lpItem != NULL) && (lpLVItem != NULL))
2386 if (lpLVItem->mask & LVIF_STATE)
2388 if ((lpItem->state & lpLVItem->stateMask) !=
2389 (lpLVItem->state & lpLVItem->stateMask))
2390 uChanged |= LVIF_STATE;
2393 if (lpLVItem->mask & LVIF_IMAGE)
2395 if (lpItem->iImage != lpLVItem->iImage)
2396 uChanged |= LVIF_IMAGE;
2399 if (lpLVItem->mask & LVIF_PARAM)
2401 if (lpItem->lParam != lpLVItem->lParam)
2402 uChanged |= LVIF_PARAM;
2405 if (lpLVItem->mask & LVIF_INDENT)
2407 if (lpItem->iIndent != lpLVItem->iIndent)
2408 uChanged |= LVIF_INDENT;
2411 if (lpLVItem->mask & LVIF_TEXT)
2413 if (lpLVItem->pszText == LPSTR_TEXTCALLBACKW)
2415 if (lpItem->pszText != LPSTR_TEXTCALLBACKW)
2416 uChanged |= LVIF_TEXT;
2420 if (lpItem->pszText == LPSTR_TEXTCALLBACKW)
2422 uChanged |= LVIF_TEXT;
2426 if (lpLVItem->pszText)
2428 if (lpItem->pszText)
2430 LPWSTR pszText = textdupTtoW(lpLVItem->pszText, isW);
2431 if (pszText && strcmpW(pszText, lpItem->pszText))
2432 uChanged |= LVIF_TEXT;
2433 textfreeT(pszText, isW);
2437 uChanged |= LVIF_TEXT;
2442 if (lpItem->pszText)
2443 uChanged |= LVIF_TEXT;
2454 * Initializes item attributes.
2457 * [I] HWND : window handle
2458 * [O] LISTVIEW_ITEM *: destination item
2459 * [I] LPLVITEM : source item
2460 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2466 static BOOL LISTVIEW_InitItemT(HWND hwnd, LISTVIEW_ITEM *lpItem,
2467 LPLVITEMW lpLVItem, BOOL isW)
2469 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2470 BOOL bResult = FALSE;
2472 if ((lpItem != NULL) && (lpLVItem != NULL))
2476 if (lpLVItem->mask & LVIF_STATE)
2478 lpItem->state &= ~lpLVItem->stateMask;
2479 lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
2482 if (lpLVItem->mask & LVIF_IMAGE)
2483 lpItem->iImage = lpLVItem->iImage;
2485 if (lpLVItem->mask & LVIF_PARAM)
2486 lpItem->lParam = lpLVItem->lParam;
2488 if (lpLVItem->mask & LVIF_INDENT)
2489 lpItem->iIndent = lpLVItem->iIndent;
2491 if (lpLVItem->mask & LVIF_TEXT)
2493 if (lpLVItem->pszText == LPSTR_TEXTCALLBACKW)
2495 if ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
2498 if (is_textW(lpItem->pszText))
2499 COMCTL32_Free(lpItem->pszText);
2501 lpItem->pszText = LPSTR_TEXTCALLBACKW;
2504 bResult = textsetptrT(&lpItem->pszText, lpLVItem->pszText, isW);
2513 * Initializes subitem attributes.
2515 * NOTE: The documentation specifies that the operation fails if the user
2516 * tries to set the indent of a subitem.
2519 * [I] HWND : window handle
2520 * [O] LISTVIEW_SUBITEM *: destination subitem
2521 * [I] LPLVITEM : source subitem
2522 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2528 static BOOL LISTVIEW_InitSubItemT(HWND hwnd, LISTVIEW_SUBITEM *lpSubItem,
2529 LPLVITEMW lpLVItem, BOOL isW)
2531 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2532 BOOL bResult = FALSE;
2534 TRACE("(hwnd=%x, lpSubItem=%p, lpLVItem=%s, isW=%d)\n",
2535 hwnd, lpSubItem, debuglvitem_t(lpLVItem, isW), isW);
2537 if ((lpSubItem != NULL) && (lpLVItem != NULL))
2539 if (!(lpLVItem->mask & LVIF_INDENT))
2543 lpSubItem->iSubItem = lpLVItem->iSubItem;
2545 if (lpLVItem->mask & LVIF_IMAGE)
2546 lpSubItem->iImage = lpLVItem->iImage;
2548 if (lpLVItem->mask & LVIF_TEXT)
2550 if (lpLVItem->pszText == LPSTR_TEXTCALLBACKW)
2552 if ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
2555 if (is_textW(lpSubItem->pszText))
2556 COMCTL32_Free(lpSubItem->pszText);
2558 lpSubItem->pszText = LPSTR_TEXTCALLBACKW;
2561 bResult = textsetptrT(&lpSubItem->pszText, lpLVItem->pszText, isW);
2571 * Adds a subitem at a given position (column index).
2574 * [I] HWND : window handle
2575 * [I] LPLVITEM : new subitem atttributes
2576 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2582 static BOOL LISTVIEW_AddSubItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
2584 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2585 LISTVIEW_SUBITEM *lpSubItem = NULL;
2586 BOOL bResult = FALSE;
2588 INT nPosition, nItem;
2589 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2591 TRACE("(hwnd=%x, lpLVItem=%s, isW=%d)\n", hwnd, debuglvitem_t(lpLVItem, isW), isW);
2593 if (lStyle & LVS_OWNERDATA)
2596 if (lpLVItem != NULL)
2598 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2599 if (hdpaSubItems != NULL)
2601 lpSubItem = (LISTVIEW_SUBITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM));
2602 if (lpSubItem != NULL)
2604 ZeroMemory(lpSubItem, sizeof(LISTVIEW_SUBITEM));
2605 if (LISTVIEW_InitSubItemT(hwnd, lpSubItem, lpLVItem, isW))
2607 nPosition = LISTVIEW_FindInsertPosition(hdpaSubItems,
2608 lpSubItem->iSubItem);
2609 nItem = DPA_InsertPtr(hdpaSubItems, nPosition, lpSubItem);
2610 if (nItem != -1) bResult = TRUE;
2616 /* cleanup if unsuccessful */
2617 if (!bResult && lpSubItem) COMCTL32_Free(lpSubItem);
2624 * Finds the dpa insert position (array index).
2627 * [I] HWND : window handle
2628 * [I] INT : subitem index
2634 static INT LISTVIEW_FindInsertPosition(HDPA hdpaSubItems, INT nSubItem)
2636 LISTVIEW_SUBITEM *lpSubItem;
2639 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2641 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2642 if (lpSubItem && lpSubItem->iSubItem > nSubItem)
2646 return hdpaSubItems->nItemCount;
2651 * Retrieves a listview subitem at a given position (column index).
2654 * [I] HWND : window handle
2655 * [I] INT : subitem index
2661 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItem(HDPA hdpaSubItems, INT nSubItem)
2663 LISTVIEW_SUBITEM *lpSubItem;
2666 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2668 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2669 if (lpSubItem != NULL)
2671 if (lpSubItem->iSubItem == nSubItem)
2673 else if (lpSubItem->iSubItem > nSubItem)
2683 * Sets item attributes.
2686 * [I] HWND : window handle
2687 * [I] LPLVITEM : new item atttributes
2688 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2694 static BOOL LISTVIEW_SetMainItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
2696 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2697 BOOL bResult = FALSE;
2699 LISTVIEW_ITEM *lpItem;
2701 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2703 UINT uView = lStyle & LVS_TYPEMASK;
2707 TRACE("(hwnd=%x, lpLVItem=%s, isW=%d)\n", hwnd, debuglvitem_t(lpLVItem, isW), isW);
2709 if (lStyle & LVS_OWNERDATA)
2711 if ((lpLVItem->iSubItem == 0)&&(lpLVItem->mask == LVIF_STATE))
2715 ZeroMemory(&itm, sizeof(itm));
2716 itm.mask = LVIF_STATE | LVIF_PARAM;
2717 itm.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
2718 itm.iItem = lpLVItem->iItem;
2720 ListView_GetItemW(hwnd, &itm);
2723 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
2724 nmlv.uNewState = lpLVItem->state;
2725 nmlv.uOldState = itm.state;
2726 nmlv.uChanged = LVIF_STATE;
2727 nmlv.lParam = itm.lParam;
2728 nmlv.iItem = lpLVItem->iItem;
2730 if ((itm.state & lpLVItem->stateMask) !=
2731 (lpLVItem->state & lpLVItem->stateMask))
2733 /* send LVN_ITEMCHANGING notification */
2734 if (!listview_notify(hwnd, LVN_ITEMCHANGING, &nmlv))
2736 if (lpLVItem->stateMask & LVIS_FOCUSED)
2738 if (lpLVItem->state & LVIS_FOCUSED)
2739 infoPtr->nFocusedItem = lpLVItem->iItem;
2740 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
2741 infoPtr->nFocusedItem = -1;
2743 if (lpLVItem->stateMask & LVIS_SELECTED)
2745 if (lpLVItem->state & LVIS_SELECTED)
2747 if (lStyle & LVS_SINGLESEL) LISTVIEW_RemoveAllSelections(hwnd);
2748 LISTVIEW_AddSelectionRange(hwnd,lpLVItem->iItem,lpLVItem->iItem);
2751 LISTVIEW_RemoveSelectionRange(hwnd,lpLVItem->iItem,
2755 listview_notify(hwnd, LVN_ITEMCHANGED, &nmlv);
2757 rcItem.left = LVIR_BOUNDS;
2758 LISTVIEW_GetItemRect(hwnd, lpLVItem->iItem, &rcItem);
2759 InvalidateRect(hwnd, &rcItem, TRUE);
2767 if (lpLVItem != NULL)
2769 if (lpLVItem->iSubItem == 0)
2771 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2772 if (hdpaSubItems != NULL && hdpaSubItems != (HDPA)-1)
2774 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, lpLVItem->iSubItem);
2777 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
2778 nmlv.lParam = lpItem->lParam;
2779 uChanged = LISTVIEW_GetItemChangesT(lpItem, lpLVItem, isW);
2782 if (uChanged & LVIF_STATE)
2784 nmlv.uNewState = lpLVItem->state & lpLVItem->stateMask;
2785 nmlv.uOldState = lpItem->state & lpLVItem->stateMask;
2787 if (nmlv.uNewState & LVIS_SELECTED)
2790 * This is redundant if called through SetSelection
2792 * however is required if the used directly calls SetItem
2793 * to set the selection.
2795 if (lStyle & LVS_SINGLESEL)
2796 LISTVIEW_RemoveAllSelections(hwnd);
2798 LISTVIEW_AddSelectionRange(hwnd,lpLVItem->iItem,
2801 else if (lpLVItem->stateMask & LVIS_SELECTED)
2803 LISTVIEW_RemoveSelectionRange(hwnd,lpLVItem->iItem,
2806 if (nmlv.uNewState & LVIS_FOCUSED)
2809 * This is a fun hoop to jump to try to catch if
2810 * the user is calling us directly to call focus or if
2811 * this function is being called as a result of a
2812 * SetItemFocus call.
2814 if (infoPtr->nFocusedItem >= 0)
2815 LISTVIEW_SetItemFocus(hwnd, lpLVItem->iItem);
2819 nmlv.uChanged = uChanged;
2820 nmlv.iItem = lpLVItem->iItem;
2821 nmlv.lParam = lpItem->lParam;
2822 /* send LVN_ITEMCHANGING notification */
2823 listview_notify(hwnd, LVN_ITEMCHANGING, &nmlv);
2825 /* copy information */
2826 bResult = LISTVIEW_InitItemT(hwnd, lpItem, lpLVItem, isW);
2828 /* if LVS_LIST or LVS_SMALLICON, update the width of the items
2829 based on the width of the items text */
2830 if((uView == LVS_LIST) || (uView == LVS_SMALLICON))
2832 item_width = LISTVIEW_GetStringWidthT(hwnd, lpItem->pszText, TRUE);
2834 if(item_width > infoPtr->nItemWidth)
2835 infoPtr->nItemWidth = item_width;
2838 /* send LVN_ITEMCHANGED notification */
2839 listview_notify(hwnd, LVN_ITEMCHANGED, &nmlv);
2848 rcItem.left = LVIR_BOUNDS;
2849 LISTVIEW_GetItemRect(hwnd, lpLVItem->iItem, &rcItem);
2850 InvalidateRect(hwnd, &rcItem, TRUE);
2862 * Sets subitem attributes.
2865 * [I] HWND : window handle
2866 * [I] LPLVITEM : new subitem atttributes
2867 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2873 static BOOL LISTVIEW_SetSubItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
2875 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2876 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2877 BOOL bResult = FALSE;
2879 LISTVIEW_SUBITEM *lpSubItem;
2882 TRACE("(hwnd=%x, lpLVItem=%s, isW=%d)\n", hwnd, debuglvitem_t(lpLVItem, isW), isW);
2884 if (lStyle & LVS_OWNERDATA)
2887 if (lpLVItem != NULL)
2889 if (lpLVItem->iSubItem > 0)
2891 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2892 if (hdpaSubItems != NULL)
2894 /* set subitem only if column is present */
2895 if (Header_GetItemCount(infoPtr->hwndHeader) > lpLVItem->iSubItem)
2897 lpSubItem = LISTVIEW_GetSubItem(hdpaSubItems, lpLVItem->iSubItem);
2898 if (lpSubItem != NULL)
2899 bResult = LISTVIEW_InitSubItemT(hwnd, lpSubItem, lpLVItem, isW);
2901 bResult = LISTVIEW_AddSubItemT(hwnd, lpLVItem, isW);
2903 rcItem.left = LVIR_BOUNDS;
2904 LISTVIEW_GetItemRect(hwnd, lpLVItem->iItem, &rcItem);
2905 InvalidateRect(hwnd, &rcItem, FALSE);
2916 * Sets item attributes.
2919 * [I] HWND : window handle
2920 * [I] LPLVITEM : new item atttributes
2921 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2927 static BOOL LISTVIEW_SetItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
2929 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2931 if (!lpLVItem || lpLVItem->iItem < 0 ||
2932 lpLVItem->iItem>=GETITEMCOUNT(infoPtr))
2934 if (lpLVItem->iSubItem == 0)
2935 return LISTVIEW_SetMainItemT(hwnd, lpLVItem, isW);
2937 return LISTVIEW_SetSubItemT(hwnd, lpLVItem, isW);
2942 * Retrieves the index of the item at coordinate (0, 0) of the client area.
2945 * [I] HWND : window handle
2950 static INT LISTVIEW_GetTopIndex(HWND hwnd)
2952 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2953 UINT uView = lStyle & LVS_TYPEMASK;
2955 SCROLLINFO scrollInfo;
2957 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
2958 scrollInfo.cbSize = sizeof(SCROLLINFO);
2959 scrollInfo.fMask = SIF_POS;
2961 if (uView == LVS_LIST)
2963 if ((lStyle & WS_HSCROLL) && GetScrollInfo(hwnd, SB_HORZ, &scrollInfo))
2964 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(hwnd);
2966 else if (uView == LVS_REPORT)
2968 if ((lStyle & WS_VSCROLL) && GetScrollInfo(hwnd, SB_VERT, &scrollInfo))
2969 nItem = scrollInfo.nPos;
2980 * [I] HWND : window handle
2981 * [I] HDC : device context handle
2982 * [I] INT : item index
2983 * [I] INT : subitem index
2984 * [I] RECT * : clipping rectangle
2989 static VOID LISTVIEW_DrawSubItem(HWND hwnd, HDC hdc, INT nItem, INT nSubItem,
2990 RECT rcItem, BOOL Selected)
2992 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2993 WCHAR szDispText[DISP_TEXT_SIZE];
2995 UINT textoutOptions = ETO_CLIPPED | ETO_OPAQUE;
2998 TRACE("(hwnd=%x, hdc=%x, nItem=%d, nSubItem=%d)\n", hwnd, hdc,
3001 /* get information needed for drawing the item */
3002 ZeroMemory(&lvItem, sizeof(lvItem));
3003 lvItem.mask = LVIF_TEXT;
3004 lvItem.iItem = nItem;
3005 lvItem.iSubItem = nSubItem;
3006 lvItem.cchTextMax = DISP_TEXT_SIZE;
3007 lvItem.pszText = szDispText;
3008 *lvItem.pszText = '\0';
3009 LISTVIEW_GetItemW(hwnd, &lvItem, TRUE);
3010 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3012 /* redraw the background of the item */
3014 if(infoPtr->nColumnCount == (nSubItem + 1))
3015 rcTemp.right = infoPtr->rcList.right;
3017 rcTemp.right += WIDTH_PADDING;
3019 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3021 /* set item colors */
3022 if (ListView_GetItemState(hwnd,nItem,LVIS_SELECTED) && Selected)
3024 if (infoPtr->bFocus)
3026 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
3027 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
3031 SetBkColor(hdc, GetSysColor(COLOR_3DFACE));
3032 SetTextColor(hdc, GetSysColor(COLOR_BTNTEXT));
3037 if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
3039 SetBkMode(hdc, TRANSPARENT);
3040 textoutOptions &= ~ETO_OPAQUE;
3044 SetBkMode(hdc, OPAQUE);
3045 SetBkColor(hdc, infoPtr->clrTextBk);
3048 SetTextColor(hdc, infoPtr->clrText);
3051 ExtTextOutW(hdc, rcItem.left, rcItem.top, textoutOptions,
3052 &rcItem, lvItem.pszText, lstrlenW(lvItem.pszText), NULL);
3056 /* fill in the gap */
3058 if (nSubItem < Header_GetItemCount(infoPtr->hwndHeader)-1)
3060 CopyRect(&rec,&rcItem);
3061 rec.left = rec.right;
3062 rec.right = rec.left+REPORT_MARGINX;
3063 ExtTextOutW(hdc, rec.left , rec.top, textoutOptions,
3064 &rec, NULL, 0, NULL);
3066 CopyRect(&rec,&rcItem);
3067 rec.right = rec.left;
3068 rec.left = rec.left - REPORT_MARGINX;
3069 ExtTextOutW(hdc, rec.left , rec.top, textoutOptions,
3070 &rec, NULL, 0, NULL);
3080 * [I] HWND : window handle
3081 * [I] HDC : device context handle
3082 * [I] INT : item index
3083 * [I] RECT * : clipping rectangle
3088 static VOID LISTVIEW_DrawItem(HWND hwnd, HDC hdc, INT nItem, RECT rcItem, BOOL FullSelect, RECT* SuggestedFocus)
3090 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3091 WCHAR szDispText[DISP_TEXT_SIZE];
3096 DWORD dwTextColor,dwTextX;
3097 BOOL bImage = FALSE;
3099 UINT textoutOptions = ETO_OPAQUE | ETO_CLIPPED;
3102 TRACE("(hwnd=%x, hdc=%x, nItem=%d)\n", hwnd, hdc, nItem);
3105 /* get information needed for drawing the item */
3106 ZeroMemory(&lvItem, sizeof(lvItem));
3107 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE | LVIF_INDENT;
3108 lvItem.stateMask = LVIS_SELECTED | LVIS_STATEIMAGEMASK;
3109 lvItem.iItem = nItem;
3110 lvItem.iSubItem = 0;
3111 lvItem.cchTextMax = DISP_TEXT_SIZE;
3112 lvItem.pszText = szDispText;
3113 *lvItem.pszText = '\0';
3114 LISTVIEW_GetItemW(hwnd, &lvItem, TRUE);
3115 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3117 /* redraw the background of the item */
3119 if(infoPtr->nColumnCount == (nItem + 1))
3120 rcTemp.right = infoPtr->rcList.right;
3122 rcTemp.right+=WIDTH_PADDING;
3124 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3127 if (lvItem.iIndent>0 && infoPtr->iconSize.cx > 0)
3129 rcItem.left += infoPtr->iconSize.cx * lvItem.iIndent;
3132 SuggestedFocus->left += infoPtr->iconSize.cx * lvItem.iIndent;
3136 if (infoPtr->himlState != NULL)
3138 UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12;
3139 if (uStateImage > 0)
3141 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, rcItem.left,
3142 rcItem.top, ILD_NORMAL);
3145 rcItem.left += infoPtr->iconSize.cx;
3147 SuggestedFocus->left += infoPtr->iconSize.cx;
3152 if (infoPtr->himlSmall != NULL)
3154 if ((lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus != FALSE) &&
3157 ImageList_SetBkColor(infoPtr->himlSmall, CLR_NONE);
3158 ImageList_Draw(infoPtr->himlSmall, lvItem.iImage, hdc, rcItem.left,
3159 rcItem.top, ILD_SELECTED);
3161 else if (lvItem.iImage>=0)
3163 ImageList_SetBkColor(infoPtr->himlSmall, CLR_NONE);
3164 ImageList_Draw(infoPtr->himlSmall, lvItem.iImage, hdc, rcItem.left,
3165 rcItem.top, ILD_NORMAL);
3168 rcItem.left += infoPtr->iconSize.cx;
3171 SuggestedFocus->left += infoPtr->iconSize.cx;
3175 /* Don't bother painting item being edited */
3176 if (infoPtr->hwndEdit && lvItem.state & LVIS_FOCUSED && !FullSelect)
3179 if ((lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus != FALSE))
3181 /* set item colors */
3182 dwBkColor = SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
3183 dwTextColor = SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
3184 /* set raster mode */
3185 nMixMode = SetROP2(hdc, R2_XORPEN);
3187 else if ((GetWindowLongW(hwnd, GWL_STYLE) & LVS_SHOWSELALWAYS) &&
3188 (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus == FALSE))
3190 dwBkColor = SetBkColor(hdc, GetSysColor(COLOR_3DFACE));
3191 dwTextColor = SetTextColor(hdc, GetSysColor(COLOR_BTNTEXT));
3192 /* set raster mode */
3193 nMixMode = SetROP2(hdc, R2_COPYPEN);
3197 /* set item colors */
3198 if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
3200 dwBkColor = GetBkColor(hdc);
3201 iBkMode = SetBkMode(hdc, TRANSPARENT);
3202 textoutOptions &= ~ETO_OPAQUE;
3206 dwBkColor = SetBkColor(hdc, infoPtr->clrTextBk);
3207 iBkMode = SetBkMode(hdc, OPAQUE);
3210 dwTextColor = SetTextColor(hdc, infoPtr->clrText);
3211 /* set raster mode */
3212 nMixMode = SetROP2(hdc, R2_COPYPEN);
3215 nLabelWidth = ListView_GetStringWidthW(hwnd, lvItem.pszText);
3216 if (rcItem.left + nLabelWidth < rcItem.right)
3219 rcItem.right = rcItem.left + nLabelWidth + TRAILING_PADDING;
3221 rcItem.right += IMAGE_PADDING;
3225 dwTextX = rcItem.left + 1;
3227 dwTextX += IMAGE_PADDING;
3230 ExtTextOutW(hdc, dwTextX, rcItem.top, textoutOptions,
3231 &rcItem, lvItem.pszText, lstrlenW(lvItem.pszText), NULL);
3233 if ((FullSelect)&&(Header_GetItemCount(infoPtr->hwndHeader) > 1))
3235 /* fill in the gap */
3237 CopyRect(&rec,&rcItem);
3238 rec.left = rec.right;
3239 rec.right = rec.left+REPORT_MARGINX;
3240 ExtTextOutW(hdc, rec.left , rec.top, textoutOptions,
3241 &rec, NULL, 0, NULL);
3245 CopyRect(SuggestedFocus,&rcItem);
3249 SetROP2(hdc, R2_COPYPEN);
3250 SetBkColor(hdc, dwBkColor);
3251 SetTextColor(hdc, dwTextColor);
3253 SetBkMode(hdc, iBkMode);
3259 * Draws an item when in large icon display mode.
3262 * [I] HWND : window handle
3263 * [I] HDC : device context handle
3264 * [I] INT : item index
3265 * [I] RECT : clipping rectangle
3266 * [O] RECT * : The text rectangle about which to draw the focus
3271 static VOID LISTVIEW_DrawLargeItem(HWND hwnd, HDC hdc, INT nItem, RECT rcItem,
3272 RECT *SuggestedFocus)
3274 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3275 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3277 UINT uFormat = DT_TOP | DT_CENTER | DT_WORDBREAK | DT_NOPREFIX |
3279 /* Maintain this format in line with the one in LISTVIEW_UpdateLargeItemLabelRect*/
3282 TRACE("(hwnd=%x, hdc=%x, nItem=%d, left=%d, top=%d, right=%d, bottom=%d)\n",
3283 hwnd, hdc, nItem, rcItem.left, rcItem.top, rcItem.right, rcItem.bottom);
3285 /* get information needed for drawing the item */
3286 ZeroMemory(&lvItem, sizeof(lvItem));
3287 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE;
3288 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3289 lvItem.iItem = nItem;
3290 lvItem.iSubItem = 0;
3291 lvItem.cchTextMax = DISP_TEXT_SIZE;
3292 lvItem.pszText = szDispText;
3293 *lvItem.pszText = '\0';
3294 LISTVIEW_GetItemW(hwnd, &lvItem, FALSE);
3295 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3297 /* redraw the background of the item */
3299 if(infoPtr->nColumnCount == (nItem + 1))
3300 rcTemp.right = infoPtr->rcList.right;
3302 rcTemp.right+=WIDTH_PADDING;
3303 /* The comment doesn't say WIDTH_PADDING applies to large icons */
3305 TRACE("background rect (%d,%d)-(%d,%d)\n",
3306 rcTemp.left, rcTemp.top, rcTemp.right, rcTemp.bottom);
3308 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3311 /* Figure out text colours etc. depending on state
3312 * At least the following states exist; there may be more.
3313 * Many items may be selected
3314 * At most one item may have the focus
3315 * The application may not actually be active currently
3316 * 1. The item is not selected in any way
3317 * 2. The cursor is flying over the icon or text and the text is being
3318 * expanded because it is not fully displayed currently.
3319 * 3. The item is selected and is focussed, i.e. the user has not clicked
3320 * in the blank area of the window, and the window (or application?)
3321 * still has the focus.
3322 * 4. As 3 except that a different window has the focus
3323 * 5. The item is the selected item of all the items, but the user has
3324 * clicked somewhere else on the window.
3325 * Only a few of these are handled currently. In particular 2 is not yet
3326 * handled since we do not support the functionality currently (or at least
3327 * we didn't when I wrote this)
3330 if (lvItem.state & LVIS_SELECTED)
3332 /* set item colors */
3333 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
3334 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
3335 SetBkMode (hdc, OPAQUE);
3336 /* set raster mode */
3337 SetROP2(hdc, R2_XORPEN);
3338 /* When exactly is it in XOR? while being dragged? */
3342 /* set item colors */
3343 if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
3345 SetBkMode(hdc, TRANSPARENT);
3349 SetBkMode(hdc, OPAQUE);
3350 SetBkColor(hdc, infoPtr->clrTextBk);
3353 SetTextColor(hdc, infoPtr->clrText);
3354 /* set raster mode */
3355 SetROP2(hdc, R2_COPYPEN);
3358 /* In cases 2,3 and 5 (see above) the full text is displayed, with word
3359 * wrapping and long words split.
3360 * In cases 1 and 4 only a portion of the text is displayed with word
3361 * wrapping and both word and end ellipsis. (I don't yet know about path
3364 uFormat |= ( (lvItem.state & LVIS_FOCUSED) && infoPtr->bFocus) ?
3367 DT_WORD_ELLIPSIS | DT_END_ELLIPSIS;
3370 if (infoPtr->himlNormal != NULL)
3372 if (lvItem.iImage >= 0)
3374 ImageList_Draw (infoPtr->himlNormal, lvItem.iImage, hdc, rcItem.left,
3376 (lvItem.state & LVIS_SELECTED) ? ILD_SELECTED : ILD_NORMAL);
3380 /* Draw the text below the icon */
3382 /* Don't bother painting item being edited */
3383 if ((infoPtr->hwndEdit && (lvItem.state & LVIS_FOCUSED)) ||
3384 !lstrlenW(lvItem.pszText))
3386 SetRectEmpty(SuggestedFocus);
3390 /* Since rcItem.left is left point of icon, compute left point of item box */
3391 rcItem.left -= ((infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2);
3392 rcItem.right = rcItem.left + infoPtr->nItemWidth;
3393 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3394 TRACE("bound box for text+icon (%d,%d)-(%d,%d), iS.cx=%ld, nItemWidth=%d\n",
3395 rcItem.left, rcItem.top, rcItem.right, rcItem.bottom,
3396 infoPtr->iconSize.cx, infoPtr->nItemWidth);
3397 TRACE("rcList (%d,%d)-(%d,%d), rcView (%d,%d)-(%d,%d)\n",
3398 infoPtr->rcList.left, infoPtr->rcList.top,
3399 infoPtr->rcList.right, infoPtr->rcList.bottom,
3400 infoPtr->rcView.left, infoPtr->rcView.top,
3401 infoPtr->rcView.right, infoPtr->rcView.bottom);
3403 InflateRect(&rcItem, -(2*CAPTION_BORDER), 0);
3404 rcItem.top += infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
3409 /* I am sure of most of the uFormat values. However I am not sure about
3410 * whether we need or do not need the following:
3411 * DT_EXTERNALLEADING, DT_INTERNAL, DT_CALCRECT, DT_NOFULLWIDTHCHARBREAK,
3412 * DT_PATH_ELLIPSIS, DT_RTLREADING,
3413 * We certainly do not need
3414 * DT_BOTTOM, DT_VCENTER, DT_MODIFYSTRING, DT_LEFT, DT_RIGHT, DT_PREFIXONLY,
3415 * DT_SINGLELINE, DT_TABSTOP, DT_EXPANDTABS
3418 /* If the text is being drawn without clipping (i.e. the full text) then we
3419 * need to jump through a few hoops to ensure that it all gets displayed and
3420 * that the background is complete
3422 if (uFormat & DT_NOCLIP)
3425 HBRUSH hBrush = CreateSolidBrush(GetBkColor (hdc));
3426 int dx, dy, old_wid, new_wid;
3427 DrawTextW (hdc, lvItem.pszText, -1, &rcItem, uFormat | DT_CALCRECT);
3428 /* Microsoft, in their great wisdom, have decided that the rectangle
3429 * returned by DrawText on DT_CALCRECT will only guarantee the dimension,
3430 * not the location. So we have to do the centring ourselves (and take
3431 * responsibility for agreeing off-by-one consistency with them).
3433 old_wid = rcItem.right-rcItem.left;
3434 new_wid = rcBack.right - rcBack.left;
3435 dx = rcBack.left - rcItem.left + (new_wid-old_wid)/2;
3436 dy = rcBack.top - rcItem.top;
3437 OffsetRect (&rcItem, dx, dy);
3438 FillRect(hdc, &rcItem, hBrush);
3439 DeleteObject(hBrush);
3441 /* else ? What if we are losing the focus? will we not get a complete
3444 DrawTextW (hdc, lvItem.pszText, -1, &rcItem, uFormat);
3446 CopyRect(SuggestedFocus, &rcItem);
3451 * Draws listview items when in report display mode.
3454 * [I] HWND : window handle
3455 * [I] HDC : device context handle
3460 static VOID LISTVIEW_RefreshReport(HWND hwnd, HDC hdc, DWORD cdmode)
3462 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd,0);
3463 SCROLLINFO scrollInfo;
3464 INT nDrawPosY = infoPtr->rcList.top;
3466 RECT rcItem, rcTemp;
3471 DWORD cditemmode = CDRF_DODEFAULT;
3472 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
3475 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
3476 scrollInfo.cbSize = sizeof(SCROLLINFO);
3477 scrollInfo.fMask = SIF_POS;
3479 nItem = ListView_GetTopIndex(hwnd);
3481 /* add 1 for displaying a partial item at the bottom */
3482 nLast = nItem + LISTVIEW_GetCountPerColumn(hwnd) + 1;
3483 nLast = min(nLast, GETITEMCOUNT(infoPtr));
3485 /* send cache hint notification */
3486 if (GetWindowLongW(hwnd,GWL_STYLE) & LVS_OWNERDATA)
3490 nmlv.hdr.hwndFrom = hwnd;
3491 nmlv.hdr.idFrom = GetWindowLongW(hwnd,GWL_ID);
3492 nmlv.hdr.code = LVN_ODCACHEHINT;
3496 SendMessageW(GetParent(hwnd), WM_NOTIFY, (WPARAM)nmlv.hdr.idFrom,
3500 nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
3501 infoPtr->nColumnCount = nColumnCount; /* update nColumnCount */
3502 FullSelected = infoPtr->dwExStyle & LVS_EX_FULLROWSELECT;
3504 /* clear the background of any part of the control that doesn't contain items */
3505 SubtractRect(&rcTemp, &infoPtr->rcList, &infoPtr->rcView);
3506 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3508 /* nothing to draw */
3509 if(GETITEMCOUNT(infoPtr) == 0)
3512 /* Get scroll bar info once before loop */
3513 GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
3514 scrollOffset = scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE;
3516 for (; nItem < nLast; nItem++)
3518 RECT SuggestedFocusRect;
3521 if (lStyle & LVS_OWNERDRAWFIXED)
3523 UINT uID = GetWindowLongW( hwnd, GWL_ID);
3528 TRACE("Owner Drawn\n");
3529 dis.CtlType = ODT_LISTVIEW;
3532 dis.itemAction = ODA_DRAWENTIRE;
3535 if (LISTVIEW_IsSelected(hwnd,nItem)) dis.itemState|=ODS_SELECTED;
3536 if (nItem==infoPtr->nFocusedItem) dis.itemState|=ODS_FOCUS;
3538 dis.hwndItem = hwnd;
3541 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
3543 dis.rcItem.left = -scrollOffset;
3544 dis.rcItem.right = max(dis.rcItem.left, br.right - scrollOffset);
3545 dis.rcItem.top = nDrawPosY;
3546 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3548 ZeroMemory(&item,sizeof(item));
3550 item.mask = LVIF_PARAM;
3551 ListView_GetItemW(hwnd, &item);
3553 dis.itemData = item.lParam;
3555 if (SendMessageW(GetParent(hwnd),WM_DRAWITEM,(WPARAM)uID,(LPARAM)&dis))
3557 nDrawPosY += infoPtr->nItemHeight;
3566 Header_GetItemRect(infoPtr->hwndHeader, 0, &ir);
3567 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
3569 ir.left += REPORT_MARGINX;
3570 ir.right = max(ir.left, br.right - REPORT_MARGINX);
3572 ir.bottom = ir.top + infoPtr->nItemHeight;
3574 CopyRect(&SuggestedFocusRect,&ir);
3577 for (j = 0; j < nColumnCount; j++)
3579 if (cdmode & CDRF_NOTIFYITEMDRAW)
3580 cditemmode = LISTVIEW_SendCustomDrawItemNotify (hwnd, hdc, nItem, j,
3582 if (cditemmode & CDRF_SKIPDEFAULT)
3585 Header_GetItemRect(infoPtr->hwndHeader, j, &rcItem);
3587 rcItem.left += REPORT_MARGINX;
3588 rcItem.right = max(rcItem.left, rcItem.right - REPORT_MARGINX);
3589 rcItem.top = nDrawPosY;
3590 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3592 /* Offset the Scroll Bar Pos */
3593 rcItem.left -= scrollOffset;
3594 rcItem.right -= scrollOffset;
3598 LISTVIEW_DrawItem(hwnd, hdc, nItem, rcItem, FullSelected,
3599 &SuggestedFocusRect);
3603 LISTVIEW_DrawSubItem(hwnd, hdc, nItem, j, rcItem, FullSelected);
3606 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3607 LISTVIEW_SendCustomDrawItemNotify(hwnd, hdc, nItem, 0,
3608 CDDS_ITEMPOSTPAINT);
3613 if (LISTVIEW_GetItemState(hwnd,nItem,LVIS_FOCUSED) && infoPtr->bFocus)
3616 if (FullSelected && LISTVIEW_GetItemState(hwnd,nItem,LVIS_SELECTED))
3617 rop = SetROP2(hdc, R2_XORPEN);
3619 Rectangle(hdc, SuggestedFocusRect.left, SuggestedFocusRect.top,
3620 SuggestedFocusRect.right,SuggestedFocusRect.bottom);
3623 SetROP2(hdc, R2_COPYPEN);
3625 nDrawPosY += infoPtr->nItemHeight;
3631 * Retrieves the number of items that can fit vertically in the client area.
3634 * [I] HWND : window handle
3637 * Number of items per row.
3639 static INT LISTVIEW_GetCountPerRow(HWND hwnd)
3641 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd,0);
3642 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3643 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
3644 INT nCountPerRow = 1;
3648 if (uView != LVS_REPORT)
3650 nCountPerRow = nListWidth / infoPtr->nItemWidth;
3651 if (nCountPerRow == 0) nCountPerRow = 1;
3655 return nCountPerRow;
3660 * Retrieves the number of items that can fit horizontally in the client
3664 * [I] HWND : window handle
3667 * Number of items per column.
3669 static INT LISTVIEW_GetCountPerColumn(HWND hwnd)
3671 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd,0);
3672 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3673 INT nCountPerColumn = 1;
3675 if (nListHeight > 0)
3677 nCountPerColumn = nListHeight / infoPtr->nItemHeight;
3678 if (nCountPerColumn == 0) nCountPerColumn = 1;
3681 return nCountPerColumn;
3686 * Retrieves the number of columns needed to display all the items when in
3687 * list display mode.
3690 * [I] HWND : window handle
3693 * Number of columns.
3695 static INT LISTVIEW_GetColumnCount(HWND hwnd)
3697 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3698 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
3699 INT nColumnCount = 0;
3701 if ((lStyle & LVS_TYPEMASK) == LVS_LIST)
3703 nColumnCount = infoPtr->rcList.right / infoPtr->nItemWidth;
3704 if (infoPtr->rcList.right % infoPtr->nItemWidth) nColumnCount++;
3707 return nColumnCount;
3713 * Draws listview items when in list display mode.
3716 * [I] HWND : window handle
3717 * [I] HDC : device context handle
3722 static VOID LISTVIEW_RefreshList(HWND hwnd, HDC hdc, DWORD cdmode)
3724 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3725 RECT rcItem, FocusRect, rcTemp;
3729 INT nCountPerColumn;
3730 INT nItemWidth = infoPtr->nItemWidth;
3731 INT nItemHeight = infoPtr->nItemHeight;
3732 DWORD cditemmode = CDRF_DODEFAULT;
3734 /* get number of fully visible columns */
3735 nColumnCount = LISTVIEW_GetColumnCount(hwnd);
3736 infoPtr->nColumnCount = nColumnCount;
3737 nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
3738 nItem = ListView_GetTopIndex(hwnd);
3740 /* paint the background of the control that doesn't contain any items */
3741 SubtractRect(&rcTemp, &infoPtr->rcList, &infoPtr->rcView);
3742 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3744 /* nothing to draw, return here */
3745 if(GETITEMCOUNT(infoPtr) == 0)
3748 for (i = 0; i < nColumnCount; i++)
3750 for (j = 0; j < nCountPerColumn; j++, nItem++)
3752 if (nItem >= GETITEMCOUNT(infoPtr))
3755 if (cdmode & CDRF_NOTIFYITEMDRAW)
3756 cditemmode = LISTVIEW_SendCustomDrawItemNotify (hwnd, hdc, nItem, 0,
3758 if (cditemmode & CDRF_SKIPDEFAULT)
3761 rcItem.top = j * nItemHeight;
3762 rcItem.left = i * nItemWidth;
3763 rcItem.bottom = rcItem.top + nItemHeight;
3764 rcItem.right = rcItem.left + nItemWidth;
3765 LISTVIEW_DrawItem(hwnd, hdc, nItem, rcItem, FALSE, &FocusRect);
3769 if (LISTVIEW_GetItemState(hwnd,nItem,LVIS_FOCUSED) && infoPtr->bFocus)
3770 Rectangle(hdc, FocusRect.left, FocusRect.top,
3771 FocusRect.right,FocusRect.bottom);
3773 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3774 LISTVIEW_SendCustomDrawItemNotify(hwnd, hdc, nItem, 0,
3775 CDDS_ITEMPOSTPAINT);
3783 * Draws listview items when in icon or small icon display mode.
3786 * [I] HWND : window handle
3787 * [I] HDC : device context handle
3792 static VOID LISTVIEW_RefreshIcon(HWND hwnd, HDC hdc, BOOL bSmall, DWORD cdmode)
3794 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3797 RECT rcItem, SuggestedFocus, rcTemp;
3799 DWORD cditemmode = CDRF_DODEFAULT;
3801 infoPtr->nColumnCount = 1; /* set this to an arbitrary value to prevent */
3802 /* DrawItem from erasing the incorrect background area */
3804 /* paint the background of the control that doesn't contain any items */
3805 SubtractRect(&rcTemp, &infoPtr->rcList, &infoPtr->rcView);
3806 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3808 /* nothing to draw, return here */
3809 if(GETITEMCOUNT(infoPtr) == 0)
3812 LISTVIEW_GetOrigin(hwnd, &ptOrigin);
3813 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
3815 if (cdmode & CDRF_NOTIFYITEMDRAW)
3816 cditemmode = LISTVIEW_SendCustomDrawItemNotify (hwnd, hdc, i, 0,
3818 if (cditemmode & CDRF_SKIPDEFAULT)
3821 LISTVIEW_GetItemPosition(hwnd, i, &ptPosition);
3822 ptPosition.x += ptOrigin.x;
3823 ptPosition.y += ptOrigin.y;
3825 if (ptPosition.y + infoPtr->nItemHeight > infoPtr->rcList.top)
3827 if (ptPosition.x + infoPtr->nItemWidth > infoPtr->rcList.left)
3829 if (ptPosition.y < infoPtr->rcList.bottom)
3831 if (ptPosition.x < infoPtr->rcList.right)
3833 rcItem.top = ptPosition.y;
3834 rcItem.left = ptPosition.x;
3835 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3836 rcItem.right = rcItem.left + infoPtr->nItemWidth;
3838 LISTVIEW_DrawItem(hwnd, hdc, i, rcItem, FALSE, &SuggestedFocus);
3840 LISTVIEW_DrawLargeItem(hwnd, hdc, i, rcItem, &SuggestedFocus);
3844 if (LISTVIEW_GetItemState(hwnd,i,LVIS_FOCUSED) &&
3845 infoPtr->bFocus && !IsRectEmpty(&SuggestedFocus))
3846 Rectangle(hdc, SuggestedFocus.left, SuggestedFocus.top,
3847 SuggestedFocus.right,SuggestedFocus.bottom);
3852 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3853 LISTVIEW_SendCustomDrawItemNotify(hwnd, hdc, i, 0,
3854 CDDS_ITEMPOSTPAINT);
3860 * Draws listview items.
3863 * [I] HWND : window handle
3864 * [I] HDC : device context handle
3869 static VOID LISTVIEW_Refresh(HWND hwnd, HDC hdc)
3871 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3872 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3878 LISTVIEW_DumpListview (infoPtr, __LINE__);
3880 GetClientRect(hwnd, &rect);
3881 cdmode = LISTVIEW_SendCustomDrawNotify(hwnd,CDDS_PREPAINT,hdc,rect);
3883 if (cdmode == CDRF_SKIPDEFAULT) return;
3886 hOldFont = SelectObject(hdc, infoPtr->hFont);
3888 /* select the dotted pen (for drawing the focus box) */
3889 hPen = CreatePen(PS_ALTERNATE, 1, 0);
3890 hOldPen = SelectObject(hdc, hPen);
3892 /* select transparent brush (for drawing the focus box) */
3893 SelectObject(hdc, GetStockObject(NULL_BRUSH));
3895 if (uView == LVS_LIST)
3896 LISTVIEW_RefreshList(hwnd, hdc, cdmode);
3897 else if (uView == LVS_REPORT)
3898 LISTVIEW_RefreshReport(hwnd, hdc, cdmode);
3899 else if (uView == LVS_SMALLICON)
3900 LISTVIEW_RefreshIcon(hwnd, hdc, TRUE, cdmode);
3901 else if (uView == LVS_ICON)
3902 LISTVIEW_RefreshIcon(hwnd, hdc, FALSE, cdmode);
3904 /* unselect objects */
3905 SelectObject(hdc, hOldFont);
3906 SelectObject(hdc, hOldPen);
3911 if (cdmode & CDRF_NOTIFYPOSTPAINT)
3912 LISTVIEW_SendCustomDrawNotify(hwnd, CDDS_POSTPAINT, hdc, rect);
3918 * Calculates the approximate width and height of a given number of items.
3921 * [I] HWND : window handle
3922 * [I] INT : number of items
3927 * Returns a DWORD. The width in the low word and the height in high word.
3929 static LRESULT LISTVIEW_ApproximateViewRect(HWND hwnd, INT nItemCount,
3930 WORD wWidth, WORD wHeight)
3932 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3933 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3934 INT nItemCountPerColumn = 1;
3935 INT nColumnCount = 0;
3936 DWORD dwViewRect = 0;
3938 if (nItemCount == -1)
3939 nItemCount = GETITEMCOUNT(infoPtr);
3941 if (uView == LVS_LIST)
3943 if (wHeight == 0xFFFF)
3945 /* use current height */
3946 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3949 if (wHeight < infoPtr->nItemHeight)
3950 wHeight = infoPtr->nItemHeight;
3954 if (infoPtr->nItemHeight > 0)
3956 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
3957 if (nItemCountPerColumn == 0)
3958 nItemCountPerColumn = 1;
3960 if (nItemCount % nItemCountPerColumn != 0)
3961 nColumnCount = nItemCount / nItemCountPerColumn;
3963 nColumnCount = nItemCount / nItemCountPerColumn + 1;
3967 /* Microsoft padding magic */
3968 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
3969 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
3971 dwViewRect = MAKELONG(wWidth, wHeight);
3973 else if (uView == LVS_REPORT)
3974 FIXME("uView == LVS_REPORT: not implemented\n");
3975 else if (uView == LVS_SMALLICON)
3976 FIXME("uView == LVS_SMALLICON: not implemented\n");
3977 else if (uView == LVS_ICON)
3978 FIXME("uView == LVS_ICON: not implemented\n");
3985 * Arranges listview items in icon display mode.
3988 * [I] HWND : window handle
3989 * [I] INT : alignment code
3995 static LRESULT LISTVIEW_Arrange(HWND hwnd, INT nAlignCode)
3997 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3998 BOOL bResult = FALSE;
4000 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4005 FIXME("nAlignCode=LVA_ALIGNLEFT: not implemented\n");
4008 FIXME("nAlignCode=LVA_ALIGNTOP: not implemented\n");
4011 FIXME("nAlignCode=LVA_DEFAULT: not implemented\n");
4013 case LVA_SNAPTOGRID:
4014 FIXME("nAlignCode=LVA_SNAPTOGRID: not implemented\n");
4022 /* << LISTVIEW_CreateDragImage >> */
4027 * Removes all listview items and subitems.
4030 * [I] HWND : window handle
4036 static LRESULT LISTVIEW_DeleteAllItems(HWND hwnd)
4038 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4039 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
4040 UINT uView = lStyle & LVS_TYPEMASK;
4041 LISTVIEW_ITEM *lpItem;
4042 LISTVIEW_SUBITEM *lpSubItem;
4045 BOOL bResult = FALSE;
4048 TRACE("(hwnd=%x,)\n", hwnd);
4050 LISTVIEW_RemoveAllSelections(hwnd);
4051 infoPtr->nSelectionMark=-1;
4052 infoPtr->nFocusedItem=-1;
4053 /* But we are supposed to leave nHotItem as is! */
4055 if (lStyle & LVS_OWNERDATA)
4057 infoPtr->hdpaItems->nItemCount = 0;
4058 InvalidateRect(hwnd, NULL, TRUE);
4062 if (GETITEMCOUNT(infoPtr) > 0)
4066 /* send LVN_DELETEALLITEMS notification */
4067 /* verify if subsequent LVN_DELETEITEM notifications should be
4069 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4071 bSuppress = listview_notify(hwnd, LVN_DELETEALLITEMS, &nmlv);
4073 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
4075 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
4076 if (hdpaSubItems != NULL)
4078 for (j = 1; j < hdpaSubItems->nItemCount; j++)
4080 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, j);
4081 if (lpSubItem != NULL)
4083 /* free subitem string */
4084 if (is_textW(lpSubItem->pszText))
4085 COMCTL32_Free(lpSubItem->pszText);
4088 COMCTL32_Free(lpSubItem);
4092 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
4097 /* send LVN_DELETEITEM notification */
4099 nmlv.lParam = lpItem->lParam;
4100 listview_notify(hwnd, LVN_DELETEITEM, &nmlv);
4103 /* free item string */
4104 if (is_textW(lpItem->pszText))
4105 COMCTL32_Free(lpItem->pszText);
4108 COMCTL32_Free(lpItem);
4111 DPA_Destroy(hdpaSubItems);
4115 /* reinitialize listview memory */
4116 bResult = DPA_DeleteAllPtrs(infoPtr->hdpaItems);
4118 /* align items (set position of each item) */
4119 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4121 if (lStyle & LVS_ALIGNLEFT)
4123 LISTVIEW_AlignLeft(hwnd);
4127 LISTVIEW_AlignTop(hwnd);
4131 LISTVIEW_UpdateScroll(hwnd);
4133 /* invalidate client area (optimization needed) */
4134 InvalidateRect(hwnd, NULL, TRUE);
4142 * Removes a column from the listview control.
4145 * [I] HWND : window handle
4146 * [I] INT : column index
4152 static LRESULT LISTVIEW_DeleteColumn(HWND hwnd, INT nColumn)
4154 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4155 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
4156 UINT uOwnerData = GetWindowLongW(hwnd, GWL_STYLE) & LVS_OWNERDATA;
4157 BOOL bResult = FALSE;
4159 if (Header_DeleteItem(infoPtr->hwndHeader, nColumn) != FALSE)
4162 bResult = LISTVIEW_RemoveColumn(infoPtr->hdpaItems, nColumn);
4164 /* Need to reset the item width when deleting a column */
4165 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
4167 /* reset scroll parameters */
4168 if (uView == LVS_REPORT)
4170 /* update scrollbar(s) */
4171 LISTVIEW_UpdateScroll(hwnd);
4173 /* refresh client area */
4174 InvalidateRect(hwnd, NULL, FALSE);
4183 * Removes an item from the listview control.
4186 * [I] HWND : window handle
4187 * [I] INT : item index
4193 static LRESULT LISTVIEW_DeleteItem(HWND hwnd, INT nItem)
4195 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4196 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
4197 UINT uView = lStyle & LVS_TYPEMASK;
4198 LONG lCtrlId = GetWindowLongW(hwnd, GWL_ID);
4200 BOOL bResult = FALSE;
4202 LISTVIEW_ITEM *lpItem;
4203 LISTVIEW_SUBITEM *lpSubItem;
4207 TRACE("(hwnd=%x, nItem=%d)\n", hwnd, nItem);
4210 /* First, send LVN_DELETEITEM notification. */
4211 memset(&nmlv, 0, sizeof (NMLISTVIEW));
4212 nmlv.hdr.hwndFrom = hwnd;
4213 nmlv.hdr.idFrom = lCtrlId;
4214 nmlv.hdr.code = LVN_DELETEITEM;
4216 SendMessageW(GetParent(hwnd), WM_NOTIFY, (WPARAM)lCtrlId,
4220 /* remove it from the selection range */
4221 ZeroMemory(&item,sizeof(item));
4222 item.stateMask = LVIS_SELECTED;
4223 LISTVIEW_SetItemState(hwnd,nItem,&item);
4225 if (lStyle & LVS_OWNERDATA)
4227 infoPtr->hdpaItems->nItemCount --;
4228 InvalidateRect(hwnd, NULL, TRUE);
4232 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
4234 /* initialize memory */
4235 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4237 hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
4238 if (hdpaSubItems != NULL)
4240 for (i = 1; i < hdpaSubItems->nItemCount; i++)
4242 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
4243 if (lpSubItem != NULL)
4245 /* free item string */
4246 if (is_textW(lpSubItem->pszText))
4247 COMCTL32_Free(lpSubItem->pszText);
4250 COMCTL32_Free(lpSubItem);
4254 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
4257 /* free item string */
4258 if (is_textW(lpItem->pszText))
4259 COMCTL32_Free(lpItem->pszText);
4262 COMCTL32_Free(lpItem);
4265 bResult = DPA_Destroy(hdpaSubItems);
4268 LISTVIEW_ShiftIndices(hwnd,nItem,-1);
4270 /* align items (set position of each item) */
4271 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4273 if (lStyle & LVS_ALIGNLEFT)
4274 LISTVIEW_AlignLeft(hwnd);
4276 LISTVIEW_AlignTop(hwnd);
4279 LISTVIEW_UpdateScroll(hwnd);
4281 /* refresh client area */
4282 InvalidateRect(hwnd, NULL, TRUE);
4291 * Return edit control handle of current edit label
4294 * [I] HWND : window handle
4300 static LRESULT LISTVIEW_GetEditControl(HWND hwnd)
4302 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4303 return infoPtr->hwndEdit;
4309 * Callback implementation for editlabel control
4312 * [I] HWND : window handle
4313 * [I] LPSTR : modified text
4314 * [I] DWORD : item index
4315 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4321 static BOOL LISTVIEW_EndEditLabelT(HWND hwnd, LPWSTR pszText, DWORD nItem, BOOL isW)
4323 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4324 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
4325 NMLVDISPINFOW dispInfo;
4326 LISTVIEW_ITEM *lpItem;
4328 LISTVIEW_ITEM lvItemRef;
4330 BOOL bResult = TRUE;
4332 TRACE("(hwnd=%x, pszText=%s, nItem=%ld, isW=%d)\n", hwnd, debugstr_t(pszText, isW), nItem, isW);
4334 if (!(lStyle & LVS_OWNERDATA))
4336 if (!(hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)))
4339 if (!(lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)))
4344 ZeroMemory(&lvItemRef,sizeof(LISTVIEW_ITEM));
4345 ZeroMemory(&item,sizeof(item));
4348 item.mask = LVIF_PARAM | LVIF_STATE;
4349 ListView_GetItemW(hwnd, &item);
4350 lvItemRef.state = item.state;
4351 lvItemRef.iImage = item.iImage;
4352 lvItemRef.lParam = item.lParam;
4353 lpItem = &lvItemRef;
4356 ZeroMemory(&dispInfo, sizeof(dispInfo));
4357 dispInfo.item.mask = 0;
4358 dispInfo.item.iItem = nItem;
4359 dispInfo.item.state = lpItem->state;
4360 dispInfo.item.stateMask = 0;
4361 dispInfo.item.pszText = pszText;
4362 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4363 dispInfo.item.iImage = lpItem->iImage;
4364 dispInfo.item.lParam = lpItem->lParam;
4365 infoPtr->hwndEdit = 0;
4367 /* Do we need to update the Item Text */
4368 if(dispinfo_notifyT(hwnd, LVN_ENDLABELEDITW, &dispInfo, isW))
4369 if (lpItem->pszText != LPSTR_TEXTCALLBACKW && !(lStyle & LVS_OWNERDATA))
4370 bResult = textsetptrT(&lpItem->pszText, pszText, isW);
4377 * Callback implementation for editlabel control
4380 * [I] HWND : window handle
4381 * [I] LPSTR : modified text
4382 * [I] DWORD : item index
4388 static BOOL LISTVIEW_EndEditLabelW(HWND hwnd, LPWSTR pszText, DWORD nItem)
4390 return LISTVIEW_EndEditLabelT(hwnd, pszText, nItem, TRUE);
4395 * Callback implementation for editlabel control
4398 * [I] HWND : window handle
4399 * [I] LPSTR : modified text
4400 * [I] DWORD : item index
4406 static BOOL LISTVIEW_EndEditLabelA(HWND hwnd, LPSTR pszText, DWORD nItem)
4408 return LISTVIEW_EndEditLabelT(hwnd, (LPWSTR)pszText, nItem, FALSE);
4413 * Begin in place editing of specified list view item
4416 * [I] HWND : window handle
4417 * [I] INT : item index
4418 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4424 static HWND LISTVIEW_EditLabelT(HWND hwnd, INT nItem, BOOL isW)
4426 NMLVDISPINFOW dispInfo;
4428 LISTVIEW_ITEM *lpItem;
4430 HINSTANCE hinst = GetWindowLongW(hwnd, GWL_HINSTANCE);
4431 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4433 WCHAR szDispText[DISP_TEXT_SIZE];
4435 LISTVIEW_ITEM lvItemRef;
4436 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
4438 if (~GetWindowLongW(hwnd, GWL_STYLE) & LVS_EDITLABELS)
4441 /* Is the EditBox still there, if so remove it */
4442 if(infoPtr->hwndEdit != 0)
4445 LISTVIEW_SetSelection(hwnd, nItem);
4446 LISTVIEW_SetItemFocus(hwnd, nItem);
4448 if (!(lStyle & LVS_OWNERDATA))
4450 if (NULL == (hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)))
4453 if (NULL == (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)))
4459 ZeroMemory(&lvItemRef,sizeof(LISTVIEW_ITEM));
4460 ZeroMemory(&item, sizeof(item));
4463 item.mask = LVIF_PARAM | LVIF_STATE;
4464 ListView_GetItemW(hwnd, &item);
4465 lvItemRef.iImage = item.iImage;
4466 lvItemRef.state = item.state;
4467 lvItemRef.lParam = item.lParam;
4468 lpItem = &lvItemRef;
4471 /* get information needed for drawing the item */
4472 ZeroMemory(&lvItem, sizeof(lvItem));
4473 lvItem.mask = LVIF_TEXT;
4474 lvItem.iItem = nItem;
4475 lvItem.iSubItem = 0;
4476 lvItem.cchTextMax = DISP_TEXT_SIZE;
4477 lvItem.pszText = szDispText;
4478 *lvItem.pszText = '\0';
4479 LISTVIEW_GetItemT(hwnd, &lvItem, FALSE, isW);
4481 ZeroMemory(&dispInfo, sizeof(dispInfo));
4482 dispInfo.item.mask = 0;
4483 dispInfo.item.iItem = nItem;
4484 dispInfo.item.state = lpItem->state;
4485 dispInfo.item.stateMask = 0;
4486 dispInfo.item.pszText = lvItem.pszText;
4487 dispInfo.item.cchTextMax = lstrlenW(lvItem.pszText);
4488 dispInfo.item.iImage = lpItem->iImage;
4489 dispInfo.item.lParam = lpItem->lParam;
4491 if (dispinfo_notifyT(hwnd, LVN_BEGINLABELEDITW, &dispInfo, isW))
4494 rect.left = LVIR_LABEL;
4495 if (!LISTVIEW_GetItemRect(hwnd, nItem, &rect))
4498 if (!(hedit = CreateEditLabelT(szDispText , WS_VISIBLE,
4499 rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, hwnd, hinst,
4500 isW ? LISTVIEW_EndEditLabelW : (EditlblCallbackW)LISTVIEW_EndEditLabelA,
4504 infoPtr->hwndEdit = hedit;
4506 SendMessageW(hedit, EM_SETSEL, 0, -1);
4514 * Ensures the specified item is visible, scrolling into view if necessary.
4517 * [I] HWND : window handle
4518 * [I] INT : item index
4519 * [I] BOOL : partially or entirely visible
4525 static BOOL LISTVIEW_EnsureVisible(HWND hwnd, INT nItem, BOOL bPartial)
4527 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4528 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
4529 INT nScrollPosHeight = 0;
4530 INT nScrollPosWidth = 0;
4531 SCROLLINFO scrollInfo;
4533 BOOL bRedraw = FALSE;
4535 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
4536 scrollInfo.cbSize = sizeof(SCROLLINFO);
4537 scrollInfo.fMask = SIF_POS;
4539 /* ALWAYS bPartial == FALSE, FOR NOW! */
4541 rcItem.left = LVIR_BOUNDS;
4542 if (LISTVIEW_GetItemRect(hwnd, nItem, &rcItem) != FALSE)
4544 if (rcItem.left < infoPtr->rcList.left)
4546 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
4550 if (uView == LVS_LIST)
4552 nScrollPosWidth = infoPtr->nItemWidth;
4553 rcItem.left += infoPtr->rcList.left;
4555 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4557 nScrollPosWidth = LISTVIEW_SCROLL_DIV_SIZE;
4558 rcItem.left += infoPtr->rcList.left;
4561 /* When in LVS_REPORT view, the scroll position should
4563 if (nScrollPosWidth != 0)
4565 if (rcItem.left % nScrollPosWidth == 0)
4566 scrollInfo.nPos += rcItem.left / nScrollPosWidth;
4568 scrollInfo.nPos += rcItem.left / nScrollPosWidth - 1;
4570 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
4574 else if (rcItem.right > infoPtr->rcList.right)
4576 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
4580 if (uView == LVS_LIST)
4582 rcItem.right -= infoPtr->rcList.right;
4583 nScrollPosWidth = infoPtr->nItemWidth;
4585 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4587 rcItem.right -= infoPtr->rcList.right;
4588 nScrollPosWidth = LISTVIEW_SCROLL_DIV_SIZE;
4591 /* When in LVS_REPORT view, the scroll position should
4593 if (nScrollPosWidth != 0)
4595 if (rcItem.right % nScrollPosWidth == 0)
4596 scrollInfo.nPos += rcItem.right / nScrollPosWidth;
4598 scrollInfo.nPos += rcItem.right / nScrollPosWidth + 1;
4600 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
4605 if (rcItem.top < infoPtr->rcList.top)
4609 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
4611 if (uView == LVS_REPORT)
4613 rcItem.top -= infoPtr->rcList.top;
4614 nScrollPosHeight = infoPtr->nItemHeight;
4616 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4618 nScrollPosHeight = LISTVIEW_SCROLL_DIV_SIZE;
4619 rcItem.top += infoPtr->rcList.top;
4622 if (rcItem.top % nScrollPosHeight == 0)
4623 scrollInfo.nPos += rcItem.top / nScrollPosHeight;
4625 scrollInfo.nPos += rcItem.top / nScrollPosHeight - 1;
4627 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
4630 else if (rcItem.bottom > infoPtr->rcList.bottom)
4634 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
4636 if (uView == LVS_REPORT)
4638 rcItem.bottom -= infoPtr->rcList.bottom;
4639 nScrollPosHeight = infoPtr->nItemHeight;
4641 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4643 nScrollPosHeight = LISTVIEW_SCROLL_DIV_SIZE;
4644 rcItem.bottom -= infoPtr->rcList.bottom;
4647 if (rcItem.bottom % nScrollPosHeight == 0)
4648 scrollInfo.nPos += rcItem.bottom / nScrollPosHeight;
4650 scrollInfo.nPos += rcItem.bottom / nScrollPosHeight + 1;
4652 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
4658 InvalidateRect(hwnd,NULL,TRUE);
4664 * Retrieves the nearest item, given a position and a direction.
4667 * [I] HWND : window handle
4668 * [I] POINT : start position
4669 * [I] UINT : direction
4672 * Item index if successdful, -1 otherwise.
4674 static INT LISTVIEW_GetNearestItem(HWND hwnd, POINT pt, UINT vkDirection)
4676 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4681 TRACE("point %ld,%ld, direction %s\n", pt.x, pt.y,
4682 (vkDirection == VK_DOWN) ? "VK_DOWN" :
4683 ((vkDirection == VK_UP) ? "VK_UP" :
4684 ((vkDirection == VK_LEFT) ? "VK_LEFT" : "VK_RIGHT")));
4686 if (LISTVIEW_GetViewRect(hwnd, &rcView) != FALSE)
4688 ZeroMemory(&lvIntHit, sizeof(lvIntHit));
4689 LISTVIEW_GetOrigin(hwnd, &lvIntHit.ht.pt);
4690 lvIntHit.ht.pt.x += pt.x;
4691 lvIntHit.ht.pt.y += pt.y;
4693 if (vkDirection == VK_DOWN)
4694 lvIntHit.ht.pt.y += infoPtr->nItemHeight;
4695 else if (vkDirection == VK_UP)
4696 lvIntHit.ht.pt.y -= infoPtr->nItemHeight;
4697 else if (vkDirection == VK_LEFT)
4698 lvIntHit.ht.pt.x -= infoPtr->nItemWidth;
4699 else if (vkDirection == VK_RIGHT)
4700 lvIntHit.ht.pt.x += infoPtr->nItemWidth;
4702 if (PtInRect(&rcView, lvIntHit.ht.pt) == FALSE)
4706 nItem = LISTVIEW_SuperHitTestItem(hwnd, &lvIntHit, TRUE);
4707 return nItem == -1 ? lvIntHit.iDistItem : nItem;
4716 * Searches for an item with specific characteristics.
4719 * [I] hwnd : window handle
4720 * [I] nStart : base item index
4721 * [I] lpFindInfo : item information to look for
4724 * SUCCESS : index of item
4727 static LRESULT LISTVIEW_FindItemW(HWND hwnd, INT nStart,
4728 LPLVFINDINFOW lpFindInfo)
4730 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4732 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4736 INT nLast = GETITEMCOUNT(infoPtr);
4738 if ((nItem >= -1) && (lpFindInfo != NULL))
4740 ZeroMemory(&lvItem, sizeof(lvItem));
4742 if (lpFindInfo->flags & LVFI_PARAM)
4744 lvItem.mask |= LVIF_PARAM;
4747 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
4749 lvItem.mask |= LVIF_TEXT;
4750 lvItem.pszText = szDispText;
4751 lvItem.cchTextMax = DISP_TEXT_SIZE;
4754 if (lpFindInfo->flags & LVFI_WRAP)
4757 if (lpFindInfo->flags & LVFI_NEARESTXY)
4759 ptItem.x = lpFindInfo->pt.x;
4760 ptItem.y = lpFindInfo->pt.y;
4765 while (nItem < nLast)
4767 if (lpFindInfo->flags & LVFI_NEARESTXY)
4769 nItem = LISTVIEW_GetNearestItem(hwnd, ptItem,
4770 lpFindInfo->vkDirection);
4773 /* get position of the new item index */
4774 if (ListView_GetItemPosition(hwnd, nItem, &ptItem) == FALSE)
4785 lvItem.iItem = nItem;
4786 lvItem.iSubItem = 0;
4787 if (LISTVIEW_GetItemW(hwnd, &lvItem, TRUE))
4789 if (lvItem.mask & LVIF_TEXT)
4791 if (lpFindInfo->flags & LVFI_PARTIAL)
4793 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL)
4798 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0)
4803 if (lvItem.mask & LVIF_PARAM)
4805 if (lpFindInfo->lParam != lvItem.lParam)
4831 * Searches for an item with specific characteristics.
4834 * [I] hwnd : window handle
4835 * [I] nStart : base item index
4836 * [I] lpFindInfo : item information to look for
4839 * SUCCESS : index of item
4842 static LRESULT LISTVIEW_FindItemA(HWND hwnd, INT nStart,
4843 LPLVFINDINFOA lpFindInfo)
4845 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
4849 memcpy(&fiw, lpFindInfo, sizeof(fiw));
4850 if (hasText) fiw.psz = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
4851 res = LISTVIEW_FindItemW(hwnd, nStart, &fiw);
4852 if (hasText) textfreeT((LPWSTR)fiw.psz, FALSE);
4858 * Retrieves the background color of the listview control.
4861 * [I] HWND : window handle
4864 * COLORREF associated with the background.
4866 static LRESULT LISTVIEW_GetBkColor(HWND hwnd)
4868 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4870 return infoPtr->clrBk;
4875 * Retrieves the background image of the listview control.
4878 * [I] HWND : window handle
4879 * [O] LPLVMKBIMAGE : background image attributes
4885 /* static LRESULT LISTVIEW_GetBkImage(HWND hwnd, LPLVBKIMAGE lpBkImage) */
4887 /* FIXME (listview, "empty stub!\n"); */
4893 * Retrieves the callback mask.
4896 * [I] HWND : window handle
4901 static UINT LISTVIEW_GetCallbackMask(HWND hwnd)
4903 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4905 return infoPtr->uCallbackMask;
4910 * Retrieves column attributes.
4913 * [I] HWND : window handle
4914 * [I] INT : column index
4915 * [IO] LPLVCOLUMNW : column information
4916 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
4917 * otherwise it is in fact a LPLVCOLUMNA
4923 static LRESULT LISTVIEW_GetColumnT(HWND hwnd, INT nItem, LPLVCOLUMNW lpColumn, BOOL isW)
4925 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4927 BOOL bResult = FALSE;
4929 if (lpColumn != NULL)
4932 TRACE("(hwnd=%x, col=%d, lpColumn=%s, isW=%d)\n",
4933 hwnd, nItem, debuglvcolumn_t(lpColumn, isW), isW);
4935 /* initialize memory */
4936 ZeroMemory(&hdi, sizeof(hdi));
4938 if (lpColumn->mask & LVCF_FMT)
4939 hdi.mask |= HDI_FORMAT;
4941 if (lpColumn->mask & LVCF_WIDTH)
4942 hdi.mask |= HDI_WIDTH;
4944 if (lpColumn->mask & LVCF_TEXT)
4946 hdi.mask |= HDI_TEXT;
4947 hdi.cchTextMax = lpColumn->cchTextMax;
4948 hdi.pszText = lpColumn->pszText;
4951 if (lpColumn->mask & LVCF_IMAGE)
4952 hdi.mask |= HDI_IMAGE;
4954 if (lpColumn->mask & LVCF_ORDER)
4955 hdi.mask |= HDI_ORDER;
4958 bResult = Header_GetItemW(infoPtr->hwndHeader, nItem, &hdi);
4960 bResult = Header_GetItemA(infoPtr->hwndHeader, nItem, &hdi);
4962 if (bResult != FALSE)
4964 if (lpColumn->mask & LVCF_FMT)
4968 if (hdi.fmt & HDF_LEFT)
4969 lpColumn->fmt |= LVCFMT_LEFT;
4970 else if (hdi.fmt & HDF_RIGHT)
4971 lpColumn->fmt |= LVCFMT_RIGHT;
4972 else if (hdi.fmt & HDF_CENTER)
4973 lpColumn->fmt |= LVCFMT_CENTER;
4975 if (hdi.fmt & HDF_IMAGE)
4976 lpColumn->fmt |= LVCFMT_COL_HAS_IMAGES;
4978 if (hdi.fmt & HDF_BITMAP_ON_RIGHT)
4979 lpColumn->fmt |= LVCFMT_BITMAP_ON_RIGHT;
4982 if (lpColumn->mask & LVCF_WIDTH)
4983 lpColumn->cx = hdi.cxy;
4985 if (lpColumn->mask & LVCF_IMAGE)
4986 lpColumn->iImage = hdi.iImage;
4988 if (lpColumn->mask & LVCF_ORDER)
4989 lpColumn->iOrder = hdi.iOrder;
4997 static LRESULT LISTVIEW_GetColumnOrderArray(HWND hwnd, INT iCount, LPINT lpiArray)
4999 /* LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); */
5006 for (i = 0; i < iCount; i++)
5014 * Retrieves the column width.
5017 * [I] HWND : window handle
5018 * [I] int : column index
5021 * SUCCESS : column width
5024 static LRESULT LISTVIEW_GetColumnWidth(HWND hwnd, INT nColumn)
5026 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5027 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5028 INT nColumnWidth = 0;
5031 if (uView == LVS_LIST)
5033 nColumnWidth = infoPtr->nItemWidth;
5035 else if (uView == LVS_REPORT)
5037 /* get column width from header */
5038 ZeroMemory(&hdi, sizeof(hdi));
5039 hdi.mask = HDI_WIDTH;
5040 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdi) != FALSE)
5041 nColumnWidth = hdi.cxy;
5044 return nColumnWidth;
5049 * In list or report display mode, retrieves the number of items that can fit
5050 * vertically in the visible area. In icon or small icon display mode,
5051 * retrieves the total number of visible items.
5054 * [I] HWND : window handle
5057 * Number of fully visible items.
5059 static LRESULT LISTVIEW_GetCountPerPage(HWND hwnd)
5061 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5062 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5065 if (uView == LVS_LIST)
5067 if (infoPtr->rcList.right > infoPtr->nItemWidth)
5069 nItemCount = LISTVIEW_GetCountPerRow(hwnd) *
5070 LISTVIEW_GetCountPerColumn(hwnd);
5073 else if (uView == LVS_REPORT)
5075 nItemCount = LISTVIEW_GetCountPerColumn(hwnd);
5079 nItemCount = GETITEMCOUNT(infoPtr);
5085 /* LISTVIEW_GetEditControl */
5089 * Retrieves the extended listview style.
5092 * [I] HWND : window handle
5095 * SUCCESS : previous style
5098 static LRESULT LISTVIEW_GetExtendedListViewStyle(HWND hwnd)
5100 LISTVIEW_INFO *infoPtr;
5102 /* make sure we can get the listview info */
5103 if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0)))
5106 return (infoPtr->dwExStyle);
5111 * Retrieves the handle to the header control.
5114 * [I] HWND : window handle
5119 static LRESULT LISTVIEW_GetHeader(HWND hwnd)
5121 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5123 return infoPtr->hwndHeader;
5126 /* LISTVIEW_GetHotCursor */
5130 * Returns the time that the mouse cursor must hover over an item
5131 * before it is selected.
5134 * [I] HWND : window handle
5137 * Returns the previously set hover time or (DWORD)-1 to indicate that the
5138 * hover time is set to the default hover time.
5140 static LRESULT LISTVIEW_GetHoverTime(HWND hwnd)
5142 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5144 return infoPtr->dwHoverTime;
5149 * Retrieves an image list handle.
5152 * [I] HWND : window handle
5153 * [I] INT : image list identifier
5156 * SUCCESS : image list handle
5159 static LRESULT LISTVIEW_GetImageList(HWND hwnd, INT nImageList)
5161 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5162 HIMAGELIST himl = NULL;
5167 himl = infoPtr->himlNormal;
5170 himl = infoPtr->himlSmall;
5173 himl = infoPtr->himlState;
5177 return (LRESULT)himl;
5180 /* LISTVIEW_GetISearchString */
5184 * Retrieves item attributes.
5187 * [I] hwnd : window handle
5188 * [IO] lpLVItem : item info
5189 * [I] internal : if true then we will use tricks that avoid copies
5190 * but are not compatible with the regular interface
5191 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5192 * if FALSE, the lpLVItem is a LPLVITEMA.
5198 static LRESULT LISTVIEW_GetItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL internal, BOOL isW)
5200 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5201 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
5202 NMLVDISPINFOW dispInfo;
5203 LISTVIEW_SUBITEM *lpSubItem;
5204 LISTVIEW_ITEM *lpItem;
5207 INT* piImage = (INT*)&null;
5208 LPWSTR* ppszText= (LPWSTR*)&null;
5209 LPARAM* plParam = (LPARAM*)&null;
5211 if (internal && !isW)
5213 ERR("We can't have internal non-Unicode GetItem!\n");
5217 /* In the following:
5218 * lpLVItem describes the information requested by the user
5219 * lpItem/lpSubItem is what we have
5220 * dispInfo is a structure we use to request the missing
5221 * information from the application
5224 TRACE("(hwnd=%x, lpLVItem=%s, internal=%d, isW=%d)\n",
5225 hwnd, debuglvitem_t(lpLVItem, isW), internal, isW);
5227 if ((lpLVItem == NULL) || (lpLVItem->iItem < 0) ||
5228 (lpLVItem->iItem >= GETITEMCOUNT(infoPtr)))
5231 ZeroMemory(&dispInfo, sizeof(dispInfo));
5233 if (lStyle & LVS_OWNERDATA)
5235 if (lpLVItem->mask & ~LVIF_STATE)
5237 memcpy(&dispInfo.item, lpLVItem, sizeof(LVITEMW));
5238 dispinfo_notifyT(hwnd, LVN_GETDISPINFOW, &dispInfo, isW);
5239 memcpy(lpLVItem, &dispInfo.item, sizeof(LVITEMW));
5240 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
5243 if ((lpLVItem->mask & LVIF_STATE)&&(lpLVItem->iSubItem == 0))
5245 lpLVItem->state = 0;
5246 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5247 lpLVItem->state |= LVIS_FOCUSED;
5248 if (LISTVIEW_IsSelected(hwnd,lpLVItem->iItem))
5249 lpLVItem->state |= LVIS_SELECTED;
5255 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
5256 if (hdpaSubItems == NULL) return FALSE;
5258 if ( (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)) == NULL)
5261 ZeroMemory(&dispInfo.item, sizeof(LVITEMW));
5262 if (lpLVItem->iSubItem == 0)
5264 piImage=&lpItem->iImage;
5265 ppszText=&lpItem->pszText;
5266 plParam=&lpItem->lParam;
5267 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask)
5269 dispInfo.item.mask |= LVIF_STATE;
5270 dispInfo.item.stateMask = infoPtr->uCallbackMask;
5275 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
5276 if (lpSubItem != NULL)
5278 piImage=&lpSubItem->iImage;
5279 ppszText=&lpSubItem->pszText;
5283 if ((lpLVItem->mask & LVIF_IMAGE) && (*piImage==I_IMAGECALLBACK))
5285 dispInfo.item.mask |= LVIF_IMAGE;
5288 if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(*ppszText))
5290 dispInfo.item.mask |= LVIF_TEXT;
5291 dispInfo.item.pszText = lpLVItem->pszText;
5292 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5293 if (dispInfo.item.pszText && lpLVItem->cchTextMax > 0)
5294 *dispInfo.item.pszText = '\0';
5295 if (dispInfo.item.pszText && (*ppszText == NULL))
5296 *dispInfo.item.pszText = '\0';
5299 if (dispInfo.item.mask != 0)
5301 /* We don't have all the requested info, query the application */
5302 dispInfo.item.iItem = lpLVItem->iItem;
5303 dispInfo.item.iSubItem = lpLVItem->iSubItem;
5304 dispInfo.item.lParam = lpItem->lParam;
5305 dispinfo_notifyT(hwnd, LVN_GETDISPINFOW, &dispInfo, isW);
5306 TRACE(" getdispinfo(2):lpLVItem=%s\n", debuglvitem_t(&dispInfo.item, isW));
5309 if (dispInfo.item.mask & LVIF_IMAGE)
5311 lpLVItem->iImage = dispInfo.item.iImage;
5312 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && (*piImage==I_IMAGECALLBACK))
5313 *piImage = dispInfo.item.iImage;
5315 else if (lpLVItem->mask & LVIF_IMAGE)
5317 lpLVItem->iImage = *piImage;
5320 if (dispInfo.item.mask & LVIF_PARAM)
5322 lpLVItem->lParam = dispInfo.item.lParam;
5323 if (dispInfo.item.mask & LVIF_DI_SETITEM)
5324 *plParam = dispInfo.item.lParam;
5326 else if (lpLVItem->mask & LVIF_PARAM)
5327 lpLVItem->lParam = lpItem->lParam;
5329 if (dispInfo.item.mask & LVIF_TEXT)
5331 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && *ppszText)
5332 textsetptrT(ppszText, dispInfo.item.pszText, isW);
5334 /* If lpLVItem->pszText==dispInfo.item.pszText a copy is unnecessary, but */
5335 /* some apps give a new pointer in ListView_Notify so we can't be sure. */
5336 if (lpLVItem->pszText != dispInfo.item.pszText)
5337 textcpynT(lpLVItem->pszText, isW, dispInfo.item.pszText, isW, lpLVItem->cchTextMax);
5340 else if (lpLVItem->mask & LVIF_TEXT)
5342 if (internal) lpLVItem->pszText = *ppszText;
5343 else textcpynT(lpLVItem->pszText, isW, *ppszText, TRUE, lpLVItem->cchTextMax);
5346 if (lpLVItem->iSubItem == 0)
5348 if (dispInfo.item.mask & LVIF_STATE)
5350 lpLVItem->state = lpItem->state;
5351 lpLVItem->state &= ~dispInfo.item.stateMask;
5352 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
5354 lpLVItem->state &= ~LVIS_SELECTED;
5355 if ((dispInfo.item.stateMask & LVIS_SELECTED) &&
5356 LISTVIEW_IsSelected(hwnd,dispInfo.item.iItem))
5357 lpLVItem->state |= LVIS_SELECTED;
5359 else if (lpLVItem->mask & LVIF_STATE)
5361 lpLVItem->state = lpItem->state & lpLVItem->stateMask;
5363 lpLVItem->state &= ~LVIS_SELECTED;
5364 if ((lpLVItem->stateMask & LVIS_SELECTED) &&
5365 LISTVIEW_IsSelected(hwnd,lpLVItem->iItem))
5366 lpLVItem->state |= LVIS_SELECTED;
5369 if (lpLVItem->mask & LVIF_PARAM)
5370 lpLVItem->lParam = lpItem->lParam;
5372 if (lpLVItem->mask & LVIF_INDENT)
5373 lpLVItem->iIndent = lpItem->iIndent;
5379 /* LISTVIEW_GetHotCursor */
5383 * Retrieves the index of the hot item.
5386 * [I] HWND : window handle
5389 * SUCCESS : hot item index
5390 * FAILURE : -1 (no hot item)
5392 static LRESULT LISTVIEW_GetHotItem(HWND hwnd)
5394 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5396 return infoPtr->nHotItem;
5399 /* LISTVIEW_GetHoverTime */
5403 * Retrieves the number of items in the listview control.
5406 * [I] HWND : window handle
5411 static LRESULT LISTVIEW_GetItemCount(HWND hwnd)
5413 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5415 return GETITEMCOUNT(infoPtr);
5420 * Retrieves the rectangle enclosing the item icon and text.
5423 * [I] HWND : window handle
5424 * [I] INT : item index
5425 * [O] LPRECT : coordinate information
5431 static BOOL LISTVIEW_GetItemBoundBox(HWND hwnd, INT nItem, LPRECT lpRect)
5433 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5434 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
5435 UINT uView = lStyle & LVS_TYPEMASK;
5436 BOOL bResult = FALSE;
5438 LISTVIEW_ITEM *lpItem;
5439 INT nCountPerColumn;
5442 TRACE("(hwnd=%x,nItem=%d,lpRect=%p)\n", hwnd, nItem, lpRect);
5444 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) &&
5447 if (uView == LVS_LIST)
5450 nItem = nItem - ListView_GetTopIndex(hwnd);
5451 nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
5454 nRow = nItem % nCountPerColumn;
5457 lpRect->left = nItem / nCountPerColumn * infoPtr->nItemWidth;
5462 lpRect->left = (nItem / nCountPerColumn -1) * infoPtr->nItemWidth;
5463 lpRect->top = (nRow + nCountPerColumn) * infoPtr->nItemHeight;
5468 lpRect->left = nItem / nCountPerColumn * infoPtr->nItemWidth;
5469 lpRect->top = nItem % nCountPerColumn * infoPtr->nItemHeight;
5472 else if (uView == LVS_REPORT)
5475 lpRect->left = REPORT_MARGINX;
5476 lpRect->top = ((nItem - ListView_GetTopIndex(hwnd)) *
5477 infoPtr->nItemHeight) + infoPtr->rcList.top;
5479 if (!(lStyle & LVS_NOSCROLL))
5481 SCROLLINFO scrollInfo;
5482 /* Adjust position by scrollbar offset */
5483 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
5484 scrollInfo.cbSize = sizeof(SCROLLINFO);
5485 scrollInfo.fMask = SIF_POS;
5486 GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
5487 lpRect->left -= scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE;
5490 else /* either LVS_ICON or LVS_SMALLICON */
5492 if ((hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)))
5494 if ((lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)))
5497 lpRect->left = lpItem->ptPosition.x;
5498 lpRect->top = lpItem->ptPosition.y;
5503 lpRect->right = lpRect->left + infoPtr->nItemWidth;
5504 lpRect->bottom = lpRect->top + infoPtr->nItemHeight;
5505 TRACE("result %s: (%d,%d)-(%d,%d)\n", bResult ? "TRUE" : "FALSE",
5506 lpRect->left, lpRect->top, lpRect->right, lpRect->bottom);
5512 * Retrieves the position (upper-left) of the listview control item.
5513 * Note that for LVS_ICON style, the upper-left is that of the icon
5514 * and not the bounding box.
5517 * [I] HWND : window handle
5518 * [I] INT : item index
5519 * [O] LPPOINT : coordinate information
5525 static BOOL LISTVIEW_GetItemPosition(HWND hwnd, INT nItem, LPPOINT lpptPosition)
5527 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5528 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5529 BOOL bResult = FALSE;
5532 TRACE("(hwnd=%x, nItem=%d, lpptPosition=%p)\n", hwnd, nItem, lpptPosition);
5534 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) &&
5535 (lpptPosition != NULL))
5537 bResult = LISTVIEW_GetItemBoundBox(hwnd, nItem, &rcBounding);
5538 lpptPosition->x = rcBounding.left;
5539 lpptPosition->y = rcBounding.top;
5540 if (uView == LVS_ICON)
5542 lpptPosition->y += ICON_TOP_PADDING;
5543 lpptPosition->x += (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
5545 TRACE("result %s (%ld,%ld)\n", bResult ? "TRUE" : "FALSE",
5546 lpptPosition->x, lpptPosition->y);
5552 * Update the bounding rectangle around the text under a large icon.
5553 * This depends on whether it has the focus or not.
5554 * On entry the rectangle's top, left and right should be set.
5555 * On return the bottom will also be set and the width may have been
5558 * This appears to be weird, even in the Microsoft implementation.
5561 static void ListView_UpdateLargeItemLabelRect (
5562 HWND hwnd, /* The window of the listview */
5563 const LISTVIEW_INFO *infoPtr, /* The listview itself */
5564 int nItem, /* The item for which we are calculating this */
5565 RECT *rect) /* The rectangle to be updated */
5567 HDC hdc = GetDC (hwnd);
5568 HFONT hOldFont = SelectObject (hdc, infoPtr->hFont);
5570 if (infoPtr->bFocus && infoPtr->nFocusedItem == nItem)
5572 /* We (aim to) display the full text. In Windows 95 it appears to
5573 * calculate the size assuming the specified font and then it draws
5574 * the text in that region with the specified font except scaled to
5575 * 10 point (or the height of the system font or ...). Thus if the
5576 * window has 24 point Helvetica the highlit rectangle will be
5577 * taller than the text and if it is 7 point Helvetica then the text
5579 * For now we will simply say that it is the correct size to display
5580 * the text in the specified font.
5583 lvItem.mask = LVIF_TEXT;
5584 lvItem.iItem = nItem;
5585 lvItem.iSubItem = 0;
5586 /* We will specify INTERNAL and so will receive back a const
5587 * pointer to the text, rather than specifying a buffer to which
5590 LISTVIEW_GetItemW (hwnd, &lvItem, TRUE);
5591 DrawTextW (hdc, lvItem.pszText, -1, rect, DT_CALCRECT |
5592 DT_NOCLIP | DT_EDITCONTROL | DT_TOP | DT_CENTER |
5593 DT_WORDBREAK | DT_NOPREFIX);
5594 /* Maintain this DT_* list in line with LISTVIEW_DrawLargeItem */
5598 /* As far as I can see the text region seems to be trying to be
5599 * "tall enough for two lines of text". Once again (comctl32.dll ver
5600 * 5.81?) it measures this on the basis of the selected font and then
5601 * draws it with the same font except in 10 point size. This can lead
5602 * to more or less than the two rows appearing.
5603 * Question; are we supposed to be including DT_EXTERNALLEADING?
5604 * Question; should the width be shrunk to the space required to
5605 * display the two lines?
5607 rect->bottom = rect->top + 2 * infoPtr->ntmHeight;
5610 SelectObject (hdc, hOldFont);
5611 ReleaseDC (hwnd, hdc);
5616 * Retrieves the bounding rectangle for a listview control item.
5619 * [I] HWND : window handle
5620 * [I] INT : item index
5621 * [IO] LPRECT : bounding rectangle coordinates
5622 * lprc->left specifies the portion of the item for which the bounding
5623 * rectangle will be retrieved.
5625 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5626 * including the icon and label.
5627 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5628 * LVIR_LABEL Returns the bounding rectangle of the item text.
5629 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5630 * rectangles, but excludes columns in report view.
5637 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5638 * upon whether the window has the focus currently and on whether the item
5639 * is the one with the focus. Ensure that the control's record of which
5640 * item has the focus agrees with the items' records.
5642 static LRESULT LISTVIEW_GetItemRect(HWND hwnd, INT nItem, LPRECT lprc)
5644 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5645 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5646 BOOL bResult = FALSE;
5655 TRACE("(hwnd=%x, nItem=%d, lprc=%p)\n", hwnd, nItem, lprc);
5657 if (uView & LVS_REPORT)
5659 ZeroMemory(&lvItem, sizeof(lvItem));
5660 lvItem.mask = LVIF_INDENT;
5661 lvItem.iItem = nItem;
5662 lvItem.iSubItem = 0;
5663 LISTVIEW_GetItemW(hwnd, &lvItem, TRUE);
5666 if (lvItem.iIndent>0 && infoPtr->iconSize.cx > 0)
5667 nIndent = infoPtr->iconSize.cx * lvItem.iIndent;
5674 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) && (lprc != NULL))
5679 if (!ListView_GetItemPosition(hwnd, nItem, &ptItem)) break;
5680 if (uView == LVS_ICON)
5682 if (infoPtr->himlNormal != NULL)
5684 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5687 lprc->left = ptItem.x + ptOrigin.x;
5688 lprc->top = ptItem.y + ptOrigin.y;
5689 lprc->right = lprc->left + infoPtr->iconSize.cx;
5690 lprc->bottom = (lprc->top + infoPtr->iconSize.cy +
5691 ICON_BOTTOM_PADDING + ICON_TOP_PADDING);
5695 else if (uView == LVS_SMALLICON)
5697 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5700 lprc->left = ptItem.x + ptOrigin.x;
5701 lprc->top = ptItem.y + ptOrigin.y;
5702 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5704 if (infoPtr->himlState != NULL)
5705 lprc->left += infoPtr->iconSize.cx;
5707 if (infoPtr->himlSmall != NULL)
5708 lprc->right = lprc->left + infoPtr->iconSize.cx;
5710 lprc->right = lprc->left;
5716 lprc->left = ptItem.x;
5717 if (uView & LVS_REPORT)
5718 lprc->left += nIndent;
5719 lprc->top = ptItem.y;
5720 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5722 if (infoPtr->himlState != NULL)
5723 lprc->left += infoPtr->iconSize.cx;
5725 if (infoPtr->himlSmall != NULL)
5726 lprc->right = lprc->left + infoPtr->iconSize.cx;
5728 lprc->right = lprc->left;
5733 if (!ListView_GetItemPosition(hwnd, nItem, &ptItem)) break;
5734 if (uView == LVS_ICON)
5736 if (infoPtr->himlNormal != NULL)
5738 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5741 lprc->left = ptItem.x + ptOrigin.x;
5742 lprc->top = (ptItem.y + ptOrigin.y + infoPtr->iconSize.cy +
5743 ICON_BOTTOM_PADDING);
5744 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5745 if (infoPtr->iconSpacing.cx - nLabelWidth > 1)
5747 lprc->left += (infoPtr->iconSpacing.cx - nLabelWidth) / 2;
5748 lprc->right = lprc->left + nLabelWidth;
5753 lprc->right = lprc->left + infoPtr->iconSpacing.cx - 1;
5754 ListView_UpdateLargeItemLabelRect (hwnd, infoPtr, nItem, lprc);
5759 else if (uView == LVS_SMALLICON)
5761 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5764 nLeftPos = lprc->left = ptItem.x + ptOrigin.x;
5765 lprc->top = ptItem.y + ptOrigin.y;
5766 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5768 if (infoPtr->himlState != NULL)
5769 lprc->left += infoPtr->iconSize.cx;
5771 if (infoPtr->himlSmall != NULL)
5772 lprc->left += infoPtr->iconSize.cx;
5774 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5775 nLabelWidth += TRAILING_PADDING;
5776 if (lprc->left + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5777 lprc->right = lprc->left + nLabelWidth;
5779 lprc->right = nLeftPos + infoPtr->nItemWidth;
5785 if (uView == LVS_REPORT)
5786 nLeftPos = lprc->left = ptItem.x + nIndent;
5788 nLeftPos = lprc->left = ptItem.x;
5789 lprc->top = ptItem.y;
5790 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5792 if (infoPtr->himlState != NULL)
5793 lprc->left += infoPtr->iconSize.cx;
5795 if (infoPtr->himlSmall != NULL)
5796 lprc->left += infoPtr->iconSize.cx;
5798 if (uView != LVS_REPORT)
5800 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5801 nLabelWidth += TRAILING_PADDING;
5802 if (infoPtr->himlSmall)
5803 nLabelWidth += IMAGE_PADDING;
5806 nLabelWidth = LISTVIEW_GetColumnWidth(hwnd, 0)-lprc->left;
5807 if (lprc->left + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5808 lprc->right = lprc->left + nLabelWidth;
5810 lprc->right = nLeftPos + infoPtr->nItemWidth;
5815 if (!LISTVIEW_GetItemBoundBox(hwnd, nItem, &rcInternal)) break;
5816 ptItem.x = rcInternal.left;
5817 ptItem.y = rcInternal.top;
5818 if (uView == LVS_ICON)
5820 if (infoPtr->himlNormal != NULL)
5822 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5825 INT text_left, text_right, icon_left, text_pos_x;
5826 /* for style LVS_ICON bounds
5827 * left = min(icon.left, text.left)
5828 * right = max(icon.right, text.right)
5829 * top = boundbox.top + NOTHITABLE
5830 * bottom = text.bottom + 1
5833 icon_left = text_left = ptItem.x;
5835 /* Correct ptItem to icon upper-left */
5836 icon_left += (infoPtr->nItemWidth - infoPtr->iconSize.cx)/2;
5837 ptItem.y += ICON_TOP_PADDING;
5839 /* Compute the label left and right */
5840 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5841 text_pos_x = infoPtr->iconSpacing.cx - 2*CAPTION_BORDER - nLabelWidth;
5844 text_left += text_pos_x / 2;
5845 text_right = text_left + nLabelWidth + 2*CAPTION_BORDER;
5850 text_right = text_left + infoPtr->iconSpacing.cx - 1;
5853 /* Compute rectangle w/o the text height */
5854 lprc->left = min(icon_left, text_left) + ptOrigin.x;
5855 lprc->right = max(icon_left + infoPtr->iconSize.cx,
5856 text_right) + ptOrigin.x;
5857 lprc->top = ptItem.y + ptOrigin.y - ICON_TOP_PADDING_HITABLE;
5858 lprc->bottom = lprc->top + ICON_TOP_PADDING_HITABLE
5859 + infoPtr->iconSize.cy + 1
5860 + ICON_BOTTOM_PADDING;
5862 CopyRect (&label_rect, lprc);
5863 label_rect.top = lprc->bottom;
5864 ListView_UpdateLargeItemLabelRect (hwnd, infoPtr, nItem, &label_rect);
5865 UnionRect (lprc, lprc, &label_rect);
5869 else if (uView == LVS_SMALLICON)
5871 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5874 lprc->left = ptItem.x + ptOrigin.x;
5875 lprc->right = lprc->left;
5876 lprc->top = ptItem.y + ptOrigin.y;
5877 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5878 if (infoPtr->himlState != NULL)
5879 lprc->right += infoPtr->iconSize.cx;
5880 if (infoPtr->himlSmall != NULL)
5881 lprc->right += infoPtr->iconSize.cx;
5883 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5884 nLabelWidth += TRAILING_PADDING;
5885 if (infoPtr->himlSmall)
5886 nLabelWidth += IMAGE_PADDING;
5887 if (lprc->right + nLabelWidth < lprc->left + infoPtr->nItemWidth)
5888 lprc->right += nLabelWidth;
5890 lprc->right = lprc->left + infoPtr->nItemWidth;
5896 lprc->left = ptItem.x;
5897 if (!(infoPtr->dwExStyle&LVS_EX_FULLROWSELECT) && uView&LVS_REPORT)
5898 lprc->left += nIndent;
5899 lprc->right = lprc->left;
5900 lprc->top = ptItem.y;
5901 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5903 if ((infoPtr->dwExStyle & LVS_EX_FULLROWSELECT) || (uView == LVS_REPORT))
5906 int nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
5907 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
5909 lprc->right = max(lprc->left, br.right - REPORT_MARGINX);
5913 if (infoPtr->himlState != NULL)
5914 lprc->right += infoPtr->iconSize.cx;
5916 if (infoPtr->himlSmall != NULL)
5917 lprc->right += infoPtr->iconSize.cx;
5919 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5920 nLabelWidth += TRAILING_PADDING;
5921 if (lprc->right + nLabelWidth < lprc->left + infoPtr->nItemWidth)
5922 lprc->right += nLabelWidth;
5924 lprc->right = lprc->left + infoPtr->nItemWidth;
5929 case LVIR_SELECTBOUNDS:
5930 if (!ListView_GetItemPosition(hwnd, nItem, &ptItem)) break;
5931 if (uView == LVS_ICON)
5933 if (infoPtr->himlNormal != NULL)
5935 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5938 lprc->left = ptItem.x + ptOrigin.x;
5939 lprc->top = ptItem.y + ptOrigin.y;
5940 lprc->right = lprc->left + infoPtr->iconSpacing.cx;
5941 lprc->bottom = lprc->top + infoPtr->iconSpacing.cy;
5945 else if (uView == LVS_SMALLICON)
5947 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5950 nLeftPos= lprc->left = ptItem.x + ptOrigin.x;
5951 lprc->top = ptItem.y + ptOrigin.y;
5952 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5954 if (infoPtr->himlState != NULL)
5955 lprc->left += infoPtr->iconSize.cx;
5957 lprc->right = lprc->left;
5959 if (infoPtr->himlSmall != NULL)
5960 lprc->right += infoPtr->iconSize.cx;
5962 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5963 nLabelWidth += TRAILING_PADDING;
5964 if (lprc->right + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5965 lprc->right += nLabelWidth;
5967 lprc->right = nLeftPos + infoPtr->nItemWidth;
5973 if (!(infoPtr->dwExStyle&LVS_EX_FULLROWSELECT) && (uView&LVS_REPORT))
5974 nLeftPos = lprc->left = ptItem.x + nIndent;
5976 nLeftPos = lprc->left = ptItem.x;
5977 lprc->top = ptItem.y;
5978 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5980 if (infoPtr->himlState != NULL)
5981 lprc->left += infoPtr->iconSize.cx;
5983 lprc->right = lprc->left;
5985 if (infoPtr->dwExStyle & LVS_EX_FULLROWSELECT)
5988 int nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
5989 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
5991 lprc->right = max(lprc->left, br.right - REPORT_MARGINX);
5995 if (infoPtr->himlSmall != NULL)
5996 lprc->right += infoPtr->iconSize.cx;
5998 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5999 nLabelWidth += TRAILING_PADDING;
6000 if (infoPtr->himlSmall)
6001 nLabelWidth += IMAGE_PADDING;
6002 if (lprc->right + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
6003 lprc->right += nLabelWidth;
6005 lprc->right = nLeftPos + infoPtr->nItemWidth;
6012 TRACE("result %s (%d,%d)-(%d,%d)\n", bResult ? "TRUE" : "FALSE",
6013 lprc->left, lprc->top, lprc->right, lprc->bottom);
6020 * Retrieves the width of a label.
6023 * [I] HWND : window handle
6026 * SUCCESS : string width (in pixels)
6029 static INT LISTVIEW_GetLabelWidth(HWND hwnd, INT nItem)
6031 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
6032 INT nLabelWidth = 0;
6035 TRACE("(hwnd=%x, nItem=%d)\n", hwnd, nItem);
6037 ZeroMemory(&lvItem, sizeof(lvItem));
6038 lvItem.mask = LVIF_TEXT;
6039 lvItem.iItem = nItem;
6040 lvItem.cchTextMax = DISP_TEXT_SIZE;
6041 lvItem.pszText = szDispText;
6042 if (LISTVIEW_GetItemW(hwnd, &lvItem, TRUE))
6043 nLabelWidth = ListView_GetStringWidthW(hwnd, lvItem.pszText);
6050 * Retrieves the spacing between listview control items.
6053 * [I] HWND : window handle
6054 * [I] BOOL : flag for small or large icon
6057 * Horizontal + vertical spacing
6059 static LRESULT LISTVIEW_GetItemSpacing(HWND hwnd, BOOL bSmall)
6061 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6064 if (bSmall == FALSE)
6066 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6070 LONG style = GetWindowLongW(hwnd, GWL_STYLE);
6071 if ((style & LVS_TYPEMASK) == LVS_ICON)
6072 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
6074 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
6081 * Retrieves the state of a listview control item.
6084 * [I] HWND : window handle
6085 * [I] INT : item index
6086 * [I] UINT : state mask
6089 * State specified by the mask.
6091 static LRESULT LISTVIEW_GetItemState(HWND hwnd, INT nItem, UINT uMask)
6093 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6097 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
6099 ZeroMemory(&lvItem, sizeof(lvItem));
6100 lvItem.iItem = nItem;
6101 lvItem.stateMask = uMask;
6102 lvItem.mask = LVIF_STATE;
6103 if (LISTVIEW_GetItemW(hwnd, &lvItem, TRUE))
6104 uState = lvItem.state;
6112 * Retrieves the text of a listview control item or subitem.
6115 * [I] hwnd : window handle
6116 * [I] nItem : item index
6117 * [IO] lpLVItem : item information
6118 * [I] isW : TRUE if lpLVItem is Unicode
6121 * SUCCESS : string length
6124 static LRESULT LISTVIEW_GetItemTextT(HWND hwnd, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
6126 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6129 if (lpLVItem != NULL)
6131 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
6133 lpLVItem->mask = LVIF_TEXT;
6134 lpLVItem->iItem = nItem;
6135 if (LISTVIEW_GetItemT(hwnd, lpLVItem, FALSE, isW))
6136 nLength = textlenT(lpLVItem->pszText, isW);
6145 * Searches for an item based on properties + relationships.
6148 * [I] HWND : window handle
6149 * [I] INT : item index
6150 * [I] INT : relationship flag
6153 * SUCCESS : item index
6156 static LRESULT LISTVIEW_GetNextItem(HWND hwnd, INT nItem, UINT uFlags)
6158 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6159 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
6161 LVFINDINFOW lvFindInfo;
6162 INT nCountPerColumn;
6165 if ((nItem >= -1) && (nItem < GETITEMCOUNT(infoPtr)))
6167 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
6169 if (uFlags & LVNI_CUT)
6172 if (uFlags & LVNI_DROPHILITED)
6173 uMask |= LVIS_DROPHILITED;
6175 if (uFlags & LVNI_FOCUSED)
6176 uMask |= LVIS_FOCUSED;
6178 if (uFlags & LVNI_SELECTED)
6179 uMask |= LVIS_SELECTED;
6181 if (uFlags & LVNI_ABOVE)
6183 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
6188 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6194 lvFindInfo.flags = LVFI_NEARESTXY;
6195 lvFindInfo.vkDirection = VK_UP;
6196 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
6197 while ((nItem = ListView_FindItemW(hwnd, nItem, &lvFindInfo)) != -1)
6199 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6204 else if (uFlags & LVNI_BELOW)
6206 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
6208 while (nItem < GETITEMCOUNT(infoPtr))
6211 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6217 lvFindInfo.flags = LVFI_NEARESTXY;
6218 lvFindInfo.vkDirection = VK_DOWN;
6219 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
6220 while ((nItem = ListView_FindItemW(hwnd, nItem, &lvFindInfo)) != -1)
6222 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6227 else if (uFlags & LVNI_TOLEFT)
6229 if (uView == LVS_LIST)
6231 nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
6232 while (nItem - nCountPerColumn >= 0)
6234 nItem -= nCountPerColumn;
6235 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6239 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6241 lvFindInfo.flags = LVFI_NEARESTXY;
6242 lvFindInfo.vkDirection = VK_LEFT;
6243 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
6244 while ((nItem = ListView_FindItemW(hwnd, nItem, &lvFindInfo)) != -1)
6246 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6251 else if (uFlags & LVNI_TORIGHT)
6253 if (uView == LVS_LIST)
6255 nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
6256 while (nItem + nCountPerColumn < GETITEMCOUNT(infoPtr))
6258 nItem += nCountPerColumn;
6259 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6263 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6265 lvFindInfo.flags = LVFI_NEARESTXY;
6266 lvFindInfo.vkDirection = VK_RIGHT;
6267 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
6268 while ((nItem = ListView_FindItemW(hwnd, nItem, &lvFindInfo)) != -1)
6270 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6279 /* search by index */
6280 for (i = nItem; i < GETITEMCOUNT(infoPtr); i++)
6282 if ((ListView_GetItemState(hwnd, i, uMask) & uMask) == uMask)
6291 /* LISTVIEW_GetNumberOfWorkAreas */
6295 * Retrieves the origin coordinates when in icon or small icon display mode.
6298 * [I] HWND : window handle
6299 * [O] LPPOINT : coordinate information
6305 static LRESULT LISTVIEW_GetOrigin(HWND hwnd, LPPOINT lpptOrigin)
6307 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
6308 UINT uView = lStyle & LVS_TYPEMASK;
6309 BOOL bResult = FALSE;
6311 TRACE("(hwnd=%x, lpptOrigin=%p)\n", hwnd, lpptOrigin);
6313 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6315 SCROLLINFO scrollInfo;
6316 ZeroMemory(lpptOrigin, sizeof(POINT));
6317 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
6318 scrollInfo.cbSize = sizeof(SCROLLINFO);
6320 if (lStyle & WS_HSCROLL)
6322 scrollInfo.fMask = SIF_POS;
6323 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
6324 lpptOrigin->x = -scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE;
6327 if (lStyle & WS_VSCROLL)
6329 scrollInfo.fMask = SIF_POS;
6330 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
6331 lpptOrigin->y = -scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE;
6342 * Retrieves the number of items that are marked as selected.
6345 * [I] HWND : window handle
6348 * Number of items selected.
6350 static LRESULT LISTVIEW_GetSelectedCount(HWND hwnd)
6353 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6354 INT nSelectedCount = 0;
6357 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
6359 if (ListView_GetItemState(hwnd, i, LVIS_SELECTED) & LVIS_SELECTED)
6363 return nSelectedCount;
6368 * Retrieves item index that marks the start of a multiple selection.
6371 * [I] HWND : window handle
6374 * Index number or -1 if there is no selection mark.
6376 static LRESULT LISTVIEW_GetSelectionMark(HWND hwnd)
6378 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6380 return infoPtr->nSelectionMark;
6386 * Retrieves the width of a string.
6389 * [I] hwnd : window handle
6390 * [I] lpszText : text string to process
6391 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
6394 * SUCCESS : string width (in pixels)
6397 static LRESULT LISTVIEW_GetStringWidthT(HWND hwnd, LPCWSTR lpszText, BOOL isW)
6399 if (is_textT(lpszText, isW))
6401 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6402 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
6403 HDC hdc = GetDC(hwnd);
6404 HFONT hOldFont = SelectObject(hdc, hFont);
6406 ZeroMemory(&stringSize, sizeof(SIZE));
6408 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
6410 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
6411 SelectObject(hdc, hOldFont);
6412 ReleaseDC(hwnd, hdc);
6413 return stringSize.cx;
6420 * Retrieves the text backgound color.
6423 * [I] HWND : window handle
6426 * COLORREF associated with the the background.
6428 static LRESULT LISTVIEW_GetTextBkColor(HWND hwnd)
6430 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
6432 return infoPtr->clrTextBk;
6437 * Retrieves the text color.
6440 * [I] HWND : window handle
6443 * COLORREF associated with the text.
6445 static LRESULT LISTVIEW_GetTextColor(HWND hwnd)
6447 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
6449 return infoPtr->clrText;
6454 * Determines item if a hit or closest if not
6457 * [I] HWND : window handle
6458 * [IO] LPLV_INTHIT : hit test information
6459 * [I] subitem : fill out iSubItem.
6462 * SUCCESS : item index of hit
6465 static INT LISTVIEW_SuperHitTestItem(HWND hwnd, LPLV_INTHIT lpInt, BOOL subitem)
6467 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6468 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
6469 UINT uView = lStyle & LVS_TYPEMASK;
6470 INT i,topindex,bottomindex;
6472 DWORD xterm, yterm, dist;
6474 TRACE("(hwnd=%x, x=%ld, y=%ld)\n", hwnd, lpInt->ht.pt.x, lpInt->ht.pt.y);
6476 topindex = ListView_GetTopIndex(hwnd);
6477 if (uView == LVS_REPORT)
6479 bottomindex = topindex + LISTVIEW_GetCountPerColumn(hwnd) + 1;
6480 bottomindex = min(bottomindex,GETITEMCOUNT(infoPtr));
6484 bottomindex = GETITEMCOUNT(infoPtr);
6487 lpInt->distance = 0x7fffffff;
6488 lpInt->iDistItem = -1;
6490 for (i = topindex; i < bottomindex; i++)
6492 rcItem.left = LVIR_BOUNDS;
6493 if (LISTVIEW_GetItemRect(hwnd, i, &rcItem))
6495 if (PtInRect(&rcItem, lpInt->ht.pt))
6497 rcItem.left = LVIR_ICON;
6498 if (LISTVIEW_GetItemRect(hwnd, i, &rcItem))
6500 if (PtInRect(&rcItem, lpInt->ht.pt))
6502 lpInt->ht.flags = LVHT_ONITEMICON;
6503 lpInt->ht.iItem = i;
6504 if (subitem) lpInt->ht.iSubItem = 0;
6509 rcItem.left = LVIR_LABEL;
6510 if (LISTVIEW_GetItemRect(hwnd, i, &rcItem))
6512 if (PtInRect(&rcItem, lpInt->ht.pt))
6514 lpInt->ht.flags = LVHT_ONITEMLABEL;
6515 lpInt->ht.iItem = i;
6516 if (subitem) lpInt->ht.iSubItem = 0;
6521 lpInt->ht.flags = LVHT_ONITEMSTATEICON;
6522 lpInt->ht.iItem = i;
6523 if (subitem) lpInt->ht.iSubItem = 0;
6529 * Now compute distance from point to center of boundary
6530 * box. Since we are only interested in the relative
6531 * distance, we can skip the nasty square root operation
6533 xterm = rcItem.left + (rcItem.right - rcItem.left)/2 - lpInt->ht.pt.x;
6534 yterm = rcItem.top + (rcItem.bottom - rcItem.top)/2 - lpInt->ht.pt.y;
6535 dist = xterm * xterm + yterm * yterm;
6536 if (dist < lpInt->distance)
6538 lpInt->distance = dist;
6539 lpInt->iDistItem = i;
6545 lpInt->ht.flags = LVHT_NOWHERE;
6546 TRACE("no hit, closest item %d, distance %ld\n", lpInt->iDistItem, lpInt->distance);
6553 * Determines which section of the item was selected (if any).
6556 * [I] HWND : window handle
6557 * [IO] LPLVHITTESTINFO : hit test information
6558 * [I] subitem : fill out iSubItem.
6561 * SUCCESS : item index
6564 static INT LISTVIEW_HitTestItem(HWND hwnd, LPLVHITTESTINFO lpHitTestInfo, BOOL subitem)
6567 LV_INTHIT lv_inthit;
6569 TRACE("(hwnd=%x, x=%ld, y=%ld)\n", hwnd, lpHitTestInfo->pt.x,
6570 lpHitTestInfo->pt.y);
6572 memcpy(&lv_inthit, lpHitTestInfo, sizeof(LVHITTESTINFO));
6573 ret = LISTVIEW_SuperHitTestItem(hwnd, &lv_inthit, subitem);
6574 memcpy(lpHitTestInfo, &lv_inthit, sizeof(LVHITTESTINFO));
6580 * Determines which listview item is located at the specified position.
6583 * [I] HWND : window handle
6584 * [IO} LPLVHITTESTINFO : hit test information
6587 * SUCCESS : item index
6590 static LRESULT LISTVIEW_HitTest(HWND hwnd, LPLVHITTESTINFO lpHitTestInfo)
6592 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6595 lpHitTestInfo->flags = 0;
6597 if (infoPtr->rcList.left > lpHitTestInfo->pt.x)
6598 lpHitTestInfo->flags = LVHT_TOLEFT;
6599 else if (infoPtr->rcList.right < lpHitTestInfo->pt.x)
6600 lpHitTestInfo->flags = LVHT_TORIGHT;
6601 if (infoPtr->rcList.top > lpHitTestInfo->pt.y)
6602 lpHitTestInfo->flags |= LVHT_ABOVE;
6603 else if (infoPtr->rcList.bottom < lpHitTestInfo->pt.y)
6604 lpHitTestInfo->flags |= LVHT_BELOW;
6606 if (lpHitTestInfo->flags == 0)
6608 /* NOTE (mm 20001022): We must not allow iSubItem to be touched, for
6609 * an app might pass only a structure with space up to iItem!
6610 * (MS Office 97 does that for instance in the file open dialog)
6612 nItem = LISTVIEW_HitTestItem(hwnd, lpHitTestInfo, FALSE);
6620 * Inserts a new column.
6623 * [I] HWND : window handle
6624 * [I] INT : column index
6625 * [I] LPLVCOLUMNW : column information
6628 * SUCCESS : new column index
6631 static LRESULT LISTVIEW_InsertColumnT(HWND hwnd, INT nColumn,
6632 LPLVCOLUMNW lpColumn, BOOL isW)
6634 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6635 INT nNewColumn = -1;
6638 TRACE("(hwnd=%x, nColumn=%d, lpColumn=%p)\n",hwnd, nColumn, lpColumn);
6640 if (lpColumn != NULL)
6642 /* initialize memory */
6643 ZeroMemory(&hdi, sizeof(hdi));
6645 if (lpColumn->mask & LVCF_FMT)
6647 /* format member is valid */
6648 hdi.mask |= HDI_FORMAT;
6650 /* set text alignment (leftmost column must be left-aligned) */
6653 hdi.fmt |= HDF_LEFT;
6657 if (lpColumn->fmt & LVCFMT_LEFT)
6659 hdi.fmt |= HDF_LEFT;
6661 else if (lpColumn->fmt & LVCFMT_RIGHT)
6663 hdi.fmt |= HDF_RIGHT;
6665 else if (lpColumn->fmt & LVCFMT_CENTER)
6667 hdi.fmt |= HDF_CENTER;
6671 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6673 hdi.fmt |= HDF_BITMAP_ON_RIGHT;
6677 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6682 if (lpColumn->fmt & LVCFMT_IMAGE)
6684 hdi.fmt |= HDF_IMAGE;
6685 hdi.iImage = I_IMAGECALLBACK;
6689 if (lpColumn->mask & LVCF_WIDTH)
6691 hdi.mask |= HDI_WIDTH;
6692 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
6694 /* make it fill the remainder of the controls width */
6699 ZeroMemory(&hdit, sizeof(hdit));
6701 /* get the width of every item except the current one */
6702 hdit.mask = HDI_WIDTH;
6705 for(item_index = 0; item_index < (nColumn - 1); item_index++) {
6706 Header_GetItemW(infoPtr->hwndHeader, item_index, (LPARAM)(&hdit));
6710 /* retrieve the layout of the header */
6711 GetClientRect(hwnd, &rcHeader);
6712 /* GetWindowRect(infoPtr->hwndHeader, &rcHeader);*/
6713 TRACE("start cxy=%d left=%d right=%d\n", hdi.cxy, rcHeader.left, rcHeader.right);
6715 hdi.cxy = (rcHeader.right - rcHeader.left) - hdi.cxy;
6718 hdi.cxy = lpColumn->cx;
6721 if (lpColumn->mask & LVCF_TEXT)
6723 hdi.mask |= HDI_TEXT | HDI_FORMAT;
6724 hdi.pszText = lpColumn->pszText;
6725 hdi.cchTextMax = textlenT(lpColumn->pszText, isW);
6726 hdi.fmt |= HDF_STRING;
6729 if (lpColumn->mask & LVCF_IMAGE)
6731 hdi.mask |= HDI_IMAGE;
6732 hdi.iImage = lpColumn->iImage;
6735 if (lpColumn->mask & LVCF_ORDER)
6737 hdi.mask |= HDI_ORDER;
6738 hdi.iOrder = lpColumn->iOrder;
6741 /* insert item in header control */
6742 nNewColumn = SendMessageW(infoPtr->hwndHeader, HDM_INSERTITEMT(isW),
6743 (WPARAM)nColumn, (LPARAM)&hdi);
6745 /* Need to reset the item width when inserting a new column */
6746 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
6748 LISTVIEW_UpdateScroll(hwnd);
6749 InvalidateRect(hwnd, NULL, FALSE);
6755 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
6756 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
6757 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6758 and not during the processing of a LVM_SORTITEMS message. Applications should provide
6759 their own sort proc. when sending LVM_SORTITEMS.
6762 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6764 LVS_SORTXXX must be specified,
6765 LVS_OWNERDRAW is not set,
6766 <item>.pszText is not LPSTR_TEXTCALLBACK.
6768 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6769 are sorted based on item text..."
6771 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
6773 LONG lStyle = GetWindowLongW((HWND) lParam, GWL_STYLE);
6774 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 );
6775 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 );
6776 INT cmpv = lstrcmpW( lv_first->pszText, lv_second->pszText );
6777 /* if we're sorting descending, negate the return value */
6778 return (lStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
6783 * Inserts a new item in the listview control.
6786 * [I] HWND : window handle
6787 * [I] LPLVITEMW : item information
6788 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6791 * SUCCESS : new item index
6794 static LRESULT LISTVIEW_InsertItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
6796 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6797 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
6798 UINT uView = lStyle & LVS_TYPEMASK;
6802 LISTVIEW_ITEM *lpItem = NULL;
6804 TRACE("(hwnd=%x, lpLVItem=%s, isW=%d)\n",
6805 hwnd, debuglvitem_t(lpLVItem, isW), isW);
6807 if (lStyle & LVS_OWNERDATA)
6809 nItem = infoPtr->hdpaItems->nItemCount;
6810 infoPtr->hdpaItems->nItemCount ++;
6814 if (lpLVItem != NULL)
6816 /* make sure it's not a subitem; cannot insert a subitem */
6817 if (lpLVItem->iSubItem == 0)
6819 if ( (lpItem = (LISTVIEW_ITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_ITEM))) )
6821 ZeroMemory(lpItem, sizeof(LISTVIEW_ITEM));
6822 if (LISTVIEW_InitItemT(hwnd, lpItem, lpLVItem, isW))
6824 /* insert item in listview control data structure */
6825 if ( (hdpaSubItems = DPA_Create(8)) )
6827 if ( (nItem = DPA_InsertPtr(hdpaSubItems, 0, lpItem)) != -1)
6829 if ( ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
6830 && !(lStyle & LVS_OWNERDRAWFIXED)
6831 && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText) )
6833 /* Insert the item in the proper sort order based on the pszText
6834 member. See comments for LISTVIEW_InsertCompare() for greater detail */
6835 nItem = DPA_InsertPtr( infoPtr->hdpaItems,
6836 GETITEMCOUNT( infoPtr ) + 1, hdpaSubItems );
6837 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, hwnd );
6838 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
6842 nItem = DPA_InsertPtr(infoPtr->hdpaItems, lpLVItem->iItem,
6849 LISTVIEW_ShiftIndices(hwnd,nItem,1);
6851 /* manage item focus */
6852 if (lpLVItem->mask & LVIF_STATE)
6854 lpItem->state &= ~(LVIS_FOCUSED|LVIS_SELECTED);
6855 if (lpLVItem->stateMask & LVIS_SELECTED)
6856 LISTVIEW_SetSelection(hwnd, nItem);
6857 else if (lpLVItem->stateMask & LVIS_FOCUSED)
6858 LISTVIEW_SetItemFocus(hwnd, nItem);
6861 /* send LVN_INSERTITEM notification */
6862 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6864 nmlv.lParam = lpItem->lParam;
6865 listview_notify(hwnd, LVN_INSERTITEM, &nmlv);
6867 if ((uView == LVS_SMALLICON) || (uView == LVS_LIST))
6869 nItemWidth = LISTVIEW_CalculateWidth(hwnd, lpLVItem->iItem);
6870 if (nItemWidth > infoPtr->nItemWidth)
6871 infoPtr->nItemWidth = nItemWidth;
6874 /* align items (set position of each item) */
6875 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6877 if (lStyle & LVS_ALIGNLEFT)
6878 LISTVIEW_AlignLeft(hwnd);
6880 LISTVIEW_AlignTop(hwnd);
6883 LISTVIEW_UpdateScroll(hwnd);
6884 /* refresh client area */
6885 InvalidateRect(hwnd, NULL, FALSE);
6894 /* free memory if unsuccessful */
6895 if ((nItem == -1) && (lpItem != NULL))
6896 COMCTL32_Free(lpItem);
6903 * Redraws a range of items.
6906 * [I] HWND : window handle
6907 * [I] INT : first item
6908 * [I] INT : last item
6914 static LRESULT LISTVIEW_RedrawItems(HWND hwnd, INT nFirst, INT nLast)
6916 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6917 BOOL bResult = FALSE;
6921 if (nFirst <= nLast)
6923 if ((nFirst >= 0) && (nFirst < GETITEMCOUNT(infoPtr)))
6925 if ((nLast >= 0) && (nLast < GETITEMCOUNT(infoPtr)))
6927 for (i = nFirst; i <= nLast; i++)
6929 rcItem.left = LVIR_BOUNDS;
6930 LISTVIEW_GetItemRect(hwnd, i, &rcItem);
6931 InvalidateRect(hwnd, &rcItem, TRUE);
6940 /* LISTVIEW_Scroll */
6944 * Sets the background color.
6947 * [I] HWND : window handle
6948 * [I] COLORREF : background color
6954 static LRESULT LISTVIEW_SetBkColor(HWND hwnd, COLORREF clrBk)
6956 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6958 if(infoPtr->clrBk!=clrBk){
6959 infoPtr->clrBk = clrBk;
6960 InvalidateRect(hwnd, NULL, TRUE);
6966 /* LISTVIEW_SetBkImage */
6970 * Sets the callback mask. This mask will be used when the parent
6971 * window stores state information (some or all).
6974 * [I] HWND : window handle
6975 * [I] UINT : state mask
6981 static BOOL LISTVIEW_SetCallbackMask(HWND hwnd, UINT uMask)
6983 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6985 infoPtr->uCallbackMask = uMask;
6992 * Sets the attributes of a header item.
6995 * [I] HWND : window handle
6996 * [I] INT : column index
6997 * [I] LPLVCOLUMNW : column attributes
6998 * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW,
6999 * otherwise it is in fact a LPLVCOLUMNA
7005 static LRESULT LISTVIEW_SetColumnT(HWND hwnd, INT nColumn,
7006 LPLVCOLUMNW lpColumn, BOOL isW)
7008 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7009 BOOL bResult = FALSE;
7010 HDITEMW hdi, hdiget;
7012 if ((lpColumn != NULL) && (nColumn >= 0) &&
7013 (nColumn < Header_GetItemCount(infoPtr->hwndHeader)))
7015 /* initialize memory */
7016 ZeroMemory(&hdi, sizeof(hdi));
7018 if (lpColumn->mask & LVCF_FMT)
7020 /* format member is valid */
7021 hdi.mask |= HDI_FORMAT;
7023 /* get current format first */
7024 hdiget.mask = HDI_FORMAT;
7025 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
7026 /* preserve HDF_STRING if present */
7027 hdi.fmt = hdiget.fmt & HDF_STRING;
7029 /* set text alignment (leftmost column must be left-aligned) */
7032 hdi.fmt |= HDF_LEFT;
7036 if (lpColumn->fmt & LVCFMT_LEFT)
7037 hdi.fmt |= HDF_LEFT;
7038 else if (lpColumn->fmt & LVCFMT_RIGHT)
7039 hdi.fmt |= HDF_RIGHT;
7040 else if (lpColumn->fmt & LVCFMT_CENTER)
7041 hdi.fmt |= HDF_CENTER;
7044 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
7045 hdi.fmt |= HDF_BITMAP_ON_RIGHT;
7047 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
7048 hdi.fmt |= HDF_IMAGE;
7050 if (lpColumn->fmt & LVCFMT_IMAGE)
7052 hdi.fmt |= HDF_IMAGE;
7053 hdi.iImage = I_IMAGECALLBACK;
7057 if (lpColumn->mask & LVCF_WIDTH)
7059 hdi.mask |= HDI_WIDTH;
7060 hdi.cxy = lpColumn->cx;
7063 if (lpColumn->mask & LVCF_TEXT)
7065 hdi.mask |= HDI_TEXT | HDI_FORMAT;
7066 hdi.pszText = lpColumn->pszText;
7067 hdi.cchTextMax = textlenT(lpColumn->pszText, isW);
7068 hdi.fmt |= HDF_STRING;
7071 if (lpColumn->mask & LVCF_IMAGE)
7073 hdi.mask |= HDI_IMAGE;
7074 hdi.iImage = lpColumn->iImage;
7077 if (lpColumn->mask & LVCF_ORDER)
7079 hdi.mask |= HDI_ORDER;
7080 hdi.iOrder = lpColumn->iOrder;
7083 /* set header item attributes */
7084 bResult = Header_SetItemW(infoPtr->hwndHeader, nColumn, &hdi);
7092 * Sets the column order array
7095 * [I] HWND : window handle
7096 * [I] INT : number of elements in column order array
7097 * [I] INT : pointer to column order array
7103 static LRESULT LISTVIEW_SetColumnOrderArray(HWND hwnd, INT iCount, LPINT lpiArray)
7105 /* LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); */
7107 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
7118 * Sets the width of a column
7121 * [I] HWND : window handle
7122 * [I] INT : column index
7123 * [I] INT : column width
7129 static LRESULT LISTVIEW_SetColumnWidth(HWND hwnd, INT iCol, INT cx)
7131 LISTVIEW_INFO *infoPtr;
7134 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
7135 UINT uView = lStyle & LVS_TYPEMASK;
7140 WCHAR text_buffer[DISP_TEXT_SIZE];
7141 INT header_item_count;
7146 WCHAR szDispText[DISP_TEXT_SIZE];
7148 /* make sure we can get the listview info */
7149 if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0)))
7152 if (!infoPtr->hwndHeader) /* make sure we have a header */
7155 /* set column width only if in report or list mode */
7156 if ((uView != LVS_REPORT) && (uView != LVS_LIST))
7159 TRACE("(hwnd=%x, iCol=%d, cx=%d\n", hwnd, iCol, cx);
7161 /* take care of invalid cx values */
7162 if((uView == LVS_REPORT) && (cx < -2))
7163 cx = LVSCW_AUTOSIZE;
7164 else if (uView == LVS_LIST && (cx < 1))
7167 /* resize all columns if in LVS_LIST mode */
7168 if(uView == LVS_LIST) {
7169 infoPtr->nItemWidth = cx;
7170 InvalidateRect(hwnd, NULL, TRUE); /* force redraw of the listview */
7174 /* autosize based on listview items width */
7175 if(cx == LVSCW_AUTOSIZE)
7177 /* set the width of the column to the width of the widest item */
7178 if (iCol == 0 || uView == LVS_LIST)
7181 for(item_index = 0; item_index < GETITEMCOUNT(infoPtr); item_index++)
7183 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, item_index);
7184 cx = (nLabelWidth>cx)?nLabelWidth:cx;
7186 /* I had to add the '3' to prevent clipping of the end of the
7187 line. Probably one of these padding numbers is incorrect. */
7188 if (infoPtr->himlSmall)
7189 cx += WIDTH_PADDING + IMAGE_PADDING + 3;
7193 ZeroMemory(&lvItem, sizeof(lvItem));
7194 lvItem.iSubItem = iCol;
7195 lvItem.mask = LVIF_TEXT;
7196 lvItem.cchTextMax = DISP_TEXT_SIZE;
7197 lvItem.pszText = szDispText;
7198 *lvItem.pszText = '\0';
7200 for(item_index = 0; item_index < GETITEMCOUNT(infoPtr); item_index++)
7202 lvItem.iItem = item_index;
7203 LISTVIEW_GetItemT(hwnd, &lvItem, FALSE, TRUE);
7204 nLabelWidth = LISTVIEW_GetStringWidthT(hwnd, lvItem.pszText, TRUE);
7205 cx = (nLabelWidth>cx)?nLabelWidth:cx;
7208 cx += TRAILING_PADDING;
7209 } /* autosize based on listview header width */
7210 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
7212 header_item_count = Header_GetItemCount(infoPtr->hwndHeader);
7214 /* if iCol is the last column make it fill the remainder of the controls width */
7215 if(iCol == (header_item_count - 1)) {
7216 /* get the width of every item except the current one */
7217 hdi.mask = HDI_WIDTH;
7220 for(item_index = 0; item_index < (header_item_count - 1); item_index++) {
7221 Header_GetItemW(infoPtr->hwndHeader, item_index, (LPARAM)(&hdi));
7225 /* retrieve the layout of the header */
7226 GetWindowRect(infoPtr->hwndHeader, &rcHeader);
7228 cx = (rcHeader.right - rcHeader.left) - cx;
7232 /* Despite what the MS docs say, if this is not the last
7233 column, then MS resizes the column to the width of the
7234 largest text string in the column, including headers
7235 and items. This is different from LVSCW_AUTOSIZE in that
7236 LVSCW_AUTOSIZE ignores the header string length.
7239 /* retrieve header font */
7240 header_font = SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0L, 0L);
7242 /* retrieve header text */
7243 hdi.mask = HDI_TEXT;
7244 hdi.cchTextMax = sizeof(text_buffer)/sizeof(text_buffer[0]);
7245 hdi.pszText = text_buffer;
7247 Header_GetItemW(infoPtr->hwndHeader, iCol, (LPARAM)(&hdi));
7249 /* determine the width of the text in the header */
7251 old_font = SelectObject(hdc, header_font); /* select the font into hdc */
7253 GetTextExtentPoint32W(hdc, text_buffer, lstrlenW(text_buffer), &size);
7255 SelectObject(hdc, old_font); /* restore the old font */
7256 ReleaseDC(hwnd, hdc);
7258 ZeroMemory(&lvItem, sizeof(lvItem));
7259 lvItem.iSubItem = iCol;
7260 lvItem.mask = LVIF_TEXT;
7261 lvItem.cchTextMax = DISP_TEXT_SIZE;
7262 lvItem.pszText = szDispText;
7263 *lvItem.pszText = '\0';
7265 for(item_index = 0; item_index < GETITEMCOUNT(infoPtr); item_index++)
7267 lvItem.iItem = item_index;
7268 LISTVIEW_GetItemT(hwnd, &lvItem, FALSE, TRUE);
7269 nLabelWidth = LISTVIEW_GetStringWidthT(hwnd, lvItem.pszText, TRUE);
7270 nLabelWidth += TRAILING_PADDING;
7271 /* While it is possible for subitems to have icons, even MS messes
7272 up the positioning, so I suspect no applications actually use
7274 if (item_index == 0 && infoPtr->himlSmall)
7275 /* I had to add the '3' to prevent clipping of the end of the
7276 line. Probably one of these padding numbers is incorrect. */
7277 nLabelWidth += WIDTH_PADDING + IMAGE_PADDING + 3;
7278 cx = (nLabelWidth>cx)?nLabelWidth:cx;
7283 /* call header to update the column change */
7284 hdi.mask = HDI_WIDTH;
7287 lret = Header_SetItemW(infoPtr->hwndHeader, (WPARAM)iCol, (LPARAM)&hdi);
7289 InvalidateRect(hwnd, NULL, TRUE); /* force redraw of the listview */
7296 * Sets the extended listview style.
7299 * [I] HWND : window handle
7304 * SUCCESS : previous style
7307 static LRESULT LISTVIEW_SetExtendedListViewStyle(HWND hwnd, DWORD dwMask, DWORD dwStyle)
7309 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7310 DWORD dwOldStyle = infoPtr->dwExStyle;
7314 infoPtr->dwExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
7316 infoPtr->dwExStyle = dwStyle;
7321 /* LISTVIEW_SetHotCursor */
7325 * Sets the hot item index.
7328 * [I] HWND : window handle
7332 * SUCCESS : previous hot item index
7333 * FAILURE : -1 (no hot item)
7335 static LRESULT LISTVIEW_SetHotItem(HWND hwnd, INT iIndex)
7337 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7338 INT iOldIndex = infoPtr->nHotItem;
7341 infoPtr->nHotItem = iIndex;
7348 * Sets the amount of time the cursor must hover over an item before it is selected.
7351 * [I] HWND : window handle
7352 * [I] DWORD : dwHoverTime, if -1 the hover time is set to the default
7355 * Returns the previous hover time
7357 static LRESULT LISTVIEW_SetHoverTime(HWND hwnd, DWORD dwHoverTime)
7359 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7360 DWORD oldHoverTime = infoPtr->dwHoverTime;
7362 infoPtr->dwHoverTime = dwHoverTime;
7364 return oldHoverTime;
7369 * Sets spacing for icons of LVS_ICON style.
7372 * [I] HWND : window handle
7373 * [I] DWORD : MAKELONG(cx, cy)
7376 * MAKELONG(oldcx, oldcy)
7378 static LRESULT LISTVIEW_SetIconSpacing(HWND hwnd, DWORD spacing)
7380 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7381 INT cy = HIWORD(spacing);
7382 INT cx = LOWORD(spacing);
7384 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
7385 UINT uView = lStyle & LVS_TYPEMASK;
7387 oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
7388 if (cx == -1) /* set to default */
7389 cx = GetSystemMetrics(SM_CXICONSPACING);
7390 if (cy == -1) /* set to default */
7391 cy = GetSystemMetrics(SM_CYICONSPACING);
7394 infoPtr->iconSpacing.cx = cx;
7396 { /* if 0 then compute width */
7397 if (uView == LVS_ICON)
7398 FIXME("width computation not yet done\n");
7400 * Should scan each item and determine max width of
7401 * icon or label, then make that the width
7403 else /* FIXME: unknown computation for non LVS_ICON - this is a guess */
7404 infoPtr->iconSpacing.cx = LISTVIEW_GetItemWidth(hwnd);
7407 infoPtr->iconSpacing.cy = cy;
7409 { /* if 0 then compute height */
7410 if (uView == LVS_ICON)
7411 infoPtr->iconSpacing.cy = infoPtr->iconSize.cy + infoPtr->ntmHeight
7412 + ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_OFFSET;
7413 /* FIXME. I don't think so; I think it is based on twice the ntmHeight */
7414 else /* FIXME: unknown computation for non LVS_ICON - this is a guess */
7415 infoPtr->iconSpacing.cy = LISTVIEW_GetItemHeight(hwnd);
7418 TRACE("old=(%d,%d), new=(%ld,%ld)\n", LOWORD(oldspacing), HIWORD(oldspacing),
7419 infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
7421 /* these depend on the iconSpacing */
7422 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
7423 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
7433 * [I] HWND : window handle
7434 * [I] INT : image list type
7435 * [I] HIMAGELIST : image list handle
7438 * SUCCESS : old image list
7441 static HIMAGELIST LISTVIEW_SetImageList(HWND hwnd, INT nType, HIMAGELIST himl)
7443 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7444 HIMAGELIST himlOld = 0;
7450 himlOld = infoPtr->himlNormal;
7451 infoPtr->himlNormal = himl;
7455 himlOld = infoPtr->himlSmall;
7456 infoPtr->himlSmall = himl;
7460 himlOld = infoPtr->himlState;
7461 infoPtr->himlState = himl;
7462 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
7466 oldHeight = infoPtr->nItemHeight;
7467 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
7468 if (infoPtr->nItemHeight != oldHeight)
7469 LISTVIEW_UpdateScroll(hwnd);
7476 * Preallocates memory (does *not* set the actual count of items !)
7479 * [I] HWND : window handle
7480 * [I] INT : item count (projected number of items to allocate)
7481 * [I] DWORD : update flags
7487 static BOOL LISTVIEW_SetItemCount(HWND hwnd, INT nItems, DWORD dwFlags)
7489 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
7491 TRACE("(hwnd=%x, nItems=%d, dwFlags=%lx)\n", hwnd, nItems, dwFlags);
7493 if (GetWindowLongW(hwnd, GWL_STYLE) & LVS_OWNERDATA)
7495 int precount,topvisible;
7497 TRACE("LVS_OWNERDATA is set!\n");
7498 if (dwFlags & (LVSICF_NOINVALIDATEALL | LVSICF_NOSCROLL))
7499 FIXME("flags %s %s not implemented\n",
7500 (dwFlags & LVSICF_NOINVALIDATEALL) ? "LVSICF_NOINVALIDATEALL"
7502 (dwFlags & LVSICF_NOSCROLL) ? "LVSICF_NOSCROLL" : "");
7505 * Internally remove all the selections.
7509 LISTVIEW_SELECTION *selection;
7510 selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,0);
7512 LISTVIEW_RemoveSelectionRange(hwnd,selection->lower,
7515 while (infoPtr->hdpaSelectionRanges->nItemCount>0);
7517 precount = infoPtr->hdpaItems->nItemCount;
7518 topvisible = ListView_GetTopIndex(hwnd) +
7519 LISTVIEW_GetCountPerColumn(hwnd) + 1;
7521 infoPtr->hdpaItems->nItemCount = nItems;
7523 LISTVIEW_UpdateSize(hwnd);
7524 LISTVIEW_UpdateScroll(hwnd);
7525 if (min(precount,infoPtr->hdpaItems->nItemCount)<topvisible)
7526 InvalidateRect(hwnd, NULL, TRUE);
7530 /* According to MSDN for non-LVS_OWNERDATA this is just
7531 * a performance issue. The control allocates its internal
7532 * data structures for the number of items specified. It
7533 * cuts down on the number of memory allocations. Therefore
7534 * we will just issue a WARN here
7536 WARN("for non-ownerdata performance option not implemented.\n");
7544 * Sets the position of an item.
7547 * [I] HWND : window handle
7548 * [I] INT : item index
7549 * [I] LONG : x coordinate
7550 * [I] LONG : y coordinate
7556 static BOOL LISTVIEW_SetItemPosition(HWND hwnd, INT nItem,
7557 LONG nPosX, LONG nPosY)
7559 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
7560 UINT lStyle = GetWindowLongW(hwnd, GWL_STYLE);
7561 UINT uView = lStyle & LVS_TYPEMASK;
7562 LISTVIEW_ITEM *lpItem;
7564 BOOL bResult = FALSE;
7566 TRACE("(hwnd=%x, nItem=%d, X=%ld, Y=%ld)\n", hwnd, nItem, nPosX, nPosY);
7568 if (lStyle & LVS_OWNERDATA)
7571 if ((nItem >= 0) || (nItem < GETITEMCOUNT(infoPtr)))
7573 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
7575 if ( (hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)) )
7577 if ( (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)) )
7581 orig = lpItem->ptPosition;
7582 if ((nPosX == -1) && (nPosY == -1))
7584 /* This point value seems to be an undocumented feature. The
7585 * best guess is that it means either at the origin, or at
7586 * the true beginning of the list. I will assume the origin.
7589 if (!LISTVIEW_GetOrigin(hwnd, &pt1))
7596 if (uView == LVS_ICON)
7598 nPosX += (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
7599 nPosY += ICON_TOP_PADDING;
7601 TRACE("requested special (-1,-1), set to origin (%ld,%ld)\n",
7605 lpItem->ptPosition.x = nPosX;
7606 lpItem->ptPosition.y = nPosY;
7607 if (uView == LVS_ICON)
7609 lpItem->ptPosition.y -= ICON_TOP_PADDING;
7610 lpItem->ptPosition.x -= (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
7611 if ((lpItem->ptPosition.y < 0) || (lpItem->ptPosition.x < 0))
7613 FIXME("failed orig (%ld,%ld), intent (%ld,%ld), is (%ld, %ld), setting neg to 0\n",
7614 orig.x, orig.y, nPosX, nPosY, lpItem->ptPosition.x, lpItem->ptPosition.y);
7617 if (lpItem->ptPosition.x < 0) lpItem->ptPosition.x = 0;
7618 if (lpItem->ptPosition.y < 0) lpItem->ptPosition.y = 0;
7623 TRACE("orig (%ld,%ld), intent (%ld,%ld), is (%ld,%ld)\n",
7624 orig.x, orig.y, nPosX, nPosY, lpItem->ptPosition.x, lpItem->ptPosition.y);
7637 * Sets the state of one or many items.
7640 * [I] HWND : window handle
7641 * [I]INT : item index
7642 * [I] LPLVITEM : item or subitem info
7648 static LRESULT LISTVIEW_SetItemState(HWND hwnd, INT nItem, LPLVITEMW lpLVItem)
7650 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7651 BOOL bResult = TRUE;
7654 TRACE("(hwnd=%x, nItem=%d, lpLVItem=%s)\n",
7655 hwnd, nItem, debuglvitem_t(lpLVItem, TRUE));
7657 ZeroMemory(&lvItem, sizeof(lvItem));
7658 lvItem.mask = LVIF_STATE;
7659 lvItem.state = lpLVItem->state;
7660 lvItem.stateMask = lpLVItem->stateMask ;
7661 lvItem.iItem = nItem;
7665 /* apply to all items */
7666 for (lvItem.iItem = 0; lvItem.iItem < GETITEMCOUNT(infoPtr); lvItem.iItem++)
7667 if (!ListView_SetItemW(hwnd, &lvItem)) bResult = FALSE;
7670 bResult = ListView_SetItemW(hwnd, &lvItem);
7677 * Sets the text of an item or subitem.
7680 * [I] hwnd : window handle
7681 * [I] nItem : item index
7682 * [I] lpLVItem : item or subitem info
7683 * [I] isW : TRUE if input is Unicode
7689 static BOOL LISTVIEW_SetItemTextT(HWND hwnd, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
7691 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7692 BOOL bResult = FALSE;
7695 TRACE("(hwnd=%x, nItem=%d, lpLVItem=%s, isW=%d)\n",
7696 hwnd, nItem, debuglvitem_t(lpLVItem, isW), isW);
7698 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
7700 ZeroMemory(&lvItem, sizeof(LVITEMW));
7701 lvItem.mask = LVIF_TEXT;
7702 lvItem.pszText = lpLVItem->pszText;
7703 lvItem.iItem = nItem;
7704 lvItem.iSubItem = lpLVItem->iSubItem;
7705 if(isW) bResult = ListView_SetItemW(hwnd, &lvItem);
7706 else bResult = ListView_SetItemA(hwnd, &lvItem);
7714 * Set item index that marks the start of a multiple selection.
7717 * [I] HWND : window handle
7721 * Index number or -1 if there is no selection mark.
7723 static LRESULT LISTVIEW_SetSelectionMark(HWND hwnd, INT nIndex)
7725 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7726 INT nOldIndex = infoPtr->nSelectionMark;
7728 TRACE("(hwnd=%x, nIndex=%d)\n", hwnd, nIndex);
7730 infoPtr->nSelectionMark = nIndex;
7737 * Sets the text background color.
7740 * [I] HWND : window handle
7741 * [I] COLORREF : text background color
7747 static LRESULT LISTVIEW_SetTextBkColor(HWND hwnd, COLORREF clrTextBk)
7749 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7751 TRACE("(hwnd=%x, clrTextBk=%lx)\n", hwnd, clrTextBk);
7753 infoPtr->clrTextBk = clrTextBk;
7754 InvalidateRect(hwnd, NULL, TRUE);
7761 * Sets the text foreground color.
7764 * [I] HWND : window handle
7765 * [I] COLORREF : text color
7771 static LRESULT LISTVIEW_SetTextColor (HWND hwnd, COLORREF clrText)
7773 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7775 TRACE("(hwnd=%x, clrText=%lx)\n", hwnd, clrText);
7777 infoPtr->clrText = clrText;
7778 InvalidateRect(hwnd, NULL, TRUE);
7783 /* LISTVIEW_SetToolTips */
7784 /* LISTVIEW_SetUnicodeFormat */
7785 /* LISTVIEW_SetWorkAreas */
7789 * Callback internally used by LISTVIEW_SortItems()
7792 * [I] LPVOID : first LISTVIEW_ITEM to compare
7793 * [I] LPVOID : second LISTVIEW_ITEM to compare
7794 * [I] LPARAM : HWND of control
7797 * if first comes before second : negative
7798 * if first comes after second : positive
7799 * if first and second are equivalent : zero
7801 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
7803 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW((HWND)lParam, 0);
7804 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 );
7805 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 );
7807 /* Forward the call to the client defined callback */
7808 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
7813 * Sorts the listview items.
7816 * [I] HWND : window handle
7817 * [I] WPARAM : application-defined value
7818 * [I] LPARAM : pointer to comparision callback
7824 static LRESULT LISTVIEW_SortItems(HWND hwnd, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
7826 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7827 UINT lStyle = GetWindowLongW(hwnd, GWL_STYLE);
7828 HDPA hdpaSubItems=NULL;
7829 LISTVIEW_ITEM *pLVItem=NULL;
7830 LPVOID selectionMarkItem;
7833 TRACE("(hwnd=%x, pfnCompare=%p, lParamSort=%lx)\n", hwnd, pfnCompare, lParamSort);
7835 if (lStyle & LVS_OWNERDATA) return FALSE;
7837 if (!infoPtr || !infoPtr->hdpaItems) return FALSE;
7839 nCount = GETITEMCOUNT(infoPtr);
7840 /* if there are 0 or 1 items, there is no need to sort */
7844 infoPtr->pfnCompare = pfnCompare;
7845 infoPtr->lParamSort = lParamSort;
7846 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, hwnd);
7848 /* Adjust selections and indices so that they are the way they should
7849 * be after the sort (otherwise, the list items move around, but
7850 * whatever is at the item's previous original position will be
7853 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
7854 for (i=0; i < nCount; i++)
7856 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
7857 pLVItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
7859 if (pLVItem->state & LVIS_SELECTED)
7860 LISTVIEW_AddSelectionRange(hwnd, i, i);
7862 LISTVIEW_RemoveSelectionRange(hwnd, i, i);
7863 if (pLVItem->state & LVIS_FOCUSED)
7864 infoPtr->nFocusedItem=i;
7866 if (selectionMarkItem != NULL)
7867 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
7868 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7870 /* align the items */
7871 LISTVIEW_AlignTop(hwnd);
7873 /* refresh the display */
7874 InvalidateRect(hwnd, NULL, TRUE);
7879 /* LISTVIEW_SubItemHitTest */
7883 * Updates an items or rearranges the listview control.
7886 * [I] HWND : window handle
7887 * [I] INT : item index
7893 static LRESULT LISTVIEW_Update(HWND hwnd, INT nItem)
7895 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7896 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
7897 BOOL bResult = FALSE;
7900 TRACE("(hwnd=%x, nItem=%d)\n", hwnd, nItem);
7902 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
7906 /* rearrange with default alignment style */
7907 if ((lStyle & LVS_AUTOARRANGE) && (((lStyle & LVS_TYPEMASK) == LVS_ICON) ||
7908 ((lStyle & LVS_TYPEMASK) == LVS_SMALLICON)))
7910 ListView_Arrange(hwnd, 0);
7914 /* get item bounding rectangle */
7915 ListView_GetItemRect(hwnd, nItem, &rc, LVIR_BOUNDS);
7916 InvalidateRect(hwnd, &rc, TRUE);
7925 * Creates the listview control.
7928 * [I] HWND : window handle
7933 static LRESULT LISTVIEW_Create(HWND hwnd, LPCREATESTRUCTW lpcs)
7935 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7936 UINT uView = lpcs->style & LVS_TYPEMASK;
7939 TRACE("(hwnd=%x, lpcs=%p)\n", hwnd, lpcs);
7941 /* initialize info pointer */
7942 ZeroMemory(infoPtr, sizeof(LISTVIEW_INFO));
7944 /* determine the type of structures to use */
7945 infoPtr->notifyFormat = SendMessageW(GetParent(hwnd), WM_NOTIFYFORMAT,
7946 (WPARAM)hwnd, (LPARAM)NF_QUERY);
7948 /* initialize color information */
7949 infoPtr->clrBk = GetSysColor(COLOR_WINDOW);
7950 infoPtr->clrText = GetSysColor(COLOR_WINDOWTEXT);
7951 infoPtr->clrTextBk = CLR_DEFAULT;
7953 /* set default values */
7954 infoPtr->hwndSelf = hwnd;
7955 infoPtr->uCallbackMask = 0;
7956 infoPtr->nFocusedItem = -1;
7957 infoPtr->nSelectionMark = -1;
7958 infoPtr->nHotItem = -1;
7959 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
7960 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
7961 ZeroMemory(&infoPtr->rcList, sizeof(RECT));
7962 infoPtr->hwndEdit = 0;
7963 infoPtr->pedititem = NULL;
7964 infoPtr->nEditLabelItem = -1;
7966 /* get default font (icon title) */
7967 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
7968 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
7969 infoPtr->hFont = infoPtr->hDefaultFont;
7970 LISTVIEW_SaveTextMetrics(hwnd);
7973 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, (LPCWSTR)NULL,
7974 WS_CHILD | HDS_HORZ | HDS_BUTTONS,
7975 0, 0, 0, 0, hwnd, (HMENU)0,
7976 lpcs->hInstance, NULL);
7978 /* set header font */
7979 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont,
7982 if (uView == LVS_ICON)
7984 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
7985 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
7987 else if (uView == LVS_REPORT)
7989 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
7991 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
7995 /* set HDS_HIDDEN flag to hide the header bar */
7996 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
7997 GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
8001 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8002 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8006 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8007 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8010 /* display unsupported listview window styles */
8011 LISTVIEW_UnsupportedStyles(lpcs->style);
8013 /* allocate memory for the data structure */
8014 infoPtr->hdpaItems = DPA_Create(10);
8016 /* allocate memory for the selection ranges */
8017 infoPtr->hdpaSelectionRanges = DPA_Create(10);
8019 /* initialize size of items */
8020 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8021 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
8023 /* initialize the hover time to -1(indicating the default system hover time) */
8024 infoPtr->dwHoverTime = -1;
8031 * Erases the background of the listview control.
8034 * [I] HWND : window handle
8035 * [I] WPARAM : device context handle
8036 * [I] LPARAM : not used
8042 static LRESULT LISTVIEW_EraseBackground(HWND hwnd, WPARAM wParam,
8045 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8048 TRACE("(hwnd=%x, wParam=%x, lParam=%lx)\n", hwnd, wParam, lParam);
8050 if (infoPtr->clrBk == CLR_NONE)
8052 bResult = SendMessageW(GetParent(hwnd), WM_ERASEBKGND, wParam, lParam);
8057 HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk);
8058 GetClientRect(hwnd, &rc);
8059 FillRect((HDC)wParam, &rc, hBrush);
8060 DeleteObject(hBrush);
8068 static void LISTVIEW_FillBackground(HWND hwnd, HDC hdc, LPRECT rc)
8070 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8072 TRACE("(hwnd=%x, hdc=%x, rc=%p)\n", hwnd, hdc, rc);
8074 if (infoPtr->clrBk != CLR_NONE)
8076 HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk);
8077 FillRect(hdc, rc, hBrush);
8078 DeleteObject(hBrush);
8084 * Retrieves the listview control font.
8087 * [I] HWND : window handle
8092 static LRESULT LISTVIEW_GetFont(HWND hwnd)
8094 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8096 TRACE("(hwnd=%x)\n", hwnd);
8098 return infoPtr->hFont;
8103 * Performs vertical scrolling.
8106 * [I] HWND : window handle
8107 * [I] INT : scroll code
8108 * [I] SHORT : current scroll position if scroll code is SB_THUMBPOSITION
8110 * [I] HWND : scrollbar control window handle
8115 static LRESULT LISTVIEW_VScroll(HWND hwnd, INT nScrollCode, SHORT nCurrentPos,
8118 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8119 SCROLLINFO scrollInfo;
8121 TRACE("(hwnd=%x, nScrollCode=%d, nCurrentPos=%d, hScrollWnd=%x)\n",
8122 hwnd, nScrollCode, nCurrentPos, hScrollWnd);
8124 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8126 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
8127 scrollInfo.cbSize = sizeof(SCROLLINFO);
8128 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
8130 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
8132 INT nOldScrollPos = scrollInfo.nPos;
8133 switch (nScrollCode)
8136 if (scrollInfo.nPos > scrollInfo.nMin)
8141 if (scrollInfo.nPos < scrollInfo.nMax)
8146 if (scrollInfo.nPos > scrollInfo.nMin)
8148 if (scrollInfo.nPos >= scrollInfo.nPage)
8149 scrollInfo.nPos -= scrollInfo.nPage;
8151 scrollInfo.nPos = scrollInfo.nMin;
8156 if (scrollInfo.nPos < scrollInfo.nMax)
8158 if (scrollInfo.nPos <= scrollInfo.nMax - scrollInfo.nPage)
8159 scrollInfo.nPos += scrollInfo.nPage;
8161 scrollInfo.nPos = scrollInfo.nMax;
8165 case SB_THUMBPOSITION:
8167 scrollInfo.nPos = nCurrentPos;
8168 if (scrollInfo.nPos > scrollInfo.nMax)
8169 scrollInfo.nPos=scrollInfo.nMax;
8171 if (scrollInfo.nPos < scrollInfo.nMin)
8172 scrollInfo.nPos=scrollInfo.nMin;
8177 if (nOldScrollPos != scrollInfo.nPos)
8179 scrollInfo.fMask = SIF_POS;
8180 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
8181 if (IsWindowVisible(infoPtr->hwndHeader))
8183 RECT rListview, rcHeader, rDest;
8184 GetClientRect(hwnd, &rListview);
8185 GetWindowRect(infoPtr->hwndHeader, &rcHeader);
8186 MapWindowPoints((HWND) NULL, hwnd, (LPPOINT) &rcHeader, 2);
8187 SubtractRect(&rDest, &rListview, &rcHeader);
8188 InvalidateRect(hwnd, &rDest, TRUE);
8191 InvalidateRect(hwnd, NULL, TRUE);
8200 * Performs horizontal scrolling.
8203 * [I] HWND : window handle
8204 * [I] INT : scroll code
8205 * [I] SHORT : current scroll position if scroll code is SB_THUMBPOSITION
8207 * [I] HWND : scrollbar control window handle
8212 static LRESULT LISTVIEW_HScroll(HWND hwnd, INT nScrollCode, SHORT nCurrentPos,
8215 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8216 SCROLLINFO scrollInfo;
8218 TRACE("(hwnd=%x, nScrollCode=%d, nCurrentPos=%d, hScrollWnd=%x)\n",
8219 hwnd, nScrollCode, nCurrentPos, hScrollWnd);
8221 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8223 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
8224 scrollInfo.cbSize = sizeof(SCROLLINFO);
8225 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
8227 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
8229 INT nOldScrollPos = scrollInfo.nPos;
8231 switch (nScrollCode)
8234 if (scrollInfo.nPos > scrollInfo.nMin)
8239 if (scrollInfo.nPos < scrollInfo.nMax)
8244 if (scrollInfo.nPos > scrollInfo.nMin)
8246 if (scrollInfo.nPos >= scrollInfo.nPage)
8247 scrollInfo.nPos -= scrollInfo.nPage;
8249 scrollInfo.nPos = scrollInfo.nMin;
8254 if (scrollInfo.nPos < scrollInfo.nMax)
8256 if (scrollInfo.nPos <= scrollInfo.nMax - scrollInfo.nPage)
8257 scrollInfo.nPos += scrollInfo.nPage;
8259 scrollInfo.nPos = scrollInfo.nMax;
8263 case SB_THUMBPOSITION:
8265 scrollInfo.nPos = nCurrentPos;
8267 if (scrollInfo.nPos > scrollInfo.nMax)
8268 scrollInfo.nPos=scrollInfo.nMax;
8270 if (scrollInfo.nPos < scrollInfo.nMin)
8271 scrollInfo.nPos=scrollInfo.nMin;
8275 if (nOldScrollPos != scrollInfo.nPos)
8277 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8278 scrollInfo.fMask = SIF_POS;
8279 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
8280 if(uView == LVS_REPORT)
8282 scrollInfo.fMask = SIF_POS;
8283 GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
8284 LISTVIEW_UpdateHeaderSize(hwnd, scrollInfo.nPos);
8286 InvalidateRect(hwnd, NULL, TRUE);
8293 static LRESULT LISTVIEW_MouseWheel(HWND hwnd, INT wheelDelta)
8295 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8296 INT gcWheelDelta = 0;
8297 UINT pulScrollLines = 3;
8298 SCROLLINFO scrollInfo;
8300 TRACE("(hwnd=%x, wheelDelta=%d)\n", hwnd, wheelDelta);
8302 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
8303 gcWheelDelta -= wheelDelta;
8305 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
8306 scrollInfo.cbSize = sizeof(SCROLLINFO);
8307 scrollInfo.fMask = SIF_POS | SIF_RANGE;
8314 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
8315 * should be fixed in the future.
8317 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
8318 LISTVIEW_VScroll(hwnd, SB_THUMBPOSITION, scrollInfo.nPos + (gcWheelDelta < 0) ? 37 : -37, 0);
8322 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
8324 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
8326 int cLineScroll = min(LISTVIEW_GetCountPerColumn(hwnd), pulScrollLines);
8327 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
8328 LISTVIEW_VScroll(hwnd, SB_THUMBPOSITION, scrollInfo.nPos + cLineScroll, 0);
8334 LISTVIEW_HScroll(hwnd, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
8345 * [I] HWND : window handle
8346 * [I] INT : virtual key
8347 * [I] LONG : key data
8352 static LRESULT LISTVIEW_KeyDown(HWND hwnd, INT nVirtualKey, LONG lKeyData)
8354 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8355 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8357 NMLVKEYDOWN nmKeyDown;
8359 TRACE("(hwnd=%x, nVirtualKey=%d, lKeyData=%ld)\n", hwnd, nVirtualKey, lKeyData);
8361 /* send LVN_KEYDOWN notification */
8362 nmKeyDown.wVKey = nVirtualKey;
8363 nmKeyDown.flags = 0;
8364 notify(hwnd, LVN_KEYDOWN, &nmKeyDown.hdr);
8366 switch (nVirtualKey)
8369 if ((GETITEMCOUNT(infoPtr) > 0) && (infoPtr->nFocusedItem != -1))
8371 hdr_notify(hwnd, NM_RETURN); /* NM_RETURN notification */
8372 hdr_notify(hwnd, LVN_ITEMACTIVATE); /* LVN_ITEMACTIVATE notification */
8377 if (GETITEMCOUNT(infoPtr) > 0)
8382 if (GETITEMCOUNT(infoPtr) > 0)
8383 nItem = GETITEMCOUNT(infoPtr) - 1;
8387 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_TOLEFT);
8391 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_ABOVE);
8395 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_TORIGHT);
8399 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_BELOW);
8403 if (uView == LVS_REPORT)
8404 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(hwnd);
8406 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(hwnd)
8407 * LISTVIEW_GetCountPerRow(hwnd);
8408 if(nItem < 0) nItem = 0;
8412 if (uView == LVS_REPORT)
8413 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(hwnd);
8415 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(hwnd)
8416 * LISTVIEW_GetCountPerRow(hwnd);
8417 if(nItem >= GETITEMCOUNT(infoPtr)) nItem = GETITEMCOUNT(infoPtr) - 1;
8421 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem))
8423 if (LISTVIEW_KeySelection(hwnd, nItem))
8424 UpdateWindow(hwnd); /* update client area */
8435 * [I] HWND : window handle
8440 static LRESULT LISTVIEW_KillFocus(HWND hwnd)
8442 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
8443 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8446 TRACE("(hwnd=%x)\n", hwnd);
8448 /* send NM_KILLFOCUS notification */
8449 hdr_notify(hwnd, NM_KILLFOCUS);
8451 /* set window focus flag */
8452 infoPtr->bFocus = FALSE;
8454 /* NEED drawing optimization ; redraw the selected items */
8455 if (uView & LVS_REPORT)
8457 nTop = LISTVIEW_GetTopIndex(hwnd);
8459 LISTVIEW_GetCountPerColumn(hwnd) + 1;
8464 nBottom = GETITEMCOUNT(infoPtr);
8466 for (i = nTop; i<nBottom; i++)
8468 if (LISTVIEW_IsSelected(hwnd,i))
8471 rcItem.left = LVIR_BOUNDS;
8472 LISTVIEW_GetItemRect(hwnd, i, &rcItem);
8473 InvalidateRect(hwnd, &rcItem, FALSE);
8482 * Processes double click messages (left mouse button).
8485 * [I] HWND : window handle
8486 * [I] WORD : key flag
8487 * [I] WORD : x coordinate
8488 * [I] WORD : y coordinate
8493 static LRESULT LISTVIEW_LButtonDblClk(HWND hwnd, WORD wKey, WORD wPosX,
8496 LVHITTESTINFO htInfo;
8499 TRACE("(hwnd=%x, key=%hu, X=%hu, Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8501 htInfo.pt.x = wPosX;
8502 htInfo.pt.y = wPosY;
8504 /* send NM_DBLCLK notification */
8505 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8506 if (LISTVIEW_HitTestItem(hwnd, &htInfo, TRUE) != -1)
8508 nmlv.iItem = htInfo.iItem;
8509 nmlv.iSubItem = htInfo.iSubItem;
8516 nmlv.ptAction.x = wPosX;
8517 nmlv.ptAction.y = wPosY;
8518 listview_notify(hwnd, NM_DBLCLK, &nmlv);
8521 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
8522 if(nmlv.iItem != -1)
8523 hdr_notify(hwnd, LVN_ITEMACTIVATE);
8530 * Processes mouse down messages (left mouse button).
8533 * [I] HWND : window handle
8534 * [I] WORD : key flag
8535 * [I] WORD : x coordinate
8536 * [I] WORD : y coordinate
8541 static LRESULT LISTVIEW_LButtonDown(HWND hwnd, WORD wKey, WORD wPosX,
8544 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8545 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
8546 static BOOL bGroupSelect = TRUE;
8550 TRACE("(hwnd=%x, key=%hu, X=%hu, Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8552 /* send NM_RELEASEDCAPTURE notification */
8553 hdr_notify(hwnd, NM_RELEASEDCAPTURE);
8555 if (infoPtr->bFocus == FALSE)
8558 /* set left button down flag */
8559 infoPtr->bLButtonDown = TRUE;
8561 ptPosition.x = wPosX;
8562 ptPosition.y = wPosY;
8563 nItem = LISTVIEW_MouseSelection(hwnd, ptPosition);
8564 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
8566 if (lStyle & LVS_SINGLESEL)
8568 if ((ListView_GetItemState(hwnd, nItem, LVIS_SELECTED) & LVIS_SELECTED)
8569 && infoPtr->nEditLabelItem == -1)
8570 infoPtr->nEditLabelItem = nItem;
8572 LISTVIEW_SetSelection(hwnd, nItem);
8576 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
8579 LISTVIEW_AddGroupSelection(hwnd, nItem);
8581 LISTVIEW_AddSelection(hwnd, nItem);
8583 else if (wKey & MK_CONTROL)
8585 bGroupSelect = LISTVIEW_ToggleSelection(hwnd, nItem);
8587 else if (wKey & MK_SHIFT)
8589 LISTVIEW_SetGroupSelection(hwnd, nItem);
8594 (ListView_GetItemState(hwnd, nItem, LVIS_SELECTED) & LVIS_SELECTED);
8596 /* set selection (clears other pre-existing selections) */
8597 LISTVIEW_SetSelection(hwnd, nItem);
8599 if (was_selected && infoPtr->nEditLabelItem == -1)
8600 infoPtr->nEditLabelItem = nItem;
8606 /* remove all selections */
8607 LISTVIEW_RemoveAllSelections(hwnd);
8610 /* redraw if we could have possibly selected something */
8611 if(!GETITEMCOUNT(infoPtr)) InvalidateRect(hwnd, NULL, TRUE);
8618 * Processes mouse up messages (left mouse button).
8621 * [I] HWND : window handle
8622 * [I] WORD : key flag
8623 * [I] WORD : x coordinate
8624 * [I] WORD : y coordinate
8629 static LRESULT LISTVIEW_LButtonUp(HWND hwnd, WORD wKey, WORD wPosX,
8632 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8634 TRACE("(hwnd=%x, key=%hu, X=%hu, Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8636 if (infoPtr->bLButtonDown != FALSE)
8638 LVHITTESTINFO lvHitTestInfo;
8641 lvHitTestInfo.pt.x = wPosX;
8642 lvHitTestInfo.pt.y = wPosY;
8644 /* send NM_CLICK notification */
8645 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8646 if (LISTVIEW_HitTestItem(hwnd, &lvHitTestInfo, TRUE) != -1)
8648 nmlv.iItem = lvHitTestInfo.iItem;
8649 nmlv.iSubItem = lvHitTestInfo.iSubItem;
8656 nmlv.ptAction.x = wPosX;
8657 nmlv.ptAction.y = wPosY;
8658 listview_notify(hwnd, NM_CLICK, &nmlv);
8660 /* set left button flag */
8661 infoPtr->bLButtonDown = FALSE;
8663 if(infoPtr->nEditLabelItem != -1)
8665 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem)
8666 LISTVIEW_EditLabelT(hwnd, lvHitTestInfo.iItem, TRUE);
8667 infoPtr->nEditLabelItem = -1;
8676 * Creates the listview control (called before WM_CREATE).
8679 * [I] HWND : window handle
8680 * [I] WPARAM : unhandled
8681 * [I] LPARAM : widow creation info
8686 static LRESULT LISTVIEW_NCCreate(HWND hwnd, WPARAM wParam, LPARAM lParam)
8688 LISTVIEW_INFO *infoPtr;
8690 TRACE("(hwnd=%x, wParam=%x, lParam=%lx)\n", hwnd, wParam, lParam);
8692 /* allocate memory for info structure */
8693 infoPtr = (LISTVIEW_INFO *)COMCTL32_Alloc(sizeof(LISTVIEW_INFO));
8694 if (infoPtr == NULL)
8696 ERR("could not allocate info memory!\n");
8700 SetWindowLongW(hwnd, 0, (LONG)infoPtr);
8701 if ((LISTVIEW_INFO *)GetWindowLongW(hwnd, 0) != infoPtr)
8703 ERR("pointer assignment error!\n");
8707 return DefWindowProcW(hwnd, WM_NCCREATE, wParam, lParam);
8712 * Destroys the listview control (called after WM_DESTROY).
8715 * [I] HWND : window handle
8720 static LRESULT LISTVIEW_NCDestroy(HWND hwnd)
8722 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8724 TRACE("(hwnd=%x)\n", hwnd);
8726 /* delete all items */
8727 LISTVIEW_DeleteAllItems(hwnd);
8729 /* destroy data structure */
8730 DPA_Destroy(infoPtr->hdpaItems);
8731 DPA_Destroy(infoPtr->hdpaSelectionRanges);
8734 infoPtr->hFont = (HFONT)0;
8735 if (infoPtr->hDefaultFont)
8737 DeleteObject(infoPtr->hDefaultFont);
8740 /* free listview info pointer*/
8741 COMCTL32_Free(infoPtr);
8743 SetWindowLongW(hwnd, 0, 0);
8749 * Handles notifications from children.
8752 * [I] HWND : window handle
8753 * [I] INT : control identifier
8754 * [I] LPNMHDR : notification information
8759 static LRESULT LISTVIEW_Notify(HWND hwnd, INT nCtrlId, LPNMHDR lpnmh)
8761 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8763 TRACE("(hwnd=%x, nCtrlId=%d, lpnmh=%p)\n", hwnd, nCtrlId, lpnmh);
8765 if (lpnmh->hwndFrom == infoPtr->hwndHeader)
8767 /* handle notification from header control */
8768 if (lpnmh->code == HDN_ENDTRACKW)
8770 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8771 InvalidateRect(hwnd, NULL, TRUE);
8773 else if(lpnmh->code == HDN_ITEMCLICKW)
8775 /* Handle sorting by Header Column */
8778 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8780 nmlv.iSubItem = ((LPNMHEADERW)lpnmh)->iItem;
8781 listview_notify(hwnd, LVN_COLUMNCLICK, &nmlv);
8783 else if(lpnmh->code == NM_RELEASEDCAPTURE)
8785 /* Idealy this should be done in HDN_ENDTRACKA
8786 * but since SetItemBounds in Header.c is called after
8787 * the notification is sent, it is neccessary to handle the
8788 * update of the scroll bar here (Header.c works fine as it is,
8789 * no need to disturb it)
8791 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8792 LISTVIEW_UpdateScroll(hwnd);
8793 InvalidateRect(hwnd, NULL, TRUE);
8803 * Determines the type of structure to use.
8806 * [I] HWND : window handle of the sender
8807 * [I] HWND : listview window handle
8808 * [I] INT : command specifying the nature of the WM_NOTIFYFORMAT
8813 static LRESULT LISTVIEW_NotifyFormat(HWND hwndFrom, HWND hwnd, INT nCommand)
8815 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8817 TRACE("(hwndFrom=%x, hwnd=%x, nCommand=%d)\n", hwndFrom, hwnd, nCommand);
8819 if (nCommand == NF_REQUERY)
8820 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT,
8821 (WPARAM)hwnd, (LPARAM)NF_QUERY);
8827 * Paints/Repaints the listview control.
8830 * [I] HWND : window handle
8831 * [I] HDC : device context handle
8836 static LRESULT LISTVIEW_Paint(HWND hwnd, HDC hdc)
8840 TRACE("(hwnd=%x, hdc=%x)\n", hwnd, hdc);
8844 hdc = BeginPaint(hwnd, &ps);
8845 LISTVIEW_Refresh(hwnd, hdc);
8846 EndPaint(hwnd, &ps);
8850 LISTVIEW_Refresh(hwnd, hdc);
8858 * Processes double click messages (right mouse button).
8861 * [I] HWND : window handle
8862 * [I] WORD : key flag
8863 * [I] WORD : x coordinate
8864 * [I] WORD : y coordinate
8869 static LRESULT LISTVIEW_RButtonDblClk(HWND hwnd, WORD wKey, WORD wPosX,
8872 TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8874 /* send NM_RELEASEDCAPTURE notification */
8875 hdr_notify(hwnd, NM_RELEASEDCAPTURE);
8877 /* send NM_RDBLCLK notification */
8878 hdr_notify(hwnd, NM_RDBLCLK);
8885 * Processes mouse down messages (right mouse button).
8888 * [I] HWND : window handle
8889 * [I] WORD : key flag
8890 * [I] WORD : x coordinate
8891 * [I] WORD : y coordinate
8896 static LRESULT LISTVIEW_RButtonDown(HWND hwnd, WORD wKey, WORD wPosX,
8899 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8903 LVHITTESTINFO lvHitTestInfo;
8905 TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8907 /* send NM_RELEASEDCAPTURE notification */
8908 hdr_notify(hwnd, NM_RELEASEDCAPTURE);
8910 /* make sure the listview control window has the focus */
8911 if (infoPtr->bFocus == FALSE)
8914 /* set right button down flag */
8915 infoPtr->bRButtonDown = TRUE;
8917 /* determine the index of the selected item */
8918 ptPosition.x = wPosX;
8919 ptPosition.y = wPosY;
8920 nItem = LISTVIEW_MouseSelection(hwnd, ptPosition);
8921 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
8923 LISTVIEW_SetItemFocus(hwnd,nItem);
8924 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
8925 !LISTVIEW_IsSelected(hwnd,nItem))
8926 LISTVIEW_SetSelection(hwnd, nItem);
8930 LISTVIEW_RemoveAllSelections(hwnd);
8933 lvHitTestInfo.pt.x = wPosX;
8934 lvHitTestInfo.pt.y = wPosY;
8936 /* Send NM_RClICK notification */
8937 ZeroMemory(&nmlv, sizeof(nmlv));
8938 if (LISTVIEW_HitTestItem(hwnd, &lvHitTestInfo, TRUE) != -1)
8940 nmlv.iItem = lvHitTestInfo.iItem;
8941 nmlv.iSubItem = lvHitTestInfo.iSubItem;
8948 nmlv.ptAction.x = wPosX;
8949 nmlv.ptAction.y = wPosY;
8950 listview_notify(hwnd, NM_RCLICK, &nmlv);
8957 * Processes mouse up messages (right mouse button).
8960 * [I] HWND : window handle
8961 * [I] WORD : key flag
8962 * [I] WORD : x coordinate
8963 * [I] WORD : y coordinate
8968 static LRESULT LISTVIEW_RButtonUp(HWND hwnd, WORD wKey, WORD wPosX,
8971 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8973 TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8975 if (infoPtr->bRButtonDown)
8982 /* set button flag */
8983 infoPtr->bRButtonDown = FALSE;
8985 /* Change to screen coordinate for WM_CONTEXTMENU */
8986 ClientToScreen(hwnd, &pt);
8988 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
8989 SendMessageW( hwnd, WM_CONTEXTMENU, (WPARAM) hwnd, MAKELPARAM(pt.x, pt.y));
9000 * [I] HWND : window handle
9001 * [I] HWND : window handle of previously focused window
9006 static LRESULT LISTVIEW_SetFocus(HWND hwnd, HWND hwndLoseFocus)
9008 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9010 TRACE("(hwnd=%x, hwndLoseFocus=%x)\n", hwnd, hwndLoseFocus);
9012 /* send NM_SETFOCUS notification */
9013 hdr_notify(hwnd, NM_SETFOCUS);
9015 /* set window focus flag */
9016 infoPtr->bFocus = TRUE;
9028 * [I] HWND : window handle
9029 * [I] HFONT : font handle
9030 * [I] WORD : redraw flag
9035 static LRESULT LISTVIEW_SetFont(HWND hwnd, HFONT hFont, WORD fRedraw)
9037 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9038 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
9040 TRACE("(hwnd=%x,hfont=%x,redraw=%hu)\n", hwnd, hFont, fRedraw);
9042 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
9043 LISTVIEW_SaveTextMetrics(hwnd);
9045 if (uView == LVS_REPORT)
9047 /* set header font */
9048 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont,
9049 MAKELPARAM(fRedraw, 0));
9052 /* invalidate listview control client area */
9053 InvalidateRect(hwnd, NULL, TRUE);
9055 if (fRedraw != FALSE)
9063 * Message handling for WM_SETREDRAW.
9064 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
9067 * [I] HWND : window handle
9068 * [I] bRedraw: state of redraw flag
9071 * DefWinProc return value
9073 static LRESULT LISTVIEW_SetRedraw(HWND hwnd, BOOL bRedraw)
9075 LRESULT lResult = DefWindowProcW(hwnd, WM_SETREDRAW, bRedraw, 0);
9077 RedrawWindow(hwnd, NULL, 0,
9078 RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ALLCHILDREN | RDW_ERASENOW);
9084 * Resizes the listview control. This function processes WM_SIZE
9085 * messages. At this time, the width and height are not used.
9088 * [I] HWND : window handle
9089 * [I] WORD : new width
9090 * [I] WORD : new height
9095 static LRESULT LISTVIEW_Size(HWND hwnd, int Width, int Height)
9097 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
9098 UINT uView = lStyle & LVS_TYPEMASK;
9100 TRACE("(hwnd=%x, width=%d, height=%d)\n", hwnd, Width, Height);
9102 LISTVIEW_UpdateSize(hwnd);
9104 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
9106 if (lStyle & LVS_ALIGNLEFT)
9107 LISTVIEW_AlignLeft(hwnd);
9109 LISTVIEW_AlignTop(hwnd);
9112 LISTVIEW_UpdateScroll(hwnd);
9114 /* invalidate client area + erase background */
9115 InvalidateRect(hwnd, NULL, TRUE);
9122 * Sets the size information.
9125 * [I] HWND : window handle
9130 static VOID LISTVIEW_UpdateSize(HWND hwnd)
9132 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9133 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
9134 UINT uView = lStyle & LVS_TYPEMASK;
9137 TRACE("(hwnd=%x)\n", hwnd);
9139 GetClientRect(hwnd, &rcList);
9140 infoPtr->rcList.left = 0;
9141 infoPtr->rcList.right = max(rcList.right - rcList.left, 1);
9142 infoPtr->rcList.top = 0;
9143 infoPtr->rcList.bottom = max(rcList.bottom - rcList.top, 1);
9145 if (uView == LVS_LIST)
9147 if (lStyle & WS_HSCROLL)
9149 INT nHScrollHeight = GetSystemMetrics(SM_CYHSCROLL);
9150 if (infoPtr->rcList.bottom > nHScrollHeight)
9151 infoPtr->rcList.bottom -= nHScrollHeight;
9154 else if (uView == LVS_REPORT)
9161 Header_Layout(infoPtr->hwndHeader, &hl);
9163 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
9165 if (!(LVS_NOCOLUMNHEADER & lStyle))
9166 infoPtr->rcList.top = max(wp.cy, 0);
9172 * Processes WM_STYLECHANGED messages.
9175 * [I] HWND : window handle
9176 * [I] WPARAM : window style type (normal or extended)
9177 * [I] LPSTYLESTRUCT : window style information
9182 static INT LISTVIEW_StyleChanged(HWND hwnd, WPARAM wStyleType,
9185 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9186 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
9187 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
9188 RECT rcList = infoPtr->rcList;
9190 TRACE("(hwnd=%x, styletype=%x, stylestruct=%p)\n",
9191 hwnd, wStyleType, lpss);
9193 if (wStyleType == GWL_STYLE)
9195 if (uOldView == LVS_REPORT)
9196 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
9198 if ((lpss->styleOld & WS_HSCROLL) != 0)
9199 ShowScrollBar(hwnd, SB_HORZ, FALSE);
9201 if ((lpss->styleOld & WS_VSCROLL) != 0)
9202 ShowScrollBar(hwnd, SB_VERT, FALSE);
9204 if (uNewView == LVS_ICON)
9206 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
9207 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
9208 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
9209 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
9210 if (lpss->styleNew & LVS_ALIGNLEFT)
9211 LISTVIEW_AlignLeft(hwnd);
9213 LISTVIEW_AlignTop(hwnd);
9215 else if (uNewView == LVS_REPORT)
9222 Header_Layout(infoPtr->hwndHeader, &hl);
9223 SetWindowPos(infoPtr->hwndHeader, hwnd, wp.x, wp.y, wp.cx, wp.cy,
9225 if (!(LVS_NOCOLUMNHEADER & lpss->styleNew))
9226 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
9228 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
9229 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
9230 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
9231 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
9233 else if (uNewView == LVS_LIST)
9235 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
9236 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
9237 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
9238 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
9242 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
9243 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
9244 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
9245 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
9246 if (lpss->styleNew & LVS_ALIGNLEFT)
9247 LISTVIEW_AlignLeft(hwnd);
9249 LISTVIEW_AlignTop(hwnd);
9252 /* update the size of the client area */
9253 LISTVIEW_UpdateSize(hwnd);
9255 /* add scrollbars if needed */
9256 LISTVIEW_UpdateScroll(hwnd);
9258 /* invalidate client area + erase background */
9259 InvalidateRect(hwnd, NULL, TRUE);
9261 /* print the list of unsupported window styles */
9262 LISTVIEW_UnsupportedStyles(lpss->styleNew);
9265 /* If they change the view and we have an active edit control
9266 we will need to kill the control since the redraw will
9267 misplace the edit control.
9269 if (infoPtr->hwndEdit &&
9270 ((uNewView & (LVS_ICON|LVS_LIST|LVS_SMALLICON)) !=
9271 ((LVS_ICON|LVS_LIST|LVS_SMALLICON) & uOldView)))
9273 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
9281 * Window procedure of the listview control.
9284 static LRESULT WINAPI LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam,
9287 TRACE("(hwnd=%x uMsg=%x wParam=%x lParam=%lx)\n", hwnd, uMsg, wParam, lParam);
9288 if (!GetWindowLongW(hwnd, 0) && (uMsg != WM_NCCREATE))
9289 return DefWindowProcW( hwnd, uMsg, wParam, lParam );
9292 case LVM_APPROXIMATEVIEWRECT:
9293 return LISTVIEW_ApproximateViewRect(hwnd, (INT)wParam,
9294 LOWORD(lParam), HIWORD(lParam));
9296 return LISTVIEW_Arrange(hwnd, (INT)wParam);
9298 /* case LVM_CREATEDRAGIMAGE: */
9300 case LVM_DELETEALLITEMS:
9301 return LISTVIEW_DeleteAllItems(hwnd);
9303 case LVM_DELETECOLUMN:
9304 return LISTVIEW_DeleteColumn(hwnd, (INT)wParam);
9306 case LVM_DELETEITEM:
9307 return LISTVIEW_DeleteItem(hwnd, (INT)wParam);
9309 case LVM_EDITLABELW:
9310 return LISTVIEW_EditLabelT(hwnd, (INT)wParam, TRUE);
9312 case LVM_EDITLABELA:
9313 return LISTVIEW_EditLabelT(hwnd, (INT)wParam, FALSE);
9315 case LVM_ENSUREVISIBLE:
9316 return LISTVIEW_EnsureVisible(hwnd, (INT)wParam, (BOOL)lParam);
9319 return LISTVIEW_FindItemW(hwnd, (INT)wParam, (LPLVFINDINFOW)lParam);
9322 return LISTVIEW_FindItemA(hwnd, (INT)wParam, (LPLVFINDINFOA)lParam);
9324 case LVM_GETBKCOLOR:
9325 return LISTVIEW_GetBkColor(hwnd);
9327 /* case LVM_GETBKIMAGE: */
9329 case LVM_GETCALLBACKMASK:
9330 return LISTVIEW_GetCallbackMask(hwnd);
9332 case LVM_GETCOLUMNA:
9333 return LISTVIEW_GetColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9335 case LVM_GETCOLUMNW:
9336 return LISTVIEW_GetColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9338 case LVM_GETCOLUMNORDERARRAY:
9339 return LISTVIEW_GetColumnOrderArray(hwnd, (INT)wParam, (LPINT)lParam);
9341 case LVM_GETCOLUMNWIDTH:
9342 return LISTVIEW_GetColumnWidth(hwnd, (INT)wParam);
9344 case LVM_GETCOUNTPERPAGE:
9345 return LISTVIEW_GetCountPerPage(hwnd);
9347 case LVM_GETEDITCONTROL:
9348 return LISTVIEW_GetEditControl(hwnd);
9350 case LVM_GETEXTENDEDLISTVIEWSTYLE:
9351 return LISTVIEW_GetExtendedListViewStyle(hwnd);
9354 return LISTVIEW_GetHeader(hwnd);
9356 case LVM_GETHOTCURSOR:
9357 FIXME("LVM_GETHOTCURSOR: unimplemented\n");
9360 case LVM_GETHOTITEM:
9361 return LISTVIEW_GetHotItem(hwnd);
9363 case LVM_GETHOVERTIME:
9364 return LISTVIEW_GetHoverTime(hwnd);
9366 case LVM_GETIMAGELIST:
9367 return LISTVIEW_GetImageList(hwnd, (INT)wParam);
9369 case LVM_GETISEARCHSTRINGA:
9370 case LVM_GETISEARCHSTRINGW:
9371 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
9375 return LISTVIEW_GetItemT(hwnd, (LPLVITEMW)lParam, FALSE, FALSE);
9378 return LISTVIEW_GetItemT(hwnd, (LPLVITEMW)lParam, FALSE, TRUE);
9380 case LVM_GETITEMCOUNT:
9381 return LISTVIEW_GetItemCount(hwnd);
9383 case LVM_GETITEMPOSITION:
9384 return LISTVIEW_GetItemPosition(hwnd, (INT)wParam, (LPPOINT)lParam);
9386 case LVM_GETITEMRECT:
9387 return LISTVIEW_GetItemRect(hwnd, (INT)wParam, (LPRECT)lParam);
9389 case LVM_GETITEMSPACING:
9390 return LISTVIEW_GetItemSpacing(hwnd, (BOOL)wParam);
9392 case LVM_GETITEMSTATE:
9393 return LISTVIEW_GetItemState(hwnd, (INT)wParam, (UINT)lParam);
9395 case LVM_GETITEMTEXTA:
9396 return LISTVIEW_GetItemTextT(hwnd, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9398 case LVM_GETITEMTEXTW:
9399 return LISTVIEW_GetItemTextT(hwnd, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9401 case LVM_GETNEXTITEM:
9402 return LISTVIEW_GetNextItem(hwnd, (INT)wParam, LOWORD(lParam));
9404 case LVM_GETNUMBEROFWORKAREAS:
9405 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
9409 return LISTVIEW_GetOrigin(hwnd, (LPPOINT)lParam);
9411 case LVM_GETSELECTEDCOUNT:
9412 return LISTVIEW_GetSelectedCount(hwnd);
9414 case LVM_GETSELECTIONMARK:
9415 return LISTVIEW_GetSelectionMark(hwnd);
9417 case LVM_GETSTRINGWIDTHA:
9418 return LISTVIEW_GetStringWidthT(hwnd, (LPCWSTR)lParam, FALSE);
9420 case LVM_GETSTRINGWIDTHW:
9421 return LISTVIEW_GetStringWidthT(hwnd, (LPCWSTR)lParam, TRUE);
9423 case LVM_GETSUBITEMRECT:
9424 FIXME("LVM_GETSUBITEMRECT: unimplemented\n");
9427 case LVM_GETTEXTBKCOLOR:
9428 return LISTVIEW_GetTextBkColor(hwnd);
9430 case LVM_GETTEXTCOLOR:
9431 return LISTVIEW_GetTextColor(hwnd);
9433 case LVM_GETTOOLTIPS:
9434 FIXME("LVM_GETTOOLTIPS: unimplemented\n");
9437 case LVM_GETTOPINDEX:
9438 return LISTVIEW_GetTopIndex(hwnd);
9440 /*case LVM_GETUNICODEFORMAT:
9441 FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
9444 case LVM_GETVIEWRECT:
9445 return LISTVIEW_GetViewRect(hwnd, (LPRECT)lParam);
9447 case LVM_GETWORKAREAS:
9448 FIXME("LVM_GETWORKAREAS: unimplemented\n");
9452 return LISTVIEW_HitTest(hwnd, (LPLVHITTESTINFO)lParam);
9454 case LVM_INSERTCOLUMNA:
9455 return LISTVIEW_InsertColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9457 case LVM_INSERTCOLUMNW:
9458 return LISTVIEW_InsertColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9460 case LVM_INSERTITEMA:
9461 return LISTVIEW_InsertItemT(hwnd, (LPLVITEMW)lParam, FALSE);
9463 case LVM_INSERTITEMW:
9464 return LISTVIEW_InsertItemT(hwnd, (LPLVITEMW)lParam, TRUE);
9466 case LVM_REDRAWITEMS:
9467 return LISTVIEW_RedrawItems(hwnd, (INT)wParam, (INT)lParam);
9469 /* case LVM_SCROLL: */
9470 /* return LISTVIEW_Scroll(hwnd, (INT)wParam, (INT)lParam); */
9472 case LVM_SETBKCOLOR:
9473 return LISTVIEW_SetBkColor(hwnd, (COLORREF)lParam);
9475 /* case LVM_SETBKIMAGE: */
9477 case LVM_SETCALLBACKMASK:
9478 return LISTVIEW_SetCallbackMask(hwnd, (UINT)wParam);
9480 case LVM_SETCOLUMNA:
9481 return LISTVIEW_SetColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9483 case LVM_SETCOLUMNW:
9484 return LISTVIEW_SetColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9486 case LVM_SETCOLUMNORDERARRAY:
9487 return LISTVIEW_SetColumnOrderArray(hwnd, (INT)wParam, (LPINT)lParam);
9489 case LVM_SETCOLUMNWIDTH:
9490 return LISTVIEW_SetColumnWidth(hwnd, (INT)wParam, SLOWORD(lParam));
9492 case LVM_SETEXTENDEDLISTVIEWSTYLE:
9493 return LISTVIEW_SetExtendedListViewStyle(hwnd, (DWORD)wParam, (DWORD)lParam);
9495 /* case LVM_SETHOTCURSOR: */
9497 case LVM_SETHOTITEM:
9498 return LISTVIEW_SetHotItem(hwnd, (INT)wParam);
9500 case LVM_SETHOVERTIME:
9501 return LISTVIEW_SetHoverTime(hwnd, (DWORD)wParam);
9503 case LVM_SETICONSPACING:
9504 return LISTVIEW_SetIconSpacing(hwnd, (DWORD)lParam);
9506 case LVM_SETIMAGELIST:
9507 return (LRESULT)LISTVIEW_SetImageList(hwnd, (INT)wParam, (HIMAGELIST)lParam);
9510 return LISTVIEW_SetItemT(hwnd, (LPLVITEMW)lParam, FALSE);
9513 return LISTVIEW_SetItemT(hwnd, (LPLVITEMW)lParam, TRUE);
9515 case LVM_SETITEMCOUNT:
9516 return LISTVIEW_SetItemCount(hwnd, (INT)wParam, (DWORD)lParam);
9518 case LVM_SETITEMPOSITION:
9519 return LISTVIEW_SetItemPosition(hwnd, (INT)wParam, (INT)LOWORD(lParam),
9520 (INT)HIWORD(lParam));
9522 case LVM_SETITEMPOSITION32:
9523 return LISTVIEW_SetItemPosition(hwnd, (INT)wParam, ((POINT*)lParam)->x,
9524 ((POINT*)lParam)->y);
9526 case LVM_SETITEMSTATE:
9527 return LISTVIEW_SetItemState(hwnd, (INT)wParam, (LPLVITEMW)lParam);
9529 case LVM_SETITEMTEXTA:
9530 return LISTVIEW_SetItemTextT(hwnd, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9532 case LVM_SETITEMTEXTW:
9533 return LISTVIEW_SetItemTextT(hwnd, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9535 case LVM_SETSELECTIONMARK:
9536 return LISTVIEW_SetSelectionMark(hwnd, (INT)lParam);
9538 case LVM_SETTEXTBKCOLOR:
9539 return LISTVIEW_SetTextBkColor(hwnd, (COLORREF)lParam);
9541 case LVM_SETTEXTCOLOR:
9542 return LISTVIEW_SetTextColor(hwnd, (COLORREF)lParam);
9544 /* case LVM_SETTOOLTIPS: */
9545 /* case LVM_SETUNICODEFORMAT: */
9546 /* case LVM_SETWORKAREAS: */
9549 return LISTVIEW_SortItems(hwnd, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
9551 /* case LVM_SUBITEMHITTEST: */
9554 return LISTVIEW_Update(hwnd, (INT)wParam);
9557 return LISTVIEW_ProcessLetterKeys( hwnd, wParam, lParam );
9560 return LISTVIEW_Command(hwnd, wParam, lParam);
9563 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
9566 return LISTVIEW_EraseBackground(hwnd, wParam, lParam);
9569 return DLGC_WANTCHARS | DLGC_WANTARROWS;
9572 return LISTVIEW_GetFont(hwnd);
9575 return LISTVIEW_HScroll(hwnd, (INT)LOWORD(wParam),
9576 (INT)HIWORD(wParam), (HWND)lParam);
9579 return LISTVIEW_KeyDown(hwnd, (INT)wParam, (LONG)lParam);
9582 return LISTVIEW_KillFocus(hwnd);
9584 case WM_LBUTTONDBLCLK:
9585 return LISTVIEW_LButtonDblClk(hwnd, (WORD)wParam, LOWORD(lParam),
9588 case WM_LBUTTONDOWN:
9589 return LISTVIEW_LButtonDown(hwnd, (WORD)wParam, LOWORD(lParam),
9592 return LISTVIEW_LButtonUp(hwnd, (WORD)wParam, LOWORD(lParam),
9595 return LISTVIEW_MouseMove (hwnd, wParam, lParam);
9598 return LISTVIEW_MouseHover(hwnd, wParam, lParam);
9601 return LISTVIEW_NCCreate(hwnd, wParam, lParam);
9604 return LISTVIEW_NCDestroy(hwnd);
9607 return LISTVIEW_Notify(hwnd, (INT)wParam, (LPNMHDR)lParam);
9609 case WM_NOTIFYFORMAT:
9610 return LISTVIEW_NotifyFormat(hwnd, (HWND)wParam, (INT)lParam);
9613 return LISTVIEW_Paint(hwnd, (HDC)wParam);
9615 case WM_RBUTTONDBLCLK:
9616 return LISTVIEW_RButtonDblClk(hwnd, (WORD)wParam, LOWORD(lParam),
9619 case WM_RBUTTONDOWN:
9620 return LISTVIEW_RButtonDown(hwnd, (WORD)wParam, LOWORD(lParam),
9624 return LISTVIEW_RButtonUp(hwnd, (WORD)wParam, LOWORD(lParam),
9628 return LISTVIEW_SetFocus(hwnd, (HWND)wParam);
9631 return LISTVIEW_SetFont(hwnd, (HFONT)wParam, (WORD)lParam);
9634 return LISTVIEW_SetRedraw(hwnd, (BOOL)wParam);
9637 return LISTVIEW_Size(hwnd, (int)SLOWORD(lParam), (int)SHIWORD(lParam));
9639 case WM_STYLECHANGED:
9640 return LISTVIEW_StyleChanged(hwnd, wParam, (LPSTYLESTRUCT)lParam);
9642 /* case WM_TIMER: */
9645 return LISTVIEW_VScroll(hwnd, (INT)LOWORD(wParam),
9646 (INT)HIWORD(wParam), (HWND)lParam);
9649 if (wParam & (MK_SHIFT | MK_CONTROL))
9650 return DefWindowProcW( hwnd, uMsg, wParam, lParam );
9651 return LISTVIEW_MouseWheel(hwnd, (short int)HIWORD(wParam));/* case WM_WINDOWPOSCHANGED: */
9653 /* case WM_WININICHANGE: */
9656 if (uMsg >= WM_USER)
9658 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam,
9662 /* call default window procedure */
9663 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9671 * Registers the window class.
9679 VOID LISTVIEW_Register(void)
9683 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
9684 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
9685 wndClass.lpfnWndProc = (WNDPROC)LISTVIEW_WindowProc;
9686 wndClass.cbClsExtra = 0;
9687 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
9688 wndClass.hCursor = LoadCursorW(0, IDC_ARROWW);
9689 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
9690 wndClass.lpszClassName = WC_LISTVIEWW;
9691 RegisterClassW(&wndClass);
9696 * Unregisters the window class.
9704 VOID LISTVIEW_Unregister(void)
9706 UnregisterClassW(WC_LISTVIEWW, (HINSTANCE)NULL);
9711 * Handle any WM_COMMAND messages
9717 static LRESULT LISTVIEW_Command(HWND hwnd, WPARAM wParam, LPARAM lParam)
9719 switch (HIWORD(wParam))
9724 * Adjust the edit window size
9727 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9728 HDC hdc = GetDC(infoPtr->hwndEdit);
9729 HFONT hFont, hOldFont = 0;
9734 len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
9735 GetWindowRect(infoPtr->hwndEdit, &rect);
9737 /* Select font to get the right dimension of the string */
9738 hFont = SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
9741 hOldFont = SelectObject(hdc, hFont);
9744 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
9746 TEXTMETRICW textMetric;
9748 /* Add Extra spacing for the next character */
9749 GetTextMetricsW(hdc, &textMetric);
9750 sz.cx += (textMetric.tmMaxCharWidth * 2);
9758 rect.bottom - rect.top,
9759 SWP_DRAWFRAME|SWP_NOMOVE);
9762 SelectObject(hdc, hOldFont);
9764 ReleaseDC(hwnd, hdc);
9770 return SendMessageW (GetParent (hwnd), WM_COMMAND, wParam, lParam);
9779 * Subclassed edit control windproc function
9785 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg,
9786 WPARAM wParam, LPARAM lParam, BOOL isW)
9788 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(GetParent(hwnd), 0);
9789 EDITLABEL_ITEM *einfo = infoPtr->pedititem;
9790 static BOOL bIgnoreKillFocus = FALSE;
9791 BOOL cancel = FALSE;
9793 TRACE("(hwnd=%x, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
9794 hwnd, uMsg, wParam, lParam, isW);
9799 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
9802 if(bIgnoreKillFocus) return TRUE;
9807 WNDPROC editProc = einfo->EditWndProc;
9808 SetWindowLongW(hwnd, GWL_WNDPROC, (LONG)editProc);
9809 COMCTL32_Free(einfo);
9810 infoPtr->pedititem = NULL;
9811 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
9815 if (VK_ESCAPE == (INT)wParam)
9820 else if (VK_RETURN == (INT)wParam)
9824 return CallWindowProcT(einfo->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
9827 if (einfo->EditLblCb)
9829 LPWSTR buffer = NULL;
9833 DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
9837 if ( (buffer = COMCTL32_Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
9839 if (isW) GetWindowTextW(hwnd, buffer, len+1);
9840 else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
9844 /* Processing LVN_ENDLABELEDIT message could kill the focus */
9845 /* eg. Using a messagebox */
9846 bIgnoreKillFocus = TRUE;
9847 einfo->EditLblCb(GetParent(hwnd), buffer, einfo->param);
9849 if (buffer) COMCTL32_Free(buffer);
9851 einfo->EditLblCb = NULL;
9852 bIgnoreKillFocus = FALSE;
9855 SendMessageW(hwnd, WM_CLOSE, 0, 0);
9861 * Subclassed edit control windproc function
9867 LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9869 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
9874 * Subclassed edit control windproc function
9880 LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9882 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
9887 * Creates a subclassed edit cotrol
9893 HWND CreateEditLabelT(LPCWSTR text, DWORD style, INT x, INT y,
9894 INT width, INT height, HWND parent, HINSTANCE hinst,
9895 EditlblCallbackW EditLblCb, DWORD param, BOOL isW)
9897 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(parent, 0);
9898 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
9903 TEXTMETRICW textMetric;
9905 TRACE("(text=%s, ..., isW=%d)\n", debugstr_t(text, isW), isW);
9907 if (NULL == (infoPtr->pedititem = COMCTL32_Alloc(sizeof(EDITLABEL_ITEM))))
9910 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|WS_BORDER;
9911 hdc = GetDC(parent);
9913 /* Select the font to get appropriate metric dimensions */
9914 if(infoPtr->hFont != 0)
9915 hOldFont = SelectObject(hdc, infoPtr->hFont);
9917 /*Get String Lenght in pixels */
9918 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
9920 /*Add Extra spacing for the next character */
9921 GetTextMetricsW(hdc, &textMetric);
9922 sz.cx += (textMetric.tmMaxCharWidth * 2);
9924 if(infoPtr->hFont != 0)
9925 SelectObject(hdc, hOldFont);
9927 ReleaseDC(parent, hdc);
9929 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, parent, 0, hinst, 0);
9931 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, parent, 0, hinst, 0);
9935 COMCTL32_Free(infoPtr->pedititem);
9939 infoPtr->pedititem->param = param;
9940 infoPtr->pedititem->EditLblCb = EditLblCb;
9941 infoPtr->pedititem->EditWndProc = (WNDPROC)
9942 (isW ? SetWindowLongW(hedit, GWL_WNDPROC, (LONG)EditLblWndProcW) :
9943 SetWindowLongA(hedit, GWL_WNDPROC, (LONG)EditLblWndProcA) );
9945 SendMessageW(hedit, WM_SETFONT, infoPtr->hFont, FALSE);