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