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