Changed DllOverrides so we use builtin rpcrt4, ole32, oleaut32.
[wine/testsucceed.git] / dlls / comctl32 / listview.c
blob88fa902222f23409cc94693432da9119718907b3
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".
63 #include "config.h"
64 #include "wine/port.h"
66 #include <ctype.h>
67 #include <string.h>
68 #include <stdlib.h>
69 #include <stdio.h>
71 #include "winbase.h"
72 #include "winnt.h"
73 #include "heap.h"
74 #include "commctrl.h"
75 #include "wine/debug.h"
77 WINE_DEFAULT_DEBUG_CHANNEL(listview);
79 /* Some definitions for inline edit control */
80 typedef BOOL (*EditlblCallbackW)(HWND, LPWSTR, DWORD);
81 typedef BOOL (*EditlblCallbackA)(HWND, LPWSTR, DWORD);
83 typedef struct tagLV_INTHIT
85 LVHITTESTINFO ht;
86 DWORD distance; /* distance to closest item */
87 INT iDistItem; /* item number that is closest */
88 } LV_INTHIT, *LPLV_INTHIT;
91 typedef struct tagEDITLABEL_ITEM
93 WNDPROC EditWndProc;
94 DWORD param;
95 EditlblCallbackW EditLblCb;
96 } EDITLABEL_ITEM;
98 typedef struct tagLISTVIEW_SUBITEM
100 LPWSTR pszText;
101 INT iImage;
102 INT iSubItem;
103 } LISTVIEW_SUBITEM;
105 typedef struct tagLISTVIEW_ITEM
107 UINT state;
108 LPWSTR pszText;
109 INT iImage;
110 LPARAM lParam;
111 INT iIndent;
112 POINT ptPosition;
114 } LISTVIEW_ITEM;
116 typedef struct tagLISTVIEW_SELECTION
118 DWORD lower;
119 DWORD upper;
120 } LISTVIEW_SELECTION;
122 typedef struct tagLISTVIEW_INFO
124 HWND hwndSelf;
125 COLORREF clrBk;
126 COLORREF clrText;
127 COLORREF clrTextBk;
128 HIMAGELIST himlNormal;
129 HIMAGELIST himlSmall;
130 HIMAGELIST himlState;
131 BOOL bLButtonDown;
132 BOOL bRButtonDown;
133 INT nFocusedItem;
134 HDPA hdpaSelectionRanges;
135 INT nItemHeight;
136 INT nItemWidth;
137 INT nSelectionMark;
138 INT nHotItem;
139 SHORT notifyFormat;
140 RECT rcList;
141 RECT rcView;
142 SIZE iconSize;
143 SIZE iconSpacing;
144 UINT uCallbackMask;
145 HWND hwndHeader;
146 HFONT hDefaultFont;
147 HFONT hFont;
148 INT ntmHeight; /* from GetTextMetrics from above font */
149 INT ntmAveCharWidth; /* from GetTextMetrics from above font */
150 BOOL bFocus;
151 DWORD dwExStyle; /* extended listview style */
152 HDPA hdpaItems;
153 PFNLVCOMPARE pfnCompare;
154 LPARAM lParamSort;
155 HWND hwndEdit;
156 INT nEditLabelItem;
157 EDITLABEL_ITEM *pedititem;
158 DWORD dwHoverTime;
159 INT nColumnCount; /* the number of columns in this control */
161 DWORD lastKeyPressTimestamp; /* Added */
162 WPARAM charCode; /* Added */
163 INT nSearchParamLength; /* Added */
164 WCHAR szSearchParam[ MAX_PATH ]; /* Added */
165 BOOL bIsDrawing;
166 } LISTVIEW_INFO;
169 * constants
172 /* maximum size of a label */
173 #define DISP_TEXT_SIZE 512
175 /* padding for items in list and small icon display modes */
176 #define WIDTH_PADDING 12
178 /* padding for items in list, report and small icon display modes */
179 #define HEIGHT_PADDING 1
181 /* offset of items in report display mode */
182 #define REPORT_MARGINX 2
184 /* padding for icon in large icon display mode
185 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
186 * that HITTEST will see.
187 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
188 * ICON_TOP_PADDING - sum of the two above.
189 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
190 * LABEL_VERT_OFFSET - between bottom of text and end of box
192 #define ICON_TOP_PADDING_NOTHITABLE 2
193 #define ICON_TOP_PADDING_HITABLE 2
194 #define ICON_TOP_PADDING ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE
195 #define ICON_BOTTOM_PADDING 4
196 #define LABEL_VERT_OFFSET 10
198 /* default label width for items in list and small icon display modes */
199 #define DEFAULT_LABEL_WIDTH 40
201 /* default column width for items in list display mode */
202 #define DEFAULT_COLUMN_WIDTH 96
204 /* Increment size of the horizontal scroll bar */
205 #define LISTVIEW_SCROLL_DIV_SIZE 10
207 /* Padding betwen image and label */
208 #define IMAGE_PADDING 2
210 /* Padding behind the label */
211 #define TRAILING_PADDING 5
213 /* Border for the icon caption */
214 #define CAPTION_BORDER 2
216 * macros
218 /* retrieve the number of items in the listview */
219 #define GETITEMCOUNT(infoPtr) ((infoPtr)->hdpaItems->nItemCount)
220 #define HDM_INSERTITEMT(isW) ( (isW) ? HDM_INSERTITEMW : HDM_INSERTITEMA )
222 HWND CreateEditLabelT(LPCWSTR text, DWORD style, INT x, INT y,
223 INT width, INT height, HWND parent, HINSTANCE hinst,
224 EditlblCallbackW EditLblCb, DWORD param, BOOL isW);
227 * forward declarations
229 static LRESULT LISTVIEW_GetItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL internal, BOOL isW);
230 static INT LISTVIEW_SuperHitTestItem(HWND, LPLV_INTHIT, BOOL);
231 static INT LISTVIEW_HitTestItem(HWND, LPLVHITTESTINFO, BOOL);
232 static INT LISTVIEW_GetCountPerRow(HWND);
233 static INT LISTVIEW_GetCountPerColumn(HWND);
234 static VOID LISTVIEW_AlignLeft(HWND);
235 static VOID LISTVIEW_AlignTop(HWND);
236 static VOID LISTVIEW_AddGroupSelection(HWND, INT);
237 static VOID LISTVIEW_AddSelection(HWND, INT);
238 static BOOL LISTVIEW_AddSubItemT(HWND, LPLVITEMW, BOOL);
239 static INT LISTVIEW_FindInsertPosition(HDPA, INT);
240 static INT LISTVIEW_GetItemHeight(HWND);
241 static BOOL LISTVIEW_GetItemBoundBox(HWND, INT, LPRECT);
242 static BOOL LISTVIEW_GetItemPosition(HWND, INT, LPPOINT);
243 static LRESULT LISTVIEW_GetItemRect(HWND, INT, LPRECT);
244 static LRESULT LISTVIEW_GetSubItemRect(HWND, INT, INT, INT, LPRECT);
245 static INT LISTVIEW_GetItemWidth(HWND);
246 static INT LISTVIEW_GetLabelWidth(HWND, INT);
247 static LRESULT LISTVIEW_GetOrigin(HWND, LPPOINT);
248 static INT LISTVIEW_CalculateWidth(HWND hwnd, INT nItem);
249 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItem(HDPA, INT);
250 static LRESULT LISTVIEW_GetViewRect(HWND, LPRECT);
251 static BOOL LISTVIEW_InitItemT(HWND, LISTVIEW_ITEM *, LPLVITEMW, BOOL);
252 static BOOL LISTVIEW_InitSubItemT(HWND, LISTVIEW_SUBITEM *, LPLVITEMW, BOOL);
253 static LRESULT LISTVIEW_MouseSelection(HWND, POINT);
254 static BOOL LISTVIEW_RemoveColumn(HDPA, INT);
255 static BOOL LISTVIEW_RemoveSubItem(HDPA, INT);
256 static VOID LISTVIEW_SetGroupSelection(HWND, INT);
257 static BOOL LISTVIEW_SetItemT(HWND, LPLVITEMW, BOOL);
258 static BOOL LISTVIEW_SetItemFocus(HWND, INT);
259 static BOOL LISTVIEW_SetItemPosition(HWND, INT, LONG, LONG);
260 static VOID LISTVIEW_UpdateScroll(HWND);
261 static VOID LISTVIEW_SetSelection(HWND, INT);
262 static BOOL LISTVIEW_UpdateSize(HWND);
263 static BOOL LISTVIEW_SetSubItemT(HWND, LPLVITEMW, BOOL);
264 static LRESULT LISTVIEW_SetViewRect(HWND, LPRECT);
265 static BOOL LISTVIEW_ToggleSelection(HWND, INT);
266 static VOID LISTVIEW_UnsupportedStyles(LONG lStyle);
267 static HWND LISTVIEW_EditLabelT(HWND hwnd, INT nItem, BOOL isW);
268 static BOOL LISTVIEW_EndEditLabelW(HWND hwnd, LPWSTR pszText, DWORD nItem);
269 static BOOL LISTVIEW_EndEditLabelA(HWND hwnd, LPSTR pszText, DWORD nItem);
270 static LRESULT LISTVIEW_Command(HWND hwnd, WPARAM wParam, LPARAM lParam);
271 static LRESULT LISTVIEW_SortItems(HWND hwnd, PFNLVCOMPARE pfnCompare, LPARAM lParamSort);
272 static LRESULT LISTVIEW_GetStringWidthT(HWND hwnd, LPCWSTR lpszText, BOOL isW);
273 static INT LISTVIEW_ProcessLetterKeys( HWND hwnd, WPARAM charCode, LPARAM keyData );
274 static BOOL LISTVIEW_KeySelection(HWND hwnd, INT nItem);
275 static LRESULT LISTVIEW_GetItemState(HWND hwnd, INT nItem, UINT uMask);
276 static LRESULT LISTVIEW_SetItemState(HWND hwnd, INT nItem, LPLVITEMW lpLVItem);
277 static BOOL LISTVIEW_IsSelected(HWND hwnd, INT nItem);
278 static VOID LISTVIEW_RemoveSelectionRange(HWND hwnd, INT lItem, INT uItem);
279 static void LISTVIEW_FillBackground(HWND hwnd, HDC hdc, LPRECT rc);
280 static void ListView_UpdateLargeItemLabelRect (HWND hwnd, const LISTVIEW_INFO* infoPtr, int nItem, RECT *rect);
281 static LRESULT LISTVIEW_GetColumnT(HWND, INT, LPLVCOLUMNW, BOOL);
283 /******** Defines that LISTVIEW_ProcessLetterKeys uses ****************/
284 #define KEY_DELAY 450
286 #define COUNTOF(array) (sizeof(array)/sizeof(array[0]))
288 static inline BOOL is_textW(LPCWSTR text)
290 return text != NULL && text != LPSTR_TEXTCALLBACKW;
293 static inline BOOL is_textT(LPCWSTR text, BOOL isW)
295 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
296 return is_textW(text);
299 static inline int textlenT(LPCWSTR text, BOOL isW)
301 return !is_textT(text, isW) ? 0 :
302 isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
305 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
307 if (isDestW)
308 if (isSrcW) lstrcpynW(dest, src, max);
309 else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
310 else
311 if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
312 else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
315 static inline LPCSTR debugstr_t(LPCWSTR text, BOOL isW)
317 return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
320 static inline LPCSTR debugstr_tn(LPCWSTR text, BOOL isW, INT n)
322 return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
325 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
327 LPWSTR wstr = (LPWSTR)text;
329 TRACE("(text=%s, isW=%d)\n", debugstr_t(text, isW), isW);
330 if (!isW && text)
332 INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
333 wstr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
334 if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
336 TRACE(" wstr=%s\n", debugstr_w(wstr));
337 return wstr;
340 static inline void textfreeT(LPWSTR wstr, BOOL isW)
342 if (!isW && wstr) HeapFree(GetProcessHeap(), 0, wstr);
346 * dest is a pointer to a Unicode string
347 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
349 static inline BOOL textsetptrT(LPWSTR *dest, LPWSTR src, BOOL isW)
351 LPWSTR pszText = textdupTtoW(src, isW);
352 BOOL bResult = TRUE;
353 if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
354 bResult = Str_SetPtrW(dest, pszText);
355 textfreeT(pszText, isW);
356 return bResult;
359 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
360 WPARAM wParam, LPARAM lParam, BOOL isW)
362 if (isW)
363 return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
364 else
365 return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
368 static inline BOOL notify(HWND self, INT code, LPNMHDR pnmh)
370 pnmh->hwndFrom = self;
371 pnmh->idFrom = GetWindowLongW(self, GWL_ID);
372 pnmh->code = code;
373 return (BOOL)SendMessageW(GetParent(self), WM_NOTIFY,
374 (WPARAM)pnmh->idFrom, (LPARAM)pnmh);
377 static inline BOOL hdr_notify(HWND self, INT code)
379 NMHDR nmh;
380 return notify(self, code, &nmh);
383 static inline BOOL listview_notify(HWND self, INT code, LPNMLISTVIEW plvnm)
385 return notify(self, code, (LPNMHDR)plvnm);
388 static int tabNotification[] = {
389 LVN_BEGINLABELEDITW, LVN_BEGINLABELEDITA,
390 LVN_ENDLABELEDITW, LVN_ENDLABELEDITA,
391 LVN_GETDISPINFOW, LVN_GETDISPINFOA,
392 LVN_SETDISPINFOW, LVN_SETDISPINFOA,
393 LVN_ODFINDITEMW, LVN_ODFINDITEMA,
394 LVN_GETINFOTIPW, LVN_GETINFOTIPA,
398 static int get_ansi_notification(INT unicodeNotificationCode)
400 int *pTabNotif = tabNotification;
401 while (*pTabNotif && (unicodeNotificationCode != *pTabNotif++));
402 if (*pTabNotif) return *pTabNotif;
403 ERR("unknown notification %x\n", unicodeNotificationCode);
404 return unicodeNotificationCode;
408 Send notification. depends on dispinfoW having same
409 structure as dispinfoA.
410 self : listview handle
411 notificationCode : *Unicode* notification code
412 pdi : dispinfo structure (can be unicode or ansi)
413 isW : TRUE if dispinfo is Unicode
415 static BOOL dispinfo_notifyT(HWND self, INT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
417 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(self, 0);
418 BOOL bResult = FALSE;
419 BOOL convertToAnsi = FALSE, convertToUnicode = FALSE;
420 INT realNotifCode;
421 INT cchTempBufMax = 0, savCchTextMax = 0;
422 LPWSTR pszTempBuf = NULL, savPszText = NULL;
424 TRACE("(self=%x, code=%x, pdi=%p, isW=%d)\n", self, notificationCode, pdi, isW);
425 TRACE(" notifyFormat=%s\n",
426 infoPtr->notifyFormat == NFR_UNICODE ? "NFR_UNICODE" :
427 infoPtr->notifyFormat == NFR_ANSI ? "NFR_ANSI" : "(not set)");
428 if (infoPtr->notifyFormat == NFR_ANSI)
429 realNotifCode = get_ansi_notification(notificationCode);
430 else
431 realNotifCode = notificationCode;
433 if (is_textT(pdi->item.pszText, isW))
435 if (isW && infoPtr->notifyFormat == NFR_ANSI)
436 convertToAnsi = TRUE;
437 if (!isW && infoPtr->notifyFormat == NFR_UNICODE)
438 convertToUnicode = TRUE;
441 if (convertToAnsi || convertToUnicode)
443 TRACE(" we have to convert the text to the correct format\n");
444 if (notificationCode != LVN_GETDISPINFOW)
445 { /* length of existing text */
446 cchTempBufMax = convertToUnicode ?
447 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0):
448 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
450 else
451 cchTempBufMax = pdi->item.cchTextMax;
453 pszTempBuf = HeapAlloc(GetProcessHeap(), 0,
454 (convertToUnicode ? sizeof(WCHAR) : sizeof(CHAR)) * cchTempBufMax);
455 if (!pszTempBuf) return FALSE;
456 if (convertToUnicode)
457 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1,
458 pszTempBuf, cchTempBufMax);
459 else
460 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) pszTempBuf,
461 cchTempBufMax, NULL, NULL);
462 TRACE(" text=%s\n", debugstr_t(pszTempBuf, convertToUnicode));
463 savCchTextMax = pdi->item.cchTextMax;
464 savPszText = pdi->item.pszText;
465 pdi->item.pszText = pszTempBuf;
466 pdi->item.cchTextMax = cchTempBufMax;
469 bResult = notify(self, realNotifCode, (LPNMHDR)pdi);
471 if (convertToUnicode || convertToAnsi)
472 { /* convert back result */
473 TRACE(" returned text=%s\n", debugstr_t(pdi->item.pszText, convertToUnicode));
474 if (convertToUnicode) /* note : pointer can be changed by app ! */
475 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) savPszText,
476 savCchTextMax, NULL, NULL);
477 else
478 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
479 savPszText, savCchTextMax);
480 pdi->item.pszText = savPszText; /* restores our buffer */
481 pdi->item.cchTextMax = savCchTextMax;
482 HeapFree(GetProcessHeap(), 0, pszTempBuf);
484 return bResult;
487 static inline LRESULT LISTVIEW_GetItemW(HWND hwnd, LPLVITEMW lpLVItem, BOOL internal)
489 return LISTVIEW_GetItemT(hwnd, lpLVItem, internal, TRUE);
492 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
494 int res;
496 n = min(min(n, strlenW(s1)), strlenW(s2));
497 res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
498 return res ? res - 2 : res;
501 static char* debuglvitem_t(LPLVITEMW lpLVItem, BOOL isW)
503 static int index = 0;
504 static char buffers[20][256];
505 char* buf = buffers[index++ % 20];
506 if (lpLVItem == NULL) return "(null)";
507 snprintf(buf, 256, "{mask=%x, iItem=%d, iSubItem=%d, state=%x, stateMask=%x,"
508 " pszText=%s, cchTextMax=%d, iImage=%d, lParam=%lx, iIndent=%d}",
509 lpLVItem->mask, lpLVItem->iItem, lpLVItem->iSubItem,
510 lpLVItem->state, lpLVItem->stateMask,
511 lpLVItem->pszText == LPSTR_TEXTCALLBACKW ? "(callback)" :
512 debugstr_tn(lpLVItem->pszText, isW, 80),
513 lpLVItem->cchTextMax, lpLVItem->iImage, lpLVItem->lParam,
514 lpLVItem->iIndent);
515 return buf;
518 static char* debuglvcolumn_t(LPLVCOLUMNW lpColumn, BOOL isW)
520 static int index = 0;
521 static char buffers[20][256];
522 char* buf = buffers[index++ % 20];
523 if (lpColumn == NULL) return "(null)";
524 snprintf(buf, 256, "{mask=%x, fmt=%x, cx=%d,"
525 " pszText=%s, cchTextMax=%d, iSubItem=%d}",
526 lpColumn->mask, lpColumn->fmt, lpColumn->cx,
527 lpColumn->mask & LVCF_TEXT ? lpColumn->pszText == LPSTR_TEXTCALLBACKW ? "(callback)" :
528 debugstr_tn(lpColumn->pszText, isW, 80): "",
529 lpColumn->mask & LVCF_TEXT ? lpColumn->cchTextMax: 0, lpColumn->iSubItem);
530 return buf;
533 static void LISTVIEW_DumpListview(LISTVIEW_INFO *iP, INT line)
535 DWORD dwStyle = GetWindowLongW (iP->hwndSelf, GWL_STYLE);
536 TRACE("listview %08x at line %d, clrBk=0x%06lx, clrText=0x%06lx, clrTextBk=0x%06lx, ItemHeight=%d, ItemWidth=%d, Style=0x%08lx\n",
537 iP->hwndSelf, line, iP->clrBk, iP->clrText, iP->clrTextBk,
538 iP->nItemHeight, iP->nItemWidth, dwStyle);
539 TRACE("listview %08x at line %d, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08lx\n",
540 iP->hwndSelf, line, iP->himlNormal, iP->himlSmall, iP->himlState,
541 iP->nFocusedItem, iP->nHotItem, iP->dwExStyle);
544 static BOOL
545 LISTVIEW_SendCustomDrawNotify (HWND hwnd, DWORD dwDrawStage, HDC hdc,
546 RECT rc)
548 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
549 NMLVCUSTOMDRAW nmcdhdr;
550 LPNMCUSTOMDRAW nmcd;
552 TRACE("(hwnd=%x, dwDrawStage=%lx, hdc=%x, rc=?)\n", hwnd, dwDrawStage, hdc);
554 nmcd= & nmcdhdr.nmcd;
555 nmcd->hdr.hwndFrom = hwnd;
556 nmcd->hdr.idFrom = GetWindowLongW( hwnd, GWL_ID);
557 nmcd->hdr.code = NM_CUSTOMDRAW;
558 nmcd->dwDrawStage= dwDrawStage;
559 nmcd->hdc = hdc;
560 nmcd->rc.left = rc.left;
561 nmcd->rc.right = rc.right;
562 nmcd->rc.bottom = rc.bottom;
563 nmcd->rc.top = rc.top;
564 nmcd->dwItemSpec = 0;
565 nmcd->uItemState = 0;
566 nmcd->lItemlParam= 0;
567 nmcdhdr.clrText = infoPtr->clrText;
568 nmcdhdr.clrTextBk= infoPtr->clrBk;
570 return (BOOL)SendMessageW (GetParent (hwnd), WM_NOTIFY,
571 (WPARAM) GetWindowLongW( hwnd, GWL_ID), (LPARAM)&nmcdhdr);
574 static BOOL
575 LISTVIEW_SendCustomDrawItemNotify (HWND hwnd, HDC hdc,
576 UINT iItem, UINT iSubItem,
577 UINT uItemDrawState)
579 LISTVIEW_INFO *infoPtr;
580 NMLVCUSTOMDRAW nmcdhdr;
581 LPNMCUSTOMDRAW nmcd;
582 DWORD dwDrawStage,dwItemSpec;
583 UINT uItemState;
584 INT retval;
585 RECT itemRect;
586 LVITEMW item;
588 infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
590 ZeroMemory(&item,sizeof(item));
591 item.iItem = iItem;
592 item.mask = LVIF_PARAM;
593 ListView_GetItemW(hwnd,&item);
595 dwDrawStage=CDDS_ITEM | uItemDrawState;
596 dwItemSpec=iItem;
597 uItemState=0;
599 if (LISTVIEW_IsSelected(hwnd,iItem)) uItemState|=CDIS_SELECTED;
600 if (iItem==infoPtr->nFocusedItem) uItemState|=CDIS_FOCUS;
601 if (iItem==infoPtr->nHotItem) uItemState|=CDIS_HOT;
603 itemRect.left = LVIR_BOUNDS;
604 LISTVIEW_GetItemRect(hwnd, iItem, &itemRect);
606 nmcd= & nmcdhdr.nmcd;
607 nmcd->hdr.hwndFrom = hwnd;
608 nmcd->hdr.idFrom = GetWindowLongW( hwnd, GWL_ID);
609 nmcd->hdr.code = NM_CUSTOMDRAW;
610 nmcd->dwDrawStage= dwDrawStage;
611 nmcd->hdc = hdc;
612 nmcd->rc.left = itemRect.left;
613 nmcd->rc.right = itemRect.right;
614 nmcd->rc.bottom = itemRect.bottom;
615 nmcd->rc.top = itemRect.top;
616 nmcd->dwItemSpec = dwItemSpec;
617 nmcd->uItemState = uItemState;
618 nmcd->lItemlParam= item.lParam;
619 nmcdhdr.clrText = infoPtr->clrText;
620 nmcdhdr.clrTextBk= infoPtr->clrBk;
621 nmcdhdr.iSubItem =iSubItem;
623 TRACE("drawstage=%lx hdc=%x item=%lx, itemstate=%x, lItemlParam=%lx\n",
624 nmcd->dwDrawStage, nmcd->hdc, nmcd->dwItemSpec,
625 nmcd->uItemState, nmcd->lItemlParam);
627 retval=SendMessageW (GetParent (hwnd), WM_NOTIFY,
628 (WPARAM) GetWindowLongW( hwnd, GWL_ID), (LPARAM)&nmcdhdr);
630 infoPtr->clrText=nmcdhdr.clrText;
631 infoPtr->clrBk =nmcdhdr.clrTextBk;
632 return (BOOL) retval;
636 /*************************************************************************
637 * LISTVIEW_ProcessLetterKeys
639 * Processes keyboard messages generated by pressing the letter keys
640 * on the keyboard.
641 * What this does is perform a case insensitive search from the
642 * current position with the following quirks:
643 * - If two chars or more are pressed in quick succession we search
644 * for the corresponding string (e.g. 'abc').
645 * - If there is a delay we wipe away the current search string and
646 * restart with just that char.
647 * - If the user keeps pressing the same character, whether slowly or
648 * fast, so that the search string is entirely composed of this
649 * character ('aaaaa' for instance), then we search for first item
650 * that starting with that character.
651 * - If the user types the above character in quick succession, then
652 * we must also search for the corresponding string ('aaaaa'), and
653 * go to that string if there is a match.
655 * RETURNS
657 * Zero.
659 * BUGS
661 * - The current implementation has a list of characters it will
662 * accept and it ignores averything else. In particular it will
663 * ignore accentuated characters which seems to match what
664 * Windows does. But I'm not sure it makes sense to follow
665 * Windows there.
666 * - We don't sound a beep when the search fails.
668 * SEE ALSO
670 * TREEVIEW_ProcessLetterKeys
672 static INT LISTVIEW_ProcessLetterKeys(
673 HWND hwnd, /* handle to the window */
674 WPARAM charCode, /* the character code, the actual character */
675 LPARAM keyData /* key data */
678 LISTVIEW_INFO *infoPtr;
679 INT nItem;
680 INT nSize;
681 INT endidx,idx;
682 LVITEMW item;
683 WCHAR buffer[MAX_PATH];
684 DWORD timestamp,elapsed;
686 /* simple parameter checking */
687 if (!hwnd || !charCode || !keyData)
688 return 0;
690 infoPtr=(LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
691 if (!infoPtr)
692 return 0;
694 /* only allow the valid WM_CHARs through */
695 if (!isalnum(charCode) &&
696 charCode != '.' && charCode != '`' && charCode != '!' &&
697 charCode != '@' && charCode != '#' && charCode != '$' &&
698 charCode != '%' && charCode != '^' && charCode != '&' &&
699 charCode != '*' && charCode != '(' && charCode != ')' &&
700 charCode != '-' && charCode != '_' && charCode != '+' &&
701 charCode != '=' && charCode != '\\'&& charCode != ']' &&
702 charCode != '}' && charCode != '[' && charCode != '{' &&
703 charCode != '/' && charCode != '?' && charCode != '>' &&
704 charCode != '<' && charCode != ',' && charCode != '~')
705 return 0;
707 nSize=GETITEMCOUNT(infoPtr);
708 /* if there's one item or less, there is no where to go */
709 if (nSize <= 1)
710 return 0;
712 /* compute how much time elapsed since last keypress */
713 timestamp=GetTickCount();
714 if (timestamp > infoPtr->lastKeyPressTimestamp) {
715 elapsed=timestamp-infoPtr->lastKeyPressTimestamp;
716 } else {
717 elapsed=infoPtr->lastKeyPressTimestamp-timestamp;
720 /* update the search parameters */
721 infoPtr->lastKeyPressTimestamp=timestamp;
722 if (elapsed < KEY_DELAY) {
723 if (infoPtr->nSearchParamLength < COUNTOF(infoPtr->szSearchParam)) {
724 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
726 if (infoPtr->charCode != charCode) {
727 infoPtr->charCode=charCode=0;
729 } else {
730 infoPtr->charCode=charCode;
731 infoPtr->szSearchParam[0]=charCode;
732 infoPtr->nSearchParamLength=1;
733 /* Redundant with the 1 char string */
734 charCode=0;
737 /* and search from the current position */
738 nItem=-1;
739 if (infoPtr->nFocusedItem >= 0) {
740 endidx=infoPtr->nFocusedItem;
741 idx=endidx;
742 /* if looking for single character match,
743 * then we must always move forward
745 if (infoPtr->nSearchParamLength == 1)
746 idx++;
747 } else {
748 endidx=nSize;
749 idx=0;
751 do {
752 if (idx == nSize) {
753 if (endidx == nSize)
754 break;
755 idx=0;
758 /* get item */
759 ZeroMemory(&item, sizeof(item));
760 item.mask = LVIF_TEXT;
761 item.iItem = idx;
762 item.iSubItem = 0;
763 item.pszText = buffer;
764 item.cchTextMax = COUNTOF(buffer);
765 ListView_GetItemW( hwnd, &item );
767 /* check for a match */
768 if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
769 nItem=idx;
770 break;
771 } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
772 (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
773 /* This would work but we must keep looking for a longer match */
774 nItem=idx;
776 idx++;
777 } while (idx != endidx);
779 if (nItem != -1) {
780 if (LISTVIEW_KeySelection(hwnd, nItem) != FALSE) {
781 /* refresh client area */
782 InvalidateRect(hwnd, NULL, TRUE);
783 UpdateWindow(hwnd);
787 return 0;
790 /*************************************************************************
791 * LISTVIEW_UpdateHeaderSize [Internal]
793 * Function to resize the header control
795 * PARAMS
796 * hwnd [I] handle to a window
797 * nNewScrollPos [I] Scroll Pos to Set
799 * RETURNS
800 * nothing
802 * NOTES
804 static VOID LISTVIEW_UpdateHeaderSize(HWND hwnd, INT nNewScrollPos)
806 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
807 RECT winRect;
808 POINT point[2];
810 GetWindowRect(infoPtr->hwndHeader, &winRect);
811 point[0].x = winRect.left;
812 point[0].y = winRect.top;
813 point[1].x = winRect.right;
814 point[1].y = winRect.bottom;
816 MapWindowPoints(HWND_DESKTOP, hwnd, point, 2);
817 point[0].x = -(nNewScrollPos * LISTVIEW_SCROLL_DIV_SIZE);
818 point[1].x += (nNewScrollPos * LISTVIEW_SCROLL_DIV_SIZE);
820 SetWindowPos(infoPtr->hwndHeader,0,
821 point[0].x,point[0].y,point[1].x,point[1].y,
822 SWP_NOZORDER | SWP_NOACTIVATE);
825 /***
826 * DESCRIPTION:
827 * Update the scrollbars. This functions should be called whenever
828 * the content, size or view changes.
830 * PARAMETER(S):
831 * [I] HWND : window handle
833 * RETURN:
834 * None
836 static VOID LISTVIEW_UpdateScroll(HWND hwnd)
838 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
839 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
840 UINT uView = lStyle & LVS_TYPEMASK;
841 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
842 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
843 SCROLLINFO scrollInfo;
845 if (lStyle & LVS_NOSCROLL) return;
847 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
848 scrollInfo.cbSize = sizeof(SCROLLINFO);
850 if (uView == LVS_LIST)
852 /* update horizontal scrollbar */
854 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
855 INT nCountPerRow = LISTVIEW_GetCountPerRow(hwnd);
856 INT nNumOfItems = GETITEMCOUNT(infoPtr);
858 scrollInfo.nMax = nNumOfItems / nCountPerColumn;
859 if((nNumOfItems % nCountPerColumn) == 0)
861 scrollInfo.nMax--;
863 else
865 scrollInfo.nMax++;
867 scrollInfo.nPos = ListView_GetTopIndex(hwnd) / nCountPerColumn;
868 scrollInfo.nPage = nCountPerRow;
869 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
870 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
871 ShowScrollBar(hwnd, SB_VERT, FALSE);
873 else if (uView == LVS_REPORT)
875 /* update vertical scrollbar */
876 scrollInfo.nMin = 0;
877 scrollInfo.nMax = GETITEMCOUNT(infoPtr) - 1;
878 scrollInfo.nPos = ListView_GetTopIndex(hwnd);
879 scrollInfo.nPage = LISTVIEW_GetCountPerColumn(hwnd);
880 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
881 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
883 /* update horizontal scrollbar */
884 nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
885 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) == FALSE
886 || GETITEMCOUNT(infoPtr) == 0)
888 scrollInfo.nPos = 0;
890 scrollInfo.nMin = 0;
891 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE ;
892 scrollInfo.nPage = nListWidth / LISTVIEW_SCROLL_DIV_SIZE;
893 scrollInfo.nMax = max(infoPtr->nItemWidth / LISTVIEW_SCROLL_DIV_SIZE, 0)-1;
894 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
896 /* Update the Header Control */
897 scrollInfo.fMask = SIF_POS;
898 GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
899 LISTVIEW_UpdateHeaderSize(hwnd, scrollInfo.nPos);
902 else
904 RECT rcView;
906 if (LISTVIEW_GetViewRect(hwnd, &rcView) != FALSE)
908 INT nViewWidth = rcView.right - rcView.left;
909 INT nViewHeight = rcView.bottom - rcView.top;
911 /* Update Horizontal Scrollbar */
912 scrollInfo.fMask = SIF_POS;
913 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) == FALSE
914 || GETITEMCOUNT(infoPtr) == 0)
916 scrollInfo.nPos = 0;
918 scrollInfo.nMax = max(nViewWidth / LISTVIEW_SCROLL_DIV_SIZE, 0)-1;
919 scrollInfo.nMin = 0;
920 scrollInfo.nPage = nListWidth / LISTVIEW_SCROLL_DIV_SIZE;
921 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
922 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
924 /* Update Vertical Scrollbar */
925 nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
926 scrollInfo.fMask = SIF_POS;
927 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) == FALSE
928 || GETITEMCOUNT(infoPtr) == 0)
930 scrollInfo.nPos = 0;
932 scrollInfo.nMax = max(nViewHeight / LISTVIEW_SCROLL_DIV_SIZE,0)-1;
933 scrollInfo.nMin = 0;
934 scrollInfo.nPage = nListHeight / LISTVIEW_SCROLL_DIV_SIZE;
935 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
936 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
941 /***
942 * DESCRIPTION:
943 * Prints a message for unsupported window styles.
944 * A kind of TODO list for window styles.
946 * PARAMETER(S):
947 * [I] LONG : window style
949 * RETURN:
950 * None
952 static VOID LISTVIEW_UnsupportedStyles(LONG lStyle)
954 if ((LVS_TYPESTYLEMASK & lStyle) == LVS_NOSCROLL)
955 FIXME(" LVS_NOSCROLL\n");
957 if (lStyle & LVS_EDITLABELS)
958 FIXME(" LVS_EDITLABELS\n");
960 if (lStyle & LVS_NOLABELWRAP)
961 FIXME(" LVS_NOLABELWRAP\n");
963 if (lStyle & LVS_SHAREIMAGELISTS)
964 FIXME(" LVS_SHAREIMAGELISTS\n");
966 if (lStyle & LVS_SORTASCENDING)
967 FIXME(" LVS_SORTASCENDING\n");
969 if (lStyle & LVS_SORTDESCENDING)
970 FIXME(" LVS_SORTDESCENDING\n");
973 /***
974 * DESCRIPTION:
975 * Aligns the items with the top edge of the window.
977 * PARAMETER(S):
978 * [I] HWND : window handle
980 * RETURN:
981 * None
983 static VOID LISTVIEW_AlignTop(HWND hwnd)
985 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
986 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
987 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
988 POINT ptItem;
989 RECT rcView;
990 INT i, off_x=0, off_y=0;
992 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
994 /* Since SetItemPosition uses upper-left of icon, and for
995 style=LVS_ICON the icon is not left adjusted, get the offset */
996 if (uView == LVS_ICON)
998 off_y = ICON_TOP_PADDING;
999 off_x = (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
1001 ptItem.x = off_x;
1002 ptItem.y = off_y;
1003 ZeroMemory(&rcView, sizeof(RECT));
1005 if (nListWidth > infoPtr->nItemWidth)
1007 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1009 if (ptItem.x + infoPtr->nItemWidth > nListWidth)
1011 ptItem.x = off_x;
1012 ptItem.y += infoPtr->nItemHeight;
1015 LISTVIEW_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
1016 ptItem.x += infoPtr->nItemWidth;
1017 rcView.right = max(rcView.right, ptItem.x);
1020 rcView.bottom = ptItem.y + infoPtr->nItemHeight;
1022 else
1024 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1026 LISTVIEW_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
1027 ptItem.y += infoPtr->nItemHeight;
1030 rcView.right = infoPtr->nItemWidth;
1031 rcView.bottom = ptItem.y;
1034 LISTVIEW_SetViewRect(hwnd, &rcView);
1038 /***
1039 * DESCRIPTION:
1040 * Aligns the items with the left edge of the window.
1042 * PARAMETER(S):
1043 * [I] HWND : window handle
1045 * RETURN:
1046 * None
1048 static VOID LISTVIEW_AlignLeft(HWND hwnd)
1050 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1051 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
1052 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1053 POINT ptItem;
1054 RECT rcView;
1055 INT i, off_x=0, off_y=0;
1057 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1059 /* Since SetItemPosition uses upper-left of icon, and for
1060 style=LVS_ICON the icon is not left adjusted, get the offset */
1061 if (uView == LVS_ICON)
1063 off_y = ICON_TOP_PADDING;
1064 off_x = (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
1066 ptItem.x = off_x;
1067 ptItem.y = off_y;
1068 ZeroMemory(&rcView, sizeof(RECT));
1070 if (nListHeight > infoPtr->nItemHeight)
1072 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1074 if (ptItem.y + infoPtr->nItemHeight > nListHeight)
1076 ptItem.y = off_y;
1077 ptItem.x += infoPtr->nItemWidth;
1080 LISTVIEW_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
1081 ptItem.y += infoPtr->nItemHeight;
1082 rcView.bottom = max(rcView.bottom, ptItem.y);
1085 rcView.right = ptItem.x + infoPtr->nItemWidth;
1087 else
1089 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1091 LISTVIEW_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
1092 ptItem.x += infoPtr->nItemWidth;
1095 rcView.bottom = infoPtr->nItemHeight;
1096 rcView.right = ptItem.x;
1099 LISTVIEW_SetViewRect(hwnd, &rcView);
1103 /***
1104 * DESCRIPTION:
1105 * Set the bounding rectangle of all the items.
1107 * PARAMETER(S):
1108 * [I] HWND : window handle
1109 * [I] LPRECT : bounding rectangle
1111 * RETURN:
1112 * SUCCESS : TRUE
1113 * FAILURE : FALSE
1115 static LRESULT LISTVIEW_SetViewRect(HWND hwnd, LPRECT lprcView)
1117 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
1118 BOOL bResult = FALSE;
1120 TRACE("(hwnd=%x, left=%d, top=%d, right=%d, bottom=%d)\n", hwnd,
1121 lprcView->left, lprcView->top, lprcView->right, lprcView->bottom);
1123 if (lprcView != NULL)
1125 bResult = TRUE;
1126 infoPtr->rcView.left = lprcView->left;
1127 infoPtr->rcView.top = lprcView->top;
1128 infoPtr->rcView.right = lprcView->right;
1129 infoPtr->rcView.bottom = lprcView->bottom;
1132 return bResult;
1135 /***
1136 * DESCRIPTION:
1137 * Retrieves the bounding rectangle of all the items.
1139 * PARAMETER(S):
1140 * [I] HWND : window handle
1141 * [O] LPRECT : bounding rectangle
1143 * RETURN:
1144 * SUCCESS : TRUE
1145 * FAILURE : FALSE
1147 static LRESULT LISTVIEW_GetViewRect(HWND hwnd, LPRECT lprcView)
1149 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
1150 BOOL bResult = FALSE;
1151 POINT ptOrigin;
1153 TRACE("(hwnd=%x, lprcView=%p)\n", hwnd, lprcView);
1155 if (lprcView != NULL)
1157 bResult = LISTVIEW_GetOrigin(hwnd, &ptOrigin);
1158 if (bResult != FALSE)
1160 lprcView->left = infoPtr->rcView.left + ptOrigin.x;
1161 lprcView->top = infoPtr->rcView.top + ptOrigin.y;
1162 lprcView->right = infoPtr->rcView.right + ptOrigin.x;
1163 lprcView->bottom = infoPtr->rcView.bottom + ptOrigin.y;
1166 TRACE("(left=%d, top=%d, right=%d, bottom=%d)\n",
1167 lprcView->left, lprcView->top, lprcView->right, lprcView->bottom);
1170 return bResult;
1173 /***
1174 * DESCRIPTION:
1175 * Retrieves the subitem pointer associated with the subitem index.
1177 * PARAMETER(S):
1178 * [I] HDPA : DPA handle for a specific item
1179 * [I] INT : index of subitem
1181 * RETURN:
1182 * SUCCESS : subitem pointer
1183 * FAILURE : NULL
1185 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems,
1186 INT nSubItem)
1188 LISTVIEW_SUBITEM *lpSubItem;
1189 INT i;
1191 for (i = 1; i < hdpaSubItems->nItemCount; i++)
1193 lpSubItem = (LISTVIEW_SUBITEM *) DPA_GetPtr(hdpaSubItems, i);
1194 if (lpSubItem != NULL)
1196 if (lpSubItem->iSubItem == nSubItem)
1198 return lpSubItem;
1203 return NULL;
1206 /***
1207 * DESCRIPTION:
1208 * Calculates the width of an item.
1210 * PARAMETER(S):
1211 * [I] HWND : window handle
1212 * [I] LONG : window style
1214 * RETURN:
1215 * Returns item width.
1217 static INT LISTVIEW_GetItemWidth(HWND hwnd)
1219 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1220 LONG style = GetWindowLongW(hwnd, GWL_STYLE);
1221 UINT uView = style & LVS_TYPEMASK;
1222 INT nHeaderItemCount;
1223 RECT rcHeaderItem;
1224 INT nItemWidth = 0;
1225 INT nLabelWidth;
1226 INT i;
1228 TRACE("(hwnd=%x)\n", hwnd);
1230 if (uView == LVS_ICON)
1232 nItemWidth = infoPtr->iconSpacing.cx;
1234 else if (uView == LVS_REPORT)
1236 /* calculate width of header */
1237 nHeaderItemCount = Header_GetItemCount(infoPtr->hwndHeader);
1238 for (i = 0; i < nHeaderItemCount; i++)
1240 if (Header_GetItemRect(infoPtr->hwndHeader, i, &rcHeaderItem) != 0)
1242 nItemWidth += (rcHeaderItem.right - rcHeaderItem.left);
1246 else
1248 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1250 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, i);
1251 nItemWidth = max(nItemWidth, nLabelWidth);
1254 /* default label size */
1255 if (GETITEMCOUNT(infoPtr) == 0)
1257 nItemWidth = DEFAULT_COLUMN_WIDTH;
1259 else
1261 if (nItemWidth == 0)
1263 nItemWidth = DEFAULT_LABEL_WIDTH;
1265 else
1267 /* add padding */
1268 nItemWidth += WIDTH_PADDING;
1270 if (infoPtr->himlSmall != NULL)
1272 nItemWidth += infoPtr->iconSize.cx;
1275 if (infoPtr->himlState != NULL)
1277 nItemWidth += infoPtr->iconSize.cx;
1282 if(nItemWidth == 0)
1284 /* nItemWidth Cannot be Zero */
1285 nItemWidth = 1;
1287 return nItemWidth;
1290 /***
1291 * DESCRIPTION:
1292 * Calculates the width of a specific item.
1294 * PARAMETER(S):
1295 * [I] HWND : window handle
1296 * [I] LPSTR : string
1298 * RETURN:
1299 * Returns the width of an item width a specified string.
1301 static INT LISTVIEW_CalculateWidth(HWND hwnd, INT nItem)
1303 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1304 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
1305 INT nHeaderItemCount;
1306 RECT rcHeaderItem;
1307 INT nItemWidth = 0;
1308 INT i;
1310 TRACE("(hwnd=%x)\n", hwnd);
1312 if (uView == LVS_ICON)
1314 nItemWidth = infoPtr->iconSpacing.cx;
1316 else if (uView == LVS_REPORT)
1318 /* calculate width of header */
1319 nHeaderItemCount = Header_GetItemCount(infoPtr->hwndHeader);
1320 for (i = 0; i < nHeaderItemCount; i++)
1322 if (Header_GetItemRect(infoPtr->hwndHeader, i, &rcHeaderItem) != 0)
1324 nItemWidth += (rcHeaderItem.right - rcHeaderItem.left);
1328 else
1330 /* get width of string */
1331 nItemWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
1333 /* default label size */
1334 if (GETITEMCOUNT(infoPtr) == 0)
1336 nItemWidth = DEFAULT_COLUMN_WIDTH;
1338 else
1340 if (nItemWidth == 0)
1342 nItemWidth = DEFAULT_LABEL_WIDTH;
1344 else
1346 /* add padding */
1347 nItemWidth += WIDTH_PADDING;
1349 if (infoPtr->himlSmall != NULL)
1351 nItemWidth += infoPtr->iconSize.cx;
1354 if (infoPtr->himlState != NULL)
1356 nItemWidth += infoPtr->iconSize.cx;
1362 return nItemWidth;
1365 /***
1366 * DESCRIPTION:
1367 * Retrieves and saves important text metrics info for the current
1368 * Listview font.
1370 * PARAMETER(S):
1371 * [I] HWND : window handle
1374 static VOID LISTVIEW_SaveTextMetrics(HWND hwnd)
1376 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1377 TEXTMETRICW tm;
1378 HDC hdc = GetDC(hwnd);
1379 HFONT hOldFont = SelectObject(hdc, infoPtr->hFont);
1380 INT oldHeight, oldACW;
1382 GetTextMetricsW(hdc, &tm);
1384 oldHeight = infoPtr->ntmHeight;
1385 oldACW = infoPtr->ntmAveCharWidth;
1386 infoPtr->ntmHeight = tm.tmHeight;
1387 infoPtr->ntmAveCharWidth = tm.tmAveCharWidth;
1389 SelectObject(hdc, hOldFont);
1390 ReleaseDC(hwnd, hdc);
1391 TRACE("tmHeight old=%d,new=%d; tmAveCharWidth old=%d,new=%d\n",
1392 oldHeight, infoPtr->ntmHeight, oldACW, infoPtr->ntmAveCharWidth);
1396 /***
1397 * DESCRIPTION:
1398 * Calculates the height of an item.
1400 * PARAMETER(S):
1401 * [I] HWND : window handle
1403 * RETURN:
1404 * Returns item height.
1406 static INT LISTVIEW_GetItemHeight(HWND hwnd)
1408 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1409 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
1410 INT nItemHeight = 0;
1412 if (uView == LVS_ICON)
1414 nItemHeight = infoPtr->iconSpacing.cy;
1416 else
1418 if(infoPtr->himlState || infoPtr->himlSmall)
1419 nItemHeight = max(infoPtr->ntmHeight, infoPtr->iconSize.cy) + HEIGHT_PADDING;
1420 else
1421 nItemHeight = infoPtr->ntmHeight;
1424 return nItemHeight;
1428 static void LISTVIEW_PrintSelectionRanges(HWND hwnd)
1430 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1431 LISTVIEW_SELECTION *selection;
1432 INT topSelection = infoPtr->hdpaSelectionRanges->nItemCount;
1433 INT i;
1435 TRACE("Selections are:\n");
1436 for (i = 0; i < topSelection; i++)
1438 selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,i);
1439 TRACE(" %lu - %lu\n",selection->lower,selection->upper);
1443 /***
1444 * DESCRIPTION:
1445 * A compare function for selection ranges
1447 *PARAMETER(S)
1448 * [I] LPVOID : Item 1;
1449 * [I] LPVOID : Item 2;
1450 * [I] LPARAM : flags
1452 *RETURNS:
1453 * >0 : if Item 1 > Item 2
1454 * <0 : if Item 2 > Item 1
1455 * 0 : if Item 1 == Item 2
1457 static INT CALLBACK LISTVIEW_CompareSelectionRanges(LPVOID range1, LPVOID range2,
1458 LPARAM flags)
1460 int l1 = ((LISTVIEW_SELECTION*)(range1))->lower;
1461 int l2 = ((LISTVIEW_SELECTION*)(range2))->lower;
1462 int u1 = ((LISTVIEW_SELECTION*)(range1))->upper;
1463 int u2 = ((LISTVIEW_SELECTION*)(range2))->upper;
1464 int rc=0;
1466 if (u1 < l2)
1467 rc= -1;
1469 if (u2 < l1)
1470 rc= 1;
1472 return rc;
1476 * DESCRIPTION:
1477 * Adds a selection range.
1479 * PARAMETER(S):
1480 * [I] HWND : window handle
1481 * [I] INT : lower item index
1482 * [I] INT : upper item index
1484 * RETURN:
1485 * None
1487 static VOID LISTVIEW_AddSelectionRange(HWND hwnd, INT lItem, INT uItem)
1489 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1490 LISTVIEW_SELECTION *selection;
1491 INT topSelection = infoPtr->hdpaSelectionRanges->nItemCount;
1492 BOOL lowerzero=FALSE;
1494 selection = (LISTVIEW_SELECTION *)COMCTL32_Alloc(sizeof(LISTVIEW_SELECTION));
1495 selection->lower = lItem;
1496 selection->upper = uItem;
1498 TRACE("Add range %i - %i\n", lItem, uItem);
1499 if (topSelection)
1501 LISTVIEW_SELECTION *checkselection,*checkselection2;
1502 INT index,mergeindex;
1504 /* find overlapping selections */
1505 /* we want to catch adjacent ranges so expand our range by 1 */
1507 selection->upper++;
1508 if (selection->lower == 0)
1509 lowerzero = TRUE;
1510 else
1511 selection->lower--;
1513 index = DPA_Search(infoPtr->hdpaSelectionRanges, selection, 0,
1514 LISTVIEW_CompareSelectionRanges,
1515 0,0);
1516 selection->upper --;
1517 if (lowerzero)
1518 lowerzero=FALSE;
1519 else
1520 selection->lower ++;
1522 if (index >=0)
1524 checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,index);
1525 TRACE("Merge with index %i (%lu - %lu)\n",index,checkselection->lower,
1526 checkselection->upper);
1528 checkselection->lower = min(selection->lower,checkselection->lower);
1529 checkselection->upper = max(selection->upper,checkselection->upper);
1531 TRACE("New range (%lu - %lu)\n", checkselection->lower,
1532 checkselection->upper);
1534 COMCTL32_Free(selection);
1536 /* merge now common selection ranges in the lower group*/
1539 checkselection->upper ++;
1540 if (checkselection->lower == 0)
1541 lowerzero = TRUE;
1542 else
1543 checkselection->lower --;
1545 TRACE("search lower range (%lu - %lu)\n", checkselection->lower,
1546 checkselection->upper);
1548 /* not sorted yet */
1549 mergeindex = DPA_Search(infoPtr->hdpaSelectionRanges, checkselection, 0,
1550 LISTVIEW_CompareSelectionRanges, 0,
1553 checkselection->upper --;
1554 if (lowerzero)
1555 lowerzero = FALSE;
1556 else
1557 checkselection->lower ++;
1559 if (mergeindex >=0 && mergeindex != index)
1561 TRACE("Merge with index %i\n",mergeindex);
1562 checkselection2 = DPA_GetPtr(infoPtr->hdpaSelectionRanges,
1563 mergeindex);
1564 checkselection->lower = min(checkselection->lower,
1565 checkselection2->lower);
1566 checkselection->upper = max(checkselection->upper,
1567 checkselection2->upper);
1568 COMCTL32_Free(checkselection2);
1569 DPA_DeletePtr(infoPtr->hdpaSelectionRanges,mergeindex);
1570 index --;
1573 while (mergeindex > -1 && mergeindex <index);
1575 /* merge now common selection ranges in the upper group*/
1578 checkselection->upper ++;
1579 if (checkselection->lower == 0)
1580 lowerzero = TRUE;
1581 else
1582 checkselection->lower --;
1584 TRACE("search upper range %i (%lu - %lu)\n",index,
1585 checkselection->lower, checkselection->upper);
1587 /* not sorted yet */
1588 mergeindex = DPA_Search(infoPtr->hdpaSelectionRanges, checkselection,
1589 index+1,
1590 LISTVIEW_CompareSelectionRanges, 0,
1593 checkselection->upper --;
1594 if (lowerzero)
1595 lowerzero = FALSE;
1596 else
1597 checkselection->lower ++;
1599 if (mergeindex >=0 && mergeindex !=index)
1601 TRACE("Merge with index %i\n",mergeindex);
1602 checkselection2 = DPA_GetPtr(infoPtr->hdpaSelectionRanges,
1603 mergeindex);
1604 checkselection->lower = min(checkselection->lower,
1605 checkselection2->lower);
1606 checkselection->upper = max(checkselection->upper,
1607 checkselection2->upper);
1608 COMCTL32_Free(checkselection2);
1609 DPA_DeletePtr(infoPtr->hdpaSelectionRanges,mergeindex);
1612 while (mergeindex > -1);
1614 else
1617 index = DPA_Search(infoPtr->hdpaSelectionRanges, selection, 0,
1618 LISTVIEW_CompareSelectionRanges, 0,
1619 DPAS_INSERTAFTER);
1621 TRACE("Insert before index %i\n",index);
1622 if (index == -1)
1623 index = 0;
1624 DPA_InsertPtr(infoPtr->hdpaSelectionRanges,index,selection);
1627 else
1629 DPA_InsertPtr(infoPtr->hdpaSelectionRanges,0,selection);
1632 * Incase of error
1634 DPA_Sort(infoPtr->hdpaSelectionRanges,LISTVIEW_CompareSelectionRanges,0);
1635 LISTVIEW_PrintSelectionRanges(hwnd);
1639 * DESCRIPTION:
1640 * check if a specified index is selected.
1642 * PARAMETER(S):
1643 * [I] HWND : window handle
1644 * [I] INT : item index
1646 * RETURN:
1647 * None
1649 static BOOL LISTVIEW_IsSelected(HWND hwnd, INT nItem)
1651 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1652 LISTVIEW_SELECTION selection;
1653 INT index;
1655 selection.upper = nItem;
1656 selection.lower = nItem;
1658 index = DPA_Search(infoPtr->hdpaSelectionRanges, &selection, 0,
1659 LISTVIEW_CompareSelectionRanges,
1660 0,DPAS_SORTED);
1661 if (index != -1)
1662 return TRUE;
1663 else
1664 return FALSE;
1667 /***
1668 * DESCRIPTION:
1669 * Removes all selection ranges
1671 * Parameters(s):
1672 * HWND: window handle
1674 * RETURNS:
1675 * SUCCESS : TRUE
1676 * FAILURE : TRUE
1678 static LRESULT LISTVIEW_RemoveAllSelections(HWND hwnd)
1680 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1681 LISTVIEW_SELECTION *selection;
1682 INT i;
1683 LVITEMW item;
1685 TRACE("(0x%x)\n",hwnd);
1687 ZeroMemory(&item,sizeof(item));
1688 item.stateMask = LVIS_SELECTED;
1692 selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,0);
1693 if (selection)
1695 TRACE("Removing %lu to %lu\n",selection->lower, selection->upper);
1696 for (i = selection->lower; i<=selection->upper; i++)
1697 LISTVIEW_SetItemState(hwnd,i,&item);
1698 LISTVIEW_RemoveSelectionRange(hwnd,selection->lower,selection->upper);
1701 while (infoPtr->hdpaSelectionRanges->nItemCount>0);
1703 TRACE("done\n");
1704 return TRUE;
1708 * DESCRIPTION:
1709 * Removes a range selections.
1711 * PARAMETER(S):
1712 * [I] HWND : window handle
1713 * [I] INT : lower item index
1714 * [I] INT : upper item index
1716 * RETURN:
1717 * None
1719 static VOID LISTVIEW_RemoveSelectionRange(HWND hwnd, INT lItem, INT uItem)
1721 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1722 LISTVIEW_SELECTION removeselection,*checkselection;
1723 INT index;
1725 removeselection.lower = lItem;
1726 removeselection.upper = uItem;
1728 TRACE("Remove range %lu - %lu\n",removeselection.lower,removeselection.upper);
1729 LISTVIEW_PrintSelectionRanges(hwnd);
1731 index = DPA_Search(infoPtr->hdpaSelectionRanges, &removeselection, 0,
1732 LISTVIEW_CompareSelectionRanges,
1733 0,0);
1735 if (index == -1)
1736 return;
1739 checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,
1740 index);
1742 TRACE("Matches range index %i (%lu-%lu)\n",index,checkselection->lower,
1743 checkselection->upper);
1745 /* case 1: Same */
1746 if ((checkselection->upper == removeselection.upper) &&
1747 (checkselection->lower == removeselection.lower))
1749 DPA_DeletePtr(infoPtr->hdpaSelectionRanges,index);
1750 TRACE("Case 1\n");
1752 /* case 2: engulf */
1753 else if (((checkselection->upper < removeselection.upper) &&
1754 (checkselection->lower > removeselection.lower))||
1755 ((checkselection->upper <= removeselection.upper) &&
1756 (checkselection->lower > removeselection.lower)) ||
1757 ((checkselection->upper < removeselection.upper) &&
1758 (checkselection->lower >= removeselection.lower)))
1761 DPA_DeletePtr(infoPtr->hdpaSelectionRanges,index);
1762 /* do it again because others may also get caught */
1763 TRACE("Case 2\n");
1764 LISTVIEW_RemoveSelectionRange(hwnd,lItem,uItem);
1766 /* case 3: overlap upper */
1767 else if ((checkselection->upper < removeselection.upper) &&
1768 (checkselection->lower < removeselection.lower))
1770 checkselection->upper = removeselection.lower - 1;
1771 TRACE("Case 3\n");
1772 LISTVIEW_RemoveSelectionRange(hwnd,lItem,uItem);
1774 /* case 4: overlap lower */
1775 else if ((checkselection->upper > removeselection.upper) &&
1776 (checkselection->lower > removeselection.lower))
1778 checkselection->lower = removeselection.upper + 1;
1779 TRACE("Case 4\n");
1780 LISTVIEW_RemoveSelectionRange(hwnd,lItem,uItem);
1782 /* case 5: fully internal */
1783 else if (checkselection->upper == removeselection.upper)
1784 checkselection->upper = removeselection.lower - 1;
1785 else if (checkselection->lower == removeselection.lower)
1786 checkselection->lower = removeselection.upper + 1;
1787 else
1789 /* bisect the range */
1790 LISTVIEW_SELECTION *newselection;
1792 newselection = (LISTVIEW_SELECTION *)
1793 COMCTL32_Alloc(sizeof(LISTVIEW_SELECTION));
1794 newselection -> lower = checkselection->lower;
1795 newselection -> upper = removeselection.lower - 1;
1796 checkselection -> lower = removeselection.upper + 1;
1797 DPA_InsertPtr(infoPtr->hdpaSelectionRanges,index,newselection);
1798 TRACE("Case 5\n");
1799 DPA_Sort(infoPtr->hdpaSelectionRanges,LISTVIEW_CompareSelectionRanges,0);
1801 LISTVIEW_PrintSelectionRanges(hwnd);
1805 * DESCRIPTION:
1806 * Updates the various indices after an item has been inserted or deleted.
1808 * PARAMETER(S):
1809 * [I] HWND : window handle
1810 * [I] INT : item index
1811 * [I] INT : Direction of shift, +1 or -1.
1813 * RETURN:
1814 * None
1816 static VOID LISTVIEW_ShiftIndices(HWND hwnd, INT nItem, INT direction)
1818 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1819 LISTVIEW_SELECTION selection,*checkselection;
1820 INT index;
1822 TRACE("Shifting %iu, %i steps\n",nItem,direction);
1824 selection.upper = nItem;
1825 selection.lower = nItem;
1827 index = DPA_Search(infoPtr->hdpaSelectionRanges, &selection, 0,
1828 LISTVIEW_CompareSelectionRanges,
1829 0,DPAS_SORTED|DPAS_INSERTAFTER);
1831 while ((index < infoPtr->hdpaSelectionRanges->nItemCount)&&(index != -1))
1833 checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,index);
1834 if ((checkselection->lower >= nItem)&&
1835 (checkselection->lower + direction >= 0))
1836 checkselection->lower += direction;
1837 if ((checkselection->upper >= nItem)&&
1838 (checkselection->upper + direction >=0))
1839 checkselection->upper += direction;
1840 index ++;
1843 /* Note that the following will fail if direction != +1 and -1 */
1844 if (infoPtr->nSelectionMark > nItem)
1845 infoPtr->nSelectionMark += direction;
1846 else if (infoPtr->nSelectionMark == nItem)
1848 if (direction > 0)
1849 infoPtr->nSelectionMark += direction;
1850 else if (infoPtr->nSelectionMark >= GETITEMCOUNT(infoPtr))
1851 infoPtr->nSelectionMark = GETITEMCOUNT(infoPtr) - 1;
1854 if (infoPtr->nFocusedItem > nItem)
1855 infoPtr->nFocusedItem += direction;
1856 else if (infoPtr->nFocusedItem == nItem)
1858 if (direction > 0)
1859 infoPtr->nFocusedItem += direction;
1860 else
1862 if (infoPtr->nFocusedItem >= GETITEMCOUNT(infoPtr))
1863 infoPtr->nFocusedItem = GETITEMCOUNT(infoPtr) - 1;
1864 if (infoPtr->nFocusedItem >= 0)
1865 LISTVIEW_SetItemFocus(hwnd, infoPtr->nFocusedItem);
1868 /* But we are not supposed to modify nHotItem! */
1873 * DESCRIPTION:
1874 * Adds a block of selections.
1876 * PARAMETER(S):
1877 * [I] HWND : window handle
1878 * [I] INT : item index
1880 * RETURN:
1881 * None
1883 static VOID LISTVIEW_AddGroupSelection(HWND hwnd, INT nItem)
1885 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1886 INT nFirst = min(infoPtr->nSelectionMark, nItem);
1887 INT nLast = max(infoPtr->nSelectionMark, nItem);
1888 INT i;
1889 LVITEMW item;
1891 if (nFirst == -1)
1892 nFirst = nItem;
1894 ZeroMemory(&item,sizeof(item));
1895 item.stateMask = LVIS_SELECTED;
1896 item.state = LVIS_SELECTED;
1898 for (i = nFirst; i <= nLast; i++)
1899 LISTVIEW_SetItemState(hwnd,i,&item);
1901 LISTVIEW_SetItemFocus(hwnd, nItem);
1902 infoPtr->nSelectionMark = nItem;
1906 /***
1907 * DESCRIPTION:
1908 * Adds a single selection.
1910 * PARAMETER(S):
1911 * [I] HWND : window handle
1912 * [I] INT : item index
1914 * RETURN:
1915 * None
1917 static VOID LISTVIEW_AddSelection(HWND hwnd, INT nItem)
1919 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1920 LVITEMW item;
1922 ZeroMemory(&item,sizeof(item));
1923 item.state = LVIS_SELECTED;
1924 item.stateMask = LVIS_SELECTED;
1926 LISTVIEW_SetItemState(hwnd,nItem,&item);
1928 LISTVIEW_SetItemFocus(hwnd, nItem);
1929 infoPtr->nSelectionMark = nItem;
1932 /***
1933 * DESCRIPTION:
1934 * Selects or unselects an item.
1936 * PARAMETER(S):
1937 * [I] HWND : window handle
1938 * [I] INT : item index
1940 * RETURN:
1941 * SELECT: TRUE
1942 * UNSELECT : FALSE
1944 static BOOL LISTVIEW_ToggleSelection(HWND hwnd, INT nItem)
1946 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1947 BOOL bResult;
1948 LVITEMW item;
1950 ZeroMemory(&item,sizeof(item));
1951 item.stateMask = LVIS_SELECTED;
1953 if (LISTVIEW_IsSelected(hwnd,nItem))
1955 LISTVIEW_SetItemState(hwnd,nItem,&item);
1956 bResult = FALSE;
1958 else
1960 item.state = LVIS_SELECTED;
1961 LISTVIEW_SetItemState(hwnd,nItem,&item);
1962 bResult = TRUE;
1965 LISTVIEW_SetItemFocus(hwnd, nItem);
1966 infoPtr->nSelectionMark = nItem;
1968 return bResult;
1971 /***
1972 * DESCRIPTION:
1973 * Selects items based on view coordinates.
1975 * PARAMETER(S):
1976 * [I] HWND : window handle
1977 * [I] RECT : selection rectangle
1979 * RETURN:
1980 * None
1982 static VOID LISTVIEW_SetSelectionRect(HWND hwnd, RECT rcSelRect)
1984 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1985 POINT ptItem;
1986 INT i;
1987 LVITEMW item;
1989 ZeroMemory(&item,sizeof(item));
1990 item.stateMask = LVIS_SELECTED;
1992 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1994 LISTVIEW_GetItemPosition(hwnd, i, &ptItem);
1996 if (PtInRect(&rcSelRect, ptItem) != FALSE)
1997 item.state = LVIS_SELECTED;
1998 else
1999 item.state = 0;
2000 LISTVIEW_SetItemState(hwnd,i,&item);
2004 /***
2005 * DESCRIPTION:
2006 * Sets a single group selection.
2008 * PARAMETER(S):
2009 * [I] HWND : window handle
2010 * [I] INT : item index
2012 * RETURN:
2013 * None
2015 static VOID LISTVIEW_SetGroupSelection(HWND hwnd, INT nItem)
2017 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2018 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
2019 LVITEMW item;
2021 ZeroMemory(&item,sizeof(item));
2022 item.stateMask = LVIS_SELECTED;
2024 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
2026 INT i;
2027 INT nFirst, nLast;
2029 if (infoPtr->nSelectionMark == -1)
2031 infoPtr->nSelectionMark = nFirst = nLast = nItem;
2033 else
2035 nFirst = min(infoPtr->nSelectionMark, nItem);
2036 nLast = max(infoPtr->nSelectionMark, nItem);
2039 for (i = 0; i <= GETITEMCOUNT(infoPtr); i++)
2041 if ((i < nFirst) || (i > nLast))
2042 item.state = 0;
2043 else
2044 item.state = LVIS_SELECTED;
2045 LISTVIEW_SetItemState(hwnd,i,&item);
2048 else
2050 RECT rcItem;
2051 RECT rcSelMark;
2052 RECT rcSel;
2053 LISTVIEW_GetItemBoundBox(hwnd, nItem, &rcItem);
2054 LISTVIEW_GetItemBoundBox(hwnd, infoPtr->nSelectionMark, &rcSelMark);
2055 rcSel.left = min(rcSelMark.left, rcItem.left);
2056 rcSel.top = min(rcSelMark.top, rcItem.top);
2057 rcSel.right = max(rcSelMark.right, rcItem.right);
2058 rcSel.bottom = max(rcSelMark.bottom, rcItem.bottom);
2059 LISTVIEW_SetSelectionRect(hwnd, rcSel);
2060 TRACE("item %d (%d,%d)-(%d,%d), mark %d (%d,%d)-(%d,%d), sel (%d,%d)-(%d,%d)\n",
2061 nItem, rcItem.left, rcItem.top, rcItem.right, rcItem.bottom,
2062 infoPtr->nSelectionMark,
2063 rcSelMark.left, rcSelMark.top, rcSelMark.right, rcSelMark.bottom,
2064 rcSel.left, rcSel.top, rcSel.right, rcSel.bottom);
2068 LISTVIEW_SetItemFocus(hwnd, nItem);
2071 /***
2072 * DESCRIPTION:
2073 * Manages the item focus.
2075 * PARAMETER(S):
2076 * [I] HWND : window handle
2077 * [I] INT : item index
2079 * RETURN:
2080 * TRUE : focused item changed
2081 * FALSE : focused item has NOT changed
2083 static BOOL LISTVIEW_SetItemFocus(HWND hwnd, INT nItem)
2085 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2086 BOOL bResult = FALSE;
2087 LVITEMW lvItem;
2089 if (infoPtr->nFocusedItem != nItem)
2091 if (infoPtr->nFocusedItem >= 0)
2093 INT oldFocus = infoPtr->nFocusedItem;
2094 bResult = TRUE;
2095 infoPtr->nFocusedItem = -1;
2096 ZeroMemory(&lvItem, sizeof(lvItem));
2097 lvItem.stateMask = LVIS_FOCUSED;
2098 ListView_SetItemState(hwnd, oldFocus, &lvItem);
2102 lvItem.state = LVIS_FOCUSED;
2103 lvItem.stateMask = LVIS_FOCUSED;
2104 ListView_SetItemState(hwnd, nItem, &lvItem);
2106 infoPtr->nFocusedItem = nItem;
2107 ListView_EnsureVisible(hwnd, nItem, FALSE);
2110 return bResult;
2113 /***
2114 * DESCRIPTION:
2115 * Sets a single selection.
2117 * PARAMETER(S):
2118 * [I] HWND : window handle
2119 * [I] INT : item index
2121 * RETURN:
2122 * None
2124 static VOID LISTVIEW_SetSelection(HWND hwnd, INT nItem)
2126 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2127 LVITEMW lvItem;
2129 ZeroMemory(&lvItem, sizeof(lvItem));
2130 lvItem.stateMask = LVIS_FOCUSED;
2131 ListView_SetItemState(hwnd, infoPtr->nFocusedItem, &lvItem);
2133 LISTVIEW_RemoveAllSelections(hwnd);
2135 lvItem.state = LVIS_FOCUSED|LVIS_SELECTED;
2136 lvItem.stateMask = LVIS_FOCUSED|LVIS_SELECTED;
2137 ListView_SetItemState(hwnd, nItem, &lvItem);
2139 infoPtr->nFocusedItem = nItem;
2140 infoPtr->nSelectionMark = nItem;
2143 /***
2144 * DESCRIPTION:
2145 * Set selection(s) with keyboard.
2147 * PARAMETER(S):
2148 * [I] HWND : window handle
2149 * [I] INT : item index
2151 * RETURN:
2152 * SUCCESS : TRUE (needs to be repainted)
2153 * FAILURE : FALSE (nothing has changed)
2155 static BOOL LISTVIEW_KeySelection(HWND hwnd, INT nItem)
2157 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2158 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2159 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
2160 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
2161 BOOL bResult = FALSE;
2163 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
2165 if (lStyle & LVS_SINGLESEL)
2167 bResult = TRUE;
2168 LISTVIEW_SetSelection(hwnd, nItem);
2169 ListView_EnsureVisible(hwnd, nItem, FALSE);
2171 else
2173 if (wShift)
2175 bResult = TRUE;
2176 LISTVIEW_SetGroupSelection(hwnd, nItem);
2178 else if (wCtrl)
2180 bResult = LISTVIEW_SetItemFocus(hwnd, nItem);
2182 else
2184 bResult = TRUE;
2185 LISTVIEW_SetSelection(hwnd, nItem);
2186 ListView_EnsureVisible(hwnd, nItem, FALSE);
2191 return bResult;
2194 /***
2195 * DESCRIPTION:
2196 * Called when the mouse is being actively tracked and has hovered for a specified
2197 * amount of time
2199 * PARAMETER(S):
2200 * [I] HWND : window handle
2201 * [I] wParam : key indicator
2202 * [I] lParam : mouse position
2204 * RETURN:
2205 * 0 if the message was processed, non-zero if there was an error
2207 * INFO:
2208 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
2209 * over the item for a certain period of time.
2212 static LRESULT LISTVIEW_MouseHover(HWND hwnd, WPARAM wParam, LPARAM lParam)
2214 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2215 POINT pt;
2217 pt.x = (INT)LOWORD(lParam);
2218 pt.y = (INT)HIWORD(lParam);
2220 if(infoPtr->dwExStyle & LVS_EX_TRACKSELECT) {
2221 /* select the item under the cursor */
2222 LISTVIEW_MouseSelection(hwnd, pt);
2225 return 0;
2228 /***
2229 * DESCRIPTION:
2230 * Called whenever WM_MOUSEMOVE is received.
2232 * PARAMETER(S):
2233 * [I] HWND : window handle
2234 * [I] wParam : key indicators
2235 * [I] lParam : cursor position
2237 * RETURN:
2238 * 0 if the message is processed, non-zero if there was an error
2240 static LRESULT LISTVIEW_MouseMove(HWND hwnd, WPARAM wParam, LPARAM lParam)
2242 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2243 TRACKMOUSEEVENT trackinfo;
2245 /* see if we are supposed to be tracking mouse hovering */
2246 if(infoPtr->dwExStyle & LVS_EX_TRACKSELECT) {
2247 /* fill in the trackinfo struct */
2248 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
2249 trackinfo.dwFlags = TME_QUERY;
2250 trackinfo.hwndTrack = hwnd;
2251 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
2253 /* see if we are already tracking this hwnd */
2254 _TrackMouseEvent(&trackinfo);
2256 if(!(trackinfo.dwFlags & TME_HOVER)) {
2257 trackinfo.dwFlags = TME_HOVER;
2259 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
2260 _TrackMouseEvent(&trackinfo);
2264 return 0;
2267 /***
2268 * DESCRIPTION:
2269 * Selects an item based on coordinates.
2271 * PARAMETER(S):
2272 * [I] HWND : window handle
2273 * [I] POINT : mouse click ccordinates
2275 * RETURN:
2276 * SUCCESS : item index
2277 * FAILURE : -1
2279 static LRESULT LISTVIEW_MouseSelection(HWND hwnd, POINT pt)
2281 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2282 RECT rcItem;
2283 INT i,topindex,bottomindex;
2284 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2285 UINT uView = lStyle & LVS_TYPEMASK;
2287 topindex = ListView_GetTopIndex(hwnd);
2288 if (uView == LVS_REPORT)
2290 bottomindex = topindex + LISTVIEW_GetCountPerColumn(hwnd) + 1;
2291 bottomindex = min(bottomindex,GETITEMCOUNT(infoPtr));
2293 else
2295 bottomindex = GETITEMCOUNT(infoPtr);
2298 for (i = topindex; i < bottomindex; i++)
2300 rcItem.left = LVIR_SELECTBOUNDS;
2301 if (LISTVIEW_GetItemRect(hwnd, i, &rcItem) == TRUE)
2303 if (PtInRect(&rcItem, pt) != FALSE)
2305 return i;
2310 return -1;
2313 /***
2314 * DESCRIPTION:
2315 * Removes a column.
2317 * PARAMETER(S):
2318 * [IO] HDPA : dynamic pointer array handle
2319 * [I] INT : column index (subitem index)
2321 * RETURN:
2322 * SUCCCESS : TRUE
2323 * FAILURE : FALSE
2325 static BOOL LISTVIEW_RemoveColumn(HDPA hdpaItems, INT nSubItem)
2327 BOOL bResult = TRUE;
2328 HDPA hdpaSubItems;
2329 INT i;
2331 for (i = 0; i < hdpaItems->nItemCount; i++)
2333 hdpaSubItems = (HDPA)DPA_GetPtr(hdpaItems, i);
2334 if (hdpaSubItems != NULL)
2336 if (LISTVIEW_RemoveSubItem(hdpaSubItems, nSubItem) == FALSE)
2338 bResult = FALSE;
2343 return bResult;
2346 /***
2347 * DESCRIPTION:
2348 * Removes a subitem at a given position.
2350 * PARAMETER(S):
2351 * [IO] HDPA : dynamic pointer array handle
2352 * [I] INT : subitem index
2354 * RETURN:
2355 * SUCCCESS : TRUE
2356 * FAILURE : FALSE
2358 static BOOL LISTVIEW_RemoveSubItem(HDPA hdpaSubItems, INT nSubItem)
2360 LISTVIEW_SUBITEM *lpSubItem;
2361 INT i;
2363 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2365 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2366 if (lpSubItem != NULL)
2368 if (lpSubItem->iSubItem == nSubItem)
2370 /* free string */
2371 if (is_textW(lpSubItem->pszText))
2372 COMCTL32_Free(lpSubItem->pszText);
2374 /* free item */
2375 COMCTL32_Free(lpSubItem);
2377 /* free dpa memory */
2378 if (DPA_DeletePtr(hdpaSubItems, i) == NULL)
2379 return FALSE;
2381 else if (lpSubItem->iSubItem > nSubItem)
2382 return TRUE;
2386 return TRUE;
2389 /***
2390 * DESCRIPTION:
2391 * Compares the item information.
2393 * PARAMETER(S):
2394 * [I] LISTVIEW_ITEM *: destination item
2395 * [I] LPLVITEM : source item
2396 * [I] isW : TRUE if lpLVItem is Unicode, FALSE it it's ANSI
2398 * RETURN:
2399 * SUCCCESS : TRUE (EQUAL)
2400 * FAILURE : FALSE (NOT EQUAL)
2402 static UINT LISTVIEW_GetItemChangesT(LISTVIEW_ITEM *lpItem, LPLVITEMW lpLVItem, BOOL isW)
2404 UINT uChanged = 0;
2406 if ((lpItem != NULL) && (lpLVItem != NULL))
2408 if (lpLVItem->mask & LVIF_STATE)
2410 if ((lpItem->state & lpLVItem->stateMask) !=
2411 (lpLVItem->state & lpLVItem->stateMask))
2412 uChanged |= LVIF_STATE;
2415 if (lpLVItem->mask & LVIF_IMAGE)
2417 if (lpItem->iImage != lpLVItem->iImage)
2418 uChanged |= LVIF_IMAGE;
2421 if (lpLVItem->mask & LVIF_PARAM)
2423 if (lpItem->lParam != lpLVItem->lParam)
2424 uChanged |= LVIF_PARAM;
2427 if (lpLVItem->mask & LVIF_INDENT)
2429 if (lpItem->iIndent != lpLVItem->iIndent)
2430 uChanged |= LVIF_INDENT;
2433 if (lpLVItem->mask & LVIF_TEXT)
2435 if (lpLVItem->pszText == LPSTR_TEXTCALLBACKW)
2437 if (lpItem->pszText != LPSTR_TEXTCALLBACKW)
2438 uChanged |= LVIF_TEXT;
2440 else
2442 if (lpItem->pszText == LPSTR_TEXTCALLBACKW)
2444 uChanged |= LVIF_TEXT;
2446 else
2448 if (lpLVItem->pszText)
2450 if (lpItem->pszText)
2452 LPWSTR pszText = textdupTtoW(lpLVItem->pszText, isW);
2453 if (pszText && strcmpW(pszText, lpItem->pszText))
2454 uChanged |= LVIF_TEXT;
2455 textfreeT(pszText, isW);
2457 else
2459 uChanged |= LVIF_TEXT;
2462 else
2464 if (lpItem->pszText)
2465 uChanged |= LVIF_TEXT;
2471 return uChanged;
2474 /***
2475 * DESCRIPTION:
2476 * Initializes item attributes.
2478 * PARAMETER(S):
2479 * [I] HWND : window handle
2480 * [O] LISTVIEW_ITEM *: destination item
2481 * [I] LPLVITEM : source item
2482 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2484 * RETURN:
2485 * SUCCCESS : TRUE
2486 * FAILURE : FALSE
2488 static BOOL LISTVIEW_InitItemT(HWND hwnd, LISTVIEW_ITEM *lpItem,
2489 LPLVITEMW lpLVItem, BOOL isW)
2491 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2492 BOOL bResult = FALSE;
2494 if ((lpItem != NULL) && (lpLVItem != NULL))
2496 bResult = TRUE;
2498 if (lpLVItem->mask & LVIF_STATE)
2500 lpItem->state &= ~lpLVItem->stateMask;
2501 lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
2504 if (lpLVItem->mask & LVIF_IMAGE)
2505 lpItem->iImage = lpLVItem->iImage;
2507 if (lpLVItem->mask & LVIF_PARAM)
2508 lpItem->lParam = lpLVItem->lParam;
2510 if (lpLVItem->mask & LVIF_INDENT)
2511 lpItem->iIndent = lpLVItem->iIndent;
2513 if (lpLVItem->mask & LVIF_TEXT)
2515 if (lpLVItem->pszText == LPSTR_TEXTCALLBACKW)
2517 if ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
2518 return FALSE;
2520 if (is_textW(lpItem->pszText))
2521 COMCTL32_Free(lpItem->pszText);
2523 lpItem->pszText = LPSTR_TEXTCALLBACKW;
2525 else
2526 bResult = textsetptrT(&lpItem->pszText, lpLVItem->pszText, isW);
2530 return bResult;
2533 /***
2534 * DESCRIPTION:
2535 * Initializes subitem attributes.
2537 * NOTE: The documentation specifies that the operation fails if the user
2538 * tries to set the indent of a subitem.
2540 * PARAMETER(S):
2541 * [I] HWND : window handle
2542 * [O] LISTVIEW_SUBITEM *: destination subitem
2543 * [I] LPLVITEM : source subitem
2544 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2546 * RETURN:
2547 * SUCCCESS : TRUE
2548 * FAILURE : FALSE
2550 static BOOL LISTVIEW_InitSubItemT(HWND hwnd, LISTVIEW_SUBITEM *lpSubItem,
2551 LPLVITEMW lpLVItem, BOOL isW)
2553 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2554 BOOL bResult = FALSE;
2556 TRACE("(hwnd=%x, lpSubItem=%p, lpLVItem=%s, isW=%d)\n",
2557 hwnd, lpSubItem, debuglvitem_t(lpLVItem, isW), isW);
2559 if ((lpSubItem != NULL) && (lpLVItem != NULL))
2561 if (!(lpLVItem->mask & LVIF_INDENT))
2563 bResult = TRUE;
2565 lpSubItem->iSubItem = lpLVItem->iSubItem;
2567 if (lpLVItem->mask & LVIF_IMAGE)
2568 lpSubItem->iImage = lpLVItem->iImage;
2570 if (lpLVItem->mask & LVIF_TEXT)
2572 if (lpLVItem->pszText == LPSTR_TEXTCALLBACKW)
2574 if ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
2575 return FALSE;
2577 if (is_textW(lpSubItem->pszText))
2578 COMCTL32_Free(lpSubItem->pszText);
2580 lpSubItem->pszText = LPSTR_TEXTCALLBACKW;
2582 else
2583 bResult = textsetptrT(&lpSubItem->pszText, lpLVItem->pszText, isW);
2588 return bResult;
2591 /***
2592 * DESCRIPTION:
2593 * Adds a subitem at a given position (column index).
2595 * PARAMETER(S):
2596 * [I] HWND : window handle
2597 * [I] LPLVITEM : new subitem atttributes
2598 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2600 * RETURN:
2601 * SUCCESS : TRUE
2602 * FAILURE : FALSE
2604 static BOOL LISTVIEW_AddSubItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
2606 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2607 LISTVIEW_SUBITEM *lpSubItem = NULL;
2608 BOOL bResult = FALSE;
2609 HDPA hdpaSubItems;
2610 INT nPosition, nItem;
2611 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2613 TRACE("(hwnd=%x, lpLVItem=%s, isW=%d)\n", hwnd, debuglvitem_t(lpLVItem, isW), isW);
2615 if (lStyle & LVS_OWNERDATA)
2616 return FALSE;
2618 if (lpLVItem != NULL)
2620 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2621 if (hdpaSubItems != NULL)
2623 lpSubItem = (LISTVIEW_SUBITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM));
2624 if (lpSubItem != NULL)
2626 ZeroMemory(lpSubItem, sizeof(LISTVIEW_SUBITEM));
2627 if (LISTVIEW_InitSubItemT(hwnd, lpSubItem, lpLVItem, isW))
2629 nPosition = LISTVIEW_FindInsertPosition(hdpaSubItems,
2630 lpSubItem->iSubItem);
2631 nItem = DPA_InsertPtr(hdpaSubItems, nPosition, lpSubItem);
2632 if (nItem != -1) bResult = TRUE;
2638 /* cleanup if unsuccessful */
2639 if (!bResult && lpSubItem) COMCTL32_Free(lpSubItem);
2641 return bResult;
2644 /***
2645 * DESCRIPTION:
2646 * Finds the dpa insert position (array index).
2648 * PARAMETER(S):
2649 * [I] HWND : window handle
2650 * [I] INT : subitem index
2652 * RETURN:
2653 * SUCCESS : TRUE
2654 * FAILURE : FALSE
2656 static INT LISTVIEW_FindInsertPosition(HDPA hdpaSubItems, INT nSubItem)
2658 LISTVIEW_SUBITEM *lpSubItem;
2659 INT i;
2661 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2663 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2664 if (lpSubItem && lpSubItem->iSubItem > nSubItem)
2665 return i;
2668 return hdpaSubItems->nItemCount;
2671 /***
2672 * DESCRIPTION:
2673 * Retrieves a listview subitem at a given position (column index).
2675 * PARAMETER(S):
2676 * [I] HWND : window handle
2677 * [I] INT : subitem index
2679 * RETURN:
2680 * SUCCESS : TRUE
2681 * FAILURE : FALSE
2683 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItem(HDPA hdpaSubItems, INT nSubItem)
2685 LISTVIEW_SUBITEM *lpSubItem;
2686 INT i;
2688 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2690 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2691 if (lpSubItem != NULL)
2693 if (lpSubItem->iSubItem == nSubItem)
2694 return lpSubItem;
2695 else if (lpSubItem->iSubItem > nSubItem)
2696 return NULL;
2700 return NULL;
2703 /***
2704 * DESCRIPTION:
2705 * Sets item attributes.
2707 * PARAMETER(S):
2708 * [I] HWND : window handle
2709 * [I] LPLVITEM : new item atttributes
2710 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2712 * RETURN:
2713 * SUCCESS : TRUE
2714 * FAILURE : FALSE
2716 static BOOL LISTVIEW_SetMainItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
2718 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2719 BOOL bResult = FALSE;
2720 HDPA hdpaSubItems;
2721 LISTVIEW_ITEM *lpItem;
2722 NMLISTVIEW nmlv;
2723 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2724 UINT uChanged;
2725 UINT uView = lStyle & LVS_TYPEMASK;
2726 INT item_width;
2727 RECT rcItem;
2729 TRACE("(hwnd=%x, lpLVItem=%s, isW=%d)\n", hwnd, debuglvitem_t(lpLVItem, isW), isW);
2731 if (lStyle & LVS_OWNERDATA)
2733 if ((lpLVItem->iSubItem == 0)&&(lpLVItem->mask == LVIF_STATE))
2735 LVITEMW itm;
2737 ZeroMemory(&itm, sizeof(itm));
2738 itm.mask = LVIF_STATE | LVIF_PARAM;
2739 itm.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
2740 itm.iItem = lpLVItem->iItem;
2741 itm.iSubItem = 0;
2742 ListView_GetItemW(hwnd, &itm);
2745 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
2746 nmlv.uNewState = lpLVItem->state;
2747 nmlv.uOldState = itm.state;
2748 nmlv.uChanged = LVIF_STATE;
2749 nmlv.lParam = itm.lParam;
2750 nmlv.iItem = lpLVItem->iItem;
2752 if ((itm.state & lpLVItem->stateMask) !=
2753 (lpLVItem->state & lpLVItem->stateMask))
2756 * As per MSDN LVN_ITEMCHANGING notifications are _NOT_ sent
2757 * by LVS_OWERNDATA list controls
2759 if (lpLVItem->stateMask & LVIS_FOCUSED)
2761 if (lpLVItem->state & LVIS_FOCUSED)
2762 infoPtr->nFocusedItem = lpLVItem->iItem;
2763 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
2764 infoPtr->nFocusedItem = -1;
2766 if (lpLVItem->stateMask & LVIS_SELECTED)
2768 if (lpLVItem->state & LVIS_SELECTED)
2770 if (lStyle & LVS_SINGLESEL) LISTVIEW_RemoveAllSelections(hwnd);
2771 LISTVIEW_AddSelectionRange(hwnd,lpLVItem->iItem,lpLVItem->iItem);
2773 else
2774 LISTVIEW_RemoveSelectionRange(hwnd,lpLVItem->iItem,
2775 lpLVItem->iItem);
2778 listview_notify(hwnd, LVN_ITEMCHANGED, &nmlv);
2780 rcItem.left = LVIR_BOUNDS;
2781 LISTVIEW_GetItemRect(hwnd, lpLVItem->iItem, &rcItem);
2782 if (!infoPtr->bIsDrawing)
2783 InvalidateRect(hwnd, &rcItem, TRUE);
2785 return TRUE;
2787 return FALSE;
2790 if (lpLVItem != NULL)
2792 if (lpLVItem->iSubItem == 0)
2794 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2795 if (hdpaSubItems != NULL && hdpaSubItems != (HDPA)-1)
2797 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, lpLVItem->iSubItem);
2798 if (lpItem != NULL)
2800 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
2801 nmlv.lParam = lpItem->lParam;
2802 uChanged = LISTVIEW_GetItemChangesT(lpItem, lpLVItem, isW);
2803 if (uChanged != 0)
2805 if (uChanged & LVIF_STATE)
2807 nmlv.uNewState = lpLVItem->state & lpLVItem->stateMask;
2808 nmlv.uOldState = lpItem->state & lpLVItem->stateMask;
2810 if (nmlv.uNewState & LVIS_SELECTED)
2813 * This is redundant if called through SetSelection
2815 * however is required if the used directly calls SetItem
2816 * to set the selection.
2818 if (lStyle & LVS_SINGLESEL)
2819 LISTVIEW_RemoveAllSelections(hwnd);
2821 LISTVIEW_AddSelectionRange(hwnd,lpLVItem->iItem,
2822 lpLVItem->iItem);
2824 else if (lpLVItem->stateMask & LVIS_SELECTED)
2826 LISTVIEW_RemoveSelectionRange(hwnd,lpLVItem->iItem,
2827 lpLVItem->iItem);
2829 if (nmlv.uNewState & LVIS_FOCUSED)
2832 * This is a fun hoop to jump to try to catch if
2833 * the user is calling us directly to call focus or if
2834 * this function is being called as a result of a
2835 * SetItemFocus call.
2837 if (infoPtr->nFocusedItem >= 0)
2838 LISTVIEW_SetItemFocus(hwnd, lpLVItem->iItem);
2842 nmlv.uChanged = uChanged;
2843 nmlv.iItem = lpLVItem->iItem;
2844 nmlv.lParam = lpItem->lParam;
2845 /* send LVN_ITEMCHANGING notification */
2846 listview_notify(hwnd, LVN_ITEMCHANGING, &nmlv);
2848 /* copy information */
2849 bResult = LISTVIEW_InitItemT(hwnd, lpItem, lpLVItem, isW);
2851 /* if LVS_LIST or LVS_SMALLICON, update the width of the items
2852 based on the width of the items text */
2853 if((uView == LVS_LIST) || (uView == LVS_SMALLICON))
2855 item_width = LISTVIEW_GetStringWidthT(hwnd, lpItem->pszText, TRUE);
2857 if(item_width > infoPtr->nItemWidth)
2858 infoPtr->nItemWidth = item_width;
2861 /* send LVN_ITEMCHANGED notification */
2862 listview_notify(hwnd, LVN_ITEMCHANGED, &nmlv);
2864 else
2866 bResult = TRUE;
2869 if (uChanged)
2871 rcItem.left = LVIR_BOUNDS;
2872 LISTVIEW_GetItemRect(hwnd, lpLVItem->iItem, &rcItem);
2873 if (!infoPtr->bIsDrawing)
2874 InvalidateRect(hwnd, &rcItem, TRUE);
2881 return bResult;
2884 /***
2885 * DESCRIPTION:
2886 * Sets subitem attributes.
2888 * PARAMETER(S):
2889 * [I] HWND : window handle
2890 * [I] LPLVITEM : new subitem atttributes
2891 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2893 * RETURN:
2894 * SUCCESS : TRUE
2895 * FAILURE : FALSE
2897 static BOOL LISTVIEW_SetSubItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
2899 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2900 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2901 BOOL bResult = FALSE;
2902 HDPA hdpaSubItems;
2903 LISTVIEW_SUBITEM *lpSubItem;
2904 RECT rcItem;
2906 TRACE("(hwnd=%x, lpLVItem=%s, isW=%d)\n", hwnd, debuglvitem_t(lpLVItem, isW), isW);
2908 if (lStyle & LVS_OWNERDATA)
2909 return FALSE;
2911 if (lpLVItem != NULL)
2913 if (lpLVItem->iSubItem > 0)
2915 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2916 if (hdpaSubItems != NULL)
2918 /* set subitem only if column is present */
2919 if (Header_GetItemCount(infoPtr->hwndHeader) > lpLVItem->iSubItem)
2921 lpSubItem = LISTVIEW_GetSubItem(hdpaSubItems, lpLVItem->iSubItem);
2922 if (lpSubItem != NULL)
2923 bResult = LISTVIEW_InitSubItemT(hwnd, lpSubItem, lpLVItem, isW);
2924 else
2925 bResult = LISTVIEW_AddSubItemT(hwnd, lpLVItem, isW);
2927 rcItem.left = LVIR_BOUNDS;
2928 LISTVIEW_GetItemRect(hwnd, lpLVItem->iItem, &rcItem);
2929 InvalidateRect(hwnd, &rcItem, FALSE);
2935 return bResult;
2938 /***
2939 * DESCRIPTION:
2940 * Sets item attributes.
2942 * PARAMETER(S):
2943 * [I] HWND : window handle
2944 * [I] LPLVITEM : new item atttributes
2945 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2947 * RETURN:
2948 * SUCCESS : TRUE
2949 * FAILURE : FALSE
2951 static BOOL LISTVIEW_SetItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
2953 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2955 if (!lpLVItem || lpLVItem->iItem < 0 ||
2956 lpLVItem->iItem>=GETITEMCOUNT(infoPtr))
2957 return FALSE;
2958 if (lpLVItem->iSubItem == 0)
2959 return LISTVIEW_SetMainItemT(hwnd, lpLVItem, isW);
2960 else
2961 return LISTVIEW_SetSubItemT(hwnd, lpLVItem, isW);
2964 /***
2965 * DESCRIPTION:
2966 * Retrieves the index of the item at coordinate (0, 0) of the client area.
2968 * PARAMETER(S):
2969 * [I] HWND : window handle
2971 * RETURN:
2972 * item index
2974 static INT LISTVIEW_GetTopIndex(HWND hwnd)
2976 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2977 UINT uView = lStyle & LVS_TYPEMASK;
2978 INT nItem = 0;
2979 SCROLLINFO scrollInfo;
2981 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
2982 scrollInfo.cbSize = sizeof(SCROLLINFO);
2983 scrollInfo.fMask = SIF_POS;
2985 if (uView == LVS_LIST)
2987 if ((lStyle & WS_HSCROLL) && GetScrollInfo(hwnd, SB_HORZ, &scrollInfo))
2988 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(hwnd);
2990 else if (uView == LVS_REPORT)
2992 if ((lStyle & WS_VSCROLL) && GetScrollInfo(hwnd, SB_VERT, &scrollInfo))
2993 nItem = scrollInfo.nPos;
2996 return nItem;
2999 /***
3000 * DESCRIPTION:
3001 * Draws a subitem.
3003 * PARAMETER(S):
3004 * [I] HWND : window handle
3005 * [I] HDC : device context handle
3006 * [I] INT : item index
3007 * [I] INT : subitem index
3008 * [I] RECT * : clipping rectangle
3010 * RETURN:
3011 * None
3013 static VOID LISTVIEW_DrawSubItem(HWND hwnd, HDC hdc, INT nItem, INT nSubItem,
3014 RECT rcItem, BOOL Selected)
3016 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3017 WCHAR szDispText[DISP_TEXT_SIZE];
3018 LVITEMW lvItem;
3019 LVCOLUMNW lvColumn;
3020 UINT textoutOptions = ETO_CLIPPED | ETO_OPAQUE;
3021 RECT rcTemp;
3022 INT textLeft;
3023 INT nLabelWidth = 0;
3025 TRACE("(hwnd=%x, hdc=%x, nItem=%d, nSubItem=%d)\n", hwnd, hdc,
3026 nItem, nSubItem);
3028 /* get information needed for drawing the item */
3029 ZeroMemory(&lvItem, sizeof(lvItem));
3030 lvItem.mask = LVIF_TEXT;
3031 lvItem.iItem = nItem;
3032 lvItem.iSubItem = nSubItem;
3033 lvItem.cchTextMax = DISP_TEXT_SIZE;
3034 lvItem.pszText = szDispText;
3035 *lvItem.pszText = '\0';
3036 LISTVIEW_GetItemW(hwnd, &lvItem, TRUE);
3037 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3039 ZeroMemory(&lvColumn, sizeof(lvColumn));
3040 lvColumn.mask = LVCF_FMT;
3041 LISTVIEW_GetColumnT(hwnd, nSubItem, &lvColumn, TRUE);
3042 textLeft = rcItem.left;
3043 if (lvColumn.fmt != LVCFMT_LEFT)
3045 if ((nLabelWidth = LISTVIEW_GetStringWidthT(hwnd, lvItem.pszText, TRUE)))
3047 if (lvColumn.fmt == LVCFMT_RIGHT)
3048 textLeft = rcItem.right - nLabelWidth;
3049 else
3050 textLeft = rcItem.left + (rcItem.right-rcItem.left-nLabelWidth)/2;
3055 /* redraw the background of the item */
3056 rcTemp = rcItem;
3057 if(infoPtr->nColumnCount == (nSubItem + 1))
3058 rcTemp.right = infoPtr->rcList.right;
3059 else
3060 rcTemp.right += WIDTH_PADDING;
3062 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3064 /* set item colors */
3065 if (ListView_GetItemState(hwnd,nItem,LVIS_SELECTED) && Selected)
3067 if (infoPtr->bFocus)
3069 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
3070 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
3072 else
3074 SetBkColor(hdc, GetSysColor(COLOR_3DFACE));
3075 SetTextColor(hdc, GetSysColor(COLOR_BTNTEXT));
3078 else
3080 if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
3082 SetBkMode(hdc, TRANSPARENT);
3083 textoutOptions &= ~ETO_OPAQUE;
3085 else
3087 SetBkMode(hdc, OPAQUE);
3088 SetBkColor(hdc, infoPtr->clrTextBk);
3091 SetTextColor(hdc, infoPtr->clrText);
3094 ExtTextOutW(hdc, textLeft, rcItem.top, textoutOptions,
3095 &rcItem, lvItem.pszText, lstrlenW(lvItem.pszText), NULL);
3097 if (Selected)
3099 /* fill in the gap */
3100 RECT rec;
3101 if (nSubItem < Header_GetItemCount(infoPtr->hwndHeader)-1)
3103 CopyRect(&rec,&rcItem);
3104 rec.left = rec.right;
3105 rec.right = rec.left+REPORT_MARGINX;
3106 ExtTextOutW(hdc, rec.left , rec.top, textoutOptions,
3107 &rec, NULL, 0, NULL);
3109 CopyRect(&rec,&rcItem);
3110 rec.right = rec.left;
3111 rec.left = rec.left - REPORT_MARGINX;
3112 ExtTextOutW(hdc, rec.left , rec.top, textoutOptions,
3113 &rec, NULL, 0, NULL);
3118 /***
3119 * DESCRIPTION:
3120 * Draws an item.
3122 * PARAMETER(S):
3123 * [I] HWND : window handle
3124 * [I] HDC : device context handle
3125 * [I] INT : item index
3126 * [I] RECT * : clipping rectangle
3128 * RETURN:
3129 * None
3131 static VOID LISTVIEW_DrawItem(HWND hwnd, HDC hdc, INT nItem, RECT rcItem, BOOL FullSelect, RECT* SuggestedFocus)
3133 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3134 WCHAR szDispText[DISP_TEXT_SIZE];
3135 INT nLabelWidth;
3136 LVITEMW lvItem;
3137 INT nMixMode;
3138 DWORD dwBkColor;
3139 DWORD dwTextColor,dwTextX;
3140 BOOL bImage = FALSE;
3141 INT iBkMode = -1;
3142 UINT textoutOptions = ETO_OPAQUE | ETO_CLIPPED;
3143 RECT rcTemp;
3145 TRACE("(hwnd=%x, hdc=%x, nItem=%d)\n", hwnd, hdc, nItem);
3148 /* get information needed for drawing the item */
3149 ZeroMemory(&lvItem, sizeof(lvItem));
3150 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE | LVIF_INDENT;
3151 lvItem.stateMask = LVIS_SELECTED | LVIS_STATEIMAGEMASK;
3152 lvItem.iItem = nItem;
3153 lvItem.iSubItem = 0;
3154 lvItem.cchTextMax = DISP_TEXT_SIZE;
3155 lvItem.pszText = szDispText;
3156 *lvItem.pszText = '\0';
3157 LISTVIEW_GetItemW(hwnd, &lvItem, TRUE);
3158 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3160 /* redraw the background of the item */
3161 rcTemp = rcItem;
3162 if(infoPtr->nColumnCount == (nItem + 1))
3163 rcTemp.right = infoPtr->rcList.right;
3164 else
3165 rcTemp.right+=WIDTH_PADDING;
3167 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3169 /* do indent */
3170 if (lvItem.iIndent>0 && infoPtr->iconSize.cx > 0)
3172 rcItem.left += infoPtr->iconSize.cx * lvItem.iIndent;
3174 if (SuggestedFocus)
3175 SuggestedFocus->left += infoPtr->iconSize.cx * lvItem.iIndent;
3178 /* state icons */
3179 if (infoPtr->himlState != NULL)
3181 UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12;
3182 if (uStateImage > 0)
3184 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, rcItem.left,
3185 rcItem.top, ILD_NORMAL);
3188 rcItem.left += infoPtr->iconSize.cx;
3189 if (SuggestedFocus)
3190 SuggestedFocus->left += infoPtr->iconSize.cx;
3191 bImage = TRUE;
3194 /* small icons */
3195 if (infoPtr->himlSmall != NULL)
3197 if ((lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus != FALSE) &&
3198 (lvItem.iImage>=0))
3200 ImageList_SetBkColor(infoPtr->himlSmall, CLR_NONE);
3201 ImageList_Draw(infoPtr->himlSmall, lvItem.iImage, hdc, rcItem.left,
3202 rcItem.top, ILD_SELECTED);
3204 else if (lvItem.iImage>=0)
3206 ImageList_SetBkColor(infoPtr->himlSmall, CLR_NONE);
3207 ImageList_Draw(infoPtr->himlSmall, lvItem.iImage, hdc, rcItem.left,
3208 rcItem.top, ILD_NORMAL);
3211 rcItem.left += infoPtr->iconSize.cx;
3213 if (SuggestedFocus)
3214 SuggestedFocus->left += infoPtr->iconSize.cx;
3215 bImage = TRUE;
3218 /* Don't bother painting item being edited */
3219 if (infoPtr->hwndEdit && lvItem.state & LVIS_FOCUSED && !FullSelect)
3220 return;
3222 if ((lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus != FALSE))
3224 /* set item colors */
3225 dwBkColor = SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
3226 dwTextColor = SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
3227 /* set raster mode */
3228 nMixMode = SetROP2(hdc, R2_XORPEN);
3230 else if ((GetWindowLongW(hwnd, GWL_STYLE) & LVS_SHOWSELALWAYS) &&
3231 (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus == FALSE))
3233 dwBkColor = SetBkColor(hdc, GetSysColor(COLOR_3DFACE));
3234 dwTextColor = SetTextColor(hdc, GetSysColor(COLOR_BTNTEXT));
3235 /* set raster mode */
3236 nMixMode = SetROP2(hdc, R2_COPYPEN);
3238 else
3240 /* set item colors */
3241 if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
3243 dwBkColor = GetBkColor(hdc);
3244 iBkMode = SetBkMode(hdc, TRANSPARENT);
3245 textoutOptions &= ~ETO_OPAQUE;
3247 else
3249 dwBkColor = SetBkColor(hdc, infoPtr->clrTextBk);
3250 iBkMode = SetBkMode(hdc, OPAQUE);
3253 dwTextColor = SetTextColor(hdc, infoPtr->clrText);
3254 /* set raster mode */
3255 nMixMode = SetROP2(hdc, R2_COPYPEN);
3258 nLabelWidth = ListView_GetStringWidthW(hwnd, lvItem.pszText);
3259 if (rcItem.left + nLabelWidth < rcItem.right)
3261 if (!FullSelect)
3262 rcItem.right = rcItem.left + nLabelWidth + TRAILING_PADDING;
3263 if (bImage)
3264 rcItem.right += IMAGE_PADDING;
3267 /* draw label */
3268 dwTextX = rcItem.left + 1;
3269 if (bImage)
3270 dwTextX += IMAGE_PADDING;
3272 if (lvItem.pszText)
3273 ExtTextOutW(hdc, dwTextX, rcItem.top, textoutOptions,
3274 &rcItem, lvItem.pszText, lstrlenW(lvItem.pszText), NULL);
3276 if ((FullSelect)&&(Header_GetItemCount(infoPtr->hwndHeader) > 1))
3278 /* fill in the gap */
3279 RECT rec;
3280 CopyRect(&rec,&rcItem);
3281 rec.left = rec.right;
3282 rec.right = rec.left+REPORT_MARGINX;
3283 ExtTextOutW(hdc, rec.left , rec.top, textoutOptions,
3284 &rec, NULL, 0, NULL);
3287 if (!FullSelect)
3288 CopyRect(SuggestedFocus,&rcItem);
3290 if (nMixMode != 0)
3292 SetROP2(hdc, R2_COPYPEN);
3293 SetBkColor(hdc, dwBkColor);
3294 SetTextColor(hdc, dwTextColor);
3295 if (iBkMode != -1)
3296 SetBkMode(hdc, iBkMode);
3300 /***
3301 * DESCRIPTION:
3302 * Draws an item when in large icon display mode.
3304 * PARAMETER(S):
3305 * [I] HWND : window handle
3306 * [I] HDC : device context handle
3307 * [I] INT : item index
3308 * [I] RECT : clipping rectangle
3309 * [O] RECT * : The text rectangle about which to draw the focus
3311 * RETURN:
3312 * None
3314 static VOID LISTVIEW_DrawLargeItem(HWND hwnd, HDC hdc, INT nItem, RECT rcItem,
3315 RECT *SuggestedFocus)
3317 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3318 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3319 LVITEMW lvItem;
3320 UINT uFormat = DT_TOP | DT_CENTER | DT_WORDBREAK | DT_NOPREFIX |
3321 DT_EDITCONTROL ;
3322 /* Maintain this format in line with the one in LISTVIEW_UpdateLargeItemLabelRect*/
3323 RECT rcTemp;
3325 TRACE("(hwnd=%x, hdc=%x, nItem=%d, left=%d, top=%d, right=%d, bottom=%d)\n",
3326 hwnd, hdc, nItem, rcItem.left, rcItem.top, rcItem.right, rcItem.bottom);
3328 /* get information needed for drawing the item */
3329 ZeroMemory(&lvItem, sizeof(lvItem));
3330 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE;
3331 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3332 lvItem.iItem = nItem;
3333 lvItem.iSubItem = 0;
3334 lvItem.cchTextMax = DISP_TEXT_SIZE;
3335 lvItem.pszText = szDispText;
3336 *lvItem.pszText = '\0';
3337 LISTVIEW_GetItemW(hwnd, &lvItem, FALSE);
3338 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3340 /* redraw the background of the item */
3341 rcTemp = rcItem;
3342 if(infoPtr->nColumnCount == (nItem + 1))
3343 rcTemp.right = infoPtr->rcList.right;
3344 else
3345 rcTemp.right+=WIDTH_PADDING;
3346 /* The comment doesn't say WIDTH_PADDING applies to large icons */
3348 TRACE("background rect (%d,%d)-(%d,%d)\n",
3349 rcTemp.left, rcTemp.top, rcTemp.right, rcTemp.bottom);
3351 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3354 /* Figure out text colours etc. depending on state
3355 * At least the following states exist; there may be more.
3356 * Many items may be selected
3357 * At most one item may have the focus
3358 * The application may not actually be active currently
3359 * 1. The item is not selected in any way
3360 * 2. The cursor is flying over the icon or text and the text is being
3361 * expanded because it is not fully displayed currently.
3362 * 3. The item is selected and is focussed, i.e. the user has not clicked
3363 * in the blank area of the window, and the window (or application?)
3364 * still has the focus.
3365 * 4. As 3 except that a different window has the focus
3366 * 5. The item is the selected item of all the items, but the user has
3367 * clicked somewhere else on the window.
3368 * Only a few of these are handled currently. In particular 2 is not yet
3369 * handled since we do not support the functionality currently (or at least
3370 * we didn't when I wrote this)
3373 if (lvItem.state & LVIS_SELECTED)
3375 /* set item colors */
3376 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
3377 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
3378 SetBkMode (hdc, OPAQUE);
3379 /* set raster mode */
3380 SetROP2(hdc, R2_XORPEN);
3381 /* When exactly is it in XOR? while being dragged? */
3383 else
3385 /* set item colors */
3386 if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
3388 SetBkMode(hdc, TRANSPARENT);
3390 else
3392 SetBkMode(hdc, OPAQUE);
3393 SetBkColor(hdc, infoPtr->clrTextBk);
3396 SetTextColor(hdc, infoPtr->clrText);
3397 /* set raster mode */
3398 SetROP2(hdc, R2_COPYPEN);
3401 /* In cases 2,3 and 5 (see above) the full text is displayed, with word
3402 * wrapping and long words split.
3403 * In cases 1 and 4 only a portion of the text is displayed with word
3404 * wrapping and both word and end ellipsis. (I don't yet know about path
3405 * ellipsis)
3407 uFormat |= ( (lvItem.state & LVIS_FOCUSED) && infoPtr->bFocus) ?
3408 DT_NOCLIP
3410 DT_WORD_ELLIPSIS | DT_END_ELLIPSIS;
3412 /* draw the icon */
3413 if (infoPtr->himlNormal != NULL)
3415 if (lvItem.iImage >= 0)
3417 ImageList_Draw (infoPtr->himlNormal, lvItem.iImage, hdc, rcItem.left,
3418 rcItem.top,
3419 (lvItem.state & LVIS_SELECTED) ? ILD_SELECTED : ILD_NORMAL);
3423 /* Draw the text below the icon */
3425 /* Don't bother painting item being edited */
3426 if ((infoPtr->hwndEdit && (lvItem.state & LVIS_FOCUSED)) ||
3427 !lstrlenW(lvItem.pszText))
3429 SetRectEmpty(SuggestedFocus);
3430 return;
3433 /* Since rcItem.left is left point of icon, compute left point of item box */
3434 rcItem.left -= ((infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2);
3435 rcItem.right = rcItem.left + infoPtr->nItemWidth;
3436 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3437 TRACE("bound box for text+icon (%d,%d)-(%d,%d), iS.cx=%ld, nItemWidth=%d\n",
3438 rcItem.left, rcItem.top, rcItem.right, rcItem.bottom,
3439 infoPtr->iconSize.cx, infoPtr->nItemWidth);
3440 TRACE("rcList (%d,%d)-(%d,%d), rcView (%d,%d)-(%d,%d)\n",
3441 infoPtr->rcList.left, infoPtr->rcList.top,
3442 infoPtr->rcList.right, infoPtr->rcList.bottom,
3443 infoPtr->rcView.left, infoPtr->rcView.top,
3444 infoPtr->rcView.right, infoPtr->rcView.bottom);
3446 InflateRect(&rcItem, -(2*CAPTION_BORDER), 0);
3447 rcItem.top += infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
3450 /* draw label */
3452 /* I am sure of most of the uFormat values. However I am not sure about
3453 * whether we need or do not need the following:
3454 * DT_EXTERNALLEADING, DT_INTERNAL, DT_CALCRECT, DT_NOFULLWIDTHCHARBREAK,
3455 * DT_PATH_ELLIPSIS, DT_RTLREADING,
3456 * We certainly do not need
3457 * DT_BOTTOM, DT_VCENTER, DT_MODIFYSTRING, DT_LEFT, DT_RIGHT, DT_PREFIXONLY,
3458 * DT_SINGLELINE, DT_TABSTOP, DT_EXPANDTABS
3461 /* If the text is being drawn without clipping (i.e. the full text) then we
3462 * need to jump through a few hoops to ensure that it all gets displayed and
3463 * that the background is complete
3465 if (uFormat & DT_NOCLIP)
3467 RECT rcBack=rcItem;
3468 HBRUSH hBrush = CreateSolidBrush(GetBkColor (hdc));
3469 int dx, dy, old_wid, new_wid;
3470 DrawTextW (hdc, lvItem.pszText, -1, &rcItem, uFormat | DT_CALCRECT);
3471 /* Microsoft, in their great wisdom, have decided that the rectangle
3472 * returned by DrawText on DT_CALCRECT will only guarantee the dimension,
3473 * not the location. So we have to do the centring ourselves (and take
3474 * responsibility for agreeing off-by-one consistency with them).
3476 old_wid = rcItem.right-rcItem.left;
3477 new_wid = rcBack.right - rcBack.left;
3478 dx = rcBack.left - rcItem.left + (new_wid-old_wid)/2;
3479 dy = rcBack.top - rcItem.top;
3480 OffsetRect (&rcItem, dx, dy);
3481 FillRect(hdc, &rcItem, hBrush);
3482 DeleteObject(hBrush);
3484 /* else ? What if we are losing the focus? will we not get a complete
3485 * background?
3487 DrawTextW (hdc, lvItem.pszText, -1, &rcItem, uFormat);
3489 CopyRect(SuggestedFocus, &rcItem);
3492 /***
3493 * DESCRIPTION:
3494 * Draws listview items when in report display mode.
3496 * PARAMETER(S):
3497 * [I] HWND : window handle
3498 * [I] HDC : device context handle
3500 * RETURN:
3501 * None
3503 static VOID LISTVIEW_RefreshReport(HWND hwnd, HDC hdc, DWORD cdmode)
3505 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd,0);
3506 SCROLLINFO scrollInfo;
3507 INT nDrawPosY = infoPtr->rcList.top;
3508 INT nColumnCount;
3509 RECT rcItem, rcTemp;
3510 INT j;
3511 INT nItem;
3512 INT nLast;
3513 BOOL FullSelected;
3514 DWORD cditemmode = CDRF_DODEFAULT;
3515 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
3516 INT scrollOffset;
3518 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
3519 scrollInfo.cbSize = sizeof(SCROLLINFO);
3520 scrollInfo.fMask = SIF_POS;
3522 nItem = ListView_GetTopIndex(hwnd);
3524 /* add 1 for displaying a partial item at the bottom */
3525 nLast = nItem + LISTVIEW_GetCountPerColumn(hwnd) + 1;
3526 nLast = min(nLast, GETITEMCOUNT(infoPtr));
3528 /* send cache hint notification */
3529 if (GetWindowLongW(hwnd,GWL_STYLE) & LVS_OWNERDATA)
3531 NMLVCACHEHINT nmlv;
3533 nmlv.hdr.hwndFrom = hwnd;
3534 nmlv.hdr.idFrom = GetWindowLongW(hwnd,GWL_ID);
3535 nmlv.hdr.code = LVN_ODCACHEHINT;
3536 nmlv.iFrom = nItem;
3537 nmlv.iTo = nLast;
3539 SendMessageW(GetParent(hwnd), WM_NOTIFY, (WPARAM)nmlv.hdr.idFrom,
3540 (LPARAM)&nmlv);
3543 nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
3544 infoPtr->nColumnCount = nColumnCount; /* update nColumnCount */
3545 FullSelected = infoPtr->dwExStyle & LVS_EX_FULLROWSELECT;
3547 /* clear the background of any part of the control that doesn't contain items */
3548 SubtractRect(&rcTemp, &infoPtr->rcList, &infoPtr->rcView);
3549 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3551 /* nothing to draw */
3552 if(GETITEMCOUNT(infoPtr) == 0)
3553 return;
3555 /* Get scroll bar info once before loop */
3556 GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
3557 scrollOffset = scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE;
3559 for (; nItem < nLast; nItem++)
3561 RECT SuggestedFocusRect;
3563 /* Do Owner Draw */
3564 if (lStyle & LVS_OWNERDRAWFIXED)
3566 UINT uID = GetWindowLongW( hwnd, GWL_ID);
3567 DRAWITEMSTRUCT dis;
3568 LVITEMW item;
3569 RECT br;
3571 TRACE("Owner Drawn\n");
3572 dis.CtlType = ODT_LISTVIEW;
3573 dis.CtlID = uID;
3574 dis.itemID = nItem;
3575 dis.itemAction = ODA_DRAWENTIRE;
3576 dis.itemState = 0;
3578 if (LISTVIEW_IsSelected(hwnd,nItem)) dis.itemState|=ODS_SELECTED;
3579 if (nItem==infoPtr->nFocusedItem) dis.itemState|=ODS_FOCUS;
3581 dis.hwndItem = hwnd;
3582 dis.hDC = hdc;
3584 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
3586 dis.rcItem.left = -scrollOffset;
3587 dis.rcItem.right = max(dis.rcItem.left, br.right - scrollOffset);
3588 dis.rcItem.top = nDrawPosY;
3589 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3591 ZeroMemory(&item,sizeof(item));
3592 item.iItem = nItem;
3593 item.mask = LVIF_PARAM;
3594 ListView_GetItemW(hwnd, &item);
3596 dis.itemData = item.lParam;
3598 if (SendMessageW(GetParent(hwnd),WM_DRAWITEM,(WPARAM)uID,(LPARAM)&dis))
3600 nDrawPosY += infoPtr->nItemHeight;
3601 continue;
3605 if (FullSelected)
3607 RECT ir,br;
3609 Header_GetItemRect(infoPtr->hwndHeader, 0, &ir);
3610 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
3612 ir.left += REPORT_MARGINX;
3613 ir.right = max(ir.left, br.right - REPORT_MARGINX);
3614 ir.top = nDrawPosY;
3615 ir.bottom = ir.top + infoPtr->nItemHeight;
3617 CopyRect(&SuggestedFocusRect,&ir);
3620 for (j = 0; j < nColumnCount; j++)
3622 if (cdmode & CDRF_NOTIFYITEMDRAW)
3623 cditemmode = LISTVIEW_SendCustomDrawItemNotify (hwnd, hdc, nItem, j,
3624 CDDS_ITEMPREPAINT);
3625 if (cditemmode & CDRF_SKIPDEFAULT)
3626 continue;
3628 Header_GetItemRect(infoPtr->hwndHeader, j, &rcItem);
3630 rcItem.left += REPORT_MARGINX;
3631 rcItem.right = max(rcItem.left, rcItem.right - REPORT_MARGINX);
3632 rcItem.top = nDrawPosY;
3633 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3635 /* Offset the Scroll Bar Pos */
3636 rcItem.left -= scrollOffset;
3637 rcItem.right -= scrollOffset;
3639 if (j == 0)
3641 LISTVIEW_DrawItem(hwnd, hdc, nItem, rcItem, FullSelected,
3642 &SuggestedFocusRect);
3644 else
3646 LISTVIEW_DrawSubItem(hwnd, hdc, nItem, j, rcItem, FullSelected);
3649 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3650 LISTVIEW_SendCustomDrawItemNotify(hwnd, hdc, nItem, 0,
3651 CDDS_ITEMPOSTPAINT);
3654 * Draw Focus Rect
3656 if (LISTVIEW_GetItemState(hwnd,nItem,LVIS_FOCUSED) && infoPtr->bFocus)
3658 BOOL rop=FALSE;
3659 if (FullSelected && LISTVIEW_GetItemState(hwnd,nItem,LVIS_SELECTED))
3660 rop = SetROP2(hdc, R2_XORPEN);
3662 Rectangle(hdc, SuggestedFocusRect.left, SuggestedFocusRect.top,
3663 SuggestedFocusRect.right,SuggestedFocusRect.bottom);
3665 if (rop)
3666 SetROP2(hdc, R2_COPYPEN);
3668 nDrawPosY += infoPtr->nItemHeight;
3672 /***
3673 * DESCRIPTION:
3674 * Retrieves the number of items that can fit vertically in the client area.
3676 * PARAMETER(S):
3677 * [I] HWND : window handle
3679 * RETURN:
3680 * Number of items per row.
3682 static INT LISTVIEW_GetCountPerRow(HWND hwnd)
3684 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd,0);
3685 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3686 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
3687 INT nCountPerRow = 1;
3689 if (nListWidth > 0)
3691 if (uView != LVS_REPORT)
3693 nCountPerRow = nListWidth / infoPtr->nItemWidth;
3694 if (nCountPerRow == 0) nCountPerRow = 1;
3698 return nCountPerRow;
3701 /***
3702 * DESCRIPTION:
3703 * Retrieves the number of items that can fit horizontally in the client
3704 * area.
3706 * PARAMETER(S):
3707 * [I] HWND : window handle
3709 * RETURN:
3710 * Number of items per column.
3712 static INT LISTVIEW_GetCountPerColumn(HWND hwnd)
3714 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd,0);
3715 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3716 INT nCountPerColumn = 1;
3718 if (nListHeight > 0)
3720 nCountPerColumn = nListHeight / infoPtr->nItemHeight;
3721 if (nCountPerColumn == 0) nCountPerColumn = 1;
3724 return nCountPerColumn;
3727 /***
3728 * DESCRIPTION:
3729 * Retrieves the number of columns needed to display all the items when in
3730 * list display mode.
3732 * PARAMETER(S):
3733 * [I] HWND : window handle
3735 * RETURN:
3736 * Number of columns.
3738 static INT LISTVIEW_GetColumnCount(HWND hwnd)
3740 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3741 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
3742 INT nColumnCount = 0;
3744 if ((lStyle & LVS_TYPEMASK) == LVS_LIST)
3746 nColumnCount = infoPtr->rcList.right / infoPtr->nItemWidth;
3747 if (infoPtr->rcList.right % infoPtr->nItemWidth) nColumnCount++;
3750 return nColumnCount;
3754 /***
3755 * DESCRIPTION:
3756 * Draws listview items when in list display mode.
3758 * PARAMETER(S):
3759 * [I] HWND : window handle
3760 * [I] HDC : device context handle
3762 * RETURN:
3763 * None
3765 static VOID LISTVIEW_RefreshList(HWND hwnd, HDC hdc, DWORD cdmode)
3767 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3768 RECT rcItem, FocusRect, rcTemp;
3769 INT i, j;
3770 INT nItem;
3771 INT nColumnCount;
3772 INT nCountPerColumn;
3773 INT nItemWidth = infoPtr->nItemWidth;
3774 INT nItemHeight = infoPtr->nItemHeight;
3775 DWORD cditemmode = CDRF_DODEFAULT;
3777 /* get number of fully visible columns */
3778 nColumnCount = LISTVIEW_GetColumnCount(hwnd);
3779 infoPtr->nColumnCount = nColumnCount;
3780 nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
3781 nItem = ListView_GetTopIndex(hwnd);
3783 /* paint the background of the control that doesn't contain any items */
3784 SubtractRect(&rcTemp, &infoPtr->rcList, &infoPtr->rcView);
3785 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3787 /* nothing to draw, return here */
3788 if(GETITEMCOUNT(infoPtr) == 0)
3789 return;
3791 for (i = 0; i < nColumnCount; i++)
3793 for (j = 0; j < nCountPerColumn; j++, nItem++)
3795 if (nItem >= GETITEMCOUNT(infoPtr))
3796 return;
3798 if (cdmode & CDRF_NOTIFYITEMDRAW)
3799 cditemmode = LISTVIEW_SendCustomDrawItemNotify (hwnd, hdc, nItem, 0,
3800 CDDS_ITEMPREPAINT);
3801 if (cditemmode & CDRF_SKIPDEFAULT)
3802 continue;
3804 rcItem.top = j * nItemHeight;
3805 rcItem.left = i * nItemWidth;
3806 rcItem.bottom = rcItem.top + nItemHeight;
3807 rcItem.right = rcItem.left + nItemWidth;
3808 LISTVIEW_DrawItem(hwnd, hdc, nItem, rcItem, FALSE, &FocusRect);
3810 * Draw Focus Rect
3812 if (LISTVIEW_GetItemState(hwnd,nItem,LVIS_FOCUSED) && infoPtr->bFocus)
3813 Rectangle(hdc, FocusRect.left, FocusRect.top,
3814 FocusRect.right,FocusRect.bottom);
3816 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3817 LISTVIEW_SendCustomDrawItemNotify(hwnd, hdc, nItem, 0,
3818 CDDS_ITEMPOSTPAINT);
3824 /***
3825 * DESCRIPTION:
3826 * Draws listview items when in icon or small icon display mode.
3828 * PARAMETER(S):
3829 * [I] HWND : window handle
3830 * [I] HDC : device context handle
3832 * RETURN:
3833 * None
3835 static VOID LISTVIEW_RefreshIcon(HWND hwnd, HDC hdc, BOOL bSmall, DWORD cdmode)
3837 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3838 POINT ptPosition;
3839 POINT ptOrigin;
3840 RECT rcItem, SuggestedFocus, rcTemp;
3841 INT i;
3842 DWORD cditemmode = CDRF_DODEFAULT;
3844 infoPtr->nColumnCount = 1; /* set this to an arbitrary value to prevent */
3845 /* DrawItem from erasing the incorrect background area */
3847 /* paint the background of the control that doesn't contain any items */
3848 SubtractRect(&rcTemp, &infoPtr->rcList, &infoPtr->rcView);
3849 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3851 /* nothing to draw, return here */
3852 if(GETITEMCOUNT(infoPtr) == 0)
3853 return;
3855 LISTVIEW_GetOrigin(hwnd, &ptOrigin);
3856 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
3858 if (cdmode & CDRF_NOTIFYITEMDRAW)
3859 cditemmode = LISTVIEW_SendCustomDrawItemNotify (hwnd, hdc, i, 0,
3860 CDDS_ITEMPREPAINT);
3861 if (cditemmode & CDRF_SKIPDEFAULT)
3862 continue;
3864 LISTVIEW_GetItemPosition(hwnd, i, &ptPosition);
3865 ptPosition.x += ptOrigin.x;
3866 ptPosition.y += ptOrigin.y;
3868 if (ptPosition.y + infoPtr->nItemHeight > infoPtr->rcList.top)
3870 if (ptPosition.x + infoPtr->nItemWidth > infoPtr->rcList.left)
3872 if (ptPosition.y < infoPtr->rcList.bottom)
3874 if (ptPosition.x < infoPtr->rcList.right)
3876 rcItem.top = ptPosition.y;
3877 rcItem.left = ptPosition.x;
3878 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3879 rcItem.right = rcItem.left + infoPtr->nItemWidth;
3880 if (bSmall)
3881 LISTVIEW_DrawItem(hwnd, hdc, i, rcItem, FALSE, &SuggestedFocus);
3882 else
3883 LISTVIEW_DrawLargeItem(hwnd, hdc, i, rcItem, &SuggestedFocus);
3885 * Draw Focus Rect
3887 if (LISTVIEW_GetItemState(hwnd,i,LVIS_FOCUSED) &&
3888 infoPtr->bFocus && !IsRectEmpty(&SuggestedFocus))
3889 Rectangle(hdc, SuggestedFocus.left, SuggestedFocus.top,
3890 SuggestedFocus.right,SuggestedFocus.bottom);
3895 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3896 LISTVIEW_SendCustomDrawItemNotify(hwnd, hdc, i, 0,
3897 CDDS_ITEMPOSTPAINT);
3901 /***
3902 * DESCRIPTION:
3903 * Draws listview items.
3905 * PARAMETER(S):
3906 * [I] HWND : window handle
3907 * [I] HDC : device context handle
3909 * RETURN:
3910 * NoneX
3912 static VOID LISTVIEW_Refresh(HWND hwnd, HDC hdc)
3914 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3915 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3916 HFONT hOldFont;
3917 HPEN hPen, hOldPen;
3918 DWORD cdmode;
3919 RECT rect;
3921 infoPtr->bIsDrawing = TRUE;
3922 LISTVIEW_DumpListview (infoPtr, __LINE__);
3924 GetClientRect(hwnd, &rect);
3925 cdmode = LISTVIEW_SendCustomDrawNotify(hwnd,CDDS_PREPAINT,hdc,rect);
3927 if (cdmode == CDRF_SKIPDEFAULT) return;
3929 /* select font */
3930 hOldFont = SelectObject(hdc, infoPtr->hFont);
3932 /* select the dotted pen (for drawing the focus box) */
3933 hPen = CreatePen(PS_ALTERNATE, 1, 0);
3934 hOldPen = SelectObject(hdc, hPen);
3936 /* select transparent brush (for drawing the focus box) */
3937 SelectObject(hdc, GetStockObject(NULL_BRUSH));
3939 if (uView == LVS_LIST)
3940 LISTVIEW_RefreshList(hwnd, hdc, cdmode);
3941 else if (uView == LVS_REPORT)
3942 LISTVIEW_RefreshReport(hwnd, hdc, cdmode);
3943 else if (uView == LVS_SMALLICON)
3944 LISTVIEW_RefreshIcon(hwnd, hdc, TRUE, cdmode);
3945 else if (uView == LVS_ICON)
3946 LISTVIEW_RefreshIcon(hwnd, hdc, FALSE, cdmode);
3948 /* unselect objects */
3949 SelectObject(hdc, hOldFont);
3950 SelectObject(hdc, hOldPen);
3952 /* delete pen */
3953 DeleteObject(hPen);
3955 if (cdmode & CDRF_NOTIFYPOSTPAINT)
3956 LISTVIEW_SendCustomDrawNotify(hwnd, CDDS_POSTPAINT, hdc, rect);
3958 infoPtr->bIsDrawing = FALSE;
3962 /***
3963 * DESCRIPTION:
3964 * Calculates the approximate width and height of a given number of items.
3966 * PARAMETER(S):
3967 * [I] HWND : window handle
3968 * [I] INT : number of items
3969 * [I] INT : width
3970 * [I] INT : height
3972 * RETURN:
3973 * Returns a DWORD. The width in the low word and the height in high word.
3975 static LRESULT LISTVIEW_ApproximateViewRect(HWND hwnd, INT nItemCount,
3976 WORD wWidth, WORD wHeight)
3978 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3979 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3980 INT nItemCountPerColumn = 1;
3981 INT nColumnCount = 0;
3982 DWORD dwViewRect = 0;
3984 if (nItemCount == -1)
3985 nItemCount = GETITEMCOUNT(infoPtr);
3987 if (uView == LVS_LIST)
3989 if (wHeight == 0xFFFF)
3991 /* use current height */
3992 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3995 if (wHeight < infoPtr->nItemHeight)
3996 wHeight = infoPtr->nItemHeight;
3998 if (nItemCount > 0)
4000 if (infoPtr->nItemHeight > 0)
4002 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
4003 if (nItemCountPerColumn == 0)
4004 nItemCountPerColumn = 1;
4006 if (nItemCount % nItemCountPerColumn != 0)
4007 nColumnCount = nItemCount / nItemCountPerColumn;
4008 else
4009 nColumnCount = nItemCount / nItemCountPerColumn + 1;
4013 /* Microsoft padding magic */
4014 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
4015 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
4017 dwViewRect = MAKELONG(wWidth, wHeight);
4019 else if (uView == LVS_REPORT)
4020 FIXME("uView == LVS_REPORT: not implemented\n");
4021 else if (uView == LVS_SMALLICON)
4022 FIXME("uView == LVS_SMALLICON: not implemented\n");
4023 else if (uView == LVS_ICON)
4024 FIXME("uView == LVS_ICON: not implemented\n");
4026 return dwViewRect;
4029 /***
4030 * DESCRIPTION:
4031 * Arranges listview items in icon display mode.
4033 * PARAMETER(S):
4034 * [I] HWND : window handle
4035 * [I] INT : alignment code
4037 * RETURN:
4038 * SUCCESS : TRUE
4039 * FAILURE : FALSE
4041 static LRESULT LISTVIEW_Arrange(HWND hwnd, INT nAlignCode)
4043 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
4044 BOOL bResult = FALSE;
4046 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4048 switch (nAlignCode)
4050 case LVA_ALIGNLEFT:
4051 FIXME("nAlignCode=LVA_ALIGNLEFT: not implemented\n");
4052 break;
4053 case LVA_ALIGNTOP:
4054 FIXME("nAlignCode=LVA_ALIGNTOP: not implemented\n");
4055 break;
4056 case LVA_DEFAULT:
4057 FIXME("nAlignCode=LVA_DEFAULT: not implemented\n");
4058 break;
4059 case LVA_SNAPTOGRID:
4060 FIXME("nAlignCode=LVA_SNAPTOGRID: not implemented\n");
4061 break;
4065 return bResult;
4068 /* << LISTVIEW_CreateDragImage >> */
4071 /***
4072 * DESCRIPTION:
4073 * Removes all listview items and subitems.
4075 * PARAMETER(S):
4076 * [I] HWND : window handle
4078 * RETURN:
4079 * SUCCESS : TRUE
4080 * FAILURE : FALSE
4082 static LRESULT LISTVIEW_DeleteAllItems(HWND hwnd)
4084 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4085 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
4086 UINT uView = lStyle & LVS_TYPEMASK;
4087 LISTVIEW_ITEM *lpItem;
4088 LISTVIEW_SUBITEM *lpSubItem;
4089 NMLISTVIEW nmlv;
4090 BOOL bSuppress;
4091 BOOL bResult = FALSE;
4092 HDPA hdpaSubItems;
4094 TRACE("(hwnd=%x,)\n", hwnd);
4096 LISTVIEW_RemoveAllSelections(hwnd);
4097 infoPtr->nSelectionMark=-1;
4098 infoPtr->nFocusedItem=-1;
4099 /* But we are supposed to leave nHotItem as is! */
4101 if (lStyle & LVS_OWNERDATA)
4103 infoPtr->hdpaItems->nItemCount = 0;
4104 InvalidateRect(hwnd, NULL, TRUE);
4105 return TRUE;
4108 if (GETITEMCOUNT(infoPtr) > 0)
4110 INT i, j;
4112 /* send LVN_DELETEALLITEMS notification */
4113 /* verify if subsequent LVN_DELETEITEM notifications should be
4114 suppressed */
4115 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4116 nmlv.iItem = -1;
4117 bSuppress = listview_notify(hwnd, LVN_DELETEALLITEMS, &nmlv);
4119 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
4121 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
4122 if (hdpaSubItems != NULL)
4124 for (j = 1; j < hdpaSubItems->nItemCount; j++)
4126 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, j);
4127 if (lpSubItem != NULL)
4129 /* free subitem string */
4130 if (is_textW(lpSubItem->pszText))
4131 COMCTL32_Free(lpSubItem->pszText);
4133 /* free subitem */
4134 COMCTL32_Free(lpSubItem);
4138 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
4139 if (lpItem != NULL)
4141 if (!bSuppress)
4143 /* send LVN_DELETEITEM notification */
4144 nmlv.iItem = i;
4145 nmlv.lParam = lpItem->lParam;
4146 listview_notify(hwnd, LVN_DELETEITEM, &nmlv);
4149 /* free item string */
4150 if (is_textW(lpItem->pszText))
4151 COMCTL32_Free(lpItem->pszText);
4153 /* free item */
4154 COMCTL32_Free(lpItem);
4157 DPA_Destroy(hdpaSubItems);
4161 /* reinitialize listview memory */
4162 bResult = DPA_DeleteAllPtrs(infoPtr->hdpaItems);
4164 /* align items (set position of each item) */
4165 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4167 if (lStyle & LVS_ALIGNLEFT)
4169 LISTVIEW_AlignLeft(hwnd);
4171 else
4173 LISTVIEW_AlignTop(hwnd);
4177 LISTVIEW_UpdateScroll(hwnd);
4179 /* invalidate client area (optimization needed) */
4180 InvalidateRect(hwnd, NULL, TRUE);
4183 return bResult;
4186 /***
4187 * DESCRIPTION:
4188 * Removes a column from the listview control.
4190 * PARAMETER(S):
4191 * [I] HWND : window handle
4192 * [I] INT : column index
4194 * RETURN:
4195 * SUCCESS : TRUE
4196 * FAILURE : FALSE
4198 static LRESULT LISTVIEW_DeleteColumn(HWND hwnd, INT nColumn)
4200 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4201 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
4202 UINT uOwnerData = GetWindowLongW(hwnd, GWL_STYLE) & LVS_OWNERDATA;
4203 BOOL bResult = FALSE;
4205 if (Header_DeleteItem(infoPtr->hwndHeader, nColumn) != FALSE)
4207 if (!uOwnerData)
4208 bResult = LISTVIEW_RemoveColumn(infoPtr->hdpaItems, nColumn);
4210 /* Need to reset the item width when deleting a column */
4211 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
4213 /* reset scroll parameters */
4214 if (uView == LVS_REPORT)
4216 /* update scrollbar(s) */
4217 LISTVIEW_UpdateScroll(hwnd);
4219 /* refresh client area */
4220 InvalidateRect(hwnd, NULL, FALSE);
4224 return bResult;
4227 /***
4228 * DESCRIPTION:
4229 * Removes an item from the listview control.
4231 * PARAMETER(S):
4232 * [I] HWND : window handle
4233 * [I] INT : item index
4235 * RETURN:
4236 * SUCCESS : TRUE
4237 * FAILURE : FALSE
4239 static LRESULT LISTVIEW_DeleteItem(HWND hwnd, INT nItem)
4241 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4242 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
4243 UINT uView = lStyle & LVS_TYPEMASK;
4244 LONG lCtrlId = GetWindowLongW(hwnd, GWL_ID);
4245 NMLISTVIEW nmlv;
4246 BOOL bResult = FALSE;
4247 HDPA hdpaSubItems;
4248 LISTVIEW_ITEM *lpItem;
4249 LISTVIEW_SUBITEM *lpSubItem;
4250 INT i;
4251 LVITEMW item;
4253 TRACE("(hwnd=%x, nItem=%d)\n", hwnd, nItem);
4256 /* First, send LVN_DELETEITEM notification. */
4257 memset(&nmlv, 0, sizeof (NMLISTVIEW));
4258 nmlv.hdr.hwndFrom = hwnd;
4259 nmlv.hdr.idFrom = lCtrlId;
4260 nmlv.hdr.code = LVN_DELETEITEM;
4261 nmlv.iItem = nItem;
4262 SendMessageW(GetParent(hwnd), WM_NOTIFY, (WPARAM)lCtrlId,
4263 (LPARAM)&nmlv);
4266 /* remove it from the selection range */
4267 ZeroMemory(&item,sizeof(item));
4268 item.stateMask = LVIS_SELECTED;
4269 LISTVIEW_SetItemState(hwnd,nItem,&item);
4271 if (lStyle & LVS_OWNERDATA)
4273 infoPtr->hdpaItems->nItemCount --;
4274 InvalidateRect(hwnd, NULL, TRUE);
4275 return TRUE;
4278 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
4280 /* initialize memory */
4281 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4283 hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
4284 if (hdpaSubItems != NULL)
4286 for (i = 1; i < hdpaSubItems->nItemCount; i++)
4288 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
4289 if (lpSubItem != NULL)
4291 /* free item string */
4292 if (is_textW(lpSubItem->pszText))
4293 COMCTL32_Free(lpSubItem->pszText);
4295 /* free item */
4296 COMCTL32_Free(lpSubItem);
4300 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
4301 if (lpItem != NULL)
4303 /* free item string */
4304 if (is_textW(lpItem->pszText))
4305 COMCTL32_Free(lpItem->pszText);
4307 /* free item */
4308 COMCTL32_Free(lpItem);
4311 bResult = DPA_Destroy(hdpaSubItems);
4314 LISTVIEW_ShiftIndices(hwnd,nItem,-1);
4316 /* align items (set position of each item) */
4317 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4319 if (lStyle & LVS_ALIGNLEFT)
4320 LISTVIEW_AlignLeft(hwnd);
4321 else
4322 LISTVIEW_AlignTop(hwnd);
4325 LISTVIEW_UpdateScroll(hwnd);
4327 /* refresh client area */
4328 InvalidateRect(hwnd, NULL, TRUE);
4331 return bResult;
4335 /***
4336 * DESCRIPTION:
4337 * Return edit control handle of current edit label
4339 * PARAMETER(S):
4340 * [I] HWND : window handle
4342 * RETURN:
4343 * SUCCESS : HWND
4344 * FAILURE : 0
4346 static LRESULT LISTVIEW_GetEditControl(HWND hwnd)
4348 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4349 return infoPtr->hwndEdit;
4353 /***
4354 * DESCRIPTION:
4355 * Callback implementation for editlabel control
4357 * PARAMETER(S):
4358 * [I] HWND : window handle
4359 * [I] LPSTR : modified text
4360 * [I] DWORD : item index
4361 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4363 * RETURN:
4364 * SUCCESS : TRUE
4365 * FAILURE : FALSE
4367 static BOOL LISTVIEW_EndEditLabelT(HWND hwnd, LPWSTR pszText, DWORD nItem, BOOL isW)
4369 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4370 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
4371 NMLVDISPINFOW dispInfo;
4372 LISTVIEW_ITEM *lpItem;
4373 HDPA hdpaSubItems;
4374 LISTVIEW_ITEM lvItemRef;
4375 LVITEMW item;
4376 BOOL bResult = TRUE;
4378 TRACE("(hwnd=%x, pszText=%s, nItem=%ld, isW=%d)\n", hwnd, debugstr_t(pszText, isW), nItem, isW);
4380 if (!(lStyle & LVS_OWNERDATA))
4382 if (!(hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)))
4383 return FALSE;
4385 if (!(lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)))
4386 return FALSE;
4388 else
4390 ZeroMemory(&lvItemRef,sizeof(LISTVIEW_ITEM));
4391 ZeroMemory(&item,sizeof(item));
4392 item.iItem = nItem;
4393 item.iSubItem = 0;
4394 item.mask = LVIF_PARAM | LVIF_STATE;
4395 ListView_GetItemW(hwnd, &item);
4396 lvItemRef.state = item.state;
4397 lvItemRef.iImage = item.iImage;
4398 lvItemRef.lParam = item.lParam;
4399 lpItem = &lvItemRef;
4402 ZeroMemory(&dispInfo, sizeof(dispInfo));
4403 dispInfo.item.mask = 0;
4404 dispInfo.item.iItem = nItem;
4405 dispInfo.item.state = lpItem->state;
4406 dispInfo.item.stateMask = 0;
4407 dispInfo.item.pszText = pszText;
4408 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4409 dispInfo.item.iImage = lpItem->iImage;
4410 dispInfo.item.lParam = lpItem->lParam;
4411 infoPtr->hwndEdit = 0;
4413 /* Do we need to update the Item Text */
4414 if(dispinfo_notifyT(hwnd, LVN_ENDLABELEDITW, &dispInfo, isW))
4415 if (lpItem->pszText != LPSTR_TEXTCALLBACKW && !(lStyle & LVS_OWNERDATA))
4416 bResult = textsetptrT(&lpItem->pszText, pszText, isW);
4418 return bResult;
4421 /***
4422 * DESCRIPTION:
4423 * Callback implementation for editlabel control
4425 * PARAMETER(S):
4426 * [I] HWND : window handle
4427 * [I] LPSTR : modified text
4428 * [I] DWORD : item index
4430 * RETURN:
4431 * SUCCESS : TRUE
4432 * FAILURE : FALSE
4434 static BOOL LISTVIEW_EndEditLabelW(HWND hwnd, LPWSTR pszText, DWORD nItem)
4436 return LISTVIEW_EndEditLabelT(hwnd, pszText, nItem, TRUE);
4439 /***
4440 * DESCRIPTION:
4441 * Callback implementation for editlabel control
4443 * PARAMETER(S):
4444 * [I] HWND : window handle
4445 * [I] LPSTR : modified text
4446 * [I] DWORD : item index
4448 * RETURN:
4449 * SUCCESS : TRUE
4450 * FAILURE : FALSE
4452 static BOOL LISTVIEW_EndEditLabelA(HWND hwnd, LPSTR pszText, DWORD nItem)
4454 return LISTVIEW_EndEditLabelT(hwnd, (LPWSTR)pszText, nItem, FALSE);
4457 /***
4458 * DESCRIPTION:
4459 * Begin in place editing of specified list view item
4461 * PARAMETER(S):
4462 * [I] HWND : window handle
4463 * [I] INT : item index
4464 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4466 * RETURN:
4467 * SUCCESS : TRUE
4468 * FAILURE : FALSE
4470 static HWND LISTVIEW_EditLabelT(HWND hwnd, INT nItem, BOOL isW)
4472 NMLVDISPINFOW dispInfo;
4473 RECT rect;
4474 LISTVIEW_ITEM *lpItem;
4475 HWND hedit;
4476 HINSTANCE hinst = GetWindowLongW(hwnd, GWL_HINSTANCE);
4477 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4478 HDPA hdpaSubItems;
4479 WCHAR szDispText[DISP_TEXT_SIZE];
4480 LVITEMW lvItem;
4481 LISTVIEW_ITEM lvItemRef;
4482 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
4484 if (~GetWindowLongW(hwnd, GWL_STYLE) & LVS_EDITLABELS)
4485 return FALSE;
4487 /* Is the EditBox still there, if so remove it */
4488 if(infoPtr->hwndEdit != 0)
4489 SetFocus(hwnd);
4491 LISTVIEW_SetSelection(hwnd, nItem);
4492 LISTVIEW_SetItemFocus(hwnd, nItem);
4494 if (!(lStyle & LVS_OWNERDATA))
4496 if (NULL == (hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)))
4497 return 0;
4499 if (NULL == (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)))
4500 return 0;
4502 else
4504 LVITEMW item;
4505 ZeroMemory(&lvItemRef,sizeof(LISTVIEW_ITEM));
4506 ZeroMemory(&item, sizeof(item));
4507 item.iItem = nItem;
4508 item.iSubItem = 0;
4509 item.mask = LVIF_PARAM | LVIF_STATE;
4510 ListView_GetItemW(hwnd, &item);
4511 lvItemRef.iImage = item.iImage;
4512 lvItemRef.state = item.state;
4513 lvItemRef.lParam = item.lParam;
4514 lpItem = &lvItemRef;
4517 /* get information needed for drawing the item */
4518 ZeroMemory(&lvItem, sizeof(lvItem));
4519 lvItem.mask = LVIF_TEXT;
4520 lvItem.iItem = nItem;
4521 lvItem.iSubItem = 0;
4522 lvItem.cchTextMax = DISP_TEXT_SIZE;
4523 lvItem.pszText = szDispText;
4524 *lvItem.pszText = '\0';
4525 LISTVIEW_GetItemT(hwnd, &lvItem, FALSE, isW);
4527 ZeroMemory(&dispInfo, sizeof(dispInfo));
4528 dispInfo.item.mask = 0;
4529 dispInfo.item.iItem = nItem;
4530 dispInfo.item.state = lpItem->state;
4531 dispInfo.item.stateMask = 0;
4532 dispInfo.item.pszText = lvItem.pszText;
4533 dispInfo.item.cchTextMax = lstrlenW(lvItem.pszText);
4534 dispInfo.item.iImage = lpItem->iImage;
4535 dispInfo.item.lParam = lpItem->lParam;
4537 if (dispinfo_notifyT(hwnd, LVN_BEGINLABELEDITW, &dispInfo, isW))
4538 return 0;
4540 rect.left = LVIR_LABEL;
4541 if (!LISTVIEW_GetItemRect(hwnd, nItem, &rect))
4542 return 0;
4544 if (!(hedit = CreateEditLabelT(szDispText , WS_VISIBLE,
4545 rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, hwnd, hinst,
4546 isW ? LISTVIEW_EndEditLabelW : (EditlblCallbackW)LISTVIEW_EndEditLabelA,
4547 nItem, isW)))
4548 return 0;
4550 infoPtr->hwndEdit = hedit;
4551 SetFocus(hedit);
4552 SendMessageW(hedit, EM_SETSEL, 0, -1);
4554 return hedit;
4558 /***
4559 * DESCRIPTION:
4560 * Ensures the specified item is visible, scrolling into view if necessary.
4562 * PARAMETER(S):
4563 * [I] HWND : window handle
4564 * [I] INT : item index
4565 * [I] BOOL : partially or entirely visible
4567 * RETURN:
4568 * SUCCESS : TRUE
4569 * FAILURE : FALSE
4571 static BOOL LISTVIEW_EnsureVisible(HWND hwnd, INT nItem, BOOL bPartial)
4573 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4574 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
4575 INT nScrollPosHeight = 0;
4576 INT nScrollPosWidth = 0;
4577 SCROLLINFO scrollInfo;
4578 RECT rcItem;
4579 BOOL bRedraw = FALSE;
4581 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
4582 scrollInfo.cbSize = sizeof(SCROLLINFO);
4583 scrollInfo.fMask = SIF_POS;
4585 /* ALWAYS bPartial == FALSE, FOR NOW! */
4587 rcItem.left = LVIR_BOUNDS;
4588 if (LISTVIEW_GetItemRect(hwnd, nItem, &rcItem) != FALSE)
4590 if (rcItem.left < infoPtr->rcList.left)
4592 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
4594 /* scroll left */
4595 bRedraw = TRUE;
4596 if (uView == LVS_LIST)
4598 nScrollPosWidth = infoPtr->nItemWidth;
4599 rcItem.left += infoPtr->rcList.left;
4601 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4603 nScrollPosWidth = LISTVIEW_SCROLL_DIV_SIZE;
4604 rcItem.left += infoPtr->rcList.left;
4607 /* When in LVS_REPORT view, the scroll position should
4608 not be updated. */
4609 if (nScrollPosWidth != 0)
4611 if (rcItem.left % nScrollPosWidth == 0)
4612 scrollInfo.nPos += rcItem.left / nScrollPosWidth;
4613 else
4614 scrollInfo.nPos += rcItem.left / nScrollPosWidth - 1;
4616 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
4620 else if (rcItem.right > infoPtr->rcList.right)
4622 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
4624 /* scroll right */
4625 bRedraw = TRUE;
4626 if (uView == LVS_LIST)
4628 rcItem.right -= infoPtr->rcList.right;
4629 nScrollPosWidth = infoPtr->nItemWidth;
4631 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4633 rcItem.right -= infoPtr->rcList.right;
4634 nScrollPosWidth = LISTVIEW_SCROLL_DIV_SIZE;
4637 /* When in LVS_REPORT view, the scroll position should
4638 not be updated. */
4639 if (nScrollPosWidth != 0)
4641 if (rcItem.right % nScrollPosWidth == 0)
4642 scrollInfo.nPos += rcItem.right / nScrollPosWidth;
4643 else
4644 scrollInfo.nPos += rcItem.right / nScrollPosWidth + 1;
4646 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
4651 if (rcItem.top < infoPtr->rcList.top)
4653 /* scroll up */
4654 bRedraw = TRUE;
4655 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
4657 if (uView == LVS_REPORT)
4659 rcItem.top -= infoPtr->rcList.top;
4660 nScrollPosHeight = infoPtr->nItemHeight;
4662 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4664 nScrollPosHeight = LISTVIEW_SCROLL_DIV_SIZE;
4665 rcItem.top += infoPtr->rcList.top;
4668 if (rcItem.top % nScrollPosHeight == 0)
4669 scrollInfo.nPos += rcItem.top / nScrollPosHeight;
4670 else
4671 scrollInfo.nPos += rcItem.top / nScrollPosHeight - 1;
4673 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
4676 else if (rcItem.bottom > infoPtr->rcList.bottom)
4678 /* scroll down */
4679 bRedraw = TRUE;
4680 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
4682 if (uView == LVS_REPORT)
4684 rcItem.bottom -= infoPtr->rcList.bottom;
4685 nScrollPosHeight = infoPtr->nItemHeight;
4687 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4689 nScrollPosHeight = LISTVIEW_SCROLL_DIV_SIZE;
4690 rcItem.bottom -= infoPtr->rcList.bottom;
4693 if (rcItem.bottom % nScrollPosHeight == 0)
4694 scrollInfo.nPos += rcItem.bottom / nScrollPosHeight;
4695 else
4696 scrollInfo.nPos += rcItem.bottom / nScrollPosHeight + 1;
4698 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
4703 if(bRedraw)
4704 InvalidateRect(hwnd,NULL,TRUE);
4705 return TRUE;
4708 /***
4709 * DESCRIPTION:
4710 * Retrieves the nearest item, given a position and a direction.
4712 * PARAMETER(S):
4713 * [I] HWND : window handle
4714 * [I] POINT : start position
4715 * [I] UINT : direction
4717 * RETURN:
4718 * Item index if successdful, -1 otherwise.
4720 static INT LISTVIEW_GetNearestItem(HWND hwnd, POINT pt, UINT vkDirection)
4722 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4723 LV_INTHIT lvIntHit;
4724 INT nItem = -1;
4725 RECT rcView;
4727 TRACE("point %ld,%ld, direction %s\n", pt.x, pt.y,
4728 (vkDirection == VK_DOWN) ? "VK_DOWN" :
4729 ((vkDirection == VK_UP) ? "VK_UP" :
4730 ((vkDirection == VK_LEFT) ? "VK_LEFT" : "VK_RIGHT")));
4732 if (LISTVIEW_GetViewRect(hwnd, &rcView) != FALSE)
4734 ZeroMemory(&lvIntHit, sizeof(lvIntHit));
4735 LISTVIEW_GetOrigin(hwnd, &lvIntHit.ht.pt);
4736 lvIntHit.ht.pt.x += pt.x;
4737 lvIntHit.ht.pt.y += pt.y;
4739 if (vkDirection == VK_DOWN)
4740 lvIntHit.ht.pt.y += infoPtr->nItemHeight;
4741 else if (vkDirection == VK_UP)
4742 lvIntHit.ht.pt.y -= infoPtr->nItemHeight;
4743 else if (vkDirection == VK_LEFT)
4744 lvIntHit.ht.pt.x -= infoPtr->nItemWidth;
4745 else if (vkDirection == VK_RIGHT)
4746 lvIntHit.ht.pt.x += infoPtr->nItemWidth;
4748 if (PtInRect(&rcView, lvIntHit.ht.pt) == FALSE)
4749 return -1;
4750 else
4752 nItem = LISTVIEW_SuperHitTestItem(hwnd, &lvIntHit, TRUE);
4753 return nItem == -1 ? lvIntHit.iDistItem : nItem;
4757 return nItem;
4760 /***
4761 * DESCRIPTION:
4762 * Searches for an item with specific characteristics.
4764 * PARAMETER(S):
4765 * [I] hwnd : window handle
4766 * [I] nStart : base item index
4767 * [I] lpFindInfo : item information to look for
4769 * RETURN:
4770 * SUCCESS : index of item
4771 * FAILURE : -1
4773 static LRESULT LISTVIEW_FindItemW(HWND hwnd, INT nStart,
4774 LPLVFINDINFOW lpFindInfo)
4776 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4777 POINT ptItem;
4778 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4779 LVITEMW lvItem;
4780 BOOL bWrap = FALSE;
4781 INT nItem = nStart;
4782 INT nLast = GETITEMCOUNT(infoPtr);
4784 if ((nItem >= -1) && (lpFindInfo != NULL))
4786 ZeroMemory(&lvItem, sizeof(lvItem));
4788 if (lpFindInfo->flags & LVFI_PARAM)
4790 lvItem.mask |= LVIF_PARAM;
4793 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
4795 lvItem.mask |= LVIF_TEXT;
4796 lvItem.pszText = szDispText;
4797 lvItem.cchTextMax = DISP_TEXT_SIZE;
4800 if (lpFindInfo->flags & LVFI_WRAP)
4801 bWrap = TRUE;
4803 if (lpFindInfo->flags & LVFI_NEARESTXY)
4805 ptItem.x = lpFindInfo->pt.x;
4806 ptItem.y = lpFindInfo->pt.y;
4809 while (1)
4811 while (nItem < nLast)
4813 if (lpFindInfo->flags & LVFI_NEARESTXY)
4815 nItem = LISTVIEW_GetNearestItem(hwnd, ptItem,
4816 lpFindInfo->vkDirection);
4817 if (nItem != -1)
4819 /* get position of the new item index */
4820 if (ListView_GetItemPosition(hwnd, nItem, &ptItem) == FALSE)
4821 return -1;
4823 else
4824 return -1;
4826 else
4828 nItem++;
4831 lvItem.iItem = nItem;
4832 lvItem.iSubItem = 0;
4833 if (LISTVIEW_GetItemW(hwnd, &lvItem, TRUE))
4835 if (lvItem.mask & LVIF_TEXT)
4837 if (lpFindInfo->flags & LVFI_PARTIAL)
4839 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL)
4840 continue;
4842 else
4844 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0)
4845 continue;
4849 if (lvItem.mask & LVIF_PARAM)
4851 if (lpFindInfo->lParam != lvItem.lParam)
4852 continue;
4855 return nItem;
4859 if (bWrap)
4861 nItem = -1;
4862 nLast = nStart + 1;
4863 bWrap = FALSE;
4865 else
4867 return -1;
4872 return -1;
4875 /***
4876 * DESCRIPTION:
4877 * Searches for an item with specific characteristics.
4879 * PARAMETER(S):
4880 * [I] hwnd : window handle
4881 * [I] nStart : base item index
4882 * [I] lpFindInfo : item information to look for
4884 * RETURN:
4885 * SUCCESS : index of item
4886 * FAILURE : -1
4888 static LRESULT LISTVIEW_FindItemA(HWND hwnd, INT nStart,
4889 LPLVFINDINFOA lpFindInfo)
4891 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
4892 LVFINDINFOW fiw;
4893 LRESULT res;
4895 memcpy(&fiw, lpFindInfo, sizeof(fiw));
4896 if (hasText) fiw.psz = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
4897 res = LISTVIEW_FindItemW(hwnd, nStart, &fiw);
4898 if (hasText) textfreeT((LPWSTR)fiw.psz, FALSE);
4899 return res;
4902 /***
4903 * DESCRIPTION:
4904 * Retrieves the background color of the listview control.
4906 * PARAMETER(S):
4907 * [I] HWND : window handle
4909 * RETURN:
4910 * COLORREF associated with the background.
4912 static LRESULT LISTVIEW_GetBkColor(HWND hwnd)
4914 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4916 return infoPtr->clrBk;
4919 /***
4920 * DESCRIPTION:
4921 * Retrieves the background image of the listview control.
4923 * PARAMETER(S):
4924 * [I] HWND : window handle
4925 * [O] LPLVMKBIMAGE : background image attributes
4927 * RETURN:
4928 * SUCCESS : TRUE
4929 * FAILURE : FALSE
4931 /* static LRESULT LISTVIEW_GetBkImage(HWND hwnd, LPLVBKIMAGE lpBkImage) */
4932 /* { */
4933 /* FIXME (listview, "empty stub!\n"); */
4934 /* return FALSE; */
4935 /* } */
4937 /***
4938 * DESCRIPTION:
4939 * Retrieves the callback mask.
4941 * PARAMETER(S):
4942 * [I] HWND : window handle
4944 * RETURN:
4945 * Value of mask
4947 static UINT LISTVIEW_GetCallbackMask(HWND hwnd)
4949 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4951 return infoPtr->uCallbackMask;
4954 /***
4955 * DESCRIPTION:
4956 * Retrieves column attributes.
4958 * PARAMETER(S):
4959 * [I] HWND : window handle
4960 * [I] INT : column index
4961 * [IO] LPLVCOLUMNW : column information
4962 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
4963 * otherwise it is in fact a LPLVCOLUMNA
4965 * RETURN:
4966 * SUCCESS : TRUE
4967 * FAILURE : FALSE
4969 static LRESULT LISTVIEW_GetColumnT(HWND hwnd, INT nItem, LPLVCOLUMNW lpColumn, BOOL isW)
4971 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4972 HDITEMW hdi;
4973 BOOL bResult = FALSE;
4975 if (lpColumn != NULL)
4978 TRACE("(hwnd=%x, col=%d, lpColumn=%s, isW=%d)\n",
4979 hwnd, nItem, debuglvcolumn_t(lpColumn, isW), isW);
4981 /* initialize memory */
4982 ZeroMemory(&hdi, sizeof(hdi));
4984 if (lpColumn->mask & LVCF_FMT)
4985 hdi.mask |= HDI_FORMAT;
4987 if (lpColumn->mask & LVCF_WIDTH)
4988 hdi.mask |= HDI_WIDTH;
4990 if (lpColumn->mask & LVCF_TEXT)
4992 hdi.mask |= HDI_TEXT;
4993 hdi.cchTextMax = lpColumn->cchTextMax;
4994 hdi.pszText = lpColumn->pszText;
4997 if (lpColumn->mask & LVCF_IMAGE)
4998 hdi.mask |= HDI_IMAGE;
5000 if (lpColumn->mask & LVCF_ORDER)
5001 hdi.mask |= HDI_ORDER;
5003 if (isW)
5004 bResult = Header_GetItemW(infoPtr->hwndHeader, nItem, &hdi);
5005 else
5006 bResult = Header_GetItemA(infoPtr->hwndHeader, nItem, &hdi);
5008 if (bResult != FALSE)
5010 if (lpColumn->mask & LVCF_FMT)
5012 lpColumn->fmt = 0;
5014 if (hdi.fmt & HDF_LEFT)
5015 lpColumn->fmt |= LVCFMT_LEFT;
5016 else if (hdi.fmt & HDF_RIGHT)
5017 lpColumn->fmt |= LVCFMT_RIGHT;
5018 else if (hdi.fmt & HDF_CENTER)
5019 lpColumn->fmt |= LVCFMT_CENTER;
5021 if (hdi.fmt & HDF_IMAGE)
5022 lpColumn->fmt |= LVCFMT_COL_HAS_IMAGES;
5024 if (hdi.fmt & HDF_BITMAP_ON_RIGHT)
5025 lpColumn->fmt |= LVCFMT_BITMAP_ON_RIGHT;
5028 if (lpColumn->mask & LVCF_WIDTH)
5029 lpColumn->cx = hdi.cxy;
5031 if (lpColumn->mask & LVCF_IMAGE)
5032 lpColumn->iImage = hdi.iImage;
5034 if (lpColumn->mask & LVCF_ORDER)
5035 lpColumn->iOrder = hdi.iOrder;
5039 return bResult;
5043 static LRESULT LISTVIEW_GetColumnOrderArray(HWND hwnd, INT iCount, LPINT lpiArray)
5045 /* LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); */
5046 INT i;
5048 if (!lpiArray)
5049 return FALSE;
5051 /* little hack */
5052 for (i = 0; i < iCount; i++)
5053 lpiArray[i] = i;
5055 return TRUE;
5058 /***
5059 * DESCRIPTION:
5060 * Retrieves the column width.
5062 * PARAMETER(S):
5063 * [I] HWND : window handle
5064 * [I] int : column index
5066 * RETURN:
5067 * SUCCESS : column width
5068 * FAILURE : zero
5070 static LRESULT LISTVIEW_GetColumnWidth(HWND hwnd, INT nColumn)
5072 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5073 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5074 INT nColumnWidth = 0;
5075 HDITEMW hdi;
5077 if (uView == LVS_LIST)
5079 nColumnWidth = infoPtr->nItemWidth;
5081 else if (uView == LVS_REPORT)
5083 /* get column width from header */
5084 ZeroMemory(&hdi, sizeof(hdi));
5085 hdi.mask = HDI_WIDTH;
5086 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdi) != FALSE)
5087 nColumnWidth = hdi.cxy;
5090 return nColumnWidth;
5093 /***
5094 * DESCRIPTION:
5095 * In list or report display mode, retrieves the number of items that can fit
5096 * vertically in the visible area. In icon or small icon display mode,
5097 * retrieves the total number of visible items.
5099 * PARAMETER(S):
5100 * [I] HWND : window handle
5102 * RETURN:
5103 * Number of fully visible items.
5105 static LRESULT LISTVIEW_GetCountPerPage(HWND hwnd)
5107 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5108 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5109 INT nItemCount = 0;
5111 if (uView == LVS_LIST)
5113 if (infoPtr->rcList.right > infoPtr->nItemWidth)
5115 nItemCount = LISTVIEW_GetCountPerRow(hwnd) *
5116 LISTVIEW_GetCountPerColumn(hwnd);
5119 else if (uView == LVS_REPORT)
5121 nItemCount = LISTVIEW_GetCountPerColumn(hwnd);
5123 else
5125 nItemCount = GETITEMCOUNT(infoPtr);
5128 return nItemCount;
5131 /* LISTVIEW_GetEditControl */
5133 /***
5134 * DESCRIPTION:
5135 * Retrieves the extended listview style.
5137 * PARAMETERS:
5138 * [I] HWND : window handle
5140 * RETURN:
5141 * SUCCESS : previous style
5142 * FAILURE : 0
5144 static LRESULT LISTVIEW_GetExtendedListViewStyle(HWND hwnd)
5146 LISTVIEW_INFO *infoPtr;
5148 /* make sure we can get the listview info */
5149 if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0)))
5150 return (0);
5152 return (infoPtr->dwExStyle);
5155 /***
5156 * DESCRIPTION:
5157 * Retrieves the handle to the header control.
5159 * PARAMETER(S):
5160 * [I] HWND : window handle
5162 * RETURN:
5163 * Header handle.
5165 static LRESULT LISTVIEW_GetHeader(HWND hwnd)
5167 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5169 return infoPtr->hwndHeader;
5172 /* LISTVIEW_GetHotCursor */
5174 /***
5175 * DESCRIPTION:
5176 * Returns the time that the mouse cursor must hover over an item
5177 * before it is selected.
5179 * PARAMETER(S):
5180 * [I] HWND : window handle
5182 * RETURN:
5183 * Returns the previously set hover time or (DWORD)-1 to indicate that the
5184 * hover time is set to the default hover time.
5186 static LRESULT LISTVIEW_GetHoverTime(HWND hwnd)
5188 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5190 return infoPtr->dwHoverTime;
5193 /***
5194 * DESCRIPTION:
5195 * Retrieves an image list handle.
5197 * PARAMETER(S):
5198 * [I] HWND : window handle
5199 * [I] INT : image list identifier
5201 * RETURN:
5202 * SUCCESS : image list handle
5203 * FAILURE : NULL
5205 static LRESULT LISTVIEW_GetImageList(HWND hwnd, INT nImageList)
5207 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5208 HIMAGELIST himl = NULL;
5210 switch (nImageList)
5212 case LVSIL_NORMAL:
5213 himl = infoPtr->himlNormal;
5214 break;
5215 case LVSIL_SMALL:
5216 himl = infoPtr->himlSmall;
5217 break;
5218 case LVSIL_STATE:
5219 himl = infoPtr->himlState;
5220 break;
5223 return (LRESULT)himl;
5226 /* LISTVIEW_GetISearchString */
5228 /***
5229 * DESCRIPTION:
5230 * Retrieves item attributes.
5232 * PARAMETER(S):
5233 * [I] hwnd : window handle
5234 * [IO] lpLVItem : item info
5235 * [I] internal : if true then we will use tricks that avoid copies
5236 * but are not compatible with the regular interface
5237 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5238 * if FALSE, the lpLVItem is a LPLVITEMA.
5240 * RETURN:
5241 * SUCCESS : TRUE
5242 * FAILURE : FALSE
5244 static LRESULT LISTVIEW_GetItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL internal, BOOL isW)
5246 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5247 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
5248 NMLVDISPINFOW dispInfo;
5249 LISTVIEW_SUBITEM *lpSubItem;
5250 LISTVIEW_ITEM *lpItem;
5251 HDPA hdpaSubItems;
5252 void* null = NULL;
5253 INT* piImage = (INT*)&null;
5254 LPWSTR* ppszText= (LPWSTR*)&null;
5255 LPARAM* plParam = (LPARAM*)&null;
5257 if (internal && !isW)
5259 ERR("We can't have internal non-Unicode GetItem!\n");
5260 return FALSE;
5263 /* In the following:
5264 * lpLVItem describes the information requested by the user
5265 * lpItem/lpSubItem is what we have
5266 * dispInfo is a structure we use to request the missing
5267 * information from the application
5270 TRACE("(hwnd=%x, lpLVItem=%s, internal=%d, isW=%d)\n",
5271 hwnd, debuglvitem_t(lpLVItem, isW), internal, isW);
5273 if ((lpLVItem == NULL) || (lpLVItem->iItem < 0) ||
5274 (lpLVItem->iItem >= GETITEMCOUNT(infoPtr)))
5275 return FALSE;
5277 ZeroMemory(&dispInfo, sizeof(dispInfo));
5279 if (lStyle & LVS_OWNERDATA)
5281 if (lpLVItem->mask & ~LVIF_STATE)
5283 memcpy(&dispInfo.item, lpLVItem, sizeof(LVITEMW));
5284 dispinfo_notifyT(hwnd, LVN_GETDISPINFOW, &dispInfo, isW);
5285 memcpy(lpLVItem, &dispInfo.item, sizeof(LVITEMW));
5286 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
5289 if ((lpLVItem->mask & LVIF_STATE)&&(lpLVItem->iSubItem == 0))
5291 lpLVItem->state = 0;
5292 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5293 lpLVItem->state |= LVIS_FOCUSED;
5294 if (LISTVIEW_IsSelected(hwnd,lpLVItem->iItem))
5295 lpLVItem->state |= LVIS_SELECTED;
5298 return TRUE;
5301 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
5302 if (hdpaSubItems == NULL) return FALSE;
5304 if ( (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)) == NULL)
5305 return FALSE;
5307 ZeroMemory(&dispInfo.item, sizeof(LVITEMW));
5308 if (lpLVItem->iSubItem == 0)
5310 piImage=&lpItem->iImage;
5311 ppszText=&lpItem->pszText;
5312 plParam=&lpItem->lParam;
5313 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask)
5315 dispInfo.item.mask |= LVIF_STATE;
5316 dispInfo.item.stateMask = infoPtr->uCallbackMask;
5319 else
5321 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
5322 if (lpSubItem != NULL)
5324 piImage=&lpSubItem->iImage;
5325 ppszText=&lpSubItem->pszText;
5329 if ((lpLVItem->mask & LVIF_IMAGE) && (*piImage==I_IMAGECALLBACK))
5331 dispInfo.item.mask |= LVIF_IMAGE;
5334 if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(*ppszText))
5336 dispInfo.item.mask |= LVIF_TEXT;
5337 dispInfo.item.pszText = lpLVItem->pszText;
5338 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5339 if (dispInfo.item.pszText && lpLVItem->cchTextMax > 0)
5340 *dispInfo.item.pszText = '\0';
5341 if (dispInfo.item.pszText && (*ppszText == NULL))
5342 *dispInfo.item.pszText = '\0';
5345 if (dispInfo.item.mask != 0)
5347 /* We don't have all the requested info, query the application */
5348 dispInfo.item.iItem = lpLVItem->iItem;
5349 dispInfo.item.iSubItem = lpLVItem->iSubItem;
5350 dispInfo.item.lParam = lpItem->lParam;
5351 dispinfo_notifyT(hwnd, LVN_GETDISPINFOW, &dispInfo, isW);
5352 TRACE(" getdispinfo(2):lpLVItem=%s\n", debuglvitem_t(&dispInfo.item, isW));
5355 if (dispInfo.item.mask & LVIF_IMAGE)
5357 lpLVItem->iImage = dispInfo.item.iImage;
5358 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && (*piImage==I_IMAGECALLBACK))
5359 *piImage = dispInfo.item.iImage;
5361 else if (lpLVItem->mask & LVIF_IMAGE)
5363 lpLVItem->iImage = *piImage;
5366 if (dispInfo.item.mask & LVIF_PARAM)
5368 lpLVItem->lParam = dispInfo.item.lParam;
5369 if (dispInfo.item.mask & LVIF_DI_SETITEM)
5370 *plParam = dispInfo.item.lParam;
5372 else if (lpLVItem->mask & LVIF_PARAM)
5373 lpLVItem->lParam = lpItem->lParam;
5375 if (dispInfo.item.mask & LVIF_TEXT)
5377 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && *ppszText)
5378 textsetptrT(ppszText, dispInfo.item.pszText, isW);
5380 /* If lpLVItem->pszText==dispInfo.item.pszText a copy is unnecessary, but */
5381 /* some apps give a new pointer in ListView_Notify so we can't be sure. */
5382 if (lpLVItem->pszText != dispInfo.item.pszText)
5383 textcpynT(lpLVItem->pszText, isW, dispInfo.item.pszText, isW, lpLVItem->cchTextMax);
5386 else if (lpLVItem->mask & LVIF_TEXT)
5388 if (internal) lpLVItem->pszText = *ppszText;
5389 else textcpynT(lpLVItem->pszText, isW, *ppszText, TRUE, lpLVItem->cchTextMax);
5392 if (lpLVItem->iSubItem == 0)
5394 if (dispInfo.item.mask & LVIF_STATE)
5396 lpLVItem->state = lpItem->state;
5397 lpLVItem->state &= ~dispInfo.item.stateMask;
5398 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
5400 lpLVItem->state &= ~LVIS_SELECTED;
5401 if ((dispInfo.item.stateMask & LVIS_SELECTED) &&
5402 LISTVIEW_IsSelected(hwnd,dispInfo.item.iItem))
5403 lpLVItem->state |= LVIS_SELECTED;
5405 else if (lpLVItem->mask & LVIF_STATE)
5407 lpLVItem->state = lpItem->state & lpLVItem->stateMask;
5409 lpLVItem->state &= ~LVIS_SELECTED;
5410 if ((lpLVItem->stateMask & LVIS_SELECTED) &&
5411 LISTVIEW_IsSelected(hwnd,lpLVItem->iItem))
5412 lpLVItem->state |= LVIS_SELECTED;
5415 if (lpLVItem->mask & LVIF_PARAM)
5416 lpLVItem->lParam = lpItem->lParam;
5418 if (lpLVItem->mask & LVIF_INDENT)
5419 lpLVItem->iIndent = lpItem->iIndent;
5422 return TRUE;
5425 /* LISTVIEW_GetHotCursor */
5427 /***
5428 * DESCRIPTION:
5429 * Retrieves the index of the hot item.
5431 * PARAMETERS:
5432 * [I] HWND : window handle
5434 * RETURN:
5435 * SUCCESS : hot item index
5436 * FAILURE : -1 (no hot item)
5438 static LRESULT LISTVIEW_GetHotItem(HWND hwnd)
5440 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5442 return infoPtr->nHotItem;
5445 /* LISTVIEW_GetHoverTime */
5447 /***
5448 * DESCRIPTION:
5449 * Retrieves the number of items in the listview control.
5451 * PARAMETER(S):
5452 * [I] HWND : window handle
5454 * RETURN:
5455 * Number of items.
5457 static LRESULT LISTVIEW_GetItemCount(HWND hwnd)
5459 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5461 return GETITEMCOUNT(infoPtr);
5464 /***
5465 * DESCRIPTION:
5466 * Retrieves the rectangle enclosing the item icon and text.
5468 * PARAMETER(S):
5469 * [I] HWND : window handle
5470 * [I] INT : item index
5471 * [O] LPRECT : coordinate information
5473 * RETURN:
5474 * SUCCESS : TRUE
5475 * FAILURE : FALSE
5477 static BOOL LISTVIEW_GetItemBoundBox(HWND hwnd, INT nItem, LPRECT lpRect)
5479 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5480 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
5481 UINT uView = lStyle & LVS_TYPEMASK;
5482 BOOL bResult = FALSE;
5483 HDPA hdpaSubItems;
5484 LISTVIEW_ITEM *lpItem;
5485 INT nCountPerColumn;
5486 INT nRow;
5488 TRACE("(hwnd=%x,nItem=%d,lpRect=%p)\n", hwnd, nItem, lpRect);
5490 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) &&
5491 (lpRect != NULL))
5493 if (uView == LVS_LIST)
5495 bResult = TRUE;
5496 nItem = nItem - ListView_GetTopIndex(hwnd);
5497 nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
5498 if (nItem < 0)
5500 nRow = nItem % nCountPerColumn;
5501 if (nRow == 0)
5503 lpRect->left = nItem / nCountPerColumn * infoPtr->nItemWidth;
5504 lpRect->top = 0;
5506 else
5508 lpRect->left = (nItem / nCountPerColumn -1) * infoPtr->nItemWidth;
5509 lpRect->top = (nRow + nCountPerColumn) * infoPtr->nItemHeight;
5512 else
5514 lpRect->left = nItem / nCountPerColumn * infoPtr->nItemWidth;
5515 lpRect->top = nItem % nCountPerColumn * infoPtr->nItemHeight;
5518 else if (uView == LVS_REPORT)
5520 bResult = TRUE;
5521 lpRect->left = REPORT_MARGINX;
5522 lpRect->top = ((nItem - ListView_GetTopIndex(hwnd)) *
5523 infoPtr->nItemHeight) + infoPtr->rcList.top;
5525 if (!(lStyle & LVS_NOSCROLL))
5527 SCROLLINFO scrollInfo;
5528 /* Adjust position by scrollbar offset */
5529 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
5530 scrollInfo.cbSize = sizeof(SCROLLINFO);
5531 scrollInfo.fMask = SIF_POS;
5532 GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
5533 lpRect->left -= scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE;
5536 else /* either LVS_ICON or LVS_SMALLICON */
5538 if ((hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)))
5540 if ((lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)))
5542 bResult = TRUE;
5543 lpRect->left = lpItem->ptPosition.x;
5544 lpRect->top = lpItem->ptPosition.y;
5549 lpRect->right = lpRect->left + infoPtr->nItemWidth;
5550 lpRect->bottom = lpRect->top + infoPtr->nItemHeight;
5551 TRACE("result %s: (%d,%d)-(%d,%d)\n", bResult ? "TRUE" : "FALSE",
5552 lpRect->left, lpRect->top, lpRect->right, lpRect->bottom);
5553 return bResult;
5556 /***
5557 * DESCRIPTION:
5558 * Retrieves the position (upper-left) of the listview control item.
5559 * Note that for LVS_ICON style, the upper-left is that of the icon
5560 * and not the bounding box.
5562 * PARAMETER(S):
5563 * [I] HWND : window handle
5564 * [I] INT : item index
5565 * [O] LPPOINT : coordinate information
5567 * RETURN:
5568 * SUCCESS : TRUE
5569 * FAILURE : FALSE
5571 static BOOL LISTVIEW_GetItemPosition(HWND hwnd, INT nItem, LPPOINT lpptPosition)
5573 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5574 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5575 BOOL bResult = FALSE;
5576 RECT rcBounding;
5578 TRACE("(hwnd=%x, nItem=%d, lpptPosition=%p)\n", hwnd, nItem, lpptPosition);
5580 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) &&
5581 (lpptPosition != NULL))
5583 bResult = LISTVIEW_GetItemBoundBox(hwnd, nItem, &rcBounding);
5584 lpptPosition->x = rcBounding.left;
5585 lpptPosition->y = rcBounding.top;
5586 if (uView == LVS_ICON)
5588 lpptPosition->y += ICON_TOP_PADDING;
5589 lpptPosition->x += (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
5591 TRACE("result %s (%ld,%ld)\n", bResult ? "TRUE" : "FALSE",
5592 lpptPosition->x, lpptPosition->y);
5594 return bResult;
5597 /***
5598 * Update the bounding rectangle around the text under a large icon.
5599 * This depends on whether it has the focus or not.
5600 * On entry the rectangle's top, left and right should be set.
5601 * On return the bottom will also be set and the width may have been
5602 * modified.
5604 * This appears to be weird, even in the Microsoft implementation.
5607 static void ListView_UpdateLargeItemLabelRect (
5608 HWND hwnd, /* The window of the listview */
5609 const LISTVIEW_INFO *infoPtr, /* The listview itself */
5610 int nItem, /* The item for which we are calculating this */
5611 RECT *rect) /* The rectangle to be updated */
5613 HDC hdc = GetDC (hwnd);
5614 HFONT hOldFont = SelectObject (hdc, infoPtr->hFont);
5616 if (infoPtr->bFocus && infoPtr->nFocusedItem == nItem)
5618 /* We (aim to) display the full text. In Windows 95 it appears to
5619 * calculate the size assuming the specified font and then it draws
5620 * the text in that region with the specified font except scaled to
5621 * 10 point (or the height of the system font or ...). Thus if the
5622 * window has 24 point Helvetica the highlit rectangle will be
5623 * taller than the text and if it is 7 point Helvetica then the text
5624 * will be clipped.
5625 * For now we will simply say that it is the correct size to display
5626 * the text in the specified font.
5628 LVITEMW lvItem;
5629 lvItem.mask = LVIF_TEXT;
5630 lvItem.iItem = nItem;
5631 lvItem.iSubItem = 0;
5632 /* We will specify INTERNAL and so will receive back a const
5633 * pointer to the text, rather than specifying a buffer to which
5634 * to copy it.
5636 LISTVIEW_GetItemW (hwnd, &lvItem, TRUE);
5637 DrawTextW (hdc, lvItem.pszText, -1, rect, DT_CALCRECT |
5638 DT_NOCLIP | DT_EDITCONTROL | DT_TOP | DT_CENTER |
5639 DT_WORDBREAK | DT_NOPREFIX);
5640 /* Maintain this DT_* list in line with LISTVIEW_DrawLargeItem */
5642 else
5644 /* As far as I can see the text region seems to be trying to be
5645 * "tall enough for two lines of text". Once again (comctl32.dll ver
5646 * 5.81?) it measures this on the basis of the selected font and then
5647 * draws it with the same font except in 10 point size. This can lead
5648 * to more or less than the two rows appearing.
5649 * Question; are we supposed to be including DT_EXTERNALLEADING?
5650 * Question; should the width be shrunk to the space required to
5651 * display the two lines?
5653 rect->bottom = rect->top + 2 * infoPtr->ntmHeight;
5656 SelectObject (hdc, hOldFont);
5657 ReleaseDC (hwnd, hdc);
5660 /***
5661 * DESCRIPTION:
5662 * Retrieves the bounding rectangle for a listview control item.
5664 * PARAMETER(S):
5665 * [I] HWND : window handle
5666 * [I] INT : item index
5667 * [IO] LPRECT : bounding rectangle coordinates
5668 * lprc->left specifies the portion of the item for which the bounding
5669 * rectangle will be retrieved.
5671 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5672 * including the icon and label.
5673 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5674 * LVIR_LABEL Returns the bounding rectangle of the item text.
5675 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5676 * rectangles, but excludes columns in report view.
5678 * RETURN:
5679 * SUCCESS : TRUE
5680 * FAILURE : FALSE
5682 * NOTES
5683 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5684 * upon whether the window has the focus currently and on whether the item
5685 * is the one with the focus. Ensure that the control's record of which
5686 * item has the focus agrees with the items' records.
5688 static LRESULT LISTVIEW_GetItemRect(HWND hwnd, INT nItem, LPRECT lprc)
5690 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5691 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5692 BOOL bResult = FALSE;
5693 POINT ptOrigin;
5694 POINT ptItem;
5695 INT nLeftPos;
5696 INT nLabelWidth;
5697 INT nIndent;
5698 LVITEMW lvItem;
5699 RECT rcInternal;
5701 TRACE("(hwnd=%x, nItem=%d, lprc=%p)\n", hwnd, nItem, lprc);
5703 if (uView & LVS_REPORT)
5705 ZeroMemory(&lvItem, sizeof(lvItem));
5706 lvItem.mask = LVIF_INDENT;
5707 lvItem.iItem = nItem;
5708 lvItem.iSubItem = 0;
5709 LISTVIEW_GetItemW(hwnd, &lvItem, TRUE);
5711 /* do indent */
5712 if (lvItem.iIndent>0 && infoPtr->iconSize.cx > 0)
5713 nIndent = infoPtr->iconSize.cx * lvItem.iIndent;
5714 else
5715 nIndent = 0;
5717 else
5718 nIndent = 0;
5720 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) && (lprc != NULL))
5722 switch(lprc->left)
5724 case LVIR_ICON:
5725 if (!ListView_GetItemPosition(hwnd, nItem, &ptItem)) break;
5726 if (uView == LVS_ICON)
5728 if (infoPtr->himlNormal != NULL)
5730 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5732 bResult = TRUE;
5733 lprc->left = ptItem.x + ptOrigin.x;
5734 lprc->top = ptItem.y + ptOrigin.y;
5735 lprc->right = lprc->left + infoPtr->iconSize.cx;
5736 lprc->bottom = (lprc->top + infoPtr->iconSize.cy +
5737 ICON_BOTTOM_PADDING + ICON_TOP_PADDING);
5741 else if (uView == LVS_SMALLICON)
5743 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5745 bResult = TRUE;
5746 lprc->left = ptItem.x + ptOrigin.x;
5747 lprc->top = ptItem.y + ptOrigin.y;
5748 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5750 if (infoPtr->himlState != NULL)
5751 lprc->left += infoPtr->iconSize.cx;
5753 if (infoPtr->himlSmall != NULL)
5754 lprc->right = lprc->left + infoPtr->iconSize.cx;
5755 else
5756 lprc->right = lprc->left;
5759 else
5761 bResult = TRUE;
5762 lprc->left = ptItem.x;
5763 if (uView & LVS_REPORT)
5764 lprc->left += nIndent;
5765 lprc->top = ptItem.y;
5766 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5768 if (infoPtr->himlState != NULL)
5769 lprc->left += infoPtr->iconSize.cx;
5771 if (infoPtr->himlSmall != NULL)
5772 lprc->right = lprc->left + infoPtr->iconSize.cx;
5773 else
5774 lprc->right = lprc->left;
5776 break;
5778 case LVIR_LABEL:
5779 if (!ListView_GetItemPosition(hwnd, nItem, &ptItem)) break;
5780 if (uView == LVS_ICON)
5782 if (infoPtr->himlNormal != NULL)
5784 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5786 bResult = TRUE;
5787 lprc->left = ptItem.x + ptOrigin.x;
5788 lprc->top = (ptItem.y + ptOrigin.y + infoPtr->iconSize.cy +
5789 ICON_BOTTOM_PADDING);
5790 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5791 if (infoPtr->iconSpacing.cx - nLabelWidth > 1)
5793 lprc->left += (infoPtr->iconSpacing.cx - nLabelWidth) / 2;
5794 lprc->right = lprc->left + nLabelWidth;
5796 else
5798 lprc->left += 1;
5799 lprc->right = lprc->left + infoPtr->iconSpacing.cx - 1;
5800 ListView_UpdateLargeItemLabelRect (hwnd, infoPtr, nItem, lprc);
5805 else if (uView == LVS_SMALLICON)
5807 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5809 bResult = TRUE;
5810 nLeftPos = lprc->left = ptItem.x + ptOrigin.x;
5811 lprc->top = ptItem.y + ptOrigin.y;
5812 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5814 if (infoPtr->himlState != NULL)
5815 lprc->left += infoPtr->iconSize.cx;
5817 if (infoPtr->himlSmall != NULL)
5818 lprc->left += infoPtr->iconSize.cx;
5820 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5821 nLabelWidth += TRAILING_PADDING;
5822 if (lprc->left + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5823 lprc->right = lprc->left + nLabelWidth;
5824 else
5825 lprc->right = nLeftPos + infoPtr->nItemWidth;
5828 else
5830 bResult = TRUE;
5831 if (uView == LVS_REPORT)
5832 nLeftPos = lprc->left = ptItem.x + nIndent;
5833 else
5834 nLeftPos = lprc->left = ptItem.x;
5835 lprc->top = ptItem.y;
5836 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5838 if (infoPtr->himlState != NULL)
5839 lprc->left += infoPtr->iconSize.cx;
5841 if (infoPtr->himlSmall != NULL)
5842 lprc->left += infoPtr->iconSize.cx;
5844 if (uView != LVS_REPORT)
5846 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5847 nLabelWidth += TRAILING_PADDING;
5848 if (infoPtr->himlSmall)
5849 nLabelWidth += IMAGE_PADDING;
5851 else
5852 nLabelWidth = LISTVIEW_GetColumnWidth(hwnd, 0)-lprc->left;
5853 if (lprc->left + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5854 lprc->right = lprc->left + nLabelWidth;
5855 else
5856 lprc->right = nLeftPos + infoPtr->nItemWidth;
5858 break;
5860 case LVIR_BOUNDS:
5861 if (!LISTVIEW_GetItemBoundBox(hwnd, nItem, &rcInternal)) break;
5862 ptItem.x = rcInternal.left;
5863 ptItem.y = rcInternal.top;
5864 if (uView == LVS_ICON)
5866 if (infoPtr->himlNormal != NULL)
5868 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5870 RECT label_rect;
5871 INT text_left, text_right, icon_left, text_pos_x;
5872 /* for style LVS_ICON bounds
5873 * left = min(icon.left, text.left)
5874 * right = max(icon.right, text.right)
5875 * top = boundbox.top + NOTHITABLE
5876 * bottom = text.bottom + 1
5878 bResult = TRUE;
5879 icon_left = text_left = ptItem.x;
5881 /* Correct ptItem to icon upper-left */
5882 icon_left += (infoPtr->nItemWidth - infoPtr->iconSize.cx)/2;
5883 ptItem.y += ICON_TOP_PADDING;
5885 /* Compute the label left and right */
5886 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5887 text_pos_x = infoPtr->iconSpacing.cx - 2*CAPTION_BORDER - nLabelWidth;
5888 if (text_pos_x > 1)
5890 text_left += text_pos_x / 2;
5891 text_right = text_left + nLabelWidth + 2*CAPTION_BORDER;
5893 else
5895 text_left += 1;
5896 text_right = text_left + infoPtr->iconSpacing.cx - 1;
5899 /* Compute rectangle w/o the text height */
5900 lprc->left = min(icon_left, text_left) + ptOrigin.x;
5901 lprc->right = max(icon_left + infoPtr->iconSize.cx,
5902 text_right) + ptOrigin.x;
5903 lprc->top = ptItem.y + ptOrigin.y - ICON_TOP_PADDING_HITABLE;
5904 lprc->bottom = lprc->top + ICON_TOP_PADDING_HITABLE
5905 + infoPtr->iconSize.cy + 1
5906 + ICON_BOTTOM_PADDING;
5908 CopyRect (&label_rect, lprc);
5909 label_rect.top = lprc->bottom;
5910 ListView_UpdateLargeItemLabelRect (hwnd, infoPtr, nItem, &label_rect);
5911 UnionRect (lprc, lprc, &label_rect);
5915 else if (uView == LVS_SMALLICON)
5917 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5919 bResult = TRUE;
5920 lprc->left = ptItem.x + ptOrigin.x;
5921 lprc->right = lprc->left;
5922 lprc->top = ptItem.y + ptOrigin.y;
5923 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5924 if (infoPtr->himlState != NULL)
5925 lprc->right += infoPtr->iconSize.cx;
5926 if (infoPtr->himlSmall != NULL)
5927 lprc->right += infoPtr->iconSize.cx;
5929 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5930 nLabelWidth += TRAILING_PADDING;
5931 if (infoPtr->himlSmall)
5932 nLabelWidth += IMAGE_PADDING;
5933 if (lprc->right + nLabelWidth < lprc->left + infoPtr->nItemWidth)
5934 lprc->right += nLabelWidth;
5935 else
5936 lprc->right = lprc->left + infoPtr->nItemWidth;
5939 else
5941 bResult = TRUE;
5942 lprc->left = ptItem.x;
5943 if (!(infoPtr->dwExStyle&LVS_EX_FULLROWSELECT) && uView&LVS_REPORT)
5944 lprc->left += nIndent;
5945 lprc->right = lprc->left;
5946 lprc->top = ptItem.y;
5947 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5949 if ((infoPtr->dwExStyle & LVS_EX_FULLROWSELECT) || (uView == LVS_REPORT))
5951 RECT br;
5952 int nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
5953 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
5955 lprc->right = max(lprc->left, br.right - REPORT_MARGINX);
5957 else
5959 if (infoPtr->himlState != NULL)
5960 lprc->right += infoPtr->iconSize.cx;
5962 if (infoPtr->himlSmall != NULL)
5963 lprc->right += infoPtr->iconSize.cx;
5965 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5966 nLabelWidth += TRAILING_PADDING;
5967 if (lprc->right + nLabelWidth < lprc->left + infoPtr->nItemWidth)
5968 lprc->right += nLabelWidth;
5969 else
5970 lprc->right = lprc->left + infoPtr->nItemWidth;
5973 break;
5975 case LVIR_SELECTBOUNDS:
5976 if (!ListView_GetItemPosition(hwnd, nItem, &ptItem)) break;
5977 if (uView == LVS_ICON)
5979 if (infoPtr->himlNormal != NULL)
5981 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5983 bResult = TRUE;
5984 lprc->left = ptItem.x + ptOrigin.x;
5985 lprc->top = ptItem.y + ptOrigin.y;
5986 lprc->right = lprc->left + infoPtr->iconSpacing.cx;
5987 lprc->bottom = lprc->top + infoPtr->iconSpacing.cy;
5991 else if (uView == LVS_SMALLICON)
5993 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5995 bResult = TRUE;
5996 nLeftPos= lprc->left = ptItem.x + ptOrigin.x;
5997 lprc->top = ptItem.y + ptOrigin.y;
5998 lprc->bottom = lprc->top + infoPtr->nItemHeight;
6000 if (infoPtr->himlState != NULL)
6001 lprc->left += infoPtr->iconSize.cx;
6003 lprc->right = lprc->left;
6005 if (infoPtr->himlSmall != NULL)
6006 lprc->right += infoPtr->iconSize.cx;
6008 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
6009 nLabelWidth += TRAILING_PADDING;
6010 if (lprc->right + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
6011 lprc->right += nLabelWidth;
6012 else
6013 lprc->right = nLeftPos + infoPtr->nItemWidth;
6016 else
6018 bResult = TRUE;
6019 if (!(infoPtr->dwExStyle&LVS_EX_FULLROWSELECT) && (uView&LVS_REPORT))
6020 nLeftPos = lprc->left = ptItem.x + nIndent;
6021 else
6022 nLeftPos = lprc->left = ptItem.x;
6023 lprc->top = ptItem.y;
6024 lprc->bottom = lprc->top + infoPtr->nItemHeight;
6026 if (infoPtr->himlState != NULL)
6027 lprc->left += infoPtr->iconSize.cx;
6029 lprc->right = lprc->left;
6031 if (infoPtr->dwExStyle & LVS_EX_FULLROWSELECT)
6033 RECT br;
6034 int nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
6035 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
6037 lprc->right = max(lprc->left, br.right - REPORT_MARGINX);
6039 else
6041 if (infoPtr->himlSmall != NULL)
6042 lprc->right += infoPtr->iconSize.cx;
6044 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
6045 nLabelWidth += TRAILING_PADDING;
6046 if (infoPtr->himlSmall)
6047 nLabelWidth += IMAGE_PADDING;
6048 if (lprc->right + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
6049 lprc->right += nLabelWidth;
6050 else
6051 lprc->right = nLeftPos + infoPtr->nItemWidth;
6054 break;
6058 TRACE("result %s (%d,%d)-(%d,%d)\n", bResult ? "TRUE" : "FALSE",
6059 lprc->left, lprc->top, lprc->right, lprc->bottom);
6061 return bResult;
6065 static LRESULT LISTVIEW_GetSubItemRect(HWND hwnd, INT nItem, INT nSubItem, INT
6066 flags, LPRECT lprc)
6068 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
6069 INT count;
6071 TRACE("(hwnd=%x, nItem=%d, nSubItem=%d lprc=%p)\n", hwnd, nItem, nSubItem,
6072 lprc);
6074 if (!(uView & LVS_REPORT))
6075 return FALSE;
6077 if (flags & LVIR_ICON)
6079 FIXME("Unimplemented LVIR_ICON\n");
6080 return FALSE;
6082 else
6084 LISTVIEW_GetItemRect(hwnd,nItem,lprc);
6085 count = 0;
6086 while (count<(nSubItem-1))
6088 lprc->left += LISTVIEW_GetColumnWidth(hwnd,count);
6089 count ++;
6092 lprc->right = LISTVIEW_GetColumnWidth(hwnd,(nSubItem-1)) +
6093 lprc->left;
6096 return TRUE;
6100 /***
6101 * DESCRIPTION:
6102 * Retrieves the width of a label.
6104 * PARAMETER(S):
6105 * [I] HWND : window handle
6107 * RETURN:
6108 * SUCCESS : string width (in pixels)
6109 * FAILURE : zero
6111 static INT LISTVIEW_GetLabelWidth(HWND hwnd, INT nItem)
6113 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
6114 INT nLabelWidth = 0;
6115 LVITEMW lvItem;
6117 TRACE("(hwnd=%x, nItem=%d)\n", hwnd, nItem);
6119 ZeroMemory(&lvItem, sizeof(lvItem));
6120 lvItem.mask = LVIF_TEXT;
6121 lvItem.iItem = nItem;
6122 lvItem.cchTextMax = DISP_TEXT_SIZE;
6123 lvItem.pszText = szDispText;
6124 if (LISTVIEW_GetItemW(hwnd, &lvItem, TRUE))
6125 nLabelWidth = ListView_GetStringWidthW(hwnd, lvItem.pszText);
6127 return nLabelWidth;
6130 /***
6131 * DESCRIPTION:
6132 * Retrieves the spacing between listview control items.
6134 * PARAMETER(S):
6135 * [I] HWND : window handle
6136 * [I] BOOL : flag for small or large icon
6138 * RETURN:
6139 * Horizontal + vertical spacing
6141 static LRESULT LISTVIEW_GetItemSpacing(HWND hwnd, BOOL bSmall)
6143 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6144 LONG lResult;
6146 if (bSmall == FALSE)
6148 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6150 else
6152 LONG style = GetWindowLongW(hwnd, GWL_STYLE);
6153 if ((style & LVS_TYPEMASK) == LVS_ICON)
6154 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
6155 else
6156 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
6158 return lResult;
6161 /***
6162 * DESCRIPTION:
6163 * Retrieves the state of a listview control item.
6165 * PARAMETER(S):
6166 * [I] HWND : window handle
6167 * [I] INT : item index
6168 * [I] UINT : state mask
6170 * RETURN:
6171 * State specified by the mask.
6173 static LRESULT LISTVIEW_GetItemState(HWND hwnd, INT nItem, UINT uMask)
6175 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6176 LVITEMW lvItem;
6177 UINT uState = 0;
6179 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
6181 ZeroMemory(&lvItem, sizeof(lvItem));
6182 lvItem.iItem = nItem;
6183 lvItem.stateMask = uMask;
6184 lvItem.mask = LVIF_STATE;
6185 if (LISTVIEW_GetItemW(hwnd, &lvItem, TRUE))
6186 uState = lvItem.state;
6189 return uState;
6192 /***
6193 * DESCRIPTION:
6194 * Retrieves the text of a listview control item or subitem.
6196 * PARAMETER(S):
6197 * [I] hwnd : window handle
6198 * [I] nItem : item index
6199 * [IO] lpLVItem : item information
6200 * [I] isW : TRUE if lpLVItem is Unicode
6202 * RETURN:
6203 * SUCCESS : string length
6204 * FAILURE : 0
6206 static LRESULT LISTVIEW_GetItemTextT(HWND hwnd, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
6208 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6209 INT nLength = 0;
6211 if (lpLVItem != NULL)
6213 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
6215 lpLVItem->mask = LVIF_TEXT;
6216 lpLVItem->iItem = nItem;
6217 if (LISTVIEW_GetItemT(hwnd, lpLVItem, FALSE, isW))
6218 nLength = textlenT(lpLVItem->pszText, isW);
6222 return nLength;
6225 /***
6226 * DESCRIPTION:
6227 * Searches for an item based on properties + relationships.
6229 * PARAMETER(S):
6230 * [I] HWND : window handle
6231 * [I] INT : item index
6232 * [I] INT : relationship flag
6234 * RETURN:
6235 * SUCCESS : item index
6236 * FAILURE : -1
6238 static LRESULT LISTVIEW_GetNextItem(HWND hwnd, INT nItem, UINT uFlags)
6240 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6241 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
6242 UINT uMask = 0;
6243 LVFINDINFOW lvFindInfo;
6244 INT nCountPerColumn;
6245 INT i;
6247 if ((nItem >= -1) && (nItem < GETITEMCOUNT(infoPtr)))
6249 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
6251 if (uFlags & LVNI_CUT)
6252 uMask |= LVIS_CUT;
6254 if (uFlags & LVNI_DROPHILITED)
6255 uMask |= LVIS_DROPHILITED;
6257 if (uFlags & LVNI_FOCUSED)
6258 uMask |= LVIS_FOCUSED;
6260 if (uFlags & LVNI_SELECTED)
6261 uMask |= LVIS_SELECTED;
6263 if (uFlags & LVNI_ABOVE)
6265 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
6267 while (nItem >= 0)
6269 nItem--;
6270 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6271 return nItem;
6274 else
6276 lvFindInfo.flags = LVFI_NEARESTXY;
6277 lvFindInfo.vkDirection = VK_UP;
6278 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
6279 while ((nItem = ListView_FindItemW(hwnd, nItem, &lvFindInfo)) != -1)
6281 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6282 return nItem;
6286 else if (uFlags & LVNI_BELOW)
6288 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
6290 while (nItem < GETITEMCOUNT(infoPtr))
6292 nItem++;
6293 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6294 return nItem;
6297 else
6299 lvFindInfo.flags = LVFI_NEARESTXY;
6300 lvFindInfo.vkDirection = VK_DOWN;
6301 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
6302 while ((nItem = ListView_FindItemW(hwnd, nItem, &lvFindInfo)) != -1)
6304 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6305 return nItem;
6309 else if (uFlags & LVNI_TOLEFT)
6311 if (uView == LVS_LIST)
6313 nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
6314 while (nItem - nCountPerColumn >= 0)
6316 nItem -= nCountPerColumn;
6317 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6318 return nItem;
6321 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6323 lvFindInfo.flags = LVFI_NEARESTXY;
6324 lvFindInfo.vkDirection = VK_LEFT;
6325 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
6326 while ((nItem = ListView_FindItemW(hwnd, nItem, &lvFindInfo)) != -1)
6328 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6329 return nItem;
6333 else if (uFlags & LVNI_TORIGHT)
6335 if (uView == LVS_LIST)
6337 nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
6338 while (nItem + nCountPerColumn < GETITEMCOUNT(infoPtr))
6340 nItem += nCountPerColumn;
6341 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6342 return nItem;
6345 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6347 lvFindInfo.flags = LVFI_NEARESTXY;
6348 lvFindInfo.vkDirection = VK_RIGHT;
6349 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
6350 while ((nItem = ListView_FindItemW(hwnd, nItem, &lvFindInfo)) != -1)
6352 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6353 return nItem;
6357 else
6359 nItem++;
6361 /* search by index */
6362 for (i = nItem; i < GETITEMCOUNT(infoPtr); i++)
6364 if ((ListView_GetItemState(hwnd, i, uMask) & uMask) == uMask)
6365 return i;
6370 return -1;
6373 /* LISTVIEW_GetNumberOfWorkAreas */
6375 /***
6376 * DESCRIPTION:
6377 * Retrieves the origin coordinates when in icon or small icon display mode.
6379 * PARAMETER(S):
6380 * [I] HWND : window handle
6381 * [O] LPPOINT : coordinate information
6383 * RETURN:
6384 * SUCCESS : TRUE
6385 * FAILURE : FALSE
6387 static LRESULT LISTVIEW_GetOrigin(HWND hwnd, LPPOINT lpptOrigin)
6389 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
6390 UINT uView = lStyle & LVS_TYPEMASK;
6391 BOOL bResult = FALSE;
6393 TRACE("(hwnd=%x, lpptOrigin=%p)\n", hwnd, lpptOrigin);
6395 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6397 SCROLLINFO scrollInfo;
6398 ZeroMemory(lpptOrigin, sizeof(POINT));
6399 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
6400 scrollInfo.cbSize = sizeof(SCROLLINFO);
6402 if (lStyle & WS_HSCROLL)
6404 scrollInfo.fMask = SIF_POS;
6405 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
6406 lpptOrigin->x = -scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE;
6409 if (lStyle & WS_VSCROLL)
6411 scrollInfo.fMask = SIF_POS;
6412 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
6413 lpptOrigin->y = -scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE;
6416 bResult = TRUE;
6419 return bResult;
6422 /***
6423 * DESCRIPTION:
6424 * Retrieves the number of items that are marked as selected.
6426 * PARAMETER(S):
6427 * [I] HWND : window handle
6429 * RETURN:
6430 * Number of items selected.
6432 static LRESULT LISTVIEW_GetSelectedCount(HWND hwnd)
6434 /* REDO THIS */
6435 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6436 INT nSelectedCount = 0;
6437 INT i;
6439 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
6441 if (ListView_GetItemState(hwnd, i, LVIS_SELECTED) & LVIS_SELECTED)
6442 nSelectedCount++;
6445 return nSelectedCount;
6448 /***
6449 * DESCRIPTION:
6450 * Retrieves item index that marks the start of a multiple selection.
6452 * PARAMETER(S):
6453 * [I] HWND : window handle
6455 * RETURN:
6456 * Index number or -1 if there is no selection mark.
6458 static LRESULT LISTVIEW_GetSelectionMark(HWND hwnd)
6460 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6462 return infoPtr->nSelectionMark;
6466 /***
6467 * DESCRIPTION:
6468 * Retrieves the width of a string.
6470 * PARAMETER(S):
6471 * [I] hwnd : window handle
6472 * [I] lpszText : text string to process
6473 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
6475 * RETURN:
6476 * SUCCESS : string width (in pixels)
6477 * FAILURE : zero
6479 static LRESULT LISTVIEW_GetStringWidthT(HWND hwnd, LPCWSTR lpszText, BOOL isW)
6481 if (is_textT(lpszText, isW))
6483 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6484 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
6485 HDC hdc = GetDC(hwnd);
6486 HFONT hOldFont = SelectObject(hdc, hFont);
6487 SIZE stringSize;
6488 ZeroMemory(&stringSize, sizeof(SIZE));
6489 if (isW)
6490 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
6491 else
6492 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
6493 SelectObject(hdc, hOldFont);
6494 ReleaseDC(hwnd, hdc);
6495 return stringSize.cx;
6497 return 0;
6500 /***
6501 * DESCRIPTION:
6502 * Retrieves the text backgound color.
6504 * PARAMETER(S):
6505 * [I] HWND : window handle
6507 * RETURN:
6508 * COLORREF associated with the the background.
6510 static LRESULT LISTVIEW_GetTextBkColor(HWND hwnd)
6512 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
6514 return infoPtr->clrTextBk;
6517 /***
6518 * DESCRIPTION:
6519 * Retrieves the text color.
6521 * PARAMETER(S):
6522 * [I] HWND : window handle
6524 * RETURN:
6525 * COLORREF associated with the text.
6527 static LRESULT LISTVIEW_GetTextColor(HWND hwnd)
6529 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
6531 return infoPtr->clrText;
6534 /***
6535 * DESCRIPTION:
6536 * Determines item if a hit or closest if not
6538 * PARAMETER(S):
6539 * [I] HWND : window handle
6540 * [IO] LPLV_INTHIT : hit test information
6541 * [I] subitem : fill out iSubItem.
6543 * RETURN:
6544 * SUCCESS : item index of hit
6545 * FAILURE : -1
6547 static INT LISTVIEW_SuperHitTestItem(HWND hwnd, LPLV_INTHIT lpInt, BOOL subitem)
6549 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6550 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
6551 UINT uView = lStyle & LVS_TYPEMASK;
6552 INT i,topindex,bottomindex;
6553 RECT rcItem;
6554 DWORD xterm, yterm, dist;
6556 TRACE("(hwnd=%x, x=%ld, y=%ld)\n", hwnd, lpInt->ht.pt.x, lpInt->ht.pt.y);
6558 topindex = ListView_GetTopIndex(hwnd);
6559 if (uView == LVS_REPORT)
6561 bottomindex = topindex + LISTVIEW_GetCountPerColumn(hwnd) + 1;
6562 bottomindex = min(bottomindex,GETITEMCOUNT(infoPtr));
6564 else
6566 bottomindex = GETITEMCOUNT(infoPtr);
6569 lpInt->distance = 0x7fffffff;
6570 lpInt->iDistItem = -1;
6572 for (i = topindex; i < bottomindex; i++)
6574 rcItem.left = LVIR_BOUNDS;
6575 if (LISTVIEW_GetItemRect(hwnd, i, &rcItem))
6577 if (PtInRect(&rcItem, lpInt->ht.pt))
6579 rcItem.left = LVIR_ICON;
6580 if (LISTVIEW_GetItemRect(hwnd, i, &rcItem))
6582 if (PtInRect(&rcItem, lpInt->ht.pt))
6584 lpInt->ht.flags = LVHT_ONITEMICON;
6585 lpInt->ht.iItem = i;
6586 if (subitem) lpInt->ht.iSubItem = 0;
6587 return i;
6591 rcItem.left = LVIR_LABEL;
6592 if (LISTVIEW_GetItemRect(hwnd, i, &rcItem))
6594 if (PtInRect(&rcItem, lpInt->ht.pt))
6596 lpInt->ht.flags = LVHT_ONITEMLABEL;
6597 lpInt->ht.iItem = i;
6598 if (subitem) lpInt->ht.iSubItem = 0;
6599 return i;
6603 lpInt->ht.flags = LVHT_ONITEMSTATEICON;
6604 lpInt->ht.iItem = i;
6605 if (subitem) lpInt->ht.iSubItem = 0;
6606 return i;
6608 else
6611 * Now compute distance from point to center of boundary
6612 * box. Since we are only interested in the relative
6613 * distance, we can skip the nasty square root operation
6615 xterm = rcItem.left + (rcItem.right - rcItem.left)/2 - lpInt->ht.pt.x;
6616 yterm = rcItem.top + (rcItem.bottom - rcItem.top)/2 - lpInt->ht.pt.y;
6617 dist = xterm * xterm + yterm * yterm;
6618 if (dist < lpInt->distance)
6620 lpInt->distance = dist;
6621 lpInt->iDistItem = i;
6627 lpInt->ht.flags = LVHT_NOWHERE;
6628 TRACE("no hit, closest item %d, distance %ld\n", lpInt->iDistItem, lpInt->distance);
6630 return -1;
6633 /***
6634 * DESCRIPTION:
6635 * Determines which section of the item was selected (if any).
6637 * PARAMETER(S):
6638 * [I] HWND : window handle
6639 * [IO] LPLVHITTESTINFO : hit test information
6640 * [I] subitem : fill out iSubItem.
6642 * RETURN:
6643 * SUCCESS : item index
6644 * FAILURE : -1
6646 static INT LISTVIEW_HitTestItem(HWND hwnd, LPLVHITTESTINFO lpHitTestInfo, BOOL subitem)
6648 INT ret;
6649 LV_INTHIT lv_inthit;
6651 TRACE("(hwnd=%x, x=%ld, y=%ld)\n", hwnd, lpHitTestInfo->pt.x,
6652 lpHitTestInfo->pt.y);
6654 memcpy(&lv_inthit, lpHitTestInfo, sizeof(LVHITTESTINFO));
6655 ret = LISTVIEW_SuperHitTestItem(hwnd, &lv_inthit, subitem);
6656 memcpy(lpHitTestInfo, &lv_inthit, sizeof(LVHITTESTINFO));
6657 return ret;
6660 /***
6661 * DESCRIPTION:
6662 * Determines which listview item is located at the specified position.
6664 * PARAMETER(S):
6665 * [I] HWND : window handle
6666 * [IO} LPLVHITTESTINFO : hit test information
6668 * RETURN:
6669 * SUCCESS : item index
6670 * FAILURE : -1
6672 static LRESULT LISTVIEW_HitTest(HWND hwnd, LPLVHITTESTINFO lpHitTestInfo)
6674 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6675 INT nItem = -1;
6677 lpHitTestInfo->flags = 0;
6679 if (infoPtr->rcList.left > lpHitTestInfo->pt.x)
6680 lpHitTestInfo->flags = LVHT_TOLEFT;
6681 else if (infoPtr->rcList.right < lpHitTestInfo->pt.x)
6682 lpHitTestInfo->flags = LVHT_TORIGHT;
6683 if (infoPtr->rcList.top > lpHitTestInfo->pt.y)
6684 lpHitTestInfo->flags |= LVHT_ABOVE;
6685 else if (infoPtr->rcList.bottom < lpHitTestInfo->pt.y)
6686 lpHitTestInfo->flags |= LVHT_BELOW;
6688 if (lpHitTestInfo->flags == 0)
6690 /* NOTE (mm 20001022): We must not allow iSubItem to be touched, for
6691 * an app might pass only a structure with space up to iItem!
6692 * (MS Office 97 does that for instance in the file open dialog)
6694 nItem = LISTVIEW_HitTestItem(hwnd, lpHitTestInfo, FALSE);
6697 return nItem;
6700 /***
6701 * DESCRIPTION:
6702 * Inserts a new column.
6704 * PARAMETER(S):
6705 * [I] HWND : window handle
6706 * [I] INT : column index
6707 * [I] LPLVCOLUMNW : column information
6709 * RETURN:
6710 * SUCCESS : new column index
6711 * FAILURE : -1
6713 static LRESULT LISTVIEW_InsertColumnT(HWND hwnd, INT nColumn,
6714 LPLVCOLUMNW lpColumn, BOOL isW)
6716 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6717 INT nNewColumn = -1;
6718 HDITEMW hdi;
6720 TRACE("(hwnd=%x, nColumn=%d, lpColumn=%p)\n",hwnd, nColumn, lpColumn);
6722 if (lpColumn != NULL)
6724 /* initialize memory */
6725 ZeroMemory(&hdi, sizeof(hdi));
6727 if (lpColumn->mask & LVCF_FMT)
6729 /* format member is valid */
6730 hdi.mask |= HDI_FORMAT;
6732 /* set text alignment (leftmost column must be left-aligned) */
6733 if (nColumn == 0)
6735 hdi.fmt |= HDF_LEFT;
6737 else
6739 if (lpColumn->fmt & LVCFMT_LEFT)
6741 hdi.fmt |= HDF_LEFT;
6743 else if (lpColumn->fmt & LVCFMT_RIGHT)
6745 hdi.fmt |= HDF_RIGHT;
6747 else if (lpColumn->fmt & LVCFMT_CENTER)
6749 hdi.fmt |= HDF_CENTER;
6753 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6755 hdi.fmt |= HDF_BITMAP_ON_RIGHT;
6756 /* ??? */
6759 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6761 /* ??? */
6764 if (lpColumn->fmt & LVCFMT_IMAGE)
6766 hdi.fmt |= HDF_IMAGE;
6767 hdi.iImage = I_IMAGECALLBACK;
6771 if (lpColumn->mask & LVCF_WIDTH)
6773 hdi.mask |= HDI_WIDTH;
6774 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
6776 /* make it fill the remainder of the controls width */
6777 HDITEMW hdit;
6778 RECT rcHeader;
6779 INT item_index;
6781 ZeroMemory(&hdit, sizeof(hdit));
6783 /* get the width of every item except the current one */
6784 hdit.mask = HDI_WIDTH;
6785 hdi.cxy = 0;
6787 for(item_index = 0; item_index < (nColumn - 1); item_index++) {
6788 Header_GetItemW(infoPtr->hwndHeader, item_index, (LPARAM)(&hdit));
6789 hdi.cxy+=hdit.cxy;
6792 /* retrieve the layout of the header */
6793 GetClientRect(hwnd, &rcHeader);
6794 /* GetWindowRect(infoPtr->hwndHeader, &rcHeader);*/
6795 TRACE("start cxy=%d left=%d right=%d\n", hdi.cxy, rcHeader.left, rcHeader.right);
6797 hdi.cxy = (rcHeader.right - rcHeader.left) - hdi.cxy;
6799 else
6800 hdi.cxy = lpColumn->cx;
6803 if (lpColumn->mask & LVCF_TEXT)
6805 hdi.mask |= HDI_TEXT | HDI_FORMAT;
6806 hdi.pszText = lpColumn->pszText;
6807 hdi.cchTextMax = textlenT(lpColumn->pszText, isW);
6808 hdi.fmt |= HDF_STRING;
6811 if (lpColumn->mask & LVCF_IMAGE)
6813 hdi.mask |= HDI_IMAGE;
6814 hdi.iImage = lpColumn->iImage;
6817 if (lpColumn->mask & LVCF_ORDER)
6819 hdi.mask |= HDI_ORDER;
6820 hdi.iOrder = lpColumn->iOrder;
6823 /* insert item in header control */
6824 nNewColumn = SendMessageW(infoPtr->hwndHeader, HDM_INSERTITEMT(isW),
6825 (WPARAM)nColumn, (LPARAM)&hdi);
6827 /* Need to reset the item width when inserting a new column */
6828 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
6830 LISTVIEW_UpdateScroll(hwnd);
6831 InvalidateRect(hwnd, NULL, FALSE);
6834 return nNewColumn;
6837 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
6838 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
6839 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6840 and not during the processing of a LVM_SORTITEMS message. Applications should provide
6841 their own sort proc. when sending LVM_SORTITEMS.
6843 /* Platform SDK:
6844 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6846 LVS_SORTXXX must be specified,
6847 LVS_OWNERDRAW is not set,
6848 <item>.pszText is not LPSTR_TEXTCALLBACK.
6850 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6851 are sorted based on item text..."
6853 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
6855 LONG lStyle = GetWindowLongW((HWND) lParam, GWL_STYLE);
6856 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 );
6857 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 );
6858 INT cmpv = lstrcmpW( lv_first->pszText, lv_second->pszText );
6859 /* if we're sorting descending, negate the return value */
6860 return (lStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
6863 /***
6864 * nESCRIPTION:
6865 * Inserts a new item in the listview control.
6867 * PARAMETER(S):
6868 * [I] HWND : window handle
6869 * [I] LPLVITEMW : item information
6870 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6872 * RETURN:
6873 * SUCCESS : new item index
6874 * FAILURE : -1
6876 static LRESULT LISTVIEW_InsertItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
6878 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6879 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
6880 UINT uView = lStyle & LVS_TYPEMASK;
6881 INT nItem = -1;
6882 HDPA hdpaSubItems;
6883 INT nItemWidth = 0;
6884 LISTVIEW_ITEM *lpItem = NULL;
6886 TRACE("(hwnd=%x, lpLVItem=%s, isW=%d)\n",
6887 hwnd, debuglvitem_t(lpLVItem, isW), isW);
6889 if (lStyle & LVS_OWNERDATA)
6891 nItem = infoPtr->hdpaItems->nItemCount;
6892 infoPtr->hdpaItems->nItemCount ++;
6893 return nItem;
6896 if (lpLVItem != NULL)
6898 /* make sure it's not a subitem; cannot insert a subitem */
6899 if (lpLVItem->iSubItem == 0)
6901 if ( (lpItem = (LISTVIEW_ITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_ITEM))) )
6903 ZeroMemory(lpItem, sizeof(LISTVIEW_ITEM));
6904 if (LISTVIEW_InitItemT(hwnd, lpItem, lpLVItem, isW))
6906 /* insert item in listview control data structure */
6907 if ( (hdpaSubItems = DPA_Create(8)) )
6909 if ( (nItem = DPA_InsertPtr(hdpaSubItems, 0, lpItem)) != -1)
6911 if ( ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
6912 && !(lStyle & LVS_OWNERDRAWFIXED)
6913 && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText) )
6915 /* Insert the item in the proper sort order based on the pszText
6916 member. See comments for LISTVIEW_InsertCompare() for greater detail */
6917 nItem = DPA_InsertPtr( infoPtr->hdpaItems,
6918 GETITEMCOUNT( infoPtr ) + 1, hdpaSubItems );
6919 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, hwnd );
6920 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
6922 else
6924 nItem = DPA_InsertPtr(infoPtr->hdpaItems, lpLVItem->iItem,
6925 hdpaSubItems);
6927 if (nItem != -1)
6929 NMLISTVIEW nmlv;
6931 LISTVIEW_ShiftIndices(hwnd,nItem,1);
6933 /* manage item focus */
6934 if (lpLVItem->mask & LVIF_STATE)
6936 lpItem->state &= ~(LVIS_FOCUSED|LVIS_SELECTED);
6937 if (lpLVItem->stateMask & LVIS_SELECTED)
6938 LISTVIEW_SetSelection(hwnd, nItem);
6939 else if (lpLVItem->stateMask & LVIS_FOCUSED)
6940 LISTVIEW_SetItemFocus(hwnd, nItem);
6943 /* send LVN_INSERTITEM notification */
6944 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6945 nmlv.iItem = nItem;
6946 nmlv.lParam = lpItem->lParam;
6947 listview_notify(hwnd, LVN_INSERTITEM, &nmlv);
6949 if ((uView == LVS_SMALLICON) || (uView == LVS_LIST))
6951 nItemWidth = LISTVIEW_CalculateWidth(hwnd, lpLVItem->iItem);
6952 if (nItemWidth > infoPtr->nItemWidth)
6953 infoPtr->nItemWidth = nItemWidth;
6956 /* align items (set position of each item) */
6957 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6959 if (lStyle & LVS_ALIGNLEFT)
6960 LISTVIEW_AlignLeft(hwnd);
6961 else
6962 LISTVIEW_AlignTop(hwnd);
6965 LISTVIEW_UpdateScroll(hwnd);
6966 /* refresh client area */
6967 InvalidateRect(hwnd, NULL, FALSE);
6976 /* free memory if unsuccessful */
6977 if ((nItem == -1) && (lpItem != NULL))
6978 COMCTL32_Free(lpItem);
6980 return nItem;
6983 /***
6984 * DESCRIPTION:
6985 * Redraws a range of items.
6987 * PARAMETER(S):
6988 * [I] HWND : window handle
6989 * [I] INT : first item
6990 * [I] INT : last item
6992 * RETURN:
6993 * SUCCESS : TRUE
6994 * FAILURE : FALSE
6996 static LRESULT LISTVIEW_RedrawItems(HWND hwnd, INT nFirst, INT nLast)
6998 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6999 BOOL bResult = FALSE;
7000 RECT rcItem;
7001 INT i;
7003 if (nFirst <= nLast)
7005 if ((nFirst >= 0) && (nFirst < GETITEMCOUNT(infoPtr)))
7007 if ((nLast >= 0) && (nLast < GETITEMCOUNT(infoPtr)))
7009 for (i = nFirst; i <= nLast; i++)
7011 rcItem.left = LVIR_BOUNDS;
7012 LISTVIEW_GetItemRect(hwnd, i, &rcItem);
7013 InvalidateRect(hwnd, &rcItem, TRUE);
7019 return bResult;
7022 /* LISTVIEW_Scroll */
7024 /***
7025 * DESCRIPTION:
7026 * Sets the background color.
7028 * PARAMETER(S):
7029 * [I] HWND : window handle
7030 * [I] COLORREF : background color
7032 * RETURN:
7033 * SUCCESS : TRUE
7034 * FAILURE : FALSE
7036 static LRESULT LISTVIEW_SetBkColor(HWND hwnd, COLORREF clrBk)
7038 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7040 if(infoPtr->clrBk!=clrBk){
7041 infoPtr->clrBk = clrBk;
7042 InvalidateRect(hwnd, NULL, TRUE);
7045 return TRUE;
7048 /* LISTVIEW_SetBkImage */
7050 /***
7051 * DESCRIPTION:
7052 * Sets the callback mask. This mask will be used when the parent
7053 * window stores state information (some or all).
7055 * PARAMETER(S):
7056 * [I] HWND : window handle
7057 * [I] UINT : state mask
7059 * RETURN:
7060 * SUCCESS : TRUE
7061 * FAILURE : FALSE
7063 static BOOL LISTVIEW_SetCallbackMask(HWND hwnd, UINT uMask)
7065 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7067 infoPtr->uCallbackMask = uMask;
7069 return TRUE;
7072 /***
7073 * DESCRIPTION:
7074 * Sets the attributes of a header item.
7076 * PARAMETER(S):
7077 * [I] HWND : window handle
7078 * [I] INT : column index
7079 * [I] LPLVCOLUMNW : column attributes
7080 * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW,
7081 * otherwise it is in fact a LPLVCOLUMNA
7083 * RETURN:
7084 * SUCCESS : TRUE
7085 * FAILURE : FALSE
7087 static LRESULT LISTVIEW_SetColumnT(HWND hwnd, INT nColumn,
7088 LPLVCOLUMNW lpColumn, BOOL isW)
7090 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7091 BOOL bResult = FALSE;
7092 HDITEMW hdi, hdiget;
7094 if ((lpColumn != NULL) && (nColumn >= 0) &&
7095 (nColumn < Header_GetItemCount(infoPtr->hwndHeader)))
7097 /* initialize memory */
7098 ZeroMemory(&hdi, sizeof(hdi));
7100 if (lpColumn->mask & LVCF_FMT)
7102 /* format member is valid */
7103 hdi.mask |= HDI_FORMAT;
7105 /* get current format first */
7106 hdiget.mask = HDI_FORMAT;
7107 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
7108 /* preserve HDF_STRING if present */
7109 hdi.fmt = hdiget.fmt & HDF_STRING;
7111 /* set text alignment (leftmost column must be left-aligned) */
7112 if (nColumn == 0)
7114 hdi.fmt |= HDF_LEFT;
7116 else
7118 if (lpColumn->fmt & LVCFMT_LEFT)
7119 hdi.fmt |= HDF_LEFT;
7120 else if (lpColumn->fmt & LVCFMT_RIGHT)
7121 hdi.fmt |= HDF_RIGHT;
7122 else if (lpColumn->fmt & LVCFMT_CENTER)
7123 hdi.fmt |= HDF_CENTER;
7126 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
7127 hdi.fmt |= HDF_BITMAP_ON_RIGHT;
7129 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
7130 hdi.fmt |= HDF_IMAGE;
7132 if (lpColumn->fmt & LVCFMT_IMAGE)
7134 hdi.fmt |= HDF_IMAGE;
7135 hdi.iImage = I_IMAGECALLBACK;
7139 if (lpColumn->mask & LVCF_WIDTH)
7141 hdi.mask |= HDI_WIDTH;
7142 hdi.cxy = lpColumn->cx;
7145 if (lpColumn->mask & LVCF_TEXT)
7147 hdi.mask |= HDI_TEXT | HDI_FORMAT;
7148 hdi.pszText = lpColumn->pszText;
7149 hdi.cchTextMax = textlenT(lpColumn->pszText, isW);
7150 hdi.fmt |= HDF_STRING;
7153 if (lpColumn->mask & LVCF_IMAGE)
7155 hdi.mask |= HDI_IMAGE;
7156 hdi.iImage = lpColumn->iImage;
7159 if (lpColumn->mask & LVCF_ORDER)
7161 hdi.mask |= HDI_ORDER;
7162 hdi.iOrder = lpColumn->iOrder;
7165 /* set header item attributes */
7166 if (isW)
7167 bResult = Header_SetItemW(infoPtr->hwndHeader, nColumn, &hdi);
7168 else
7169 bResult = Header_SetItemA(infoPtr->hwndHeader, nColumn, &hdi);
7172 return bResult;
7175 /***
7176 * DESCRIPTION:
7177 * Sets the column order array
7179 * PARAMETERS:
7180 * [I] HWND : window handle
7181 * [I] INT : number of elements in column order array
7182 * [I] INT : pointer to column order array
7184 * RETURN:
7185 * SUCCESS : TRUE
7186 * FAILURE : FALSE
7188 static LRESULT LISTVIEW_SetColumnOrderArray(HWND hwnd, INT iCount, LPINT lpiArray)
7190 /* LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); */
7192 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
7194 if (!lpiArray)
7195 return FALSE;
7197 return TRUE;
7201 /***
7202 * DESCRIPTION:
7203 * Sets the width of a column
7205 * PARAMETERS:
7206 * [I] HWND : window handle
7207 * [I] INT : column index
7208 * [I] INT : column width
7210 * RETURN:
7211 * SUCCESS : TRUE
7212 * FAILURE : FALSE
7214 static LRESULT LISTVIEW_SetColumnWidth(HWND hwnd, INT iCol, INT cx)
7216 LISTVIEW_INFO *infoPtr;
7217 HDITEMW hdi;
7218 LRESULT lret;
7219 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
7220 UINT uView = lStyle & LVS_TYPEMASK;
7221 HDC hdc;
7222 HFONT header_font;
7223 HFONT old_font;
7224 SIZE size;
7225 WCHAR text_buffer[DISP_TEXT_SIZE];
7226 INT header_item_count;
7227 INT item_index;
7228 INT nLabelWidth;
7229 RECT rcHeader;
7230 LVITEMW lvItem;
7231 WCHAR szDispText[DISP_TEXT_SIZE];
7233 /* make sure we can get the listview info */
7234 if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0)))
7235 return (FALSE);
7237 if (!infoPtr->hwndHeader) /* make sure we have a header */
7238 return (FALSE);
7240 /* set column width only if in report or list mode */
7241 if ((uView != LVS_REPORT) && (uView != LVS_LIST))
7242 return (FALSE);
7244 TRACE("(hwnd=%x, iCol=%d, cx=%d\n", hwnd, iCol, cx);
7246 /* take care of invalid cx values */
7247 if((uView == LVS_REPORT) && (cx < -2))
7248 cx = LVSCW_AUTOSIZE;
7249 else if (uView == LVS_LIST && (cx < 1))
7250 return FALSE;
7252 /* resize all columns if in LVS_LIST mode */
7253 if(uView == LVS_LIST) {
7254 infoPtr->nItemWidth = cx;
7255 InvalidateRect(hwnd, NULL, TRUE); /* force redraw of the listview */
7256 return TRUE;
7259 /* autosize based on listview items width */
7260 if(cx == LVSCW_AUTOSIZE)
7262 /* set the width of the column to the width of the widest item */
7263 if (iCol == 0 || uView == LVS_LIST)
7265 cx = 0;
7266 for(item_index = 0; item_index < GETITEMCOUNT(infoPtr); item_index++)
7268 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, item_index);
7269 cx = (nLabelWidth>cx)?nLabelWidth:cx;
7271 if (infoPtr->himlSmall)
7272 cx += infoPtr->iconSize.cx + IMAGE_PADDING;
7274 else
7276 ZeroMemory(&lvItem, sizeof(lvItem));
7277 lvItem.iSubItem = iCol;
7278 lvItem.mask = LVIF_TEXT;
7279 lvItem.cchTextMax = DISP_TEXT_SIZE;
7280 lvItem.pszText = szDispText;
7281 *lvItem.pszText = '\0';
7282 cx = 0;
7283 for(item_index = 0; item_index < GETITEMCOUNT(infoPtr); item_index++)
7285 lvItem.iItem = item_index;
7286 LISTVIEW_GetItemT(hwnd, &lvItem, FALSE, TRUE);
7287 nLabelWidth = LISTVIEW_GetStringWidthT(hwnd, lvItem.pszText, TRUE);
7288 cx = (nLabelWidth>cx)?nLabelWidth:cx;
7291 cx += TRAILING_PADDING;
7292 } /* autosize based on listview header width */
7293 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
7295 header_item_count = Header_GetItemCount(infoPtr->hwndHeader);
7297 /* if iCol is the last column make it fill the remainder of the controls width */
7298 if(iCol == (header_item_count - 1)) {
7299 /* get the width of every item except the current one */
7300 hdi.mask = HDI_WIDTH;
7301 cx = 0;
7303 for(item_index = 0; item_index < (header_item_count - 1); item_index++) {
7304 Header_GetItemW(infoPtr->hwndHeader, item_index, (LPARAM)(&hdi));
7305 cx+=hdi.cxy;
7308 /* retrieve the layout of the header */
7309 GetWindowRect(infoPtr->hwndHeader, &rcHeader);
7311 cx = (rcHeader.right - rcHeader.left) - cx;
7313 else
7315 /* Despite what the MS docs say, if this is not the last
7316 column, then MS resizes the column to the width of the
7317 largest text string in the column, including headers
7318 and items. This is different from LVSCW_AUTOSIZE in that
7319 LVSCW_AUTOSIZE ignores the header string length.
7322 /* retrieve header font */
7323 header_font = SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0L, 0L);
7325 /* retrieve header text */
7326 hdi.mask = HDI_TEXT;
7327 hdi.cchTextMax = sizeof(text_buffer)/sizeof(text_buffer[0]);
7328 hdi.pszText = text_buffer;
7330 Header_GetItemW(infoPtr->hwndHeader, iCol, (LPARAM)(&hdi));
7332 /* determine the width of the text in the header */
7333 hdc = GetDC(hwnd);
7334 old_font = SelectObject(hdc, header_font); /* select the font into hdc */
7336 GetTextExtentPoint32W(hdc, text_buffer, lstrlenW(text_buffer), &size);
7338 SelectObject(hdc, old_font); /* restore the old font */
7339 ReleaseDC(hwnd, hdc);
7341 ZeroMemory(&lvItem, sizeof(lvItem));
7342 lvItem.iSubItem = iCol;
7343 lvItem.mask = LVIF_TEXT;
7344 lvItem.cchTextMax = DISP_TEXT_SIZE;
7345 lvItem.pszText = szDispText;
7346 *lvItem.pszText = '\0';
7347 cx = size.cx;
7348 for(item_index = 0; item_index < GETITEMCOUNT(infoPtr); item_index++)
7350 lvItem.iItem = item_index;
7351 LISTVIEW_GetItemT(hwnd, &lvItem, FALSE, TRUE);
7352 nLabelWidth = LISTVIEW_GetStringWidthT(hwnd, lvItem.pszText, TRUE);
7353 nLabelWidth += TRAILING_PADDING;
7354 /* While it is possible for subitems to have icons, even MS messes
7355 up the positioning, so I suspect no applications actually use
7356 them. */
7357 if (item_index == 0 && infoPtr->himlSmall)
7358 nLabelWidth += infoPtr->iconSize.cx + IMAGE_PADDING;
7359 cx = (nLabelWidth>cx)?nLabelWidth:cx;
7364 /* call header to update the column change */
7365 hdi.mask = HDI_WIDTH;
7367 hdi.cxy = cx;
7368 lret = Header_SetItemW(infoPtr->hwndHeader, (WPARAM)iCol, (LPARAM)&hdi);
7370 InvalidateRect(hwnd, NULL, TRUE); /* force redraw of the listview */
7372 return lret;
7375 /***
7376 * DESCRIPTION:
7377 * Sets the extended listview style.
7379 * PARAMETERS:
7380 * [I] HWND : window handle
7381 * [I] DWORD : mask
7382 * [I] DWORD : style
7384 * RETURN:
7385 * SUCCESS : previous style
7386 * FAILURE : 0
7388 static LRESULT LISTVIEW_SetExtendedListViewStyle(HWND hwnd, DWORD dwMask, DWORD dwStyle)
7390 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7391 DWORD dwOldStyle = infoPtr->dwExStyle;
7393 /* set new style */
7394 if (dwMask)
7395 infoPtr->dwExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
7396 else
7397 infoPtr->dwExStyle = dwStyle;
7399 return dwOldStyle;
7402 /* LISTVIEW_SetHotCursor */
7404 /***
7405 * DESCRIPTION:
7406 * Sets the hot item index.
7408 * PARAMETERS:
7409 * [I] HWND : window handle
7410 * [I] INT : index
7412 * RETURN:
7413 * SUCCESS : previous hot item index
7414 * FAILURE : -1 (no hot item)
7416 static LRESULT LISTVIEW_SetHotItem(HWND hwnd, INT iIndex)
7418 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7419 INT iOldIndex = infoPtr->nHotItem;
7421 /* set new style */
7422 infoPtr->nHotItem = iIndex;
7424 return iOldIndex;
7427 /***
7428 * DESCRIPTION:
7429 * Sets the amount of time the cursor must hover over an item before it is selected.
7431 * PARAMETER(S):
7432 * [I] HWND : window handle
7433 * [I] DWORD : dwHoverTime, if -1 the hover time is set to the default
7435 * RETURN:
7436 * Returns the previous hover time
7438 static LRESULT LISTVIEW_SetHoverTime(HWND hwnd, DWORD dwHoverTime)
7440 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7441 DWORD oldHoverTime = infoPtr->dwHoverTime;
7443 infoPtr->dwHoverTime = dwHoverTime;
7445 return oldHoverTime;
7448 /***
7449 * DESCRIPTION:
7450 * Sets spacing for icons of LVS_ICON style.
7452 * PARAMETER(S):
7453 * [I] HWND : window handle
7454 * [I] DWORD : MAKELONG(cx, cy)
7456 * RETURN:
7457 * MAKELONG(oldcx, oldcy)
7459 static LRESULT LISTVIEW_SetIconSpacing(HWND hwnd, DWORD spacing)
7461 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7462 INT cy = HIWORD(spacing);
7463 INT cx = LOWORD(spacing);
7464 DWORD oldspacing;
7465 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
7466 UINT uView = lStyle & LVS_TYPEMASK;
7468 oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
7469 if (cx == -1) /* set to default */
7470 cx = GetSystemMetrics(SM_CXICONSPACING);
7471 if (cy == -1) /* set to default */
7472 cy = GetSystemMetrics(SM_CYICONSPACING);
7474 if (cx)
7475 infoPtr->iconSpacing.cx = cx;
7476 else
7477 { /* if 0 then compute width */
7478 if (uView == LVS_ICON)
7479 FIXME("width computation not yet done\n");
7481 * Should scan each item and determine max width of
7482 * icon or label, then make that the width
7484 else /* FIXME: unknown computation for non LVS_ICON - this is a guess */
7485 infoPtr->iconSpacing.cx = LISTVIEW_GetItemWidth(hwnd);
7487 if (cy)
7488 infoPtr->iconSpacing.cy = cy;
7489 else
7490 { /* if 0 then compute height */
7491 if (uView == LVS_ICON)
7492 infoPtr->iconSpacing.cy = infoPtr->iconSize.cy + infoPtr->ntmHeight
7493 + ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_OFFSET;
7494 /* FIXME. I don't think so; I think it is based on twice the ntmHeight */
7495 else /* FIXME: unknown computation for non LVS_ICON - this is a guess */
7496 infoPtr->iconSpacing.cy = LISTVIEW_GetItemHeight(hwnd);
7499 TRACE("old=(%d,%d), new=(%ld,%ld)\n", LOWORD(oldspacing), HIWORD(oldspacing),
7500 infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
7502 /* these depend on the iconSpacing */
7503 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
7504 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
7506 return oldspacing;
7509 /***
7510 * DESCRIPTION:
7511 * Sets image lists.
7513 * PARAMETER(S):
7514 * [I] HWND : window handle
7515 * [I] INT : image list type
7516 * [I] HIMAGELIST : image list handle
7518 * RETURN:
7519 * SUCCESS : old image list
7520 * FAILURE : NULL
7522 static HIMAGELIST LISTVIEW_SetImageList(HWND hwnd, INT nType, HIMAGELIST himl)
7524 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7525 HIMAGELIST himlOld = 0;
7526 INT oldHeight;
7528 switch (nType)
7530 case LVSIL_NORMAL:
7531 himlOld = infoPtr->himlNormal;
7532 infoPtr->himlNormal = himl;
7533 break;
7535 case LVSIL_SMALL:
7536 himlOld = infoPtr->himlSmall;
7537 infoPtr->himlSmall = himl;
7538 break;
7540 case LVSIL_STATE:
7541 himlOld = infoPtr->himlState;
7542 infoPtr->himlState = himl;
7543 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
7544 break;
7547 oldHeight = infoPtr->nItemHeight;
7548 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
7549 if (infoPtr->nItemHeight != oldHeight)
7550 LISTVIEW_UpdateScroll(hwnd);
7552 return himlOld;
7555 /***
7556 * DESCRIPTION:
7557 * Preallocates memory (does *not* set the actual count of items !)
7559 * PARAMETER(S):
7560 * [I] HWND : window handle
7561 * [I] INT : item count (projected number of items to allocate)
7562 * [I] DWORD : update flags
7564 * RETURN:
7565 * SUCCESS : TRUE
7566 * FAILURE : FALSE
7568 static BOOL LISTVIEW_SetItemCount(HWND hwnd, INT nItems, DWORD dwFlags)
7570 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
7572 TRACE("(hwnd=%x, nItems=%d, dwFlags=%lx)\n", hwnd, nItems, dwFlags);
7574 if (GetWindowLongW(hwnd, GWL_STYLE) & LVS_OWNERDATA)
7576 int precount,topvisible;
7578 TRACE("LVS_OWNERDATA is set!\n");
7579 if (dwFlags & (LVSICF_NOINVALIDATEALL | LVSICF_NOSCROLL))
7580 FIXME("flags %s %s not implemented\n",
7581 (dwFlags & LVSICF_NOINVALIDATEALL) ? "LVSICF_NOINVALIDATEALL"
7582 : "",
7583 (dwFlags & LVSICF_NOSCROLL) ? "LVSICF_NOSCROLL" : "");
7586 * Internally remove all the selections.
7590 LISTVIEW_SELECTION *selection;
7591 selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,0);
7592 if (selection)
7593 LISTVIEW_RemoveSelectionRange(hwnd,selection->lower,
7594 selection->upper);
7596 while (infoPtr->hdpaSelectionRanges->nItemCount>0);
7598 precount = infoPtr->hdpaItems->nItemCount;
7599 topvisible = ListView_GetTopIndex(hwnd) +
7600 LISTVIEW_GetCountPerColumn(hwnd) + 1;
7602 infoPtr->hdpaItems->nItemCount = nItems;
7604 infoPtr->nItemWidth = max(LISTVIEW_GetItemWidth(hwnd),
7605 DEFAULT_COLUMN_WIDTH);
7607 LISTVIEW_UpdateSize(hwnd);
7608 LISTVIEW_UpdateScroll(hwnd);
7610 if (min(precount,infoPtr->hdpaItems->nItemCount)<topvisible)
7611 InvalidateRect(hwnd, NULL, TRUE);
7613 else
7615 /* According to MSDN for non-LVS_OWNERDATA this is just
7616 * a performance issue. The control allocates its internal
7617 * data structures for the number of items specified. It
7618 * cuts down on the number of memory allocations. Therefore
7619 * we will just issue a WARN here
7621 WARN("for non-ownerdata performance option not implemented.\n");
7624 return TRUE;
7627 /***
7628 * DESCRIPTION:
7629 * Sets the position of an item.
7631 * PARAMETER(S):
7632 * [I] HWND : window handle
7633 * [I] INT : item index
7634 * [I] LONG : x coordinate
7635 * [I] LONG : y coordinate
7637 * RETURN:
7638 * SUCCESS : TRUE
7639 * FAILURE : FALSE
7641 static BOOL LISTVIEW_SetItemPosition(HWND hwnd, INT nItem,
7642 LONG nPosX, LONG nPosY)
7644 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
7645 UINT lStyle = GetWindowLongW(hwnd, GWL_STYLE);
7646 UINT uView = lStyle & LVS_TYPEMASK;
7647 LISTVIEW_ITEM *lpItem;
7648 HDPA hdpaSubItems;
7649 BOOL bResult = FALSE;
7651 TRACE("(hwnd=%x, nItem=%d, X=%ld, Y=%ld)\n", hwnd, nItem, nPosX, nPosY);
7653 if (lStyle & LVS_OWNERDATA)
7654 return FALSE;
7656 if ((nItem >= 0) || (nItem < GETITEMCOUNT(infoPtr)))
7658 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
7660 if ( (hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)) )
7662 if ( (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)) )
7664 POINT orig;
7665 bResult = TRUE;
7666 orig = lpItem->ptPosition;
7667 if ((nPosX == -1) && (nPosY == -1))
7669 /* This point value seems to be an undocumented feature. The
7670 * best guess is that it means either at the origin, or at
7671 * the true beginning of the list. I will assume the origin.
7673 POINT pt1;
7674 if (!LISTVIEW_GetOrigin(hwnd, &pt1))
7676 pt1.x = 0;
7677 pt1.y = 0;
7679 nPosX = pt1.x;
7680 nPosY = pt1.y;
7681 if (uView == LVS_ICON)
7683 nPosX += (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
7684 nPosY += ICON_TOP_PADDING;
7686 TRACE("requested special (-1,-1), set to origin (%ld,%ld)\n",
7687 nPosX, nPosY);
7690 lpItem->ptPosition.x = nPosX;
7691 lpItem->ptPosition.y = nPosY;
7692 if (uView == LVS_ICON)
7694 lpItem->ptPosition.y -= ICON_TOP_PADDING;
7695 lpItem->ptPosition.x -= (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
7696 if ((lpItem->ptPosition.y < 0) || (lpItem->ptPosition.x < 0))
7698 FIXME("failed orig (%ld,%ld), intent (%ld,%ld), is (%ld, %ld), setting neg to 0\n",
7699 orig.x, orig.y, nPosX, nPosY, lpItem->ptPosition.x, lpItem->ptPosition.y);
7702 if (lpItem->ptPosition.x < 0) lpItem->ptPosition.x = 0;
7703 if (lpItem->ptPosition.y < 0) lpItem->ptPosition.y = 0;
7706 else
7708 TRACE("orig (%ld,%ld), intent (%ld,%ld), is (%ld,%ld)\n",
7709 orig.x, orig.y, nPosX, nPosY, lpItem->ptPosition.x, lpItem->ptPosition.y);
7717 return bResult;
7720 /***
7721 * DESCRIPTION:
7722 * Sets the state of one or many items.
7724 * PARAMETER(S):
7725 * [I] HWND : window handle
7726 * [I]INT : item index
7727 * [I] LPLVITEM : item or subitem info
7729 * RETURN:
7730 * SUCCESS : TRUE
7731 * FAILURE : FALSE
7733 static LRESULT LISTVIEW_SetItemState(HWND hwnd, INT nItem, LPLVITEMW lpLVItem)
7735 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7736 BOOL bResult = TRUE;
7737 LVITEMW lvItem;
7739 TRACE("(hwnd=%x, nItem=%d, lpLVItem=%s)\n",
7740 hwnd, nItem, debuglvitem_t(lpLVItem, TRUE));
7742 ZeroMemory(&lvItem, sizeof(lvItem));
7743 lvItem.mask = LVIF_STATE;
7744 lvItem.state = lpLVItem->state;
7745 lvItem.stateMask = lpLVItem->stateMask ;
7746 lvItem.iItem = nItem;
7748 if (nItem == -1)
7750 /* apply to all items */
7751 for (lvItem.iItem = 0; lvItem.iItem < GETITEMCOUNT(infoPtr); lvItem.iItem++)
7752 if (!ListView_SetItemW(hwnd, &lvItem)) bResult = FALSE;
7754 else
7755 bResult = ListView_SetItemW(hwnd, &lvItem);
7757 return bResult;
7760 /***
7761 * DESCRIPTION:
7762 * Sets the text of an item or subitem.
7764 * PARAMETER(S):
7765 * [I] hwnd : window handle
7766 * [I] nItem : item index
7767 * [I] lpLVItem : item or subitem info
7768 * [I] isW : TRUE if input is Unicode
7770 * RETURN:
7771 * SUCCESS : TRUE
7772 * FAILURE : FALSE
7774 static BOOL LISTVIEW_SetItemTextT(HWND hwnd, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
7776 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7777 BOOL bResult = FALSE;
7778 LVITEMW lvItem;
7780 TRACE("(hwnd=%x, nItem=%d, lpLVItem=%s, isW=%d)\n",
7781 hwnd, nItem, debuglvitem_t(lpLVItem, isW), isW);
7783 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
7785 ZeroMemory(&lvItem, sizeof(LVITEMW));
7786 lvItem.mask = LVIF_TEXT;
7787 lvItem.pszText = lpLVItem->pszText;
7788 lvItem.iItem = nItem;
7789 lvItem.iSubItem = lpLVItem->iSubItem;
7790 if(isW) bResult = ListView_SetItemW(hwnd, &lvItem);
7791 else bResult = ListView_SetItemA(hwnd, &lvItem);
7794 return bResult;
7797 /***
7798 * DESCRIPTION:
7799 * Set item index that marks the start of a multiple selection.
7801 * PARAMETER(S):
7802 * [I] HWND : window handle
7803 * [I] INT : index
7805 * RETURN:
7806 * Index number or -1 if there is no selection mark.
7808 static LRESULT LISTVIEW_SetSelectionMark(HWND hwnd, INT nIndex)
7810 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7811 INT nOldIndex = infoPtr->nSelectionMark;
7813 TRACE("(hwnd=%x, nIndex=%d)\n", hwnd, nIndex);
7815 infoPtr->nSelectionMark = nIndex;
7817 return nOldIndex;
7820 /***
7821 * DESCRIPTION:
7822 * Sets the text background color.
7824 * PARAMETER(S):
7825 * [I] HWND : window handle
7826 * [I] COLORREF : text background color
7828 * RETURN:
7829 * SUCCESS : TRUE
7830 * FAILURE : FALSE
7832 static LRESULT LISTVIEW_SetTextBkColor(HWND hwnd, COLORREF clrTextBk)
7834 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7836 TRACE("(hwnd=%x, clrTextBk=%lx)\n", hwnd, clrTextBk);
7838 infoPtr->clrTextBk = clrTextBk;
7839 InvalidateRect(hwnd, NULL, TRUE);
7841 return TRUE;
7844 /***
7845 * DESCRIPTION:
7846 * Sets the text foreground color.
7848 * PARAMETER(S):
7849 * [I] HWND : window handle
7850 * [I] COLORREF : text color
7852 * RETURN:
7853 * SUCCESS : TRUE
7854 * FAILURE : FALSE
7856 static LRESULT LISTVIEW_SetTextColor (HWND hwnd, COLORREF clrText)
7858 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7860 TRACE("(hwnd=%x, clrText=%lx)\n", hwnd, clrText);
7862 infoPtr->clrText = clrText;
7863 InvalidateRect(hwnd, NULL, TRUE);
7865 return TRUE;
7868 /* LISTVIEW_SetToolTips */
7869 /* LISTVIEW_SetUnicodeFormat */
7870 /* LISTVIEW_SetWorkAreas */
7872 /***
7873 * DESCRIPTION:
7874 * Callback internally used by LISTVIEW_SortItems()
7876 * PARAMETER(S):
7877 * [I] LPVOID : first LISTVIEW_ITEM to compare
7878 * [I] LPVOID : second LISTVIEW_ITEM to compare
7879 * [I] LPARAM : HWND of control
7881 * RETURN:
7882 * if first comes before second : negative
7883 * if first comes after second : positive
7884 * if first and second are equivalent : zero
7886 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
7888 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW((HWND)lParam, 0);
7889 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 );
7890 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 );
7892 /* Forward the call to the client defined callback */
7893 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
7896 /***
7897 * DESCRIPTION:
7898 * Sorts the listview items.
7900 * PARAMETER(S):
7901 * [I] HWND : window handle
7902 * [I] WPARAM : application-defined value
7903 * [I] LPARAM : pointer to comparision callback
7905 * RETURN:
7906 * SUCCESS : TRUE
7907 * FAILURE : FALSE
7909 static LRESULT LISTVIEW_SortItems(HWND hwnd, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
7911 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7912 UINT lStyle = GetWindowLongW(hwnd, GWL_STYLE);
7913 HDPA hdpaSubItems=NULL;
7914 LISTVIEW_ITEM *pLVItem=NULL;
7915 LPVOID selectionMarkItem;
7916 int nCount, i;
7918 TRACE("(hwnd=%x, pfnCompare=%p, lParamSort=%lx)\n", hwnd, pfnCompare, lParamSort);
7920 if (lStyle & LVS_OWNERDATA) return FALSE;
7922 if (!infoPtr || !infoPtr->hdpaItems) return FALSE;
7924 nCount = GETITEMCOUNT(infoPtr);
7925 /* if there are 0 or 1 items, there is no need to sort */
7926 if (nCount < 2)
7927 return TRUE;
7929 infoPtr->pfnCompare = pfnCompare;
7930 infoPtr->lParamSort = lParamSort;
7931 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, hwnd);
7933 /* Adjust selections and indices so that they are the way they should
7934 * be after the sort (otherwise, the list items move around, but
7935 * whatever is at the item's previous original position will be
7936 * selected instead)
7938 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
7939 for (i=0; i < nCount; i++)
7941 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
7942 pLVItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
7944 if (pLVItem->state & LVIS_SELECTED)
7945 LISTVIEW_AddSelectionRange(hwnd, i, i);
7946 else
7947 LISTVIEW_RemoveSelectionRange(hwnd, i, i);
7948 if (pLVItem->state & LVIS_FOCUSED)
7949 infoPtr->nFocusedItem=i;
7951 if (selectionMarkItem != NULL)
7952 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
7953 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7955 /* align the items */
7956 LISTVIEW_AlignTop(hwnd);
7958 /* refresh the display */
7959 InvalidateRect(hwnd, NULL, TRUE);
7961 return TRUE;
7964 /* LISTVIEW_SubItemHitTest */
7966 /***
7967 * DESCRIPTION:
7968 * Updates an items or rearranges the listview control.
7970 * PARAMETER(S):
7971 * [I] HWND : window handle
7972 * [I] INT : item index
7974 * RETURN:
7975 * SUCCESS : TRUE
7976 * FAILURE : FALSE
7978 static LRESULT LISTVIEW_Update(HWND hwnd, INT nItem)
7980 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7981 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
7982 BOOL bResult = FALSE;
7983 RECT rc;
7985 TRACE("(hwnd=%x, nItem=%d)\n", hwnd, nItem);
7987 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
7989 bResult = TRUE;
7991 /* rearrange with default alignment style */
7992 if ((lStyle & LVS_AUTOARRANGE) && (((lStyle & LVS_TYPEMASK) == LVS_ICON) ||
7993 ((lStyle & LVS_TYPEMASK) == LVS_SMALLICON)))
7995 ListView_Arrange(hwnd, 0);
7997 else
7999 /* get item bounding rectangle */
8000 ListView_GetItemRect(hwnd, nItem, &rc, LVIR_BOUNDS);
8001 InvalidateRect(hwnd, &rc, TRUE);
8005 return bResult;
8008 /***
8009 * DESCRIPTION:
8010 * Creates the listview control.
8012 * PARAMETER(S):
8013 * [I] HWND : window handle
8015 * RETURN:
8016 * Zero
8018 static LRESULT LISTVIEW_Create(HWND hwnd, LPCREATESTRUCTW lpcs)
8020 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8021 UINT uView = lpcs->style & LVS_TYPEMASK;
8022 LOGFONTW logFont;
8024 TRACE("(hwnd=%x, lpcs=%p)\n", hwnd, lpcs);
8026 /* initialize info pointer */
8027 ZeroMemory(infoPtr, sizeof(LISTVIEW_INFO));
8029 /* determine the type of structures to use */
8030 infoPtr->notifyFormat = SendMessageW(GetParent(hwnd), WM_NOTIFYFORMAT,
8031 (WPARAM)hwnd, (LPARAM)NF_QUERY);
8033 /* initialize color information */
8034 infoPtr->clrBk = GetSysColor(COLOR_WINDOW);
8035 infoPtr->clrText = GetSysColor(COLOR_WINDOWTEXT);
8036 infoPtr->clrTextBk = CLR_DEFAULT;
8038 /* set default values */
8039 infoPtr->hwndSelf = hwnd;
8040 infoPtr->uCallbackMask = 0;
8041 infoPtr->nFocusedItem = -1;
8042 infoPtr->nSelectionMark = -1;
8043 infoPtr->nHotItem = -1;
8044 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
8045 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
8046 ZeroMemory(&infoPtr->rcList, sizeof(RECT));
8047 infoPtr->hwndEdit = 0;
8048 infoPtr->pedititem = NULL;
8049 infoPtr->nEditLabelItem = -1;
8050 infoPtr->bIsDrawing = FALSE;
8052 /* get default font (icon title) */
8053 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
8054 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
8055 infoPtr->hFont = infoPtr->hDefaultFont;
8056 LISTVIEW_SaveTextMetrics(hwnd);
8058 /* create header */
8059 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, (LPCWSTR)NULL,
8060 WS_CHILD | HDS_HORZ | (DWORD)((LVS_NOSORTHEADER & lpcs->style)?0:HDS_BUTTONS),
8061 0, 0, 0, 0, hwnd, (HMENU)0,
8062 lpcs->hInstance, NULL);
8064 /* set header unicode format */
8065 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT,(WPARAM)TRUE,(LPARAM)NULL);
8067 /* set header font */
8068 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont,
8069 (LPARAM)TRUE);
8071 if (uView == LVS_ICON)
8073 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
8074 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
8076 else if (uView == LVS_REPORT)
8078 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
8080 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
8082 else
8084 /* set HDS_HIDDEN flag to hide the header bar */
8085 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
8086 GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
8090 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8091 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8093 else
8095 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8096 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8099 /* display unsupported listview window styles */
8100 LISTVIEW_UnsupportedStyles(lpcs->style);
8102 /* allocate memory for the data structure */
8103 infoPtr->hdpaItems = DPA_Create(10);
8105 /* allocate memory for the selection ranges */
8106 infoPtr->hdpaSelectionRanges = DPA_Create(10);
8108 /* initialize size of items */
8109 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8110 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
8112 /* initialize the hover time to -1(indicating the default system hover time) */
8113 infoPtr->dwHoverTime = -1;
8115 return 0;
8118 /***
8119 * DESCRIPTION:
8120 * Erases the background of the listview control.
8122 * PARAMETER(S):
8123 * [I] HWND : window handle
8124 * [I] WPARAM : device context handle
8125 * [I] LPARAM : not used
8127 * RETURN:
8128 * SUCCESS : TRUE
8129 * FAILURE : FALSE
8131 static LRESULT LISTVIEW_EraseBackground(HWND hwnd, WPARAM wParam,
8132 LPARAM lParam)
8134 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8135 BOOL bResult;
8137 TRACE("(hwnd=%x, wParam=%x, lParam=%lx)\n", hwnd, wParam, lParam);
8139 if (infoPtr->clrBk == CLR_NONE)
8141 bResult = SendMessageW(GetParent(hwnd), WM_ERASEBKGND, wParam, lParam);
8143 else
8145 RECT rc;
8146 HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk);
8147 GetClientRect(hwnd, &rc);
8148 FillRect((HDC)wParam, &rc, hBrush);
8149 DeleteObject(hBrush);
8150 bResult = TRUE;
8153 return bResult;
8157 static void LISTVIEW_FillBackground(HWND hwnd, HDC hdc, LPRECT rc)
8159 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8161 TRACE("(hwnd=%x, hdc=%x, rc=%p)\n", hwnd, hdc, rc);
8163 if (infoPtr->clrBk != CLR_NONE)
8165 HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk);
8166 FillRect(hdc, rc, hBrush);
8167 DeleteObject(hBrush);
8171 /***
8172 * DESCRIPTION:
8173 * Retrieves the listview control font.
8175 * PARAMETER(S):
8176 * [I] HWND : window handle
8178 * RETURN:
8179 * Font handle.
8181 static LRESULT LISTVIEW_GetFont(HWND hwnd)
8183 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8185 TRACE("(hwnd=%x)\n", hwnd);
8187 return infoPtr->hFont;
8190 /***
8191 * DESCRIPTION:
8192 * Performs vertical scrolling.
8194 * PARAMETER(S):
8195 * [I] HWND : window handle
8196 * [I] INT : scroll code
8197 * [I] SHORT : current scroll position if scroll code is SB_THUMBPOSITION
8198 * or SB_THUMBTRACK.
8199 * [I] HWND : scrollbar control window handle
8201 * RETURN:
8202 * Zero
8204 static LRESULT LISTVIEW_VScroll(HWND hwnd, INT nScrollCode, SHORT nCurrentPos,
8205 HWND hScrollWnd)
8207 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8208 SCROLLINFO scrollInfo;
8210 TRACE("(hwnd=%x, nScrollCode=%d, nCurrentPos=%d, hScrollWnd=%x)\n",
8211 hwnd, nScrollCode, nCurrentPos, hScrollWnd);
8213 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8215 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
8216 scrollInfo.cbSize = sizeof(SCROLLINFO);
8217 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
8219 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
8221 INT nOldScrollPos = scrollInfo.nPos;
8222 switch (nScrollCode)
8224 case SB_LINEUP:
8225 if (scrollInfo.nPos > scrollInfo.nMin)
8226 scrollInfo.nPos--;
8227 break;
8229 case SB_LINEDOWN:
8230 if (scrollInfo.nPos < scrollInfo.nMax)
8231 scrollInfo.nPos++;
8232 break;
8234 case SB_PAGEUP:
8235 if (scrollInfo.nPos > scrollInfo.nMin)
8237 if (scrollInfo.nPos >= scrollInfo.nPage)
8238 scrollInfo.nPos -= scrollInfo.nPage;
8239 else
8240 scrollInfo.nPos = scrollInfo.nMin;
8242 break;
8244 case SB_PAGEDOWN:
8245 if (scrollInfo.nPos < scrollInfo.nMax)
8247 if (scrollInfo.nPos <= scrollInfo.nMax - scrollInfo.nPage)
8248 scrollInfo.nPos += scrollInfo.nPage;
8249 else
8250 scrollInfo.nPos = scrollInfo.nMax;
8252 break;
8254 case SB_THUMBPOSITION:
8255 case SB_THUMBTRACK:
8256 scrollInfo.nPos = nCurrentPos;
8257 if (scrollInfo.nPos > scrollInfo.nMax)
8258 scrollInfo.nPos=scrollInfo.nMax;
8260 if (scrollInfo.nPos < scrollInfo.nMin)
8261 scrollInfo.nPos=scrollInfo.nMin;
8263 break;
8266 if (nOldScrollPos != scrollInfo.nPos)
8268 scrollInfo.fMask = SIF_POS;
8269 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
8270 if (IsWindowVisible(infoPtr->hwndHeader))
8272 RECT rListview, rcHeader, rDest;
8273 GetClientRect(hwnd, &rListview);
8274 GetWindowRect(infoPtr->hwndHeader, &rcHeader);
8275 MapWindowPoints((HWND) NULL, hwnd, (LPPOINT) &rcHeader, 2);
8276 SubtractRect(&rDest, &rListview, &rcHeader);
8277 InvalidateRect(hwnd, &rDest, TRUE);
8279 else
8280 InvalidateRect(hwnd, NULL, TRUE);
8284 return 0;
8287 /***
8288 * DESCRIPTION:
8289 * Performs horizontal scrolling.
8291 * PARAMETER(S):
8292 * [I] HWND : window handle
8293 * [I] INT : scroll code
8294 * [I] SHORT : current scroll position if scroll code is SB_THUMBPOSITION
8295 * or SB_THUMBTRACK.
8296 * [I] HWND : scrollbar control window handle
8298 * RETURN:
8299 * Zero
8301 static LRESULT LISTVIEW_HScroll(HWND hwnd, INT nScrollCode, SHORT nCurrentPos,
8302 HWND hScrollWnd)
8304 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8305 SCROLLINFO scrollInfo;
8307 TRACE("(hwnd=%x, nScrollCode=%d, nCurrentPos=%d, hScrollWnd=%x)\n",
8308 hwnd, nScrollCode, nCurrentPos, hScrollWnd);
8310 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8312 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
8313 scrollInfo.cbSize = sizeof(SCROLLINFO);
8314 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
8316 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
8318 INT nOldScrollPos = scrollInfo.nPos;
8320 switch (nScrollCode)
8322 case SB_LINELEFT:
8323 if (scrollInfo.nPos > scrollInfo.nMin)
8324 scrollInfo.nPos--;
8325 break;
8327 case SB_LINERIGHT:
8328 if (scrollInfo.nPos < scrollInfo.nMax)
8329 scrollInfo.nPos++;
8330 break;
8332 case SB_PAGELEFT:
8333 if (scrollInfo.nPos > scrollInfo.nMin)
8335 if (scrollInfo.nPos >= scrollInfo.nPage)
8336 scrollInfo.nPos -= scrollInfo.nPage;
8337 else
8338 scrollInfo.nPos = scrollInfo.nMin;
8340 break;
8342 case SB_PAGERIGHT:
8343 if (scrollInfo.nPos < scrollInfo.nMax)
8345 if (scrollInfo.nPos <= scrollInfo.nMax - scrollInfo.nPage)
8346 scrollInfo.nPos += scrollInfo.nPage;
8347 else
8348 scrollInfo.nPos = scrollInfo.nMax;
8350 break;
8352 case SB_THUMBPOSITION:
8353 case SB_THUMBTRACK:
8354 scrollInfo.nPos = nCurrentPos;
8356 if (scrollInfo.nPos > scrollInfo.nMax)
8357 scrollInfo.nPos=scrollInfo.nMax;
8359 if (scrollInfo.nPos < scrollInfo.nMin)
8360 scrollInfo.nPos=scrollInfo.nMin;
8361 break;
8364 if (nOldScrollPos != scrollInfo.nPos)
8366 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8367 scrollInfo.fMask = SIF_POS;
8368 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
8369 if(uView == LVS_REPORT)
8371 scrollInfo.fMask = SIF_POS;
8372 GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
8373 LISTVIEW_UpdateHeaderSize(hwnd, scrollInfo.nPos);
8375 InvalidateRect(hwnd, NULL, TRUE);
8379 return 0;
8382 static LRESULT LISTVIEW_MouseWheel(HWND hwnd, INT wheelDelta)
8384 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8385 INT gcWheelDelta = 0;
8386 UINT pulScrollLines = 3;
8387 SCROLLINFO scrollInfo;
8389 TRACE("(hwnd=%x, wheelDelta=%d)\n", hwnd, wheelDelta);
8391 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
8392 gcWheelDelta -= wheelDelta;
8394 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
8395 scrollInfo.cbSize = sizeof(SCROLLINFO);
8396 scrollInfo.fMask = SIF_POS | SIF_RANGE;
8398 switch(uView)
8400 case LVS_ICON:
8401 case LVS_SMALLICON:
8403 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
8404 * should be fixed in the future.
8406 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
8407 LISTVIEW_VScroll(hwnd, SB_THUMBPOSITION, scrollInfo.nPos + (gcWheelDelta < 0) ? 37 : -37, 0);
8408 break;
8410 case LVS_REPORT:
8411 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
8413 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
8415 int cLineScroll = min(LISTVIEW_GetCountPerColumn(hwnd), pulScrollLines);
8416 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
8417 LISTVIEW_VScroll(hwnd, SB_THUMBPOSITION, scrollInfo.nPos + cLineScroll, 0);
8420 break;
8422 case LVS_LIST:
8423 LISTVIEW_HScroll(hwnd, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
8424 break;
8426 return 0;
8429 /***
8430 * DESCRIPTION:
8431 * ???
8433 * PARAMETER(S):
8434 * [I] HWND : window handle
8435 * [I] INT : virtual key
8436 * [I] LONG : key data
8438 * RETURN:
8439 * Zero
8441 static LRESULT LISTVIEW_KeyDown(HWND hwnd, INT nVirtualKey, LONG lKeyData)
8443 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8444 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8445 INT nItem = -1;
8446 NMLVKEYDOWN nmKeyDown;
8448 TRACE("(hwnd=%x, nVirtualKey=%d, lKeyData=%ld)\n", hwnd, nVirtualKey, lKeyData);
8450 /* send LVN_KEYDOWN notification */
8451 nmKeyDown.wVKey = nVirtualKey;
8452 nmKeyDown.flags = 0;
8453 notify(hwnd, LVN_KEYDOWN, &nmKeyDown.hdr);
8455 switch (nVirtualKey)
8457 case VK_RETURN:
8458 if ((GETITEMCOUNT(infoPtr) > 0) && (infoPtr->nFocusedItem != -1))
8460 hdr_notify(hwnd, NM_RETURN); /* NM_RETURN notification */
8461 hdr_notify(hwnd, LVN_ITEMACTIVATE); /* LVN_ITEMACTIVATE notification */
8463 break;
8465 case VK_HOME:
8466 if (GETITEMCOUNT(infoPtr) > 0)
8467 nItem = 0;
8468 break;
8470 case VK_END:
8471 if (GETITEMCOUNT(infoPtr) > 0)
8472 nItem = GETITEMCOUNT(infoPtr) - 1;
8473 break;
8475 case VK_LEFT:
8476 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_TOLEFT);
8477 break;
8479 case VK_UP:
8480 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_ABOVE);
8481 break;
8483 case VK_RIGHT:
8484 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_TORIGHT);
8485 break;
8487 case VK_DOWN:
8488 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_BELOW);
8489 break;
8491 case VK_PRIOR:
8492 if (uView == LVS_REPORT)
8493 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(hwnd);
8494 else
8495 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(hwnd)
8496 * LISTVIEW_GetCountPerRow(hwnd);
8497 if(nItem < 0) nItem = 0;
8498 break;
8500 case VK_NEXT:
8501 if (uView == LVS_REPORT)
8502 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(hwnd);
8503 else
8504 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(hwnd)
8505 * LISTVIEW_GetCountPerRow(hwnd);
8506 if(nItem >= GETITEMCOUNT(infoPtr)) nItem = GETITEMCOUNT(infoPtr) - 1;
8507 break;
8510 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem))
8512 if (LISTVIEW_KeySelection(hwnd, nItem))
8513 UpdateWindow(hwnd); /* update client area */
8516 return 0;
8519 /***
8520 * DESCRIPTION:
8521 * Kills the focus.
8523 * PARAMETER(S):
8524 * [I] HWND : window handle
8526 * RETURN:
8527 * Zero
8529 static LRESULT LISTVIEW_KillFocus(HWND hwnd)
8531 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
8532 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8533 INT i,nTop,nBottom;
8535 TRACE("(hwnd=%x)\n", hwnd);
8537 /* send NM_KILLFOCUS notification */
8538 hdr_notify(hwnd, NM_KILLFOCUS);
8540 /* set window focus flag */
8541 infoPtr->bFocus = FALSE;
8543 /* NEED drawing optimization ; redraw the selected items */
8544 if (uView & LVS_REPORT)
8546 nTop = LISTVIEW_GetTopIndex(hwnd);
8547 nBottom = nTop +
8548 LISTVIEW_GetCountPerColumn(hwnd) + 1;
8550 else
8552 nTop = 0;
8553 nBottom = GETITEMCOUNT(infoPtr);
8555 for (i = nTop; i<nBottom; i++)
8557 if (LISTVIEW_IsSelected(hwnd,i))
8559 RECT rcItem;
8560 rcItem.left = LVIR_BOUNDS;
8561 LISTVIEW_GetItemRect(hwnd, i, &rcItem);
8562 InvalidateRect(hwnd, &rcItem, FALSE);
8566 return 0;
8569 /***
8570 * DESCRIPTION:
8571 * Processes double click messages (left mouse button).
8573 * PARAMETER(S):
8574 * [I] HWND : window handle
8575 * [I] WORD : key flag
8576 * [I] WORD : x coordinate
8577 * [I] WORD : y coordinate
8579 * RETURN:
8580 * Zero
8582 static LRESULT LISTVIEW_LButtonDblClk(HWND hwnd, WORD wKey, WORD wPosX,
8583 WORD wPosY)
8585 LVHITTESTINFO htInfo;
8586 NMLISTVIEW nmlv;
8588 TRACE("(hwnd=%x, key=%hu, X=%hu, Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8590 htInfo.pt.x = wPosX;
8591 htInfo.pt.y = wPosY;
8593 /* send NM_DBLCLK notification */
8594 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8595 if (LISTVIEW_HitTestItem(hwnd, &htInfo, TRUE) != -1)
8597 nmlv.iItem = htInfo.iItem;
8598 nmlv.iSubItem = htInfo.iSubItem;
8600 else
8602 nmlv.iItem = -1;
8603 nmlv.iSubItem = 0;
8605 nmlv.ptAction.x = wPosX;
8606 nmlv.ptAction.y = wPosY;
8607 listview_notify(hwnd, NM_DBLCLK, &nmlv);
8610 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
8611 if(nmlv.iItem != -1)
8612 hdr_notify(hwnd, LVN_ITEMACTIVATE);
8614 return 0;
8617 /***
8618 * DESCRIPTION:
8619 * Processes mouse down messages (left mouse button).
8621 * PARAMETER(S):
8622 * [I] HWND : window handle
8623 * [I] WORD : key flag
8624 * [I] WORD : x coordinate
8625 * [I] WORD : y coordinate
8627 * RETURN:
8628 * Zero
8630 static LRESULT LISTVIEW_LButtonDown(HWND hwnd, WORD wKey, WORD wPosX,
8631 WORD wPosY)
8633 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8634 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
8635 static BOOL bGroupSelect = TRUE;
8636 POINT ptPosition;
8637 INT nItem;
8639 TRACE("(hwnd=%x, key=%hu, X=%hu, Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8641 /* send NM_RELEASEDCAPTURE notification */
8642 hdr_notify(hwnd, NM_RELEASEDCAPTURE);
8644 if (infoPtr->bFocus == FALSE)
8645 SetFocus(hwnd);
8647 /* set left button down flag */
8648 infoPtr->bLButtonDown = TRUE;
8650 ptPosition.x = wPosX;
8651 ptPosition.y = wPosY;
8652 nItem = LISTVIEW_MouseSelection(hwnd, ptPosition);
8653 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
8655 if (lStyle & LVS_SINGLESEL)
8657 if ((ListView_GetItemState(hwnd, nItem, LVIS_SELECTED) & LVIS_SELECTED)
8658 && infoPtr->nEditLabelItem == -1)
8659 infoPtr->nEditLabelItem = nItem;
8660 else
8661 LISTVIEW_SetSelection(hwnd, nItem);
8663 else
8665 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
8667 if (bGroupSelect)
8668 LISTVIEW_AddGroupSelection(hwnd, nItem);
8669 else
8670 LISTVIEW_AddSelection(hwnd, nItem);
8672 else if (wKey & MK_CONTROL)
8674 bGroupSelect = LISTVIEW_ToggleSelection(hwnd, nItem);
8676 else if (wKey & MK_SHIFT)
8678 LISTVIEW_SetGroupSelection(hwnd, nItem);
8680 else
8682 BOOL was_selected =
8683 (ListView_GetItemState(hwnd, nItem, LVIS_SELECTED) & LVIS_SELECTED);
8685 /* set selection (clears other pre-existing selections) */
8686 LISTVIEW_SetSelection(hwnd, nItem);
8688 if (was_selected && infoPtr->nEditLabelItem == -1)
8689 infoPtr->nEditLabelItem = nItem;
8693 else
8695 /* remove all selections */
8696 LISTVIEW_RemoveAllSelections(hwnd);
8699 /* redraw if we could have possibly selected something */
8700 if(!GETITEMCOUNT(infoPtr)) InvalidateRect(hwnd, NULL, TRUE);
8702 return 0;
8705 /***
8706 * DESCRIPTION:
8707 * Processes mouse up messages (left mouse button).
8709 * PARAMETER(S):
8710 * [I] HWND : window handle
8711 * [I] WORD : key flag
8712 * [I] WORD : x coordinate
8713 * [I] WORD : y coordinate
8715 * RETURN:
8716 * Zero
8718 static LRESULT LISTVIEW_LButtonUp(HWND hwnd, WORD wKey, WORD wPosX,
8719 WORD wPosY)
8721 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8723 TRACE("(hwnd=%x, key=%hu, X=%hu, Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8725 if (infoPtr->bLButtonDown != FALSE)
8727 LVHITTESTINFO lvHitTestInfo;
8728 NMLISTVIEW nmlv;
8730 lvHitTestInfo.pt.x = wPosX;
8731 lvHitTestInfo.pt.y = wPosY;
8733 /* send NM_CLICK notification */
8734 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8735 if (LISTVIEW_HitTestItem(hwnd, &lvHitTestInfo, TRUE) != -1)
8737 nmlv.iItem = lvHitTestInfo.iItem;
8738 nmlv.iSubItem = lvHitTestInfo.iSubItem;
8740 else
8742 nmlv.iItem = -1;
8743 nmlv.iSubItem = 0;
8745 nmlv.ptAction.x = wPosX;
8746 nmlv.ptAction.y = wPosY;
8747 listview_notify(hwnd, NM_CLICK, &nmlv);
8749 /* set left button flag */
8750 infoPtr->bLButtonDown = FALSE;
8752 if(infoPtr->nEditLabelItem != -1)
8754 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && lvHitTestInfo.flags & LVHT_ONITEMLABEL)
8755 LISTVIEW_EditLabelT(hwnd, lvHitTestInfo.iItem, TRUE);
8756 infoPtr->nEditLabelItem = -1;
8760 return 0;
8763 /***
8764 * DESCRIPTION:
8765 * Creates the listview control (called before WM_CREATE).
8767 * PARAMETER(S):
8768 * [I] HWND : window handle
8769 * [I] WPARAM : unhandled
8770 * [I] LPARAM : widow creation info
8772 * RETURN:
8773 * Zero
8775 static LRESULT LISTVIEW_NCCreate(HWND hwnd, WPARAM wParam, LPARAM lParam)
8777 LISTVIEW_INFO *infoPtr;
8779 TRACE("(hwnd=%x, wParam=%x, lParam=%lx)\n", hwnd, wParam, lParam);
8781 /* allocate memory for info structure */
8782 infoPtr = (LISTVIEW_INFO *)COMCTL32_Alloc(sizeof(LISTVIEW_INFO));
8783 if (infoPtr == NULL)
8785 ERR("could not allocate info memory!\n");
8786 return 0;
8789 SetWindowLongW(hwnd, 0, (LONG)infoPtr);
8790 if ((LISTVIEW_INFO *)GetWindowLongW(hwnd, 0) != infoPtr)
8792 ERR("pointer assignment error!\n");
8793 return 0;
8796 return DefWindowProcW(hwnd, WM_NCCREATE, wParam, lParam);
8799 /***
8800 * DESCRIPTION:
8801 * Destroys the listview control (called after WM_DESTROY).
8803 * PARAMETER(S):
8804 * [I] HWND : window handle
8806 * RETURN:
8807 * Zero
8809 static LRESULT LISTVIEW_NCDestroy(HWND hwnd)
8811 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8813 TRACE("(hwnd=%x)\n", hwnd);
8815 /* delete all items */
8816 LISTVIEW_DeleteAllItems(hwnd);
8818 /* destroy data structure */
8819 DPA_Destroy(infoPtr->hdpaItems);
8820 DPA_Destroy(infoPtr->hdpaSelectionRanges);
8822 /* destroy font */
8823 infoPtr->hFont = (HFONT)0;
8824 if (infoPtr->hDefaultFont)
8826 DeleteObject(infoPtr->hDefaultFont);
8829 /* free listview info pointer*/
8830 COMCTL32_Free(infoPtr);
8832 SetWindowLongW(hwnd, 0, 0);
8833 return 0;
8836 /***
8837 * DESCRIPTION:
8838 * Handles notifications from children.
8840 * PARAMETER(S):
8841 * [I] HWND : window handle
8842 * [I] INT : control identifier
8843 * [I] LPNMHDR : notification information
8845 * RETURN:
8846 * Zero
8848 static LRESULT LISTVIEW_Notify(HWND hwnd, INT nCtrlId, LPNMHDR lpnmh)
8850 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8852 TRACE("(hwnd=%x, nCtrlId=%d, lpnmh=%p)\n", hwnd, nCtrlId, lpnmh);
8854 if (lpnmh->hwndFrom == infoPtr->hwndHeader)
8856 /* handle notification from header control */
8857 if (lpnmh->code == HDN_ENDTRACKW)
8859 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8860 InvalidateRect(hwnd, NULL, TRUE);
8862 else if(lpnmh->code == HDN_ITEMCLICKW || lpnmh->code == HDN_ITEMCLICKA)
8864 /* Handle sorting by Header Column */
8865 NMLISTVIEW nmlv;
8867 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8868 nmlv.iItem = -1;
8869 nmlv.iSubItem = ((LPNMHEADERW)lpnmh)->iItem;
8870 listview_notify(hwnd, LVN_COLUMNCLICK, &nmlv);
8872 else if(lpnmh->code == NM_RELEASEDCAPTURE)
8874 /* Idealy this should be done in HDN_ENDTRACKA
8875 * but since SetItemBounds in Header.c is called after
8876 * the notification is sent, it is neccessary to handle the
8877 * update of the scroll bar here (Header.c works fine as it is,
8878 * no need to disturb it)
8880 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8881 LISTVIEW_UpdateScroll(hwnd);
8882 InvalidateRect(hwnd, NULL, TRUE);
8887 return 0;
8890 /***
8891 * DESCRIPTION:
8892 * Determines the type of structure to use.
8894 * PARAMETER(S):
8895 * [I] HWND : window handle of the sender
8896 * [I] HWND : listview window handle
8897 * [I] INT : command specifying the nature of the WM_NOTIFYFORMAT
8899 * RETURN:
8900 * Zero
8902 static LRESULT LISTVIEW_NotifyFormat(HWND hwndFrom, HWND hwnd, INT nCommand)
8904 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8906 TRACE("(hwndFrom=%x, hwnd=%x, nCommand=%d)\n", hwndFrom, hwnd, nCommand);
8908 if (nCommand == NF_REQUERY)
8909 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT,
8910 (WPARAM)hwnd, (LPARAM)NF_QUERY);
8911 return 0;
8914 /***
8915 * DESCRIPTION:
8916 * Paints/Repaints the listview control.
8918 * PARAMETER(S):
8919 * [I] HWND : window handle
8920 * [I] HDC : device context handle
8922 * RETURN:
8923 * Zero
8925 static LRESULT LISTVIEW_Paint(HWND hwnd, HDC hdc)
8927 PAINTSTRUCT ps;
8929 TRACE("(hwnd=%x, hdc=%x)\n", hwnd, hdc);
8931 if (hdc == 0)
8933 hdc = BeginPaint(hwnd, &ps);
8934 LISTVIEW_Refresh(hwnd, hdc);
8935 EndPaint(hwnd, &ps);
8937 else
8939 LISTVIEW_Refresh(hwnd, hdc);
8942 return 0;
8945 /***
8946 * DESCRIPTION:
8947 * Processes double click messages (right mouse button).
8949 * PARAMETER(S):
8950 * [I] HWND : window handle
8951 * [I] WORD : key flag
8952 * [I] WORD : x coordinate
8953 * [I] WORD : y coordinate
8955 * RETURN:
8956 * Zero
8958 static LRESULT LISTVIEW_RButtonDblClk(HWND hwnd, WORD wKey, WORD wPosX,
8959 WORD wPosY)
8961 TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8963 /* send NM_RELEASEDCAPTURE notification */
8964 hdr_notify(hwnd, NM_RELEASEDCAPTURE);
8966 /* send NM_RDBLCLK notification */
8967 hdr_notify(hwnd, NM_RDBLCLK);
8969 return 0;
8972 /***
8973 * DESCRIPTION:
8974 * Processes mouse down messages (right mouse button).
8976 * PARAMETER(S):
8977 * [I] HWND : window handle
8978 * [I] WORD : key flag
8979 * [I] WORD : x coordinate
8980 * [I] WORD : y coordinate
8982 * RETURN:
8983 * Zero
8985 static LRESULT LISTVIEW_RButtonDown(HWND hwnd, WORD wKey, WORD wPosX,
8986 WORD wPosY)
8988 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8989 POINT ptPosition;
8990 INT nItem;
8991 NMLISTVIEW nmlv;
8992 LVHITTESTINFO lvHitTestInfo;
8994 TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8996 /* send NM_RELEASEDCAPTURE notification */
8997 hdr_notify(hwnd, NM_RELEASEDCAPTURE);
8999 /* make sure the listview control window has the focus */
9000 if (infoPtr->bFocus == FALSE)
9001 SetFocus(hwnd);
9003 /* set right button down flag */
9004 infoPtr->bRButtonDown = TRUE;
9006 /* determine the index of the selected item */
9007 ptPosition.x = wPosX;
9008 ptPosition.y = wPosY;
9009 nItem = LISTVIEW_MouseSelection(hwnd, ptPosition);
9010 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
9012 LISTVIEW_SetItemFocus(hwnd,nItem);
9013 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
9014 !LISTVIEW_IsSelected(hwnd,nItem))
9015 LISTVIEW_SetSelection(hwnd, nItem);
9017 else
9019 LISTVIEW_RemoveAllSelections(hwnd);
9022 lvHitTestInfo.pt.x = wPosX;
9023 lvHitTestInfo.pt.y = wPosY;
9025 /* Send NM_RClICK notification */
9026 ZeroMemory(&nmlv, sizeof(nmlv));
9027 if (LISTVIEW_HitTestItem(hwnd, &lvHitTestInfo, TRUE) != -1)
9029 nmlv.iItem = lvHitTestInfo.iItem;
9030 nmlv.iSubItem = lvHitTestInfo.iSubItem;
9032 else
9034 nmlv.iItem = -1;
9035 nmlv.iSubItem = 0;
9037 nmlv.ptAction.x = wPosX;
9038 nmlv.ptAction.y = wPosY;
9039 listview_notify(hwnd, NM_RCLICK, &nmlv);
9041 return 0;
9044 /***
9045 * DESCRIPTION:
9046 * Processes mouse up messages (right mouse button).
9048 * PARAMETER(S):
9049 * [I] HWND : window handle
9050 * [I] WORD : key flag
9051 * [I] WORD : x coordinate
9052 * [I] WORD : y coordinate
9054 * RETURN:
9055 * Zero
9057 static LRESULT LISTVIEW_RButtonUp(HWND hwnd, WORD wKey, WORD wPosX,
9058 WORD wPosY)
9060 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9062 TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
9064 if (infoPtr->bRButtonDown)
9066 POINT pt;
9068 pt.x = wPosX;
9069 pt.y = wPosY;
9071 /* set button flag */
9072 infoPtr->bRButtonDown = FALSE;
9074 /* Change to screen coordinate for WM_CONTEXTMENU */
9075 ClientToScreen(hwnd, &pt);
9077 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
9078 SendMessageW( hwnd, WM_CONTEXTMENU, (WPARAM) hwnd, MAKELPARAM(pt.x, pt.y));
9081 return 0;
9084 /***
9085 * DESCRIPTION:
9086 * Sets the focus.
9088 * PARAMETER(S):
9089 * [I] HWND : window handle
9090 * [I] HWND : window handle of previously focused window
9092 * RETURN:
9093 * Zero
9095 static LRESULT LISTVIEW_SetFocus(HWND hwnd, HWND hwndLoseFocus)
9097 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9099 TRACE("(hwnd=%x, hwndLoseFocus=%x)\n", hwnd, hwndLoseFocus);
9101 /* send NM_SETFOCUS notification */
9102 hdr_notify(hwnd, NM_SETFOCUS);
9104 /* set window focus flag */
9105 infoPtr->bFocus = TRUE;
9107 UpdateWindow(hwnd);
9109 return 0;
9112 /***
9113 * DESCRIPTION:
9114 * Sets the font.
9116 * PARAMETER(S):
9117 * [I] HWND : window handle
9118 * [I] HFONT : font handle
9119 * [I] WORD : redraw flag
9121 * RETURN:
9122 * Zero
9124 static LRESULT LISTVIEW_SetFont(HWND hwnd, HFONT hFont, WORD fRedraw)
9126 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9127 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
9129 TRACE("(hwnd=%x,hfont=%x,redraw=%hu)\n", hwnd, hFont, fRedraw);
9131 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
9132 LISTVIEW_SaveTextMetrics(hwnd);
9134 if (uView == LVS_REPORT)
9136 /* set header font */
9137 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont,
9138 MAKELPARAM(fRedraw, 0));
9141 /* invalidate listview control client area */
9142 InvalidateRect(hwnd, NULL, TRUE);
9144 if (fRedraw != FALSE)
9145 UpdateWindow(hwnd);
9147 return 0;
9150 /***
9151 * DESCRIPTION:
9152 * Message handling for WM_SETREDRAW.
9153 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
9155 * PARAMETER(S):
9156 * [I] HWND : window handle
9157 * [I] bRedraw: state of redraw flag
9159 * RETURN:
9160 * DefWinProc return value
9162 static LRESULT LISTVIEW_SetRedraw(HWND hwnd, BOOL bRedraw)
9164 LRESULT lResult = DefWindowProcW(hwnd, WM_SETREDRAW, bRedraw, 0);
9165 if(bRedraw)
9166 RedrawWindow(hwnd, NULL, 0,
9167 RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ALLCHILDREN | RDW_ERASENOW);
9168 return lResult;
9171 /***
9172 * DESCRIPTION:
9173 * Resizes the listview control. This function processes WM_SIZE
9174 * messages. At this time, the width and height are not used.
9176 * PARAMETER(S):
9177 * [I] HWND : window handle
9178 * [I] WORD : new width
9179 * [I] WORD : new height
9181 * RETURN:
9182 * Zero
9184 static LRESULT LISTVIEW_Size(HWND hwnd, int Width, int Height)
9186 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
9187 UINT uView = lStyle & LVS_TYPEMASK;
9189 TRACE("(hwnd=%x, width=%d, height=%d)\n", hwnd, Width, Height);
9191 if (LISTVIEW_UpdateSize(hwnd))
9193 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
9195 if (lStyle & LVS_ALIGNLEFT)
9196 LISTVIEW_AlignLeft(hwnd);
9197 else
9198 LISTVIEW_AlignTop(hwnd);
9201 LISTVIEW_UpdateScroll(hwnd);
9203 /* invalidate client area + erase background */
9204 InvalidateRect(hwnd, NULL, TRUE);
9207 return 0;
9210 /***
9211 * DESCRIPTION:
9212 * Sets the size information.
9214 * PARAMETER(S):
9215 * [I] HWND : window handle
9217 * RETURN:
9218 * Zero if no size change
9219 * 1 of size changed
9221 static BOOL LISTVIEW_UpdateSize(HWND hwnd)
9223 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9224 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
9225 UINT uView = lStyle & LVS_TYPEMASK;
9226 RECT rcList;
9227 RECT rcOld;
9229 TRACE("(hwnd=%x)\n", hwnd);
9231 GetClientRect(hwnd, &rcList);
9232 CopyRect(&rcOld,&(infoPtr->rcList));
9233 infoPtr->rcList.left = 0;
9234 infoPtr->rcList.right = max(rcList.right - rcList.left, 1);
9235 infoPtr->rcList.top = 0;
9236 infoPtr->rcList.bottom = max(rcList.bottom - rcList.top, 1);
9238 if (uView == LVS_LIST)
9240 if (lStyle & WS_HSCROLL)
9242 INT nHScrollHeight = GetSystemMetrics(SM_CYHSCROLL);
9243 if (infoPtr->rcList.bottom > nHScrollHeight)
9244 infoPtr->rcList.bottom -= nHScrollHeight;
9247 else if (uView == LVS_REPORT)
9249 HDLAYOUT hl;
9250 WINDOWPOS wp;
9252 hl.prc = &rcList;
9253 hl.pwpos = &wp;
9254 Header_Layout(infoPtr->hwndHeader, &hl);
9256 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
9258 if (!(LVS_NOCOLUMNHEADER & lStyle))
9259 infoPtr->rcList.top = max(wp.cy, 0);
9261 return (EqualRect(&rcOld,&(infoPtr->rcList)));
9264 /***
9265 * DESCRIPTION:
9266 * Processes WM_STYLECHANGED messages.
9268 * PARAMETER(S):
9269 * [I] HWND : window handle
9270 * [I] WPARAM : window style type (normal or extended)
9271 * [I] LPSTYLESTRUCT : window style information
9273 * RETURN:
9274 * Zero
9276 static INT LISTVIEW_StyleChanged(HWND hwnd, WPARAM wStyleType,
9277 LPSTYLESTRUCT lpss)
9279 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9280 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
9281 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
9282 RECT rcList = infoPtr->rcList;
9284 TRACE("(hwnd=%x, styletype=%x, stylestruct=%p)\n",
9285 hwnd, wStyleType, lpss);
9287 if (wStyleType == GWL_STYLE)
9289 if (uOldView == LVS_REPORT)
9290 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
9292 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
9293 ((lpss->styleNew & WS_HSCROLL) == 0))
9294 ShowScrollBar(hwnd, SB_HORZ, FALSE);
9296 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
9297 ((lpss->styleNew & WS_VSCROLL) == 0))
9298 ShowScrollBar(hwnd, SB_VERT, FALSE);
9300 if (uNewView == LVS_ICON)
9302 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
9303 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
9304 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
9305 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
9306 if (lpss->styleNew & LVS_ALIGNLEFT)
9307 LISTVIEW_AlignLeft(hwnd);
9308 else
9309 LISTVIEW_AlignTop(hwnd);
9311 else if (uNewView == LVS_REPORT)
9313 HDLAYOUT hl;
9314 WINDOWPOS wp;
9316 hl.prc = &rcList;
9317 hl.pwpos = &wp;
9318 Header_Layout(infoPtr->hwndHeader, &hl);
9319 SetWindowPos(infoPtr->hwndHeader, hwnd, wp.x, wp.y, wp.cx, wp.cy,
9320 wp.flags);
9321 if (!(LVS_NOCOLUMNHEADER & lpss->styleNew))
9322 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
9324 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
9325 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
9326 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
9327 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
9329 else if (uNewView == LVS_LIST)
9331 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
9332 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
9333 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
9334 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
9336 else
9338 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
9339 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
9340 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
9341 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
9342 if (lpss->styleNew & LVS_ALIGNLEFT)
9343 LISTVIEW_AlignLeft(hwnd);
9344 else
9345 LISTVIEW_AlignTop(hwnd);
9348 /* update the size of the client area */
9349 LISTVIEW_UpdateSize(hwnd);
9351 /* add scrollbars if needed */
9352 LISTVIEW_UpdateScroll(hwnd);
9354 /* invalidate client area + erase background */
9355 InvalidateRect(hwnd, NULL, TRUE);
9357 /* print the list of unsupported window styles */
9358 LISTVIEW_UnsupportedStyles(lpss->styleNew);
9361 /* If they change the view and we have an active edit control
9362 we will need to kill the control since the redraw will
9363 misplace the edit control.
9365 if (infoPtr->hwndEdit &&
9366 ((uNewView & (LVS_ICON|LVS_LIST|LVS_SMALLICON)) !=
9367 ((LVS_ICON|LVS_LIST|LVS_SMALLICON) & uOldView)))
9369 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
9372 return 0;
9375 /***
9376 * DESCRIPTION:
9377 * Window procedure of the listview control.
9380 static LRESULT WINAPI LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam,
9381 LPARAM lParam)
9383 TRACE("(hwnd=%x uMsg=%x wParam=%x lParam=%lx)\n", hwnd, uMsg, wParam, lParam);
9384 if (!GetWindowLongW(hwnd, 0) && (uMsg != WM_NCCREATE))
9385 return DefWindowProcW( hwnd, uMsg, wParam, lParam );
9386 switch (uMsg)
9388 case LVM_APPROXIMATEVIEWRECT:
9389 return LISTVIEW_ApproximateViewRect(hwnd, (INT)wParam,
9390 LOWORD(lParam), HIWORD(lParam));
9391 case LVM_ARRANGE:
9392 return LISTVIEW_Arrange(hwnd, (INT)wParam);
9394 /* case LVM_CREATEDRAGIMAGE: */
9396 case LVM_DELETEALLITEMS:
9397 return LISTVIEW_DeleteAllItems(hwnd);
9399 case LVM_DELETECOLUMN:
9400 return LISTVIEW_DeleteColumn(hwnd, (INT)wParam);
9402 case LVM_DELETEITEM:
9403 return LISTVIEW_DeleteItem(hwnd, (INT)wParam);
9405 case LVM_EDITLABELW:
9406 return LISTVIEW_EditLabelT(hwnd, (INT)wParam, TRUE);
9408 case LVM_EDITLABELA:
9409 return LISTVIEW_EditLabelT(hwnd, (INT)wParam, FALSE);
9411 case LVM_ENSUREVISIBLE:
9412 return LISTVIEW_EnsureVisible(hwnd, (INT)wParam, (BOOL)lParam);
9414 case LVM_FINDITEMW:
9415 return LISTVIEW_FindItemW(hwnd, (INT)wParam, (LPLVFINDINFOW)lParam);
9417 case LVM_FINDITEMA:
9418 return LISTVIEW_FindItemA(hwnd, (INT)wParam, (LPLVFINDINFOA)lParam);
9420 case LVM_GETBKCOLOR:
9421 return LISTVIEW_GetBkColor(hwnd);
9423 /* case LVM_GETBKIMAGE: */
9425 case LVM_GETCALLBACKMASK:
9426 return LISTVIEW_GetCallbackMask(hwnd);
9428 case LVM_GETCOLUMNA:
9429 return LISTVIEW_GetColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9431 case LVM_GETCOLUMNW:
9432 return LISTVIEW_GetColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9434 case LVM_GETCOLUMNORDERARRAY:
9435 return LISTVIEW_GetColumnOrderArray(hwnd, (INT)wParam, (LPINT)lParam);
9437 case LVM_GETCOLUMNWIDTH:
9438 return LISTVIEW_GetColumnWidth(hwnd, (INT)wParam);
9440 case LVM_GETCOUNTPERPAGE:
9441 return LISTVIEW_GetCountPerPage(hwnd);
9443 case LVM_GETEDITCONTROL:
9444 return LISTVIEW_GetEditControl(hwnd);
9446 case LVM_GETEXTENDEDLISTVIEWSTYLE:
9447 return LISTVIEW_GetExtendedListViewStyle(hwnd);
9449 case LVM_GETHEADER:
9450 return LISTVIEW_GetHeader(hwnd);
9452 case LVM_GETHOTCURSOR:
9453 FIXME("LVM_GETHOTCURSOR: unimplemented\n");
9454 return FALSE;
9456 case LVM_GETHOTITEM:
9457 return LISTVIEW_GetHotItem(hwnd);
9459 case LVM_GETHOVERTIME:
9460 return LISTVIEW_GetHoverTime(hwnd);
9462 case LVM_GETIMAGELIST:
9463 return LISTVIEW_GetImageList(hwnd, (INT)wParam);
9465 case LVM_GETISEARCHSTRINGA:
9466 case LVM_GETISEARCHSTRINGW:
9467 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
9468 return FALSE;
9470 case LVM_GETITEMA:
9471 return LISTVIEW_GetItemT(hwnd, (LPLVITEMW)lParam, FALSE, FALSE);
9473 case LVM_GETITEMW:
9474 return LISTVIEW_GetItemT(hwnd, (LPLVITEMW)lParam, FALSE, TRUE);
9476 case LVM_GETITEMCOUNT:
9477 return LISTVIEW_GetItemCount(hwnd);
9479 case LVM_GETITEMPOSITION:
9480 return LISTVIEW_GetItemPosition(hwnd, (INT)wParam, (LPPOINT)lParam);
9482 case LVM_GETITEMRECT:
9483 return LISTVIEW_GetItemRect(hwnd, (INT)wParam, (LPRECT)lParam);
9485 case LVM_GETITEMSPACING:
9486 return LISTVIEW_GetItemSpacing(hwnd, (BOOL)wParam);
9488 case LVM_GETITEMSTATE:
9489 return LISTVIEW_GetItemState(hwnd, (INT)wParam, (UINT)lParam);
9491 case LVM_GETITEMTEXTA:
9492 return LISTVIEW_GetItemTextT(hwnd, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9494 case LVM_GETITEMTEXTW:
9495 return LISTVIEW_GetItemTextT(hwnd, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9497 case LVM_GETNEXTITEM:
9498 return LISTVIEW_GetNextItem(hwnd, (INT)wParam, LOWORD(lParam));
9500 case LVM_GETNUMBEROFWORKAREAS:
9501 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
9502 return 1;
9504 case LVM_GETORIGIN:
9505 return LISTVIEW_GetOrigin(hwnd, (LPPOINT)lParam);
9507 case LVM_GETSELECTEDCOUNT:
9508 return LISTVIEW_GetSelectedCount(hwnd);
9510 case LVM_GETSELECTIONMARK:
9511 return LISTVIEW_GetSelectionMark(hwnd);
9513 case LVM_GETSTRINGWIDTHA:
9514 return LISTVIEW_GetStringWidthT(hwnd, (LPCWSTR)lParam, FALSE);
9516 case LVM_GETSTRINGWIDTHW:
9517 return LISTVIEW_GetStringWidthT(hwnd, (LPCWSTR)lParam, TRUE);
9519 case LVM_GETSUBITEMRECT:
9520 return LISTVIEW_GetSubItemRect(hwnd, (UINT)wParam, ((LPRECT)lParam)->top,
9521 ((LPRECT)lParam)->left, (LPRECT)lParam);
9523 case LVM_GETTEXTBKCOLOR:
9524 return LISTVIEW_GetTextBkColor(hwnd);
9526 case LVM_GETTEXTCOLOR:
9527 return LISTVIEW_GetTextColor(hwnd);
9529 case LVM_GETTOOLTIPS:
9530 FIXME("LVM_GETTOOLTIPS: unimplemented\n");
9531 return FALSE;
9533 case LVM_GETTOPINDEX:
9534 return LISTVIEW_GetTopIndex(hwnd);
9536 /*case LVM_GETUNICODEFORMAT:
9537 FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
9538 return FALSE;*/
9540 case LVM_GETVIEWRECT:
9541 return LISTVIEW_GetViewRect(hwnd, (LPRECT)lParam);
9543 case LVM_GETWORKAREAS:
9544 FIXME("LVM_GETWORKAREAS: unimplemented\n");
9545 return FALSE;
9547 case LVM_HITTEST:
9548 return LISTVIEW_HitTest(hwnd, (LPLVHITTESTINFO)lParam);
9550 case LVM_INSERTCOLUMNA:
9551 return LISTVIEW_InsertColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9553 case LVM_INSERTCOLUMNW:
9554 return LISTVIEW_InsertColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9556 case LVM_INSERTITEMA:
9557 return LISTVIEW_InsertItemT(hwnd, (LPLVITEMW)lParam, FALSE);
9559 case LVM_INSERTITEMW:
9560 return LISTVIEW_InsertItemT(hwnd, (LPLVITEMW)lParam, TRUE);
9562 case LVM_REDRAWITEMS:
9563 return LISTVIEW_RedrawItems(hwnd, (INT)wParam, (INT)lParam);
9565 /* case LVM_SCROLL: */
9566 /* return LISTVIEW_Scroll(hwnd, (INT)wParam, (INT)lParam); */
9568 case LVM_SETBKCOLOR:
9569 return LISTVIEW_SetBkColor(hwnd, (COLORREF)lParam);
9571 /* case LVM_SETBKIMAGE: */
9573 case LVM_SETCALLBACKMASK:
9574 return LISTVIEW_SetCallbackMask(hwnd, (UINT)wParam);
9576 case LVM_SETCOLUMNA:
9577 return LISTVIEW_SetColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9579 case LVM_SETCOLUMNW:
9580 return LISTVIEW_SetColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9582 case LVM_SETCOLUMNORDERARRAY:
9583 return LISTVIEW_SetColumnOrderArray(hwnd, (INT)wParam, (LPINT)lParam);
9585 case LVM_SETCOLUMNWIDTH:
9586 return LISTVIEW_SetColumnWidth(hwnd, (INT)wParam, SLOWORD(lParam));
9588 case LVM_SETEXTENDEDLISTVIEWSTYLE:
9589 return LISTVIEW_SetExtendedListViewStyle(hwnd, (DWORD)wParam, (DWORD)lParam);
9591 /* case LVM_SETHOTCURSOR: */
9593 case LVM_SETHOTITEM:
9594 return LISTVIEW_SetHotItem(hwnd, (INT)wParam);
9596 case LVM_SETHOVERTIME:
9597 return LISTVIEW_SetHoverTime(hwnd, (DWORD)wParam);
9599 case LVM_SETICONSPACING:
9600 return LISTVIEW_SetIconSpacing(hwnd, (DWORD)lParam);
9602 case LVM_SETIMAGELIST:
9603 return (LRESULT)LISTVIEW_SetImageList(hwnd, (INT)wParam, (HIMAGELIST)lParam);
9605 case LVM_SETITEMA:
9606 return LISTVIEW_SetItemT(hwnd, (LPLVITEMW)lParam, FALSE);
9608 case LVM_SETITEMW:
9609 return LISTVIEW_SetItemT(hwnd, (LPLVITEMW)lParam, TRUE);
9611 case LVM_SETITEMCOUNT:
9612 return LISTVIEW_SetItemCount(hwnd, (INT)wParam, (DWORD)lParam);
9614 case LVM_SETITEMPOSITION:
9615 return LISTVIEW_SetItemPosition(hwnd, (INT)wParam, (INT)LOWORD(lParam),
9616 (INT)HIWORD(lParam));
9618 case LVM_SETITEMPOSITION32:
9619 return LISTVIEW_SetItemPosition(hwnd, (INT)wParam, ((POINT*)lParam)->x,
9620 ((POINT*)lParam)->y);
9622 case LVM_SETITEMSTATE:
9623 return LISTVIEW_SetItemState(hwnd, (INT)wParam, (LPLVITEMW)lParam);
9625 case LVM_SETITEMTEXTA:
9626 return LISTVIEW_SetItemTextT(hwnd, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9628 case LVM_SETITEMTEXTW:
9629 return LISTVIEW_SetItemTextT(hwnd, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9631 case LVM_SETSELECTIONMARK:
9632 return LISTVIEW_SetSelectionMark(hwnd, (INT)lParam);
9634 case LVM_SETTEXTBKCOLOR:
9635 return LISTVIEW_SetTextBkColor(hwnd, (COLORREF)lParam);
9637 case LVM_SETTEXTCOLOR:
9638 return LISTVIEW_SetTextColor(hwnd, (COLORREF)lParam);
9640 /* case LVM_SETTOOLTIPS: */
9641 /* case LVM_SETUNICODEFORMAT: */
9642 /* case LVM_SETWORKAREAS: */
9644 case LVM_SORTITEMS:
9645 return LISTVIEW_SortItems(hwnd, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
9647 /* case LVM_SUBITEMHITTEST: */
9649 case LVM_UPDATE:
9650 return LISTVIEW_Update(hwnd, (INT)wParam);
9652 case WM_CHAR:
9653 return LISTVIEW_ProcessLetterKeys( hwnd, wParam, lParam );
9655 case WM_COMMAND:
9656 return LISTVIEW_Command(hwnd, wParam, lParam);
9658 case WM_CREATE:
9659 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
9661 case WM_ERASEBKGND:
9662 return LISTVIEW_EraseBackground(hwnd, wParam, lParam);
9664 case WM_GETDLGCODE:
9665 return DLGC_WANTCHARS | DLGC_WANTARROWS;
9667 case WM_GETFONT:
9668 return LISTVIEW_GetFont(hwnd);
9670 case WM_HSCROLL:
9671 return LISTVIEW_HScroll(hwnd, (INT)LOWORD(wParam),
9672 (INT)HIWORD(wParam), (HWND)lParam);
9674 case WM_KEYDOWN:
9675 return LISTVIEW_KeyDown(hwnd, (INT)wParam, (LONG)lParam);
9677 case WM_KILLFOCUS:
9678 return LISTVIEW_KillFocus(hwnd);
9680 case WM_LBUTTONDBLCLK:
9681 return LISTVIEW_LButtonDblClk(hwnd, (WORD)wParam, LOWORD(lParam),
9682 HIWORD(lParam));
9684 case WM_LBUTTONDOWN:
9685 return LISTVIEW_LButtonDown(hwnd, (WORD)wParam, LOWORD(lParam),
9686 HIWORD(lParam));
9687 case WM_LBUTTONUP:
9688 return LISTVIEW_LButtonUp(hwnd, (WORD)wParam, LOWORD(lParam),
9689 HIWORD(lParam));
9690 case WM_MOUSEMOVE:
9691 return LISTVIEW_MouseMove (hwnd, wParam, lParam);
9693 case WM_MOUSEHOVER:
9694 return LISTVIEW_MouseHover(hwnd, wParam, lParam);
9696 case WM_NCCREATE:
9697 return LISTVIEW_NCCreate(hwnd, wParam, lParam);
9699 case WM_NCDESTROY:
9700 return LISTVIEW_NCDestroy(hwnd);
9702 case WM_NOTIFY:
9703 return LISTVIEW_Notify(hwnd, (INT)wParam, (LPNMHDR)lParam);
9705 case WM_NOTIFYFORMAT:
9706 return LISTVIEW_NotifyFormat(hwnd, (HWND)wParam, (INT)lParam);
9708 case WM_PAINT:
9709 return LISTVIEW_Paint(hwnd, (HDC)wParam);
9711 case WM_RBUTTONDBLCLK:
9712 return LISTVIEW_RButtonDblClk(hwnd, (WORD)wParam, LOWORD(lParam),
9713 HIWORD(lParam));
9715 case WM_RBUTTONDOWN:
9716 return LISTVIEW_RButtonDown(hwnd, (WORD)wParam, LOWORD(lParam),
9717 HIWORD(lParam));
9719 case WM_RBUTTONUP:
9720 return LISTVIEW_RButtonUp(hwnd, (WORD)wParam, LOWORD(lParam),
9721 HIWORD(lParam));
9723 case WM_SETFOCUS:
9724 return LISTVIEW_SetFocus(hwnd, (HWND)wParam);
9726 case WM_SETFONT:
9727 return LISTVIEW_SetFont(hwnd, (HFONT)wParam, (WORD)lParam);
9729 case WM_SETREDRAW:
9730 return LISTVIEW_SetRedraw(hwnd, (BOOL)wParam);
9732 case WM_SIZE:
9733 return LISTVIEW_Size(hwnd, (int)SLOWORD(lParam), (int)SHIWORD(lParam));
9735 case WM_STYLECHANGED:
9736 return LISTVIEW_StyleChanged(hwnd, wParam, (LPSTYLESTRUCT)lParam);
9738 /* case WM_TIMER: */
9740 case WM_VSCROLL:
9741 return LISTVIEW_VScroll(hwnd, (INT)LOWORD(wParam),
9742 (INT)HIWORD(wParam), (HWND)lParam);
9744 case WM_MOUSEWHEEL:
9745 if (wParam & (MK_SHIFT | MK_CONTROL))
9746 return DefWindowProcW( hwnd, uMsg, wParam, lParam );
9747 return LISTVIEW_MouseWheel(hwnd, (short int)HIWORD(wParam));/* case WM_WINDOWPOSCHANGED: */
9749 /* case WM_WININICHANGE: */
9751 default:
9752 if (uMsg >= WM_USER)
9754 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam,
9755 lParam);
9758 /* call default window procedure */
9759 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9762 return 0;
9765 /***
9766 * DESCRIPTION:
9767 * Registers the window class.
9769 * PARAMETER(S):
9770 * None
9772 * RETURN:
9773 * None
9775 VOID LISTVIEW_Register(void)
9777 WNDCLASSW wndClass;
9779 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
9780 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
9781 wndClass.lpfnWndProc = (WNDPROC)LISTVIEW_WindowProc;
9782 wndClass.cbClsExtra = 0;
9783 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
9784 wndClass.hCursor = LoadCursorW(0, IDC_ARROWW);
9785 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
9786 wndClass.lpszClassName = WC_LISTVIEWW;
9787 RegisterClassW(&wndClass);
9790 /***
9791 * DESCRIPTION:
9792 * Unregisters the window class.
9794 * PARAMETER(S):
9795 * None
9797 * RETURN:
9798 * None
9800 VOID LISTVIEW_Unregister(void)
9802 UnregisterClassW(WC_LISTVIEWW, (HINSTANCE)NULL);
9805 /***
9806 * DESCRIPTION:
9807 * Handle any WM_COMMAND messages
9809 * PARAMETER(S):
9811 * RETURN:
9813 static LRESULT LISTVIEW_Command(HWND hwnd, WPARAM wParam, LPARAM lParam)
9815 switch (HIWORD(wParam))
9817 case EN_UPDATE:
9820 * Adjust the edit window size
9822 WCHAR buffer[1024];
9823 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9824 HDC hdc = GetDC(infoPtr->hwndEdit);
9825 HFONT hFont, hOldFont = 0;
9826 RECT rect;
9827 SIZE sz;
9828 int len;
9830 len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
9831 GetWindowRect(infoPtr->hwndEdit, &rect);
9833 /* Select font to get the right dimension of the string */
9834 hFont = SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
9835 if(hFont != 0)
9837 hOldFont = SelectObject(hdc, hFont);
9840 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
9842 TEXTMETRICW textMetric;
9844 /* Add Extra spacing for the next character */
9845 GetTextMetricsW(hdc, &textMetric);
9846 sz.cx += (textMetric.tmMaxCharWidth * 2);
9848 SetWindowPos (
9849 infoPtr->hwndEdit,
9850 HWND_TOP,
9853 sz.cx,
9854 rect.bottom - rect.top,
9855 SWP_DRAWFRAME|SWP_NOMOVE);
9857 if(hFont != 0)
9858 SelectObject(hdc, hOldFont);
9860 ReleaseDC(hwnd, hdc);
9862 break;
9865 default:
9866 return SendMessageW (GetParent (hwnd), WM_COMMAND, wParam, lParam);
9869 return 0;
9873 /***
9874 * DESCRIPTION:
9875 * Subclassed edit control windproc function
9877 * PARAMETER(S):
9879 * RETURN:
9881 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg,
9882 WPARAM wParam, LPARAM lParam, BOOL isW)
9884 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(GetParent(hwnd), 0);
9885 EDITLABEL_ITEM *einfo = infoPtr->pedititem;
9886 static BOOL bIgnoreKillFocus = FALSE;
9887 BOOL cancel = FALSE;
9889 TRACE("(hwnd=%x, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
9890 hwnd, uMsg, wParam, lParam, isW);
9892 switch (uMsg)
9894 case WM_GETDLGCODE:
9895 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
9897 case WM_KILLFOCUS:
9898 if(bIgnoreKillFocus) return TRUE;
9899 break;
9901 case WM_DESTROY:
9903 WNDPROC editProc = einfo->EditWndProc;
9904 SetWindowLongW(hwnd, GWL_WNDPROC, (LONG)editProc);
9905 COMCTL32_Free(einfo);
9906 infoPtr->pedititem = NULL;
9907 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
9910 case WM_KEYDOWN:
9911 if (VK_ESCAPE == (INT)wParam)
9913 cancel = TRUE;
9914 break;
9916 else if (VK_RETURN == (INT)wParam)
9917 break;
9919 default:
9920 return CallWindowProcT(einfo->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
9923 if (einfo->EditLblCb)
9925 LPWSTR buffer = NULL;
9927 if (!cancel)
9929 DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
9931 if (len)
9933 if ( (buffer = COMCTL32_Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
9935 if (isW) GetWindowTextW(hwnd, buffer, len+1);
9936 else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
9940 /* Processing LVN_ENDLABELEDIT message could kill the focus */
9941 /* eg. Using a messagebox */
9942 bIgnoreKillFocus = TRUE;
9943 einfo->EditLblCb(GetParent(hwnd), buffer, einfo->param);
9945 if (buffer) COMCTL32_Free(buffer);
9947 einfo->EditLblCb = NULL;
9948 bIgnoreKillFocus = FALSE;
9951 SendMessageW(hwnd, WM_CLOSE, 0, 0);
9952 return TRUE;
9955 /***
9956 * DESCRIPTION:
9957 * Subclassed edit control windproc function
9959 * PARAMETER(S):
9961 * RETURN:
9963 LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9965 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
9968 /***
9969 * DESCRIPTION:
9970 * Subclassed edit control windproc function
9972 * PARAMETER(S):
9974 * RETURN:
9976 LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9978 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
9981 /***
9982 * DESCRIPTION:
9983 * Creates a subclassed edit cotrol
9985 * PARAMETER(S):
9987 * RETURN:
9989 HWND CreateEditLabelT(LPCWSTR text, DWORD style, INT x, INT y,
9990 INT width, INT height, HWND parent, HINSTANCE hinst,
9991 EditlblCallbackW EditLblCb, DWORD param, BOOL isW)
9993 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(parent, 0);
9994 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
9995 HWND hedit;
9996 SIZE sz;
9997 HDC hdc;
9998 HDC hOldFont=0;
9999 TEXTMETRICW textMetric;
10001 TRACE("(text=%s, ..., isW=%d)\n", debugstr_t(text, isW), isW);
10003 if (NULL == (infoPtr->pedititem = COMCTL32_Alloc(sizeof(EDITLABEL_ITEM))))
10004 return 0;
10006 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|WS_BORDER;
10007 hdc = GetDC(parent);
10009 /* Select the font to get appropriate metric dimensions */
10010 if(infoPtr->hFont != 0)
10011 hOldFont = SelectObject(hdc, infoPtr->hFont);
10013 /*Get String Lenght in pixels */
10014 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
10016 /*Add Extra spacing for the next character */
10017 GetTextMetricsW(hdc, &textMetric);
10018 sz.cx += (textMetric.tmMaxCharWidth * 2);
10020 if(infoPtr->hFont != 0)
10021 SelectObject(hdc, hOldFont);
10023 ReleaseDC(parent, hdc);
10024 if (isW)
10025 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, parent, 0, hinst, 0);
10026 else
10027 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, parent, 0, hinst, 0);
10029 if (!hedit)
10031 COMCTL32_Free(infoPtr->pedititem);
10032 return 0;
10035 infoPtr->pedititem->param = param;
10036 infoPtr->pedititem->EditLblCb = EditLblCb;
10037 infoPtr->pedititem->EditWndProc = (WNDPROC)
10038 (isW ? SetWindowLongW(hedit, GWL_WNDPROC, (LONG)EditLblWndProcW) :
10039 SetWindowLongA(hedit, GWL_WNDPROC, (LONG)EditLblWndProcA) );
10041 SendMessageW(hedit, WM_SETFONT, infoPtr->hFont, FALSE);
10043 return hedit;