Added LGPL standard comment, and copyright notices where necessary.
[wine/multimedia.git] / dlls / comctl32 / listview.c
bloba948d701baf3edb8cad97243031d9cd9220515df
1 /*
2 * Listview control
4 * Copyright 1998, 1999 Eric Kohl
5 * Copyright 1999 Luc Tourangeau
6 * Copyright 2000 Jason Mawdsley
7 * Copyright 2001 Codeweavers Inc.
8 * Copyright 2002 Dimitrie O. Paun
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 * NOTES
25 * Listview control implementation.
27 * TODO:
28 * 1. No horizontal scrolling when header is larger than the client area.
29 * 2. Drawing optimizations.
30 * 3. Hot item handling.
32 * Notifications:
33 * LISTVIEW_Notify : most notifications from children (editbox and header)
35 * Data structure:
36 * LISTVIEW_SetItemCount : not completed for non OWNERDATA
38 * Advanced functionality:
39 * LISTVIEW_GetNumberOfWorkAreas : not implemented
40 * LISTVIEW_GetHotCursor : not implemented
41 * LISTVIEW_GetISearchString : not implemented
42 * LISTVIEW_GetBkImage : not implemented
43 * LISTVIEW_SetBkImage : not implemented
44 * LISTVIEW_GetColumnOrderArray : simple hack only
45 * LISTVIEW_SetColumnOrderArray : simple hack only
46 * LISTVIEW_Arrange : empty stub
47 * LISTVIEW_ApproximateViewRect : incomplete
48 * LISTVIEW_Scroll : not implemented
49 * LISTVIEW_Update : not completed
51 * Known differences in message stream from native control (not known if
52 * these differences cause problems):
53 * LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
54 * LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
55 * WM_PAINT does LVN_GETDISPINFO in item order 0->n, native does n->0.
56 * WM_SETREDRAW(True) native does LVN_GETDISPINFO for all items and
57 * does *not* invoke DefWindowProc
58 * WM_CREATE does not issue WM_QUERYUISTATE and associated registry
59 * processing for "USEDOUBLECLICKTIME".
62 #include <ctype.h>
63 #include <string.h>
64 #include <stdlib.h>
65 #include <stdio.h>
67 #include "winbase.h"
68 #include "winnt.h"
69 #include "heap.h"
70 #include "commctrl.h"
71 #include "wine/debug.h"
73 WINE_DEFAULT_DEBUG_CHANNEL(listview);
75 /* Some definitions for inline edit control */
76 typedef BOOL (*EditlblCallbackW)(HWND, LPWSTR, DWORD);
77 typedef BOOL (*EditlblCallbackA)(HWND, LPWSTR, DWORD);
79 typedef struct tagLV_INTHIT
81 LVHITTESTINFO ht;
82 DWORD distance; /* distance to closest item */
83 INT iDistItem; /* item number that is closest */
84 } LV_INTHIT, *LPLV_INTHIT;
87 typedef struct tagEDITLABEL_ITEM
89 WNDPROC EditWndProc;
90 DWORD param;
91 EditlblCallbackW EditLblCb;
92 } EDITLABEL_ITEM;
94 typedef struct tagLISTVIEW_SUBITEM
96 LPWSTR pszText;
97 INT iImage;
98 INT iSubItem;
99 } LISTVIEW_SUBITEM;
101 typedef struct tagLISTVIEW_ITEM
103 UINT state;
104 LPWSTR pszText;
105 INT iImage;
106 LPARAM lParam;
107 INT iIndent;
108 POINT ptPosition;
110 } LISTVIEW_ITEM;
112 typedef struct tagLISTVIEW_SELECTION
114 DWORD lower;
115 DWORD upper;
116 } LISTVIEW_SELECTION;
118 typedef struct tagLISTVIEW_INFO
120 HWND hwndSelf;
121 COLORREF clrBk;
122 COLORREF clrText;
123 COLORREF clrTextBk;
124 HIMAGELIST himlNormal;
125 HIMAGELIST himlSmall;
126 HIMAGELIST himlState;
127 BOOL bLButtonDown;
128 BOOL bRButtonDown;
129 INT nFocusedItem;
130 HDPA hdpaSelectionRanges;
131 INT nItemHeight;
132 INT nItemWidth;
133 INT nSelectionMark;
134 INT nHotItem;
135 SHORT notifyFormat;
136 RECT rcList;
137 RECT rcView;
138 SIZE iconSize;
139 SIZE iconSpacing;
140 UINT uCallbackMask;
141 HWND hwndHeader;
142 HFONT hDefaultFont;
143 HFONT hFont;
144 INT ntmHeight; /* from GetTextMetrics from above font */
145 INT ntmAveCharWidth; /* from GetTextMetrics from above font */
146 BOOL bFocus;
147 DWORD dwExStyle; /* extended listview style */
148 HDPA hdpaItems;
149 PFNLVCOMPARE pfnCompare;
150 LPARAM lParamSort;
151 HWND hwndEdit;
152 INT nEditLabelItem;
153 EDITLABEL_ITEM *pedititem;
154 DWORD dwHoverTime;
155 INT nColumnCount; /* the number of columns in this control */
157 DWORD lastKeyPressTimestamp; /* Added */
158 WPARAM charCode; /* Added */
159 INT nSearchParamLength; /* Added */
160 WCHAR szSearchParam[ MAX_PATH ]; /* Added */
161 } LISTVIEW_INFO;
164 * constants
167 /* maximum size of a label */
168 #define DISP_TEXT_SIZE 512
170 /* padding for items in list and small icon display modes */
171 #define WIDTH_PADDING 12
173 /* padding for items in list, report and small icon display modes */
174 #define HEIGHT_PADDING 1
176 /* offset of items in report display mode */
177 #define REPORT_MARGINX 2
179 /* padding for icon in large icon display mode
180 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
181 * that HITTEST will see.
182 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
183 * ICON_TOP_PADDING - sum of the two above.
184 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
185 * LABEL_VERT_OFFSET - between bottom of text and end of box
187 #define ICON_TOP_PADDING_NOTHITABLE 2
188 #define ICON_TOP_PADDING_HITABLE 2
189 #define ICON_TOP_PADDING ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE
190 #define ICON_BOTTOM_PADDING 4
191 #define LABEL_VERT_OFFSET 10
193 /* default label width for items in list and small icon display modes */
194 #define DEFAULT_LABEL_WIDTH 40
196 /* default column width for items in list display mode */
197 #define DEFAULT_COLUMN_WIDTH 96
199 /* Increment size of the horizontal scroll bar */
200 #define LISTVIEW_SCROLL_DIV_SIZE 10
202 /* Padding betwen image and label */
203 #define IMAGE_PADDING 2
205 /* Padding behind the label */
206 #define TRAILING_PADDING 5
208 /* Border for the icon caption */
209 #define CAPTION_BORDER 2
211 * macros
213 /* retrieve the number of items in the listview */
214 #define GETITEMCOUNT(infoPtr) ((infoPtr)->hdpaItems->nItemCount)
215 #define HDM_INSERTITEMT(isW) ( (isW) ? HDM_INSERTITEMW : HDM_INSERTITEMA )
217 HWND CreateEditLabelT(LPCWSTR text, DWORD style, INT x, INT y,
218 INT width, INT height, HWND parent, HINSTANCE hinst,
219 EditlblCallbackW EditLblCb, DWORD param, BOOL isW);
222 * forward declarations
224 static LRESULT LISTVIEW_GetItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL internal, BOOL isW);
225 static INT LISTVIEW_SuperHitTestItem(HWND, LPLV_INTHIT, BOOL);
226 static INT LISTVIEW_HitTestItem(HWND, LPLVHITTESTINFO, BOOL);
227 static INT LISTVIEW_GetCountPerRow(HWND);
228 static INT LISTVIEW_GetCountPerColumn(HWND);
229 static VOID LISTVIEW_AlignLeft(HWND);
230 static VOID LISTVIEW_AlignTop(HWND);
231 static VOID LISTVIEW_AddGroupSelection(HWND, INT);
232 static VOID LISTVIEW_AddSelection(HWND, INT);
233 static BOOL LISTVIEW_AddSubItemT(HWND, LPLVITEMW, BOOL);
234 static INT LISTVIEW_FindInsertPosition(HDPA, INT);
235 static INT LISTVIEW_GetItemHeight(HWND);
236 static BOOL LISTVIEW_GetItemBoundBox(HWND, INT, LPRECT);
237 static BOOL LISTVIEW_GetItemPosition(HWND, INT, LPPOINT);
238 static LRESULT LISTVIEW_GetItemRect(HWND, INT, LPRECT);
239 static INT LISTVIEW_GetItemWidth(HWND);
240 static INT LISTVIEW_GetLabelWidth(HWND, INT);
241 static LRESULT LISTVIEW_GetOrigin(HWND, LPPOINT);
242 static INT LISTVIEW_CalculateWidth(HWND hwnd, INT nItem);
243 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItem(HDPA, INT);
244 static LRESULT LISTVIEW_GetViewRect(HWND, LPRECT);
245 static BOOL LISTVIEW_InitItemT(HWND, LISTVIEW_ITEM *, LPLVITEMW, BOOL);
246 static BOOL LISTVIEW_InitSubItemT(HWND, LISTVIEW_SUBITEM *, LPLVITEMW, BOOL);
247 static LRESULT LISTVIEW_MouseSelection(HWND, POINT);
248 static BOOL LISTVIEW_RemoveColumn(HDPA, INT);
249 static BOOL LISTVIEW_RemoveSubItem(HDPA, INT);
250 static VOID LISTVIEW_SetGroupSelection(HWND, INT);
251 static BOOL LISTVIEW_SetItemT(HWND, LPLVITEMW, BOOL);
252 static BOOL LISTVIEW_SetItemFocus(HWND, INT);
253 static BOOL LISTVIEW_SetItemPosition(HWND, INT, LONG, LONG);
254 static VOID LISTVIEW_UpdateScroll(HWND);
255 static VOID LISTVIEW_SetSelection(HWND, INT);
256 static VOID LISTVIEW_UpdateSize(HWND);
257 static BOOL LISTVIEW_SetSubItemT(HWND, LPLVITEMW, BOOL);
258 static LRESULT LISTVIEW_SetViewRect(HWND, LPRECT);
259 static BOOL LISTVIEW_ToggleSelection(HWND, INT);
260 static VOID LISTVIEW_UnsupportedStyles(LONG lStyle);
261 static HWND LISTVIEW_EditLabelT(HWND hwnd, INT nItem, BOOL isW);
262 static BOOL LISTVIEW_EndEditLabelW(HWND hwnd, LPWSTR pszText, DWORD nItem);
263 static BOOL LISTVIEW_EndEditLabelA(HWND hwnd, LPSTR pszText, DWORD nItem);
264 static LRESULT LISTVIEW_Command(HWND hwnd, WPARAM wParam, LPARAM lParam);
265 static LRESULT LISTVIEW_SortItems(HWND hwnd, PFNLVCOMPARE pfnCompare, LPARAM lParamSort);
266 static LRESULT LISTVIEW_GetStringWidthT(HWND hwnd, LPCWSTR lpszText, BOOL isW);
267 static INT LISTVIEW_ProcessLetterKeys( HWND hwnd, WPARAM charCode, LPARAM keyData );
268 static BOOL LISTVIEW_KeySelection(HWND hwnd, INT nItem);
269 static LRESULT LISTVIEW_GetItemState(HWND hwnd, INT nItem, UINT uMask);
270 static LRESULT LISTVIEW_SetItemState(HWND hwnd, INT nItem, LPLVITEMW lpLVItem);
271 static BOOL LISTVIEW_IsSelected(HWND hwnd, INT nItem);
272 static VOID LISTVIEW_RemoveSelectionRange(HWND hwnd, INT lItem, INT uItem);
273 static void LISTVIEW_FillBackground(HWND hwnd, HDC hdc, LPRECT rc);
274 static void ListView_UpdateLargeItemLabelRect (HWND hwnd, const LISTVIEW_INFO* infoPtr, int nItem, RECT *rect);
276 /******** Defines that LISTVIEW_ProcessLetterKeys uses ****************/
277 #define KEY_DELAY 450
279 #define COUNTOF(array) (sizeof(array)/sizeof(array[0]))
281 static inline BOOL is_textW(LPCWSTR text)
283 return text != NULL && text != LPSTR_TEXTCALLBACKW;
286 static inline BOOL is_textT(LPCWSTR text, BOOL isW)
288 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
289 return is_textW(text);
292 static inline int textlenT(LPCWSTR text, BOOL isW)
294 return !is_textT(text, isW) ? 0 :
295 isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
298 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
300 if (isDestW)
301 if (isSrcW) lstrcpynW(dest, src, max);
302 else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
303 else
304 if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
305 else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
308 static inline LPCSTR debugstr_t(LPCWSTR text, BOOL isW)
310 return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
313 static inline LPCSTR debugstr_tn(LPCWSTR text, BOOL isW, INT n)
315 return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
318 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
320 LPWSTR wstr = (LPWSTR)text;
322 TRACE("(text=%s, isW=%d)\n", debugstr_t(text, isW), isW);
323 if (!isW && text)
325 INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
326 wstr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
327 if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
329 TRACE(" wstr=%s\n", debugstr_w(wstr));
330 return wstr;
333 static inline void textfreeT(LPWSTR wstr, BOOL isW)
335 if (!isW && wstr) HeapFree(GetProcessHeap(), 0, wstr);
339 * dest is a pointer to a Unicode string
340 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
342 static inline BOOL textsetptrT(LPWSTR *dest, LPWSTR src, BOOL isW)
344 LPWSTR pszText = textdupTtoW(src, isW);
345 BOOL bResult = TRUE;
346 if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
347 bResult = Str_SetPtrW(dest, pszText);
348 textfreeT(pszText, isW);
349 return bResult;
352 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
353 WPARAM wParam, LPARAM lParam, BOOL isW)
355 if (isW)
356 return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
357 else
358 return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
361 static inline BOOL notify(HWND self, INT code, LPNMHDR pnmh)
363 pnmh->hwndFrom = self;
364 pnmh->idFrom = GetWindowLongW(self, GWL_ID);
365 pnmh->code = code;
366 return (BOOL)SendMessageW(GetParent(self), WM_NOTIFY,
367 (WPARAM)pnmh->idFrom, (LPARAM)pnmh);
370 static inline BOOL hdr_notify(HWND self, INT code)
372 NMHDR nmh;
373 return notify(self, code, &nmh);
376 static inline BOOL listview_notify(HWND self, INT code, LPNMLISTVIEW plvnm)
378 return notify(self, code, (LPNMHDR)plvnm);
381 static int tabNotification[] = {
382 LVN_BEGINLABELEDITW, LVN_BEGINLABELEDITA,
383 LVN_ENDLABELEDITW, LVN_ENDLABELEDITA,
384 LVN_GETDISPINFOW, LVN_GETDISPINFOA,
385 LVN_SETDISPINFOW, LVN_SETDISPINFOA,
386 LVN_ODFINDITEMW, LVN_ODFINDITEMA,
387 LVN_GETINFOTIPW, LVN_GETINFOTIPA,
391 static int get_ansi_notification(INT unicodeNotificationCode)
393 int *pTabNotif = tabNotification;
394 while (*pTabNotif && (unicodeNotificationCode != *pTabNotif++));
395 if (*pTabNotif) return *pTabNotif;
396 ERR("unknown notification %x\n", unicodeNotificationCode);
397 return unicodeNotificationCode;
401 Send notification. depends on dispinfoW having same
402 structure as dispinfoA.
403 self : listview handle
404 notificationCode : *Unicode* notification code
405 pdi : dispinfo structure (can be unicode or ansi)
406 isW : TRUE if dispinfo is Unicode
408 static BOOL dispinfo_notifyT(HWND self, INT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
410 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(self, 0);
411 BOOL bResult = FALSE;
412 BOOL convertToAnsi = FALSE, convertToUnicode = FALSE;
413 INT realNotifCode;
414 INT cchTempBufMax = 0, savCchTextMax = 0;
415 LPWSTR pszTempBuf = NULL, savPszText = NULL;
417 TRACE("(self=%x, code=%x, pdi=%p, isW=%d)\n", self, notificationCode, pdi, isW);
418 TRACE(" notifyFormat=%s\n",
419 infoPtr->notifyFormat == NFR_UNICODE ? "NFR_UNICODE" :
420 infoPtr->notifyFormat == NFR_ANSI ? "NFR_ANSI" : "(not set)");
421 if (infoPtr->notifyFormat == NFR_ANSI)
422 realNotifCode = get_ansi_notification(notificationCode);
423 else
424 realNotifCode = notificationCode;
426 if (is_textT(pdi->item.pszText, isW))
428 if (isW && infoPtr->notifyFormat == NFR_ANSI)
429 convertToAnsi = TRUE;
430 if (!isW && infoPtr->notifyFormat == NFR_UNICODE)
431 convertToUnicode = TRUE;
434 if (convertToAnsi || convertToUnicode)
436 TRACE(" we have to convert the text to the correct format\n");
437 if (notificationCode != LVN_GETDISPINFOW)
438 { /* length of existing text */
439 cchTempBufMax = convertToUnicode ?
440 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0):
441 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
443 else
444 cchTempBufMax = pdi->item.cchTextMax;
446 pszTempBuf = HeapAlloc(GetProcessHeap(), 0,
447 (convertToUnicode ? sizeof(WCHAR) : sizeof(CHAR)) * cchTempBufMax);
448 if (!pszTempBuf) return FALSE;
449 if (convertToUnicode)
450 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1,
451 pszTempBuf, cchTempBufMax);
452 else
453 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) pszTempBuf,
454 cchTempBufMax, NULL, NULL);
455 TRACE(" text=%s\n", debugstr_t(pszTempBuf, convertToUnicode));
456 savCchTextMax = pdi->item.cchTextMax;
457 savPszText = pdi->item.pszText;
458 pdi->item.pszText = pszTempBuf;
459 pdi->item.cchTextMax = cchTempBufMax;
462 bResult = notify(self, realNotifCode, (LPNMHDR)pdi);
464 if (convertToUnicode || convertToAnsi)
465 { /* convert back result */
466 TRACE(" returned text=%s\n", debugstr_t(pdi->item.pszText, convertToUnicode));
467 if (convertToUnicode) /* note : pointer can be changed by app ! */
468 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) savPszText,
469 savCchTextMax, NULL, NULL);
470 else
471 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
472 savPszText, savCchTextMax);
473 pdi->item.pszText = savPszText; /* restores our buffer */
474 pdi->item.cchTextMax = savCchTextMax;
475 HeapFree(GetProcessHeap(), 0, pszTempBuf);
477 return bResult;
480 static inline LRESULT LISTVIEW_GetItemW(HWND hwnd, LPLVITEMW lpLVItem, BOOL internal)
482 return LISTVIEW_GetItemT(hwnd, lpLVItem, internal, TRUE);
485 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
487 int res;
489 n = min(min(n, strlenW(s1)), strlenW(s2));
490 res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
491 return res ? res - 2 : res;
494 static char* debuglvitem_t(LPLVITEMW lpLVItem, BOOL isW)
496 static int index = 0;
497 static char buffers[20][256];
498 char* buf = buffers[index++ % 20];
499 if (lpLVItem == NULL) return "(null)";
500 snprintf(buf, 256, "{mask=%x, iItem=%d, iSubItem=%d, state=%x, stateMask=%x,"
501 " pszText=%s, cchTextMax=%d, iImage=%d, lParam=%lx, iIndent=%d}",
502 lpLVItem->mask, lpLVItem->iItem, lpLVItem->iSubItem,
503 lpLVItem->state, lpLVItem->stateMask,
504 lpLVItem->pszText == LPSTR_TEXTCALLBACKW ? "(callback)" :
505 debugstr_tn(lpLVItem->pszText, isW, 80),
506 lpLVItem->cchTextMax, lpLVItem->iImage, lpLVItem->lParam,
507 lpLVItem->iIndent);
508 return buf;
511 static char* debuglvcolumn_t(LPLVCOLUMNW lpColumn, BOOL isW)
513 static int index = 0;
514 static char buffers[20][256];
515 char* buf = buffers[index++ % 20];
516 if (lpColumn == NULL) return "(null)";
517 snprintf(buf, 256, "{mask=%x, fmt=%x, cx=%d,"
518 " pszText=%s, cchTextMax=%d, iSubItem=%d}",
519 lpColumn->mask, lpColumn->fmt, lpColumn->cx,
520 lpColumn->mask & LVCF_TEXT ? lpColumn->pszText == LPSTR_TEXTCALLBACKW ? "(callback)" :
521 debugstr_tn(lpColumn->pszText, isW, 80): "",
522 lpColumn->mask & LVCF_TEXT ? lpColumn->cchTextMax: 0, lpColumn->iSubItem);
523 return buf;
526 static void LISTVIEW_DumpListview(LISTVIEW_INFO *iP, INT line)
528 DWORD dwStyle = GetWindowLongW (iP->hwndSelf, GWL_STYLE);
529 TRACE("listview %08x at line %d, clrBk=0x%06lx, clrText=0x%06lx, clrTextBk=0x%06lx, ItemHeight=%d, ItemWidth=%d, Style=0x%08lx\n",
530 iP->hwndSelf, line, iP->clrBk, iP->clrText, iP->clrTextBk,
531 iP->nItemHeight, iP->nItemWidth, dwStyle);
532 TRACE("listview %08x at line %d, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08lx\n",
533 iP->hwndSelf, line, iP->himlNormal, iP->himlSmall, iP->himlState,
534 iP->nFocusedItem, iP->nHotItem, iP->dwExStyle);
537 static BOOL
538 LISTVIEW_SendCustomDrawNotify (HWND hwnd, DWORD dwDrawStage, HDC hdc,
539 RECT rc)
541 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
542 NMLVCUSTOMDRAW nmcdhdr;
543 LPNMCUSTOMDRAW nmcd;
545 TRACE("(hwnd=%x, dwDrawStage=%lx, hdc=%x, rc=?)\n", hwnd, dwDrawStage, hdc);
547 nmcd= & nmcdhdr.nmcd;
548 nmcd->hdr.hwndFrom = hwnd;
549 nmcd->hdr.idFrom = GetWindowLongW( hwnd, GWL_ID);
550 nmcd->hdr.code = NM_CUSTOMDRAW;
551 nmcd->dwDrawStage= dwDrawStage;
552 nmcd->hdc = hdc;
553 nmcd->rc.left = rc.left;
554 nmcd->rc.right = rc.right;
555 nmcd->rc.bottom = rc.bottom;
556 nmcd->rc.top = rc.top;
557 nmcd->dwItemSpec = 0;
558 nmcd->uItemState = 0;
559 nmcd->lItemlParam= 0;
560 nmcdhdr.clrText = infoPtr->clrText;
561 nmcdhdr.clrTextBk= infoPtr->clrBk;
563 return (BOOL)SendMessageW (GetParent (hwnd), WM_NOTIFY,
564 (WPARAM) GetWindowLongW( hwnd, GWL_ID), (LPARAM)&nmcdhdr);
567 static BOOL
568 LISTVIEW_SendCustomDrawItemNotify (HWND hwnd, HDC hdc,
569 UINT iItem, UINT iSubItem,
570 UINT uItemDrawState)
572 LISTVIEW_INFO *infoPtr;
573 NMLVCUSTOMDRAW nmcdhdr;
574 LPNMCUSTOMDRAW nmcd;
575 DWORD dwDrawStage,dwItemSpec;
576 UINT uItemState;
577 INT retval;
578 RECT itemRect;
579 LVITEMW item;
581 infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
583 ZeroMemory(&item,sizeof(item));
584 item.iItem = iItem;
585 item.mask = LVIF_PARAM;
586 ListView_GetItemW(hwnd,&item);
588 dwDrawStage=CDDS_ITEM | uItemDrawState;
589 dwItemSpec=iItem;
590 uItemState=0;
592 if (LISTVIEW_IsSelected(hwnd,iItem)) uItemState|=CDIS_SELECTED;
593 if (iItem==infoPtr->nFocusedItem) uItemState|=CDIS_FOCUS;
594 if (iItem==infoPtr->nHotItem) uItemState|=CDIS_HOT;
596 itemRect.left = LVIR_BOUNDS;
597 LISTVIEW_GetItemRect(hwnd, iItem, &itemRect);
599 nmcd= & nmcdhdr.nmcd;
600 nmcd->hdr.hwndFrom = hwnd;
601 nmcd->hdr.idFrom = GetWindowLongW( hwnd, GWL_ID);
602 nmcd->hdr.code = NM_CUSTOMDRAW;
603 nmcd->dwDrawStage= dwDrawStage;
604 nmcd->hdc = hdc;
605 nmcd->rc.left = itemRect.left;
606 nmcd->rc.right = itemRect.right;
607 nmcd->rc.bottom = itemRect.bottom;
608 nmcd->rc.top = itemRect.top;
609 nmcd->dwItemSpec = dwItemSpec;
610 nmcd->uItemState = uItemState;
611 nmcd->lItemlParam= item.lParam;
612 nmcdhdr.clrText = infoPtr->clrText;
613 nmcdhdr.clrTextBk= infoPtr->clrBk;
614 nmcdhdr.iSubItem =iSubItem;
616 TRACE("drawstage=%lx hdc=%x item=%lx, itemstate=%x, lItemlParam=%lx\n",
617 nmcd->dwDrawStage, nmcd->hdc, nmcd->dwItemSpec,
618 nmcd->uItemState, nmcd->lItemlParam);
620 retval=SendMessageW (GetParent (hwnd), WM_NOTIFY,
621 (WPARAM) GetWindowLongW( hwnd, GWL_ID), (LPARAM)&nmcdhdr);
623 infoPtr->clrText=nmcdhdr.clrText;
624 infoPtr->clrBk =nmcdhdr.clrTextBk;
625 return (BOOL) retval;
629 /*************************************************************************
630 * LISTVIEW_ProcessLetterKeys
632 * Processes keyboard messages generated by pressing the letter keys
633 * on the keyboard.
634 * What this does is perform a case insensitive search from the
635 * current position with the following quirks:
636 * - If two chars or more are pressed in quick succession we search
637 * for the corresponding string (e.g. 'abc').
638 * - If there is a delay we wipe away the current search string and
639 * restart with just that char.
640 * - If the user keeps pressing the same character, whether slowly or
641 * fast, so that the search string is entirely composed of this
642 * character ('aaaaa' for instance), then we search for first item
643 * that starting with that character.
644 * - If the user types the above character in quick succession, then
645 * we must also search for the corresponding string ('aaaaa'), and
646 * go to that string if there is a match.
648 * RETURNS
650 * Zero.
652 * BUGS
654 * - The current implementation has a list of characters it will
655 * accept and it ignores averything else. In particular it will
656 * ignore accentuated characters which seems to match what
657 * Windows does. But I'm not sure it makes sense to follow
658 * Windows there.
659 * - We don't sound a beep when the search fails.
661 * SEE ALSO
663 * TREEVIEW_ProcessLetterKeys
665 static INT LISTVIEW_ProcessLetterKeys(
666 HWND hwnd, /* handle to the window */
667 WPARAM charCode, /* the character code, the actual character */
668 LPARAM keyData /* key data */
671 LISTVIEW_INFO *infoPtr;
672 INT nItem;
673 INT nSize;
674 INT endidx,idx;
675 LVITEMW item;
676 WCHAR buffer[MAX_PATH];
677 DWORD timestamp,elapsed;
679 /* simple parameter checking */
680 if (!hwnd || !charCode || !keyData)
681 return 0;
683 infoPtr=(LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
684 if (!infoPtr)
685 return 0;
687 /* only allow the valid WM_CHARs through */
688 if (!isalnum(charCode) &&
689 charCode != '.' && charCode != '`' && charCode != '!' &&
690 charCode != '@' && charCode != '#' && charCode != '$' &&
691 charCode != '%' && charCode != '^' && charCode != '&' &&
692 charCode != '*' && charCode != '(' && charCode != ')' &&
693 charCode != '-' && charCode != '_' && charCode != '+' &&
694 charCode != '=' && charCode != '\\'&& charCode != ']' &&
695 charCode != '}' && charCode != '[' && charCode != '{' &&
696 charCode != '/' && charCode != '?' && charCode != '>' &&
697 charCode != '<' && charCode != ',' && charCode != '~')
698 return 0;
700 nSize=GETITEMCOUNT(infoPtr);
701 /* if there's one item or less, there is no where to go */
702 if (nSize <= 1)
703 return 0;
705 /* compute how much time elapsed since last keypress */
706 timestamp=GetTickCount();
707 if (timestamp > infoPtr->lastKeyPressTimestamp) {
708 elapsed=timestamp-infoPtr->lastKeyPressTimestamp;
709 } else {
710 elapsed=infoPtr->lastKeyPressTimestamp-timestamp;
713 /* update the search parameters */
714 infoPtr->lastKeyPressTimestamp=timestamp;
715 if (elapsed < KEY_DELAY) {
716 if (infoPtr->nSearchParamLength < COUNTOF(infoPtr->szSearchParam)) {
717 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
719 if (infoPtr->charCode != charCode) {
720 infoPtr->charCode=charCode=0;
722 } else {
723 infoPtr->charCode=charCode;
724 infoPtr->szSearchParam[0]=charCode;
725 infoPtr->nSearchParamLength=1;
726 /* Redundant with the 1 char string */
727 charCode=0;
730 /* and search from the current position */
731 nItem=-1;
732 if (infoPtr->nFocusedItem >= 0) {
733 endidx=infoPtr->nFocusedItem;
734 idx=endidx;
735 /* if looking for single character match,
736 * then we must always move forward
738 if (infoPtr->nSearchParamLength == 1)
739 idx++;
740 } else {
741 endidx=nSize;
742 idx=0;
744 do {
745 if (idx == nSize) {
746 if (endidx == nSize)
747 break;
748 idx=0;
751 /* get item */
752 ZeroMemory(&item, sizeof(item));
753 item.mask = LVIF_TEXT;
754 item.iItem = idx;
755 item.iSubItem = 0;
756 item.pszText = buffer;
757 item.cchTextMax = COUNTOF(buffer);
758 ListView_GetItemW( hwnd, &item );
760 /* check for a match */
761 if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
762 nItem=idx;
763 break;
764 } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
765 (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
766 /* This would work but we must keep looking for a longer match */
767 nItem=idx;
769 idx++;
770 } while (idx != endidx);
772 if (nItem != -1) {
773 if (LISTVIEW_KeySelection(hwnd, nItem) != FALSE) {
774 /* refresh client area */
775 InvalidateRect(hwnd, NULL, TRUE);
776 UpdateWindow(hwnd);
780 return 0;
783 /*************************************************************************
784 * LISTVIEW_UpdateHeaderSize [Internal]
786 * Function to resize the header control
788 * PARAMS
789 * hwnd [I] handle to a window
790 * nNewScrollPos [I] Scroll Pos to Set
792 * RETURNS
793 * nothing
795 * NOTES
797 static VOID LISTVIEW_UpdateHeaderSize(HWND hwnd, INT nNewScrollPos)
799 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
800 RECT winRect;
801 POINT point[2];
803 GetWindowRect(infoPtr->hwndHeader, &winRect);
804 point[0].x = winRect.left;
805 point[0].y = winRect.top;
806 point[1].x = winRect.right;
807 point[1].y = winRect.bottom;
809 MapWindowPoints(HWND_DESKTOP, hwnd, point, 2);
810 point[0].x = -(nNewScrollPos * LISTVIEW_SCROLL_DIV_SIZE);
811 point[1].x += (nNewScrollPos * LISTVIEW_SCROLL_DIV_SIZE);
813 SetWindowPos(infoPtr->hwndHeader,0,
814 point[0].x,point[0].y,point[1].x,point[1].y,
815 SWP_NOZORDER | SWP_NOACTIVATE);
818 /***
819 * DESCRIPTION:
820 * Update the scrollbars. This functions should be called whenever
821 * the content, size or view changes.
823 * PARAMETER(S):
824 * [I] HWND : window handle
826 * RETURN:
827 * None
829 static VOID LISTVIEW_UpdateScroll(HWND hwnd)
831 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
832 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
833 UINT uView = lStyle & LVS_TYPEMASK;
834 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
835 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
836 SCROLLINFO scrollInfo;
838 if (lStyle & LVS_NOSCROLL) return;
840 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
841 scrollInfo.cbSize = sizeof(SCROLLINFO);
843 if (uView == LVS_LIST)
845 /* update horizontal scrollbar */
847 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
848 INT nCountPerRow = LISTVIEW_GetCountPerRow(hwnd);
849 INT nNumOfItems = GETITEMCOUNT(infoPtr);
851 scrollInfo.nMax = nNumOfItems / nCountPerColumn;
852 if((nNumOfItems % nCountPerColumn) == 0)
854 scrollInfo.nMax--;
856 scrollInfo.nPos = ListView_GetTopIndex(hwnd) / nCountPerColumn;
857 scrollInfo.nPage = nCountPerRow;
858 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
859 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
860 ShowScrollBar(hwnd, SB_VERT, FALSE);
862 else if (uView == LVS_REPORT)
864 /* update vertical scrollbar */
865 scrollInfo.nMin = 0;
866 scrollInfo.nMax = GETITEMCOUNT(infoPtr) - 1;
867 scrollInfo.nPos = ListView_GetTopIndex(hwnd);
868 scrollInfo.nPage = LISTVIEW_GetCountPerColumn(hwnd);
869 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
870 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
872 /* update horizontal scrollbar */
873 nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
874 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) == FALSE
875 || GETITEMCOUNT(infoPtr) == 0)
877 scrollInfo.nPos = 0;
879 scrollInfo.nMin = 0;
880 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE ;
881 scrollInfo.nPage = nListWidth / LISTVIEW_SCROLL_DIV_SIZE;
882 scrollInfo.nMax = max(infoPtr->nItemWidth / LISTVIEW_SCROLL_DIV_SIZE, 0)-1;
883 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
885 /* Update the Header Control */
886 scrollInfo.fMask = SIF_POS;
887 GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
888 LISTVIEW_UpdateHeaderSize(hwnd, scrollInfo.nPos);
891 else
893 RECT rcView;
895 if (LISTVIEW_GetViewRect(hwnd, &rcView) != FALSE)
897 INT nViewWidth = rcView.right - rcView.left;
898 INT nViewHeight = rcView.bottom - rcView.top;
900 /* Update Horizontal Scrollbar */
901 scrollInfo.fMask = SIF_POS;
902 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) == FALSE
903 || GETITEMCOUNT(infoPtr) == 0)
905 scrollInfo.nPos = 0;
907 scrollInfo.nMax = max(nViewWidth / LISTVIEW_SCROLL_DIV_SIZE, 0)-1;
908 scrollInfo.nMin = 0;
909 scrollInfo.nPage = nListWidth / LISTVIEW_SCROLL_DIV_SIZE;
910 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
911 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
913 /* Update Vertical Scrollbar */
914 nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
915 scrollInfo.fMask = SIF_POS;
916 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) == FALSE
917 || GETITEMCOUNT(infoPtr) == 0)
919 scrollInfo.nPos = 0;
921 scrollInfo.nMax = max(nViewHeight / LISTVIEW_SCROLL_DIV_SIZE,0)-1;
922 scrollInfo.nMin = 0;
923 scrollInfo.nPage = nListHeight / LISTVIEW_SCROLL_DIV_SIZE;
924 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
925 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
930 /***
931 * DESCRIPTION:
932 * Prints a message for unsupported window styles.
933 * A kind of TODO list for window styles.
935 * PARAMETER(S):
936 * [I] LONG : window style
938 * RETURN:
939 * None
941 static VOID LISTVIEW_UnsupportedStyles(LONG lStyle)
943 if ((LVS_TYPESTYLEMASK & lStyle) == LVS_NOSCROLL)
944 FIXME(" LVS_NOSCROLL\n");
946 if ((LVS_TYPESTYLEMASK & lStyle) == LVS_NOSORTHEADER)
947 FIXME(" LVS_NOSORTHEADER\n");
949 if (lStyle & LVS_EDITLABELS)
950 FIXME(" LVS_EDITLABELS\n");
952 if (lStyle & LVS_NOLABELWRAP)
953 FIXME(" LVS_NOLABELWRAP\n");
955 if (lStyle & LVS_SHAREIMAGELISTS)
956 FIXME(" LVS_SHAREIMAGELISTS\n");
958 if (lStyle & LVS_SORTASCENDING)
959 FIXME(" LVS_SORTASCENDING\n");
961 if (lStyle & LVS_SORTDESCENDING)
962 FIXME(" LVS_SORTDESCENDING\n");
965 /***
966 * DESCRIPTION:
967 * Aligns the items with the top edge of the window.
969 * PARAMETER(S):
970 * [I] HWND : window handle
972 * RETURN:
973 * None
975 static VOID LISTVIEW_AlignTop(HWND hwnd)
977 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
978 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
979 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
980 POINT ptItem;
981 RECT rcView;
982 INT i, off_x=0, off_y=0;
984 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
986 /* Since SetItemPosition uses upper-left of icon, and for
987 style=LVS_ICON the icon is not left adjusted, get the offset */
988 if (uView == LVS_ICON)
990 off_y = ICON_TOP_PADDING;
991 off_x = (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
993 ptItem.x = off_x;
994 ptItem.y = off_y;
995 ZeroMemory(&rcView, sizeof(RECT));
997 if (nListWidth > infoPtr->nItemWidth)
999 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1001 if (ptItem.x + infoPtr->nItemWidth > nListWidth)
1003 ptItem.x = off_x;
1004 ptItem.y += infoPtr->nItemHeight;
1007 LISTVIEW_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
1008 ptItem.x += infoPtr->nItemWidth;
1009 rcView.right = max(rcView.right, ptItem.x);
1012 rcView.bottom = ptItem.y + infoPtr->nItemHeight;
1014 else
1016 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1018 LISTVIEW_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
1019 ptItem.y += infoPtr->nItemHeight;
1022 rcView.right = infoPtr->nItemWidth;
1023 rcView.bottom = ptItem.y;
1026 LISTVIEW_SetViewRect(hwnd, &rcView);
1030 /***
1031 * DESCRIPTION:
1032 * Aligns the items with the left edge of the window.
1034 * PARAMETER(S):
1035 * [I] HWND : window handle
1037 * RETURN:
1038 * None
1040 static VOID LISTVIEW_AlignLeft(HWND hwnd)
1042 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1043 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
1044 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1045 POINT ptItem;
1046 RECT rcView;
1047 INT i, off_x=0, off_y=0;
1049 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1051 /* Since SetItemPosition uses upper-left of icon, and for
1052 style=LVS_ICON the icon is not left adjusted, get the offset */
1053 if (uView == LVS_ICON)
1055 off_y = ICON_TOP_PADDING;
1056 off_x = (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
1058 ptItem.x = off_x;
1059 ptItem.y = off_y;
1060 ZeroMemory(&rcView, sizeof(RECT));
1062 if (nListHeight > infoPtr->nItemHeight)
1064 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1066 if (ptItem.y + infoPtr->nItemHeight > nListHeight)
1068 ptItem.y = off_y;
1069 ptItem.x += infoPtr->nItemWidth;
1072 LISTVIEW_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
1073 ptItem.y += infoPtr->nItemHeight;
1074 rcView.bottom = max(rcView.bottom, ptItem.y);
1077 rcView.right = ptItem.x + infoPtr->nItemWidth;
1079 else
1081 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1083 LISTVIEW_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
1084 ptItem.x += infoPtr->nItemWidth;
1087 rcView.bottom = infoPtr->nItemHeight;
1088 rcView.right = ptItem.x;
1091 LISTVIEW_SetViewRect(hwnd, &rcView);
1095 /***
1096 * DESCRIPTION:
1097 * Set the bounding rectangle of all the items.
1099 * PARAMETER(S):
1100 * [I] HWND : window handle
1101 * [I] LPRECT : bounding rectangle
1103 * RETURN:
1104 * SUCCESS : TRUE
1105 * FAILURE : FALSE
1107 static LRESULT LISTVIEW_SetViewRect(HWND hwnd, LPRECT lprcView)
1109 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
1110 BOOL bResult = FALSE;
1112 TRACE("(hwnd=%x, left=%d, top=%d, right=%d, bottom=%d)\n", hwnd,
1113 lprcView->left, lprcView->top, lprcView->right, lprcView->bottom);
1115 if (lprcView != NULL)
1117 bResult = TRUE;
1118 infoPtr->rcView.left = lprcView->left;
1119 infoPtr->rcView.top = lprcView->top;
1120 infoPtr->rcView.right = lprcView->right;
1121 infoPtr->rcView.bottom = lprcView->bottom;
1124 return bResult;
1127 /***
1128 * DESCRIPTION:
1129 * Retrieves the bounding rectangle of all the items.
1131 * PARAMETER(S):
1132 * [I] HWND : window handle
1133 * [O] LPRECT : bounding rectangle
1135 * RETURN:
1136 * SUCCESS : TRUE
1137 * FAILURE : FALSE
1139 static LRESULT LISTVIEW_GetViewRect(HWND hwnd, LPRECT lprcView)
1141 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
1142 BOOL bResult = FALSE;
1143 POINT ptOrigin;
1145 TRACE("(hwnd=%x, lprcView=%p)\n", hwnd, lprcView);
1147 if (lprcView != NULL)
1149 bResult = LISTVIEW_GetOrigin(hwnd, &ptOrigin);
1150 if (bResult != FALSE)
1152 lprcView->left = infoPtr->rcView.left + ptOrigin.x;
1153 lprcView->top = infoPtr->rcView.top + ptOrigin.y;
1154 lprcView->right = infoPtr->rcView.right + ptOrigin.x;
1155 lprcView->bottom = infoPtr->rcView.bottom + ptOrigin.y;
1158 TRACE("(left=%d, top=%d, right=%d, bottom=%d)\n",
1159 lprcView->left, lprcView->top, lprcView->right, lprcView->bottom);
1162 return bResult;
1165 /***
1166 * DESCRIPTION:
1167 * Retrieves the subitem pointer associated with the subitem index.
1169 * PARAMETER(S):
1170 * [I] HDPA : DPA handle for a specific item
1171 * [I] INT : index of subitem
1173 * RETURN:
1174 * SUCCESS : subitem pointer
1175 * FAILURE : NULL
1177 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems,
1178 INT nSubItem)
1180 LISTVIEW_SUBITEM *lpSubItem;
1181 INT i;
1183 for (i = 1; i < hdpaSubItems->nItemCount; i++)
1185 lpSubItem = (LISTVIEW_SUBITEM *) DPA_GetPtr(hdpaSubItems, i);
1186 if (lpSubItem != NULL)
1188 if (lpSubItem->iSubItem == nSubItem)
1190 return lpSubItem;
1195 return NULL;
1198 /***
1199 * DESCRIPTION:
1200 * Calculates the width of an item.
1202 * PARAMETER(S):
1203 * [I] HWND : window handle
1204 * [I] LONG : window style
1206 * RETURN:
1207 * Returns item width.
1209 static INT LISTVIEW_GetItemWidth(HWND hwnd)
1211 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1212 LONG style = GetWindowLongW(hwnd, GWL_STYLE);
1213 UINT uView = style & LVS_TYPEMASK;
1214 INT nHeaderItemCount;
1215 RECT rcHeaderItem;
1216 INT nItemWidth = 0;
1217 INT nLabelWidth;
1218 INT i;
1220 TRACE("(hwnd=%x)\n", hwnd);
1222 if (uView == LVS_ICON)
1224 nItemWidth = infoPtr->iconSpacing.cx;
1226 else if (uView == LVS_REPORT)
1228 /* calculate width of header */
1229 nHeaderItemCount = Header_GetItemCount(infoPtr->hwndHeader);
1230 for (i = 0; i < nHeaderItemCount; i++)
1232 if (Header_GetItemRect(infoPtr->hwndHeader, i, &rcHeaderItem) != 0)
1234 nItemWidth += (rcHeaderItem.right - rcHeaderItem.left);
1238 else
1240 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1242 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, i);
1243 nItemWidth = max(nItemWidth, nLabelWidth);
1246 /* default label size */
1247 if (GETITEMCOUNT(infoPtr) == 0)
1249 nItemWidth = DEFAULT_COLUMN_WIDTH;
1251 else
1253 if (nItemWidth == 0)
1255 nItemWidth = DEFAULT_LABEL_WIDTH;
1257 else
1259 /* add padding */
1260 nItemWidth += WIDTH_PADDING;
1262 if (infoPtr->himlSmall != NULL)
1264 nItemWidth += infoPtr->iconSize.cx;
1267 if (infoPtr->himlState != NULL)
1269 nItemWidth += infoPtr->iconSize.cx;
1274 if(nItemWidth == 0)
1276 /* nItemWidth Cannot be Zero */
1277 nItemWidth = 1;
1279 return nItemWidth;
1282 /***
1283 * DESCRIPTION:
1284 * Calculates the width of a specific item.
1286 * PARAMETER(S):
1287 * [I] HWND : window handle
1288 * [I] LPSTR : string
1290 * RETURN:
1291 * Returns the width of an item width a specified string.
1293 static INT LISTVIEW_CalculateWidth(HWND hwnd, INT nItem)
1295 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1296 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
1297 INT nHeaderItemCount;
1298 RECT rcHeaderItem;
1299 INT nItemWidth = 0;
1300 INT i;
1302 TRACE("(hwnd=%x)\n", hwnd);
1304 if (uView == LVS_ICON)
1306 nItemWidth = infoPtr->iconSpacing.cx;
1308 else if (uView == LVS_REPORT)
1310 /* calculate width of header */
1311 nHeaderItemCount = Header_GetItemCount(infoPtr->hwndHeader);
1312 for (i = 0; i < nHeaderItemCount; i++)
1314 if (Header_GetItemRect(infoPtr->hwndHeader, i, &rcHeaderItem) != 0)
1316 nItemWidth += (rcHeaderItem.right - rcHeaderItem.left);
1320 else
1322 /* get width of string */
1323 nItemWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
1325 /* default label size */
1326 if (GETITEMCOUNT(infoPtr) == 0)
1328 nItemWidth = DEFAULT_COLUMN_WIDTH;
1330 else
1332 if (nItemWidth == 0)
1334 nItemWidth = DEFAULT_LABEL_WIDTH;
1336 else
1338 /* add padding */
1339 nItemWidth += WIDTH_PADDING;
1341 if (infoPtr->himlSmall != NULL)
1343 nItemWidth += infoPtr->iconSize.cx;
1346 if (infoPtr->himlState != NULL)
1348 nItemWidth += infoPtr->iconSize.cx;
1354 return nItemWidth;
1357 /***
1358 * DESCRIPTION:
1359 * Retrieves and saves important text metrics info for the current
1360 * Listview font.
1362 * PARAMETER(S):
1363 * [I] HWND : window handle
1366 static VOID LISTVIEW_SaveTextMetrics(HWND hwnd)
1368 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1369 TEXTMETRICW tm;
1370 HDC hdc = GetDC(hwnd);
1371 HFONT hOldFont = SelectObject(hdc, infoPtr->hFont);
1372 INT oldHeight, oldACW;
1374 GetTextMetricsW(hdc, &tm);
1376 oldHeight = infoPtr->ntmHeight;
1377 oldACW = infoPtr->ntmAveCharWidth;
1378 infoPtr->ntmHeight = tm.tmHeight;
1379 infoPtr->ntmAveCharWidth = tm.tmAveCharWidth;
1381 SelectObject(hdc, hOldFont);
1382 ReleaseDC(hwnd, hdc);
1383 TRACE("tmHeight old=%d,new=%d; tmAveCharWidth old=%d,new=%d\n",
1384 oldHeight, infoPtr->ntmHeight, oldACW, infoPtr->ntmAveCharWidth);
1388 /***
1389 * DESCRIPTION:
1390 * Calculates the height of an item.
1392 * PARAMETER(S):
1393 * [I] HWND : window handle
1395 * RETURN:
1396 * Returns item height.
1398 static INT LISTVIEW_GetItemHeight(HWND hwnd)
1400 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1401 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
1402 INT nItemHeight = 0;
1404 if (uView == LVS_ICON)
1406 nItemHeight = infoPtr->iconSpacing.cy;
1408 else
1410 if(infoPtr->himlState || infoPtr->himlSmall)
1411 nItemHeight = max(infoPtr->ntmHeight, infoPtr->iconSize.cy) + HEIGHT_PADDING;
1412 else
1413 nItemHeight = infoPtr->ntmHeight;
1416 return nItemHeight;
1420 static void LISTVIEW_PrintSelectionRanges(HWND hwnd)
1422 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1423 LISTVIEW_SELECTION *selection;
1424 INT topSelection = infoPtr->hdpaSelectionRanges->nItemCount;
1425 INT i;
1427 TRACE("Selections are:\n");
1428 for (i = 0; i < topSelection; i++)
1430 selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,i);
1431 TRACE(" %lu - %lu\n",selection->lower,selection->upper);
1435 /***
1436 * DESCRIPTION:
1437 * A compare function for selection ranges
1439 *PARAMETER(S)
1440 * [I] LPVOID : Item 1;
1441 * [I] LPVOID : Item 2;
1442 * [I] LPARAM : flags
1444 *RETURNS:
1445 * >0 : if Item 1 > Item 2
1446 * <0 : if Item 2 > Item 1
1447 * 0 : if Item 1 == Item 2
1449 static INT CALLBACK LISTVIEW_CompareSelectionRanges(LPVOID range1, LPVOID range2,
1450 LPARAM flags)
1452 int l1 = ((LISTVIEW_SELECTION*)(range1))->lower;
1453 int l2 = ((LISTVIEW_SELECTION*)(range2))->lower;
1454 int u1 = ((LISTVIEW_SELECTION*)(range1))->upper;
1455 int u2 = ((LISTVIEW_SELECTION*)(range2))->upper;
1456 int rc=0;
1458 if (u1 < l2)
1459 rc= -1;
1461 if (u2 < l1)
1462 rc= 1;
1464 return rc;
1468 * DESCRIPTION:
1469 * Adds a selection range.
1471 * PARAMETER(S):
1472 * [I] HWND : window handle
1473 * [I] INT : lower item index
1474 * [I] INT : upper item index
1476 * RETURN:
1477 * None
1479 static VOID LISTVIEW_AddSelectionRange(HWND hwnd, INT lItem, INT uItem)
1481 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1482 LISTVIEW_SELECTION *selection;
1483 INT topSelection = infoPtr->hdpaSelectionRanges->nItemCount;
1484 BOOL lowerzero=FALSE;
1486 selection = (LISTVIEW_SELECTION *)COMCTL32_Alloc(sizeof(LISTVIEW_SELECTION));
1487 selection->lower = lItem;
1488 selection->upper = uItem;
1490 TRACE("Add range %i - %i\n", lItem, uItem);
1491 if (topSelection)
1493 LISTVIEW_SELECTION *checkselection,*checkselection2;
1494 INT index,mergeindex;
1496 /* find overlapping selections */
1497 /* we want to catch adjacent ranges so expand our range by 1 */
1499 selection->upper++;
1500 if (selection->lower == 0)
1501 lowerzero = TRUE;
1502 else
1503 selection->lower--;
1505 index = DPA_Search(infoPtr->hdpaSelectionRanges, selection, 0,
1506 LISTVIEW_CompareSelectionRanges,
1507 0,0);
1508 selection->upper --;
1509 if (lowerzero)
1510 lowerzero=FALSE;
1511 else
1512 selection->lower ++;
1514 if (index >=0)
1516 checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,index);
1517 TRACE("Merge with index %i (%lu - %lu)\n",index,checkselection->lower,
1518 checkselection->upper);
1520 checkselection->lower = min(selection->lower,checkselection->lower);
1521 checkselection->upper = max(selection->upper,checkselection->upper);
1523 TRACE("New range (%lu - %lu)\n", checkselection->lower,
1524 checkselection->upper);
1526 COMCTL32_Free(selection);
1528 /* merge now common selection ranges in the lower group*/
1531 checkselection->upper ++;
1532 if (checkselection->lower == 0)
1533 lowerzero = TRUE;
1534 else
1535 checkselection->lower --;
1537 TRACE("search lower range (%lu - %lu)\n", checkselection->lower,
1538 checkselection->upper);
1540 /* not sorted yet */
1541 mergeindex = DPA_Search(infoPtr->hdpaSelectionRanges, checkselection, 0,
1542 LISTVIEW_CompareSelectionRanges, 0,
1545 checkselection->upper --;
1546 if (lowerzero)
1547 lowerzero = FALSE;
1548 else
1549 checkselection->lower ++;
1551 if (mergeindex >=0 && mergeindex != index)
1553 TRACE("Merge with index %i\n",mergeindex);
1554 checkselection2 = DPA_GetPtr(infoPtr->hdpaSelectionRanges,
1555 mergeindex);
1556 checkselection->lower = min(checkselection->lower,
1557 checkselection2->lower);
1558 checkselection->upper = max(checkselection->upper,
1559 checkselection2->upper);
1560 COMCTL32_Free(checkselection2);
1561 DPA_DeletePtr(infoPtr->hdpaSelectionRanges,mergeindex);
1562 index --;
1565 while (mergeindex > -1 && mergeindex <index);
1567 /* merge now common selection ranges in the upper group*/
1570 checkselection->upper ++;
1571 if (checkselection->lower == 0)
1572 lowerzero = TRUE;
1573 else
1574 checkselection->lower --;
1576 TRACE("search upper range %i (%lu - %lu)\n",index,
1577 checkselection->lower, checkselection->upper);
1579 /* not sorted yet */
1580 mergeindex = DPA_Search(infoPtr->hdpaSelectionRanges, checkselection,
1581 index+1,
1582 LISTVIEW_CompareSelectionRanges, 0,
1585 checkselection->upper --;
1586 if (lowerzero)
1587 lowerzero = FALSE;
1588 else
1589 checkselection->lower ++;
1591 if (mergeindex >=0 && mergeindex !=index)
1593 TRACE("Merge with index %i\n",mergeindex);
1594 checkselection2 = DPA_GetPtr(infoPtr->hdpaSelectionRanges,
1595 mergeindex);
1596 checkselection->lower = min(checkselection->lower,
1597 checkselection2->lower);
1598 checkselection->upper = max(checkselection->upper,
1599 checkselection2->upper);
1600 COMCTL32_Free(checkselection2);
1601 DPA_DeletePtr(infoPtr->hdpaSelectionRanges,mergeindex);
1604 while (mergeindex > -1);
1606 else
1609 index = DPA_Search(infoPtr->hdpaSelectionRanges, selection, 0,
1610 LISTVIEW_CompareSelectionRanges, 0,
1611 DPAS_INSERTAFTER);
1613 TRACE("Insert before index %i\n",index);
1614 if (index == -1)
1615 index = 0;
1616 DPA_InsertPtr(infoPtr->hdpaSelectionRanges,index,selection);
1619 else
1621 DPA_InsertPtr(infoPtr->hdpaSelectionRanges,0,selection);
1624 * Incase of error
1626 DPA_Sort(infoPtr->hdpaSelectionRanges,LISTVIEW_CompareSelectionRanges,0);
1627 LISTVIEW_PrintSelectionRanges(hwnd);
1631 * DESCRIPTION:
1632 * check if a specified index is selected.
1634 * PARAMETER(S):
1635 * [I] HWND : window handle
1636 * [I] INT : item index
1638 * RETURN:
1639 * None
1641 static BOOL LISTVIEW_IsSelected(HWND hwnd, INT nItem)
1643 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1644 LISTVIEW_SELECTION selection;
1645 INT index;
1647 selection.upper = nItem;
1648 selection.lower = nItem;
1650 index = DPA_Search(infoPtr->hdpaSelectionRanges, &selection, 0,
1651 LISTVIEW_CompareSelectionRanges,
1652 0,DPAS_SORTED);
1653 if (index != -1)
1654 return TRUE;
1655 else
1656 return FALSE;
1659 /***
1660 * DESCRIPTION:
1661 * Removes all selection ranges
1663 * Parameters(s):
1664 * HWND: window handle
1666 * RETURNS:
1667 * SUCCESS : TRUE
1668 * FAILURE : TRUE
1670 static LRESULT LISTVIEW_RemoveAllSelections(HWND hwnd)
1672 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1673 LISTVIEW_SELECTION *selection;
1674 INT i;
1675 LVITEMW item;
1677 TRACE("(0x%x)\n",hwnd);
1679 ZeroMemory(&item,sizeof(item));
1680 item.stateMask = LVIS_SELECTED;
1684 selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,0);
1685 if (selection)
1687 TRACE("Removing %lu to %lu\n",selection->lower, selection->upper);
1688 for (i = selection->lower; i<=selection->upper; i++)
1689 LISTVIEW_SetItemState(hwnd,i,&item);
1690 LISTVIEW_RemoveSelectionRange(hwnd,selection->lower,selection->upper);
1693 while (infoPtr->hdpaSelectionRanges->nItemCount>0);
1695 TRACE("done\n");
1696 return TRUE;
1700 * DESCRIPTION:
1701 * Removes a range selections.
1703 * PARAMETER(S):
1704 * [I] HWND : window handle
1705 * [I] INT : lower item index
1706 * [I] INT : upper item index
1708 * RETURN:
1709 * None
1711 static VOID LISTVIEW_RemoveSelectionRange(HWND hwnd, INT lItem, INT uItem)
1713 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1714 LISTVIEW_SELECTION removeselection,*checkselection;
1715 INT index;
1717 removeselection.lower = lItem;
1718 removeselection.upper = uItem;
1720 TRACE("Remove range %lu - %lu\n",removeselection.lower,removeselection.upper);
1721 LISTVIEW_PrintSelectionRanges(hwnd);
1723 index = DPA_Search(infoPtr->hdpaSelectionRanges, &removeselection, 0,
1724 LISTVIEW_CompareSelectionRanges,
1725 0,0);
1727 if (index == -1)
1728 return;
1731 checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,
1732 index);
1734 TRACE("Matches range index %i (%lu-%lu)\n",index,checkselection->lower,
1735 checkselection->upper);
1737 /* case 1: Same */
1738 if ((checkselection->upper == removeselection.upper) &&
1739 (checkselection->lower == removeselection.lower))
1741 DPA_DeletePtr(infoPtr->hdpaSelectionRanges,index);
1742 TRACE("Case 1\n");
1744 /* case 2: engulf */
1745 else if (((checkselection->upper < removeselection.upper) &&
1746 (checkselection->lower > removeselection.lower))||
1747 ((checkselection->upper <= removeselection.upper) &&
1748 (checkselection->lower > removeselection.lower)) ||
1749 ((checkselection->upper < removeselection.upper) &&
1750 (checkselection->lower >= removeselection.lower)))
1753 DPA_DeletePtr(infoPtr->hdpaSelectionRanges,index);
1754 /* do it again because others may also get caught */
1755 TRACE("Case 2\n");
1756 LISTVIEW_RemoveSelectionRange(hwnd,lItem,uItem);
1758 /* case 3: overlap upper */
1759 else if ((checkselection->upper < removeselection.upper) &&
1760 (checkselection->lower < removeselection.lower))
1762 checkselection->upper = removeselection.lower - 1;
1763 TRACE("Case 3\n");
1764 LISTVIEW_RemoveSelectionRange(hwnd,lItem,uItem);
1766 /* case 4: overlap lower */
1767 else if ((checkselection->upper > removeselection.upper) &&
1768 (checkselection->lower > removeselection.lower))
1770 checkselection->lower = removeselection.upper + 1;
1771 TRACE("Case 4\n");
1772 LISTVIEW_RemoveSelectionRange(hwnd,lItem,uItem);
1774 /* case 5: fully internal */
1775 else if (checkselection->upper == removeselection.upper)
1776 checkselection->upper = removeselection.lower - 1;
1777 else if (checkselection->lower == removeselection.lower)
1778 checkselection->lower = removeselection.upper + 1;
1779 else
1781 /* bisect the range */
1782 LISTVIEW_SELECTION *newselection;
1784 newselection = (LISTVIEW_SELECTION *)
1785 COMCTL32_Alloc(sizeof(LISTVIEW_SELECTION));
1786 newselection -> lower = checkselection->lower;
1787 newselection -> upper = removeselection.lower - 1;
1788 checkselection -> lower = removeselection.upper + 1;
1789 DPA_InsertPtr(infoPtr->hdpaSelectionRanges,index,newselection);
1790 TRACE("Case 5\n");
1791 DPA_Sort(infoPtr->hdpaSelectionRanges,LISTVIEW_CompareSelectionRanges,0);
1793 LISTVIEW_PrintSelectionRanges(hwnd);
1797 * DESCRIPTION:
1798 * Updates the various indices after an item has been inserted or deleted.
1800 * PARAMETER(S):
1801 * [I] HWND : window handle
1802 * [I] INT : item index
1803 * [I] INT : Direction of shift, +1 or -1.
1805 * RETURN:
1806 * None
1808 static VOID LISTVIEW_ShiftIndices(HWND hwnd, INT nItem, INT direction)
1810 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1811 LISTVIEW_SELECTION selection,*checkselection;
1812 INT index;
1814 TRACE("Shifting %iu, %i steps\n",nItem,direction);
1816 selection.upper = nItem;
1817 selection.lower = nItem;
1819 index = DPA_Search(infoPtr->hdpaSelectionRanges, &selection, 0,
1820 LISTVIEW_CompareSelectionRanges,
1821 0,DPAS_SORTED|DPAS_INSERTAFTER);
1823 while ((index < infoPtr->hdpaSelectionRanges->nItemCount)&&(index != -1))
1825 checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,index);
1826 if ((checkselection->lower >= nItem)&&
1827 (checkselection->lower + direction >= 0))
1828 checkselection->lower += direction;
1829 if ((checkselection->upper >= nItem)&&
1830 (checkselection->upper + direction >=0))
1831 checkselection->upper += direction;
1832 index ++;
1835 /* Note that the following will fail if direction != +1 and -1 */
1836 if (infoPtr->nSelectionMark > nItem)
1837 infoPtr->nSelectionMark += direction;
1838 else if (infoPtr->nSelectionMark == nItem)
1840 if (direction > 0)
1841 infoPtr->nSelectionMark += direction;
1842 else if (infoPtr->nSelectionMark >= GETITEMCOUNT(infoPtr))
1843 infoPtr->nSelectionMark = GETITEMCOUNT(infoPtr) - 1;
1846 if (infoPtr->nFocusedItem > nItem)
1847 infoPtr->nFocusedItem += direction;
1848 else if (infoPtr->nFocusedItem == nItem)
1850 if (direction > 0)
1851 infoPtr->nFocusedItem += direction;
1852 else
1854 if (infoPtr->nFocusedItem >= GETITEMCOUNT(infoPtr))
1855 infoPtr->nFocusedItem = GETITEMCOUNT(infoPtr) - 1;
1856 if (infoPtr->nFocusedItem >= 0)
1857 LISTVIEW_SetItemFocus(hwnd, infoPtr->nFocusedItem);
1860 /* But we are not supposed to modify nHotItem! */
1865 * DESCRIPTION:
1866 * Adds a block of selections.
1868 * PARAMETER(S):
1869 * [I] HWND : window handle
1870 * [I] INT : item index
1872 * RETURN:
1873 * None
1875 static VOID LISTVIEW_AddGroupSelection(HWND hwnd, INT nItem)
1877 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1878 INT nFirst = min(infoPtr->nSelectionMark, nItem);
1879 INT nLast = max(infoPtr->nSelectionMark, nItem);
1880 INT i;
1881 LVITEMW item;
1883 if (nFirst == -1)
1884 nFirst = nItem;
1886 ZeroMemory(&item,sizeof(item));
1887 item.stateMask = LVIS_SELECTED;
1888 item.state = LVIS_SELECTED;
1890 for (i = nFirst; i <= nLast; i++)
1891 LISTVIEW_SetItemState(hwnd,i,&item);
1893 LISTVIEW_SetItemFocus(hwnd, nItem);
1894 infoPtr->nSelectionMark = nItem;
1898 /***
1899 * DESCRIPTION:
1900 * Adds a single selection.
1902 * PARAMETER(S):
1903 * [I] HWND : window handle
1904 * [I] INT : item index
1906 * RETURN:
1907 * None
1909 static VOID LISTVIEW_AddSelection(HWND hwnd, INT nItem)
1911 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1912 LVITEMW item;
1914 ZeroMemory(&item,sizeof(item));
1915 item.state = LVIS_SELECTED;
1916 item.stateMask = LVIS_SELECTED;
1918 LISTVIEW_SetItemState(hwnd,nItem,&item);
1920 LISTVIEW_SetItemFocus(hwnd, nItem);
1921 infoPtr->nSelectionMark = nItem;
1924 /***
1925 * DESCRIPTION:
1926 * Selects or unselects an item.
1928 * PARAMETER(S):
1929 * [I] HWND : window handle
1930 * [I] INT : item index
1932 * RETURN:
1933 * SELECT: TRUE
1934 * UNSELECT : FALSE
1936 static BOOL LISTVIEW_ToggleSelection(HWND hwnd, INT nItem)
1938 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1939 BOOL bResult;
1940 LVITEMW item;
1942 ZeroMemory(&item,sizeof(item));
1943 item.stateMask = LVIS_SELECTED;
1945 if (LISTVIEW_IsSelected(hwnd,nItem))
1947 LISTVIEW_SetItemState(hwnd,nItem,&item);
1948 bResult = FALSE;
1950 else
1952 item.state = LVIS_SELECTED;
1953 LISTVIEW_SetItemState(hwnd,nItem,&item);
1954 bResult = TRUE;
1957 LISTVIEW_SetItemFocus(hwnd, nItem);
1958 infoPtr->nSelectionMark = nItem;
1960 return bResult;
1963 /***
1964 * DESCRIPTION:
1965 * Selects items based on view coordinates.
1967 * PARAMETER(S):
1968 * [I] HWND : window handle
1969 * [I] RECT : selection rectangle
1971 * RETURN:
1972 * None
1974 static VOID LISTVIEW_SetSelectionRect(HWND hwnd, RECT rcSelRect)
1976 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1977 POINT ptItem;
1978 INT i;
1979 LVITEMW item;
1981 ZeroMemory(&item,sizeof(item));
1982 item.stateMask = LVIS_SELECTED;
1984 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1986 LISTVIEW_GetItemPosition(hwnd, i, &ptItem);
1988 if (PtInRect(&rcSelRect, ptItem) != FALSE)
1989 item.state = LVIS_SELECTED;
1990 else
1991 item.state = 0;
1992 LISTVIEW_SetItemState(hwnd,i,&item);
1996 /***
1997 * DESCRIPTION:
1998 * Sets a single group selection.
2000 * PARAMETER(S):
2001 * [I] HWND : window handle
2002 * [I] INT : item index
2004 * RETURN:
2005 * None
2007 static VOID LISTVIEW_SetGroupSelection(HWND hwnd, INT nItem)
2009 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2010 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
2011 LVITEMW item;
2013 ZeroMemory(&item,sizeof(item));
2014 item.stateMask = LVIS_SELECTED;
2016 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
2018 INT i;
2019 INT nFirst, nLast;
2021 if (infoPtr->nSelectionMark == -1)
2023 infoPtr->nSelectionMark = nFirst = nLast = nItem;
2025 else
2027 nFirst = min(infoPtr->nSelectionMark, nItem);
2028 nLast = max(infoPtr->nSelectionMark, nItem);
2031 for (i = 0; i <= GETITEMCOUNT(infoPtr); i++)
2033 if ((i < nFirst) || (i > nLast))
2034 item.state = 0;
2035 else
2036 item.state = LVIS_SELECTED;
2037 LISTVIEW_SetItemState(hwnd,i,&item);
2040 else
2042 RECT rcItem;
2043 RECT rcSelMark;
2044 RECT rcSel;
2045 LISTVIEW_GetItemBoundBox(hwnd, nItem, &rcItem);
2046 LISTVIEW_GetItemBoundBox(hwnd, infoPtr->nSelectionMark, &rcSelMark);
2047 rcSel.left = min(rcSelMark.left, rcItem.left);
2048 rcSel.top = min(rcSelMark.top, rcItem.top);
2049 rcSel.right = max(rcSelMark.right, rcItem.right);
2050 rcSel.bottom = max(rcSelMark.bottom, rcItem.bottom);
2051 LISTVIEW_SetSelectionRect(hwnd, rcSel);
2052 TRACE("item %d (%d,%d)-(%d,%d), mark %d (%d,%d)-(%d,%d), sel (%d,%d)-(%d,%d)\n",
2053 nItem, rcItem.left, rcItem.top, rcItem.right, rcItem.bottom,
2054 infoPtr->nSelectionMark,
2055 rcSelMark.left, rcSelMark.top, rcSelMark.right, rcSelMark.bottom,
2056 rcSel.left, rcSel.top, rcSel.right, rcSel.bottom);
2060 LISTVIEW_SetItemFocus(hwnd, nItem);
2063 /***
2064 * DESCRIPTION:
2065 * Manages the item focus.
2067 * PARAMETER(S):
2068 * [I] HWND : window handle
2069 * [I] INT : item index
2071 * RETURN:
2072 * TRUE : focused item changed
2073 * FALSE : focused item has NOT changed
2075 static BOOL LISTVIEW_SetItemFocus(HWND hwnd, INT nItem)
2077 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2078 BOOL bResult = FALSE;
2079 LVITEMW lvItem;
2081 if (infoPtr->nFocusedItem != nItem)
2083 if (infoPtr->nFocusedItem >= 0)
2085 INT oldFocus = infoPtr->nFocusedItem;
2086 bResult = TRUE;
2087 infoPtr->nFocusedItem = -1;
2088 ZeroMemory(&lvItem, sizeof(lvItem));
2089 lvItem.stateMask = LVIS_FOCUSED;
2090 ListView_SetItemState(hwnd, oldFocus, &lvItem);
2094 lvItem.state = LVIS_FOCUSED;
2095 lvItem.stateMask = LVIS_FOCUSED;
2096 ListView_SetItemState(hwnd, nItem, &lvItem);
2098 infoPtr->nFocusedItem = nItem;
2099 ListView_EnsureVisible(hwnd, nItem, FALSE);
2102 return bResult;
2105 /***
2106 * DESCRIPTION:
2107 * Sets a single selection.
2109 * PARAMETER(S):
2110 * [I] HWND : window handle
2111 * [I] INT : item index
2113 * RETURN:
2114 * None
2116 static VOID LISTVIEW_SetSelection(HWND hwnd, INT nItem)
2118 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2119 LVITEMW lvItem;
2121 ZeroMemory(&lvItem, sizeof(lvItem));
2122 lvItem.stateMask = LVIS_FOCUSED;
2123 ListView_SetItemState(hwnd, infoPtr->nFocusedItem, &lvItem);
2125 LISTVIEW_RemoveAllSelections(hwnd);
2127 lvItem.state = LVIS_FOCUSED|LVIS_SELECTED;
2128 lvItem.stateMask = LVIS_FOCUSED|LVIS_SELECTED;
2129 ListView_SetItemState(hwnd, nItem, &lvItem);
2131 infoPtr->nFocusedItem = nItem;
2132 infoPtr->nSelectionMark = nItem;
2135 /***
2136 * DESCRIPTION:
2137 * Set selection(s) with keyboard.
2139 * PARAMETER(S):
2140 * [I] HWND : window handle
2141 * [I] INT : item index
2143 * RETURN:
2144 * SUCCESS : TRUE (needs to be repainted)
2145 * FAILURE : FALSE (nothing has changed)
2147 static BOOL LISTVIEW_KeySelection(HWND hwnd, INT nItem)
2149 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2150 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2151 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
2152 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
2153 BOOL bResult = FALSE;
2155 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
2157 if (lStyle & LVS_SINGLESEL)
2159 bResult = TRUE;
2160 LISTVIEW_SetSelection(hwnd, nItem);
2161 ListView_EnsureVisible(hwnd, nItem, FALSE);
2163 else
2165 if (wShift)
2167 bResult = TRUE;
2168 LISTVIEW_SetGroupSelection(hwnd, nItem);
2170 else if (wCtrl)
2172 bResult = LISTVIEW_SetItemFocus(hwnd, nItem);
2174 else
2176 bResult = TRUE;
2177 LISTVIEW_SetSelection(hwnd, nItem);
2178 ListView_EnsureVisible(hwnd, nItem, FALSE);
2183 return bResult;
2186 /***
2187 * DESCRIPTION:
2188 * Called when the mouse is being actively tracked and has hovered for a specified
2189 * amount of time
2191 * PARAMETER(S):
2192 * [I] HWND : window handle
2193 * [I] wParam : key indicator
2194 * [I] lParam : mouse position
2196 * RETURN:
2197 * 0 if the message was processed, non-zero if there was an error
2199 * INFO:
2200 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
2201 * over the item for a certain period of time.
2204 static LRESULT LISTVIEW_MouseHover(HWND hwnd, WPARAM wParam, LPARAM lParam)
2206 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2207 POINT pt;
2209 pt.x = (INT)LOWORD(lParam);
2210 pt.y = (INT)HIWORD(lParam);
2212 if(infoPtr->dwExStyle & LVS_EX_TRACKSELECT) {
2213 /* select the item under the cursor */
2214 LISTVIEW_MouseSelection(hwnd, pt);
2217 return 0;
2220 /***
2221 * DESCRIPTION:
2222 * Called whenever WM_MOUSEMOVE is received.
2224 * PARAMETER(S):
2225 * [I] HWND : window handle
2226 * [I] wParam : key indicators
2227 * [I] lParam : cursor position
2229 * RETURN:
2230 * 0 if the message is processed, non-zero if there was an error
2232 static LRESULT LISTVIEW_MouseMove(HWND hwnd, WPARAM wParam, LPARAM lParam)
2234 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2235 TRACKMOUSEEVENT trackinfo;
2237 /* see if we are supposed to be tracking mouse hovering */
2238 if(infoPtr->dwExStyle & LVS_EX_TRACKSELECT) {
2239 /* fill in the trackinfo struct */
2240 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
2241 trackinfo.dwFlags = TME_QUERY;
2242 trackinfo.hwndTrack = hwnd;
2243 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
2245 /* see if we are already tracking this hwnd */
2246 _TrackMouseEvent(&trackinfo);
2248 if(!(trackinfo.dwFlags & TME_HOVER)) {
2249 trackinfo.dwFlags = TME_HOVER;
2251 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
2252 _TrackMouseEvent(&trackinfo);
2256 return 0;
2259 /***
2260 * DESCRIPTION:
2261 * Selects an item based on coordinates.
2263 * PARAMETER(S):
2264 * [I] HWND : window handle
2265 * [I] POINT : mouse click ccordinates
2267 * RETURN:
2268 * SUCCESS : item index
2269 * FAILURE : -1
2271 static LRESULT LISTVIEW_MouseSelection(HWND hwnd, POINT pt)
2273 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2274 RECT rcItem;
2275 INT i,topindex,bottomindex;
2276 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2277 UINT uView = lStyle & LVS_TYPEMASK;
2279 topindex = ListView_GetTopIndex(hwnd);
2280 if (uView == LVS_REPORT)
2282 bottomindex = topindex + LISTVIEW_GetCountPerColumn(hwnd) + 1;
2283 bottomindex = min(bottomindex,GETITEMCOUNT(infoPtr));
2285 else
2287 bottomindex = GETITEMCOUNT(infoPtr);
2290 for (i = topindex; i < bottomindex; i++)
2292 rcItem.left = LVIR_SELECTBOUNDS;
2293 if (LISTVIEW_GetItemRect(hwnd, i, &rcItem) == TRUE)
2295 if (PtInRect(&rcItem, pt) != FALSE)
2297 return i;
2302 return -1;
2305 /***
2306 * DESCRIPTION:
2307 * Removes a column.
2309 * PARAMETER(S):
2310 * [IO] HDPA : dynamic pointer array handle
2311 * [I] INT : column index (subitem index)
2313 * RETURN:
2314 * SUCCCESS : TRUE
2315 * FAILURE : FALSE
2317 static BOOL LISTVIEW_RemoveColumn(HDPA hdpaItems, INT nSubItem)
2319 BOOL bResult = TRUE;
2320 HDPA hdpaSubItems;
2321 INT i;
2323 for (i = 0; i < hdpaItems->nItemCount; i++)
2325 hdpaSubItems = (HDPA)DPA_GetPtr(hdpaItems, i);
2326 if (hdpaSubItems != NULL)
2328 if (LISTVIEW_RemoveSubItem(hdpaSubItems, nSubItem) == FALSE)
2330 bResult = FALSE;
2335 return bResult;
2338 /***
2339 * DESCRIPTION:
2340 * Removes a subitem at a given position.
2342 * PARAMETER(S):
2343 * [IO] HDPA : dynamic pointer array handle
2344 * [I] INT : subitem index
2346 * RETURN:
2347 * SUCCCESS : TRUE
2348 * FAILURE : FALSE
2350 static BOOL LISTVIEW_RemoveSubItem(HDPA hdpaSubItems, INT nSubItem)
2352 LISTVIEW_SUBITEM *lpSubItem;
2353 INT i;
2355 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2357 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2358 if (lpSubItem != NULL)
2360 if (lpSubItem->iSubItem == nSubItem)
2362 /* free string */
2363 if (is_textW(lpSubItem->pszText))
2364 COMCTL32_Free(lpSubItem->pszText);
2366 /* free item */
2367 COMCTL32_Free(lpSubItem);
2369 /* free dpa memory */
2370 if (DPA_DeletePtr(hdpaSubItems, i) == NULL)
2371 return FALSE;
2373 else if (lpSubItem->iSubItem > nSubItem)
2374 return TRUE;
2378 return TRUE;
2381 /***
2382 * DESCRIPTION:
2383 * Compares the item information.
2385 * PARAMETER(S):
2386 * [I] LISTVIEW_ITEM *: destination item
2387 * [I] LPLVITEM : source item
2388 * [I] isW : TRUE if lpLVItem is Unicode, FALSE it it's ANSI
2390 * RETURN:
2391 * SUCCCESS : TRUE (EQUAL)
2392 * FAILURE : FALSE (NOT EQUAL)
2394 static UINT LISTVIEW_GetItemChangesT(LISTVIEW_ITEM *lpItem, LPLVITEMW lpLVItem, BOOL isW)
2396 UINT uChanged = 0;
2398 if ((lpItem != NULL) && (lpLVItem != NULL))
2400 if (lpLVItem->mask & LVIF_STATE)
2402 if ((lpItem->state & lpLVItem->stateMask) !=
2403 (lpLVItem->state & lpLVItem->stateMask))
2404 uChanged |= LVIF_STATE;
2407 if (lpLVItem->mask & LVIF_IMAGE)
2409 if (lpItem->iImage != lpLVItem->iImage)
2410 uChanged |= LVIF_IMAGE;
2413 if (lpLVItem->mask & LVIF_PARAM)
2415 if (lpItem->lParam != lpLVItem->lParam)
2416 uChanged |= LVIF_PARAM;
2419 if (lpLVItem->mask & LVIF_INDENT)
2421 if (lpItem->iIndent != lpLVItem->iIndent)
2422 uChanged |= LVIF_INDENT;
2425 if (lpLVItem->mask & LVIF_TEXT)
2427 if (lpLVItem->pszText == LPSTR_TEXTCALLBACKW)
2429 if (lpItem->pszText != LPSTR_TEXTCALLBACKW)
2430 uChanged |= LVIF_TEXT;
2432 else
2434 if (lpItem->pszText == LPSTR_TEXTCALLBACKW)
2436 uChanged |= LVIF_TEXT;
2438 else
2440 if (lpLVItem->pszText)
2442 if (lpItem->pszText)
2444 LPWSTR pszText = textdupTtoW(lpLVItem->pszText, isW);
2445 if (pszText && strcmpW(pszText, lpItem->pszText))
2446 uChanged |= LVIF_TEXT;
2447 textfreeT(pszText, isW);
2449 else
2451 uChanged |= LVIF_TEXT;
2454 else
2456 if (lpItem->pszText)
2457 uChanged |= LVIF_TEXT;
2463 return uChanged;
2466 /***
2467 * DESCRIPTION:
2468 * Initializes item attributes.
2470 * PARAMETER(S):
2471 * [I] HWND : window handle
2472 * [O] LISTVIEW_ITEM *: destination item
2473 * [I] LPLVITEM : source item
2474 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2476 * RETURN:
2477 * SUCCCESS : TRUE
2478 * FAILURE : FALSE
2480 static BOOL LISTVIEW_InitItemT(HWND hwnd, LISTVIEW_ITEM *lpItem,
2481 LPLVITEMW lpLVItem, BOOL isW)
2483 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2484 BOOL bResult = FALSE;
2486 if ((lpItem != NULL) && (lpLVItem != NULL))
2488 bResult = TRUE;
2490 if (lpLVItem->mask & LVIF_STATE)
2492 lpItem->state &= ~lpLVItem->stateMask;
2493 lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
2496 if (lpLVItem->mask & LVIF_IMAGE)
2497 lpItem->iImage = lpLVItem->iImage;
2499 if (lpLVItem->mask & LVIF_PARAM)
2500 lpItem->lParam = lpLVItem->lParam;
2502 if (lpLVItem->mask & LVIF_INDENT)
2503 lpItem->iIndent = lpLVItem->iIndent;
2505 if (lpLVItem->mask & LVIF_TEXT)
2507 if (lpLVItem->pszText == LPSTR_TEXTCALLBACKW)
2509 if ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
2510 return FALSE;
2512 if (is_textW(lpItem->pszText))
2513 COMCTL32_Free(lpItem->pszText);
2515 lpItem->pszText = LPSTR_TEXTCALLBACKW;
2517 else
2518 bResult = textsetptrT(&lpItem->pszText, lpLVItem->pszText, isW);
2522 return bResult;
2525 /***
2526 * DESCRIPTION:
2527 * Initializes subitem attributes.
2529 * NOTE: The documentation specifies that the operation fails if the user
2530 * tries to set the indent of a subitem.
2532 * PARAMETER(S):
2533 * [I] HWND : window handle
2534 * [O] LISTVIEW_SUBITEM *: destination subitem
2535 * [I] LPLVITEM : source subitem
2536 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2538 * RETURN:
2539 * SUCCCESS : TRUE
2540 * FAILURE : FALSE
2542 static BOOL LISTVIEW_InitSubItemT(HWND hwnd, LISTVIEW_SUBITEM *lpSubItem,
2543 LPLVITEMW lpLVItem, BOOL isW)
2545 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2546 BOOL bResult = FALSE;
2548 TRACE("(hwnd=%x, lpSubItem=%p, lpLVItem=%s, isW=%d)\n",
2549 hwnd, lpSubItem, debuglvitem_t(lpLVItem, isW), isW);
2551 if ((lpSubItem != NULL) && (lpLVItem != NULL))
2553 if (!(lpLVItem->mask & LVIF_INDENT))
2555 bResult = TRUE;
2557 lpSubItem->iSubItem = lpLVItem->iSubItem;
2559 if (lpLVItem->mask & LVIF_IMAGE)
2560 lpSubItem->iImage = lpLVItem->iImage;
2562 if (lpLVItem->mask & LVIF_TEXT)
2564 if (lpLVItem->pszText == LPSTR_TEXTCALLBACKW)
2566 if ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
2567 return FALSE;
2569 if (is_textW(lpSubItem->pszText))
2570 COMCTL32_Free(lpSubItem->pszText);
2572 lpSubItem->pszText = LPSTR_TEXTCALLBACKW;
2574 else
2575 bResult = textsetptrT(&lpSubItem->pszText, lpLVItem->pszText, isW);
2580 return bResult;
2583 /***
2584 * DESCRIPTION:
2585 * Adds a subitem at a given position (column index).
2587 * PARAMETER(S):
2588 * [I] HWND : window handle
2589 * [I] LPLVITEM : new subitem atttributes
2590 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2592 * RETURN:
2593 * SUCCESS : TRUE
2594 * FAILURE : FALSE
2596 static BOOL LISTVIEW_AddSubItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
2598 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2599 LISTVIEW_SUBITEM *lpSubItem = NULL;
2600 BOOL bResult = FALSE;
2601 HDPA hdpaSubItems;
2602 INT nPosition, nItem;
2603 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2605 TRACE("(hwnd=%x, lpLVItem=%s, isW=%d)\n", hwnd, debuglvitem_t(lpLVItem, isW), isW);
2607 if (lStyle & LVS_OWNERDATA)
2608 return FALSE;
2610 if (lpLVItem != NULL)
2612 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2613 if (hdpaSubItems != NULL)
2615 lpSubItem = (LISTVIEW_SUBITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM));
2616 if (lpSubItem != NULL)
2618 ZeroMemory(lpSubItem, sizeof(LISTVIEW_SUBITEM));
2619 if (LISTVIEW_InitSubItemT(hwnd, lpSubItem, lpLVItem, isW))
2621 nPosition = LISTVIEW_FindInsertPosition(hdpaSubItems,
2622 lpSubItem->iSubItem);
2623 nItem = DPA_InsertPtr(hdpaSubItems, nPosition, lpSubItem);
2624 if (nItem != -1) bResult = TRUE;
2630 /* cleanup if unsuccessful */
2631 if (!bResult && lpSubItem) COMCTL32_Free(lpSubItem);
2633 return bResult;
2636 /***
2637 * DESCRIPTION:
2638 * Finds the dpa insert position (array index).
2640 * PARAMETER(S):
2641 * [I] HWND : window handle
2642 * [I] INT : subitem index
2644 * RETURN:
2645 * SUCCESS : TRUE
2646 * FAILURE : FALSE
2648 static INT LISTVIEW_FindInsertPosition(HDPA hdpaSubItems, INT nSubItem)
2650 LISTVIEW_SUBITEM *lpSubItem;
2651 INT i;
2653 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2655 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2656 if (lpSubItem && lpSubItem->iSubItem > nSubItem)
2657 return i;
2660 return hdpaSubItems->nItemCount;
2663 /***
2664 * DESCRIPTION:
2665 * Retrieves a listview subitem at a given position (column index).
2667 * PARAMETER(S):
2668 * [I] HWND : window handle
2669 * [I] INT : subitem index
2671 * RETURN:
2672 * SUCCESS : TRUE
2673 * FAILURE : FALSE
2675 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItem(HDPA hdpaSubItems, INT nSubItem)
2677 LISTVIEW_SUBITEM *lpSubItem;
2678 INT i;
2680 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2682 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2683 if (lpSubItem != NULL)
2685 if (lpSubItem->iSubItem == nSubItem)
2686 return lpSubItem;
2687 else if (lpSubItem->iSubItem > nSubItem)
2688 return NULL;
2692 return NULL;
2695 /***
2696 * DESCRIPTION:
2697 * Sets item attributes.
2699 * PARAMETER(S):
2700 * [I] HWND : window handle
2701 * [I] LPLVITEM : new item atttributes
2702 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2704 * RETURN:
2705 * SUCCESS : TRUE
2706 * FAILURE : FALSE
2708 static BOOL LISTVIEW_SetMainItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
2710 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2711 BOOL bResult = FALSE;
2712 HDPA hdpaSubItems;
2713 LISTVIEW_ITEM *lpItem;
2714 NMLISTVIEW nmlv;
2715 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2716 UINT uChanged;
2717 UINT uView = lStyle & LVS_TYPEMASK;
2718 INT item_width;
2719 RECT rcItem;
2721 TRACE("(hwnd=%x, lpLVItem=%s, isW=%d)\n", hwnd, debuglvitem_t(lpLVItem, isW), isW);
2723 if (lStyle & LVS_OWNERDATA)
2725 if ((lpLVItem->iSubItem == 0)&&(lpLVItem->mask == LVIF_STATE))
2727 LVITEMW itm;
2729 ZeroMemory(&itm, sizeof(itm));
2730 itm.mask = LVIF_STATE | LVIF_PARAM;
2731 itm.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
2732 itm.iItem = lpLVItem->iItem;
2733 itm.iSubItem = 0;
2734 ListView_GetItemW(hwnd, &itm);
2737 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
2738 nmlv.uNewState = lpLVItem->state;
2739 nmlv.uOldState = itm.state;
2740 nmlv.uChanged = LVIF_STATE;
2741 nmlv.lParam = itm.lParam;
2742 nmlv.iItem = lpLVItem->iItem;
2744 if ((itm.state & lpLVItem->stateMask) !=
2745 (lpLVItem->state & lpLVItem->stateMask))
2747 /* send LVN_ITEMCHANGING notification */
2748 if (!listview_notify(hwnd, LVN_ITEMCHANGING, &nmlv))
2750 if (lpLVItem->stateMask & LVIS_FOCUSED)
2752 if (lpLVItem->state & LVIS_FOCUSED)
2753 infoPtr->nFocusedItem = lpLVItem->iItem;
2754 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
2755 infoPtr->nFocusedItem = -1;
2757 if (lpLVItem->stateMask & LVIS_SELECTED)
2759 if (lpLVItem->state & LVIS_SELECTED)
2761 if (lStyle & LVS_SINGLESEL) LISTVIEW_RemoveAllSelections(hwnd);
2762 LISTVIEW_AddSelectionRange(hwnd,lpLVItem->iItem,lpLVItem->iItem);
2764 else
2765 LISTVIEW_RemoveSelectionRange(hwnd,lpLVItem->iItem,
2766 lpLVItem->iItem);
2769 listview_notify(hwnd, LVN_ITEMCHANGED, &nmlv);
2771 rcItem.left = LVIR_BOUNDS;
2772 LISTVIEW_GetItemRect(hwnd, lpLVItem->iItem, &rcItem);
2773 InvalidateRect(hwnd, &rcItem, TRUE);
2776 return TRUE;
2778 return FALSE;
2781 if (lpLVItem != NULL)
2783 if (lpLVItem->iSubItem == 0)
2785 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2786 if (hdpaSubItems != NULL && hdpaSubItems != (HDPA)-1)
2788 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, lpLVItem->iSubItem);
2789 if (lpItem != NULL)
2791 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
2792 nmlv.lParam = lpItem->lParam;
2793 uChanged = LISTVIEW_GetItemChangesT(lpItem, lpLVItem, isW);
2794 if (uChanged != 0)
2796 if (uChanged & LVIF_STATE)
2798 nmlv.uNewState = lpLVItem->state & lpLVItem->stateMask;
2799 nmlv.uOldState = lpItem->state & lpLVItem->stateMask;
2801 if (nmlv.uNewState & LVIS_SELECTED)
2804 * This is redundant if called through SetSelection
2806 * however is required if the used directly calls SetItem
2807 * to set the selection.
2809 if (lStyle & LVS_SINGLESEL)
2810 LISTVIEW_RemoveAllSelections(hwnd);
2812 LISTVIEW_AddSelectionRange(hwnd,lpLVItem->iItem,
2813 lpLVItem->iItem);
2815 else if (lpLVItem->stateMask & LVIS_SELECTED)
2817 LISTVIEW_RemoveSelectionRange(hwnd,lpLVItem->iItem,
2818 lpLVItem->iItem);
2820 if (nmlv.uNewState & LVIS_FOCUSED)
2823 * This is a fun hoop to jump to try to catch if
2824 * the user is calling us directly to call focus or if
2825 * this function is being called as a result of a
2826 * SetItemFocus call.
2828 if (infoPtr->nFocusedItem >= 0)
2829 LISTVIEW_SetItemFocus(hwnd, lpLVItem->iItem);
2833 nmlv.uChanged = uChanged;
2834 nmlv.iItem = lpLVItem->iItem;
2835 nmlv.lParam = lpItem->lParam;
2836 /* send LVN_ITEMCHANGING notification */
2837 listview_notify(hwnd, LVN_ITEMCHANGING, &nmlv);
2839 /* copy information */
2840 bResult = LISTVIEW_InitItemT(hwnd, lpItem, lpLVItem, isW);
2842 /* if LVS_LIST or LVS_SMALLICON, update the width of the items
2843 based on the width of the items text */
2844 if((uView == LVS_LIST) || (uView == LVS_SMALLICON))
2846 item_width = LISTVIEW_GetStringWidthT(hwnd, lpItem->pszText, TRUE);
2848 if(item_width > infoPtr->nItemWidth)
2849 infoPtr->nItemWidth = item_width;
2852 /* send LVN_ITEMCHANGED notification */
2853 listview_notify(hwnd, LVN_ITEMCHANGED, &nmlv);
2855 else
2857 bResult = TRUE;
2860 if (uChanged)
2862 rcItem.left = LVIR_BOUNDS;
2863 LISTVIEW_GetItemRect(hwnd, lpLVItem->iItem, &rcItem);
2864 InvalidateRect(hwnd, &rcItem, TRUE);
2871 return bResult;
2874 /***
2875 * DESCRIPTION:
2876 * Sets subitem attributes.
2878 * PARAMETER(S):
2879 * [I] HWND : window handle
2880 * [I] LPLVITEM : new subitem atttributes
2881 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2883 * RETURN:
2884 * SUCCESS : TRUE
2885 * FAILURE : FALSE
2887 static BOOL LISTVIEW_SetSubItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
2889 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2890 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2891 BOOL bResult = FALSE;
2892 HDPA hdpaSubItems;
2893 LISTVIEW_SUBITEM *lpSubItem;
2894 RECT rcItem;
2896 TRACE("(hwnd=%x, lpLVItem=%s, isW=%d)\n", hwnd, debuglvitem_t(lpLVItem, isW), isW);
2898 if (lStyle & LVS_OWNERDATA)
2899 return FALSE;
2901 if (lpLVItem != NULL)
2903 if (lpLVItem->iSubItem > 0)
2905 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2906 if (hdpaSubItems != NULL)
2908 /* set subitem only if column is present */
2909 if (Header_GetItemCount(infoPtr->hwndHeader) > lpLVItem->iSubItem)
2911 lpSubItem = LISTVIEW_GetSubItem(hdpaSubItems, lpLVItem->iSubItem);
2912 if (lpSubItem != NULL)
2913 bResult = LISTVIEW_InitSubItemT(hwnd, lpSubItem, lpLVItem, isW);
2914 else
2915 bResult = LISTVIEW_AddSubItemT(hwnd, lpLVItem, isW);
2917 rcItem.left = LVIR_BOUNDS;
2918 LISTVIEW_GetItemRect(hwnd, lpLVItem->iItem, &rcItem);
2919 InvalidateRect(hwnd, &rcItem, FALSE);
2925 return bResult;
2928 /***
2929 * DESCRIPTION:
2930 * Sets item attributes.
2932 * PARAMETER(S):
2933 * [I] HWND : window handle
2934 * [I] LPLVITEM : new item atttributes
2935 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2937 * RETURN:
2938 * SUCCESS : TRUE
2939 * FAILURE : FALSE
2941 static BOOL LISTVIEW_SetItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
2943 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2945 if (!lpLVItem || lpLVItem->iItem < 0 ||
2946 lpLVItem->iItem>=GETITEMCOUNT(infoPtr))
2947 return FALSE;
2948 if (lpLVItem->iSubItem == 0)
2949 return LISTVIEW_SetMainItemT(hwnd, lpLVItem, isW);
2950 else
2951 return LISTVIEW_SetSubItemT(hwnd, lpLVItem, isW);
2954 /***
2955 * DESCRIPTION:
2956 * Retrieves the index of the item at coordinate (0, 0) of the client area.
2958 * PARAMETER(S):
2959 * [I] HWND : window handle
2961 * RETURN:
2962 * item index
2964 static INT LISTVIEW_GetTopIndex(HWND hwnd)
2966 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2967 UINT uView = lStyle & LVS_TYPEMASK;
2968 INT nItem = 0;
2969 SCROLLINFO scrollInfo;
2971 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
2972 scrollInfo.cbSize = sizeof(SCROLLINFO);
2973 scrollInfo.fMask = SIF_POS;
2975 if (uView == LVS_LIST)
2977 if ((lStyle & WS_HSCROLL) && GetScrollInfo(hwnd, SB_HORZ, &scrollInfo))
2978 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(hwnd);
2980 else if (uView == LVS_REPORT)
2982 if ((lStyle & WS_VSCROLL) && GetScrollInfo(hwnd, SB_VERT, &scrollInfo))
2983 nItem = scrollInfo.nPos;
2986 return nItem;
2989 /***
2990 * DESCRIPTION:
2991 * Draws a subitem.
2993 * PARAMETER(S):
2994 * [I] HWND : window handle
2995 * [I] HDC : device context handle
2996 * [I] INT : item index
2997 * [I] INT : subitem index
2998 * [I] RECT * : clipping rectangle
3000 * RETURN:
3001 * None
3003 static VOID LISTVIEW_DrawSubItem(HWND hwnd, HDC hdc, INT nItem, INT nSubItem,
3004 RECT rcItem, BOOL Selected)
3006 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3007 WCHAR szDispText[DISP_TEXT_SIZE];
3008 LVITEMW lvItem;
3009 UINT textoutOptions = ETO_CLIPPED | ETO_OPAQUE;
3010 RECT rcTemp;
3012 TRACE("(hwnd=%x, hdc=%x, nItem=%d, nSubItem=%d)\n", hwnd, hdc,
3013 nItem, nSubItem);
3015 /* get information needed for drawing the item */
3016 ZeroMemory(&lvItem, sizeof(lvItem));
3017 lvItem.mask = LVIF_TEXT;
3018 lvItem.iItem = nItem;
3019 lvItem.iSubItem = nSubItem;
3020 lvItem.cchTextMax = DISP_TEXT_SIZE;
3021 lvItem.pszText = szDispText;
3022 *lvItem.pszText = '\0';
3023 LISTVIEW_GetItemW(hwnd, &lvItem, TRUE);
3024 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3026 /* redraw the background of the item */
3027 rcTemp = rcItem;
3028 if(infoPtr->nColumnCount == (nSubItem + 1))
3029 rcTemp.right = infoPtr->rcList.right;
3030 else
3031 rcTemp.right += WIDTH_PADDING;
3033 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3035 /* set item colors */
3036 if (ListView_GetItemState(hwnd,nItem,LVIS_SELECTED) && Selected)
3038 if (infoPtr->bFocus)
3040 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
3041 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
3043 else
3045 SetBkColor(hdc, GetSysColor(COLOR_3DFACE));
3046 SetTextColor(hdc, GetSysColor(COLOR_BTNTEXT));
3049 else
3051 if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
3053 SetBkMode(hdc, TRANSPARENT);
3054 textoutOptions &= ~ETO_OPAQUE;
3056 else
3058 SetBkMode(hdc, OPAQUE);
3059 SetBkColor(hdc, infoPtr->clrTextBk);
3062 SetTextColor(hdc, infoPtr->clrText);
3065 ExtTextOutW(hdc, rcItem.left, rcItem.top, textoutOptions,
3066 &rcItem, lvItem.pszText, lstrlenW(lvItem.pszText), NULL);
3068 if (Selected)
3070 /* fill in the gap */
3071 RECT rec;
3072 if (nSubItem < Header_GetItemCount(infoPtr->hwndHeader)-1)
3074 CopyRect(&rec,&rcItem);
3075 rec.left = rec.right;
3076 rec.right = rec.left+REPORT_MARGINX;
3077 ExtTextOutW(hdc, rec.left , rec.top, textoutOptions,
3078 &rec, NULL, 0, NULL);
3080 CopyRect(&rec,&rcItem);
3081 rec.right = rec.left;
3082 rec.left = rec.left - REPORT_MARGINX;
3083 ExtTextOutW(hdc, rec.left , rec.top, textoutOptions,
3084 &rec, NULL, 0, NULL);
3089 /***
3090 * DESCRIPTION:
3091 * Draws an item.
3093 * PARAMETER(S):
3094 * [I] HWND : window handle
3095 * [I] HDC : device context handle
3096 * [I] INT : item index
3097 * [I] RECT * : clipping rectangle
3099 * RETURN:
3100 * None
3102 static VOID LISTVIEW_DrawItem(HWND hwnd, HDC hdc, INT nItem, RECT rcItem, BOOL FullSelect, RECT* SuggestedFocus)
3104 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3105 WCHAR szDispText[DISP_TEXT_SIZE];
3106 INT nLabelWidth;
3107 LVITEMW lvItem;
3108 INT nMixMode;
3109 DWORD dwBkColor;
3110 DWORD dwTextColor,dwTextX;
3111 BOOL bImage = FALSE;
3112 INT iBkMode = -1;
3113 UINT textoutOptions = ETO_OPAQUE | ETO_CLIPPED;
3114 RECT rcTemp;
3116 TRACE("(hwnd=%x, hdc=%x, nItem=%d)\n", hwnd, hdc, nItem);
3119 /* get information needed for drawing the item */
3120 ZeroMemory(&lvItem, sizeof(lvItem));
3121 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE | LVIF_INDENT;
3122 lvItem.stateMask = LVIS_SELECTED | LVIS_STATEIMAGEMASK;
3123 lvItem.iItem = nItem;
3124 lvItem.iSubItem = 0;
3125 lvItem.cchTextMax = DISP_TEXT_SIZE;
3126 lvItem.pszText = szDispText;
3127 *lvItem.pszText = '\0';
3128 LISTVIEW_GetItemW(hwnd, &lvItem, TRUE);
3129 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3131 /* redraw the background of the item */
3132 rcTemp = rcItem;
3133 if(infoPtr->nColumnCount == (nItem + 1))
3134 rcTemp.right = infoPtr->rcList.right;
3135 else
3136 rcTemp.right+=WIDTH_PADDING;
3138 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3140 /* do indent */
3141 if (lvItem.iIndent>0 && infoPtr->iconSize.cx > 0)
3143 rcItem.left += infoPtr->iconSize.cx * lvItem.iIndent;
3145 if (SuggestedFocus)
3146 SuggestedFocus->left += infoPtr->iconSize.cx * lvItem.iIndent;
3149 /* state icons */
3150 if (infoPtr->himlState != NULL)
3152 UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12;
3153 if (uStateImage > 0)
3155 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, rcItem.left,
3156 rcItem.top, ILD_NORMAL);
3159 rcItem.left += infoPtr->iconSize.cx;
3160 if (SuggestedFocus)
3161 SuggestedFocus->left += infoPtr->iconSize.cx;
3162 bImage = TRUE;
3165 /* small icons */
3166 if (infoPtr->himlSmall != NULL)
3168 if ((lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus != FALSE) &&
3169 (lvItem.iImage>=0))
3171 ImageList_SetBkColor(infoPtr->himlSmall, CLR_NONE);
3172 ImageList_Draw(infoPtr->himlSmall, lvItem.iImage, hdc, rcItem.left,
3173 rcItem.top, ILD_SELECTED);
3175 else if (lvItem.iImage>=0)
3177 ImageList_SetBkColor(infoPtr->himlSmall, CLR_NONE);
3178 ImageList_Draw(infoPtr->himlSmall, lvItem.iImage, hdc, rcItem.left,
3179 rcItem.top, ILD_NORMAL);
3182 rcItem.left += infoPtr->iconSize.cx;
3184 if (SuggestedFocus)
3185 SuggestedFocus->left += infoPtr->iconSize.cx;
3186 bImage = TRUE;
3189 /* Don't bother painting item being edited */
3190 if (infoPtr->hwndEdit && lvItem.state & LVIS_FOCUSED && !FullSelect)
3191 return;
3193 if ((lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus != FALSE))
3195 /* set item colors */
3196 dwBkColor = SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
3197 dwTextColor = SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
3198 /* set raster mode */
3199 nMixMode = SetROP2(hdc, R2_XORPEN);
3201 else if ((GetWindowLongW(hwnd, GWL_STYLE) & LVS_SHOWSELALWAYS) &&
3202 (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus == FALSE))
3204 dwBkColor = SetBkColor(hdc, GetSysColor(COLOR_3DFACE));
3205 dwTextColor = SetTextColor(hdc, GetSysColor(COLOR_BTNTEXT));
3206 /* set raster mode */
3207 nMixMode = SetROP2(hdc, R2_COPYPEN);
3209 else
3211 /* set item colors */
3212 if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
3214 dwBkColor = GetBkColor(hdc);
3215 iBkMode = SetBkMode(hdc, TRANSPARENT);
3216 textoutOptions &= ~ETO_OPAQUE;
3218 else
3220 dwBkColor = SetBkColor(hdc, infoPtr->clrTextBk);
3221 iBkMode = SetBkMode(hdc, OPAQUE);
3224 dwTextColor = SetTextColor(hdc, infoPtr->clrText);
3225 /* set raster mode */
3226 nMixMode = SetROP2(hdc, R2_COPYPEN);
3229 nLabelWidth = ListView_GetStringWidthW(hwnd, lvItem.pszText);
3230 if (rcItem.left + nLabelWidth < rcItem.right)
3232 if (!FullSelect)
3233 rcItem.right = rcItem.left + nLabelWidth + TRAILING_PADDING;
3234 if (bImage)
3235 rcItem.right += IMAGE_PADDING;
3238 /* draw label */
3239 dwTextX = rcItem.left + 1;
3240 if (bImage)
3241 dwTextX += IMAGE_PADDING;
3243 if (lvItem.pszText)
3244 ExtTextOutW(hdc, dwTextX, rcItem.top, textoutOptions,
3245 &rcItem, lvItem.pszText, lstrlenW(lvItem.pszText), NULL);
3247 if ((FullSelect)&&(Header_GetItemCount(infoPtr->hwndHeader) > 1))
3249 /* fill in the gap */
3250 RECT rec;
3251 CopyRect(&rec,&rcItem);
3252 rec.left = rec.right;
3253 rec.right = rec.left+REPORT_MARGINX;
3254 ExtTextOutW(hdc, rec.left , rec.top, textoutOptions,
3255 &rec, NULL, 0, NULL);
3258 if (!FullSelect)
3259 CopyRect(SuggestedFocus,&rcItem);
3261 if (nMixMode != 0)
3263 SetROP2(hdc, R2_COPYPEN);
3264 SetBkColor(hdc, dwBkColor);
3265 SetTextColor(hdc, dwTextColor);
3266 if (iBkMode != -1)
3267 SetBkMode(hdc, iBkMode);
3271 /***
3272 * DESCRIPTION:
3273 * Draws an item when in large icon display mode.
3275 * PARAMETER(S):
3276 * [I] HWND : window handle
3277 * [I] HDC : device context handle
3278 * [I] INT : item index
3279 * [I] RECT : clipping rectangle
3280 * [O] RECT * : The text rectangle about which to draw the focus
3282 * RETURN:
3283 * None
3285 static VOID LISTVIEW_DrawLargeItem(HWND hwnd, HDC hdc, INT nItem, RECT rcItem,
3286 RECT *SuggestedFocus)
3288 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3289 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3290 LVITEMW lvItem;
3291 UINT uFormat = DT_TOP | DT_CENTER | DT_WORDBREAK | DT_NOPREFIX |
3292 DT_EDITCONTROL ;
3293 /* Maintain this format in line with the one in LISTVIEW_UpdateLargeItemLabelRect*/
3294 RECT rcTemp;
3296 TRACE("(hwnd=%x, hdc=%x, nItem=%d, left=%d, top=%d, right=%d, bottom=%d)\n",
3297 hwnd, hdc, nItem, rcItem.left, rcItem.top, rcItem.right, rcItem.bottom);
3299 /* get information needed for drawing the item */
3300 ZeroMemory(&lvItem, sizeof(lvItem));
3301 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE;
3302 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3303 lvItem.iItem = nItem;
3304 lvItem.iSubItem = 0;
3305 lvItem.cchTextMax = DISP_TEXT_SIZE;
3306 lvItem.pszText = szDispText;
3307 *lvItem.pszText = '\0';
3308 LISTVIEW_GetItemW(hwnd, &lvItem, FALSE);
3309 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3311 /* redraw the background of the item */
3312 rcTemp = rcItem;
3313 if(infoPtr->nColumnCount == (nItem + 1))
3314 rcTemp.right = infoPtr->rcList.right;
3315 else
3316 rcTemp.right+=WIDTH_PADDING;
3317 /* The comment doesn't say WIDTH_PADDING applies to large icons */
3319 TRACE("background rect (%d,%d)-(%d,%d)\n",
3320 rcTemp.left, rcTemp.top, rcTemp.right, rcTemp.bottom);
3322 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3325 /* Figure out text colours etc. depending on state
3326 * At least the following states exist; there may be more.
3327 * Many items may be selected
3328 * At most one item may have the focus
3329 * The application may not actually be active currently
3330 * 1. The item is not selected in any way
3331 * 2. The cursor is flying over the icon or text and the text is being
3332 * expanded because it is not fully displayed currently.
3333 * 3. The item is selected and is focussed, i.e. the user has not clicked
3334 * in the blank area of the window, and the window (or application?)
3335 * still has the focus.
3336 * 4. As 3 except that a different window has the focus
3337 * 5. The item is the selected item of all the items, but the user has
3338 * clicked somewhere else on the window.
3339 * Only a few of these are handled currently. In particular 2 is not yet
3340 * handled since we do not support the functionality currently (or at least
3341 * we didn't when I wrote this)
3344 if (lvItem.state & LVIS_SELECTED)
3346 /* set item colors */
3347 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
3348 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
3349 SetBkMode (hdc, OPAQUE);
3350 /* set raster mode */
3351 SetROP2(hdc, R2_XORPEN);
3352 /* When exactly is it in XOR? while being dragged? */
3354 else
3356 /* set item colors */
3357 if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
3359 SetBkMode(hdc, TRANSPARENT);
3361 else
3363 SetBkMode(hdc, OPAQUE);
3364 SetBkColor(hdc, infoPtr->clrTextBk);
3367 SetTextColor(hdc, infoPtr->clrText);
3368 /* set raster mode */
3369 SetROP2(hdc, R2_COPYPEN);
3372 /* In cases 2,3 and 5 (see above) the full text is displayed, with word
3373 * wrapping and long words split.
3374 * In cases 1 and 4 only a portion of the text is displayed with word
3375 * wrapping and both word and end ellipsis. (I don't yet know about path
3376 * ellipsis)
3378 uFormat |= ( (lvItem.state & LVIS_FOCUSED) && infoPtr->bFocus) ?
3379 DT_NOCLIP
3381 DT_WORD_ELLIPSIS | DT_END_ELLIPSIS;
3383 /* draw the icon */
3384 if (infoPtr->himlNormal != NULL)
3386 if (lvItem.iImage >= 0)
3388 ImageList_Draw (infoPtr->himlNormal, lvItem.iImage, hdc, rcItem.left,
3389 rcItem.top,
3390 (lvItem.state & LVIS_SELECTED) ? ILD_SELECTED : ILD_NORMAL);
3394 /* Draw the text below the icon */
3396 /* Don't bother painting item being edited */
3397 if ((infoPtr->hwndEdit && (lvItem.state & LVIS_FOCUSED)) ||
3398 !lstrlenW(lvItem.pszText))
3400 SetRectEmpty(SuggestedFocus);
3401 return;
3404 /* Since rcItem.left is left point of icon, compute left point of item box */
3405 rcItem.left -= ((infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2);
3406 rcItem.right = rcItem.left + infoPtr->nItemWidth;
3407 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3408 TRACE("bound box for text+icon (%d,%d)-(%d,%d), iS.cx=%ld, nItemWidth=%d\n",
3409 rcItem.left, rcItem.top, rcItem.right, rcItem.bottom,
3410 infoPtr->iconSize.cx, infoPtr->nItemWidth);
3411 TRACE("rcList (%d,%d)-(%d,%d), rcView (%d,%d)-(%d,%d)\n",
3412 infoPtr->rcList.left, infoPtr->rcList.top,
3413 infoPtr->rcList.right, infoPtr->rcList.bottom,
3414 infoPtr->rcView.left, infoPtr->rcView.top,
3415 infoPtr->rcView.right, infoPtr->rcView.bottom);
3417 InflateRect(&rcItem, -(2*CAPTION_BORDER), 0);
3418 rcItem.top += infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
3421 /* draw label */
3423 /* I am sure of most of the uFormat values. However I am not sure about
3424 * whether we need or do not need the following:
3425 * DT_EXTERNALLEADING, DT_INTERNAL, DT_CALCRECT, DT_NOFULLWIDTHCHARBREAK,
3426 * DT_PATH_ELLIPSIS, DT_RTLREADING,
3427 * We certainly do not need
3428 * DT_BOTTOM, DT_VCENTER, DT_MODIFYSTRING, DT_LEFT, DT_RIGHT, DT_PREFIXONLY,
3429 * DT_SINGLELINE, DT_TABSTOP, DT_EXPANDTABS
3432 /* If the text is being drawn without clipping (i.e. the full text) then we
3433 * need to jump through a few hoops to ensure that it all gets displayed and
3434 * that the background is complete
3436 if (uFormat & DT_NOCLIP)
3438 RECT rcBack=rcItem;
3439 HBRUSH hBrush = CreateSolidBrush(GetBkColor (hdc));
3440 int dx, dy, old_wid, new_wid;
3441 DrawTextW (hdc, lvItem.pszText, -1, &rcItem, uFormat | DT_CALCRECT);
3442 /* Microsoft, in their great wisdom, have decided that the rectangle
3443 * returned by DrawText on DT_CALCRECT will only guarantee the dimension,
3444 * not the location. So we have to do the centring ourselves (and take
3445 * responsibility for agreeing off-by-one consistency with them).
3447 old_wid = rcItem.right-rcItem.left;
3448 new_wid = rcBack.right - rcBack.left;
3449 dx = rcBack.left - rcItem.left + (new_wid-old_wid)/2;
3450 dy = rcBack.top - rcItem.top;
3451 OffsetRect (&rcItem, dx, dy);
3452 FillRect(hdc, &rcItem, hBrush);
3453 DeleteObject(hBrush);
3455 /* else ? What if we are losing the focus? will we not get a complete
3456 * background?
3458 DrawTextW (hdc, lvItem.pszText, -1, &rcItem, uFormat);
3460 CopyRect(SuggestedFocus, &rcItem);
3463 /***
3464 * DESCRIPTION:
3465 * Draws listview items when in report display mode.
3467 * PARAMETER(S):
3468 * [I] HWND : window handle
3469 * [I] HDC : device context handle
3471 * RETURN:
3472 * None
3474 static VOID LISTVIEW_RefreshReport(HWND hwnd, HDC hdc, DWORD cdmode)
3476 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd,0);
3477 SCROLLINFO scrollInfo;
3478 INT nDrawPosY = infoPtr->rcList.top;
3479 INT nColumnCount;
3480 RECT rcItem, rcTemp;
3481 INT j;
3482 INT nItem;
3483 INT nLast;
3484 BOOL FullSelected;
3485 DWORD cditemmode = CDRF_DODEFAULT;
3486 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
3487 INT scrollOffset;
3489 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
3490 scrollInfo.cbSize = sizeof(SCROLLINFO);
3491 scrollInfo.fMask = SIF_POS;
3493 nItem = ListView_GetTopIndex(hwnd);
3495 /* add 1 for displaying a partial item at the bottom */
3496 nLast = nItem + LISTVIEW_GetCountPerColumn(hwnd) + 1;
3497 nLast = min(nLast, GETITEMCOUNT(infoPtr));
3499 /* send cache hint notification */
3500 if (GetWindowLongW(hwnd,GWL_STYLE) & LVS_OWNERDATA)
3502 NMLVCACHEHINT nmlv;
3504 nmlv.hdr.hwndFrom = hwnd;
3505 nmlv.hdr.idFrom = GetWindowLongW(hwnd,GWL_ID);
3506 nmlv.hdr.code = LVN_ODCACHEHINT;
3507 nmlv.iFrom = nItem;
3508 nmlv.iTo = nLast;
3510 SendMessageW(GetParent(hwnd), WM_NOTIFY, (WPARAM)nmlv.hdr.idFrom,
3511 (LPARAM)&nmlv);
3514 nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
3515 infoPtr->nColumnCount = nColumnCount; /* update nColumnCount */
3516 FullSelected = infoPtr->dwExStyle & LVS_EX_FULLROWSELECT;
3518 /* clear the background of any part of the control that doesn't contain items */
3519 SubtractRect(&rcTemp, &infoPtr->rcList, &infoPtr->rcView);
3520 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3522 /* nothing to draw */
3523 if(GETITEMCOUNT(infoPtr) == 0)
3524 return;
3526 /* Get scroll bar info once before loop */
3527 GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
3528 scrollOffset = scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE;
3530 for (; nItem < nLast; nItem++)
3532 RECT SuggestedFocusRect;
3534 /* Do Owner Draw */
3535 if (lStyle & LVS_OWNERDRAWFIXED)
3537 UINT uID = GetWindowLongW( hwnd, GWL_ID);
3538 DRAWITEMSTRUCT dis;
3539 LVITEMW item;
3540 RECT br;
3542 TRACE("Owner Drawn\n");
3543 dis.CtlType = ODT_LISTVIEW;
3544 dis.CtlID = uID;
3545 dis.itemID = nItem;
3546 dis.itemAction = ODA_DRAWENTIRE;
3547 dis.itemState = 0;
3549 if (LISTVIEW_IsSelected(hwnd,nItem)) dis.itemState|=ODS_SELECTED;
3550 if (nItem==infoPtr->nFocusedItem) dis.itemState|=ODS_FOCUS;
3552 dis.hwndItem = hwnd;
3553 dis.hDC = hdc;
3555 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
3557 dis.rcItem.left = -scrollOffset;
3558 dis.rcItem.right = max(dis.rcItem.left, br.right - scrollOffset);
3559 dis.rcItem.top = nDrawPosY;
3560 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3562 ZeroMemory(&item,sizeof(item));
3563 item.iItem = nItem;
3564 item.mask = LVIF_PARAM;
3565 ListView_GetItemW(hwnd, &item);
3567 dis.itemData = item.lParam;
3569 if (SendMessageW(GetParent(hwnd),WM_DRAWITEM,(WPARAM)uID,(LPARAM)&dis))
3571 nDrawPosY += infoPtr->nItemHeight;
3572 continue;
3576 if (FullSelected)
3578 RECT ir,br;
3580 Header_GetItemRect(infoPtr->hwndHeader, 0, &ir);
3581 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
3583 ir.left += REPORT_MARGINX;
3584 ir.right = max(ir.left, br.right - REPORT_MARGINX);
3585 ir.top = nDrawPosY;
3586 ir.bottom = ir.top + infoPtr->nItemHeight;
3588 CopyRect(&SuggestedFocusRect,&ir);
3591 for (j = 0; j < nColumnCount; j++)
3593 if (cdmode & CDRF_NOTIFYITEMDRAW)
3594 cditemmode = LISTVIEW_SendCustomDrawItemNotify (hwnd, hdc, nItem, j,
3595 CDDS_ITEMPREPAINT);
3596 if (cditemmode & CDRF_SKIPDEFAULT)
3597 continue;
3599 Header_GetItemRect(infoPtr->hwndHeader, j, &rcItem);
3601 rcItem.left += REPORT_MARGINX;
3602 rcItem.right = max(rcItem.left, rcItem.right - REPORT_MARGINX);
3603 rcItem.top = nDrawPosY;
3604 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3606 /* Offset the Scroll Bar Pos */
3607 rcItem.left -= scrollOffset;
3608 rcItem.right -= scrollOffset;
3610 if (j == 0)
3612 LISTVIEW_DrawItem(hwnd, hdc, nItem, rcItem, FullSelected,
3613 &SuggestedFocusRect);
3615 else
3617 LISTVIEW_DrawSubItem(hwnd, hdc, nItem, j, rcItem, FullSelected);
3620 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3621 LISTVIEW_SendCustomDrawItemNotify(hwnd, hdc, nItem, 0,
3622 CDDS_ITEMPOSTPAINT);
3625 * Draw Focus Rect
3627 if (LISTVIEW_GetItemState(hwnd,nItem,LVIS_FOCUSED) && infoPtr->bFocus)
3629 BOOL rop=FALSE;
3630 if (FullSelected && LISTVIEW_GetItemState(hwnd,nItem,LVIS_SELECTED))
3631 rop = SetROP2(hdc, R2_XORPEN);
3633 Rectangle(hdc, SuggestedFocusRect.left, SuggestedFocusRect.top,
3634 SuggestedFocusRect.right,SuggestedFocusRect.bottom);
3636 if (rop)
3637 SetROP2(hdc, R2_COPYPEN);
3639 nDrawPosY += infoPtr->nItemHeight;
3643 /***
3644 * DESCRIPTION:
3645 * Retrieves the number of items that can fit vertically in the client area.
3647 * PARAMETER(S):
3648 * [I] HWND : window handle
3650 * RETURN:
3651 * Number of items per row.
3653 static INT LISTVIEW_GetCountPerRow(HWND hwnd)
3655 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd,0);
3656 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3657 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
3658 INT nCountPerRow = 1;
3660 if (nListWidth > 0)
3662 if (uView != LVS_REPORT)
3664 nCountPerRow = nListWidth / infoPtr->nItemWidth;
3665 if (nCountPerRow == 0) nCountPerRow = 1;
3669 return nCountPerRow;
3672 /***
3673 * DESCRIPTION:
3674 * Retrieves the number of items that can fit horizontally in the client
3675 * area.
3677 * PARAMETER(S):
3678 * [I] HWND : window handle
3680 * RETURN:
3681 * Number of items per column.
3683 static INT LISTVIEW_GetCountPerColumn(HWND hwnd)
3685 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd,0);
3686 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3687 INT nCountPerColumn = 1;
3689 if (nListHeight > 0)
3691 nCountPerColumn = nListHeight / infoPtr->nItemHeight;
3692 if (nCountPerColumn == 0) nCountPerColumn = 1;
3695 return nCountPerColumn;
3698 /***
3699 * DESCRIPTION:
3700 * Retrieves the number of columns needed to display all the items when in
3701 * list display mode.
3703 * PARAMETER(S):
3704 * [I] HWND : window handle
3706 * RETURN:
3707 * Number of columns.
3709 static INT LISTVIEW_GetColumnCount(HWND hwnd)
3711 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3712 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
3713 INT nColumnCount = 0;
3715 if ((lStyle & LVS_TYPEMASK) == LVS_LIST)
3717 nColumnCount = infoPtr->rcList.right / infoPtr->nItemWidth;
3718 if (infoPtr->rcList.right % infoPtr->nItemWidth) nColumnCount++;
3721 return nColumnCount;
3725 /***
3726 * DESCRIPTION:
3727 * Draws listview items when in list display mode.
3729 * PARAMETER(S):
3730 * [I] HWND : window handle
3731 * [I] HDC : device context handle
3733 * RETURN:
3734 * None
3736 static VOID LISTVIEW_RefreshList(HWND hwnd, HDC hdc, DWORD cdmode)
3738 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3739 RECT rcItem, FocusRect, rcTemp;
3740 INT i, j;
3741 INT nItem;
3742 INT nColumnCount;
3743 INT nCountPerColumn;
3744 INT nItemWidth = infoPtr->nItemWidth;
3745 INT nItemHeight = infoPtr->nItemHeight;
3746 DWORD cditemmode = CDRF_DODEFAULT;
3748 /* get number of fully visible columns */
3749 nColumnCount = LISTVIEW_GetColumnCount(hwnd);
3750 infoPtr->nColumnCount = nColumnCount;
3751 nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
3752 nItem = ListView_GetTopIndex(hwnd);
3754 /* paint the background of the control that doesn't contain any items */
3755 SubtractRect(&rcTemp, &infoPtr->rcList, &infoPtr->rcView);
3756 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3758 /* nothing to draw, return here */
3759 if(GETITEMCOUNT(infoPtr) == 0)
3760 return;
3762 for (i = 0; i < nColumnCount; i++)
3764 for (j = 0; j < nCountPerColumn; j++, nItem++)
3766 if (nItem >= GETITEMCOUNT(infoPtr))
3767 return;
3769 if (cdmode & CDRF_NOTIFYITEMDRAW)
3770 cditemmode = LISTVIEW_SendCustomDrawItemNotify (hwnd, hdc, nItem, 0,
3771 CDDS_ITEMPREPAINT);
3772 if (cditemmode & CDRF_SKIPDEFAULT)
3773 continue;
3775 rcItem.top = j * nItemHeight;
3776 rcItem.left = i * nItemWidth;
3777 rcItem.bottom = rcItem.top + nItemHeight;
3778 rcItem.right = rcItem.left + nItemWidth;
3779 LISTVIEW_DrawItem(hwnd, hdc, nItem, rcItem, FALSE, &FocusRect);
3781 * Draw Focus Rect
3783 if (LISTVIEW_GetItemState(hwnd,nItem,LVIS_FOCUSED) && infoPtr->bFocus)
3784 Rectangle(hdc, FocusRect.left, FocusRect.top,
3785 FocusRect.right,FocusRect.bottom);
3787 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3788 LISTVIEW_SendCustomDrawItemNotify(hwnd, hdc, nItem, 0,
3789 CDDS_ITEMPOSTPAINT);
3795 /***
3796 * DESCRIPTION:
3797 * Draws listview items when in icon or small icon display mode.
3799 * PARAMETER(S):
3800 * [I] HWND : window handle
3801 * [I] HDC : device context handle
3803 * RETURN:
3804 * None
3806 static VOID LISTVIEW_RefreshIcon(HWND hwnd, HDC hdc, BOOL bSmall, DWORD cdmode)
3808 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3809 POINT ptPosition;
3810 POINT ptOrigin;
3811 RECT rcItem, SuggestedFocus, rcTemp;
3812 INT i;
3813 DWORD cditemmode = CDRF_DODEFAULT;
3815 infoPtr->nColumnCount = 1; /* set this to an arbitrary value to prevent */
3816 /* DrawItem from erasing the incorrect background area */
3818 /* paint the background of the control that doesn't contain any items */
3819 SubtractRect(&rcTemp, &infoPtr->rcList, &infoPtr->rcView);
3820 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3822 /* nothing to draw, return here */
3823 if(GETITEMCOUNT(infoPtr) == 0)
3824 return;
3826 LISTVIEW_GetOrigin(hwnd, &ptOrigin);
3827 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
3829 if (cdmode & CDRF_NOTIFYITEMDRAW)
3830 cditemmode = LISTVIEW_SendCustomDrawItemNotify (hwnd, hdc, i, 0,
3831 CDDS_ITEMPREPAINT);
3832 if (cditemmode & CDRF_SKIPDEFAULT)
3833 continue;
3835 LISTVIEW_GetItemPosition(hwnd, i, &ptPosition);
3836 ptPosition.x += ptOrigin.x;
3837 ptPosition.y += ptOrigin.y;
3839 if (ptPosition.y + infoPtr->nItemHeight > infoPtr->rcList.top)
3841 if (ptPosition.x + infoPtr->nItemWidth > infoPtr->rcList.left)
3843 if (ptPosition.y < infoPtr->rcList.bottom)
3845 if (ptPosition.x < infoPtr->rcList.right)
3847 rcItem.top = ptPosition.y;
3848 rcItem.left = ptPosition.x;
3849 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3850 rcItem.right = rcItem.left + infoPtr->nItemWidth;
3851 if (bSmall)
3852 LISTVIEW_DrawItem(hwnd, hdc, i, rcItem, FALSE, &SuggestedFocus);
3853 else
3854 LISTVIEW_DrawLargeItem(hwnd, hdc, i, rcItem, &SuggestedFocus);
3856 * Draw Focus Rect
3858 if (LISTVIEW_GetItemState(hwnd,i,LVIS_FOCUSED) &&
3859 infoPtr->bFocus && !IsRectEmpty(&SuggestedFocus))
3860 Rectangle(hdc, SuggestedFocus.left, SuggestedFocus.top,
3861 SuggestedFocus.right,SuggestedFocus.bottom);
3866 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3867 LISTVIEW_SendCustomDrawItemNotify(hwnd, hdc, i, 0,
3868 CDDS_ITEMPOSTPAINT);
3872 /***
3873 * DESCRIPTION:
3874 * Draws listview items.
3876 * PARAMETER(S):
3877 * [I] HWND : window handle
3878 * [I] HDC : device context handle
3880 * RETURN:
3881 * NoneX
3883 static VOID LISTVIEW_Refresh(HWND hwnd, HDC hdc)
3885 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3886 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3887 HFONT hOldFont;
3888 HPEN hPen, hOldPen;
3889 DWORD cdmode;
3890 RECT rect;
3892 LISTVIEW_DumpListview (infoPtr, __LINE__);
3894 GetClientRect(hwnd, &rect);
3895 cdmode = LISTVIEW_SendCustomDrawNotify(hwnd,CDDS_PREPAINT,hdc,rect);
3897 if (cdmode == CDRF_SKIPDEFAULT) return;
3899 /* select font */
3900 hOldFont = SelectObject(hdc, infoPtr->hFont);
3902 /* select the dotted pen (for drawing the focus box) */
3903 hPen = CreatePen(PS_ALTERNATE, 1, 0);
3904 hOldPen = SelectObject(hdc, hPen);
3906 /* select transparent brush (for drawing the focus box) */
3907 SelectObject(hdc, GetStockObject(NULL_BRUSH));
3909 if (uView == LVS_LIST)
3910 LISTVIEW_RefreshList(hwnd, hdc, cdmode);
3911 else if (uView == LVS_REPORT)
3912 LISTVIEW_RefreshReport(hwnd, hdc, cdmode);
3913 else if (uView == LVS_SMALLICON)
3914 LISTVIEW_RefreshIcon(hwnd, hdc, TRUE, cdmode);
3915 else if (uView == LVS_ICON)
3916 LISTVIEW_RefreshIcon(hwnd, hdc, FALSE, cdmode);
3918 /* unselect objects */
3919 SelectObject(hdc, hOldFont);
3920 SelectObject(hdc, hOldPen);
3922 /* delete pen */
3923 DeleteObject(hPen);
3925 if (cdmode & CDRF_NOTIFYPOSTPAINT)
3926 LISTVIEW_SendCustomDrawNotify(hwnd, CDDS_POSTPAINT, hdc, rect);
3930 /***
3931 * DESCRIPTION:
3932 * Calculates the approximate width and height of a given number of items.
3934 * PARAMETER(S):
3935 * [I] HWND : window handle
3936 * [I] INT : number of items
3937 * [I] INT : width
3938 * [I] INT : height
3940 * RETURN:
3941 * Returns a DWORD. The width in the low word and the height in high word.
3943 static LRESULT LISTVIEW_ApproximateViewRect(HWND hwnd, INT nItemCount,
3944 WORD wWidth, WORD wHeight)
3946 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3947 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3948 INT nItemCountPerColumn = 1;
3949 INT nColumnCount = 0;
3950 DWORD dwViewRect = 0;
3952 if (nItemCount == -1)
3953 nItemCount = GETITEMCOUNT(infoPtr);
3955 if (uView == LVS_LIST)
3957 if (wHeight == 0xFFFF)
3959 /* use current height */
3960 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3963 if (wHeight < infoPtr->nItemHeight)
3964 wHeight = infoPtr->nItemHeight;
3966 if (nItemCount > 0)
3968 if (infoPtr->nItemHeight > 0)
3970 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
3971 if (nItemCountPerColumn == 0)
3972 nItemCountPerColumn = 1;
3974 if (nItemCount % nItemCountPerColumn != 0)
3975 nColumnCount = nItemCount / nItemCountPerColumn;
3976 else
3977 nColumnCount = nItemCount / nItemCountPerColumn + 1;
3981 /* Microsoft padding magic */
3982 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
3983 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
3985 dwViewRect = MAKELONG(wWidth, wHeight);
3987 else if (uView == LVS_REPORT)
3988 FIXME("uView == LVS_REPORT: not implemented\n");
3989 else if (uView == LVS_SMALLICON)
3990 FIXME("uView == LVS_SMALLICON: not implemented\n");
3991 else if (uView == LVS_ICON)
3992 FIXME("uView == LVS_ICON: not implemented\n");
3994 return dwViewRect;
3997 /***
3998 * DESCRIPTION:
3999 * Arranges listview items in icon display mode.
4001 * PARAMETER(S):
4002 * [I] HWND : window handle
4003 * [I] INT : alignment code
4005 * RETURN:
4006 * SUCCESS : TRUE
4007 * FAILURE : FALSE
4009 static LRESULT LISTVIEW_Arrange(HWND hwnd, INT nAlignCode)
4011 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
4012 BOOL bResult = FALSE;
4014 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4016 switch (nAlignCode)
4018 case LVA_ALIGNLEFT:
4019 FIXME("nAlignCode=LVA_ALIGNLEFT: not implemented\n");
4020 break;
4021 case LVA_ALIGNTOP:
4022 FIXME("nAlignCode=LVA_ALIGNTOP: not implemented\n");
4023 break;
4024 case LVA_DEFAULT:
4025 FIXME("nAlignCode=LVA_DEFAULT: not implemented\n");
4026 break;
4027 case LVA_SNAPTOGRID:
4028 FIXME("nAlignCode=LVA_SNAPTOGRID: not implemented\n");
4029 break;
4033 return bResult;
4036 /* << LISTVIEW_CreateDragImage >> */
4039 /***
4040 * DESCRIPTION:
4041 * Removes all listview items and subitems.
4043 * PARAMETER(S):
4044 * [I] HWND : window handle
4046 * RETURN:
4047 * SUCCESS : TRUE
4048 * FAILURE : FALSE
4050 static LRESULT LISTVIEW_DeleteAllItems(HWND hwnd)
4052 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4053 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
4054 UINT uView = lStyle & LVS_TYPEMASK;
4055 LISTVIEW_ITEM *lpItem;
4056 LISTVIEW_SUBITEM *lpSubItem;
4057 NMLISTVIEW nmlv;
4058 BOOL bSuppress;
4059 BOOL bResult = FALSE;
4060 HDPA hdpaSubItems;
4062 TRACE("(hwnd=%x,)\n", hwnd);
4064 LISTVIEW_RemoveAllSelections(hwnd);
4065 infoPtr->nSelectionMark=-1;
4066 infoPtr->nFocusedItem=-1;
4067 /* But we are supposed to leave nHotItem as is! */
4069 if (lStyle & LVS_OWNERDATA)
4071 infoPtr->hdpaItems->nItemCount = 0;
4072 InvalidateRect(hwnd, NULL, TRUE);
4073 return TRUE;
4076 if (GETITEMCOUNT(infoPtr) > 0)
4078 INT i, j;
4080 /* send LVN_DELETEALLITEMS notification */
4081 /* verify if subsequent LVN_DELETEITEM notifications should be
4082 suppressed */
4083 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4084 nmlv.iItem = -1;
4085 bSuppress = listview_notify(hwnd, LVN_DELETEALLITEMS, &nmlv);
4087 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
4089 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
4090 if (hdpaSubItems != NULL)
4092 for (j = 1; j < hdpaSubItems->nItemCount; j++)
4094 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, j);
4095 if (lpSubItem != NULL)
4097 /* free subitem string */
4098 if (is_textW(lpSubItem->pszText))
4099 COMCTL32_Free(lpSubItem->pszText);
4101 /* free subitem */
4102 COMCTL32_Free(lpSubItem);
4106 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
4107 if (lpItem != NULL)
4109 if (!bSuppress)
4111 /* send LVN_DELETEITEM notification */
4112 nmlv.iItem = i;
4113 nmlv.lParam = lpItem->lParam;
4114 listview_notify(hwnd, LVN_DELETEITEM, &nmlv);
4117 /* free item string */
4118 if (is_textW(lpItem->pszText))
4119 COMCTL32_Free(lpItem->pszText);
4121 /* free item */
4122 COMCTL32_Free(lpItem);
4125 DPA_Destroy(hdpaSubItems);
4129 /* reinitialize listview memory */
4130 bResult = DPA_DeleteAllPtrs(infoPtr->hdpaItems);
4132 /* align items (set position of each item) */
4133 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4135 if (lStyle & LVS_ALIGNLEFT)
4137 LISTVIEW_AlignLeft(hwnd);
4139 else
4141 LISTVIEW_AlignTop(hwnd);
4145 LISTVIEW_UpdateScroll(hwnd);
4147 /* invalidate client area (optimization needed) */
4148 InvalidateRect(hwnd, NULL, TRUE);
4151 return bResult;
4154 /***
4155 * DESCRIPTION:
4156 * Removes a column from the listview control.
4158 * PARAMETER(S):
4159 * [I] HWND : window handle
4160 * [I] INT : column index
4162 * RETURN:
4163 * SUCCESS : TRUE
4164 * FAILURE : FALSE
4166 static LRESULT LISTVIEW_DeleteColumn(HWND hwnd, INT nColumn)
4168 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4169 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
4170 UINT uOwnerData = GetWindowLongW(hwnd, GWL_STYLE) & LVS_OWNERDATA;
4171 BOOL bResult = FALSE;
4173 if (Header_DeleteItem(infoPtr->hwndHeader, nColumn) != FALSE)
4175 if (!uOwnerData)
4176 bResult = LISTVIEW_RemoveColumn(infoPtr->hdpaItems, nColumn);
4178 /* Need to reset the item width when deleting a column */
4179 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
4181 /* reset scroll parameters */
4182 if (uView == LVS_REPORT)
4184 /* update scrollbar(s) */
4185 LISTVIEW_UpdateScroll(hwnd);
4187 /* refresh client area */
4188 InvalidateRect(hwnd, NULL, FALSE);
4192 return bResult;
4195 /***
4196 * DESCRIPTION:
4197 * Removes an item from the listview control.
4199 * PARAMETER(S):
4200 * [I] HWND : window handle
4201 * [I] INT : item index
4203 * RETURN:
4204 * SUCCESS : TRUE
4205 * FAILURE : FALSE
4207 static LRESULT LISTVIEW_DeleteItem(HWND hwnd, INT nItem)
4209 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4210 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
4211 UINT uView = lStyle & LVS_TYPEMASK;
4212 LONG lCtrlId = GetWindowLongW(hwnd, GWL_ID);
4213 NMLISTVIEW nmlv;
4214 BOOL bResult = FALSE;
4215 HDPA hdpaSubItems;
4216 LISTVIEW_ITEM *lpItem;
4217 LISTVIEW_SUBITEM *lpSubItem;
4218 INT i;
4219 LVITEMW item;
4221 TRACE("(hwnd=%x, nItem=%d)\n", hwnd, nItem);
4224 /* First, send LVN_DELETEITEM notification. */
4225 memset(&nmlv, 0, sizeof (NMLISTVIEW));
4226 nmlv.hdr.hwndFrom = hwnd;
4227 nmlv.hdr.idFrom = lCtrlId;
4228 nmlv.hdr.code = LVN_DELETEITEM;
4229 nmlv.iItem = nItem;
4230 SendMessageW(GetParent(hwnd), WM_NOTIFY, (WPARAM)lCtrlId,
4231 (LPARAM)&nmlv);
4234 /* remove it from the selection range */
4235 ZeroMemory(&item,sizeof(item));
4236 item.stateMask = LVIS_SELECTED;
4237 LISTVIEW_SetItemState(hwnd,nItem,&item);
4239 if (lStyle & LVS_OWNERDATA)
4241 infoPtr->hdpaItems->nItemCount --;
4242 InvalidateRect(hwnd, NULL, TRUE);
4243 return TRUE;
4246 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
4248 /* initialize memory */
4249 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4251 hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
4252 if (hdpaSubItems != NULL)
4254 for (i = 1; i < hdpaSubItems->nItemCount; i++)
4256 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
4257 if (lpSubItem != NULL)
4259 /* free item string */
4260 if (is_textW(lpSubItem->pszText))
4261 COMCTL32_Free(lpSubItem->pszText);
4263 /* free item */
4264 COMCTL32_Free(lpSubItem);
4268 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
4269 if (lpItem != NULL)
4271 /* free item string */
4272 if (is_textW(lpItem->pszText))
4273 COMCTL32_Free(lpItem->pszText);
4275 /* free item */
4276 COMCTL32_Free(lpItem);
4279 bResult = DPA_Destroy(hdpaSubItems);
4282 LISTVIEW_ShiftIndices(hwnd,nItem,-1);
4284 /* align items (set position of each item) */
4285 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4287 if (lStyle & LVS_ALIGNLEFT)
4288 LISTVIEW_AlignLeft(hwnd);
4289 else
4290 LISTVIEW_AlignTop(hwnd);
4293 LISTVIEW_UpdateScroll(hwnd);
4295 /* refresh client area */
4296 InvalidateRect(hwnd, NULL, TRUE);
4299 return bResult;
4303 /***
4304 * DESCRIPTION:
4305 * Return edit control handle of current edit label
4307 * PARAMETER(S):
4308 * [I] HWND : window handle
4310 * RETURN:
4311 * SUCCESS : HWND
4312 * FAILURE : 0
4314 static LRESULT LISTVIEW_GetEditControl(HWND hwnd)
4316 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4317 return infoPtr->hwndEdit;
4321 /***
4322 * DESCRIPTION:
4323 * Callback implementation for editlabel control
4325 * PARAMETER(S):
4326 * [I] HWND : window handle
4327 * [I] LPSTR : modified text
4328 * [I] DWORD : item index
4329 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4331 * RETURN:
4332 * SUCCESS : TRUE
4333 * FAILURE : FALSE
4335 static BOOL LISTVIEW_EndEditLabelT(HWND hwnd, LPWSTR pszText, DWORD nItem, BOOL isW)
4337 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4338 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
4339 NMLVDISPINFOW dispInfo;
4340 LISTVIEW_ITEM *lpItem;
4341 HDPA hdpaSubItems;
4342 LISTVIEW_ITEM lvItemRef;
4343 LVITEMW item;
4344 BOOL bResult = TRUE;
4346 TRACE("(hwnd=%x, pszText=%s, nItem=%ld, isW=%d)\n", hwnd, debugstr_t(pszText, isW), nItem, isW);
4348 if (!(lStyle & LVS_OWNERDATA))
4350 if (!(hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)))
4351 return FALSE;
4353 if (!(lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)))
4354 return FALSE;
4356 else
4358 ZeroMemory(&lvItemRef,sizeof(LISTVIEW_ITEM));
4359 ZeroMemory(&item,sizeof(item));
4360 item.iItem = nItem;
4361 item.iSubItem = 0;
4362 item.mask = LVIF_PARAM | LVIF_STATE;
4363 ListView_GetItemW(hwnd, &item);
4364 lvItemRef.state = item.state;
4365 lvItemRef.iImage = item.iImage;
4366 lvItemRef.lParam = item.lParam;
4367 lpItem = &lvItemRef;
4370 ZeroMemory(&dispInfo, sizeof(dispInfo));
4371 dispInfo.item.mask = 0;
4372 dispInfo.item.iItem = nItem;
4373 dispInfo.item.state = lpItem->state;
4374 dispInfo.item.stateMask = 0;
4375 dispInfo.item.pszText = pszText;
4376 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4377 dispInfo.item.iImage = lpItem->iImage;
4378 dispInfo.item.lParam = lpItem->lParam;
4379 infoPtr->hwndEdit = 0;
4381 /* Do we need to update the Item Text */
4382 if(dispinfo_notifyT(hwnd, LVN_ENDLABELEDITW, &dispInfo, isW))
4383 if (lpItem->pszText != LPSTR_TEXTCALLBACKW && !(lStyle & LVS_OWNERDATA))
4384 bResult = textsetptrT(&lpItem->pszText, pszText, isW);
4386 return bResult;
4389 /***
4390 * DESCRIPTION:
4391 * Callback implementation for editlabel control
4393 * PARAMETER(S):
4394 * [I] HWND : window handle
4395 * [I] LPSTR : modified text
4396 * [I] DWORD : item index
4398 * RETURN:
4399 * SUCCESS : TRUE
4400 * FAILURE : FALSE
4402 static BOOL LISTVIEW_EndEditLabelW(HWND hwnd, LPWSTR pszText, DWORD nItem)
4404 return LISTVIEW_EndEditLabelT(hwnd, pszText, nItem, TRUE);
4407 /***
4408 * DESCRIPTION:
4409 * Callback implementation for editlabel control
4411 * PARAMETER(S):
4412 * [I] HWND : window handle
4413 * [I] LPSTR : modified text
4414 * [I] DWORD : item index
4416 * RETURN:
4417 * SUCCESS : TRUE
4418 * FAILURE : FALSE
4420 static BOOL LISTVIEW_EndEditLabelA(HWND hwnd, LPSTR pszText, DWORD nItem)
4422 return LISTVIEW_EndEditLabelT(hwnd, (LPWSTR)pszText, nItem, FALSE);
4425 /***
4426 * DESCRIPTION:
4427 * Begin in place editing of specified list view item
4429 * PARAMETER(S):
4430 * [I] HWND : window handle
4431 * [I] INT : item index
4432 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4434 * RETURN:
4435 * SUCCESS : TRUE
4436 * FAILURE : FALSE
4438 static HWND LISTVIEW_EditLabelT(HWND hwnd, INT nItem, BOOL isW)
4440 NMLVDISPINFOW dispInfo;
4441 RECT rect;
4442 LISTVIEW_ITEM *lpItem;
4443 HWND hedit;
4444 HINSTANCE hinst = GetWindowLongW(hwnd, GWL_HINSTANCE);
4445 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4446 HDPA hdpaSubItems;
4447 WCHAR szDispText[DISP_TEXT_SIZE];
4448 LVITEMW lvItem;
4449 LISTVIEW_ITEM lvItemRef;
4450 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
4452 if (~GetWindowLongW(hwnd, GWL_STYLE) & LVS_EDITLABELS)
4453 return FALSE;
4455 /* Is the EditBox still there, if so remove it */
4456 if(infoPtr->hwndEdit != 0)
4457 SetFocus(hwnd);
4459 LISTVIEW_SetSelection(hwnd, nItem);
4460 LISTVIEW_SetItemFocus(hwnd, nItem);
4462 if (!(lStyle & LVS_OWNERDATA))
4464 if (NULL == (hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)))
4465 return 0;
4467 if (NULL == (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)))
4468 return 0;
4470 else
4472 LVITEMW item;
4473 ZeroMemory(&lvItemRef,sizeof(LISTVIEW_ITEM));
4474 ZeroMemory(&item, sizeof(item));
4475 item.iItem = nItem;
4476 item.iSubItem = 0;
4477 item.mask = LVIF_PARAM | LVIF_STATE;
4478 ListView_GetItemW(hwnd, &item);
4479 lvItemRef.iImage = item.iImage;
4480 lvItemRef.state = item.state;
4481 lvItemRef.lParam = item.lParam;
4482 lpItem = &lvItemRef;
4485 /* get information needed for drawing the item */
4486 ZeroMemory(&lvItem, sizeof(lvItem));
4487 lvItem.mask = LVIF_TEXT;
4488 lvItem.iItem = nItem;
4489 lvItem.iSubItem = 0;
4490 lvItem.cchTextMax = DISP_TEXT_SIZE;
4491 lvItem.pszText = szDispText;
4492 *lvItem.pszText = '\0';
4493 LISTVIEW_GetItemT(hwnd, &lvItem, FALSE, isW);
4495 ZeroMemory(&dispInfo, sizeof(dispInfo));
4496 dispInfo.item.mask = 0;
4497 dispInfo.item.iItem = nItem;
4498 dispInfo.item.state = lpItem->state;
4499 dispInfo.item.stateMask = 0;
4500 dispInfo.item.pszText = lvItem.pszText;
4501 dispInfo.item.cchTextMax = lstrlenW(lvItem.pszText);
4502 dispInfo.item.iImage = lpItem->iImage;
4503 dispInfo.item.lParam = lpItem->lParam;
4505 if (dispinfo_notifyT(hwnd, LVN_BEGINLABELEDITW, &dispInfo, isW))
4506 return 0;
4508 rect.left = LVIR_LABEL;
4509 if (!LISTVIEW_GetItemRect(hwnd, nItem, &rect))
4510 return 0;
4512 if (!(hedit = CreateEditLabelT(szDispText , WS_VISIBLE,
4513 rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, hwnd, hinst,
4514 isW ? LISTVIEW_EndEditLabelW : (EditlblCallbackW)LISTVIEW_EndEditLabelA,
4515 nItem, isW)))
4516 return 0;
4518 infoPtr->hwndEdit = hedit;
4519 SetFocus(hedit);
4520 SendMessageW(hedit, EM_SETSEL, 0, -1);
4522 return hedit;
4526 /***
4527 * DESCRIPTION:
4528 * Ensures the specified item is visible, scrolling into view if necessary.
4530 * PARAMETER(S):
4531 * [I] HWND : window handle
4532 * [I] INT : item index
4533 * [I] BOOL : partially or entirely visible
4535 * RETURN:
4536 * SUCCESS : TRUE
4537 * FAILURE : FALSE
4539 static BOOL LISTVIEW_EnsureVisible(HWND hwnd, INT nItem, BOOL bPartial)
4541 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4542 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
4543 INT nScrollPosHeight = 0;
4544 INT nScrollPosWidth = 0;
4545 SCROLLINFO scrollInfo;
4546 RECT rcItem;
4547 BOOL bRedraw = FALSE;
4549 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
4550 scrollInfo.cbSize = sizeof(SCROLLINFO);
4551 scrollInfo.fMask = SIF_POS;
4553 /* ALWAYS bPartial == FALSE, FOR NOW! */
4555 rcItem.left = LVIR_BOUNDS;
4556 if (LISTVIEW_GetItemRect(hwnd, nItem, &rcItem) != FALSE)
4558 if (rcItem.left < infoPtr->rcList.left)
4560 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
4562 /* scroll left */
4563 bRedraw = TRUE;
4564 if (uView == LVS_LIST)
4566 nScrollPosWidth = infoPtr->nItemWidth;
4567 rcItem.left += infoPtr->rcList.left;
4569 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4571 nScrollPosWidth = LISTVIEW_SCROLL_DIV_SIZE;
4572 rcItem.left += infoPtr->rcList.left;
4575 /* When in LVS_REPORT view, the scroll position should
4576 not be updated. */
4577 if (nScrollPosWidth != 0)
4579 if (rcItem.left % nScrollPosWidth == 0)
4580 scrollInfo.nPos += rcItem.left / nScrollPosWidth;
4581 else
4582 scrollInfo.nPos += rcItem.left / nScrollPosWidth - 1;
4584 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
4588 else if (rcItem.right > infoPtr->rcList.right)
4590 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
4592 /* scroll right */
4593 bRedraw = TRUE;
4594 if (uView == LVS_LIST)
4596 rcItem.right -= infoPtr->rcList.right;
4597 nScrollPosWidth = infoPtr->nItemWidth;
4599 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4601 rcItem.right -= infoPtr->rcList.right;
4602 nScrollPosWidth = LISTVIEW_SCROLL_DIV_SIZE;
4605 /* When in LVS_REPORT view, the scroll position should
4606 not be updated. */
4607 if (nScrollPosWidth != 0)
4609 if (rcItem.right % nScrollPosWidth == 0)
4610 scrollInfo.nPos += rcItem.right / nScrollPosWidth;
4611 else
4612 scrollInfo.nPos += rcItem.right / nScrollPosWidth + 1;
4614 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
4619 if (rcItem.top < infoPtr->rcList.top)
4621 /* scroll up */
4622 bRedraw = TRUE;
4623 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
4625 if (uView == LVS_REPORT)
4627 rcItem.top -= infoPtr->rcList.top;
4628 nScrollPosHeight = infoPtr->nItemHeight;
4630 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4632 nScrollPosHeight = LISTVIEW_SCROLL_DIV_SIZE;
4633 rcItem.top += infoPtr->rcList.top;
4636 if (rcItem.top % nScrollPosHeight == 0)
4637 scrollInfo.nPos += rcItem.top / nScrollPosHeight;
4638 else
4639 scrollInfo.nPos += rcItem.top / nScrollPosHeight - 1;
4641 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
4644 else if (rcItem.bottom > infoPtr->rcList.bottom)
4646 /* scroll down */
4647 bRedraw = TRUE;
4648 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
4650 if (uView == LVS_REPORT)
4652 rcItem.bottom -= infoPtr->rcList.bottom;
4653 nScrollPosHeight = infoPtr->nItemHeight;
4655 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4657 nScrollPosHeight = LISTVIEW_SCROLL_DIV_SIZE;
4658 rcItem.bottom -= infoPtr->rcList.bottom;
4661 if (rcItem.bottom % nScrollPosHeight == 0)
4662 scrollInfo.nPos += rcItem.bottom / nScrollPosHeight;
4663 else
4664 scrollInfo.nPos += rcItem.bottom / nScrollPosHeight + 1;
4666 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
4671 if(bRedraw)
4672 InvalidateRect(hwnd,NULL,TRUE);
4673 return TRUE;
4676 /***
4677 * DESCRIPTION:
4678 * Retrieves the nearest item, given a position and a direction.
4680 * PARAMETER(S):
4681 * [I] HWND : window handle
4682 * [I] POINT : start position
4683 * [I] UINT : direction
4685 * RETURN:
4686 * Item index if successdful, -1 otherwise.
4688 static INT LISTVIEW_GetNearestItem(HWND hwnd, POINT pt, UINT vkDirection)
4690 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4691 LV_INTHIT lvIntHit;
4692 INT nItem = -1;
4693 RECT rcView;
4695 TRACE("point %ld,%ld, direction %s\n", pt.x, pt.y,
4696 (vkDirection == VK_DOWN) ? "VK_DOWN" :
4697 ((vkDirection == VK_UP) ? "VK_UP" :
4698 ((vkDirection == VK_LEFT) ? "VK_LEFT" : "VK_RIGHT")));
4700 if (LISTVIEW_GetViewRect(hwnd, &rcView) != FALSE)
4702 ZeroMemory(&lvIntHit, sizeof(lvIntHit));
4703 LISTVIEW_GetOrigin(hwnd, &lvIntHit.ht.pt);
4704 lvIntHit.ht.pt.x += pt.x;
4705 lvIntHit.ht.pt.y += pt.y;
4707 if (vkDirection == VK_DOWN)
4708 lvIntHit.ht.pt.y += infoPtr->nItemHeight;
4709 else if (vkDirection == VK_UP)
4710 lvIntHit.ht.pt.y -= infoPtr->nItemHeight;
4711 else if (vkDirection == VK_LEFT)
4712 lvIntHit.ht.pt.x -= infoPtr->nItemWidth;
4713 else if (vkDirection == VK_RIGHT)
4714 lvIntHit.ht.pt.x += infoPtr->nItemWidth;
4716 if (PtInRect(&rcView, lvIntHit.ht.pt) == FALSE)
4717 return -1;
4718 else
4720 nItem = LISTVIEW_SuperHitTestItem(hwnd, &lvIntHit, TRUE);
4721 return nItem == -1 ? lvIntHit.iDistItem : nItem;
4725 return nItem;
4728 /***
4729 * DESCRIPTION:
4730 * Searches for an item with specific characteristics.
4732 * PARAMETER(S):
4733 * [I] hwnd : window handle
4734 * [I] nStart : base item index
4735 * [I] lpFindInfo : item information to look for
4737 * RETURN:
4738 * SUCCESS : index of item
4739 * FAILURE : -1
4741 static LRESULT LISTVIEW_FindItemW(HWND hwnd, INT nStart,
4742 LPLVFINDINFOW lpFindInfo)
4744 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4745 POINT ptItem;
4746 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4747 LVITEMW lvItem;
4748 BOOL bWrap = FALSE;
4749 INT nItem = nStart;
4750 INT nLast = GETITEMCOUNT(infoPtr);
4752 if ((nItem >= -1) && (lpFindInfo != NULL))
4754 ZeroMemory(&lvItem, sizeof(lvItem));
4756 if (lpFindInfo->flags & LVFI_PARAM)
4758 lvItem.mask |= LVIF_PARAM;
4761 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
4763 lvItem.mask |= LVIF_TEXT;
4764 lvItem.pszText = szDispText;
4765 lvItem.cchTextMax = DISP_TEXT_SIZE;
4768 if (lpFindInfo->flags & LVFI_WRAP)
4769 bWrap = TRUE;
4771 if (lpFindInfo->flags & LVFI_NEARESTXY)
4773 ptItem.x = lpFindInfo->pt.x;
4774 ptItem.y = lpFindInfo->pt.y;
4777 while (1)
4779 while (nItem < nLast)
4781 if (lpFindInfo->flags & LVFI_NEARESTXY)
4783 nItem = LISTVIEW_GetNearestItem(hwnd, ptItem,
4784 lpFindInfo->vkDirection);
4785 if (nItem != -1)
4787 /* get position of the new item index */
4788 if (ListView_GetItemPosition(hwnd, nItem, &ptItem) == FALSE)
4789 return -1;
4791 else
4792 return -1;
4794 else
4796 nItem++;
4799 lvItem.iItem = nItem;
4800 lvItem.iSubItem = 0;
4801 if (LISTVIEW_GetItemW(hwnd, &lvItem, TRUE))
4803 if (lvItem.mask & LVIF_TEXT)
4805 if (lpFindInfo->flags & LVFI_PARTIAL)
4807 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL)
4808 continue;
4810 else
4812 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0)
4813 continue;
4817 if (lvItem.mask & LVIF_PARAM)
4819 if (lpFindInfo->lParam != lvItem.lParam)
4820 continue;
4823 return nItem;
4827 if (bWrap)
4829 nItem = -1;
4830 nLast = nStart + 1;
4831 bWrap = FALSE;
4833 else
4835 return -1;
4840 return -1;
4843 /***
4844 * DESCRIPTION:
4845 * Searches for an item with specific characteristics.
4847 * PARAMETER(S):
4848 * [I] hwnd : window handle
4849 * [I] nStart : base item index
4850 * [I] lpFindInfo : item information to look for
4852 * RETURN:
4853 * SUCCESS : index of item
4854 * FAILURE : -1
4856 static LRESULT LISTVIEW_FindItemA(HWND hwnd, INT nStart,
4857 LPLVFINDINFOA lpFindInfo)
4859 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
4860 LVFINDINFOW fiw;
4861 LRESULT res;
4863 memcpy(&fiw, lpFindInfo, sizeof(fiw));
4864 if (hasText) fiw.psz = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
4865 res = LISTVIEW_FindItemW(hwnd, nStart, &fiw);
4866 if (hasText) textfreeT((LPWSTR)fiw.psz, FALSE);
4867 return res;
4870 /***
4871 * DESCRIPTION:
4872 * Retrieves the background color of the listview control.
4874 * PARAMETER(S):
4875 * [I] HWND : window handle
4877 * RETURN:
4878 * COLORREF associated with the background.
4880 static LRESULT LISTVIEW_GetBkColor(HWND hwnd)
4882 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4884 return infoPtr->clrBk;
4887 /***
4888 * DESCRIPTION:
4889 * Retrieves the background image of the listview control.
4891 * PARAMETER(S):
4892 * [I] HWND : window handle
4893 * [O] LPLVMKBIMAGE : background image attributes
4895 * RETURN:
4896 * SUCCESS : TRUE
4897 * FAILURE : FALSE
4899 /* static LRESULT LISTVIEW_GetBkImage(HWND hwnd, LPLVBKIMAGE lpBkImage) */
4900 /* { */
4901 /* FIXME (listview, "empty stub!\n"); */
4902 /* return FALSE; */
4903 /* } */
4905 /***
4906 * DESCRIPTION:
4907 * Retrieves the callback mask.
4909 * PARAMETER(S):
4910 * [I] HWND : window handle
4912 * RETURN:
4913 * Value of mask
4915 static UINT LISTVIEW_GetCallbackMask(HWND hwnd)
4917 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4919 return infoPtr->uCallbackMask;
4922 /***
4923 * DESCRIPTION:
4924 * Retrieves column attributes.
4926 * PARAMETER(S):
4927 * [I] HWND : window handle
4928 * [I] INT : column index
4929 * [IO] LPLVCOLUMNW : column information
4930 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
4931 * otherwise it is in fact a LPLVCOLUMNA
4933 * RETURN:
4934 * SUCCESS : TRUE
4935 * FAILURE : FALSE
4937 static LRESULT LISTVIEW_GetColumnT(HWND hwnd, INT nItem, LPLVCOLUMNW lpColumn, BOOL isW)
4939 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4940 HDITEMW hdi;
4941 BOOL bResult = FALSE;
4943 if (lpColumn != NULL)
4946 TRACE("(hwnd=%x, col=%d, lpColumn=%s, isW=%d)\n",
4947 hwnd, nItem, debuglvcolumn_t(lpColumn, isW), isW);
4949 /* initialize memory */
4950 ZeroMemory(&hdi, sizeof(hdi));
4952 if (lpColumn->mask & LVCF_FMT)
4953 hdi.mask |= HDI_FORMAT;
4955 if (lpColumn->mask & LVCF_WIDTH)
4956 hdi.mask |= HDI_WIDTH;
4958 if (lpColumn->mask & LVCF_TEXT)
4960 hdi.mask |= HDI_TEXT;
4961 hdi.cchTextMax = lpColumn->cchTextMax;
4962 hdi.pszText = lpColumn->pszText;
4965 if (lpColumn->mask & LVCF_IMAGE)
4966 hdi.mask |= HDI_IMAGE;
4968 if (lpColumn->mask & LVCF_ORDER)
4969 hdi.mask |= HDI_ORDER;
4971 if (isW)
4972 bResult = Header_GetItemW(infoPtr->hwndHeader, nItem, &hdi);
4973 else
4974 bResult = Header_GetItemA(infoPtr->hwndHeader, nItem, &hdi);
4976 if (bResult != FALSE)
4978 if (lpColumn->mask & LVCF_FMT)
4980 lpColumn->fmt = 0;
4982 if (hdi.fmt & HDF_LEFT)
4983 lpColumn->fmt |= LVCFMT_LEFT;
4984 else if (hdi.fmt & HDF_RIGHT)
4985 lpColumn->fmt |= LVCFMT_RIGHT;
4986 else if (hdi.fmt & HDF_CENTER)
4987 lpColumn->fmt |= LVCFMT_CENTER;
4989 if (hdi.fmt & HDF_IMAGE)
4990 lpColumn->fmt |= LVCFMT_COL_HAS_IMAGES;
4992 if (hdi.fmt & HDF_BITMAP_ON_RIGHT)
4993 lpColumn->fmt |= LVCFMT_BITMAP_ON_RIGHT;
4996 if (lpColumn->mask & LVCF_WIDTH)
4997 lpColumn->cx = hdi.cxy;
4999 if (lpColumn->mask & LVCF_IMAGE)
5000 lpColumn->iImage = hdi.iImage;
5002 if (lpColumn->mask & LVCF_ORDER)
5003 lpColumn->iOrder = hdi.iOrder;
5007 return bResult;
5011 static LRESULT LISTVIEW_GetColumnOrderArray(HWND hwnd, INT iCount, LPINT lpiArray)
5013 /* LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); */
5014 INT i;
5016 if (!lpiArray)
5017 return FALSE;
5019 /* little hack */
5020 for (i = 0; i < iCount; i++)
5021 lpiArray[i] = i;
5023 return TRUE;
5026 /***
5027 * DESCRIPTION:
5028 * Retrieves the column width.
5030 * PARAMETER(S):
5031 * [I] HWND : window handle
5032 * [I] int : column index
5034 * RETURN:
5035 * SUCCESS : column width
5036 * FAILURE : zero
5038 static LRESULT LISTVIEW_GetColumnWidth(HWND hwnd, INT nColumn)
5040 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5041 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5042 INT nColumnWidth = 0;
5043 HDITEMW hdi;
5045 if (uView == LVS_LIST)
5047 nColumnWidth = infoPtr->nItemWidth;
5049 else if (uView == LVS_REPORT)
5051 /* get column width from header */
5052 ZeroMemory(&hdi, sizeof(hdi));
5053 hdi.mask = HDI_WIDTH;
5054 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdi) != FALSE)
5055 nColumnWidth = hdi.cxy;
5058 return nColumnWidth;
5061 /***
5062 * DESCRIPTION:
5063 * In list or report display mode, retrieves the number of items that can fit
5064 * vertically in the visible area. In icon or small icon display mode,
5065 * retrieves the total number of visible items.
5067 * PARAMETER(S):
5068 * [I] HWND : window handle
5070 * RETURN:
5071 * Number of fully visible items.
5073 static LRESULT LISTVIEW_GetCountPerPage(HWND hwnd)
5075 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5076 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5077 INT nItemCount = 0;
5079 if (uView == LVS_LIST)
5081 if (infoPtr->rcList.right > infoPtr->nItemWidth)
5083 nItemCount = LISTVIEW_GetCountPerRow(hwnd) *
5084 LISTVIEW_GetCountPerColumn(hwnd);
5087 else if (uView == LVS_REPORT)
5089 nItemCount = LISTVIEW_GetCountPerColumn(hwnd);
5091 else
5093 nItemCount = GETITEMCOUNT(infoPtr);
5096 return nItemCount;
5099 /* LISTVIEW_GetEditControl */
5101 /***
5102 * DESCRIPTION:
5103 * Retrieves the extended listview style.
5105 * PARAMETERS:
5106 * [I] HWND : window handle
5108 * RETURN:
5109 * SUCCESS : previous style
5110 * FAILURE : 0
5112 static LRESULT LISTVIEW_GetExtendedListViewStyle(HWND hwnd)
5114 LISTVIEW_INFO *infoPtr;
5116 /* make sure we can get the listview info */
5117 if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0)))
5118 return (0);
5120 return (infoPtr->dwExStyle);
5123 /***
5124 * DESCRIPTION:
5125 * Retrieves the handle to the header control.
5127 * PARAMETER(S):
5128 * [I] HWND : window handle
5130 * RETURN:
5131 * Header handle.
5133 static LRESULT LISTVIEW_GetHeader(HWND hwnd)
5135 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5137 return infoPtr->hwndHeader;
5140 /* LISTVIEW_GetHotCursor */
5142 /***
5143 * DESCRIPTION:
5144 * Returns the time that the mouse cursor must hover over an item
5145 * before it is selected.
5147 * PARAMETER(S):
5148 * [I] HWND : window handle
5150 * RETURN:
5151 * Returns the previously set hover time or (DWORD)-1 to indicate that the
5152 * hover time is set to the default hover time.
5154 static LRESULT LISTVIEW_GetHoverTime(HWND hwnd)
5156 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5158 return infoPtr->dwHoverTime;
5161 /***
5162 * DESCRIPTION:
5163 * Retrieves an image list handle.
5165 * PARAMETER(S):
5166 * [I] HWND : window handle
5167 * [I] INT : image list identifier
5169 * RETURN:
5170 * SUCCESS : image list handle
5171 * FAILURE : NULL
5173 static LRESULT LISTVIEW_GetImageList(HWND hwnd, INT nImageList)
5175 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5176 HIMAGELIST himl = NULL;
5178 switch (nImageList)
5180 case LVSIL_NORMAL:
5181 himl = infoPtr->himlNormal;
5182 break;
5183 case LVSIL_SMALL:
5184 himl = infoPtr->himlSmall;
5185 break;
5186 case LVSIL_STATE:
5187 himl = infoPtr->himlState;
5188 break;
5191 return (LRESULT)himl;
5194 /* LISTVIEW_GetISearchString */
5196 /***
5197 * DESCRIPTION:
5198 * Retrieves item attributes.
5200 * PARAMETER(S):
5201 * [I] hwnd : window handle
5202 * [IO] lpLVItem : item info
5203 * [I] internal : if true then we will use tricks that avoid copies
5204 * but are not compatible with the regular interface
5205 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5206 * if FALSE, the lpLVItem is a LPLVITEMA.
5208 * RETURN:
5209 * SUCCESS : TRUE
5210 * FAILURE : FALSE
5212 static LRESULT LISTVIEW_GetItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL internal, BOOL isW)
5214 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5215 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
5216 NMLVDISPINFOW dispInfo;
5217 LISTVIEW_SUBITEM *lpSubItem;
5218 LISTVIEW_ITEM *lpItem;
5219 HDPA hdpaSubItems;
5220 void* null = NULL;
5221 INT* piImage = (INT*)&null;
5222 LPWSTR* ppszText= (LPWSTR*)&null;
5223 LPARAM* plParam = (LPARAM*)&null;
5225 if (internal && !isW)
5227 ERR("We can't have internal non-Unicode GetItem!\n");
5228 return FALSE;
5231 /* In the following:
5232 * lpLVItem describes the information requested by the user
5233 * lpItem/lpSubItem is what we have
5234 * dispInfo is a structure we use to request the missing
5235 * information from the application
5238 TRACE("(hwnd=%x, lpLVItem=%s, internal=%d, isW=%d)\n",
5239 hwnd, debuglvitem_t(lpLVItem, isW), internal, isW);
5241 if ((lpLVItem == NULL) || (lpLVItem->iItem < 0) ||
5242 (lpLVItem->iItem >= GETITEMCOUNT(infoPtr)))
5243 return FALSE;
5245 ZeroMemory(&dispInfo, sizeof(dispInfo));
5247 if (lStyle & LVS_OWNERDATA)
5249 if (lpLVItem->mask & ~LVIF_STATE)
5251 memcpy(&dispInfo.item, lpLVItem, sizeof(LVITEMW));
5252 dispinfo_notifyT(hwnd, LVN_GETDISPINFOW, &dispInfo, isW);
5253 memcpy(lpLVItem, &dispInfo.item, sizeof(LVITEMW));
5254 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
5257 if ((lpLVItem->mask & LVIF_STATE)&&(lpLVItem->iSubItem == 0))
5259 lpLVItem->state = 0;
5260 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5261 lpLVItem->state |= LVIS_FOCUSED;
5262 if (LISTVIEW_IsSelected(hwnd,lpLVItem->iItem))
5263 lpLVItem->state |= LVIS_SELECTED;
5266 return TRUE;
5269 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
5270 if (hdpaSubItems == NULL) return FALSE;
5272 if ( (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)) == NULL)
5273 return FALSE;
5275 ZeroMemory(&dispInfo.item, sizeof(LVITEMW));
5276 if (lpLVItem->iSubItem == 0)
5278 piImage=&lpItem->iImage;
5279 ppszText=&lpItem->pszText;
5280 plParam=&lpItem->lParam;
5281 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask)
5283 dispInfo.item.mask |= LVIF_STATE;
5284 dispInfo.item.stateMask = infoPtr->uCallbackMask;
5287 else
5289 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
5290 if (lpSubItem != NULL)
5292 piImage=&lpSubItem->iImage;
5293 ppszText=&lpSubItem->pszText;
5297 if ((lpLVItem->mask & LVIF_IMAGE) && (*piImage==I_IMAGECALLBACK))
5299 dispInfo.item.mask |= LVIF_IMAGE;
5302 if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(*ppszText))
5304 dispInfo.item.mask |= LVIF_TEXT;
5305 dispInfo.item.pszText = lpLVItem->pszText;
5306 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5307 if (dispInfo.item.pszText && lpLVItem->cchTextMax > 0)
5308 *dispInfo.item.pszText = '\0';
5309 if (dispInfo.item.pszText && (*ppszText == NULL))
5310 *dispInfo.item.pszText = '\0';
5313 if (dispInfo.item.mask != 0)
5315 /* We don't have all the requested info, query the application */
5316 dispInfo.item.iItem = lpLVItem->iItem;
5317 dispInfo.item.iSubItem = lpLVItem->iSubItem;
5318 dispInfo.item.lParam = lpItem->lParam;
5319 dispinfo_notifyT(hwnd, LVN_GETDISPINFOW, &dispInfo, isW);
5320 TRACE(" getdispinfo(2):lpLVItem=%s\n", debuglvitem_t(&dispInfo.item, isW));
5323 if (dispInfo.item.mask & LVIF_IMAGE)
5325 lpLVItem->iImage = dispInfo.item.iImage;
5326 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && (*piImage==I_IMAGECALLBACK))
5327 *piImage = dispInfo.item.iImage;
5329 else if (lpLVItem->mask & LVIF_IMAGE)
5331 lpLVItem->iImage = *piImage;
5334 if (dispInfo.item.mask & LVIF_PARAM)
5336 lpLVItem->lParam = dispInfo.item.lParam;
5337 if (dispInfo.item.mask & LVIF_DI_SETITEM)
5338 *plParam = dispInfo.item.lParam;
5340 else if (lpLVItem->mask & LVIF_PARAM)
5341 lpLVItem->lParam = lpItem->lParam;
5343 if (dispInfo.item.mask & LVIF_TEXT)
5345 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && *ppszText)
5346 textsetptrT(ppszText, dispInfo.item.pszText, isW);
5348 /* If lpLVItem->pszText==dispInfo.item.pszText a copy is unnecessary, but */
5349 /* some apps give a new pointer in ListView_Notify so we can't be sure. */
5350 if (lpLVItem->pszText != dispInfo.item.pszText)
5351 textcpynT(lpLVItem->pszText, isW, dispInfo.item.pszText, isW, lpLVItem->cchTextMax);
5354 else if (lpLVItem->mask & LVIF_TEXT)
5356 if (internal) lpLVItem->pszText = *ppszText;
5357 else textcpynT(lpLVItem->pszText, isW, *ppszText, TRUE, lpLVItem->cchTextMax);
5360 if (lpLVItem->iSubItem == 0)
5362 if (dispInfo.item.mask & LVIF_STATE)
5364 lpLVItem->state = lpItem->state;
5365 lpLVItem->state &= ~dispInfo.item.stateMask;
5366 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
5368 lpLVItem->state &= ~LVIS_SELECTED;
5369 if ((dispInfo.item.stateMask & LVIS_SELECTED) &&
5370 LISTVIEW_IsSelected(hwnd,dispInfo.item.iItem))
5371 lpLVItem->state |= LVIS_SELECTED;
5373 else if (lpLVItem->mask & LVIF_STATE)
5375 lpLVItem->state = lpItem->state & lpLVItem->stateMask;
5377 lpLVItem->state &= ~LVIS_SELECTED;
5378 if ((lpLVItem->stateMask & LVIS_SELECTED) &&
5379 LISTVIEW_IsSelected(hwnd,lpLVItem->iItem))
5380 lpLVItem->state |= LVIS_SELECTED;
5383 if (lpLVItem->mask & LVIF_PARAM)
5384 lpLVItem->lParam = lpItem->lParam;
5386 if (lpLVItem->mask & LVIF_INDENT)
5387 lpLVItem->iIndent = lpItem->iIndent;
5390 return TRUE;
5393 /* LISTVIEW_GetHotCursor */
5395 /***
5396 * DESCRIPTION:
5397 * Retrieves the index of the hot item.
5399 * PARAMETERS:
5400 * [I] HWND : window handle
5402 * RETURN:
5403 * SUCCESS : hot item index
5404 * FAILURE : -1 (no hot item)
5406 static LRESULT LISTVIEW_GetHotItem(HWND hwnd)
5408 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5410 return infoPtr->nHotItem;
5413 /* LISTVIEW_GetHoverTime */
5415 /***
5416 * DESCRIPTION:
5417 * Retrieves the number of items in the listview control.
5419 * PARAMETER(S):
5420 * [I] HWND : window handle
5422 * RETURN:
5423 * Number of items.
5425 static LRESULT LISTVIEW_GetItemCount(HWND hwnd)
5427 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5429 return GETITEMCOUNT(infoPtr);
5432 /***
5433 * DESCRIPTION:
5434 * Retrieves the rectangle enclosing the item icon and text.
5436 * PARAMETER(S):
5437 * [I] HWND : window handle
5438 * [I] INT : item index
5439 * [O] LPRECT : coordinate information
5441 * RETURN:
5442 * SUCCESS : TRUE
5443 * FAILURE : FALSE
5445 static BOOL LISTVIEW_GetItemBoundBox(HWND hwnd, INT nItem, LPRECT lpRect)
5447 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5448 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
5449 UINT uView = lStyle & LVS_TYPEMASK;
5450 BOOL bResult = FALSE;
5451 HDPA hdpaSubItems;
5452 LISTVIEW_ITEM *lpItem;
5453 INT nCountPerColumn;
5454 INT nRow;
5456 TRACE("(hwnd=%x,nItem=%d,lpRect=%p)\n", hwnd, nItem, lpRect);
5458 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) &&
5459 (lpRect != NULL))
5461 if (uView == LVS_LIST)
5463 bResult = TRUE;
5464 nItem = nItem - ListView_GetTopIndex(hwnd);
5465 nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
5466 if (nItem < 0)
5468 nRow = nItem % nCountPerColumn;
5469 if (nRow == 0)
5471 lpRect->left = nItem / nCountPerColumn * infoPtr->nItemWidth;
5472 lpRect->top = 0;
5474 else
5476 lpRect->left = (nItem / nCountPerColumn -1) * infoPtr->nItemWidth;
5477 lpRect->top = (nRow + nCountPerColumn) * infoPtr->nItemHeight;
5480 else
5482 lpRect->left = nItem / nCountPerColumn * infoPtr->nItemWidth;
5483 lpRect->top = nItem % nCountPerColumn * infoPtr->nItemHeight;
5486 else if (uView == LVS_REPORT)
5488 bResult = TRUE;
5489 lpRect->left = REPORT_MARGINX;
5490 lpRect->top = ((nItem - ListView_GetTopIndex(hwnd)) *
5491 infoPtr->nItemHeight) + infoPtr->rcList.top;
5493 if (!(lStyle & LVS_NOSCROLL))
5495 SCROLLINFO scrollInfo;
5496 /* Adjust position by scrollbar offset */
5497 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
5498 scrollInfo.cbSize = sizeof(SCROLLINFO);
5499 scrollInfo.fMask = SIF_POS;
5500 GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
5501 lpRect->left -= scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE;
5504 else /* either LVS_ICON or LVS_SMALLICON */
5506 if ((hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)))
5508 if ((lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)))
5510 bResult = TRUE;
5511 lpRect->left = lpItem->ptPosition.x;
5512 lpRect->top = lpItem->ptPosition.y;
5517 lpRect->right = lpRect->left + infoPtr->nItemWidth;
5518 lpRect->bottom = lpRect->top + infoPtr->nItemHeight;
5519 TRACE("result %s: (%d,%d)-(%d,%d)\n", bResult ? "TRUE" : "FALSE",
5520 lpRect->left, lpRect->top, lpRect->right, lpRect->bottom);
5521 return bResult;
5524 /***
5525 * DESCRIPTION:
5526 * Retrieves the position (upper-left) of the listview control item.
5527 * Note that for LVS_ICON style, the upper-left is that of the icon
5528 * and not the bounding box.
5530 * PARAMETER(S):
5531 * [I] HWND : window handle
5532 * [I] INT : item index
5533 * [O] LPPOINT : coordinate information
5535 * RETURN:
5536 * SUCCESS : TRUE
5537 * FAILURE : FALSE
5539 static BOOL LISTVIEW_GetItemPosition(HWND hwnd, INT nItem, LPPOINT lpptPosition)
5541 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5542 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5543 BOOL bResult = FALSE;
5544 RECT rcBounding;
5546 TRACE("(hwnd=%x, nItem=%d, lpptPosition=%p)\n", hwnd, nItem, lpptPosition);
5548 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) &&
5549 (lpptPosition != NULL))
5551 bResult = LISTVIEW_GetItemBoundBox(hwnd, nItem, &rcBounding);
5552 lpptPosition->x = rcBounding.left;
5553 lpptPosition->y = rcBounding.top;
5554 if (uView == LVS_ICON)
5556 lpptPosition->y += ICON_TOP_PADDING;
5557 lpptPosition->x += (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
5559 TRACE("result %s (%ld,%ld)\n", bResult ? "TRUE" : "FALSE",
5560 lpptPosition->x, lpptPosition->y);
5562 return bResult;
5565 /***
5566 * Update the bounding rectangle around the text under a large icon.
5567 * This depends on whether it has the focus or not.
5568 * On entry the rectangle's top, left and right should be set.
5569 * On return the bottom will also be set and the width may have been
5570 * modified.
5572 * This appears to be weird, even in the Microsoft implementation.
5575 static void ListView_UpdateLargeItemLabelRect (
5576 HWND hwnd, /* The window of the listview */
5577 const LISTVIEW_INFO *infoPtr, /* The listview itself */
5578 int nItem, /* The item for which we are calculating this */
5579 RECT *rect) /* The rectangle to be updated */
5581 HDC hdc = GetDC (hwnd);
5582 HFONT hOldFont = SelectObject (hdc, infoPtr->hFont);
5584 if (infoPtr->bFocus && infoPtr->nFocusedItem == nItem)
5586 /* We (aim to) display the full text. In Windows 95 it appears to
5587 * calculate the size assuming the specified font and then it draws
5588 * the text in that region with the specified font except scaled to
5589 * 10 point (or the height of the system font or ...). Thus if the
5590 * window has 24 point Helvetica the highlit rectangle will be
5591 * taller than the text and if it is 7 point Helvetica then the text
5592 * will be clipped.
5593 * For now we will simply say that it is the correct size to display
5594 * the text in the specified font.
5596 LVITEMW lvItem;
5597 lvItem.mask = LVIF_TEXT;
5598 lvItem.iItem = nItem;
5599 lvItem.iSubItem = 0;
5600 /* We will specify INTERNAL and so will receive back a const
5601 * pointer to the text, rather than specifying a buffer to which
5602 * to copy it.
5604 LISTVIEW_GetItemW (hwnd, &lvItem, TRUE);
5605 DrawTextW (hdc, lvItem.pszText, -1, rect, DT_CALCRECT |
5606 DT_NOCLIP | DT_EDITCONTROL | DT_TOP | DT_CENTER |
5607 DT_WORDBREAK | DT_NOPREFIX);
5608 /* Maintain this DT_* list in line with LISTVIEW_DrawLargeItem */
5610 else
5612 /* As far as I can see the text region seems to be trying to be
5613 * "tall enough for two lines of text". Once again (comctl32.dll ver
5614 * 5.81?) it measures this on the basis of the selected font and then
5615 * draws it with the same font except in 10 point size. This can lead
5616 * to more or less than the two rows appearing.
5617 * Question; are we supposed to be including DT_EXTERNALLEADING?
5618 * Question; should the width be shrunk to the space required to
5619 * display the two lines?
5621 rect->bottom = rect->top + 2 * infoPtr->ntmHeight;
5624 SelectObject (hdc, hOldFont);
5625 ReleaseDC (hwnd, hdc);
5628 /***
5629 * DESCRIPTION:
5630 * Retrieves the bounding rectangle for a listview control item.
5632 * PARAMETER(S):
5633 * [I] HWND : window handle
5634 * [I] INT : item index
5635 * [IO] LPRECT : bounding rectangle coordinates
5636 * lprc->left specifies the portion of the item for which the bounding
5637 * rectangle will be retrieved.
5639 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5640 * including the icon and label.
5641 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5642 * LVIR_LABEL Returns the bounding rectangle of the item text.
5643 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5644 * rectangles, but excludes columns in report view.
5646 * RETURN:
5647 * SUCCESS : TRUE
5648 * FAILURE : FALSE
5650 * NOTES
5651 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5652 * upon whether the window has the focus currently and on whether the item
5653 * is the one with the focus. Ensure that the control's record of which
5654 * item has the focus agrees with the items' records.
5656 static LRESULT LISTVIEW_GetItemRect(HWND hwnd, INT nItem, LPRECT lprc)
5658 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5659 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5660 BOOL bResult = FALSE;
5661 POINT ptOrigin;
5662 POINT ptItem;
5663 INT nLeftPos;
5664 INT nLabelWidth;
5665 INT nIndent;
5666 LVITEMW lvItem;
5667 RECT rcInternal;
5669 TRACE("(hwnd=%x, nItem=%d, lprc=%p)\n", hwnd, nItem, lprc);
5671 if (uView & LVS_REPORT)
5673 ZeroMemory(&lvItem, sizeof(lvItem));
5674 lvItem.mask = LVIF_INDENT;
5675 lvItem.iItem = nItem;
5676 lvItem.iSubItem = 0;
5677 LISTVIEW_GetItemW(hwnd, &lvItem, TRUE);
5679 /* do indent */
5680 if (lvItem.iIndent>0 && infoPtr->iconSize.cx > 0)
5681 nIndent = infoPtr->iconSize.cx * lvItem.iIndent;
5682 else
5683 nIndent = 0;
5685 else
5686 nIndent = 0;
5688 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) && (lprc != NULL))
5690 switch(lprc->left)
5692 case LVIR_ICON:
5693 if (!ListView_GetItemPosition(hwnd, nItem, &ptItem)) break;
5694 if (uView == LVS_ICON)
5696 if (infoPtr->himlNormal != NULL)
5698 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5700 bResult = TRUE;
5701 lprc->left = ptItem.x + ptOrigin.x;
5702 lprc->top = ptItem.y + ptOrigin.y;
5703 lprc->right = lprc->left + infoPtr->iconSize.cx;
5704 lprc->bottom = (lprc->top + infoPtr->iconSize.cy +
5705 ICON_BOTTOM_PADDING + ICON_TOP_PADDING);
5709 else if (uView == LVS_SMALLICON)
5711 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5713 bResult = TRUE;
5714 lprc->left = ptItem.x + ptOrigin.x;
5715 lprc->top = ptItem.y + ptOrigin.y;
5716 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5718 if (infoPtr->himlState != NULL)
5719 lprc->left += infoPtr->iconSize.cx;
5721 if (infoPtr->himlSmall != NULL)
5722 lprc->right = lprc->left + infoPtr->iconSize.cx;
5723 else
5724 lprc->right = lprc->left;
5727 else
5729 bResult = TRUE;
5730 lprc->left = ptItem.x;
5731 if (uView & LVS_REPORT)
5732 lprc->left += nIndent;
5733 lprc->top = ptItem.y;
5734 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5736 if (infoPtr->himlState != NULL)
5737 lprc->left += infoPtr->iconSize.cx;
5739 if (infoPtr->himlSmall != NULL)
5740 lprc->right = lprc->left + infoPtr->iconSize.cx;
5741 else
5742 lprc->right = lprc->left;
5744 break;
5746 case LVIR_LABEL:
5747 if (!ListView_GetItemPosition(hwnd, nItem, &ptItem)) break;
5748 if (uView == LVS_ICON)
5750 if (infoPtr->himlNormal != NULL)
5752 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5754 bResult = TRUE;
5755 lprc->left = ptItem.x + ptOrigin.x;
5756 lprc->top = (ptItem.y + ptOrigin.y + infoPtr->iconSize.cy +
5757 ICON_BOTTOM_PADDING);
5758 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5759 if (infoPtr->iconSpacing.cx - nLabelWidth > 1)
5761 lprc->left += (infoPtr->iconSpacing.cx - nLabelWidth) / 2;
5762 lprc->right = lprc->left + nLabelWidth;
5764 else
5766 lprc->left += 1;
5767 lprc->right = lprc->left + infoPtr->iconSpacing.cx - 1;
5768 ListView_UpdateLargeItemLabelRect (hwnd, infoPtr, nItem, lprc);
5773 else if (uView == LVS_SMALLICON)
5775 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5777 bResult = TRUE;
5778 nLeftPos = lprc->left = ptItem.x + ptOrigin.x;
5779 lprc->top = ptItem.y + ptOrigin.y;
5780 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5782 if (infoPtr->himlState != NULL)
5783 lprc->left += infoPtr->iconSize.cx;
5785 if (infoPtr->himlSmall != NULL)
5786 lprc->left += infoPtr->iconSize.cx;
5788 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5789 nLabelWidth += TRAILING_PADDING;
5790 if (lprc->left + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5791 lprc->right = lprc->left + nLabelWidth;
5792 else
5793 lprc->right = nLeftPos + infoPtr->nItemWidth;
5796 else
5798 bResult = TRUE;
5799 if (uView == LVS_REPORT)
5800 nLeftPos = lprc->left = ptItem.x + nIndent;
5801 else
5802 nLeftPos = lprc->left = ptItem.x;
5803 lprc->top = ptItem.y;
5804 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5806 if (infoPtr->himlState != NULL)
5807 lprc->left += infoPtr->iconSize.cx;
5809 if (infoPtr->himlSmall != NULL)
5810 lprc->left += infoPtr->iconSize.cx;
5812 if (uView != LVS_REPORT)
5814 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5815 nLabelWidth += TRAILING_PADDING;
5816 if (infoPtr->himlSmall)
5817 nLabelWidth += IMAGE_PADDING;
5819 else
5820 nLabelWidth = LISTVIEW_GetColumnWidth(hwnd, 0)-lprc->left;
5821 if (lprc->left + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5822 lprc->right = lprc->left + nLabelWidth;
5823 else
5824 lprc->right = nLeftPos + infoPtr->nItemWidth;
5826 break;
5828 case LVIR_BOUNDS:
5829 if (!LISTVIEW_GetItemBoundBox(hwnd, nItem, &rcInternal)) break;
5830 ptItem.x = rcInternal.left;
5831 ptItem.y = rcInternal.top;
5832 if (uView == LVS_ICON)
5834 if (infoPtr->himlNormal != NULL)
5836 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5838 RECT label_rect;
5839 INT text_left, text_right, icon_left, text_pos_x;
5840 /* for style LVS_ICON bounds
5841 * left = min(icon.left, text.left)
5842 * right = max(icon.right, text.right)
5843 * top = boundbox.top + NOTHITABLE
5844 * bottom = text.bottom + 1
5846 bResult = TRUE;
5847 icon_left = text_left = ptItem.x;
5849 /* Correct ptItem to icon upper-left */
5850 icon_left += (infoPtr->nItemWidth - infoPtr->iconSize.cx)/2;
5851 ptItem.y += ICON_TOP_PADDING;
5853 /* Compute the label left and right */
5854 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5855 text_pos_x = infoPtr->iconSpacing.cx - 2*CAPTION_BORDER - nLabelWidth;
5856 if (text_pos_x > 1)
5858 text_left += text_pos_x / 2;
5859 text_right = text_left + nLabelWidth + 2*CAPTION_BORDER;
5861 else
5863 text_left += 1;
5864 text_right = text_left + infoPtr->iconSpacing.cx - 1;
5867 /* Compute rectangle w/o the text height */
5868 lprc->left = min(icon_left, text_left) + ptOrigin.x;
5869 lprc->right = max(icon_left + infoPtr->iconSize.cx,
5870 text_right) + ptOrigin.x;
5871 lprc->top = ptItem.y + ptOrigin.y - ICON_TOP_PADDING_HITABLE;
5872 lprc->bottom = lprc->top + ICON_TOP_PADDING_HITABLE
5873 + infoPtr->iconSize.cy + 1
5874 + ICON_BOTTOM_PADDING;
5876 CopyRect (&label_rect, lprc);
5877 label_rect.top = lprc->bottom;
5878 ListView_UpdateLargeItemLabelRect (hwnd, infoPtr, nItem, &label_rect);
5879 UnionRect (lprc, lprc, &label_rect);
5883 else if (uView == LVS_SMALLICON)
5885 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5887 bResult = TRUE;
5888 lprc->left = ptItem.x + ptOrigin.x;
5889 lprc->right = lprc->left;
5890 lprc->top = ptItem.y + ptOrigin.y;
5891 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5892 if (infoPtr->himlState != NULL)
5893 lprc->right += infoPtr->iconSize.cx;
5894 if (infoPtr->himlSmall != NULL)
5895 lprc->right += infoPtr->iconSize.cx;
5897 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5898 nLabelWidth += TRAILING_PADDING;
5899 if (infoPtr->himlSmall)
5900 nLabelWidth += IMAGE_PADDING;
5901 if (lprc->right + nLabelWidth < lprc->left + infoPtr->nItemWidth)
5902 lprc->right += nLabelWidth;
5903 else
5904 lprc->right = lprc->left + infoPtr->nItemWidth;
5907 else
5909 bResult = TRUE;
5910 lprc->left = ptItem.x;
5911 if (!(infoPtr->dwExStyle&LVS_EX_FULLROWSELECT) && uView&LVS_REPORT)
5912 lprc->left += nIndent;
5913 lprc->right = lprc->left;
5914 lprc->top = ptItem.y;
5915 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5917 if ((infoPtr->dwExStyle & LVS_EX_FULLROWSELECT) || (uView == LVS_REPORT))
5919 RECT br;
5920 int nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
5921 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
5923 lprc->right = max(lprc->left, br.right - REPORT_MARGINX);
5925 else
5927 if (infoPtr->himlState != NULL)
5928 lprc->right += infoPtr->iconSize.cx;
5930 if (infoPtr->himlSmall != NULL)
5931 lprc->right += infoPtr->iconSize.cx;
5933 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5934 nLabelWidth += TRAILING_PADDING;
5935 if (lprc->right + nLabelWidth < lprc->left + infoPtr->nItemWidth)
5936 lprc->right += nLabelWidth;
5937 else
5938 lprc->right = lprc->left + infoPtr->nItemWidth;
5941 break;
5943 case LVIR_SELECTBOUNDS:
5944 if (!ListView_GetItemPosition(hwnd, nItem, &ptItem)) break;
5945 if (uView == LVS_ICON)
5947 if (infoPtr->himlNormal != NULL)
5949 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5951 bResult = TRUE;
5952 lprc->left = ptItem.x + ptOrigin.x;
5953 lprc->top = ptItem.y + ptOrigin.y;
5954 lprc->right = lprc->left + infoPtr->iconSpacing.cx;
5955 lprc->bottom = lprc->top + infoPtr->iconSpacing.cy;
5959 else if (uView == LVS_SMALLICON)
5961 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5963 bResult = TRUE;
5964 nLeftPos= lprc->left = ptItem.x + ptOrigin.x;
5965 lprc->top = ptItem.y + ptOrigin.y;
5966 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5968 if (infoPtr->himlState != NULL)
5969 lprc->left += infoPtr->iconSize.cx;
5971 lprc->right = lprc->left;
5973 if (infoPtr->himlSmall != NULL)
5974 lprc->right += infoPtr->iconSize.cx;
5976 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5977 nLabelWidth += TRAILING_PADDING;
5978 if (lprc->right + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5979 lprc->right += nLabelWidth;
5980 else
5981 lprc->right = nLeftPos + infoPtr->nItemWidth;
5984 else
5986 bResult = TRUE;
5987 if (!(infoPtr->dwExStyle&LVS_EX_FULLROWSELECT) && (uView&LVS_REPORT))
5988 nLeftPos = lprc->left = ptItem.x + nIndent;
5989 else
5990 nLeftPos = lprc->left = ptItem.x;
5991 lprc->top = ptItem.y;
5992 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5994 if (infoPtr->himlState != NULL)
5995 lprc->left += infoPtr->iconSize.cx;
5997 lprc->right = lprc->left;
5999 if (infoPtr->dwExStyle & LVS_EX_FULLROWSELECT)
6001 RECT br;
6002 int nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
6003 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
6005 lprc->right = max(lprc->left, br.right - REPORT_MARGINX);
6007 else
6009 if (infoPtr->himlSmall != NULL)
6010 lprc->right += infoPtr->iconSize.cx;
6012 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
6013 nLabelWidth += TRAILING_PADDING;
6014 if (infoPtr->himlSmall)
6015 nLabelWidth += IMAGE_PADDING;
6016 if (lprc->right + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
6017 lprc->right += nLabelWidth;
6018 else
6019 lprc->right = nLeftPos + infoPtr->nItemWidth;
6022 break;
6026 TRACE("result %s (%d,%d)-(%d,%d)\n", bResult ? "TRUE" : "FALSE",
6027 lprc->left, lprc->top, lprc->right, lprc->bottom);
6029 return bResult;
6032 /***
6033 * DESCRIPTION:
6034 * Retrieves the width of a label.
6036 * PARAMETER(S):
6037 * [I] HWND : window handle
6039 * RETURN:
6040 * SUCCESS : string width (in pixels)
6041 * FAILURE : zero
6043 static INT LISTVIEW_GetLabelWidth(HWND hwnd, INT nItem)
6045 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
6046 INT nLabelWidth = 0;
6047 LVITEMW lvItem;
6049 TRACE("(hwnd=%x, nItem=%d)\n", hwnd, nItem);
6051 ZeroMemory(&lvItem, sizeof(lvItem));
6052 lvItem.mask = LVIF_TEXT;
6053 lvItem.iItem = nItem;
6054 lvItem.cchTextMax = DISP_TEXT_SIZE;
6055 lvItem.pszText = szDispText;
6056 if (LISTVIEW_GetItemW(hwnd, &lvItem, TRUE))
6057 nLabelWidth = ListView_GetStringWidthW(hwnd, lvItem.pszText);
6059 return nLabelWidth;
6062 /***
6063 * DESCRIPTION:
6064 * Retrieves the spacing between listview control items.
6066 * PARAMETER(S):
6067 * [I] HWND : window handle
6068 * [I] BOOL : flag for small or large icon
6070 * RETURN:
6071 * Horizontal + vertical spacing
6073 static LRESULT LISTVIEW_GetItemSpacing(HWND hwnd, BOOL bSmall)
6075 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6076 LONG lResult;
6078 if (bSmall == FALSE)
6080 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6082 else
6084 LONG style = GetWindowLongW(hwnd, GWL_STYLE);
6085 if ((style & LVS_TYPEMASK) == LVS_ICON)
6086 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
6087 else
6088 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
6090 return lResult;
6093 /***
6094 * DESCRIPTION:
6095 * Retrieves the state of a listview control item.
6097 * PARAMETER(S):
6098 * [I] HWND : window handle
6099 * [I] INT : item index
6100 * [I] UINT : state mask
6102 * RETURN:
6103 * State specified by the mask.
6105 static LRESULT LISTVIEW_GetItemState(HWND hwnd, INT nItem, UINT uMask)
6107 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6108 LVITEMW lvItem;
6109 UINT uState = 0;
6111 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
6113 ZeroMemory(&lvItem, sizeof(lvItem));
6114 lvItem.iItem = nItem;
6115 lvItem.stateMask = uMask;
6116 lvItem.mask = LVIF_STATE;
6117 if (LISTVIEW_GetItemW(hwnd, &lvItem, TRUE))
6118 uState = lvItem.state;
6121 return uState;
6124 /***
6125 * DESCRIPTION:
6126 * Retrieves the text of a listview control item or subitem.
6128 * PARAMETER(S):
6129 * [I] hwnd : window handle
6130 * [I] nItem : item index
6131 * [IO] lpLVItem : item information
6132 * [I] isW : TRUE if lpLVItem is Unicode
6134 * RETURN:
6135 * SUCCESS : string length
6136 * FAILURE : 0
6138 static LRESULT LISTVIEW_GetItemTextT(HWND hwnd, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
6140 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6141 INT nLength = 0;
6143 if (lpLVItem != NULL)
6145 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
6147 lpLVItem->mask = LVIF_TEXT;
6148 lpLVItem->iItem = nItem;
6149 if (LISTVIEW_GetItemT(hwnd, lpLVItem, FALSE, isW))
6150 nLength = textlenT(lpLVItem->pszText, isW);
6154 return nLength;
6157 /***
6158 * DESCRIPTION:
6159 * Searches for an item based on properties + relationships.
6161 * PARAMETER(S):
6162 * [I] HWND : window handle
6163 * [I] INT : item index
6164 * [I] INT : relationship flag
6166 * RETURN:
6167 * SUCCESS : item index
6168 * FAILURE : -1
6170 static LRESULT LISTVIEW_GetNextItem(HWND hwnd, INT nItem, UINT uFlags)
6172 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6173 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
6174 UINT uMask = 0;
6175 LVFINDINFOW lvFindInfo;
6176 INT nCountPerColumn;
6177 INT i;
6179 if ((nItem >= -1) && (nItem < GETITEMCOUNT(infoPtr)))
6181 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
6183 if (uFlags & LVNI_CUT)
6184 uMask |= LVIS_CUT;
6186 if (uFlags & LVNI_DROPHILITED)
6187 uMask |= LVIS_DROPHILITED;
6189 if (uFlags & LVNI_FOCUSED)
6190 uMask |= LVIS_FOCUSED;
6192 if (uFlags & LVNI_SELECTED)
6193 uMask |= LVIS_SELECTED;
6195 if (uFlags & LVNI_ABOVE)
6197 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
6199 while (nItem >= 0)
6201 nItem--;
6202 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6203 return nItem;
6206 else
6208 lvFindInfo.flags = LVFI_NEARESTXY;
6209 lvFindInfo.vkDirection = VK_UP;
6210 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
6211 while ((nItem = ListView_FindItemW(hwnd, nItem, &lvFindInfo)) != -1)
6213 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6214 return nItem;
6218 else if (uFlags & LVNI_BELOW)
6220 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
6222 while (nItem < GETITEMCOUNT(infoPtr))
6224 nItem++;
6225 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6226 return nItem;
6229 else
6231 lvFindInfo.flags = LVFI_NEARESTXY;
6232 lvFindInfo.vkDirection = VK_DOWN;
6233 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
6234 while ((nItem = ListView_FindItemW(hwnd, nItem, &lvFindInfo)) != -1)
6236 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6237 return nItem;
6241 else if (uFlags & LVNI_TOLEFT)
6243 if (uView == LVS_LIST)
6245 nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
6246 while (nItem - nCountPerColumn >= 0)
6248 nItem -= nCountPerColumn;
6249 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6250 return nItem;
6253 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6255 lvFindInfo.flags = LVFI_NEARESTXY;
6256 lvFindInfo.vkDirection = VK_LEFT;
6257 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
6258 while ((nItem = ListView_FindItemW(hwnd, nItem, &lvFindInfo)) != -1)
6260 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6261 return nItem;
6265 else if (uFlags & LVNI_TORIGHT)
6267 if (uView == LVS_LIST)
6269 nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
6270 while (nItem + nCountPerColumn < GETITEMCOUNT(infoPtr))
6272 nItem += nCountPerColumn;
6273 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6274 return nItem;
6277 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6279 lvFindInfo.flags = LVFI_NEARESTXY;
6280 lvFindInfo.vkDirection = VK_RIGHT;
6281 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
6282 while ((nItem = ListView_FindItemW(hwnd, nItem, &lvFindInfo)) != -1)
6284 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6285 return nItem;
6289 else
6291 nItem++;
6293 /* search by index */
6294 for (i = nItem; i < GETITEMCOUNT(infoPtr); i++)
6296 if ((ListView_GetItemState(hwnd, i, uMask) & uMask) == uMask)
6297 return i;
6302 return -1;
6305 /* LISTVIEW_GetNumberOfWorkAreas */
6307 /***
6308 * DESCRIPTION:
6309 * Retrieves the origin coordinates when in icon or small icon display mode.
6311 * PARAMETER(S):
6312 * [I] HWND : window handle
6313 * [O] LPPOINT : coordinate information
6315 * RETURN:
6316 * SUCCESS : TRUE
6317 * FAILURE : FALSE
6319 static LRESULT LISTVIEW_GetOrigin(HWND hwnd, LPPOINT lpptOrigin)
6321 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
6322 UINT uView = lStyle & LVS_TYPEMASK;
6323 BOOL bResult = FALSE;
6325 TRACE("(hwnd=%x, lpptOrigin=%p)\n", hwnd, lpptOrigin);
6327 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6329 SCROLLINFO scrollInfo;
6330 ZeroMemory(lpptOrigin, sizeof(POINT));
6331 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
6332 scrollInfo.cbSize = sizeof(SCROLLINFO);
6334 if (lStyle & WS_HSCROLL)
6336 scrollInfo.fMask = SIF_POS;
6337 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
6338 lpptOrigin->x = -scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE;
6341 if (lStyle & WS_VSCROLL)
6343 scrollInfo.fMask = SIF_POS;
6344 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
6345 lpptOrigin->y = -scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE;
6348 bResult = TRUE;
6351 return bResult;
6354 /***
6355 * DESCRIPTION:
6356 * Retrieves the number of items that are marked as selected.
6358 * PARAMETER(S):
6359 * [I] HWND : window handle
6361 * RETURN:
6362 * Number of items selected.
6364 static LRESULT LISTVIEW_GetSelectedCount(HWND hwnd)
6366 /* REDO THIS */
6367 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6368 INT nSelectedCount = 0;
6369 INT i;
6371 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
6373 if (ListView_GetItemState(hwnd, i, LVIS_SELECTED) & LVIS_SELECTED)
6374 nSelectedCount++;
6377 return nSelectedCount;
6380 /***
6381 * DESCRIPTION:
6382 * Retrieves item index that marks the start of a multiple selection.
6384 * PARAMETER(S):
6385 * [I] HWND : window handle
6387 * RETURN:
6388 * Index number or -1 if there is no selection mark.
6390 static LRESULT LISTVIEW_GetSelectionMark(HWND hwnd)
6392 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6394 return infoPtr->nSelectionMark;
6398 /***
6399 * DESCRIPTION:
6400 * Retrieves the width of a string.
6402 * PARAMETER(S):
6403 * [I] hwnd : window handle
6404 * [I] lpszText : text string to process
6405 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
6407 * RETURN:
6408 * SUCCESS : string width (in pixels)
6409 * FAILURE : zero
6411 static LRESULT LISTVIEW_GetStringWidthT(HWND hwnd, LPCWSTR lpszText, BOOL isW)
6413 if (is_textT(lpszText, isW))
6415 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6416 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
6417 HDC hdc = GetDC(hwnd);
6418 HFONT hOldFont = SelectObject(hdc, hFont);
6419 SIZE stringSize;
6420 ZeroMemory(&stringSize, sizeof(SIZE));
6421 if (isW)
6422 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
6423 else
6424 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
6425 SelectObject(hdc, hOldFont);
6426 ReleaseDC(hwnd, hdc);
6427 return stringSize.cx;
6429 return 0;
6432 /***
6433 * DESCRIPTION:
6434 * Retrieves the text backgound color.
6436 * PARAMETER(S):
6437 * [I] HWND : window handle
6439 * RETURN:
6440 * COLORREF associated with the the background.
6442 static LRESULT LISTVIEW_GetTextBkColor(HWND hwnd)
6444 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
6446 return infoPtr->clrTextBk;
6449 /***
6450 * DESCRIPTION:
6451 * Retrieves the text color.
6453 * PARAMETER(S):
6454 * [I] HWND : window handle
6456 * RETURN:
6457 * COLORREF associated with the text.
6459 static LRESULT LISTVIEW_GetTextColor(HWND hwnd)
6461 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
6463 return infoPtr->clrText;
6466 /***
6467 * DESCRIPTION:
6468 * Determines item if a hit or closest if not
6470 * PARAMETER(S):
6471 * [I] HWND : window handle
6472 * [IO] LPLV_INTHIT : hit test information
6473 * [I] subitem : fill out iSubItem.
6475 * RETURN:
6476 * SUCCESS : item index of hit
6477 * FAILURE : -1
6479 static INT LISTVIEW_SuperHitTestItem(HWND hwnd, LPLV_INTHIT lpInt, BOOL subitem)
6481 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6482 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
6483 UINT uView = lStyle & LVS_TYPEMASK;
6484 INT i,topindex,bottomindex;
6485 RECT rcItem;
6486 DWORD xterm, yterm, dist;
6488 TRACE("(hwnd=%x, x=%ld, y=%ld)\n", hwnd, lpInt->ht.pt.x, lpInt->ht.pt.y);
6490 topindex = ListView_GetTopIndex(hwnd);
6491 if (uView == LVS_REPORT)
6493 bottomindex = topindex + LISTVIEW_GetCountPerColumn(hwnd) + 1;
6494 bottomindex = min(bottomindex,GETITEMCOUNT(infoPtr));
6496 else
6498 bottomindex = GETITEMCOUNT(infoPtr);
6501 lpInt->distance = 0x7fffffff;
6502 lpInt->iDistItem = -1;
6504 for (i = topindex; i < bottomindex; i++)
6506 rcItem.left = LVIR_BOUNDS;
6507 if (LISTVIEW_GetItemRect(hwnd, i, &rcItem))
6509 if (PtInRect(&rcItem, lpInt->ht.pt))
6511 rcItem.left = LVIR_ICON;
6512 if (LISTVIEW_GetItemRect(hwnd, i, &rcItem))
6514 if (PtInRect(&rcItem, lpInt->ht.pt))
6516 lpInt->ht.flags = LVHT_ONITEMICON;
6517 lpInt->ht.iItem = i;
6518 if (subitem) lpInt->ht.iSubItem = 0;
6519 return i;
6523 rcItem.left = LVIR_LABEL;
6524 if (LISTVIEW_GetItemRect(hwnd, i, &rcItem))
6526 if (PtInRect(&rcItem, lpInt->ht.pt))
6528 lpInt->ht.flags = LVHT_ONITEMLABEL;
6529 lpInt->ht.iItem = i;
6530 if (subitem) lpInt->ht.iSubItem = 0;
6531 return i;
6535 lpInt->ht.flags = LVHT_ONITEMSTATEICON;
6536 lpInt->ht.iItem = i;
6537 if (subitem) lpInt->ht.iSubItem = 0;
6538 return i;
6540 else
6543 * Now compute distance from point to center of boundary
6544 * box. Since we are only interested in the relative
6545 * distance, we can skip the nasty square root operation
6547 xterm = rcItem.left + (rcItem.right - rcItem.left)/2 - lpInt->ht.pt.x;
6548 yterm = rcItem.top + (rcItem.bottom - rcItem.top)/2 - lpInt->ht.pt.y;
6549 dist = xterm * xterm + yterm * yterm;
6550 if (dist < lpInt->distance)
6552 lpInt->distance = dist;
6553 lpInt->iDistItem = i;
6559 lpInt->ht.flags = LVHT_NOWHERE;
6560 TRACE("no hit, closest item %d, distance %ld\n", lpInt->iDistItem, lpInt->distance);
6562 return -1;
6565 /***
6566 * DESCRIPTION:
6567 * Determines which section of the item was selected (if any).
6569 * PARAMETER(S):
6570 * [I] HWND : window handle
6571 * [IO] LPLVHITTESTINFO : hit test information
6572 * [I] subitem : fill out iSubItem.
6574 * RETURN:
6575 * SUCCESS : item index
6576 * FAILURE : -1
6578 static INT LISTVIEW_HitTestItem(HWND hwnd, LPLVHITTESTINFO lpHitTestInfo, BOOL subitem)
6580 INT ret;
6581 LV_INTHIT lv_inthit;
6583 TRACE("(hwnd=%x, x=%ld, y=%ld)\n", hwnd, lpHitTestInfo->pt.x,
6584 lpHitTestInfo->pt.y);
6586 memcpy(&lv_inthit, lpHitTestInfo, sizeof(LVHITTESTINFO));
6587 ret = LISTVIEW_SuperHitTestItem(hwnd, &lv_inthit, subitem);
6588 memcpy(lpHitTestInfo, &lv_inthit, sizeof(LVHITTESTINFO));
6589 return ret;
6592 /***
6593 * DESCRIPTION:
6594 * Determines which listview item is located at the specified position.
6596 * PARAMETER(S):
6597 * [I] HWND : window handle
6598 * [IO} LPLVHITTESTINFO : hit test information
6600 * RETURN:
6601 * SUCCESS : item index
6602 * FAILURE : -1
6604 static LRESULT LISTVIEW_HitTest(HWND hwnd, LPLVHITTESTINFO lpHitTestInfo)
6606 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6607 INT nItem = -1;
6609 lpHitTestInfo->flags = 0;
6611 if (infoPtr->rcList.left > lpHitTestInfo->pt.x)
6612 lpHitTestInfo->flags = LVHT_TOLEFT;
6613 else if (infoPtr->rcList.right < lpHitTestInfo->pt.x)
6614 lpHitTestInfo->flags = LVHT_TORIGHT;
6615 if (infoPtr->rcList.top > lpHitTestInfo->pt.y)
6616 lpHitTestInfo->flags |= LVHT_ABOVE;
6617 else if (infoPtr->rcList.bottom < lpHitTestInfo->pt.y)
6618 lpHitTestInfo->flags |= LVHT_BELOW;
6620 if (lpHitTestInfo->flags == 0)
6622 /* NOTE (mm 20001022): We must not allow iSubItem to be touched, for
6623 * an app might pass only a structure with space up to iItem!
6624 * (MS Office 97 does that for instance in the file open dialog)
6626 nItem = LISTVIEW_HitTestItem(hwnd, lpHitTestInfo, FALSE);
6629 return nItem;
6632 /***
6633 * DESCRIPTION:
6634 * Inserts a new column.
6636 * PARAMETER(S):
6637 * [I] HWND : window handle
6638 * [I] INT : column index
6639 * [I] LPLVCOLUMNW : column information
6641 * RETURN:
6642 * SUCCESS : new column index
6643 * FAILURE : -1
6645 static LRESULT LISTVIEW_InsertColumnT(HWND hwnd, INT nColumn,
6646 LPLVCOLUMNW lpColumn, BOOL isW)
6648 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6649 INT nNewColumn = -1;
6650 HDITEMW hdi;
6652 TRACE("(hwnd=%x, nColumn=%d, lpColumn=%p)\n",hwnd, nColumn, lpColumn);
6654 if (lpColumn != NULL)
6656 /* initialize memory */
6657 ZeroMemory(&hdi, sizeof(hdi));
6659 if (lpColumn->mask & LVCF_FMT)
6661 /* format member is valid */
6662 hdi.mask |= HDI_FORMAT;
6664 /* set text alignment (leftmost column must be left-aligned) */
6665 if (nColumn == 0)
6667 hdi.fmt |= HDF_LEFT;
6669 else
6671 if (lpColumn->fmt & LVCFMT_LEFT)
6673 hdi.fmt |= HDF_LEFT;
6675 else if (lpColumn->fmt & LVCFMT_RIGHT)
6677 hdi.fmt |= HDF_RIGHT;
6679 else if (lpColumn->fmt & LVCFMT_CENTER)
6681 hdi.fmt |= HDF_CENTER;
6685 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6687 hdi.fmt |= HDF_BITMAP_ON_RIGHT;
6688 /* ??? */
6691 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6693 /* ??? */
6696 if (lpColumn->fmt & LVCFMT_IMAGE)
6698 hdi.fmt |= HDF_IMAGE;
6699 hdi.iImage = I_IMAGECALLBACK;
6703 if (lpColumn->mask & LVCF_WIDTH)
6705 hdi.mask |= HDI_WIDTH;
6706 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
6708 /* make it fill the remainder of the controls width */
6709 HDITEMW hdit;
6710 RECT rcHeader;
6711 INT item_index;
6713 ZeroMemory(&hdit, sizeof(hdit));
6715 /* get the width of every item except the current one */
6716 hdit.mask = HDI_WIDTH;
6717 hdi.cxy = 0;
6719 for(item_index = 0; item_index < (nColumn - 1); item_index++) {
6720 Header_GetItemW(infoPtr->hwndHeader, item_index, (LPARAM)(&hdit));
6721 hdi.cxy+=hdit.cxy;
6724 /* retrieve the layout of the header */
6725 GetClientRect(hwnd, &rcHeader);
6726 /* GetWindowRect(infoPtr->hwndHeader, &rcHeader);*/
6727 TRACE("start cxy=%d left=%d right=%d\n", hdi.cxy, rcHeader.left, rcHeader.right);
6729 hdi.cxy = (rcHeader.right - rcHeader.left) - hdi.cxy;
6731 else
6732 hdi.cxy = lpColumn->cx;
6735 if (lpColumn->mask & LVCF_TEXT)
6737 hdi.mask |= HDI_TEXT | HDI_FORMAT;
6738 hdi.pszText = lpColumn->pszText;
6739 hdi.cchTextMax = textlenT(lpColumn->pszText, isW);
6740 hdi.fmt |= HDF_STRING;
6743 if (lpColumn->mask & LVCF_IMAGE)
6745 hdi.mask |= HDI_IMAGE;
6746 hdi.iImage = lpColumn->iImage;
6749 if (lpColumn->mask & LVCF_ORDER)
6751 hdi.mask |= HDI_ORDER;
6752 hdi.iOrder = lpColumn->iOrder;
6755 /* insert item in header control */
6756 nNewColumn = SendMessageW(infoPtr->hwndHeader, HDM_INSERTITEMT(isW),
6757 (WPARAM)nColumn, (LPARAM)&hdi);
6759 /* Need to reset the item width when inserting a new column */
6760 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
6762 LISTVIEW_UpdateScroll(hwnd);
6763 InvalidateRect(hwnd, NULL, FALSE);
6766 return nNewColumn;
6769 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
6770 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
6771 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6772 and not during the processing of a LVM_SORTITEMS message. Applications should provide
6773 their own sort proc. when sending LVM_SORTITEMS.
6775 /* Platform SDK:
6776 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6778 LVS_SORTXXX must be specified,
6779 LVS_OWNERDRAW is not set,
6780 <item>.pszText is not LPSTR_TEXTCALLBACK.
6782 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6783 are sorted based on item text..."
6785 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
6787 LONG lStyle = GetWindowLongW((HWND) lParam, GWL_STYLE);
6788 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 );
6789 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 );
6790 INT cmpv = lstrcmpW( lv_first->pszText, lv_second->pszText );
6791 /* if we're sorting descending, negate the return value */
6792 return (lStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
6795 /***
6796 * nESCRIPTION:
6797 * Inserts a new item in the listview control.
6799 * PARAMETER(S):
6800 * [I] HWND : window handle
6801 * [I] LPLVITEMW : item information
6802 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6804 * RETURN:
6805 * SUCCESS : new item index
6806 * FAILURE : -1
6808 static LRESULT LISTVIEW_InsertItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
6810 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6811 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
6812 UINT uView = lStyle & LVS_TYPEMASK;
6813 INT nItem = -1;
6814 HDPA hdpaSubItems;
6815 INT nItemWidth = 0;
6816 LISTVIEW_ITEM *lpItem = NULL;
6818 TRACE("(hwnd=%x, lpLVItem=%s, isW=%d)\n",
6819 hwnd, debuglvitem_t(lpLVItem, isW), isW);
6821 if (lStyle & LVS_OWNERDATA)
6823 nItem = infoPtr->hdpaItems->nItemCount;
6824 infoPtr->hdpaItems->nItemCount ++;
6825 return nItem;
6828 if (lpLVItem != NULL)
6830 /* make sure it's not a subitem; cannot insert a subitem */
6831 if (lpLVItem->iSubItem == 0)
6833 if ( (lpItem = (LISTVIEW_ITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_ITEM))) )
6835 ZeroMemory(lpItem, sizeof(LISTVIEW_ITEM));
6836 if (LISTVIEW_InitItemT(hwnd, lpItem, lpLVItem, isW))
6838 /* insert item in listview control data structure */
6839 if ( (hdpaSubItems = DPA_Create(8)) )
6841 if ( (nItem = DPA_InsertPtr(hdpaSubItems, 0, lpItem)) != -1)
6843 if ( ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
6844 && !(lStyle & LVS_OWNERDRAWFIXED)
6845 && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText) )
6847 /* Insert the item in the proper sort order based on the pszText
6848 member. See comments for LISTVIEW_InsertCompare() for greater detail */
6849 nItem = DPA_InsertPtr( infoPtr->hdpaItems,
6850 GETITEMCOUNT( infoPtr ) + 1, hdpaSubItems );
6851 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, hwnd );
6852 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
6854 else
6856 nItem = DPA_InsertPtr(infoPtr->hdpaItems, lpLVItem->iItem,
6857 hdpaSubItems);
6859 if (nItem != -1)
6861 NMLISTVIEW nmlv;
6863 LISTVIEW_ShiftIndices(hwnd,nItem,1);
6865 /* manage item focus */
6866 if (lpLVItem->mask & LVIF_STATE)
6868 lpItem->state &= ~(LVIS_FOCUSED|LVIS_SELECTED);
6869 if (lpLVItem->stateMask & LVIS_SELECTED)
6870 LISTVIEW_SetSelection(hwnd, nItem);
6871 else if (lpLVItem->stateMask & LVIS_FOCUSED)
6872 LISTVIEW_SetItemFocus(hwnd, nItem);
6875 /* send LVN_INSERTITEM notification */
6876 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6877 nmlv.iItem = nItem;
6878 nmlv.lParam = lpItem->lParam;
6879 listview_notify(hwnd, LVN_INSERTITEM, &nmlv);
6881 if ((uView == LVS_SMALLICON) || (uView == LVS_LIST))
6883 nItemWidth = LISTVIEW_CalculateWidth(hwnd, lpLVItem->iItem);
6884 if (nItemWidth > infoPtr->nItemWidth)
6885 infoPtr->nItemWidth = nItemWidth;
6888 /* align items (set position of each item) */
6889 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6891 if (lStyle & LVS_ALIGNLEFT)
6892 LISTVIEW_AlignLeft(hwnd);
6893 else
6894 LISTVIEW_AlignTop(hwnd);
6897 LISTVIEW_UpdateScroll(hwnd);
6898 /* refresh client area */
6899 InvalidateRect(hwnd, NULL, FALSE);
6908 /* free memory if unsuccessful */
6909 if ((nItem == -1) && (lpItem != NULL))
6910 COMCTL32_Free(lpItem);
6912 return nItem;
6915 /***
6916 * DESCRIPTION:
6917 * Redraws a range of items.
6919 * PARAMETER(S):
6920 * [I] HWND : window handle
6921 * [I] INT : first item
6922 * [I] INT : last item
6924 * RETURN:
6925 * SUCCESS : TRUE
6926 * FAILURE : FALSE
6928 static LRESULT LISTVIEW_RedrawItems(HWND hwnd, INT nFirst, INT nLast)
6930 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6931 BOOL bResult = FALSE;
6932 RECT rcItem;
6933 INT i;
6935 if (nFirst <= nLast)
6937 if ((nFirst >= 0) && (nFirst < GETITEMCOUNT(infoPtr)))
6939 if ((nLast >= 0) && (nLast < GETITEMCOUNT(infoPtr)))
6941 for (i = nFirst; i <= nLast; i++)
6943 rcItem.left = LVIR_BOUNDS;
6944 LISTVIEW_GetItemRect(hwnd, i, &rcItem);
6945 InvalidateRect(hwnd, &rcItem, TRUE);
6951 return bResult;
6954 /* LISTVIEW_Scroll */
6956 /***
6957 * DESCRIPTION:
6958 * Sets the background color.
6960 * PARAMETER(S):
6961 * [I] HWND : window handle
6962 * [I] COLORREF : background color
6964 * RETURN:
6965 * SUCCESS : TRUE
6966 * FAILURE : FALSE
6968 static LRESULT LISTVIEW_SetBkColor(HWND hwnd, COLORREF clrBk)
6970 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6972 if(infoPtr->clrBk!=clrBk){
6973 infoPtr->clrBk = clrBk;
6974 InvalidateRect(hwnd, NULL, TRUE);
6977 return TRUE;
6980 /* LISTVIEW_SetBkImage */
6982 /***
6983 * DESCRIPTION:
6984 * Sets the callback mask. This mask will be used when the parent
6985 * window stores state information (some or all).
6987 * PARAMETER(S):
6988 * [I] HWND : window handle
6989 * [I] UINT : state mask
6991 * RETURN:
6992 * SUCCESS : TRUE
6993 * FAILURE : FALSE
6995 static BOOL LISTVIEW_SetCallbackMask(HWND hwnd, UINT uMask)
6997 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6999 infoPtr->uCallbackMask = uMask;
7001 return TRUE;
7004 /***
7005 * DESCRIPTION:
7006 * Sets the attributes of a header item.
7008 * PARAMETER(S):
7009 * [I] HWND : window handle
7010 * [I] INT : column index
7011 * [I] LPLVCOLUMNW : column attributes
7012 * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW,
7013 * otherwise it is in fact a LPLVCOLUMNA
7015 * RETURN:
7016 * SUCCESS : TRUE
7017 * FAILURE : FALSE
7019 static LRESULT LISTVIEW_SetColumnT(HWND hwnd, INT nColumn,
7020 LPLVCOLUMNW lpColumn, BOOL isW)
7022 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7023 BOOL bResult = FALSE;
7024 HDITEMW hdi, hdiget;
7026 if ((lpColumn != NULL) && (nColumn >= 0) &&
7027 (nColumn < Header_GetItemCount(infoPtr->hwndHeader)))
7029 /* initialize memory */
7030 ZeroMemory(&hdi, sizeof(hdi));
7032 if (lpColumn->mask & LVCF_FMT)
7034 /* format member is valid */
7035 hdi.mask |= HDI_FORMAT;
7037 /* get current format first */
7038 hdiget.mask = HDI_FORMAT;
7039 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
7040 /* preserve HDF_STRING if present */
7041 hdi.fmt = hdiget.fmt & HDF_STRING;
7043 /* set text alignment (leftmost column must be left-aligned) */
7044 if (nColumn == 0)
7046 hdi.fmt |= HDF_LEFT;
7048 else
7050 if (lpColumn->fmt & LVCFMT_LEFT)
7051 hdi.fmt |= HDF_LEFT;
7052 else if (lpColumn->fmt & LVCFMT_RIGHT)
7053 hdi.fmt |= HDF_RIGHT;
7054 else if (lpColumn->fmt & LVCFMT_CENTER)
7055 hdi.fmt |= HDF_CENTER;
7058 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
7059 hdi.fmt |= HDF_BITMAP_ON_RIGHT;
7061 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
7062 hdi.fmt |= HDF_IMAGE;
7064 if (lpColumn->fmt & LVCFMT_IMAGE)
7066 hdi.fmt |= HDF_IMAGE;
7067 hdi.iImage = I_IMAGECALLBACK;
7071 if (lpColumn->mask & LVCF_WIDTH)
7073 hdi.mask |= HDI_WIDTH;
7074 hdi.cxy = lpColumn->cx;
7077 if (lpColumn->mask & LVCF_TEXT)
7079 hdi.mask |= HDI_TEXT | HDI_FORMAT;
7080 hdi.pszText = lpColumn->pszText;
7081 hdi.cchTextMax = textlenT(lpColumn->pszText, isW);
7082 hdi.fmt |= HDF_STRING;
7085 if (lpColumn->mask & LVCF_IMAGE)
7087 hdi.mask |= HDI_IMAGE;
7088 hdi.iImage = lpColumn->iImage;
7091 if (lpColumn->mask & LVCF_ORDER)
7093 hdi.mask |= HDI_ORDER;
7094 hdi.iOrder = lpColumn->iOrder;
7097 /* set header item attributes */
7098 bResult = Header_SetItemW(infoPtr->hwndHeader, nColumn, &hdi);
7101 return bResult;
7104 /***
7105 * DESCRIPTION:
7106 * Sets the column order array
7108 * PARAMETERS:
7109 * [I] HWND : window handle
7110 * [I] INT : number of elements in column order array
7111 * [I] INT : pointer to column order array
7113 * RETURN:
7114 * SUCCESS : TRUE
7115 * FAILURE : FALSE
7117 static LRESULT LISTVIEW_SetColumnOrderArray(HWND hwnd, INT iCount, LPINT lpiArray)
7119 /* LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); */
7121 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
7123 if (!lpiArray)
7124 return FALSE;
7126 return TRUE;
7130 /***
7131 * DESCRIPTION:
7132 * Sets the width of a column
7134 * PARAMETERS:
7135 * [I] HWND : window handle
7136 * [I] INT : column index
7137 * [I] INT : column width
7139 * RETURN:
7140 * SUCCESS : TRUE
7141 * FAILURE : FALSE
7143 static LRESULT LISTVIEW_SetColumnWidth(HWND hwnd, INT iCol, INT cx)
7145 LISTVIEW_INFO *infoPtr;
7146 HDITEMW hdi;
7147 LRESULT lret;
7148 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
7149 UINT uView = lStyle & LVS_TYPEMASK;
7150 HDC hdc;
7151 HFONT header_font;
7152 HFONT old_font;
7153 SIZE size;
7154 WCHAR text_buffer[DISP_TEXT_SIZE];
7155 INT header_item_count;
7156 INT item_index;
7157 INT nLabelWidth;
7158 RECT rcHeader;
7159 LVITEMW lvItem;
7160 WCHAR szDispText[DISP_TEXT_SIZE];
7162 /* make sure we can get the listview info */
7163 if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0)))
7164 return (FALSE);
7166 if (!infoPtr->hwndHeader) /* make sure we have a header */
7167 return (FALSE);
7169 /* set column width only if in report or list mode */
7170 if ((uView != LVS_REPORT) && (uView != LVS_LIST))
7171 return (FALSE);
7173 TRACE("(hwnd=%x, iCol=%d, cx=%d\n", hwnd, iCol, cx);
7175 /* take care of invalid cx values */
7176 if((uView == LVS_REPORT) && (cx < -2))
7177 cx = LVSCW_AUTOSIZE;
7178 else if (uView == LVS_LIST && (cx < 1))
7179 return FALSE;
7181 /* resize all columns if in LVS_LIST mode */
7182 if(uView == LVS_LIST) {
7183 infoPtr->nItemWidth = cx;
7184 InvalidateRect(hwnd, NULL, TRUE); /* force redraw of the listview */
7185 return TRUE;
7188 /* autosize based on listview items width */
7189 if(cx == LVSCW_AUTOSIZE)
7191 /* set the width of the column to the width of the widest item */
7192 if (iCol == 0 || uView == LVS_LIST)
7194 cx = 0;
7195 for(item_index = 0; item_index < GETITEMCOUNT(infoPtr); item_index++)
7197 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, item_index);
7198 cx = (nLabelWidth>cx)?nLabelWidth:cx;
7200 /* I had to add the '3' to prevent clipping of the end of the
7201 line. Probably one of these padding numbers is incorrect. */
7202 if (infoPtr->himlSmall)
7203 cx += WIDTH_PADDING + IMAGE_PADDING + 3;
7205 else
7207 ZeroMemory(&lvItem, sizeof(lvItem));
7208 lvItem.iSubItem = iCol;
7209 lvItem.mask = LVIF_TEXT;
7210 lvItem.cchTextMax = DISP_TEXT_SIZE;
7211 lvItem.pszText = szDispText;
7212 *lvItem.pszText = '\0';
7213 cx = 0;
7214 for(item_index = 0; item_index < GETITEMCOUNT(infoPtr); item_index++)
7216 lvItem.iItem = item_index;
7217 LISTVIEW_GetItemT(hwnd, &lvItem, FALSE, TRUE);
7218 nLabelWidth = LISTVIEW_GetStringWidthT(hwnd, lvItem.pszText, TRUE);
7219 cx = (nLabelWidth>cx)?nLabelWidth:cx;
7222 cx += TRAILING_PADDING;
7223 } /* autosize based on listview header width */
7224 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
7226 header_item_count = Header_GetItemCount(infoPtr->hwndHeader);
7228 /* if iCol is the last column make it fill the remainder of the controls width */
7229 if(iCol == (header_item_count - 1)) {
7230 /* get the width of every item except the current one */
7231 hdi.mask = HDI_WIDTH;
7232 cx = 0;
7234 for(item_index = 0; item_index < (header_item_count - 1); item_index++) {
7235 Header_GetItemW(infoPtr->hwndHeader, item_index, (LPARAM)(&hdi));
7236 cx+=hdi.cxy;
7239 /* retrieve the layout of the header */
7240 GetWindowRect(infoPtr->hwndHeader, &rcHeader);
7242 cx = (rcHeader.right - rcHeader.left) - cx;
7244 else
7246 /* Despite what the MS docs say, if this is not the last
7247 column, then MS resizes the column to the width of the
7248 largest text string in the column, including headers
7249 and items. This is different from LVSCW_AUTOSIZE in that
7250 LVSCW_AUTOSIZE ignores the header string length.
7253 /* retrieve header font */
7254 header_font = SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0L, 0L);
7256 /* retrieve header text */
7257 hdi.mask = HDI_TEXT;
7258 hdi.cchTextMax = sizeof(text_buffer)/sizeof(text_buffer[0]);
7259 hdi.pszText = text_buffer;
7261 Header_GetItemW(infoPtr->hwndHeader, iCol, (LPARAM)(&hdi));
7263 /* determine the width of the text in the header */
7264 hdc = GetDC(hwnd);
7265 old_font = SelectObject(hdc, header_font); /* select the font into hdc */
7267 GetTextExtentPoint32W(hdc, text_buffer, lstrlenW(text_buffer), &size);
7269 SelectObject(hdc, old_font); /* restore the old font */
7270 ReleaseDC(hwnd, hdc);
7272 ZeroMemory(&lvItem, sizeof(lvItem));
7273 lvItem.iSubItem = iCol;
7274 lvItem.mask = LVIF_TEXT;
7275 lvItem.cchTextMax = DISP_TEXT_SIZE;
7276 lvItem.pszText = szDispText;
7277 *lvItem.pszText = '\0';
7278 cx = size.cx;
7279 for(item_index = 0; item_index < GETITEMCOUNT(infoPtr); item_index++)
7281 lvItem.iItem = item_index;
7282 LISTVIEW_GetItemT(hwnd, &lvItem, FALSE, TRUE);
7283 nLabelWidth = LISTVIEW_GetStringWidthT(hwnd, lvItem.pszText, TRUE);
7284 nLabelWidth += TRAILING_PADDING;
7285 /* While it is possible for subitems to have icons, even MS messes
7286 up the positioning, so I suspect no applications actually use
7287 them. */
7288 if (item_index == 0 && infoPtr->himlSmall)
7289 /* I had to add the '3' to prevent clipping of the end of the
7290 line. Probably one of these padding numbers is incorrect. */
7291 nLabelWidth += WIDTH_PADDING + IMAGE_PADDING + 3;
7292 cx = (nLabelWidth>cx)?nLabelWidth:cx;
7297 /* call header to update the column change */
7298 hdi.mask = HDI_WIDTH;
7300 hdi.cxy = cx;
7301 lret = Header_SetItemW(infoPtr->hwndHeader, (WPARAM)iCol, (LPARAM)&hdi);
7303 InvalidateRect(hwnd, NULL, TRUE); /* force redraw of the listview */
7305 return lret;
7308 /***
7309 * DESCRIPTION:
7310 * Sets the extended listview style.
7312 * PARAMETERS:
7313 * [I] HWND : window handle
7314 * [I] DWORD : mask
7315 * [I] DWORD : style
7317 * RETURN:
7318 * SUCCESS : previous style
7319 * FAILURE : 0
7321 static LRESULT LISTVIEW_SetExtendedListViewStyle(HWND hwnd, DWORD dwMask, DWORD dwStyle)
7323 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7324 DWORD dwOldStyle = infoPtr->dwExStyle;
7326 /* set new style */
7327 if (dwMask)
7328 infoPtr->dwExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
7329 else
7330 infoPtr->dwExStyle = dwStyle;
7332 return dwOldStyle;
7335 /* LISTVIEW_SetHotCursor */
7337 /***
7338 * DESCRIPTION:
7339 * Sets the hot item index.
7341 * PARAMETERS:
7342 * [I] HWND : window handle
7343 * [I] INT : index
7345 * RETURN:
7346 * SUCCESS : previous hot item index
7347 * FAILURE : -1 (no hot item)
7349 static LRESULT LISTVIEW_SetHotItem(HWND hwnd, INT iIndex)
7351 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7352 INT iOldIndex = infoPtr->nHotItem;
7354 /* set new style */
7355 infoPtr->nHotItem = iIndex;
7357 return iOldIndex;
7360 /***
7361 * DESCRIPTION:
7362 * Sets the amount of time the cursor must hover over an item before it is selected.
7364 * PARAMETER(S):
7365 * [I] HWND : window handle
7366 * [I] DWORD : dwHoverTime, if -1 the hover time is set to the default
7368 * RETURN:
7369 * Returns the previous hover time
7371 static LRESULT LISTVIEW_SetHoverTime(HWND hwnd, DWORD dwHoverTime)
7373 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7374 DWORD oldHoverTime = infoPtr->dwHoverTime;
7376 infoPtr->dwHoverTime = dwHoverTime;
7378 return oldHoverTime;
7381 /***
7382 * DESCRIPTION:
7383 * Sets spacing for icons of LVS_ICON style.
7385 * PARAMETER(S):
7386 * [I] HWND : window handle
7387 * [I] DWORD : MAKELONG(cx, cy)
7389 * RETURN:
7390 * MAKELONG(oldcx, oldcy)
7392 static LRESULT LISTVIEW_SetIconSpacing(HWND hwnd, DWORD spacing)
7394 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7395 INT cy = HIWORD(spacing);
7396 INT cx = LOWORD(spacing);
7397 DWORD oldspacing;
7398 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
7399 UINT uView = lStyle & LVS_TYPEMASK;
7401 oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
7402 if (cx == -1) /* set to default */
7403 cx = GetSystemMetrics(SM_CXICONSPACING);
7404 if (cy == -1) /* set to default */
7405 cy = GetSystemMetrics(SM_CYICONSPACING);
7407 if (cx)
7408 infoPtr->iconSpacing.cx = cx;
7409 else
7410 { /* if 0 then compute width */
7411 if (uView == LVS_ICON)
7412 FIXME("width computation not yet done\n");
7414 * Should scan each item and determine max width of
7415 * icon or label, then make that the width
7417 else /* FIXME: unknown computation for non LVS_ICON - this is a guess */
7418 infoPtr->iconSpacing.cx = LISTVIEW_GetItemWidth(hwnd);
7420 if (cy)
7421 infoPtr->iconSpacing.cy = cy;
7422 else
7423 { /* if 0 then compute height */
7424 if (uView == LVS_ICON)
7425 infoPtr->iconSpacing.cy = infoPtr->iconSize.cy + infoPtr->ntmHeight
7426 + ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_OFFSET;
7427 /* FIXME. I don't think so; I think it is based on twice the ntmHeight */
7428 else /* FIXME: unknown computation for non LVS_ICON - this is a guess */
7429 infoPtr->iconSpacing.cy = LISTVIEW_GetItemHeight(hwnd);
7432 TRACE("old=(%d,%d), new=(%ld,%ld)\n", LOWORD(oldspacing), HIWORD(oldspacing),
7433 infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
7435 /* these depend on the iconSpacing */
7436 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
7437 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
7439 return oldspacing;
7442 /***
7443 * DESCRIPTION:
7444 * Sets image lists.
7446 * PARAMETER(S):
7447 * [I] HWND : window handle
7448 * [I] INT : image list type
7449 * [I] HIMAGELIST : image list handle
7451 * RETURN:
7452 * SUCCESS : old image list
7453 * FAILURE : NULL
7455 static HIMAGELIST LISTVIEW_SetImageList(HWND hwnd, INT nType, HIMAGELIST himl)
7457 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7458 HIMAGELIST himlOld = 0;
7459 INT oldHeight;
7461 switch (nType)
7463 case LVSIL_NORMAL:
7464 himlOld = infoPtr->himlNormal;
7465 infoPtr->himlNormal = himl;
7466 break;
7468 case LVSIL_SMALL:
7469 himlOld = infoPtr->himlSmall;
7470 infoPtr->himlSmall = himl;
7471 break;
7473 case LVSIL_STATE:
7474 himlOld = infoPtr->himlState;
7475 infoPtr->himlState = himl;
7476 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
7477 break;
7480 oldHeight = infoPtr->nItemHeight;
7481 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
7482 if (infoPtr->nItemHeight != oldHeight)
7483 LISTVIEW_UpdateScroll(hwnd);
7485 return himlOld;
7488 /***
7489 * DESCRIPTION:
7490 * Preallocates memory (does *not* set the actual count of items !)
7492 * PARAMETER(S):
7493 * [I] HWND : window handle
7494 * [I] INT : item count (projected number of items to allocate)
7495 * [I] DWORD : update flags
7497 * RETURN:
7498 * SUCCESS : TRUE
7499 * FAILURE : FALSE
7501 static BOOL LISTVIEW_SetItemCount(HWND hwnd, INT nItems, DWORD dwFlags)
7503 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
7505 TRACE("(hwnd=%x, nItems=%d, dwFlags=%lx)\n", hwnd, nItems, dwFlags);
7507 if (GetWindowLongW(hwnd, GWL_STYLE) & LVS_OWNERDATA)
7509 int precount,topvisible;
7511 TRACE("LVS_OWNERDATA is set!\n");
7512 if (dwFlags & (LVSICF_NOINVALIDATEALL | LVSICF_NOSCROLL))
7513 FIXME("flags %s %s not implemented\n",
7514 (dwFlags & LVSICF_NOINVALIDATEALL) ? "LVSICF_NOINVALIDATEALL"
7515 : "",
7516 (dwFlags & LVSICF_NOSCROLL) ? "LVSICF_NOSCROLL" : "");
7519 * Internally remove all the selections.
7523 LISTVIEW_SELECTION *selection;
7524 selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,0);
7525 if (selection)
7526 LISTVIEW_RemoveSelectionRange(hwnd,selection->lower,
7527 selection->upper);
7529 while (infoPtr->hdpaSelectionRanges->nItemCount>0);
7531 precount = infoPtr->hdpaItems->nItemCount;
7532 topvisible = ListView_GetTopIndex(hwnd) +
7533 LISTVIEW_GetCountPerColumn(hwnd) + 1;
7535 infoPtr->hdpaItems->nItemCount = nItems;
7537 LISTVIEW_UpdateSize(hwnd);
7538 LISTVIEW_UpdateScroll(hwnd);
7539 if (min(precount,infoPtr->hdpaItems->nItemCount)<topvisible)
7540 InvalidateRect(hwnd, NULL, TRUE);
7542 else
7544 /* According to MSDN for non-LVS_OWNERDATA this is just
7545 * a performance issue. The control allocates its internal
7546 * data structures for the number of items specified. It
7547 * cuts down on the number of memory allocations. Therefore
7548 * we will just issue a WARN here
7550 WARN("for non-ownerdata performance option not implemented.\n");
7553 return TRUE;
7556 /***
7557 * DESCRIPTION:
7558 * Sets the position of an item.
7560 * PARAMETER(S):
7561 * [I] HWND : window handle
7562 * [I] INT : item index
7563 * [I] LONG : x coordinate
7564 * [I] LONG : y coordinate
7566 * RETURN:
7567 * SUCCESS : TRUE
7568 * FAILURE : FALSE
7570 static BOOL LISTVIEW_SetItemPosition(HWND hwnd, INT nItem,
7571 LONG nPosX, LONG nPosY)
7573 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
7574 UINT lStyle = GetWindowLongW(hwnd, GWL_STYLE);
7575 UINT uView = lStyle & LVS_TYPEMASK;
7576 LISTVIEW_ITEM *lpItem;
7577 HDPA hdpaSubItems;
7578 BOOL bResult = FALSE;
7580 TRACE("(hwnd=%x, nItem=%d, X=%ld, Y=%ld)\n", hwnd, nItem, nPosX, nPosY);
7582 if (lStyle & LVS_OWNERDATA)
7583 return FALSE;
7585 if ((nItem >= 0) || (nItem < GETITEMCOUNT(infoPtr)))
7587 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
7589 if ( (hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)) )
7591 if ( (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)) )
7593 POINT orig;
7594 bResult = TRUE;
7595 orig = lpItem->ptPosition;
7596 if ((nPosX == -1) && (nPosY == -1))
7598 /* This point value seems to be an undocumented feature. The
7599 * best guess is that it means either at the origin, or at
7600 * the true beginning of the list. I will assume the origin.
7602 POINT pt1;
7603 if (!LISTVIEW_GetOrigin(hwnd, &pt1))
7605 pt1.x = 0;
7606 pt1.y = 0;
7608 nPosX = pt1.x;
7609 nPosY = pt1.y;
7610 if (uView == LVS_ICON)
7612 nPosX += (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
7613 nPosY += ICON_TOP_PADDING;
7615 TRACE("requested special (-1,-1), set to origin (%ld,%ld)\n",
7616 nPosX, nPosY);
7619 lpItem->ptPosition.x = nPosX;
7620 lpItem->ptPosition.y = nPosY;
7621 if (uView == LVS_ICON)
7623 lpItem->ptPosition.y -= ICON_TOP_PADDING;
7624 lpItem->ptPosition.x -= (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
7625 if ((lpItem->ptPosition.y < 0) || (lpItem->ptPosition.x < 0))
7627 FIXME("failed orig (%ld,%ld), intent (%ld,%ld), is (%ld, %ld), setting neg to 0\n",
7628 orig.x, orig.y, nPosX, nPosY, lpItem->ptPosition.x, lpItem->ptPosition.y);
7631 if (lpItem->ptPosition.x < 0) lpItem->ptPosition.x = 0;
7632 if (lpItem->ptPosition.y < 0) lpItem->ptPosition.y = 0;
7635 else
7637 TRACE("orig (%ld,%ld), intent (%ld,%ld), is (%ld,%ld)\n",
7638 orig.x, orig.y, nPosX, nPosY, lpItem->ptPosition.x, lpItem->ptPosition.y);
7646 return bResult;
7649 /***
7650 * DESCRIPTION:
7651 * Sets the state of one or many items.
7653 * PARAMETER(S):
7654 * [I] HWND : window handle
7655 * [I]INT : item index
7656 * [I] LPLVITEM : item or subitem info
7658 * RETURN:
7659 * SUCCESS : TRUE
7660 * FAILURE : FALSE
7662 static LRESULT LISTVIEW_SetItemState(HWND hwnd, INT nItem, LPLVITEMW lpLVItem)
7664 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7665 BOOL bResult = TRUE;
7666 LVITEMW lvItem;
7668 TRACE("(hwnd=%x, nItem=%d, lpLVItem=%s)\n",
7669 hwnd, nItem, debuglvitem_t(lpLVItem, TRUE));
7671 ZeroMemory(&lvItem, sizeof(lvItem));
7672 lvItem.mask = LVIF_STATE;
7673 lvItem.state = lpLVItem->state;
7674 lvItem.stateMask = lpLVItem->stateMask ;
7675 lvItem.iItem = nItem;
7677 if (nItem == -1)
7679 /* apply to all items */
7680 for (lvItem.iItem = 0; lvItem.iItem < GETITEMCOUNT(infoPtr); lvItem.iItem++)
7681 if (!ListView_SetItemW(hwnd, &lvItem)) bResult = FALSE;
7683 else
7684 bResult = ListView_SetItemW(hwnd, &lvItem);
7686 return bResult;
7689 /***
7690 * DESCRIPTION:
7691 * Sets the text of an item or subitem.
7693 * PARAMETER(S):
7694 * [I] hwnd : window handle
7695 * [I] nItem : item index
7696 * [I] lpLVItem : item or subitem info
7697 * [I] isW : TRUE if input is Unicode
7699 * RETURN:
7700 * SUCCESS : TRUE
7701 * FAILURE : FALSE
7703 static BOOL LISTVIEW_SetItemTextT(HWND hwnd, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
7705 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7706 BOOL bResult = FALSE;
7707 LVITEMW lvItem;
7709 TRACE("(hwnd=%x, nItem=%d, lpLVItem=%s, isW=%d)\n",
7710 hwnd, nItem, debuglvitem_t(lpLVItem, isW), isW);
7712 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
7714 ZeroMemory(&lvItem, sizeof(LVITEMW));
7715 lvItem.mask = LVIF_TEXT;
7716 lvItem.pszText = lpLVItem->pszText;
7717 lvItem.iItem = nItem;
7718 lvItem.iSubItem = lpLVItem->iSubItem;
7719 if(isW) bResult = ListView_SetItemW(hwnd, &lvItem);
7720 else bResult = ListView_SetItemA(hwnd, &lvItem);
7723 return bResult;
7726 /***
7727 * DESCRIPTION:
7728 * Set item index that marks the start of a multiple selection.
7730 * PARAMETER(S):
7731 * [I] HWND : window handle
7732 * [I] INT : index
7734 * RETURN:
7735 * Index number or -1 if there is no selection mark.
7737 static LRESULT LISTVIEW_SetSelectionMark(HWND hwnd, INT nIndex)
7739 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7740 INT nOldIndex = infoPtr->nSelectionMark;
7742 TRACE("(hwnd=%x, nIndex=%d)\n", hwnd, nIndex);
7744 infoPtr->nSelectionMark = nIndex;
7746 return nOldIndex;
7749 /***
7750 * DESCRIPTION:
7751 * Sets the text background color.
7753 * PARAMETER(S):
7754 * [I] HWND : window handle
7755 * [I] COLORREF : text background color
7757 * RETURN:
7758 * SUCCESS : TRUE
7759 * FAILURE : FALSE
7761 static LRESULT LISTVIEW_SetTextBkColor(HWND hwnd, COLORREF clrTextBk)
7763 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7765 TRACE("(hwnd=%x, clrTextBk=%lx)\n", hwnd, clrTextBk);
7767 infoPtr->clrTextBk = clrTextBk;
7768 InvalidateRect(hwnd, NULL, TRUE);
7770 return TRUE;
7773 /***
7774 * DESCRIPTION:
7775 * Sets the text foreground color.
7777 * PARAMETER(S):
7778 * [I] HWND : window handle
7779 * [I] COLORREF : text color
7781 * RETURN:
7782 * SUCCESS : TRUE
7783 * FAILURE : FALSE
7785 static LRESULT LISTVIEW_SetTextColor (HWND hwnd, COLORREF clrText)
7787 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7789 TRACE("(hwnd=%x, clrText=%lx)\n", hwnd, clrText);
7791 infoPtr->clrText = clrText;
7792 InvalidateRect(hwnd, NULL, TRUE);
7794 return TRUE;
7797 /* LISTVIEW_SetToolTips */
7798 /* LISTVIEW_SetUnicodeFormat */
7799 /* LISTVIEW_SetWorkAreas */
7801 /***
7802 * DESCRIPTION:
7803 * Callback internally used by LISTVIEW_SortItems()
7805 * PARAMETER(S):
7806 * [I] LPVOID : first LISTVIEW_ITEM to compare
7807 * [I] LPVOID : second LISTVIEW_ITEM to compare
7808 * [I] LPARAM : HWND of control
7810 * RETURN:
7811 * if first comes before second : negative
7812 * if first comes after second : positive
7813 * if first and second are equivalent : zero
7815 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
7817 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW((HWND)lParam, 0);
7818 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 );
7819 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 );
7821 /* Forward the call to the client defined callback */
7822 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
7825 /***
7826 * DESCRIPTION:
7827 * Sorts the listview items.
7829 * PARAMETER(S):
7830 * [I] HWND : window handle
7831 * [I] WPARAM : application-defined value
7832 * [I] LPARAM : pointer to comparision callback
7834 * RETURN:
7835 * SUCCESS : TRUE
7836 * FAILURE : FALSE
7838 static LRESULT LISTVIEW_SortItems(HWND hwnd, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
7840 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7841 UINT lStyle = GetWindowLongW(hwnd, GWL_STYLE);
7842 HDPA hdpaSubItems=NULL;
7843 LISTVIEW_ITEM *pLVItem=NULL;
7844 LPVOID selectionMarkItem;
7845 int nCount, i;
7847 TRACE("(hwnd=%x, pfnCompare=%p, lParamSort=%lx)\n", hwnd, pfnCompare, lParamSort);
7849 if (lStyle & LVS_OWNERDATA) return FALSE;
7851 if (!infoPtr || !infoPtr->hdpaItems) return FALSE;
7853 nCount = GETITEMCOUNT(infoPtr);
7854 /* if there are 0 or 1 items, there is no need to sort */
7855 if (nCount < 2)
7856 return TRUE;
7858 infoPtr->pfnCompare = pfnCompare;
7859 infoPtr->lParamSort = lParamSort;
7860 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, hwnd);
7862 /* Adjust selections and indices so that they are the way they should
7863 * be after the sort (otherwise, the list items move around, but
7864 * whatever is at the item's previous original position will be
7865 * selected instead)
7867 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
7868 for (i=0; i < nCount; i++)
7870 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
7871 pLVItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
7873 if (pLVItem->state & LVIS_SELECTED)
7874 LISTVIEW_AddSelectionRange(hwnd, i, i);
7875 else
7876 LISTVIEW_RemoveSelectionRange(hwnd, i, i);
7877 if (pLVItem->state & LVIS_FOCUSED)
7878 infoPtr->nFocusedItem=i;
7880 if (selectionMarkItem != NULL)
7881 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
7882 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7884 /* align the items */
7885 LISTVIEW_AlignTop(hwnd);
7887 /* refresh the display */
7888 InvalidateRect(hwnd, NULL, TRUE);
7890 return TRUE;
7893 /* LISTVIEW_SubItemHitTest */
7895 /***
7896 * DESCRIPTION:
7897 * Updates an items or rearranges the listview control.
7899 * PARAMETER(S):
7900 * [I] HWND : window handle
7901 * [I] INT : item index
7903 * RETURN:
7904 * SUCCESS : TRUE
7905 * FAILURE : FALSE
7907 static LRESULT LISTVIEW_Update(HWND hwnd, INT nItem)
7909 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7910 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
7911 BOOL bResult = FALSE;
7912 RECT rc;
7914 TRACE("(hwnd=%x, nItem=%d)\n", hwnd, nItem);
7916 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
7918 bResult = TRUE;
7920 /* rearrange with default alignment style */
7921 if ((lStyle & LVS_AUTOARRANGE) && (((lStyle & LVS_TYPEMASK) == LVS_ICON) ||
7922 ((lStyle & LVS_TYPEMASK) == LVS_SMALLICON)))
7924 ListView_Arrange(hwnd, 0);
7926 else
7928 /* get item bounding rectangle */
7929 ListView_GetItemRect(hwnd, nItem, &rc, LVIR_BOUNDS);
7930 InvalidateRect(hwnd, &rc, TRUE);
7934 return bResult;
7937 /***
7938 * DESCRIPTION:
7939 * Creates the listview control.
7941 * PARAMETER(S):
7942 * [I] HWND : window handle
7944 * RETURN:
7945 * Zero
7947 static LRESULT LISTVIEW_Create(HWND hwnd, LPCREATESTRUCTW lpcs)
7949 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7950 UINT uView = lpcs->style & LVS_TYPEMASK;
7951 LOGFONTW logFont;
7953 TRACE("(hwnd=%x, lpcs=%p)\n", hwnd, lpcs);
7955 /* initialize info pointer */
7956 ZeroMemory(infoPtr, sizeof(LISTVIEW_INFO));
7958 /* determine the type of structures to use */
7959 infoPtr->notifyFormat = SendMessageW(GetParent(hwnd), WM_NOTIFYFORMAT,
7960 (WPARAM)hwnd, (LPARAM)NF_QUERY);
7962 /* initialize color information */
7963 infoPtr->clrBk = GetSysColor(COLOR_WINDOW);
7964 infoPtr->clrText = GetSysColor(COLOR_WINDOWTEXT);
7965 infoPtr->clrTextBk = CLR_DEFAULT;
7967 /* set default values */
7968 infoPtr->hwndSelf = hwnd;
7969 infoPtr->uCallbackMask = 0;
7970 infoPtr->nFocusedItem = -1;
7971 infoPtr->nSelectionMark = -1;
7972 infoPtr->nHotItem = -1;
7973 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
7974 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
7975 ZeroMemory(&infoPtr->rcList, sizeof(RECT));
7976 infoPtr->hwndEdit = 0;
7977 infoPtr->pedititem = NULL;
7978 infoPtr->nEditLabelItem = -1;
7980 /* get default font (icon title) */
7981 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
7982 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
7983 infoPtr->hFont = infoPtr->hDefaultFont;
7984 LISTVIEW_SaveTextMetrics(hwnd);
7986 /* create header */
7987 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, (LPCWSTR)NULL,
7988 WS_CHILD | HDS_HORZ | HDS_BUTTONS,
7989 0, 0, 0, 0, hwnd, (HMENU)0,
7990 lpcs->hInstance, NULL);
7992 /* set header font */
7993 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont,
7994 (LPARAM)TRUE);
7996 if (uView == LVS_ICON)
7998 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
7999 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
8001 else if (uView == LVS_REPORT)
8003 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
8005 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
8007 else
8009 /* set HDS_HIDDEN flag to hide the header bar */
8010 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
8011 GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
8015 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8016 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8018 else
8020 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8021 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8024 /* display unsupported listview window styles */
8025 LISTVIEW_UnsupportedStyles(lpcs->style);
8027 /* allocate memory for the data structure */
8028 infoPtr->hdpaItems = DPA_Create(10);
8030 /* allocate memory for the selection ranges */
8031 infoPtr->hdpaSelectionRanges = DPA_Create(10);
8033 /* initialize size of items */
8034 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8035 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
8037 /* initialize the hover time to -1(indicating the default system hover time) */
8038 infoPtr->dwHoverTime = -1;
8040 return 0;
8043 /***
8044 * DESCRIPTION:
8045 * Erases the background of the listview control.
8047 * PARAMETER(S):
8048 * [I] HWND : window handle
8049 * [I] WPARAM : device context handle
8050 * [I] LPARAM : not used
8052 * RETURN:
8053 * SUCCESS : TRUE
8054 * FAILURE : FALSE
8056 static LRESULT LISTVIEW_EraseBackground(HWND hwnd, WPARAM wParam,
8057 LPARAM lParam)
8059 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8060 BOOL bResult;
8062 TRACE("(hwnd=%x, wParam=%x, lParam=%lx)\n", hwnd, wParam, lParam);
8064 if (infoPtr->clrBk == CLR_NONE)
8066 bResult = SendMessageW(GetParent(hwnd), WM_ERASEBKGND, wParam, lParam);
8068 else
8070 RECT rc;
8071 HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk);
8072 GetClientRect(hwnd, &rc);
8073 FillRect((HDC)wParam, &rc, hBrush);
8074 DeleteObject(hBrush);
8075 bResult = TRUE;
8078 return bResult;
8082 static void LISTVIEW_FillBackground(HWND hwnd, HDC hdc, LPRECT rc)
8084 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8086 TRACE("(hwnd=%x, hdc=%x, rc=%p)\n", hwnd, hdc, rc);
8088 if (infoPtr->clrBk != CLR_NONE)
8090 HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk);
8091 FillRect(hdc, rc, hBrush);
8092 DeleteObject(hBrush);
8096 /***
8097 * DESCRIPTION:
8098 * Retrieves the listview control font.
8100 * PARAMETER(S):
8101 * [I] HWND : window handle
8103 * RETURN:
8104 * Font handle.
8106 static LRESULT LISTVIEW_GetFont(HWND hwnd)
8108 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8110 TRACE("(hwnd=%x)\n", hwnd);
8112 return infoPtr->hFont;
8115 /***
8116 * DESCRIPTION:
8117 * Performs vertical scrolling.
8119 * PARAMETER(S):
8120 * [I] HWND : window handle
8121 * [I] INT : scroll code
8122 * [I] SHORT : current scroll position if scroll code is SB_THUMBPOSITION
8123 * or SB_THUMBTRACK.
8124 * [I] HWND : scrollbar control window handle
8126 * RETURN:
8127 * Zero
8129 static LRESULT LISTVIEW_VScroll(HWND hwnd, INT nScrollCode, SHORT nCurrentPos,
8130 HWND hScrollWnd)
8132 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8133 SCROLLINFO scrollInfo;
8135 TRACE("(hwnd=%x, nScrollCode=%d, nCurrentPos=%d, hScrollWnd=%x)\n",
8136 hwnd, nScrollCode, nCurrentPos, hScrollWnd);
8138 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8140 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
8141 scrollInfo.cbSize = sizeof(SCROLLINFO);
8142 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
8144 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
8146 INT nOldScrollPos = scrollInfo.nPos;
8147 switch (nScrollCode)
8149 case SB_LINEUP:
8150 if (scrollInfo.nPos > scrollInfo.nMin)
8151 scrollInfo.nPos--;
8152 break;
8154 case SB_LINEDOWN:
8155 if (scrollInfo.nPos < scrollInfo.nMax)
8156 scrollInfo.nPos++;
8157 break;
8159 case SB_PAGEUP:
8160 if (scrollInfo.nPos > scrollInfo.nMin)
8162 if (scrollInfo.nPos >= scrollInfo.nPage)
8163 scrollInfo.nPos -= scrollInfo.nPage;
8164 else
8165 scrollInfo.nPos = scrollInfo.nMin;
8167 break;
8169 case SB_PAGEDOWN:
8170 if (scrollInfo.nPos < scrollInfo.nMax)
8172 if (scrollInfo.nPos <= scrollInfo.nMax - scrollInfo.nPage)
8173 scrollInfo.nPos += scrollInfo.nPage;
8174 else
8175 scrollInfo.nPos = scrollInfo.nMax;
8177 break;
8179 case SB_THUMBPOSITION:
8180 case SB_THUMBTRACK:
8181 scrollInfo.nPos = nCurrentPos;
8182 if (scrollInfo.nPos > scrollInfo.nMax)
8183 scrollInfo.nPos=scrollInfo.nMax;
8185 if (scrollInfo.nPos < scrollInfo.nMin)
8186 scrollInfo.nPos=scrollInfo.nMin;
8188 break;
8191 if (nOldScrollPos != scrollInfo.nPos)
8193 scrollInfo.fMask = SIF_POS;
8194 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
8195 if (IsWindowVisible(infoPtr->hwndHeader))
8197 RECT rListview, rcHeader, rDest;
8198 GetClientRect(hwnd, &rListview);
8199 GetWindowRect(infoPtr->hwndHeader, &rcHeader);
8200 MapWindowPoints((HWND) NULL, hwnd, (LPPOINT) &rcHeader, 2);
8201 SubtractRect(&rDest, &rListview, &rcHeader);
8202 InvalidateRect(hwnd, &rDest, TRUE);
8204 else
8205 InvalidateRect(hwnd, NULL, TRUE);
8209 return 0;
8212 /***
8213 * DESCRIPTION:
8214 * Performs horizontal scrolling.
8216 * PARAMETER(S):
8217 * [I] HWND : window handle
8218 * [I] INT : scroll code
8219 * [I] SHORT : current scroll position if scroll code is SB_THUMBPOSITION
8220 * or SB_THUMBTRACK.
8221 * [I] HWND : scrollbar control window handle
8223 * RETURN:
8224 * Zero
8226 static LRESULT LISTVIEW_HScroll(HWND hwnd, INT nScrollCode, SHORT nCurrentPos,
8227 HWND hScrollWnd)
8229 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8230 SCROLLINFO scrollInfo;
8232 TRACE("(hwnd=%x, nScrollCode=%d, nCurrentPos=%d, hScrollWnd=%x)\n",
8233 hwnd, nScrollCode, nCurrentPos, hScrollWnd);
8235 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8237 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
8238 scrollInfo.cbSize = sizeof(SCROLLINFO);
8239 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
8241 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
8243 INT nOldScrollPos = scrollInfo.nPos;
8245 switch (nScrollCode)
8247 case SB_LINELEFT:
8248 if (scrollInfo.nPos > scrollInfo.nMin)
8249 scrollInfo.nPos--;
8250 break;
8252 case SB_LINERIGHT:
8253 if (scrollInfo.nPos < scrollInfo.nMax)
8254 scrollInfo.nPos++;
8255 break;
8257 case SB_PAGELEFT:
8258 if (scrollInfo.nPos > scrollInfo.nMin)
8260 if (scrollInfo.nPos >= scrollInfo.nPage)
8261 scrollInfo.nPos -= scrollInfo.nPage;
8262 else
8263 scrollInfo.nPos = scrollInfo.nMin;
8265 break;
8267 case SB_PAGERIGHT:
8268 if (scrollInfo.nPos < scrollInfo.nMax)
8270 if (scrollInfo.nPos <= scrollInfo.nMax - scrollInfo.nPage)
8271 scrollInfo.nPos += scrollInfo.nPage;
8272 else
8273 scrollInfo.nPos = scrollInfo.nMax;
8275 break;
8277 case SB_THUMBPOSITION:
8278 case SB_THUMBTRACK:
8279 scrollInfo.nPos = nCurrentPos;
8281 if (scrollInfo.nPos > scrollInfo.nMax)
8282 scrollInfo.nPos=scrollInfo.nMax;
8284 if (scrollInfo.nPos < scrollInfo.nMin)
8285 scrollInfo.nPos=scrollInfo.nMin;
8286 break;
8289 if (nOldScrollPos != scrollInfo.nPos)
8291 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8292 scrollInfo.fMask = SIF_POS;
8293 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
8294 if(uView == LVS_REPORT)
8296 scrollInfo.fMask = SIF_POS;
8297 GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
8298 LISTVIEW_UpdateHeaderSize(hwnd, scrollInfo.nPos);
8300 InvalidateRect(hwnd, NULL, TRUE);
8304 return 0;
8307 static LRESULT LISTVIEW_MouseWheel(HWND hwnd, INT wheelDelta)
8309 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8310 INT gcWheelDelta = 0;
8311 UINT pulScrollLines = 3;
8312 SCROLLINFO scrollInfo;
8314 TRACE("(hwnd=%x, wheelDelta=%d)\n", hwnd, wheelDelta);
8316 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
8317 gcWheelDelta -= wheelDelta;
8319 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
8320 scrollInfo.cbSize = sizeof(SCROLLINFO);
8321 scrollInfo.fMask = SIF_POS | SIF_RANGE;
8323 switch(uView)
8325 case LVS_ICON:
8326 case LVS_SMALLICON:
8328 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
8329 * should be fixed in the future.
8331 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
8332 LISTVIEW_VScroll(hwnd, SB_THUMBPOSITION, scrollInfo.nPos + (gcWheelDelta < 0) ? 37 : -37, 0);
8333 break;
8335 case LVS_REPORT:
8336 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
8338 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
8340 int cLineScroll = min(LISTVIEW_GetCountPerColumn(hwnd), pulScrollLines);
8341 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
8342 LISTVIEW_VScroll(hwnd, SB_THUMBPOSITION, scrollInfo.nPos + cLineScroll, 0);
8345 break;
8347 case LVS_LIST:
8348 LISTVIEW_HScroll(hwnd, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
8349 break;
8351 return 0;
8354 /***
8355 * DESCRIPTION:
8356 * ???
8358 * PARAMETER(S):
8359 * [I] HWND : window handle
8360 * [I] INT : virtual key
8361 * [I] LONG : key data
8363 * RETURN:
8364 * Zero
8366 static LRESULT LISTVIEW_KeyDown(HWND hwnd, INT nVirtualKey, LONG lKeyData)
8368 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8369 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8370 INT nItem = -1;
8371 NMLVKEYDOWN nmKeyDown;
8373 TRACE("(hwnd=%x, nVirtualKey=%d, lKeyData=%ld)\n", hwnd, nVirtualKey, lKeyData);
8375 /* send LVN_KEYDOWN notification */
8376 nmKeyDown.wVKey = nVirtualKey;
8377 nmKeyDown.flags = 0;
8378 notify(hwnd, LVN_KEYDOWN, &nmKeyDown.hdr);
8380 switch (nVirtualKey)
8382 case VK_RETURN:
8383 if ((GETITEMCOUNT(infoPtr) > 0) && (infoPtr->nFocusedItem != -1))
8385 hdr_notify(hwnd, NM_RETURN); /* NM_RETURN notification */
8386 hdr_notify(hwnd, LVN_ITEMACTIVATE); /* LVN_ITEMACTIVATE notification */
8388 break;
8390 case VK_HOME:
8391 if (GETITEMCOUNT(infoPtr) > 0)
8392 nItem = 0;
8393 break;
8395 case VK_END:
8396 if (GETITEMCOUNT(infoPtr) > 0)
8397 nItem = GETITEMCOUNT(infoPtr) - 1;
8398 break;
8400 case VK_LEFT:
8401 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_TOLEFT);
8402 break;
8404 case VK_UP:
8405 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_ABOVE);
8406 break;
8408 case VK_RIGHT:
8409 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_TORIGHT);
8410 break;
8412 case VK_DOWN:
8413 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_BELOW);
8414 break;
8416 case VK_PRIOR:
8417 if (uView == LVS_REPORT)
8418 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(hwnd);
8419 else
8420 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(hwnd)
8421 * LISTVIEW_GetCountPerRow(hwnd);
8422 if(nItem < 0) nItem = 0;
8423 break;
8425 case VK_NEXT:
8426 if (uView == LVS_REPORT)
8427 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(hwnd);
8428 else
8429 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(hwnd)
8430 * LISTVIEW_GetCountPerRow(hwnd);
8431 if(nItem >= GETITEMCOUNT(infoPtr)) nItem = GETITEMCOUNT(infoPtr) - 1;
8432 break;
8435 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem))
8437 if (LISTVIEW_KeySelection(hwnd, nItem))
8438 UpdateWindow(hwnd); /* update client area */
8441 return 0;
8444 /***
8445 * DESCRIPTION:
8446 * Kills the focus.
8448 * PARAMETER(S):
8449 * [I] HWND : window handle
8451 * RETURN:
8452 * Zero
8454 static LRESULT LISTVIEW_KillFocus(HWND hwnd)
8456 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
8457 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8458 INT i,nTop,nBottom;
8460 TRACE("(hwnd=%x)\n", hwnd);
8462 /* send NM_KILLFOCUS notification */
8463 hdr_notify(hwnd, NM_KILLFOCUS);
8465 /* set window focus flag */
8466 infoPtr->bFocus = FALSE;
8468 /* NEED drawing optimization ; redraw the selected items */
8469 if (uView & LVS_REPORT)
8471 nTop = LISTVIEW_GetTopIndex(hwnd);
8472 nBottom = nTop +
8473 LISTVIEW_GetCountPerColumn(hwnd) + 1;
8475 else
8477 nTop = 0;
8478 nBottom = GETITEMCOUNT(infoPtr);
8480 for (i = nTop; i<nBottom; i++)
8482 if (LISTVIEW_IsSelected(hwnd,i))
8484 RECT rcItem;
8485 rcItem.left = LVIR_BOUNDS;
8486 LISTVIEW_GetItemRect(hwnd, i, &rcItem);
8487 InvalidateRect(hwnd, &rcItem, FALSE);
8491 return 0;
8494 /***
8495 * DESCRIPTION:
8496 * Processes double click messages (left mouse button).
8498 * PARAMETER(S):
8499 * [I] HWND : window handle
8500 * [I] WORD : key flag
8501 * [I] WORD : x coordinate
8502 * [I] WORD : y coordinate
8504 * RETURN:
8505 * Zero
8507 static LRESULT LISTVIEW_LButtonDblClk(HWND hwnd, WORD wKey, WORD wPosX,
8508 WORD wPosY)
8510 LVHITTESTINFO htInfo;
8511 NMLISTVIEW nmlv;
8513 TRACE("(hwnd=%x, key=%hu, X=%hu, Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8515 htInfo.pt.x = wPosX;
8516 htInfo.pt.y = wPosY;
8518 /* send NM_DBLCLK notification */
8519 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8520 if (LISTVIEW_HitTestItem(hwnd, &htInfo, TRUE) != -1)
8522 nmlv.iItem = htInfo.iItem;
8523 nmlv.iSubItem = htInfo.iSubItem;
8525 else
8527 nmlv.iItem = -1;
8528 nmlv.iSubItem = 0;
8530 nmlv.ptAction.x = wPosX;
8531 nmlv.ptAction.y = wPosY;
8532 listview_notify(hwnd, NM_DBLCLK, &nmlv);
8535 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
8536 if(nmlv.iItem != -1)
8537 hdr_notify(hwnd, LVN_ITEMACTIVATE);
8539 return 0;
8542 /***
8543 * DESCRIPTION:
8544 * Processes mouse down messages (left mouse button).
8546 * PARAMETER(S):
8547 * [I] HWND : window handle
8548 * [I] WORD : key flag
8549 * [I] WORD : x coordinate
8550 * [I] WORD : y coordinate
8552 * RETURN:
8553 * Zero
8555 static LRESULT LISTVIEW_LButtonDown(HWND hwnd, WORD wKey, WORD wPosX,
8556 WORD wPosY)
8558 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8559 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
8560 static BOOL bGroupSelect = TRUE;
8561 POINT ptPosition;
8562 INT nItem;
8564 TRACE("(hwnd=%x, key=%hu, X=%hu, Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8566 /* send NM_RELEASEDCAPTURE notification */
8567 hdr_notify(hwnd, NM_RELEASEDCAPTURE);
8569 if (infoPtr->bFocus == FALSE)
8570 SetFocus(hwnd);
8572 /* set left button down flag */
8573 infoPtr->bLButtonDown = TRUE;
8575 ptPosition.x = wPosX;
8576 ptPosition.y = wPosY;
8577 nItem = LISTVIEW_MouseSelection(hwnd, ptPosition);
8578 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
8580 if (lStyle & LVS_SINGLESEL)
8582 if ((ListView_GetItemState(hwnd, nItem, LVIS_SELECTED) & LVIS_SELECTED)
8583 && infoPtr->nEditLabelItem == -1)
8584 infoPtr->nEditLabelItem = nItem;
8585 else
8586 LISTVIEW_SetSelection(hwnd, nItem);
8588 else
8590 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
8592 if (bGroupSelect)
8593 LISTVIEW_AddGroupSelection(hwnd, nItem);
8594 else
8595 LISTVIEW_AddSelection(hwnd, nItem);
8597 else if (wKey & MK_CONTROL)
8599 bGroupSelect = LISTVIEW_ToggleSelection(hwnd, nItem);
8601 else if (wKey & MK_SHIFT)
8603 LISTVIEW_SetGroupSelection(hwnd, nItem);
8605 else
8607 BOOL was_selected =
8608 (ListView_GetItemState(hwnd, nItem, LVIS_SELECTED) & LVIS_SELECTED);
8610 /* set selection (clears other pre-existing selections) */
8611 LISTVIEW_SetSelection(hwnd, nItem);
8613 if (was_selected && infoPtr->nEditLabelItem == -1)
8614 infoPtr->nEditLabelItem = nItem;
8618 else
8620 /* remove all selections */
8621 LISTVIEW_RemoveAllSelections(hwnd);
8624 /* redraw if we could have possibly selected something */
8625 if(!GETITEMCOUNT(infoPtr)) InvalidateRect(hwnd, NULL, TRUE);
8627 return 0;
8630 /***
8631 * DESCRIPTION:
8632 * Processes mouse up messages (left mouse button).
8634 * PARAMETER(S):
8635 * [I] HWND : window handle
8636 * [I] WORD : key flag
8637 * [I] WORD : x coordinate
8638 * [I] WORD : y coordinate
8640 * RETURN:
8641 * Zero
8643 static LRESULT LISTVIEW_LButtonUp(HWND hwnd, WORD wKey, WORD wPosX,
8644 WORD wPosY)
8646 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8648 TRACE("(hwnd=%x, key=%hu, X=%hu, Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8650 if (infoPtr->bLButtonDown != FALSE)
8652 LVHITTESTINFO lvHitTestInfo;
8653 NMLISTVIEW nmlv;
8655 lvHitTestInfo.pt.x = wPosX;
8656 lvHitTestInfo.pt.y = wPosY;
8658 /* send NM_CLICK notification */
8659 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8660 if (LISTVIEW_HitTestItem(hwnd, &lvHitTestInfo, TRUE) != -1)
8662 nmlv.iItem = lvHitTestInfo.iItem;
8663 nmlv.iSubItem = lvHitTestInfo.iSubItem;
8665 else
8667 nmlv.iItem = -1;
8668 nmlv.iSubItem = 0;
8670 nmlv.ptAction.x = wPosX;
8671 nmlv.ptAction.y = wPosY;
8672 listview_notify(hwnd, NM_CLICK, &nmlv);
8674 /* set left button flag */
8675 infoPtr->bLButtonDown = FALSE;
8677 if(infoPtr->nEditLabelItem != -1)
8679 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem)
8680 LISTVIEW_EditLabelT(hwnd, lvHitTestInfo.iItem, TRUE);
8681 infoPtr->nEditLabelItem = -1;
8685 return 0;
8688 /***
8689 * DESCRIPTION:
8690 * Creates the listview control (called before WM_CREATE).
8692 * PARAMETER(S):
8693 * [I] HWND : window handle
8694 * [I] WPARAM : unhandled
8695 * [I] LPARAM : widow creation info
8697 * RETURN:
8698 * Zero
8700 static LRESULT LISTVIEW_NCCreate(HWND hwnd, WPARAM wParam, LPARAM lParam)
8702 LISTVIEW_INFO *infoPtr;
8704 TRACE("(hwnd=%x, wParam=%x, lParam=%lx)\n", hwnd, wParam, lParam);
8706 /* allocate memory for info structure */
8707 infoPtr = (LISTVIEW_INFO *)COMCTL32_Alloc(sizeof(LISTVIEW_INFO));
8708 if (infoPtr == NULL)
8710 ERR("could not allocate info memory!\n");
8711 return 0;
8714 SetWindowLongW(hwnd, 0, (LONG)infoPtr);
8715 if ((LISTVIEW_INFO *)GetWindowLongW(hwnd, 0) != infoPtr)
8717 ERR("pointer assignment error!\n");
8718 return 0;
8721 return DefWindowProcW(hwnd, WM_NCCREATE, wParam, lParam);
8724 /***
8725 * DESCRIPTION:
8726 * Destroys the listview control (called after WM_DESTROY).
8728 * PARAMETER(S):
8729 * [I] HWND : window handle
8731 * RETURN:
8732 * Zero
8734 static LRESULT LISTVIEW_NCDestroy(HWND hwnd)
8736 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8738 TRACE("(hwnd=%x)\n", hwnd);
8740 /* delete all items */
8741 LISTVIEW_DeleteAllItems(hwnd);
8743 /* destroy data structure */
8744 DPA_Destroy(infoPtr->hdpaItems);
8745 DPA_Destroy(infoPtr->hdpaSelectionRanges);
8747 /* destroy font */
8748 infoPtr->hFont = (HFONT)0;
8749 if (infoPtr->hDefaultFont)
8751 DeleteObject(infoPtr->hDefaultFont);
8754 /* free listview info pointer*/
8755 COMCTL32_Free(infoPtr);
8757 SetWindowLongW(hwnd, 0, 0);
8758 return 0;
8761 /***
8762 * DESCRIPTION:
8763 * Handles notifications from children.
8765 * PARAMETER(S):
8766 * [I] HWND : window handle
8767 * [I] INT : control identifier
8768 * [I] LPNMHDR : notification information
8770 * RETURN:
8771 * Zero
8773 static LRESULT LISTVIEW_Notify(HWND hwnd, INT nCtrlId, LPNMHDR lpnmh)
8775 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8777 TRACE("(hwnd=%x, nCtrlId=%d, lpnmh=%p)\n", hwnd, nCtrlId, lpnmh);
8779 if (lpnmh->hwndFrom == infoPtr->hwndHeader)
8781 /* handle notification from header control */
8782 if (lpnmh->code == HDN_ENDTRACKW)
8784 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8785 InvalidateRect(hwnd, NULL, TRUE);
8787 else if(lpnmh->code == HDN_ITEMCLICKW)
8789 /* Handle sorting by Header Column */
8790 NMLISTVIEW nmlv;
8792 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8793 nmlv.iItem = -1;
8794 nmlv.iSubItem = ((LPNMHEADERW)lpnmh)->iItem;
8795 listview_notify(hwnd, LVN_COLUMNCLICK, &nmlv);
8797 else if(lpnmh->code == NM_RELEASEDCAPTURE)
8799 /* Idealy this should be done in HDN_ENDTRACKA
8800 * but since SetItemBounds in Header.c is called after
8801 * the notification is sent, it is neccessary to handle the
8802 * update of the scroll bar here (Header.c works fine as it is,
8803 * no need to disturb it)
8805 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8806 LISTVIEW_UpdateScroll(hwnd);
8807 InvalidateRect(hwnd, NULL, TRUE);
8812 return 0;
8815 /***
8816 * DESCRIPTION:
8817 * Determines the type of structure to use.
8819 * PARAMETER(S):
8820 * [I] HWND : window handle of the sender
8821 * [I] HWND : listview window handle
8822 * [I] INT : command specifying the nature of the WM_NOTIFYFORMAT
8824 * RETURN:
8825 * Zero
8827 static LRESULT LISTVIEW_NotifyFormat(HWND hwndFrom, HWND hwnd, INT nCommand)
8829 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8831 TRACE("(hwndFrom=%x, hwnd=%x, nCommand=%d)\n", hwndFrom, hwnd, nCommand);
8833 if (nCommand == NF_REQUERY)
8834 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT,
8835 (WPARAM)hwnd, (LPARAM)NF_QUERY);
8836 return 0;
8839 /***
8840 * DESCRIPTION:
8841 * Paints/Repaints the listview control.
8843 * PARAMETER(S):
8844 * [I] HWND : window handle
8845 * [I] HDC : device context handle
8847 * RETURN:
8848 * Zero
8850 static LRESULT LISTVIEW_Paint(HWND hwnd, HDC hdc)
8852 PAINTSTRUCT ps;
8854 TRACE("(hwnd=%x, hdc=%x)\n", hwnd, hdc);
8856 if (hdc == 0)
8858 hdc = BeginPaint(hwnd, &ps);
8859 LISTVIEW_Refresh(hwnd, hdc);
8860 EndPaint(hwnd, &ps);
8862 else
8864 LISTVIEW_Refresh(hwnd, hdc);
8867 return 0;
8870 /***
8871 * DESCRIPTION:
8872 * Processes double click messages (right mouse button).
8874 * PARAMETER(S):
8875 * [I] HWND : window handle
8876 * [I] WORD : key flag
8877 * [I] WORD : x coordinate
8878 * [I] WORD : y coordinate
8880 * RETURN:
8881 * Zero
8883 static LRESULT LISTVIEW_RButtonDblClk(HWND hwnd, WORD wKey, WORD wPosX,
8884 WORD wPosY)
8886 TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8888 /* send NM_RELEASEDCAPTURE notification */
8889 hdr_notify(hwnd, NM_RELEASEDCAPTURE);
8891 /* send NM_RDBLCLK notification */
8892 hdr_notify(hwnd, NM_RDBLCLK);
8894 return 0;
8897 /***
8898 * DESCRIPTION:
8899 * Processes mouse down messages (right mouse button).
8901 * PARAMETER(S):
8902 * [I] HWND : window handle
8903 * [I] WORD : key flag
8904 * [I] WORD : x coordinate
8905 * [I] WORD : y coordinate
8907 * RETURN:
8908 * Zero
8910 static LRESULT LISTVIEW_RButtonDown(HWND hwnd, WORD wKey, WORD wPosX,
8911 WORD wPosY)
8913 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8914 POINT ptPosition;
8915 INT nItem;
8916 NMLISTVIEW nmlv;
8917 LVHITTESTINFO lvHitTestInfo;
8919 TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8921 /* send NM_RELEASEDCAPTURE notification */
8922 hdr_notify(hwnd, NM_RELEASEDCAPTURE);
8924 /* make sure the listview control window has the focus */
8925 if (infoPtr->bFocus == FALSE)
8926 SetFocus(hwnd);
8928 /* set right button down flag */
8929 infoPtr->bRButtonDown = TRUE;
8931 /* determine the index of the selected item */
8932 ptPosition.x = wPosX;
8933 ptPosition.y = wPosY;
8934 nItem = LISTVIEW_MouseSelection(hwnd, ptPosition);
8935 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
8937 LISTVIEW_SetItemFocus(hwnd,nItem);
8938 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
8939 !LISTVIEW_IsSelected(hwnd,nItem))
8940 LISTVIEW_SetSelection(hwnd, nItem);
8942 else
8944 LISTVIEW_RemoveAllSelections(hwnd);
8947 lvHitTestInfo.pt.x = wPosX;
8948 lvHitTestInfo.pt.y = wPosY;
8950 /* Send NM_RClICK notification */
8951 ZeroMemory(&nmlv, sizeof(nmlv));
8952 if (LISTVIEW_HitTestItem(hwnd, &lvHitTestInfo, TRUE) != -1)
8954 nmlv.iItem = lvHitTestInfo.iItem;
8955 nmlv.iSubItem = lvHitTestInfo.iSubItem;
8957 else
8959 nmlv.iItem = -1;
8960 nmlv.iSubItem = 0;
8962 nmlv.ptAction.x = wPosX;
8963 nmlv.ptAction.y = wPosY;
8964 listview_notify(hwnd, NM_RCLICK, &nmlv);
8966 return 0;
8969 /***
8970 * DESCRIPTION:
8971 * Processes mouse up messages (right mouse button).
8973 * PARAMETER(S):
8974 * [I] HWND : window handle
8975 * [I] WORD : key flag
8976 * [I] WORD : x coordinate
8977 * [I] WORD : y coordinate
8979 * RETURN:
8980 * Zero
8982 static LRESULT LISTVIEW_RButtonUp(HWND hwnd, WORD wKey, WORD wPosX,
8983 WORD wPosY)
8985 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8987 TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8989 if (infoPtr->bRButtonDown)
8991 POINT pt;
8993 pt.x = wPosX;
8994 pt.y = wPosY;
8996 /* set button flag */
8997 infoPtr->bRButtonDown = FALSE;
8999 /* Change to screen coordinate for WM_CONTEXTMENU */
9000 ClientToScreen(hwnd, &pt);
9002 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
9003 SendMessageW( hwnd, WM_CONTEXTMENU, (WPARAM) hwnd, MAKELPARAM(pt.x, pt.y));
9006 return 0;
9009 /***
9010 * DESCRIPTION:
9011 * Sets the focus.
9013 * PARAMETER(S):
9014 * [I] HWND : window handle
9015 * [I] HWND : window handle of previously focused window
9017 * RETURN:
9018 * Zero
9020 static LRESULT LISTVIEW_SetFocus(HWND hwnd, HWND hwndLoseFocus)
9022 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9024 TRACE("(hwnd=%x, hwndLoseFocus=%x)\n", hwnd, hwndLoseFocus);
9026 /* send NM_SETFOCUS notification */
9027 hdr_notify(hwnd, NM_SETFOCUS);
9029 /* set window focus flag */
9030 infoPtr->bFocus = TRUE;
9032 UpdateWindow(hwnd);
9034 return 0;
9037 /***
9038 * DESCRIPTION:
9039 * Sets the font.
9041 * PARAMETER(S):
9042 * [I] HWND : window handle
9043 * [I] HFONT : font handle
9044 * [I] WORD : redraw flag
9046 * RETURN:
9047 * Zero
9049 static LRESULT LISTVIEW_SetFont(HWND hwnd, HFONT hFont, WORD fRedraw)
9051 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9052 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
9054 TRACE("(hwnd=%x,hfont=%x,redraw=%hu)\n", hwnd, hFont, fRedraw);
9056 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
9057 LISTVIEW_SaveTextMetrics(hwnd);
9059 if (uView == LVS_REPORT)
9061 /* set header font */
9062 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont,
9063 MAKELPARAM(fRedraw, 0));
9066 /* invalidate listview control client area */
9067 InvalidateRect(hwnd, NULL, TRUE);
9069 if (fRedraw != FALSE)
9070 UpdateWindow(hwnd);
9072 return 0;
9075 /***
9076 * DESCRIPTION:
9077 * Message handling for WM_SETREDRAW.
9078 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
9080 * PARAMETER(S):
9081 * [I] HWND : window handle
9082 * [I] bRedraw: state of redraw flag
9084 * RETURN:
9085 * DefWinProc return value
9087 static LRESULT LISTVIEW_SetRedraw(HWND hwnd, BOOL bRedraw)
9089 LRESULT lResult = DefWindowProcW(hwnd, WM_SETREDRAW, bRedraw, 0);
9090 if(bRedraw)
9091 RedrawWindow(hwnd, NULL, 0,
9092 RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ALLCHILDREN | RDW_ERASENOW);
9093 return lResult;
9096 /***
9097 * DESCRIPTION:
9098 * Resizes the listview control. This function processes WM_SIZE
9099 * messages. At this time, the width and height are not used.
9101 * PARAMETER(S):
9102 * [I] HWND : window handle
9103 * [I] WORD : new width
9104 * [I] WORD : new height
9106 * RETURN:
9107 * Zero
9109 static LRESULT LISTVIEW_Size(HWND hwnd, int Width, int Height)
9111 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
9112 UINT uView = lStyle & LVS_TYPEMASK;
9114 TRACE("(hwnd=%x, width=%d, height=%d)\n", hwnd, Width, Height);
9116 LISTVIEW_UpdateSize(hwnd);
9118 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
9120 if (lStyle & LVS_ALIGNLEFT)
9121 LISTVIEW_AlignLeft(hwnd);
9122 else
9123 LISTVIEW_AlignTop(hwnd);
9126 LISTVIEW_UpdateScroll(hwnd);
9128 /* invalidate client area + erase background */
9129 InvalidateRect(hwnd, NULL, TRUE);
9131 return 0;
9134 /***
9135 * DESCRIPTION:
9136 * Sets the size information.
9138 * PARAMETER(S):
9139 * [I] HWND : window handle
9141 * RETURN:
9142 * Zero
9144 static VOID LISTVIEW_UpdateSize(HWND hwnd)
9146 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9147 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
9148 UINT uView = lStyle & LVS_TYPEMASK;
9149 RECT rcList;
9151 TRACE("(hwnd=%x)\n", hwnd);
9153 GetClientRect(hwnd, &rcList);
9154 infoPtr->rcList.left = 0;
9155 infoPtr->rcList.right = max(rcList.right - rcList.left, 1);
9156 infoPtr->rcList.top = 0;
9157 infoPtr->rcList.bottom = max(rcList.bottom - rcList.top, 1);
9159 if (uView == LVS_LIST)
9161 if (lStyle & WS_HSCROLL)
9163 INT nHScrollHeight = GetSystemMetrics(SM_CYHSCROLL);
9164 if (infoPtr->rcList.bottom > nHScrollHeight)
9165 infoPtr->rcList.bottom -= nHScrollHeight;
9168 else if (uView == LVS_REPORT)
9170 HDLAYOUT hl;
9171 WINDOWPOS wp;
9173 hl.prc = &rcList;
9174 hl.pwpos = &wp;
9175 Header_Layout(infoPtr->hwndHeader, &hl);
9177 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
9179 if (!(LVS_NOCOLUMNHEADER & lStyle))
9180 infoPtr->rcList.top = max(wp.cy, 0);
9184 /***
9185 * DESCRIPTION:
9186 * Processes WM_STYLECHANGED messages.
9188 * PARAMETER(S):
9189 * [I] HWND : window handle
9190 * [I] WPARAM : window style type (normal or extended)
9191 * [I] LPSTYLESTRUCT : window style information
9193 * RETURN:
9194 * Zero
9196 static INT LISTVIEW_StyleChanged(HWND hwnd, WPARAM wStyleType,
9197 LPSTYLESTRUCT lpss)
9199 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9200 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
9201 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
9202 RECT rcList = infoPtr->rcList;
9204 TRACE("(hwnd=%x, styletype=%x, stylestruct=%p)\n",
9205 hwnd, wStyleType, lpss);
9207 if (wStyleType == GWL_STYLE)
9209 if (uOldView == LVS_REPORT)
9210 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
9212 if ((lpss->styleOld & WS_HSCROLL) != 0)
9213 ShowScrollBar(hwnd, SB_HORZ, FALSE);
9215 if ((lpss->styleOld & WS_VSCROLL) != 0)
9216 ShowScrollBar(hwnd, SB_VERT, FALSE);
9218 if (uNewView == LVS_ICON)
9220 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
9221 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
9222 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
9223 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
9224 if (lpss->styleNew & LVS_ALIGNLEFT)
9225 LISTVIEW_AlignLeft(hwnd);
9226 else
9227 LISTVIEW_AlignTop(hwnd);
9229 else if (uNewView == LVS_REPORT)
9231 HDLAYOUT hl;
9232 WINDOWPOS wp;
9234 hl.prc = &rcList;
9235 hl.pwpos = &wp;
9236 Header_Layout(infoPtr->hwndHeader, &hl);
9237 SetWindowPos(infoPtr->hwndHeader, hwnd, wp.x, wp.y, wp.cx, wp.cy,
9238 wp.flags);
9239 if (!(LVS_NOCOLUMNHEADER & lpss->styleNew))
9240 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
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);
9247 else if (uNewView == LVS_LIST)
9249 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
9250 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
9251 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
9252 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
9254 else
9256 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
9257 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
9258 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
9259 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
9260 if (lpss->styleNew & LVS_ALIGNLEFT)
9261 LISTVIEW_AlignLeft(hwnd);
9262 else
9263 LISTVIEW_AlignTop(hwnd);
9266 /* update the size of the client area */
9267 LISTVIEW_UpdateSize(hwnd);
9269 /* add scrollbars if needed */
9270 LISTVIEW_UpdateScroll(hwnd);
9272 /* invalidate client area + erase background */
9273 InvalidateRect(hwnd, NULL, TRUE);
9275 /* print the list of unsupported window styles */
9276 LISTVIEW_UnsupportedStyles(lpss->styleNew);
9279 /* If they change the view and we have an active edit control
9280 we will need to kill the control since the redraw will
9281 misplace the edit control.
9283 if (infoPtr->hwndEdit &&
9284 ((uNewView & (LVS_ICON|LVS_LIST|LVS_SMALLICON)) !=
9285 ((LVS_ICON|LVS_LIST|LVS_SMALLICON) & uOldView)))
9287 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
9290 return 0;
9293 /***
9294 * DESCRIPTION:
9295 * Window procedure of the listview control.
9298 static LRESULT WINAPI LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam,
9299 LPARAM lParam)
9301 TRACE("(hwnd=%x uMsg=%x wParam=%x lParam=%lx)\n", hwnd, uMsg, wParam, lParam);
9302 if (!GetWindowLongW(hwnd, 0) && (uMsg != WM_NCCREATE))
9303 return DefWindowProcW( hwnd, uMsg, wParam, lParam );
9304 switch (uMsg)
9306 case LVM_APPROXIMATEVIEWRECT:
9307 return LISTVIEW_ApproximateViewRect(hwnd, (INT)wParam,
9308 LOWORD(lParam), HIWORD(lParam));
9309 case LVM_ARRANGE:
9310 return LISTVIEW_Arrange(hwnd, (INT)wParam);
9312 /* case LVM_CREATEDRAGIMAGE: */
9314 case LVM_DELETEALLITEMS:
9315 return LISTVIEW_DeleteAllItems(hwnd);
9317 case LVM_DELETECOLUMN:
9318 return LISTVIEW_DeleteColumn(hwnd, (INT)wParam);
9320 case LVM_DELETEITEM:
9321 return LISTVIEW_DeleteItem(hwnd, (INT)wParam);
9323 case LVM_EDITLABELW:
9324 return LISTVIEW_EditLabelT(hwnd, (INT)wParam, TRUE);
9326 case LVM_EDITLABELA:
9327 return LISTVIEW_EditLabelT(hwnd, (INT)wParam, FALSE);
9329 case LVM_ENSUREVISIBLE:
9330 return LISTVIEW_EnsureVisible(hwnd, (INT)wParam, (BOOL)lParam);
9332 case LVM_FINDITEMW:
9333 return LISTVIEW_FindItemW(hwnd, (INT)wParam, (LPLVFINDINFOW)lParam);
9335 case LVM_FINDITEMA:
9336 return LISTVIEW_FindItemA(hwnd, (INT)wParam, (LPLVFINDINFOA)lParam);
9338 case LVM_GETBKCOLOR:
9339 return LISTVIEW_GetBkColor(hwnd);
9341 /* case LVM_GETBKIMAGE: */
9343 case LVM_GETCALLBACKMASK:
9344 return LISTVIEW_GetCallbackMask(hwnd);
9346 case LVM_GETCOLUMNA:
9347 return LISTVIEW_GetColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9349 case LVM_GETCOLUMNW:
9350 return LISTVIEW_GetColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9352 case LVM_GETCOLUMNORDERARRAY:
9353 return LISTVIEW_GetColumnOrderArray(hwnd, (INT)wParam, (LPINT)lParam);
9355 case LVM_GETCOLUMNWIDTH:
9356 return LISTVIEW_GetColumnWidth(hwnd, (INT)wParam);
9358 case LVM_GETCOUNTPERPAGE:
9359 return LISTVIEW_GetCountPerPage(hwnd);
9361 case LVM_GETEDITCONTROL:
9362 return LISTVIEW_GetEditControl(hwnd);
9364 case LVM_GETEXTENDEDLISTVIEWSTYLE:
9365 return LISTVIEW_GetExtendedListViewStyle(hwnd);
9367 case LVM_GETHEADER:
9368 return LISTVIEW_GetHeader(hwnd);
9370 case LVM_GETHOTCURSOR:
9371 FIXME("LVM_GETHOTCURSOR: unimplemented\n");
9372 return FALSE;
9374 case LVM_GETHOTITEM:
9375 return LISTVIEW_GetHotItem(hwnd);
9377 case LVM_GETHOVERTIME:
9378 return LISTVIEW_GetHoverTime(hwnd);
9380 case LVM_GETIMAGELIST:
9381 return LISTVIEW_GetImageList(hwnd, (INT)wParam);
9383 case LVM_GETISEARCHSTRINGA:
9384 case LVM_GETISEARCHSTRINGW:
9385 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
9386 return FALSE;
9388 case LVM_GETITEMA:
9389 return LISTVIEW_GetItemT(hwnd, (LPLVITEMW)lParam, FALSE, FALSE);
9391 case LVM_GETITEMW:
9392 return LISTVIEW_GetItemT(hwnd, (LPLVITEMW)lParam, FALSE, TRUE);
9394 case LVM_GETITEMCOUNT:
9395 return LISTVIEW_GetItemCount(hwnd);
9397 case LVM_GETITEMPOSITION:
9398 return LISTVIEW_GetItemPosition(hwnd, (INT)wParam, (LPPOINT)lParam);
9400 case LVM_GETITEMRECT:
9401 return LISTVIEW_GetItemRect(hwnd, (INT)wParam, (LPRECT)lParam);
9403 case LVM_GETITEMSPACING:
9404 return LISTVIEW_GetItemSpacing(hwnd, (BOOL)wParam);
9406 case LVM_GETITEMSTATE:
9407 return LISTVIEW_GetItemState(hwnd, (INT)wParam, (UINT)lParam);
9409 case LVM_GETITEMTEXTA:
9410 return LISTVIEW_GetItemTextT(hwnd, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9412 case LVM_GETITEMTEXTW:
9413 return LISTVIEW_GetItemTextT(hwnd, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9415 case LVM_GETNEXTITEM:
9416 return LISTVIEW_GetNextItem(hwnd, (INT)wParam, LOWORD(lParam));
9418 case LVM_GETNUMBEROFWORKAREAS:
9419 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
9420 return 1;
9422 case LVM_GETORIGIN:
9423 return LISTVIEW_GetOrigin(hwnd, (LPPOINT)lParam);
9425 case LVM_GETSELECTEDCOUNT:
9426 return LISTVIEW_GetSelectedCount(hwnd);
9428 case LVM_GETSELECTIONMARK:
9429 return LISTVIEW_GetSelectionMark(hwnd);
9431 case LVM_GETSTRINGWIDTHA:
9432 return LISTVIEW_GetStringWidthT(hwnd, (LPCWSTR)lParam, FALSE);
9434 case LVM_GETSTRINGWIDTHW:
9435 return LISTVIEW_GetStringWidthT(hwnd, (LPCWSTR)lParam, TRUE);
9437 case LVM_GETSUBITEMRECT:
9438 FIXME("LVM_GETSUBITEMRECT: unimplemented\n");
9439 return FALSE;
9441 case LVM_GETTEXTBKCOLOR:
9442 return LISTVIEW_GetTextBkColor(hwnd);
9444 case LVM_GETTEXTCOLOR:
9445 return LISTVIEW_GetTextColor(hwnd);
9447 case LVM_GETTOOLTIPS:
9448 FIXME("LVM_GETTOOLTIPS: unimplemented\n");
9449 return FALSE;
9451 case LVM_GETTOPINDEX:
9452 return LISTVIEW_GetTopIndex(hwnd);
9454 /*case LVM_GETUNICODEFORMAT:
9455 FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
9456 return FALSE;*/
9458 case LVM_GETVIEWRECT:
9459 return LISTVIEW_GetViewRect(hwnd, (LPRECT)lParam);
9461 case LVM_GETWORKAREAS:
9462 FIXME("LVM_GETWORKAREAS: unimplemented\n");
9463 return FALSE;
9465 case LVM_HITTEST:
9466 return LISTVIEW_HitTest(hwnd, (LPLVHITTESTINFO)lParam);
9468 case LVM_INSERTCOLUMNA:
9469 return LISTVIEW_InsertColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9471 case LVM_INSERTCOLUMNW:
9472 return LISTVIEW_InsertColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9474 case LVM_INSERTITEMA:
9475 return LISTVIEW_InsertItemT(hwnd, (LPLVITEMW)lParam, FALSE);
9477 case LVM_INSERTITEMW:
9478 return LISTVIEW_InsertItemT(hwnd, (LPLVITEMW)lParam, TRUE);
9480 case LVM_REDRAWITEMS:
9481 return LISTVIEW_RedrawItems(hwnd, (INT)wParam, (INT)lParam);
9483 /* case LVM_SCROLL: */
9484 /* return LISTVIEW_Scroll(hwnd, (INT)wParam, (INT)lParam); */
9486 case LVM_SETBKCOLOR:
9487 return LISTVIEW_SetBkColor(hwnd, (COLORREF)lParam);
9489 /* case LVM_SETBKIMAGE: */
9491 case LVM_SETCALLBACKMASK:
9492 return LISTVIEW_SetCallbackMask(hwnd, (UINT)wParam);
9494 case LVM_SETCOLUMNA:
9495 return LISTVIEW_SetColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9497 case LVM_SETCOLUMNW:
9498 return LISTVIEW_SetColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9500 case LVM_SETCOLUMNORDERARRAY:
9501 return LISTVIEW_SetColumnOrderArray(hwnd, (INT)wParam, (LPINT)lParam);
9503 case LVM_SETCOLUMNWIDTH:
9504 return LISTVIEW_SetColumnWidth(hwnd, (INT)wParam, SLOWORD(lParam));
9506 case LVM_SETEXTENDEDLISTVIEWSTYLE:
9507 return LISTVIEW_SetExtendedListViewStyle(hwnd, (DWORD)wParam, (DWORD)lParam);
9509 /* case LVM_SETHOTCURSOR: */
9511 case LVM_SETHOTITEM:
9512 return LISTVIEW_SetHotItem(hwnd, (INT)wParam);
9514 case LVM_SETHOVERTIME:
9515 return LISTVIEW_SetHoverTime(hwnd, (DWORD)wParam);
9517 case LVM_SETICONSPACING:
9518 return LISTVIEW_SetIconSpacing(hwnd, (DWORD)lParam);
9520 case LVM_SETIMAGELIST:
9521 return (LRESULT)LISTVIEW_SetImageList(hwnd, (INT)wParam, (HIMAGELIST)lParam);
9523 case LVM_SETITEMA:
9524 return LISTVIEW_SetItemT(hwnd, (LPLVITEMW)lParam, FALSE);
9526 case LVM_SETITEMW:
9527 return LISTVIEW_SetItemT(hwnd, (LPLVITEMW)lParam, TRUE);
9529 case LVM_SETITEMCOUNT:
9530 return LISTVIEW_SetItemCount(hwnd, (INT)wParam, (DWORD)lParam);
9532 case LVM_SETITEMPOSITION:
9533 return LISTVIEW_SetItemPosition(hwnd, (INT)wParam, (INT)LOWORD(lParam),
9534 (INT)HIWORD(lParam));
9536 case LVM_SETITEMPOSITION32:
9537 return LISTVIEW_SetItemPosition(hwnd, (INT)wParam, ((POINT*)lParam)->x,
9538 ((POINT*)lParam)->y);
9540 case LVM_SETITEMSTATE:
9541 return LISTVIEW_SetItemState(hwnd, (INT)wParam, (LPLVITEMW)lParam);
9543 case LVM_SETITEMTEXTA:
9544 return LISTVIEW_SetItemTextT(hwnd, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9546 case LVM_SETITEMTEXTW:
9547 return LISTVIEW_SetItemTextT(hwnd, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9549 case LVM_SETSELECTIONMARK:
9550 return LISTVIEW_SetSelectionMark(hwnd, (INT)lParam);
9552 case LVM_SETTEXTBKCOLOR:
9553 return LISTVIEW_SetTextBkColor(hwnd, (COLORREF)lParam);
9555 case LVM_SETTEXTCOLOR:
9556 return LISTVIEW_SetTextColor(hwnd, (COLORREF)lParam);
9558 /* case LVM_SETTOOLTIPS: */
9559 /* case LVM_SETUNICODEFORMAT: */
9560 /* case LVM_SETWORKAREAS: */
9562 case LVM_SORTITEMS:
9563 return LISTVIEW_SortItems(hwnd, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
9565 /* case LVM_SUBITEMHITTEST: */
9567 case LVM_UPDATE:
9568 return LISTVIEW_Update(hwnd, (INT)wParam);
9570 case WM_CHAR:
9571 return LISTVIEW_ProcessLetterKeys( hwnd, wParam, lParam );
9573 case WM_COMMAND:
9574 return LISTVIEW_Command(hwnd, wParam, lParam);
9576 case WM_CREATE:
9577 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
9579 case WM_ERASEBKGND:
9580 return LISTVIEW_EraseBackground(hwnd, wParam, lParam);
9582 case WM_GETDLGCODE:
9583 return DLGC_WANTCHARS | DLGC_WANTARROWS;
9585 case WM_GETFONT:
9586 return LISTVIEW_GetFont(hwnd);
9588 case WM_HSCROLL:
9589 return LISTVIEW_HScroll(hwnd, (INT)LOWORD(wParam),
9590 (INT)HIWORD(wParam), (HWND)lParam);
9592 case WM_KEYDOWN:
9593 return LISTVIEW_KeyDown(hwnd, (INT)wParam, (LONG)lParam);
9595 case WM_KILLFOCUS:
9596 return LISTVIEW_KillFocus(hwnd);
9598 case WM_LBUTTONDBLCLK:
9599 return LISTVIEW_LButtonDblClk(hwnd, (WORD)wParam, LOWORD(lParam),
9600 HIWORD(lParam));
9602 case WM_LBUTTONDOWN:
9603 return LISTVIEW_LButtonDown(hwnd, (WORD)wParam, LOWORD(lParam),
9604 HIWORD(lParam));
9605 case WM_LBUTTONUP:
9606 return LISTVIEW_LButtonUp(hwnd, (WORD)wParam, LOWORD(lParam),
9607 HIWORD(lParam));
9608 case WM_MOUSEMOVE:
9609 return LISTVIEW_MouseMove (hwnd, wParam, lParam);
9611 case WM_MOUSEHOVER:
9612 return LISTVIEW_MouseHover(hwnd, wParam, lParam);
9614 case WM_NCCREATE:
9615 return LISTVIEW_NCCreate(hwnd, wParam, lParam);
9617 case WM_NCDESTROY:
9618 return LISTVIEW_NCDestroy(hwnd);
9620 case WM_NOTIFY:
9621 return LISTVIEW_Notify(hwnd, (INT)wParam, (LPNMHDR)lParam);
9623 case WM_NOTIFYFORMAT:
9624 return LISTVIEW_NotifyFormat(hwnd, (HWND)wParam, (INT)lParam);
9626 case WM_PAINT:
9627 return LISTVIEW_Paint(hwnd, (HDC)wParam);
9629 case WM_RBUTTONDBLCLK:
9630 return LISTVIEW_RButtonDblClk(hwnd, (WORD)wParam, LOWORD(lParam),
9631 HIWORD(lParam));
9633 case WM_RBUTTONDOWN:
9634 return LISTVIEW_RButtonDown(hwnd, (WORD)wParam, LOWORD(lParam),
9635 HIWORD(lParam));
9637 case WM_RBUTTONUP:
9638 return LISTVIEW_RButtonUp(hwnd, (WORD)wParam, LOWORD(lParam),
9639 HIWORD(lParam));
9641 case WM_SETFOCUS:
9642 return LISTVIEW_SetFocus(hwnd, (HWND)wParam);
9644 case WM_SETFONT:
9645 return LISTVIEW_SetFont(hwnd, (HFONT)wParam, (WORD)lParam);
9647 case WM_SETREDRAW:
9648 return LISTVIEW_SetRedraw(hwnd, (BOOL)wParam);
9650 case WM_SIZE:
9651 return LISTVIEW_Size(hwnd, (int)SLOWORD(lParam), (int)SHIWORD(lParam));
9653 case WM_STYLECHANGED:
9654 return LISTVIEW_StyleChanged(hwnd, wParam, (LPSTYLESTRUCT)lParam);
9656 /* case WM_TIMER: */
9658 case WM_VSCROLL:
9659 return LISTVIEW_VScroll(hwnd, (INT)LOWORD(wParam),
9660 (INT)HIWORD(wParam), (HWND)lParam);
9662 case WM_MOUSEWHEEL:
9663 if (wParam & (MK_SHIFT | MK_CONTROL))
9664 return DefWindowProcW( hwnd, uMsg, wParam, lParam );
9665 return LISTVIEW_MouseWheel(hwnd, (short int)HIWORD(wParam));/* case WM_WINDOWPOSCHANGED: */
9667 /* case WM_WININICHANGE: */
9669 default:
9670 if (uMsg >= WM_USER)
9672 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam,
9673 lParam);
9676 /* call default window procedure */
9677 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9680 return 0;
9683 /***
9684 * DESCRIPTION:
9685 * Registers the window class.
9687 * PARAMETER(S):
9688 * None
9690 * RETURN:
9691 * None
9693 VOID LISTVIEW_Register(void)
9695 WNDCLASSW wndClass;
9697 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
9698 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
9699 wndClass.lpfnWndProc = (WNDPROC)LISTVIEW_WindowProc;
9700 wndClass.cbClsExtra = 0;
9701 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
9702 wndClass.hCursor = LoadCursorW(0, IDC_ARROWW);
9703 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
9704 wndClass.lpszClassName = WC_LISTVIEWW;
9705 RegisterClassW(&wndClass);
9708 /***
9709 * DESCRIPTION:
9710 * Unregisters the window class.
9712 * PARAMETER(S):
9713 * None
9715 * RETURN:
9716 * None
9718 VOID LISTVIEW_Unregister(void)
9720 UnregisterClassW(WC_LISTVIEWW, (HINSTANCE)NULL);
9723 /***
9724 * DESCRIPTION:
9725 * Handle any WM_COMMAND messages
9727 * PARAMETER(S):
9729 * RETURN:
9731 static LRESULT LISTVIEW_Command(HWND hwnd, WPARAM wParam, LPARAM lParam)
9733 switch (HIWORD(wParam))
9735 case EN_UPDATE:
9738 * Adjust the edit window size
9740 WCHAR buffer[1024];
9741 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9742 HDC hdc = GetDC(infoPtr->hwndEdit);
9743 HFONT hFont, hOldFont = 0;
9744 RECT rect;
9745 SIZE sz;
9746 int len;
9748 len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
9749 GetWindowRect(infoPtr->hwndEdit, &rect);
9751 /* Select font to get the right dimension of the string */
9752 hFont = SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
9753 if(hFont != 0)
9755 hOldFont = SelectObject(hdc, hFont);
9758 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
9760 TEXTMETRICW textMetric;
9762 /* Add Extra spacing for the next character */
9763 GetTextMetricsW(hdc, &textMetric);
9764 sz.cx += (textMetric.tmMaxCharWidth * 2);
9766 SetWindowPos (
9767 infoPtr->hwndEdit,
9768 HWND_TOP,
9771 sz.cx,
9772 rect.bottom - rect.top,
9773 SWP_DRAWFRAME|SWP_NOMOVE);
9775 if(hFont != 0)
9776 SelectObject(hdc, hOldFont);
9778 ReleaseDC(hwnd, hdc);
9780 break;
9783 default:
9784 return SendMessageW (GetParent (hwnd), WM_COMMAND, wParam, lParam);
9787 return 0;
9791 /***
9792 * DESCRIPTION:
9793 * Subclassed edit control windproc function
9795 * PARAMETER(S):
9797 * RETURN:
9799 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg,
9800 WPARAM wParam, LPARAM lParam, BOOL isW)
9802 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(GetParent(hwnd), 0);
9803 EDITLABEL_ITEM *einfo = infoPtr->pedititem;
9804 static BOOL bIgnoreKillFocus = FALSE;
9805 BOOL cancel = FALSE;
9807 TRACE("(hwnd=%x, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
9808 hwnd, uMsg, wParam, lParam, isW);
9810 switch (uMsg)
9812 case WM_GETDLGCODE:
9813 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
9815 case WM_KILLFOCUS:
9816 if(bIgnoreKillFocus) return TRUE;
9817 break;
9819 case WM_DESTROY:
9821 WNDPROC editProc = einfo->EditWndProc;
9822 SetWindowLongW(hwnd, GWL_WNDPROC, (LONG)editProc);
9823 COMCTL32_Free(einfo);
9824 infoPtr->pedititem = NULL;
9825 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
9828 case WM_KEYDOWN:
9829 if (VK_ESCAPE == (INT)wParam)
9831 cancel = TRUE;
9832 break;
9834 else if (VK_RETURN == (INT)wParam)
9835 break;
9837 default:
9838 return CallWindowProcT(einfo->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
9841 if (einfo->EditLblCb)
9843 LPWSTR buffer = NULL;
9845 if (!cancel)
9847 DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
9849 if (len)
9851 if ( (buffer = COMCTL32_Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
9853 if (isW) GetWindowTextW(hwnd, buffer, len+1);
9854 else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
9858 /* Processing LVN_ENDLABELEDIT message could kill the focus */
9859 /* eg. Using a messagebox */
9860 bIgnoreKillFocus = TRUE;
9861 einfo->EditLblCb(GetParent(hwnd), buffer, einfo->param);
9863 if (buffer) COMCTL32_Free(buffer);
9865 einfo->EditLblCb = NULL;
9866 bIgnoreKillFocus = FALSE;
9869 SendMessageW(hwnd, WM_CLOSE, 0, 0);
9870 return TRUE;
9873 /***
9874 * DESCRIPTION:
9875 * Subclassed edit control windproc function
9877 * PARAMETER(S):
9879 * RETURN:
9881 LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9883 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
9886 /***
9887 * DESCRIPTION:
9888 * Subclassed edit control windproc function
9890 * PARAMETER(S):
9892 * RETURN:
9894 LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9896 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
9899 /***
9900 * DESCRIPTION:
9901 * Creates a subclassed edit cotrol
9903 * PARAMETER(S):
9905 * RETURN:
9907 HWND CreateEditLabelT(LPCWSTR text, DWORD style, INT x, INT y,
9908 INT width, INT height, HWND parent, HINSTANCE hinst,
9909 EditlblCallbackW EditLblCb, DWORD param, BOOL isW)
9911 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(parent, 0);
9912 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
9913 HWND hedit;
9914 SIZE sz;
9915 HDC hdc;
9916 HDC hOldFont=0;
9917 TEXTMETRICW textMetric;
9919 TRACE("(text=%s, ..., isW=%d)\n", debugstr_t(text, isW), isW);
9921 if (NULL == (infoPtr->pedititem = COMCTL32_Alloc(sizeof(EDITLABEL_ITEM))))
9922 return 0;
9924 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|WS_BORDER;
9925 hdc = GetDC(parent);
9927 /* Select the font to get appropriate metric dimensions */
9928 if(infoPtr->hFont != 0)
9929 hOldFont = SelectObject(hdc, infoPtr->hFont);
9931 /*Get String Lenght in pixels */
9932 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
9934 /*Add Extra spacing for the next character */
9935 GetTextMetricsW(hdc, &textMetric);
9936 sz.cx += (textMetric.tmMaxCharWidth * 2);
9938 if(infoPtr->hFont != 0)
9939 SelectObject(hdc, hOldFont);
9941 ReleaseDC(parent, hdc);
9942 if (isW)
9943 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, parent, 0, hinst, 0);
9944 else
9945 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, parent, 0, hinst, 0);
9947 if (!hedit)
9949 COMCTL32_Free(infoPtr->pedititem);
9950 return 0;
9953 infoPtr->pedititem->param = param;
9954 infoPtr->pedititem->EditLblCb = EditLblCb;
9955 infoPtr->pedititem->EditWndProc = (WNDPROC)
9956 (isW ? SetWindowLongW(hedit, GWL_WNDPROC, (LONG)EditLblWndProcW) :
9957 SetWindowLongA(hedit, GWL_WNDPROC, (LONG)EditLblWndProcA) );
9959 SendMessageW(hedit, WM_SETFONT, infoPtr->hFont, FALSE);
9961 return hedit;