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 if (!(clone = ranges_create(DPA_GetPtrCount(ranges->hdpa)))) goto fail; 3259 3260 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++) 3261 { 3262 RANGE *newrng = Alloc(sizeof(RANGE)); 3263 if (!newrng) goto fail; 3264 *newrng = *((RANGE*)DPA_GetPtr(ranges->hdpa, i)); 3265 if (!DPA_SetPtr(clone->hdpa, i, newrng)) 3266 { 3267 Free(newrng); 3268 goto fail; 3269 } 3270 } 3271 return clone; 3272 3273 fail: 3274 TRACE ("clone failed\n"); 3275 ranges_destroy(clone); 3276 return NULL; 3277 } 3278 3279 static RANGES ranges_diff(RANGES ranges, RANGES sub) 3280 { 3281 INT i; 3282 3283 for (i = 0; i < DPA_GetPtrCount(sub->hdpa); i++) 3284 ranges_del(ranges, *((RANGE *)DPA_GetPtr(sub->hdpa, i))); 3285 3286 return ranges; 3287 } 3288 3289 static void ranges_dump(RANGES ranges) 3290 { 3291 INT i; 3292 3293 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++) 3294 TRACE(" %s\n", debugrange(DPA_GetPtr(ranges->hdpa, i))); 3295 } 3296 3297 static inline BOOL ranges_contain(RANGES ranges, INT nItem) 3298 { 3299 RANGE srchrng = { nItem, nItem + 1 }; 3300 3301 TRACE("(nItem=%d)\n", nItem); 3302 ranges_check(ranges, "before contain"); 3303 return DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED) != -1; 3304 } 3305 3306 static INT ranges_itemcount(RANGES ranges) 3307 { 3308 INT i, count = 0; 3309 3310 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++) 3311 { 3312 RANGE *sel = DPA_GetPtr(ranges->hdpa, i); 3313 count += sel->upper - sel->lower; 3314 } 3315 3316 return count; 3317 } 3318 3319 static BOOL ranges_shift(RANGES ranges, INT nItem, INT delta, INT nUpper) 3320 { 3321 RANGE srchrng = { nItem, nItem + 1 }, *chkrng; 3322 INT index; 3323 3324 index = DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER); 3325 if (index == -1) return TRUE; 3326 3327 for (; index < DPA_GetPtrCount(ranges->hdpa); index++) 3328 { 3329 chkrng = DPA_GetPtr(ranges->hdpa, index); 3330 if (chkrng->lower >= nItem) 3331 chkrng->lower = max(min(chkrng->lower + delta, nUpper - 1), 0); 3332 if (chkrng->upper > nItem) 3333 chkrng->upper = max(min(chkrng->upper + delta, nUpper), 0); 3334 } 3335 return TRUE; 3336 } 3337 3338 static BOOL ranges_add(RANGES ranges, RANGE range) 3339 { 3340 RANGE srchrgn; 3341 INT index; 3342 3343 TRACE("(%s)\n", debugrange(&range)); 3344 ranges_check(ranges, "before add"); 3345 3346 /* try find overlapping regions first */ 3347 srchrgn.lower = range.lower - 1; 3348 srchrgn.upper = range.upper + 1; 3349 index = DPA_Search(ranges->hdpa, &srchrgn, 0, ranges_cmp, 0, DPAS_SORTED); 3350 3351 if (index == -1) 3352 { 3353 RANGE *newrgn; 3354 3355 TRACE("Adding new range\n"); 3356 3357 /* create the brand new range to insert */ 3358 newrgn = Alloc(sizeof(RANGE)); 3359 if(!newrgn) goto fail; 3360 *newrgn = range; 3361 3362 /* figure out where to insert it */ 3363 index = DPA_Search(ranges->hdpa, newrgn, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER); 3364 TRACE("index=%d\n", index); 3365 if (index == -1) index = 0; 3366 3367 /* and get it over with */ 3368 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1) 3369 { 3370 Free(newrgn); 3371 goto fail; 3372 } 3373 } 3374 else 3375 { 3376 RANGE *chkrgn, *mrgrgn; 3377 INT fromindex, mergeindex; 3378 3379 chkrgn = DPA_GetPtr(ranges->hdpa, index); 3380 TRACE("Merge with %s @%d\n", debugrange(chkrgn), index); 3381 3382 chkrgn->lower = min(range.lower, chkrgn->lower); 3383 chkrgn->upper = max(range.upper, chkrgn->upper); 3384 3385 TRACE("New range %s @%d\n", debugrange(chkrgn), index); 3386 3387 /* merge now common ranges */ 3388 fromindex = 0; 3389 srchrgn.lower = chkrgn->lower - 1; 3390 srchrgn.upper = chkrgn->upper + 1; 3391 3392 do 3393 { 3394 mergeindex = DPA_Search(ranges->hdpa, &srchrgn, fromindex, ranges_cmp, 0, 0); 3395 if (mergeindex == -1) break; 3396 if (mergeindex == index) 3397 { 3398 fromindex = index + 1; 3399 continue; 3400 } 3401 3402 TRACE("Merge with index %i\n", mergeindex); 3403 3404 mrgrgn = DPA_GetPtr(ranges->hdpa, mergeindex); 3405 chkrgn->lower = min(chkrgn->lower, mrgrgn->lower); 3406 chkrgn->upper = max(chkrgn->upper, mrgrgn->upper); 3407 Free(mrgrgn); 3408 DPA_DeletePtr(ranges->hdpa, mergeindex); 3409 if (mergeindex < index) index --; 3410 } while(1); 3411 } 3412 3413 ranges_check(ranges, "after add"); 3414 return TRUE; 3415 3416 fail: 3417 ranges_check(ranges, "failed add"); 3418 return FALSE; 3419 } 3420 3421 static BOOL ranges_del(RANGES ranges, RANGE range) 3422 { 3423 RANGE *chkrgn; 3424 INT index; 3425 3426 TRACE("(%s)\n", debugrange(&range)); 3427 ranges_check(ranges, "before del"); 3428 3429 /* we don't use DPAS_SORTED here, since we need * 3430 * to find the first overlapping range */ 3431 index = DPA_Search(ranges->hdpa, &range, 0, ranges_cmp, 0, 0); 3432 while(index != -1) 3433 { 3434 chkrgn = DPA_GetPtr(ranges->hdpa, index); 3435 3436 TRACE("Matches range %s @%d\n", debugrange(chkrgn), index); 3437 3438 /* case 1: Same range */ 3439 if ( (chkrgn->upper == range.upper) && 3440 (chkrgn->lower == range.lower) ) 3441 { 3442 DPA_DeletePtr(ranges->hdpa, index); 3443 Free(chkrgn); 3444 break; 3445 } 3446 /* case 2: engulf */ 3447 else if ( (chkrgn->upper <= range.upper) && 3448 (chkrgn->lower >= range.lower) ) 3449 { 3450 DPA_DeletePtr(ranges->hdpa, index); 3451 Free(chkrgn); 3452 } 3453 /* case 3: overlap upper */ 3454 else if ( (chkrgn->upper <= range.upper) && 3455 (chkrgn->lower < range.lower) ) 3456 { 3457 chkrgn->upper = range.lower; 3458 } 3459 /* case 4: overlap lower */ 3460 else if ( (chkrgn->upper > range.upper) && 3461 (chkrgn->lower >= range.lower) ) 3462 { 3463 chkrgn->lower = range.upper; 3464 break; 3465 } 3466 /* case 5: fully internal */ 3467 else 3468 { 3469 RANGE *newrgn; 3470 3471 if (!(newrgn = Alloc(sizeof(RANGE)))) goto fail; 3472 newrgn->lower = chkrgn->lower; 3473 newrgn->upper = range.lower; 3474 chkrgn->lower = range.upper; 3475 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1) 3476 { 3477 Free(newrgn); 3478 goto fail; 3479 } 3480 break; 3481 } 3482 3483 index = DPA_Search(ranges->hdpa, &range, index, ranges_cmp, 0, 0); 3484 } 3485 3486 ranges_check(ranges, "after del"); 3487 return TRUE; 3488 3489 fail: 3490 ranges_check(ranges, "failed del"); 3491 return FALSE; 3492 } 3493 3494 /*** 3495 * DESCRIPTION: 3496 * Removes all selection ranges 3497 * 3498 * Parameters(s): 3499 * [I] infoPtr : valid pointer to the listview structure 3500 * [I] toSkip : item range to skip removing the selection 3501 * 3502 * RETURNS: 3503 * SUCCESS : TRUE 3504 * FAILURE : FALSE 3505 */ 3506 static BOOL LISTVIEW_DeselectAllSkipItems(LISTVIEW_INFO *infoPtr, RANGES toSkip) 3507 { 3508 LVITEMW lvItem; 3509 ITERATOR i; 3510 RANGES clone; 3511 3512 TRACE("()\n"); 3513 3514 lvItem.state = 0; 3515 lvItem.stateMask = LVIS_SELECTED; 3516 3517 /* need to clone the DPA because callbacks can change it */ 3518 if (!(clone = ranges_clone(infoPtr->selectionRanges))) return FALSE; 3519 iterator_rangesitems(&i, ranges_diff(clone, toSkip)); 3520 while(iterator_next(&i)) 3521 LISTVIEW_SetItemState(infoPtr, i.nItem, &lvItem); 3522 /* note that the iterator destructor will free the cloned range */ 3523 iterator_destroy(&i); 3524 3525 return TRUE; 3526 } 3527 3528 static inline BOOL LISTVIEW_DeselectAllSkipItem(LISTVIEW_INFO *infoPtr, INT nItem) 3529 { 3530 RANGES toSkip; 3531 3532 if (!(toSkip = ranges_create(1))) return FALSE; 3533 if (nItem != -1) ranges_additem(toSkip, nItem); 3534 LISTVIEW_DeselectAllSkipItems(infoPtr, toSkip); 3535 ranges_destroy(toSkip); 3536 return TRUE; 3537 } 3538 3539 static inline BOOL LISTVIEW_DeselectAll(LISTVIEW_INFO *infoPtr) 3540 { 3541 return LISTVIEW_DeselectAllSkipItem(infoPtr, -1); 3542 } 3543 3544 /*** 3545 * DESCRIPTION: 3546 * Retrieves the number of items that are marked as selected. 3547 * 3548 * PARAMETER(S): 3549 * [I] infoPtr : valid pointer to the listview structure 3550 * 3551 * RETURN: 3552 * Number of items selected. 3553 */ 3554 static INT LISTVIEW_GetSelectedCount(const LISTVIEW_INFO *infoPtr) 3555 { 3556 INT nSelectedCount = 0; 3557 3558 if (infoPtr->uCallbackMask & LVIS_SELECTED) 3559 { 3560 INT i; 3561 for (i = 0; i < infoPtr->nItemCount; i++) 3562 { 3563 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED)) 3564 nSelectedCount++; 3565 } 3566 } 3567 else 3568 nSelectedCount = ranges_itemcount(infoPtr->selectionRanges); 3569 3570 TRACE("nSelectedCount=%d\n", nSelectedCount); 3571 return nSelectedCount; 3572 } 3573 3574 /*** 3575 * DESCRIPTION: 3576 * Manages the item focus. 3577 * 3578 * PARAMETER(S): 3579 * [I] infoPtr : valid pointer to the listview structure 3580 * [I] nItem : item index 3581 * 3582 * RETURN: 3583 * TRUE : focused item changed 3584 * FALSE : focused item has NOT changed 3585 */ 3586 static inline BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem) 3587 { 3588 INT oldFocus = infoPtr->nFocusedItem; 3589 LVITEMW lvItem; 3590 3591 if (nItem == infoPtr->nFocusedItem) return FALSE; 3592 3593 lvItem.state = nItem == -1 ? 0 : LVIS_FOCUSED; 3594 lvItem.stateMask = LVIS_FOCUSED; 3595 LISTVIEW_SetItemState(infoPtr, nItem == -1 ? infoPtr->nFocusedItem : nItem, &lvItem); 3596 3597 return oldFocus != infoPtr->nFocusedItem; 3598 } 3599 3600 static INT shift_item(const LISTVIEW_INFO *infoPtr, INT nShiftItem, INT nItem, INT direction) 3601 { 3602 if (nShiftItem < nItem) return nShiftItem; 3603 3604 if (nShiftItem > nItem) return nShiftItem + direction; 3605 3606 if (direction > 0) return nShiftItem + direction; 3607 3608 return min(nShiftItem, infoPtr->nItemCount - 1); 3609 } 3610 3611 /* This function updates focus index. 3612 3613 Parameters: 3614 focus : current focus index 3615 item : index of item to be added/removed 3616 direction : add/remove flag 3617 */ 3618 static void LISTVIEW_ShiftFocus(LISTVIEW_INFO *infoPtr, INT focus, INT item, INT direction) 3619 { 3620 DWORD old_mask = infoPtr->notify_mask & NOTIFY_MASK_ITEM_CHANGE; 3621 3622 infoPtr->notify_mask &= ~NOTIFY_MASK_ITEM_CHANGE; 3623 focus = shift_item(infoPtr, focus, item, direction); 3624 if (focus != infoPtr->nFocusedItem) 3625 LISTVIEW_SetItemFocus(infoPtr, focus); 3626 infoPtr->notify_mask |= old_mask; 3627 } 3628 3629 /** 3630 * DESCRIPTION: 3631 * Updates the various indices after an item has been inserted or deleted. 3632 * 3633 * PARAMETER(S): 3634 * [I] infoPtr : valid pointer to the listview structure 3635 * [I] nItem : item index 3636 * [I] direction : Direction of shift, +1 or -1. 3637 * 3638 * RETURN: 3639 * None 3640 */ 3641 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction) 3642 { 3643 TRACE("Shifting %i, %i steps\n", nItem, direction); 3644 3645 ranges_shift(infoPtr->selectionRanges, nItem, direction, infoPtr->nItemCount); 3646 assert(abs(direction) == 1); 3647 infoPtr->nSelectionMark = shift_item(infoPtr, infoPtr->nSelectionMark, nItem, direction); 3648 3649 /* But we are not supposed to modify nHotItem! */ 3650 } 3651 3652 /** 3653 * DESCRIPTION: 3654 * Adds a block of selections. 3655 * 3656 * PARAMETER(S): 3657 * [I] infoPtr : valid pointer to the listview structure 3658 * [I] nItem : item index 3659 * 3660 * RETURN: 3661 * Whether the window is still valid. 3662 */ 3663 static BOOL LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem) 3664 { 3665 INT nFirst = min(infoPtr->nSelectionMark, nItem); 3666 INT nLast = max(infoPtr->nSelectionMark, nItem); 3667 HWND hwndSelf = infoPtr->hwndSelf; 3668 NMLVODSTATECHANGE nmlv; 3669 DWORD old_mask; 3670 LVITEMW item; 3671 INT i; 3672 3673 /* Temporarily disable change notification 3674 * If the control is LVS_OWNERDATA, we need to send 3675 * only one LVN_ODSTATECHANGED notification. 3676 * See MSDN documentation for LVN_ITEMCHANGED. 3677 */ 3678 old_mask = infoPtr->notify_mask & NOTIFY_MASK_ITEM_CHANGE; 3679 if (infoPtr->dwStyle & LVS_OWNERDATA) 3680 infoPtr->notify_mask &= ~NOTIFY_MASK_ITEM_CHANGE; 3681 3682 if (nFirst == -1) nFirst = nItem; 3683 3684 item.state = LVIS_SELECTED; 3685 item.stateMask = LVIS_SELECTED; 3686 3687 for (i = nFirst; i <= nLast; i++) 3688 LISTVIEW_SetItemState(infoPtr,i,&item); 3689 3690 ZeroMemory(&nmlv, sizeof(nmlv)); 3691 nmlv.iFrom = nFirst; 3692 nmlv.iTo = nLast; 3693 nmlv.uOldState = 0; 3694 nmlv.uNewState = item.state; 3695 3696 notify_hdr(infoPtr, LVN_ODSTATECHANGED, (LPNMHDR)&nmlv); 3697 if (!IsWindow(hwndSelf)) 3698 return FALSE; 3699 infoPtr->notify_mask |= old_mask; 3700 return TRUE; 3701 } 3702 3703 3704 /*** 3705 * DESCRIPTION: 3706 * Sets a single group selection. 3707 * 3708 * PARAMETER(S): 3709 * [I] infoPtr : valid pointer to the listview structure 3710 * [I] nItem : item index 3711 * 3712 * RETURN: 3713 * None 3714 */ 3715 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem) 3716 { 3717 RANGES selection; 3718 DWORD old_mask; 3719 LVITEMW item; 3720 ITERATOR i; 3721 3722 if (!(selection = ranges_create(100))) return; 3723 3724 item.state = LVIS_SELECTED; 3725 item.stateMask = LVIS_SELECTED; 3726 3727 if ((infoPtr->uView == LV_VIEW_LIST) || (infoPtr->uView == LV_VIEW_DETAILS)) 3728 { 3729 if (infoPtr->nSelectionMark == -1) 3730 { 3731 infoPtr->nSelectionMark = nItem; 3732 ranges_additem(selection, nItem); 3733 } 3734 else 3735 { 3736 RANGE sel; 3737 3738 sel.lower = min(infoPtr->nSelectionMark, nItem); 3739 sel.upper = max(infoPtr->nSelectionMark, nItem) + 1; 3740 ranges_add(selection, sel); 3741 } 3742 } 3743 else 3744 { 3745 RECT rcItem, rcSel, rcSelMark; 3746 POINT ptItem; 3747 3748 rcItem.left = LVIR_BOUNDS; 3749 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) { 3750 ranges_destroy (selection); 3751 return; 3752 } 3753 rcSelMark.left = LVIR_BOUNDS; 3754 if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) { 3755 ranges_destroy (selection); 3756 return; 3757 } 3758 UnionRect(&rcSel, &rcItem, &rcSelMark); 3759 iterator_frameditems(&i, infoPtr, &rcSel); 3760 while(iterator_next(&i)) 3761 { 3762 LISTVIEW_GetItemPosition(infoPtr, i.nItem, &ptItem); 3763 if (PtInRect(&rcSel, ptItem)) ranges_additem(selection, i.nItem); 3764 } 3765 iterator_destroy(&i); 3766 } 3767 3768 /* disable per item notifications on LVS_OWNERDATA style 3769 FIXME: single LVN_ODSTATECHANGED should be used */ 3770 old_mask = infoPtr->notify_mask & NOTIFY_MASK_ITEM_CHANGE; 3771 if (infoPtr->dwStyle & LVS_OWNERDATA) 3772 infoPtr->notify_mask &= ~NOTIFY_MASK_ITEM_CHANGE; 3773 3774 LISTVIEW_DeselectAllSkipItems(infoPtr, selection); 3775 3776 3777 iterator_rangesitems(&i, selection); 3778 while(iterator_next(&i)) 3779 LISTVIEW_SetItemState(infoPtr, i.nItem, &item); 3780 /* this will also destroy the selection */ 3781 iterator_destroy(&i); 3782 3783 infoPtr->notify_mask |= old_mask; 3784 LISTVIEW_SetItemFocus(infoPtr, nItem); 3785 } 3786 3787 /*** 3788 * DESCRIPTION: 3789 * Sets a single selection. 3790 * 3791 * PARAMETER(S): 3792 * [I] infoPtr : valid pointer to the listview structure 3793 * [I] nItem : item index 3794 * 3795 * RETURN: 3796 * None 3797 */ 3798 static void LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem) 3799 { 3800 LVITEMW lvItem; 3801 3802 TRACE("nItem=%d\n", nItem); 3803 3804 LISTVIEW_DeselectAllSkipItem(infoPtr, nItem); 3805 3806 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED; 3807 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED; 3808 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem); 3809 3810 infoPtr->nSelectionMark = nItem; 3811 } 3812 3813 /*** 3814 * DESCRIPTION: 3815 * Set selection(s) with keyboard. 3816 * 3817 * PARAMETER(S): 3818 * [I] infoPtr : valid pointer to the listview structure 3819 * [I] nItem : item index 3820 * [I] space : VK_SPACE code sent 3821 * 3822 * RETURN: 3823 * SUCCESS : TRUE (needs to be repainted) 3824 * FAILURE : FALSE (nothing has changed) 3825 */ 3826 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem, BOOL space) 3827 { 3828 /* FIXME: pass in the state */ 3829 WORD wShift = GetKeyState(VK_SHIFT) & 0x8000; 3830 WORD wCtrl = GetKeyState(VK_CONTROL) & 0x8000; 3831 BOOL bResult = FALSE; 3832 3833 TRACE("nItem=%d, wShift=%d, wCtrl=%d\n", nItem, wShift, wCtrl); 3834 if ((nItem >= 0) && (nItem < infoPtr->nItemCount)) 3835 { 3836 bResult = TRUE; 3837 3838 if (infoPtr->dwStyle & LVS_SINGLESEL || (wShift == 0 && wCtrl == 0)) 3839 LISTVIEW_SetSelection(infoPtr, nItem); 3840 else 3841 { 3842 if (wShift) 3843 LISTVIEW_SetGroupSelection(infoPtr, nItem); 3844 else if (wCtrl) 3845 { 3846 LVITEMW lvItem; 3847 lvItem.state = ~LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED); 3848 lvItem.stateMask = LVIS_SELECTED; 3849 if (space) 3850 { 3851 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem); 3852 if (lvItem.state & LVIS_SELECTED) 3853 infoPtr->nSelectionMark = nItem; 3854 } 3855 bResult = LISTVIEW_SetItemFocus(infoPtr, nItem); 3856 } 3857 } 3858 LISTVIEW_EnsureVisible(infoPtr, nItem, FALSE); 3859 } 3860 3861 UpdateWindow(infoPtr->hwndSelf); /* update client area */ 3862 return bResult; 3863 } 3864 3865 static BOOL LISTVIEW_GetItemAtPt(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, POINT pt) 3866 { 3867 LVHITTESTINFO lvHitTestInfo; 3868 3869 ZeroMemory(&lvHitTestInfo, sizeof(lvHitTestInfo)); 3870 lvHitTestInfo.pt.x = pt.x; 3871 lvHitTestInfo.pt.y = pt.y; 3872 3873 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE); 3874 3875 lpLVItem->mask = LVIF_PARAM; 3876 lpLVItem->iItem = lvHitTestInfo.iItem; 3877 lpLVItem->iSubItem = 0; 3878 3879 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE); 3880 } 3881 3882 static inline BOOL LISTVIEW_IsHotTracking(const LISTVIEW_INFO *infoPtr) 3883 { 3884 return ((infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) || 3885 (infoPtr->dwLvExStyle & LVS_EX_ONECLICKACTIVATE) || 3886 (infoPtr->dwLvExStyle & LVS_EX_TWOCLICKACTIVATE)); 3887 } 3888 3889 /*** 3890 * DESCRIPTION: 3891 * Called when the mouse is being actively tracked and has hovered for a specified 3892 * amount of time 3893 * 3894 * PARAMETER(S): 3895 * [I] infoPtr : valid pointer to the listview structure 3896 * [I] fwKeys : key indicator 3897 * [I] x,y : mouse position 3898 * 3899 * RETURN: 3900 * 0 if the message was processed, non-zero if there was an error 3901 * 3902 * INFO: 3903 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains 3904 * over the item for a certain period of time. 3905 * 3906 */ 3907 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, INT x, INT y) 3908 { 3909 NMHDR hdr; 3910 3911 if (notify_hdr(infoPtr, NM_HOVER, &hdr)) return 0; 3912 3913 if (LISTVIEW_IsHotTracking(infoPtr)) 3914 { 3915 LVITEMW item; 3916 POINT pt; 3917 3918 pt.x = x; 3919 pt.y = y; 3920 3921 if (LISTVIEW_GetItemAtPt(infoPtr, &item, pt)) 3922 LISTVIEW_SetSelection(infoPtr, item.iItem); 3923 3924 SetFocus(infoPtr->hwndSelf); 3925 } 3926 3927 return 0; 3928 } 3929 3930 #define SCROLL_LEFT 0x1 3931 #define SCROLL_RIGHT 0x2 3932 #define SCROLL_UP 0x4 3933 #define SCROLL_DOWN 0x8 3934 3935 /*** 3936 * DESCRIPTION: 3937 * Utility routine to draw and highlight items within a marquee selection rectangle. 3938 * 3939 * PARAMETER(S): 3940 * [I] infoPtr : valid pointer to the listview structure 3941 * [I] coords_orig : original co-ordinates of the cursor 3942 * [I] coords_offs : offsetted coordinates of the cursor 3943 * [I] offset : offset amount 3944 * [I] scroll : Bitmask of which directions we should scroll, if at all 3945 * 3946 * RETURN: 3947 * None. 3948 */ 3949 static void LISTVIEW_MarqueeHighlight(LISTVIEW_INFO *infoPtr, const POINT *coords_orig, 3950 INT scroll) 3951 { 3952 BOOL controlDown = FALSE; 3953 LVITEMW item; 3954 ITERATOR old_elems, new_elems; 3955 RECT rect; 3956 POINT coords_offs, offset; 3957 3958 /* Ensure coordinates are within client bounds */ 3959 coords_offs.x = max(min(coords_orig->x, infoPtr->rcList.right), 0); 3960 coords_offs.y = max(min(coords_orig->y, infoPtr->rcList.bottom), 0); 3961 3962 /* Get offset */ 3963 LISTVIEW_GetOrigin(infoPtr, &offset); 3964 3965 /* Offset coordinates by the appropriate amount */ 3966 coords_offs.x -= offset.x; 3967 coords_offs.y -= offset.y; 3968 3969 if (coords_offs.x > infoPtr->marqueeOrigin.x) 3970 { 3971 rect.left = infoPtr->marqueeOrigin.x; 3972 rect.right = coords_offs.x; 3973 } 3974 else 3975 { 3976 rect.left = coords_offs.x; 3977 rect.right = infoPtr->marqueeOrigin.x; 3978 } 3979 3980 if (coords_offs.y > infoPtr->marqueeOrigin.y) 3981 { 3982 rect.top = infoPtr->marqueeOrigin.y; 3983 rect.bottom = coords_offs.y; 3984 } 3985 else 3986 { 3987 rect.top = coords_offs.y; 3988 rect.bottom = infoPtr->marqueeOrigin.y; 3989 } 3990 3991 /* Cancel out the old marquee rectangle and draw the new one */ 3992 LISTVIEW_InvalidateRect(infoPtr, &infoPtr->marqueeDrawRect); 3993 3994 /* Scroll by the appropriate distance if applicable - speed up scrolling as 3995 the cursor is further away */ 3996 3997 if ((scroll & SCROLL_LEFT) && (coords_orig->x <= 0)) 3998 LISTVIEW_Scroll(infoPtr, coords_orig->x, 0); 3999 4000 if ((scroll & SCROLL_RIGHT) && (coords_orig->x >= infoPtr->rcList.right)) 4001 LISTVIEW_Scroll(infoPtr, (coords_orig->x - infoPtr->rcList.right), 0); 4002 4003 if ((scroll & SCROLL_UP) && (coords_orig->y <= 0)) 4004 LISTVIEW_Scroll(infoPtr, 0, coords_orig->y); 4005 4006 if ((scroll & SCROLL_DOWN) && (coords_orig->y >= infoPtr->rcList.bottom)) 4007 LISTVIEW_Scroll(infoPtr, 0, (coords_orig->y - infoPtr->rcList.bottom)); 4008 4009 iterator_frameditems_absolute(&old_elems, infoPtr, &infoPtr->marqueeRect); 4010 4011 infoPtr->marqueeRect = rect; 4012 infoPtr->marqueeDrawRect = rect; 4013 OffsetRect(&infoPtr->marqueeDrawRect, offset.x, offset.y); 4014 4015 iterator_frameditems_absolute(&new_elems, infoPtr, &infoPtr->marqueeRect); 4016 iterator_remove_common_items(&old_elems, &new_elems); 4017 4018 /* Iterate over no longer selected items */ 4019 while (iterator_next(&old_elems)) 4020 { 4021 if (old_elems.nItem > -1) 4022 { 4023 if (LISTVIEW_GetItemState(infoPtr, old_elems.nItem, LVIS_SELECTED) == LVIS_SELECTED) 4024 item.state = 0; 4025 else 4026 item.state = LVIS_SELECTED; 4027 4028 item.stateMask = LVIS_SELECTED; 4029 4030 LISTVIEW_SetItemState(infoPtr, old_elems.nItem, &item); 4031 } 4032 } 4033 iterator_destroy(&old_elems); 4034 4035 4036 /* Iterate over newly selected items */ 4037 if (GetKeyState(VK_CONTROL) & 0x8000) 4038 controlDown = TRUE; 4039 4040 while (iterator_next(&new_elems)) 4041 { 4042 if (new_elems.nItem > -1) 4043 { 4044 /* If CTRL is pressed, invert. If not, always select the item. */ 4045 if ((controlDown) && (LISTVIEW_GetItemState(infoPtr, new_elems.nItem, LVIS_SELECTED))) 4046 item.state = 0; 4047 else 4048 item.state = LVIS_SELECTED; 4049 4050 item.stateMask = LVIS_SELECTED; 4051 4052 LISTVIEW_SetItemState(infoPtr, new_elems.nItem, &item); 4053 } 4054 } 4055 iterator_destroy(&new_elems); 4056 4057 LISTVIEW_InvalidateRect(infoPtr, &infoPtr->marqueeDrawRect); 4058 } 4059 4060 /*** 4061 * DESCRIPTION: 4062 * Called when we are in a marquee selection that involves scrolling the listview (ie, 4063 * the cursor is outside the bounds of the client area). This is a TIMERPROC. 4064 * 4065 * PARAMETER(S): 4066 * [I] hwnd : Handle to the listview 4067 * [I] uMsg : WM_TIMER (ignored) 4068 * [I] idEvent : The timer ID interpreted as a pointer to a LISTVIEW_INFO struct 4069 * [I] dwTimer : The elapsed time (ignored) 4070 * 4071 * RETURN: 4072 * None. 4073 */ 4074 static VOID CALLBACK LISTVIEW_ScrollTimer(HWND hWnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime) 4075 { 4076 LISTVIEW_INFO *infoPtr; 4077 SCROLLINFO scrollInfo; 4078 POINT coords; 4079 INT scroll = 0; 4080 4081 infoPtr = (LISTVIEW_INFO *) idEvent; 4082 4083 if (!infoPtr) 4084 return; 4085 4086 /* Get the current cursor position and convert to client coordinates */ 4087 GetCursorPos(&coords); 4088 ScreenToClient(hWnd, &coords); 4089 4090 scrollInfo.cbSize = sizeof(SCROLLINFO); 4091 scrollInfo.fMask = SIF_ALL; 4092 4093 /* Work out in which directions we can scroll */ 4094 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) 4095 { 4096 if (scrollInfo.nPos != scrollInfo.nMin) 4097 scroll |= SCROLL_UP; 4098 4099 if (((scrollInfo.nPage + scrollInfo.nPos) - 1) != scrollInfo.nMax) 4100 scroll |= SCROLL_DOWN; 4101 } 4102 4103 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) 4104 { 4105 if (scrollInfo.nPos != scrollInfo.nMin) 4106 scroll |= SCROLL_LEFT; 4107 4108 if (((scrollInfo.nPage + scrollInfo.nPos) - 1) != scrollInfo.nMax) 4109 scroll |= SCROLL_RIGHT; 4110 } 4111 4112 if (((coords.x <= 0) && (scroll & SCROLL_LEFT)) || 4113 ((coords.y <= 0) && (scroll & SCROLL_UP)) || 4114 ((coords.x >= infoPtr->rcList.right) && (scroll & SCROLL_RIGHT)) || 4115 ((coords.y >= infoPtr->rcList.bottom) && (scroll & SCROLL_DOWN))) 4116 { 4117 LISTVIEW_MarqueeHighlight(infoPtr, &coords, scroll); 4118 } 4119 } 4120 4121 /*** 4122 * DESCRIPTION: 4123 * Called whenever WM_MOUSEMOVE is received. 4124 * 4125 * PARAMETER(S): 4126 * [I] infoPtr : valid pointer to the listview structure 4127 * [I] fwKeys : key indicator 4128 * [I] x,y : mouse position 4129 * 4130 * RETURN: 4131 * 0 if the message is processed, non-zero if there was an error 4132 */ 4133 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, INT x, INT y) 4134 { 4135 LVHITTESTINFO ht; 4136 RECT rect; 4137 POINT pt; 4138 4139 pt.x = x; 4140 pt.y = y; 4141 4142 if (!(fwKeys & MK_LBUTTON)) 4143 infoPtr->bLButtonDown = FALSE; 4144 4145 if (infoPtr->bLButtonDown) 4146 { 4147 rect.left = rect.right = infoPtr->ptClickPos.x; 4148 rect.top = rect.bottom = infoPtr->ptClickPos.y; 4149 4150 InflateRect(&rect, GetSystemMetrics(SM_CXDRAG), GetSystemMetrics(SM_CYDRAG)); 4151 4152 if (infoPtr->bMarqueeSelect) 4153 { 4154 /* Enable the timer if we're going outside our bounds, in case the user doesn't 4155 move the mouse again */ 4156 4157 if ((x <= 0) || (y <= 0) || (x >= infoPtr->rcList.right) || 4158 (y >= infoPtr->rcList.bottom)) 4159 { 4160 if (!infoPtr->bScrolling) 4161 { 4162 infoPtr->bScrolling = TRUE; 4163 SetTimer(infoPtr->hwndSelf, (UINT_PTR) infoPtr, 1, LISTVIEW_ScrollTimer); 4164 } 4165 } 4166 else 4167 { 4168 infoPtr->bScrolling = FALSE; 4169 KillTimer(infoPtr->hwndSelf, (UINT_PTR) infoPtr); 4170 } 4171 4172 LISTVIEW_MarqueeHighlight(infoPtr, &pt, 0); 4173 return 0; 4174 } 4175 4176 ht.pt = pt; 4177 LISTVIEW_HitTest(infoPtr, &ht, TRUE, TRUE); 4178 4179 /* reset item marker */ 4180 if (infoPtr->nLButtonDownItem != ht.iItem) 4181 infoPtr->nLButtonDownItem = -1; 4182 4183 if (!PtInRect(&rect, pt)) 4184 { 4185 /* this path covers the following: 4186 1. WM_LBUTTONDOWN over selected item (sets focus on it) 4187 2. change focus with keys 4188 3. move mouse over item from step 1 selects it and moves focus on it */ 4189 if (infoPtr->nLButtonDownItem != -1 && 4190 !LISTVIEW_GetItemState(infoPtr, infoPtr->nLButtonDownItem, LVIS_SELECTED)) 4191 { 4192 LVITEMW lvItem; 4193 4194 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED; 4195 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED; 4196 4197 LISTVIEW_SetItemState(infoPtr, infoPtr->nLButtonDownItem, &lvItem); 4198 infoPtr->nLButtonDownItem = -1; 4199 } 4200 4201 if (!infoPtr->bDragging) 4202 { 4203 ht.pt = infoPtr->ptClickPos; 4204 LISTVIEW_HitTest(infoPtr, &ht, TRUE, TRUE); 4205 4206 /* If the click is outside the range of an item, begin a 4207 highlight. If not, begin an item drag. */ 4208 if (ht.iItem == -1) 4209 { 4210 NMHDR hdr; 4211 4212 /* If we're allowing multiple selections, send notification. 4213 If return value is non-zero, cancel. */ 4214 if (!(infoPtr->dwStyle & LVS_SINGLESEL) && (notify_hdr(infoPtr, LVN_MARQUEEBEGIN, &hdr) == 0)) 4215 { 4216 /* Store the absolute coordinates of the click */ 4217 POINT offset; 4218 LISTVIEW_GetOrigin(infoPtr, &offset); 4219 4220 infoPtr->marqueeOrigin.x = infoPtr->ptClickPos.x - offset.x; 4221 infoPtr->marqueeOrigin.y = infoPtr->ptClickPos.y - offset.y; 4222 4223 /* Begin selection and capture mouse */ 4224 infoPtr->bMarqueeSelect = TRUE; 4225 infoPtr->marqueeRect = rect; 4226 SetCapture(infoPtr->hwndSelf); 4227 } 4228 } 4229 else 4230 { 4231 NMLISTVIEW nmlv; 4232 4233 ZeroMemory(&nmlv, sizeof(nmlv)); 4234 nmlv.iItem = ht.iItem; 4235 nmlv.ptAction = infoPtr->ptClickPos; 4236 4237 notify_listview(infoPtr, LVN_BEGINDRAG, &nmlv); 4238 infoPtr->bDragging = TRUE; 4239 } 4240 } 4241 4242 return 0; 4243 } 4244 } 4245 4246 /* see if we are supposed to be tracking mouse hovering */ 4247 if (LISTVIEW_IsHotTracking(infoPtr)) { 4248 TRACKMOUSEEVENT trackinfo; 4249 DWORD flags; 4250 4251 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT); 4252 trackinfo.dwFlags = TME_QUERY; 4253 4254 /* see if we are already tracking this hwnd */ 4255 _TrackMouseEvent(&trackinfo); 4256 4257 flags = TME_LEAVE; 4258 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) 4259 flags |= TME_HOVER; 4260 4261 if((trackinfo.dwFlags & flags) != flags || trackinfo.hwndTrack != infoPtr->hwndSelf) { 4262 trackinfo.dwFlags = flags; 4263 trackinfo.dwHoverTime = infoPtr->dwHoverTime; 4264 trackinfo.hwndTrack = infoPtr->hwndSelf; 4265 4266 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */ 4267 _TrackMouseEvent(&trackinfo); 4268 } 4269 } 4270 4271 return 0; 4272 } 4273 4274 4275 /*** 4276 * Tests whether the item is assignable to a list with style lStyle 4277 */ 4278 static inline BOOL is_assignable_item(const LVITEMW *lpLVItem, LONG lStyle) 4279 { 4280 if ( (lpLVItem->mask & LVIF_TEXT) && 4281 (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) && 4282 (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE; 4283 4284 return TRUE; 4285 } 4286 4287 4288 /*** 4289 * DESCRIPTION: 4290 * Helper for LISTVIEW_SetItemT and LISTVIEW_InsertItemT: sets item attributes. 4291 * 4292 * PARAMETER(S): 4293 * [I] infoPtr : valid pointer to the listview structure 4294 * [I] lpLVItem : valid pointer to new item attributes 4295 * [I] isNew : the item being set is being inserted 4296 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI 4297 * [O] bChanged : will be set to TRUE if the item really changed 4298 * 4299 * RETURN: 4300 * SUCCESS : TRUE 4301 * FAILURE : FALSE 4302 */ 4303 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isNew, BOOL isW, BOOL *bChanged) 4304 { 4305 ITEM_INFO *lpItem; 4306 NMLISTVIEW nmlv; 4307 UINT uChanged = 0; 4308 LVITEMW item; 4309 /* stateMask is ignored for LVM_INSERTITEM */ 4310 UINT stateMask = isNew ? ~0 : lpLVItem->stateMask; 4311 4312 TRACE("()\n"); 4313 4314 assert(lpLVItem->iItem >= 0 && lpLVItem->iItem < infoPtr->nItemCount); 4315 4316 if (lpLVItem->mask == 0) return TRUE; 4317 4318 if (infoPtr->dwStyle & LVS_OWNERDATA) 4319 { 4320 /* a virtual listview only stores selection and focus */ 4321 if (lpLVItem->mask & ~LVIF_STATE) 4322 return FALSE; 4323 lpItem = NULL; 4324 } 4325 else 4326 { 4327 HDPA hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem); 4328 lpItem = DPA_GetPtr(hdpaSubItems, 0); 4329 assert (lpItem); 4330 } 4331 4332 /* we need to get the lParam and state of the item */ 4333 item.iItem = lpLVItem->iItem; 4334 item.iSubItem = lpLVItem->iSubItem; 4335 item.mask = LVIF_STATE | LVIF_PARAM; 4336 item.stateMask = (infoPtr->dwStyle & LVS_OWNERDATA) ? LVIS_FOCUSED | LVIS_SELECTED : ~0; 4337 4338 item.state = 0; 4339 item.lParam = 0; 4340 if (!isNew && !LISTVIEW_GetItemW(infoPtr, &item)) return FALSE; 4341 4342 TRACE("oldState=%x, newState=%x\n", item.state, lpLVItem->state); 4343 /* determine what fields will change */ 4344 if ((lpLVItem->mask & LVIF_STATE) && ((item.state ^ lpLVItem->state) & stateMask & ~infoPtr->uCallbackMask)) 4345 uChanged |= LVIF_STATE; 4346 4347 if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage)) 4348 uChanged |= LVIF_IMAGE; 4349 4350 if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam)) 4351 uChanged |= LVIF_PARAM; 4352 4353 if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent)) 4354 uChanged |= LVIF_INDENT; 4355 4356 if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW)) 4357 uChanged |= LVIF_TEXT; 4358 4359 TRACE("change mask=0x%x\n", uChanged); 4360 4361 memset(&nmlv, 0, sizeof(NMLISTVIEW)); 4362 nmlv.iItem = lpLVItem->iItem; 4363 if (lpLVItem->mask & LVIF_STATE) 4364 { 4365 nmlv.uNewState = (item.state & ~stateMask) | (lpLVItem->state & stateMask); 4366 nmlv.uOldState = item.state; 4367 } 4368 nmlv.uChanged = uChanged ? uChanged : lpLVItem->mask; 4369 nmlv.lParam = item.lParam; 4370 4371 /* Send LVN_ITEMCHANGING notification, if the item is not being inserted 4372 and we are _NOT_ virtual (LVS_OWNERDATA), and change notifications 4373 are enabled. Even nothing really changed we still need to send this, 4374 in this case uChanged mask is just set to passed item mask. */ 4375 if (lpItem && !isNew && (infoPtr->notify_mask & NOTIFY_MASK_ITEM_CHANGE)) 4376 { 4377 HWND hwndSelf = infoPtr->hwndSelf; 4378 4379 if (notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv)) 4380 return FALSE; 4381 if (!IsWindow(hwndSelf)) 4382 return FALSE; 4383 } 4384 4385 /* When item is inserted we need to shift existing focus index if new item has lower index. */ 4386 if (isNew && (stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED) && 4387 /* this means we won't hit a focus change path later */ 4388 ((uChanged & LVIF_STATE) == 0 || (!(lpLVItem->state & LVIS_FOCUSED) && (infoPtr->nFocusedItem != lpLVItem->iItem)))) 4389 { 4390 if (infoPtr->nFocusedItem != -1 && (lpLVItem->iItem <= infoPtr->nFocusedItem)) 4391 infoPtr->nFocusedItem++; 4392 } 4393 4394 if (!uChanged) return TRUE; 4395 *bChanged = TRUE; 4396 4397 /* copy information */ 4398 if (lpLVItem->mask & LVIF_TEXT) 4399 textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW); 4400 4401 if (lpLVItem->mask & LVIF_IMAGE) 4402 lpItem->hdr.iImage = lpLVItem->iImage; 4403 4404 if (lpLVItem->mask & LVIF_PARAM) 4405 lpItem->lParam = lpLVItem->lParam; 4406 4407 if (lpLVItem->mask & LVIF_INDENT) 4408 lpItem->iIndent = lpLVItem->iIndent; 4409 4410 if (uChanged & LVIF_STATE) 4411 { 4412 if (lpItem && (stateMask & ~infoPtr->uCallbackMask)) 4413 { 4414 lpItem->state &= ~stateMask; 4415 lpItem->state |= (lpLVItem->state & stateMask); 4416 } 4417 if (lpLVItem->state & stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED) 4418 { 4419 if (infoPtr->dwStyle & LVS_SINGLESEL) LISTVIEW_DeselectAllSkipItem(infoPtr, lpLVItem->iItem); 4420 ranges_additem(infoPtr->selectionRanges, lpLVItem->iItem); 4421 } 4422 else if (stateMask & LVIS_SELECTED) 4423 { 4424 ranges_delitem(infoPtr->selectionRanges, lpLVItem->iItem); 4425 } 4426 /* If we are asked to change focus, and we manage it, do it. 4427 It's important to have all new item data stored at this point, 4428 because changing existing focus could result in a redrawing operation, 4429 which in turn could ask for disp data, application should see all data 4430 for inserted item when processing LVN_GETDISPINFO. 4431 4432 The way this works application will see nested item change notifications - 4433 changed item notifications interrupted by ones from item losing focus. */ 4434 if (stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED) 4435 { 4436 if (lpLVItem->state & LVIS_FOCUSED) 4437 { 4438 /* update selection mark */ 4439 if (infoPtr->nFocusedItem == -1 && infoPtr->nSelectionMark == -1) 4440 infoPtr->nSelectionMark = lpLVItem->iItem; 4441 4442 if (infoPtr->nFocusedItem != -1) 4443 { 4444 /* remove current focus */ 4445 item.mask = LVIF_STATE; 4446 item.state = 0; 4447 item.stateMask = LVIS_FOCUSED; 4448 4449 /* recurse with redrawing an item */ 4450 LISTVIEW_SetItemState(infoPtr, infoPtr->nFocusedItem, &item); 4451 } 4452 4453 infoPtr->nFocusedItem = lpLVItem->iItem; 4454 LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, infoPtr->uView == LV_VIEW_LIST); 4455 } 4456 else if (infoPtr->nFocusedItem == lpLVItem->iItem) 4457 { 4458 infoPtr->nFocusedItem = -1; 4459 } 4460 } 4461 } 4462 4463 /* if we're inserting the item, we're done */ 4464 if (isNew) return TRUE; 4465 4466 /* send LVN_ITEMCHANGED notification */ 4467 if (lpLVItem->mask & LVIF_PARAM) nmlv.lParam = lpLVItem->lParam; 4468 if (infoPtr->notify_mask & NOTIFY_MASK_ITEM_CHANGE) 4469 notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv); 4470 4471 return TRUE; 4472 } 4473 4474 /*** 4475 * DESCRIPTION: 4476 * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes. 4477 * 4478 * PARAMETER(S): 4479 * [I] infoPtr : valid pointer to the listview structure 4480 * [I] lpLVItem : valid pointer to new subitem attributes 4481 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI 4482 * [O] bChanged : will be set to TRUE if the item really changed 4483 * 4484 * RETURN: 4485 * SUCCESS : TRUE 4486 * FAILURE : FALSE 4487 */ 4488 static BOOL set_sub_item(const LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW, BOOL *bChanged) 4489 { 4490 HDPA hdpaSubItems; 4491 SUBITEM_INFO *lpSubItem; 4492 4493 /* we do not support subitems for virtual listviews */ 4494 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE; 4495 4496 /* set subitem only if column is present */ 4497 if (lpLVItem->iSubItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE; 4498 4499 /* First do some sanity checks */ 4500 /* The LVIF_STATE flag is valid for subitems, but does not appear to be 4501 particularly useful. We currently do not actually do anything with 4502 the flag on subitems. 4503 */ 4504 if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE | LVIF_STATE | LVIF_DI_SETITEM)) return FALSE; 4505 if (!(lpLVItem->mask & (LVIF_TEXT | LVIF_IMAGE | LVIF_STATE))) return TRUE; 4506 4507 /* get the subitem structure, and create it if not there */ 4508 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem); 4509 assert (hdpaSubItems); 4510 4511 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem); 4512 if (!lpSubItem) 4513 { 4514 SUBITEM_INFO *tmpSubItem; 4515 INT i; 4516 4517 lpSubItem = Alloc(sizeof(SUBITEM_INFO)); 4518 if (!lpSubItem) return FALSE; 4519 /* we could binary search here, if need be...*/ 4520 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++) 4521 { 4522 tmpSubItem = DPA_GetPtr(hdpaSubItems, i); 4523 if (tmpSubItem->iSubItem > lpLVItem->iSubItem) break; 4524 } 4525 if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1) 4526 { 4527 Free(lpSubItem); 4528 return FALSE; 4529 } 4530 lpSubItem->iSubItem = lpLVItem->iSubItem; 4531 lpSubItem->hdr.iImage = I_IMAGECALLBACK; 4532 *bChanged = TRUE; 4533 } 4534 4535 if ((lpLVItem->mask & LVIF_IMAGE) && (lpSubItem->hdr.iImage != lpLVItem->iImage)) 4536 { 4537 lpSubItem->hdr.iImage = lpLVItem->iImage; 4538 *bChanged = TRUE; 4539 } 4540 4541 if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpSubItem->hdr.pszText, lpLVItem->pszText, isW)) 4542 { 4543 textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW); 4544 *bChanged = TRUE; 4545 } 4546 4547 return TRUE; 4548 } 4549 4550 /*** 4551 * DESCRIPTION: 4552 * Sets item attributes. 4553 * 4554 * PARAMETER(S): 4555 * [I] infoPtr : valid pointer to the listview structure 4556 * [I] lpLVItem : new item attributes 4557 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI 4558 * 4559 * RETURN: 4560 * SUCCESS : TRUE 4561 * FAILURE : FALSE 4562 */ 4563 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, LVITEMW *lpLVItem, BOOL isW) 4564 { 4565 HWND hwndSelf = infoPtr->hwndSelf; 4566 LPWSTR pszText = NULL; 4567 BOOL bResult, bChanged = FALSE; 4568 RECT oldItemArea; 4569 4570 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW); 4571 4572 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount) 4573 return FALSE; 4574 4575 /* Store old item area */ 4576 LISTVIEW_GetItemBox(infoPtr, lpLVItem->iItem, &oldItemArea); 4577 4578 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */ 4579 if ((lpLVItem->mask & LVIF_TEXT) && is_text(lpLVItem->pszText)) 4580 { 4581 pszText = lpLVItem->pszText; 4582 lpLVItem->pszText = textdupTtoW(lpLVItem->pszText, isW); 4583 } 4584 4585 /* actually set the fields */ 4586 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE; 4587 4588 if (lpLVItem->iSubItem) 4589 bResult = set_sub_item(infoPtr, lpLVItem, TRUE, &bChanged); 4590 else 4591 bResult = set_main_item(infoPtr, lpLVItem, FALSE, TRUE, &bChanged); 4592 if (!IsWindow(hwndSelf)) 4593 return FALSE; 4594 4595 /* redraw item, if necessary */ 4596 if (bChanged && !infoPtr->bIsDrawing) 4597 { 4598 /* this little optimization eliminates some nasty flicker */ 4599 if ( infoPtr->uView == LV_VIEW_DETAILS && !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && 4600 !(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && 4601 lpLVItem->iSubItem > 0 && lpLVItem->iSubItem <= DPA_GetPtrCount(infoPtr->hdpaColumns) ) 4602 LISTVIEW_InvalidateSubItem(infoPtr, lpLVItem->iItem, lpLVItem->iSubItem); 4603 else 4604 { 4605 LISTVIEW_InvalidateRect(infoPtr, &oldItemArea); 4606 LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem); 4607 } 4608 } 4609 /* restore text */ 4610 if (pszText) 4611 { 4612 textfreeT(lpLVItem->pszText, isW); 4613 lpLVItem->pszText = pszText; 4614 } 4615 4616 return bResult; 4617 } 4618 4619 /*** 4620 * DESCRIPTION: 4621 * Retrieves the index of the item at coordinate (0, 0) of the client area. 4622 * 4623 * PARAMETER(S): 4624 * [I] infoPtr : valid pointer to the listview structure 4625 * 4626 * RETURN: 4627 * item index 4628 */ 4629 static INT LISTVIEW_GetTopIndex(const LISTVIEW_INFO *infoPtr) 4630 { 4631 INT nItem = 0; 4632 SCROLLINFO scrollInfo; 4633 4634 scrollInfo.cbSize = sizeof(SCROLLINFO); 4635 scrollInfo.fMask = SIF_POS; 4636 4637 if (infoPtr->uView == LV_VIEW_LIST) 4638 { 4639 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) 4640 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr); 4641 } 4642 else if (infoPtr->uView == LV_VIEW_DETAILS) 4643 { 4644 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) 4645 nItem = scrollInfo.nPos; 4646 } 4647 else 4648 { 4649 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) 4650 nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight); 4651 } 4652 4653 TRACE("nItem=%d\n", nItem); 4654 4655 return nItem; 4656 } 4657 4658 4659 /*** 4660 * DESCRIPTION: 4661 * Erases the background of the given rectangle 4662 * 4663 * PARAMETER(S): 4664 * [I] infoPtr : valid pointer to the listview structure 4665 * [I] hdc : device context handle 4666 * [I] lprcBox : clipping rectangle 4667 * 4668 * RETURN: 4669 * Success: TRUE 4670 * Failure: FALSE 4671 */ 4672 static inline BOOL LISTVIEW_FillBkgnd(const LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *lprcBox) 4673 { 4674 if (!infoPtr->hBkBrush) return FALSE; 4675 4676 TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc, wine_dbgstr_rect(lprcBox), infoPtr->hBkBrush); 4677 4678 return FillRect(hdc, lprcBox, infoPtr->hBkBrush); 4679 } 4680 4681 /* Draw main item or subitem */ 4682 static void LISTVIEW_DrawItemPart(LISTVIEW_INFO *infoPtr, LVITEMW *item, const NMLVCUSTOMDRAW *nmlvcd, const POINT *pos) 4683 { 4684 RECT rcSelect, rcLabel, rcBox, rcStateIcon, rcIcon; 4685 const RECT *background; 4686 HIMAGELIST himl; 4687 UINT format; 4688 RECT *focus; 4689 4690 /* now check if we need to update the focus rectangle */ 4691 focus = infoPtr->bFocus && (item->state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0; 4692 if (!focus) item->state &= ~LVIS_FOCUSED; 4693 4694 LISTVIEW_GetItemMetrics(infoPtr, item, &rcBox, &rcSelect, &rcIcon, &rcStateIcon, &rcLabel); 4695 OffsetRect(&rcBox, pos->x, pos->y); 4696 OffsetRect(&rcSelect, pos->x, pos->y); 4697 OffsetRect(&rcIcon, pos->x, pos->y); 4698 OffsetRect(&rcStateIcon, pos->x, pos->y); 4699 OffsetRect(&rcLabel, pos->x, pos->y); 4700 TRACE("%d: box=%s, select=%s, icon=%s. label=%s\n", item->iSubItem, 4701 wine_dbgstr_rect(&rcBox), wine_dbgstr_rect(&rcSelect), 4702 wine_dbgstr_rect(&rcIcon), wine_dbgstr_rect(&rcLabel)); 4703 4704 /* FIXME: temporary hack */ 4705 rcSelect.left = rcLabel.left; 4706 4707 if (infoPtr->uView == LV_VIEW_DETAILS && item->iSubItem == 0) 4708 { 4709 if (!(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)) 4710 OffsetRect(&rcSelect, LISTVIEW_GetColumnInfo(infoPtr, 0)->rcHeader.left, 0); 4711 OffsetRect(&rcIcon, LISTVIEW_GetColumnInfo(infoPtr, 0)->rcHeader.left, 0); 4712 OffsetRect(&rcStateIcon, LISTVIEW_GetColumnInfo(infoPtr, 0)->rcHeader.left, 0); 4713 OffsetRect(&rcLabel, LISTVIEW_GetColumnInfo(infoPtr, 0)->rcHeader.left, 0); 4714 } 4715 4716 /* in icon mode, the label rect is really what we want to draw the 4717 * background for */ 4718 /* in detail mode, we want to paint background for label rect when 4719 * item is not selected or listview has full row select; otherwise paint 4720 * background for text only */ 4721 if ( infoPtr->uView == LV_VIEW_ICON || 4722 (infoPtr->uView == LV_VIEW_DETAILS && (!(item->state & LVIS_SELECTED) || 4723 (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)))) 4724 background = &rcLabel; 4725 else 4726 background = &rcSelect; 4727 4728 if (nmlvcd->clrTextBk != CLR_NONE) 4729 ExtTextOutW(nmlvcd->nmcd.hdc, background->left, background->top, ETO_OPAQUE, background, NULL, 0, NULL); 4730 4731 if (item->state & LVIS_FOCUSED) 4732 { 4733 if (infoPtr->uView == LV_VIEW_DETAILS) 4734 { 4735 if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) 4736 { 4737 /* we have to update left focus bound too if item isn't in leftmost column 4738 and reduce right box bound */ 4739 if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0) 4740 { 4741 INT leftmost; 4742 4743 if ((leftmost = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX, 0, 0))) 4744 { 4745 INT Originx = pos->x - LISTVIEW_GetColumnInfo(infoPtr, leftmost)->rcHeader.left; 4746 INT rightmost = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX, 4747 DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, 0); 4748 4749 rcBox.right = LISTVIEW_GetColumnInfo(infoPtr, rightmost)->rcHeader.right + Originx; 4750 rcSelect.left = LISTVIEW_GetColumnInfo(infoPtr, leftmost)->rcHeader.left + Originx; 4751 } 4752 } 4753 rcSelect.right = rcBox.right; 4754 } 4755 infoPtr->rcFocus = rcSelect; 4756 } 4757 else 4758 infoPtr->rcFocus = rcLabel; 4759 } 4760 4761 /* state icons */ 4762 if (infoPtr->himlState && STATEIMAGEINDEX(item->state) && (item->iSubItem == 0)) 4763 { 4764 UINT stateimage = STATEIMAGEINDEX(item->state); 4765 if (stateimage) 4766 { 4767 TRACE("stateimage=%d\n", stateimage); 4768 ImageList_Draw(infoPtr->himlState, stateimage-1, nmlvcd->nmcd.hdc, rcStateIcon.left, rcStateIcon.top, ILD_NORMAL); 4769 } 4770 } 4771 4772 /* item icons */ 4773 himl = (infoPtr->uView == LV_VIEW_ICON ? infoPtr->himlNormal : infoPtr->himlSmall); 4774 if (himl && item->iImage >= 0 && !IsRectEmpty(&rcIcon)) 4775 { 4776 UINT style; 4777 4778 TRACE("iImage=%d\n", item->iImage); 4779 4780 if (item->state & (LVIS_SELECTED | LVIS_CUT) && infoPtr->bFocus) 4781 style = ILD_SELECTED; 4782 else 4783 style = ILD_NORMAL; 4784 4785 ImageList_DrawEx(himl, item->iImage, nmlvcd->nmcd.hdc, rcIcon.left, rcIcon.top, 4786 rcIcon.right - rcIcon.left, rcIcon.bottom - rcIcon.top, infoPtr->clrBk, 4787 item->state & LVIS_CUT ? RGB(255, 255, 255) : CLR_DEFAULT, 4788 style | (item->state & LVIS_OVERLAYMASK)); 4789 } 4790 4791 /* Don't bother painting item being edited */ 4792 if (infoPtr->hwndEdit && item->iItem == infoPtr->nEditLabelItem && item->iSubItem == 0) return; 4793 4794 /* figure out the text drawing flags */ 4795 format = (infoPtr->uView == LV_VIEW_ICON ? (focus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS) : LV_SL_DT_FLAGS); 4796 if (infoPtr->uView == LV_VIEW_ICON) 4797 format = (focus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS); 4798 else if (item->iSubItem) 4799 { 4800 switch (LISTVIEW_GetColumnInfo(infoPtr, item->iSubItem)->fmt & LVCFMT_JUSTIFYMASK) 4801 { 4802 case LVCFMT_RIGHT: format |= DT_RIGHT; break; 4803 case LVCFMT_CENTER: format |= DT_CENTER; break; 4804 default: format |= DT_LEFT; 4805 } 4806 } 4807 if (!(format & (DT_RIGHT | DT_CENTER))) 4808 { 4809 if (himl && item->iImage >= 0 && !IsRectEmpty(&rcIcon)) rcLabel.left += IMAGE_PADDING; 4810 else rcLabel.left += LABEL_HOR_PADDING; 4811 } 4812 else if (format & DT_RIGHT) rcLabel.right -= LABEL_HOR_PADDING; 4813 4814 /* for GRIDLINES reduce the bottom so the text formats correctly */ 4815 if (infoPtr->uView == LV_VIEW_DETAILS && infoPtr->dwLvExStyle & LVS_EX_GRIDLINES) 4816 rcLabel.bottom--; 4817 4818 #ifdef __REACTOS__ 4819 if ((!(item->state & LVIS_SELECTED) || !infoPtr->bFocus) && (infoPtr->dwLvExStyle & LVS_EX_TRANSPARENTSHADOWTEXT)) 4820 DrawShadowText(nmlvcd->nmcd.hdc, item->pszText, -1, &rcLabel, format, RGB(255, 255, 255), RGB(0, 0, 0), 2, 2); 4821 else 4822 #endif 4823 DrawTextW(nmlvcd->nmcd.hdc, item->pszText, -1, &rcLabel, format); 4824 } 4825 4826 /*** 4827 * DESCRIPTION: 4828 * Draws an item. 4829 * 4830 * PARAMETER(S): 4831 * [I] infoPtr : valid pointer to the listview structure 4832 * [I] hdc : device context handle 4833 * [I] nItem : item index 4834 * [I] nSubItem : subitem index 4835 * [I] pos : item position in client coordinates 4836 * [I] cdmode : custom draw mode 4837 * 4838 * RETURN: 4839 * Success: TRUE 4840 * Failure: FALSE 4841 */ 4842 static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, ITERATOR *subitems, POINT pos, DWORD cdmode) 4843 { 4844 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' }; 4845 static WCHAR callbackW[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 }; 4846 DWORD cdsubitemmode = CDRF_DODEFAULT; 4847 RECT *focus, rcBox; 4848 NMLVCUSTOMDRAW nmlvcd; 4849 LVITEMW lvItem; 4850 4851 TRACE("(hdc=%p, nItem=%d, subitems=%p, pos=%s)\n", hdc, nItem, subitems, wine_dbgstr_point(&pos)); 4852 4853 /* get information needed for drawing the item */ 4854 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM | LVIF_STATE; 4855 if (infoPtr->uView == LV_VIEW_DETAILS) lvItem.mask |= LVIF_INDENT; 4856 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK | LVIS_CUT | LVIS_OVERLAYMASK; 4857 lvItem.iItem = nItem; 4858 lvItem.iSubItem = 0; 4859 lvItem.state = 0; 4860 lvItem.lParam = 0; 4861 lvItem.cchTextMax = DISP_TEXT_SIZE; 4862 lvItem.pszText = szDispText; 4863 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE; 4864 if (lvItem.pszText == LPSTR_TEXTCALLBACKW) lvItem.pszText = callbackW; 4865 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE)); 4866 4867 /* now check if we need to update the focus rectangle */ 4868 focus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0; 4869 if (!focus) lvItem.state &= ~LVIS_FOCUSED; 4870 4871 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, NULL, NULL, NULL, NULL); 4872 OffsetRect(&rcBox, pos.x, pos.y); 4873 4874 /* Full custom draw stage sequence looks like this: 4875 4876 LV_VIEW_DETAILS: 4877 4878 - CDDS_ITEMPREPAINT 4879 - CDDS_ITEMPREPAINT|CDDS_SUBITEM | => sent n times, where n is number of subitems, 4880 CDDS_ITEMPOSTPAINT|CDDS_SUBITEM | including item itself 4881 - CDDS_ITEMPOSTPAINT 4882 4883 other styles: 4884 4885 - CDDS_ITEMPREPAINT 4886 - CDDS_ITEMPOSTPAINT 4887 */ 4888 4889 /* fill in the custom draw structure */ 4890 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcBox, &lvItem); 4891 if (cdmode & CDRF_NOTIFYITEMDRAW) 4892 cdsubitemmode = notify_customdraw(infoPtr, CDDS_ITEMPREPAINT, &nmlvcd); 4893 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint; 4894 4895 if (subitems) 4896 { 4897 while (iterator_next(subitems)) 4898 { 4899 DWORD subitemstage = CDRF_DODEFAULT; 4900 NMLVCUSTOMDRAW temp_nmlvcd; 4901 4902 /* We need to query for each subitem, item's data (subitem == 0) is already here at this point */ 4903 if (subitems->nItem) 4904 { 4905 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM | LVIF_INDENT; 4906 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK | LVIS_CUT | LVIS_OVERLAYMASK; 4907 lvItem.iItem = nItem; 4908 lvItem.iSubItem = subitems->nItem; 4909 lvItem.state = 0; 4910 lvItem.lParam = 0; 4911 lvItem.cchTextMax = DISP_TEXT_SIZE; 4912 lvItem.pszText = szDispText; 4913 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE; 4914 if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) 4915 lvItem.state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED); 4916 if (lvItem.pszText == LPSTR_TEXTCALLBACKW) lvItem.pszText = callbackW; 4917 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE)); 4918 4919 /* update custom draw data */ 4920 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &nmlvcd.nmcd.rc, NULL, NULL, NULL, NULL); 4921 OffsetRect(&nmlvcd.nmcd.rc, pos.x, pos.y); 4922 nmlvcd.iSubItem = subitems->nItem; 4923 } 4924 4925 if (cdsubitemmode & CDRF_NOTIFYSUBITEMDRAW) 4926 subitemstage = notify_customdraw(infoPtr, CDDS_SUBITEM | CDDS_ITEMPREPAINT, &nmlvcd); 4927 4928 /* 4929 * A selection should neither affect the colors in the post paint notification nor 4930 * affect the colors of the next drawn subitem. Copy the structure to prevent this. 4931 */ 4932 temp_nmlvcd = nmlvcd; 4933 prepaint_setup(infoPtr, hdc, &temp_nmlvcd, subitems->nItem); 4934 4935 if (!(subitemstage & CDRF_SKIPDEFAULT)) 4936 LISTVIEW_DrawItemPart(infoPtr, &lvItem, &temp_nmlvcd, &pos); 4937 4938 if (subitemstage & CDRF_NOTIFYPOSTPAINT) 4939 subitemstage = notify_customdraw(infoPtr, CDDS_SUBITEM | CDDS_ITEMPOSTPAINT, &nmlvcd); 4940 } 4941 } 4942 else 4943 { 4944 prepaint_setup(infoPtr, hdc, &nmlvcd, FALSE); 4945 LISTVIEW_DrawItemPart(infoPtr, &lvItem, &nmlvcd, &pos); 4946 } 4947 4948 postpaint: 4949 if (cdsubitemmode & CDRF_NOTIFYPOSTPAINT) 4950 { 4951 nmlvcd.iSubItem = 0; 4952 notify_customdraw(infoPtr, CDDS_ITEMPOSTPAINT, &nmlvcd); 4953 } 4954 4955 return TRUE; 4956 } 4957 4958 /*** 4959 * DESCRIPTION: 4960 * Draws listview items when in owner draw mode. 4961 * 4962 * PARAMETER(S): 4963 * [I] infoPtr : valid pointer to the listview structure 4964 * [I] hdc : device context handle 4965 * 4966 * RETURN: 4967 * None 4968 */ 4969 static void LISTVIEW_RefreshOwnerDraw(const LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode) 4970 { 4971 UINT uID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID); 4972 DWORD cditemmode = CDRF_DODEFAULT; 4973 NMLVCUSTOMDRAW nmlvcd; 4974 POINT Origin, Position; 4975 DRAWITEMSTRUCT dis; 4976 LVITEMW item; 4977 4978 TRACE("()\n"); 4979 4980 ZeroMemory(&dis, sizeof(dis)); 4981 4982 /* Get scroll info once before loop */ 4983 LISTVIEW_GetOrigin(infoPtr, &Origin); 4984 4985 /* iterate through the invalidated rows */ 4986 while(iterator_next(i)) 4987 { 4988 item.iItem = i->nItem; 4989 item.iSubItem = 0; 4990 item.mask = LVIF_PARAM | LVIF_STATE; 4991 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED; 4992 if (!LISTVIEW_GetItemW(infoPtr, &item)) continue; 4993 4994 dis.CtlType = ODT_LISTVIEW; 4995 dis.CtlID = uID; 4996 dis.itemID = item.iItem; 4997 dis.itemAction = ODA_DRAWENTIRE; 4998 dis.itemState = 0; 4999 if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED; 5000 if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS; 5001 dis.hwndItem = infoPtr->hwndSelf; 5002 dis.hDC = hdc; 5003 LISTVIEW_GetItemOrigin(infoPtr, dis.itemID, &Position); 5004 dis.rcItem.left = Position.x + Origin.x; 5005 dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth; 5006 dis.rcItem.top = Position.y + Origin.y; 5007 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight; 5008 dis.itemData = item.lParam; 5009 5010 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), wine_dbgstr_rect(&dis.rcItem)); 5011 5012 /* 5013 * Even if we do not send the CDRF_NOTIFYITEMDRAW we need to fill the nmlvcd 5014 * structure for the rest. of the paint cycle 5015 */ 5016 customdraw_fill(&nmlvcd, infoPtr, hdc, &dis.rcItem, &item); 5017 if (cdmode & CDRF_NOTIFYITEMDRAW) 5018 cditemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd); 5019 5020 if (!(cditemmode & CDRF_SKIPDEFAULT)) 5021 { 5022 prepaint_setup (infoPtr, hdc, &nmlvcd, FALSE); 5023 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis); 5024 } 5025 5026 if (cditemmode & CDRF_NOTIFYPOSTPAINT) 5027 notify_postpaint(infoPtr, &nmlvcd); 5028 } 5029 } 5030 5031 /*** 5032 * DESCRIPTION: 5033 * Draws listview items when in report display mode. 5034 * 5035 * PARAMETER(S): 5036 * [I] infoPtr : valid pointer to the listview structure 5037 * [I] hdc : device context handle 5038 * [I] cdmode : custom draw mode 5039 * 5040 * RETURN: 5041 * None 5042 */ 5043 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode) 5044 { 5045 INT rgntype; 5046 RECT rcClip, rcItem; 5047 POINT Origin; 5048 RANGES colRanges; 5049 INT col; 5050 ITERATOR j; 5051 5052 TRACE("()\n"); 5053 5054 /* figure out what to draw */ 5055 rgntype = GetClipBox(hdc, &rcClip); 5056 if (rgntype == NULLREGION) return; 5057 5058 /* Get scroll info once before loop */ 5059 LISTVIEW_GetOrigin(infoPtr, &Origin); 5060 5061 colRanges = ranges_create(DPA_GetPtrCount(infoPtr->hdpaColumns)); 5062 5063 /* narrow down the columns we need to paint */ 5064 for(col = 0; col < DPA_GetPtrCount(infoPtr->hdpaColumns); col++) 5065 { 5066 INT index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX, col, 0); 5067 5068 LISTVIEW_GetHeaderRect(infoPtr, index, &rcItem); 5069 if ((rcItem.right + Origin.x >= rcClip.left) && (rcItem.left + Origin.x < rcClip.right)) 5070 ranges_additem(colRanges, index); 5071 } 5072 iterator_rangesitems(&j, colRanges); 5073 5074 /* in full row select, we _have_ to draw the main item */ 5075 if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) 5076 j.nSpecial = 0; 5077 5078 /* iterate through the invalidated rows */ 5079 while(iterator_next(i)) 5080 { 5081 RANGES subitems; 5082 POINT Position; 5083 ITERATOR k; 5084 5085 SelectObject(hdc, infoPtr->hFont); 5086 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position); 5087 Position.x = Origin.x; 5088 Position.y += Origin.y; 5089 5090 subitems = ranges_create(DPA_GetPtrCount(infoPtr->hdpaColumns)); 5091 5092 /* iterate through the invalidated columns */ 5093 while(iterator_next(&j)) 5094 { 5095 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem); 5096 5097 if (rgntype == COMPLEXREGION && !((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && j.nItem == 0)) 5098 { 5099 rcItem.top = 0; 5100 rcItem.bottom = infoPtr->nItemHeight; 5101 OffsetRect(&rcItem, Origin.x, Position.y); 5102 if (!RectVisible(hdc, &rcItem)) continue; 5103 } 5104 5105 ranges_additem(subitems, j.nItem); 5106 } 5107 5108 iterator_rangesitems(&k, subitems); 5109 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, &k, Position, cdmode); 5110 iterator_destroy(&k); 5111 } 5112 iterator_destroy(&j); 5113 } 5114 5115 /*** 5116 * DESCRIPTION: 5117 * Draws the gridlines if necessary when in report display mode. 5118 * 5119 * PARAMETER(S): 5120 * [I] infoPtr : valid pointer to the listview structure 5121 * [I] hdc : device context handle 5122 * 5123 * RETURN: 5124 * None 5125 */ 5126 static void LISTVIEW_RefreshReportGrid(LISTVIEW_INFO *infoPtr, HDC hdc) 5127 { 5128 INT rgntype; 5129 INT y, itemheight; 5130 INT col, index; 5131 HPEN hPen, hOldPen; 5132 RECT rcClip, rcItem = {0}; 5133 POINT Origin; 5134 RANGES colRanges; 5135 ITERATOR j; 5136 BOOL rmost = FALSE; 5137 5138 TRACE("()\n"); 5139 5140 /* figure out what to draw */ 5141 rgntype = GetClipBox(hdc, &rcClip); 5142 if (rgntype == NULLREGION) return; 5143 5144 /* Get scroll info once before loop */ 5145 LISTVIEW_GetOrigin(infoPtr, &Origin); 5146 5147 colRanges = ranges_create(DPA_GetPtrCount(infoPtr->hdpaColumns)); 5148 5149 /* narrow down the columns we need to paint */ 5150 for(col = 0; col < DPA_GetPtrCount(infoPtr->hdpaColumns); col++) 5151 { 5152 index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX, col, 0); 5153 5154 LISTVIEW_GetHeaderRect(infoPtr, index, &rcItem); 5155 if ((rcItem.right + Origin.x >= rcClip.left) && (rcItem.left + Origin.x < rcClip.right)) 5156 ranges_additem(colRanges, index); 5157 } 5158 5159 /* is right most vertical line visible? */ 5160 if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0) 5161 { 5162 index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, 0); 5163 LISTVIEW_GetHeaderRect(infoPtr, index, &rcItem); 5164 rmost = (rcItem.right + Origin.x < rcClip.right); 5165 } 5166 5167 if ((hPen = CreatePen( PS_SOLID, 1, comctl32_color.clr3dFace ))) 5168 { 5169 hOldPen = SelectObject ( hdc, hPen ); 5170 5171 /* draw the vertical lines for the columns */ 5172 iterator_rangesitems(&j, colRanges); 5173 while(iterator_next(&j)) 5174 { 5175 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem); 5176 if (rcItem.left == 0) continue; /* skip leftmost column */ 5177 rcItem.left += Origin.x; 5178 rcItem.right += Origin.x; 5179 rcItem.top = infoPtr->rcList.top; 5180 rcItem.bottom = infoPtr->rcList.bottom; 5181 TRACE("vert col=%d, rcItem=%s\n", j.nItem, wine_dbgstr_rect(&rcItem)); 5182 MoveToEx (hdc, rcItem.left, rcItem.top, NULL); 5183 LineTo (hdc, rcItem.left, rcItem.bottom); 5184 } 5185 iterator_destroy(&j); 5186 /* draw rightmost grid line if visible */ 5187 if (rmost) 5188 { 5189 index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX, 5190 DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, 0); 5191 LISTVIEW_GetHeaderRect(infoPtr, index, &rcItem); 5192 5193 rcItem.right += Origin.x; 5194 5195 MoveToEx (hdc, rcItem.right, infoPtr->rcList.top, NULL); 5196 LineTo (hdc, rcItem.right, infoPtr->rcList.bottom); 5197 } 5198 5199 /* draw the horizontal lines for the rows */ 5200 itemheight = LISTVIEW_CalculateItemHeight(infoPtr); 5201 rcItem.left = infoPtr->rcList.left; 5202 rcItem.right = infoPtr->rcList.right; 5203 for(y = Origin.y > 1 ? Origin.y - 1 : itemheight - 1 + Origin.y % itemheight; y<=infoPtr->rcList.bottom; y+=itemheight) 5204 { 5205 rcItem.bottom = rcItem.top = y; 5206 TRACE("horz rcItem=%s\n", wine_dbgstr_rect(&rcItem)); 5207 MoveToEx (hdc, rcItem.left, rcItem.top, NULL); 5208 LineTo (hdc, rcItem.right, rcItem.top); 5209 } 5210 5211 SelectObject( hdc, hOldPen ); 5212 DeleteObject( hPen ); 5213 } 5214 else 5215 ranges_destroy(colRanges); 5216 } 5217 5218 /*** 5219 * DESCRIPTION: 5220 * Draws listview items when in list display mode. 5221 * 5222 * PARAMETER(S): 5223 * [I] infoPtr : valid pointer to the listview structure 5224 * [I] hdc : device context handle 5225 * [I] cdmode : custom draw mode 5226 * 5227 * RETURN: 5228 * None 5229 */ 5230 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode) 5231 { 5232 POINT Origin, Position; 5233 5234 /* Get scroll info once before loop */ 5235 LISTVIEW_GetOrigin(infoPtr, &Origin); 5236 5237 while(iterator_prev(i)) 5238 { 5239 SelectObject(hdc, infoPtr->hFont); 5240 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position); 5241 Position.x += Origin.x; 5242 Position.y += Origin.y; 5243 5244 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, NULL, Position, cdmode); 5245 } 5246 } 5247 5248 5249 /*** 5250 * DESCRIPTION: 5251 * Draws listview items. 5252 * 5253 * PARAMETER(S): 5254 * [I] infoPtr : valid pointer to the listview structure 5255 * [I] hdc : device context handle 5256 * [I] prcErase : rect to be erased before refresh (may be NULL) 5257 * 5258 * RETURN: 5259 * NoneX 5260 */ 5261 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *prcErase) 5262 { 5263 COLORREF oldTextColor = 0, oldBkColor = 0; 5264 NMLVCUSTOMDRAW nmlvcd; 5265 HFONT hOldFont = 0; 5266 DWORD cdmode; 5267 INT oldBkMode = 0; 5268 RECT rcClient; 5269 ITERATOR i; 5270 HDC hdcOrig = hdc; 5271 HBITMAP hbmp = NULL; 5272 RANGE range; 5273 5274 LISTVIEW_DUMP(infoPtr); 5275 5276 if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) { 5277 TRACE("double buffering\n"); 5278 5279 hdc = CreateCompatibleDC(hdcOrig); 5280 if (!hdc) { 5281 ERR("Failed to create DC for backbuffer\n"); 5282 return; 5283 } 5284 hbmp = CreateCompatibleBitmap(hdcOrig, infoPtr->rcList.right, 5285 infoPtr->rcList.bottom); 5286 if (!hbmp) { 5287 ERR("Failed to create bitmap for backbuffer\n"); 5288 DeleteDC(hdc); 5289 return; 5290 } 5291 5292 SelectObject(hdc, hbmp); 5293 SelectObject(hdc, infoPtr->hFont); 5294 5295 if(GetClipBox(hdcOrig, &rcClient)) 5296 IntersectClipRect(hdc, rcClient.left, rcClient.top, rcClient.right, rcClient.bottom); 5297 } else { 5298 /* Save dc values we're gonna trash while drawing 5299 * FIXME: Should be done in LISTVIEW_DrawItem() */ 5300 hOldFont = SelectObject(hdc, infoPtr->hFont); 5301 oldBkMode = GetBkMode(hdc); 5302 oldBkColor = GetBkColor(hdc); 5303 oldTextColor = GetTextColor(hdc); 5304 } 5305 5306 infoPtr->bIsDrawing = TRUE; 5307 5308 if (prcErase) { 5309 LISTVIEW_FillBkgnd(infoPtr, hdc, prcErase); 5310 } else if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) { 5311 /* If no erasing was done (usually because RedrawWindow was called 5312 * with RDW_INVALIDATE only) we need to copy the old contents into 5313 * the backbuffer before continuing. */ 5314 BitBlt(hdc, infoPtr->rcList.left, infoPtr->rcList.top, 5315 infoPtr->rcList.right - infoPtr->rcList.left, 5316 infoPtr->rcList.bottom - infoPtr->rcList.top, 5317 hdcOrig, infoPtr->rcList.left, infoPtr->rcList.top, SRCCOPY); 5318 } 5319 5320 GetClientRect(infoPtr->hwndSelf, &rcClient); 5321 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient, 0); 5322 cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd); 5323 if (cdmode & CDRF_SKIPDEFAULT) goto enddraw; 5324 5325 /* nothing to draw */ 5326 if(infoPtr->nItemCount == 0) goto enddraw; 5327 5328 /* figure out what we need to draw */ 5329 iterator_visibleitems(&i, infoPtr, hdc); 5330 range = iterator_range(&i); 5331 5332 /* send cache hint notification */ 5333 if (infoPtr->dwStyle & LVS_OWNERDATA) 5334 { 5335 NMLVCACHEHINT nmlv; 5336 5337 ZeroMemory(&nmlv, sizeof(NMLVCACHEHINT)); 5338 nmlv.iFrom = range.lower; 5339 nmlv.iTo = range.upper - 1; 5340 notify_hdr(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr); 5341 } 5342 5343 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (infoPtr->uView == LV_VIEW_DETAILS)) 5344 LISTVIEW_RefreshOwnerDraw(infoPtr, &i, hdc, cdmode); 5345 else 5346 { 5347 if (infoPtr->uView == LV_VIEW_DETAILS) 5348 LISTVIEW_RefreshReport(infoPtr, &i, hdc, cdmode); 5349 else /* LV_VIEW_LIST, LV_VIEW_ICON or LV_VIEW_SMALLICON */ 5350 LISTVIEW_RefreshList(infoPtr, &i, hdc, cdmode); 5351 5352 /* if we have a focus rect and it's visible, draw it */ 5353 if (infoPtr->bFocus && range.lower <= infoPtr->nFocusedItem && 5354 (range.upper - 1) >= infoPtr->nFocusedItem) 5355 LISTVIEW_DrawFocusRect(infoPtr, hdc); 5356 } 5357 iterator_destroy(&i); 5358 5359 enddraw: 5360 /* For LVS_EX_GRIDLINES go and draw lines */ 5361 /* This includes the case where there were *no* items */ 5362 if ((infoPtr->uView == LV_VIEW_DETAILS) && infoPtr->dwLvExStyle & LVS_EX_GRIDLINES) 5363 LISTVIEW_RefreshReportGrid(infoPtr, hdc); 5364 5365 /* Draw marquee rectangle if appropriate */ 5366 if (infoPtr->bMarqueeSelect) 5367 #ifdef __REACTOS__ 5368 { 5369 SetBkColor(hdc, RGB(255, 255, 255)); 5370 SetTextColor(hdc, RGB(0, 0, 0)); 5371 DrawFocusRect(hdc, &infoPtr->marqueeDrawRect); 5372 } 5373 #else 5374 DrawFocusRect(hdc, &infoPtr->marqueeDrawRect); 5375 #endif 5376 5377 if (cdmode & CDRF_NOTIFYPOSTPAINT) 5378 notify_postpaint(infoPtr, &nmlvcd); 5379 5380 if(hbmp) { 5381 BitBlt(hdcOrig, infoPtr->rcList.left, infoPtr->rcList.top, 5382 infoPtr->rcList.right - infoPtr->rcList.left, 5383 infoPtr->rcList.bottom - infoPtr->rcList.top, 5384 hdc, infoPtr->rcList.left, infoPtr->rcList.top, SRCCOPY); 5385 5386 DeleteObject(hbmp); 5387 DeleteDC(hdc); 5388 } else { 5389 SelectObject(hdc, hOldFont); 5390 SetBkMode(hdc, oldBkMode); 5391 SetBkColor(hdc, oldBkColor); 5392 SetTextColor(hdc, oldTextColor); 5393 } 5394 5395 infoPtr->bIsDrawing = FALSE; 5396 } 5397 5398 5399 /*** 5400 * DESCRIPTION: 5401 * Calculates the approximate width and height of a given number of items. 5402 * 5403 * PARAMETER(S): 5404 * [I] infoPtr : valid pointer to the listview structure 5405 * [I] nItemCount : number of items 5406 * [I] wWidth : width 5407 * [I] wHeight : height 5408 * 5409 * RETURN: 5410 * Returns a DWORD. The width in the low word and the height in high word. 5411 */ 5412 static DWORD LISTVIEW_ApproximateViewRect(const LISTVIEW_INFO *infoPtr, INT nItemCount, 5413 WORD wWidth, WORD wHeight) 5414 { 5415 DWORD dwViewRect = 0; 5416 5417 if (nItemCount == -1) 5418 nItemCount = infoPtr->nItemCount; 5419 5420 if (infoPtr->uView == LV_VIEW_LIST) 5421 { 5422 INT nItemCountPerColumn = 1; 5423 INT nColumnCount = 0; 5424 5425 if (wHeight == 0xFFFF) 5426 { 5427 /* use current height */ 5428 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top; 5429 } 5430 5431 if (wHeight < infoPtr->nItemHeight) 5432 wHeight = infoPtr->nItemHeight; 5433 5434 if (nItemCount > 0) 5435 { 5436 if (infoPtr->nItemHeight > 0) 5437 { 5438 nItemCountPerColumn = wHeight / infoPtr->nItemHeight; 5439 if (nItemCountPerColumn == 0) 5440 nItemCountPerColumn = 1; 5441 5442 if (nItemCount % nItemCountPerColumn != 0) 5443 nColumnCount = nItemCount / nItemCountPerColumn; 5444 else 5445 nColumnCount = nItemCount / nItemCountPerColumn + 1; 5446 } 5447 } 5448 5449 /* Microsoft padding magic */ 5450 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2; 5451 wWidth = nColumnCount * infoPtr->nItemWidth + 2; 5452 5453 dwViewRect = MAKELONG(wWidth, wHeight); 5454 } 5455 else if (infoPtr->uView == LV_VIEW_DETAILS) 5456 { 5457 RECT rcBox; 5458 5459 if (infoPtr->nItemCount > 0) 5460 { 5461 LISTVIEW_GetItemBox(infoPtr, 0, &rcBox); 5462 wWidth = rcBox.right - rcBox.left; 5463 wHeight = (rcBox.bottom - rcBox.top) * nItemCount; 5464 } 5465 else 5466 { 5467 /* use current height and width */ 5468 if (wHeight == 0xffff) 5469 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top; 5470 if (wWidth == 0xffff) 5471 wWidth = infoPtr->rcList.right - infoPtr->rcList.left; 5472 } 5473 5474 dwViewRect = MAKELONG(wWidth, wHeight); 5475 } 5476 else if (infoPtr->uView == LV_VIEW_ICON) 5477 { 5478 UINT rows,cols; 5479 UINT nItemWidth; 5480 UINT nItemHeight; 5481 5482 nItemWidth = infoPtr->iconSpacing.cx; 5483 nItemHeight = infoPtr->iconSpacing.cy; 5484 5485 if (wWidth == 0xffff) 5486 wWidth = infoPtr->rcList.right - infoPtr->rcList.left; 5487 5488 if (wWidth < nItemWidth) 5489 wWidth = nItemWidth; 5490 5491 cols = wWidth / nItemWidth; 5492 if (cols > nItemCount) 5493 cols = nItemCount; 5494 if (cols < 1) 5495 cols = 1; 5496 5497 if (nItemCount) 5498 { 5499 rows = nItemCount / cols; 5500 if (nItemCount % cols) 5501 rows++; 5502 } 5503 else 5504 rows = 0; 5505 5506 wHeight = (nItemHeight * rows)+2; 5507 wWidth = (nItemWidth * cols)+2; 5508 5509 dwViewRect = MAKELONG(wWidth, wHeight); 5510 } 5511 else if (infoPtr->uView == LV_VIEW_SMALLICON) 5512 FIXME("uView == LV_VIEW_SMALLICON: not implemented\n"); 5513 5514 return dwViewRect; 5515 } 5516 5517 /*** 5518 * DESCRIPTION: 5519 * Cancel edit label with saving item text. 5520 * 5521 * PARAMETER(S): 5522 * [I] infoPtr : valid pointer to the listview structure 5523 * 5524 * RETURN: 5525 * Always returns TRUE. 5526 */ 5527 static LRESULT LISTVIEW_CancelEditLabel(LISTVIEW_INFO *infoPtr) 5528 { 5529 if (infoPtr->hwndEdit) 5530 { 5531 /* handle value will be lost after LISTVIEW_EndEditLabelT */ 5532 HWND edit = infoPtr->hwndEdit; 5533 5534 LISTVIEW_EndEditLabelT(infoPtr, TRUE, IsWindowUnicode(infoPtr->hwndEdit)); 5535 SendMessageW(edit, WM_CLOSE, 0, 0); 5536 } 5537 5538 return TRUE; 5539 } 5540 5541 /*** 5542 * DESCRIPTION: 5543 * Create a drag image list for the specified item. 5544 * 5545 * PARAMETER(S): 5546 * [I] infoPtr : valid pointer to the listview structure 5547 * [I] iItem : index of item 5548 * [O] lppt : Upper-left corner of the image 5549 * 5550 * RETURN: 5551 * Returns a handle to the image list if successful, NULL otherwise. 5552 */ 5553 static HIMAGELIST LISTVIEW_CreateDragImage(LISTVIEW_INFO *infoPtr, INT iItem, LPPOINT lppt) 5554 { 5555 RECT rcItem; 5556 SIZE size; 5557 POINT pos; 5558 HDC hdc, hdcOrig; 5559 HBITMAP hbmp, hOldbmp; 5560 HFONT hOldFont; 5561 HIMAGELIST dragList = 0; 5562 TRACE("iItem=%d Count=%d\n", iItem, infoPtr->nItemCount); 5563 5564 if (iItem < 0 || iItem >= infoPtr->nItemCount || !lppt) 5565 return 0; 5566 5567 rcItem.left = LVIR_BOUNDS; 5568 if (!LISTVIEW_GetItemRect(infoPtr, iItem, &rcItem)) 5569 return 0; 5570 5571 lppt->x = rcItem.left; 5572 lppt->y = rcItem.top; 5573 5574 size.cx = rcItem.right - rcItem.left; 5575 size.cy = rcItem.bottom - rcItem.top; 5576 5577 hdcOrig = GetDC(infoPtr->hwndSelf); 5578 hdc = CreateCompatibleDC(hdcOrig); 5579 hbmp = CreateCompatibleBitmap(hdcOrig, size.cx, size.cy); 5580 hOldbmp = SelectObject(hdc, hbmp); 5581 hOldFont = SelectObject(hdc, infoPtr->hFont); 5582 5583 SetRect(&rcItem, 0, 0, size.cx, size.cy); 5584 FillRect(hdc, &rcItem, infoPtr->hBkBrush); 5585 5586 pos.x = pos.y = 0; 5587 if (LISTVIEW_DrawItem(infoPtr, hdc, iItem, NULL, pos, CDRF_DODEFAULT)) 5588 { 5589 dragList = ImageList_Create(size.cx, size.cy, ILC_COLOR, 10, 10); 5590 SelectObject(hdc, hOldbmp); 5591 ImageList_Add(dragList, hbmp, 0); 5592 } 5593 else 5594 SelectObject(hdc, hOldbmp); 5595 5596 SelectObject(hdc, hOldFont); 5597 DeleteObject(hbmp); 5598 DeleteDC(hdc); 5599 ReleaseDC(infoPtr->hwndSelf, hdcOrig); 5600 5601 TRACE("ret=%p\n", dragList); 5602 5603 return dragList; 5604 } 5605 5606 5607 /*** 5608 * DESCRIPTION: 5609 * Removes all listview items and subitems. 5610 * 5611 * PARAMETER(S): 5612 * [I] infoPtr : valid pointer to the listview structure 5613 * 5614 * RETURN: 5615 * SUCCESS : TRUE 5616 * FAILURE : FALSE 5617 */ 5618 static BOOL LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr, BOOL destroy) 5619 { 5620 HDPA hdpaSubItems = NULL; 5621 BOOL suppress = FALSE; 5622 ITEMHDR *hdrItem; 5623 ITEM_INFO *lpItem; 5624 ITEM_ID *lpID; 5625 INT i, j; 5626 5627 TRACE("()\n"); 5628 5629 /* we do it directly, to avoid notifications */ 5630 ranges_clear(infoPtr->selectionRanges); 5631 infoPtr->nSelectionMark = -1; 5632 infoPtr->nFocusedItem = -1; 5633 SetRectEmpty(&infoPtr->rcFocus); 5634 /* But we are supposed to leave nHotItem as is! */ 5635 5636 /* send LVN_DELETEALLITEMS notification */ 5637 if (!(infoPtr->dwStyle & LVS_OWNERDATA) || !destroy) 5638 { 5639 NMLISTVIEW nmlv; 5640 5641 memset(&nmlv, 0, sizeof(NMLISTVIEW)); 5642 nmlv.iItem = -1; 5643 suppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv); 5644 } 5645 5646 for (i = infoPtr->nItemCount - 1; i >= 0; i--) 5647 { 5648 if (!(infoPtr->dwStyle & LVS_OWNERDATA)) 5649 { 5650 /* send LVN_DELETEITEM notification, if not suppressed 5651 and if it is not a virtual listview */ 5652 if (!suppress) notify_deleteitem(infoPtr, i); 5653 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, i); 5654 lpItem = DPA_GetPtr(hdpaSubItems, 0); 5655 /* free id struct */ 5656 j = DPA_GetPtrIndex(infoPtr->hdpaItemIds, lpItem->id); 5657 lpID = DPA_GetPtr(infoPtr->hdpaItemIds, j); 5658 DPA_DeletePtr(infoPtr->hdpaItemIds, j); 5659 Free(lpID); 5660 /* both item and subitem start with ITEMHDR header */ 5661 for (j = 0; j < DPA_GetPtrCount(hdpaSubItems); j++) 5662 { 5663 hdrItem = DPA_GetPtr(hdpaSubItems, j); 5664 if (is_text(hdrItem->pszText)) Free(hdrItem->pszText); 5665 Free(hdrItem); 5666 } 5667 DPA_Destroy(hdpaSubItems); 5668 DPA_DeletePtr(infoPtr->hdpaItems, i); 5669 } 5670 DPA_DeletePtr(infoPtr->hdpaPosX, i); 5671 DPA_DeletePtr(infoPtr->hdpaPosY, i); 5672 infoPtr->nItemCount --; 5673 } 5674 5675 if (!destroy) 5676 { 5677 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT); 5678 LISTVIEW_UpdateScroll(infoPtr); 5679 } 5680 LISTVIEW_InvalidateList(infoPtr); 5681 5682 return TRUE; 5683 } 5684 5685 /*** 5686 * DESCRIPTION: 5687 * Scrolls, and updates the columns, when a column is changing width. 5688 * 5689 * PARAMETER(S): 5690 * [I] infoPtr : valid pointer to the listview structure 5691 * [I] nColumn : column to scroll 5692 * [I] dx : amount of scroll, in pixels 5693 * 5694 * RETURN: 5695 * None. 5696 */ 5697 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO *infoPtr, INT nColumn, INT dx) 5698 { 5699 COLUMN_INFO *lpColumnInfo; 5700 RECT rcOld, rcCol; 5701 POINT ptOrigin; 5702 INT nCol; 5703 HDITEMW hdi; 5704 5705 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) < 1) return; 5706 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1)); 5707 rcCol = lpColumnInfo->rcHeader; 5708 if (nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) 5709 rcCol.left = rcCol.right; 5710 5711 /* adjust the other columns */ 5712 hdi.mask = HDI_ORDER; 5713 if (SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, nColumn, (LPARAM)&hdi)) 5714 { 5715 INT nOrder = hdi.iOrder; 5716 for (nCol = 0; nCol < DPA_GetPtrCount(infoPtr->hdpaColumns); nCol++) 5717 { 5718 hdi.mask = HDI_ORDER; 5719 SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, nCol, (LPARAM)&hdi); 5720 if (hdi.iOrder >= nOrder) { 5721 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nCol); 5722 lpColumnInfo->rcHeader.left += dx; 5723 lpColumnInfo->rcHeader.right += dx; 5724 } 5725 } 5726 } 5727 5728 /* do not update screen if not in report mode */ 5729 if (!is_redrawing(infoPtr) || infoPtr->uView != LV_VIEW_DETAILS) return; 5730 5731 /* Need to reset the item width when inserting a new column */ 5732 infoPtr->nItemWidth += dx; 5733 5734 LISTVIEW_UpdateScroll(infoPtr); 5735 LISTVIEW_GetOrigin(infoPtr, &ptOrigin); 5736 5737 /* scroll to cover the deleted column, and invalidate for redraw */ 5738 rcOld = infoPtr->rcList; 5739 rcOld.left = ptOrigin.x + rcCol.left + dx; 5740 ScrollWindowEx(infoPtr->hwndSelf, dx, 0, &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE); 5741 } 5742 5743 /*** 5744 * DESCRIPTION: 5745 * Removes a column from the listview control. 5746 * 5747 * PARAMETER(S): 5748 * [I] infoPtr : valid pointer to the listview structure 5749 * [I] nColumn : column index 5750 * 5751 * RETURN: 5752 * SUCCESS : TRUE 5753 * FAILURE : FALSE 5754 */ 5755 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn) 5756 { 5757 RECT rcCol; 5758 5759 TRACE("nColumn=%d\n", nColumn); 5760 5761 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) 5762 return FALSE; 5763 5764 /* While the MSDN specifically says that column zero should not be deleted, 5765 what actually happens is that the column itself is deleted but no items or subitems 5766 are removed. 5767 */ 5768 5769 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol); 5770 5771 if (!SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nColumn, 0)) 5772 return FALSE; 5773 5774 Free(DPA_GetPtr(infoPtr->hdpaColumns, nColumn)); 5775 DPA_DeletePtr(infoPtr->hdpaColumns, nColumn); 5776 5777 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && nColumn) 5778 { 5779 SUBITEM_INFO *lpSubItem, *lpDelItem; 5780 HDPA hdpaSubItems; 5781 INT nItem, nSubItem, i; 5782 5783 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++) 5784 { 5785 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, nItem); 5786 nSubItem = 0; 5787 lpDelItem = 0; 5788 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++) 5789 { 5790 lpSubItem = DPA_GetPtr(hdpaSubItems, i); 5791 if (lpSubItem->iSubItem == nColumn) 5792 { 5793 nSubItem = i; 5794 lpDelItem = lpSubItem; 5795 } 5796 else if (lpSubItem->iSubItem > nColumn) 5797 { 5798 lpSubItem->iSubItem--; 5799 } 5800 } 5801 5802 /* if we found our subitem, zap it */ 5803 if (nSubItem > 0) 5804 { 5805 /* free string */ 5806 if (is_text(lpDelItem->hdr.pszText)) 5807 Free(lpDelItem->hdr.pszText); 5808 5809 /* free item */ 5810 Free(lpDelItem); 5811 5812 /* free dpa memory */ 5813 DPA_DeletePtr(hdpaSubItems, nSubItem); 5814 } 5815 } 5816 } 5817 5818 /* update the other column info */ 5819 if(DPA_GetPtrCount(infoPtr->hdpaColumns) == 0) 5820 LISTVIEW_InvalidateList(infoPtr); 5821 else 5822 LISTVIEW_ScrollColumns(infoPtr, nColumn, -(rcCol.right - rcCol.left)); 5823 LISTVIEW_UpdateItemSize(infoPtr); 5824 5825 return TRUE; 5826 } 5827 5828 /*** 5829 * DESCRIPTION: 5830 * Invalidates the listview after an item's insertion or deletion. 5831 * 5832 * PARAMETER(S): 5833 * [I] infoPtr : valid pointer to the listview structure 5834 * [I] nItem : item index 5835 * [I] dir : -1 if deleting, 1 if inserting 5836 * 5837 * RETURN: 5838 * None 5839 */ 5840 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO *infoPtr, INT nItem, INT dir) 5841 { 5842 INT nPerCol, nItemCol, nItemRow; 5843 RECT rcScroll; 5844 POINT Origin; 5845 5846 /* if we don't refresh, what's the point of scrolling? */ 5847 if (!is_redrawing(infoPtr)) return; 5848 5849 assert (abs(dir) == 1); 5850 5851 /* arrange icons if autoarrange is on */ 5852 if (is_autoarrange(infoPtr)) 5853 { 5854 BOOL arrange = TRUE; 5855 if (dir < 0 && nItem >= infoPtr->nItemCount) arrange = FALSE; 5856 if (dir > 0 && nItem == infoPtr->nItemCount - 1) arrange = FALSE; 5857 if (arrange) LISTVIEW_Arrange(infoPtr, LVA_DEFAULT); 5858 } 5859 5860 /* scrollbars need updating */ 5861 LISTVIEW_UpdateScroll(infoPtr); 5862 5863 /* figure out the item's position */ 5864 if (infoPtr->uView == LV_VIEW_DETAILS) 5865 nPerCol = infoPtr->nItemCount + 1; 5866 else if (infoPtr->uView == LV_VIEW_LIST) 5867 nPerCol = LISTVIEW_GetCountPerColumn(infoPtr); 5868 else /* LV_VIEW_ICON, or LV_VIEW_SMALLICON */ 5869 return; 5870 5871 nItemCol = nItem / nPerCol; 5872 nItemRow = nItem % nPerCol; 5873 LISTVIEW_GetOrigin(infoPtr, &Origin); 5874 5875 /* move the items below up a slot */ 5876 rcScroll.left = nItemCol * infoPtr->nItemWidth; 5877 rcScroll.top = nItemRow * infoPtr->nItemHeight; 5878 rcScroll.right = rcScroll.left + infoPtr->nItemWidth; 5879 rcScroll.bottom = nPerCol * infoPtr->nItemHeight; 5880 OffsetRect(&rcScroll, Origin.x, Origin.y); 5881 TRACE("rcScroll=%s, dx=%d\n", wine_dbgstr_rect(&rcScroll), dir * infoPtr->nItemHeight); 5882 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList)) 5883 { 5884 TRACE("Invalidating rcScroll=%s, rcList=%s\n", wine_dbgstr_rect(&rcScroll), wine_dbgstr_rect(&infoPtr->rcList)); 5885 InvalidateRect(infoPtr->hwndSelf, &rcScroll, TRUE); 5886 } 5887 5888 /* report has only that column, so we're done */ 5889 if (infoPtr->uView == LV_VIEW_DETAILS) return; 5890 5891 /* now for LISTs, we have to deal with the columns to the right */ 5892 SetRect(&rcScroll, (nItemCol + 1) * infoPtr->nItemWidth, 0, 5893 (infoPtr->nItemCount / nPerCol + 1) * infoPtr->nItemWidth, 5894 nPerCol * infoPtr->nItemHeight); 5895 OffsetRect(&rcScroll, Origin.x, Origin.y); 5896 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList)) 5897 InvalidateRect(infoPtr->hwndSelf, &rcScroll, TRUE); 5898 } 5899 5900 /*** 5901 * DESCRIPTION: 5902 * Removes an item from the listview control. 5903 * 5904 * PARAMETER(S): 5905 * [I] infoPtr : valid pointer to the listview structure 5906 * [I] nItem : item index 5907 * 5908 * RETURN: 5909 * SUCCESS : TRUE 5910 * FAILURE : FALSE 5911 */ 5912 static BOOL LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem) 5913 { 5914 LVITEMW item; 5915 const BOOL is_icon = (infoPtr->uView == LV_VIEW_SMALLICON || infoPtr->uView == LV_VIEW_ICON); 5916 INT focus = infoPtr->nFocusedItem; 5917 5918 TRACE("(nItem=%d)\n", nItem); 5919 5920 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE; 5921 5922 /* remove selection, and focus */ 5923 item.state = 0; 5924 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED; 5925 LISTVIEW_SetItemState(infoPtr, nItem, &item); 5926 5927 /* send LVN_DELETEITEM notification. */ 5928 if (!notify_deleteitem(infoPtr, nItem)) return FALSE; 5929 5930 /* we need to do this here, because we'll be deleting stuff */ 5931 if (is_icon) 5932 LISTVIEW_InvalidateItem(infoPtr, nItem); 5933 5934 if (!(infoPtr->dwStyle & LVS_OWNERDATA)) 5935 { 5936 HDPA hdpaSubItems; 5937 ITEMHDR *hdrItem; 5938 ITEM_INFO *lpItem; 5939 ITEM_ID *lpID; 5940 INT i; 5941 5942 hdpaSubItems = DPA_DeletePtr(infoPtr->hdpaItems, nItem); 5943 lpItem = DPA_GetPtr(hdpaSubItems, 0); 5944 5945 /* free id struct */ 5946 i = DPA_GetPtrIndex(infoPtr->hdpaItemIds, lpItem->id); 5947 lpID = DPA_GetPtr(infoPtr->hdpaItemIds, i); 5948 DPA_DeletePtr(infoPtr->hdpaItemIds, i); 5949 Free(lpID); 5950 for (i = 0; i < DPA_GetPtrCount(hdpaSubItems); i++) 5951 { 5952 hdrItem = DPA_GetPtr(hdpaSubItems, i); 5953 if (is_text(hdrItem->pszText)) Free(hdrItem->pszText); 5954 Free(hdrItem); 5955 } 5956 DPA_Destroy(hdpaSubItems); 5957 } 5958 5959 if (is_icon) 5960 { 5961 DPA_DeletePtr(infoPtr->hdpaPosX, nItem); 5962 DPA_DeletePtr(infoPtr->hdpaPosY, nItem); 5963 } 5964 5965 infoPtr->nItemCount--; 5966 LISTVIEW_ShiftIndices(infoPtr, nItem, -1); 5967 LISTVIEW_ShiftFocus(infoPtr, focus, nItem, -1); 5968 5969 /* now is the invalidation fun */ 5970 if (!is_icon) 5971 LISTVIEW_ScrollOnInsert(infoPtr, nItem, -1); 5972 return TRUE; 5973 } 5974 5975 5976 /*** 5977 * DESCRIPTION: 5978 * Callback implementation for editlabel control 5979 * 5980 * PARAMETER(S): 5981 * [I] infoPtr : valid pointer to the listview structure 5982 * [I] storeText : store edit box text as item text 5983 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI 5984 * 5985 * RETURN: 5986 * SUCCESS : TRUE 5987 * FAILURE : FALSE 5988 */ 5989 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, BOOL storeText, BOOL isW) 5990 { 5991 HWND hwndSelf = infoPtr->hwndSelf; 5992 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 }; 5993 NMLVDISPINFOW dispInfo; 5994 INT editedItem = infoPtr->nEditLabelItem; 5995 BOOL same; 5996 WCHAR *pszText = NULL; 5997 BOOL res; 5998 5999 if (storeText) 6000 { 6001 DWORD len = isW ? GetWindowTextLengthW(infoPtr->hwndEdit) : GetWindowTextLengthA(infoPtr->hwndEdit); 6002 6003 if (len++) 6004 { 6005 if (!(pszText = Alloc(len * (isW ? sizeof(WCHAR) : sizeof(CHAR))))) 6006 return FALSE; 6007 6008 if (isW) 6009 GetWindowTextW(infoPtr->hwndEdit, pszText, len); 6010 else 6011 GetWindowTextA(infoPtr->hwndEdit, (CHAR*)pszText, len); 6012 } 6013 } 6014 6015 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW); 6016 6017 ZeroMemory(&dispInfo, sizeof(dispInfo)); 6018 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT; 6019 dispInfo.item.iItem = editedItem; 6020 dispInfo.item.iSubItem = 0; 6021 dispInfo.item.stateMask = ~0; 6022 dispInfo.item.pszText = szDispText; 6023 dispInfo.item.cchTextMax = DISP_TEXT_SIZE; 6024 if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) 6025 { 6026 res = FALSE; 6027 goto cleanup; 6028 } 6029 6030 if (isW) 6031 same = (lstrcmpW(dispInfo.item.pszText, pszText) == 0); 6032 else 6033 { 6034 LPWSTR tmp = textdupTtoW(pszText, FALSE); 6035 same = (lstrcmpW(dispInfo.item.pszText, tmp) == 0); 6036 textfreeT(tmp, FALSE); 6037 } 6038 6039 /* add the text from the edit in */ 6040 dispInfo.item.mask |= LVIF_TEXT; 6041 dispInfo.item.pszText = same ? NULL : pszText; 6042 dispInfo.item.cchTextMax = textlenT(dispInfo.item.pszText, isW); 6043 6044 infoPtr->notify_mask &= ~NOTIFY_MASK_END_LABEL_EDIT; 6045 6046 /* Do we need to update the Item Text */ 6047 res = notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW); 6048 6049 infoPtr->notify_mask |= NOTIFY_MASK_END_LABEL_EDIT; 6050 6051 infoPtr->nEditLabelItem = -1; 6052 infoPtr->hwndEdit = 0; 6053 6054 if (!res) goto cleanup; 6055 6056 if (!IsWindow(hwndSelf)) 6057 { 6058 res = FALSE; 6059 goto cleanup; 6060 } 6061 if (!pszText) return TRUE; 6062 if (same) 6063 { 6064 res = TRUE; 6065 goto cleanup; 6066 } 6067 6068 if (!(infoPtr->dwStyle & LVS_OWNERDATA)) 6069 { 6070 HDPA hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, editedItem); 6071 ITEM_INFO* lpItem = DPA_GetPtr(hdpaSubItems, 0); 6072 if (lpItem && lpItem->hdr.pszText == LPSTR_TEXTCALLBACKW) 6073 { 6074 LISTVIEW_InvalidateItem(infoPtr, editedItem); 6075 res = TRUE; 6076 goto cleanup; 6077 } 6078 } 6079 6080 ZeroMemory(&dispInfo, sizeof(dispInfo)); 6081 dispInfo.item.mask = LVIF_TEXT; 6082 dispInfo.item.iItem = editedItem; 6083 dispInfo.item.iSubItem = 0; 6084 dispInfo.item.pszText = pszText; 6085 dispInfo.item.cchTextMax = textlenT(pszText, isW); 6086 res = LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW); 6087 6088 cleanup: 6089 Free(pszText); 6090 6091 return res; 6092 } 6093 6094 /*** 6095 * DESCRIPTION: 6096 * Subclassed edit control windproc function 6097 * 6098 * PARAMETER(S): 6099 * [I] hwnd : the edit window handle 6100 * [I] uMsg : the message that is to be processed 6101 * [I] wParam : first message parameter 6102 * [I] lParam : second message parameter 6103 * [I] isW : TRUE if input is Unicode 6104 * 6105 * RETURN: 6106 * Zero. 6107 */ 6108 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL isW) 6109 { 6110 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(GetParent(hwnd), 0); 6111 BOOL save = TRUE; 6112 6113 TRACE("(hwnd=%p, uMsg=%x, wParam=%lx, lParam=%lx, isW=%d)\n", 6114 hwnd, uMsg, wParam, lParam, isW); 6115 6116 switch (uMsg) 6117 { 6118 case WM_GETDLGCODE: 6119 return DLGC_WANTARROWS | DLGC_WANTALLKEYS; 6120 6121 case WM_DESTROY: 6122 { 6123 WNDPROC editProc = infoPtr->EditWndProc; 6124 infoPtr->EditWndProc = 0; 6125 SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (DWORD_PTR)editProc); 6126 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW); 6127 } 6128 6129 case WM_KEYDOWN: 6130 if (VK_ESCAPE == (INT)wParam) 6131 { 6132 save = FALSE; 6133 break; 6134 } 6135 else if (VK_RETURN == (INT)wParam) 6136 break; 6137 6138 default: 6139 return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW); 6140 } 6141 6142 /* kill the edit */ 6143 if (infoPtr->hwndEdit) 6144 LISTVIEW_EndEditLabelT(infoPtr, save, isW); 6145 6146 SendMessageW(hwnd, WM_CLOSE, 0, 0); 6147 return 0; 6148 } 6149 6150 /*** 6151 * DESCRIPTION: 6152 * Subclassed edit control Unicode windproc function 6153 * 6154 * PARAMETER(S): 6155 * [I] hwnd : the edit window handle 6156 * [I] uMsg : the message that is to be processed 6157 * [I] wParam : first message parameter 6158 * [I] lParam : second message parameter 6159 * 6160 * RETURN: 6161 */ 6162 static LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 6163 { 6164 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE); 6165 } 6166 6167 /*** 6168 * DESCRIPTION: 6169 * Subclassed edit control ANSI windproc function 6170 * 6171 * PARAMETER(S): 6172 * [I] hwnd : the edit window handle 6173 * [I] uMsg : the message that is to be processed 6174 * [I] wParam : first message parameter 6175 * [I] lParam : second message parameter 6176 * 6177 * RETURN: 6178 */ 6179 static LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 6180 { 6181 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE); 6182 } 6183 6184 /*** 6185 * DESCRIPTION: 6186 * Creates a subclassed edit control 6187 * 6188 * PARAMETER(S): 6189 * [I] infoPtr : valid pointer to the listview structure 6190 * [I] text : initial text for the edit 6191 * [I] style : the window style 6192 * [I] isW : TRUE if input is Unicode 6193 * 6194 * RETURN: 6195 */ 6196 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, BOOL isW) 6197 { 6198 static const DWORD style = WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|ES_AUTOHSCROLL|WS_BORDER|WS_VISIBLE; 6199 HINSTANCE hinst = (HINSTANCE)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_HINSTANCE); 6200 HWND hedit; 6201 6202 TRACE("(%p, text=%s, isW=%d)\n", infoPtr, debugtext_t(text, isW), isW); 6203 6204 /* window will be resized and positioned after LVN_BEGINLABELEDIT */ 6205 if (isW) 6206 hedit = CreateWindowW(WC_EDITW, text, style, 0, 0, 0, 0, infoPtr->hwndSelf, 0, hinst, 0); 6207 else 6208 hedit = CreateWindowA(WC_EDITA, (LPCSTR)text, style, 0, 0, 0, 0, infoPtr->hwndSelf, 0, hinst, 0); 6209 6210 if (!hedit) return 0; 6211 6212 infoPtr->EditWndProc = (WNDPROC) 6213 (isW ? SetWindowLongPtrW(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcW) : 6214 SetWindowLongPtrA(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcA) ); 6215 6216 SendMessageW(hedit, WM_SETFONT, (WPARAM)infoPtr->hFont, FALSE); 6217 SendMessageW(hedit, EM_SETLIMITTEXT, DISP_TEXT_SIZE-1, 0); 6218 6219 return hedit; 6220 } 6221 6222 /*** 6223 * DESCRIPTION: 6224 * Begin in place editing of specified list view item 6225 * 6226 * PARAMETER(S): 6227 * [I] infoPtr : valid pointer to the listview structure 6228 * [I] nItem : item index 6229 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII 6230 * 6231 * RETURN: 6232 * SUCCESS : TRUE 6233 * FAILURE : FALSE 6234 */ 6235 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW) 6236 { 6237 WCHAR disptextW[DISP_TEXT_SIZE] = { 0 }; 6238 HWND hwndSelf = infoPtr->hwndSelf; 6239 NMLVDISPINFOW dispInfo; 6240 HFONT hOldFont = NULL; 6241 TEXTMETRICW tm; 6242 RECT rect; 6243 SIZE sz; 6244 HDC hdc; 6245 6246 TRACE("(nItem=%d, isW=%d)\n", nItem, isW); 6247 6248 if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0; 6249 6250 /* remove existing edit box */ 6251 if (infoPtr->hwndEdit) 6252 { 6253 SetFocus(infoPtr->hwndSelf); 6254 infoPtr->hwndEdit = 0; 6255 } 6256 6257 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0; 6258 6259 infoPtr->nEditLabelItem = nItem; 6260 6261 LISTVIEW_SetSelection(infoPtr, nItem); 6262 LISTVIEW_SetItemFocus(infoPtr, nItem); 6263 LISTVIEW_InvalidateItem(infoPtr, nItem); 6264 6265 rect.left = LVIR_LABEL; 6266 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0; 6267 6268 ZeroMemory(&dispInfo, sizeof(dispInfo)); 6269 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT; 6270 dispInfo.item.iItem = nItem; 6271 dispInfo.item.iSubItem = 0; 6272 dispInfo.item.stateMask = ~0; 6273 dispInfo.item.pszText = disptextW; 6274 dispInfo.item.cchTextMax = DISP_TEXT_SIZE; 6275 if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0; 6276 6277 infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, isW); 6278 if (!infoPtr->hwndEdit) return 0; 6279 6280 if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW)) 6281 { 6282 if (!IsWindow(hwndSelf)) 6283 return 0; 6284 SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0); 6285 infoPtr->hwndEdit = 0; 6286 return 0; 6287 } 6288 6289 TRACE("disp text=%s\n", debugtext_t(dispInfo.item.pszText, isW)); 6290 6291 /* position and display edit box */ 6292 hdc = GetDC(infoPtr->hwndSelf); 6293 6294 /* select the font to get appropriate metric dimensions */ 6295 if (infoPtr->hFont) 6296 hOldFont = SelectObject(hdc, infoPtr->hFont); 6297 6298 /* use real edit box content, it could be altered during LVN_BEGINLABELEDIT notification */ 6299 GetWindowTextW(infoPtr->hwndEdit, disptextW, DISP_TEXT_SIZE); 6300 TRACE("edit box text=%s\n", debugstr_w(disptextW)); 6301 6302 /* get string length in pixels */ 6303 GetTextExtentPoint32W(hdc, disptextW, lstrlenW(disptextW), &sz); 6304 6305 /* add extra spacing for the next character */ 6306 GetTextMetricsW(hdc, &tm); 6307 sz.cx += tm.tmMaxCharWidth * 2; 6308 6309 if (infoPtr->hFont) 6310 SelectObject(hdc, hOldFont); 6311 6312 ReleaseDC(infoPtr->hwndSelf, hdc); 6313 6314 sz.cy = rect.bottom - rect.top + 2; 6315 rect.left -= 2; 6316 rect.top -= 1; 6317 TRACE("moving edit=(%d,%d)-(%d,%d)\n", rect.left, rect.top, sz.cx, sz.cy); 6318 MoveWindow(infoPtr->hwndEdit, rect.left, rect.top, sz.cx, sz.cy, FALSE); 6319 ShowWindow(infoPtr->hwndEdit, SW_NORMAL); 6320 SetFocus(infoPtr->hwndEdit); 6321 SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1); 6322 return infoPtr->hwndEdit; 6323 } 6324 6325 6326 /*** 6327 * DESCRIPTION: 6328 * Ensures the specified item is visible, scrolling into view if necessary. 6329 * 6330 * PARAMETER(S): 6331 * [I] infoPtr : valid pointer to the listview structure 6332 * [I] nItem : item index 6333 * [I] bPartial : partially or entirely visible 6334 * 6335 * RETURN: 6336 * SUCCESS : TRUE 6337 * FAILURE : FALSE 6338 */ 6339 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial) 6340 { 6341 INT nScrollPosHeight = 0; 6342 INT nScrollPosWidth = 0; 6343 INT nHorzAdjust = 0; 6344 INT nVertAdjust = 0; 6345 INT nHorzDiff = 0; 6346 INT nVertDiff = 0; 6347 RECT rcItem, rcTemp; 6348 6349 rcItem.left = LVIR_BOUNDS; 6350 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE; 6351 6352 if (bPartial && IntersectRect(&rcTemp, &infoPtr->rcList, &rcItem)) return TRUE; 6353 6354 if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right) 6355 { 6356 /* scroll left/right, but in LV_VIEW_DETAILS mode */ 6357 if (infoPtr->uView == LV_VIEW_LIST) 6358 nScrollPosWidth = infoPtr->nItemWidth; 6359 else if ((infoPtr->uView == LV_VIEW_SMALLICON) || (infoPtr->uView == LV_VIEW_ICON)) 6360 nScrollPosWidth = 1; 6361 6362 if (rcItem.left < infoPtr->rcList.left) 6363 { 6364 nHorzAdjust = -1; 6365 if (infoPtr->uView != LV_VIEW_DETAILS) nHorzDiff = rcItem.left - infoPtr->rcList.left; 6366 } 6367 else 6368 { 6369 nHorzAdjust = 1; 6370 if (infoPtr->uView != LV_VIEW_DETAILS) nHorzDiff = rcItem.right - infoPtr->rcList.right; 6371 } 6372 } 6373 6374 if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom) 6375 { 6376 /* scroll up/down, but not in LVS_LIST mode */ 6377 if (infoPtr->uView == LV_VIEW_DETAILS) 6378 nScrollPosHeight = infoPtr->nItemHeight; 6379 else if ((infoPtr->uView == LV_VIEW_ICON) || (infoPtr->uView == LV_VIEW_SMALLICON)) 6380 nScrollPosHeight = 1; 6381 6382 if (rcItem.top < infoPtr->rcList.top) 6383 { 6384 nVertAdjust = -1; 6385 if (infoPtr->uView != LV_VIEW_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top; 6386 } 6387 else 6388 { 6389 nVertAdjust = 1; 6390 if (infoPtr->uView != LV_VIEW_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom; 6391 } 6392 } 6393 6394 if (!nScrollPosWidth && !nScrollPosHeight) return TRUE; 6395 6396 if (nScrollPosWidth) 6397 { 6398 INT diff = nHorzDiff / nScrollPosWidth; 6399 if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust; 6400 LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff); 6401 } 6402 6403 if (nScrollPosHeight) 6404 { 6405 INT diff = nVertDiff / nScrollPosHeight; 6406 if (nVertDiff % nScrollPosHeight) diff += nVertAdjust; 6407 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff); 6408 } 6409 6410 return TRUE; 6411 } 6412 6413 /*** 6414 * DESCRIPTION: 6415 * Searches for an item with specific characteristics. 6416 * 6417 * PARAMETER(S): 6418 * [I] hwnd : window handle 6419 * [I] nStart : base item index 6420 * [I] lpFindInfo : item information to look for 6421 * 6422 * RETURN: 6423 * SUCCESS : index of item 6424 * FAILURE : -1 6425 */ 6426 static INT LISTVIEW_FindItemW(const LISTVIEW_INFO *infoPtr, INT nStart, 6427 const LVFINDINFOW *lpFindInfo) 6428 { 6429 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' }; 6430 BOOL bWrap = FALSE, bNearest = FALSE; 6431 INT nItem = nStart + 1, nLast = infoPtr->nItemCount, nNearestItem = -1; 6432 ULONG xdist, ydist, dist, mindist = 0x7fffffff; 6433 POINT Position, Destination; 6434 LVITEMW lvItem; 6435 6436 /* Search in virtual listviews should be done by application, not by 6437 listview control, so we just send LVN_ODFINDITEMW and return the result */ 6438 if (infoPtr->dwStyle & LVS_OWNERDATA) 6439 { 6440 NMLVFINDITEMW nmlv; 6441 6442 nmlv.iStart = nStart; 6443 nmlv.lvfi = *lpFindInfo; 6444 return notify_hdr(infoPtr, LVN_ODFINDITEMW, (LPNMHDR)&nmlv.hdr); 6445 } 6446 6447 if (!lpFindInfo || nItem < 0) return -1; 6448 6449 lvItem.mask = 0; 6450 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL | LVFI_SUBSTRING)) 6451 { 6452 lvItem.mask |= LVIF_TEXT; 6453 lvItem.pszText = szDispText; 6454 lvItem.cchTextMax = DISP_TEXT_SIZE; 6455 } 6456 6457 if (lpFindInfo->flags & LVFI_WRAP) 6458 bWrap = TRUE; 6459 6460 if ((lpFindInfo->flags & LVFI_NEARESTXY) && 6461 (infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON)) 6462 { 6463 POINT Origin; 6464 RECT rcArea; 6465 6466 LISTVIEW_GetOrigin(infoPtr, &Origin); 6467 Destination.x = lpFindInfo->pt.x - Origin.x; 6468 Destination.y = lpFindInfo->pt.y - Origin.y; 6469 switch(lpFindInfo->vkDirection) 6470 { 6471 case VK_DOWN: Destination.y += infoPtr->nItemHeight; break; 6472 case VK_UP: Destination.y -= infoPtr->nItemHeight; break; 6473 case VK_RIGHT: Destination.x += infoPtr->nItemWidth; break; 6474 case VK_LEFT: Destination.x -= infoPtr->nItemWidth; break; 6475 case VK_HOME: Destination.x = Destination.y = 0; break; 6476 case VK_NEXT: Destination.y += infoPtr->rcList.bottom - infoPtr->rcList.top; break; 6477 case VK_PRIOR: Destination.y -= infoPtr->rcList.bottom - infoPtr->rcList.top; break; 6478 case VK_END: 6479 LISTVIEW_GetAreaRect(infoPtr, &rcArea); 6480 Destination.x = rcArea.right; 6481 Destination.y = rcArea.bottom; 6482 break; 6483 default: ERR("Unknown vkDirection=%d\n", lpFindInfo->vkDirection); 6484 } 6485 bNearest = TRUE; 6486 } 6487 else Destination.x = Destination.y = 0; 6488 6489 /* if LVFI_PARAM is specified, all other flags are ignored */ 6490 if (lpFindInfo->flags & LVFI_PARAM) 6491 { 6492 lvItem.mask |= LVIF_PARAM; 6493 bNearest = FALSE; 6494 lvItem.mask &= ~LVIF_TEXT; 6495 } 6496 6497 nItem = bNearest ? -1 : nStart + 1; 6498 6499 again: 6500 for (; nItem < nLast; nItem++) 6501 { 6502 lvItem.iItem = nItem; 6503 lvItem.iSubItem = 0; 6504 lvItem.pszText = szDispText; 6505 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue; 6506 6507 if (lvItem.mask & LVIF_PARAM) 6508 { 6509 if (lpFindInfo->lParam == lvItem.lParam) 6510 return nItem; 6511 else 6512 continue; 6513 } 6514 6515 if (lvItem.mask & LVIF_TEXT) 6516 { 6517 if (lpFindInfo->flags & (LVFI_PARTIAL | LVFI_SUBSTRING)) 6518 { 6519 WCHAR *p = wcsstr(lvItem.pszText, lpFindInfo->psz); 6520 if (!p || p != lvItem.pszText) continue; 6521 } 6522 else 6523 { 6524 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0) continue; 6525 } 6526 } 6527 6528 if (!bNearest) return nItem; 6529 6530 /* This is very inefficient. To do a good job here, 6531 * we need a sorted array of (x,y) item positions */ 6532 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position); 6533 6534 /* compute the distance^2 to the destination */ 6535 xdist = Destination.x - Position.x; 6536 ydist = Destination.y - Position.y; 6537 dist = xdist * xdist + ydist * ydist; 6538 6539 /* remember the distance, and item if it's closer */ 6540 if (dist < mindist) 6541 { 6542 mindist = dist; 6543 nNearestItem = nItem; 6544 } 6545 } 6546 6547 if (bWrap) 6548 { 6549 nItem = 0; 6550 nLast = min(nStart + 1, infoPtr->nItemCount); 6551 bWrap = FALSE; 6552 goto again; 6553 } 6554 6555 return nNearestItem; 6556 } 6557 6558 /*** 6559 * DESCRIPTION: 6560 * Searches for an item with specific characteristics. 6561 * 6562 * PARAMETER(S): 6563 * [I] hwnd : window handle 6564 * [I] nStart : base item index 6565 * [I] lpFindInfo : item information to look for 6566 * 6567 * RETURN: 6568 * SUCCESS : index of item 6569 * FAILURE : -1 6570 */ 6571 static INT LISTVIEW_FindItemA(const LISTVIEW_INFO *infoPtr, INT nStart, 6572 const LVFINDINFOA *lpFindInfo) 6573 { 6574 LVFINDINFOW fiw; 6575 INT res; 6576 LPWSTR strW = NULL; 6577 6578 memcpy(&fiw, lpFindInfo, sizeof(fiw)); 6579 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL | LVFI_SUBSTRING)) 6580 fiw.psz = strW = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE); 6581 res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw); 6582 textfreeT(strW, FALSE); 6583 return res; 6584 } 6585 6586 /*** 6587 * DESCRIPTION: 6588 * Retrieves column attributes. 6589 * 6590 * PARAMETER(S): 6591 * [I] infoPtr : valid pointer to the listview structure 6592 * [I] nColumn : column index 6593 * [IO] lpColumn : column information 6594 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW 6595 * otherwise it is in fact a LPLVCOLUMNA 6596 * 6597 * RETURN: 6598 * SUCCESS : TRUE 6599 * FAILURE : FALSE 6600 */ 6601 static BOOL LISTVIEW_GetColumnT(const LISTVIEW_INFO *infoPtr, INT nColumn, LPLVCOLUMNW lpColumn, BOOL isW) 6602 { 6603 COLUMN_INFO *lpColumnInfo; 6604 HDITEMW hdi; 6605 6606 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE; 6607 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn); 6608 6609 /* initialize memory */ 6610 ZeroMemory(&hdi, sizeof(hdi)); 6611 6612 if (lpColumn->mask & LVCF_TEXT) 6613 { 6614 hdi.mask |= HDI_TEXT; 6615 hdi.pszText = lpColumn->pszText; 6616 hdi.cchTextMax = lpColumn->cchTextMax; 6617 } 6618 6619 if (lpColumn->mask & LVCF_IMAGE) 6620 hdi.mask |= HDI_IMAGE; 6621 6622 if (lpColumn->mask & LVCF_ORDER) 6623 hdi.mask |= HDI_ORDER; 6624 6625 if (lpColumn->mask & LVCF_SUBITEM) 6626 hdi.mask |= HDI_LPARAM; 6627 6628 if (!SendMessageW(infoPtr->hwndHeader, isW ? HDM_GETITEMW : HDM_GETITEMA, nColumn, (LPARAM)&hdi)) return FALSE; 6629 6630 if (lpColumn->mask & LVCF_FMT) 6631 lpColumn->fmt = lpColumnInfo->fmt; 6632 6633 if (lpColumn->mask & LVCF_WIDTH) 6634 lpColumn->cx = lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left; 6635 6636 if (lpColumn->mask & LVCF_IMAGE) 6637 lpColumn->iImage = hdi.iImage; 6638 6639 if (lpColumn->mask & LVCF_ORDER) 6640 lpColumn->iOrder = hdi.iOrder; 6641 6642 if (lpColumn->mask & LVCF_SUBITEM) 6643 lpColumn->iSubItem = hdi.lParam; 6644 6645 if (lpColumn->mask & LVCF_MINWIDTH) 6646 lpColumn->cxMin = lpColumnInfo->cxMin; 6647 6648 return TRUE; 6649 } 6650 6651 static inline BOOL LISTVIEW_GetColumnOrderArray(const LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray) 6652 { 6653 if (!infoPtr->hwndHeader) return FALSE; 6654 return SendMessageW(infoPtr->hwndHeader, HDM_GETORDERARRAY, iCount, (LPARAM)lpiArray); 6655 } 6656 6657 /*** 6658 * DESCRIPTION: 6659 * Retrieves the column width. 6660 * 6661 * PARAMETER(S): 6662 * [I] infoPtr : valid pointer to the listview structure 6663 * [I] int : column index 6664 * 6665 * RETURN: 6666 * SUCCESS : column width 6667 * FAILURE : zero 6668 */ 6669 static INT LISTVIEW_GetColumnWidth(const LISTVIEW_INFO *infoPtr, INT nColumn) 6670 { 6671 INT nColumnWidth = 0; 6672 HDITEMW hdItem; 6673 6674 TRACE("nColumn=%d\n", nColumn); 6675 6676 /* we have a 'column' in LIST and REPORT mode only */ 6677 switch(infoPtr->uView) 6678 { 6679 case LV_VIEW_LIST: 6680 nColumnWidth = infoPtr->nItemWidth; 6681 break; 6682 case LV_VIEW_DETAILS: 6683 /* We are not using LISTVIEW_GetHeaderRect as this data is updated only after a HDN_ITEMCHANGED. 6684 * There is an application that subclasses the listview, calls LVM_GETCOLUMNWIDTH in the 6685 * HDN_ITEMCHANGED handler and goes into infinite recursion if it receives old data. 6686 */ 6687 hdItem.mask = HDI_WIDTH; 6688 if (!SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, nColumn, (LPARAM)&hdItem)) 6689 { 6690 WARN("(%p): HDM_GETITEMW failed for item %d\n", infoPtr->hwndSelf, nColumn); 6691 return 0; 6692 } 6693 nColumnWidth = hdItem.cxy; 6694 break; 6695 } 6696 6697 TRACE("nColumnWidth=%d\n", nColumnWidth); 6698 return nColumnWidth; 6699 } 6700 6701 /*** 6702 * DESCRIPTION: 6703 * In list or report display mode, retrieves the number of items that can fit 6704 * vertically in the visible area. In icon or small icon display mode, 6705 * retrieves the total number of visible items. 6706 * 6707 * PARAMETER(S): 6708 * [I] infoPtr : valid pointer to the listview structure 6709 * 6710 * RETURN: 6711 * Number of fully visible items. 6712 */ 6713 static INT LISTVIEW_GetCountPerPage(const LISTVIEW_INFO *infoPtr) 6714 { 6715 switch (infoPtr->uView) 6716 { 6717 case LV_VIEW_ICON: 6718 case LV_VIEW_SMALLICON: 6719 return infoPtr->nItemCount; 6720 case LV_VIEW_DETAILS: 6721 return LISTVIEW_GetCountPerColumn(infoPtr); 6722 case LV_VIEW_LIST: 6723 return LISTVIEW_GetCountPerRow(infoPtr) * LISTVIEW_GetCountPerColumn(infoPtr); 6724 } 6725 assert(FALSE); 6726 return 0; 6727 } 6728 6729 /*** 6730 * DESCRIPTION: 6731 * Retrieves an image list handle. 6732 * 6733 * PARAMETER(S): 6734 * [I] infoPtr : valid pointer to the listview structure 6735 * [I] nImageList : image list identifier 6736 * 6737 * RETURN: 6738 * SUCCESS : image list handle 6739 * FAILURE : NULL 6740 */ 6741 static HIMAGELIST LISTVIEW_GetImageList(const LISTVIEW_INFO *infoPtr, INT nImageList) 6742 { 6743 switch (nImageList) 6744 { 6745 case LVSIL_NORMAL: return infoPtr->himlNormal; 6746 case LVSIL_SMALL: return infoPtr->himlSmall; 6747 case LVSIL_STATE: return infoPtr->himlState; 6748 case LVSIL_GROUPHEADER: 6749 FIXME("LVSIL_GROUPHEADER not supported\n"); 6750 break; 6751 default: 6752 WARN("got unknown imagelist index - %d\n", nImageList); 6753 } 6754 return NULL; 6755 } 6756 6757 /* LISTVIEW_GetISearchString */ 6758 6759 /*** 6760 * DESCRIPTION: 6761 * Retrieves item attributes. 6762 * 6763 * PARAMETER(S): 6764 * [I] hwnd : window handle 6765 * [IO] lpLVItem : item info 6766 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW, 6767 * if FALSE, then lpLVItem is a LPLVITEMA. 6768 * 6769 * NOTE: 6770 * This is the internal 'GetItem' interface -- it tries to 6771 * be smart and avoid text copies, if possible, by modifying 6772 * lpLVItem->pszText to point to the text string. Please note 6773 * that this is not always possible (e.g. OWNERDATA), so on 6774 * entry you *must* supply valid values for pszText, and cchTextMax. 6775 * The only difference to the documented interface is that upon 6776 * return, you should use *only* the lpLVItem->pszText, rather than 6777 * the buffer pointer you provided on input. Most code already does 6778 * that, so it's not a problem. 6779 * For the two cases when the text must be copied (that is, 6780 * for LVM_GETITEM, and LVM_GETITEMTEXT), use LISTVIEW_GetItemExtT. 6781 * 6782 * RETURN: 6783 * SUCCESS : TRUE 6784 * FAILURE : FALSE 6785 */ 6786 static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW) 6787 { 6788 ITEMHDR callbackHdr = { LPSTR_TEXTCALLBACKW, I_IMAGECALLBACK }; 6789 NMLVDISPINFOW dispInfo; 6790 ITEM_INFO *lpItem; 6791 ITEMHDR* pItemHdr; 6792 HDPA hdpaSubItems; 6793 INT isubitem; 6794 6795 TRACE("(item=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW); 6796 6797 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount) 6798 return FALSE; 6799 6800 if (lpLVItem->mask == 0) return TRUE; 6801 TRACE("mask=%x\n", lpLVItem->mask); 6802 6803 /* make a local copy */ 6804 isubitem = lpLVItem->iSubItem; 6805 6806 if (isubitem && (lpLVItem->mask & LVIF_STATE)) 6807 lpLVItem->state = 0; 6808 6809 /* a quick optimization if all we're asked is the focus state 6810 * these queries are worth optimising since they are common, 6811 * and can be answered in constant time, without the heavy accesses */ 6812 if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIS_FOCUSED) && 6813 !(infoPtr->uCallbackMask & LVIS_FOCUSED) ) 6814 { 6815 lpLVItem->state = 0; 6816 if (infoPtr->nFocusedItem == lpLVItem->iItem && isubitem == 0) 6817 lpLVItem->state |= LVIS_FOCUSED; 6818 return TRUE; 6819 } 6820 6821 ZeroMemory(&dispInfo, sizeof(dispInfo)); 6822 6823 /* if the app stores all the data, handle it separately */ 6824 if (infoPtr->dwStyle & LVS_OWNERDATA) 6825 { 6826 dispInfo.item.state = 0; 6827 6828 /* apparently, we should not callback for lParam in LVS_OWNERDATA */ 6829 if ((lpLVItem->mask & ~(LVIF_STATE | LVIF_PARAM)) || 6830 ((lpLVItem->mask & LVIF_STATE) && (infoPtr->uCallbackMask & lpLVItem->stateMask))) 6831 { 6832 UINT mask = lpLVItem->mask; 6833 6834 /* NOTE: copy only fields which we _know_ are initialized, some apps 6835 * depend on the uninitialized fields being 0 */ 6836 dispInfo.item.mask = lpLVItem->mask & ~LVIF_PARAM; 6837 dispInfo.item.iItem = lpLVItem->iItem; 6838 dispInfo.item.iSubItem = isubitem; 6839 if (lpLVItem->mask & LVIF_TEXT) 6840 { 6841 if (lpLVItem->mask & LVIF_NORECOMPUTE) 6842 /* reset mask */ 6843 dispInfo.item.mask &= ~(LVIF_TEXT | LVIF_NORECOMPUTE); 6844 else 6845 { 6846 dispInfo.item.pszText = lpLVItem->pszText; 6847 dispInfo.item.cchTextMax = lpLVItem->cchTextMax; 6848 } 6849 } 6850 if (lpLVItem->mask & LVIF_STATE) 6851 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask; 6852 /* could be zeroed on LVIF_NORECOMPUTE case */ 6853 if (dispInfo.item.mask) 6854 { 6855 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW); 6856 dispInfo.item.stateMask = lpLVItem->stateMask; 6857 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS)) 6858 { 6859 /* full size structure expected - _WIN32IE >= 0x560 */ 6860 *lpLVItem = dispInfo.item; 6861 } 6862 else if (lpLVItem->mask & LVIF_INDENT) 6863 { 6864 /* indent member expected - _WIN32IE >= 0x300 */ 6865 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iGroupId )); 6866 } 6867 else 6868 { 6869 /* minimal structure expected */ 6870 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iIndent )); 6871 } 6872 lpLVItem->mask = mask; 6873 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW)); 6874 } 6875 } 6876 6877 /* make sure lParam is zeroed out */ 6878 if (lpLVItem->mask & LVIF_PARAM) lpLVItem->lParam = 0; 6879 6880 /* callback marked pointer required here */ 6881 if ((lpLVItem->mask & LVIF_TEXT) && (lpLVItem->mask & LVIF_NORECOMPUTE)) 6882 lpLVItem->pszText = LPSTR_TEXTCALLBACKW; 6883 6884 /* we store only a little state, so if we're not asked, we're done */ 6885 if (!(lpLVItem->mask & LVIF_STATE) || isubitem) return TRUE; 6886 6887 /* if focus is handled by us, report it */ 6888 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED ) 6889 { 6890 lpLVItem->state &= ~LVIS_FOCUSED; 6891 if (infoPtr->nFocusedItem == lpLVItem->iItem) 6892 lpLVItem->state |= LVIS_FOCUSED; 6893 } 6894 6895 /* and do the same for selection, if we handle it */ 6896 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED ) 6897 { 6898 lpLVItem->state &= ~LVIS_SELECTED; 6899 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem)) 6900 lpLVItem->state |= LVIS_SELECTED; 6901 } 6902 6903 return TRUE; 6904 } 6905 6906 /* find the item and subitem structures before we proceed */ 6907 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem); 6908 lpItem = DPA_GetPtr(hdpaSubItems, 0); 6909 assert (lpItem); 6910 6911 if (isubitem) 6912 { 6913 SUBITEM_INFO *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, isubitem); 6914 pItemHdr = lpSubItem ? &lpSubItem->hdr : &callbackHdr; 6915 if (!lpSubItem) 6916 { 6917 WARN(" iSubItem invalid (%08x), ignored.\n", isubitem); 6918 isubitem = 0; 6919 } 6920 } 6921 else 6922 pItemHdr = &lpItem->hdr; 6923 6924 /* Do we need to query the state from the app? */ 6925 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && isubitem == 0) 6926 { 6927 dispInfo.item.mask |= LVIF_STATE; 6928 dispInfo.item.stateMask = infoPtr->uCallbackMask; 6929 } 6930 6931 /* Do we need to enquire about the image? */ 6932 if ((lpLVItem->mask & LVIF_IMAGE) && pItemHdr->iImage == I_IMAGECALLBACK && 6933 (isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES))) 6934 { 6935 dispInfo.item.mask |= LVIF_IMAGE; 6936 dispInfo.item.iImage = I_IMAGECALLBACK; 6937 } 6938 6939 /* Only items support indentation */ 6940 if ((lpLVItem->mask & LVIF_INDENT) && lpItem->iIndent == I_INDENTCALLBACK && 6941 (isubitem == 0)) 6942 { 6943 dispInfo.item.mask |= LVIF_INDENT; 6944 dispInfo.item.iIndent = I_INDENTCALLBACK; 6945 } 6946 6947 /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */ 6948 if ((lpLVItem->mask & LVIF_TEXT) && !(lpLVItem->mask & LVIF_NORECOMPUTE) && 6949 !is_text(pItemHdr->pszText)) 6950 { 6951 dispInfo.item.mask |= LVIF_TEXT; 6952 dispInfo.item.pszText = lpLVItem->pszText; 6953 dispInfo.item.cchTextMax = lpLVItem->cchTextMax; 6954 if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0) 6955 *dispInfo.item.pszText = '\0'; 6956 } 6957 6958 /* If we don't have all the requested info, query the application */ 6959 if (dispInfo.item.mask) 6960 { 6961 dispInfo.item.iItem = lpLVItem->iItem; 6962 dispInfo.item.iSubItem = lpLVItem->iSubItem; /* yes: the original subitem */ 6963 dispInfo.item.lParam = lpItem->lParam; 6964 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW); 6965 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW)); 6966 } 6967 6968 /* we should not store values for subitems */ 6969 if (isubitem) dispInfo.item.mask &= ~LVIF_DI_SETITEM; 6970 6971 /* Now, handle the iImage field */ 6972 if (dispInfo.item.mask & LVIF_IMAGE) 6973 { 6974 lpLVItem->iImage = dispInfo.item.iImage; 6975 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->iImage == I_IMAGECALLBACK) 6976 pItemHdr->iImage = dispInfo.item.iImage; 6977 } 6978 else if (lpLVItem->mask & LVIF_IMAGE) 6979 { 6980 if(isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES)) 6981 lpLVItem->iImage = pItemHdr->iImage; 6982 else 6983 lpLVItem->iImage = 0; 6984 } 6985 6986 /* The pszText field */ 6987 if (dispInfo.item.mask & LVIF_TEXT) 6988 { 6989 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText) 6990 textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW); 6991 6992 lpLVItem->pszText = dispInfo.item.pszText; 6993 } 6994 else if (lpLVItem->mask & LVIF_TEXT) 6995 { 6996 /* if LVN_GETDISPINFO's disabled with LVIF_NORECOMPUTE return callback placeholder */ 6997 if (isW || !is_text(pItemHdr->pszText)) lpLVItem->pszText = pItemHdr->pszText; 6998 else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax); 6999 } 7000 7001 /* Next is the lParam field */ 7002 if (dispInfo.item.mask & LVIF_PARAM) 7003 { 7004 lpLVItem->lParam = dispInfo.item.lParam; 7005 if ((dispInfo.item.mask & LVIF_DI_SETITEM)) 7006 lpItem->lParam = dispInfo.item.lParam; 7007 } 7008 else if (lpLVItem->mask & LVIF_PARAM) 7009 lpLVItem->lParam = lpItem->lParam; 7010 7011 /* if this is a subitem, we're done */ 7012 if (isubitem) return TRUE; 7013 7014 /* ... the state field (this one is different due to uCallbackmask) */ 7015 if (lpLVItem->mask & LVIF_STATE) 7016 { 7017 lpLVItem->state = lpItem->state & lpLVItem->stateMask; 7018 if (dispInfo.item.mask & LVIF_STATE) 7019 { 7020 lpLVItem->state &= ~dispInfo.item.stateMask; 7021 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask); 7022 } 7023 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED ) 7024 { 7025 lpLVItem->state &= ~LVIS_FOCUSED; 7026 if (infoPtr->nFocusedItem == lpLVItem->iItem) 7027 lpLVItem->state |= LVIS_FOCUSED; 7028 } 7029 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED ) 7030 { 7031 lpLVItem->state &= ~LVIS_SELECTED; 7032 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem)) 7033 lpLVItem->state |= LVIS_SELECTED; 7034 } 7035 } 7036 7037 /* and last, but not least, the indent field */ 7038 if (dispInfo.item.mask & LVIF_INDENT) 7039 { 7040 lpLVItem->iIndent = dispInfo.item.iIndent; 7041 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && lpItem->iIndent == I_INDENTCALLBACK) 7042 lpItem->iIndent = dispInfo.item.iIndent; 7043 } 7044 else if (lpLVItem->mask & LVIF_INDENT) 7045 { 7046 lpLVItem->iIndent = lpItem->iIndent; 7047 } 7048 7049 return TRUE; 7050 } 7051 7052 /*** 7053 * DESCRIPTION: 7054 * Retrieves item attributes. 7055 * 7056 * PARAMETER(S): 7057 * [I] hwnd : window handle 7058 * [IO] lpLVItem : item info 7059 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW, 7060 * if FALSE, then lpLVItem is a LPLVITEMA. 7061 * 7062 * NOTE: 7063 * This is the external 'GetItem' interface -- it properly copies 7064 * the text in the provided buffer. 7065 * 7066 * RETURN: 7067 * SUCCESS : TRUE 7068 * FAILURE : FALSE 7069 */ 7070 static BOOL LISTVIEW_GetItemExtT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW) 7071 { 7072 LPWSTR pszText; 7073 BOOL bResult; 7074 7075 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount) 7076 return FALSE; 7077 7078 pszText = lpLVItem->pszText; 7079 bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW); 7080 if (bResult && (lpLVItem->mask & LVIF_TEXT) && lpLVItem->pszText != pszText) 7081 { 7082 if (lpLVItem->pszText != LPSTR_TEXTCALLBACKW) 7083 textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax); 7084 else 7085 pszText = LPSTR_TEXTCALLBACKW; 7086 } 7087 lpLVItem->pszText = pszText; 7088 7089 return bResult; 7090 } 7091 7092 7093 /*** 7094 * DESCRIPTION: 7095 * Retrieves the position (upper-left) of the listview control item. 7096 * Note that for LVS_ICON style, the upper-left is that of the icon 7097 * and not the bounding box. 7098 * 7099 * PARAMETER(S): 7100 * [I] infoPtr : valid pointer to the listview structure 7101 * [I] nItem : item index 7102 * [O] lpptPosition : coordinate information 7103 * 7104 * RETURN: 7105 * SUCCESS : TRUE 7106 * FAILURE : FALSE 7107 */ 7108 static BOOL LISTVIEW_GetItemPosition(const LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition) 7109 { 7110 POINT Origin; 7111 7112 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition); 7113 7114 if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE; 7115 7116 LISTVIEW_GetOrigin(infoPtr, &Origin); 7117 LISTVIEW_GetItemOrigin(infoPtr, nItem, lpptPosition); 7118 7119 if (infoPtr->uView == LV_VIEW_ICON) 7120 { 7121 lpptPosition->x += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2; 7122 lpptPosition->y += ICON_TOP_PADDING; 7123 } 7124 lpptPosition->x += Origin.x; 7125 lpptPosition->y += Origin.y; 7126 7127 TRACE (" lpptPosition=%s\n", wine_dbgstr_point(lpptPosition)); 7128 return TRUE; 7129 } 7130 7131 7132 /*** 7133 * DESCRIPTION: 7134 * Retrieves the bounding rectangle for a listview control item. 7135 * 7136 * PARAMETER(S): 7137 * [I] infoPtr : valid pointer to the listview structure 7138 * [I] nItem : item index 7139 * [IO] lprc : bounding rectangle coordinates 7140 * lprc->left specifies the portion of the item for which the bounding 7141 * rectangle will be retrieved. 7142 * 7143 * LVIR_BOUNDS Returns the bounding rectangle of the entire item, 7144 * including the icon and label. 7145 * * 7146 * * For LVS_ICON 7147 * * Experiment shows that native control returns: 7148 * * width = min (48, length of text line) 7149 * * .left = position.x - (width - iconsize.cx)/2 7150 * * .right = .left + width 7151 * * height = #lines of text * ntmHeight + icon height + 8 7152 * * .top = position.y - 2 7153 * * .bottom = .top + height 7154 * * separation between items .y = itemSpacing.cy - height 7155 * * .x = itemSpacing.cx - width 7156 * LVIR_ICON Returns the bounding rectangle of the icon or small icon. 7157 * * 7158 * * For LVS_ICON 7159 * * Experiment shows that native control returns: 7160 * * width = iconSize.cx + 16 7161 * * .left = position.x - (width - iconsize.cx)/2 7162 * * .right = .left + width 7163 * * height = iconSize.cy + 4 7164 * * .top = position.y - 2 7165 * * .bottom = .top + height 7166 * * separation between items .y = itemSpacing.cy - height 7167 * * .x = itemSpacing.cx - width 7168 * LVIR_LABEL Returns the bounding rectangle of the item text. 7169 * * 7170 * * For LVS_ICON 7171 * * Experiment shows that native control returns: 7172 * * width = text length 7173 * * .left = position.x - width/2 7174 * * .right = .left + width 7175 * * height = ntmH * linecount + 2 7176 * * .top = position.y + iconSize.cy + 6 7177 * * .bottom = .top + height 7178 * * separation between items .y = itemSpacing.cy - height 7179 * * .x = itemSpacing.cx - width 7180 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL 7181 * rectangles, but excludes columns in report view. 7182 * 7183 * RETURN: 7184 * SUCCESS : TRUE 7185 * FAILURE : FALSE 7186 * 7187 * NOTES 7188 * Note that the bounding rectangle of the label in the LVS_ICON view depends 7189 * upon whether the window has the focus currently and on whether the item 7190 * is the one with the focus. Ensure that the control's record of which 7191 * item has the focus agrees with the items' records. 7192 */ 7193 static BOOL LISTVIEW_GetItemRect(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc) 7194 { 7195 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' }; 7196 BOOL doLabel = TRUE, oversizedBox = FALSE; 7197 POINT Position, Origin; 7198 LVITEMW lvItem; 7199 LONG mode; 7200 7201 TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc); 7202 7203 if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE; 7204 7205 LISTVIEW_GetOrigin(infoPtr, &Origin); 7206 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position); 7207 7208 /* Be smart and try to figure out the minimum we have to do */ 7209 if (lprc->left == LVIR_ICON) doLabel = FALSE; 7210 if (infoPtr->uView == LV_VIEW_DETAILS && lprc->left == LVIR_BOUNDS) doLabel = FALSE; 7211 if (infoPtr->uView == LV_VIEW_ICON && lprc->left != LVIR_ICON && 7212 infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED)) 7213 oversizedBox = TRUE; 7214 7215 /* get what we need from the item before hand, so we make 7216 * only one request. This can speed up things, if data 7217 * is stored on the app side */ 7218 lvItem.mask = 0; 7219 if (infoPtr->uView == LV_VIEW_DETAILS) lvItem.mask |= LVIF_INDENT; 7220 if (doLabel) lvItem.mask |= LVIF_TEXT; 7221 lvItem.iItem = nItem; 7222 lvItem.iSubItem = 0; 7223 lvItem.pszText = szDispText; 7224 lvItem.cchTextMax = DISP_TEXT_SIZE; 7225 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE; 7226 /* we got the state already up, simulate it here, to avoid a reget */ 7227 if (infoPtr->uView == LV_VIEW_ICON && (lprc->left != LVIR_ICON)) 7228 { 7229 lvItem.mask |= LVIF_STATE; 7230 lvItem.stateMask = LVIS_FOCUSED; 7231 lvItem.state = (oversizedBox ? LVIS_FOCUSED : 0); 7232 } 7233 7234 if (infoPtr->uView == LV_VIEW_DETAILS && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && lprc->left == LVIR_SELECTBOUNDS) 7235 lprc->left = LVIR_BOUNDS; 7236 7237 mode = lprc->left; 7238 switch(lprc->left) 7239 { 7240 case LVIR_ICON: 7241 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL, NULL); 7242 break; 7243 7244 case LVIR_LABEL: 7245 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, NULL, NULL, lprc); 7246 break; 7247 7248 case LVIR_BOUNDS: 7249 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL, NULL); 7250 break; 7251 7252 case LVIR_SELECTBOUNDS: 7253 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, lprc, NULL, NULL, NULL); 7254 break; 7255 7256 default: 7257 WARN("Unknown value: %d\n", lprc->left); 7258 return FALSE; 7259 } 7260 7261 if (infoPtr->uView == LV_VIEW_DETAILS) 7262 { 7263 if (mode != LVIR_BOUNDS) 7264 OffsetRect(lprc, Origin.x + LISTVIEW_GetColumnInfo(infoPtr, 0)->rcHeader.left, 7265 Position.y + Origin.y); 7266 else 7267 OffsetRect(lprc, Origin.x, Position.y + Origin.y); 7268 } 7269 else 7270 OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y); 7271 7272 TRACE(" rect=%s\n", wine_dbgstr_rect(lprc)); 7273 7274 return TRUE; 7275 } 7276 7277 /*** 7278 * DESCRIPTION: 7279 * Retrieves the spacing between listview control items. 7280 * 7281 * PARAMETER(S): 7282 * [I] infoPtr : valid pointer to the listview structure 7283 * [IO] lprc : rectangle to receive the output 7284 * on input, lprc->top = nSubItem 7285 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL 7286 * 7287 * NOTE: for subItem = 0, we should return the bounds of the _entire_ item, 7288 * not only those of the first column. 7289 * 7290 * RETURN: 7291 * TRUE: success 7292 * FALSE: failure 7293 */ 7294 static BOOL LISTVIEW_GetSubItemRect(const LISTVIEW_INFO *infoPtr, INT item, LPRECT lprc) 7295 { 7296 RECT rect = { 0, 0, 0, 0 }; 7297 POINT origin; 7298 INT y; 7299 7300 if (!lprc) return FALSE; 7301 7302 TRACE("(item=%d, subitem=%d, type=%d)\n", item, lprc->top, lprc->left); 7303 /* Subitem of '0' means item itself, and this works for all control view modes */ 7304 if (lprc->top == 0) 7305 return LISTVIEW_GetItemRect(infoPtr, item, lprc); 7306 7307 if (infoPtr->uView != LV_VIEW_DETAILS) return FALSE; 7308 7309 LISTVIEW_GetOrigin(infoPtr, &origin); 7310 /* this works for any item index, no matter if it exists or not */ 7311 y = item * infoPtr->nItemHeight + origin.y; 7312 7313 if (infoPtr->hwndHeader && SendMessageW(infoPtr->hwndHeader, HDM_GETITEMRECT, lprc->top, (LPARAM)&rect)) 7314 { 7315 rect.top = 0; 7316 rect.bottom = infoPtr->nItemHeight; 7317 } 7318 else 7319 { 7320 /* Native implementation is broken for this case and garbage is left for left and right fields, 7321 we zero them to get predictable output */ 7322 lprc->left = lprc->right = lprc->top = 0; 7323 lprc->bottom = infoPtr->nItemHeight; 7324 OffsetRect(lprc, origin.x, y); 7325 TRACE("return rect %s\n", wine_dbgstr_rect(lprc)); 7326 return TRUE; 7327 } 7328 7329 switch (lprc->left) 7330 { 7331 case LVIR_ICON: 7332 { 7333 /* it doesn't matter if main item actually has an icon, if imagelist is set icon width is returned */ 7334 if (infoPtr->himlSmall) 7335 rect.right = rect.left + infoPtr->iconSize.cx; 7336 else 7337 rect.right = rect.left; 7338 7339 rect.bottom = rect.top + infoPtr->iconSize.cy; 7340 break; 7341 } 7342 case LVIR_LABEL: 7343 case LVIR_BOUNDS: 7344 break; 7345 7346 default: 7347 ERR("Unknown bounds=%d\n", lprc->left); 7348 return FALSE; 7349 } 7350 7351 OffsetRect(&rect, origin.x, y); 7352 *lprc = rect; 7353 TRACE("return rect %s\n", wine_dbgstr_rect(lprc)); 7354 7355 return TRUE; 7356 } 7357 7358 /*** 7359 * DESCRIPTION: 7360 * Retrieves the spacing between listview control items. 7361 * 7362 * PARAMETER(S): 7363 * [I] infoPtr : valid pointer to the listview structure 7364 * [I] bSmall : flag for small or large icon 7365 * 7366 * RETURN: 7367 * Horizontal + vertical spacing 7368 */ 7369 static LONG LISTVIEW_GetItemSpacing(const LISTVIEW_INFO *infoPtr, BOOL bSmall) 7370 { 7371 LONG lResult; 7372 7373 if (!bSmall) 7374 { 7375 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy); 7376 } 7377 else 7378 { 7379 if (infoPtr->uView == LV_VIEW_ICON) 7380 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING); 7381 else 7382 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight); 7383 } 7384 return lResult; 7385 } 7386 7387 /*** 7388 * DESCRIPTION: 7389 * Retrieves the state of a listview control item. 7390 * 7391 * PARAMETER(S): 7392 * [I] infoPtr : valid pointer to the listview structure 7393 * [I] nItem : item index 7394 * [I] uMask : state mask 7395 * 7396 * RETURN: 7397 * State specified by the mask. 7398 */ 7399 static UINT LISTVIEW_GetItemState(const LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask) 7400 { 7401 LVITEMW lvItem; 7402 7403 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0; 7404 7405 lvItem.iItem = nItem; 7406 lvItem.iSubItem = 0; 7407 lvItem.mask = LVIF_STATE; 7408 lvItem.stateMask = uMask; 7409 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0; 7410 7411 return lvItem.state & uMask; 7412 } 7413 7414 /*** 7415 * DESCRIPTION: 7416 * Retrieves the text of a listview control item or subitem. 7417 * 7418 * PARAMETER(S): 7419 * [I] hwnd : window handle 7420 * [I] nItem : item index 7421 * [IO] lpLVItem : item information 7422 * [I] isW : TRUE if lpLVItem is Unicode 7423 * 7424 * RETURN: 7425 * SUCCESS : string length 7426 * FAILURE : 0 7427 */ 7428 static INT LISTVIEW_GetItemTextT(const LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW) 7429 { 7430 if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0; 7431 7432 lpLVItem->mask = LVIF_TEXT; 7433 lpLVItem->iItem = nItem; 7434 if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0; 7435 7436 return textlenT(lpLVItem->pszText, isW); 7437 } 7438 7439 /*** 7440 * DESCRIPTION: 7441 * Searches for an item based on properties + relationships. 7442 * 7443 * PARAMETER(S): 7444 * [I] infoPtr : valid pointer to the listview structure 7445 * [I] nItem : item index 7446 * [I] uFlags : relationship flag 7447 * 7448 * RETURN: 7449 * SUCCESS : item index 7450 * FAILURE : -1 7451 */ 7452 static INT LISTVIEW_GetNextItem(const LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags) 7453 { 7454 UINT uMask = 0; 7455 LVFINDINFOW lvFindInfo; 7456 INT nCountPerColumn; 7457 #ifndef __REACTOS__ 7458 INT nCountPerRow; 7459 #endif 7460 INT i; 7461 7462 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount); 7463 if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1; 7464 7465 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo)); 7466 7467 if (uFlags & LVNI_CUT) 7468 uMask |= LVIS_CUT; 7469 7470 if (uFlags & LVNI_DROPHILITED) 7471 uMask |= LVIS_DROPHILITED; 7472 7473 if (uFlags & LVNI_FOCUSED) 7474 uMask |= LVIS_FOCUSED; 7475 7476 if (uFlags & LVNI_SELECTED) 7477 uMask |= LVIS_SELECTED; 7478 7479 /* if we're asked for the focused item, that's only one, 7480 * so it's worth optimizing */ 7481 if (uFlags & LVNI_FOCUSED) 7482 { 7483 if ((LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) != uMask) return -1; 7484 return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem; 7485 } 7486 7487 if (uFlags & LVNI_ABOVE) 7488 { 7489 if ((infoPtr->uView == LV_VIEW_LIST) || (infoPtr->uView == LV_VIEW_DETAILS)) 7490 { 7491 while (nItem >= 0) 7492 { 7493 nItem--; 7494 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask) 7495 return nItem; 7496 } 7497 } 7498 else 7499 { 7500 #ifndef __REACTOS__ 7501 /* Special case for autoarrange - move 'til the top of a list */ 7502 if (is_autoarrange(infoPtr)) 7503 { 7504 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr); 7505 while (nItem - nCountPerRow >= 0) 7506 { 7507 nItem -= nCountPerRow; 7508 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask) 7509 return nItem; 7510 } 7511 return -1; 7512 } 7513 #endif 7514 lvFindInfo.flags = LVFI_NEARESTXY; 7515 lvFindInfo.vkDirection = VK_UP; 7516 LISTVIEW_GetItemPosition(infoPtr, nItem, &lvFindInfo.pt); 7517 while ((nItem = LISTVIEW_FindItemW(infoPtr, nItem, &lvFindInfo)) != -1) 7518 { 7519 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask) 7520 return nItem; 7521 } 7522 } 7523 } 7524 else if (uFlags & LVNI_BELOW) 7525 { 7526 if ((infoPtr->uView == LV_VIEW_LIST) || (infoPtr->uView == LV_VIEW_DETAILS)) 7527 { 7528 while (nItem < infoPtr->nItemCount) 7529 { 7530 nItem++; 7531 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask) 7532 return nItem; 7533 } 7534 } 7535 else 7536 { 7537 #ifndef __REACTOS__ 7538 /* Special case for autoarrange - move 'til the bottom of a list */ 7539 if (is_autoarrange(infoPtr)) 7540 { 7541 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr); 7542 while (nItem + nCountPerRow < infoPtr->nItemCount ) 7543 { 7544 nItem += nCountPerRow; 7545 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask) 7546 return nItem; 7547 } 7548 return -1; 7549 } 7550 #endif 7551 lvFindInfo.flags = LVFI_NEARESTXY; 7552 lvFindInfo.vkDirection = VK_DOWN; 7553 LISTVIEW_GetItemPosition(infoPtr, nItem, &lvFindInfo.pt); 7554 while ((nItem = LISTVIEW_FindItemW(infoPtr, nItem, &lvFindInfo)) != -1) 7555 { 7556 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask) 7557 return nItem; 7558 } 7559 } 7560 } 7561 else if (uFlags & LVNI_TOLEFT) 7562 { 7563 if (infoPtr->uView == LV_VIEW_LIST) 7564 { 7565 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr); 7566 while (nItem - nCountPerColumn >= 0) 7567 { 7568 nItem -= nCountPerColumn; 7569 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask) 7570 return nItem; 7571 } 7572 } 7573 else if ((infoPtr->uView == LV_VIEW_SMALLICON) || (infoPtr->uView == LV_VIEW_ICON)) 7574 { 7575 #ifndef __REACTOS__ 7576 /* Special case for autoarrange - move 'til the beginning of a row */ 7577 if (is_autoarrange(infoPtr)) 7578 { 7579 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr); 7580 while (nItem % nCountPerRow > 0) 7581 { 7582 nItem --; 7583 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask) 7584 return nItem; 7585 } 7586 return -1; 7587 } 7588 #endif 7589 lvFindInfo.flags = LVFI_NEARESTXY; 7590 lvFindInfo.vkDirection = VK_LEFT; 7591 LISTVIEW_GetItemPosition(infoPtr, nItem, &lvFindInfo.pt); 7592 while ((nItem = LISTVIEW_FindItemW(infoPtr, nItem, &lvFindInfo)) != -1) 7593 { 7594 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask) 7595 return nItem; 7596 } 7597 } 7598 } 7599 else if (uFlags & LVNI_TORIGHT) 7600 { 7601 if (infoPtr->uView == LV_VIEW_LIST) 7602 { 7603 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr); 7604 while (nItem + nCountPerColumn < infoPtr->nItemCount) 7605 { 7606 nItem += nCountPerColumn; 7607 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask) 7608 return nItem; 7609 } 7610 } 7611 else if ((infoPtr->uView == LV_VIEW_SMALLICON) || (infoPtr->uView == LV_VIEW_ICON)) 7612 { 7613 #ifndef __REACTOS__ 7614 /* Special case for autoarrange - move 'til the end of a row */ 7615 if (is_autoarrange(infoPtr)) 7616 { 7617 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr); 7618 while (nItem % nCountPerRow < nCountPerRow - 1 ) 7619 { 7620 nItem ++; 7621 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask) 7622 return nItem; 7623 } 7624 return -1; 7625 } 7626 #endif 7627 lvFindInfo.flags = LVFI_NEARESTXY; 7628 lvFindInfo.vkDirection = VK_RIGHT; 7629 LISTVIEW_GetItemPosition(infoPtr, nItem, &lvFindInfo.pt); 7630 while ((nItem = LISTVIEW_FindItemW(infoPtr, nItem, &lvFindInfo)) != -1) 7631 { 7632 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask) 7633 return nItem; 7634 } 7635 } 7636 } 7637 else 7638 { 7639 nItem++; 7640 7641 /* search by index */ 7642 for (i = nItem; i < infoPtr->nItemCount; i++) 7643 { 7644 if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask) 7645 return i; 7646 } 7647 } 7648 7649 return -1; 7650 } 7651 7652 /* LISTVIEW_GetNumberOfWorkAreas */ 7653 7654 /*** 7655 * DESCRIPTION: 7656 * Retrieves the origin coordinates when in icon or small icon display mode. 7657 * 7658 * PARAMETER(S): 7659 * [I] infoPtr : valid pointer to the listview structure 7660 * [O] lpptOrigin : coordinate information 7661 * 7662 * RETURN: 7663 * None. 7664 */ 7665 static void LISTVIEW_GetOrigin(const LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin) 7666 { 7667 INT nHorzPos = 0, nVertPos = 0; 7668 SCROLLINFO scrollInfo; 7669 7670 scrollInfo.cbSize = sizeof(SCROLLINFO); 7671 scrollInfo.fMask = SIF_POS; 7672 7673 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) 7674 nHorzPos = scrollInfo.nPos; 7675 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) 7676 nVertPos = scrollInfo.nPos; 7677 7678 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos); 7679 7680 lpptOrigin->x = infoPtr->rcList.left; 7681 lpptOrigin->y = infoPtr->rcList.top; 7682 if (infoPtr->uView == LV_VIEW_LIST) 7683 nHorzPos *= infoPtr->nItemWidth; 7684 else if (infoPtr->uView == LV_VIEW_DETAILS) 7685 nVertPos *= infoPtr->nItemHeight; 7686 7687 lpptOrigin->x -= nHorzPos; 7688 lpptOrigin->y -= nVertPos; 7689 7690 TRACE(" origin=%s\n", wine_dbgstr_point(lpptOrigin)); 7691 } 7692 7693 /*** 7694 * DESCRIPTION: 7695 * Retrieves the width of a string. 7696 * 7697 * PARAMETER(S): 7698 * [I] hwnd : window handle 7699 * [I] lpszText : text string to process 7700 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise 7701 * 7702 * RETURN: 7703 * SUCCESS : string width (in pixels) 7704 * FAILURE : zero 7705 */ 7706 static INT LISTVIEW_GetStringWidthT(const LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW) 7707 { 7708 SIZE stringSize; 7709 7710 stringSize.cx = 0; 7711 if (is_text(lpszText)) 7712 { 7713 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont; 7714 HDC hdc = GetDC(infoPtr->hwndSelf); 7715 HFONT hOldFont = SelectObject(hdc, hFont); 7716 7717 if (isW) 7718 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize); 7719 else 7720 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize); 7721 SelectObject(hdc, hOldFont); 7722 ReleaseDC(infoPtr->hwndSelf, hdc); 7723 } 7724 return stringSize.cx; 7725 } 7726 7727 /*** 7728 * DESCRIPTION: 7729 * Determines which listview item is located at the specified position. 7730 * 7731 * PARAMETER(S): 7732 * [I] infoPtr : valid pointer to the listview structure 7733 * [IO] lpht : hit test information 7734 * [I] subitem : fill out iSubItem. 7735 * [I] select : return the index only if the hit selects the item 7736 * 7737 * NOTE: 7738 * (mm 20001022): We must not allow iSubItem to be touched, for 7739 * an app might pass only a structure with space up to iItem! 7740 * (MS Office 97 does that for instance in the file open dialog) 7741 * 7742 * RETURN: 7743 * SUCCESS : item index 7744 * FAILURE : -1 7745 */ 7746 static INT LISTVIEW_HitTest(const LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select) 7747 { 7748 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' }; 7749 RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch; 7750 POINT Origin, Position, opt; 7751 BOOL is_fullrow; 7752 LVITEMW lvItem; 7753 ITERATOR i; 7754 INT iItem; 7755 7756 TRACE("(pt=%s, subitem=%d, select=%d)\n", wine_dbgstr_point(&lpht->pt), subitem, select); 7757 7758 lpht->flags = 0; 7759 lpht->iItem = -1; 7760 if (subitem) lpht->iSubItem = 0; 7761 7762 LISTVIEW_GetOrigin(infoPtr, &Origin); 7763 7764 /* set whole list relation flags */ 7765 if (subitem && infoPtr->uView == LV_VIEW_DETAILS) 7766 { 7767 /* LVM_SUBITEMHITTEST checks left bound of possible client area */ 7768 if (infoPtr->rcList.left > lpht->pt.x && Origin.x < lpht->pt.x) 7769 lpht->flags |= LVHT_TOLEFT; 7770 7771 if (lpht->pt.y < infoPtr->rcList.top && lpht->pt.y >= 0) 7772 opt.y = lpht->pt.y + infoPtr->rcList.top; 7773 else 7774 opt.y = lpht->pt.y; 7775 7776 if (infoPtr->rcList.bottom < opt.y) 7777 lpht->flags |= LVHT_BELOW; 7778 } 7779 else 7780 { 7781 if (infoPtr->rcList.left > lpht->pt.x) 7782 lpht->flags |= LVHT_TOLEFT; 7783 else if (infoPtr->rcList.right < lpht->pt.x) 7784 lpht->flags |= LVHT_TORIGHT; 7785 7786 if (infoPtr->rcList.top > lpht->pt.y) 7787 lpht->flags |= LVHT_ABOVE; 7788 else if (infoPtr->rcList.bottom < lpht->pt.y) 7789 lpht->flags |= LVHT_BELOW; 7790 } 7791 7792 /* even if item is invalid try to find subitem */ 7793 if (infoPtr->uView == LV_VIEW_DETAILS && subitem) 7794 { 7795 RECT *pRect; 7796 INT j; 7797 7798 opt.x = lpht->pt.x - Origin.x; 7799 7800 lpht->iSubItem = -1; 7801 for (j = 0; j < DPA_GetPtrCount(infoPtr->hdpaColumns); j++) 7802 { 7803 pRect = &LISTVIEW_GetColumnInfo(infoPtr, j)->rcHeader; 7804 7805 if ((opt.x >= pRect->left) && (opt.x < pRect->right)) 7806 { 7807 lpht->iSubItem = j; 7808 break; 7809 } 7810 } 7811 TRACE("lpht->iSubItem=%d\n", lpht->iSubItem); 7812 7813 /* if we're outside horizontal columns bounds there's nothing to test further */ 7814 if (lpht->iSubItem == -1) 7815 { 7816 lpht->iItem = -1; 7817 lpht->flags = LVHT_NOWHERE; 7818 return -1; 7819 } 7820 } 7821 7822 TRACE("lpht->flags=0x%x\n", lpht->flags); 7823 if (lpht->flags) return -1; 7824 7825 lpht->flags |= LVHT_NOWHERE; 7826 7827 /* first deal with the large items */ 7828 rcSearch.left = lpht->pt.x; 7829 rcSearch.top = lpht->pt.y; 7830 rcSearch.right = rcSearch.left + 1; 7831 rcSearch.bottom = rcSearch.top + 1; 7832 7833 iterator_frameditems(&i, infoPtr, &rcSearch); 7834 iterator_next(&i); /* go to first item in the sequence */ 7835 iItem = i.nItem; 7836 iterator_destroy(&i); 7837 7838 TRACE("lpht->iItem=%d\n", iItem); 7839 if (iItem == -1) return -1; 7840 7841 lvItem.mask = LVIF_STATE | LVIF_TEXT; 7842 if (infoPtr->uView == LV_VIEW_DETAILS) lvItem.mask |= LVIF_INDENT; 7843 lvItem.stateMask = LVIS_STATEIMAGEMASK; 7844 if (infoPtr->uView == LV_VIEW_ICON) lvItem.stateMask |= LVIS_FOCUSED; 7845 lvItem.iItem = iItem; 7846 lvItem.iSubItem = subitem ? lpht->iSubItem : 0; 7847 lvItem.pszText = szDispText; 7848 lvItem.cchTextMax = DISP_TEXT_SIZE; 7849 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1; 7850 if (!infoPtr->bFocus) lvItem.state &= ~LVIS_FOCUSED; 7851 7852 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, NULL, &rcIcon, &rcState, &rcLabel); 7853 LISTVIEW_GetItemOrigin(infoPtr, iItem, &Position); 7854 opt.x = lpht->pt.x - Position.x - Origin.x; 7855 7856 if (lpht->pt.y < infoPtr->rcList.top && lpht->pt.y >= 0) 7857 opt.y = lpht->pt.y - Position.y - Origin.y + infoPtr->rcList.top; 7858 else 7859 opt.y = lpht->pt.y - Position.y - Origin.y; 7860 7861 if (infoPtr->uView == LV_VIEW_DETAILS) 7862 { 7863 rcBounds = rcBox; 7864 if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) 7865 opt.x = lpht->pt.x - Origin.x; 7866 } 7867 else 7868 { 7869 UnionRect(&rcBounds, &rcIcon, &rcLabel); 7870 UnionRect(&rcBounds, &rcBounds, &rcState); 7871 } 7872 TRACE("rcBounds=%s\n", wine_dbgstr_rect(&rcBounds)); 7873 if (!PtInRect(&rcBounds, opt)) return -1; 7874 7875 /* That's a special case - row rectangle is used as item rectangle and 7876 returned flags contain all item parts. */ 7877 is_fullrow = (infoPtr->uView == LV_VIEW_DETAILS) && ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) || (infoPtr->dwStyle & LVS_OWNERDRAWFIXED)); 7878 7879 if (PtInRect(&rcIcon, opt)) 7880 lpht->flags |= LVHT_ONITEMICON; 7881 else if (PtInRect(&rcLabel, opt)) 7882 lpht->flags |= LVHT_ONITEMLABEL; 7883 else if (infoPtr->himlState && PtInRect(&rcState, opt)) 7884 lpht->flags |= LVHT_ONITEMSTATEICON; 7885 if (is_fullrow && !(lpht->flags & LVHT_ONITEM)) 7886 { 7887 lpht->flags = LVHT_ONITEM | LVHT_ABOVE; 7888 } 7889 if (lpht->flags & LVHT_ONITEM) 7890 lpht->flags &= ~LVHT_NOWHERE; 7891 TRACE("lpht->flags=0x%x\n", lpht->flags); 7892 7893 if (select && !is_fullrow) 7894 { 7895 if (infoPtr->uView == LV_VIEW_DETAILS) 7896 { 7897 /* get main item bounds */ 7898 lvItem.iSubItem = 0; 7899 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, NULL, &rcIcon, &rcState, &rcLabel); 7900 UnionRect(&rcBounds, &rcIcon, &rcLabel); 7901 UnionRect(&rcBounds, &rcBounds, &rcState); 7902 } 7903 if (!PtInRect(&rcBounds, opt)) iItem = -1; 7904 } 7905 return lpht->iItem = iItem; 7906 } 7907 7908 /*** 7909 * DESCRIPTION: 7910 * Inserts a new item in the listview control. 7911 * 7912 * PARAMETER(S): 7913 * [I] infoPtr : valid pointer to the listview structure 7914 * [I] lpLVItem : item information 7915 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI 7916 * 7917 * RETURN: 7918 * SUCCESS : new item index 7919 * FAILURE : -1 7920 */ 7921 static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW) 7922 { 7923 INT nItem; 7924 HDPA hdpaSubItems; 7925 NMLISTVIEW nmlv; 7926 ITEM_INFO *lpItem; 7927 ITEM_ID *lpID; 7928 BOOL is_sorted, has_changed; 7929 LVITEMW item; 7930 HWND hwndSelf = infoPtr->hwndSelf; 7931 7932 TRACE("(item=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW); 7933 7934 if (infoPtr->dwStyle & LVS_OWNERDATA) return infoPtr->nItemCount++; 7935 7936 /* make sure it's an item, and not a subitem; cannot insert a subitem */ 7937 if (!lpLVItem || lpLVItem->iSubItem) return -1; 7938 7939 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return -1; 7940 7941 if (!(lpItem = Alloc(sizeof(ITEM_INFO)))) return -1; 7942 7943 /* insert item in listview control data structure */ 7944 if ( !(hdpaSubItems = DPA_Create(8)) ) goto fail; 7945 if ( !DPA_SetPtr(hdpaSubItems, 0, lpItem) ) assert (FALSE); 7946 7947 /* link with id struct */ 7948 if (!(lpID = Alloc(sizeof(ITEM_ID)))) goto fail; 7949 lpItem->id = lpID; 7950 lpID->item = hdpaSubItems; 7951 lpID->id = get_next_itemid(infoPtr); 7952 if ( DPA_InsertPtr(infoPtr->hdpaItemIds, infoPtr->nItemCount, lpID) == -1) goto fail; 7953 7954 is_sorted = (infoPtr->dwStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) && 7955 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText); 7956 7957 if (lpLVItem->iItem < 0 && !is_sorted) return -1; 7958 7959 /* calculate new item index */ 7960 if (is_sorted) 7961 { 7962 HDPA hItem; 7963 ITEM_INFO *item_s; 7964 INT i = 0, cmpv; 7965 WCHAR *textW; 7966 7967 textW = textdupTtoW(lpLVItem->pszText, isW); 7968 7969 while (i < infoPtr->nItemCount) 7970 { 7971 hItem = DPA_GetPtr( infoPtr->hdpaItems, i); 7972 item_s = DPA_GetPtr(hItem, 0); 7973 7974 cmpv = textcmpWT(item_s->hdr.pszText, textW, TRUE); 7975 if (infoPtr->dwStyle & LVS_SORTDESCENDING) cmpv *= -1; 7976 7977 if (cmpv >= 0) break; 7978 i++; 7979 } 7980 7981 textfreeT(textW, isW); 7982 7983 nItem = i; 7984 } 7985 else 7986 nItem = min(lpLVItem->iItem, infoPtr->nItemCount); 7987 7988 TRACE("inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem); 7989 nItem = DPA_InsertPtr( infoPtr->hdpaItems, nItem, hdpaSubItems ); 7990 if (nItem == -1) goto fail; 7991 infoPtr->nItemCount++; 7992 7993 /* shift indices first so they don't get tangled */ 7994 LISTVIEW_ShiftIndices(infoPtr, nItem, 1); 7995 7996 /* set the item attributes */ 7997 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS)) 7998 { 7999 /* full size structure expected - _WIN32IE >= 0x560 */ 8000 item = *lpLVItem; 8001 } 8002 else if (lpLVItem->mask & LVIF_INDENT) 8003 { 8004 /* indent member expected - _WIN32IE >= 0x300 */ 8005 memcpy(&item, lpLVItem, offsetof( LVITEMW, iGroupId )); 8006 } 8007 else 8008 { 8009 /* minimal structure expected */ 8010 memcpy(&item, lpLVItem, offsetof( LVITEMW, iIndent )); 8011 } 8012 item.iItem = nItem; 8013 if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) 8014 { 8015 if (item.mask & LVIF_STATE) 8016 { 8017 item.stateMask |= LVIS_STATEIMAGEMASK; 8018 item.state &= ~LVIS_STATEIMAGEMASK; 8019 item.state |= INDEXTOSTATEIMAGEMASK(1); 8020 } 8021 else 8022 { 8023 item.mask |= LVIF_STATE; 8024 item.stateMask = LVIS_STATEIMAGEMASK; 8025 item.state = INDEXTOSTATEIMAGEMASK(1); 8026 } 8027 } 8028 8029 if (!set_main_item(infoPtr, &item, TRUE, isW, &has_changed)) goto undo; 8030 8031 /* make room for the position, if we are in the right mode */ 8032 if ((infoPtr->uView == LV_VIEW_SMALLICON) || (infoPtr->uView == LV_VIEW_ICON)) 8033 { 8034 if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1) 8035 goto undo; 8036 if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1) 8037 { 8038 DPA_DeletePtr(infoPtr->hdpaPosX, nItem); 8039 goto undo; 8040 } 8041 } 8042 8043 /* send LVN_INSERTITEM notification */ 8044 memset(&nmlv, 0, sizeof(NMLISTVIEW)); 8045 nmlv.iItem = nItem; 8046 nmlv.lParam = lpItem->lParam; 8047 notify_listview(infoPtr, LVN_INSERTITEM, &nmlv); 8048 if (!IsWindow(hwndSelf)) 8049 return -1; 8050 8051 /* align items (set position of each item) */ 8052 if (infoPtr->uView == LV_VIEW_SMALLICON || infoPtr->uView == LV_VIEW_ICON) 8053 { 8054 POINT pt; 8055 8056 #ifdef __REACTOS__ 8057 if (infoPtr->dwStyle & LVS_ALIGNLEFT) 8058 LISTVIEW_NextIconPosLeft(infoPtr, &pt, nItem); 8059 else 8060 LISTVIEW_NextIconPosTop(infoPtr, &pt, nItem); 8061 #else 8062 if (infoPtr->dwStyle & LVS_ALIGNLEFT) 8063 LISTVIEW_NextIconPosLeft(infoPtr, &pt); 8064 else 8065 LISTVIEW_NextIconPosTop(infoPtr, &pt); 8066 #endif 8067 8068 LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, TRUE); 8069 } 8070 8071 /* now is the invalidation fun */ 8072 LISTVIEW_ScrollOnInsert(infoPtr, nItem, 1); 8073 return nItem; 8074 8075 undo: 8076 LISTVIEW_ShiftIndices(infoPtr, nItem, -1); 8077 LISTVIEW_ShiftFocus(infoPtr, infoPtr->nFocusedItem, nItem, -1); 8078 DPA_DeletePtr(infoPtr->hdpaItems, nItem); 8079 infoPtr->nItemCount--; 8080 fail: 8081 DPA_DeletePtr(hdpaSubItems, 0); 8082 DPA_Destroy (hdpaSubItems); 8083 Free (lpItem); 8084 return -1; 8085 } 8086 8087 /*** 8088 * DESCRIPTION: 8089 * Checks item visibility. 8090 * 8091 * PARAMETER(S): 8092 * [I] infoPtr : valid pointer to the listview structure 8093 * [I] nFirst : item index to check for 8094 * 8095 * RETURN: 8096 * Item visible : TRUE 8097 * Item invisible or failure : FALSE 8098 */ 8099 static BOOL LISTVIEW_IsItemVisible(const LISTVIEW_INFO *infoPtr, INT nItem) 8100 { 8101 POINT Origin, Position; 8102 RECT rcItem; 8103 HDC hdc; 8104 BOOL ret; 8105 8106 TRACE("nItem=%d\n", nItem); 8107 8108 if (nItem < 0 || nItem >= DPA_GetPtrCount(infoPtr->hdpaItems)) return FALSE; 8109 8110 LISTVIEW_GetOrigin(infoPtr, &Origin); 8111 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position); 8112 rcItem.left = Position.x + Origin.x; 8113 rcItem.top = Position.y + Origin.y; 8114 rcItem.right = rcItem.left + infoPtr->nItemWidth; 8115 rcItem.bottom = rcItem.top + infoPtr->nItemHeight; 8116 8117 hdc = GetDC(infoPtr->hwndSelf); 8118 if (!hdc) return FALSE; 8119 ret = RectVisible(hdc, &rcItem); 8120 ReleaseDC(infoPtr->hwndSelf, hdc); 8121 8122 return ret; 8123 } 8124 8125 /*** 8126 * DESCRIPTION: 8127 * Redraws a range of items. 8128 * 8129 * PARAMETER(S): 8130 * [I] infoPtr : valid pointer to the listview structure 8131 * [I] nFirst : first item 8132 * [I] nLast : last item 8133 * 8134 * RETURN: 8135 * SUCCESS : TRUE 8136 * FAILURE : FALSE 8137 */ 8138 static BOOL LISTVIEW_RedrawItems(const LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast) 8139 { 8140 INT i; 8141 8142 for (i = max(nFirst, 0); i <= min(nLast, infoPtr->nItemCount - 1); i++) 8143 LISTVIEW_InvalidateItem(infoPtr, i); 8144 8145 return TRUE; 8146 } 8147 8148 /*** 8149 * DESCRIPTION: 8150 * Scroll the content of a listview. 8151 * 8152 * PARAMETER(S): 8153 * [I] infoPtr : valid pointer to the listview structure 8154 * [I] dx : horizontal scroll amount in pixels 8155 * [I] dy : vertical scroll amount in pixels 8156 * 8157 * RETURN: 8158 * SUCCESS : TRUE 8159 * FAILURE : FALSE 8160 * 8161 * COMMENTS: 8162 * If the control is in report view (LV_VIEW_DETAILS) the control can 8163 * be scrolled only in line increments. "dy" will be rounded to the 8164 * nearest number of pixels that are a whole line. Ex: if line height 8165 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7 8166 * is passed, then the scroll will be 0. (per MSDN 7/2002) 8167 */ 8168 static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy) 8169 { 8170 switch(infoPtr->uView) { 8171 case LV_VIEW_DETAILS: 8172 dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2; 8173 dy /= infoPtr->nItemHeight; 8174 break; 8175 case LV_VIEW_LIST: 8176 if (dy != 0) return FALSE; 8177 break; 8178 default: /* icon */ 8179 break; 8180 } 8181 8182 if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx); 8183 if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy); 8184 8185 return TRUE; 8186 } 8187 8188 /*** 8189 * DESCRIPTION: 8190 * Sets the background color. 8191 * 8192 * PARAMETER(S): 8193 * [I] infoPtr : valid pointer to the listview structure 8194 * [I] color : background color 8195 * 8196 * RETURN: 8197 * SUCCESS : TRUE 8198 * FAILURE : FALSE 8199 */ 8200 static BOOL LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF color) 8201 { 8202 TRACE("(color=%x)\n", color); 8203 8204 #ifdef __REACTOS__ 8205 infoPtr->bDefaultBkColor = FALSE; 8206 #endif 8207 if(infoPtr->clrBk != color) { 8208 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush); 8209 infoPtr->clrBk = color; 8210 if (color == CLR_NONE) 8211 infoPtr->hBkBrush = (HBRUSH)GetClassLongPtrW(infoPtr->hwndSelf, GCLP_HBRBACKGROUND); 8212 else 8213 { 8214 infoPtr->hBkBrush = CreateSolidBrush(color); 8215 infoPtr->dwLvExStyle &= ~LVS_EX_TRANSPARENTBKGND; 8216 } 8217 } 8218 8219 return TRUE; 8220 } 8221 8222 /* LISTVIEW_SetBkImage */ 8223 8224 /*** Helper for {Insert,Set}ColumnT *only* */ 8225 static void column_fill_hditem(const LISTVIEW_INFO *infoPtr, HDITEMW *lphdi, INT nColumn, 8226 const LVCOLUMNW *lpColumn, BOOL isW) 8227 { 8228 if (lpColumn->mask & LVCF_FMT) 8229 { 8230 /* format member is valid */ 8231 lphdi->mask |= HDI_FORMAT; 8232 8233 /* set text alignment (leftmost column must be left-aligned) */ 8234 if (nColumn == 0 || (lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT) 8235 lphdi->fmt |= HDF_LEFT; 8236 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_RIGHT) 8237 lphdi->fmt |= HDF_RIGHT; 8238 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_CENTER) 8239 lphdi->fmt |= HDF_CENTER; 8240 8241 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT) 8242 lphdi->fmt |= HDF_BITMAP_ON_RIGHT; 8243 8244 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES) 8245 { 8246 lphdi->fmt |= HDF_IMAGE; 8247 lphdi->iImage = I_IMAGECALLBACK; 8248 } 8249 8250 if (lpColumn->fmt & LVCFMT_FIXED_WIDTH) 8251 lphdi->fmt |= HDF_FIXEDWIDTH; 8252 } 8253 8254 if (lpColumn->mask & LVCF_WIDTH) 8255 { 8256 lphdi->mask |= HDI_WIDTH; 8257 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER) 8258 { 8259 /* make it fill the remainder of the controls width */ 8260 RECT rcHeader; 8261 INT item_index; 8262 8263 for(item_index = 0; item_index < (nColumn - 1); item_index++) 8264 { 8265 LISTVIEW_GetHeaderRect(infoPtr, item_index, &rcHeader); 8266 lphdi->cxy += rcHeader.right - rcHeader.left; 8267 } 8268 8269 /* retrieve the layout of the header */ 8270 GetClientRect(infoPtr->hwndSelf, &rcHeader); 8271 TRACE("start cxy=%d rcHeader=%s\n", lphdi->cxy, wine_dbgstr_rect(&rcHeader)); 8272 8273 lphdi->cxy = (rcHeader.right - rcHeader.left) - lphdi->cxy; 8274 } 8275 else 8276 lphdi->cxy = lpColumn->cx; 8277 } 8278 8279 if (lpColumn->mask & LVCF_TEXT) 8280 { 8281 lphdi->mask |= HDI_TEXT | HDI_FORMAT; 8282 lphdi->fmt |= HDF_STRING; 8283 lphdi->pszText = lpColumn->pszText; 8284 lphdi->cchTextMax = textlenT(lpColumn->pszText, isW); 8285 } 8286 8287 if (lpColumn->mask & LVCF_IMAGE) 8288 { 8289 lphdi->mask |= HDI_IMAGE; 8290 lphdi->iImage = lpColumn->iImage; 8291 } 8292 8293 if (lpColumn->mask & LVCF_ORDER) 8294 { 8295 lphdi->mask |= HDI_ORDER; 8296 lphdi->iOrder = lpColumn->iOrder; 8297 } 8298 } 8299 8300 8301 /*** 8302 * DESCRIPTION: 8303 * Inserts a new column. 8304 * 8305 * PARAMETER(S): 8306 * [I] infoPtr : valid pointer to the listview structure 8307 * [I] nColumn : column index 8308 * [I] lpColumn : column information 8309 * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise 8310 * 8311 * RETURN: 8312 * SUCCESS : new column index 8313 * FAILURE : -1 8314 */ 8315 static INT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn, 8316 const LVCOLUMNW *lpColumn, BOOL isW) 8317 { 8318 COLUMN_INFO *lpColumnInfo; 8319 INT nNewColumn; 8320 HDITEMW hdi; 8321 8322 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW); 8323 8324 if (!lpColumn || nColumn < 0) return -1; 8325 nColumn = min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns)); 8326 8327 ZeroMemory(&hdi, sizeof(HDITEMW)); 8328 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW); 8329 8330 /* 8331 * A mask not including LVCF_WIDTH turns into a mask of width, width 10 8332 * (can be seen in SPY) otherwise column never gets added. 8333 */ 8334 if (!(lpColumn->mask & LVCF_WIDTH)) { 8335 hdi.mask |= HDI_WIDTH; 8336 hdi.cxy = 10; 8337 } 8338 8339 /* 8340 * when the iSubItem is available Windows copies it to the header lParam. It seems 8341 * to happen only in LVM_INSERTCOLUMN - not in LVM_SETCOLUMN 8342 */ 8343 if (lpColumn->mask & LVCF_SUBITEM) 8344 { 8345 hdi.mask |= HDI_LPARAM; 8346 hdi.lParam = lpColumn->iSubItem; 8347 } 8348 8349 /* create header if not present */ 8350 LISTVIEW_CreateHeader(infoPtr); 8351 if (!(LVS_NOCOLUMNHEADER & infoPtr->dwStyle) && 8352 (infoPtr->uView == LV_VIEW_DETAILS) && (WS_VISIBLE & infoPtr->dwStyle)) 8353 { 8354 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL); 8355 } 8356 8357 /* insert item in header control */ 8358 nNewColumn = SendMessageW(infoPtr->hwndHeader, 8359 isW ? HDM_INSERTITEMW : HDM_INSERTITEMA, 8360 nColumn, (LPARAM)&hdi); 8361 if (nNewColumn == -1) return -1; 8362 if (nNewColumn != nColumn) ERR("nColumn=%d, nNewColumn=%d\n", nColumn, nNewColumn); 8363 8364 /* create our own column info */ 8365 if (!(lpColumnInfo = Alloc(sizeof(COLUMN_INFO)))) goto fail; 8366 if (DPA_InsertPtr(infoPtr->hdpaColumns, nNewColumn, lpColumnInfo) == -1) goto fail; 8367 8368 if (lpColumn->mask & LVCF_FMT) lpColumnInfo->fmt = lpColumn->fmt; 8369 if (lpColumn->mask & LVCF_MINWIDTH) lpColumnInfo->cxMin = lpColumn->cxMin; 8370 if (!SendMessageW(infoPtr->hwndHeader, HDM_GETITEMRECT, nNewColumn, (LPARAM)&lpColumnInfo->rcHeader)) 8371 goto fail; 8372 8373 /* now we have to actually adjust the data */ 8374 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0) 8375 { 8376 SUBITEM_INFO *lpSubItem; 8377 HDPA hdpaSubItems; 8378 INT nItem, i; 8379 LVITEMW item; 8380 BOOL changed; 8381 8382 item.iSubItem = nNewColumn; 8383 item.mask = LVIF_TEXT | LVIF_IMAGE; 8384 item.iImage = I_IMAGECALLBACK; 8385 item.pszText = LPSTR_TEXTCALLBACKW; 8386 8387 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++) 8388 { 8389 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, nItem); 8390 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++) 8391 { 8392 lpSubItem = DPA_GetPtr(hdpaSubItems, i); 8393 if (lpSubItem->iSubItem >= nNewColumn) 8394 lpSubItem->iSubItem++; 8395 } 8396 8397 /* add new subitem for each item */ 8398 item.iItem = nItem; 8399 set_sub_item(infoPtr, &item, isW, &changed); 8400 } 8401 } 8402 8403 /* make space for the new column */ 8404 LISTVIEW_ScrollColumns(infoPtr, nNewColumn + 1, lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left); 8405 LISTVIEW_UpdateItemSize(infoPtr); 8406 8407 return nNewColumn; 8408 8409 fail: 8410 if (nNewColumn != -1) SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nNewColumn, 0); 8411 if (lpColumnInfo) 8412 { 8413 DPA_DeletePtr(infoPtr->hdpaColumns, nNewColumn); 8414 Free(lpColumnInfo); 8415 } 8416 return -1; 8417 } 8418 8419 /*** 8420 * DESCRIPTION: 8421 * Sets the attributes of a header item. 8422 * 8423 * PARAMETER(S): 8424 * [I] infoPtr : valid pointer to the listview structure 8425 * [I] nColumn : column index 8426 * [I] lpColumn : column attributes 8427 * [I] isW: if TRUE, then lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA 8428 * 8429 * RETURN: 8430 * SUCCESS : TRUE 8431 * FAILURE : FALSE 8432 */ 8433 static BOOL LISTVIEW_SetColumnT(const LISTVIEW_INFO *infoPtr, INT nColumn, 8434 const LVCOLUMNW *lpColumn, BOOL isW) 8435 { 8436 HDITEMW hdi, hdiget; 8437 BOOL bResult; 8438 8439 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW); 8440 8441 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE; 8442 8443 ZeroMemory(&hdi, sizeof(HDITEMW)); 8444 if (lpColumn->mask & LVCF_FMT) 8445 { 8446 hdi.mask |= HDI_FORMAT; 8447 hdiget.mask = HDI_FORMAT; 8448 if (SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, nColumn, (LPARAM)&hdiget)) 8449 hdi.fmt = hdiget.fmt & HDF_STRING; 8450 } 8451 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW); 8452 8453 /* set header item attributes */ 8454 bResult = SendMessageW(infoPtr->hwndHeader, isW ? HDM_SETITEMW : HDM_SETITEMA, nColumn, (LPARAM)&hdi); 8455 if (!bResult) return FALSE; 8456 8457 if (lpColumn->mask & LVCF_FMT) 8458 { 8459 COLUMN_INFO *lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn); 8460 INT oldFmt = lpColumnInfo->fmt; 8461 8462 lpColumnInfo->fmt = lpColumn->fmt; 8463 if ((oldFmt ^ lpColumn->fmt) & (LVCFMT_JUSTIFYMASK | LVCFMT_IMAGE)) 8464 { 8465 if (infoPtr->uView == LV_VIEW_DETAILS) LISTVIEW_InvalidateColumn(infoPtr, nColumn); 8466 } 8467 } 8468 8469 if (lpColumn->mask & LVCF_MINWIDTH) 8470 LISTVIEW_GetColumnInfo(infoPtr, nColumn)->cxMin = lpColumn->cxMin; 8471 8472 return TRUE; 8473 } 8474 8475 /*** 8476 * DESCRIPTION: 8477 * Sets the column order array 8478 * 8479 * PARAMETERS: 8480 * [I] infoPtr : valid pointer to the listview structure 8481 * [I] iCount : number of elements in column order array 8482 * [I] lpiArray : pointer to column order array 8483 * 8484 * RETURN: 8485 * SUCCESS : TRUE 8486 * FAILURE : FALSE 8487 */ 8488 static BOOL LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, const INT *lpiArray) 8489 { 8490 if (!infoPtr->hwndHeader) return FALSE; 8491 infoPtr->colRectsDirty = TRUE; 8492 return SendMessageW(infoPtr->hwndHeader, HDM_SETORDERARRAY, iCount, (LPARAM)lpiArray); 8493 } 8494 8495 /*** 8496 * DESCRIPTION: 8497 * Sets the width of a column 8498 * 8499 * PARAMETERS: 8500 * [I] infoPtr : valid pointer to the listview structure 8501 * [I] nColumn : column index 8502 * [I] cx : column width 8503 * 8504 * RETURN: 8505 * SUCCESS : TRUE 8506 * FAILURE : FALSE 8507 */ 8508 static BOOL LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn, INT cx) 8509 { 8510 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 }; 8511 INT max_cx = 0; 8512 HDITEMW hdi; 8513 8514 TRACE("(nColumn=%d, cx=%d)\n", nColumn, cx); 8515 8516 /* set column width only if in report or list mode */ 8517 if (infoPtr->uView != LV_VIEW_DETAILS && infoPtr->uView != LV_VIEW_LIST) return FALSE; 8518 8519 /* take care of invalid cx values - LVSCW_AUTOSIZE_* values are negative, 8520 with _USEHEADER being the lowest */ 8521 if (infoPtr->uView == LV_VIEW_DETAILS && cx < LVSCW_AUTOSIZE_USEHEADER) cx = LVSCW_AUTOSIZE; 8522 else if (infoPtr->uView == LV_VIEW_LIST && cx <= 0) return FALSE; 8523 8524 /* resize all columns if in LV_VIEW_LIST mode */ 8525 if(infoPtr->uView == LV_VIEW_LIST) 8526 { 8527 infoPtr->nItemWidth = cx; 8528 LISTVIEW_InvalidateList(infoPtr); 8529 return TRUE; 8530 } 8531 8532 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE; 8533 8534 if (cx == LVSCW_AUTOSIZE || (cx == LVSCW_AUTOSIZE_USEHEADER && nColumn < DPA_GetPtrCount(infoPtr->hdpaColumns) -1)) 8535 { 8536 INT nLabelWidth; 8537 LVITEMW lvItem; 8538 8539 lvItem.mask = LVIF_TEXT; 8540 lvItem.iItem = 0; 8541 lvItem.iSubItem = nColumn; 8542 lvItem.cchTextMax = DISP_TEXT_SIZE; 8543 for (; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++) 8544 { 8545 lvItem.pszText = szDispText; 8546 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue; 8547 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE); 8548 if (max_cx < nLabelWidth) max_cx = nLabelWidth; 8549 } 8550 if (infoPtr->himlSmall && (nColumn == 0 || (LISTVIEW_GetColumnInfo(infoPtr, nColumn)->fmt & LVCFMT_IMAGE))) 8551 max_cx += infoPtr->iconSize.cx; 8552 max_cx += TRAILING_LABEL_PADDING; 8553 if (nColumn == 0 && (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)) 8554 max_cx += GetSystemMetrics(SM_CXSMICON); 8555 } 8556 8557 /* autosize based on listview items width */ 8558 if(cx == LVSCW_AUTOSIZE) 8559 cx = max_cx; 8560 else if(cx == LVSCW_AUTOSIZE_USEHEADER) 8561 { 8562 /* if iCol is the last column make it fill the remainder of the controls width */ 8563 if(nColumn == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1) 8564 { 8565 RECT rcHeader; 8566 POINT Origin; 8567 8568 LISTVIEW_GetOrigin(infoPtr, &Origin); 8569 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader); 8570 8571 cx = infoPtr->rcList.right - Origin.x - rcHeader.left; 8572 } 8573 else 8574 { 8575 /* Despite what the MS docs say, if this is not the last 8576 column, then MS resizes the column to the width of the 8577 largest text string in the column, including headers 8578 and items. This is different from LVSCW_AUTOSIZE in that 8579 LVSCW_AUTOSIZE ignores the header string length. */ 8580 cx = 0; 8581 8582 /* retrieve header text */ 8583 hdi.mask = HDI_TEXT|HDI_FORMAT|HDI_IMAGE|HDI_BITMAP; 8584 hdi.cchTextMax = DISP_TEXT_SIZE; 8585 hdi.pszText = szDispText; 8586 if (SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, nColumn, (LPARAM)&hdi)) 8587 { 8588 HDC hdc = GetDC(infoPtr->hwndSelf); 8589 HFONT old_font = SelectObject(hdc, (HFONT)SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0, 0)); 8590 HIMAGELIST himl = (HIMAGELIST)SendMessageW(infoPtr->hwndHeader, HDM_GETIMAGELIST, 0, 0); 8591 INT bitmap_margin = 0; 8592 SIZE size; 8593 8594 if (GetTextExtentPoint32W(hdc, hdi.pszText, lstrlenW(hdi.pszText), &size)) 8595 cx = size.cx + TRAILING_HEADER_PADDING; 8596 8597 if (hdi.fmt & (HDF_IMAGE|HDF_BITMAP)) 8598 bitmap_margin = SendMessageW(infoPtr->hwndHeader, HDM_GETBITMAPMARGIN, 0, 0); 8599 8600 if ((hdi.fmt & HDF_IMAGE) && himl) 8601 { 8602 INT icon_cx, icon_cy; 8603 8604 if (!ImageList_GetIconSize(himl, &icon_cx, &icon_cy)) 8605 cx += icon_cx + 2*bitmap_margin; 8606 } 8607 else if (hdi.fmt & HDF_BITMAP) 8608 { 8609 BITMAP bmp; 8610 8611 GetObjectW(hdi.hbm, sizeof(BITMAP), &bmp); 8612 cx += bmp.bmWidth + 2*bitmap_margin; 8613 } 8614 8615 SelectObject(hdc, old_font); 8616 ReleaseDC(infoPtr->hwndSelf, hdc); 8617 } 8618 cx = max (cx, max_cx); 8619 } 8620 } 8621 8622 if (cx < 0) return FALSE; 8623 8624 /* call header to update the column change */ 8625 hdi.mask = HDI_WIDTH; 8626 hdi.cxy = max(cx, LISTVIEW_GetColumnInfo(infoPtr, nColumn)->cxMin); 8627 TRACE("hdi.cxy=%d\n", hdi.cxy); 8628 return SendMessageW(infoPtr->hwndHeader, HDM_SETITEMW, nColumn, (LPARAM)&hdi); 8629 } 8630 8631 /*** 8632 * Creates the checkbox imagelist. Helper for LISTVIEW_SetExtendedListViewStyle 8633 * 8634 */ 8635 static HIMAGELIST LISTVIEW_CreateCheckBoxIL(const LISTVIEW_INFO *infoPtr) 8636 { 8637 HDC hdc_wnd, hdc; 8638 HBITMAP hbm_im, hbm_mask, hbm_orig; 8639 RECT rc; 8640 HBRUSH hbr_white = GetStockObject(WHITE_BRUSH); 8641 HBRUSH hbr_black = GetStockObject(BLACK_BRUSH); 8642 HIMAGELIST himl; 8643 8644 himl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 8645 ILC_COLOR | ILC_MASK, 2, 2); 8646 hdc_wnd = GetDC(infoPtr->hwndSelf); 8647 hdc = CreateCompatibleDC(hdc_wnd); 8648 hbm_im = CreateCompatibleBitmap(hdc_wnd, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON)); 8649 hbm_mask = CreateBitmap(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 1, 1, NULL); 8650 ReleaseDC(infoPtr->hwndSelf, hdc_wnd); 8651 8652 SetRect(&rc, 0, 0, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON)); 8653 hbm_orig = SelectObject(hdc, hbm_mask); 8654 FillRect(hdc, &rc, hbr_white); 8655 InflateRect(&rc, -2, -2); 8656 FillRect(hdc, &rc, hbr_black); 8657 8658 SelectObject(hdc, hbm_im); 8659 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO); 8660 SelectObject(hdc, hbm_orig); 8661 ImageList_Add(himl, hbm_im, hbm_mask); 8662 8663 SelectObject(hdc, hbm_im); 8664 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO | DFCS_CHECKED); 8665 SelectObject(hdc, hbm_orig); 8666 ImageList_Add(himl, hbm_im, hbm_mask); 8667 8668 DeleteObject(hbm_mask); 8669 DeleteObject(hbm_im); 8670 DeleteDC(hdc); 8671 8672 return himl; 8673 } 8674 8675 /*** 8676 * DESCRIPTION: 8677 * Sets the extended listview style. 8678 * 8679 * PARAMETERS: 8680 * [I] infoPtr : valid pointer to the listview structure 8681 * [I] dwMask : mask 8682 * [I] dwStyle : style 8683 * 8684 * RETURN: 8685 * SUCCESS : previous style 8686 * FAILURE : 0 8687 */ 8688 static DWORD LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD mask, DWORD ex_style) 8689 { 8690 DWORD old_ex_style = infoPtr->dwLvExStyle; 8691 8692 TRACE("mask=0x%08x, ex_style=0x%08x\n", mask, ex_style); 8693 8694 /* set new style */ 8695 if (mask) 8696 infoPtr->dwLvExStyle = (old_ex_style & ~mask) | (ex_style & mask); 8697 else 8698 infoPtr->dwLvExStyle = ex_style; 8699 8700 if((infoPtr->dwLvExStyle ^ old_ex_style) & LVS_EX_CHECKBOXES) 8701 { 8702 HIMAGELIST himl = 0; 8703 if(infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) 8704 { 8705 LVITEMW item; 8706 item.mask = LVIF_STATE; 8707 item.stateMask = LVIS_STATEIMAGEMASK; 8708 item.state = INDEXTOSTATEIMAGEMASK(1); 8709 LISTVIEW_SetItemState(infoPtr, -1, &item); 8710 8711 himl = LISTVIEW_CreateCheckBoxIL(infoPtr); 8712 if(!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS)) 8713 ImageList_Destroy(infoPtr->himlState); 8714 } 8715 himl = LISTVIEW_SetImageList(infoPtr, LVSIL_STATE, himl); 8716 /* checkbox list replaces previous custom list or... */ 8717 if(((infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) && 8718 !(infoPtr->dwStyle & LVS_SHAREIMAGELISTS)) || 8719 /* ...previous was checkbox list */ 8720 (old_ex_style & LVS_EX_CHECKBOXES)) 8721 ImageList_Destroy(himl); 8722 } 8723 8724 if((infoPtr->dwLvExStyle ^ old_ex_style) & LVS_EX_HEADERDRAGDROP) 8725 { 8726 DWORD style; 8727 8728 /* if not already created */ 8729 LISTVIEW_CreateHeader(infoPtr); 8730 8731 style = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE); 8732 if (infoPtr->dwLvExStyle & LVS_EX_HEADERDRAGDROP) 8733 style |= HDS_DRAGDROP; 8734 else 8735 style &= ~HDS_DRAGDROP; 8736 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, style); 8737 } 8738 8739 /* GRIDLINES adds decoration at top so changes sizes */ 8740 if((infoPtr->dwLvExStyle ^ old_ex_style) & LVS_EX_GRIDLINES) 8741 { 8742 LISTVIEW_CreateHeader(infoPtr); 8743 LISTVIEW_UpdateSize(infoPtr); 8744 } 8745 8746 if((infoPtr->dwLvExStyle ^ old_ex_style) & LVS_EX_FULLROWSELECT) 8747 { 8748 LISTVIEW_CreateHeader(infoPtr); 8749 } 8750 8751 if((infoPtr->dwLvExStyle ^ old_ex_style) & LVS_EX_TRANSPARENTBKGND) 8752 { 8753 if (infoPtr->dwLvExStyle & LVS_EX_TRANSPARENTBKGND) 8754 LISTVIEW_SetBkColor(infoPtr, CLR_NONE); 8755 } 8756 8757 if((infoPtr->dwLvExStyle ^ old_ex_style) & LVS_EX_HEADERINALLVIEWS) 8758 { 8759 if (infoPtr->dwLvExStyle & LVS_EX_HEADERINALLVIEWS) 8760 LISTVIEW_CreateHeader(infoPtr); 8761 else 8762 ShowWindow(infoPtr->hwndHeader, SW_HIDE); 8763 LISTVIEW_UpdateSize(infoPtr); 8764 LISTVIEW_UpdateScroll(infoPtr); 8765 } 8766 8767 #ifdef __REACTOS__ 8768 if ((infoPtr->dwLvExStyle & LVS_EX_SNAPTOGRID) > (old_ex_style & LVS_EX_SNAPTOGRID)) 8769 { 8770 LISTVIEW_Arrange(infoPtr, LVA_SNAPTOGRID); 8771 } 8772 #endif 8773 8774 LISTVIEW_InvalidateList(infoPtr); 8775 return old_ex_style; 8776 } 8777 8778 /*** 8779 * DESCRIPTION: 8780 * Sets the new hot cursor used during hot tracking and hover selection. 8781 * 8782 * PARAMETER(S): 8783 * [I] infoPtr : valid pointer to the listview structure 8784 * [I] hCursor : the new hot cursor handle 8785 * 8786 * RETURN: 8787 * Returns the previous hot cursor 8788 */ 8789 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor) 8790 { 8791 HCURSOR oldCursor = infoPtr->hHotCursor; 8792 8793 infoPtr->hHotCursor = hCursor; 8794 8795 return oldCursor; 8796 } 8797 8798 8799 /*** 8800 * DESCRIPTION: 8801 * Sets the hot item index. 8802 * 8803 * PARAMETERS: 8804 * [I] infoPtr : valid pointer to the listview structure 8805 * [I] iIndex : index 8806 * 8807 * RETURN: 8808 * SUCCESS : previous hot item index 8809 * FAILURE : -1 (no hot item) 8810 */ 8811 static INT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex) 8812 { 8813 INT iOldIndex = infoPtr->nHotItem; 8814 8815 infoPtr->nHotItem = iIndex; 8816 8817 return iOldIndex; 8818 } 8819 8820 8821 /*** 8822 * DESCRIPTION: 8823 * Sets the amount of time the cursor must hover over an item before it is selected. 8824 * 8825 * PARAMETER(S): 8826 * [I] infoPtr : valid pointer to the listview structure 8827 * [I] dwHoverTime : hover time, if -1 the hover time is set to the default 8828 * 8829 * RETURN: 8830 * Returns the previous hover time 8831 */ 8832 static DWORD LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime) 8833 { 8834 DWORD oldHoverTime = infoPtr->dwHoverTime; 8835 8836 infoPtr->dwHoverTime = dwHoverTime; 8837 8838 return oldHoverTime; 8839 } 8840 8841 /*** 8842 * DESCRIPTION: 8843 * Sets spacing for icons of LVS_ICON style. 8844 * 8845 * PARAMETER(S): 8846 * [I] infoPtr : valid pointer to the listview structure 8847 * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize) 8848 * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize) 8849 * 8850 * RETURN: 8851 * MAKELONG(oldcx, oldcy) 8852 */ 8853 static DWORD LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, INT cx, INT cy) 8854 { 8855 INT iconWidth = 0, iconHeight = 0; 8856 DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy); 8857 8858 TRACE("requested=(%d,%d)\n", cx, cy); 8859 8860 /* set to defaults, if instructed to */ 8861 if (cx == -1 && cy == -1) 8862 { 8863 infoPtr->autoSpacing = TRUE; 8864 if (infoPtr->himlNormal) 8865 ImageList_GetIconSize(infoPtr->himlNormal, &iconWidth, &iconHeight); 8866 cx = GetSystemMetrics(SM_CXICONSPACING) - GetSystemMetrics(SM_CXICON) + iconWidth; 8867 cy = GetSystemMetrics(SM_CYICONSPACING) - GetSystemMetrics(SM_CYICON) + iconHeight; 8868 } 8869 else 8870 infoPtr->autoSpacing = FALSE; 8871 8872 /* if 0 then keep width */ 8873 if (cx != 0) 8874 infoPtr->iconSpacing.cx = cx; 8875 8876 /* if 0 then keep height */ 8877 if (cy != 0) 8878 infoPtr->iconSpacing.cy = cy; 8879 8880 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%d,%d), ntmH=%d\n", 8881 LOWORD(oldspacing), HIWORD(oldspacing), infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy, 8882 infoPtr->iconSize.cx, infoPtr->iconSize.cy, 8883 infoPtr->ntmHeight); 8884 8885 /* these depend on the iconSpacing */ 8886 LISTVIEW_UpdateItemSize(infoPtr); 8887 8888 return oldspacing; 8889 } 8890 8891 static inline void set_icon_size(SIZE *size, HIMAGELIST himl, BOOL is_small) 8892 { 8893 INT cx, cy; 8894 8895 if (himl && ImageList_GetIconSize(himl, &cx, &cy)) 8896 { 8897 size->cx = cx; 8898 size->cy = cy; 8899 } 8900 else 8901 { 8902 size->cx = GetSystemMetrics(is_small ? SM_CXSMICON : SM_CXICON); 8903 size->cy = GetSystemMetrics(is_small ? SM_CYSMICON : SM_CYICON); 8904 } 8905 } 8906 8907 /*** 8908 * DESCRIPTION: 8909 * Sets image lists. 8910 * 8911 * PARAMETER(S): 8912 * [I] infoPtr : valid pointer to the listview structure 8913 * [I] nType : image list type 8914 * [I] himl : image list handle 8915 * 8916 * RETURN: 8917 * SUCCESS : old image list 8918 * FAILURE : NULL 8919 */ 8920 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl) 8921 { 8922 INT oldHeight = infoPtr->nItemHeight; 8923 HIMAGELIST himlOld = 0; 8924 8925 TRACE("(nType=%d, himl=%p)\n", nType, himl); 8926 8927 switch (nType) 8928 { 8929 case LVSIL_NORMAL: 8930 himlOld = infoPtr->himlNormal; 8931 infoPtr->himlNormal = himl; 8932 if (infoPtr->uView == LV_VIEW_ICON) set_icon_size(&infoPtr->iconSize, himl, FALSE); 8933 if (infoPtr->autoSpacing) 8934 LISTVIEW_SetIconSpacing(infoPtr, -1, -1); 8935 break; 8936 8937 case LVSIL_SMALL: 8938 himlOld = infoPtr->himlSmall; 8939 infoPtr->himlSmall = himl; 8940 if (infoPtr->uView != LV_VIEW_ICON) set_icon_size(&infoPtr->iconSize, himl, TRUE); 8941 if (infoPtr->hwndHeader) 8942 SendMessageW(infoPtr->hwndHeader, HDM_SETIMAGELIST, 0, (LPARAM)himl); 8943 break; 8944 8945 case LVSIL_STATE: 8946 himlOld = infoPtr->himlState; 8947 infoPtr->himlState = himl; 8948 set_icon_size(&infoPtr->iconStateSize, himl, TRUE); 8949 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE); 8950 break; 8951 8952 default: 8953 ERR("Unknown icon type=%d\n", nType); 8954 return NULL; 8955 } 8956 8957 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr); 8958 if (infoPtr->nItemHeight != oldHeight) 8959 LISTVIEW_UpdateScroll(infoPtr); 8960 8961 return himlOld; 8962 } 8963 8964 /*** 8965 * DESCRIPTION: 8966 * Preallocates memory (does *not* set the actual count of items !) 8967 * 8968 * PARAMETER(S): 8969 * [I] infoPtr : valid pointer to the listview structure 8970 * [I] nItems : item count (projected number of items to allocate) 8971 * [I] dwFlags : update flags 8972 * 8973 * RETURN: 8974 * SUCCESS : TRUE 8975 * FAILURE : FALSE 8976 */ 8977 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags) 8978 { 8979 TRACE("(nItems=%d, dwFlags=%x)\n", nItems, dwFlags); 8980 8981 if (infoPtr->dwStyle & LVS_OWNERDATA) 8982 { 8983 INT nOldCount = infoPtr->nItemCount; 8984 infoPtr->nItemCount = nItems; 8985 8986 if (nItems < nOldCount) 8987 { 8988 RANGE range = { nItems, nOldCount }; 8989 ranges_del(infoPtr->selectionRanges, range); 8990 if (infoPtr->nFocusedItem >= nItems) 8991 { 8992 LISTVIEW_SetItemFocus(infoPtr, -1); 8993 infoPtr->nFocusedItem = -1; 8994 SetRectEmpty(&infoPtr->rcFocus); 8995 } 8996 } 8997 8998 LISTVIEW_UpdateScroll(infoPtr); 8999 9000 /* the flags are valid only in ownerdata report and list modes */ 9001 if (infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON) dwFlags = 0; 9002 9003 if (!(dwFlags & LVSICF_NOSCROLL) && infoPtr->nFocusedItem != -1) 9004 LISTVIEW_EnsureVisible(infoPtr, infoPtr->nFocusedItem, FALSE); 9005 9006 if (!(dwFlags & LVSICF_NOINVALIDATEALL)) 9007 LISTVIEW_InvalidateList(infoPtr); 9008 else 9009 { 9010 INT nFrom, nTo; 9011 POINT Origin; 9012 RECT rcErase; 9013 9014 LISTVIEW_GetOrigin(infoPtr, &Origin); 9015 nFrom = min(nOldCount, nItems); 9016 nTo = max(nOldCount, nItems); 9017 9018 if (infoPtr->uView == LV_VIEW_DETAILS) 9019 { 9020 SetRect(&rcErase, 0, nFrom * infoPtr->nItemHeight, infoPtr->nItemWidth, 9021 nTo * infoPtr->nItemHeight); 9022 OffsetRect(&rcErase, Origin.x, Origin.y); 9023 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList)) 9024 LISTVIEW_InvalidateRect(infoPtr, &rcErase); 9025 } 9026 else /* LV_VIEW_LIST */ 9027 { 9028 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr); 9029 9030 rcErase.left = (nFrom / nPerCol) * infoPtr->nItemWidth; 9031 rcErase.top = (nFrom % nPerCol) * infoPtr->nItemHeight; 9032 rcErase.right = rcErase.left + infoPtr->nItemWidth; 9033 rcErase.bottom = nPerCol * infoPtr->nItemHeight; 9034 OffsetRect(&rcErase, Origin.x, Origin.y); 9035 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList)) 9036 LISTVIEW_InvalidateRect(infoPtr, &rcErase); 9037 9038 rcErase.left = (nFrom / nPerCol + 1) * infoPtr->nItemWidth; 9039 rcErase.top = 0; 9040 rcErase.right = (nTo / nPerCol + 1) * infoPtr->nItemWidth; 9041 rcErase.bottom = nPerCol * infoPtr->nItemHeight; 9042 OffsetRect(&rcErase, Origin.x, Origin.y); 9043 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList)) 9044 LISTVIEW_InvalidateRect(infoPtr, &rcErase); 9045 } 9046 } 9047 } 9048 else 9049 { 9050 /* According to MSDN for non-LVS_OWNERDATA this is just 9051 * a performance issue. The control allocates its internal 9052 * data structures for the number of items specified. It 9053 * cuts down on the number of memory allocations. Therefore 9054 * we will just issue a WARN here 9055 */ 9056 WARN("for non-ownerdata performance option not implemented.\n"); 9057 } 9058 9059 return TRUE; 9060 } 9061 9062 /*** 9063 * DESCRIPTION: 9064 * Sets the position of an item. 9065 * 9066 * PARAMETER(S): 9067 * [I] infoPtr : valid pointer to the listview structure 9068 * [I] nItem : item index 9069 * [I] pt : coordinate 9070 * 9071 * RETURN: 9072 * SUCCESS : TRUE 9073 * FAILURE : FALSE 9074 */ 9075 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, const POINT *pt) 9076 { 9077 POINT Origin, Pt; 9078 9079 TRACE("(nItem=%d, pt=%s)\n", nItem, wine_dbgstr_point(pt)); 9080 9081 if (!pt || nItem < 0 || nItem >= infoPtr->nItemCount || 9082 !(infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON)) return FALSE; 9083 9084 #ifdef __REACTOS__ 9085 /* FIXME: This should really call snap to grid if auto-arrange is enabled 9086 and limit the size of the grid to nItemCount elements */ 9087 if (is_autoarrange(infoPtr)) return FALSE; 9088 #endif 9089 9090 Pt = *pt; 9091 LISTVIEW_GetOrigin(infoPtr, &Origin); 9092 9093 /* This point value seems to be an undocumented feature. 9094 * The best guess is that it means either at the origin, 9095 * or at true beginning of the list. I will assume the origin. */ 9096 if ((Pt.x == -1) && (Pt.y == -1)) 9097 Pt = Origin; 9098 9099 if (infoPtr->uView == LV_VIEW_ICON) 9100 { 9101 Pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2; 9102 Pt.y -= ICON_TOP_PADDING; 9103 } 9104 Pt.x -= Origin.x; 9105 Pt.y -= Origin.y; 9106 9107 #ifdef __REACTOS__ 9108 if (infoPtr->dwLvExStyle & LVS_EX_SNAPTOGRID) 9109 { 9110 Pt.x = max(0, Pt.x + (infoPtr->nItemWidth >> 1) - (Pt.x + (infoPtr->nItemWidth >> 1)) % infoPtr->nItemWidth); 9111 Pt.y = max(0, Pt.y + (infoPtr->nItemHeight >> 1) - (Pt.y + (infoPtr->nItemHeight >> 1)) % infoPtr->nItemHeight); 9112 } 9113 #endif 9114 9115 return LISTVIEW_MoveIconTo(infoPtr, nItem, &Pt, FALSE); 9116 } 9117 9118 /*** 9119 * DESCRIPTION: 9120 * Sets the state of one or many items. 9121 * 9122 * PARAMETER(S): 9123 * [I] infoPtr : valid pointer to the listview structure 9124 * [I] nItem : item index 9125 * [I] item : item or subitem info 9126 * 9127 * RETURN: 9128 * SUCCESS : TRUE 9129 * FAILURE : FALSE 9130 */ 9131 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *item) 9132 { 9133 BOOL ret = TRUE; 9134 LVITEMW lvItem; 9135 9136 if (!item) return FALSE; 9137 9138 lvItem.iItem = nItem; 9139 lvItem.iSubItem = 0; 9140 lvItem.mask = LVIF_STATE; 9141 lvItem.state = item->state; 9142 lvItem.stateMask = item->stateMask; 9143 TRACE("item=%s\n", debuglvitem_t(&lvItem, TRUE)); 9144 9145 if (nItem == -1) 9146 { 9147 UINT oldstate = 0; 9148 DWORD old_mask; 9149 9150 /* special case optimization for recurring attempt to deselect all */ 9151 if (lvItem.state == 0 && lvItem.stateMask == LVIS_SELECTED && !LISTVIEW_GetSelectedCount(infoPtr)) 9152 return TRUE; 9153 9154 /* select all isn't allowed in LVS_SINGLESEL */ 9155 if ((lvItem.state & lvItem.stateMask & LVIS_SELECTED) && (infoPtr->dwStyle & LVS_SINGLESEL)) 9156 return FALSE; 9157 9158 /* focus all isn't allowed */ 9159 if (lvItem.state & lvItem.stateMask & LVIS_FOCUSED) return FALSE; 9160 9161 old_mask = infoPtr->notify_mask & NOTIFY_MASK_ITEM_CHANGE; 9162 if (infoPtr->dwStyle & LVS_OWNERDATA) 9163 { 9164 infoPtr->notify_mask &= ~NOTIFY_MASK_ITEM_CHANGE; 9165 if (!(lvItem.state & LVIS_SELECTED) && LISTVIEW_GetSelectedCount(infoPtr)) 9166 oldstate |= LVIS_SELECTED; 9167 if (infoPtr->nFocusedItem != -1) oldstate |= LVIS_FOCUSED; 9168 } 9169 9170 /* apply to all items */ 9171 for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++) 9172 if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) ret = FALSE; 9173 9174 if (infoPtr->dwStyle & LVS_OWNERDATA) 9175 { 9176 NMLISTVIEW nmlv; 9177 9178 infoPtr->notify_mask |= old_mask; 9179 9180 nmlv.iItem = -1; 9181 nmlv.iSubItem = 0; 9182 nmlv.uNewState = lvItem.state & lvItem.stateMask; 9183 nmlv.uOldState = oldstate & lvItem.stateMask; 9184 nmlv.uChanged = LVIF_STATE; 9185 nmlv.ptAction.x = nmlv.ptAction.y = 0; 9186 nmlv.lParam = 0; 9187 9188 notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv); 9189 } 9190 } 9191 else 9192 ret = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE); 9193 9194 return ret; 9195 } 9196 9197 /*** 9198 * DESCRIPTION: 9199 * Sets the text of an item or subitem. 9200 * 9201 * PARAMETER(S): 9202 * [I] hwnd : window handle 9203 * [I] nItem : item index 9204 * [I] lpLVItem : item or subitem info 9205 * [I] isW : TRUE if input is Unicode 9206 * 9207 * RETURN: 9208 * SUCCESS : TRUE 9209 * FAILURE : FALSE 9210 */ 9211 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem, BOOL isW) 9212 { 9213 LVITEMW lvItem; 9214 9215 if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE; 9216 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE; 9217 9218 lvItem.iItem = nItem; 9219 lvItem.iSubItem = lpLVItem->iSubItem; 9220 lvItem.mask = LVIF_TEXT; 9221 lvItem.pszText = lpLVItem->pszText; 9222 lvItem.cchTextMax = lpLVItem->cchTextMax; 9223 9224 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW); 9225 9226 return LISTVIEW_SetItemT(infoPtr, &lvItem, isW); 9227 } 9228 9229 /*** 9230 * DESCRIPTION: 9231 * Set item index that marks the start of a multiple selection. 9232 * 9233 * PARAMETER(S): 9234 * [I] infoPtr : valid pointer to the listview structure 9235 * [I] nIndex : index 9236 * 9237 * RETURN: 9238 * Index number or -1 if there is no selection mark. 9239 */ 9240 static INT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex) 9241 { 9242 INT nOldIndex = infoPtr->nSelectionMark; 9243 9244 TRACE("(nIndex=%d)\n", nIndex); 9245 9246 if (nIndex >= -1 && nIndex < infoPtr->nItemCount) 9247 infoPtr->nSelectionMark = nIndex; 9248 9249 return nOldIndex; 9250 } 9251 9252 /*** 9253 * DESCRIPTION: 9254 * Sets the text background color. 9255 * 9256 * PARAMETER(S): 9257 * [I] infoPtr : valid pointer to the listview structure 9258 * [I] color : text background color 9259 * 9260 * RETURN: 9261 * SUCCESS : TRUE 9262 * FAILURE : FALSE 9263 */ 9264 static BOOL LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF color) 9265 { 9266 TRACE("(color=%x)\n", color); 9267 9268 infoPtr->clrTextBk = color; 9269 return TRUE; 9270 } 9271 9272 /*** 9273 * DESCRIPTION: 9274 * Sets the text foreground color. 9275 * 9276 * PARAMETER(S): 9277 * [I] infoPtr : valid pointer to the listview structure 9278 * [I] color : text color 9279 * 9280 * RETURN: 9281 * SUCCESS : TRUE 9282 * FAILURE : FALSE 9283 */ 9284 static BOOL LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF color) 9285 { 9286 TRACE("(color=%x)\n", color); 9287 9288 infoPtr->clrText = color; 9289 return TRUE; 9290 } 9291 9292 /*** 9293 * DESCRIPTION: 9294 * Sets new ToolTip window to ListView control. 9295 * 9296 * PARAMETER(S): 9297 * [I] infoPtr : valid pointer to the listview structure 9298 * [I] hwndNewToolTip : handle to new ToolTip 9299 * 9300 * RETURN: 9301 * old tool tip 9302 */ 9303 static HWND LISTVIEW_SetToolTips( LISTVIEW_INFO *infoPtr, HWND hwndNewToolTip) 9304 { 9305 HWND hwndOldToolTip = infoPtr->hwndToolTip; 9306 infoPtr->hwndToolTip = hwndNewToolTip; 9307 return hwndOldToolTip; 9308 } 9309 9310 /* 9311 * DESCRIPTION: 9312 * sets the Unicode character format flag for the control 9313 * PARAMETER(S): 9314 * [I] infoPtr :valid pointer to the listview structure 9315 * [I] fUnicode :true to switch to UNICODE false to switch to ANSI 9316 * 9317 * RETURN: 9318 * Old Unicode Format 9319 */ 9320 static BOOL LISTVIEW_SetUnicodeFormat( LISTVIEW_INFO *infoPtr, BOOL unicode) 9321 { 9322 SHORT rc = infoPtr->notifyFormat; 9323 infoPtr->notifyFormat = (unicode) ? NFR_UNICODE : NFR_ANSI; 9324 return rc == NFR_UNICODE; 9325 } 9326 9327 /* 9328 * DESCRIPTION: 9329 * sets the control view mode 9330 * PARAMETER(S): 9331 * [I] infoPtr :valid pointer to the listview structure 9332 * [I] nView :new view mode value 9333 * 9334 * RETURN: 9335 * SUCCESS: 1 9336 * FAILURE: -1 9337 */ 9338 static INT LISTVIEW_SetView(LISTVIEW_INFO *infoPtr, DWORD nView) 9339 { 9340 HIMAGELIST himl; 9341 9342 if (infoPtr->uView == nView) return 1; 9343 9344 if ((INT)nView < 0 || nView > LV_VIEW_MAX) return -1; 9345 if (nView == LV_VIEW_TILE) 9346 { 9347 FIXME("View LV_VIEW_TILE unimplemented\n"); 9348 return -1; 9349 } 9350 9351 infoPtr->uView = nView; 9352 9353 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0); 9354 ShowWindow(infoPtr->hwndHeader, SW_HIDE); 9355 9356 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE); 9357 SetRectEmpty(&infoPtr->rcFocus); 9358 9359 himl = (nView == LV_VIEW_ICON ? infoPtr->himlNormal : infoPtr->himlSmall); 9360 set_icon_size(&infoPtr->iconSize, himl, nView != LV_VIEW_ICON); 9361 9362 switch (nView) 9363 { 9364 case LV_VIEW_ICON: 9365 case LV_VIEW_SMALLICON: 9366 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT); 9367 break; 9368 case LV_VIEW_DETAILS: 9369 { 9370 HDLAYOUT hl; 9371 WINDOWPOS wp; 9372 9373 LISTVIEW_CreateHeader( infoPtr ); 9374 9375 hl.prc = &infoPtr->rcList; 9376 hl.pwpos = ℘ 9377 SendMessageW(infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl); 9378 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy, 9379 wp.flags | ((infoPtr->dwStyle & LVS_NOCOLUMNHEADER) ? SWP_HIDEWINDOW : SWP_SHOWWINDOW)); 9380 break; 9381 } 9382 case LV_VIEW_LIST: 9383 break; 9384 } 9385 9386 LISTVIEW_UpdateItemSize(infoPtr); 9387 LISTVIEW_UpdateSize(infoPtr); 9388 LISTVIEW_UpdateScroll(infoPtr); 9389 LISTVIEW_InvalidateList(infoPtr); 9390 9391 TRACE("nView=%d\n", nView); 9392 9393 return 1; 9394 } 9395 9396 /* LISTVIEW_SetWorkAreas */ 9397 9398 /*** 9399 * DESCRIPTION: 9400 * Callback internally used by LISTVIEW_SortItems() in response of LVM_SORTITEMS 9401 * 9402 * PARAMETER(S): 9403 * [I] first : pointer to first ITEM_INFO to compare 9404 * [I] second : pointer to second ITEM_INFO to compare 9405 * [I] lParam : HWND of control 9406 * 9407 * RETURN: 9408 * if first comes before second : negative 9409 * if first comes after second : positive 9410 * if first and second are equivalent : zero 9411 */ 9412 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam) 9413 { 9414 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam; 9415 ITEM_INFO* lv_first = DPA_GetPtr( first, 0 ); 9416 ITEM_INFO* lv_second = DPA_GetPtr( second, 0 ); 9417 9418 /* Forward the call to the client defined callback */ 9419 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort ); 9420 } 9421 9422 /*** 9423 * DESCRIPTION: 9424 * Callback internally used by LISTVIEW_SortItems() in response of LVM_SORTITEMSEX 9425 * 9426 * PARAMETER(S): 9427 * [I] first : pointer to first ITEM_INFO to compare 9428 * [I] second : pointer to second ITEM_INFO to compare 9429 * [I] lParam : HWND of control 9430 * 9431 * RETURN: 9432 * if first comes before second : negative 9433 * if first comes after second : positive 9434 * if first and second are equivalent : zero 9435 */ 9436 static INT WINAPI LISTVIEW_CallBackCompareEx(LPVOID first, LPVOID second, LPARAM lParam) 9437 { 9438 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam; 9439 INT first_idx = DPA_GetPtrIndex( infoPtr->hdpaItems, first ); 9440 INT second_idx = DPA_GetPtrIndex( infoPtr->hdpaItems, second ); 9441 9442 /* Forward the call to the client defined callback */ 9443 return (infoPtr->pfnCompare)( first_idx, second_idx, infoPtr->lParamSort ); 9444 } 9445 9446 /*** 9447 * DESCRIPTION: 9448 * Sorts the listview items. 9449 * 9450 * PARAMETER(S): 9451 * [I] infoPtr : valid pointer to the listview structure 9452 * [I] pfnCompare : application-defined value 9453 * [I] lParamSort : pointer to comparison callback 9454 * [I] IsEx : TRUE when LVM_SORTITEMSEX used 9455 * 9456 * RETURN: 9457 * SUCCESS : TRUE 9458 * FAILURE : FALSE 9459 */ 9460 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare, 9461 LPARAM lParamSort, BOOL IsEx) 9462 { 9463 HDPA hdpaSubItems; 9464 ITEM_INFO *lpItem; 9465 LPVOID selectionMarkItem = NULL; 9466 LPVOID focusedItem = NULL; 9467 int i; 9468 9469 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort); 9470 9471 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE; 9472 9473 if (!pfnCompare) return FALSE; 9474 if (!infoPtr->hdpaItems) return FALSE; 9475 9476 /* if there are 0 or 1 items, there is no need to sort */ 9477 if (infoPtr->nItemCount < 2) return TRUE; 9478 9479 /* clear selection */ 9480 ranges_clear(infoPtr->selectionRanges); 9481 9482 /* save selection mark and focused item */ 9483 if (infoPtr->nSelectionMark >= 0) 9484 selectionMarkItem = DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark); 9485 if (infoPtr->nFocusedItem >= 0) 9486 focusedItem = DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem); 9487 9488 infoPtr->pfnCompare = pfnCompare; 9489 infoPtr->lParamSort = lParamSort; 9490 if (IsEx) 9491 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompareEx, (LPARAM)infoPtr); 9492 else 9493 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr); 9494 9495 /* restore selection ranges */ 9496 for (i=0; i < infoPtr->nItemCount; i++) 9497 { 9498 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, i); 9499 lpItem = DPA_GetPtr(hdpaSubItems, 0); 9500 9501 if (lpItem->state & LVIS_SELECTED) 9502 ranges_additem(infoPtr->selectionRanges, i); 9503 } 9504 /* restore selection mark and focused item */ 9505 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem); 9506 infoPtr->nFocusedItem = DPA_GetPtrIndex(infoPtr->hdpaItems, focusedItem); 9507 9508 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */ 9509 9510 /* refresh the display */ 9511 LISTVIEW_InvalidateList(infoPtr); 9512 return TRUE; 9513 } 9514 9515 /*** 9516 * DESCRIPTION: 9517 * Update theme handle after a theme change. 9518 * 9519 * PARAMETER(S): 9520 * [I] infoPtr : valid pointer to the listview structure 9521 * 9522 * RETURN: 9523 * SUCCESS : 0 9524 * FAILURE : something else 9525 */ 9526 static LRESULT LISTVIEW_ThemeChanged(const LISTVIEW_INFO *infoPtr) 9527 { 9528 HTHEME theme = GetWindowTheme(infoPtr->hwndSelf); 9529 CloseThemeData(theme); 9530 OpenThemeData(infoPtr->hwndSelf, themeClass); 9531 return 0; 9532 } 9533 9534 /*** 9535 * DESCRIPTION: 9536 * Updates an items or rearranges the listview control. 9537 * 9538 * PARAMETER(S): 9539 * [I] infoPtr : valid pointer to the listview structure 9540 * [I] nItem : item index 9541 * 9542 * RETURN: 9543 * SUCCESS : TRUE 9544 * FAILURE : FALSE 9545 */ 9546 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem) 9547 { 9548 TRACE("(nItem=%d)\n", nItem); 9549 9550 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE; 9551 9552 /* rearrange with default alignment style */ 9553 if (is_autoarrange(infoPtr)) 9554 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT); 9555 else 9556 LISTVIEW_InvalidateItem(infoPtr, nItem); 9557 9558 return TRUE; 9559 } 9560 9561 /*** 9562 * DESCRIPTION: 9563 * Draw the track line at the place defined in the infoPtr structure. 9564 * The line is drawn with a XOR pen so drawing the line for the second time 9565 * in the same place erases the line. 9566 * 9567 * PARAMETER(S): 9568 * [I] infoPtr : valid pointer to the listview structure 9569 * 9570 * RETURN: 9571 * SUCCESS : TRUE 9572 * FAILURE : FALSE 9573 */ 9574 static BOOL LISTVIEW_DrawTrackLine(const LISTVIEW_INFO *infoPtr) 9575 { 9576 HDC hdc; 9577 9578 if (infoPtr->xTrackLine == -1) 9579 return FALSE; 9580 9581 if (!(hdc = GetDC(infoPtr->hwndSelf))) 9582 return FALSE; 9583 PatBlt( hdc, infoPtr->xTrackLine, infoPtr->rcList.top, 9584 1, infoPtr->rcList.bottom - infoPtr->rcList.top, DSTINVERT ); 9585 ReleaseDC(infoPtr->hwndSelf, hdc); 9586 return TRUE; 9587 } 9588 9589 /*** 9590 * DESCRIPTION: 9591 * Called when an edit control should be displayed. This function is called after 9592 * we are sure that there was a single click - not a double click (this is a TIMERPROC). 9593 * 9594 * PARAMETER(S): 9595 * [I] hwnd : Handle to the listview 9596 * [I] uMsg : WM_TIMER (ignored) 9597 * [I] idEvent : The timer ID interpreted as a pointer to a DELAYED_EDIT_ITEM struct 9598 * [I] dwTimer : The elapsed time (ignored) 9599 * 9600 * RETURN: 9601 * None. 9602 */ 9603 static VOID CALLBACK LISTVIEW_DelayedEditItem(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime) 9604 { 9605 DELAYED_ITEM_EDIT *editItem = (DELAYED_ITEM_EDIT *)idEvent; 9606 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0); 9607 9608 KillTimer(hwnd, idEvent); 9609 editItem->fEnabled = FALSE; 9610 /* check if the item is still selected */ 9611 if (infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, editItem->iItem, LVIS_SELECTED)) 9612 LISTVIEW_EditLabelT(infoPtr, editItem->iItem, TRUE); 9613 } 9614 9615 /*** 9616 * DESCRIPTION: 9617 * Creates the listview control - the WM_NCCREATE phase. 9618 * 9619 * PARAMETER(S): 9620 * [I] hwnd : window handle 9621 * [I] lpcs : the create parameters 9622 * 9623 * RETURN: 9624 * Success: TRUE 9625 * Failure: FALSE 9626 */ 9627 static LRESULT LISTVIEW_NCCreate(HWND hwnd, WPARAM wParam, const CREATESTRUCTW *lpcs) 9628 { 9629 LISTVIEW_INFO *infoPtr; 9630 LOGFONTW logFont; 9631 9632 TRACE("(lpcs=%p)\n", lpcs); 9633 9634 /* initialize info pointer */ 9635 infoPtr = Alloc(sizeof(LISTVIEW_INFO)); 9636 if (!infoPtr) return FALSE; 9637 9638 SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr); 9639 9640 infoPtr->hwndSelf = hwnd; 9641 infoPtr->dwStyle = lpcs->style; /* Note: may be changed in WM_CREATE */ 9642 map_style_view(infoPtr); 9643 /* determine the type of structures to use */ 9644 infoPtr->hwndNotify = lpcs->hwndParent; 9645 /* infoPtr->notifyFormat will be filled in WM_CREATE */ 9646 9647 /* initialize color information */ 9648 infoPtr->clrBk = CLR_NONE; 9649 infoPtr->clrText = CLR_DEFAULT; 9650 infoPtr->clrTextBk = CLR_DEFAULT; 9651 LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow); 9652 #ifdef __REACTOS__ 9653 infoPtr->bDefaultBkColor = TRUE; 9654 #endif 9655 9656 /* set default values */ 9657 infoPtr->nFocusedItem = -1; 9658 infoPtr->nSelectionMark = -1; 9659 infoPtr->nHotItem = -1; 9660 infoPtr->redraw = TRUE; 9661 infoPtr->bNoItemMetrics = TRUE; 9662 infoPtr->notify_mask = NOTIFY_MASK_UNMASK_ALL; 9663 infoPtr->autoSpacing = TRUE; 9664 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING) - GetSystemMetrics(SM_CXICON); 9665 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING) - GetSystemMetrics(SM_CYICON); 9666 infoPtr->nEditLabelItem = -1; 9667 infoPtr->nLButtonDownItem = -1; 9668 infoPtr->dwHoverTime = HOVER_DEFAULT; /* default system hover time */ 9669 infoPtr->cWheelRemainder = 0; 9670 infoPtr->nMeasureItemHeight = 0; 9671 infoPtr->xTrackLine = -1; /* no track line */ 9672 infoPtr->itemEdit.fEnabled = FALSE; 9673 infoPtr->iVersion = COMCTL32_VERSION; 9674 infoPtr->colRectsDirty = FALSE; 9675 9676 /* get default font (icon title) */ 9677 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0); 9678 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont); 9679 infoPtr->hFont = infoPtr->hDefaultFont; 9680 LISTVIEW_SaveTextMetrics(infoPtr); 9681 9682 /* allocate memory for the data structure */ 9683 if (!(infoPtr->selectionRanges = ranges_create(10))) goto fail; 9684 if (!(infoPtr->hdpaItems = DPA_Create(10))) goto fail; 9685 if (!(infoPtr->hdpaItemIds = DPA_Create(10))) goto fail; 9686 if (!(infoPtr->hdpaPosX = DPA_Create(10))) goto fail; 9687 if (!(infoPtr->hdpaPosY = DPA_Create(10))) goto fail; 9688 if (!(infoPtr->hdpaColumns = DPA_Create(10))) goto fail; 9689 9690 return DefWindowProcW(hwnd, WM_NCCREATE, wParam, (LPARAM)lpcs); 9691 9692 fail: 9693 DestroyWindow(infoPtr->hwndHeader); 9694 ranges_destroy(infoPtr->selectionRanges); 9695 DPA_Destroy(infoPtr->hdpaItems); 9696 DPA_Destroy(infoPtr->hdpaItemIds); 9697 DPA_Destroy(infoPtr->hdpaPosX); 9698 DPA_Destroy(infoPtr->hdpaPosY); 9699 DPA_Destroy(infoPtr->hdpaColumns); 9700 Free(infoPtr); 9701 return FALSE; 9702 } 9703 9704 /*** 9705 * DESCRIPTION: 9706 * Creates the listview control - the WM_CREATE phase. Most of the data is 9707 * already set up in LISTVIEW_NCCreate 9708 * 9709 * PARAMETER(S): 9710 * [I] hwnd : window handle 9711 * [I] lpcs : the create parameters 9712 * 9713 * RETURN: 9714 * Success: 0 9715 * Failure: -1 9716 */ 9717 static LRESULT LISTVIEW_Create(HWND hwnd, const CREATESTRUCTW *lpcs) 9718 { 9719 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0); 9720 9721 TRACE("(lpcs=%p, style=0x%08x)\n", lpcs, lpcs->style); 9722 9723 infoPtr->dwStyle = lpcs->style; 9724 map_style_view(infoPtr); 9725 9726 infoPtr->notifyFormat = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT, 9727 (WPARAM)infoPtr->hwndSelf, NF_QUERY); 9728 /* on error defaulting to ANSI notifications */ 9729 if (infoPtr->notifyFormat == 0) infoPtr->notifyFormat = NFR_ANSI; 9730 TRACE("notify format=%d\n", infoPtr->notifyFormat); 9731 9732 if ((infoPtr->uView == LV_VIEW_DETAILS) && (lpcs->style & WS_VISIBLE)) 9733 { 9734 if (LISTVIEW_CreateHeader(infoPtr) < 0) return -1; 9735 } 9736 else 9737 infoPtr->hwndHeader = 0; 9738 9739 /* init item size to avoid division by 0 */ 9740 LISTVIEW_UpdateItemSize (infoPtr); 9741 LISTVIEW_UpdateSize (infoPtr); 9742 9743 if (infoPtr->uView == LV_VIEW_DETAILS) 9744 { 9745 if (!(LVS_NOCOLUMNHEADER & lpcs->style) && (WS_VISIBLE & lpcs->style)) 9746 { 9747 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL); 9748 } 9749 LISTVIEW_UpdateScroll(infoPtr); 9750 /* send WM_MEASUREITEM notification */ 9751 if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED) notify_measureitem(infoPtr); 9752 } 9753 9754 OpenThemeData(hwnd, themeClass); 9755 9756 /* initialize the icon sizes */ 9757 set_icon_size(&infoPtr->iconSize, infoPtr->himlNormal, infoPtr->uView != LV_VIEW_ICON); 9758 set_icon_size(&infoPtr->iconStateSize, infoPtr->himlState, TRUE); 9759 return 0; 9760 } 9761 9762 /*** 9763 * DESCRIPTION: 9764 * Destroys the listview control. 9765 * 9766 * PARAMETER(S): 9767 * [I] infoPtr : valid pointer to the listview structure 9768 * 9769 * RETURN: 9770 * Success: 0 9771 * Failure: -1 9772 */ 9773 static LRESULT LISTVIEW_Destroy(LISTVIEW_INFO *infoPtr) 9774 { 9775 HTHEME theme = GetWindowTheme(infoPtr->hwndSelf); 9776 CloseThemeData(theme); 9777 9778 /* delete all items */ 9779 LISTVIEW_DeleteAllItems(infoPtr, TRUE); 9780 9781 return 0; 9782 } 9783 9784 /*** 9785 * DESCRIPTION: 9786 * Enables the listview control. 9787 * 9788 * PARAMETER(S): 9789 * [I] infoPtr : valid pointer to the listview structure 9790 * [I] bEnable : specifies whether to enable or disable the window 9791 * 9792 * RETURN: 9793 * SUCCESS : TRUE 9794 * FAILURE : FALSE 9795 */ 9796 static BOOL LISTVIEW_Enable(const LISTVIEW_INFO *infoPtr) 9797 { 9798 if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED) 9799 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE); 9800 return TRUE; 9801 } 9802 9803 /*** 9804 * DESCRIPTION: 9805 * Erases the background of the listview control. 9806 * 9807 * PARAMETER(S): 9808 * [I] infoPtr : valid pointer to the listview structure 9809 * [I] hdc : device context handle 9810 * 9811 * RETURN: 9812 * SUCCESS : TRUE 9813 * FAILURE : FALSE 9814 */ 9815 static inline BOOL LISTVIEW_EraseBkgnd(const LISTVIEW_INFO *infoPtr, HDC hdc) 9816 { 9817 RECT rc; 9818 9819 TRACE("(hdc=%p)\n", hdc); 9820 9821 if (!GetClipBox(hdc, &rc)) return FALSE; 9822 9823 if (infoPtr->clrBk == CLR_NONE) 9824 { 9825 if (infoPtr->dwLvExStyle & LVS_EX_TRANSPARENTBKGND) 9826 return SendMessageW(infoPtr->hwndNotify, WM_PRINTCLIENT, 9827 (WPARAM)hdc, PRF_ERASEBKGND); 9828 else 9829 return SendMessageW(infoPtr->hwndNotify, WM_ERASEBKGND, (WPARAM)hdc, 0); 9830 } 9831 9832 /* for double buffered controls we need to do this during refresh */ 9833 if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) return FALSE; 9834 9835 return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc); 9836 } 9837 9838 9839 /*** 9840 * DESCRIPTION: 9841 * Helper function for LISTVIEW_[HV]Scroll *only*. 9842 * Performs vertical/horizontal scrolling by a give amount. 9843 * 9844 * PARAMETER(S): 9845 * [I] infoPtr : valid pointer to the listview structure 9846 * [I] dx : amount of horizontal scroll 9847 * [I] dy : amount of vertical scroll 9848 */ 9849 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy) 9850 { 9851 /* now we can scroll the list */ 9852 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList, 9853 &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE); 9854 /* if we have focus, adjust rect */ 9855 OffsetRect(&infoPtr->rcFocus, dx, dy); 9856 UpdateWindow(infoPtr->hwndSelf); 9857 } 9858 9859 /*** 9860 * DESCRIPTION: 9861 * Performs vertical scrolling. 9862 * 9863 * PARAMETER(S): 9864 * [I] infoPtr : valid pointer to the listview structure 9865 * [I] nScrollCode : scroll code 9866 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise 9867 * [I] hScrollWnd : scrollbar control window handle 9868 * 9869 * RETURN: 9870 * Zero 9871 * 9872 * NOTES: 9873 * SB_LINEUP/SB_LINEDOWN: 9874 * for LVS_ICON, LVS_SMALLICON is 37 by experiment 9875 * for LVS_REPORT is 1 line 9876 * for LVS_LIST cannot occur 9877 * 9878 */ 9879 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode, 9880 INT nScrollDiff) 9881 { 9882 INT nOldScrollPos, nNewScrollPos; 9883 SCROLLINFO scrollInfo; 9884 BOOL is_an_icon; 9885 9886 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode, 9887 debugscrollcode(nScrollCode), nScrollDiff); 9888 9889 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0); 9890 9891 scrollInfo.cbSize = sizeof(SCROLLINFO); 9892 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS; 9893 9894 is_an_icon = ((infoPtr->uView == LV_VIEW_ICON) || (infoPtr->uView == LV_VIEW_SMALLICON)); 9895 9896 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1; 9897 9898 nOldScrollPos = scrollInfo.nPos; 9899 switch (nScrollCode) 9900 { 9901 case SB_INTERNAL: 9902 break; 9903 9904 case SB_LINEUP: 9905 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1; 9906 break; 9907 9908 case SB_LINEDOWN: 9909 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1; 9910 break; 9911 9912 case SB_PAGEUP: 9913 nScrollDiff = -scrollInfo.nPage; 9914 break; 9915 9916 case SB_PAGEDOWN: 9917 nScrollDiff = scrollInfo.nPage; 9918 break; 9919 9920 case SB_THUMBPOSITION: 9921 case SB_THUMBTRACK: 9922 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos; 9923 break; 9924 9925 default: 9926 nScrollDiff = 0; 9927 } 9928 9929 /* quit right away if pos isn't changing */ 9930 if (nScrollDiff == 0) return 0; 9931 9932 /* calculate new position, and handle overflows */ 9933 nNewScrollPos = scrollInfo.nPos + nScrollDiff; 9934 if (nScrollDiff > 0) { 9935 if (nNewScrollPos < nOldScrollPos || 9936 nNewScrollPos > scrollInfo.nMax) 9937 nNewScrollPos = scrollInfo.nMax; 9938 } else { 9939 if (nNewScrollPos > nOldScrollPos || 9940 nNewScrollPos < scrollInfo.nMin) 9941 nNewScrollPos = scrollInfo.nMin; 9942 } 9943 9944 /* set the new position, and reread in case it changed */ 9945 scrollInfo.fMask = SIF_POS; 9946 scrollInfo.nPos = nNewScrollPos; 9947 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE); 9948 9949 /* carry on only if it really changed */ 9950 if (nNewScrollPos == nOldScrollPos) return 0; 9951 9952 /* now adjust to client coordinates */ 9953 nScrollDiff = nOldScrollPos - nNewScrollPos; 9954 if (infoPtr->uView == LV_VIEW_DETAILS) nScrollDiff *= infoPtr->nItemHeight; 9955 9956 /* and scroll the window */ 9957 scroll_list(infoPtr, 0, nScrollDiff); 9958 9959 return 0; 9960 } 9961 9962 /*** 9963 * DESCRIPTION: 9964 * Performs horizontal scrolling. 9965 * 9966 * PARAMETER(S): 9967 * [I] infoPtr : valid pointer to the listview structure 9968 * [I] nScrollCode : scroll code 9969 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise 9970 * [I] hScrollWnd : scrollbar control window handle 9971 * 9972 * RETURN: 9973 * Zero 9974 * 9975 * NOTES: 9976 * SB_LINELEFT/SB_LINERIGHT: 9977 * for LVS_ICON, LVS_SMALLICON 1 pixel 9978 * for LVS_REPORT is 1 pixel 9979 * for LVS_LIST is 1 column --> which is a 1 because the 9980 * scroll is based on columns not pixels 9981 * 9982 */ 9983 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode, 9984 INT nScrollDiff) 9985 { 9986 INT nOldScrollPos, nNewScrollPos; 9987 SCROLLINFO scrollInfo; 9988 BOOL is_an_icon; 9989 9990 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode, 9991 debugscrollcode(nScrollCode), nScrollDiff); 9992 9993 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0); 9994 9995 scrollInfo.cbSize = sizeof(SCROLLINFO); 9996 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS; 9997 9998 is_an_icon = ((infoPtr->uView == LV_VIEW_ICON) || (infoPtr->uView == LV_VIEW_SMALLICON)); 9999 10000 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1; 10001 10002 nOldScrollPos = scrollInfo.nPos; 10003 10004 switch (nScrollCode) 10005 { 10006 case SB_INTERNAL: 10007 break; 10008 10009 case SB_LINELEFT: 10010 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1; 10011 break; 10012 10013 case SB_LINERIGHT: 10014 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1; 10015 break; 10016 10017 case SB_PAGELEFT: 10018 nScrollDiff = -scrollInfo.nPage; 10019 break; 10020 10021 case SB_PAGERIGHT: 10022 nScrollDiff = scrollInfo.nPage; 10023 break; 10024 10025 case SB_THUMBPOSITION: 10026 case SB_THUMBTRACK: 10027 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos; 10028 break; 10029 10030 default: 10031 nScrollDiff = 0; 10032 } 10033 10034 /* quit right away if pos isn't changing */ 10035 if (nScrollDiff == 0) return 0; 10036 10037 /* calculate new position, and handle overflows */ 10038 nNewScrollPos = scrollInfo.nPos + nScrollDiff; 10039 if (nScrollDiff > 0) { 10040 if (nNewScrollPos < nOldScrollPos || 10041 nNewScrollPos > scrollInfo.nMax) 10042 nNewScrollPos = scrollInfo.nMax; 10043 } else { 10044 if (nNewScrollPos > nOldScrollPos || 10045 nNewScrollPos < scrollInfo.nMin) 10046 nNewScrollPos = scrollInfo.nMin; 10047 } 10048 10049 /* set the new position, and reread in case it changed */ 10050 scrollInfo.fMask = SIF_POS; 10051 scrollInfo.nPos = nNewScrollPos; 10052 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE); 10053 10054 /* carry on only if it really changed */ 10055 if (nNewScrollPos == nOldScrollPos) return 0; 10056 10057 LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos); 10058 10059 /* now adjust to client coordinates */ 10060 nScrollDiff = nOldScrollPos - nNewScrollPos; 10061 if (infoPtr->uView == LV_VIEW_LIST) nScrollDiff *= infoPtr->nItemWidth; 10062 10063 /* and scroll the window */ 10064 scroll_list(infoPtr, nScrollDiff, 0); 10065 10066 return 0; 10067 } 10068 10069 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta) 10070 { 10071 INT pulScrollLines = 3; 10072 10073 TRACE("(wheelDelta=%d)\n", wheelDelta); 10074 10075 switch(infoPtr->uView) 10076 { 10077 case LV_VIEW_ICON: 10078 case LV_VIEW_SMALLICON: 10079 /* 10080 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number 10081 * should be fixed in the future. 10082 */ 10083 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, (wheelDelta > 0) ? 10084 -LISTVIEW_SCROLL_ICON_LINE_SIZE : LISTVIEW_SCROLL_ICON_LINE_SIZE); 10085 break; 10086 10087 case LV_VIEW_DETAILS: 10088 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0); 10089 10090 /* if scrolling changes direction, ignore left overs */ 10091 if ((wheelDelta < 0 && infoPtr->cWheelRemainder < 0) || 10092 (wheelDelta > 0 && infoPtr->cWheelRemainder > 0)) 10093 infoPtr->cWheelRemainder += wheelDelta; 10094 else 10095 infoPtr->cWheelRemainder = wheelDelta; 10096 if (infoPtr->cWheelRemainder && pulScrollLines) 10097 { 10098 int cLineScroll; 10099 pulScrollLines = min((UINT)LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines); 10100 cLineScroll = pulScrollLines * infoPtr->cWheelRemainder / WHEEL_DELTA; 10101 infoPtr->cWheelRemainder -= WHEEL_DELTA * cLineScroll / pulScrollLines; 10102 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, -cLineScroll); 10103 } 10104 break; 10105 10106 case LV_VIEW_LIST: 10107 LISTVIEW_HScroll(infoPtr, (wheelDelta > 0) ? SB_LINELEFT : SB_LINERIGHT, 0); 10108 break; 10109 } 10110 return 0; 10111 } 10112 10113 /*** 10114 * DESCRIPTION: 10115 * ??? 10116 * 10117 * PARAMETER(S): 10118 * [I] infoPtr : valid pointer to the listview structure 10119 * [I] nVirtualKey : virtual key 10120 * [I] lKeyData : key data 10121 * 10122 * RETURN: 10123 * Zero 10124 */ 10125 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData) 10126 { 10127 HWND hwndSelf = infoPtr->hwndSelf; 10128 INT nItem = -1; 10129 NMLVKEYDOWN nmKeyDown; 10130 10131 TRACE("(nVirtualKey=%d, lKeyData=%d)\n", nVirtualKey, lKeyData); 10132 10133 /* send LVN_KEYDOWN notification */ 10134 nmKeyDown.wVKey = nVirtualKey; 10135 nmKeyDown.flags = 0; 10136 notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr); 10137 if (!IsWindow(hwndSelf)) 10138 return 0; 10139 10140 switch (nVirtualKey) 10141 { 10142 case VK_SPACE: 10143 nItem = infoPtr->nFocusedItem; 10144 if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) 10145 toggle_checkbox_state(infoPtr, infoPtr->nFocusedItem); 10146 break; 10147 10148 case VK_RETURN: 10149 if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1)) 10150 { 10151 if (!notify(infoPtr, NM_RETURN)) return 0; 10152 if (!notify(infoPtr, LVN_ITEMACTIVATE)) return 0; 10153 } 10154 break; 10155 10156 case VK_HOME: 10157 if (infoPtr->nItemCount > 0) 10158 nItem = 0; 10159 break; 10160 10161 case VK_END: 10162 if (infoPtr->nItemCount > 0) 10163 nItem = infoPtr->nItemCount - 1; 10164 break; 10165 10166 case VK_LEFT: 10167 nItem = LISTVIEW_GetNextItem(infoPtr, infoPtr->nFocusedItem, LVNI_TOLEFT); 10168 break; 10169 10170 case VK_UP: 10171 nItem = LISTVIEW_GetNextItem(infoPtr, infoPtr->nFocusedItem, LVNI_ABOVE); 10172 break; 10173 10174 case VK_RIGHT: 10175 nItem = LISTVIEW_GetNextItem(infoPtr, infoPtr->nFocusedItem, LVNI_TORIGHT); 10176 break; 10177 10178 case VK_DOWN: 10179 nItem = LISTVIEW_GetNextItem(infoPtr, infoPtr->nFocusedItem, LVNI_BELOW); 10180 break; 10181 10182 case VK_PRIOR: 10183 if (infoPtr->uView == LV_VIEW_DETAILS) 10184 { 10185 INT topidx = LISTVIEW_GetTopIndex(infoPtr); 10186 if (infoPtr->nFocusedItem == topidx) 10187 nItem = topidx - LISTVIEW_GetCountPerColumn(infoPtr) + 1; 10188 else 10189 nItem = topidx; 10190 } 10191 else 10192 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr) 10193 * LISTVIEW_GetCountPerRow(infoPtr); 10194 if(nItem < 0) nItem = 0; 10195 break; 10196 10197 case VK_NEXT: 10198 if (infoPtr->uView == LV_VIEW_DETAILS) 10199 { 10200 INT topidx = LISTVIEW_GetTopIndex(infoPtr); 10201 INT cnt = LISTVIEW_GetCountPerColumn(infoPtr); 10202 if (infoPtr->nFocusedItem == topidx + cnt - 1) 10203 nItem = infoPtr->nFocusedItem + cnt - 1; 10204 else 10205 nItem = topidx + cnt - 1; 10206 } 10207 else 10208 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr) 10209 * LISTVIEW_GetCountPerRow(infoPtr); 10210 if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1; 10211 break; 10212 } 10213 10214 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem || nVirtualKey == VK_SPACE)) 10215 LISTVIEW_KeySelection(infoPtr, nItem, nVirtualKey == VK_SPACE); 10216 10217 return 0; 10218 } 10219 10220 /*** 10221 * DESCRIPTION: 10222 * Kills the focus. 10223 * 10224 * PARAMETER(S): 10225 * [I] infoPtr : valid pointer to the listview structure 10226 * 10227 * RETURN: 10228 * Zero 10229 */ 10230 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr) 10231 { 10232 TRACE("()\n"); 10233 10234 /* drop any left over scroll amount */ 10235 infoPtr->cWheelRemainder = 0; 10236 10237 /* if we did not have the focus, there's nothing more to do */ 10238 if (!infoPtr->bFocus) return 0; 10239 10240 /* send NM_KILLFOCUS notification */ 10241 if (!notify(infoPtr, NM_KILLFOCUS)) return 0; 10242 10243 /* if we have a focus rectangle, get rid of it */ 10244 LISTVIEW_ShowFocusRect(infoPtr, FALSE); 10245 10246 /* if have a marquee selection, stop it */ 10247 if (infoPtr->bMarqueeSelect) 10248 { 10249 /* Remove the marquee rectangle and release our mouse capture */ 10250 LISTVIEW_InvalidateRect(infoPtr, &infoPtr->marqueeRect); 10251 ReleaseCapture(); 10252 10253 SetRectEmpty(&infoPtr->marqueeRect); 10254 10255 infoPtr->bMarqueeSelect = FALSE; 10256 infoPtr->bScrolling = FALSE; 10257 KillTimer(infoPtr->hwndSelf, (UINT_PTR) infoPtr); 10258 } 10259 10260 /* set window focus flag */ 10261 infoPtr->bFocus = FALSE; 10262 10263 /* invalidate the selected items before resetting focus flag */ 10264 LISTVIEW_InvalidateSelectedItems(infoPtr); 10265 10266 return 0; 10267 } 10268 10269 /*** 10270 * DESCRIPTION: 10271 * Processes double click messages (left mouse button). 10272 * 10273 * PARAMETER(S): 10274 * [I] infoPtr : valid pointer to the listview structure 10275 * [I] wKey : key flag 10276 * [I] x,y : mouse coordinate 10277 * 10278 * RETURN: 10279 * Zero 10280 */ 10281 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y) 10282 { 10283 LVHITTESTINFO htInfo; 10284 10285 TRACE("(key=%hu, X=%u, Y=%u)\n", wKey, x, y); 10286 10287 /* Cancel the item edition if any */ 10288 if (infoPtr->itemEdit.fEnabled) 10289 { 10290 KillTimer(infoPtr->hwndSelf, (UINT_PTR)&infoPtr->itemEdit); 10291 infoPtr->itemEdit.fEnabled = FALSE; 10292 } 10293 10294 /* send NM_RELEASEDCAPTURE notification */ 10295 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0; 10296 10297 htInfo.pt.x = x; 10298 htInfo.pt.y = y; 10299 10300 /* send NM_DBLCLK notification */ 10301 LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE); 10302 if (!notify_click(infoPtr, NM_DBLCLK, &htInfo)) return 0; 10303 10304 /* To send the LVN_ITEMACTIVATE, it must be on an Item */ 10305 if(htInfo.iItem != -1) notify_itemactivate(infoPtr,&htInfo); 10306 10307 return 0; 10308 } 10309 10310 static LRESULT LISTVIEW_TrackMouse(const LISTVIEW_INFO *infoPtr, POINT pt) 10311 { 10312 MSG msg; 10313 RECT r; 10314 10315 r.top = r.bottom = pt.y; 10316 r.left = r.right = pt.x; 10317 10318 InflateRect(&r, GetSystemMetrics(SM_CXDRAG), GetSystemMetrics(SM_CYDRAG)); 10319 10320 SetCapture(infoPtr->hwndSelf); 10321 10322 while (1) 10323 { 10324 if (PeekMessageW(&msg, 0, 0, 0, PM_REMOVE | PM_NOYIELD)) 10325 { 10326 if (msg.message == WM_MOUSEMOVE) 10327 { 10328 pt.x = (short)LOWORD(msg.lParam); 10329 pt.y = (short)HIWORD(msg.lParam); 10330 if (PtInRect(&r, pt)) 10331 continue; 10332 else 10333 { 10334 ReleaseCapture(); 10335 return 1; 10336 } 10337 } 10338 else if (msg.message >= WM_LBUTTONDOWN && 10339 msg.message <= WM_RBUTTONDBLCLK) 10340 { 10341 break; 10342 } 10343 10344 DispatchMessageW(&msg); 10345 } 10346 10347 if (GetCapture() != infoPtr->hwndSelf) 10348 return 0; 10349 } 10350 10351 ReleaseCapture(); 10352 return 0; 10353 } 10354 10355 10356 /*** 10357 * DESCRIPTION: 10358 * Processes mouse down messages (left mouse button). 10359 * 10360 * PARAMETERS: 10361 * infoPtr [I ] valid pointer to the listview structure 10362 * wKey [I ] key flag 10363 * x,y [I ] mouse coordinate 10364 * 10365 * RETURN: 10366 * Zero 10367 */ 10368 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y) 10369 { 10370 LVHITTESTINFO lvHitTestInfo; 10371 static BOOL bGroupSelect = TRUE; 10372 POINT pt = { x, y }; 10373 INT nItem; 10374 10375 TRACE("(key=%hu, X=%u, Y=%u)\n", wKey, x, y); 10376 10377 /* send NM_RELEASEDCAPTURE notification */ 10378 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0; 10379 10380 /* set left button down flag and record the click position */ 10381 infoPtr->bLButtonDown = TRUE; 10382 infoPtr->ptClickPos = pt; 10383 infoPtr->bDragging = FALSE; 10384 infoPtr->bMarqueeSelect = FALSE; 10385 infoPtr->bScrolling = FALSE; 10386 10387 lvHitTestInfo.pt.x = x; 10388 lvHitTestInfo.pt.y = y; 10389 10390 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE); 10391 TRACE("at %s, nItem=%d\n", wine_dbgstr_point(&pt), nItem); 10392 if ((nItem >= 0) && (nItem < infoPtr->nItemCount)) 10393 { 10394 if ((infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) && (lvHitTestInfo.flags & LVHT_ONITEMSTATEICON)) 10395 { 10396 notify_click(infoPtr, NM_CLICK, &lvHitTestInfo); 10397 toggle_checkbox_state(infoPtr, nItem); 10398 infoPtr->bLButtonDown = FALSE; 10399 return 0; 10400 } 10401 10402 if (infoPtr->dwStyle & LVS_SINGLESEL) 10403 { 10404 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED)) 10405 infoPtr->nEditLabelItem = nItem; 10406 else 10407 LISTVIEW_SetSelection(infoPtr, nItem); 10408 } 10409 else 10410 { 10411 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT)) 10412 { 10413 if (bGroupSelect) 10414 { 10415 if (!LISTVIEW_AddGroupSelection(infoPtr, nItem)) return 0; 10416 LISTVIEW_SetItemFocus(infoPtr, nItem); 10417 infoPtr->nSelectionMark = nItem; 10418 } 10419 else 10420 { 10421 LVITEMW item; 10422 10423 item.state = LVIS_SELECTED | LVIS_FOCUSED; 10424 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED; 10425 10426 LISTVIEW_SetItemState(infoPtr,nItem,&item); 10427 infoPtr->nSelectionMark = nItem; 10428 } 10429 } 10430 else if (wKey & MK_CONTROL) 10431 { 10432 LVITEMW item; 10433 10434 bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0); 10435 10436 item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED; 10437 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED; 10438 LISTVIEW_SetItemState(infoPtr, nItem, &item); 10439 infoPtr->nSelectionMark = nItem; 10440 } 10441 else if (wKey & MK_SHIFT) 10442 { 10443 LISTVIEW_SetGroupSelection(infoPtr, nItem); 10444 } 10445 else 10446 { 10447 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED)) 10448 { 10449 infoPtr->nEditLabelItem = nItem; 10450 infoPtr->nLButtonDownItem = nItem; 10451 10452 LISTVIEW_SetItemFocus(infoPtr, nItem); 10453 } 10454 else 10455 /* set selection (clears other pre-existing selections) */ 10456 LISTVIEW_SetSelection(infoPtr, nItem); 10457 } 10458 } 10459 10460 if (!infoPtr->bFocus) 10461 SetFocus(infoPtr->hwndSelf); 10462 10463 if (infoPtr->dwLvExStyle & LVS_EX_ONECLICKACTIVATE) 10464 if(lvHitTestInfo.iItem != -1) notify_itemactivate(infoPtr,&lvHitTestInfo); 10465 } 10466 else 10467 { 10468 if (!infoPtr->bFocus) 10469 SetFocus(infoPtr->hwndSelf); 10470 10471 /* remove all selections */ 10472 if (!(wKey & MK_CONTROL) && !(wKey & MK_SHIFT)) 10473 LISTVIEW_DeselectAll(infoPtr); 10474 ReleaseCapture(); 10475 } 10476 10477 return 0; 10478 } 10479 10480 /*** 10481 * DESCRIPTION: 10482 * Processes mouse up messages (left mouse button). 10483 * 10484 * PARAMETERS: 10485 * infoPtr [I ] valid pointer to the listview structure 10486 * wKey [I ] key flag 10487 * x,y [I ] mouse coordinate 10488 * 10489 * RETURN: 10490 * Zero 10491 */ 10492 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y) 10493 { 10494 LVHITTESTINFO lvHitTestInfo; 10495 10496 TRACE("(key=%hu, X=%u, Y=%u)\n", wKey, x, y); 10497 10498 if (!infoPtr->bLButtonDown) return 0; 10499 10500 lvHitTestInfo.pt.x = x; 10501 lvHitTestInfo.pt.y = y; 10502 10503 /* send NM_CLICK notification */ 10504 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE); 10505 if (!notify_click(infoPtr, NM_CLICK, &lvHitTestInfo)) return 0; 10506 10507 /* set left button flag */ 10508 infoPtr->bLButtonDown = FALSE; 10509 10510 /* set a single selection, reset others */ 10511 if(lvHitTestInfo.iItem == infoPtr->nLButtonDownItem && lvHitTestInfo.iItem != -1) 10512 LISTVIEW_SetSelection(infoPtr, infoPtr->nLButtonDownItem); 10513 infoPtr->nLButtonDownItem = -1; 10514 10515 if (infoPtr->bDragging || infoPtr->bMarqueeSelect) 10516 { 10517 /* Remove the marquee rectangle and release our mouse capture */ 10518 if (infoPtr->bMarqueeSelect) 10519 { 10520 LISTVIEW_InvalidateRect(infoPtr, &infoPtr->marqueeDrawRect); 10521 ReleaseCapture(); 10522 } 10523 10524 SetRectEmpty(&infoPtr->marqueeRect); 10525 SetRectEmpty(&infoPtr->marqueeDrawRect); 10526 10527 infoPtr->bDragging = FALSE; 10528 infoPtr->bMarqueeSelect = FALSE; 10529 infoPtr->bScrolling = FALSE; 10530 10531 KillTimer(infoPtr->hwndSelf, (UINT_PTR) infoPtr); 10532 return 0; 10533 } 10534 10535 /* if we clicked on a selected item, edit the label */ 10536 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && (lvHitTestInfo.flags & LVHT_ONITEMLABEL)) 10537 { 10538 /* we want to make sure the user doesn't want to do a double click. So we will 10539 * delay the edit. WM_LBUTTONDBLCLICK will cancel the timer 10540 */ 10541 infoPtr->itemEdit.fEnabled = TRUE; 10542 infoPtr->itemEdit.iItem = lvHitTestInfo.iItem; 10543 SetTimer(infoPtr->hwndSelf, 10544 (UINT_PTR)&infoPtr->itemEdit, 10545 GetDoubleClickTime(), 10546 LISTVIEW_DelayedEditItem); 10547 } 10548 10549 return 0; 10550 } 10551 10552 /*** 10553 * DESCRIPTION: 10554 * Destroys the listview control (called after WM_DESTROY). 10555 * 10556 * PARAMETER(S): 10557 * [I] infoPtr : valid pointer to the listview structure 10558 * 10559 * RETURN: 10560 * Zero 10561 */ 10562 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr) 10563 { 10564 INT i; 10565 10566 TRACE("()\n"); 10567 10568 /* destroy data structure */ 10569 DPA_Destroy(infoPtr->hdpaItems); 10570 DPA_Destroy(infoPtr->hdpaItemIds); 10571 DPA_Destroy(infoPtr->hdpaPosX); 10572 DPA_Destroy(infoPtr->hdpaPosY); 10573 /* columns */ 10574 for (i = 0; i < DPA_GetPtrCount(infoPtr->hdpaColumns); i++) 10575 Free(DPA_GetPtr(infoPtr->hdpaColumns, i)); 10576 DPA_Destroy(infoPtr->hdpaColumns); 10577 ranges_destroy(infoPtr->selectionRanges); 10578 10579 /* destroy image lists */ 10580 if (!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS)) 10581 { 10582 ImageList_Destroy(infoPtr->himlNormal); 10583 ImageList_Destroy(infoPtr->himlSmall); 10584 ImageList_Destroy(infoPtr->himlState); 10585 } 10586 10587 /* destroy font, bkgnd brush */ 10588 infoPtr->hFont = 0; 10589 if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont); 10590 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush); 10591 10592 SetWindowLongPtrW(infoPtr->hwndSelf, 0, 0); 10593 10594 /* free listview info pointer*/ 10595 Free(infoPtr); 10596 10597 return 0; 10598 } 10599 10600 /*** 10601 * DESCRIPTION: 10602 * Handles notifications. 10603 * 10604 * PARAMETER(S): 10605 * [I] infoPtr : valid pointer to the listview structure 10606 * [I] lpnmhdr : notification information 10607 * 10608 * RETURN: 10609 * Zero 10610 */ 10611 static LRESULT LISTVIEW_Notify(LISTVIEW_INFO *infoPtr, NMHDR *lpnmhdr) 10612 { 10613 NMHEADERW *lpnmh; 10614 10615 TRACE("(lpnmhdr=%p)\n", lpnmhdr); 10616 10617 if (!lpnmhdr || lpnmhdr->hwndFrom != infoPtr->hwndHeader) return 0; 10618 10619 /* remember: HDN_LAST < HDN_FIRST */ 10620 if (lpnmhdr->code > HDN_FIRST || lpnmhdr->code < HDN_LAST) return 0; 10621 lpnmh = (NMHEADERW *)lpnmhdr; 10622 10623 if (lpnmh->iItem < 0 || lpnmh->iItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0; 10624 10625 switch (lpnmhdr->code) 10626 { 10627 case HDN_TRACKW: 10628 case HDN_TRACKA: 10629 { 10630 COLUMN_INFO *lpColumnInfo; 10631 POINT ptOrigin; 10632 INT x; 10633 10634 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH)) 10635 break; 10636 10637 /* remove the old line (if any) */ 10638 LISTVIEW_DrawTrackLine(infoPtr); 10639 10640 /* compute & draw the new line */ 10641 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem); 10642 x = lpColumnInfo->rcHeader.left + lpnmh->pitem->cxy; 10643 LISTVIEW_GetOrigin(infoPtr, &ptOrigin); 10644 infoPtr->xTrackLine = x + ptOrigin.x; 10645 LISTVIEW_DrawTrackLine(infoPtr); 10646 return notify_forward_header(infoPtr, lpnmh); 10647 } 10648 10649 case HDN_ENDTRACKA: 10650 case HDN_ENDTRACKW: 10651 /* remove the track line (if any) */ 10652 LISTVIEW_DrawTrackLine(infoPtr); 10653 infoPtr->xTrackLine = -1; 10654 return notify_forward_header(infoPtr, lpnmh); 10655 10656 case HDN_BEGINDRAG: 10657 if ((infoPtr->dwLvExStyle & LVS_EX_HEADERDRAGDROP) == 0) return 1; 10658 return notify_forward_header(infoPtr, lpnmh); 10659 10660 case HDN_ENDDRAG: 10661 infoPtr->colRectsDirty = TRUE; 10662 LISTVIEW_InvalidateList(infoPtr); 10663 return notify_forward_header(infoPtr, lpnmh); 10664 10665 case HDN_ITEMCHANGEDW: 10666 case HDN_ITEMCHANGEDA: 10667 { 10668 COLUMN_INFO *lpColumnInfo; 10669 HDITEMW hdi; 10670 INT dx, cxy; 10671 10672 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH)) 10673 { 10674 hdi.mask = HDI_WIDTH; 10675 if (!SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, lpnmh->iItem, (LPARAM)&hdi)) return 0; 10676 cxy = hdi.cxy; 10677 } 10678 else 10679 cxy = lpnmh->pitem->cxy; 10680 10681 /* determine how much we change since the last know position */ 10682 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem); 10683 dx = cxy - (lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left); 10684 if (dx != 0) 10685 { 10686 lpColumnInfo->rcHeader.right += dx; 10687 10688 hdi.mask = HDI_ORDER; 10689 SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, lpnmh->iItem, (LPARAM)&hdi); 10690 10691 /* not the rightmost one */ 10692 if (hdi.iOrder + 1 < DPA_GetPtrCount(infoPtr->hdpaColumns)) 10693 { 10694 INT nIndex = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX, 10695 hdi.iOrder + 1, 0); 10696 LISTVIEW_ScrollColumns(infoPtr, nIndex, dx); 10697 } 10698 else 10699 { 10700 /* only needs to update the scrolls */ 10701 infoPtr->nItemWidth += dx; 10702 LISTVIEW_UpdateScroll(infoPtr); 10703 } 10704 LISTVIEW_UpdateItemSize(infoPtr); 10705 if (infoPtr->uView == LV_VIEW_DETAILS && is_redrawing(infoPtr)) 10706 { 10707 POINT ptOrigin; 10708 RECT rcCol = lpColumnInfo->rcHeader; 10709 10710 LISTVIEW_GetOrigin(infoPtr, &ptOrigin); 10711 OffsetRect(&rcCol, ptOrigin.x, 0); 10712 10713 rcCol.top = infoPtr->rcList.top; 10714 rcCol.bottom = infoPtr->rcList.bottom; 10715 10716 /* resizing left-aligned columns leaves most of the left side untouched */ 10717 if ((lpColumnInfo->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT) 10718 { 10719 INT nMaxDirty = infoPtr->nEllipsisWidth + infoPtr->ntmMaxCharWidth; 10720 if (dx > 0) 10721 nMaxDirty += dx; 10722 rcCol.left = max (rcCol.left, rcCol.right - nMaxDirty); 10723 } 10724 10725 /* when shrinking the last column clear the now unused field */ 10726 if (hdi.iOrder == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1) 10727 { 10728 RECT right; 10729 10730 rcCol.right -= dx; 10731 10732 /* deal with right from rightmost column area */ 10733 right.left = rcCol.right; 10734 right.top = rcCol.top; 10735 right.bottom = rcCol.bottom; 10736 right.right = infoPtr->rcList.right; 10737 10738 LISTVIEW_InvalidateRect(infoPtr, &right); 10739 } 10740 10741 LISTVIEW_InvalidateRect(infoPtr, &rcCol); 10742 } 10743 } 10744 break; 10745 } 10746 10747 case HDN_ITEMCLICKW: 10748 case HDN_ITEMCLICKA: 10749 { 10750 /* Handle sorting by Header Column */ 10751 NMLISTVIEW nmlv; 10752 10753 ZeroMemory(&nmlv, sizeof(NMLISTVIEW)); 10754 nmlv.iItem = -1; 10755 nmlv.iSubItem = lpnmh->iItem; 10756 notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv); 10757 return notify_forward_header(infoPtr, lpnmh); 10758 } 10759 10760 case HDN_DIVIDERDBLCLICKW: 10761 case HDN_DIVIDERDBLCLICKA: 10762 /* FIXME: for LVS_EX_HEADERINALLVIEWS and not LV_VIEW_DETAILS 10763 we should use LVSCW_AUTOSIZE_USEHEADER, helper rework or 10764 split needed for that */ 10765 LISTVIEW_SetColumnWidth(infoPtr, lpnmh->iItem, LVSCW_AUTOSIZE); 10766 return notify_forward_header(infoPtr, lpnmh); 10767 } 10768 return 0; 10769 } 10770 10771 /*** 10772 * DESCRIPTION: 10773 * Paint non-client area of control. 10774 * 10775 * PARAMETER(S): 10776 * [I] infoPtr : valid pointer to the listview structureof the sender 10777 * [I] region : update region 10778 * 10779 * RETURN: 10780 * TRUE - frame was painted 10781 * FALSE - call default window proc 10782 */ 10783 static BOOL LISTVIEW_NCPaint(const LISTVIEW_INFO *infoPtr, HRGN region) 10784 { 10785 HTHEME theme = GetWindowTheme (infoPtr->hwndSelf); 10786 HDC dc; 10787 RECT r; 10788 HRGN cliprgn; 10789 int cxEdge = GetSystemMetrics (SM_CXEDGE), 10790 cyEdge = GetSystemMetrics (SM_CYEDGE); 10791 10792 if (!theme) 10793 return DefWindowProcW (infoPtr->hwndSelf, WM_NCPAINT, (WPARAM)region, 0); 10794 10795 GetWindowRect(infoPtr->hwndSelf, &r); 10796 10797 cliprgn = CreateRectRgn (r.left + cxEdge, r.top + cyEdge, 10798 r.right - cxEdge, r.bottom - cyEdge); 10799 if (region != (HRGN)1) 10800 CombineRgn (cliprgn, cliprgn, region, RGN_AND); 10801 OffsetRect(&r, -r.left, -r.top); 10802 10803 #ifdef __REACTOS__ /* r73789 */ 10804 dc = GetWindowDC(infoPtr->hwndSelf); 10805 /* Exclude client part */ 10806 ExcludeClipRect(dc, r.left + cxEdge, r.top + cyEdge, 10807 r.right - cxEdge, r.bottom -cyEdge); 10808 #else 10809 dc = GetDCEx(infoPtr->hwndSelf, region, DCX_WINDOW|DCX_INTERSECTRGN); 10810 OffsetRect(&r, -r.left, -r.top); 10811 #endif 10812 10813 if (IsThemeBackgroundPartiallyTransparent (theme, 0, 0)) 10814 DrawThemeParentBackground(infoPtr->hwndSelf, dc, &r); 10815 DrawThemeBackground (theme, dc, 0, 0, &r, 0); 10816 ReleaseDC(infoPtr->hwndSelf, dc); 10817 10818 /* Call default proc to get the scrollbars etc. painted */ 10819 DefWindowProcW (infoPtr->hwndSelf, WM_NCPAINT, (WPARAM)cliprgn, 0); 10820 10821 return FALSE; 10822 } 10823 10824 /*** 10825 * DESCRIPTION: 10826 * Determines the type of structure to use. 10827 * 10828 * PARAMETER(S): 10829 * [I] infoPtr : valid pointer to the listview structureof the sender 10830 * [I] hwndFrom : listview window handle 10831 * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT 10832 * 10833 * RETURN: 10834 * Zero 10835 */ 10836 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand) 10837 { 10838 TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom, nCommand); 10839 10840 if (nCommand == NF_REQUERY) 10841 infoPtr->notifyFormat = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY); 10842 10843 return infoPtr->notifyFormat; 10844 } 10845 10846 /*** 10847 * DESCRIPTION: 10848 * Paints/Repaints the listview control. Internal use. 10849 * 10850 * PARAMETER(S): 10851 * [I] infoPtr : valid pointer to the listview structure 10852 * [I] hdc : device context handle 10853 * 10854 * RETURN: 10855 * Zero 10856 */ 10857 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc) 10858 { 10859 TRACE("(hdc=%p)\n", hdc); 10860 10861 if (infoPtr->bNoItemMetrics && infoPtr->nItemCount) 10862 { 10863 infoPtr->bNoItemMetrics = FALSE; 10864 LISTVIEW_UpdateItemSize(infoPtr); 10865 if (infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON) 10866 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT); 10867 LISTVIEW_UpdateScroll(infoPtr); 10868 } 10869 10870 if (infoPtr->hwndHeader) UpdateWindow(infoPtr->hwndHeader); 10871 10872 if (hdc) 10873 LISTVIEW_Refresh(infoPtr, hdc, NULL); 10874 else 10875 { 10876 PAINTSTRUCT ps; 10877 10878 hdc = BeginPaint(infoPtr->hwndSelf, &ps); 10879 if (!hdc) return 1; 10880 LISTVIEW_Refresh(infoPtr, hdc, ps.fErase ? &ps.rcPaint : NULL); 10881 EndPaint(infoPtr->hwndSelf, &ps); 10882 } 10883 10884 return 0; 10885 } 10886 10887 /*** 10888 * DESCRIPTION: 10889 * Paints/Repaints the listview control, WM_PAINT handler. 10890 * 10891 * PARAMETER(S): 10892 * [I] infoPtr : valid pointer to the listview structure 10893 * [I] hdc : device context handle 10894 * 10895 * RETURN: 10896 * Zero 10897 */ 10898 static inline LRESULT LISTVIEW_WMPaint(LISTVIEW_INFO *infoPtr, HDC hdc) 10899 { 10900 TRACE("(hdc=%p)\n", hdc); 10901 10902 if (!is_redrawing(infoPtr)) 10903 return DefWindowProcW (infoPtr->hwndSelf, WM_PAINT, (WPARAM)hdc, 0); 10904 10905 return LISTVIEW_Paint(infoPtr, hdc); 10906 } 10907 10908 /*** 10909 * DESCRIPTION: 10910 * Paints/Repaints the listview control. 10911 * 10912 * PARAMETER(S): 10913 * [I] infoPtr : valid pointer to the listview structure 10914 * [I] hdc : device context handle 10915 * [I] options : drawing options 10916 * 10917 * RETURN: 10918 * Zero 10919 */ 10920 static LRESULT LISTVIEW_PrintClient(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD options) 10921 { 10922 if ((options & PRF_CHECKVISIBLE) && !IsWindowVisible(infoPtr->hwndSelf)) 10923 return 0; 10924 10925 if (options & ~(PRF_ERASEBKGND|PRF_CLIENT)) 10926 FIXME("(hdc=%p options=0x%08x) partial stub\n", hdc, options); 10927 10928 if (options & PRF_ERASEBKGND) 10929 LISTVIEW_EraseBkgnd(infoPtr, hdc); 10930 10931 if (options & PRF_CLIENT) 10932 LISTVIEW_Paint(infoPtr, hdc); 10933 10934 return 0; 10935 } 10936 10937 10938 /*** 10939 * DESCRIPTION: 10940 * Processes double click messages (right mouse button). 10941 * 10942 * PARAMETER(S): 10943 * [I] infoPtr : valid pointer to the listview structure 10944 * [I] wKey : key flag 10945 * [I] x,y : mouse coordinate 10946 * 10947 * RETURN: 10948 * Zero 10949 */ 10950 static LRESULT LISTVIEW_RButtonDblClk(const LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y) 10951 { 10952 LVHITTESTINFO lvHitTestInfo; 10953 10954 TRACE("(key=%hu,X=%u,Y=%u)\n", wKey, x, y); 10955 10956 /* send NM_RELEASEDCAPTURE notification */ 10957 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0; 10958 10959 /* send NM_RDBLCLK notification */ 10960 lvHitTestInfo.pt.x = x; 10961 lvHitTestInfo.pt.y = y; 10962 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE); 10963 notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo); 10964 10965 return 0; 10966 } 10967 10968 /*** 10969 * DESCRIPTION: 10970 * Processes WM_RBUTTONDOWN message and corresponding drag operation. 10971 * 10972 * PARAMETER(S): 10973 * [I] infoPtr : valid pointer to the listview structure 10974 * [I] wKey : key flag 10975 * [I] x, y : mouse coordinate 10976 * 10977 * RETURN: 10978 * Zero 10979 */ 10980 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y) 10981 { 10982 LVHITTESTINFO ht; 10983 INT item; 10984 10985 TRACE("(key=%hu, x=%d, y=%d)\n", wKey, x, y); 10986 10987 /* send NM_RELEASEDCAPTURE notification */ 10988 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0; 10989 10990 /* determine the index of the selected item */ 10991 ht.pt.x = x; 10992 ht.pt.y = y; 10993 item = LISTVIEW_HitTest(infoPtr, &ht, TRUE, TRUE); 10994 10995 /* make sure the listview control window has the focus */ 10996 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf); 10997 10998 if ((item >= 0) && (item < infoPtr->nItemCount)) 10999 { 11000 LISTVIEW_SetItemFocus(infoPtr, item); 11001 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) && 11002 !LISTVIEW_GetItemState(infoPtr, item, LVIS_SELECTED)) 11003 LISTVIEW_SetSelection(infoPtr, item); 11004 } 11005 else 11006 LISTVIEW_DeselectAll(infoPtr); 11007 11008 if (LISTVIEW_TrackMouse(infoPtr, ht.pt)) 11009 { 11010 if (ht.iItem != -1) 11011 { 11012 NMLISTVIEW nmlv; 11013 11014 memset(&nmlv, 0, sizeof(nmlv)); 11015 nmlv.iItem = ht.iItem; 11016 nmlv.ptAction = ht.pt; 11017 11018 notify_listview(infoPtr, LVN_BEGINRDRAG, &nmlv); 11019 } 11020 } 11021 else 11022 { 11023 SetFocus(infoPtr->hwndSelf); 11024 11025 ht.pt.x = x; 11026 ht.pt.y = y; 11027 LISTVIEW_HitTest(infoPtr, &ht, TRUE, FALSE); 11028 11029 if (notify_click(infoPtr, NM_RCLICK, &ht)) 11030 { 11031 /* Send a WM_CONTEXTMENU message in response to the WM_RBUTTONUP */ 11032 SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU, 11033 (WPARAM)infoPtr->hwndSelf, (LPARAM)GetMessagePos()); 11034 } 11035 } 11036 11037 return 0; 11038 } 11039 11040 /*** 11041 * DESCRIPTION: 11042 * Sets the cursor. 11043 * 11044 * PARAMETER(S): 11045 * [I] infoPtr : valid pointer to the listview structure 11046 * [I] hwnd : window handle of window containing the cursor 11047 * [I] nHittest : hit-test code 11048 * [I] wMouseMsg : ideintifier of the mouse message 11049 * 11050 * RETURN: 11051 * TRUE if cursor is set 11052 * FALSE otherwise 11053 */ 11054 static BOOL LISTVIEW_SetCursor(const LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam) 11055 { 11056 LVHITTESTINFO lvHitTestInfo; 11057 11058 if (!LISTVIEW_IsHotTracking(infoPtr)) goto forward; 11059 11060 if (!infoPtr->hHotCursor) goto forward; 11061 11062 GetCursorPos(&lvHitTestInfo.pt); 11063 if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) goto forward; 11064 11065 SetCursor(infoPtr->hHotCursor); 11066 11067 return TRUE; 11068 11069 forward: 11070 11071 return DefWindowProcW(infoPtr->hwndSelf, WM_SETCURSOR, wParam, lParam); 11072 } 11073 11074 /*** 11075 * DESCRIPTION: 11076 * Sets the focus. 11077 * 11078 * PARAMETER(S): 11079 * [I] infoPtr : valid pointer to the listview structure 11080 * [I] hwndLoseFocus : handle of previously focused window 11081 * 11082 * RETURN: 11083 * Zero 11084 */ 11085 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus) 11086 { 11087 TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus); 11088 11089 /* if we have the focus already, there's nothing to do */ 11090 if (infoPtr->bFocus) return 0; 11091 11092 /* send NM_SETFOCUS notification */ 11093 if (!notify(infoPtr, NM_SETFOCUS)) return 0; 11094 11095 /* set window focus flag */ 11096 infoPtr->bFocus = TRUE; 11097 11098 /* put the focus rect back on */ 11099 LISTVIEW_ShowFocusRect(infoPtr, TRUE); 11100 11101 /* redraw all visible selected items */ 11102 LISTVIEW_InvalidateSelectedItems(infoPtr); 11103 11104 return 0; 11105 } 11106 11107 /*** 11108 * DESCRIPTION: 11109 * Sets the font. 11110 * 11111 * PARAMETER(S): 11112 * [I] infoPtr : valid pointer to the listview structure 11113 * [I] fRedraw : font handle 11114 * [I] fRedraw : redraw flag 11115 * 11116 * RETURN: 11117 * Zero 11118 */ 11119 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw) 11120 { 11121 HFONT oldFont = infoPtr->hFont; 11122 INT oldHeight = infoPtr->nItemHeight; 11123 11124 TRACE("(hfont=%p,redraw=%hu)\n", hFont, fRedraw); 11125 11126 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont; 11127 if (infoPtr->hFont == oldFont) return 0; 11128 11129 LISTVIEW_SaveTextMetrics(infoPtr); 11130 11131 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr); 11132 11133 if (infoPtr->uView == LV_VIEW_DETAILS) 11134 { 11135 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0)); 11136 LISTVIEW_UpdateSize(infoPtr); 11137 LISTVIEW_UpdateScroll(infoPtr); 11138 } 11139 else if (infoPtr->nItemHeight != oldHeight) 11140 LISTVIEW_UpdateScroll(infoPtr); 11141 11142 if (fRedraw) LISTVIEW_InvalidateList(infoPtr); 11143 11144 return 0; 11145 } 11146 11147 /*** 11148 * DESCRIPTION: 11149 * Message handling for WM_SETREDRAW. 11150 * For the Listview, it invalidates the entire window (the doc specifies otherwise) 11151 * 11152 * PARAMETER(S): 11153 * [I] infoPtr : valid pointer to the listview structure 11154 * [I] redraw: state of redraw flag 11155 * 11156 * RETURN: 11157 * Zero. 11158 */ 11159 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL redraw) 11160 { 11161 TRACE("old=%d, new=%d\n", infoPtr->redraw, redraw); 11162 11163 if (infoPtr->redraw == !!redraw) 11164 return 0; 11165 11166 if (!(infoPtr->redraw = !!redraw)) 11167 return 0; 11168 11169 if (is_autoarrange(infoPtr)) 11170 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT); 11171 LISTVIEW_UpdateScroll(infoPtr); 11172 11173 /* despite what the WM_SETREDRAW docs says, apps expect us 11174 * to invalidate the listview here... stupid! */ 11175 LISTVIEW_InvalidateList(infoPtr); 11176 11177 return 0; 11178 } 11179 11180 /*** 11181 * DESCRIPTION: 11182 * Resizes the listview control. This function processes WM_SIZE 11183 * messages. At this time, the width and height are not used. 11184 * 11185 * PARAMETER(S): 11186 * [I] infoPtr : valid pointer to the listview structure 11187 * [I] Width : new width 11188 * [I] Height : new height 11189 * 11190 * RETURN: 11191 * Zero 11192 */ 11193 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height) 11194 { 11195 RECT rcOld = infoPtr->rcList; 11196 11197 TRACE("(width=%d, height=%d)\n", Width, Height); 11198 11199 LISTVIEW_UpdateSize(infoPtr); 11200 if (EqualRect(&rcOld, &infoPtr->rcList)) return 0; 11201 11202 /* do not bother with display related stuff if we're not redrawing */ 11203 if (!is_redrawing(infoPtr)) return 0; 11204 11205 if (is_autoarrange(infoPtr)) 11206 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT); 11207 11208 LISTVIEW_UpdateScroll(infoPtr); 11209 11210 /* refresh all only for lists whose height changed significantly */ 11211 if ((infoPtr->uView == LV_VIEW_LIST) && 11212 (rcOld.bottom - rcOld.top) / infoPtr->nItemHeight != 11213 (infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight) 11214 LISTVIEW_InvalidateList(infoPtr); 11215 11216 return 0; 11217 } 11218 11219 /*** 11220 * DESCRIPTION: 11221 * Sets the size information. 11222 * 11223 * PARAMETER(S): 11224 * [I] infoPtr : valid pointer to the listview structure 11225 * 11226 * RETURN: 11227 * None 11228 */ 11229 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr) 11230 { 11231 TRACE("uView=%d, rcList(old)=%s\n", infoPtr->uView, wine_dbgstr_rect(&infoPtr->rcList)); 11232 11233 GetClientRect(infoPtr->hwndSelf, &infoPtr->rcList); 11234 11235 if (infoPtr->uView == LV_VIEW_LIST) 11236 { 11237 /* Apparently the "LIST" style is supposed to have the same 11238 * number of items in a column even if there is no scroll bar. 11239 * Since if a scroll bar already exists then the bottom is already 11240 * reduced, only reduce if the scroll bar does not currently exist. 11241 * The "2" is there to mimic the native control. I think it may be 11242 * related to either padding or edges. (GLA 7/2002) 11243 */ 11244 if (!(GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_HSCROLL)) 11245 infoPtr->rcList.bottom -= GetSystemMetrics(SM_CYHSCROLL); 11246 infoPtr->rcList.bottom = max (infoPtr->rcList.bottom - 2, 0); 11247 } 11248 11249 /* When ListView control is created invisible, header isn't created right away. */ 11250 if (infoPtr->hwndHeader) 11251 { 11252 POINT origin; 11253 WINDOWPOS wp; 11254 HDLAYOUT hl; 11255 RECT rect; 11256 11257 LISTVIEW_GetOrigin(infoPtr, &origin); 11258 11259 rect = infoPtr->rcList; 11260 rect.left += origin.x; 11261 11262 hl.prc = ▭ 11263 hl.pwpos = ℘ 11264 SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl ); 11265 TRACE(" wp.flags=0x%08x, wp=%d,%d (%dx%d)\n", wp.flags, wp.x, wp.y, wp.cx, wp.cy); 11266 11267 if (LISTVIEW_IsHeaderEnabled(infoPtr)) 11268 wp.flags |= SWP_SHOWWINDOW; 11269 else 11270 { 11271 wp.flags |= SWP_HIDEWINDOW; 11272 wp.cy = 0; 11273 } 11274 11275 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags); 11276 TRACE(" after SWP wp=%d,%d (%dx%d)\n", wp.x, wp.y, wp.cx, wp.cy); 11277 11278 infoPtr->rcList.top = max(wp.cy, 0); 11279 } 11280 /* extra padding for grid */ 11281 if (infoPtr->uView == LV_VIEW_DETAILS && infoPtr->dwLvExStyle & LVS_EX_GRIDLINES) 11282 infoPtr->rcList.top += 2; 11283 11284 TRACE(" rcList=%s\n", wine_dbgstr_rect(&infoPtr->rcList)); 11285 } 11286 11287 /*** 11288 * DESCRIPTION: 11289 * Processes WM_STYLECHANGED messages. 11290 * 11291 * PARAMETER(S): 11292 * [I] infoPtr : valid pointer to the listview structure 11293 * [I] wStyleType : window style type (normal or extended) 11294 * [I] lpss : window style information 11295 * 11296 * RETURN: 11297 * Zero 11298 */ 11299 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType, 11300 const STYLESTRUCT *lpss) 11301 { 11302 UINT uNewView, uOldView; 11303 UINT style; 11304 11305 TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n", 11306 wStyleType, lpss->styleOld, lpss->styleNew); 11307 11308 if (wStyleType != GWL_STYLE || lpss->styleNew == infoPtr->dwStyle) return 0; 11309 11310 infoPtr->dwStyle = lpss->styleNew; 11311 11312 if (((lpss->styleOld & WS_HSCROLL) != 0)&& 11313 ((lpss->styleNew & WS_HSCROLL) == 0)) 11314 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE); 11315 11316 if (((lpss->styleOld & WS_VSCROLL) != 0)&& 11317 ((lpss->styleNew & WS_VSCROLL) == 0)) 11318 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE); 11319 11320 uNewView = lpss->styleNew & LVS_TYPEMASK; 11321 uOldView = lpss->styleOld & LVS_TYPEMASK; 11322 11323 if (uNewView != uOldView) 11324 { 11325 HIMAGELIST himl; 11326 11327 /* LVM_SETVIEW doesn't change window style bits within LVS_TYPEMASK, 11328 changing style updates current view only when view bits change. */ 11329 map_style_view(infoPtr); 11330 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0); 11331 ShowWindow(infoPtr->hwndHeader, SW_HIDE); 11332 11333 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE); 11334 SetRectEmpty(&infoPtr->rcFocus); 11335 11336 himl = (uNewView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall); 11337 set_icon_size(&infoPtr->iconSize, himl, uNewView != LVS_ICON); 11338 11339 if (uNewView == LVS_REPORT) 11340 { 11341 HDLAYOUT hl; 11342 WINDOWPOS wp; 11343 11344 LISTVIEW_CreateHeader( infoPtr ); 11345 11346 hl.prc = &infoPtr->rcList; 11347 hl.pwpos = ℘ 11348 SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl ); 11349 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy, 11350 wp.flags | ((infoPtr->dwStyle & LVS_NOCOLUMNHEADER) 11351 ? SWP_HIDEWINDOW : SWP_SHOWWINDOW)); 11352 } 11353 11354 LISTVIEW_UpdateItemSize(infoPtr); 11355 } 11356 11357 if (uNewView == LVS_REPORT || infoPtr->dwLvExStyle & LVS_EX_HEADERINALLVIEWS) 11358 { 11359 if ((lpss->styleOld ^ lpss->styleNew) & LVS_NOCOLUMNHEADER) 11360 { 11361 if (lpss->styleNew & LVS_NOCOLUMNHEADER) 11362 { 11363 /* Turn off the header control */ 11364 style = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE); 11365 TRACE("Hide header control, was 0x%08x\n", style); 11366 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, style | HDS_HIDDEN); 11367 } else { 11368 /* Turn on the header control */ 11369 if ((style = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE)) & HDS_HIDDEN) 11370 { 11371 TRACE("Show header control, was 0x%08x\n", style); 11372 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, (style & ~HDS_HIDDEN) | WS_VISIBLE); 11373 } 11374 } 11375 } 11376 } 11377 11378 if ( (uNewView == LVS_ICON || uNewView == LVS_SMALLICON) && 11379 (uNewView != uOldView || ((lpss->styleNew ^ lpss->styleOld) & LVS_ALIGNMASK)) ) 11380 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT); 11381 11382 /* update the size of the client area */ 11383 LISTVIEW_UpdateSize(infoPtr); 11384 11385 /* add scrollbars if needed */ 11386 LISTVIEW_UpdateScroll(infoPtr); 11387 11388 /* invalidate client area + erase background */ 11389 LISTVIEW_InvalidateList(infoPtr); 11390 11391 return 0; 11392 } 11393 11394 /*** 11395 * DESCRIPTION: 11396 * Processes WM_STYLECHANGING messages. 11397 * 11398 * PARAMETER(S): 11399 * [I] wStyleType : window style type (normal or extended) 11400 * [I0] lpss : window style information 11401 * 11402 * RETURN: 11403 * Zero 11404 */ 11405 static INT LISTVIEW_StyleChanging(WPARAM wStyleType, 11406 STYLESTRUCT *lpss) 11407 { 11408 TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n", 11409 wStyleType, lpss->styleOld, lpss->styleNew); 11410 11411 /* don't forward LVS_OWNERDATA only if not already set to */ 11412 if ((lpss->styleNew ^ lpss->styleOld) & LVS_OWNERDATA) 11413 { 11414 if (lpss->styleOld & LVS_OWNERDATA) 11415 lpss->styleNew |= LVS_OWNERDATA; 11416 else 11417 lpss->styleNew &= ~LVS_OWNERDATA; 11418 } 11419 11420 return 0; 11421 } 11422 11423 /*** 11424 * DESCRIPTION: 11425 * Processes WM_SHOWWINDOW messages. 11426 * 11427 * PARAMETER(S): 11428 * [I] infoPtr : valid pointer to the listview structure 11429 * [I] bShown : window is being shown (FALSE when hidden) 11430 * [I] iStatus : window show status 11431 * 11432 * RETURN: 11433 * Zero 11434 */ 11435 static LRESULT LISTVIEW_ShowWindow(LISTVIEW_INFO *infoPtr, WPARAM bShown, LPARAM iStatus) 11436 { 11437 /* header delayed creation */ 11438 if ((infoPtr->uView == LV_VIEW_DETAILS) && bShown) 11439 { 11440 LISTVIEW_CreateHeader(infoPtr); 11441 11442 if (!(LVS_NOCOLUMNHEADER & infoPtr->dwStyle)) 11443 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL); 11444 } 11445 11446 return DefWindowProcW(infoPtr->hwndSelf, WM_SHOWWINDOW, bShown, iStatus); 11447 } 11448 11449 /*** 11450 * DESCRIPTION: 11451 * Processes CCM_GETVERSION messages. 11452 * 11453 * PARAMETER(S): 11454 * [I] infoPtr : valid pointer to the listview structure 11455 * 11456 * RETURN: 11457 * Current version 11458 */ 11459 static inline LRESULT LISTVIEW_GetVersion(const LISTVIEW_INFO *infoPtr) 11460 { 11461 return infoPtr->iVersion; 11462 } 11463 11464 /*** 11465 * DESCRIPTION: 11466 * Processes CCM_SETVERSION messages. 11467 * 11468 * PARAMETER(S): 11469 * [I] infoPtr : valid pointer to the listview structure 11470 * [I] iVersion : version to be set 11471 * 11472 * RETURN: 11473 * -1 when requested version is greater than DLL version; 11474 * previous version otherwise 11475 */ 11476 static LRESULT LISTVIEW_SetVersion(LISTVIEW_INFO *infoPtr, DWORD iVersion) 11477 { 11478 INT iOldVersion = infoPtr->iVersion; 11479 11480 if (iVersion > COMCTL32_VERSION) 11481 return -1; 11482 11483 infoPtr->iVersion = iVersion; 11484 11485 TRACE("new version %d\n", iVersion); 11486 11487 return iOldVersion; 11488 } 11489 11490 /*** 11491 * DESCRIPTION: 11492 * Window procedure of the listview control. 11493 * 11494 */ 11495 static LRESULT WINAPI 11496 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 11497 { 11498 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0); 11499 11500 TRACE("(hwnd=%p uMsg=%x wParam=%lx lParam=%lx)\n", hwnd, uMsg, wParam, lParam); 11501 11502 if (!infoPtr && (uMsg != WM_NCCREATE)) 11503 return DefWindowProcW(hwnd, uMsg, wParam, lParam); 11504 11505 switch (uMsg) 11506 { 11507 case LVM_APPROXIMATEVIEWRECT: 11508 return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam, 11509 LOWORD(lParam), HIWORD(lParam)); 11510 case LVM_ARRANGE: 11511 return LISTVIEW_Arrange(infoPtr, (INT)wParam); 11512 11513 case LVM_CANCELEDITLABEL: 11514 return LISTVIEW_CancelEditLabel(infoPtr); 11515 11516 case LVM_CREATEDRAGIMAGE: 11517 return (LRESULT)LISTVIEW_CreateDragImage(infoPtr, (INT)wParam, (LPPOINT)lParam); 11518 11519 case LVM_DELETEALLITEMS: 11520 return LISTVIEW_DeleteAllItems(infoPtr, FALSE); 11521 11522 case LVM_DELETECOLUMN: 11523 return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam); 11524 11525 case LVM_DELETEITEM: 11526 return LISTVIEW_DeleteItem(infoPtr, (INT)wParam); 11527 11528 case LVM_EDITLABELA: 11529 case LVM_EDITLABELW: 11530 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, 11531 uMsg == LVM_EDITLABELW); 11532 /* case LVM_ENABLEGROUPVIEW: */ 11533 11534 case LVM_ENSUREVISIBLE: 11535 return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam); 11536 11537 case LVM_FINDITEMW: 11538 return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam); 11539 11540 case LVM_FINDITEMA: 11541 return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam); 11542 11543 case LVM_GETBKCOLOR: 11544 return infoPtr->clrBk; 11545 11546 /* case LVM_GETBKIMAGE: */ 11547 11548 case LVM_GETCALLBACKMASK: 11549 return infoPtr->uCallbackMask; 11550 11551 case LVM_GETCOLUMNA: 11552 case LVM_GETCOLUMNW: 11553 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, 11554 uMsg == LVM_GETCOLUMNW); 11555 11556 case LVM_GETCOLUMNORDERARRAY: 11557 return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam); 11558 11559 case LVM_GETCOLUMNWIDTH: 11560 return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam); 11561 11562 case LVM_GETCOUNTPERPAGE: 11563 return LISTVIEW_GetCountPerPage(infoPtr); 11564 11565 case LVM_GETEDITCONTROL: 11566 return (LRESULT)infoPtr->hwndEdit; 11567 11568 case LVM_GETEXTENDEDLISTVIEWSTYLE: 11569 return infoPtr->dwLvExStyle; 11570 11571 /* case LVM_GETGROUPINFO: */ 11572 11573 /* case LVM_GETGROUPMETRICS: */ 11574 11575 case LVM_GETHEADER: 11576 return (LRESULT)infoPtr->hwndHeader; 11577 11578 case LVM_GETHOTCURSOR: 11579 return (LRESULT)infoPtr->hHotCursor; 11580 11581 case LVM_GETHOTITEM: 11582 return infoPtr->nHotItem; 11583 11584 case LVM_GETHOVERTIME: 11585 return infoPtr->dwHoverTime; 11586 11587 case LVM_GETIMAGELIST: 11588 return (LRESULT)LISTVIEW_GetImageList(infoPtr, (INT)wParam); 11589 11590 /* case LVM_GETINSERTMARK: */ 11591 11592 /* case LVM_GETINSERTMARKCOLOR: */ 11593 11594 /* case LVM_GETINSERTMARKRECT: */ 11595 11596 case LVM_GETISEARCHSTRINGA: 11597 case LVM_GETISEARCHSTRINGW: 11598 FIXME("LVM_GETISEARCHSTRING: unimplemented\n"); 11599 return FALSE; 11600 11601 case LVM_GETITEMA: 11602 case LVM_GETITEMW: 11603 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, uMsg == LVM_GETITEMW); 11604 11605 case LVM_GETITEMCOUNT: 11606 return infoPtr->nItemCount; 11607 11608 case LVM_GETITEMPOSITION: 11609 return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam); 11610 11611 case LVM_GETITEMRECT: 11612 return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam); 11613 11614 case LVM_GETITEMSPACING: 11615 return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam); 11616 11617 case LVM_GETITEMSTATE: 11618 return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam); 11619 11620 case LVM_GETITEMTEXTA: 11621 case LVM_GETITEMTEXTW: 11622 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, 11623 uMsg == LVM_GETITEMTEXTW); 11624 11625 case LVM_GETNEXTITEM: 11626 return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam)); 11627 11628 case LVM_GETNUMBEROFWORKAREAS: 11629 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n"); 11630 return 1; 11631 11632 case LVM_GETORIGIN: 11633 if (!lParam) return FALSE; 11634 if (infoPtr->uView == LV_VIEW_DETAILS || 11635 infoPtr->uView == LV_VIEW_LIST) return FALSE; 11636 LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam); 11637 return TRUE; 11638 11639 /* case LVM_GETOUTLINECOLOR: */ 11640 11641 /* case LVM_GETSELECTEDCOLUMN: */ 11642 11643 case LVM_GETSELECTEDCOUNT: 11644 return LISTVIEW_GetSelectedCount(infoPtr); 11645 11646 case LVM_GETSELECTIONMARK: 11647 return infoPtr->nSelectionMark; 11648 11649 case LVM_GETSTRINGWIDTHA: 11650 case LVM_GETSTRINGWIDTHW: 11651 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, 11652 uMsg == LVM_GETSTRINGWIDTHW); 11653 11654 case LVM_GETSUBITEMRECT: 11655 return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam); 11656 11657 case LVM_GETTEXTBKCOLOR: 11658 return infoPtr->clrTextBk; 11659 11660 case LVM_GETTEXTCOLOR: 11661 return infoPtr->clrText; 11662 11663 /* case LVM_GETTILEINFO: */ 11664 11665 /* case LVM_GETTILEVIEWINFO: */ 11666 11667 case LVM_GETTOOLTIPS: 11668 if( !infoPtr->hwndToolTip ) 11669 infoPtr->hwndToolTip = COMCTL32_CreateToolTip( hwnd ); 11670 return (LRESULT)infoPtr->hwndToolTip; 11671 11672 case LVM_GETTOPINDEX: 11673 return LISTVIEW_GetTopIndex(infoPtr); 11674 11675 case LVM_GETUNICODEFORMAT: 11676 return (infoPtr->notifyFormat == NFR_UNICODE); 11677 11678 case LVM_GETVIEW: 11679 return infoPtr->uView; 11680 11681 case LVM_GETVIEWRECT: 11682 return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam); 11683 11684 case LVM_GETWORKAREAS: 11685 FIXME("LVM_GETWORKAREAS: unimplemented\n"); 11686 return FALSE; 11687 11688 /* case LVM_HASGROUP: */ 11689 11690 case LVM_HITTEST: 11691 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, TRUE); 11692 11693 case LVM_INSERTCOLUMNA: 11694 case LVM_INSERTCOLUMNW: 11695 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, 11696 uMsg == LVM_INSERTCOLUMNW); 11697 11698 /* case LVM_INSERTGROUP: */ 11699 11700 /* case LVM_INSERTGROUPSORTED: */ 11701 11702 case LVM_INSERTITEMA: 11703 case LVM_INSERTITEMW: 11704 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, uMsg == LVM_INSERTITEMW); 11705 11706 /* case LVM_INSERTMARKHITTEST: */ 11707 11708 /* case LVM_ISGROUPVIEWENABLED: */ 11709 11710 case LVM_ISITEMVISIBLE: 11711 return LISTVIEW_IsItemVisible(infoPtr, (INT)wParam); 11712 11713 case LVM_MAPIDTOINDEX: 11714 return LISTVIEW_MapIdToIndex(infoPtr, (UINT)wParam); 11715 11716 case LVM_MAPINDEXTOID: 11717 return LISTVIEW_MapIndexToId(infoPtr, (INT)wParam); 11718 11719 /* case LVM_MOVEGROUP: */ 11720 11721 /* case LVM_MOVEITEMTOGROUP: */ 11722 11723 case LVM_REDRAWITEMS: 11724 return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam); 11725 11726 /* case LVM_REMOVEALLGROUPS: */ 11727 11728 /* case LVM_REMOVEGROUP: */ 11729 11730 case LVM_SCROLL: 11731 return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam); 11732 11733 case LVM_SETBKCOLOR: 11734 return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam); 11735 11736 /* case LVM_SETBKIMAGE: */ 11737 11738 case LVM_SETCALLBACKMASK: 11739 infoPtr->uCallbackMask = (UINT)wParam; 11740 return TRUE; 11741 11742 case LVM_SETCOLUMNA: 11743 case LVM_SETCOLUMNW: 11744 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, 11745 uMsg == LVM_SETCOLUMNW); 11746 11747 case LVM_SETCOLUMNORDERARRAY: 11748 return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam); 11749 11750 case LVM_SETCOLUMNWIDTH: 11751 return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, (short)LOWORD(lParam)); 11752 11753 case LVM_SETEXTENDEDLISTVIEWSTYLE: 11754 return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam); 11755 11756 /* case LVM_SETGROUPINFO: */ 11757 11758 /* case LVM_SETGROUPMETRICS: */ 11759 11760 case LVM_SETHOTCURSOR: 11761 return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam); 11762 11763 case LVM_SETHOTITEM: 11764 return LISTVIEW_SetHotItem(infoPtr, (INT)wParam); 11765 11766 case LVM_SETHOVERTIME: 11767 return LISTVIEW_SetHoverTime(infoPtr, (DWORD)lParam); 11768 11769 case LVM_SETICONSPACING: 11770 if(lParam == -1) 11771 return LISTVIEW_SetIconSpacing(infoPtr, -1, -1); 11772 return LISTVIEW_SetIconSpacing(infoPtr, LOWORD(lParam), HIWORD(lParam)); 11773 11774 case LVM_SETIMAGELIST: 11775 return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam); 11776 11777 /* case LVM_SETINFOTIP: */ 11778 11779 /* case LVM_SETINSERTMARK: */ 11780 11781 /* case LVM_SETINSERTMARKCOLOR: */ 11782 11783 case LVM_SETITEMA: 11784 case LVM_SETITEMW: 11785 { 11786 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE; 11787 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, (uMsg == LVM_SETITEMW)); 11788 } 11789 11790 case LVM_SETITEMCOUNT: 11791 return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam); 11792 11793 case LVM_SETITEMPOSITION: 11794 { 11795 POINT pt; 11796 pt.x = (short)LOWORD(lParam); 11797 pt.y = (short)HIWORD(lParam); 11798 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, &pt); 11799 } 11800 11801 case LVM_SETITEMPOSITION32: 11802 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, (POINT*)lParam); 11803 11804 case LVM_SETITEMSTATE: 11805 return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam); 11806 11807 case LVM_SETITEMTEXTA: 11808 case LVM_SETITEMTEXTW: 11809 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, 11810 uMsg == LVM_SETITEMTEXTW); 11811 11812 /* case LVM_SETOUTLINECOLOR: */ 11813 11814 /* case LVM_SETSELECTEDCOLUMN: */ 11815 11816 case LVM_SETSELECTIONMARK: 11817 return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam); 11818 11819 case LVM_SETTEXTBKCOLOR: 11820 return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam); 11821 11822 case LVM_SETTEXTCOLOR: 11823 return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam); 11824 11825 /* case LVM_SETTILEINFO: */ 11826 11827 /* case LVM_SETTILEVIEWINFO: */ 11828 11829 /* case LVM_SETTILEWIDTH: */ 11830 11831 case LVM_SETTOOLTIPS: 11832 return (LRESULT)LISTVIEW_SetToolTips(infoPtr, (HWND)lParam); 11833 11834 case LVM_SETUNICODEFORMAT: 11835 return LISTVIEW_SetUnicodeFormat(infoPtr, wParam); 11836 11837 case LVM_SETVIEW: 11838 return LISTVIEW_SetView(infoPtr, wParam); 11839 11840 /* case LVM_SETWORKAREAS: */ 11841 11842 /* case LVM_SORTGROUPS: */ 11843 11844 case LVM_SORTITEMS: 11845 case LVM_SORTITEMSEX: 11846 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, wParam, 11847 uMsg == LVM_SORTITEMSEX); 11848 case LVM_SUBITEMHITTEST: 11849 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE); 11850 11851 case LVM_UPDATE: 11852 return LISTVIEW_Update(infoPtr, (INT)wParam); 11853 11854 case CCM_GETVERSION: 11855 return LISTVIEW_GetVersion(infoPtr); 11856 11857 case CCM_SETVERSION: 11858 return LISTVIEW_SetVersion(infoPtr, wParam); 11859 11860 case WM_CHAR: 11861 return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam ); 11862 11863 case WM_COMMAND: 11864 return LISTVIEW_Command(infoPtr, wParam, lParam); 11865 11866 case WM_NCCREATE: 11867 return LISTVIEW_NCCreate(hwnd, wParam, (LPCREATESTRUCTW)lParam); 11868 11869 case WM_CREATE: 11870 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam); 11871 11872 case WM_DESTROY: 11873 return LISTVIEW_Destroy(infoPtr); 11874 11875 case WM_ENABLE: 11876 return LISTVIEW_Enable(infoPtr); 11877 11878 case WM_ERASEBKGND: 11879 return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam); 11880 11881 case WM_GETDLGCODE: 11882 return DLGC_WANTCHARS | DLGC_WANTARROWS; 11883 11884 case WM_GETFONT: 11885 return (LRESULT)infoPtr->hFont; 11886 11887 case WM_HSCROLL: 11888 return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0); 11889 11890 case WM_KEYDOWN: 11891 return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam); 11892 11893 case WM_KILLFOCUS: 11894 return LISTVIEW_KillFocus(infoPtr); 11895 11896 case WM_LBUTTONDBLCLK: 11897 return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam)); 11898 11899 case WM_LBUTTONDOWN: 11900 return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam)); 11901 11902 case WM_LBUTTONUP: 11903 return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam)); 11904 11905 case WM_MOUSEMOVE: 11906 return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam)); 11907 11908 case WM_MOUSEHOVER: 11909 return LISTVIEW_MouseHover(infoPtr, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam)); 11910 11911 case WM_NCDESTROY: 11912 return LISTVIEW_NCDestroy(infoPtr); 11913 11914 case WM_NCPAINT: 11915 return LISTVIEW_NCPaint(infoPtr, (HRGN)wParam); 11916 11917 case WM_NOTIFY: 11918 return LISTVIEW_Notify(infoPtr, (LPNMHDR)lParam); 11919 11920 case WM_NOTIFYFORMAT: 11921 return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam); 11922 11923 case WM_PRINTCLIENT: 11924 return LISTVIEW_PrintClient(infoPtr, (HDC)wParam, (DWORD)lParam); 11925 11926 case WM_PAINT: 11927 return LISTVIEW_WMPaint(infoPtr, (HDC)wParam); 11928 11929 case WM_RBUTTONDBLCLK: 11930 return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam)); 11931 11932 case WM_RBUTTONDOWN: 11933 return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam)); 11934 11935 case WM_SETCURSOR: 11936 return LISTVIEW_SetCursor(infoPtr, wParam, lParam); 11937 11938 case WM_SETFOCUS: 11939 return LISTVIEW_SetFocus(infoPtr, (HWND)wParam); 11940 11941 case WM_SETFONT: 11942 return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam); 11943 11944 case WM_SETREDRAW: 11945 return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam); 11946 11947 case WM_SHOWWINDOW: 11948 return LISTVIEW_ShowWindow(infoPtr, wParam, lParam); 11949 11950 case WM_STYLECHANGED: 11951 return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam); 11952 11953 case WM_STYLECHANGING: 11954 return LISTVIEW_StyleChanging(wParam, (LPSTYLESTRUCT)lParam); 11955 11956 case WM_SYSCOLORCHANGE: 11957 COMCTL32_RefreshSysColors(); 11958 #ifdef __REACTOS__ 11959 if (infoPtr->bDefaultBkColor) 11960 { 11961 LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow); 11962 infoPtr->bDefaultBkColor = TRUE; 11963 LISTVIEW_InvalidateList(infoPtr); 11964 } 11965 #endif 11966 return 0; 11967 11968 /* case WM_TIMER: */ 11969 case WM_THEMECHANGED: 11970 return LISTVIEW_ThemeChanged(infoPtr); 11971 11972 case WM_VSCROLL: 11973 return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0); 11974 11975 case WM_MOUSEWHEEL: 11976 if (wParam & (MK_SHIFT | MK_CONTROL)) 11977 return DefWindowProcW(hwnd, uMsg, wParam, lParam); 11978 return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam)); 11979 11980 case WM_WINDOWPOSCHANGED: 11981 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE)) 11982 { 11983 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE | 11984 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE); 11985 11986 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (infoPtr->uView == LV_VIEW_DETAILS)) 11987 { 11988 if (notify_measureitem(infoPtr)) LISTVIEW_InvalidateList(infoPtr); 11989 } 11990 LISTVIEW_Size(infoPtr, ((WINDOWPOS *)lParam)->cx, ((WINDOWPOS *)lParam)->cy); 11991 } 11992 return DefWindowProcW(hwnd, uMsg, wParam, lParam); 11993 11994 /* case WM_WININICHANGE: */ 11995 11996 default: 11997 if ((uMsg >= WM_USER) && (uMsg < WM_APP) && !COMCTL32_IsReflectedMessage(uMsg)) 11998 ERR("unknown msg %04x wp=%08lx lp=%08lx\n", uMsg, wParam, lParam); 11999 12000 return DefWindowProcW(hwnd, uMsg, wParam, lParam); 12001 } 12002 12003 } 12004 12005 /*** 12006 * DESCRIPTION: 12007 * Registers the window class. 12008 * 12009 * PARAMETER(S): 12010 * None 12011 * 12012 * RETURN: 12013 * None 12014 */ 12015 void LISTVIEW_Register(void) 12016 { 12017 WNDCLASSW wndClass; 12018 12019 ZeroMemory(&wndClass, sizeof(WNDCLASSW)); 12020 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS; 12021 wndClass.lpfnWndProc = LISTVIEW_WindowProc; 12022 wndClass.cbClsExtra = 0; 12023 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *); 12024 wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW); 12025 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); 12026 wndClass.lpszClassName = WC_LISTVIEWW; 12027 RegisterClassW(&wndClass); 12028 } 12029 12030 /*** 12031 * DESCRIPTION: 12032 * Unregisters the window class. 12033 * 12034 * PARAMETER(S): 12035 * None 12036 * 12037 * RETURN: 12038 * None 12039 */ 12040 void LISTVIEW_Unregister(void) 12041 { 12042 UnregisterClassW(WC_LISTVIEWW, NULL); 12043 } 12044 12045 /*** 12046 * DESCRIPTION: 12047 * Handle any WM_COMMAND messages 12048 * 12049 * PARAMETER(S): 12050 * [I] infoPtr : valid pointer to the listview structure 12051 * [I] wParam : the first message parameter 12052 * [I] lParam : the second message parameter 12053 * 12054 * RETURN: 12055 * Zero. 12056 */ 12057 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam) 12058 { 12059 12060 TRACE("(%p %x %x %lx)\n", infoPtr, HIWORD(wParam), LOWORD(wParam), lParam); 12061 12062 if (!infoPtr->hwndEdit) return 0; 12063 12064 switch (HIWORD(wParam)) 12065 { 12066 case EN_UPDATE: 12067 { 12068 /* 12069 * Adjust the edit window size 12070 */ 12071 WCHAR buffer[1024]; 12072 HDC hdc = GetDC(infoPtr->hwndEdit); 12073 HFONT hFont, hOldFont = 0; 12074 RECT rect; 12075 SIZE sz; 12076 12077 if (!infoPtr->hwndEdit || !hdc) return 0; 12078 GetWindowTextW(infoPtr->hwndEdit, buffer, ARRAY_SIZE(buffer)); 12079 GetWindowRect(infoPtr->hwndEdit, &rect); 12080 12081 /* Select font to get the right dimension of the string */ 12082 hFont = (HFONT)SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0); 12083 if (hFont) 12084 { 12085 hOldFont = SelectObject(hdc, hFont); 12086 } 12087 12088 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz)) 12089 { 12090 TEXTMETRICW textMetric; 12091 12092 /* Add Extra spacing for the next character */ 12093 GetTextMetricsW(hdc, &textMetric); 12094 sz.cx += (textMetric.tmMaxCharWidth * 2); 12095 12096 SetWindowPos(infoPtr->hwndEdit, NULL, 0, 0, sz.cx, 12097 rect.bottom - rect.top, SWP_DRAWFRAME | SWP_NOMOVE | SWP_NOZORDER); 12098 } 12099 if (hFont) 12100 SelectObject(hdc, hOldFont); 12101 12102 ReleaseDC(infoPtr->hwndEdit, hdc); 12103 12104 break; 12105 } 12106 case EN_KILLFOCUS: 12107 { 12108 if (infoPtr->notify_mask & NOTIFY_MASK_END_LABEL_EDIT) 12109 LISTVIEW_CancelEditLabel(infoPtr); 12110 break; 12111 } 12112 12113 default: 12114 return SendMessageW (infoPtr->hwndNotify, WM_COMMAND, wParam, lParam); 12115 } 12116 12117 return 0; 12118 } 12119