Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygenlistview.c
Go to the documentation of this file.
00001 /* 00002 * Listview control 00003 * 00004 * Copyright 1998, 1999 Eric Kohl 00005 * Copyright 1999 Luc Tourangeau 00006 * Copyright 2000 Jason Mawdsley 00007 * Copyright 2001 CodeWeavers Inc. 00008 * Copyright 2002 Dimitrie O. Paun 00009 * Copyright 2009-2011 Nikolay Sivov 00010 * Copyright 2009 Owen Rudge for CodeWeavers 00011 * 00012 * This library is free software; you can redistribute it and/or 00013 * modify it under the terms of the GNU Lesser General Public 00014 * License as published by the Free Software Foundation; either 00015 * version 2.1 of the License, or (at your option) any later version. 00016 * 00017 * This library is distributed in the hope that it will be useful, 00018 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00019 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00020 * Lesser General Public License for more details. 00021 * 00022 * You should have received a copy of the GNU Lesser General Public 00023 * License along with this library; if not, write to the Free Software 00024 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 00025 * 00026 * NOTES 00027 * 00028 * This code was audited for completeness against the documented features 00029 * of Comctl32.dll version 6.0 on May. 20, 2005, by James Hawkins. 00030 * 00031 * Unless otherwise noted, we believe this code to be complete, as per 00032 * the specification mentioned above. 00033 * If you discover missing features, or bugs, please note them below. 00034 * 00035 * TODO: 00036 * 00037 * Default Message Processing 00038 * -- WM_CREATE: create the icon and small icon image lists at this point only if 00039 * the LVS_SHAREIMAGELISTS style is not specified. 00040 * -- WM_WINDOWPOSCHANGED: arrange the list items if the current view is icon 00041 * or small icon and the LVS_AUTOARRANGE style is specified. 00042 * -- WM_TIMER 00043 * -- WM_WININICHANGE 00044 * 00045 * Features 00046 * -- Hot item handling, mouse hovering 00047 * -- Workareas support 00048 * -- Tilemode support 00049 * -- Groups support 00050 * 00051 * Bugs 00052 * -- Expand large item in ICON mode when the cursor is flying over the icon or text. 00053 * -- Support CustomDraw options for _WIN32_IE >= 0x560 (see NMLVCUSTOMDRAW docs). 00054 * -- LVA_SNAPTOGRID not implemented 00055 * -- LISTVIEW_ApproximateViewRect partially implemented 00056 * -- LISTVIEW_SetColumnWidth ignores header images & bitmap 00057 * -- LISTVIEW_SetIconSpacing is incomplete 00058 * -- LISTVIEW_StyleChanged doesn't handle some changes too well 00059 * 00060 * Speedups 00061 * -- LISTVIEW_GetNextItem needs to be rewritten. It is currently 00062 * linear in the number of items in the list, and this is 00063 * unacceptable for large lists. 00064 * -- if list is sorted by item text LISTVIEW_InsertItemT could use 00065 * binary search to calculate item index (e.g. DPA_Search()). 00066 * This requires sorted state to be reliably tracked in item modifiers. 00067 * -- we should keep an ordered array of coordinates in iconic mode 00068 * this would allow to frame items (iterator_frameditems), 00069 * and find nearest item (LVFI_NEARESTXY) a lot more efficiently 00070 * 00071 * Flags 00072 * -- LVIF_COLUMNS 00073 * -- LVIF_GROUPID 00074 * 00075 * States 00076 * -- LVIS_ACTIVATING (not currently supported by comctl32.dll version 6.0) 00077 * -- LVIS_DROPHILITED 00078 * -- LVIS_OVERLAYMASK 00079 * 00080 * Styles 00081 * -- LVS_NOLABELWRAP 00082 * -- LVS_NOSCROLL (see Q137520) 00083 * -- LVS_ALIGNTOP 00084 * 00085 * Extended Styles 00086 * -- LVS_EX_BORDERSELECT 00087 * -- LVS_EX_FLATSB 00088 * -- LVS_EX_INFOTIP 00089 * -- LVS_EX_LABELTIP 00090 * -- LVS_EX_MULTIWORKAREAS 00091 * -- LVS_EX_REGIONAL 00092 * -- LVS_EX_SIMPLESELECT 00093 * -- LVS_EX_TWOCLICKACTIVATE 00094 * -- LVS_EX_UNDERLINECOLD 00095 * -- LVS_EX_UNDERLINEHOT 00096 * 00097 * Notifications: 00098 * -- LVN_BEGINSCROLL, LVN_ENDSCROLL 00099 * -- LVN_GETINFOTIP 00100 * -- LVN_HOTTRACK 00101 * -- LVN_SETDISPINFO 00102 * -- LVN_BEGINRDRAG 00103 * 00104 * Messages: 00105 * -- LVM_ENABLEGROUPVIEW 00106 * -- LVM_GETBKIMAGE, LVM_SETBKIMAGE 00107 * -- LVM_GETGROUPINFO, LVM_SETGROUPINFO 00108 * -- LVM_GETGROUPMETRICS, LVM_SETGROUPMETRICS 00109 * -- LVM_GETINSERTMARK, LVM_SETINSERTMARK 00110 * -- LVM_GETINSERTMARKCOLOR, LVM_SETINSERTMARKCOLOR 00111 * -- LVM_GETINSERTMARKRECT 00112 * -- LVM_GETNUMBEROFWORKAREAS 00113 * -- LVM_GETOUTLINECOLOR, LVM_SETOUTLINECOLOR 00114 * -- LVM_GETSELECTEDCOLUMN, LVM_SETSELECTEDCOLUMN 00115 * -- LVM_GETISEARCHSTRINGW, LVM_GETISEARCHSTRINGA 00116 * -- LVM_GETTILEINFO, LVM_SETTILEINFO 00117 * -- LVM_GETTILEVIEWINFO, LVM_SETTILEVIEWINFO 00118 * -- LVM_GETWORKAREAS, LVM_SETWORKAREAS 00119 * -- LVM_HASGROUP, LVM_INSERTGROUP, LVM_REMOVEGROUP, LVM_REMOVEALLGROUPS 00120 * -- LVM_INSERTGROUPSORTED 00121 * -- LVM_INSERTMARKHITTEST 00122 * -- LVM_ISGROUPVIEWENABLED 00123 * -- LVM_MOVEGROUP 00124 * -- LVM_MOVEITEMTOGROUP 00125 * -- LVM_SETINFOTIP 00126 * -- LVM_SETTILEWIDTH 00127 * -- LVM_SORTGROUPS 00128 * 00129 * Macros: 00130 * -- ListView_GetHoverTime, ListView_SetHoverTime 00131 * -- ListView_GetISearchString 00132 * -- ListView_GetNumberOfWorkAreas 00133 * -- ListView_GetWorkAreas, ListView_SetWorkAreas 00134 * 00135 * Functions: 00136 * -- LVGroupComparE 00137 * 00138 * Known differences in message stream from native control (not known if 00139 * these differences cause problems): 00140 * LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases. 00141 * LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED. 00142 * WM_CREATE does not issue WM_QUERYUISTATE and associated registry 00143 * processing for "USEDOUBLECLICKTIME". 00144 */ 00145 00146 #include "config.h" 00147 #include "wine/port.h" 00148 00149 #include <assert.h> 00150 #include <ctype.h> 00151 #include <string.h> 00152 #include <stdlib.h> 00153 #include <stdarg.h> 00154 #include <stdio.h> 00155 00156 #include "windef.h" 00157 #include "winbase.h" 00158 #include "winnt.h" 00159 #include "wingdi.h" 00160 #include "winuser.h" 00161 #include "winnls.h" 00162 #include "commctrl.h" 00163 #include "comctl32.h" 00164 #include "uxtheme.h" 00165 00166 #include "wine/debug.h" 00167 #include "wine/unicode.h" 00168 00169 WINE_DEFAULT_DEBUG_CHANNEL(listview); 00170 00171 typedef struct tagCOLUMN_INFO 00172 { 00173 RECT rcHeader; /* tracks the header's rectangle */ 00174 INT fmt; /* same as LVCOLUMN.fmt */ 00175 INT cxMin; 00176 } COLUMN_INFO; 00177 00178 typedef struct tagITEMHDR 00179 { 00180 LPWSTR pszText; 00181 INT iImage; 00182 } ITEMHDR, *LPITEMHDR; 00183 00184 typedef struct tagSUBITEM_INFO 00185 { 00186 ITEMHDR hdr; 00187 INT iSubItem; 00188 } SUBITEM_INFO; 00189 00190 typedef struct tagITEM_ID ITEM_ID; 00191 00192 typedef struct tagITEM_INFO 00193 { 00194 ITEMHDR hdr; 00195 UINT state; 00196 LPARAM lParam; 00197 INT iIndent; 00198 ITEM_ID *id; 00199 } ITEM_INFO; 00200 00201 struct tagITEM_ID 00202 { 00203 UINT id; /* item id */ 00204 HDPA item; /* link to item data */ 00205 }; 00206 00207 typedef struct tagRANGE 00208 { 00209 INT lower; 00210 INT upper; 00211 } RANGE; 00212 00213 typedef struct tagRANGES 00214 { 00215 HDPA hdpa; 00216 } *RANGES; 00217 00218 typedef struct tagITERATOR 00219 { 00220 INT nItem; 00221 INT nSpecial; 00222 RANGE range; 00223 RANGES ranges; 00224 INT index; 00225 } ITERATOR; 00226 00227 typedef struct tagDELAYED_ITEM_EDIT 00228 { 00229 BOOL fEnabled; 00230 INT iItem; 00231 } DELAYED_ITEM_EDIT; 00232 00233 typedef struct tagLISTVIEW_INFO 00234 { 00235 /* control window */ 00236 HWND hwndSelf; 00237 RECT rcList; /* This rectangle is really the window 00238 * client rectangle possibly reduced by the 00239 * horizontal scroll bar and/or header - see 00240 * LISTVIEW_UpdateSize. This rectangle offset 00241 * by the LISTVIEW_GetOrigin value is in 00242 * client coordinates */ 00243 00244 /* notification window */ 00245 SHORT notifyFormat; 00246 HWND hwndNotify; 00247 BOOL bDoChangeNotify; /* send change notification messages? */ 00248 UINT uCallbackMask; 00249 00250 /* tooltips */ 00251 HWND hwndToolTip; 00252 00253 /* items */ 00254 INT nItemCount; /* the number of items in the list */ 00255 HDPA hdpaItems; /* array ITEM_INFO pointers */ 00256 HDPA hdpaItemIds; /* array of ITEM_ID pointers */ 00257 HDPA hdpaPosX; /* maintains the (X, Y) coordinates of the */ 00258 HDPA hdpaPosY; /* items in LVS_ICON, and LVS_SMALLICON modes */ 00259 RANGES selectionRanges; 00260 INT nSelectionMark; /* item to start next multiselection from */ 00261 INT nHotItem; 00262 BOOL bAutoarrange; /* Autoarrange flag when NOT in LVS_AUTOARRANGE */ 00263 00264 /* columns */ 00265 HDPA hdpaColumns; /* array of COLUMN_INFO pointers */ 00266 BOOL colRectsDirty; /* trigger column rectangles requery from header */ 00267 00268 /* item metrics */ 00269 BOOL bNoItemMetrics; /* flags if item metrics are not yet computed */ 00270 INT nItemHeight; 00271 INT nItemWidth; 00272 00273 /* sorting */ 00274 PFNLVCOMPARE pfnCompare; /* sorting callback pointer */ 00275 LPARAM lParamSort; 00276 00277 /* style */ 00278 DWORD dwStyle; /* the cached window GWL_STYLE */ 00279 DWORD dwLvExStyle; /* extended listview style */ 00280 DWORD uView; /* current view available through LVM_[G,S]ETVIEW */ 00281 00282 /* edit item */ 00283 HWND hwndEdit; 00284 WNDPROC EditWndProc; 00285 INT nEditLabelItem; 00286 DELAYED_ITEM_EDIT itemEdit; /* Pointer to this structure will be the timer ID */ 00287 00288 /* icons */ 00289 HIMAGELIST himlNormal; 00290 HIMAGELIST himlSmall; 00291 HIMAGELIST himlState; 00292 SIZE iconSize; 00293 SIZE iconSpacing; 00294 SIZE iconStateSize; 00295 POINT currIconPos; /* this is the position next icon will be placed */ 00296 00297 /* header */ 00298 HWND hwndHeader; 00299 INT xTrackLine; /* The x coefficient of the track line or -1 if none */ 00300 00301 /* marquee selection */ 00302 BOOL bMarqueeSelect; /* marquee selection/highlight underway */ 00303 BOOL bScrolling; 00304 RECT marqueeRect; /* absolute coordinates of marquee selection */ 00305 RECT marqueeDrawRect; /* relative coordinates for drawing marquee */ 00306 POINT marqueeOrigin; /* absolute coordinates of marquee click origin */ 00307 00308 /* focus drawing */ 00309 BOOL bFocus; /* control has focus */ 00310 INT nFocusedItem; 00311 RECT rcFocus; /* focus bounds */ 00312 00313 /* colors */ 00314 HBRUSH hBkBrush; 00315 COLORREF clrBk; 00316 COLORREF clrText; 00317 COLORREF clrTextBk; 00318 BOOL bDefaultBkColor; 00319 00320 /* font */ 00321 HFONT hDefaultFont; 00322 HFONT hFont; 00323 INT ntmHeight; /* Some cached metrics of the font used */ 00324 INT ntmMaxCharWidth; /* by the listview to draw items */ 00325 INT nEllipsisWidth; 00326 00327 /* mouse operation */ 00328 BOOL bLButtonDown; 00329 BOOL bRButtonDown; 00330 BOOL bDragging; 00331 POINT ptClickPos; /* point where the user clicked */ 00332 INT nLButtonDownItem; /* tracks item to reset multiselection on WM_LBUTTONUP */ 00333 DWORD dwHoverTime; 00334 HCURSOR hHotCursor; 00335 00336 /* keyboard operation */ 00337 DWORD lastKeyPressTimestamp; 00338 WPARAM charCode; 00339 INT nSearchParamLength; 00340 WCHAR szSearchParam[ MAX_PATH ]; 00341 00342 /* painting */ 00343 DWORD cditemmode; /* Keep the custom draw flags for an item/row */ 00344 BOOL bIsDrawing; /* Drawing in progress */ 00345 INT nMeasureItemHeight; /* WM_MEASUREITEM result */ 00346 BOOL bRedraw; /* WM_SETREDRAW switch */ 00347 00348 /* misc */ 00349 DWORD iVersion; /* CCM_[G,S]ETVERSION */ 00350 } LISTVIEW_INFO; 00351 00352 /* 00353 * constants 00354 */ 00355 /* How many we debug buffer to allocate */ 00356 #define DEBUG_BUFFERS 20 00357 /* The size of a single debug buffer */ 00358 #define DEBUG_BUFFER_SIZE 256 00359 00360 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */ 00361 #define SB_INTERNAL -1 00362 00363 /* maximum size of a label */ 00364 #define DISP_TEXT_SIZE 260 00365 00366 /* padding for items in list and small icon display modes */ 00367 #define WIDTH_PADDING 12 00368 00369 /* padding for items in list, report and small icon display modes */ 00370 #define HEIGHT_PADDING 1 00371 00372 /* offset of items in report display mode */ 00373 #define REPORT_MARGINX 2 00374 00375 /* padding for icon in large icon display mode 00376 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area 00377 * that HITTEST will see. 00378 * ICON_TOP_PADDING_HITABLE - spacing between above and icon. 00379 * ICON_TOP_PADDING - sum of the two above. 00380 * ICON_BOTTOM_PADDING - between bottom of icon and top of text 00381 * LABEL_HOR_PADDING - between text and sides of box 00382 * LABEL_VERT_PADDING - between bottom of text and end of box 00383 * 00384 * ICON_LR_PADDING - additional width above icon size. 00385 * ICON_LR_HALF - half of the above value 00386 */ 00387 #define ICON_TOP_PADDING_NOTHITABLE 2 00388 #define ICON_TOP_PADDING_HITABLE 2 00389 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE) 00390 #define ICON_BOTTOM_PADDING 4 00391 #define LABEL_HOR_PADDING 5 00392 #define LABEL_VERT_PADDING 7 00393 #define ICON_LR_PADDING 16 00394 #define ICON_LR_HALF (ICON_LR_PADDING/2) 00395 00396 /* default label width for items in list and small icon display modes */ 00397 #define DEFAULT_LABEL_WIDTH 40 00398 /* maximum select rectangle width for empty text item in LV_VIEW_DETAILS */ 00399 #define MAX_EMPTYTEXT_SELECT_WIDTH 80 00400 00401 /* default column width for items in list display mode */ 00402 #define DEFAULT_COLUMN_WIDTH 128 00403 00404 /* Size of "line" scroll for V & H scrolls */ 00405 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37 00406 00407 /* Padding between image and label */ 00408 #define IMAGE_PADDING 2 00409 00410 /* Padding behind the label */ 00411 #define TRAILING_LABEL_PADDING 12 00412 #define TRAILING_HEADER_PADDING 11 00413 00414 /* Border for the icon caption */ 00415 #define CAPTION_BORDER 2 00416 00417 /* Standard DrawText flags */ 00418 #define LV_ML_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS) 00419 #define LV_FL_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_NOCLIP) 00420 #define LV_SL_DT_FLAGS (DT_VCENTER | DT_NOPREFIX | DT_EDITCONTROL | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS) 00421 00422 /* Image index from state */ 00423 #define STATEIMAGEINDEX(x) (((x) & LVIS_STATEIMAGEMASK) >> 12) 00424 00425 /* The time in milliseconds to reset the search in the list */ 00426 #define KEY_DELAY 450 00427 00428 /* Dump the LISTVIEW_INFO structure to the debug channel */ 00429 #define LISTVIEW_DUMP(iP) do { \ 00430 TRACE("hwndSelf=%p, clrBk=0x%06x, clrText=0x%06x, clrTextBk=0x%06x, ItemHeight=%d, ItemWidth=%d, Style=0x%08x\n", \ 00431 iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \ 00432 iP->nItemHeight, iP->nItemWidth, iP->dwStyle); \ 00433 TRACE("hwndSelf=%p, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08x, Focus=%d\n", \ 00434 iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \ 00435 iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, iP->bFocus ); \ 00436 TRACE("hwndSelf=%p, ntmH=%d, icSz.cx=%d, icSz.cy=%d, icSp.cx=%d, icSp.cy=%d, notifyFmt=%d\n", \ 00437 iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \ 00438 iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \ 00439 TRACE("hwndSelf=%p, rcList=%s\n", iP->hwndSelf, wine_dbgstr_rect(&iP->rcList)); \ 00440 } while(0) 00441 00442 static const WCHAR themeClass[] = {'L','i','s','t','V','i','e','w',0}; 00443 00444 /* 00445 * forward declarations 00446 */ 00447 static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *, LPLVITEMW, BOOL); 00448 static void LISTVIEW_GetItemBox(const LISTVIEW_INFO *, INT, LPRECT); 00449 static void LISTVIEW_GetItemOrigin(const LISTVIEW_INFO *, INT, LPPOINT); 00450 static BOOL LISTVIEW_GetItemPosition(const LISTVIEW_INFO *, INT, LPPOINT); 00451 static BOOL LISTVIEW_GetItemRect(const LISTVIEW_INFO *, INT, LPRECT); 00452 static void LISTVIEW_GetOrigin(const LISTVIEW_INFO *, LPPOINT); 00453 static BOOL LISTVIEW_GetViewRect(const LISTVIEW_INFO *, LPRECT); 00454 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *); 00455 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *, WPARAM, LPARAM); 00456 static INT LISTVIEW_GetStringWidthT(const LISTVIEW_INFO *, LPCWSTR, BOOL); 00457 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *, INT, BOOL); 00458 static UINT LISTVIEW_GetItemState(const LISTVIEW_INFO *, INT, UINT); 00459 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *, INT, const LVITEMW *); 00460 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *, INT, INT); 00461 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *, INT, INT); 00462 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *, INT, BOOL); 00463 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *, INT, HIMAGELIST); 00464 static INT LISTVIEW_HitTest(const LISTVIEW_INFO *, LPLVHITTESTINFO, BOOL, BOOL); 00465 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *, BOOL, BOOL); 00466 static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *, INT, INT); 00467 00468 /******** Text handling functions *************************************/ 00469 00470 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a 00471 * text string. The string may be ANSI or Unicode, in which case 00472 * the boolean isW tells us the type of the string. 00473 * 00474 * The name of the function tell what type of strings it expects: 00475 * W: Unicode, T: ANSI/Unicode - function of isW 00476 */ 00477 00478 static inline BOOL is_text(LPCWSTR text) 00479 { 00480 return text != NULL && text != LPSTR_TEXTCALLBACKW; 00481 } 00482 00483 static inline int textlenT(LPCWSTR text, BOOL isW) 00484 { 00485 return !is_text(text) ? 0 : 00486 isW ? lstrlenW(text) : lstrlenA((LPCSTR)text); 00487 } 00488 00489 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max) 00490 { 00491 if (isDestW) 00492 if (isSrcW) lstrcpynW(dest, src, max); 00493 else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max); 00494 else 00495 if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL); 00496 else lstrcpynA((LPSTR)dest, (LPCSTR)src, max); 00497 } 00498 00499 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW) 00500 { 00501 LPWSTR wstr = (LPWSTR)text; 00502 00503 if (!isW && is_text(text)) 00504 { 00505 INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0); 00506 wstr = Alloc(len * sizeof(WCHAR)); 00507 if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len); 00508 } 00509 TRACE(" wstr=%s\n", text == LPSTR_TEXTCALLBACKW ? "(callback)" : debugstr_w(wstr)); 00510 return wstr; 00511 } 00512 00513 static inline void textfreeT(LPWSTR wstr, BOOL isW) 00514 { 00515 if (!isW && is_text(wstr)) Free (wstr); 00516 } 00517 00518 /* 00519 * dest is a pointer to a Unicode string 00520 * src is a pointer to a string (Unicode if isW, ANSI if !isW) 00521 */ 00522 static BOOL textsetptrT(LPWSTR *dest, LPCWSTR src, BOOL isW) 00523 { 00524 BOOL bResult = TRUE; 00525 00526 if (src == LPSTR_TEXTCALLBACKW) 00527 { 00528 if (is_text(*dest)) Free(*dest); 00529 *dest = LPSTR_TEXTCALLBACKW; 00530 } 00531 else 00532 { 00533 LPWSTR pszText = textdupTtoW(src, isW); 00534 if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL; 00535 bResult = Str_SetPtrW(dest, pszText); 00536 textfreeT(pszText, isW); 00537 } 00538 return bResult; 00539 } 00540 00541 /* 00542 * compares a Unicode to a Unicode/ANSI text string 00543 */ 00544 static inline int textcmpWT(LPCWSTR aw, LPCWSTR bt, BOOL isW) 00545 { 00546 if (!aw) return bt ? -1 : 0; 00547 if (!bt) return 1; 00548 if (aw == LPSTR_TEXTCALLBACKW) 00549 return bt == LPSTR_TEXTCALLBACKW ? 1 : -1; 00550 if (bt != LPSTR_TEXTCALLBACKW) 00551 { 00552 LPWSTR bw = textdupTtoW(bt, isW); 00553 int r = bw ? lstrcmpW(aw, bw) : 1; 00554 textfreeT(bw, isW); 00555 return r; 00556 } 00557 00558 return 1; 00559 } 00560 00561 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n) 00562 { 00563 int res; 00564 00565 n = min(min(n, lstrlenW(s1)), lstrlenW(s2)); 00566 res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n); 00567 return res ? res - sizeof(WCHAR) : res; 00568 } 00569 00570 /******** Debugging functions *****************************************/ 00571 00572 static inline LPCSTR debugtext_t(LPCWSTR text, BOOL isW) 00573 { 00574 if (text == LPSTR_TEXTCALLBACKW) return "(callback)"; 00575 return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text); 00576 } 00577 00578 static inline LPCSTR debugtext_tn(LPCWSTR text, BOOL isW, INT n) 00579 { 00580 if (text == LPSTR_TEXTCALLBACKW) return "(callback)"; 00581 n = min(textlenT(text, isW), n); 00582 return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n); 00583 } 00584 00585 static char* debug_getbuf(void) 00586 { 00587 static int index = 0; 00588 static char buffers[DEBUG_BUFFERS][DEBUG_BUFFER_SIZE]; 00589 return buffers[index++ % DEBUG_BUFFERS]; 00590 } 00591 00592 static inline const char* debugrange(const RANGE *lprng) 00593 { 00594 if (!lprng) return "(null)"; 00595 return wine_dbg_sprintf("[%d, %d]", lprng->lower, lprng->upper); 00596 } 00597 00598 static const char* debugscrollinfo(const SCROLLINFO *pScrollInfo) 00599 { 00600 char* buf = debug_getbuf(), *text = buf; 00601 int len, size = DEBUG_BUFFER_SIZE; 00602 00603 if (pScrollInfo == NULL) return "(null)"; 00604 len = snprintf(buf, size, "{cbSize=%d, ", pScrollInfo->cbSize); 00605 if (len == -1) goto end; buf += len; size -= len; 00606 if (pScrollInfo->fMask & SIF_RANGE) 00607 len = snprintf(buf, size, "nMin=%d, nMax=%d, ", pScrollInfo->nMin, pScrollInfo->nMax); 00608 else len = 0; 00609 if (len == -1) goto end; buf += len; size -= len; 00610 if (pScrollInfo->fMask & SIF_PAGE) 00611 len = snprintf(buf, size, "nPage=%u, ", pScrollInfo->nPage); 00612 else len = 0; 00613 if (len == -1) goto end; buf += len; size -= len; 00614 if (pScrollInfo->fMask & SIF_POS) 00615 len = snprintf(buf, size, "nPos=%d, ", pScrollInfo->nPos); 00616 else len = 0; 00617 if (len == -1) goto end; buf += len; size -= len; 00618 if (pScrollInfo->fMask & SIF_TRACKPOS) 00619 len = snprintf(buf, size, "nTrackPos=%d, ", pScrollInfo->nTrackPos); 00620 else len = 0; 00621 if (len == -1) goto end; buf += len; 00622 goto undo; 00623 end: 00624 buf = text + strlen(text); 00625 undo: 00626 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; } 00627 return text; 00628 } 00629 00630 static const char* debugnmlistview(const NMLISTVIEW *plvnm) 00631 { 00632 if (!plvnm) return "(null)"; 00633 return wine_dbg_sprintf("iItem=%d, iSubItem=%d, uNewState=0x%x," 00634 " uOldState=0x%x, uChanged=0x%x, ptAction=%s, lParam=%ld", 00635 plvnm->iItem, plvnm->iSubItem, plvnm->uNewState, plvnm->uOldState, 00636 plvnm->uChanged, wine_dbgstr_point(&plvnm->ptAction), plvnm->lParam); 00637 } 00638 00639 static const char* debuglvitem_t(const LVITEMW *lpLVItem, BOOL isW) 00640 { 00641 char* buf = debug_getbuf(), *text = buf; 00642 int len, size = DEBUG_BUFFER_SIZE; 00643 00644 if (lpLVItem == NULL) return "(null)"; 00645 len = snprintf(buf, size, "{iItem=%d, iSubItem=%d, ", lpLVItem->iItem, lpLVItem->iSubItem); 00646 if (len == -1) goto end; buf += len; size -= len; 00647 if (lpLVItem->mask & LVIF_STATE) 00648 len = snprintf(buf, size, "state=%x, stateMask=%x, ", lpLVItem->state, lpLVItem->stateMask); 00649 else len = 0; 00650 if (len == -1) goto end; buf += len; size -= len; 00651 if (lpLVItem->mask & LVIF_TEXT) 00652 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem->pszText, isW, 80), lpLVItem->cchTextMax); 00653 else len = 0; 00654 if (len == -1) goto end; buf += len; size -= len; 00655 if (lpLVItem->mask & LVIF_IMAGE) 00656 len = snprintf(buf, size, "iImage=%d, ", lpLVItem->iImage); 00657 else len = 0; 00658 if (len == -1) goto end; buf += len; size -= len; 00659 if (lpLVItem->mask & LVIF_PARAM) 00660 len = snprintf(buf, size, "lParam=%lx, ", lpLVItem->lParam); 00661 else len = 0; 00662 if (len == -1) goto end; buf += len; size -= len; 00663 if (lpLVItem->mask & LVIF_INDENT) 00664 len = snprintf(buf, size, "iIndent=%d, ", lpLVItem->iIndent); 00665 else len = 0; 00666 if (len == -1) goto end; buf += len; 00667 goto undo; 00668 end: 00669 buf = text + strlen(text); 00670 undo: 00671 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; } 00672 return text; 00673 } 00674 00675 static const char* debuglvcolumn_t(const LVCOLUMNW *lpColumn, BOOL isW) 00676 { 00677 char* buf = debug_getbuf(), *text = buf; 00678 int len, size = DEBUG_BUFFER_SIZE; 00679 00680 if (lpColumn == NULL) return "(null)"; 00681 len = snprintf(buf, size, "{"); 00682 if (len == -1) goto end; buf += len; size -= len; 00683 if (lpColumn->mask & LVCF_SUBITEM) 00684 len = snprintf(buf, size, "iSubItem=%d, ", lpColumn->iSubItem); 00685 else len = 0; 00686 if (len == -1) goto end; buf += len; size -= len; 00687 if (lpColumn->mask & LVCF_FMT) 00688 len = snprintf(buf, size, "fmt=%x, ", lpColumn->fmt); 00689 else len = 0; 00690 if (len == -1) goto end; buf += len; size -= len; 00691 if (lpColumn->mask & LVCF_WIDTH) 00692 len = snprintf(buf, size, "cx=%d, ", lpColumn->cx); 00693 else len = 0; 00694 if (len == -1) goto end; buf += len; size -= len; 00695 if (lpColumn->mask & LVCF_TEXT) 00696 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn->pszText, isW, 80), lpColumn->cchTextMax); 00697 else len = 0; 00698 if (len == -1) goto end; buf += len; size -= len; 00699 if (lpColumn->mask & LVCF_IMAGE) 00700 len = snprintf(buf, size, "iImage=%d, ", lpColumn->iImage); 00701 else len = 0; 00702 if (len == -1) goto end; buf += len; size -= len; 00703 if (lpColumn->mask & LVCF_ORDER) 00704 len = snprintf(buf, size, "iOrder=%d, ", lpColumn->iOrder); 00705 else len = 0; 00706 if (len == -1) goto end; buf += len; 00707 goto undo; 00708 end: 00709 buf = text + strlen(text); 00710 undo: 00711 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; } 00712 return text; 00713 } 00714 00715 static const char* debuglvhittestinfo(const LVHITTESTINFO *lpht) 00716 { 00717 if (!lpht) return "(null)"; 00718 00719 return wine_dbg_sprintf("{pt=%s, flags=0x%x, iItem=%d, iSubItem=%d}", 00720 wine_dbgstr_point(&lpht->pt), lpht->flags, lpht->iItem, lpht->iSubItem); 00721 } 00722 00723 /* Return the corresponding text for a given scroll value */ 00724 static inline LPCSTR debugscrollcode(int nScrollCode) 00725 { 00726 switch(nScrollCode) 00727 { 00728 case SB_LINELEFT: return "SB_LINELEFT"; 00729 case SB_LINERIGHT: return "SB_LINERIGHT"; 00730 case SB_PAGELEFT: return "SB_PAGELEFT"; 00731 case SB_PAGERIGHT: return "SB_PAGERIGHT"; 00732 case SB_THUMBPOSITION: return "SB_THUMBPOSITION"; 00733 case SB_THUMBTRACK: return "SB_THUMBTRACK"; 00734 case SB_ENDSCROLL: return "SB_ENDSCROLL"; 00735 case SB_INTERNAL: return "SB_INTERNAL"; 00736 default: return "unknown"; 00737 } 00738 } 00739 00740 00741 /******** Notification functions ************************************/ 00742 00743 static int get_ansi_notification(UINT unicodeNotificationCode) 00744 { 00745 switch (unicodeNotificationCode) 00746 { 00747 case LVN_BEGINLABELEDITA: 00748 case LVN_BEGINLABELEDITW: return LVN_BEGINLABELEDITA; 00749 case LVN_ENDLABELEDITA: 00750 case LVN_ENDLABELEDITW: return LVN_ENDLABELEDITA; 00751 case LVN_GETDISPINFOA: 00752 case LVN_GETDISPINFOW: return LVN_GETDISPINFOA; 00753 case LVN_SETDISPINFOA: 00754 case LVN_SETDISPINFOW: return LVN_SETDISPINFOA; 00755 case LVN_ODFINDITEMA: 00756 case LVN_ODFINDITEMW: return LVN_ODFINDITEMA; 00757 case LVN_GETINFOTIPA: 00758 case LVN_GETINFOTIPW: return LVN_GETINFOTIPA; 00759 /* header forwards */ 00760 case HDN_TRACKA: 00761 case HDN_TRACKW: return HDN_TRACKA; 00762 case HDN_ENDTRACKA: 00763 case HDN_ENDTRACKW: return HDN_ENDTRACKA; 00764 case HDN_BEGINDRAG: return HDN_BEGINDRAG; 00765 case HDN_ENDDRAG: return HDN_ENDDRAG; 00766 case HDN_ITEMCHANGINGA: 00767 case HDN_ITEMCHANGINGW: return HDN_ITEMCHANGINGA; 00768 case HDN_ITEMCHANGEDA: 00769 case HDN_ITEMCHANGEDW: return HDN_ITEMCHANGEDA; 00770 case HDN_ITEMCLICKA: 00771 case HDN_ITEMCLICKW: return HDN_ITEMCLICKA; 00772 case HDN_DIVIDERDBLCLICKA: 00773 case HDN_DIVIDERDBLCLICKW: return HDN_DIVIDERDBLCLICKA; 00774 default: break; 00775 } 00776 FIXME("unknown notification %x\n", unicodeNotificationCode); 00777 return unicodeNotificationCode; 00778 } 00779 00780 /* forwards header notifications to listview parent */ 00781 static LRESULT notify_forward_header(const LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh) 00782 { 00783 NMHEADERA nmhA; 00784 HDITEMA hditema; 00785 HD_TEXTFILTERA textfilter; 00786 LPSTR text = NULL, filter = NULL; 00787 LRESULT ret; 00788 00789 /* on unicode format exit earlier */ 00790 if (infoPtr->notifyFormat == NFR_UNICODE) 00791 return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, lpnmh->hdr.idFrom, 00792 (LPARAM)lpnmh); 00793 00794 /* header always supplies unicode notifications, 00795 all we have to do is to convert strings to ANSI */ 00796 nmhA = *(const NMHEADERA*)lpnmh; 00797 if (lpnmh->pitem) 00798 { 00799 hditema = *(HDITEMA*)lpnmh->pitem; 00800 nmhA.pitem = &hditema; 00801 /* convert item text */ 00802 if (lpnmh->pitem->mask & HDI_TEXT) 00803 { 00804 hditema.pszText = NULL; 00805 Str_SetPtrWtoA(&hditema.pszText, lpnmh->pitem->pszText); 00806 text = hditema.pszText; 00807 } 00808 /* convert filter text */ 00809 if ((lpnmh->pitem->mask & HDI_FILTER) && (lpnmh->pitem->type == HDFT_ISSTRING) && 00810 lpnmh->pitem->pvFilter) 00811 { 00812 hditema.pvFilter = &textfilter; 00813 textfilter = *(HD_TEXTFILTERA*)(lpnmh->pitem->pvFilter); 00814 textfilter.pszText = NULL; 00815 Str_SetPtrWtoA(&textfilter.pszText, ((HD_TEXTFILTERW*)lpnmh->pitem->pvFilter)->pszText); 00816 filter = textfilter.pszText; 00817 } 00818 } 00819 nmhA.hdr.code = get_ansi_notification(lpnmh->hdr.code); 00820 00821 ret = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, nmhA.hdr.idFrom, 00822 (LPARAM)&nmhA); 00823 00824 /* cleanup */ 00825 Free(text); 00826 Free(filter); 00827 00828 return ret; 00829 } 00830 00831 static LRESULT notify_hdr(const LISTVIEW_INFO *infoPtr, INT code, LPNMHDR pnmh) 00832 { 00833 LRESULT result; 00834 00835 TRACE("(code=%d)\n", code); 00836 00837 pnmh->hwndFrom = infoPtr->hwndSelf; 00838 pnmh->idFrom = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID); 00839 pnmh->code = code; 00840 result = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, pnmh->idFrom, (LPARAM)pnmh); 00841 00842 TRACE(" <= %ld\n", result); 00843 00844 return result; 00845 } 00846 00847 static inline BOOL notify(const LISTVIEW_INFO *infoPtr, INT code) 00848 { 00849 NMHDR nmh; 00850 HWND hwnd = infoPtr->hwndSelf; 00851 notify_hdr(infoPtr, code, &nmh); 00852 return IsWindow(hwnd); 00853 } 00854 00855 static inline void notify_itemactivate(const LISTVIEW_INFO *infoPtr, const LVHITTESTINFO *htInfo) 00856 { 00857 NMITEMACTIVATE nmia; 00858 LVITEMW item; 00859 00860 if (htInfo) { 00861 nmia.uNewState = 0; 00862 nmia.uOldState = 0; 00863 nmia.uChanged = 0; 00864 nmia.uKeyFlags = 0; 00865 00866 item.mask = LVIF_PARAM|LVIF_STATE; 00867 item.iItem = htInfo->iItem; 00868 item.iSubItem = 0; 00869 item.stateMask = (UINT)-1; 00870 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) { 00871 nmia.lParam = item.lParam; 00872 nmia.uOldState = item.state; 00873 nmia.uNewState = item.state | LVIS_ACTIVATING; 00874 nmia.uChanged = LVIF_STATE; 00875 } 00876 00877 nmia.iItem = htInfo->iItem; 00878 nmia.iSubItem = htInfo->iSubItem; 00879 nmia.ptAction = htInfo->pt; 00880 00881 if (GetKeyState(VK_SHIFT) & 0x8000) nmia.uKeyFlags |= LVKF_SHIFT; 00882 if (GetKeyState(VK_CONTROL) & 0x8000) nmia.uKeyFlags |= LVKF_CONTROL; 00883 if (GetKeyState(VK_MENU) & 0x8000) nmia.uKeyFlags |= LVKF_ALT; 00884 } 00885 notify_hdr(infoPtr, LVN_ITEMACTIVATE, (LPNMHDR)&nmia); 00886 } 00887 00888 static inline LRESULT notify_listview(const LISTVIEW_INFO *infoPtr, INT code, LPNMLISTVIEW plvnm) 00889 { 00890 TRACE("(code=%d, plvnm=%s)\n", code, debugnmlistview(plvnm)); 00891 return notify_hdr(infoPtr, code, (LPNMHDR)plvnm); 00892 } 00893 00894 static BOOL notify_click(const LISTVIEW_INFO *infoPtr, INT code, const LVHITTESTINFO *lvht) 00895 { 00896 NMITEMACTIVATE nmia; 00897 LVITEMW item; 00898 HWND hwnd = infoPtr->hwndSelf; 00899 00900 TRACE("code=%d, lvht=%s\n", code, debuglvhittestinfo(lvht)); 00901 ZeroMemory(&nmia, sizeof(nmia)); 00902 nmia.iItem = lvht->iItem; 00903 nmia.iSubItem = lvht->iSubItem; 00904 nmia.ptAction = lvht->pt; 00905 item.mask = LVIF_PARAM; 00906 item.iItem = lvht->iItem; 00907 item.iSubItem = 0; 00908 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmia.lParam = item.lParam; 00909 notify_hdr(infoPtr, code, (LPNMHDR)&nmia); 00910 return IsWindow(hwnd); 00911 } 00912 00913 static BOOL notify_deleteitem(const LISTVIEW_INFO *infoPtr, INT nItem) 00914 { 00915 NMLISTVIEW nmlv; 00916 LVITEMW item; 00917 HWND hwnd = infoPtr->hwndSelf; 00918 00919 ZeroMemory(&nmlv, sizeof (NMLISTVIEW)); 00920 nmlv.iItem = nItem; 00921 item.mask = LVIF_PARAM; 00922 item.iItem = nItem; 00923 item.iSubItem = 0; 00924 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam; 00925 notify_listview(infoPtr, LVN_DELETEITEM, &nmlv); 00926 return IsWindow(hwnd); 00927 } 00928 00929 /* 00930 Send notification. depends on dispinfoW having same 00931 structure as dispinfoA. 00932 infoPtr : listview struct 00933 code : *Unicode* notification code 00934 pdi : dispinfo structure (can be unicode or ansi) 00935 isW : TRUE if dispinfo is Unicode 00936 */ 00937 static BOOL notify_dispinfoT(const LISTVIEW_INFO *infoPtr, UINT code, LPNMLVDISPINFOW pdi, BOOL isW) 00938 { 00939 INT length = 0, ret_length; 00940 LPWSTR buffer = NULL, ret_text; 00941 BOOL return_ansi = FALSE; 00942 BOOL return_unicode = FALSE; 00943 BOOL ret; 00944 00945 if ((pdi->item.mask & LVIF_TEXT) && is_text(pdi->item.pszText)) 00946 { 00947 return_unicode = ( isW && infoPtr->notifyFormat == NFR_ANSI); 00948 return_ansi = (!isW && infoPtr->notifyFormat == NFR_UNICODE); 00949 } 00950 00951 ret_length = pdi->item.cchTextMax; 00952 ret_text = pdi->item.pszText; 00953 00954 if (return_unicode || return_ansi) 00955 { 00956 if (code != LVN_GETDISPINFOW) 00957 { 00958 length = return_ansi ? 00959 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0): 00960 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL); 00961 } 00962 else 00963 { 00964 length = pdi->item.cchTextMax; 00965 *pdi->item.pszText = 0; /* make sure we don't process garbage */ 00966 } 00967 00968 buffer = Alloc( (return_ansi ? sizeof(WCHAR) : sizeof(CHAR)) * length); 00969 if (!buffer) return FALSE; 00970 00971 if (return_ansi) 00972 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, 00973 buffer, length); 00974 else 00975 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) buffer, 00976 length, NULL, NULL); 00977 00978 pdi->item.pszText = buffer; 00979 pdi->item.cchTextMax = length; 00980 } 00981 00982 if (infoPtr->notifyFormat == NFR_ANSI) 00983 code = get_ansi_notification(code); 00984 00985 TRACE(" pdi->item=%s\n", debuglvitem_t(&pdi->item, infoPtr->notifyFormat != NFR_ANSI)); 00986 ret = notify_hdr(infoPtr, code, &pdi->hdr); 00987 TRACE(" resulting code=%d\n", pdi->hdr.code); 00988 00989 if (return_ansi || return_unicode) 00990 { 00991 if (return_ansi && (pdi->hdr.code == LVN_GETDISPINFOA)) 00992 { 00993 strcpy((char*)ret_text, (char*)pdi->item.pszText); 00994 } 00995 else if (return_unicode && (pdi->hdr.code == LVN_GETDISPINFOW)) 00996 { 00997 strcpyW(ret_text, pdi->item.pszText); 00998 } 00999 else if (return_ansi) /* note : pointer can be changed by app ! */ 01000 { 01001 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) ret_text, 01002 ret_length, NULL, NULL); 01003 } 01004 else 01005 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1, 01006 ret_text, ret_length); 01007 01008 pdi->item.pszText = ret_text; /* restores our buffer */ 01009 pdi->item.cchTextMax = ret_length; 01010 01011 Free(buffer); 01012 return ret; 01013 } 01014 01015 /* if dipsinfo holder changed notification code then convert */ 01016 if (!isW && (pdi->hdr.code == LVN_GETDISPINFOW) && (pdi->item.mask & LVIF_TEXT)) 01017 { 01018 length = WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL); 01019 01020 buffer = Alloc(length * sizeof(CHAR)); 01021 if (!buffer) return FALSE; 01022 01023 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) buffer, 01024 ret_length, NULL, NULL); 01025 01026 strcpy((LPSTR)pdi->item.pszText, (LPSTR)buffer); 01027 Free(buffer); 01028 } 01029 01030 return ret; 01031 } 01032 01033 static void customdraw_fill(NMLVCUSTOMDRAW *lpnmlvcd, const LISTVIEW_INFO *infoPtr, HDC hdc, 01034 const RECT *rcBounds, const LVITEMW *lplvItem) 01035 { 01036 ZeroMemory(lpnmlvcd, sizeof(NMLVCUSTOMDRAW)); 01037 lpnmlvcd->nmcd.hdc = hdc; 01038 lpnmlvcd->nmcd.rc = *rcBounds; 01039 lpnmlvcd->clrTextBk = infoPtr->clrTextBk; 01040 lpnmlvcd->clrText = infoPtr->clrText; 01041 if (!lplvItem) return; 01042 lpnmlvcd->nmcd.dwItemSpec = lplvItem->iItem + 1; 01043 lpnmlvcd->iSubItem = lplvItem->iSubItem; 01044 if (lplvItem->state & LVIS_SELECTED) lpnmlvcd->nmcd.uItemState |= CDIS_SELECTED; 01045 if (lplvItem->state & LVIS_FOCUSED) lpnmlvcd->nmcd.uItemState |= CDIS_FOCUS; 01046 if (lplvItem->iItem == infoPtr->nHotItem) lpnmlvcd->nmcd.uItemState |= CDIS_HOT; 01047 lpnmlvcd->nmcd.lItemlParam = lplvItem->lParam; 01048 } 01049 01050 static inline DWORD notify_customdraw (const LISTVIEW_INFO *infoPtr, DWORD dwDrawStage, NMLVCUSTOMDRAW *lpnmlvcd) 01051 { 01052 BOOL isForItem = (lpnmlvcd->nmcd.dwItemSpec != 0); 01053 DWORD result; 01054 01055 lpnmlvcd->nmcd.dwDrawStage = dwDrawStage; 01056 if (isForItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_ITEM; 01057 if (lpnmlvcd->iSubItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_SUBITEM; 01058 if (isForItem) lpnmlvcd->nmcd.dwItemSpec--; 01059 result = notify_hdr(infoPtr, NM_CUSTOMDRAW, &lpnmlvcd->nmcd.hdr); 01060 if (isForItem) lpnmlvcd->nmcd.dwItemSpec++; 01061 return result; 01062 } 01063 01064 static void prepaint_setup (const LISTVIEW_INFO *infoPtr, HDC hdc, NMLVCUSTOMDRAW *lpnmlvcd, BOOL SubItem) 01065 { 01066 if (lpnmlvcd->clrTextBk == CLR_DEFAULT) 01067 lpnmlvcd->clrTextBk = comctl32_color.clrWindow; 01068 if (lpnmlvcd->clrText == CLR_DEFAULT) 01069 lpnmlvcd->clrText = comctl32_color.clrWindowText; 01070 01071 /* apparently, for selected items, we have to override the returned values */ 01072 if (!SubItem) 01073 { 01074 if (lpnmlvcd->nmcd.uItemState & CDIS_SELECTED) 01075 { 01076 if (infoPtr->bFocus) 01077 { 01078 lpnmlvcd->clrTextBk = comctl32_color.clrHighlight; 01079 lpnmlvcd->clrText = comctl32_color.clrHighlightText; 01080 } 01081 else if (infoPtr->dwStyle & LVS_SHOWSELALWAYS) 01082 { 01083 lpnmlvcd->clrTextBk = comctl32_color.clr3dFace; 01084 lpnmlvcd->clrText = comctl32_color.clrBtnText; 01085 } 01086 } 01087 } 01088 01089 /* Set the text attributes */ 01090 if (lpnmlvcd->clrTextBk != CLR_NONE) 01091 { 01092 SetBkMode(hdc, OPAQUE); 01093 SetBkColor(hdc,lpnmlvcd->clrTextBk); 01094 } 01095 else 01096 SetBkMode(hdc, TRANSPARENT); 01097 SetTextColor(hdc, lpnmlvcd->clrText); 01098 } 01099 01100 static inline DWORD notify_postpaint (const LISTVIEW_INFO *infoPtr, NMLVCUSTOMDRAW *lpnmlvcd) 01101 { 01102 return notify_customdraw(infoPtr, CDDS_POSTPAINT, lpnmlvcd); 01103 } 01104 01105 /* returns TRUE when repaint needed, FALSE otherwise */ 01106 static BOOL notify_measureitem(LISTVIEW_INFO *infoPtr) 01107 { 01108 MEASUREITEMSTRUCT mis; 01109 mis.CtlType = ODT_LISTVIEW; 01110 mis.CtlID = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID); 01111 mis.itemID = -1; 01112 mis.itemWidth = 0; 01113 mis.itemData = 0; 01114 mis.itemHeight= infoPtr->nItemHeight; 01115 SendMessageW(infoPtr->hwndNotify, WM_MEASUREITEM, mis.CtlID, (LPARAM)&mis); 01116 if (infoPtr->nItemHeight != max(mis.itemHeight, 1)) 01117 { 01118 infoPtr->nMeasureItemHeight = infoPtr->nItemHeight = max(mis.itemHeight, 1); 01119 return TRUE; 01120 } 01121 return FALSE; 01122 } 01123 01124 /******** Item iterator functions **********************************/ 01125 01126 static RANGES ranges_create(int count); 01127 static void ranges_destroy(RANGES ranges); 01128 static BOOL ranges_add(RANGES ranges, RANGE range); 01129 static BOOL ranges_del(RANGES ranges, RANGE range); 01130 static void ranges_dump(RANGES ranges); 01131 01132 static inline BOOL ranges_additem(RANGES ranges, INT nItem) 01133 { 01134 RANGE range = { nItem, nItem + 1 }; 01135 01136 return ranges_add(ranges, range); 01137 } 01138 01139 static inline BOOL ranges_delitem(RANGES ranges, INT nItem) 01140 { 01141 RANGE range = { nItem, nItem + 1 }; 01142 01143 return ranges_del(ranges, range); 01144 } 01145 01146 /*** 01147 * ITERATOR DOCUMENTATION 01148 * 01149 * The iterator functions allow for easy, and convenient iteration 01150 * over items of interest in the list. Typically, you create a 01151 * iterator, use it, and destroy it, as such: 01152 * ITERATOR i; 01153 * 01154 * iterator_xxxitems(&i, ...); 01155 * while (iterator_{prev,next}(&i) 01156 * { 01157 * //code which uses i.nItem 01158 * } 01159 * iterator_destroy(&i); 01160 * 01161 * where xxx is either: framed, or visible. 01162 * Note that it is important that the code destroys the iterator 01163 * after it's done with it, as the creation of the iterator may 01164 * allocate memory, which thus needs to be freed. 01165 * 01166 * You can iterate both forwards, and backwards through the list, 01167 * by using iterator_next or iterator_prev respectively. 01168 * 01169 * Lower numbered items are draw on top of higher number items in 01170 * LVS_ICON, and LVS_SMALLICON (which are the only modes where 01171 * items may overlap). So, to test items, you should use 01172 * iterator_next 01173 * which lists the items top to bottom (in Z-order). 01174 * For drawing items, you should use 01175 * iterator_prev 01176 * which lists the items bottom to top (in Z-order). 01177 * If you keep iterating over the items after the end-of-items 01178 * marker (-1) is returned, the iterator will start from the 01179 * beginning. Typically, you don't need to test for -1, 01180 * because iterator_{next,prev} will return TRUE if more items 01181 * are to be iterated over, or FALSE otherwise. 01182 * 01183 * Note: the iterator is defined to be bidirectional. That is, 01184 * any number of prev followed by any number of next, or 01185 * five versa, should leave the iterator at the same item: 01186 * prev * n, next * n = next * n, prev * n 01187 * 01188 * The iterator has a notion of an out-of-order, special item, 01189 * which sits at the start of the list. This is used in 01190 * LVS_ICON, and LVS_SMALLICON mode to handle the focused item, 01191 * which needs to be first, as it may overlap other items. 01192 * 01193 * The code is a bit messy because we have: 01194 * - a special item to deal with 01195 * - simple range, or composite range 01196 * - empty range. 01197 * If you find bugs, or want to add features, please make sure you 01198 * always check/modify *both* iterator_prev, and iterator_next. 01199 */ 01200 01201 /**** 01202 * This function iterates through the items in increasing order, 01203 * but prefixed by the special item, then -1. That is: 01204 * special, 1, 2, 3, ..., n, -1. 01205 * Each item is listed only once. 01206 */ 01207 static inline BOOL iterator_next(ITERATOR* i) 01208 { 01209 if (i->nItem == -1) 01210 { 01211 i->nItem = i->nSpecial; 01212 if (i->nItem != -1) return TRUE; 01213 } 01214 if (i->nItem == i->nSpecial) 01215 { 01216 if (i->ranges) i->index = 0; 01217 goto pickarange; 01218 } 01219 01220 i->nItem++; 01221 testitem: 01222 if (i->nItem == i->nSpecial) i->nItem++; 01223 if (i->nItem < i->range.upper) return TRUE; 01224 01225 pickarange: 01226 if (i->ranges) 01227 { 01228 if (i->index < DPA_GetPtrCount(i->ranges->hdpa)) 01229 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, i->index++); 01230 else goto end; 01231 } 01232 else if (i->nItem >= i->range.upper) goto end; 01233 01234 i->nItem = i->range.lower; 01235 if (i->nItem >= 0) goto testitem; 01236 end: 01237 i->nItem = -1; 01238 return FALSE; 01239 } 01240 01241 /**** 01242 * This function iterates through the items in decreasing order, 01243 * followed by the special item, then -1. That is: 01244 * n, n-1, ..., 3, 2, 1, special, -1. 01245 * Each item is listed only once. 01246 */ 01247 static inline BOOL iterator_prev(ITERATOR* i) 01248 { 01249 BOOL start = FALSE; 01250 01251 if (i->nItem == -1) 01252 { 01253 start = TRUE; 01254 if (i->ranges) i->index = DPA_GetPtrCount(i->ranges->hdpa); 01255 goto pickarange; 01256 } 01257 if (i->nItem == i->nSpecial) 01258 { 01259 i->nItem = -1; 01260 return FALSE; 01261 } 01262 01263 testitem: 01264 i->nItem--; 01265 if (i->nItem == i->nSpecial) i->nItem--; 01266 if (i->nItem >= i->range.lower) return TRUE; 01267 01268 pickarange: 01269 if (i->ranges) 01270 { 01271 if (i->index > 0) 01272 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, --i->index); 01273 else goto end; 01274 } 01275 else if (!start && i->nItem < i->range.lower) goto end; 01276 01277 i->nItem = i->range.upper; 01278 if (i->nItem > 0) goto testitem; 01279 end: 01280 return (i->nItem = i->nSpecial) != -1; 01281 } 01282 01283 static RANGE iterator_range(const ITERATOR *i) 01284 { 01285 RANGE range; 01286 01287 if (!i->ranges) return i->range; 01288 01289 if (DPA_GetPtrCount(i->ranges->hdpa) > 0) 01290 { 01291 range.lower = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, 0)).lower; 01292 range.upper = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, DPA_GetPtrCount(i->ranges->hdpa) - 1)).upper; 01293 } 01294 else range.lower = range.upper = 0; 01295 01296 return range; 01297 } 01298 01299 /*** 01300 * Releases resources associated with this iterator. 01301 */ 01302 static inline void iterator_destroy(const ITERATOR *i) 01303 { 01304 ranges_destroy(i->ranges); 01305 } 01306 01307 /*** 01308 * Create an empty iterator. 01309 */ 01310 static inline BOOL iterator_empty(ITERATOR* i) 01311 { 01312 ZeroMemory(i, sizeof(*i)); 01313 i->nItem = i->nSpecial = i->range.lower = i->range.upper = -1; 01314 return TRUE; 01315 } 01316 01317 /*** 01318 * Create an iterator over a range. 01319 */ 01320 static inline BOOL iterator_rangeitems(ITERATOR* i, RANGE range) 01321 { 01322 iterator_empty(i); 01323 i->range = range; 01324 return TRUE; 01325 } 01326 01327 /*** 01328 * Create an iterator over a bunch of ranges. 01329 * Please note that the iterator will take ownership of the ranges, 01330 * and will free them upon destruction. 01331 */ 01332 static inline BOOL iterator_rangesitems(ITERATOR* i, RANGES ranges) 01333 { 01334 iterator_empty(i); 01335 i->ranges = ranges; 01336 return TRUE; 01337 } 01338 01339 /*** 01340 * Creates an iterator over the items which intersect frame. 01341 * Uses absolute coordinates rather than compensating for the current offset. 01342 */ 01343 static BOOL iterator_frameditems_absolute(ITERATOR* i, const LISTVIEW_INFO* infoPtr, const RECT *frame) 01344 { 01345 RECT rcItem, rcTemp; 01346 01347 /* in case we fail, we want to return an empty iterator */ 01348 if (!iterator_empty(i)) return FALSE; 01349 01350 TRACE("(frame=%s)\n", wine_dbgstr_rect(frame)); 01351 01352 if (infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON) 01353 { 01354 INT nItem; 01355 01356 if (infoPtr->uView == LV_VIEW_ICON && infoPtr->nFocusedItem != -1) 01357 { 01358 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcItem); 01359 if (IntersectRect(&rcTemp, &rcItem, frame)) 01360 i->nSpecial = infoPtr->nFocusedItem; 01361 } 01362 if (!(iterator_rangesitems(i, ranges_create(50)))) return FALSE; 01363 /* to do better here, we need to have PosX, and PosY sorted */ 01364 TRACE("building icon ranges:\n"); 01365 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++) 01366 { 01367 rcItem.left = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem); 01368 rcItem.top = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem); 01369 rcItem.right = rcItem.left + infoPtr->nItemWidth; 01370 rcItem.bottom = rcItem.top + infoPtr->nItemHeight; 01371 if (IntersectRect(&rcTemp, &rcItem, frame)) 01372 ranges_additem(i->ranges, nItem); 01373 } 01374 return TRUE; 01375 } 01376 else if (infoPtr->uView == LV_VIEW_DETAILS) 01377 { 01378 RANGE range; 01379 01380 if (frame->left >= infoPtr->nItemWidth) return TRUE; 01381 if (frame->top >= infoPtr->nItemHeight * infoPtr->nItemCount) return TRUE; 01382 01383 range.lower = max(frame->top / infoPtr->nItemHeight, 0); 01384 range.upper = min((frame->bottom - 1) / infoPtr->nItemHeight, infoPtr->nItemCount - 1) + 1; 01385 if (range.upper <= range.lower) return TRUE; 01386 if (!iterator_rangeitems(i, range)) return FALSE; 01387 TRACE(" report=%s\n", debugrange(&i->range)); 01388 } 01389 else 01390 { 01391 INT nPerCol = max((infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight, 1); 01392 INT nFirstRow = max(frame->top / infoPtr->nItemHeight, 0); 01393 INT nLastRow = min((frame->bottom - 1) / infoPtr->nItemHeight, nPerCol - 1); 01394 INT nFirstCol; 01395 INT nLastCol; 01396 INT lower; 01397 RANGE item_range; 01398 INT nCol; 01399 01400 if (infoPtr->nItemWidth) 01401 { 01402 nFirstCol = max(frame->left / infoPtr->nItemWidth, 0); 01403 nLastCol = min((frame->right - 1) / infoPtr->nItemWidth, (infoPtr->nItemCount + nPerCol - 1) / nPerCol); 01404 } 01405 else 01406 { 01407 nFirstCol = max(frame->left, 0); 01408 nLastCol = min(frame->right - 1, (infoPtr->nItemCount + nPerCol - 1) / nPerCol); 01409 } 01410 01411 lower = nFirstCol * nPerCol + nFirstRow; 01412 01413 TRACE("nPerCol=%d, nFirstRow=%d, nLastRow=%d, nFirstCol=%d, nLastCol=%d, lower=%d\n", 01414 nPerCol, nFirstRow, nLastRow, nFirstCol, nLastCol, lower); 01415 01416 if (nLastCol < nFirstCol || nLastRow < nFirstRow) return TRUE; 01417 01418 if (!(iterator_rangesitems(i, ranges_create(nLastCol - nFirstCol + 1)))) return FALSE; 01419 TRACE("building list ranges:\n"); 01420 for (nCol = nFirstCol; nCol <= nLastCol; nCol++) 01421 { 01422 item_range.lower = nCol * nPerCol + nFirstRow; 01423 if(item_range.lower >= infoPtr->nItemCount) break; 01424 item_range.upper = min(nCol * nPerCol + nLastRow + 1, infoPtr->nItemCount); 01425 TRACE(" list=%s\n", debugrange(&item_range)); 01426 ranges_add(i->ranges, item_range); 01427 } 01428 } 01429 01430 return TRUE; 01431 } 01432 01433 /*** 01434 * Creates an iterator over the items which intersect lprc. 01435 */ 01436 static BOOL iterator_frameditems(ITERATOR* i, const LISTVIEW_INFO* infoPtr, const RECT *lprc) 01437 { 01438 RECT frame = *lprc; 01439 POINT Origin; 01440 01441 TRACE("(lprc=%s)\n", wine_dbgstr_rect(lprc)); 01442 01443 LISTVIEW_GetOrigin(infoPtr, &Origin); 01444 OffsetRect(&frame, -Origin.x, -Origin.y); 01445 01446 return iterator_frameditems_absolute(i, infoPtr, &frame); 01447 } 01448 01449 /*** 01450 * Creates an iterator over the items which intersect the visible region of hdc. 01451 */ 01452 static BOOL iterator_visibleitems(ITERATOR *i, const LISTVIEW_INFO *infoPtr, HDC hdc) 01453 { 01454 POINT Origin, Position; 01455 RECT rcItem, rcClip; 01456 INT rgntype; 01457 01458 rgntype = GetClipBox(hdc, &rcClip); 01459 if (rgntype == NULLREGION) return iterator_empty(i); 01460 if (!iterator_frameditems(i, infoPtr, &rcClip)) return FALSE; 01461 if (rgntype == SIMPLEREGION) return TRUE; 01462 01463 /* first deal with the special item */ 01464 if (i->nSpecial != -1) 01465 { 01466 LISTVIEW_GetItemBox(infoPtr, i->nSpecial, &rcItem); 01467 if (!RectVisible(hdc, &rcItem)) i->nSpecial = -1; 01468 } 01469 01470 /* if we can't deal with the region, we'll just go with the simple range */ 01471 LISTVIEW_GetOrigin(infoPtr, &Origin); 01472 TRACE("building visible range:\n"); 01473 if (!i->ranges && i->range.lower < i->range.upper) 01474 { 01475 if (!(i->ranges = ranges_create(50))) return TRUE; 01476 if (!ranges_add(i->ranges, i->range)) 01477 { 01478 ranges_destroy(i->ranges); 01479 i->ranges = 0; 01480 return TRUE; 01481 } 01482 } 01483 01484 /* now delete the invisible items from the list */ 01485 while(iterator_next(i)) 01486 { 01487 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position); 01488 rcItem.left = (infoPtr->uView == LV_VIEW_DETAILS) ? Origin.x : Position.x + Origin.x; 01489 rcItem.top = Position.y + Origin.y; 01490 rcItem.right = rcItem.left + infoPtr->nItemWidth; 01491 rcItem.bottom = rcItem.top + infoPtr->nItemHeight; 01492 if (!RectVisible(hdc, &rcItem)) 01493 ranges_delitem(i->ranges, i->nItem); 01494 } 01495 /* the iterator should restart on the next iterator_next */ 01496 TRACE("done\n"); 01497 01498 return TRUE; 01499 } 01500 01501 /* Remove common elements from two iterators */ 01502 /* Passed iterators have to point on the first elements */ 01503 static BOOL iterator_remove_common_items(ITERATOR *iter1, ITERATOR *iter2) 01504 { 01505 if(!iter1->ranges || !iter2->ranges) { 01506 int lower, upper; 01507 01508 if(iter1->ranges || iter2->ranges || 01509 (iter1->range.lower<iter2->range.lower && iter1->range.upper>iter2->range.upper) || 01510 (iter1->range.lower>iter2->range.lower && iter1->range.upper<iter2->range.upper)) { 01511 ERR("result is not a one range iterator\n"); 01512 return FALSE; 01513 } 01514 01515 if(iter1->range.lower==-1 || iter2->range.lower==-1) 01516 return TRUE; 01517 01518 lower = iter1->range.lower; 01519 upper = iter1->range.upper; 01520 01521 if(lower < iter2->range.lower) 01522 iter1->range.upper = iter2->range.lower; 01523 else if(upper > iter2->range.upper) 01524 iter1->range.lower = iter2->range.upper; 01525 else 01526 iter1->range.lower = iter1->range.upper = -1; 01527 01528 if(iter2->range.lower < lower) 01529 iter2->range.upper = lower; 01530 else if(iter2->range.upper > upper) 01531 iter2->range.lower = upper; 01532 else 01533 iter2->range.lower = iter2->range.upper = -1; 01534 01535 return TRUE; 01536 } 01537 01538 iterator_next(iter1); 01539 iterator_next(iter2); 01540 01541 while(1) { 01542 if(iter1->nItem==-1 || iter2->nItem==-1) 01543 break; 01544 01545 if(iter1->nItem == iter2->nItem) { 01546 int delete = iter1->nItem; 01547 01548 iterator_prev(iter1); 01549 iterator_prev(iter2); 01550 ranges_delitem(iter1->ranges, delete); 01551 ranges_delitem(iter2->ranges, delete); 01552 iterator_next(iter1); 01553 iterator_next(iter2); 01554 } else if(iter1->nItem > iter2->nItem) 01555 iterator_next(iter2); 01556 else 01557 iterator_next(iter1); 01558 } 01559 01560 iter1->nItem = iter1->range.lower = iter1->range.upper = -1; 01561 iter2->nItem = iter2->range.lower = iter2->range.upper = -1; 01562 return TRUE; 01563 } 01564 01565 /******** Misc helper functions ************************************/ 01566 01567 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg, 01568 WPARAM wParam, LPARAM lParam, BOOL isW) 01569 { 01570 if (isW) return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam); 01571 else return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam); 01572 } 01573 01574 static inline BOOL is_autoarrange(const LISTVIEW_INFO *infoPtr) 01575 { 01576 return ((infoPtr->dwStyle & LVS_AUTOARRANGE) || infoPtr->bAutoarrange) && 01577 (infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON); 01578 } 01579 01580 static void toggle_checkbox_state(LISTVIEW_INFO *infoPtr, INT nItem) 01581 { 01582 DWORD state = STATEIMAGEINDEX(LISTVIEW_GetItemState(infoPtr, nItem, LVIS_STATEIMAGEMASK)); 01583 if(state == 1 || state == 2) 01584 { 01585 LVITEMW lvitem; 01586 state ^= 3; 01587 lvitem.state = INDEXTOSTATEIMAGEMASK(state); 01588 lvitem.stateMask = LVIS_STATEIMAGEMASK; 01589 LISTVIEW_SetItemState(infoPtr, nItem, &lvitem); 01590 } 01591 } 01592 01593 /* this should be called after window style got updated, 01594 it used to reset view state to match current window style */ 01595 static inline void map_style_view(LISTVIEW_INFO *infoPtr) 01596 { 01597 switch (infoPtr->dwStyle & LVS_TYPEMASK) 01598 { 01599 case LVS_ICON: 01600 infoPtr->uView = LV_VIEW_ICON; 01601 break; 01602 case LVS_REPORT: 01603 infoPtr->uView = LV_VIEW_DETAILS; 01604 break; 01605 case LVS_SMALLICON: 01606 infoPtr->uView = LV_VIEW_SMALLICON; 01607 break; 01608 case LVS_LIST: 01609 infoPtr->uView = LV_VIEW_LIST; 01610 } 01611 } 01612 01613 /* computes next item id value */ 01614 static DWORD get_next_itemid(const LISTVIEW_INFO *infoPtr) 01615 { 01616 INT count = DPA_GetPtrCount(infoPtr->hdpaItemIds); 01617 01618 if (count > 0) 01619 { 01620 ITEM_ID *lpID = DPA_GetPtr(infoPtr->hdpaItemIds, count - 1); 01621 return lpID->id + 1; 01622 } 01623 return 0; 01624 } 01625 01626 /******** Internal API functions ************************************/ 01627 01628 static inline COLUMN_INFO * LISTVIEW_GetColumnInfo(const LISTVIEW_INFO *infoPtr, INT nSubItem) 01629 { 01630 static COLUMN_INFO mainItem; 01631 01632 if (nSubItem == 0 && DPA_GetPtrCount(infoPtr->hdpaColumns) == 0) return &mainItem; 01633 assert (nSubItem >= 0 && nSubItem < DPA_GetPtrCount(infoPtr->hdpaColumns)); 01634 01635 /* update cached column rectangles */ 01636 if (infoPtr->colRectsDirty) 01637 { 01638 COLUMN_INFO *info; 01639 LISTVIEW_INFO *Ptr = (LISTVIEW_INFO*)infoPtr; 01640 INT i; 01641 01642 for (i = 0; i < DPA_GetPtrCount(infoPtr->hdpaColumns); i++) { 01643 info = DPA_GetPtr(infoPtr->hdpaColumns, i); 01644 SendMessageW(infoPtr->hwndHeader, HDM_GETITEMRECT, i, (LPARAM)&info->rcHeader); 01645 } 01646 Ptr->colRectsDirty = FALSE; 01647 } 01648 01649 return DPA_GetPtr(infoPtr->hdpaColumns, nSubItem); 01650 } 01651 01652 static INT LISTVIEW_CreateHeader(LISTVIEW_INFO *infoPtr) 01653 { 01654 DWORD dFlags = WS_CHILD | HDS_HORZ | HDS_FULLDRAG | HDS_DRAGDROP; 01655 HINSTANCE hInst; 01656 01657 if (infoPtr->hwndHeader) return 0; 01658 01659 TRACE("Creating header for list %p\n", infoPtr->hwndSelf); 01660 01661 /* setup creation flags */ 01662 dFlags |= (LVS_NOSORTHEADER & infoPtr->dwStyle) ? 0 : HDS_BUTTONS; 01663 dFlags |= (LVS_NOCOLUMNHEADER & infoPtr->dwStyle) ? HDS_HIDDEN : 0; 01664 01665 hInst = (HINSTANCE)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_HINSTANCE); 01666 01667 /* create header */ 01668 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, NULL, dFlags, 01669 0, 0, 0, 0, infoPtr->hwndSelf, NULL, hInst, NULL); 01670 if (!infoPtr->hwndHeader) return -1; 01671 01672 /* set header unicode format */ 01673 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT, TRUE, 0); 01674 01675 /* set header font */ 01676 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, TRUE); 01677 01678 LISTVIEW_UpdateSize(infoPtr); 01679 01680 return 0; 01681 } 01682 01683 static inline void LISTVIEW_GetHeaderRect(const LISTVIEW_INFO *infoPtr, INT nSubItem, LPRECT lprc) 01684 { 01685 *lprc = LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->rcHeader; 01686 } 01687 01688 static inline BOOL LISTVIEW_IsHeaderEnabled(const LISTVIEW_INFO *infoPtr) 01689 { 01690 return (infoPtr->uView == LV_VIEW_DETAILS || 01691 infoPtr->dwLvExStyle & LVS_EX_HEADERINALLVIEWS) && 01692 !(infoPtr->dwStyle & LVS_NOCOLUMNHEADER); 01693 } 01694 01695 static inline BOOL LISTVIEW_GetItemW(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem) 01696 { 01697 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE); 01698 } 01699 01700 /* used to handle collapse main item column case */ 01701 static inline BOOL LISTVIEW_DrawFocusRect(const LISTVIEW_INFO *infoPtr, HDC hdc) 01702 { 01703 BOOL Ret = FALSE; 01704 01705 if (infoPtr->rcFocus.left < infoPtr->rcFocus.right) 01706 { 01707 DWORD dwOldBkColor, dwOldTextColor; 01708 01709 dwOldBkColor = SetBkColor(hdc, RGB(255, 255, 255)); 01710 dwOldTextColor = SetBkColor(hdc, RGB(0, 0, 0)); 01711 Ret = DrawFocusRect(hdc, &infoPtr->rcFocus); 01712 SetBkColor(hdc, dwOldBkColor); 01713 SetBkColor(hdc, dwOldTextColor); 01714 } 01715 return Ret; 01716 } 01717 01718 /* Listview invalidation functions: use _only_ these functions to invalidate */ 01719 01720 static inline BOOL is_redrawing(const LISTVIEW_INFO *infoPtr) 01721 { 01722 return infoPtr->bRedraw; 01723 } 01724 01725 static inline void LISTVIEW_InvalidateRect(const LISTVIEW_INFO *infoPtr, const RECT* rect) 01726 { 01727 if(!is_redrawing(infoPtr)) return; 01728 TRACE(" invalidating rect=%s\n", wine_dbgstr_rect(rect)); 01729 InvalidateRect(infoPtr->hwndSelf, rect, TRUE); 01730 } 01731 01732 static inline void LISTVIEW_InvalidateItem(const LISTVIEW_INFO *infoPtr, INT nItem) 01733 { 01734 RECT rcBox; 01735 01736 if(!is_redrawing(infoPtr)) return; 01737 LISTVIEW_GetItemBox(infoPtr, nItem, &rcBox); 01738 LISTVIEW_InvalidateRect(infoPtr, &rcBox); 01739 } 01740 01741 static inline void LISTVIEW_InvalidateSubItem(const LISTVIEW_INFO *infoPtr, INT nItem, INT nSubItem) 01742 { 01743 POINT Origin, Position; 01744 RECT rcBox; 01745 01746 if(!is_redrawing(infoPtr)) return; 01747 assert (infoPtr->uView == LV_VIEW_DETAILS); 01748 LISTVIEW_GetOrigin(infoPtr, &Origin); 01749 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position); 01750 LISTVIEW_GetHeaderRect(infoPtr, nSubItem, &rcBox); 01751 rcBox.top = 0; 01752 rcBox.bottom = infoPtr->nItemHeight; 01753 OffsetRect(&rcBox, Origin.x + Position.x, Origin.y + Position.y); 01754 LISTVIEW_InvalidateRect(infoPtr, &rcBox); 01755 } 01756 01757 static inline void LISTVIEW_InvalidateList(const LISTVIEW_INFO *infoPtr) 01758 { 01759 LISTVIEW_InvalidateRect(infoPtr, NULL); 01760 } 01761 01762 static inline void LISTVIEW_InvalidateColumn(const LISTVIEW_INFO *infoPtr, INT nColumn) 01763 { 01764 RECT rcCol; 01765 01766 if(!is_redrawing(infoPtr)) return; 01767 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol); 01768 rcCol.top = infoPtr->rcList.top; 01769 rcCol.bottom = infoPtr->rcList.bottom; 01770 LISTVIEW_InvalidateRect(infoPtr, &rcCol); 01771 } 01772 01773 /*** 01774 * DESCRIPTION: 01775 * Retrieves the number of items that can fit vertically in the client area. 01776 * 01777 * PARAMETER(S): 01778 * [I] infoPtr : valid pointer to the listview structure 01779 * 01780 * RETURN: 01781 * Number of items per row. 01782 */ 01783 static inline INT LISTVIEW_GetCountPerRow(const LISTVIEW_INFO *infoPtr) 01784 { 01785 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left; 01786 01787 return max(nListWidth/(infoPtr->nItemWidth ? infoPtr->nItemWidth : 1), 1); 01788 } 01789 01790 /*** 01791 * DESCRIPTION: 01792 * Retrieves the number of items that can fit horizontally in the client 01793 * area. 01794 * 01795 * PARAMETER(S): 01796 * [I] infoPtr : valid pointer to the listview structure 01797 * 01798 * RETURN: 01799 * Number of items per column. 01800 */ 01801 static inline INT LISTVIEW_GetCountPerColumn(const LISTVIEW_INFO *infoPtr) 01802 { 01803 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top; 01804 01805 return max(nListHeight / infoPtr->nItemHeight, 1); 01806 } 01807 01808 01809 /************************************************************************* 01810 * LISTVIEW_ProcessLetterKeys 01811 * 01812 * Processes keyboard messages generated by pressing the letter keys 01813 * on the keyboard. 01814 * What this does is perform a case insensitive search from the 01815 * current position with the following quirks: 01816 * - If two chars or more are pressed in quick succession we search 01817 * for the corresponding string (e.g. 'abc'). 01818 * - If there is a delay we wipe away the current search string and 01819 * restart with just that char. 01820 * - If the user keeps pressing the same character, whether slowly or 01821 * fast, so that the search string is entirely composed of this 01822 * character ('aaaaa' for instance), then we search for first item 01823 * that starting with that character. 01824 * - If the user types the above character in quick succession, then 01825 * we must also search for the corresponding string ('aaaaa'), and 01826 * go to that string if there is a match. 01827 * 01828 * PARAMETERS 01829 * [I] hwnd : handle to the window 01830 * [I] charCode : the character code, the actual character 01831 * [I] keyData : key data 01832 * 01833 * RETURNS 01834 * 01835 * Zero. 01836 * 01837 * BUGS 01838 * 01839 * - The current implementation has a list of characters it will 01840 * accept and it ignores everything else. In particular it will 01841 * ignore accentuated characters which seems to match what 01842 * Windows does. But I'm not sure it makes sense to follow 01843 * Windows there. 01844 * - We don't sound a beep when the search fails. 01845 * 01846 * SEE ALSO 01847 * 01848 * TREEVIEW_ProcessLetterKeys 01849 */ 01850 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData) 01851 { 01852 WCHAR buffer[MAX_PATH]; 01853 INT endidx, startidx; 01854 DWORD prevTime; 01855 LVITEMW item; 01856 INT nItem; 01857 INT diff; 01858 01859 /* simple parameter checking */ 01860 if (!charCode || !keyData || infoPtr->nItemCount == 0) return 0; 01861 01862 /* only allow the valid WM_CHARs through */ 01863 if (!isalnumW(charCode) && 01864 charCode != '.' && charCode != '`' && charCode != '!' && 01865 charCode != '@' && charCode != '#' && charCode != '$' && 01866 charCode != '%' && charCode != '^' && charCode != '&' && 01867 charCode != '*' && charCode != '(' && charCode != ')' && 01868 charCode != '-' && charCode != '_' && charCode != '+' && 01869 charCode != '=' && charCode != '\\'&& charCode != ']' && 01870 charCode != '}' && charCode != '[' && charCode != '{' && 01871 charCode != '/' && charCode != '?' && charCode != '>' && 01872 charCode != '<' && charCode != ',' && charCode != '~') 01873 return 0; 01874 01875 /* update the search parameters */ 01876 prevTime = infoPtr->lastKeyPressTimestamp; 01877 infoPtr->lastKeyPressTimestamp = GetTickCount(); 01878 diff = infoPtr->lastKeyPressTimestamp - prevTime; 01879 01880 if (diff >= 0 && diff < KEY_DELAY) 01881 { 01882 if (infoPtr->nSearchParamLength < MAX_PATH - 1) 01883 infoPtr->szSearchParam[infoPtr->nSearchParamLength++] = charCode; 01884 01885 if (infoPtr->charCode != charCode) 01886 infoPtr->charCode = charCode = 0; 01887 } 01888 else 01889 { 01890 infoPtr->charCode = charCode; 01891 infoPtr->szSearchParam[0] = charCode; 01892 infoPtr->nSearchParamLength = 1; 01893 } 01894 01895 /* and search from the current position */ 01896 nItem = -1; 01897 endidx = infoPtr->nItemCount; 01898 01899 /* should start from next after focused item, so next item that matches 01900 will be selected, if there isn't any and focused matches it will be selected 01901 on second search stage from beginning of the list */ 01902 if (infoPtr->nFocusedItem >= 0 && infoPtr->nItemCount > 1) 01903 startidx = infoPtr->nFocusedItem + 1; 01904 else 01905 startidx = 0; 01906 01907 /* let application handle this for virtual listview */ 01908 if (infoPtr->dwStyle & LVS_OWNERDATA) 01909 { 01910 NMLVFINDITEMW nmlv; 01911 01912 memset(&nmlv.lvfi, 0, sizeof(nmlv.lvfi)); 01913 nmlv.lvfi.flags = (LVFI_WRAP | LVFI_PARTIAL); 01914 nmlv.lvfi.psz = infoPtr->szSearchParam; 01915 nmlv.iStart = startidx; 01916 01917 infoPtr->szSearchParam[infoPtr->nSearchParamLength] = 0; 01918 01919 nItem = notify_hdr(infoPtr, LVN_ODFINDITEMW, (LPNMHDR)&nmlv.hdr); 01920 } 01921 else 01922 { 01923 INT i = startidx; 01924 01925 /* first search in [startidx, endidx), on failure continue in [0, startidx) */ 01926 while (1) 01927 { 01928 /* start from first item if not found with >= startidx */ 01929 if (i == infoPtr->nItemCount && startidx > 0) 01930 { 01931 endidx = startidx; 01932 startidx = 0; 01933 } 01934 01935 for (i = startidx; i < endidx; i++) 01936 { 01937 /* retrieve text */ 01938 item.mask = LVIF_TEXT; 01939 item.iItem = i; 01940 item.iSubItem = 0; 01941 item.pszText = buffer; 01942 item.cchTextMax = MAX_PATH; 01943 if (!LISTVIEW_GetItemW(infoPtr, &item)) return 0; 01944 01945 if (lstrncmpiW(item.pszText, infoPtr->szSearchParam, infoPtr->nSearchParamLength) == 0) 01946 { 01947 nItem = i; 01948 break; 01949 } 01950 else if (nItem == -1 && lstrncmpiW(item.pszText, infoPtr->szSearchParam, 1) == 0) 01951 { 01952 /* this would work but we must keep looking for a longer match */ 01953 nItem = i; 01954 } 01955 } 01956 01957 if ( nItem != -1 || /* found something */ 01958 endidx != infoPtr->nItemCount || /* second search done */ 01959 (startidx == 0 && endidx == infoPtr->nItemCount) /* full range for first search */ ) 01960 break; 01961 }; 01962 } 01963 01964 if (nItem != -1) 01965 LISTVIEW_KeySelection(infoPtr, nItem, FALSE); 01966 01967 return 0; 01968 } 01969 01970 /************************************************************************* 01971 * LISTVIEW_UpdateHeaderSize [Internal] 01972 * 01973 * Function to resize the header control 01974 * 01975 * PARAMS 01976 * [I] hwnd : handle to a window 01977 * [I] nNewScrollPos : scroll pos to set 01978 * 01979 * RETURNS 01980 * None. 01981 */ 01982 static void LISTVIEW_UpdateHeaderSize(const LISTVIEW_INFO *infoPtr, INT nNewScrollPos) 01983 { 01984 RECT winRect; 01985 POINT point[2]; 01986 01987 TRACE("nNewScrollPos=%d\n", nNewScrollPos); 01988 01989 if (!infoPtr->hwndHeader) return; 01990 01991 GetWindowRect(infoPtr->hwndHeader, &winRect); 01992 point[0].x = winRect.left; 01993 point[0].y = winRect.top; 01994 point[1].x = winRect.right; 01995 point[1].y = winRect.bottom; 01996 01997 MapWindowPoints(HWND_DESKTOP, infoPtr->hwndSelf, point, 2); 01998 point[0].x = -nNewScrollPos; 01999 point[1].x += nNewScrollPos; 02000 02001 SetWindowPos(infoPtr->hwndHeader,0, 02002 point[0].x,point[0].y,point[1].x,point[1].y, 02003 (infoPtr->dwStyle & LVS_NOCOLUMNHEADER) ? SWP_HIDEWINDOW : SWP_SHOWWINDOW | 02004 SWP_NOZORDER | SWP_NOACTIVATE); 02005 } 02006 02007 /*** 02008 * DESCRIPTION: 02009 * Update the scrollbars. This functions should be called whenever 02010 * the content, size or view changes. 02011 * 02012 * PARAMETER(S): 02013 * [I] infoPtr : valid pointer to the listview structure 02014 * 02015 * RETURN: 02016 * None 02017 */ 02018 static void LISTVIEW_UpdateScroll(const LISTVIEW_INFO *infoPtr) 02019 { 02020 SCROLLINFO horzInfo, vertInfo; 02021 INT dx, dy; 02022 02023 if ((infoPtr->dwStyle & LVS_NOSCROLL) || !is_redrawing(infoPtr)) return; 02024 02025 ZeroMemory(&horzInfo, sizeof(SCROLLINFO)); 02026 horzInfo.cbSize = sizeof(SCROLLINFO); 02027 horzInfo.nPage = infoPtr->rcList.right - infoPtr->rcList.left; 02028 02029 /* for now, we'll set info.nMax to the _count_, and adjust it later */ 02030 if (infoPtr->uView == LV_VIEW_LIST) 02031 { 02032 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr); 02033 horzInfo.nMax = (infoPtr->nItemCount + nPerCol - 1) / nPerCol; 02034 02035 /* scroll by at least one column per page */ 02036 if(horzInfo.nPage < infoPtr->nItemWidth) 02037 horzInfo.nPage = infoPtr->nItemWidth; 02038 02039 if (infoPtr->nItemWidth) 02040 horzInfo.nPage /= infoPtr->nItemWidth; 02041 } 02042 else if (infoPtr->uView == LV_VIEW_DETAILS) 02043 { 02044 horzInfo.nMax = infoPtr->nItemWidth; 02045 } 02046 else /* LV_VIEW_ICON, or LV_VIEW_SMALLICON */ 02047 { 02048 RECT rcView; 02049 02050 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) horzInfo.nMax = rcView.right - rcView.left; 02051 } 02052 02053 if (LISTVIEW_IsHeaderEnabled(infoPtr)) 02054 { 02055 if (DPA_GetPtrCount(infoPtr->hdpaColumns)) 02056 { 02057 RECT rcHeader; 02058 INT index; 02059 02060 index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX, 02061 DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, 0); 02062 02063 LISTVIEW_GetHeaderRect(infoPtr, index, &rcHeader); 02064 horzInfo.nMax = rcHeader.right; 02065 TRACE("horzInfo.nMax=%d\n", horzInfo.nMax); 02066 } 02067 } 02068 02069 horzInfo.fMask = SIF_RANGE | SIF_PAGE; 02070 horzInfo.nMax = max(horzInfo.nMax - 1, 0); 02071 dx = GetScrollPos(infoPtr->hwndSelf, SB_HORZ); 02072 dx -= SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo, TRUE); 02073 TRACE("horzInfo=%s\n", debugscrollinfo(&horzInfo)); 02074 02075 /* Setting the horizontal scroll can change the listview size 02076 * (and potentially everything else) so we need to recompute 02077 * everything again for the vertical scroll 02078 */ 02079 02080 ZeroMemory(&vertInfo, sizeof(SCROLLINFO)); 02081 vertInfo.cbSize = sizeof(SCROLLINFO); 02082 vertInfo.nPage = infoPtr->rcList.bottom - infoPtr->rcList.top; 02083 02084 if (infoPtr->uView == LV_VIEW_DETAILS) 02085 { 02086 vertInfo.nMax = infoPtr->nItemCount; 02087 02088 /* scroll by at least one page */ 02089 if(vertInfo.nPage < infoPtr->nItemHeight) 02090 vertInfo.nPage = infoPtr->nItemHeight; 02091 02092 if (infoPtr->nItemHeight > 0) 02093 vertInfo.nPage /= infoPtr->nItemHeight; 02094 } 02095 else if (infoPtr->uView != LV_VIEW_LIST) /* LV_VIEW_ICON, or LV_VIEW_SMALLICON */ 02096 { 02097 RECT rcView; 02098 02099 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) vertInfo.nMax = rcView.bottom - rcView.top; 02100 } 02101 02102 vertInfo.fMask = SIF_RANGE | SIF_PAGE; 02103 vertInfo.nMax = max(vertInfo.nMax - 1, 0); 02104 dy = GetScrollPos(infoPtr->hwndSelf, SB_VERT); 02105 dy -= SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &vertInfo, TRUE); 02106 TRACE("vertInfo=%s\n", debugscrollinfo(&vertInfo)); 02107 02108 /* Change of the range may have changed the scroll pos. If so move the content */ 02109 if (dx != 0 || dy != 0) 02110 { 02111 RECT listRect; 02112 listRect = infoPtr->rcList; 02113 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &listRect, &listRect, 0, 0, 02114 SW_ERASE | SW_INVALIDATE); 02115 } 02116 02117 /* Update the Header Control */ 02118 if (infoPtr->hwndHeader) 02119 { 02120 horzInfo.fMask = SIF_POS; 02121 GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo); 02122 LISTVIEW_UpdateHeaderSize(infoPtr, horzInfo.nPos); 02123 } 02124 } 02125 02126 02127 /*** 02128 * DESCRIPTION: 02129 * Shows/hides the focus rectangle. 02130 * 02131 * PARAMETER(S): 02132 * [I] infoPtr : valid pointer to the listview structure 02133 * [I] fShow : TRUE to show the focus, FALSE to hide it. 02134 * 02135 * RETURN: 02136 * None 02137 */ 02138 static void LISTVIEW_ShowFocusRect(const LISTVIEW_INFO *infoPtr, BOOL fShow) 02139 { 02140 HDC hdc; 02141 02142 TRACE("fShow=%d, nItem=%d\n", fShow, infoPtr->nFocusedItem); 02143 02144 if (infoPtr->nFocusedItem < 0) return; 02145 02146 /* we need some gymnastics in ICON mode to handle large items */ 02147 if (infoPtr->uView == LV_VIEW_ICON) 02148 { 02149 RECT rcBox; 02150 02151 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcBox); 02152 if ((rcBox.bottom - rcBox.top) > infoPtr->nItemHeight) 02153 { 02154 LISTVIEW_InvalidateRect(infoPtr, &rcBox); 02155 return; 02156 } 02157 } 02158 02159 if (!(hdc = GetDC(infoPtr->hwndSelf))) return; 02160 02161 /* for some reason, owner draw should work only in report mode */ 02162 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (infoPtr->uView == LV_VIEW_DETAILS)) 02163 { 02164 DRAWITEMSTRUCT dis; 02165 LVITEMW item; 02166 02167 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont; 02168 HFONT hOldFont = SelectObject(hdc, hFont); 02169 02170 item.iItem = infoPtr->nFocusedItem; 02171 item.iSubItem = 0; 02172 item.mask = LVIF_PARAM; 02173 if (!LISTVIEW_GetItemW(infoPtr, &item)) goto done; 02174 02175 ZeroMemory(&dis, sizeof(dis)); 02176 dis.CtlType = ODT_LISTVIEW; 02177 dis.CtlID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID); 02178 dis.itemID = item.iItem; 02179 dis.itemAction = ODA_FOCUS; 02180 if (fShow) dis.itemState |= ODS_FOCUS; 02181 dis.hwndItem = infoPtr->hwndSelf; 02182 dis.hDC = hdc; 02183 LISTVIEW_GetItemBox(infoPtr, dis.itemID, &dis.rcItem); 02184 dis.itemData = item.lParam; 02185 02186 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis); 02187 02188 SelectObject(hdc, hOldFont); 02189 } 02190 else 02191 { 02192 LISTVIEW_DrawFocusRect(infoPtr, hdc); 02193 } 02194 done: 02195 ReleaseDC(infoPtr->hwndSelf, hdc); 02196 } 02197 02198 /*** 02199 * Invalidates all visible selected items. 02200 */ 02201 static void LISTVIEW_InvalidateSelectedItems(const LISTVIEW_INFO *infoPtr) 02202 { 02203 ITERATOR i; 02204 02205 iterator_frameditems(&i, infoPtr, &infoPtr->rcList); 02206 while(iterator_next(&i)) 02207 { 02208 if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED)) 02209 LISTVIEW_InvalidateItem(infoPtr, i.nItem); 02210 } 02211 iterator_destroy(&i); 02212 } 02213 02214 02215 /*** 02216 * DESCRIPTION: [INTERNAL] 02217 * Computes an item's (left,top) corner, relative to rcView. 02218 * That is, the position has NOT been made relative to the Origin. 02219 * This is deliberate, to avoid computing the Origin over, and 02220 * over again, when this function is called in a loop. Instead, 02221 * one can factor the computation of the Origin before the loop, 02222 * and offset the value returned by this function, on every iteration. 02223 * 02224 * PARAMETER(S): 02225 * [I] infoPtr : valid pointer to the listview structure 02226 * [I] nItem : item number 02227 * [O] lpptOrig : item top, left corner 02228 * 02229 * RETURN: 02230 * None. 02231 */ 02232 static void LISTVIEW_GetItemOrigin(const LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition) 02233 { 02234 assert(nItem >= 0 && nItem < infoPtr->nItemCount); 02235 02236 if ((infoPtr->uView == LV_VIEW_SMALLICON) || (infoPtr->uView == LV_VIEW_ICON)) 02237 { 02238 lpptPosition->x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem); 02239 lpptPosition->y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem); 02240 } 02241 else if (infoPtr->uView == LV_VIEW_LIST) 02242 { 02243 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr); 02244 lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth; 02245 lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight; 02246 } 02247 else /* LV_VIEW_DETAILS */ 02248 { 02249 lpptPosition->x = REPORT_MARGINX; 02250 /* item is always at zero indexed column */ 02251 if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0) 02252 lpptPosition->x += LISTVIEW_GetColumnInfo(infoPtr, 0)->rcHeader.left; 02253 lpptPosition->y = nItem * infoPtr->nItemHeight; 02254 } 02255 } 02256 02257 /*** 02258 * DESCRIPTION: [INTERNAL] 02259 * Compute the rectangles of an item. This is to localize all 02260 * the computations in one place. If you are not interested in some 02261 * of these values, simply pass in a NULL -- the function is smart 02262 * enough to compute only what's necessary. The function computes 02263 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard 02264 * one, the BOX rectangle. This rectangle is very cheap to compute, 02265 * and is guaranteed to contain all the other rectangles. Computing 02266 * the ICON rect is also cheap, but all the others are potentially 02267 * expensive. This gives an easy and effective optimization when 02268 * searching (like point inclusion, or rectangle intersection): 02269 * first test against the BOX, and if TRUE, test against the desired 02270 * rectangle. 02271 * If the function does not have all the necessary information 02272 * to computed the requested rectangles, will crash with a 02273 * failed assertion. This is done so we catch all programming 02274 * errors, given that the function is called only from our code. 02275 * 02276 * We have the following 'special' meanings for a few fields: 02277 * * If LVIS_FOCUSED is set, we assume the item has the focus 02278 * This is important in ICON mode, where it might get a larger 02279 * then usual rectangle 02280 * 02281 * Please note that subitem support works only in REPORT mode. 02282 * 02283 * PARAMETER(S): 02284 * [I] infoPtr : valid pointer to the listview structure 02285 * [I] lpLVItem : item to compute the measures for 02286 * [O] lprcBox : ptr to Box rectangle 02287 * Same as LVM_GETITEMRECT with LVIR_BOUNDS 02288 * [0] lprcSelectBox : ptr to select box rectangle 02289 * Same as LVM_GETITEMRECT with LVIR_SELECTEDBOUNDS 02290 * [O] lprcIcon : ptr to Icon rectangle 02291 * Same as LVM_GETITEMRECT with LVIR_ICON 02292 * [O] lprcStateIcon: ptr to State Icon rectangle 02293 * [O] lprcLabel : ptr to Label rectangle 02294 * Same as LVM_GETITEMRECT with LVIR_LABEL 02295 * 02296 * RETURN: 02297 * None. 02298 */ 02299 static void LISTVIEW_GetItemMetrics(const LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, 02300 LPRECT lprcBox, LPRECT lprcSelectBox, 02301 LPRECT lprcIcon, LPRECT lprcStateIcon, LPRECT lprcLabel) 02302 { 02303 BOOL doSelectBox = FALSE, doIcon = FALSE, doLabel = FALSE, oversizedBox = FALSE; 02304 RECT Box, SelectBox, Icon, Label; 02305 COLUMN_INFO *lpColumnInfo = NULL; 02306 SIZE labelSize = { 0, 0 }; 02307 02308 TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem, TRUE)); 02309 02310 /* Be smart and try to figure out the minimum we have to do */ 02311 if (lpLVItem->iSubItem) assert(infoPtr->uView == LV_VIEW_DETAILS); 02312 if (infoPtr->uView == LV_VIEW_ICON && (lprcBox || lprcLabel)) 02313 { 02314 assert((lpLVItem->mask & LVIF_STATE) && (lpLVItem->stateMask & LVIS_FOCUSED)); 02315 if (lpLVItem->state & LVIS_FOCUSED) oversizedBox = doLabel = TRUE; 02316 } 02317 if (lprcSelectBox) doSelectBox = TRUE; 02318 if (lprcLabel) doLabel = TRUE; 02319 if (doLabel || lprcIcon || lprcStateIcon) doIcon = TRUE; 02320 if (doSelectBox) 02321 { 02322 doIcon = TRUE; 02323 doLabel = TRUE; 02324 } 02325 02326 /************************************************************/ 02327 /* compute the box rectangle (it should be cheap to do) */ 02328 /************************************************************/ 02329 if (lpLVItem->iSubItem || infoPtr->uView == LV_VIEW_DETAILS) 02330 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpLVItem->iSubItem); 02331 02332 if (lpLVItem->iSubItem) 02333 { 02334 Box = lpColumnInfo->rcHeader; 02335 } 02336 else 02337 { 02338 Box.left = 0; 02339 Box.right = infoPtr->nItemWidth; 02340 } 02341 Box.top = 0; 02342 Box.bottom = infoPtr->nItemHeight; 02343 02344 /******************************************************************/ 02345 /* compute ICON bounding box (ala LVM_GETITEMRECT) and STATEICON */ 02346 /******************************************************************/ 02347 if (doIcon) 02348 { 02349 LONG state_width = 0; 02350 02351 if (infoPtr->himlState && lpLVItem->iSubItem == 0) 02352 state_width = infoPtr->iconStateSize.cx; 02353 02354 if (infoPtr->uView == LV_VIEW_ICON) 02355 { 02356 Icon.left = Box.left + state_width; 02357 if (infoPtr->himlNormal) 02358 Icon.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx - state_width) / 2; 02359 Icon.top = Box.top + ICON_TOP_PADDING; 02360 Icon.right = Icon.left; 02361 Icon.bottom = Icon.top; 02362 if (infoPtr->himlNormal) 02363 { 02364 Icon.right += infoPtr->iconSize.cx; 02365 Icon.bottom += infoPtr->iconSize.cy; 02366 } 02367 } 02368 else /* LV_VIEW_SMALLICON, LV_VIEW_LIST or LV_VIEW_DETAILS */ 02369 { 02370 Icon.left = Box.left + state_width; 02371 02372 if (infoPtr->uView == LV_VIEW_DETAILS && lpLVItem->iSubItem == 0) 02373 { 02374 /* we need the indent in report mode */ 02375 assert(lpLVItem->mask & LVIF_INDENT); 02376 Icon.left += infoPtr->iconSize.cx * lpLVItem->iIndent + REPORT_MARGINX; 02377 } 02378 02379 Icon.top = Box.top; 02380 Icon.right = Icon.left; 02381 if (infoPtr->himlSmall && 02382 (!lpColumnInfo || lpLVItem->iSubItem == 0 || (lpColumnInfo->fmt & LVCFMT_IMAGE) || 02383 ((infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES) && lpLVItem->iImage != I_IMAGECALLBACK))) 02384 Icon.right += infoPtr->iconSize.cx; 02385 Icon.bottom = Icon.top + infoPtr->iconSize.cy; 02386 } 02387 if(lprcIcon) *lprcIcon = Icon; 02388 TRACE(" - icon=%s\n", wine_dbgstr_rect(&Icon)); 02389 02390 /* TODO: is this correct? */ 02391 if (lprcStateIcon) 02392 { 02393 lprcStateIcon->left = Icon.left - state_width; 02394 lprcStateIcon->right = Icon.left; 02395 lprcStateIcon->top = Icon.top; 02396 lprcStateIcon->bottom = lprcStateIcon->top + infoPtr->iconSize.cy; 02397 TRACE(" - state icon=%s\n", wine_dbgstr_rect(lprcStateIcon)); 02398 } 02399 } 02400 else Icon.right = 0; 02401 02402 /************************************************************/ 02403 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */ 02404 /************************************************************/ 02405 if (doLabel) 02406 { 02407 /* calculate how far to the right can the label stretch */ 02408 Label.right = Box.right; 02409 if (infoPtr->uView == LV_VIEW_DETAILS) 02410 { 02411 if (lpLVItem->iSubItem == 0) 02412 { 02413 /* we need a zero based rect here */ 02414 Label = lpColumnInfo->rcHeader; 02415 OffsetRect(&Label, -Label.left, 0); 02416 } 02417 } 02418 02419 if (lpLVItem->iSubItem || ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && infoPtr->uView == LV_VIEW_DETAILS)) 02420 { 02421 labelSize.cx = infoPtr->nItemWidth; 02422 labelSize.cy = infoPtr->nItemHeight; 02423 goto calc_label; 02424 } 02425 02426 /* we need the text in non owner draw mode */ 02427 assert(lpLVItem->mask & LVIF_TEXT); 02428 if (is_text(lpLVItem->pszText)) 02429 { 02430 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont; 02431 HDC hdc = GetDC(infoPtr->hwndSelf); 02432 HFONT hOldFont = SelectObject(hdc, hFont); 02433 UINT uFormat; 02434 RECT rcText; 02435 02436 /* compute rough rectangle where the label will go */ 02437 SetRectEmpty(&rcText); 02438 rcText.right = infoPtr->nItemWidth - TRAILING_LABEL_PADDING; 02439 rcText.bottom = infoPtr->nItemHeight; 02440 if (infoPtr->uView == LV_VIEW_ICON) 02441 rcText.bottom -= ICON_TOP_PADDING + infoPtr->iconSize.cy + ICON_BOTTOM_PADDING; 02442 02443 /* now figure out the flags */ 02444 if (infoPtr->uView == LV_VIEW_ICON) 02445 uFormat = oversizedBox ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS; 02446 else 02447 uFormat = LV_SL_DT_FLAGS; 02448 02449 DrawTextW (hdc, lpLVItem->pszText, -1, &rcText, uFormat | DT_CALCRECT); 02450 02451 if (rcText.right != rcText.left) 02452 labelSize.cx = min(rcText.right - rcText.left + TRAILING_LABEL_PADDING, infoPtr->nItemWidth); 02453 02454 labelSize.cy = rcText.bottom - rcText.top; 02455 02456 SelectObject(hdc, hOldFont); 02457 ReleaseDC(infoPtr->hwndSelf, hdc); 02458 } 02459 02460 calc_label: 02461 if (infoPtr->uView == LV_VIEW_ICON) 02462 { 02463 Label.left = Box.left + (infoPtr->nItemWidth - labelSize.cx) / 2; 02464 Label.top = Box.top + ICON_TOP_PADDING_HITABLE + 02465 infoPtr->iconSize.cy + ICON_BOTTOM_PADDING; 02466 Label.right = Label.left + labelSize.cx; 02467 Label.bottom = Label.top + infoPtr->nItemHeight; 02468 if (!oversizedBox && labelSize.cy > infoPtr->ntmHeight) 02469 { 02470 labelSize.cy = min(Box.bottom - Label.top, labelSize.cy); 02471 labelSize.cy /= infoPtr->ntmHeight; 02472 labelSize.cy = max(labelSize.cy, 1); 02473 labelSize.cy *= infoPtr->ntmHeight; 02474 } 02475 Label.bottom = Label.top + labelSize.cy + HEIGHT_PADDING; 02476 } 02477 else if (infoPtr->uView == LV_VIEW_DETAILS) 02478 { 02479 Label.left = Icon.right; 02480 Label.top = Box.top; 02481 Label.right = lpLVItem->iSubItem ? lpColumnInfo->rcHeader.right : 02482 lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left; 02483 Label.bottom = Label.top + infoPtr->nItemHeight; 02484 } 02485 else /* LV_VIEW_SMALLICON or LV_VIEW_LIST */ 02486 { 02487 Label.left = Icon.right; 02488 Label.top = Box.top; 02489 Label.right = min(Label.left + labelSize.cx, Label.right); 02490 Label.bottom = Label.top + infoPtr->nItemHeight; 02491 } 02492 02493 if (lprcLabel) *lprcLabel = Label; 02494 TRACE(" - label=%s\n", wine_dbgstr_rect(&Label)); 02495 } 02496 02497 /************************************************************/ 02498 /* compute SELECT bounding box */ 02499 /************************************************************/ 02500 if (doSelectBox) 02501 { 02502 if (infoPtr->uView == LV_VIEW_DETAILS) 02503 { 02504 SelectBox.left = Icon.left; 02505 SelectBox.top = Box.top; 02506 SelectBox.bottom = Box.bottom; 02507 02508 if (labelSize.cx) 02509 SelectBox.right = min(Label.left + labelSize.cx, Label.right); 02510 else 02511 SelectBox.right = min(Label.left + MAX_EMPTYTEXT_SELECT_WIDTH, Label.right); 02512 } 02513 else 02514 { 02515 UnionRect(&SelectBox, &Icon, &Label); 02516 } 02517 if (lprcSelectBox) *lprcSelectBox = SelectBox; 02518 TRACE(" - select box=%s\n", wine_dbgstr_rect(&SelectBox)); 02519 } 02520 02521 /* Fix the Box if necessary */ 02522 if (lprcBox) 02523 { 02524 if (oversizedBox) UnionRect(lprcBox, &Box, &Label); 02525 else *lprcBox = Box; 02526 } 02527 TRACE(" - box=%s\n", wine_dbgstr_rect(&Box)); 02528 } 02529 02530 /*** 02531 * DESCRIPTION: [INTERNAL] 02532 * 02533 * PARAMETER(S): 02534 * [I] infoPtr : valid pointer to the listview structure 02535 * [I] nItem : item number 02536 * [O] lprcBox : ptr to Box rectangle 02537 * 02538 * RETURN: 02539 * None. 02540 */ 02541 static void LISTVIEW_GetItemBox(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprcBox) 02542 { 02543 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' }; 02544 POINT Position, Origin; 02545 LVITEMW lvItem; 02546 02547 LISTVIEW_GetOrigin(infoPtr, &Origin); 02548 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position); 02549 02550 /* Be smart and try to figure out the minimum we have to do */ 02551 lvItem.mask = 0; 02552 if (infoPtr->uView == LV_VIEW_ICON && infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED)) 02553 lvItem.mask |= LVIF_TEXT; 02554 lvItem.iItem = nItem; 02555 lvItem.iSubItem = 0; 02556 lvItem.pszText = szDispText; 02557 lvItem.cchTextMax = DISP_TEXT_SIZE; 02558 if (lvItem.mask) LISTVIEW_GetItemW(infoPtr, &lvItem); 02559 if (infoPtr->uView == LV_VIEW_ICON) 02560 { 02561 lvItem.mask |= LVIF_STATE; 02562 lvItem.stateMask = LVIS_FOCUSED; 02563 lvItem.state = (lvItem.mask & LVIF_TEXT ? LVIS_FOCUSED : 0); 02564 } 02565 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprcBox, 0, 0, 0, 0); 02566 02567 if (infoPtr->uView == LV_VIEW_DETAILS && infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT && 02568 SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX, 0, 0)) 02569 { 02570 OffsetRect(lprcBox, Origin.x, Position.y + Origin.y); 02571 } 02572 else 02573 OffsetRect(lprcBox, Position.x + Origin.x, Position.y + Origin.y); 02574 } 02575 02576 /* LISTVIEW_MapIdToIndex helper */ 02577 static INT CALLBACK MapIdSearchCompare(LPVOID p1, LPVOID p2, LPARAM lParam) 02578 { 02579 ITEM_ID *id1 = (ITEM_ID*)p1; 02580 ITEM_ID *id2 = (ITEM_ID*)p2; 02581 02582 if (id1->id == id2->id) return 0; 02583 02584 return (id1->id < id2->id) ? -1 : 1; 02585 } 02586 02587 /*** 02588 * DESCRIPTION: 02589 * Returns the item index for id specified. 02590 * 02591 * PARAMETER(S): 02592 * [I] infoPtr : valid pointer to the listview structure 02593 * [I] iID : item id to get index for 02594 * 02595 * RETURN: 02596 * Item index, or -1 on failure. 02597 */ 02598 static INT LISTVIEW_MapIdToIndex(const LISTVIEW_INFO *infoPtr, UINT iID) 02599 { 02600 ITEM_ID ID; 02601 INT index; 02602 02603 TRACE("iID=%d\n", iID); 02604 02605 if (infoPtr->dwStyle & LVS_OWNERDATA) return -1; 02606 if (infoPtr->nItemCount == 0) return -1; 02607 02608 ID.id = iID; 02609 index = DPA_Search(infoPtr->hdpaItemIds, &ID, -1, MapIdSearchCompare, 0, DPAS_SORTED); 02610 02611 if (index != -1) 02612 { 02613 ITEM_ID *lpID = DPA_GetPtr(infoPtr->hdpaItemIds, index); 02614 return DPA_GetPtrIndex(infoPtr->hdpaItems, lpID->item); 02615 } 02616 02617 return -1; 02618 } 02619 02620 /*** 02621 * DESCRIPTION: 02622 * Returns the item id for index given. 02623 * 02624 * PARAMETER(S): 02625 * [I] infoPtr : valid pointer to the listview structure 02626 * [I] iItem : item index to get id for 02627 * 02628 * RETURN: 02629 * Item id. 02630 */ 02631 static DWORD LISTVIEW_MapIndexToId(const LISTVIEW_INFO *infoPtr, INT iItem) 02632 { 02633 ITEM_INFO *lpItem; 02634 HDPA hdpaSubItems; 02635 02636 TRACE("iItem=%d\n", iItem); 02637 02638 if (infoPtr->dwStyle & LVS_OWNERDATA) return -1; 02639 if (iItem < 0 || iItem >= infoPtr->nItemCount) return -1; 02640 02641 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, iItem); 02642 lpItem = DPA_GetPtr(hdpaSubItems, 0); 02643 02644 return lpItem->id->id; 02645 } 02646 02647 /*** 02648 * DESCRIPTION: 02649 * Returns the current icon position, and advances it along the top. 02650 * The returned position is not offset by Origin. 02651 * 02652 * PARAMETER(S): 02653 * [I] infoPtr : valid pointer to the listview structure 02654 * [O] lpPos : will get the current icon position 02655 * 02656 * RETURN: 02657 * None 02658 */ 02659 static void LISTVIEW_NextIconPosTop(LISTVIEW_INFO *infoPtr, LPPOINT lpPos) 02660 { 02661 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left; 02662 02663 *lpPos = infoPtr->currIconPos; 02664 02665 infoPtr->currIconPos.x += infoPtr->nItemWidth; 02666 if (infoPtr->currIconPos.x + infoPtr->nItemWidth <= nListWidth) return; 02667 02668 infoPtr->currIconPos.x = 0; 02669 infoPtr->currIconPos.y += infoPtr->nItemHeight; 02670 } 02671 02672 02673 /*** 02674 * DESCRIPTION: 02675 * Returns the current icon position, and advances it down the left edge. 02676 * The returned position is not offset by Origin. 02677 * 02678 * PARAMETER(S): 02679 * [I] infoPtr : valid pointer to the listview structure 02680 * [O] lpPos : will get the current icon position 02681 * 02682 * RETURN: 02683 * None 02684 */ 02685 static void LISTVIEW_NextIconPosLeft(LISTVIEW_INFO *infoPtr, LPPOINT lpPos) 02686 { 02687 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top; 02688 02689 *lpPos = infoPtr->currIconPos; 02690 02691 infoPtr->currIconPos.y += infoPtr->nItemHeight; 02692 if (infoPtr->currIconPos.y + infoPtr->nItemHeight <= nListHeight) return; 02693 02694 infoPtr->currIconPos.x += infoPtr->nItemWidth; 02695 infoPtr->currIconPos.y = 0; 02696 } 02697 02698 02699 /*** 02700 * DESCRIPTION: 02701 * Moves an icon to the specified position. 02702 * It takes care of invalidating the item, etc. 02703 * 02704 * PARAMETER(S): 02705 * [I] infoPtr : valid pointer to the listview structure 02706 * [I] nItem : the item to move 02707 * [I] lpPos : the new icon position 02708 * [I] isNew : flags the item as being new 02709 * 02710 * RETURN: 02711 * Success: TRUE 02712 * Failure: FALSE 02713 */ 02714 static BOOL LISTVIEW_MoveIconTo(const LISTVIEW_INFO *infoPtr, INT nItem, const POINT *lppt, BOOL isNew) 02715 { 02716 POINT old; 02717 02718 if (!isNew) 02719 { 02720 old.x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem); 02721 old.y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem); 02722 02723 if (lppt->x == old.x && lppt->y == old.y) return TRUE; 02724 LISTVIEW_InvalidateItem(infoPtr, nItem); 02725 } 02726 02727 /* Allocating a POINTER for every item is too resource intensive, 02728 * so we'll keep the (x,y) in different arrays */ 02729 if (!DPA_SetPtr(infoPtr->hdpaPosX, nItem, (void *)(LONG_PTR)lppt->x)) return FALSE; 02730 if (!DPA_SetPtr(infoPtr->hdpaPosY, nItem, (void *)(LONG_PTR)lppt->y)) return FALSE; 02731 02732 LISTVIEW_InvalidateItem(infoPtr, nItem); 02733 02734 return TRUE; 02735 } 02736 02737 /*** 02738 * DESCRIPTION: 02739 * Arranges listview items in icon display mode. 02740 * 02741 * PARAMETER(S): 02742 * [I] infoPtr : valid pointer to the listview structure 02743 * [I] nAlignCode : alignment code 02744 * 02745 * RETURN: 02746 * SUCCESS : TRUE 02747 * FAILURE : FALSE 02748 */ 02749 static BOOL LISTVIEW_Arrange(LISTVIEW_INFO *infoPtr, INT nAlignCode) 02750 { 02751 void (*next_pos)(LISTVIEW_INFO *, LPPOINT); 02752 POINT pos; 02753 INT i; 02754 02755 if (infoPtr->uView != LV_VIEW_ICON && infoPtr->uView != LV_VIEW_SMALLICON) return FALSE; 02756 02757 TRACE("nAlignCode=%d\n", nAlignCode); 02758 02759 if (nAlignCode == LVA_DEFAULT) 02760 { 02761 if (infoPtr->dwStyle & LVS_ALIGNLEFT) nAlignCode = LVA_ALIGNLEFT; 02762 else nAlignCode = LVA_ALIGNTOP; 02763 } 02764 02765 switch (nAlignCode) 02766 { 02767 case LVA_ALIGNLEFT: next_pos = LISTVIEW_NextIconPosLeft; break; 02768 case LVA_ALIGNTOP: next_pos = LISTVIEW_NextIconPosTop; break; 02769 case LVA_SNAPTOGRID: next_pos = LISTVIEW_NextIconPosTop; break; /* FIXME */ 02770 default: return FALSE; 02771 } 02772 02773 infoPtr->bAutoarrange = TRUE; 02774 infoPtr->currIconPos.x = infoPtr->currIconPos.y = 0; 02775 for (i = 0; i < infoPtr->nItemCount; i++) 02776 { 02777 next_pos(infoPtr, &pos); 02778 LISTVIEW_MoveIconTo(infoPtr, i, &pos, FALSE); 02779 } 02780 02781 return TRUE; 02782 } 02783 02784 /*** 02785 * DESCRIPTION: 02786 * Retrieves the bounding rectangle of all the items, not offset by Origin. 02787 * For LVS_REPORT always returns empty rectangle. 02788 * 02789 * PARAMETER(S): 02790 * [I] infoPtr : valid pointer to the listview structure 02791 * [O] lprcView : bounding rectangle 02792 * 02793 * RETURN: 02794 * SUCCESS : TRUE 02795 * FAILURE : FALSE 02796 */ 02797 static void LISTVIEW_GetAreaRect(const LISTVIEW_INFO *infoPtr, LPRECT lprcView) 02798 { 02799 INT i, x, y; 02800 02801 SetRectEmpty(lprcView); 02802 02803 switch (infoPtr->uView) 02804 { 02805 case LV_VIEW_ICON: 02806 case LV_VIEW_SMALLICON: 02807 for (i = 0; i < infoPtr->nItemCount; i++) 02808 { 02809 x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, i); 02810 y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, i); 02811 lprcView->right = max(lprcView->right, x); 02812 lprcView->bottom = max(lprcView->bottom, y); 02813 } 02814 if (infoPtr->nItemCount > 0) 02815 { 02816 lprcView->right += infoPtr->nItemWidth; 02817 lprcView->bottom += infoPtr->nItemHeight; 02818 } 02819 break; 02820 02821 case LV_VIEW_LIST: 02822 y = LISTVIEW_GetCountPerColumn(infoPtr); 02823 x = infoPtr->nItemCount / y; 02824 if (infoPtr->nItemCount % y) x++; 02825 lprcView->right = x * infoPtr->nItemWidth; 02826 lprcView->bottom = y * infoPtr->nItemHeight; 02827 break; 02828 } 02829 } 02830 02831 /*** 02832 * DESCRIPTION: 02833 * Retrieves the bounding rectangle of all the items. 02834 * 02835 * PARAMETER(S): 02836 * [I] infoPtr : valid pointer to the listview structure 02837 * [O] lprcView : bounding rectangle 02838 * 02839 * RETURN: 02840 * SUCCESS : TRUE 02841 * FAILURE : FALSE 02842 */ 02843 static BOOL LISTVIEW_GetViewRect(const LISTVIEW_INFO *infoPtr, LPRECT lprcView) 02844 { 02845 POINT ptOrigin; 02846 02847 TRACE("(lprcView=%p)\n", lprcView); 02848 02849 if (!lprcView) return FALSE; 02850 02851 LISTVIEW_GetAreaRect(infoPtr, lprcView); 02852 02853 if (infoPtr->uView != LV_VIEW_DETAILS) 02854 { 02855 LISTVIEW_GetOrigin(infoPtr, &ptOrigin); 02856 OffsetRect(lprcView, ptOrigin.x, ptOrigin.y); 02857 } 02858 02859 TRACE("lprcView=%s\n", wine_dbgstr_rect(lprcView)); 02860 02861 return TRUE; 02862 } 02863 02864 /*** 02865 * DESCRIPTION: 02866 * Retrieves the subitem pointer associated with the subitem index. 02867 * 02868 * PARAMETER(S): 02869 * [I] hdpaSubItems : DPA handle for a specific item 02870 * [I] nSubItem : index of subitem 02871 * 02872 * RETURN: 02873 * SUCCESS : subitem pointer 02874 * FAILURE : NULL 02875 */ 02876 static SUBITEM_INFO* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems, INT nSubItem) 02877 { 02878 SUBITEM_INFO *lpSubItem; 02879 INT i; 02880 02881 /* we should binary search here if need be */ 02882 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++) 02883 { 02884 lpSubItem = DPA_GetPtr(hdpaSubItems, i); 02885 if (lpSubItem->iSubItem == nSubItem) 02886 return lpSubItem; 02887 } 02888 02889 return NULL; 02890 } 02891 02892 02893 /*** 02894 * DESCRIPTION: 02895 * Calculates the desired item width. 02896 * 02897 * PARAMETER(S): 02898 * [I] infoPtr : valid pointer to the listview structure 02899 * 02900 * RETURN: 02901 * The desired item width. 02902 */ 02903 static INT LISTVIEW_CalculateItemWidth(const LISTVIEW_INFO *infoPtr) 02904 { 02905 INT nItemWidth = 0; 02906 02907 TRACE("uView=%d\n", infoPtr->uView); 02908 02909 if (infoPtr->uView == LV_VIEW_ICON) 02910 nItemWidth = infoPtr->iconSpacing.cx; 02911 else if (infoPtr->uView == LV_VIEW_DETAILS) 02912 { 02913 if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0) 02914 { 02915 RECT rcHeader; 02916 INT index; 02917 02918 index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX, 02919 DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, 0); 02920 02921 LISTVIEW_GetHeaderRect(infoPtr, index, &rcHeader); 02922 nItemWidth = rcHeader.right; 02923 } 02924 } 02925 else /* LV_VIEW_SMALLICON, or LV_VIEW_LIST */ 02926 { 02927 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' }; 02928 LVITEMW lvItem; 02929 INT i; 02930 02931 lvItem.mask = LVIF_TEXT; 02932 lvItem.iSubItem = 0; 02933 02934 for (i = 0; i < infoPtr->nItemCount; i++) 02935 { 02936 lvItem.iItem = i; 02937 lvItem.pszText = szDispText; 02938 lvItem.cchTextMax = DISP_TEXT_SIZE; 02939 if (LISTVIEW_GetItemW(infoPtr, &lvItem)) 02940 nItemWidth = max(LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE), 02941 nItemWidth); 02942 } 02943 02944 if (infoPtr->himlSmall) nItemWidth += infoPtr->iconSize.cx; 02945 if (infoPtr->himlState) nItemWidth += infoPtr->iconStateSize.cx; 02946 02947 nItemWidth = max(DEFAULT_COLUMN_WIDTH, nItemWidth + WIDTH_PADDING); 02948 } 02949 02950 return nItemWidth; 02951 } 02952 02953 /*** 02954 * DESCRIPTION: 02955 * Calculates the desired item height. 02956 * 02957 * PARAMETER(S): 02958 * [I] infoPtr : valid pointer to the listview structure 02959 * 02960 * RETURN: 02961 * The desired item height. 02962 */ 02963 static INT LISTVIEW_CalculateItemHeight(const LISTVIEW_INFO *infoPtr) 02964 { 02965 INT nItemHeight; 02966 02967 TRACE("uView=%d\n", infoPtr->uView); 02968 02969 if (infoPtr->uView == LV_VIEW_ICON) 02970 nItemHeight = infoPtr->iconSpacing.cy; 02971 else 02972 { 02973 nItemHeight = infoPtr->ntmHeight; 02974 if (infoPtr->uView == LV_VIEW_DETAILS && infoPtr->dwLvExStyle & LVS_EX_GRIDLINES) 02975 nItemHeight++; 02976 if (infoPtr->himlState) 02977 nItemHeight = max(nItemHeight, infoPtr->iconStateSize.cy); 02978 if (infoPtr->himlSmall) 02979 nItemHeight = max(nItemHeight, infoPtr->iconSize.cy); 02980 if (infoPtr->himlState || infoPtr->himlSmall) 02981 nItemHeight += HEIGHT_PADDING; 02982 if (infoPtr->nMeasureItemHeight > 0) 02983 nItemHeight = infoPtr->nMeasureItemHeight; 02984 } 02985 02986 return max(nItemHeight, 1); 02987 } 02988 02989 /*** 02990 * DESCRIPTION: 02991 * Updates the width, and height of an item. 02992 * 02993 * PARAMETER(S): 02994 * [I] infoPtr : valid pointer to the listview structure 02995 * 02996 * RETURN: 02997 * None. 02998 */ 02999 static inline void LISTVIEW_UpdateItemSize(LISTVIEW_INFO *infoPtr) 03000 { 03001 infoPtr->nItemWidth = LISTVIEW_CalculateItemWidth(infoPtr); 03002 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr); 03003 } 03004 03005 03006 /*** 03007 * DESCRIPTION: 03008 * Retrieves and saves important text metrics info for the current 03009 * Listview font. 03010 * 03011 * PARAMETER(S): 03012 * [I] infoPtr : valid pointer to the listview structure 03013 * 03014 */ 03015 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO *infoPtr) 03016 { 03017 HDC hdc = GetDC(infoPtr->hwndSelf); 03018 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont; 03019 HFONT hOldFont = SelectObject(hdc, hFont); 03020 TEXTMETRICW tm; 03021 SIZE sz; 03022 03023 if (GetTextMetricsW(hdc, &tm)) 03024 { 03025 infoPtr->ntmHeight = tm.tmHeight; 03026 infoPtr->ntmMaxCharWidth = tm.tmMaxCharWidth; 03027 } 03028 03029 if (GetTextExtentPoint32A(hdc, "...", 3, &sz)) 03030 infoPtr->nEllipsisWidth = sz.cx; 03031 03032 SelectObject(hdc, hOldFont); 03033 ReleaseDC(infoPtr->hwndSelf, hdc); 03034 03035 TRACE("tmHeight=%d\n", infoPtr->ntmHeight); 03036 } 03037 03038 /*** 03039 * DESCRIPTION: 03040 * A compare function for ranges 03041 * 03042 * PARAMETER(S) 03043 * [I] range1 : pointer to range 1; 03044 * [I] range2 : pointer to range 2; 03045 * [I] flags : flags 03046 * 03047 * RETURNS: 03048 * > 0 : if range 1 > range 2 03049 * < 0 : if range 2 > range 1 03050 * = 0 : if range intersects range 2 03051 */ 03052 static INT CALLBACK ranges_cmp(LPVOID range1, LPVOID range2, LPARAM flags) 03053 { 03054 INT cmp; 03055 03056 if (((RANGE*)range1)->upper <= ((RANGE*)range2)->lower) 03057 cmp = -1; 03058 else if (((RANGE*)range2)->upper <= ((RANGE*)range1)->lower) 03059 cmp = 1; 03060 else 03061 cmp = 0; 03062 03063 TRACE("range1=%s, range2=%s, cmp=%d\n", debugrange(range1), debugrange(range2), cmp); 03064 03065 return cmp; 03066 } 03067 03068 #define ranges_check(ranges, desc) if (TRACE_ON(listview)) ranges_assert(ranges, desc, __FILE__, __LINE__) 03069 03070 static void ranges_assert(RANGES ranges, LPCSTR desc, const char *file, int line) 03071 { 03072 INT i; 03073 RANGE *prev, *curr; 03074 03075 TRACE("*** Checking %s:%d:%s ***\n", file, line, desc); 03076 assert (ranges); 03077 assert (DPA_GetPtrCount(ranges->hdpa) >= 0); 03078 ranges_dump(ranges); 03079 if (DPA_GetPtrCount(ranges->hdpa) > 0) 03080 { 03081 prev = DPA_GetPtr(ranges->hdpa, 0); 03082 assert (prev->lower >= 0 && prev->lower < prev->upper); 03083 for (i = 1; i < DPA_GetPtrCount(ranges->hdpa); i++) 03084 { 03085 curr = DPA_GetPtr(ranges->hdpa, i); 03086 assert (prev->upper <= curr->lower); 03087 assert (curr->lower < curr->upper); 03088 prev = curr; 03089 } 03090 } 03091 TRACE("--- Done checking---\n"); 03092 } 03093 03094 static RANGES ranges_create(int count) 03095 { 03096 RANGES ranges = Alloc(sizeof(struct tagRANGES)); 03097 if (!ranges) return NULL; 03098 ranges->hdpa = DPA_Create(count); 03099 if (ranges->hdpa) return ranges; 03100 Free(ranges); 03101 return NULL; 03102 } 03103 03104 static void ranges_clear(RANGES ranges) 03105 { 03106 INT i; 03107 03108 for(i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++) 03109 Free(DPA_GetPtr(ranges->hdpa, i)); 03110 DPA_DeleteAllPtrs(ranges->hdpa); 03111 } 03112 03113 03114 static void ranges_destroy(RANGES ranges) 03115 { 03116 if (!ranges) return; 03117 ranges_clear(ranges); 03118 DPA_Destroy(ranges->hdpa); 03119 Free(ranges); 03120 } 03121 03122 static RANGES ranges_clone(RANGES ranges) 03123 { 03124 RANGES clone; 03125 INT i; 03126 03127 if (!(clone = ranges_create(DPA_GetPtrCount(ranges->hdpa)))) goto fail; 03128 03129 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++) 03130 { 03131 RANGE *newrng = Alloc(sizeof(RANGE)); 03132 if (!newrng) goto fail; 03133 *newrng = *((RANGE*)DPA_GetPtr(ranges->hdpa, i)); 03134 DPA_SetPtr(clone->hdpa, i, newrng); 03135 } 03136 return clone; 03137 03138 fail: 03139 TRACE ("clone failed\n"); 03140 ranges_destroy(clone); 03141 return NULL; 03142 } 03143 03144 static RANGES ranges_diff(RANGES ranges, RANGES sub) 03145 { 03146 INT i; 03147 03148 for (i = 0; i < DPA_GetPtrCount(sub->hdpa); i++) 03149 ranges_del(ranges, *((RANGE *)DPA_GetPtr(sub->hdpa, i))); 03150 03151 return ranges; 03152 } 03153 03154 static void ranges_dump(RANGES ranges) 03155 { 03156 INT i; 03157 03158 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++) 03159 TRACE(" %s\n", debugrange(DPA_GetPtr(ranges->hdpa, i))); 03160 } 03161 03162 static inline BOOL ranges_contain(RANGES ranges, INT nItem) 03163 { 03164 RANGE srchrng = { nItem, nItem + 1 }; 03165 03166 TRACE("(nItem=%d)\n", nItem); 03167 ranges_check(ranges, "before contain"); 03168 return DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED) != -1; 03169 } 03170 03171 static INT ranges_itemcount(RANGES ranges) 03172 { 03173 INT i, count = 0; 03174 03175 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++) 03176 { 03177 RANGE *sel = DPA_GetPtr(ranges->hdpa, i); 03178 count += sel->upper - sel->lower; 03179 } 03180 03181 return count; 03182 } 03183 03184 static BOOL ranges_shift(RANGES ranges, INT nItem, INT delta, INT nUpper) 03185 { 03186 RANGE srchrng = { nItem, nItem + 1 }, *chkrng; 03187 INT index; 03188 03189 index = DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER); 03190 if (index == -1) return TRUE; 03191 03192 for (; index < DPA_GetPtrCount(ranges->hdpa); index++) 03193 { 03194 chkrng = DPA_GetPtr(ranges->hdpa, index); 03195 if (chkrng->lower >= nItem) 03196 chkrng->lower = max(min(chkrng->lower + delta, nUpper - 1), 0); 03197 if (chkrng->upper > nItem) 03198 chkrng->upper = max(min(chkrng->upper + delta, nUpper), 0); 03199 } 03200 return TRUE; 03201 } 03202 03203 static BOOL ranges_add(RANGES ranges, RANGE range) 03204 { 03205 RANGE srchrgn; 03206 INT index; 03207 03208 TRACE("(%s)\n", debugrange(&range)); 03209 ranges_check(ranges, "before add"); 03210 03211 /* try find overlapping regions first */ 03212 srchrgn.lower = range.lower - 1; 03213 srchrgn.upper = range.upper + 1; 03214 index = DPA_Search(ranges->hdpa, &srchrgn, 0, ranges_cmp, 0, DPAS_SORTED); 03215 03216 if (index == -1) 03217 { 03218 RANGE *newrgn; 03219 03220 TRACE("Adding new range\n"); 03221 03222 /* create the brand new range to insert */ 03223 newrgn = Alloc(sizeof(RANGE)); 03224 if(!newrgn) goto fail; 03225 *newrgn = range; 03226 03227 /* figure out where to insert it */ 03228 index = DPA_Search(ranges->hdpa, newrgn, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER); 03229 TRACE("index=%d\n", index); 03230 if (index == -1) index = 0; 03231 03232 /* and get it over with */ 03233 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1) 03234 { 03235 Free(newrgn); 03236 goto fail; 03237 } 03238 } 03239 else 03240 { 03241 RANGE *chkrgn, *mrgrgn; 03242 INT fromindex, mergeindex; 03243 03244 chkrgn = DPA_GetPtr(ranges->hdpa, index); 03245 TRACE("Merge with %s @%d\n", debugrange(chkrgn), index); 03246 03247 chkrgn->lower = min(range.lower, chkrgn->lower); 03248 chkrgn->upper = max(range.upper, chkrgn->upper); 03249 03250 TRACE("New range %s @%d\n", debugrange(chkrgn), index); 03251 03252 /* merge now common ranges */ 03253 fromindex = 0; 03254 srchrgn.lower = chkrgn->lower - 1; 03255 srchrgn.upper = chkrgn->upper + 1; 03256 03257 do 03258 { 03259 mergeindex = DPA_Search(ranges->hdpa, &srchrgn, fromindex, ranges_cmp, 0, 0); 03260 if (mergeindex == -1) break; 03261 if (mergeindex == index) 03262 { 03263 fromindex = index + 1; 03264 continue; 03265 } 03266 03267 TRACE("Merge with index %i\n", mergeindex); 03268 03269 mrgrgn = DPA_GetPtr(ranges->hdpa, mergeindex); 03270 chkrgn->lower = min(chkrgn->lower, mrgrgn->lower); 03271 chkrgn->upper = max(chkrgn->upper, mrgrgn->upper); 03272 Free(mrgrgn); 03273 DPA_DeletePtr(ranges->hdpa, mergeindex); 03274 if (mergeindex < index) index --; 03275 } while(1); 03276 } 03277 03278 ranges_check(ranges, "after add"); 03279 return TRUE; 03280 03281 fail: 03282 ranges_check(ranges, "failed add"); 03283 return FALSE; 03284 } 03285 03286 static BOOL ranges_del(RANGES ranges, RANGE range) 03287 { 03288 RANGE *chkrgn; 03289 INT index; 03290 03291 TRACE("(%s)\n", debugrange(&range)); 03292 ranges_check(ranges, "before del"); 03293 03294 /* we don't use DPAS_SORTED here, since we need * 03295 * to find the first overlapping range */ 03296 index = DPA_Search(ranges->hdpa, &range, 0, ranges_cmp, 0, 0); 03297 while(index != -1) 03298 { 03299 chkrgn = DPA_GetPtr(ranges->hdpa, index); 03300 03301 TRACE("Matches range %s @%d\n", debugrange(chkrgn), index); 03302 03303 /* case 1: Same range */ 03304 if ( (chkrgn->upper == range.upper) && 03305 (chkrgn->lower == range.lower) ) 03306 { 03307 DPA_DeletePtr(ranges->hdpa, index); 03308 Free(chkrgn); 03309 break; 03310 } 03311 /* case 2: engulf */ 03312 else if ( (chkrgn->upper <= range.upper) && 03313 (chkrgn->lower >= range.lower) ) 03314 { 03315 DPA_DeletePtr(ranges->hdpa, index); 03316 Free(chkrgn); 03317 } 03318 /* case 3: overlap upper */ 03319 else if ( (chkrgn->upper <= range.upper) && 03320 (chkrgn->lower < range.lower) ) 03321 { 03322 chkrgn->upper = range.lower; 03323 } 03324 /* case 4: overlap lower */ 03325 else if ( (chkrgn->upper > range.upper) && 03326 (chkrgn->lower >= range.lower) ) 03327 { 03328 chkrgn->lower = range.upper; 03329 break; 03330 } 03331 /* case 5: fully internal */ 03332 else 03333 { 03334 RANGE *newrgn; 03335 03336 if (!(newrgn = Alloc(sizeof(RANGE)))) goto fail; 03337 newrgn->lower = chkrgn->lower; 03338 newrgn->upper = range.lower; 03339 chkrgn->lower = range.upper; 03340 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1) 03341 { 03342 Free(newrgn); 03343 goto fail; 03344 } 03345 break; 03346 } 03347 03348 index = DPA_Search(ranges->hdpa, &range, index, ranges_cmp, 0, 0); 03349 } 03350 03351 ranges_check(ranges, "after del"); 03352 return TRUE; 03353 03354 fail: 03355 ranges_check(ranges, "failed del"); 03356 return FALSE; 03357 } 03358 03359 /*** 03360 * DESCRIPTION: 03361 * Removes all selection ranges 03362 * 03363 * Parameters(s): 03364 * [I] infoPtr : valid pointer to the listview structure 03365 * [I] toSkip : item range to skip removing the selection 03366 * 03367 * RETURNS: 03368 * SUCCESS : TRUE 03369 * FAILURE : FALSE 03370 */ 03371 static BOOL LISTVIEW_DeselectAllSkipItems(LISTVIEW_INFO *infoPtr, RANGES toSkip) 03372 { 03373 LVITEMW lvItem; 03374 ITERATOR i; 03375 RANGES clone; 03376 03377 TRACE("()\n"); 03378 03379 lvItem.state = 0; 03380 lvItem.stateMask = LVIS_SELECTED; 03381 03382 /* need to clone the DPA because callbacks can change it */ 03383 if (!(clone = ranges_clone(infoPtr->selectionRanges))) return FALSE; 03384 iterator_rangesitems(&i, ranges_diff(clone, toSkip)); 03385 while(iterator_next(&i)) 03386 LISTVIEW_SetItemState(infoPtr, i.nItem, &lvItem); 03387 /* note that the iterator destructor will free the cloned range */ 03388 iterator_destroy(&i); 03389 03390 return TRUE; 03391 } 03392 03393 static inline BOOL LISTVIEW_DeselectAllSkipItem(LISTVIEW_INFO *infoPtr, INT nItem) 03394 { 03395 RANGES toSkip; 03396 03397 if (!(toSkip = ranges_create(1))) return FALSE; 03398 if (nItem != -1) ranges_additem(toSkip, nItem); 03399 LISTVIEW_DeselectAllSkipItems(infoPtr, toSkip); 03400 ranges_destroy(toSkip); 03401 return TRUE; 03402 } 03403 03404 static inline BOOL LISTVIEW_DeselectAll(LISTVIEW_INFO *infoPtr) 03405 { 03406 return LISTVIEW_DeselectAllSkipItem(infoPtr, -1); 03407 } 03408 03409 /*** 03410 * DESCRIPTION: 03411 * Retrieves the number of items that are marked as selected. 03412 * 03413 * PARAMETER(S): 03414 * [I] infoPtr : valid pointer to the listview structure 03415 * 03416 * RETURN: 03417 * Number of items selected. 03418 */ 03419 static INT LISTVIEW_GetSelectedCount(const LISTVIEW_INFO *infoPtr) 03420 { 03421 INT nSelectedCount = 0; 03422 03423 if (infoPtr->uCallbackMask & LVIS_SELECTED) 03424 { 03425 INT i; 03426 for (i = 0; i < infoPtr->nItemCount; i++) 03427 { 03428 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED)) 03429 nSelectedCount++; 03430 } 03431 } 03432 else 03433 nSelectedCount = ranges_itemcount(infoPtr->selectionRanges); 03434 03435 TRACE("nSelectedCount=%d\n", nSelectedCount); 03436 return nSelectedCount; 03437 } 03438 03439 /*** 03440 * DESCRIPTION: 03441 * Manages the item focus. 03442 * 03443 * PARAMETER(S): 03444 * [I] infoPtr : valid pointer to the listview structure 03445 * [I] nItem : item index 03446 * 03447 * RETURN: 03448 * TRUE : focused item changed 03449 * FALSE : focused item has NOT changed 03450 */ 03451 static inline BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem) 03452 { 03453 INT oldFocus = infoPtr->nFocusedItem; 03454 LVITEMW lvItem; 03455 03456 if (nItem == infoPtr->nFocusedItem) return FALSE; 03457 03458 lvItem.state = nItem == -1 ? 0 : LVIS_FOCUSED; 03459 lvItem.stateMask = LVIS_FOCUSED; 03460 LISTVIEW_SetItemState(infoPtr, nItem == -1 ? infoPtr->nFocusedItem : nItem, &lvItem); 03461 03462 return oldFocus != infoPtr->nFocusedItem; 03463 } 03464 03465 /* Helper function for LISTVIEW_ShiftIndices *only* */ 03466 static INT shift_item(const LISTVIEW_INFO *infoPtr, INT nShiftItem, INT nItem, INT direction) 03467 { 03468 if (nShiftItem < nItem) return nShiftItem; 03469 03470 if (nShiftItem > nItem) return nShiftItem + direction; 03471 03472 if (direction > 0) return nShiftItem + direction; 03473 03474 return min(nShiftItem, infoPtr->nItemCount - 1); 03475 } 03476 03489 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction) 03490 { 03491 INT nNewFocus; 03492 BOOL bOldChange; 03493 03494 /* temporarily disable change notification while shifting items */ 03495 bOldChange = infoPtr->bDoChangeNotify; 03496 infoPtr->bDoChangeNotify = FALSE; 03497 03498 TRACE("Shifting %iu, %i steps\n", nItem, direction); 03499 03500 ranges_shift(infoPtr->selectionRanges, nItem, direction, infoPtr->nItemCount); 03501 03502 assert(abs(direction) == 1); 03503 03504 infoPtr->nSelectionMark = shift_item(infoPtr, infoPtr->nSelectionMark, nItem, direction); 03505 03506 nNewFocus = shift_item(infoPtr, infoPtr->nFocusedItem, nItem, direction); 03507 if (nNewFocus != infoPtr->nFocusedItem) 03508 LISTVIEW_SetItemFocus(infoPtr, nNewFocus); 03509 03510 /* But we are not supposed to modify nHotItem! */ 03511 03512 infoPtr->bDoChangeNotify = bOldChange; 03513 } 03514 03515 03527 static BOOL LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem) 03528 { 03529 INT nFirst = min(infoPtr->nSelectionMark, nItem); 03530 INT nLast = max(infoPtr->nSelectionMark, nItem); 03531 HWND hwndSelf = infoPtr->hwndSelf; 03532 NMLVODSTATECHANGE nmlv; 03533 LVITEMW item; 03534 BOOL bOldChange; 03535 INT i; 03536 03537 /* Temporarily disable change notification 03538 * If the control is LVS_OWNERDATA, we need to send 03539 * only one LVN_ODSTATECHANGED notification. 03540 * See MSDN documentation for LVN_ITEMCHANGED. 03541 */ 03542 bOldChange = infoPtr->bDoChangeNotify; 03543 if (infoPtr->dwStyle & LVS_OWNERDATA) infoPtr->bDoChangeNotify = FALSE; 03544 03545 if (nFirst == -1) nFirst = nItem; 03546 03547 item.state = LVIS_SELECTED; 03548 item.stateMask = LVIS_SELECTED; 03549 03550 for (i = nFirst; i <= nLast; i++) 03551 LISTVIEW_SetItemState(infoPtr,i,&item); 03552 03553 ZeroMemory(&nmlv, sizeof(nmlv)); 03554 nmlv.iFrom = nFirst; 03555 nmlv.iTo = nLast; 03556 nmlv.uNewState = 0; 03557 nmlv.uOldState = item.state; 03558 03559 notify_hdr(infoPtr, LVN_ODSTATECHANGED, (LPNMHDR)&nmlv); 03560 if (!IsWindow(hwndSelf)) 03561 return FALSE; 03562 infoPtr->bDoChangeNotify = bOldChange; 03563 return TRUE; 03564 } 03565 03566 03567 /*** 03568 * DESCRIPTION: 03569 * Sets a single group selection. 03570 * 03571 * PARAMETER(S): 03572 * [I] infoPtr : valid pointer to the listview structure 03573 * [I] nItem : item index 03574 * 03575 * RETURN: 03576 * None 03577 */ 03578 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem) 03579 { 03580 RANGES selection; 03581 LVITEMW item; 03582 ITERATOR i; 03583 BOOL bOldChange; 03584 03585 if (!(selection = ranges_create(100))) return; 03586 03587 item.state = LVIS_SELECTED; 03588 item.stateMask = LVIS_SELECTED; 03589 03590 if ((infoPtr->uView == LV_VIEW_LIST) || (infoPtr->uView == LV_VIEW_DETAILS)) 03591 { 03592 if (infoPtr->nSelectionMark == -1) 03593 { 03594 infoPtr->nSelectionMark = nItem; 03595 ranges_additem(selection, nItem); 03596 } 03597 else 03598 { 03599 RANGE sel; 03600 03601 sel.lower = min(infoPtr->nSelectionMark, nItem); 03602 sel.upper = max(infoPtr->nSelectionMark, nItem) + 1; 03603 ranges_add(selection, sel); 03604 } 03605 } 03606 else 03607 { 03608 RECT rcItem, rcSel, rcSelMark; 03609 POINT ptItem; 03610 03611 rcItem.left = LVIR_BOUNDS; 03612 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return; 03613 rcSelMark.left = LVIR_BOUNDS; 03614 if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) return; 03615 UnionRect(&rcSel, &rcItem, &rcSelMark); 03616 iterator_frameditems(&i, infoPtr, &rcSel); 03617 while(iterator_next(&i)) 03618 { 03619 LISTVIEW_GetItemPosition(infoPtr, i.nItem, &ptItem); 03620 if (PtInRect(&rcSel, ptItem)) ranges_additem(selection, i.nItem); 03621 } 03622 iterator_destroy(&i); 03623 } 03624 03625 /* disable per item notifications on LVS_OWNERDATA style 03626 FIXME: single LVN_ODSTATECHANGED should be used */ 03627 bOldChange = infoPtr->bDoChangeNotify; 03628 if (infoPtr->dwStyle & LVS_OWNERDATA) infoPtr->bDoChangeNotify = FALSE; 03629 03630 LISTVIEW_DeselectAllSkipItems(infoPtr, selection); 03631 03632 03633 iterator_rangesitems(&i, selection); 03634 while(iterator_next(&i)) 03635 LISTVIEW_SetItemState(infoPtr, i.nItem, &item); 03636 /* this will also destroy the selection */ 03637 iterator_destroy(&i); 03638 03639 infoPtr->bDoChangeNotify = bOldChange; 03640 03641 LISTVIEW_SetItemFocus(infoPtr, nItem); 03642 } 03643 03644 /*** 03645 * DESCRIPTION: 03646 * Sets a single selection. 03647 * 03648 * PARAMETER(S): 03649 * [I] infoPtr : valid pointer to the listview structure 03650 * [I] nItem : item index 03651 * 03652 * RETURN: 03653 * None 03654 */ 03655 static void LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem) 03656 { 03657 LVITEMW lvItem; 03658 03659 TRACE("nItem=%d\n", nItem); 03660 03661 LISTVIEW_DeselectAllSkipItem(infoPtr, nItem); 03662 03663 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED; 03664 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED; 03665 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem); 03666 03667 infoPtr->nSelectionMark = nItem; 03668 } 03669 03670 /*** 03671 * DESCRIPTION: 03672 * Set selection(s) with keyboard. 03673 * 03674 * PARAMETER(S): 03675 * [I] infoPtr : valid pointer to the listview structure 03676 * [I] nItem : item index 03677 * [I] space : VK_SPACE code sent 03678 * 03679 * RETURN: 03680 * SUCCESS : TRUE (needs to be repainted) 03681 * FAILURE : FALSE (nothing has changed) 03682 */ 03683 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem, BOOL space) 03684 { 03685 /* FIXME: pass in the state */ 03686 WORD wShift = HIWORD(GetKeyState(VK_SHIFT)); 03687 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL)); 03688 BOOL bResult = FALSE; 03689 03690 TRACE("nItem=%d, wShift=%d, wCtrl=%d\n", nItem, wShift, wCtrl); 03691 if ((nItem >= 0) && (nItem < infoPtr->nItemCount)) 03692 { 03693 bResult = TRUE; 03694 03695 if (infoPtr->dwStyle & LVS_SINGLESEL || (wShift == 0 && wCtrl == 0)) 03696 LISTVIEW_SetSelection(infoPtr, nItem); 03697 else 03698 { 03699 if (wShift) 03700 LISTVIEW_SetGroupSelection(infoPtr, nItem); 03701 else if (wCtrl) 03702 { 03703 LVITEMW lvItem; 03704 lvItem.state = ~LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED); 03705 lvItem.stateMask = LVIS_SELECTED; 03706 if (space) 03707 { 03708 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem); 03709 if (lvItem.state & LVIS_SELECTED) 03710 infoPtr->nSelectionMark = nItem; 03711 } 03712 bResult = LISTVIEW_SetItemFocus(infoPtr, nItem); 03713 } 03714 } 03715 LISTVIEW_EnsureVisible(infoPtr, nItem, FALSE); 03716 } 03717 03718 UpdateWindow(infoPtr->hwndSelf); /* update client area */ 03719 return bResult; 03720 } 03721 03722 static BOOL LISTVIEW_GetItemAtPt(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, POINT pt) 03723 { 03724 LVHITTESTINFO lvHitTestInfo; 03725 03726 ZeroMemory(&lvHitTestInfo, sizeof(lvHitTestInfo)); 03727 lvHitTestInfo.pt.x = pt.x; 03728 lvHitTestInfo.pt.y = pt.y; 03729 03730 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE); 03731 03732 lpLVItem->mask = LVIF_PARAM; 03733 lpLVItem->iItem = lvHitTestInfo.iItem; 03734 lpLVItem->iSubItem = 0; 03735 03736 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE); 03737 } 03738 03739 static inline BOOL LISTVIEW_IsHotTracking(const LISTVIEW_INFO *infoPtr) 03740 { 03741 return ((infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) || 03742 (infoPtr->dwLvExStyle & LVS_EX_ONECLICKACTIVATE) || 03743 (infoPtr->dwLvExStyle & LVS_EX_TWOCLICKACTIVATE)); 03744 } 03745 03746 /*** 03747 * DESCRIPTION: 03748 * Called when the mouse is being actively tracked and has hovered for a specified 03749 * amount of time 03750 * 03751 * PARAMETER(S): 03752 * [I] infoPtr : valid pointer to the listview structure 03753 * [I] fwKeys : key indicator 03754 * [I] x,y : mouse position 03755 * 03756 * RETURN: 03757 * 0 if the message was processed, non-zero if there was an error 03758 * 03759 * INFO: 03760 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains 03761 * over the item for a certain period of time. 03762 * 03763 */ 03764 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, INT x, INT y) 03765 { 03766 NMHDR hdr; 03767 03768 if (notify_hdr(infoPtr, NM_HOVER, &hdr)) return 0; 03769 03770 if (LISTVIEW_IsHotTracking(infoPtr)) 03771 { 03772 LVITEMW item; 03773 POINT pt; 03774 03775 pt.x = x; 03776 pt.y = y; 03777 03778 if (LISTVIEW_GetItemAtPt(infoPtr, &item, pt)) 03779 LISTVIEW_SetSelection(infoPtr, item.iItem); 03780 03781 SetFocus(infoPtr->hwndSelf); 03782 } 03783 03784 return 0; 03785 } 03786 03787 #define SCROLL_LEFT 0x1 03788 #define SCROLL_RIGHT 0x2 03789 #define SCROLL_UP 0x4 03790 #define SCROLL_DOWN 0x8 03791 03792 /*** 03793 * DESCRIPTION: 03794 * Utility routine to draw and highlight items within a marquee selection rectangle. 03795 * 03796 * PARAMETER(S): 03797 * [I] infoPtr : valid pointer to the listview structure 03798 * [I] coords_orig : original co-ordinates of the cursor 03799 * [I] coords_offs : offsetted coordinates of the cursor 03800 * [I] offset : offset amount 03801 * [I] scroll : Bitmask of which directions we should scroll, if at all 03802 * 03803 * RETURN: 03804 * None. 03805 */ 03806 static void LISTVIEW_MarqueeHighlight(LISTVIEW_INFO *infoPtr, const POINT *coords_orig, 03807 const POINT *coords_offs, const POINT *offset, 03808 INT scroll) 03809 { 03810 BOOL controlDown = FALSE; 03811 LVITEMW item; 03812 ITERATOR old_elems, new_elems; 03813 RECT rect; 03814 03815 if (coords_offs->x > infoPtr->marqueeOrigin.x) 03816 { 03817 rect.left = infoPtr->marqueeOrigin.x; 03818 rect.right = coords_offs->x; 03819 } 03820 else 03821 { 03822 rect.left = coords_offs->x; 03823 rect.right = infoPtr->marqueeOrigin.x; 03824 } 03825 03826 if (coords_offs->y > infoPtr->marqueeOrigin.y) 03827 { 03828 rect.top = infoPtr->marqueeOrigin.y; 03829 rect.bottom = coords_offs->y; 03830 } 03831 else 03832 { 03833 rect.top = coords_offs->y; 03834 rect.bottom = infoPtr->marqueeOrigin.y; 03835 } 03836 03837 /* Cancel out the old marquee rectangle and draw the new one */ 03838 LISTVIEW_InvalidateRect(infoPtr, &infoPtr->marqueeDrawRect); 03839 03840 /* Scroll by the appropriate distance if applicable - speed up scrolling as 03841 the cursor is further away */ 03842 03843 if ((scroll & SCROLL_LEFT) && (coords_orig->x <= 0)) 03844 LISTVIEW_Scroll(infoPtr, coords_orig->x, 0); 03845 03846 if ((scroll & SCROLL_RIGHT) && (coords_orig->x >= infoPtr->rcList.right)) 03847 LISTVIEW_Scroll(infoPtr, (coords_orig->x - infoPtr->rcList.right), 0); 03848 03849 if ((scroll & SCROLL_UP) && (coords_orig->y <= 0)) 03850 LISTVIEW_Scroll(infoPtr, 0, coords_orig->y); 03851 03852 if ((scroll & SCROLL_DOWN) && (coords_orig->y >= infoPtr->rcList.bottom)) 03853 LISTVIEW_Scroll(infoPtr, 0, (coords_orig->y - infoPtr->rcList.bottom)); 03854 03855 iterator_frameditems_absolute(&old_elems, infoPtr, &infoPtr->marqueeRect); 03856 03857 CopyRect(&infoPtr->marqueeRect, &rect); 03858 03859 CopyRect(&infoPtr->marqueeDrawRect, &rect); 03860 OffsetRect(&infoPtr->marqueeDrawRect, offset->x, offset->y); 03861 03862 iterator_frameditems_absolute(&new_elems, infoPtr, &infoPtr->marqueeRect); 03863 iterator_remove_common_items(&old_elems, &new_elems); 03864 03865 /* Iterate over no longer selected items */ 03866 while (iterator_next(&old_elems)) 03867 { 03868 if (old_elems.nItem > -1) 03869 { 03870 if (LISTVIEW_GetItemState(infoPtr, old_elems.nItem, LVIS_SELECTED) == LVIS_SELECTED) 03871 item.state = 0; 03872 else 03873 item.state = LVIS_SELECTED; 03874 03875 item.stateMask = LVIS_SELECTED; 03876 03877 LISTVIEW_SetItemState(infoPtr, old_elems.nItem, &item); 03878 } 03879 } 03880 iterator_destroy(&old_elems); 03881 03882 03883 /* Iterate over newly selected items */ 03884 if (GetKeyState(VK_CONTROL) & 0x8000) 03885 controlDown = TRUE; 03886 03887 while (iterator_next(&new_elems)) 03888 { 03889 if (new_elems.nItem > -1) 03890 { 03891 /* If CTRL is pressed, invert. If not, always select the item. */ 03892 if ((controlDown) && (LISTVIEW_GetItemState(infoPtr, new_elems.nItem, LVIS_SELECTED))) 03893 item.state = 0; 03894 else 03895 item.state = LVIS_SELECTED; 03896 03897 item.stateMask = LVIS_SELECTED; 03898 03899 LISTVIEW_SetItemState(infoPtr, new_elems.nItem, &item); 03900 } 03901 } 03902 iterator_destroy(&new_elems); 03903 03904 LISTVIEW_InvalidateRect(infoPtr, &rect); 03905 } 03906 03907 /*** 03908 * DESCRIPTION: 03909 * Called when we are in a marquee selection that involves scrolling the listview (ie, 03910 * the cursor is outside the bounds of the client area). This is a TIMERPROC. 03911 * 03912 * PARAMETER(S): 03913 * [I] hwnd : Handle to the listview 03914 * [I] uMsg : WM_TIMER (ignored) 03915 * [I] idEvent : The timer ID interpreted as a pointer to a LISTVIEW_INFO struct 03916 * [I] dwTimer : The elapsed time (ignored) 03917 * 03918 * RETURN: 03919 * None. 03920 */ 03921 static VOID CALLBACK LISTVIEW_ScrollTimer(HWND hWnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime) 03922 { 03923 LISTVIEW_INFO *infoPtr; 03924 SCROLLINFO scrollInfo; 03925 POINT coords_orig; 03926 POINT coords_offs; 03927 POINT offset; 03928 INT scroll = 0; 03929 03930 infoPtr = (LISTVIEW_INFO *) idEvent; 03931 03932 if (!infoPtr) 03933 return; 03934 03935 /* Get the current cursor position and convert to client coordinates */ 03936 GetCursorPos(&coords_orig); 03937 ScreenToClient(hWnd, &coords_orig); 03938 03939 /* Ensure coordinates are within client bounds */ 03940 coords_offs.x = max(min(coords_orig.x, infoPtr->rcList.right), 0); 03941 coords_offs.y = max(min(coords_orig.y, infoPtr->rcList.bottom), 0); 03942 03943 /* Get offset */ 03944 LISTVIEW_GetOrigin(infoPtr, &offset); 03945 03946 /* Offset coordinates by the appropriate amount */ 03947 coords_offs.x -= offset.x; 03948 coords_offs.y -= offset.y; 03949 03950 scrollInfo.cbSize = sizeof(SCROLLINFO); 03951 scrollInfo.fMask = SIF_ALL; 03952 03953 /* Work out in which directions we can scroll */ 03954 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) 03955 { 03956 if (scrollInfo.nPos != scrollInfo.nMin) 03957 scroll |= SCROLL_UP; 03958 03959 if (((scrollInfo.nPage + scrollInfo.nPos) - 1) != scrollInfo.nMax) 03960 scroll |= SCROLL_DOWN; 03961 } 03962 03963 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) 03964 { 03965 if (scrollInfo.nPos != scrollInfo.nMin) 03966 scroll |= SCROLL_LEFT; 03967 03968 if (((scrollInfo.nPage + scrollInfo.nPos) - 1) != scrollInfo.nMax) 03969 scroll |= SCROLL_RIGHT; 03970 } 03971 03972 if (((coords_orig.x <= 0) && (scroll & SCROLL_LEFT)) || 03973 ((coords_orig.y <= 0) && (scroll & SCROLL_UP)) || 03974 ((coords_orig.x >= infoPtr->rcList.right) && (scroll & SCROLL_RIGHT)) || 03975 ((coords_orig.y >= infoPtr->rcList.bottom) && (scroll & SCROLL_DOWN))) 03976 { 03977 LISTVIEW_MarqueeHighlight(infoPtr, &coords_orig, &coords_offs, &offset, scroll); 03978 } 03979 } 03980 03981 /*** 03982 * DESCRIPTION: 03983 * Called whenever WM_MOUSEMOVE is received. 03984 * 03985 * PARAMETER(S): 03986 * [I] infoPtr : valid pointer to the listview structure 03987 * [I] fwKeys : key indicator 03988 * [I] x,y : mouse position 03989 * 03990 * RETURN: 03991 * 0 if the message is processed, non-zero if there was an error 03992 */ 03993 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, INT x, INT y) 03994 { 03995 if (!(fwKeys & MK_LBUTTON)) 03996 infoPtr->bLButtonDown = FALSE; 03997 03998 if (infoPtr->bLButtonDown) 03999 { 04000 POINT tmp; 04001 RECT rect; 04002 LVHITTESTINFO lvHitTestInfo; 04003 WORD wDragWidth = GetSystemMetrics(SM_CXDRAG); 04004 WORD wDragHeight= GetSystemMetrics(SM_CYDRAG); 04005 04006 if (infoPtr->bMarqueeSelect) 04007 { 04008 POINT coords_orig; 04009 POINT coords_offs; 04010 POINT offset; 04011 04012 coords_orig.x = x; 04013 coords_orig.y = y; 04014 04015 /* Get offset */ 04016 LISTVIEW_GetOrigin(infoPtr, &offset); 04017 04018 /* Ensure coordinates are within client bounds */ 04019 coords_offs.x = max(min(x, infoPtr->rcList.right), 0); 04020 coords_offs.y = max(min(y, infoPtr->rcList.bottom), 0); 04021 04022 /* Offset coordinates by the appropriate amount */ 04023 coords_offs.x -= offset.x; 04024 coords_offs.y -= offset.y; 04025 04026 /* Enable the timer if we're going outside our bounds, in case the user doesn't 04027 move the mouse again */ 04028 04029 if ((x <= 0) || (y <= 0) || (x >= infoPtr->rcList.right) || 04030 (y >= infoPtr->rcList.bottom)) 04031 { 04032 if (!infoPtr->bScrolling) 04033 { 04034 infoPtr->bScrolling = TRUE; 04035 SetTimer(infoPtr->hwndSelf, (UINT_PTR) infoPtr, 1, LISTVIEW_ScrollTimer); 04036 } 04037 } 04038 else 04039 { 04040 infoPtr->bScrolling = FALSE; 04041 KillTimer(infoPtr->hwndSelf, (UINT_PTR) infoPtr); 04042 } 04043 04044 LISTVIEW_MarqueeHighlight(infoPtr, &coords_orig, &coords_offs, &offset, 0); 04045 return 0; 04046 } 04047 04048 rect.left = infoPtr->ptClickPos.x - wDragWidth; 04049 rect.right = infoPtr->ptClickPos.x + wDragWidth; 04050 rect.top = infoPtr->ptClickPos.y - wDragHeight; 04051 rect.bottom = infoPtr->ptClickPos.y + wDragHeight; 04052 04053 tmp.x = x; 04054 tmp.y = y; 04055 04056 lvHitTestInfo.pt = tmp; 04057 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE); 04058 04059 /* reset item marker */ 04060 if (infoPtr->nLButtonDownItem != lvHitTestInfo.iItem) 04061 infoPtr->nLButtonDownItem = -1; 04062 04063 if (!PtInRect(&rect, tmp)) 04064 { 04065 /* this path covers the following: 04066 1. WM_LBUTTONDOWN over selected item (sets focus on it) 04067 2. change focus with keys 04068 3. move mouse over item from step 1 selects it and moves focus on it */ 04069 if (infoPtr->nLButtonDownItem != -1 && 04070 !LISTVIEW_GetItemState(infoPtr, infoPtr->nLButtonDownItem, LVIS_SELECTED)) 04071 { 04072 LVITEMW lvItem; 04073 04074 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED; 04075 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED; 04076 04077 LISTVIEW_SetItemState(infoPtr, infoPtr->nLButtonDownItem, &lvItem); 04078 infoPtr->nLButtonDownItem = -1; 04079 } 04080 04081 if (!infoPtr->bDragging) 04082 { 04083 lvHitTestInfo.pt = infoPtr->ptClickPos; 04084 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE); 04085 04086 /* If the click is outside the range of an item, begin a 04087 highlight. If not, begin an item drag. */ 04088 if (lvHitTestInfo.iItem == -1) 04089 { 04090 NMHDR hdr; 04091 04092 /* If we're allowing multiple selections, send notification. 04093 If return value is non-zero, cancel. */ 04094 if (!(infoPtr->dwStyle & LVS_SINGLESEL) && (notify_hdr(infoPtr, LVN_MARQUEEBEGIN, &hdr) == 0)) 04095 { 04096 /* Store the absolute coordinates of the click */ 04097 POINT offset; 04098 LISTVIEW_GetOrigin(infoPtr, &offset); 04099 04100 infoPtr->marqueeOrigin.x = infoPtr->ptClickPos.x - offset.x; 04101 infoPtr->marqueeOrigin.y = infoPtr->ptClickPos.y - offset.y; 04102 04103 /* Begin selection and capture mouse */ 04104 infoPtr->bMarqueeSelect = TRUE; 04105 SetCapture(infoPtr->hwndSelf); 04106 } 04107 } 04108 else 04109 { 04110 NMLISTVIEW nmlv; 04111 04112 ZeroMemory(&nmlv, sizeof(nmlv)); 04113 nmlv.iItem = lvHitTestInfo.iItem; 04114 nmlv.ptAction = infoPtr->ptClickPos; 04115 04116 notify_listview(infoPtr, LVN_BEGINDRAG, &nmlv); 04117 infoPtr->bDragging = TRUE; 04118 } 04119 } 04120 04121 return 0; 04122 } 04123 } 04124 04125 /* see if we are supposed to be tracking mouse hovering */ 04126 if (LISTVIEW_IsHotTracking(infoPtr)) { 04127 TRACKMOUSEEVENT trackinfo; 04128 04129 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT); 04130 trackinfo.dwFlags = TME_QUERY; 04131 04132 /* see if we are already tracking this hwnd */ 04133 _TrackMouseEvent(&trackinfo); 04134 04135 if(!(trackinfo.dwFlags & TME_HOVER) || trackinfo.hwndTrack != infoPtr->hwndSelf) { 04136 trackinfo.dwFlags = TME_HOVER; 04137 trackinfo.dwHoverTime = infoPtr->dwHoverTime; 04138 trackinfo.hwndTrack = infoPtr->hwndSelf; 04139 04140 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */ 04141 _TrackMouseEvent(&trackinfo); 04142 } 04143 } 04144 04145 return 0; 04146 } 04147 04148 04149 /*** 04150 * Tests whether the item is assignable to a list with style lStyle 04151 */ 04152 static inline BOOL is_assignable_item(const LVITEMW *lpLVItem, LONG lStyle) 04153 { 04154 if ( (lpLVItem->mask & LVIF_TEXT) && 04155 (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) && 04156 (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE; 04157 04158 return TRUE; 04159 } 04160 04161 04162 /*** 04163 * DESCRIPTION: 04164 * Helper for LISTVIEW_SetItemT *only*: sets item attributes. 04165 * 04166 * PARAMETER(S): 04167 * [I] infoPtr : valid pointer to the listview structure 04168 * [I] lpLVItem : valid pointer to new item attributes 04169 * [I] isNew : the item being set is being inserted 04170 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI 04171 * [O] bChanged : will be set to TRUE if the item really changed 04172 * 04173 * RETURN: 04174 * SUCCESS : TRUE 04175 * FAILURE : FALSE 04176 */ 04177 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isNew, BOOL isW, BOOL *bChanged) 04178 { 04179 ITEM_INFO *lpItem; 04180 NMLISTVIEW nmlv; 04181 UINT uChanged = 0; 04182 LVITEMW item; 04183 /* stateMask is ignored for LVM_INSERTITEM */ 04184 UINT stateMask = isNew ? ~0 : lpLVItem->stateMask; 04185 04186 TRACE("()\n"); 04187 04188 assert(lpLVItem->iItem >= 0 && lpLVItem->iItem < infoPtr->nItemCount); 04189 04190 if (lpLVItem->mask == 0) return TRUE; 04191 04192 if (infoPtr->dwStyle & LVS_OWNERDATA) 04193 { 04194 /* a virtual listview only stores selection and focus */ 04195 if (lpLVItem->mask & ~LVIF_STATE) 04196 return FALSE; 04197 lpItem = NULL; 04198 } 04199 else 04200 { 04201 HDPA hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem); 04202 lpItem = DPA_GetPtr(hdpaSubItems, 0); 04203 assert (lpItem); 04204 } 04205 04206 /* we need to get the lParam and state of the item */ 04207 item.iItem = lpLVItem->iItem; 04208 item.iSubItem = lpLVItem->iSubItem; 04209 item.mask = LVIF_STATE | LVIF_PARAM; 04210 item.stateMask = (infoPtr->dwStyle & LVS_OWNERDATA) ? LVIS_FOCUSED | LVIS_SELECTED : ~0; 04211 04212 item.state = 0; 04213 item.lParam = 0; 04214 if (!isNew && !LISTVIEW_GetItemW(infoPtr, &item)) return FALSE; 04215 04216 TRACE("oldState=%x, newState=%x\n", item.state, lpLVItem->state); 04217 /* determine what fields will change */ 04218 if ((lpLVItem->mask & LVIF_STATE) && ((item.state ^ lpLVItem->state) & stateMask & ~infoPtr->uCallbackMask)) 04219 uChanged |= LVIF_STATE; 04220 04221 if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage)) 04222 uChanged |= LVIF_IMAGE; 04223 04224 if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam)) 04225 uChanged |= LVIF_PARAM; 04226 04227 if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent)) 04228 uChanged |= LVIF_INDENT; 04229 04230 if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW)) 04231 uChanged |= LVIF_TEXT; 04232 04233 TRACE("uChanged=0x%x\n", uChanged); 04234 if (!uChanged) return TRUE; 04235 *bChanged = TRUE; 04236 04237 ZeroMemory(&nmlv, sizeof(NMLISTVIEW)); 04238 nmlv.iItem = lpLVItem->iItem; 04239 nmlv.uNewState = (item.state & ~stateMask) | (lpLVItem->state & stateMask); 04240 nmlv.uOldState = item.state; 04241 nmlv.uChanged = uChanged; 04242 nmlv.lParam = item.lParam; 04243 04244 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */ 04245 /* and we are _NOT_ virtual (LVS_OWNERDATA), and change notifications */ 04246 /* are enabled */ 04247 if(lpItem && !isNew && infoPtr->bDoChangeNotify) 04248 { 04249 HWND hwndSelf = infoPtr->hwndSelf; 04250 04251 if (notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv)) 04252 return FALSE; 04253 if (!IsWindow(hwndSelf)) 04254 return FALSE; 04255 } 04256 04257 /* copy information */ 04258 if (lpLVItem->mask & LVIF_TEXT) 04259 textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW); 04260 04261 if (lpLVItem->mask & LVIF_IMAGE) 04262 lpItem->hdr.iImage = lpLVItem->iImage; 04263 04264 if (lpLVItem->mask & LVIF_PARAM) 04265 lpItem->lParam = lpLVItem->lParam; 04266 04267 if (lpLVItem->mask & LVIF_INDENT) 04268 lpItem->iIndent = lpLVItem->iIndent; 04269 04270 if (uChanged & LVIF_STATE) 04271 { 04272 if (lpItem && (stateMask & ~infoPtr->uCallbackMask)) 04273 { 04274 lpItem->state &= ~stateMask; 04275 lpItem->state |= (lpLVItem->state & stateMask); 04276 } 04277 if (lpLVItem->state & stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED) 04278 { 04279 if (infoPtr->dwStyle & LVS_SINGLESEL) LISTVIEW_DeselectAllSkipItem(infoPtr, lpLVItem->iItem); 04280 ranges_additem(infoPtr->selectionRanges, lpLVItem->iItem); 04281 } 04282 else if (stateMask & LVIS_SELECTED) 04283 { 04284 ranges_delitem(infoPtr->selectionRanges, lpLVItem->iItem); 04285 } 04286 /* if we are asked to change focus, and we manage it, do it */ 04287 if (stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED) 04288 { 04289 if (lpLVItem->state & LVIS_FOCUSED) 04290 { 04291 if (infoPtr->nFocusedItem != -1) 04292 { 04293 /* remove current focus */ 04294 item.mask = LVIF_STATE; 04295 item.state = 0; 04296 item.stateMask = LVIS_FOCUSED; 04297 04298 /* recurse with redrawing an item */ 04299 LISTVIEW_SetItemState(infoPtr, infoPtr->nFocusedItem, &item); 04300 } 04301 04302 infoPtr->nFocusedItem = lpLVItem->iItem; 04303 LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, infoPtr->uView == LV_VIEW_LIST); 04304 } 04305 else if (infoPtr->nFocusedItem == lpLVItem->iItem) 04306 { 04307 infoPtr->nFocusedItem = -1; 04308 } 04309 } 04310 } 04311 04312 /* if we're inserting the item, we're done */ 04313 if (isNew) return TRUE; 04314 04315 /* send LVN_ITEMCHANGED notification */ 04316 if (lpLVItem->mask & LVIF_PARAM) nmlv.lParam = lpLVItem->lParam; 04317 if (infoPtr->bDoChangeNotify) notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv); 04318 04319 return TRUE; 04320 } 04321 04322 /*** 04323 * DESCRIPTION: 04324 * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes. 04325 * 04326 * PARAMETER(S): 04327 * [I] infoPtr : valid pointer to the listview structure 04328 * [I] lpLVItem : valid pointer to new subitem attributes 04329 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI 04330 * [O] bChanged : will be set to TRUE if the item really changed 04331 * 04332 * RETURN: 04333 * SUCCESS : TRUE 04334 * FAILURE : FALSE 04335 */ 04336 static BOOL set_sub_item(const LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW, BOOL *bChanged) 04337 { 04338 HDPA hdpaSubItems; 04339 SUBITEM_INFO *lpSubItem; 04340 04341 /* we do not support subitems for virtual listviews */ 04342 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE; 04343 04344 /* set subitem only if column is present */ 04345 if (lpLVItem->iSubItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE; 04346 04347 /* First do some sanity checks */ 04348 /* The LVIF_STATE flag is valid for subitems, but does not appear to be 04349 particularly useful. We currently do not actually do anything with 04350 the flag on subitems. 04351 */ 04352 if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE | LVIF_STATE | LVIF_DI_SETITEM)) return FALSE; 04353 if (!(lpLVItem->mask & (LVIF_TEXT | LVIF_IMAGE | LVIF_STATE))) return TRUE; 04354 04355 /* get the subitem structure, and create it if not there */ 04356 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem); 04357 assert (hdpaSubItems); 04358 04359 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem); 04360 if (!lpSubItem) 04361 { 04362 SUBITEM_INFO *tmpSubItem; 04363 INT i; 04364 04365 lpSubItem = Alloc(sizeof(SUBITEM_INFO)); 04366 if (!lpSubItem) return FALSE; 04367 /* we could binary search here, if need be...*/ 04368 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++) 04369 { 04370 tmpSubItem = DPA_GetPtr(hdpaSubItems, i); 04371 if (tmpSubItem->iSubItem > lpLVItem->iSubItem) break; 04372 } 04373 if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1) 04374 { 04375 Free(lpSubItem); 04376 return FALSE; 04377 } 04378 lpSubItem->iSubItem = lpLVItem->iSubItem; 04379 lpSubItem->hdr.iImage = I_IMAGECALLBACK; 04380 *bChanged = TRUE; 04381 } 04382 04383 if ((lpLVItem->mask & LVIF_IMAGE) && (lpSubItem->hdr.iImage != lpLVItem->iImage)) 04384 { 04385 lpSubItem->hdr.iImage = lpLVItem->iImage; 04386 *bChanged = TRUE; 04387 } 04388 04389 if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpSubItem->hdr.pszText, lpLVItem->pszText, isW)) 04390 { 04391 textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW); 04392 *bChanged = TRUE; 04393 } 04394 04395 return TRUE; 04396 } 04397 04398 /*** 04399 * DESCRIPTION: 04400 * Sets item attributes. 04401 * 04402 * PARAMETER(S): 04403 * [I] infoPtr : valid pointer to the listview structure 04404 * [I] lpLVItem : new item attributes 04405 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI 04406 * 04407 * RETURN: 04408 * SUCCESS : TRUE 04409 * FAILURE : FALSE 04410 */ 04411 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, LVITEMW *lpLVItem, BOOL isW) 04412 { 04413 HWND hwndSelf = infoPtr->hwndSelf; 04414 LPWSTR pszText = NULL; 04415 BOOL bResult, bChanged = FALSE; 04416 RECT oldItemArea; 04417 04418 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW); 04419 04420 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount) 04421 return FALSE; 04422 04423 /* Store old item area */ 04424 LISTVIEW_GetItemBox(infoPtr, lpLVItem->iItem, &oldItemArea); 04425 04426 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */ 04427 if ((lpLVItem->mask & LVIF_TEXT) && is_text(lpLVItem->pszText)) 04428 { 04429 pszText = lpLVItem->pszText; 04430 lpLVItem->pszText = textdupTtoW(lpLVItem->pszText, isW); 04431 } 04432 04433 /* actually set the fields */ 04434 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE; 04435 04436 if (lpLVItem->iSubItem) 04437 bResult = set_sub_item(infoPtr, lpLVItem, TRUE, &bChanged); 04438 else 04439 bResult = set_main_item(infoPtr, lpLVItem, FALSE, TRUE, &bChanged); 04440 if (!IsWindow(hwndSelf)) 04441 return FALSE; 04442 04443 /* redraw item, if necessary */ 04444 if (bChanged && !infoPtr->bIsDrawing) 04445 { 04446 /* this little optimization eliminates some nasty flicker */ 04447 if ( infoPtr->uView == LV_VIEW_DETAILS && !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && 04448 !(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && 04449 lpLVItem->iSubItem > 0 && lpLVItem->iSubItem <= DPA_GetPtrCount(infoPtr->hdpaColumns) ) 04450 LISTVIEW_InvalidateSubItem(infoPtr, lpLVItem->iItem, lpLVItem->iSubItem); 04451 else 04452 { 04453 LISTVIEW_InvalidateRect(infoPtr, &oldItemArea); 04454 LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem); 04455 } 04456 } 04457 /* restore text */ 04458 if (pszText) 04459 { 04460 textfreeT(lpLVItem->pszText, isW); 04461 lpLVItem->pszText = pszText; 04462 } 04463 04464 return bResult; 04465 } 04466 04467 /*** 04468 * DESCRIPTION: 04469 * Retrieves the index of the item at coordinate (0, 0) of the client area. 04470 * 04471 * PARAMETER(S): 04472 * [I] infoPtr : valid pointer to the listview structure 04473 * 04474 * RETURN: 04475 * item index 04476 */ 04477 static INT LISTVIEW_GetTopIndex(const LISTVIEW_INFO *infoPtr) 04478 { 04479 INT nItem = 0; 04480 SCROLLINFO scrollInfo; 04481 04482 scrollInfo.cbSize = sizeof(SCROLLINFO); 04483 scrollInfo.fMask = SIF_POS; 04484 04485 if (infoPtr->uView == LV_VIEW_LIST) 04486 { 04487 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) 04488 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr); 04489 } 04490 else if (infoPtr->uView == LV_VIEW_DETAILS) 04491 { 04492 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) 04493 nItem = scrollInfo.nPos; 04494 } 04495 else 04496 { 04497 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) 04498 nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight); 04499 } 04500 04501 TRACE("nItem=%d\n", nItem); 04502 04503 return nItem; 04504 } 04505 04506 04507 /*** 04508 * DESCRIPTION: 04509 * Erases the background of the given rectangle 04510 * 04511 * PARAMETER(S): 04512 * [I] infoPtr : valid pointer to the listview structure 04513 * [I] hdc : device context handle 04514 * [I] lprcBox : clipping rectangle 04515 * 04516 * RETURN: 04517 * Success: TRUE 04518 * Failure: FALSE 04519 */ 04520 static inline BOOL LISTVIEW_FillBkgnd(const LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *lprcBox) 04521 { 04522 if (!infoPtr->hBkBrush) return FALSE; 04523 04524 TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc, wine_dbgstr_rect(lprcBox), infoPtr->hBkBrush); 04525 04526 return FillRect(hdc, lprcBox, infoPtr->hBkBrush); 04527 } 04528 04529 /*** 04530 * DESCRIPTION: 04531 * Draws an item. 04532 * 04533 * PARAMETER(S): 04534 * [I] infoPtr : valid pointer to the listview structure 04535 * [I] hdc : device context handle 04536 * [I] nItem : item index 04537 * [I] nSubItem : subitem index 04538 * [I] pos : item position in client coordinates 04539 * [I] cdmode : custom draw mode 04540 * 04541 * RETURN: 04542 * Success: TRUE 04543 * Failure: FALSE 04544 */ 04545 static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, INT nSubItem, POINT pos, DWORD cdmode) 04546 { 04547 UINT uFormat; 04548 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' }; 04549 static WCHAR szCallback[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 }; 04550 DWORD cdsubitemmode = CDRF_DODEFAULT; 04551 LPRECT lprcFocus; 04552 RECT rcSelect, rcBox, rcIcon, rcLabel, rcStateIcon; 04553 NMLVCUSTOMDRAW nmlvcd; 04554 HIMAGELIST himl; 04555 LVITEMW lvItem; 04556 HFONT hOldFont; 04557 04558 TRACE("(hdc=%p, nItem=%d, nSubItem=%d, pos=%s)\n", hdc, nItem, nSubItem, wine_dbgstr_point(&pos)); 04559 04560 /* get information needed for drawing the item */ 04561 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM; 04562 if (nSubItem == 0) lvItem.mask |= LVIF_STATE; 04563 if (infoPtr->uView == LV_VIEW_DETAILS) lvItem.mask |= LVIF_INDENT; 04564 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK | LVIS_CUT; 04565 lvItem.iItem = nItem; 04566 lvItem.iSubItem = nSubItem; 04567 lvItem.state = 0; 04568 lvItem.lParam = 0; 04569 lvItem.cchTextMax = DISP_TEXT_SIZE; 04570 lvItem.pszText = szDispText; 04571 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE; 04572 if (nSubItem > 0 && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)) 04573 lvItem.state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED); 04574 if (lvItem.pszText == LPSTR_TEXTCALLBACKW) lvItem.pszText = szCallback; 04575 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE)); 04576 04577 /* now check if we need to update the focus rectangle */ 04578 lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0; 04579 04580 if (!lprcFocus) lvItem.state &= ~LVIS_FOCUSED; 04581 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcSelect, &rcIcon, &rcStateIcon, &rcLabel); 04582 OffsetRect(&rcBox, pos.x, pos.y); 04583 OffsetRect(&rcSelect, pos.x, pos.y); 04584 OffsetRect(&rcIcon, pos.x, pos.y); 04585 OffsetRect(&rcStateIcon, pos.x, pos.y); 04586 OffsetRect(&rcLabel, pos.x, pos.y); 04587 TRACE(" rcBox=%s, rcSelect=%s, rcIcon=%s. rcLabel=%s\n", 04588 wine_dbgstr_rect(&rcBox), wine_dbgstr_rect(&rcSelect), 04589 wine_dbgstr_rect(&rcIcon), wine_dbgstr_rect(&rcLabel)); 04590 04591 /* fill in the custom draw structure */ 04592 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcBox, &lvItem); 04593 04594 hOldFont = GetCurrentObject(hdc, OBJ_FONT); 04595 if (nSubItem > 0) cdmode = infoPtr->cditemmode; 04596 if (cdmode & CDRF_SKIPDEFAULT) goto postpaint; 04597 if (cdmode & CDRF_NOTIFYITEMDRAW) 04598 cdsubitemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd); 04599 if (nSubItem == 0) infoPtr->cditemmode = cdsubitemmode; 04600 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint; 04601 /* we have to send a CDDS_SUBITEM customdraw explicitly for subitem 0 */ 04602 if (nSubItem == 0 && cdsubitemmode == CDRF_NOTIFYITEMDRAW) 04603 { 04604 cdsubitemmode = notify_customdraw(infoPtr, CDDS_SUBITEM | CDDS_ITEMPREPAINT, &nmlvcd); 04605 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint; 04606 } 04607 if (nSubItem == 0 || (cdmode & CDRF_NOTIFYITEMDRAW)) 04608 prepaint_setup(infoPtr, hdc, &nmlvcd, FALSE); 04609 else if ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) == FALSE) 04610 prepaint_setup(infoPtr, hdc, &nmlvcd, TRUE); 04611 04612 /* in full row select, subitems, will just use main item's colors */ 04613 if (nSubItem && infoPtr->uView == LV_VIEW_DETAILS && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)) 04614 nmlvcd.clrTextBk = CLR_NONE; 04615 04616 /* FIXME: temporary hack */ 04617 rcSelect.left = rcLabel.left; 04618 04619 /* draw the selection background, if we're drawing the main item */ 04620 if (nSubItem == 0) 04621 { 04622 /* in icon mode, the label rect is really what we want to draw the 04623 * background for */ 04624 if (infoPtr->uView == LV_VIEW_ICON) 04625 rcSelect = rcLabel; 04626 04627 if (infoPtr->uView == LV_VIEW_DETAILS && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)) 04628 { 04629 /* we have to update left focus bound too if item isn't in leftmost column 04630 and reduce right box bound */ 04631 if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0) 04632 { 04633 INT leftmost; 04634 04635 if ((leftmost = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX, 0, 0))) 04636 { 04637 INT Originx = pos.x - LISTVIEW_GetColumnInfo(infoPtr, 0)->rcHeader.left; 04638 INT index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX, 04639 DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, 0); 04640 04641 rcBox.right = LISTVIEW_GetColumnInfo(infoPtr, index)->rcHeader.right + Originx; 04642 rcSelect.left = LISTVIEW_GetColumnInfo(infoPtr, leftmost)->rcHeader.left + Originx; 04643 } 04644 } 04645 04646 rcSelect.right = rcBox.right; 04647 } 04648 04649 if (nmlvcd.clrTextBk != CLR_NONE) 04650 ExtTextOutW(hdc, rcSelect.left, rcSelect.top, ETO_OPAQUE, &rcSelect, NULL, 0, NULL); 04651 /* store new focus rectangle */ 04652 if (infoPtr->nFocusedItem == nItem) infoPtr->rcFocus = rcSelect; 04653 } 04654 04655 /* state icons */ 04656 if (infoPtr->himlState && STATEIMAGEINDEX(lvItem.state) && (nSubItem == 0)) 04657 { 04658 UINT uStateImage = STATEIMAGEINDEX(lvItem.state); 04659 if (uStateImage) 04660 { 04661 TRACE("uStateImage=%d\n", uStateImage); 04662 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, 04663 rcStateIcon.left, rcStateIcon.top, ILD_NORMAL); 04664 } 04665 } 04666 04667 /* item icons */ 04668 himl = (infoPtr->uView == LV_VIEW_ICON ? infoPtr->himlNormal : infoPtr->himlSmall); 04669 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon)) 04670 { 04671 UINT style; 04672 04673 TRACE("iImage=%d\n", lvItem.iImage); 04674 04675 if (lvItem.state & (LVIS_SELECTED | LVIS_CUT) && infoPtr->bFocus) 04676 style = ILD_SELECTED; 04677 else 04678 style = ILD_NORMAL; 04679 04680 ImageList_DrawEx(himl, lvItem.iImage, hdc, rcIcon.left, rcIcon.top, 04681 rcIcon.right - rcIcon.left, rcIcon.bottom - rcIcon.top, infoPtr->clrBk, 04682 lvItem.state & LVIS_CUT ? RGB(255, 255, 255) : CLR_DEFAULT, 04683 style); 04684 } 04685 04686 /* Don't bother painting item being edited */ 04687 if (infoPtr->hwndEdit && nItem == infoPtr->nEditLabelItem && nSubItem == 0) goto postpaint; 04688 04689 /* figure out the text drawing flags */ 04690 uFormat = (infoPtr->uView == LV_VIEW_ICON ? (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS) : LV_SL_DT_FLAGS); 04691 if (infoPtr->uView == LV_VIEW_ICON) 04692 uFormat = (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS); 04693 else if (nSubItem) 04694 { 04695 switch (LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->fmt & LVCFMT_JUSTIFYMASK) 04696 { 04697 case LVCFMT_RIGHT: uFormat |= DT_RIGHT; break; 04698 case LVCFMT_CENTER: uFormat |= DT_CENTER; break; 04699 default: uFormat |= DT_LEFT; 04700 } 04701 } 04702 if (!(uFormat & (DT_RIGHT | DT_CENTER))) 04703 { 04704 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon)) rcLabel.left += IMAGE_PADDING; 04705 else rcLabel.left += LABEL_HOR_PADDING; 04706 } 04707 else if (uFormat & DT_RIGHT) rcLabel.right -= LABEL_HOR_PADDING; 04708 04709 /* for GRIDLINES reduce the bottom so the text formats correctly */ 04710 if (infoPtr->uView == LV_VIEW_DETAILS && infoPtr->dwLvExStyle & LVS_EX_GRIDLINES) 04711 rcLabel.bottom--; 04712 04713 if ((!(lvItem.state & LVIS_SELECTED) || !infoPtr->bFocus) && (infoPtr->dwLvExStyle & LVS_EX_TRANSPARENTSHADOWTEXT)) 04714 DrawShadowText(hdc, lvItem.pszText, -1, &rcLabel, uFormat, RGB(255, 255, 255), RGB(0, 0, 0), 2, 2); 04715 else 04716 DrawTextW(hdc, lvItem.pszText, -1, &rcLabel, uFormat); 04717 04718 postpaint: 04719 if (cdsubitemmode & CDRF_NOTIFYPOSTPAINT) 04720 notify_postpaint(infoPtr, &nmlvcd); 04721 if (cdsubitemmode & CDRF_NEWFONT) 04722 SelectObject(hdc, hOldFont); 04723 return TRUE; 04724 } 04725 04726 /*** 04727 * DESCRIPTION: 04728 * Draws listview items when in owner draw mode. 04729 * 04730 * PARAMETER(S): 04731 * [I] infoPtr : valid pointer to the listview structure 04732 * [I] hdc : device context handle 04733 * 04734 * RETURN: 04735 * None 04736 */ 04737 static void LISTVIEW_RefreshOwnerDraw(const LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode) 04738 { 04739 UINT uID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID); 04740 DWORD cditemmode = CDRF_DODEFAULT; 04741 NMLVCUSTOMDRAW nmlvcd; 04742 POINT Origin, Position; 04743 DRAWITEMSTRUCT dis; 04744 LVITEMW item; 04745 04746 TRACE("()\n"); 04747 04748 ZeroMemory(&dis, sizeof(dis)); 04749 04750 /* Get scroll info once before loop */ 04751 LISTVIEW_GetOrigin(infoPtr, &Origin); 04752 04753 /* iterate through the invalidated rows */ 04754 while(iterator_next(i)) 04755 { 04756 item.iItem = i->nItem; 04757 item.iSubItem = 0; 04758 item.mask = LVIF_PARAM | LVIF_STATE; 04759 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED; 04760 if (!LISTVIEW_GetItemW(infoPtr, &item)) continue; 04761 04762 dis.CtlType = ODT_LISTVIEW; 04763 dis.CtlID = uID; 04764 dis.itemID = item.iItem; 04765 dis.itemAction = ODA_DRAWENTIRE; 04766 dis.itemState = 0; 04767 if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED; 04768 if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS; 04769 dis.hwndItem = infoPtr->hwndSelf; 04770 dis.hDC = hdc; 04771 LISTVIEW_GetItemOrigin(infoPtr, dis.itemID, &Position); 04772 dis.rcItem.left = Position.x + Origin.x; 04773 dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth; 04774 dis.rcItem.top = Position.y + Origin.y; 04775 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight; 04776 dis.itemData = item.lParam; 04777 04778 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), wine_dbgstr_rect(&dis.rcItem)); 04779 04780 /* 04781 * Even if we do not send the CDRF_NOTIFYITEMDRAW we need to fill the nmlvcd 04782 * structure for the rest. of the paint cycle 04783 */ 04784 customdraw_fill(&nmlvcd, infoPtr, hdc, &dis.rcItem, &item); 04785 if (cdmode & CDRF_NOTIFYITEMDRAW) 04786 cditemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd); 04787 04788 if (!(cditemmode & CDRF_SKIPDEFAULT)) 04789 { 04790 prepaint_setup (infoPtr, hdc, &nmlvcd, FALSE); 04791 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis); 04792 } 04793 04794 if (cditemmode & CDRF_NOTIFYPOSTPAINT) 04795 notify_postpaint(infoPtr, &nmlvcd); 04796 } 04797 } 04798 04799 /*** 04800 * DESCRIPTION: 04801 * Draws listview items when in report display mode. 04802 * 04803 * PARAMETER(S): 04804 * [I] infoPtr : valid pointer to the listview structure 04805 * [I] hdc : device context handle 04806 * [I] cdmode : custom draw mode 04807 * 04808 * RETURN: 04809 * None 04810 */ 04811 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode) 04812 { 04813 INT rgntype; 04814 RECT rcClip, rcItem; 04815 POINT Origin, Position; 04816 RANGES colRanges; 04817 INT col, index; 04818 ITERATOR j; 04819 04820 TRACE("()\n"); 04821 04822 /* figure out what to draw */ 04823 rgntype = GetClipBox(hdc, &rcClip); 04824 if (rgntype == NULLREGION) return; 04825 04826 /* Get scroll info once before loop */ 04827 LISTVIEW_GetOrigin(infoPtr, &Origin); 04828 04829 colRanges = ranges_create(DPA_GetPtrCount(infoPtr->hdpaColumns)); 04830 04831 /* narrow down the columns we need to paint */ 04832 for(col = 0; col < DPA_GetPtrCount(infoPtr->hdpaColumns); col++) 04833 { 04834 index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX, col, 0); 04835 04836 LISTVIEW_GetHeaderRect(infoPtr, index, &rcItem); 04837 if ((rcItem.right + Origin.x >= rcClip.left) && (rcItem.left + Origin.x < rcClip.right)) 04838 ranges_additem(colRanges, index); 04839 } 04840 iterator_rangesitems(&j, colRanges); 04841 04842 /* in full row select, we _have_ to draw the main item */ 04843 if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) 04844 j.nSpecial = 0; 04845 04846 /* iterate through the invalidated rows */ 04847 while(iterator_next(i)) 04848 { 04849 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position); 04850 Position.y += Origin.y; 04851 04852 /* iterate through the invalidated columns */ 04853 while(iterator_next(&j)) 04854 { 04855 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem); 04856 Position.x = (j.nItem == 0) ? rcItem.left + Origin.x : Origin.x; 04857 04858 if (rgntype == COMPLEXREGION && !((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && j.nItem == 0)) 04859 { 04860 rcItem.top = 0; 04861 rcItem.bottom = infoPtr->nItemHeight; 04862 OffsetRect(&rcItem, Origin.x, Position.y); 04863 if (!RectVisible(hdc, &rcItem)) continue; 04864 } 04865 04866 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, j.nItem, Position, cdmode); 04867 } 04868 } 04869 iterator_destroy(&j); 04870 } 04871 04872 /*** 04873 * DESCRIPTION: 04874 * Draws the gridlines if necessary when in report display mode. 04875 * 04876 * PARAMETER(S): 04877 * [I] infoPtr : valid pointer to the listview structure 04878 * [I] hdc : device context handle 04879 * 04880 * RETURN: 04881 * None 04882 */ 04883 static void LISTVIEW_RefreshReportGrid(LISTVIEW_INFO *infoPtr, HDC hdc) 04884 { 04885 INT rgntype; 04886 INT y, itemheight; 04887 INT col, index; 04888 HPEN hPen, hOldPen; 04889 RECT rcClip, rcItem = {0}; 04890 POINT Origin; 04891 RANGES colRanges; 04892 ITERATOR j; 04893 BOOL rmost = FALSE; 04894 04895 TRACE("()\n"); 04896 04897 /* figure out what to draw */ 04898 rgntype = GetClipBox(hdc, &rcClip); 04899 if (rgntype == NULLREGION) return; 04900 04901 /* Get scroll info once before loop */ 04902 LISTVIEW_GetOrigin(infoPtr, &Origin); 04903 04904 colRanges = ranges_create(DPA_GetPtrCount(infoPtr->hdpaColumns)); 04905 04906 /* narrow down the columns we need to paint */ 04907 for(col = 0; col < DPA_GetPtrCount(infoPtr->hdpaColumns); col++) 04908 { 04909 index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX, col, 0); 04910 04911 LISTVIEW_GetHeaderRect(infoPtr, index, &rcItem); 04912 if ((rcItem.right + Origin.x >= rcClip.left) && (rcItem.left + Origin.x < rcClip.right)) 04913 ranges_additem(colRanges, index); 04914 } 04915 04916 /* is right most vertical line visible? */ 04917 if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0) 04918 { 04919 index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, 0); 04920 LISTVIEW_GetHeaderRect(infoPtr, index, &rcItem); 04921 rmost = (rcItem.right + Origin.x < rcClip.right); 04922 } 04923 04924 if ((hPen = CreatePen( PS_SOLID, 1, comctl32_color.clr3dFace ))) 04925 { 04926 hOldPen = SelectObject ( hdc, hPen ); 04927 04928 /* draw the vertical lines for the columns */ 04929 iterator_rangesitems(&j, colRanges); 04930 while(iterator_next(&j)) 04931 { 04932 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem); 04933 if (rcItem.left == 0) continue; /* skip leftmost column */ 04934 rcItem.left += Origin.x; 04935 rcItem.right += Origin.x; 04936 rcItem.top = infoPtr->rcList.top; 04937 rcItem.bottom = infoPtr->rcList.bottom; 04938 TRACE("vert col=%d, rcItem=%s\n", j.nItem, wine_dbgstr_rect(&rcItem)); 04939 MoveToEx (hdc, rcItem.left, rcItem.top, NULL); 04940 LineTo (hdc, rcItem.left, rcItem.bottom); 04941 } 04942 iterator_destroy(&j); 04943 /* draw rightmost grid line if visible */ 04944 if (rmost) 04945 { 04946 index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX, 04947 DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, 0); 04948 LISTVIEW_GetHeaderRect(infoPtr, index, &rcItem); 04949 04950 rcItem.right += Origin.x; 04951 04952 MoveToEx (hdc, rcItem.right, infoPtr->rcList.top, NULL); 04953 LineTo (hdc, rcItem.right, infoPtr->rcList.bottom); 04954 } 04955 04956 /* draw the horizontal lines for the rows */ 04957 itemheight = LISTVIEW_CalculateItemHeight(infoPtr); 04958 rcItem.left = infoPtr->rcList.left; 04959 rcItem.right = infoPtr->rcList.right; 04960 rcItem.bottom = rcItem.top = Origin.y - 1; 04961 MoveToEx(hdc, rcItem.left, rcItem.top, NULL); 04962 LineTo(hdc, rcItem.right, rcItem.top); 04963 for(y=itemheight-1+Origin.y; y<=infoPtr->rcList.bottom; y+=itemheight) 04964 { 04965 rcItem.bottom = rcItem.top = y; 04966 TRACE("horz rcItem=%s\n", wine_dbgstr_rect(&rcItem)); 04967 MoveToEx (hdc, rcItem.left, rcItem.top, NULL); 04968 LineTo (hdc, rcItem.right, rcItem.top); 04969 } 04970 04971 SelectObject( hdc, hOldPen ); 04972 DeleteObject( hPen ); 04973 } 04974 } 04975 04976 /*** 04977 * DESCRIPTION: 04978 * Draws listview items when in list display mode. 04979 * 04980 * PARAMETER(S): 04981 * [I] infoPtr : valid pointer to the listview structure 04982 * [I] hdc : device context handle 04983 * [I] cdmode : custom draw mode 04984 * 04985 * RETURN: 04986 * None 04987 */ 04988 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode) 04989 { 04990 POINT Origin, Position; 04991 04992 /* Get scroll info once before loop */ 04993 LISTVIEW_GetOrigin(infoPtr, &Origin); 04994 04995 while(iterator_prev(i)) 04996 { 04997 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position); 04998 Position.x += Origin.x; 04999 Position.y += Origin.y; 05000 05001 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, 0, Position, cdmode); 05002 } 05003 } 05004 05005 05006 /*** 05007 * DESCRIPTION: 05008 * Draws listview items. 05009 * 05010 * PARAMETER(S): 05011 * [I] infoPtr : valid pointer to the listview structure 05012 * [I] hdc : device context handle 05013 * [I] prcErase : rect to be erased before refresh (may be NULL) 05014 * 05015 * RETURN: 05016 * NoneX 05017 */ 05018 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *prcErase) 05019 { 05020 COLORREF oldTextColor = 0, oldBkColor = 0, oldClrTextBk, oldClrText; 05021 NMLVCUSTOMDRAW nmlvcd; 05022 HFONT hOldFont = 0; 05023 DWORD cdmode; 05024 INT oldBkMode = 0; 05025 RECT rcClient; 05026 ITERATOR i; 05027 HDC hdcOrig = hdc; 05028 HBITMAP hbmp = NULL; 05029 RANGE range; 05030 05031 LISTVIEW_DUMP(infoPtr); 05032 05033 if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) { 05034 TRACE("double buffering\n"); 05035 05036 hdc = CreateCompatibleDC(hdcOrig); 05037 if (!hdc) { 05038 ERR("Failed to create DC for backbuffer\n"); 05039 return; 05040 } 05041 hbmp = CreateCompatibleBitmap(hdcOrig, infoPtr->rcList.right, 05042 infoPtr->rcList.bottom); 05043 if (!hbmp) { 05044 ERR("Failed to create bitmap for backbuffer\n"); 05045 DeleteDC(hdc); 05046 return; 05047 } 05048 05049 SelectObject(hdc, hbmp); 05050 SelectObject(hdc, infoPtr->hFont); 05051 05052 if(GetClipBox(hdcOrig, &rcClient)) 05053 IntersectClipRect(hdc, rcClient.left, rcClient.top, rcClient.right, rcClient.bottom); 05054 } else { 05055 /* Save dc values we're gonna trash while drawing 05056 * FIXME: Should be done in LISTVIEW_DrawItem() */ 05057 hOldFont = SelectObject(hdc, infoPtr->hFont); 05058 oldBkMode = GetBkMode(hdc); 05059 oldBkColor = GetBkColor(hdc); 05060 oldTextColor = GetTextColor(hdc); 05061 } 05062 05063 infoPtr->bIsDrawing = TRUE; 05064 05065 if (prcErase) { 05066 LISTVIEW_FillBkgnd(infoPtr, hdc, prcErase); 05067 } else if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) { 05068 /* If no erasing was done (usually because RedrawWindow was called 05069 * with RDW_INVALIDATE only) we need to copy the old contents into 05070 * the backbuffer before continuing. */ 05071 BitBlt(hdc, infoPtr->rcList.left, infoPtr->rcList.top, 05072 infoPtr->rcList.right - infoPtr->rcList.left, 05073 infoPtr->rcList.bottom - infoPtr->rcList.top, 05074 hdcOrig, infoPtr->rcList.left, infoPtr->rcList.top, SRCCOPY); 05075 } 05076 05077 /* FIXME: Shouldn't need to do this */ 05078 oldClrTextBk = infoPtr->clrTextBk; 05079 oldClrText = infoPtr->clrText; 05080 05081 infoPtr->cditemmode = CDRF_DODEFAULT; 05082 05083 GetClientRect(infoPtr->hwndSelf, &rcClient); 05084 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient, 0); 05085 cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd); 05086 if (cdmode & CDRF_SKIPDEFAULT) goto enddraw; 05087 prepaint_setup(infoPtr, hdc, &nmlvcd, FALSE); 05088 05089 /* Use these colors to draw the items */ 05090 infoPtr->clrTextBk = nmlvcd.clrTextBk; 05091 infoPtr->clrText = nmlvcd.clrText; 05092 05093 /* nothing to draw */ 05094 if(infoPtr->nItemCount == 0) goto enddraw; 05095 05096 /* figure out what we need to draw */ 05097 iterator_visibleitems(&i, infoPtr, hdc); 05098 range = iterator_range(&i); 05099 05100 /* send cache hint notification */ 05101 if (infoPtr->dwStyle & LVS_OWNERDATA) 05102 { 05103 NMLVCACHEHINT nmlv; 05104 05105 ZeroMemory(&nmlv, sizeof(NMLVCACHEHINT)); 05106 nmlv.iFrom = range.lower; 05107 nmlv.iTo = range.upper - 1; 05108 notify_hdr(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr); 05109 } 05110 05111 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (infoPtr->uView == LV_VIEW_DETAILS)) 05112 LISTVIEW_RefreshOwnerDraw(infoPtr, &i, hdc, cdmode); 05113 else 05114 { 05115 if (infoPtr->uView == LV_VIEW_DETAILS) 05116 LISTVIEW_RefreshReport(infoPtr, &i, hdc, cdmode); 05117 else /* LV_VIEW_LIST, LV_VIEW_ICON or LV_VIEW_SMALLICON */ 05118 LISTVIEW_RefreshList(infoPtr, &i, hdc, cdmode); 05119 05120 /* if we have a focus rect and it's visible, draw it */ 05121 if (infoPtr->bFocus && range.lower <= infoPtr->nFocusedItem && 05122 (range.upper - 1) >= infoPtr->nFocusedItem) 05123 LISTVIEW_DrawFocusRect(infoPtr, hdc); 05124 } 05125 iterator_destroy(&i); 05126 05127 enddraw: 05128 /* For LVS_EX_GRIDLINES go and draw lines */ 05129 /* This includes the case where there were *no* items */ 05130 if ((infoPtr->uView == LV_VIEW_DETAILS) && infoPtr->dwLvExStyle & LVS_EX_GRIDLINES) 05131 LISTVIEW_RefreshReportGrid(infoPtr, hdc); 05132 05133 /* Draw marquee rectangle if appropriate */ 05134 if (infoPtr->bMarqueeSelect) 05135 { 05136 SetBkColor(hdc, RGB(255, 255, 255)); 05137 SetTextColor(hdc, RGB(0, 0, 0)); 05138 DrawFocusRect(hdc, &infoPtr->marqueeDrawRect); 05139 } 05140 05141 if (cdmode & CDRF_NOTIFYPOSTPAINT) 05142 notify_postpaint(infoPtr, &nmlvcd); 05143 05144 infoPtr->clrTextBk = oldClrTextBk; 05145 infoPtr->clrText = oldClrText; 05146 05147 if(hbmp) { 05148 BitBlt(hdcOrig, infoPtr->rcList.left, infoPtr->rcList.top, 05149 infoPtr->rcList.right - infoPtr->rcList.left, 05150 infoPtr->rcList.bottom - infoPtr->rcList.top, 05151 hdc, infoPtr->rcList.left, infoPtr->rcList.top, SRCCOPY); 05152 05153 DeleteObject(hbmp); 05154 DeleteDC(hdc); 05155 } else { 05156 SelectObject(hdc, hOldFont); 05157 SetBkMode(hdc, oldBkMode); 05158 SetBkColor(hdc, oldBkColor); 05159 SetTextColor(hdc, oldTextColor); 05160 } 05161 05162 infoPtr->bIsDrawing = FALSE; 05163 } 05164 05165 05166 /*** 05167 * DESCRIPTION: 05168 * Calculates the approximate width and height of a given number of items. 05169 * 05170 * PARAMETER(S): 05171 * [I] infoPtr : valid pointer to the listview structure 05172 * [I] nItemCount : number of items 05173 * [I] wWidth : width 05174 * [I] wHeight : height 05175 * 05176 * RETURN: 05177 * Returns a DWORD. The width in the low word and the height in high word. 05178 */ 05179 static DWORD LISTVIEW_ApproximateViewRect(const LISTVIEW_INFO *infoPtr, INT nItemCount, 05180 WORD wWidth, WORD wHeight) 05181 { 05182 DWORD dwViewRect = 0; 05183 05184 if (nItemCount == -1) 05185 nItemCount = infoPtr->nItemCount; 05186 05187 if (infoPtr->uView == LV_VIEW_LIST) 05188 { 05189 INT nItemCountPerColumn = 1; 05190 INT nColumnCount = 0; 05191 05192 if (wHeight == 0xFFFF) 05193 { 05194 /* use current height */ 05195 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top; 05196 } 05197 05198 if (wHeight < infoPtr->nItemHeight) 05199 wHeight = infoPtr->nItemHeight; 05200 05201 if (nItemCount > 0) 05202 { 05203 if (infoPtr->nItemHeight > 0) 05204 { 05205 nItemCountPerColumn = wHeight / infoPtr->nItemHeight; 05206 if (nItemCountPerColumn == 0) 05207 nItemCountPerColumn = 1; 05208 05209 if (nItemCount % nItemCountPerColumn != 0) 05210 nColumnCount = nItemCount / nItemCountPerColumn; 05211 else 05212 nColumnCount = nItemCount / nItemCountPerColumn + 1; 05213 } 05214 } 05215 05216 /* Microsoft padding magic */ 05217 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2; 05218 wWidth = nColumnCount * infoPtr->nItemWidth + 2; 05219 05220 dwViewRect = MAKELONG(wWidth, wHeight); 05221 } 05222 else if (infoPtr->uView == LV_VIEW_DETAILS) 05223 { 05224 RECT rcBox; 05225 05226 if (infoPtr->nItemCount > 0) 05227 { 05228 LISTVIEW_GetItemBox(infoPtr, 0, &rcBox); 05229 wWidth = rcBox.right - rcBox.left; 05230 wHeight = (rcBox.bottom - rcBox.top) * nItemCount; 05231 } 05232 else 05233 { 05234 /* use current height and width */ 05235 if (wHeight == 0xffff) 05236 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top; 05237 if (wWidth == 0xffff) 05238 wWidth = infoPtr->rcList.right - infoPtr->rcList.left; 05239 } 05240 05241 dwViewRect = MAKELONG(wWidth, wHeight); 05242 } 05243 else if (infoPtr->uView == LV_VIEW_ICON) 05244 { 05245 UINT rows,cols; 05246 UINT nItemWidth; 05247 UINT nItemHeight; 05248 05249 nItemWidth = infoPtr->iconSpacing.cx; 05250 nItemHeight = infoPtr->iconSpacing.cy; 05251 05252 if (wWidth == 0xffff) 05253 wWidth = infoPtr->rcList.right - infoPtr->rcList.left; 05254 05255 if (wWidth < nItemWidth) 05256 wWidth = nItemWidth; 05257 05258 cols = wWidth / nItemWidth; 05259 if (cols > nItemCount) 05260 cols = nItemCount; 05261 if (cols < 1) 05262 cols = 1; 05263 05264 if (nItemCount) 05265 { 05266 rows = nItemCount / cols; 05267 if (nItemCount % cols) 05268 rows++; 05269 } 05270 else 05271 rows = 0; 05272 05273 wHeight = (nItemHeight * rows)+2; 05274 wWidth = (nItemWidth * cols)+2; 05275 05276 dwViewRect = MAKELONG(wWidth, wHeight); 05277 } 05278 else if (infoPtr->uView == LV_VIEW_SMALLICON) 05279 FIXME("uView == LV_VIEW_SMALLICON: not implemented\n"); 05280 05281 return dwViewRect; 05282 } 05283 05284 /*** 05285 * DESCRIPTION: 05286 * Cancel edit label with saving item text. 05287 * 05288 * PARAMETER(S): 05289 * [I] infoPtr : valid pointer to the listview structure 05290 * 05291 * RETURN: 05292 * Always returns TRUE. 05293 */ 05294 static LRESULT LISTVIEW_CancelEditLabel(LISTVIEW_INFO *infoPtr) 05295 { 05296 if (infoPtr->hwndEdit) 05297 { 05298 /* handle value will be lost after LISTVIEW_EndEditLabelT */ 05299 HWND edit = infoPtr->hwndEdit; 05300 05301 LISTVIEW_EndEditLabelT(infoPtr, TRUE, IsWindowUnicode(infoPtr->hwndEdit)); 05302 SendMessageW(edit, WM_CLOSE, 0, 0); 05303 } 05304 05305 return TRUE; 05306 } 05307 05308 /*** 05309 * DESCRIPTION: 05310 * Create a drag image list for the specified item. 05311 * 05312 * PARAMETER(S): 05313 * [I] infoPtr : valid pointer to the listview structure 05314 * [I] iItem : index of item 05315 * [O] lppt : Upper-left corner of the image 05316 * 05317 * RETURN: 05318 * Returns a handle to the image list if successful, NULL otherwise. 05319 */ 05320 static HIMAGELIST LISTVIEW_CreateDragImage(LISTVIEW_INFO *infoPtr, INT iItem, LPPOINT lppt) 05321 { 05322 RECT rcItem; 05323 SIZE size; 05324 POINT pos; 05325 HDC hdc, hdcOrig; 05326 HBITMAP hbmp, hOldbmp; 05327 HIMAGELIST dragList = 0; 05328 TRACE("iItem=%d Count=%d\n", iItem, infoPtr->nItemCount); 05329 05330 if (iItem < 0 || iItem >= infoPtr->nItemCount || !lppt) 05331 return 0; 05332 05333 rcItem.left = LVIR_BOUNDS; 05334 if (!LISTVIEW_GetItemRect(infoPtr, iItem, &rcItem)) 05335 return 0; 05336 05337 lppt->x = rcItem.left; 05338 lppt->y = rcItem.top; 05339 05340 size.cx = rcItem.right - rcItem.left; 05341 size.cy = rcItem.bottom - rcItem.top; 05342 05343 hdcOrig = GetDC(infoPtr->hwndSelf); 05344 hdc = CreateCompatibleDC(hdcOrig); 05345 hbmp = CreateCompatibleBitmap(hdcOrig, size.cx, size.cy); 05346 hOldbmp = SelectObject(hdc, hbmp); 05347 05348 rcItem.left = rcItem.top = 0; 05349 rcItem.right = size.cx; 05350 rcItem.bottom = size.cy; 05351 FillRect(hdc, &rcItem, infoPtr->hBkBrush); 05352 05353 pos.x = pos.y = 0; 05354 if (LISTVIEW_DrawItem(infoPtr, hdc, iItem, 0, pos, infoPtr->cditemmode)) 05355 { 05356 dragList = ImageList_Create(size.cx, size.cy, ILC_COLOR, 10, 10); 05357 SelectObject(hdc, hOldbmp); 05358 ImageList_Add(dragList, hbmp, 0); 05359 } 05360 else 05361 SelectObject(hdc, hOldbmp); 05362 05363 DeleteObject(hbmp); 05364 DeleteDC(hdc); 05365 ReleaseDC(infoPtr->hwndSelf, hdcOrig); 05366 05367 TRACE("ret=%p\n", dragList); 05368 05369 return dragList; 05370 } 05371 05372 05373 /*** 05374 * DESCRIPTION: 05375 * Removes all listview items and subitems. 05376 * 05377 * PARAMETER(S): 05378 * [I] infoPtr : valid pointer to the listview structure 05379 * 05380 * RETURN: 05381 * SUCCESS : TRUE 05382 * FAILURE : FALSE 05383 */ 05384 static BOOL LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr, BOOL destroy) 05385 { 05386 NMLISTVIEW nmlv; 05387 HDPA hdpaSubItems = NULL; 05388 BOOL bSuppress; 05389 ITEMHDR *hdrItem; 05390 ITEM_INFO *lpItem; 05391 ITEM_ID *lpID; 05392 INT i, j; 05393 05394 TRACE("()\n"); 05395 05396 /* we do it directly, to avoid notifications */ 05397 ranges_clear(infoPtr->selectionRanges); 05398 infoPtr->nSelectionMark = -1; 05399 infoPtr->nFocusedItem = -1; 05400 SetRectEmpty(&infoPtr->rcFocus); 05401 /* But we are supposed to leave nHotItem as is! */ 05402 05403 05404 /* send LVN_DELETEALLITEMS notification */ 05405 ZeroMemory(&nmlv, sizeof(NMLISTVIEW)); 05406 nmlv.iItem = -1; 05407 bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv); 05408 05409 for (i = infoPtr->nItemCount - 1; i >= 0; i--) 05410 { 05411 if (!(infoPtr->dwStyle & LVS_OWNERDATA)) 05412 { 05413 /* send LVN_DELETEITEM notification, if not suppressed 05414 and if it is not a virtual listview */ 05415 if (!bSuppress) notify_deleteitem(infoPtr, i); 05416 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, i); 05417 lpItem = DPA_GetPtr(hdpaSubItems, 0); 05418 /* free id struct */ 05419 j = DPA_GetPtrIndex(infoPtr->hdpaItemIds, lpItem->id); 05420 lpID = DPA_GetPtr(infoPtr->hdpaItemIds, j); 05421 DPA_DeletePtr(infoPtr->hdpaItemIds, j); 05422 Free(lpID); 05423 /* both item and subitem start with ITEMHDR header */ 05424 for (j = 0; j < DPA_GetPtrCount(hdpaSubItems); j++) 05425 { 05426 hdrItem = DPA_GetPtr(hdpaSubItems, j); 05427 if (is_text(hdrItem->pszText)) Free(hdrItem->pszText); 05428 Free(hdrItem); 05429 } 05430 DPA_Destroy(hdpaSubItems); 05431 DPA_DeletePtr(infoPtr->hdpaItems, i); 05432 } 05433 DPA_DeletePtr(infoPtr->hdpaPosX, i); 05434 DPA_DeletePtr(infoPtr->hdpaPosY, i); 05435 infoPtr->nItemCount --; 05436 } 05437 05438 if (!destroy) 05439 { 05440 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT); 05441 LISTVIEW_UpdateScroll(infoPtr); 05442 } 05443 LISTVIEW_InvalidateList(infoPtr); 05444 05445 return TRUE; 05446 } 05447 05448 /*** 05449 * DESCRIPTION: 05450 * Scrolls, and updates the columns, when a column is changing width. 05451 * 05452 * PARAMETER(S): 05453 * [I] infoPtr : valid pointer to the listview structure 05454 * [I] nColumn : column to scroll 05455 * [I] dx : amount of scroll, in pixels 05456 * 05457 * RETURN: 05458 * None. 05459 */ 05460 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO *infoPtr, INT nColumn, INT dx) 05461 { 05462 COLUMN_INFO *lpColumnInfo; 05463 RECT rcOld, rcCol; 05464 POINT ptOrigin; 05465 INT nCol; 05466 HDITEMW hdi; 05467 05468 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) < 1) return; 05469 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1)); 05470 rcCol = lpColumnInfo->rcHeader; 05471 if (nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) 05472 rcCol.left = rcCol.right; 05473 05474 /* adjust the other columns */ 05475 hdi.mask = HDI_ORDER; 05476 if (SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, nColumn, (LPARAM)&hdi)) 05477 { 05478 INT nOrder = hdi.iOrder; 05479 for (nCol = 0; nCol < DPA_GetPtrCount(infoPtr->hdpaColumns); nCol++) 05480 { 05481 hdi.mask = HDI_ORDER; 05482 SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, nCol, (LPARAM)&hdi); 05483 if (hdi.iOrder >= nOrder) { 05484 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nCol); 05485 lpColumnInfo->rcHeader.left += dx; 05486 lpColumnInfo->rcHeader.right += dx; 05487 } 05488 } 05489 } 05490 05491 /* do not update screen if not in report mode */ 05492 if (!is_redrawing(infoPtr) || infoPtr->uView != LV_VIEW_DETAILS) return; 05493 05494 /* Need to reset the item width when inserting a new column */ 05495 infoPtr->nItemWidth += dx; 05496 05497 LISTVIEW_UpdateScroll(infoPtr); 05498 LISTVIEW_GetOrigin(infoPtr, &ptOrigin); 05499 05500 /* scroll to cover the deleted column, and invalidate for redraw */ 05501 rcOld = infoPtr->rcList; 05502 rcOld.left = ptOrigin.x + rcCol.left + dx; 05503 ScrollWindowEx(infoPtr->hwndSelf, dx, 0, &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE); 05504 } 05505 05506 /*** 05507 * DESCRIPTION: 05508 * Removes a column from the listview control. 05509 * 05510 * PARAMETER(S): 05511 * [I] infoPtr : valid pointer to the listview structure 05512 * [I] nColumn : column index 05513 * 05514 * RETURN: 05515 * SUCCESS : TRUE 05516 * FAILURE : FALSE 05517 */ 05518 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn) 05519 { 05520 RECT rcCol; 05521 05522 TRACE("nColumn=%d\n", nColumn); 05523 05524 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) == 0 05525 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE; 05526 05527 /* While the MSDN specifically says that column zero should not be deleted, 05528 what actually happens is that the column itself is deleted but no items or subitems 05529 are removed. 05530 */ 05531 05532 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol); 05533 05534 if (!SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nColumn, 0)) 05535 return FALSE; 05536 05537 Free(DPA_GetPtr(infoPtr->hdpaColumns, nColumn)); 05538 DPA_DeletePtr(infoPtr->hdpaColumns, nColumn); 05539 05540 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && nColumn) 05541 { 05542 SUBITEM_INFO *lpSubItem, *lpDelItem; 05543 HDPA hdpaSubItems; 05544 INT nItem, nSubItem, i; 05545 05546 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++) 05547 { 05548 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, nItem); 05549 nSubItem = 0; 05550 lpDelItem = 0; 05551 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++) 05552 { 05553 lpSubItem = DPA_GetPtr(hdpaSubItems, i); 05554 if (lpSubItem->iSubItem == nColumn) 05555 { 05556 nSubItem = i; 05557 lpDelItem = lpSubItem; 05558 } 05559 else if (lpSubItem->iSubItem > nColumn) 05560 { 05561 lpSubItem->iSubItem--; 05562 } 05563 } 05564 05565 /* if we found our subitem, zap it */ 05566 if (nSubItem > 0) 05567 { 05568 /* free string */ 05569 if (is_text(lpDelItem->hdr.pszText)) 05570 Free(lpDelItem->hdr.pszText); 05571 05572 /* free item */ 05573 Free(lpDelItem); 05574 05575 /* free dpa memory */ 05576 DPA_DeletePtr(hdpaSubItems, nSubItem); 05577 } 05578 } 05579 } 05580 05581 /* update the other column info */ 05582 LISTVIEW_UpdateItemSize(infoPtr); 05583 if(DPA_GetPtrCount(infoPtr->hdpaColumns) == 0) 05584 LISTVIEW_InvalidateList(infoPtr); 05585 else 05586 LISTVIEW_ScrollColumns(infoPtr, nColumn, -(rcCol.right - rcCol.left)); 05587 05588 return TRUE; 05589 } 05590 05591 /*** 05592 * DESCRIPTION: 05593 * Invalidates the listview after an item's insertion or deletion. 05594 * 05595 * PARAMETER(S): 05596 * [I] infoPtr : valid pointer to the listview structure 05597 * [I] nItem : item index 05598 * [I] dir : -1 if deleting, 1 if inserting 05599 * 05600 * RETURN: 05601 * None 05602 */ 05603 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO *infoPtr, INT nItem, INT dir) 05604 { 05605 INT nPerCol, nItemCol, nItemRow; 05606 RECT rcScroll; 05607 POINT Origin; 05608 05609 /* if we don't refresh, what's the point of scrolling? */ 05610 if (!is_redrawing(infoPtr)) return; 05611 05612 assert (abs(dir) == 1); 05613 05614 /* arrange icons if autoarrange is on */ 05615 if (is_autoarrange(infoPtr)) 05616 { 05617 BOOL arrange = TRUE; 05618 if (dir < 0 && nItem >= infoPtr->nItemCount) arrange = FALSE; 05619 if (dir > 0 && nItem == infoPtr->nItemCount - 1) arrange = FALSE; 05620 if (arrange) LISTVIEW_Arrange(infoPtr, LVA_DEFAULT); 05621 } 05622 05623 /* scrollbars need updating */ 05624 LISTVIEW_UpdateScroll(infoPtr); 05625 05626 /* figure out the item's position */ 05627 if (infoPtr->uView == LV_VIEW_DETAILS) 05628 nPerCol = infoPtr->nItemCount + 1; 05629 else if (infoPtr->uView == LV_VIEW_LIST) 05630 nPerCol = LISTVIEW_GetCountPerColumn(infoPtr); 05631 else /* LV_VIEW_ICON, or LV_VIEW_SMALLICON */ 05632 return; 05633 05634 nItemCol = nItem / nPerCol; 05635 nItemRow = nItem % nPerCol; 05636 LISTVIEW_GetOrigin(infoPtr, &Origin); 05637 05638 /* move the items below up a slot */ 05639 rcScroll.left = nItemCol * infoPtr->nItemWidth; 05640 rcScroll.top = nItemRow * infoPtr->nItemHeight; 05641 rcScroll.right = rcScroll.left + infoPtr->nItemWidth; 05642 rcScroll.bottom = nPerCol * infoPtr->nItemHeight; 05643 OffsetRect(&rcScroll, Origin.x, Origin.y); 05644 TRACE("rcScroll=%s, dx=%d\n", wine_dbgstr_rect(&rcScroll), dir * infoPtr->nItemHeight); 05645 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList)) 05646 { 05647 TRACE("Scrolling rcScroll=%s, rcList=%s\n", wine_dbgstr_rect(&rcScroll), wine_dbgstr_rect(&infoPtr->rcList)); 05648 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight, 05649 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE); 05650 } 05651 05652 /* report has only that column, so we're done */ 05653 if (infoPtr->uView == LV_VIEW_DETAILS) return; 05654 05655 /* now for LISTs, we have to deal with the columns to the right */ 05656 rcScroll.left = (nItemCol + 1) * infoPtr->nItemWidth; 05657 rcScroll.top = 0; 05658 rcScroll.right = (infoPtr->nItemCount / nPerCol + 1) * infoPtr->nItemWidth; 05659 rcScroll.bottom = nPerCol * infoPtr->nItemHeight; 05660 OffsetRect(&rcScroll, Origin.x, Origin.y); 05661 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList)) 05662 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight, 05663 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE); 05664 } 05665 05666 /*** 05667 * DESCRIPTION: 05668 * Removes an item from the listview control. 05669 * 05670 * PARAMETER(S): 05671 * [I] infoPtr : valid pointer to the listview structure 05672 * [I] nItem : item index 05673 * 05674 * RETURN: 05675 * SUCCESS : TRUE 05676 * FAILURE : FALSE 05677 */ 05678 static BOOL LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem) 05679 { 05680 LVITEMW item; 05681 const BOOL is_icon = (infoPtr->uView == LV_VIEW_SMALLICON || infoPtr->uView == LV_VIEW_ICON); 05682 05683 TRACE("(nItem=%d)\n", nItem); 05684 05685 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE; 05686 05687 /* remove selection, and focus */ 05688 item.state = 0; 05689 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED; 05690 LISTVIEW_SetItemState(infoPtr, nItem, &item); 05691 05692 /* send LVN_DELETEITEM notification. */ 05693 if (!notify_deleteitem(infoPtr, nItem)) return FALSE; 05694 05695 /* we need to do this here, because we'll be deleting stuff */ 05696 if (is_icon) 05697 LISTVIEW_InvalidateItem(infoPtr, nItem); 05698 05699 if (!(infoPtr->dwStyle & LVS_OWNERDATA)) 05700 { 05701 HDPA hdpaSubItems; 05702 ITEMHDR *hdrItem; 05703 ITEM_INFO *lpItem; 05704 ITEM_ID *lpID; 05705 INT i; 05706 05707 hdpaSubItems = DPA_DeletePtr(infoPtr->hdpaItems, nItem); 05708 lpItem = DPA_GetPtr(hdpaSubItems, 0); 05709 05710 /* free id struct */ 05711 i = DPA_GetPtrIndex(infoPtr->hdpaItemIds, lpItem->id); 05712 lpID = DPA_GetPtr(infoPtr->hdpaItemIds, i); 05713 DPA_DeletePtr(infoPtr->hdpaItemIds, i); 05714 Free(lpID); 05715 for (i = 0; i < DPA_GetPtrCount(hdpaSubItems); i++) 05716 { 05717 hdrItem = DPA_GetPtr(hdpaSubItems, i); 05718 if (is_text(hdrItem->pszText)) Free(hdrItem->pszText); 05719 Free(hdrItem); 05720 } 05721 DPA_Destroy(hdpaSubItems); 05722 } 05723 05724 if (is_icon) 05725 { 05726 DPA_DeletePtr(infoPtr->hdpaPosX, nItem); 05727 DPA_DeletePtr(infoPtr->hdpaPosY, nItem); 05728 } 05729 05730 infoPtr->nItemCount--; 05731 LISTVIEW_ShiftIndices(infoPtr, nItem, -1); 05732 05733 /* now is the invalidation fun */ 05734 if (!is_icon) 05735 LISTVIEW_ScrollOnInsert(infoPtr, nItem, -1); 05736 return TRUE; 05737 } 05738 05739 05740 /*** 05741 * DESCRIPTION: 05742 * Callback implementation for editlabel control 05743 * 05744 * PARAMETER(S): 05745 * [I] infoPtr : valid pointer to the listview structure 05746 * [I] storeText : store edit box text as item text 05747 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI 05748 * 05749 * RETURN: 05750 * SUCCESS : TRUE 05751 * FAILURE : FALSE 05752 */ 05753 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, BOOL storeText, BOOL isW) 05754 { 05755 HWND hwndSelf = infoPtr->hwndSelf; 05756 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 }; 05757 NMLVDISPINFOW dispInfo; 05758 INT editedItem = infoPtr->nEditLabelItem; 05759 BOOL same; 05760 WCHAR *pszText = NULL; 05761 BOOL res; 05762 05763 if (storeText) 05764 { 05765 DWORD len = isW ? GetWindowTextLengthW(infoPtr->hwndEdit) : GetWindowTextLengthA(infoPtr->hwndEdit); 05766 05767 if (len) 05768 { 05769 if ((pszText = Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR))))) 05770 { 05771 if (isW) GetWindowTextW(infoPtr->hwndEdit, pszText, len+1); 05772 else GetWindowTextA(infoPtr->hwndEdit, (CHAR*)pszText, len+1); 05773 } 05774 } 05775 } 05776 05777 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW); 05778 05779 ZeroMemory(&dispInfo, sizeof(dispInfo)); 05780 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT; 05781 dispInfo.item.iItem = editedItem; 05782 dispInfo.item.iSubItem = 0; 05783 dispInfo.item.stateMask = ~0; 05784 dispInfo.item.pszText = szDispText; 05785 dispInfo.item.cchTextMax = DISP_TEXT_SIZE; 05786 if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) 05787 { 05788 res = FALSE; 05789 goto cleanup; 05790 } 05791 05792 if (isW) 05793 same = (lstrcmpW(dispInfo.item.pszText, pszText) == 0); 05794 else 05795 { 05796 LPWSTR tmp = textdupTtoW(pszText, FALSE); 05797 same = (lstrcmpW(dispInfo.item.pszText, tmp) == 0); 05798 textfreeT(tmp, FALSE); 05799 } 05800 05801 /* add the text from the edit in */ 05802 dispInfo.item.mask |= LVIF_TEXT; 05803 dispInfo.item.pszText = same ? NULL : pszText; 05804 dispInfo.item.cchTextMax = textlenT(dispInfo.item.pszText, isW); 05805 05806 /* Do we need to update the Item Text */ 05807 res = notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW); 05808 05809 infoPtr->nEditLabelItem = -1; 05810 infoPtr->hwndEdit = 0; 05811 05812 if (!res) goto cleanup; 05813 05814 if (!IsWindow(hwndSelf)) 05815 { 05816 res = FALSE; 05817 goto cleanup; 05818 } 05819 if (!pszText) return TRUE; 05820 if (same) 05821 { 05822 res = TRUE; 05823 goto cleanup; 05824 } 05825 05826 if (!(infoPtr->dwStyle & LVS_OWNERDATA)) 05827 { 05828 HDPA hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, editedItem); 05829 ITEM_INFO* lpItem = DPA_GetPtr(hdpaSubItems, 0); 05830 if (lpItem && lpItem->hdr.pszText == LPSTR_TEXTCALLBACKW) 05831 { 05832 LISTVIEW_InvalidateItem(infoPtr, editedItem); 05833 res = TRUE; 05834 goto cleanup; 05835 } 05836 } 05837 05838 ZeroMemory(&dispInfo, sizeof(dispInfo)); 05839 dispInfo.item.mask = LVIF_TEXT; 05840 dispInfo.item.iItem = editedItem; 05841 dispInfo.item.iSubItem = 0; 05842 dispInfo.item.pszText = pszText; 05843 dispInfo.item.cchTextMax = textlenT(pszText, isW); 05844 res = LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW); 05845 05846 cleanup: 05847 Free(pszText); 05848 05849 return res; 05850 } 05851 05852 /*** 05853 * DESCRIPTION: 05854 * Subclassed edit control windproc function 05855 * 05856 * PARAMETER(S): 05857 * [I] hwnd : the edit window handle 05858 * [I] uMsg : the message that is to be processed 05859 * [I] wParam : first message parameter 05860 * [I] lParam : second message parameter 05861 * [I] isW : TRUE if input is Unicode 05862 * 05863 * RETURN: 05864 * Zero. 05865 */ 05866 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL isW) 05867 { 05868 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(GetParent(hwnd), 0); 05869 BOOL save = TRUE; 05870 05871 TRACE("(hwnd=%p, uMsg=%x, wParam=%lx, lParam=%lx, isW=%d)\n", 05872 hwnd, uMsg, wParam, lParam, isW); 05873 05874 switch (uMsg) 05875 { 05876 case WM_GETDLGCODE: 05877 return DLGC_WANTARROWS | DLGC_WANTALLKEYS; 05878 05879 case WM_DESTROY: 05880 { 05881 WNDPROC editProc = infoPtr->EditWndProc; 05882 infoPtr->EditWndProc = 0; 05883 SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (DWORD_PTR)editProc); 05884 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW); 05885 } 05886 05887 case WM_KEYDOWN: 05888 if (VK_ESCAPE == (INT)wParam) 05889 { 05890 save = FALSE; 05891 break; 05892 } 05893 else if (VK_RETURN == (INT)wParam) 05894 break; 05895 05896 default: 05897 return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW); 05898 } 05899 05900 /* kill the edit */ 05901 if (infoPtr->hwndEdit) 05902 LISTVIEW_EndEditLabelT(infoPtr, save, isW); 05903 05904 SendMessageW(hwnd, WM_CLOSE, 0, 0); 05905 return 0; 05906 } 05907 05908 /*** 05909 * DESCRIPTION: 05910 * Subclassed edit control Unicode windproc function 05911 * 05912 * PARAMETER(S): 05913 * [I] hwnd : the edit window handle 05914 * [I] uMsg : the message that is to be processed 05915 * [I] wParam : first message parameter 05916 * [I] lParam : second message parameter 05917 * 05918 * RETURN: 05919 */ 05920 static LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 05921 { 05922 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE); 05923 } 05924 05925 /*** 05926 * DESCRIPTION: 05927 * Subclassed edit control ANSI windproc function 05928 * 05929 * PARAMETER(S): 05930 * [I] hwnd : the edit window handle 05931 * [I] uMsg : the message that is to be processed 05932 * [I] wParam : first message parameter 05933 * [I] lParam : second message parameter 05934 * 05935 * RETURN: 05936 */ 05937 static LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 05938 { 05939 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE); 05940 } 05941 05942 /*** 05943 * DESCRIPTION: 05944 * Creates a subclassed edit control 05945 * 05946 * PARAMETER(S): 05947 * [I] infoPtr : valid pointer to the listview structure 05948 * [I] text : initial text for the edit 05949 * [I] style : the window style 05950 * [I] isW : TRUE if input is Unicode 05951 * 05952 * RETURN: 05953 */ 05954 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, BOOL isW) 05955 { 05956 static const DWORD style = WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|ES_AUTOHSCROLL|WS_BORDER|WS_VISIBLE; 05957 HINSTANCE hinst = (HINSTANCE)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_HINSTANCE); 05958 HWND hedit; 05959 05960 TRACE("(%p, text=%s, isW=%d)\n", infoPtr, debugtext_t(text, isW), isW); 05961 05962 /* window will be resized and positioned after LVN_BEGINLABELEDIT */ 05963 if (isW) 05964 hedit = CreateWindowW(WC_EDITW, text, style, 0, 0, 0, 0, infoPtr->hwndSelf, 0, hinst, 0); 05965 else 05966 hedit = CreateWindowA(WC_EDITA, (LPCSTR)text, style, 0, 0, 0, 0, infoPtr->hwndSelf, 0, hinst, 0); 05967 05968 if (!hedit) return 0; 05969 05970 infoPtr->EditWndProc = (WNDPROC) 05971 (isW ? SetWindowLongPtrW(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcW) : 05972 SetWindowLongPtrA(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcA) ); 05973 05974 SendMessageW(hedit, WM_SETFONT, (WPARAM)infoPtr->hFont, FALSE); 05975 SendMessageW(hedit, EM_SETLIMITTEXT, DISP_TEXT_SIZE-1, 0); 05976 05977 return hedit; 05978 } 05979 05980 /*** 05981 * DESCRIPTION: 05982 * Begin in place editing of specified list view item 05983 * 05984 * PARAMETER(S): 05985 * [I] infoPtr : valid pointer to the listview structure 05986 * [I] nItem : item index 05987 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII 05988 * 05989 * RETURN: 05990 * SUCCESS : TRUE 05991 * FAILURE : FALSE 05992 */ 05993 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW) 05994 { 05995 WCHAR disptextW[DISP_TEXT_SIZE] = { 0 }; 05996 HWND hwndSelf = infoPtr->hwndSelf; 05997 NMLVDISPINFOW dispInfo; 05998 HFONT hOldFont = NULL; 05999 TEXTMETRICW tm; 06000 RECT rect; 06001 SIZE sz; 06002 HDC hdc; 06003 06004 TRACE("(nItem=%d, isW=%d)\n", nItem, isW); 06005 06006 if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0; 06007 06008 /* remove existing edit box */ 06009 if (infoPtr->hwndEdit) 06010 { 06011 SetFocus(infoPtr->hwndSelf); 06012 infoPtr->hwndEdit = 0; 06013 } 06014 06015 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0; 06016 06017 infoPtr->nEditLabelItem = nItem; 06018 06019 LISTVIEW_SetSelection(infoPtr, nItem); 06020 LISTVIEW_SetItemFocus(infoPtr, nItem); 06021 LISTVIEW_InvalidateItem(infoPtr, nItem); 06022 06023 rect.left = LVIR_LABEL; 06024 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0; 06025 06026 ZeroMemory(&dispInfo, sizeof(dispInfo)); 06027 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT; 06028 dispInfo.item.iItem = nItem; 06029 dispInfo.item.iSubItem = 0; 06030 dispInfo.item.stateMask = ~0; 06031 dispInfo.item.pszText = disptextW; 06032 dispInfo.item.cchTextMax = DISP_TEXT_SIZE; 06033 if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0; 06034 06035 infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, isW); 06036 if (!infoPtr->hwndEdit) return 0; 06037 06038 if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW)) 06039 { 06040 if (!IsWindow(hwndSelf)) 06041 return 0; 06042 SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0); 06043 infoPtr->hwndEdit = 0; 06044 return 0; 06045 } 06046 06047 TRACE("disp text=%s\n", debugtext_t(dispInfo.item.pszText, isW)); 06048 06049 /* position and display edit box */ 06050 hdc = GetDC(infoPtr->hwndSelf); 06051 06052 /* select the font to get appropriate metric dimensions */ 06053 if (infoPtr->hFont) 06054 hOldFont = SelectObject(hdc, infoPtr->hFont); 06055 06056 /* use real edit box content, it could be altered during LVN_BEGINLABELEDIT notification */ 06057 GetWindowTextW(infoPtr->hwndEdit, disptextW, DISP_TEXT_SIZE); 06058 TRACE("edit box text=%s\n", debugstr_w(disptextW)); 06059 06060 /* get string length in pixels */ 06061 GetTextExtentPoint32W(hdc, disptextW, lstrlenW(disptextW), &sz); 06062 06063 /* add extra spacing for the next character */ 06064 GetTextMetricsW(hdc, &tm); 06065 sz.cx += tm.tmMaxCharWidth * 2; 06066 06067 if (infoPtr->hFont) 06068 SelectObject(hdc, hOldFont); 06069 06070 ReleaseDC(infoPtr->hwndSelf, hdc); 06071 06072 sz.cy = rect.bottom - rect.top + 2; 06073 rect.left -= 2; 06074 rect.top -= 1; 06075 TRACE("moving edit=(%d,%d)-(%d,%d)\n", rect.left, rect.top, sz.cx, sz.cy); 06076 MoveWindow(infoPtr->hwndEdit, rect.left, rect.top, sz.cx, sz.cy, FALSE); 06077 ShowWindow(infoPtr->hwndEdit, SW_NORMAL); 06078 SetFocus(infoPtr->hwndEdit); 06079 SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1); 06080 return infoPtr->hwndEdit; 06081 } 06082 06083 06084 /*** 06085 * DESCRIPTION: 06086 * Ensures the specified item is visible, scrolling into view if necessary. 06087 * 06088 * PARAMETER(S): 06089 * [I] infoPtr : valid pointer to the listview structure 06090 * [I] nItem : item index 06091 * [I] bPartial : partially or entirely visible 06092 * 06093 * RETURN: 06094 * SUCCESS : TRUE 06095 * FAILURE : FALSE 06096 */ 06097 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial) 06098 { 06099 INT nScrollPosHeight = 0; 06100 INT nScrollPosWidth = 0; 06101 INT nHorzAdjust = 0; 06102 INT nVertAdjust = 0; 06103 INT nHorzDiff = 0; 06104 INT nVertDiff = 0; 06105 RECT rcItem, rcTemp; 06106 06107 rcItem.left = LVIR_BOUNDS; 06108 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE; 06109 06110 if (bPartial && IntersectRect(&rcTemp, &infoPtr->rcList, &rcItem)) return TRUE; 06111 06112 if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right) 06113 { 06114 /* scroll left/right, but in LV_VIEW_DETAILS mode */ 06115 if (infoPtr->uView == LV_VIEW_LIST) 06116 nScrollPosWidth = infoPtr->nItemWidth; 06117 else if ((infoPtr->uView == LV_VIEW_SMALLICON) || (infoPtr->uView == LV_VIEW_ICON)) 06118 nScrollPosWidth = 1; 06119 06120 if (rcItem.left < infoPtr->rcList.left) 06121 { 06122 nHorzAdjust = -1; 06123 if (infoPtr->uView != LV_VIEW_DETAILS) nHorzDiff = rcItem.left - infoPtr->rcList.left; 06124 } 06125 else 06126 { 06127 nHorzAdjust = 1; 06128 if (infoPtr->uView != LV_VIEW_DETAILS) nHorzDiff = rcItem.right - infoPtr->rcList.right; 06129 } 06130 } 06131 06132 if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom) 06133 { 06134 /* scroll up/down, but not in LVS_LIST mode */ 06135 if (infoPtr->uView == LV_VIEW_DETAILS) 06136 nScrollPosHeight = infoPtr->nItemHeight; 06137 else if ((infoPtr->uView == LV_VIEW_ICON) || (infoPtr->uView == LV_VIEW_SMALLICON)) 06138 nScrollPosHeight = 1; 06139 06140 if (rcItem.top < infoPtr->rcList.top) 06141 { 06142 nVertAdjust = -1; 06143 if (infoPtr->uView != LV_VIEW_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top; 06144 } 06145 else 06146 { 06147 nVertAdjust = 1; 06148 if (infoPtr->uView != LV_VIEW_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom; 06149 } 06150 } 06151 06152 if (!nScrollPosWidth && !nScrollPosHeight) return TRUE; 06153 06154 if (nScrollPosWidth) 06155 { 06156 INT diff = nHorzDiff / nScrollPosWidth; 06157 if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust; 06158 LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff); 06159 } 06160 06161 if (nScrollPosHeight) 06162 { 06163 INT diff = nVertDiff / nScrollPosHeight; 06164 if (nVertDiff % nScrollPosHeight) diff += nVertAdjust; 06165 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff); 06166 } 06167 06168 return TRUE; 06169 } 06170 06171 /*** 06172 * DESCRIPTION: 06173 * Searches for an item with specific characteristics. 06174 * 06175 * PARAMETER(S): 06176 * [I] hwnd : window handle 06177 * [I] nStart : base item index 06178 * [I] lpFindInfo : item information to look for 06179 * 06180 * RETURN: 06181 * SUCCESS : index of item 06182 * FAILURE : -1 06183 */ 06184 static INT LISTVIEW_FindItemW(const LISTVIEW_INFO *infoPtr, INT nStart, 06185 const LVFINDINFOW *lpFindInfo) 06186 { 06187 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' }; 06188 BOOL bWrap = FALSE, bNearest = FALSE; 06189 INT nItem = nStart + 1, nLast = infoPtr->nItemCount, nNearestItem = -1; 06190 ULONG xdist, ydist, dist, mindist = 0x7fffffff; 06191 POINT Position, Destination; 06192 LVITEMW lvItem; 06193 06194 /* Search in virtual listviews should be done by application, not by 06195 listview control, so we just send LVN_ODFINDITEMW and return the result */ 06196 if (infoPtr->dwStyle & LVS_OWNERDATA) 06197 { 06198 NMLVFINDITEMW nmlv; 06199 06200 nmlv.iStart = nStart; 06201 nmlv.lvfi = *lpFindInfo; 06202 return notify_hdr(infoPtr, LVN_ODFINDITEMW, (LPNMHDR)&nmlv.hdr); 06203 } 06204 06205 if (!lpFindInfo || nItem < 0) return -1; 06206 06207 lvItem.mask = 0; 06208 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL) || 06209 lpFindInfo->flags & LVFI_SUBSTRING) 06210 { 06211 lvItem.mask |= LVIF_TEXT; 06212 lvItem.pszText = szDispText; 06213 lvItem.cchTextMax = DISP_TEXT_SIZE; 06214 } 06215 06216 if (lpFindInfo->flags & LVFI_WRAP) 06217 bWrap = TRUE; 06218 06219 if ((lpFindInfo->flags & LVFI_NEARESTXY) && 06220 (infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON)) 06221 { 06222 POINT Origin; 06223 RECT rcArea; 06224 06225 LISTVIEW_GetOrigin(infoPtr, &Origin); 06226 Destination.x = lpFindInfo->pt.x - Origin.x; 06227 Destination.y = lpFindInfo->pt.y - Origin.y; 06228 switch(lpFindInfo->vkDirection) 06229 { 06230 case VK_DOWN: Destination.y += infoPtr->nItemHeight; break; 06231 case VK_UP: Destination.y -= infoPtr->nItemHeight; break; 06232 case VK_RIGHT: Destination.x += infoPtr->nItemWidth; break; 06233 case VK_LEFT: Destination.x -= infoPtr->nItemWidth; break; 06234 case VK_HOME: Destination.x = Destination.y = 0; break; 06235 case VK_NEXT: Destination.y += infoPtr->rcList.bottom - infoPtr->rcList.top; break; 06236 case VK_PRIOR: Destination.y -= infoPtr->rcList.bottom - infoPtr->rcList.top; break; 06237 case VK_END: 06238 LISTVIEW_GetAreaRect(infoPtr, &rcArea); 06239 Destination.x = rcArea.right; 06240 Destination.y = rcArea.bottom; 06241 break; 06242 default: ERR("Unknown vkDirection=%d\n", lpFindInfo->vkDirection); 06243 } 06244 bNearest = TRUE; 06245 } 06246 else Destination.x = Destination.y = 0; 06247 06248 /* if LVFI_PARAM is specified, all other flags are ignored */ 06249 if (lpFindInfo->flags & LVFI_PARAM) 06250 { 06251 lvItem.mask |= LVIF_PARAM; 06252 bNearest = FALSE; 06253 lvItem.mask &= ~LVIF_TEXT; 06254 } 06255 06256 again: 06257 for (; nItem < nLast; nItem++) 06258 { 06259 lvItem.iItem = nItem; 06260 lvItem.iSubItem = 0; 06261 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue; 06262 06263 if (lvItem.mask & LVIF_PARAM) 06264 { 06265 if (lpFindInfo->lParam == lvItem.lParam) 06266 return nItem; 06267 else 06268 continue; 06269 } 06270 06271 if (lvItem.mask & LVIF_TEXT) 06272 { 06273 if (lpFindInfo->flags & (LVFI_PARTIAL | LVFI_SUBSTRING)) 06274 { 06275 WCHAR *p = strstrW(lvItem.pszText, lpFindInfo->psz); 06276 if (!p || p != lvItem.pszText) continue; 06277 } 06278 else 06279 { 06280 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0) continue; 06281 } 06282 } 06283 06284 if (!bNearest) return nItem; 06285 06286 /* This is very inefficient. To do a good job here, 06287 * we need a sorted array of (x,y) item positions */ 06288 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position); 06289 06290 /* compute the distance^2 to the destination */ 06291 xdist = Destination.x - Position.x; 06292 ydist = Destination.y - Position.y; 06293 dist = xdist * xdist + ydist * ydist; 06294 06295 /* remember the distance, and item if it's closer */ 06296 if (dist < mindist) 06297 { 06298 mindist = dist; 06299 nNearestItem = nItem; 06300 } 06301 } 06302 06303 if (bWrap) 06304 { 06305 nItem = 0; 06306 nLast = min(nStart + 1, infoPtr->nItemCount); 06307 bWrap = FALSE; 06308 goto again; 06309 } 06310 06311 return nNearestItem; 06312 } 06313 06314 /*** 06315 * DESCRIPTION: 06316 * Searches for an item with specific characteristics. 06317 * 06318 * PARAMETER(S): 06319 * [I] hwnd : window handle 06320 * [I] nStart : base item index 06321 * [I] lpFindInfo : item information to look for 06322 * 06323 * RETURN: 06324 * SUCCESS : index of item 06325 * FAILURE : -1 06326 */ 06327 static INT LISTVIEW_FindItemA(const LISTVIEW_INFO *infoPtr, INT nStart, 06328 const LVFINDINFOA *lpFindInfo) 06329 { 06330 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL) || 06331 lpFindInfo->flags & LVFI_SUBSTRING; 06332 LVFINDINFOW fiw; 06333 INT res; 06334 LPWSTR strW = NULL; 06335 06336 memcpy(&fiw, lpFindInfo, sizeof(fiw)); 06337 if (hasText) fiw.psz = strW = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE); 06338 res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw); 06339 textfreeT(strW, FALSE); 06340 return res; 06341 } 06342 06343 /*** 06344 * DESCRIPTION: 06345 * Retrieves column attributes. 06346 * 06347 * PARAMETER(S): 06348 * [I] infoPtr : valid pointer to the listview structure 06349 * [I] nColumn : column index 06350 * [IO] lpColumn : column information 06351 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW 06352 * otherwise it is in fact a LPLVCOLUMNA 06353 * 06354 * RETURN: 06355 * SUCCESS : TRUE 06356 * FAILURE : FALSE 06357 */ 06358 static BOOL LISTVIEW_GetColumnT(const LISTVIEW_INFO *infoPtr, INT nColumn, LPLVCOLUMNW lpColumn, BOOL isW) 06359 { 06360 COLUMN_INFO *lpColumnInfo; 06361 HDITEMW hdi; 06362 06363 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE; 06364 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn); 06365 06366 /* initialize memory */ 06367 ZeroMemory(&hdi, sizeof(hdi)); 06368 06369 if (lpColumn->mask & LVCF_TEXT) 06370 { 06371 hdi.mask |= HDI_TEXT; 06372 hdi.pszText = lpColumn->pszText; 06373 hdi.cchTextMax = lpColumn->cchTextMax; 06374 } 06375 06376 if (lpColumn->mask & LVCF_IMAGE) 06377 hdi.mask |= HDI_IMAGE; 06378 06379 if (lpColumn->mask & LVCF_ORDER) 06380 hdi.mask |= HDI_ORDER; 06381 06382 if (lpColumn->mask & LVCF_SUBITEM) 06383 hdi.mask |= HDI_LPARAM; 06384 06385 if (!SendMessageW(infoPtr->hwndHeader, isW ? HDM_GETITEMW : HDM_GETITEMA, nColumn, (LPARAM)&hdi)) return FALSE; 06386 06387 if (lpColumn->mask & LVCF_FMT) 06388 lpColumn->fmt = lpColumnInfo->fmt; 06389 06390 if (lpColumn->mask & LVCF_WIDTH) 06391 lpColumn->cx = lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left; 06392 06393 if (lpColumn->mask & LVCF_IMAGE) 06394 lpColumn->iImage = hdi.iImage; 06395 06396 if (lpColumn->mask & LVCF_ORDER) 06397 lpColumn->iOrder = hdi.iOrder; 06398 06399 if (lpColumn->mask & LVCF_SUBITEM) 06400 lpColumn->iSubItem = hdi.lParam; 06401 06402 if (lpColumn->mask & LVCF_MINWIDTH) 06403 lpColumn->cxMin = lpColumnInfo->cxMin; 06404 06405 return TRUE; 06406 } 06407 06408 06409 static BOOL LISTVIEW_GetColumnOrderArray(const LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray) 06410 { 06411 TRACE("iCount=%d, lpiArray=%p\n", iCount, lpiArray); 06412 06413 if (!lpiArray) 06414 return FALSE; 06415 06416 return SendMessageW(infoPtr->hwndHeader, HDM_GETORDERARRAY, iCount, (LPARAM)lpiArray); 06417 } 06418 06419 /*** 06420 * DESCRIPTION: 06421 * Retrieves the column width. 06422 * 06423 * PARAMETER(S): 06424 * [I] infoPtr : valid pointer to the listview structure 06425 * [I] int : column index 06426 * 06427 * RETURN: 06428 * SUCCESS : column width 06429 * FAILURE : zero 06430 */ 06431 static INT LISTVIEW_GetColumnWidth(const LISTVIEW_INFO *infoPtr, INT nColumn) 06432 { 06433 INT nColumnWidth = 0; 06434 HDITEMW hdItem; 06435 06436 TRACE("nColumn=%d\n", nColumn); 06437 06438 /* we have a 'column' in LIST and REPORT mode only */ 06439 switch(infoPtr->uView) 06440 { 06441 case LV_VIEW_LIST: 06442 nColumnWidth = infoPtr->nItemWidth; 06443 break; 06444 case LV_VIEW_DETAILS: 06445 /* We are not using LISTVIEW_GetHeaderRect as this data is updated only after a HDN_ITEMCHANGED. 06446 * There is an application that subclasses the listview, calls LVM_GETCOLUMNWIDTH in the 06447 * HDN_ITEMCHANGED handler and goes into infinite recursion if it receives old data. 06448 * 06449 * TODO: should we do the same in LVM_GETCOLUMN? 06450 */ 06451 hdItem.mask = HDI_WIDTH; 06452 if (!SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, nColumn, (LPARAM)&hdItem)) 06453 { 06454 WARN("(%p): HDM_GETITEMW failed for item %d\n", infoPtr->hwndSelf, nColumn); 06455 return 0; 06456 } 06457 nColumnWidth = hdItem.cxy; 06458 break; 06459 } 06460 06461 TRACE("nColumnWidth=%d\n", nColumnWidth); 06462 return nColumnWidth; 06463 } 06464 06465 /*** 06466 * DESCRIPTION: 06467 * In list or report display mode, retrieves the number of items that can fit 06468 * vertically in the visible area. In icon or small icon display mode, 06469 * retrieves the total number of visible items. 06470 * 06471 * PARAMETER(S): 06472 * [I] infoPtr : valid pointer to the listview structure 06473 * 06474 * RETURN: 06475 * Number of fully visible items. 06476 */ 06477 static INT LISTVIEW_GetCountPerPage(const LISTVIEW_INFO *infoPtr) 06478 { 06479 switch (infoPtr->uView) 06480 { 06481 case LV_VIEW_ICON: 06482 case LV_VIEW_SMALLICON: 06483 return infoPtr->nItemCount; 06484 case LV_VIEW_DETAILS: 06485 return LISTVIEW_GetCountPerColumn(infoPtr); 06486 case LV_VIEW_LIST: 06487 return LISTVIEW_GetCountPerRow(infoPtr) * LISTVIEW_GetCountPerColumn(infoPtr); 06488 } 06489 assert(FALSE); 06490 return 0; 06491 } 06492 06493 /*** 06494 * DESCRIPTION: 06495 * Retrieves an image list handle. 06496 * 06497 * PARAMETER(S): 06498 * [I] infoPtr : valid pointer to the listview structure 06499 * [I] nImageList : image list identifier 06500 * 06501 * RETURN: 06502 * SUCCESS : image list handle 06503 * FAILURE : NULL 06504 */ 06505 static HIMAGELIST LISTVIEW_GetImageList(const LISTVIEW_INFO *infoPtr, INT nImageList) 06506 { 06507 switch (nImageList) 06508 { 06509 case LVSIL_NORMAL: return infoPtr->himlNormal; 06510 case LVSIL_SMALL: return infoPtr->himlSmall; 06511 case LVSIL_STATE: return infoPtr->himlState; 06512 case LVSIL_GROUPHEADER: 06513 FIXME("LVSIL_GROUPHEADER not supported\n"); 06514 break; 06515 default: 06516 WARN("got unknown imagelist index - %d\n", nImageList); 06517 } 06518 return NULL; 06519 } 06520 06521 /* LISTVIEW_GetISearchString */ 06522 06523 /*** 06524 * DESCRIPTION: 06525 * Retrieves item attributes. 06526 * 06527 * PARAMETER(S): 06528 * [I] hwnd : window handle 06529 * [IO] lpLVItem : item info 06530 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW, 06531 * if FALSE, then lpLVItem is a LPLVITEMA. 06532 * 06533 * NOTE: 06534 * This is the internal 'GetItem' interface -- it tries to 06535 * be smart and avoid text copies, if possible, by modifying 06536 * lpLVItem->pszText to point to the text string. Please note 06537 * that this is not always possible (e.g. OWNERDATA), so on 06538 * entry you *must* supply valid values for pszText, and cchTextMax. 06539 * The only difference to the documented interface is that upon 06540 * return, you should use *only* the lpLVItem->pszText, rather than 06541 * the buffer pointer you provided on input. Most code already does 06542 * that, so it's not a problem. 06543 * For the two cases when the text must be copied (that is, 06544 * for LVM_GETITEM, and LVM_GETITEMTEXT), use LISTVIEW_GetItemExtT. 06545 * 06546 * RETURN: 06547 * SUCCESS : TRUE 06548 * FAILURE : FALSE 06549 */ 06550 static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW) 06551 { 06552 ITEMHDR callbackHdr = { LPSTR_TEXTCALLBACKW, I_IMAGECALLBACK }; 06553 NMLVDISPINFOW dispInfo; 06554 ITEM_INFO *lpItem; 06555 ITEMHDR* pItemHdr; 06556 HDPA hdpaSubItems; 06557 INT isubitem; 06558 06559 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW); 06560 06561 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount) 06562 return FALSE; 06563 06564 if (lpLVItem->mask == 0) return TRUE; 06565 TRACE("mask=%x\n", lpLVItem->mask); 06566 06567 /* make a local copy */ 06568 isubitem = lpLVItem->iSubItem; 06569 06570 /* a quick optimization if all we're asked is the focus state 06571 * these queries are worth optimising since they are common, 06572 * and can be answered in constant time, without the heavy accesses */ 06573 if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIS_FOCUSED) && 06574 !(infoPtr->uCallbackMask & LVIS_FOCUSED) ) 06575 { 06576 lpLVItem->state = 0; 06577 if (infoPtr->nFocusedItem == lpLVItem->iItem) 06578 lpLVItem->state |= LVIS_FOCUSED; 06579 return TRUE; 06580 } 06581 06582 ZeroMemory(&dispInfo, sizeof(dispInfo)); 06583 06584 /* if the app stores all the data, handle it separately */ 06585 if (infoPtr->dwStyle & LVS_OWNERDATA) 06586 { 06587 dispInfo.item.state = 0; 06588 06589 /* apparently, we should not callback for lParam in LVS_OWNERDATA */ 06590 if ((lpLVItem->mask & ~(LVIF_STATE | LVIF_PARAM)) || 06591 ((lpLVItem->mask & LVIF_STATE) && (infoPtr->uCallbackMask & lpLVItem->stateMask))) 06592 { 06593 UINT mask = lpLVItem->mask; 06594 06595 /* NOTE: copy only fields which we _know_ are initialized, some apps 06596 * depend on the uninitialized fields being 0 */ 06597 dispInfo.item.mask = lpLVItem->mask & ~LVIF_PARAM; 06598 dispInfo.item.iItem = lpLVItem->iItem; 06599 dispInfo.item.iSubItem = isubitem; 06600 if (lpLVItem->mask & LVIF_TEXT) 06601 { 06602 if (lpLVItem->mask & LVIF_NORECOMPUTE) 06603 /* reset mask */ 06604 dispInfo.item.mask &= ~(LVIF_TEXT | LVIF_NORECOMPUTE); 06605 else 06606 { 06607 dispInfo.item.pszText = lpLVItem->pszText; 06608 dispInfo.item.cchTextMax = lpLVItem->cchTextMax; 06609 } 06610 } 06611 if (lpLVItem->mask & LVIF_STATE) 06612 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask; 06613 /* could be zeroed on LVIF_NORECOMPUTE case */ 06614 if (dispInfo.item.mask) 06615 { 06616 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW); 06617 dispInfo.item.stateMask = lpLVItem->stateMask; 06618 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS)) 06619 { 06620 /* full size structure expected - _WIN32IE >= 0x560 */ 06621 *lpLVItem = dispInfo.item; 06622 } 06623 else if (lpLVItem->mask & LVIF_INDENT) 06624 { 06625 /* indent member expected - _WIN32IE >= 0x300 */ 06626 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iGroupId )); 06627 } 06628 else 06629 { 06630 /* minimal structure expected */ 06631 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iIndent )); 06632 } 06633 lpLVItem->mask = mask; 06634 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW)); 06635 } 06636 } 06637 06638 /* make sure lParam is zeroed out */ 06639 if (lpLVItem->mask & LVIF_PARAM) lpLVItem->lParam = 0; 06640 06641 /* callback marked pointer required here */ 06642 if ((lpLVItem->mask & LVIF_TEXT) && (lpLVItem->mask & LVIF_NORECOMPUTE)) 06643 lpLVItem->pszText = LPSTR_TEXTCALLBACKW; 06644 06645 /* we store only a little state, so if we're not asked, we're done */ 06646 if (!(lpLVItem->mask & LVIF_STATE) || isubitem) return TRUE; 06647 06648 /* if focus is handled by us, report it */ 06649 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED ) 06650 { 06651 lpLVItem->state &= ~LVIS_FOCUSED; 06652 if (infoPtr->nFocusedItem == lpLVItem->iItem) 06653 lpLVItem->state |= LVIS_FOCUSED; 06654 } 06655 06656 /* and do the same for selection, if we handle it */ 06657 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED ) 06658 { 06659 lpLVItem->state &= ~LVIS_SELECTED; 06660 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem)) 06661 lpLVItem->state |= LVIS_SELECTED; 06662 } 06663 06664 return TRUE; 06665 } 06666 06667 /* find the item and subitem structures before we proceed */ 06668 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem); 06669 lpItem = DPA_GetPtr(hdpaSubItems, 0); 06670 assert (lpItem); 06671 06672 if (isubitem) 06673 { 06674 SUBITEM_INFO *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, isubitem); 06675 pItemHdr = lpSubItem ? &lpSubItem->hdr : &callbackHdr; 06676 if (!lpSubItem) 06677 { 06678 WARN(" iSubItem invalid (%08x), ignored.\n", isubitem); 06679 isubitem = 0; 06680 } 06681 } 06682 else 06683 pItemHdr = &lpItem->hdr; 06684 06685 /* Do we need to query the state from the app? */ 06686 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && isubitem == 0) 06687 { 06688 dispInfo.item.mask |= LVIF_STATE; 06689 dispInfo.item.stateMask = infoPtr->uCallbackMask; 06690 } 06691 06692 /* Do we need to enquire about the image? */ 06693 if ((lpLVItem->mask & LVIF_IMAGE) && pItemHdr->iImage == I_IMAGECALLBACK && 06694 (isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES))) 06695 { 06696 dispInfo.item.mask |= LVIF_IMAGE; 06697 dispInfo.item.iImage = I_IMAGECALLBACK; 06698 } 06699 06700 /* Only items support indentation */ 06701 if ((lpLVItem->mask & LVIF_INDENT) && lpItem->iIndent == I_INDENTCALLBACK && 06702 (isubitem == 0)) 06703 { 06704 dispInfo.item.mask |= LVIF_INDENT; 06705 dispInfo.item.iIndent = I_INDENTCALLBACK; 06706 } 06707 06708 /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */ 06709 if ((lpLVItem->mask & LVIF_TEXT) && !(lpLVItem->mask & LVIF_NORECOMPUTE) && 06710 !is_text(pItemHdr->pszText)) 06711 { 06712 dispInfo.item.mask |= LVIF_TEXT; 06713 dispInfo.item.pszText = lpLVItem->pszText; 06714 dispInfo.item.cchTextMax = lpLVItem->cchTextMax; 06715 if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0) 06716 *dispInfo.item.pszText = '\0'; 06717 } 06718 06719 /* If we don't have all the requested info, query the application */ 06720 if (dispInfo.item.mask) 06721 { 06722 dispInfo.item.iItem = lpLVItem->iItem; 06723 dispInfo.item.iSubItem = lpLVItem->iSubItem; /* yes: the original subitem */ 06724 dispInfo.item.lParam = lpItem->lParam; 06725 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW); 06726 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW)); 06727 } 06728 06729 /* we should not store values for subitems */ 06730 if (isubitem) dispInfo.item.mask &= ~LVIF_DI_SETITEM; 06731 06732 /* Now, handle the iImage field */ 06733 if (dispInfo.item.mask & LVIF_IMAGE) 06734 { 06735 lpLVItem->iImage = dispInfo.item.iImage; 06736 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->iImage == I_IMAGECALLBACK) 06737 pItemHdr->iImage = dispInfo.item.iImage; 06738 } 06739 else if (lpLVItem->mask & LVIF_IMAGE) 06740 { 06741 if(isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES)) 06742 lpLVItem->iImage = pItemHdr->iImage; 06743 else 06744 lpLVItem->iImage = 0; 06745 } 06746 06747 /* The pszText field */ 06748 if (dispInfo.item.mask & LVIF_TEXT) 06749 { 06750 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText) 06751 textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW); 06752 06753 lpLVItem->pszText = dispInfo.item.pszText; 06754 } 06755 else if (lpLVItem->mask & LVIF_TEXT) 06756 { 06757 /* if LVN_GETDISPINFO's disabled with LVIF_NORECOMPUTE return callback placeholder */ 06758 if (isW || !is_text(pItemHdr->pszText)) lpLVItem->pszText = pItemHdr->pszText; 06759 else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax); 06760 } 06761 06762 /* Next is the lParam field */ 06763 if (dispInfo.item.mask & LVIF_PARAM) 06764 { 06765 lpLVItem->lParam = dispInfo.item.lParam; 06766 if ((dispInfo.item.mask & LVIF_DI_SETITEM)) 06767 lpItem->lParam = dispInfo.item.lParam; 06768 } 06769 else if (lpLVItem->mask & LVIF_PARAM) 06770 lpLVItem->lParam = lpItem->lParam; 06771 06772 /* if this is a subitem, we're done */ 06773 if (isubitem) return TRUE; 06774 06775 /* ... the state field (this one is different due to uCallbackmask) */ 06776 if (lpLVItem->mask & LVIF_STATE) 06777 { 06778 lpLVItem->state = lpItem->state & lpLVItem->stateMask; 06779 if (dispInfo.item.mask & LVIF_STATE) 06780 { 06781 lpLVItem->state &= ~dispInfo.item.stateMask; 06782 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask); 06783 } 06784 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED ) 06785 { 06786 lpLVItem->state &= ~LVIS_FOCUSED; 06787 if (infoPtr->nFocusedItem == lpLVItem->iItem) 06788 lpLVItem->state |= LVIS_FOCUSED; 06789 } 06790 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED ) 06791 { 06792 lpLVItem->state &= ~LVIS_SELECTED; 06793 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem)) 06794 lpLVItem->state |= LVIS_SELECTED; 06795 } 06796 } 06797 06798 /* and last, but not least, the indent field */ 06799 if (dispInfo.item.mask & LVIF_INDENT) 06800 { 06801 lpLVItem->iIndent = dispInfo.item.iIndent; 06802 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && lpItem->iIndent == I_INDENTCALLBACK) 06803 lpItem->iIndent = dispInfo.item.iIndent; 06804 } 06805 else if (lpLVItem->mask & LVIF_INDENT) 06806 { 06807 lpLVItem->iIndent = lpItem->iIndent; 06808 } 06809 06810 return TRUE; 06811 } 06812 06813 /*** 06814 * DESCRIPTION: 06815 * Retrieves item attributes. 06816 * 06817 * PARAMETER(S): 06818 * [I] hwnd : window handle 06819 * [IO] lpLVItem : item info 06820 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW, 06821 * if FALSE, then lpLVItem is a LPLVITEMA. 06822 * 06823 * NOTE: 06824 * This is the external 'GetItem' interface -- it properly copies 06825 * the text in the provided buffer. 06826 * 06827 * RETURN: 06828 * SUCCESS : TRUE 06829 * FAILURE : FALSE 06830 */ 06831 static BOOL LISTVIEW_GetItemExtT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW) 06832 { 06833 LPWSTR pszText; 06834 BOOL bResult; 06835 06836 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount) 06837 return FALSE; 06838 06839 pszText = lpLVItem->pszText; 06840 bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW); 06841 if (bResult && (lpLVItem->mask & LVIF_TEXT) && lpLVItem->pszText != pszText) 06842 { 06843 if (lpLVItem->pszText != LPSTR_TEXTCALLBACKW) 06844 textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax); 06845 else 06846 pszText = LPSTR_TEXTCALLBACKW; 06847 } 06848 lpLVItem->pszText = pszText; 06849 06850 return bResult; 06851 } 06852 06853 06854 /*** 06855 * DESCRIPTION: 06856 * Retrieves the position (upper-left) of the listview control item. 06857 * Note that for LVS_ICON style, the upper-left is that of the icon 06858 * and not the bounding box. 06859 * 06860 * PARAMETER(S): 06861 * [I] infoPtr : valid pointer to the listview structure 06862 * [I] nItem : item index 06863 * [O] lpptPosition : coordinate information 06864 * 06865 * RETURN: 06866 * SUCCESS : TRUE 06867 * FAILURE : FALSE 06868 */ 06869 static BOOL LISTVIEW_GetItemPosition(const LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition) 06870 { 06871 POINT Origin; 06872 06873 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition); 06874 06875 if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE; 06876 06877 LISTVIEW_GetOrigin(infoPtr, &Origin); 06878 LISTVIEW_GetItemOrigin(infoPtr, nItem, lpptPosition); 06879 06880 if (infoPtr->uView == LV_VIEW_ICON) 06881 { 06882 lpptPosition->x += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2; 06883 lpptPosition->y += ICON_TOP_PADDING; 06884 } 06885 lpptPosition->x += Origin.x; 06886 lpptPosition->y += Origin.y; 06887 06888 TRACE (" lpptPosition=%s\n", wine_dbgstr_point(lpptPosition)); 06889 return TRUE; 06890 } 06891 06892 06893 /*** 06894 * DESCRIPTION: 06895 * Retrieves the bounding rectangle for a listview control item. 06896 * 06897 * PARAMETER(S): 06898 * [I] infoPtr : valid pointer to the listview structure 06899 * [I] nItem : item index 06900 * [IO] lprc : bounding rectangle coordinates 06901 * lprc->left specifies the portion of the item for which the bounding 06902 * rectangle will be retrieved. 06903 * 06904 * LVIR_BOUNDS Returns the bounding rectangle of the entire item, 06905 * including the icon and label. 06906 * * 06907 * * For LVS_ICON 06908 * * Experiment shows that native control returns: 06909 * * width = min (48, length of text line) 06910 * * .left = position.x - (width - iconsize.cx)/2 06911 * * .right = .left + width 06912 * * height = #lines of text * ntmHeight + icon height + 8 06913 * * .top = position.y - 2 06914 * * .bottom = .top + height 06915 * * separation between items .y = itemSpacing.cy - height 06916 * * .x = itemSpacing.cx - width 06917 * LVIR_ICON Returns the bounding rectangle of the icon or small icon. 06918 * * 06919 * * For LVS_ICON 06920 * * Experiment shows that native control returns: 06921 * * width = iconSize.cx + 16 06922 * * .left = position.x - (width - iconsize.cx)/2 06923 * * .right = .left + width 06924 * * height = iconSize.cy + 4 06925 * * .top = position.y - 2 06926 * * .bottom = .top + height 06927 * * separation between items .y = itemSpacing.cy - height 06928 * * .x = itemSpacing.cx - width 06929 * LVIR_LABEL Returns the bounding rectangle of the item text. 06930 * * 06931 * * For LVS_ICON 06932 * * Experiment shows that native control returns: 06933 * * width = text length 06934 * * .left = position.x - width/2 06935 * * .right = .left + width 06936 * * height = ntmH * linecount + 2 06937 * * .top = position.y + iconSize.cy + 6 06938 * * .bottom = .top + height 06939 * * separation between items .y = itemSpacing.cy - height 06940 * * .x = itemSpacing.cx - width 06941 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL 06942 * rectangles, but excludes columns in report view. 06943 * 06944 * RETURN: 06945 * SUCCESS : TRUE 06946 * FAILURE : FALSE 06947 * 06948 * NOTES 06949 * Note that the bounding rectangle of the label in the LVS_ICON view depends 06950 * upon whether the window has the focus currently and on whether the item 06951 * is the one with the focus. Ensure that the control's record of which 06952 * item has the focus agrees with the items' records. 06953 */ 06954 static BOOL LISTVIEW_GetItemRect(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc) 06955 { 06956 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' }; 06957 BOOL doLabel = TRUE, oversizedBox = FALSE; 06958 POINT Position, Origin; 06959 LVITEMW lvItem; 06960 LONG mode; 06961 06962 TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc); 06963 06964 if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE; 06965 06966 LISTVIEW_GetOrigin(infoPtr, &Origin); 06967 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position); 06968 06969 /* Be smart and try to figure out the minimum we have to do */ 06970 if (lprc->left == LVIR_ICON) doLabel = FALSE; 06971 if (infoPtr->uView == LV_VIEW_DETAILS && lprc->left == LVIR_BOUNDS) doLabel = FALSE; 06972 if (infoPtr->uView == LV_VIEW_ICON && lprc->left != LVIR_ICON && 06973 infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED)) 06974 oversizedBox = TRUE; 06975 06976 /* get what we need from the item before hand, so we make 06977 * only one request. This can speed up things, if data 06978 * is stored on the app side */ 06979 lvItem.mask = 0; 06980 if (infoPtr->uView == LV_VIEW_DETAILS) lvItem.mask |= LVIF_INDENT; 06981 if (doLabel) lvItem.mask |= LVIF_TEXT; 06982 lvItem.iItem = nItem; 06983 lvItem.iSubItem = 0; 06984 lvItem.pszText = szDispText; 06985 lvItem.cchTextMax = DISP_TEXT_SIZE; 06986 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE; 06987 /* we got the state already up, simulate it here, to avoid a reget */ 06988 if (infoPtr->uView == LV_VIEW_ICON && (lprc->left != LVIR_ICON)) 06989 { 06990 lvItem.mask |= LVIF_STATE; 06991 lvItem.stateMask = LVIS_FOCUSED; 06992 lvItem.state = (oversizedBox ? LVIS_FOCUSED : 0); 06993 } 06994 06995 if (infoPtr->uView == LV_VIEW_DETAILS && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && lprc->left == LVIR_SELECTBOUNDS) 06996 lprc->left = LVIR_BOUNDS; 06997 06998 mode = lprc->left; 06999 switch(lprc->left) 07000 { 07001 case LVIR_ICON: 07002 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL, NULL); 07003 break; 07004 07005 case LVIR_LABEL: 07006 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, NULL, NULL, lprc); 07007 break; 07008 07009 case LVIR_BOUNDS: 07010 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL, NULL); 07011 break; 07012 07013 case LVIR_SELECTBOUNDS: 07014 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, lprc, NULL, NULL, NULL); 07015 break; 07016 07017 default: 07018 WARN("Unknown value: %d\n", lprc->left); 07019 return FALSE; 07020 } 07021 07022 if (infoPtr->uView == LV_VIEW_DETAILS) 07023 { 07024 if (mode != LVIR_BOUNDS) 07025 OffsetRect(lprc, Origin.x + LISTVIEW_GetColumnInfo(infoPtr, 0)->rcHeader.left, 07026 Position.y + Origin.y); 07027 else 07028 OffsetRect(lprc, Origin.x, Position.y + Origin.y); 07029 } 07030 else 07031 OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y); 07032 07033 TRACE(" rect=%s\n", wine_dbgstr_rect(lprc)); 07034 07035 return TRUE; 07036 } 07037 07038 /*** 07039 * DESCRIPTION: 07040 * Retrieves the spacing between listview control items. 07041 * 07042 * PARAMETER(S): 07043 * [I] infoPtr : valid pointer to the listview structure 07044 * [IO] lprc : rectangle to receive the output 07045 * on input, lprc->top = nSubItem 07046 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL 07047 * 07048 * NOTE: for subItem = 0, we should return the bounds of the _entire_ item, 07049 * not only those of the first column. 07050 * Fortunately, LISTVIEW_GetItemMetrics does the right thing. 07051 * 07052 * RETURN: 07053 * TRUE: success 07054 * FALSE: failure 07055 */ 07056 static BOOL LISTVIEW_GetSubItemRect(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc) 07057 { 07058 POINT Position, Origin; 07059 LVITEMW lvItem; 07060 INT nColumn; 07061 07062 if (!lprc) return FALSE; 07063 07064 nColumn = lprc->top; 07065 07066 TRACE("(nItem=%d, nSubItem=%d, type=%d)\n", nItem, lprc->top, lprc->left); 07067 /* On WinNT, a subitem of '0' calls LISTVIEW_GetItemRect */ 07068 if (lprc->top == 0) 07069 return LISTVIEW_GetItemRect(infoPtr, nItem, lprc); 07070 07071 if (infoPtr->uView != LV_VIEW_DETAILS) return FALSE; 07072 07073 /* special case for header items */ 07074 if (nItem == -1) 07075 { 07076 if (lprc->left != LVIR_BOUNDS) 07077 { 07078 FIXME("Only LVIR_BOUNDS is implemented for header, got %d\n", lprc->left); 07079 return FALSE; 07080 } 07081 07082 if (infoPtr->hwndHeader) 07083 return SendMessageW(infoPtr->hwndHeader, HDM_GETITEMRECT, lprc->top, (LPARAM)lprc); 07084 else 07085 { 07086 memset(lprc, 0, sizeof(RECT)); 07087 return TRUE; 07088 } 07089 } 07090 07091 if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &Position)) return FALSE; 07092 LISTVIEW_GetOrigin(infoPtr, &Origin); 07093 07094 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE; 07095 07096 lvItem.mask = 0; 07097 lvItem.iItem = nItem; 07098 lvItem.iSubItem = nColumn; 07099 07100 switch(lprc->left) 07101 { 07102 case LVIR_ICON: 07103 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL, NULL); 07104 break; 07105 07106 case LVIR_LABEL: 07107 case LVIR_BOUNDS: 07108 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL, NULL); 07109 break; 07110 07111 default: 07112 ERR("Unknown bounds=%d\n", lprc->left); 07113 return FALSE; 07114 } 07115 07116 OffsetRect(lprc, Origin.x, Position.y); 07117 TRACE("return rect %s\n", wine_dbgstr_rect(lprc)); 07118 07119 return TRUE; 07120 } 07121 07122 /*** 07123 * DESCRIPTION: 07124 * Retrieves the spacing between listview control items. 07125 * 07126 * PARAMETER(S): 07127 * [I] infoPtr : valid pointer to the listview structure 07128 * [I] bSmall : flag for small or large icon 07129 * 07130 * RETURN: 07131 * Horizontal + vertical spacing 07132 */ 07133 static LONG LISTVIEW_GetItemSpacing(const LISTVIEW_INFO *infoPtr, BOOL bSmall) 07134 { 07135 LONG lResult; 07136 07137 if (!bSmall) 07138 { 07139 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy); 07140 } 07141 else 07142 { 07143 if (infoPtr->uView == LV_VIEW_ICON) 07144 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING); 07145 else 07146 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight); 07147 } 07148 return lResult; 07149 } 07150 07151 /*** 07152 * DESCRIPTION: 07153 * Retrieves the state of a listview control item. 07154 * 07155 * PARAMETER(S): 07156 * [I] infoPtr : valid pointer to the listview structure 07157 * [I] nItem : item index 07158 * [I] uMask : state mask 07159 * 07160 * RETURN: 07161 * State specified by the mask. 07162 */ 07163 static UINT LISTVIEW_GetItemState(const LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask) 07164 { 07165 LVITEMW lvItem; 07166 07167 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0; 07168 07169 lvItem.iItem = nItem; 07170 lvItem.iSubItem = 0; 07171 lvItem.mask = LVIF_STATE; 07172 lvItem.stateMask = uMask; 07173 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0; 07174 07175 return lvItem.state & uMask; 07176 } 07177 07178 /*** 07179 * DESCRIPTION: 07180 * Retrieves the text of a listview control item or subitem. 07181 * 07182 * PARAMETER(S): 07183 * [I] hwnd : window handle 07184 * [I] nItem : item index 07185 * [IO] lpLVItem : item information 07186 * [I] isW : TRUE if lpLVItem is Unicode 07187 * 07188 * RETURN: 07189 * SUCCESS : string length 07190 * FAILURE : 0 07191 */ 07192 static INT LISTVIEW_GetItemTextT(const LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW) 07193 { 07194 if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0; 07195 07196 lpLVItem->mask = LVIF_TEXT; 07197 lpLVItem->iItem = nItem; 07198 if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0; 07199 07200 return textlenT(lpLVItem->pszText, isW); 07201 } 07202 07203 /*** 07204 * DESCRIPTION: 07205 * Searches for an item based on properties + relationships. 07206 * 07207 * PARAMETER(S): 07208 * [I] infoPtr : valid pointer to the listview structure 07209 * [I] nItem : item index 07210 * [I] uFlags : relationship flag 07211 * 07212 * RETURN: 07213 * SUCCESS : item index 07214 * FAILURE : -1 07215 */ 07216 static INT LISTVIEW_GetNextItem(const LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags) 07217 { 07218 UINT uMask = 0; 07219 LVFINDINFOW lvFindInfo; 07220 INT nCountPerColumn; 07221 INT nCountPerRow; 07222 INT i; 07223 07224 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount); 07225 if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1; 07226 07227 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo)); 07228 07229 if (uFlags & LVNI_CUT) 07230 uMask |= LVIS_CUT; 07231 07232 if (uFlags & LVNI_DROPHILITED) 07233 uMask |= LVIS_DROPHILITED; 07234 07235 if (uFlags & LVNI_FOCUSED) 07236 uMask |= LVIS_FOCUSED; 07237 07238 if (uFlags & LVNI_SELECTED) 07239 uMask |= LVIS_SELECTED; 07240 07241 /* if we're asked for the focused item, that's only one, 07242 * so it's worth optimizing */ 07243 if (uFlags & LVNI_FOCUSED) 07244 { 07245 if ((LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) != uMask) return -1; 07246 return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem; 07247 } 07248 07249 if (uFlags & LVNI_ABOVE) 07250 { 07251 if ((infoPtr->uView == LV_VIEW_LIST) || (infoPtr->uView == LV_VIEW_DETAILS)) 07252 { 07253 while (nItem >= 0) 07254 { 07255 nItem--; 07256 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask) 07257 return nItem; 07258 } 07259 } 07260 else 07261 { 07262 /* Special case for autoarrange - move 'til the top of a list */ 07263 if (is_autoarrange(infoPtr)) 07264 { 07265 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr); 07266 while (nItem - nCountPerRow >= 0) 07267 { 07268 nItem -= nCountPerRow; 07269 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask) 07270 return nItem; 07271 } 07272 return -1; 07273 } 07274 lvFindInfo.flags = LVFI_NEARESTXY; 07275 lvFindInfo.vkDirection = VK_UP; 07276 LISTVIEW_GetItemPosition(infoPtr, nItem, &lvFindInfo.pt); 07277 while ((nItem = LISTVIEW_FindItemW(infoPtr, nItem, &lvFindInfo)) != -1) 07278 { 07279 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask) 07280 return nItem; 07281 } 07282 } 07283 } 07284 else if (uFlags & LVNI_BELOW) 07285 { 07286 if ((infoPtr->uView == LV_VIEW_LIST) || (infoPtr->uView == LV_VIEW_DETAILS)) 07287 { 07288 while (nItem < infoPtr->nItemCount) 07289 { 07290 nItem++; 07291 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask) 07292 return nItem; 07293 } 07294 } 07295 else 07296 { 07297 /* Special case for autoarrange - move 'til the bottom of a list */ 07298 if (is_autoarrange(infoPtr)) 07299 { 07300 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr); 07301 while (nItem + nCountPerRow < infoPtr->nItemCount ) 07302 { 07303 nItem += nCountPerRow; 07304 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask) 07305 return nItem; 07306 } 07307 return -1; 07308 } 07309 lvFindInfo.flags = LVFI_NEARESTXY; 07310 lvFindInfo.vkDirection = VK_DOWN; 07311 LISTVIEW_GetItemPosition(infoPtr, nItem, &lvFindInfo.pt); 07312 while ((nItem = LISTVIEW_FindItemW(infoPtr, nItem, &lvFindInfo)) != -1) 07313 { 07314 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask) 07315 return nItem; 07316 } 07317 } 07318 } 07319 else if (uFlags & LVNI_TOLEFT) 07320 { 07321 if (infoPtr->uView == LV_VIEW_LIST) 07322 { 07323 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr); 07324 while (nItem - nCountPerColumn >= 0) 07325 { 07326 nItem -= nCountPerColumn; 07327 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask) 07328 return nItem; 07329 } 07330 } 07331 else if ((infoPtr->uView == LV_VIEW_SMALLICON) || (infoPtr->uView == LV_VIEW_ICON)) 07332 { 07333 /* Special case for autoarrange - move 'til the beginning of a row */ 07334 if (is_autoarrange(infoPtr)) 07335 { 07336 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr); 07337 while (nItem % nCountPerRow > 0) 07338 { 07339 nItem --; 07340 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask) 07341 return nItem; 07342 } 07343 return -1; 07344 } 07345 lvFindInfo.flags = LVFI_NEARESTXY; 07346 lvFindInfo.vkDirection = VK_LEFT; 07347 LISTVIEW_GetItemPosition(infoPtr, nItem, &lvFindInfo.pt); 07348 while ((nItem = LISTVIEW_FindItemW(infoPtr, nItem, &lvFindInfo)) != -1) 07349 { 07350 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask) 07351 return nItem; 07352 } 07353 } 07354 } 07355 else if (uFlags & LVNI_TORIGHT) 07356 { 07357 if (infoPtr->uView == LV_VIEW_LIST) 07358 { 07359 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr); 07360 while (nItem + nCountPerColumn < infoPtr->nItemCount) 07361 { 07362 nItem += nCountPerColumn; 07363 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask) 07364 return nItem; 07365 } 07366 } 07367 else if ((infoPtr->uView == LV_VIEW_SMALLICON) || (infoPtr->uView == LV_VIEW_ICON)) 07368 { 07369 /* Special case for autoarrange - move 'til the end of a row */ 07370 if (is_autoarrange(infoPtr)) 07371 { 07372 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr); 07373 while (nItem % nCountPerRow < nCountPerRow - 1 ) 07374 { 07375 nItem ++; 07376 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask) 07377 return nItem; 07378 } 07379 return -1; 07380 } 07381 lvFindInfo.flags = LVFI_NEARESTXY; 07382 lvFindInfo.vkDirection = VK_RIGHT; 07383 LISTVIEW_GetItemPosition(infoPtr, nItem, &lvFindInfo.pt); 07384 while ((nItem = LISTVIEW_FindItemW(infoPtr, nItem, &lvFindInfo)) != -1) 07385 { 07386 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask) 07387 return nItem; 07388 } 07389 } 07390 } 07391 else 07392 { 07393 nItem++; 07394 07395 /* search by index */ 07396 for (i = nItem; i < infoPtr->nItemCount; i++) 07397 { 07398 if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask) 07399 return i; 07400 } 07401 } 07402 07403 return -1; 07404 } 07405 07406 /* LISTVIEW_GetNumberOfWorkAreas */ 07407 07408 /*** 07409 * DESCRIPTION: 07410 * Retrieves the origin coordinates when in icon or small icon display mode. 07411 * 07412 * PARAMETER(S): 07413 * [I] infoPtr : valid pointer to the listview structure 07414 * [O] lpptOrigin : coordinate information 07415 * 07416 * RETURN: 07417 * None. 07418 */ 07419 static void LISTVIEW_GetOrigin(const LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin) 07420 { 07421 INT nHorzPos = 0, nVertPos = 0; 07422 SCROLLINFO scrollInfo; 07423 07424 scrollInfo.cbSize = sizeof(SCROLLINFO); 07425 scrollInfo.fMask = SIF_POS; 07426 07427 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) 07428 nHorzPos = scrollInfo.nPos; 07429 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) 07430 nVertPos = scrollInfo.nPos; 07431 07432 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos); 07433 07434 lpptOrigin->x = infoPtr->rcList.left; 07435 lpptOrigin->y = infoPtr->rcList.top; 07436 if (infoPtr->uView == LV_VIEW_LIST) 07437 nHorzPos *= infoPtr->nItemWidth; 07438 else if (infoPtr->uView == LV_VIEW_DETAILS) 07439 nVertPos *= infoPtr->nItemHeight; 07440 07441 lpptOrigin->x -= nHorzPos; 07442 lpptOrigin->y -= nVertPos; 07443 07444 TRACE(" origin=%s\n", wine_dbgstr_point(lpptOrigin)); 07445 } 07446 07447 /*** 07448 * DESCRIPTION: 07449 * Retrieves the width of a string. 07450 * 07451 * PARAMETER(S): 07452 * [I] hwnd : window handle 07453 * [I] lpszText : text string to process 07454 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise 07455 * 07456 * RETURN: 07457 * SUCCESS : string width (in pixels) 07458 * FAILURE : zero 07459 */ 07460 static INT LISTVIEW_GetStringWidthT(const LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW) 07461 { 07462 SIZE stringSize; 07463 07464 stringSize.cx = 0; 07465 if (is_text(lpszText)) 07466 { 07467 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont; 07468 HDC hdc = GetDC(infoPtr->hwndSelf); 07469 HFONT hOldFont = SelectObject(hdc, hFont); 07470 07471 if (isW) 07472 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize); 07473 else 07474 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize); 07475 SelectObject(hdc, hOldFont); 07476 ReleaseDC(infoPtr->hwndSelf, hdc); 07477 } 07478 return stringSize.cx; 07479 } 07480 07481 /*** 07482 * DESCRIPTION: 07483 * Determines which listview item is located at the specified position. 07484 * 07485 * PARAMETER(S): 07486 * [I] infoPtr : valid pointer to the listview structure 07487 * [IO] lpht : hit test information 07488 * [I] subitem : fill out iSubItem. 07489 * [I] select : return the index only if the hit selects the item 07490 * 07491 * NOTE: 07492 * (mm 20001022): We must not allow iSubItem to be touched, for 07493 * an app might pass only a structure with space up to iItem! 07494 * (MS Office 97 does that for instance in the file open dialog) 07495 * 07496 * RETURN: 07497 * SUCCESS : item index 07498 * FAILURE : -1 07499 */ 07500 static INT LISTVIEW_HitTest(const LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select) 07501 { 07502 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' }; 07503 RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch; 07504 POINT Origin, Position, opt; 07505 LVITEMW lvItem; 07506 ITERATOR i; 07507 INT iItem; 07508 07509 TRACE("(pt=%s, subitem=%d, select=%d)\n", wine_dbgstr_point(&lpht->pt), subitem, select); 07510 07511 lpht->flags = 0; 07512 lpht->iItem = -1; 07513 if (subitem) lpht->iSubItem = 0; 07514 07515 LISTVIEW_GetOrigin(infoPtr, &Origin); 07516 07517 /* set whole list relation flags */ 07518 if (subitem && infoPtr->uView == LV_VIEW_DETAILS) 07519 { 07520 /* LVM_SUBITEMHITTEST checks left bound of possible client area */ 07521 if (infoPtr->rcList.left > lpht->pt.x && Origin.x < lpht->pt.x) 07522 lpht->flags |= LVHT_TOLEFT; 07523 07524 if (lpht->pt.y < infoPtr->rcList.top && lpht->pt.y >= 0) 07525 opt.y = lpht->pt.y + infoPtr->rcList.top; 07526 else 07527 opt.y = lpht->pt.y; 07528 07529 if (infoPtr->rcList.bottom < opt.y) 07530 lpht->flags |= LVHT_BELOW; 07531 } 07532 else 07533 { 07534 if (infoPtr->rcList.left > lpht->pt.x) 07535 lpht->flags |= LVHT_TOLEFT; 07536 else if (infoPtr->rcList.right < lpht->pt.x) 07537 lpht->flags |= LVHT_TORIGHT; 07538 07539 if (infoPtr->rcList.top > lpht->pt.y) 07540 lpht->flags |= LVHT_ABOVE; 07541 else if (infoPtr->rcList.bottom < lpht->pt.y) 07542 lpht->flags |= LVHT_BELOW; 07543 } 07544 07545 /* even if item is invalid try to find subitem */ 07546 if (infoPtr->uView == LV_VIEW_DETAILS && subitem) 07547 { 07548 RECT *pRect; 07549 INT j; 07550 07551 opt.x = lpht->pt.x - Origin.x; 07552 07553 lpht->iSubItem = -1; 07554 for (j = 0; j < DPA_GetPtrCount(infoPtr->hdpaColumns); j++) 07555 { 07556 pRect = &LISTVIEW_GetColumnInfo(infoPtr, j)->rcHeader; 07557 07558 if ((opt.x >= pRect->left) && (opt.x < pRect->right)) 07559 { 07560 lpht->iSubItem = j; 07561 break; 07562 } 07563 } 07564 TRACE("lpht->iSubItem=%d\n", lpht->iSubItem); 07565 07566 /* if we're outside horizontal columns bounds there's nothing to test further */ 07567 if (lpht->iSubItem == -1) 07568 { 07569 lpht->iItem = -1; 07570 lpht->flags = LVHT_NOWHERE; 07571 return -1; 07572 } 07573 } 07574 07575 TRACE("lpht->flags=0x%x\n", lpht->flags); 07576 if (lpht->flags) return -1; 07577 07578 lpht->flags |= LVHT_NOWHERE; 07579 07580 /* first deal with the large items */ 07581 rcSearch.left = lpht->pt.x; 07582 rcSearch.top = lpht->pt.y; 07583 rcSearch.right = rcSearch.left + 1; 07584 rcSearch.bottom = rcSearch.top + 1; 07585 07586 iterator_frameditems(&i, infoPtr, &rcSearch); 07587 iterator_next(&i); /* go to first item in the sequence */ 07588 iItem = i.nItem; 07589 iterator_destroy(&i); 07590 07591 TRACE("lpht->iItem=%d\n", iItem); 07592 if (iItem == -1) return -1; 07593 07594 lvItem.mask = LVIF_STATE | LVIF_TEXT; 07595 if (infoPtr->uView == LV_VIEW_DETAILS) lvItem.mask |= LVIF_INDENT; 07596 lvItem.stateMask = LVIS_STATEIMAGEMASK; 07597 if (infoPtr->uView == LV_VIEW_ICON) lvItem.stateMask |= LVIS_FOCUSED; 07598 lvItem.iItem = iItem; 07599 lvItem.iSubItem = subitem ? lpht->iSubItem : 0; 07600 lvItem.pszText = szDispText; 07601 lvItem.cchTextMax = DISP_TEXT_SIZE; 07602 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1; 07603 if (!infoPtr->bFocus) lvItem.state &= ~LVIS_FOCUSED; 07604 07605 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, NULL, &rcIcon, &rcState, &rcLabel); 07606 LISTVIEW_GetItemOrigin(infoPtr, iItem, &Position); 07607 opt.x = lpht->pt.x - Position.x - Origin.x; 07608 07609 if (lpht->pt.y < infoPtr->rcList.top && lpht->pt.y >= 0) 07610 opt.y = lpht->pt.y - Position.y - Origin.y + infoPtr->rcList.top; 07611 else 07612 opt.y = lpht->pt.y - Position.y - Origin.y; 07613 07614 if (infoPtr->uView == LV_VIEW_DETAILS) 07615 { 07616 rcBounds = rcBox; 07617 if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) 07618 opt.x = lpht->pt.x - Origin.x; 07619 } 07620 else 07621 { 07622 UnionRect(&rcBounds, &rcIcon, &rcLabel); 07623 UnionRect(&rcBounds, &rcBounds, &rcState); 07624 } 07625 TRACE("rcBounds=%s\n", wine_dbgstr_rect(&rcBounds)); 07626 if (!PtInRect(&rcBounds, opt)) return -1; 07627 07628 if (PtInRect(&rcIcon, opt)) 07629 lpht->flags |= LVHT_ONITEMICON; 07630 else if (PtInRect(&rcLabel, opt)) 07631 lpht->flags |= LVHT_ONITEMLABEL; 07632 else if (infoPtr->himlState && PtInRect(&rcState, opt)) 07633 lpht->flags |= LVHT_ONITEMSTATEICON; 07634 /* special case for LVS_EX_FULLROWSELECT */ 07635 if (infoPtr->uView == LV_VIEW_DETAILS && infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT && 07636 !(lpht->flags & LVHT_ONITEM)) 07637 { 07638 lpht->flags = LVHT_ONITEM | LVHT_ABOVE; 07639 } 07640 if (lpht->flags & LVHT_ONITEM) 07641 lpht->flags &= ~LVHT_NOWHERE; 07642 TRACE("lpht->flags=0x%x\n", lpht->flags); 07643 07644 if (select && !(infoPtr->uView == LV_VIEW_DETAILS && 07645 ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) || 07646 (infoPtr->dwStyle & LVS_OWNERDRAWFIXED)))) 07647 { 07648 if (infoPtr->uView == LV_VIEW_DETAILS) 07649 { 07650 /* get main item bounds */ 07651 lvItem.iSubItem = 0; 07652 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, NULL, &rcIcon, &rcState, &rcLabel); 07653 UnionRect(&rcBounds, &rcIcon, &rcLabel); 07654 UnionRect(&rcBounds, &rcBounds, &rcState); 07655 } 07656 if (!PtInRect(&rcBounds, opt)) iItem = -1; 07657 } 07658 return lpht->iItem = iItem; 07659 } 07660 07661 /*** 07662 * DESCRIPTION: 07663 * Inserts a new item in the listview control. 07664 * 07665 * PARAMETER(S): 07666 * [I] infoPtr : valid pointer to the listview structure 07667 * [I] lpLVItem : item information 07668 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI 07669 * 07670 * RETURN: 07671 * SUCCESS : new item index 07672 * FAILURE : -1 07673 */ 07674 static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW) 07675 { 07676 INT nItem; 07677 HDPA hdpaSubItems; 07678 NMLISTVIEW nmlv; 07679 ITEM_INFO *lpItem; 07680 ITEM_ID *lpID; 07681 BOOL is_sorted, has_changed; 07682 LVITEMW item; 07683 HWND hwndSelf = infoPtr->hwndSelf; 07684 07685 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW); 07686 07687 if (infoPtr->dwStyle & LVS_OWNERDATA) return infoPtr->nItemCount++; 07688 07689 /* make sure it's an item, and not a subitem; cannot insert a subitem */ 07690 if (!lpLVItem || lpLVItem->iSubItem) return -1; 07691 07692 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return -1; 07693 07694 if (!(lpItem = Alloc(sizeof(ITEM_INFO)))) return -1; 07695 07696 /* insert item in listview control data structure */ 07697 if ( !(hdpaSubItems = DPA_Create(8)) ) goto fail; 07698 if ( !DPA_SetPtr(hdpaSubItems, 0, lpItem) ) assert (FALSE); 07699 07700 /* link with id struct */ 07701 if (!(lpID = Alloc(sizeof(ITEM_ID)))) goto fail; 07702 lpItem->id = lpID; 07703 lpID->item = hdpaSubItems; 07704 lpID->id = get_next_itemid(infoPtr); 07705 if ( DPA_InsertPtr(infoPtr->hdpaItemIds, infoPtr->nItemCount, lpID) == -1) goto fail; 07706 07707 is_sorted = (infoPtr->dwStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) && 07708 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText); 07709 07710 if (lpLVItem->iItem < 0 && !is_sorted) return -1; 07711 07712 /* calculate new item index */ 07713 if (is_sorted) 07714 { 07715 HDPA hItem; 07716 ITEM_INFO *item_s; 07717 INT i = 0, cmpv; 07718 07719 while (i < infoPtr->nItemCount) 07720 { 07721 hItem = DPA_GetPtr( infoPtr->hdpaItems, i); 07722 item_s = DPA_GetPtr(hItem, 0); 07723 07724 cmpv = textcmpWT(item_s->hdr.pszText, lpLVItem->pszText, isW); 07725 if (infoPtr->dwStyle & LVS_SORTDESCENDING) cmpv *= -1; 07726 07727 if (cmpv >= 0) break; 07728 i++; 07729 } 07730 nItem = i; 07731 } 07732 else 07733 nItem = min(lpLVItem->iItem, infoPtr->nItemCount); 07734 07735 TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem); 07736 nItem = DPA_InsertPtr( infoPtr->hdpaItems, nItem, hdpaSubItems ); 07737 if (nItem == -1) goto fail; 07738 infoPtr->nItemCount++; 07739 07740 /* shift indices first so they don't get tangled */ 07741 LISTVIEW_ShiftIndices(infoPtr, nItem, 1); 07742 07743 /* set the item attributes */ 07744 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS)) 07745 { 07746 /* full size structure expected - _WIN32IE >= 0x560 */ 07747 item = *lpLVItem; 07748 } 07749 else if (lpLVItem->mask & LVIF_INDENT) 07750 { 07751 /* indent member expected - _WIN32IE >= 0x300 */ 07752 memcpy(&item, lpLVItem, offsetof( LVITEMW, iGroupId )); 07753 } 07754 else 07755 { 07756 /* minimal structure expected */ 07757 memcpy(&item, lpLVItem, offsetof( LVITEMW, iIndent )); 07758 } 07759 item.iItem = nItem; 07760 if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) 07761 { 07762 item.mask |= LVIF_STATE; 07763 item.stateMask |= LVIS_STATEIMAGEMASK; 07764 item.state &= ~LVIS_STATEIMAGEMASK; 07765 item.state |= INDEXTOSTATEIMAGEMASK(1); 07766 } 07767 if (!set_main_item(infoPtr, &item, TRUE, isW, &has_changed)) goto undo; 07768 07769 /* make room for the position, if we are in the right mode */ 07770 if ((infoPtr->uView == LV_VIEW_SMALLICON) || (infoPtr->uView == LV_VIEW_ICON)) 07771 { 07772 if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1) 07773 goto undo; 07774 if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1) 07775 { 07776 DPA_DeletePtr(infoPtr->hdpaPosX, nItem); 07777 goto undo; 07778 } 07779 } 07780 07781 /* send LVN_INSERTITEM notification */ 07782 ZeroMemory(&nmlv, sizeof(NMLISTVIEW)); 07783 nmlv.iItem = nItem; 07784 nmlv.lParam = lpItem->lParam; 07785 notify_listview(infoPtr, LVN_INSERTITEM, &nmlv); 07786 if (!IsWindow(hwndSelf)) 07787 return -1; 07788 07789 /* align items (set position of each item) */ 07790 if (infoPtr->uView == LV_VIEW_SMALLICON || infoPtr->uView == LV_VIEW_ICON) 07791 { 07792 POINT pt; 07793 07794 if (infoPtr->dwStyle & LVS_ALIGNLEFT) 07795 LISTVIEW_NextIconPosLeft(infoPtr, &pt); 07796 else 07797 LISTVIEW_NextIconPosTop(infoPtr, &pt); 07798 07799 LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, TRUE); 07800 } 07801 07802 /* now is the invalidation fun */ 07803 LISTVIEW_ScrollOnInsert(infoPtr, nItem, 1); 07804 return nItem; 07805 07806 undo: 07807 LISTVIEW_ShiftIndices(infoPtr, nItem, -1); 07808 DPA_DeletePtr(infoPtr->hdpaItems, nItem); 07809 infoPtr->nItemCount--; 07810 fail: 07811 DPA_DeletePtr(hdpaSubItems, 0); 07812 DPA_Destroy (hdpaSubItems); 07813 Free (lpItem); 07814 return -1; 07815 } 07816 07817 /*** 07818 * DESCRIPTION: 07819 * Checks item visibility. 07820 * 07821 * PARAMETER(S): 07822 * [I] infoPtr : valid pointer to the listview structure 07823 * [I] nFirst : item index to check for 07824 * 07825 * RETURN: 07826 * Item visible : TRUE 07827 * Item invisible or failure : FALSE 07828 */ 07829 static BOOL LISTVIEW_IsItemVisible(const LISTVIEW_INFO *infoPtr, INT nItem) 07830 { 07831 POINT Origin, Position; 07832 RECT rcItem; 07833 HDC hdc; 07834 BOOL ret; 07835 07836 TRACE("nItem=%d\n", nItem); 07837 07838 if (nItem < 0 || nItem >= DPA_GetPtrCount(infoPtr->hdpaItems)) return FALSE; 07839 07840 LISTVIEW_GetOrigin(infoPtr, &Origin); 07841 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position); 07842 rcItem.left = Position.x + Origin.x; 07843 rcItem.top = Position.y + Origin.y; 07844 rcItem.right = rcItem.left + infoPtr->nItemWidth; 07845 rcItem.bottom = rcItem.top + infoPtr->nItemHeight; 07846 07847 hdc = GetDC(infoPtr->hwndSelf); 07848 if (!hdc) return FALSE; 07849 ret = RectVisible(hdc, &rcItem); 07850 ReleaseDC(infoPtr->hwndSelf, hdc); 07851 07852 return ret; 07853 } 07854 07855 /*** 07856 * DESCRIPTION: 07857 * Redraws a range of items. 07858 * 07859 * PARAMETER(S): 07860 * [I] infoPtr : valid pointer to the listview structure 07861 * [I] nFirst : first item 07862 * [I] nLast : last item 07863 * 07864 * RETURN: 07865 * SUCCESS : TRUE 07866 * FAILURE : FALSE 07867 */ 07868 static BOOL LISTVIEW_RedrawItems(const LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast) 07869 { 07870 INT i; 07871 07872 if (nLast < nFirst || min(nFirst, nLast) < 0 || 07873 max(nFirst, nLast) >= infoPtr->nItemCount) 07874 return FALSE; 07875 07876 for (i = nFirst; i <= nLast; i++) 07877 LISTVIEW_InvalidateItem(infoPtr, i); 07878 07879 return TRUE; 07880 } 07881 07882 /*** 07883 * DESCRIPTION: 07884 * Scroll the content of a listview. 07885 * 07886 * PARAMETER(S): 07887 * [I] infoPtr : valid pointer to the listview structure 07888 * [I] dx : horizontal scroll amount in pixels 07889 * [I] dy : vertical scroll amount in pixels 07890 * 07891 * RETURN: 07892 * SUCCESS : TRUE 07893 * FAILURE : FALSE 07894 * 07895 * COMMENTS: 07896 * If the control is in report view (LV_VIEW_DETAILS) the control can 07897 * be scrolled only in line increments. "dy" will be rounded to the 07898 * nearest number of pixels that are a whole line. Ex: if line height 07899 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7 07900 * is passed, then the scroll will be 0. (per MSDN 7/2002) 07901 * 07902 * For: (per experimentation with native control and CSpy ListView) 07903 * LV_VIEW_ICON scrolling in any direction is allowed 07904 * LV_VIEW_SMALLICON scrolling in any direction is allowed 07905 * LV_VIEW_LIST dx=1 = 1 column (horizontal only) 07906 * but will only scroll 1 column per message 07907 * no matter what the value. 07908 * dy must be 0 or FALSE returned. 07909 * LV_VIEW_DETAILS dx=1 = 1 pixel 07910 * dy= see above 07911 * 07912 */ 07913 static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy) 07914 { 07915 switch(infoPtr->uView) { 07916 case LV_VIEW_DETAILS: 07917 dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2; 07918 dy /= infoPtr->nItemHeight; 07919 break; 07920 case LV_VIEW_LIST: 07921 if (dy != 0) return FALSE; 07922 break; 07923 default: /* icon */ 07924 break; 07925 } 07926 07927 if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx); 07928 if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy); 07929 07930 return TRUE; 07931 } 07932 07933 /*** 07934 * DESCRIPTION: 07935 * Sets the background color. 07936 * 07937 * PARAMETER(S): 07938 * [I] infoPtr : valid pointer to the listview structure 07939 * [I] color : background color 07940 * 07941 * RETURN: 07942 * SUCCESS : TRUE 07943 * FAILURE : FALSE 07944 */ 07945 static BOOL LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF color) 07946 { 07947 TRACE("(color=%x)\n", color); 07948 07949 infoPtr->bDefaultBkColor = FALSE; 07950 if(infoPtr->clrBk != color) { 07951 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush); 07952 infoPtr->clrBk = color; 07953 if (color == CLR_NONE) 07954 infoPtr->hBkBrush = (HBRUSH)GetClassLongPtrW(infoPtr->hwndSelf, GCLP_HBRBACKGROUND); 07955 else 07956 { 07957 infoPtr->hBkBrush = CreateSolidBrush(color); 07958 infoPtr->dwLvExStyle &= ~LVS_EX_TRANSPARENTBKGND; 07959 } 07960 } 07961 07962 return TRUE; 07963 } 07964 07965 /* LISTVIEW_SetBkImage */ 07966 07967 /*** Helper for {Insert,Set}ColumnT *only* */ 07968 static void column_fill_hditem(const LISTVIEW_INFO *infoPtr, HDITEMW *lphdi, INT nColumn, 07969 const LVCOLUMNW *lpColumn, BOOL isW) 07970 { 07971 if (lpColumn->mask & LVCF_FMT) 07972 { 07973 /* format member is valid */ 07974 lphdi->mask |= HDI_FORMAT; 07975 07976 /* set text alignment (leftmost column must be left-aligned) */ 07977 if (nColumn == 0 || (lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT) 07978 lphdi->fmt |= HDF_LEFT; 07979 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_RIGHT) 07980 lphdi->fmt |= HDF_RIGHT; 07981 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_CENTER) 07982 lphdi->fmt |= HDF_CENTER; 07983 07984 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT) 07985 lphdi->fmt |= HDF_BITMAP_ON_RIGHT; 07986 07987 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES) 07988 { 07989 lphdi->fmt |= HDF_IMAGE; 07990 lphdi->iImage = I_IMAGECALLBACK; 07991 } 07992 07993 if (lpColumn->fmt & LVCFMT_FIXED_WIDTH) 07994 lphdi->fmt |= HDF_FIXEDWIDTH; 07995 } 07996 07997 if (lpColumn->mask & LVCF_WIDTH) 07998 { 07999 lphdi->mask |= HDI_WIDTH; 08000 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER) 08001 { 08002 /* make it fill the remainder of the controls width */ 08003 RECT rcHeader; 08004 INT item_index; 08005 08006 for(item_index = 0; item_index < (nColumn - 1); item_index++) 08007 { 08008 LISTVIEW_GetHeaderRect(infoPtr, item_index, &rcHeader); 08009 lphdi->cxy += rcHeader.right - rcHeader.left; 08010 } 08011 08012 /* retrieve the layout of the header */ 08013 GetClientRect(infoPtr->hwndSelf, &rcHeader); 08014 TRACE("start cxy=%d rcHeader=%s\n", lphdi->cxy, wine_dbgstr_rect(&rcHeader)); 08015 08016 lphdi->cxy = (rcHeader.right - rcHeader.left) - lphdi->cxy; 08017 } 08018 else 08019 lphdi->cxy = lpColumn->cx; 08020 } 08021 08022 if (lpColumn->mask & LVCF_TEXT) 08023 { 08024 lphdi->mask |= HDI_TEXT | HDI_FORMAT; 08025 lphdi->fmt |= HDF_STRING; 08026 lphdi->pszText = lpColumn->pszText; 08027 lphdi->cchTextMax = textlenT(lpColumn->pszText, isW); 08028 } 08029 08030 if (lpColumn->mask & LVCF_IMAGE) 08031 { 08032 lphdi->mask |= HDI_IMAGE; 08033 lphdi->iImage = lpColumn->iImage; 08034 } 08035 08036 if (lpColumn->mask & LVCF_ORDER) 08037 { 08038 lphdi->mask |= HDI_ORDER; 08039 lphdi->iOrder = lpColumn->iOrder; 08040 } 08041 } 08042 08043 08044 /*** 08045 * DESCRIPTION: 08046 * Inserts a new column. 08047 * 08048 * PARAMETER(S): 08049 * [I] infoPtr : valid pointer to the listview structure 08050 * [I] nColumn : column index 08051 * [I] lpColumn : column information 08052 * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise 08053 * 08054 * RETURN: 08055 * SUCCESS : new column index 08056 * FAILURE : -1 08057 */ 08058 static INT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn, 08059 const LVCOLUMNW *lpColumn, BOOL isW) 08060 { 08061 COLUMN_INFO *lpColumnInfo; 08062 INT nNewColumn; 08063 HDITEMW hdi; 08064 08065 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW); 08066 08067 if (!lpColumn || nColumn < 0) return -1; 08068 nColumn = min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns)); 08069 08070 ZeroMemory(&hdi, sizeof(HDITEMW)); 08071 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW); 08072 08073 /* 08074 * A mask not including LVCF_WIDTH turns into a mask of width, width 10 08075 * (can be seen in SPY) otherwise column never gets added. 08076 */ 08077 if (!(lpColumn->mask & LVCF_WIDTH)) { 08078 hdi.mask |= HDI_WIDTH; 08079 hdi.cxy = 10; 08080 } 08081 08082 /* 08083 * when the iSubItem is available Windows copies it to the header lParam. It seems 08084 * to happen only in LVM_INSERTCOLUMN - not in LVM_SETCOLUMN 08085 */ 08086 if (lpColumn->mask & LVCF_SUBITEM) 08087 { 08088 hdi.mask |= HDI_LPARAM; 08089 hdi.lParam = lpColumn->iSubItem; 08090 } 08091 08092 /* create header if not present */ 08093 LISTVIEW_CreateHeader(infoPtr); 08094 if (!(LVS_NOCOLUMNHEADER & infoPtr->dwStyle) && 08095 (infoPtr->uView == LV_VIEW_DETAILS) && (WS_VISIBLE & infoPtr->dwStyle)) 08096 { 08097 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL); 08098 } 08099 08100 /* insert item in header control */ 08101 nNewColumn = SendMessageW(infoPtr->hwndHeader, 08102 isW ? HDM_INSERTITEMW : HDM_INSERTITEMA, 08103 nColumn, (LPARAM)&hdi); 08104 if (nNewColumn == -1) return -1; 08105 if (nNewColumn != nColumn) ERR("nColumn=%d, nNewColumn=%d\n", nColumn, nNewColumn); 08106 08107 /* create our own column info */ 08108 if (!(lpColumnInfo = Alloc(sizeof(COLUMN_INFO)))) goto fail; 08109 if (DPA_InsertPtr(infoPtr->hdpaColumns, nNewColumn, lpColumnInfo) == -1) goto fail; 08110 08111 if (lpColumn->mask & LVCF_FMT) lpColumnInfo->fmt = lpColumn->fmt; 08112 if (lpColumn->mask & LVCF_MINWIDTH) lpColumnInfo->cxMin = lpColumn->cxMin; 08113 if (!SendMessageW(infoPtr->hwndHeader, HDM_GETITEMRECT, nNewColumn, (LPARAM)&lpColumnInfo->rcHeader)) 08114 goto fail; 08115 08116 /* now we have to actually adjust the data */ 08117 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0) 08118 { 08119 SUBITEM_INFO *lpSubItem; 08120 HDPA hdpaSubItems; 08121 INT nItem, i; 08122 LVITEMW item; 08123 BOOL changed; 08124 08125 item.iSubItem = nNewColumn; 08126 item.mask = LVIF_TEXT | LVIF_IMAGE; 08127 item.iImage = I_IMAGECALLBACK; 08128 item.pszText = LPSTR_TEXTCALLBACKW; 08129 08130 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++) 08131 { 08132 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, nItem); 08133 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++) 08134 { 08135 lpSubItem = DPA_GetPtr(hdpaSubItems, i); 08136 if (lpSubItem->iSubItem >= nNewColumn) 08137 lpSubItem->iSubItem++; 08138 } 08139 08140 /* add new subitem for each item */ 08141 item.iItem = nItem; 08142 set_sub_item(infoPtr, &item, isW, &changed); 08143 } 08144 } 08145 08146 /* make space for the new column */ 08147 LISTVIEW_ScrollColumns(infoPtr, nNewColumn + 1, lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left); 08148 LISTVIEW_UpdateItemSize(infoPtr); 08149 08150 return nNewColumn; 08151 08152 fail: 08153 if (nNewColumn != -1) SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nNewColumn, 0); 08154 if (lpColumnInfo) 08155 { 08156 DPA_DeletePtr(infoPtr->hdpaColumns, nNewColumn); 08157 Free(lpColumnInfo); 08158 } 08159 return -1; 08160 } 08161 08162 /*** 08163 * DESCRIPTION: 08164 * Sets the attributes of a header item. 08165 * 08166 * PARAMETER(S): 08167 * [I] infoPtr : valid pointer to the listview structure 08168 * [I] nColumn : column index 08169 * [I] lpColumn : column attributes 08170 * [I] isW: if TRUE, then lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA 08171 * 08172 * RETURN: 08173 * SUCCESS : TRUE 08174 * FAILURE : FALSE 08175 */ 08176 static BOOL LISTVIEW_SetColumnT(const LISTVIEW_INFO *infoPtr, INT nColumn, 08177 const LVCOLUMNW *lpColumn, BOOL isW) 08178 { 08179 HDITEMW hdi, hdiget; 08180 BOOL bResult; 08181 08182 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW); 08183 08184 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE; 08185 08186 ZeroMemory(&hdi, sizeof(HDITEMW)); 08187 if (lpColumn->mask & LVCF_FMT) 08188 { 08189 hdi.mask |= HDI_FORMAT; 08190 hdiget.mask = HDI_FORMAT; 08191 if (SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, nColumn, (LPARAM)&hdiget)) 08192 hdi.fmt = hdiget.fmt & HDF_STRING; 08193 } 08194 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW); 08195 08196 /* set header item attributes */ 08197 bResult = SendMessageW(infoPtr->hwndHeader, isW ? HDM_SETITEMW : HDM_SETITEMA, nColumn, (LPARAM)&hdi); 08198 if (!bResult) return FALSE; 08199 08200 if (lpColumn->mask & LVCF_FMT) 08201 { 08202 COLUMN_INFO *lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn); 08203 INT oldFmt = lpColumnInfo->fmt; 08204 08205 lpColumnInfo->fmt = lpColumn->fmt; 08206 if ((oldFmt ^ lpColumn->fmt) & (LVCFMT_JUSTIFYMASK | LVCFMT_IMAGE)) 08207 { 08208 if (infoPtr->uView == LV_VIEW_DETAILS) LISTVIEW_InvalidateColumn(infoPtr, nColumn); 08209 } 08210 } 08211 08212 if (lpColumn->mask & LVCF_MINWIDTH) 08213 LISTVIEW_GetColumnInfo(infoPtr, nColumn)->cxMin = lpColumn->cxMin; 08214 08215 return TRUE; 08216 } 08217 08218 /*** 08219 * DESCRIPTION: 08220 * Sets the column order array 08221 * 08222 * PARAMETERS: 08223 * [I] infoPtr : valid pointer to the listview structure 08224 * [I] iCount : number of elements in column order array 08225 * [I] lpiArray : pointer to column order array 08226 * 08227 * RETURN: 08228 * SUCCESS : TRUE 08229 * FAILURE : FALSE 08230 */ 08231 static BOOL LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, const INT *lpiArray) 08232 { 08233 TRACE("iCount %d lpiArray %p\n", iCount, lpiArray); 08234 08235 if (!lpiArray || !IsWindow(infoPtr->hwndHeader)) return FALSE; 08236 08237 infoPtr->colRectsDirty = TRUE; 08238 08239 return SendMessageW(infoPtr->hwndHeader, HDM_SETORDERARRAY, iCount, (LPARAM)lpiArray); 08240 } 08241 08242 /*** 08243 * DESCRIPTION: 08244 * Sets the width of a column 08245 * 08246 * PARAMETERS: 08247 * [I] infoPtr : valid pointer to the listview structure 08248 * [I] nColumn : column index 08249 * [I] cx : column width 08250 * 08251 * RETURN: 08252 * SUCCESS : TRUE 08253 * FAILURE : FALSE 08254 */ 08255 static BOOL LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn, INT cx) 08256 { 08257 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 }; 08258 INT max_cx = 0; 08259 HDITEMW hdi; 08260 08261 TRACE("(nColumn=%d, cx=%d\n", nColumn, cx); 08262 08263 /* set column width only if in report or list mode */ 08264 if (infoPtr->uView != LV_VIEW_DETAILS && infoPtr->uView != LV_VIEW_LIST) return FALSE; 08265 08266 /* take care of invalid cx values */ 08267 if(infoPtr->uView == LV_VIEW_DETAILS && cx < -2) cx = LVSCW_AUTOSIZE; 08268 else if (infoPtr->uView == LV_VIEW_LIST && cx < 1) return FALSE; 08269 08270 /* resize all columns if in LV_VIEW_LIST mode */ 08271 if(infoPtr->uView == LV_VIEW_LIST) 08272 { 08273 infoPtr->nItemWidth = cx; 08274 LISTVIEW_InvalidateList(infoPtr); 08275 return TRUE; 08276 } 08277 08278 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE; 08279 08280 if (cx == LVSCW_AUTOSIZE || (cx == LVSCW_AUTOSIZE_USEHEADER && nColumn < DPA_GetPtrCount(infoPtr->hdpaColumns) -1)) 08281 { 08282 INT nLabelWidth; 08283 LVITEMW lvItem; 08284 08285 lvItem.mask = LVIF_TEXT; 08286 lvItem.iItem = 0; 08287 lvItem.iSubItem = nColumn; 08288 lvItem.pszText = szDispText; 08289 lvItem.cchTextMax = DISP_TEXT_SIZE; 08290 for (; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++) 08291 { 08292 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue; 08293 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE); 08294 if (max_cx < nLabelWidth) max_cx = nLabelWidth; 08295 } 08296 if (infoPtr->himlSmall && (nColumn == 0 || (LISTVIEW_GetColumnInfo(infoPtr, nColumn)->fmt & LVCFMT_IMAGE))) 08297 max_cx += infoPtr->iconSize.cx; 08298 max_cx += TRAILING_LABEL_PADDING; 08299 } 08300 08301 /* autosize based on listview items width */ 08302 if(cx == LVSCW_AUTOSIZE) 08303 cx = max_cx; 08304 else if(cx == LVSCW_AUTOSIZE_USEHEADER) 08305 { 08306 /* if iCol is the last column make it fill the remainder of the controls width */ 08307 if(nColumn == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1) 08308 { 08309 RECT rcHeader; 08310 POINT Origin; 08311 08312 LISTVIEW_GetOrigin(infoPtr, &Origin); 08313 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader); 08314 08315 cx = infoPtr->rcList.right - Origin.x - rcHeader.left; 08316 } 08317 else 08318 { 08319 /* Despite what the MS docs say, if this is not the last 08320 column, then MS resizes the column to the width of the 08321 largest text string in the column, including headers 08322 and items. This is different from LVSCW_AUTOSIZE in that 08323 LVSCW_AUTOSIZE ignores the header string length. */ 08324 cx = 0; 08325 08326 /* retrieve header text */ 08327 hdi.mask = HDI_TEXT; 08328 hdi.cchTextMax = DISP_TEXT_SIZE; 08329 hdi.pszText = szDispText; 08330 if (SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, nColumn, (LPARAM)&hdi)) 08331 { 08332 HDC hdc = GetDC(infoPtr->hwndSelf); 08333 HFONT old_font = SelectObject(hdc, (HFONT)SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0, 0)); 08334 SIZE size; 08335 08336 if (GetTextExtentPoint32W(hdc, hdi.pszText, lstrlenW(hdi.pszText), &size)) 08337 cx = size.cx + TRAILING_HEADER_PADDING; 08338 /* FIXME: Take into account the header image, if one is present */ 08339 SelectObject(hdc, old_font); 08340 ReleaseDC(infoPtr->hwndSelf, hdc); 08341 } 08342 cx = max (cx, max_cx); 08343 } 08344 } 08345 08346 if (cx < 0) return FALSE; 08347 08348 /* call header to update the column change */ 08349 hdi.mask = HDI_WIDTH; 08350 hdi.cxy = max(cx, LISTVIEW_GetColumnInfo(infoPtr, nColumn)->cxMin); 08351 TRACE("hdi.cxy=%d\n", hdi.cxy); 08352 return SendMessageW(infoPtr->hwndHeader, HDM_SETITEMW, nColumn, (LPARAM)&hdi); 08353 } 08354 08355 /*** 08356 * Creates the checkbox imagelist. Helper for LISTVIEW_SetExtendedListViewStyle 08357 * 08358 */ 08359 static HIMAGELIST LISTVIEW_CreateCheckBoxIL(const LISTVIEW_INFO *infoPtr) 08360 { 08361 HDC hdc_wnd, hdc; 08362 HBITMAP hbm_im, hbm_mask, hbm_orig; 08363 RECT rc; 08364 HBRUSH hbr_white = GetStockObject(WHITE_BRUSH); 08365 HBRUSH hbr_black = GetStockObject(BLACK_BRUSH); 08366 HIMAGELIST himl; 08367 08368 himl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 08369 ILC_COLOR | ILC_MASK, 2, 2); 08370 hdc_wnd = GetDC(infoPtr->hwndSelf); 08371 hdc = CreateCompatibleDC(hdc_wnd); 08372 hbm_im = CreateCompatibleBitmap(hdc_wnd, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON)); 08373 hbm_mask = CreateBitmap(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 1, 1, NULL); 08374 ReleaseDC(infoPtr->hwndSelf, hdc_wnd); 08375 08376 rc.left = rc.top = 0; 08377 rc.right = GetSystemMetrics(SM_CXSMICON); 08378 rc.bottom = GetSystemMetrics(SM_CYSMICON); 08379 08380 hbm_orig = SelectObject(hdc, hbm_mask); 08381 FillRect(hdc, &rc, hbr_white); 08382 InflateRect(&rc, -2, -2); 08383 FillRect(hdc, &rc, hbr_black); 08384 08385 SelectObject(hdc, hbm_im); 08386 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO); 08387 SelectObject(hdc, hbm_orig); 08388 ImageList_Add(himl, hbm_im, hbm_mask); 08389 08390 SelectObject(hdc, hbm_im); 08391 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO | DFCS_CHECKED); 08392 SelectObject(hdc, hbm_orig); 08393 ImageList_Add(himl, hbm_im, hbm_mask); 08394 08395 DeleteObject(hbm_mask); 08396 DeleteObject(hbm_im); 08397 DeleteDC(hdc); 08398 08399 return himl; 08400 } 08401 08402 /*** 08403 * DESCRIPTION: 08404 * Sets the extended listview style. 08405 * 08406 * PARAMETERS: 08407 * [I] infoPtr : valid pointer to the listview structure 08408 * [I] dwMask : mask 08409 * [I] dwStyle : style 08410 * 08411 * RETURN: 08412 * SUCCESS : previous style 08413 * FAILURE : 0 08414 */ 08415 static DWORD LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD mask, DWORD ex_style) 08416 { 08417 DWORD old_ex_style = infoPtr->dwLvExStyle; 08418 08419 TRACE("mask=0x%08x, ex_style=0x%08x\n", mask, ex_style); 08420 08421 /* set new style */ 08422 if (mask) 08423 infoPtr->dwLvExStyle = (old_ex_style & ~mask) | (ex_style & mask); 08424 else 08425 infoPtr->dwLvExStyle = ex_style; 08426 08427 if((infoPtr->dwLvExStyle ^ old_ex_style) & LVS_EX_CHECKBOXES) 08428 { 08429 HIMAGELIST himl = 0; 08430 if(infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) 08431 { 08432 LVITEMW item; 08433 item.mask = LVIF_STATE; 08434 item.stateMask = LVIS_STATEIMAGEMASK; 08435 item.state = INDEXTOSTATEIMAGEMASK(1); 08436 LISTVIEW_SetItemState(infoPtr, -1, &item); 08437 08438 himl = LISTVIEW_CreateCheckBoxIL(infoPtr); 08439 if(!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS)) 08440 ImageList_Destroy(infoPtr->himlState); 08441 } 08442 himl = LISTVIEW_SetImageList(infoPtr, LVSIL_STATE, himl); 08443 /* checkbox list replaces previous custom list or... */ 08444 if(((infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) && 08445 !(infoPtr->dwStyle & LVS_SHAREIMAGELISTS)) || 08446 /* ...previous was checkbox list */ 08447 (old_ex_style & LVS_EX_CHECKBOXES)) 08448 ImageList_Destroy(himl); 08449 } 08450 08451 if((infoPtr->dwLvExStyle ^ old_ex_style) & LVS_EX_HEADERDRAGDROP) 08452 { 08453 DWORD style; 08454 08455 /* if not already created */ 08456 LISTVIEW_CreateHeader(infoPtr); 08457 08458 style = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE); 08459 if (infoPtr->dwLvExStyle & LVS_EX_HEADERDRAGDROP) 08460 style |= HDS_DRAGDROP; 08461 else 08462 style &= ~HDS_DRAGDROP; 08463 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, style); 08464 } 08465 08466 /* GRIDLINES adds decoration at top so changes sizes */ 08467 if((infoPtr->dwLvExStyle ^ old_ex_style) & LVS_EX_GRIDLINES) 08468 { 08469 LISTVIEW_CreateHeader(infoPtr); 08470 LISTVIEW_UpdateSize(infoPtr); 08471 } 08472 08473 if((infoPtr->dwLvExStyle ^ old_ex_style) & LVS_EX_FULLROWSELECT) 08474 { 08475 LISTVIEW_CreateHeader(infoPtr); 08476 } 08477 08478 if((infoPtr->dwLvExStyle ^ old_ex_style) & LVS_EX_TRANSPARENTBKGND) 08479 { 08480 if (infoPtr->dwLvExStyle & LVS_EX_TRANSPARENTBKGND) 08481 LISTVIEW_SetBkColor(infoPtr, CLR_NONE); 08482 } 08483 08484 if((infoPtr->dwLvExStyle ^ old_ex_style) & LVS_EX_HEADERINALLVIEWS) 08485 { 08486 if (infoPtr->dwLvExStyle & LVS_EX_HEADERINALLVIEWS) 08487 LISTVIEW_CreateHeader(infoPtr); 08488 else 08489 ShowWindow(infoPtr->hwndHeader, SW_HIDE); 08490 LISTVIEW_UpdateSize(infoPtr); 08491 LISTVIEW_UpdateScroll(infoPtr); 08492 } 08493 08494 LISTVIEW_InvalidateList(infoPtr); 08495 return old_ex_style; 08496 } 08497 08498 /*** 08499 * DESCRIPTION: 08500 * Sets the new hot cursor used during hot tracking and hover selection. 08501 * 08502 * PARAMETER(S): 08503 * [I] infoPtr : valid pointer to the listview structure 08504 * [I] hCursor : the new hot cursor handle 08505 * 08506 * RETURN: 08507 * Returns the previous hot cursor 08508 */ 08509 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor) 08510 { 08511 HCURSOR oldCursor = infoPtr->hHotCursor; 08512 08513 infoPtr->hHotCursor = hCursor; 08514 08515 return oldCursor; 08516 } 08517 08518 08519 /*** 08520 * DESCRIPTION: 08521 * Sets the hot item index. 08522 * 08523 * PARAMETERS: 08524 * [I] infoPtr : valid pointer to the listview structure 08525 * [I] iIndex : index 08526 * 08527 * RETURN: 08528 * SUCCESS : previous hot item index 08529 * FAILURE : -1 (no hot item) 08530 */ 08531 static INT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex) 08532 { 08533 INT iOldIndex = infoPtr->nHotItem; 08534 08535 infoPtr->nHotItem = iIndex; 08536 08537 return iOldIndex; 08538 } 08539 08540 08541 /*** 08542 * DESCRIPTION: 08543 * Sets the amount of time the cursor must hover over an item before it is selected. 08544 * 08545 * PARAMETER(S): 08546 * [I] infoPtr : valid pointer to the listview structure 08547 * [I] dwHoverTime : hover time, if -1 the hover time is set to the default 08548 * 08549 * RETURN: 08550 * Returns the previous hover time 08551 */ 08552 static DWORD LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime) 08553 { 08554 DWORD oldHoverTime = infoPtr->dwHoverTime; 08555 08556 infoPtr->dwHoverTime = dwHoverTime; 08557 08558 return oldHoverTime; 08559 } 08560 08561 /*** 08562 * DESCRIPTION: 08563 * Sets spacing for icons of LVS_ICON style. 08564 * 08565 * PARAMETER(S): 08566 * [I] infoPtr : valid pointer to the listview structure 08567 * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize) 08568 * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize) 08569 * 08570 * RETURN: 08571 * MAKELONG(oldcx, oldcy) 08572 */ 08573 static DWORD LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, INT cx, INT cy) 08574 { 08575 DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy); 08576 08577 TRACE("requested=(%d,%d)\n", cx, cy); 08578 08579 /* this is supported only for LVS_ICON style */ 08580 if (infoPtr->uView != LV_VIEW_ICON) return oldspacing; 08581 08582 /* set to defaults, if instructed to */ 08583 if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING); 08584 if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING); 08585 08586 /* if 0 then compute width 08587 * FIXME: Should scan each item and determine max width of 08588 * icon or label, then make that the width */ 08589 if (cx == 0) 08590 cx = infoPtr->iconSpacing.cx; 08591 08592 /* if 0 then compute height */ 08593 if (cy == 0) 08594 cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight + 08595 ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING; 08596 08597 08598 infoPtr->iconSpacing.cx = cx; 08599 infoPtr->iconSpacing.cy = cy; 08600 08601 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%d,%d), ntmH=%d\n", 08602 LOWORD(oldspacing), HIWORD(oldspacing), cx, cy, 08603 infoPtr->iconSize.cx, infoPtr->iconSize.cy, 08604 infoPtr->ntmHeight); 08605 08606 /* these depend on the iconSpacing */ 08607 LISTVIEW_UpdateItemSize(infoPtr); 08608 08609 return oldspacing; 08610 } 08611 08612 static inline void set_icon_size(SIZE *size, HIMAGELIST himl, BOOL small) 08613 { 08614 INT cx, cy; 08615 08616 if (himl && ImageList_GetIconSize(himl, &cx, &cy)) 08617 { 08618 size->cx = cx; 08619 size->cy = cy; 08620 } 08621 else 08622 { 08623 size->cx = GetSystemMetrics(small ? SM_CXSMICON : SM_CXICON); 08624 size->cy = GetSystemMetrics(small ? SM_CYSMICON : SM_CYICON); 08625 } 08626 } 08627 08628 /*** 08629 * DESCRIPTION: 08630 * Sets image lists. 08631 * 08632 * PARAMETER(S): 08633 * [I] infoPtr : valid pointer to the listview structure 08634 * [I] nType : image list type 08635 * [I] himl : image list handle 08636 * 08637 * RETURN: 08638 * SUCCESS : old image list 08639 * FAILURE : NULL 08640 */ 08641 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl) 08642 { 08643 INT oldHeight = infoPtr->nItemHeight; 08644 HIMAGELIST himlOld = 0; 08645 08646 TRACE("(nType=%d, himl=%p\n", nType, himl); 08647 08648 switch (nType) 08649 { 08650 case LVSIL_NORMAL: 08651 himlOld = infoPtr->himlNormal; 08652 infoPtr->himlNormal = himl; 08653 if (infoPtr->uView == LV_VIEW_ICON) set_icon_size(&infoPtr->iconSize, himl, FALSE); 08654 LISTVIEW_SetIconSpacing(infoPtr, 0, 0); 08655 break; 08656 08657 case LVSIL_SMALL: 08658 himlOld = infoPtr->himlSmall; 08659 infoPtr->himlSmall = himl; 08660 if (infoPtr->uView != LV_VIEW_ICON) set_icon_size(&infoPtr->iconSize, himl, TRUE); 08661 break; 08662 08663 case LVSIL_STATE: 08664 himlOld = infoPtr->himlState; 08665 infoPtr->himlState = himl; 08666 set_icon_size(&infoPtr->iconStateSize, himl, TRUE); 08667 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE); 08668 break; 08669 08670 default: 08671 ERR("Unknown icon type=%d\n", nType); 08672 return NULL; 08673 } 08674 08675 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr); 08676 if (infoPtr->nItemHeight != oldHeight) 08677 LISTVIEW_UpdateScroll(infoPtr); 08678 08679 return himlOld; 08680 } 08681 08682 /*** 08683 * DESCRIPTION: 08684 * Preallocates memory (does *not* set the actual count of items !) 08685 * 08686 * PARAMETER(S): 08687 * [I] infoPtr : valid pointer to the listview structure 08688 * [I] nItems : item count (projected number of items to allocate) 08689 * [I] dwFlags : update flags 08690 * 08691 * RETURN: 08692 * SUCCESS : TRUE 08693 * FAILURE : FALSE 08694 */ 08695 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags) 08696 { 08697 TRACE("(nItems=%d, dwFlags=%x)\n", nItems, dwFlags); 08698 08699 if (infoPtr->dwStyle & LVS_OWNERDATA) 08700 { 08701 INT nOldCount = infoPtr->nItemCount; 08702 08703 if (nItems < nOldCount) 08704 { 08705 RANGE range = { nItems, nOldCount }; 08706 ranges_del(infoPtr->selectionRanges, range); 08707 if (infoPtr->nFocusedItem >= nItems) 08708 { 08709 LISTVIEW_SetItemFocus(infoPtr, -1); 08710 SetRectEmpty(&infoPtr->rcFocus); 08711 } 08712 } 08713 08714 infoPtr->nItemCount = nItems; 08715 LISTVIEW_UpdateScroll(infoPtr); 08716 08717 /* the flags are valid only in ownerdata report and list modes */ 08718 if (infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON) dwFlags = 0; 08719 08720 if (!(dwFlags & LVSICF_NOSCROLL) && infoPtr->nFocusedItem != -1) 08721 LISTVIEW_EnsureVisible(infoPtr, infoPtr->nFocusedItem, FALSE); 08722 08723 if (!(dwFlags & LVSICF_NOINVALIDATEALL)) 08724 LISTVIEW_InvalidateList(infoPtr); 08725 else 08726 { 08727 INT nFrom, nTo; 08728 POINT Origin; 08729 RECT rcErase; 08730 08731 LISTVIEW_GetOrigin(infoPtr, &Origin); 08732 nFrom = min(nOldCount, nItems); 08733 nTo = max(nOldCount, nItems); 08734 08735 if (infoPtr->uView == LV_VIEW_DETAILS) 08736 { 08737 rcErase.left = 0; 08738 rcErase.top = nFrom * infoPtr->nItemHeight; 08739 rcErase.right = infoPtr->nItemWidth; 08740 rcErase.bottom = nTo * infoPtr->nItemHeight; 08741 OffsetRect(&rcErase, Origin.x, Origin.y); 08742 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList)) 08743 LISTVIEW_InvalidateRect(infoPtr, &rcErase); 08744 } 08745 else /* LV_VIEW_LIST */ 08746 { 08747 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr); 08748 08749 rcErase.left = (nFrom / nPerCol) * infoPtr->nItemWidth; 08750 rcErase.top = (nFrom % nPerCol) * infoPtr->nItemHeight; 08751 rcErase.right = rcErase.left + infoPtr->nItemWidth; 08752 rcErase.bottom = nPerCol * infoPtr->nItemHeight; 08753 OffsetRect(&rcErase, Origin.x, Origin.y); 08754 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList)) 08755 LISTVIEW_InvalidateRect(infoPtr, &rcErase); 08756 08757 rcErase.left = (nFrom / nPerCol + 1) * infoPtr->nItemWidth; 08758 rcErase.top = 0; 08759 rcErase.right = (nTo / nPerCol + 1) * infoPtr->nItemWidth; 08760 rcErase.bottom = nPerCol * infoPtr->nItemHeight; 08761 OffsetRect(&rcErase, Origin.x, Origin.y); 08762 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList)) 08763 LISTVIEW_InvalidateRect(infoPtr, &rcErase); 08764 } 08765 } 08766 } 08767 else 08768 { 08769 /* According to MSDN for non-LVS_OWNERDATA this is just 08770 * a performance issue. The control allocates its internal 08771 * data structures for the number of items specified. It 08772 * cuts down on the number of memory allocations. Therefore 08773 * we will just issue a WARN here 08774 */ 08775 WARN("for non-ownerdata performance option not implemented.\n"); 08776 } 08777 08778 return TRUE; 08779 } 08780 08781 /*** 08782 * DESCRIPTION: 08783 * Sets the position of an item. 08784 * 08785 * PARAMETER(S): 08786 * [I] infoPtr : valid pointer to the listview structure 08787 * [I] nItem : item index 08788 * [I] pt : coordinate 08789 * 08790 * RETURN: 08791 * SUCCESS : TRUE 08792 * FAILURE : FALSE 08793 */ 08794 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, const POINT *pt) 08795 { 08796 POINT Origin, Pt; 08797 08798 TRACE("(nItem=%d, pt=%s\n", nItem, wine_dbgstr_point(pt)); 08799 08800 if (!pt || nItem < 0 || nItem >= infoPtr->nItemCount || 08801 !(infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON)) return FALSE; 08802 08803 Pt = *pt; 08804 LISTVIEW_GetOrigin(infoPtr, &Origin); 08805 08806 /* This point value seems to be an undocumented feature. 08807 * The best guess is that it means either at the origin, 08808 * or at true beginning of the list. I will assume the origin. */ 08809 if ((Pt.x == -1) && (Pt.y == -1)) 08810 Pt = Origin; 08811 08812 if (infoPtr->uView == LV_VIEW_ICON) 08813 { 08814 Pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2; 08815 Pt.y -= ICON_TOP_PADDING; 08816 } 08817 Pt.x -= Origin.x; 08818 Pt.y -= Origin.y; 08819 08820 infoPtr->bAutoarrange = FALSE; 08821 08822 return LISTVIEW_MoveIconTo(infoPtr, nItem, &Pt, FALSE); 08823 } 08824 08825 /*** 08826 * DESCRIPTION: 08827 * Sets the state of one or many items. 08828 * 08829 * PARAMETER(S): 08830 * [I] infoPtr : valid pointer to the listview structure 08831 * [I] nItem : item index 08832 * [I] lpLVItem : item or subitem info 08833 * 08834 * RETURN: 08835 * SUCCESS : TRUE 08836 * FAILURE : FALSE 08837 */ 08838 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem) 08839 { 08840 BOOL bResult = TRUE; 08841 LVITEMW lvItem; 08842 08843 lvItem.iItem = nItem; 08844 lvItem.iSubItem = 0; 08845 lvItem.mask = LVIF_STATE; 08846 lvItem.state = lpLVItem->state; 08847 lvItem.stateMask = lpLVItem->stateMask; 08848 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE)); 08849 08850 if (nItem == -1) 08851 { 08852 /* select all isn't allowed in LVS_SINGLESEL */ 08853 if ((lvItem.state & lvItem.stateMask & LVIS_SELECTED) && (infoPtr->dwStyle & LVS_SINGLESEL)) 08854 return FALSE; 08855 08856 /* focus all isn't allowed */ 08857 if (lvItem.state & lvItem.stateMask & LVIS_FOCUSED) return FALSE; 08858 08859 /* apply to all items */ 08860 for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++) 08861 if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE; 08862 } 08863 else 08864 bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE); 08865 08866 return bResult; 08867 } 08868 08869 /*** 08870 * DESCRIPTION: 08871 * Sets the text of an item or subitem. 08872 * 08873 * PARAMETER(S): 08874 * [I] hwnd : window handle 08875 * [I] nItem : item index 08876 * [I] lpLVItem : item or subitem info 08877 * [I] isW : TRUE if input is Unicode 08878 * 08879 * RETURN: 08880 * SUCCESS : TRUE 08881 * FAILURE : FALSE 08882 */ 08883 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem, BOOL isW) 08884 { 08885 LVITEMW lvItem; 08886 08887 if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE; 08888 08889 lvItem.iItem = nItem; 08890 lvItem.iSubItem = lpLVItem->iSubItem; 08891 lvItem.mask = LVIF_TEXT; 08892 lvItem.pszText = lpLVItem->pszText; 08893 lvItem.cchTextMax = lpLVItem->cchTextMax; 08894 08895 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW); 08896 08897 return LISTVIEW_SetItemT(infoPtr, &lvItem, isW); 08898 } 08899 08900 /*** 08901 * DESCRIPTION: 08902 * Set item index that marks the start of a multiple selection. 08903 * 08904 * PARAMETER(S): 08905 * [I] infoPtr : valid pointer to the listview structure 08906 * [I] nIndex : index 08907 * 08908 * RETURN: 08909 * Index number or -1 if there is no selection mark. 08910 */ 08911 static INT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex) 08912 { 08913 INT nOldIndex = infoPtr->nSelectionMark; 08914 08915 TRACE("(nIndex=%d)\n", nIndex); 08916 08917 infoPtr->nSelectionMark = nIndex; 08918 08919 return nOldIndex; 08920 } 08921 08922 /*** 08923 * DESCRIPTION: 08924 * Sets the text background color. 08925 * 08926 * PARAMETER(S): 08927 * [I] infoPtr : valid pointer to the listview structure 08928 * [I] color : text background color 08929 * 08930 * RETURN: 08931 * SUCCESS : TRUE 08932 * FAILURE : FALSE 08933 */ 08934 static BOOL LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF color) 08935 { 08936 TRACE("(color=%x)\n", color); 08937 08938 infoPtr->clrTextBk = color; 08939 return TRUE; 08940 } 08941 08942 /*** 08943 * DESCRIPTION: 08944 * Sets the text foreground color. 08945 * 08946 * PARAMETER(S): 08947 * [I] infoPtr : valid pointer to the listview structure 08948 * [I] color : text color 08949 * 08950 * RETURN: 08951 * SUCCESS : TRUE 08952 * FAILURE : FALSE 08953 */ 08954 static BOOL LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF color) 08955 { 08956 TRACE("(color=%x)\n", color); 08957 08958 infoPtr->clrText = color; 08959 return TRUE; 08960 } 08961 08962 /*** 08963 * DESCRIPTION: 08964 * Sets new ToolTip window to ListView control. 08965 * 08966 * PARAMETER(S): 08967 * [I] infoPtr : valid pointer to the listview structure 08968 * [I] hwndNewToolTip : handle to new ToolTip 08969 * 08970 * RETURN: 08971 * old tool tip 08972 */ 08973 static HWND LISTVIEW_SetToolTips( LISTVIEW_INFO *infoPtr, HWND hwndNewToolTip) 08974 { 08975 HWND hwndOldToolTip = infoPtr->hwndToolTip; 08976 infoPtr->hwndToolTip = hwndNewToolTip; 08977 return hwndOldToolTip; 08978 } 08979 08980 /* 08981 * DESCRIPTION: 08982 * sets the Unicode character format flag for the control 08983 * PARAMETER(S): 08984 * [I] infoPtr :valid pointer to the listview structure 08985 * [I] fUnicode :true to switch to UNICODE false to switch to ANSI 08986 * 08987 * RETURN: 08988 * Old Unicode Format 08989 */ 08990 static BOOL LISTVIEW_SetUnicodeFormat( LISTVIEW_INFO *infoPtr, BOOL unicode) 08991 { 08992 SHORT rc = infoPtr->notifyFormat; 08993 infoPtr->notifyFormat = (unicode) ? NFR_UNICODE : NFR_ANSI; 08994 return rc == NFR_UNICODE; 08995 } 08996 08997 /* 08998 * DESCRIPTION: 08999 * sets the control view mode 09000 * PARAMETER(S): 09001 * [I] infoPtr :valid pointer to the listview structure 09002 * [I] nView :new view mode value 09003 * 09004 * RETURN: 09005 * SUCCESS: 1 09006 * FAILURE: -1 09007 */ 09008 static INT LISTVIEW_SetView(LISTVIEW_INFO *infoPtr, DWORD nView) 09009 { 09010 SIZE oldIconSize = infoPtr->iconSize; 09011 HIMAGELIST himl; 09012 09013 if (infoPtr->uView == nView) return 1; 09014 09015 if ((INT)nView < 0 || nView > LV_VIEW_MAX) return -1; 09016 if (nView == LV_VIEW_TILE) 09017 { 09018 FIXME("View LV_VIEW_TILE unimplemented\n"); 09019 return -1; 09020 } 09021 09022 infoPtr->uView = nView; 09023 09024 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0); 09025 ShowWindow(infoPtr->hwndHeader, SW_HIDE); 09026 09027 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE); 09028 SetRectEmpty(&infoPtr->rcFocus); 09029 09030 himl = (nView == LV_VIEW_ICON ? infoPtr->himlNormal : infoPtr->himlSmall); 09031 set_icon_size(&infoPtr->iconSize, himl, nView != LV_VIEW_ICON); 09032 09033 switch (nView) 09034 { 09035 case LV_VIEW_ICON: 09036 if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy)) 09037 { 09038 TRACE("icon old size=(%d,%d), new size=(%d,%d)\n", 09039 oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy); 09040 LISTVIEW_SetIconSpacing(infoPtr, 0, 0); 09041 } 09042 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT); 09043 break; 09044 case LV_VIEW_SMALLICON: 09045 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT); 09046 break; 09047 case LV_VIEW_DETAILS: 09048 { 09049 HDLAYOUT hl; 09050 WINDOWPOS wp; 09051 09052 LISTVIEW_CreateHeader( infoPtr ); 09053 09054 hl.prc = &infoPtr->rcList; 09055 hl.pwpos = ℘ 09056 SendMessageW(infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl); 09057 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy, 09058 wp.flags | ((infoPtr->dwStyle & LVS_NOCOLUMNHEADER) ? SWP_HIDEWINDOW : SWP_SHOWWINDOW)); 09059 break; 09060 } 09061 case LV_VIEW_LIST: 09062 break; 09063 } 09064 09065 LISTVIEW_UpdateItemSize(infoPtr); 09066 LISTVIEW_UpdateSize(infoPtr); 09067 LISTVIEW_UpdateScroll(infoPtr); 09068 LISTVIEW_InvalidateList(infoPtr); 09069 09070 TRACE("nView=%d\n", nView); 09071 09072 return 1; 09073 } 09074 09075 /* LISTVIEW_SetWorkAreas */ 09076 09077 /*** 09078 * DESCRIPTION: 09079 * Callback internally used by LISTVIEW_SortItems() in response of LVM_SORTITEMS 09080 * 09081 * PARAMETER(S): 09082 * [I] first : pointer to first ITEM_INFO to compare 09083 * [I] second : pointer to second ITEM_INFO to compare 09084 * [I] lParam : HWND of control 09085 * 09086 * RETURN: 09087 * if first comes before second : negative 09088 * if first comes after second : positive 09089 * if first and second are equivalent : zero 09090 */ 09091 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam) 09092 { 09093 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam; 09094 ITEM_INFO* lv_first = DPA_GetPtr( first, 0 ); 09095 ITEM_INFO* lv_second = DPA_GetPtr( second, 0 ); 09096 09097 /* Forward the call to the client defined callback */ 09098 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort ); 09099 } 09100 09101 /*** 09102 * DESCRIPTION: 09103 * Callback internally used by LISTVIEW_SortItems() in response of LVM_SORTITEMSEX 09104 * 09105 * PARAMETER(S): 09106 * [I] first : pointer to first ITEM_INFO to compare 09107 * [I] second : pointer to second ITEM_INFO to compare 09108 * [I] lParam : HWND of control 09109 * 09110 * RETURN: 09111 * if first comes before second : negative 09112 * if first comes after second : positive 09113 * if first and second are equivalent : zero 09114 */ 09115 static INT WINAPI LISTVIEW_CallBackCompareEx(LPVOID first, LPVOID second, LPARAM lParam) 09116 { 09117 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam; 09118 INT first_idx = DPA_GetPtrIndex( infoPtr->hdpaItems, first ); 09119 INT second_idx = DPA_GetPtrIndex( infoPtr->hdpaItems, second ); 09120 09121 /* Forward the call to the client defined callback */ 09122 return (infoPtr->pfnCompare)( first_idx, second_idx, infoPtr->lParamSort ); 09123 } 09124 09125 /*** 09126 * DESCRIPTION: 09127 * Sorts the listview items. 09128 * 09129 * PARAMETER(S): 09130 * [I] infoPtr : valid pointer to the listview structure 09131 * [I] pfnCompare : application-defined value 09132 * [I] lParamSort : pointer to comparison callback 09133 * [I] IsEx : TRUE when LVM_SORTITEMSEX used 09134 * 09135 * RETURN: 09136 * SUCCESS : TRUE 09137 * FAILURE : FALSE 09138 */ 09139 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare, 09140 LPARAM lParamSort, BOOL IsEx) 09141 { 09142 HDPA hdpaSubItems; 09143 ITEM_INFO *lpItem; 09144 LPVOID selectionMarkItem = NULL; 09145 LPVOID focusedItem = NULL; 09146 int i; 09147 09148 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort); 09149 09150 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE; 09151 09152 if (!pfnCompare) return FALSE; 09153 if (!infoPtr->hdpaItems) return FALSE; 09154 09155 /* if there are 0 or 1 items, there is no need to sort */ 09156 if (infoPtr->nItemCount < 2) return TRUE; 09157 09158 /* clear selection */ 09159 ranges_clear(infoPtr->selectionRanges); 09160 09161 /* save selection mark and focused item */ 09162 if (infoPtr->nSelectionMark >= 0) 09163 selectionMarkItem = DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark); 09164 if (infoPtr->nFocusedItem >= 0) 09165 focusedItem = DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem); 09166 09167 infoPtr->pfnCompare = pfnCompare; 09168 infoPtr->lParamSort = lParamSort; 09169 if (IsEx) 09170 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompareEx, (LPARAM)infoPtr); 09171 else 09172 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr); 09173 09174 /* restore selection ranges */ 09175 for (i=0; i < infoPtr->nItemCount; i++) 09176 { 09177 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, i); 09178 lpItem = DPA_GetPtr(hdpaSubItems, 0); 09179 09180 if (lpItem->state & LVIS_SELECTED) 09181 ranges_additem(infoPtr->selectionRanges, i); 09182 } 09183 /* restore selection mark and focused item */ 09184 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem); 09185 infoPtr->nFocusedItem = DPA_GetPtrIndex(infoPtr->hdpaItems, focusedItem); 09186 09187 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */ 09188 09189 /* refresh the display */ 09190 LISTVIEW_InvalidateList(infoPtr); 09191 return TRUE; 09192 } 09193 09194 /*** 09195 * DESCRIPTION: 09196 * Update theme handle after a theme change. 09197 * 09198 * PARAMETER(S): 09199 * [I] infoPtr : valid pointer to the listview structure 09200 * 09201 * RETURN: 09202 * SUCCESS : 0 09203 * FAILURE : something else 09204 */ 09205 static LRESULT LISTVIEW_ThemeChanged(const LISTVIEW_INFO *infoPtr) 09206 { 09207 HTHEME theme = GetWindowTheme(infoPtr->hwndSelf); 09208 CloseThemeData(theme); 09209 OpenThemeData(infoPtr->hwndSelf, themeClass); 09210 return 0; 09211 } 09212 09213 /*** 09214 * DESCRIPTION: 09215 * Updates an items or rearranges the listview control. 09216 * 09217 * PARAMETER(S): 09218 * [I] infoPtr : valid pointer to the listview structure 09219 * [I] nItem : item index 09220 * 09221 * RETURN: 09222 * SUCCESS : TRUE 09223 * FAILURE : FALSE 09224 */ 09225 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem) 09226 { 09227 TRACE("(nItem=%d)\n", nItem); 09228 09229 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE; 09230 09231 /* rearrange with default alignment style */ 09232 if (is_autoarrange(infoPtr)) 09233 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT); 09234 else 09235 LISTVIEW_InvalidateItem(infoPtr, nItem); 09236 09237 return TRUE; 09238 } 09239 09240 /*** 09241 * DESCRIPTION: 09242 * Draw the track line at the place defined in the infoPtr structure. 09243 * The line is drawn with a XOR pen so drawing the line for the second time 09244 * in the same place erases the line. 09245 * 09246 * PARAMETER(S): 09247 * [I] infoPtr : valid pointer to the listview structure 09248 * 09249 * RETURN: 09250 * SUCCESS : TRUE 09251 * FAILURE : FALSE 09252 */ 09253 static BOOL LISTVIEW_DrawTrackLine(const LISTVIEW_INFO *infoPtr) 09254 { 09255 HPEN hOldPen; 09256 HDC hdc; 09257 INT oldROP; 09258 09259 if (infoPtr->xTrackLine == -1) 09260 return FALSE; 09261 09262 if (!(hdc = GetDC(infoPtr->hwndSelf))) 09263 return FALSE; 09264 hOldPen = SelectObject(hdc, GetStockObject(BLACK_PEN)); 09265 oldROP = SetROP2(hdc, R2_XORPEN); 09266 MoveToEx(hdc, infoPtr->xTrackLine, infoPtr->rcList.top, NULL); 09267 LineTo(hdc, infoPtr->xTrackLine, infoPtr->rcList.bottom); 09268 SetROP2(hdc, oldROP); 09269 SelectObject(hdc, hOldPen); 09270 ReleaseDC(infoPtr->hwndSelf, hdc); 09271 return TRUE; 09272 } 09273 09274 /*** 09275 * DESCRIPTION: 09276 * Called when an edit control should be displayed. This function is called after 09277 * we are sure that there was a single click - not a double click (this is a TIMERPROC). 09278 * 09279 * PARAMETER(S): 09280 * [I] hwnd : Handle to the listview 09281 * [I] uMsg : WM_TIMER (ignored) 09282 * [I] idEvent : The timer ID interpreted as a pointer to a DELAYED_EDIT_ITEM struct 09283 * [I] dwTimer : The elapsed time (ignored) 09284 * 09285 * RETURN: 09286 * None. 09287 */ 09288 static VOID CALLBACK LISTVIEW_DelayedEditItem(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime) 09289 { 09290 DELAYED_ITEM_EDIT *editItem = (DELAYED_ITEM_EDIT *)idEvent; 09291 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0); 09292 09293 KillTimer(hwnd, idEvent); 09294 editItem->fEnabled = FALSE; 09295 /* check if the item is still selected */ 09296 if (infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, editItem->iItem, LVIS_SELECTED)) 09297 LISTVIEW_EditLabelT(infoPtr, editItem->iItem, TRUE); 09298 } 09299 09300 /*** 09301 * DESCRIPTION: 09302 * Creates the listview control - the WM_NCCREATE phase. 09303 * 09304 * PARAMETER(S): 09305 * [I] hwnd : window handle 09306 * [I] lpcs : the create parameters 09307 * 09308 * RETURN: 09309 * Success: TRUE 09310 * Failure: FALSE 09311 */ 09312 static LRESULT LISTVIEW_NCCreate(HWND hwnd, const CREATESTRUCTW *lpcs) 09313 { 09314 LISTVIEW_INFO *infoPtr; 09315 LOGFONTW logFont; 09316 09317 TRACE("(lpcs=%p)\n", lpcs); 09318 09319 /* initialize info pointer */ 09320 infoPtr = Alloc(sizeof(LISTVIEW_INFO)); 09321 if (!infoPtr) return FALSE; 09322 09323 SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr); 09324 09325 infoPtr->hwndSelf = hwnd; 09326 infoPtr->dwStyle = lpcs->style; /* Note: may be changed in WM_CREATE */ 09327 map_style_view(infoPtr); 09328 /* determine the type of structures to use */ 09329 infoPtr->hwndNotify = lpcs->hwndParent; 09330 /* infoPtr->notifyFormat will be filled in WM_CREATE */ 09331 09332 /* initialize color information */ 09333 infoPtr->clrBk = CLR_NONE; 09334 infoPtr->clrText = CLR_DEFAULT; 09335 infoPtr->clrTextBk = CLR_DEFAULT; 09336 LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow); 09337 infoPtr->bDefaultBkColor = TRUE; 09338 09339 /* set default values */ 09340 infoPtr->nFocusedItem = -1; 09341 infoPtr->nSelectionMark = -1; 09342 infoPtr->nHotItem = -1; 09343 infoPtr->bRedraw = TRUE; 09344 infoPtr->bNoItemMetrics = TRUE; 09345 infoPtr->bDoChangeNotify = TRUE; 09346 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING); 09347 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING); 09348 infoPtr->nEditLabelItem = -1; 09349 infoPtr->nLButtonDownItem = -1; 09350 infoPtr->dwHoverTime = HOVER_DEFAULT; /* default system hover time */ 09351 infoPtr->nMeasureItemHeight = 0; 09352 infoPtr->xTrackLine = -1; /* no track line */ 09353 infoPtr->itemEdit.fEnabled = FALSE; 09354 infoPtr->iVersion = COMCTL32_VERSION; 09355 infoPtr->colRectsDirty = FALSE; 09356 09357 /* get default font (icon title) */ 09358 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0); 09359 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont); 09360 infoPtr->hFont = infoPtr->hDefaultFont; 09361 LISTVIEW_SaveTextMetrics(infoPtr); 09362 09363 /* allocate memory for the data structure */ 09364 if (!(infoPtr->selectionRanges = ranges_create(10))) goto fail; 09365 if (!(infoPtr->hdpaItems = DPA_Create(10))) goto fail; 09366 if (!(infoPtr->hdpaItemIds = DPA_Create(10))) goto fail; 09367 if (!(infoPtr->hdpaPosX = DPA_Create(10))) goto fail; 09368 if (!(infoPtr->hdpaPosY = DPA_Create(10))) goto fail; 09369 if (!(infoPtr->hdpaColumns = DPA_Create(10))) goto fail; 09370 return TRUE; 09371 09372 fail: 09373 DestroyWindow(infoPtr->hwndHeader); 09374 ranges_destroy(infoPtr->selectionRanges); 09375 DPA_Destroy(infoPtr->hdpaItems); 09376 DPA_Destroy(infoPtr->hdpaItemIds); 09377 DPA_Destroy(infoPtr->hdpaPosX); 09378 DPA_Destroy(infoPtr->hdpaPosY); 09379 DPA_Destroy(infoPtr->hdpaColumns); 09380 Free(infoPtr); 09381 return FALSE; 09382 } 09383 09384 /*** 09385 * DESCRIPTION: 09386 * Creates the listview control - the WM_CREATE phase. Most of the data is 09387 * already set up in LISTVIEW_NCCreate 09388 * 09389 * PARAMETER(S): 09390 * [I] hwnd : window handle 09391 * [I] lpcs : the create parameters 09392 * 09393 * RETURN: 09394 * Success: 0 09395 * Failure: -1 09396 */ 09397 static LRESULT LISTVIEW_Create(HWND hwnd, const CREATESTRUCTW *lpcs) 09398 { 09399 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0); 09400 09401 TRACE("(lpcs=%p, style=0x%08x)\n", lpcs, lpcs->style); 09402 09403 infoPtr->dwStyle = lpcs->style; 09404 map_style_view(infoPtr); 09405 09406 infoPtr->notifyFormat = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT, 09407 (WPARAM)infoPtr->hwndSelf, NF_QUERY); 09408 /* on error defaulting to ANSI notifications */ 09409 if (infoPtr->notifyFormat == 0) infoPtr->notifyFormat = NFR_ANSI; 09410 TRACE("notify format=%d\n", infoPtr->notifyFormat); 09411 09412 if ((infoPtr->uView == LV_VIEW_DETAILS) && (lpcs->style & WS_VISIBLE)) 09413 { 09414 if (LISTVIEW_CreateHeader(infoPtr) < 0) return -1; 09415 } 09416 else 09417 infoPtr->hwndHeader = 0; 09418 09419 /* init item size to avoid division by 0 */ 09420 LISTVIEW_UpdateItemSize (infoPtr); 09421 09422 if (infoPtr->uView == LV_VIEW_DETAILS) 09423 { 09424 if (!(LVS_NOCOLUMNHEADER & lpcs->style) && (WS_VISIBLE & lpcs->style)) 09425 { 09426 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL); 09427 } 09428 LISTVIEW_UpdateScroll(infoPtr); 09429 /* send WM_MEASUREITEM notification */ 09430 if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED) notify_measureitem(infoPtr); 09431 } 09432 09433 OpenThemeData(hwnd, themeClass); 09434 09435 /* initialize the icon sizes */ 09436 set_icon_size(&infoPtr->iconSize, infoPtr->himlNormal, infoPtr->uView != LV_VIEW_ICON); 09437 set_icon_size(&infoPtr->iconStateSize, infoPtr->himlState, TRUE); 09438 return 0; 09439 } 09440 09441 /*** 09442 * DESCRIPTION: 09443 * Destroys the listview control. 09444 * 09445 * PARAMETER(S): 09446 * [I] infoPtr : valid pointer to the listview structure 09447 * 09448 * RETURN: 09449 * Success: 0 09450 * Failure: -1 09451 */ 09452 static LRESULT LISTVIEW_Destroy(LISTVIEW_INFO *infoPtr) 09453 { 09454 HTHEME theme = GetWindowTheme(infoPtr->hwndSelf); 09455 CloseThemeData(theme); 09456 09457 /* delete all items */ 09458 LISTVIEW_DeleteAllItems(infoPtr, TRUE); 09459 09460 return 0; 09461 } 09462 09463 /*** 09464 * DESCRIPTION: 09465 * Enables the listview control. 09466 * 09467 * PARAMETER(S): 09468 * [I] infoPtr : valid pointer to the listview structure 09469 * [I] bEnable : specifies whether to enable or disable the window 09470 * 09471 * RETURN: 09472 * SUCCESS : TRUE 09473 * FAILURE : FALSE 09474 */ 09475 static BOOL LISTVIEW_Enable(const LISTVIEW_INFO *infoPtr) 09476 { 09477 if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED) 09478 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE); 09479 return TRUE; 09480 } 09481 09482 /*** 09483 * DESCRIPTION: 09484 * Erases the background of the listview control. 09485 * 09486 * PARAMETER(S): 09487 * [I] infoPtr : valid pointer to the listview structure 09488 * [I] hdc : device context handle 09489 * 09490 * RETURN: 09491 * SUCCESS : TRUE 09492 * FAILURE : FALSE 09493 */ 09494 static inline BOOL LISTVIEW_EraseBkgnd(const LISTVIEW_INFO *infoPtr, HDC hdc) 09495 { 09496 RECT rc; 09497 09498 TRACE("(hdc=%p)\n", hdc); 09499 09500 if (!GetClipBox(hdc, &rc)) return FALSE; 09501 09502 if (infoPtr->clrBk == CLR_NONE) 09503 { 09504 if (infoPtr->dwLvExStyle & LVS_EX_TRANSPARENTBKGND) 09505 return SendMessageW(infoPtr->hwndNotify, WM_PRINTCLIENT, 09506 (WPARAM)hdc, PRF_ERASEBKGND); 09507 else 09508 return SendMessageW(infoPtr->hwndNotify, WM_ERASEBKGND, (WPARAM)hdc, 0); 09509 } 09510 09511 /* for double buffered controls we need to do this during refresh */ 09512 if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) return FALSE; 09513 09514 return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc); 09515 } 09516 09517 09518 /*** 09519 * DESCRIPTION: 09520 * Helper function for LISTVIEW_[HV]Scroll *only*. 09521 * Performs vertical/horizontal scrolling by a give amount. 09522 * 09523 * PARAMETER(S): 09524 * [I] infoPtr : valid pointer to the listview structure 09525 * [I] dx : amount of horizontal scroll 09526 * [I] dy : amount of vertical scroll 09527 */ 09528 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy) 09529 { 09530 /* now we can scroll the list */ 09531 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList, 09532 &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE); 09533 /* if we have focus, adjust rect */ 09534 OffsetRect(&infoPtr->rcFocus, dx, dy); 09535 UpdateWindow(infoPtr->hwndSelf); 09536 } 09537 09538 /*** 09539 * DESCRIPTION: 09540 * Performs vertical scrolling. 09541 * 09542 * PARAMETER(S): 09543 * [I] infoPtr : valid pointer to the listview structure 09544 * [I] nScrollCode : scroll code 09545 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise 09546 * [I] hScrollWnd : scrollbar control window handle 09547 * 09548 * RETURN: 09549 * Zero 09550 * 09551 * NOTES: 09552 * SB_LINEUP/SB_LINEDOWN: 09553 * for LVS_ICON, LVS_SMALLICON is 37 by experiment 09554 * for LVS_REPORT is 1 line 09555 * for LVS_LIST cannot occur 09556 * 09557 */ 09558 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode, 09559 INT nScrollDiff) 09560 { 09561 INT nOldScrollPos, nNewScrollPos; 09562 SCROLLINFO scrollInfo; 09563 BOOL is_an_icon; 09564 09565 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode, 09566 debugscrollcode(nScrollCode), nScrollDiff); 09567 09568 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0); 09569 09570 scrollInfo.cbSize = sizeof(SCROLLINFO); 09571 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS; 09572 09573 is_an_icon = ((infoPtr->uView == LV_VIEW_ICON) || (infoPtr->uView == LV_VIEW_SMALLICON)); 09574 09575 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1; 09576 09577 nOldScrollPos = scrollInfo.nPos; 09578 switch (nScrollCode) 09579 { 09580 case SB_INTERNAL: 09581 break; 09582 09583 case SB_LINEUP: 09584 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1; 09585 break; 09586 09587 case SB_LINEDOWN: 09588 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1; 09589 break; 09590 09591 case SB_PAGEUP: 09592 nScrollDiff = -scrollInfo.nPage; 09593 break; 09594 09595 case SB_PAGEDOWN: 09596 nScrollDiff = scrollInfo.nPage; 09597 break; 09598 09599 case SB_THUMBPOSITION: 09600 case SB_THUMBTRACK: 09601 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos; 09602 break; 09603 09604 default: 09605 nScrollDiff = 0; 09606 } 09607 09608 /* quit right away if pos isn't changing */ 09609 if (nScrollDiff == 0) return 0; 09610 09611 /* calculate new position, and handle overflows */ 09612 nNewScrollPos = scrollInfo.nPos + nScrollDiff; 09613 if (nScrollDiff > 0) { 09614 if (nNewScrollPos < nOldScrollPos || 09615 nNewScrollPos > scrollInfo.nMax) 09616 nNewScrollPos = scrollInfo.nMax; 09617 } else { 09618 if (nNewScrollPos > nOldScrollPos || 09619 nNewScrollPos < scrollInfo.nMin) 09620 nNewScrollPos = scrollInfo.nMin; 09621 } 09622 09623 /* set the new position, and reread in case it changed */ 09624 scrollInfo.fMask = SIF_POS; 09625 scrollInfo.nPos = nNewScrollPos; 09626 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE); 09627 09628 /* carry on only if it really changed */ 09629 if (nNewScrollPos == nOldScrollPos) return 0; 09630 09631 /* now adjust to client coordinates */ 09632 nScrollDiff = nOldScrollPos - nNewScrollPos; 09633 if (infoPtr->uView == LV_VIEW_DETAILS) nScrollDiff *= infoPtr->nItemHeight; 09634 09635 /* and scroll the window */ 09636 scroll_list(infoPtr, 0, nScrollDiff); 09637 09638 return 0; 09639 } 09640 09641 /*** 09642 * DESCRIPTION: 09643 * Performs horizontal scrolling. 09644 * 09645 * PARAMETER(S): 09646 * [I] infoPtr : valid pointer to the listview structure 09647 * [I] nScrollCode : scroll code 09648 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise 09649 * [I] hScrollWnd : scrollbar control window handle 09650 * 09651 * RETURN: 09652 * Zero 09653 * 09654 * NOTES: 09655 * SB_LINELEFT/SB_LINERIGHT: 09656 * for LVS_ICON, LVS_SMALLICON 1 pixel 09657 * for LVS_REPORT is 1 pixel 09658 * for LVS_LIST is 1 column --> which is a 1 because the 09659 * scroll is based on columns not pixels 09660 * 09661 */ 09662 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode, 09663 INT nScrollDiff) 09664 { 09665 INT nOldScrollPos, nNewScrollPos; 09666 SCROLLINFO scrollInfo; 09667 BOOL is_an_icon; 09668 09669 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode, 09670 debugscrollcode(nScrollCode), nScrollDiff); 09671 09672 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0); 09673 09674 scrollInfo.cbSize = sizeof(SCROLLINFO); 09675 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS; 09676 09677 is_an_icon = ((infoPtr->uView == LV_VIEW_ICON) || (infoPtr->uView == LV_VIEW_SMALLICON)); 09678 09679 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1; 09680 09681 nOldScrollPos = scrollInfo.nPos; 09682 09683 switch (nScrollCode) 09684 { 09685 case SB_INTERNAL: 09686 break; 09687 09688 case SB_LINELEFT: 09689 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1; 09690 break; 09691 09692 case SB_LINERIGHT: 09693 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1; 09694 break; 09695 09696 case SB_PAGELEFT: 09697 nScrollDiff = -scrollInfo.nPage; 09698 break; 09699 09700 case SB_PAGERIGHT: 09701 nScrollDiff = scrollInfo.nPage; 09702 break; 09703 09704 case SB_THUMBPOSITION: 09705 case SB_THUMBTRACK: 09706 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos; 09707 break; 09708 09709 default: 09710 nScrollDiff = 0; 09711 } 09712 09713 /* quit right away if pos isn't changing */ 09714 if (nScrollDiff == 0) return 0; 09715 09716 /* calculate new position, and handle overflows */ 09717 nNewScrollPos = scrollInfo.nPos + nScrollDiff; 09718 if (nScrollDiff > 0) { 09719 if (nNewScrollPos < nOldScrollPos || 09720 nNewScrollPos > scrollInfo.nMax) 09721 nNewScrollPos = scrollInfo.nMax; 09722 } else { 09723 if (nNewScrollPos > nOldScrollPos || 09724 nNewScrollPos < scrollInfo.nMin) 09725 nNewScrollPos = scrollInfo.nMin; 09726 } 09727 09728 /* set the new position, and reread in case it changed */ 09729 scrollInfo.fMask = SIF_POS; 09730 scrollInfo.nPos = nNewScrollPos; 09731 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE); 09732 09733 /* carry on only if it really changed */ 09734 if (nNewScrollPos == nOldScrollPos) return 0; 09735 09736 if (infoPtr->hwndHeader) LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos); 09737 09738 /* now adjust to client coordinates */ 09739 nScrollDiff = nOldScrollPos - nNewScrollPos; 09740 if (infoPtr->uView == LV_VIEW_LIST) nScrollDiff *= infoPtr->nItemWidth; 09741 09742 /* and scroll the window */ 09743 scroll_list(infoPtr, nScrollDiff, 0); 09744 09745 return 0; 09746 } 09747 09748 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta) 09749 { 09750 INT gcWheelDelta = 0; 09751 INT pulScrollLines = 3; 09752 09753 TRACE("(wheelDelta=%d)\n", wheelDelta); 09754 09755 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0); 09756 gcWheelDelta -= wheelDelta; 09757 09758 switch(infoPtr->uView) 09759 { 09760 case LV_VIEW_ICON: 09761 case LV_VIEW_SMALLICON: 09762 /* 09763 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number 09764 * should be fixed in the future. 09765 */ 09766 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, (gcWheelDelta < 0) ? 09767 -LISTVIEW_SCROLL_ICON_LINE_SIZE : LISTVIEW_SCROLL_ICON_LINE_SIZE); 09768 break; 09769 09770 case LV_VIEW_DETAILS: 09771 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines) 09772 { 09773 int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines); 09774 cLineScroll *= (gcWheelDelta / WHEEL_DELTA); 09775 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, cLineScroll); 09776 } 09777 break; 09778 09779 case LV_VIEW_LIST: 09780 LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0); 09781 break; 09782 } 09783 return 0; 09784 } 09785 09786 /*** 09787 * DESCRIPTION: 09788 * ??? 09789 * 09790 * PARAMETER(S): 09791 * [I] infoPtr : valid pointer to the listview structure 09792 * [I] nVirtualKey : virtual key 09793 * [I] lKeyData : key data 09794 * 09795 * RETURN: 09796 * Zero 09797 */ 09798 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData) 09799 { 09800 HWND hwndSelf = infoPtr->hwndSelf; 09801 INT nItem = -1; 09802 NMLVKEYDOWN nmKeyDown; 09803 09804 TRACE("(nVirtualKey=%d, lKeyData=%d)\n", nVirtualKey, lKeyData); 09805 09806 /* send LVN_KEYDOWN notification */ 09807 nmKeyDown.wVKey = nVirtualKey; 09808 nmKeyDown.flags = 0; 09809 notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr); 09810 if (!IsWindow(hwndSelf)) 09811 return 0; 09812 09813 switch (nVirtualKey) 09814 { 09815 case VK_SPACE: 09816 nItem = infoPtr->nFocusedItem; 09817 if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) 09818 toggle_checkbox_state(infoPtr, infoPtr->nFocusedItem); 09819 break; 09820 09821 case VK_RETURN: 09822 if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1)) 09823 { 09824 if (!notify(infoPtr, NM_RETURN)) return 0; 09825 if (!notify(infoPtr, LVN_ITEMACTIVATE)) return 0; 09826 } 09827 break; 09828 09829 case VK_HOME: 09830 if (infoPtr->nItemCount > 0) 09831 nItem = 0; 09832 break; 09833 09834 case VK_END: 09835 if (infoPtr->nItemCount > 0) 09836 nItem = infoPtr->nItemCount - 1; 09837 break; 09838 09839 case VK_LEFT: 09840 nItem = LISTVIEW_GetNextItem(infoPtr, infoPtr->nFocusedItem, LVNI_TOLEFT); 09841 break; 09842 09843 case VK_UP: 09844 nItem = LISTVIEW_GetNextItem(infoPtr, infoPtr->nFocusedItem, LVNI_ABOVE); 09845 break; 09846 09847 case VK_RIGHT: 09848 nItem = LISTVIEW_GetNextItem(infoPtr, infoPtr->nFocusedItem, LVNI_TORIGHT); 09849 break; 09850 09851 case VK_DOWN: 09852 nItem = LISTVIEW_GetNextItem(infoPtr, infoPtr->nFocusedItem, LVNI_BELOW); 09853 break; 09854 09855 case VK_PRIOR: 09856 if (infoPtr->uView == LV_VIEW_DETAILS) 09857 { 09858 INT topidx = LISTVIEW_GetTopIndex(infoPtr); 09859 if (infoPtr->nFocusedItem == topidx) 09860 nItem = topidx - LISTVIEW_GetCountPerColumn(infoPtr) + 1; 09861 else 09862 nItem = topidx; 09863 } 09864 else 09865 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr) 09866 * LISTVIEW_GetCountPerRow(infoPtr); 09867 if(nItem < 0) nItem = 0; 09868 break; 09869 09870 case VK_NEXT: 09871 if (infoPtr->uView == LV_VIEW_DETAILS) 09872 { 09873 INT topidx = LISTVIEW_GetTopIndex(infoPtr); 09874 INT cnt = LISTVIEW_GetCountPerColumn(infoPtr); 09875 if (infoPtr->nFocusedItem == topidx + cnt - 1) 09876 nItem = infoPtr->nFocusedItem + cnt - 1; 09877 else 09878 nItem = topidx + cnt - 1; 09879 } 09880 else 09881 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr) 09882 * LISTVIEW_GetCountPerRow(infoPtr); 09883 if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1; 09884 break; 09885 } 09886 09887 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem || nVirtualKey == VK_SPACE)) 09888 LISTVIEW_KeySelection(infoPtr, nItem, nVirtualKey == VK_SPACE); 09889 09890 return 0; 09891 } 09892 09893 /*** 09894 * DESCRIPTION: 09895 * Kills the focus. 09896 * 09897 * PARAMETER(S): 09898 * [I] infoPtr : valid pointer to the listview structure 09899 * 09900 * RETURN: 09901 * Zero 09902 */ 09903 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr) 09904 { 09905 TRACE("()\n"); 09906 09907 /* if we did not have the focus, there's nothing to do */ 09908 if (!infoPtr->bFocus) return 0; 09909 09910 /* send NM_KILLFOCUS notification */ 09911 if (!notify(infoPtr, NM_KILLFOCUS)) return 0; 09912 09913 /* if we have a focus rectangle, get rid of it */ 09914 LISTVIEW_ShowFocusRect(infoPtr, FALSE); 09915 09916 /* if have a marquee selection, stop it */ 09917 if (infoPtr->bMarqueeSelect) 09918 { 09919 /* Remove the marquee rectangle and release our mouse capture */ 09920 LISTVIEW_InvalidateRect(infoPtr, &infoPtr->marqueeRect); 09921 ReleaseCapture(); 09922 09923 SetRect(&infoPtr->marqueeRect, 0, 0, 0, 0); 09924 09925 infoPtr->bMarqueeSelect = FALSE; 09926 infoPtr->bScrolling = FALSE; 09927 KillTimer(infoPtr->hwndSelf, (UINT_PTR) infoPtr); 09928 } 09929 09930 /* set window focus flag */ 09931 infoPtr->bFocus = FALSE; 09932 09933 /* invalidate the selected items before resetting focus flag */ 09934 LISTVIEW_InvalidateSelectedItems(infoPtr); 09935 09936 return 0; 09937 } 09938 09939 /*** 09940 * DESCRIPTION: 09941 * Processes double click messages (left mouse button). 09942 * 09943 * PARAMETER(S): 09944 * [I] infoPtr : valid pointer to the listview structure 09945 * [I] wKey : key flag 09946 * [I] x,y : mouse coordinate 09947 * 09948 * RETURN: 09949 * Zero 09950 */ 09951 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y) 09952 { 09953 LVHITTESTINFO htInfo; 09954 09955 TRACE("(key=%hu, X=%u, Y=%u)\n", wKey, x, y); 09956 09957 /* Cancel the item edition if any */ 09958 if (infoPtr->itemEdit.fEnabled) 09959 { 09960 KillTimer(infoPtr->hwndSelf, (UINT_PTR)&infoPtr->itemEdit); 09961 infoPtr->itemEdit.fEnabled = FALSE; 09962 } 09963 09964 /* send NM_RELEASEDCAPTURE notification */ 09965 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0; 09966 09967 htInfo.pt.x = x; 09968 htInfo.pt.y = y; 09969 09970 /* send NM_DBLCLK notification */ 09971 LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE); 09972 if (!notify_click(infoPtr, NM_DBLCLK, &htInfo)) return 0; 09973 09974 /* To send the LVN_ITEMACTIVATE, it must be on an Item */ 09975 if(htInfo.iItem != -1) notify_itemactivate(infoPtr,&htInfo); 09976 09977 return 0; 09978 } 09979 09980 /*** 09981 * DESCRIPTION: 09982 * Processes mouse down messages (left mouse button). 09983 * 09984 * PARAMETERS: 09985 * infoPtr [I ] valid pointer to the listview structure 09986 * wKey [I ] key flag 09987 * x,y [I ] mouse coordinate 09988 * 09989 * RETURN: 09990 * Zero 09991 */ 09992 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y) 09993 { 09994 LVHITTESTINFO lvHitTestInfo; 09995 static BOOL bGroupSelect = TRUE; 09996 POINT pt = { x, y }; 09997 INT nItem; 09998 09999 TRACE("(key=%hu, X=%u, Y=%u)\n", wKey, x, y); 10000 10001 /* send NM_RELEASEDCAPTURE notification */ 10002 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0; 10003 10004 /* set left button down flag and record the click position */ 10005 infoPtr->bLButtonDown = TRUE; 10006 infoPtr->ptClickPos = pt; 10007 infoPtr->bDragging = FALSE; 10008 infoPtr->bMarqueeSelect = FALSE; 10009 infoPtr->bScrolling = FALSE; 10010 10011 lvHitTestInfo.pt.x = x; 10012 lvHitTestInfo.pt.y = y; 10013 10014 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE); 10015 TRACE("at %s, nItem=%d\n", wine_dbgstr_point(&pt), nItem); 10016 if ((nItem >= 0) && (nItem < infoPtr->nItemCount)) 10017 { 10018 if ((infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) && (lvHitTestInfo.flags & LVHT_ONITEMSTATEICON)) 10019 { 10020 toggle_checkbox_state(infoPtr, nItem); 10021 return 0; 10022 } 10023 10024 if (infoPtr->dwStyle & LVS_SINGLESEL) 10025 { 10026 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED)) 10027 infoPtr->nEditLabelItem = nItem; 10028 else 10029 LISTVIEW_SetSelection(infoPtr, nItem); 10030 } 10031 else 10032 { 10033 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT)) 10034 { 10035 if (bGroupSelect) 10036 { 10037 if (!LISTVIEW_AddGroupSelection(infoPtr, nItem)) return 0; 10038 LISTVIEW_SetItemFocus(infoPtr, nItem); 10039 infoPtr->nSelectionMark = nItem; 10040 } 10041 else 10042 { 10043 LVITEMW item; 10044 10045 item.state = LVIS_SELECTED | LVIS_FOCUSED; 10046 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED; 10047 10048 LISTVIEW_SetItemState(infoPtr,nItem,&item); 10049 infoPtr->nSelectionMark = nItem; 10050 } 10051 } 10052 else if (wKey & MK_CONTROL) 10053 { 10054 LVITEMW item; 10055 10056 bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0); 10057 10058 item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED; 10059 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED; 10060 LISTVIEW_SetItemState(infoPtr, nItem, &item); 10061 infoPtr->nSelectionMark = nItem; 10062 } 10063 else if (wKey & MK_SHIFT) 10064 { 10065 LISTVIEW_SetGroupSelection(infoPtr, nItem); 10066 } 10067 else 10068 { 10069 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED)) 10070 { 10071 infoPtr->nEditLabelItem = nItem; 10072 infoPtr->nLButtonDownItem = nItem; 10073 10074 LISTVIEW_SetItemFocus(infoPtr, nItem); 10075 } 10076 else 10077 /* set selection (clears other pre-existing selections) */ 10078 LISTVIEW_SetSelection(infoPtr, nItem); 10079 } 10080 } 10081 10082 if (infoPtr->dwLvExStyle & LVS_EX_ONECLICKACTIVATE) 10083 if(lvHitTestInfo.iItem != -1) notify_itemactivate(infoPtr,&lvHitTestInfo); 10084 } 10085 else 10086 { 10087 if (!infoPtr->bFocus) 10088 SetFocus(infoPtr->hwndSelf); 10089 10090 /* remove all selections */ 10091 if (!(wKey & MK_CONTROL) && !(wKey & MK_SHIFT)) 10092 LISTVIEW_DeselectAll(infoPtr); 10093 ReleaseCapture(); 10094 } 10095 10096 return 0; 10097 } 10098 10099 /*** 10100 * DESCRIPTION: 10101 * Processes mouse up messages (left mouse button). 10102 * 10103 * PARAMETERS: 10104 * infoPtr [I ] valid pointer to the listview structure 10105 * wKey [I ] key flag 10106 * x,y [I ] mouse coordinate 10107 * 10108 * RETURN: 10109 * Zero 10110 */ 10111 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y) 10112 { 10113 LVHITTESTINFO lvHitTestInfo; 10114 10115 TRACE("(key=%hu, X=%u, Y=%u)\n", wKey, x, y); 10116 10117 if (!infoPtr->bLButtonDown) return 0; 10118 10119 lvHitTestInfo.pt.x = x; 10120 lvHitTestInfo.pt.y = y; 10121 10122 /* send NM_CLICK notification */ 10123 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE); 10124 if (!notify_click(infoPtr, NM_CLICK, &lvHitTestInfo)) return 0; 10125 10126 /* set left button flag */ 10127 infoPtr->bLButtonDown = FALSE; 10128 10129 /* set a single selection, reset others */ 10130 if(lvHitTestInfo.iItem == infoPtr->nLButtonDownItem && lvHitTestInfo.iItem != -1) 10131 LISTVIEW_SetSelection(infoPtr, infoPtr->nLButtonDownItem); 10132 infoPtr->nLButtonDownItem = -1; 10133 10134 if (infoPtr->bDragging || infoPtr->bMarqueeSelect) 10135 { 10136 /* Remove the marquee rectangle and release our mouse capture */ 10137 if (infoPtr->bMarqueeSelect) 10138 { 10139 LISTVIEW_InvalidateRect(infoPtr, &infoPtr->marqueeDrawRect); 10140 ReleaseCapture(); 10141 } 10142 10143 SetRect(&infoPtr->marqueeRect, 0, 0, 0, 0); 10144 SetRect(&infoPtr->marqueeDrawRect, 0, 0, 0, 0); 10145 10146 infoPtr->bDragging = FALSE; 10147 infoPtr->bMarqueeSelect = FALSE; 10148 infoPtr->bScrolling = FALSE; 10149 10150 KillTimer(infoPtr->hwndSelf, (UINT_PTR) infoPtr); 10151 return 0; 10152 } 10153 10154 /* if we clicked on a selected item, edit the label */ 10155 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && (lvHitTestInfo.flags & LVHT_ONITEMLABEL)) 10156 { 10157 /* we want to make sure the user doesn't want to do a double click. So we will 10158 * delay the edit. WM_LBUTTONDBLCLICK will cancel the timer 10159 */ 10160 infoPtr->itemEdit.fEnabled = TRUE; 10161 infoPtr->itemEdit.iItem = lvHitTestInfo.iItem; 10162 SetTimer(infoPtr->hwndSelf, 10163 (UINT_PTR)&infoPtr->itemEdit, 10164 GetDoubleClickTime(), 10165 LISTVIEW_DelayedEditItem); 10166 } 10167 10168 if (!infoPtr->bFocus) 10169 SetFocus(infoPtr->hwndSelf); 10170 10171 return 0; 10172 } 10173 10174 /*** 10175 * DESCRIPTION: 10176 * Destroys the listview control (called after WM_DESTROY). 10177 * 10178 * PARAMETER(S): 10179 * [I] infoPtr : valid pointer to the listview structure 10180 * 10181 * RETURN: 10182 * Zero 10183 */ 10184 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr) 10185 { 10186 INT i; 10187 10188 TRACE("()\n"); 10189 10190 /* destroy data structure */ 10191 DPA_Destroy(infoPtr->hdpaItems); 10192 DPA_Destroy(infoPtr->hdpaItemIds); 10193 DPA_Destroy(infoPtr->hdpaPosX); 10194 DPA_Destroy(infoPtr->hdpaPosY); 10195 /* columns */ 10196 for (i = 0; i < DPA_GetPtrCount(infoPtr->hdpaColumns); i++) 10197 Free(DPA_GetPtr(infoPtr->hdpaColumns, i)); 10198 DPA_Destroy(infoPtr->hdpaColumns); 10199 ranges_destroy(infoPtr->selectionRanges); 10200 10201 /* destroy image lists */ 10202 if (!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS)) 10203 { 10204 ImageList_Destroy(infoPtr->himlNormal); 10205 ImageList_Destroy(infoPtr->himlSmall); 10206 ImageList_Destroy(infoPtr->himlState); 10207 } 10208 10209 /* destroy font, bkgnd brush */ 10210 infoPtr->hFont = 0; 10211 if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont); 10212 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush); 10213 10214 SetWindowLongPtrW(infoPtr->hwndSelf, 0, 0); 10215 10216 /* free listview info pointer*/ 10217 Free(infoPtr); 10218 10219 return 0; 10220 } 10221 10222 /*** 10223 * DESCRIPTION: 10224 * Handles notifications. 10225 * 10226 * PARAMETER(S): 10227 * [I] infoPtr : valid pointer to the listview structure 10228 * [I] lpnmhdr : notification information 10229 * 10230 * RETURN: 10231 * Zero 10232 */ 10233 static LRESULT LISTVIEW_Notify(LISTVIEW_INFO *infoPtr, const NMHDR *lpnmhdr) 10234 { 10235 const NMHEADERW *lpnmh; 10236 10237 TRACE("(lpnmhdr=%p)\n", lpnmhdr); 10238 10239 if (!lpnmhdr || lpnmhdr->hwndFrom != infoPtr->hwndHeader) return 0; 10240 10241 /* remember: HDN_LAST < HDN_FIRST */ 10242 if (lpnmhdr->code > HDN_FIRST || lpnmhdr->code < HDN_LAST) return 0; 10243 lpnmh = (const NMHEADERW *)lpnmhdr; 10244 10245 if (lpnmh->iItem < 0 || lpnmh->iItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0; 10246 10247 switch (lpnmhdr->code) 10248 { 10249 case HDN_TRACKW: 10250 case HDN_TRACKA: 10251 { 10252 COLUMN_INFO *lpColumnInfo; 10253 POINT ptOrigin; 10254 INT x; 10255 10256 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH)) 10257 break; 10258 10259 /* remove the old line (if any) */ 10260 LISTVIEW_DrawTrackLine(infoPtr); 10261 10262 /* compute & draw the new line */ 10263 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem); 10264 x = lpColumnInfo->rcHeader.left + lpnmh->pitem->cxy; 10265 LISTVIEW_GetOrigin(infoPtr, &ptOrigin); 10266 infoPtr->xTrackLine = x + ptOrigin.x; 10267 LISTVIEW_DrawTrackLine(infoPtr); 10268 break; 10269 } 10270 10271 case HDN_ENDTRACKA: 10272 case HDN_ENDTRACKW: 10273 /* remove the track line (if any) */ 10274 LISTVIEW_DrawTrackLine(infoPtr); 10275 infoPtr->xTrackLine = -1; 10276 break; 10277 10278 case HDN_BEGINDRAG: 10279 notify_forward_header(infoPtr, lpnmh); 10280 return (infoPtr->dwLvExStyle & LVS_EX_HEADERDRAGDROP) == 0; 10281 10282 case HDN_ENDDRAG: 10283 infoPtr->colRectsDirty = TRUE; 10284 LISTVIEW_InvalidateList(infoPtr); 10285 notify_forward_header(infoPtr, lpnmh); 10286 return FALSE; 10287 10288 case HDN_ITEMCHANGEDW: 10289 case HDN_ITEMCHANGEDA: 10290 { 10291 COLUMN_INFO *lpColumnInfo; 10292 HDITEMW hdi; 10293 INT dx, cxy; 10294 10295 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH)) 10296 { 10297 hdi.mask = HDI_WIDTH; 10298 if (!SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, lpnmh->iItem, (LPARAM)&hdi)) return 0; 10299 cxy = hdi.cxy; 10300 } 10301 else 10302 cxy = lpnmh->pitem->cxy; 10303 10304 /* determine how much we change since the last know position */ 10305 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem); 10306 dx = cxy - (lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left); 10307 if (dx != 0) 10308 { 10309 lpColumnInfo->rcHeader.right += dx; 10310 10311 hdi.mask = HDI_ORDER; 10312 SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, lpnmh->iItem, (LPARAM)&hdi); 10313 10314 /* not the rightmost one */ 10315 if (hdi.iOrder + 1 < DPA_GetPtrCount(infoPtr->hdpaColumns)) 10316 { 10317 INT nIndex = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX, 10318 hdi.iOrder + 1, 0); 10319 LISTVIEW_ScrollColumns(infoPtr, nIndex, dx); 10320 } 10321 else 10322 { 10323 /* only needs to update the scrolls */ 10324 infoPtr->nItemWidth += dx; 10325 LISTVIEW_UpdateScroll(infoPtr); 10326 } 10327 LISTVIEW_UpdateItemSize(infoPtr); 10328 if (infoPtr->uView == LV_VIEW_DETAILS && is_redrawing(infoPtr)) 10329 { 10330 POINT ptOrigin; 10331 RECT rcCol = lpColumnInfo->rcHeader; 10332 10333 LISTVIEW_GetOrigin(infoPtr, &ptOrigin); 10334 OffsetRect(&rcCol, ptOrigin.x, 0); 10335 10336 rcCol.top = infoPtr->rcList.top; 10337 rcCol.bottom = infoPtr->rcList.bottom; 10338 10339 /* resizing left-aligned columns leaves most of the left side untouched */ 10340 if ((lpColumnInfo->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT) 10341 { 10342 INT nMaxDirty = infoPtr->nEllipsisWidth + infoPtr->ntmMaxCharWidth; 10343 if (dx > 0) 10344 nMaxDirty += dx; 10345 rcCol.left = max (rcCol.left, rcCol.right - nMaxDirty); 10346 } 10347 10348 /* when shrinking the last column clear the now unused field */ 10349 if (hdi.iOrder == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1) 10350 { 10351 RECT right; 10352 10353 rcCol.right -= dx; 10354 10355 /* deal with right from rightmost column area */ 10356 right.left = rcCol.right; 10357 right.top = rcCol.top; 10358 right.bottom = rcCol.bottom; 10359 right.right = infoPtr->rcList.right; 10360 10361 LISTVIEW_InvalidateRect(infoPtr, &right); 10362 } 10363 10364 LISTVIEW_InvalidateRect(infoPtr, &rcCol); 10365 } 10366 } 10367 } 10368 break; 10369 10370 case HDN_ITEMCLICKW: 10371 case HDN_ITEMCLICKA: 10372 { 10373 /* Handle sorting by Header Column */ 10374 NMLISTVIEW nmlv; 10375 10376 ZeroMemory(&nmlv, sizeof(NMLISTVIEW)); 10377 nmlv.iItem = -1; 10378 nmlv.iSubItem = lpnmh->iItem; 10379 notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv); 10380 notify_forward_header(infoPtr, lpnmh); 10381 } 10382 break; 10383 10384 case HDN_DIVIDERDBLCLICKW: 10385 case HDN_DIVIDERDBLCLICKA: 10386 /* FIXME: for LVS_EX_HEADERINALLVIEWS and not LV_VIEW_DETAILS 10387 we should use LVSCW_AUTOSIZE_USEHEADER, helper rework or 10388 split needed for that */ 10389 LISTVIEW_SetColumnWidth(infoPtr, lpnmh->iItem, LVSCW_AUTOSIZE); 10390 notify_forward_header(infoPtr, lpnmh); 10391 break; 10392 } 10393 10394 return 0; 10395 } 10396 10397 /*** 10398 * DESCRIPTION: 10399 * Paint non-client area of control. 10400 * 10401 * PARAMETER(S): 10402 * [I] infoPtr : valid pointer to the listview structureof the sender 10403 * [I] region : update region 10404 * 10405 * RETURN: 10406 * TRUE - frame was painted 10407 * FALSE - call default window proc 10408 */ 10409 static BOOL LISTVIEW_NCPaint(const LISTVIEW_INFO *infoPtr, HRGN region) 10410 { 10411 HTHEME theme = GetWindowTheme (infoPtr->hwndSelf); 10412 HDC dc; 10413 RECT r; 10414 HRGN cliprgn; 10415 int cxEdge = GetSystemMetrics (SM_CXEDGE), 10416 cyEdge = GetSystemMetrics (SM_CYEDGE); 10417 10418 if (!theme) 10419 return DefWindowProcW (infoPtr->hwndSelf, WM_NCPAINT, (WPARAM)region, 0); 10420 10421 GetWindowRect(infoPtr->hwndSelf, &r); 10422 10423 cliprgn = CreateRectRgn (r.left + cxEdge, r.top + cyEdge, 10424 r.right - cxEdge, r.bottom - cyEdge); 10425 if (region != (HRGN)1) 10426 CombineRgn (cliprgn, cliprgn, region, RGN_AND); 10427 OffsetRect(&r, -r.left, -r.top); 10428 10429 dc = GetDCEx(infoPtr->hwndSelf, region, DCX_WINDOW|DCX_INTERSECTRGN); 10430 OffsetRect(&r, -r.left, -r.top); 10431 10432 if (IsThemeBackgroundPartiallyTransparent (theme, 0, 0)) 10433 DrawThemeParentBackground(infoPtr->hwndSelf, dc, &r); 10434 DrawThemeBackground (theme, dc, 0, 0, &r, 0); 10435 ReleaseDC(infoPtr->hwndSelf, dc); 10436 10437 /* Call default proc to get the scrollbars etc. painted */ 10438 DefWindowProcW (infoPtr->hwndSelf, WM_NCPAINT, (WPARAM)cliprgn, 0); 10439 10440 return FALSE; 10441 } 10442 10443 /*** 10444 * DESCRIPTION: 10445 * Determines the type of structure to use. 10446 * 10447 * PARAMETER(S): 10448 * [I] infoPtr : valid pointer to the listview structureof the sender 10449 * [I] hwndFrom : listview window handle 10450 * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT 10451 * 10452 * RETURN: 10453 * Zero 10454 */ 10455 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand) 10456 { 10457 TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom, nCommand); 10458 10459 if (nCommand == NF_REQUERY) 10460 infoPtr->notifyFormat = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY); 10461 10462 return infoPtr->notifyFormat; 10463 } 10464 10465 /*** 10466 * DESCRIPTION: 10467 * Paints/Repaints the listview control. Internal use. 10468 * 10469 * PARAMETER(S): 10470 * [I] infoPtr : valid pointer to the listview structure 10471 * [I] hdc : device context handle 10472 * 10473 * RETURN: 10474 * Zero 10475 */ 10476 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc) 10477 { 10478 TRACE("(hdc=%p)\n", hdc); 10479 10480 if (infoPtr->bNoItemMetrics && infoPtr->nItemCount) 10481 { 10482 infoPtr->bNoItemMetrics = FALSE; 10483 LISTVIEW_UpdateItemSize(infoPtr); 10484 if (infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON) 10485 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT); 10486 LISTVIEW_UpdateScroll(infoPtr); 10487 } 10488 10489 if (infoPtr->hwndHeader) UpdateWindow(infoPtr->hwndHeader); 10490 10491 if (hdc) 10492 LISTVIEW_Refresh(infoPtr, hdc, NULL); 10493 else 10494 { 10495 PAINTSTRUCT ps; 10496 10497 hdc = BeginPaint(infoPtr->hwndSelf, &ps); 10498 if (!hdc) return 1; 10499 LISTVIEW_Refresh(infoPtr, hdc, ps.fErase ? &ps.rcPaint : NULL); 10500 EndPaint(infoPtr->hwndSelf, &ps); 10501 } 10502 10503 return 0; 10504 } 10505 10506 /*** 10507 * DESCRIPTION: 10508 * Paints/Repaints the listview control, WM_PAINT handler. 10509 * 10510 * PARAMETER(S): 10511 * [I] infoPtr : valid pointer to the listview structure 10512 * [I] hdc : device context handle 10513 * 10514 * RETURN: 10515 * Zero 10516 */ 10517 static inline LRESULT LISTVIEW_WMPaint(LISTVIEW_INFO *infoPtr, HDC hdc) 10518 { 10519 TRACE("(hdc=%p)\n", hdc); 10520 10521 if (!is_redrawing(infoPtr)) 10522 return DefWindowProcW (infoPtr->hwndSelf, WM_PAINT, (WPARAM)hdc, 0); 10523 10524 return LISTVIEW_Paint(infoPtr, hdc); 10525 } 10526 10527 /*** 10528 * DESCRIPTION: 10529 * Paints/Repaints the listview control. 10530 * 10531 * PARAMETER(S): 10532 * [I] infoPtr : valid pointer to the listview structure 10533 * [I] hdc : device context handle 10534 * [I] options : drawing options 10535 * 10536 * RETURN: 10537 * Zero 10538 */ 10539 static LRESULT LISTVIEW_PrintClient(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD options) 10540 { 10541 FIXME("Partial Stub: (hdc=%p options=0x%08x)\n", hdc, options); 10542 10543 if ((options & PRF_CHECKVISIBLE) && !IsWindowVisible(infoPtr->hwndSelf)) 10544 return 0; 10545 10546 if (options & PRF_ERASEBKGND) 10547 LISTVIEW_EraseBkgnd(infoPtr, hdc); 10548 10549 if (options & PRF_CLIENT) 10550 LISTVIEW_Paint(infoPtr, hdc); 10551 10552 return 0; 10553 } 10554 10555 10556 /*** 10557 * DESCRIPTION: 10558 * Processes double click messages (right mouse button). 10559 * 10560 * PARAMETER(S): 10561 * [I] infoPtr : valid pointer to the listview structure 10562 * [I] wKey : key flag 10563 * [I] x,y : mouse coordinate 10564 * 10565 * RETURN: 10566 * Zero 10567 */ 10568 static LRESULT LISTVIEW_RButtonDblClk(const LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y) 10569 { 10570 LVHITTESTINFO lvHitTestInfo; 10571 10572 TRACE("(key=%hu,X=%u,Y=%u)\n", wKey, x, y); 10573 10574 /* send NM_RELEASEDCAPTURE notification */ 10575 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0; 10576 10577 /* send NM_RDBLCLK notification */ 10578 lvHitTestInfo.pt.x = x; 10579 lvHitTestInfo.pt.y = y; 10580 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE); 10581 notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo); 10582 10583 return 0; 10584 } 10585 10586 /*** 10587 * DESCRIPTION: 10588 * Processes mouse down messages (right mouse button). 10589 * 10590 * PARAMETER(S): 10591 * [I] infoPtr : valid pointer to the listview structure 10592 * [I] wKey : key flag 10593 * [I] x,y : mouse coordinate 10594 * 10595 * RETURN: 10596 * Zero 10597 */ 10598 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y) 10599 { 10600 LVHITTESTINFO lvHitTestInfo; 10601 INT nItem; 10602 10603 TRACE("(key=%hu,X=%u,Y=%u)\n", wKey, x, y); 10604 10605 /* send NM_RELEASEDCAPTURE notification */ 10606 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0; 10607 10608 /* make sure the listview control window has the focus */ 10609 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf); 10610 10611 /* set right button down flag */ 10612 infoPtr->bRButtonDown = TRUE; 10613 10614 /* determine the index of the selected item */ 10615 lvHitTestInfo.pt.x = x; 10616 lvHitTestInfo.pt.y = y; 10617 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE); 10618 10619 if ((nItem >= 0) && (nItem < infoPtr->nItemCount)) 10620 { 10621 LISTVIEW_SetItemFocus(infoPtr, nItem); 10622 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) && 10623 !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED)) 10624 LISTVIEW_SetSelection(infoPtr, nItem); 10625 } 10626 else 10627 { 10628 LISTVIEW_DeselectAll(infoPtr); 10629 } 10630 10631 return 0; 10632 } 10633 10634 /*** 10635 * DESCRIPTION: 10636 * Processes mouse up messages (right mouse button). 10637 * 10638 * PARAMETER(S): 10639 * [I] infoPtr : valid pointer to the listview structure 10640 * [I] wKey : key flag 10641 * [I] x,y : mouse coordinate 10642 * 10643 * RETURN: 10644 * Zero 10645 */ 10646 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y) 10647 { 10648 LVHITTESTINFO lvHitTestInfo; 10649 POINT pt; 10650 10651 TRACE("(key=%hu,X=%u,Y=%u)\n", wKey, x, y); 10652 10653 if (!infoPtr->bRButtonDown) return 0; 10654 10655 /* set button flag */ 10656 infoPtr->bRButtonDown = FALSE; 10657 10658 /* Send NM_RCLICK notification */ 10659 lvHitTestInfo.pt.x = x; 10660 lvHitTestInfo.pt.y = y; 10661 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE); 10662 if (!notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo)) return 0; 10663 10664 /* Change to screen coordinate for WM_CONTEXTMENU */ 10665 pt = lvHitTestInfo.pt; 10666 ClientToScreen(infoPtr->hwndSelf, &pt); 10667 10668 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */ 10669 SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU, 10670 (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y)); 10671 10672 return 0; 10673 } 10674 10675 10676 /*** 10677 * DESCRIPTION: 10678 * Sets the cursor. 10679 * 10680 * PARAMETER(S): 10681 * [I] infoPtr : valid pointer to the listview structure 10682 * [I] hwnd : window handle of window containing the cursor 10683 * [I] nHittest : hit-test code 10684 * [I] wMouseMsg : ideintifier of the mouse message 10685 * 10686 * RETURN: 10687 * TRUE if cursor is set 10688 * FALSE otherwise 10689 */ 10690 static BOOL LISTVIEW_SetCursor(const LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam) 10691 { 10692 LVHITTESTINFO lvHitTestInfo; 10693 10694 if (!LISTVIEW_IsHotTracking(infoPtr)) goto forward; 10695 10696 if (!infoPtr->hHotCursor) goto forward; 10697 10698 GetCursorPos(&lvHitTestInfo.pt); 10699 if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) goto forward; 10700 10701 SetCursor(infoPtr->hHotCursor); 10702 10703 return TRUE; 10704 10705 forward: 10706 10707 return DefWindowProcW(infoPtr->hwndSelf, WM_SETCURSOR, wParam, lParam); 10708 } 10709 10710 /*** 10711 * DESCRIPTION: 10712 * Sets the focus. 10713 * 10714 * PARAMETER(S): 10715 * [I] infoPtr : valid pointer to the listview structure 10716 * [I] hwndLoseFocus : handle of previously focused window 10717 * 10718 * RETURN: 10719 * Zero 10720 */ 10721 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus) 10722 { 10723 TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus); 10724 10725 /* if we have the focus already, there's nothing to do */ 10726 if (infoPtr->bFocus) return 0; 10727 10728 /* send NM_SETFOCUS notification */ 10729 if (!notify(infoPtr, NM_SETFOCUS)) return 0; 10730 10731 /* set window focus flag */ 10732 infoPtr->bFocus = TRUE; 10733 10734 /* put the focus rect back on */ 10735 LISTVIEW_ShowFocusRect(infoPtr, TRUE); 10736 10737 /* redraw all visible selected items */ 10738 LISTVIEW_InvalidateSelectedItems(infoPtr); 10739 10740 return 0; 10741 } 10742 10743 /*** 10744 * DESCRIPTION: 10745 * Sets the font. 10746 * 10747 * PARAMETER(S): 10748 * [I] infoPtr : valid pointer to the listview structure 10749 * [I] fRedraw : font handle 10750 * [I] fRedraw : redraw flag 10751 * 10752 * RETURN: 10753 * Zero 10754 */ 10755 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw) 10756 { 10757 HFONT oldFont = infoPtr->hFont; 10758 10759 TRACE("(hfont=%p,redraw=%hu)\n", hFont, fRedraw); 10760 10761 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont; 10762 if (infoPtr->hFont == oldFont) return 0; 10763 10764 LISTVIEW_SaveTextMetrics(infoPtr); 10765 10766 if (infoPtr->uView == LV_VIEW_DETAILS) 10767 { 10768 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0)); 10769 LISTVIEW_UpdateSize(infoPtr); 10770 LISTVIEW_UpdateScroll(infoPtr); 10771 } 10772 10773 if (fRedraw) LISTVIEW_InvalidateList(infoPtr); 10774 10775 return 0; 10776 } 10777 10778 /*** 10779 * DESCRIPTION: 10780 * Message handling for WM_SETREDRAW. 10781 * For the Listview, it invalidates the entire window (the doc specifies otherwise) 10782 * 10783 * PARAMETER(S): 10784 * [I] infoPtr : valid pointer to the listview structure 10785 * [I] bRedraw: state of redraw flag 10786 * 10787 * RETURN: 10788 * DefWinProc return value 10789 */ 10790 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw) 10791 { 10792 TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr->bRedraw, bRedraw); 10793 10794 /* we cannot use straight equality here because _any_ non-zero value is TRUE */ 10795 if ((infoPtr->bRedraw && bRedraw) || (!infoPtr->bRedraw && !bRedraw)) return 0; 10796 10797 infoPtr->bRedraw = bRedraw; 10798 10799 if(!bRedraw) return 0; 10800 10801 if (is_autoarrange(infoPtr)) 10802 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT); 10803 LISTVIEW_UpdateScroll(infoPtr); 10804 10805 /* despite what the WM_SETREDRAW docs says, apps expect us 10806 * to invalidate the listview here... stupid! */ 10807 LISTVIEW_InvalidateList(infoPtr); 10808 10809 return 0; 10810 } 10811 10812 /*** 10813 * DESCRIPTION: 10814 * Resizes the listview control. This function processes WM_SIZE 10815 * messages. At this time, the width and height are not used. 10816 * 10817 * PARAMETER(S): 10818 * [I] infoPtr : valid pointer to the listview structure 10819 * [I] Width : new width 10820 * [I] Height : new height 10821 * 10822 * RETURN: 10823 * Zero 10824 */ 10825 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height) 10826 { 10827 RECT rcOld = infoPtr->rcList; 10828 10829 TRACE("(width=%d, height=%d)\n", Width, Height); 10830 10831 LISTVIEW_UpdateSize(infoPtr); 10832 if (EqualRect(&rcOld, &infoPtr->rcList)) return 0; 10833 10834 /* do not bother with display related stuff if we're not redrawing */ 10835 if (!is_redrawing(infoPtr)) return 0; 10836 10837 if (is_autoarrange(infoPtr)) 10838 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT); 10839 10840 LISTVIEW_UpdateScroll(infoPtr); 10841 10842 /* refresh all only for lists whose height changed significantly */ 10843 if ((infoPtr->uView == LV_VIEW_LIST) && 10844 (rcOld.bottom - rcOld.top) / infoPtr->nItemHeight != 10845 (infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight) 10846 LISTVIEW_InvalidateList(infoPtr); 10847 10848 return 0; 10849 } 10850 10851 /*** 10852 * DESCRIPTION: 10853 * Sets the size information. 10854 * 10855 * PARAMETER(S): 10856 * [I] infoPtr : valid pointer to the listview structure 10857 * 10858 * RETURN: 10859 * None 10860 */ 10861 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr) 10862 { 10863 TRACE("uView=%d, rcList(old)=%s\n", infoPtr->uView, wine_dbgstr_rect(&infoPtr->rcList)); 10864 10865 GetClientRect(infoPtr->hwndSelf, &infoPtr->rcList); 10866 10867 if (infoPtr->uView == LV_VIEW_LIST) 10868 { 10869 /* Apparently the "LIST" style is supposed to have the same 10870 * number of items in a column even if there is no scroll bar. 10871 * Since if a scroll bar already exists then the bottom is already 10872 * reduced, only reduce if the scroll bar does not currently exist. 10873 * The "2" is there to mimic the native control. I think it may be 10874 * related to either padding or edges. (GLA 7/2002) 10875 */ 10876 if (!(infoPtr->dwStyle & WS_HSCROLL)) 10877 infoPtr->rcList.bottom -= GetSystemMetrics(SM_CYHSCROLL); 10878 infoPtr->rcList.bottom = max (infoPtr->rcList.bottom - 2, 0); 10879 } 10880 10881 /* if control created invisible header isn't created */ 10882 if (infoPtr->hwndHeader) 10883 { 10884 HDLAYOUT hl; 10885 WINDOWPOS wp; 10886 10887 hl.prc = &infoPtr->rcList; 10888 hl.pwpos = ℘ 10889 SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl ); 10890 TRACE(" wp.flags=0x%08x, wp=%d,%d (%dx%d)\n", wp.flags, wp.x, wp.y, wp.cx, wp.cy); 10891 10892 if (LISTVIEW_IsHeaderEnabled(infoPtr)) 10893 wp.flags |= SWP_SHOWWINDOW; 10894 else 10895 { 10896 wp.flags |= SWP_HIDEWINDOW; 10897 wp.cy = 0; 10898 } 10899 10900 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags); 10901 TRACE(" after SWP wp=%d,%d (%dx%d)\n", wp.x, wp.y, wp.cx, wp.cy); 10902 10903 infoPtr->rcList.top = max(wp.cy, 0); 10904 } 10905 /* extra padding for grid */ 10906 if (infoPtr->uView == LV_VIEW_DETAILS && infoPtr->dwLvExStyle & LVS_EX_GRIDLINES) 10907 infoPtr->rcList.top += 2; 10908 10909 TRACE(" rcList=%s\n", wine_dbgstr_rect(&infoPtr->rcList)); 10910 } 10911 10912 /*** 10913 * DESCRIPTION: 10914 * Processes WM_STYLECHANGED messages. 10915 * 10916 * PARAMETER(S): 10917 * [I] infoPtr : valid pointer to the listview structure 10918 * [I] wStyleType : window style type (normal or extended) 10919 * [I] lpss : window style information 10920 * 10921 * RETURN: 10922 * Zero 10923 */ 10924 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType, 10925 const STYLESTRUCT *lpss) 10926 { 10927 UINT uNewView = lpss->styleNew & LVS_TYPEMASK; 10928 UINT uOldView = lpss->styleOld & LVS_TYPEMASK; 10929 UINT style; 10930 10931 TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n", 10932 wStyleType, lpss->styleOld, lpss->styleNew); 10933 10934 if (wStyleType != GWL_STYLE) return 0; 10935 10936 infoPtr->dwStyle = lpss->styleNew; 10937 map_style_view(infoPtr); 10938 10939 if (((lpss->styleOld & WS_HSCROLL) != 0)&& 10940 ((lpss->styleNew & WS_HSCROLL) == 0)) 10941 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE); 10942 10943 if (((lpss->styleOld & WS_VSCROLL) != 0)&& 10944 ((lpss->styleNew & WS_VSCROLL) == 0)) 10945 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE); 10946 10947 if (uNewView != uOldView) 10948 { 10949 SIZE oldIconSize = infoPtr->iconSize; 10950 HIMAGELIST himl; 10951 10952 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0); 10953 ShowWindow(infoPtr->hwndHeader, SW_HIDE); 10954 10955 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE); 10956 SetRectEmpty(&infoPtr->rcFocus); 10957 10958 himl = (uNewView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall); 10959 set_icon_size(&infoPtr->iconSize, himl, uNewView != LVS_ICON); 10960 10961 if (uNewView == LVS_ICON) 10962 { 10963 if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy)) 10964 { 10965 TRACE("icon old size=(%d,%d), new size=(%d,%d)\n", 10966 oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy); 10967 LISTVIEW_SetIconSpacing(infoPtr, 0, 0); 10968 } 10969 } 10970 else if (uNewView == LVS_REPORT) 10971 { 10972 HDLAYOUT hl; 10973 WINDOWPOS wp; 10974 10975 LISTVIEW_CreateHeader( infoPtr ); 10976 10977 hl.prc = &infoPtr->rcList; 10978 hl.pwpos = ℘ 10979 SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl ); 10980 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy, 10981 wp.flags | ((infoPtr->dwStyle & LVS_NOCOLUMNHEADER) 10982 ? SWP_HIDEWINDOW : SWP_SHOWWINDOW)); 10983 } 10984 10985 LISTVIEW_UpdateItemSize(infoPtr); 10986 } 10987 10988 if (uNewView == LVS_REPORT || infoPtr->dwLvExStyle & LVS_EX_HEADERINALLVIEWS) 10989 { 10990 if ((lpss->styleOld ^ lpss->styleNew) & LVS_NOCOLUMNHEADER) 10991 { 10992 if (lpss->styleNew & LVS_NOCOLUMNHEADER) 10993 { 10994 /* Turn off the header control */ 10995 style = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE); 10996 TRACE("Hide header control, was 0x%08x\n", style); 10997 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, style | HDS_HIDDEN); 10998 } else { 10999 /* Turn on the header control */ 11000 if ((style = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE)) & HDS_HIDDEN) 11001 { 11002 TRACE("Show header control, was 0x%08x\n", style); 11003 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, (style & ~HDS_HIDDEN) | WS_VISIBLE); 11004 } 11005 } 11006 } 11007 } 11008 11009 if ( (uNewView == LVS_ICON || uNewView == LVS_SMALLICON) && 11010 (uNewView != uOldView || ((lpss->styleNew ^ lpss->styleOld) & LVS_ALIGNMASK)) ) 11011 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT); 11012 11013 /* update the size of the client area */ 11014 LISTVIEW_UpdateSize(infoPtr); 11015 11016 /* add scrollbars if needed */ 11017 LISTVIEW_UpdateScroll(infoPtr); 11018 11019 /* invalidate client area + erase background */ 11020 LISTVIEW_InvalidateList(infoPtr); 11021 11022 return 0; 11023 } 11024 11025 /*** 11026 * DESCRIPTION: 11027 * Processes WM_STYLECHANGING messages. 11028 * 11029 * PARAMETER(S): 11030 * [I] wStyleType : window style type (normal or extended) 11031 * [I0] lpss : window style information 11032 * 11033 * RETURN: 11034 * Zero 11035 */ 11036 static INT LISTVIEW_StyleChanging(WPARAM wStyleType, 11037 STYLESTRUCT *lpss) 11038 { 11039 TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n", 11040 wStyleType, lpss->styleOld, lpss->styleNew); 11041 11042 /* don't forward LVS_OWNERDATA only if not already set to */ 11043 if ((lpss->styleNew ^ lpss->styleOld) & LVS_OWNERDATA) 11044 { 11045 if (lpss->styleOld & LVS_OWNERDATA) 11046 lpss->styleNew |= LVS_OWNERDATA; 11047 else 11048 lpss->styleNew &= ~LVS_OWNERDATA; 11049 } 11050 11051 return 0; 11052 } 11053 11054 /*** 11055 * DESCRIPTION: 11056 * Processes WM_SHOWWINDOW messages. 11057 * 11058 * PARAMETER(S): 11059 * [I] infoPtr : valid pointer to the listview structure 11060 * [I] bShown : window is being shown (FALSE when hidden) 11061 * [I] iStatus : window show status 11062 * 11063 * RETURN: 11064 * Zero 11065 */ 11066 static LRESULT LISTVIEW_ShowWindow(LISTVIEW_INFO *infoPtr, WPARAM bShown, LPARAM iStatus) 11067 { 11068 /* header delayed creation */ 11069 if ((infoPtr->uView == LV_VIEW_DETAILS) && bShown) 11070 { 11071 LISTVIEW_CreateHeader(infoPtr); 11072 11073 if (!(LVS_NOCOLUMNHEADER & infoPtr->dwStyle)) 11074 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL); 11075 } 11076 11077 return DefWindowProcW(infoPtr->hwndSelf, WM_SHOWWINDOW, bShown, iStatus); 11078 } 11079 11080 /*** 11081 * DESCRIPTION: 11082 * Processes CCM_GETVERSION messages. 11083 * 11084 * PARAMETER(S): 11085 * [I] infoPtr : valid pointer to the listview structure 11086 * 11087 * RETURN: 11088 * Current version 11089 */ 11090 static inline LRESULT LISTVIEW_GetVersion(const LISTVIEW_INFO *infoPtr) 11091 { 11092 return infoPtr->iVersion; 11093 } 11094 11095 /*** 11096 * DESCRIPTION: 11097 * Processes CCM_SETVERSION messages. 11098 * 11099 * PARAMETER(S): 11100 * [I] infoPtr : valid pointer to the listview structure 11101 * [I] iVersion : version to be set 11102 * 11103 * RETURN: 11104 * -1 when requested version is greater than DLL version; 11105 * previous version otherwise 11106 */ 11107 static LRESULT LISTVIEW_SetVersion(LISTVIEW_INFO *infoPtr, DWORD iVersion) 11108 { 11109 INT iOldVersion = infoPtr->iVersion; 11110 11111 if (iVersion > COMCTL32_VERSION) 11112 return -1; 11113 11114 infoPtr->iVersion = iVersion; 11115 11116 TRACE("new version %d\n", iVersion); 11117 11118 return iOldVersion; 11119 } 11120 11121 /*** 11122 * DESCRIPTION: 11123 * Window procedure of the listview control. 11124 * 11125 */ 11126 static LRESULT WINAPI 11127 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 11128 { 11129 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0); 11130 11131 TRACE("(hwnd=%p uMsg=%x wParam=%lx lParam=%lx)\n", hwnd, uMsg, wParam, lParam); 11132 11133 if (!infoPtr && (uMsg != WM_NCCREATE)) 11134 return DefWindowProcW(hwnd, uMsg, wParam, lParam); 11135 11136 switch (uMsg) 11137 { 11138 case LVM_APPROXIMATEVIEWRECT: 11139 return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam, 11140 LOWORD(lParam), HIWORD(lParam)); 11141 case LVM_ARRANGE: 11142 return LISTVIEW_Arrange(infoPtr, (INT)wParam); 11143 11144 case LVM_CANCELEDITLABEL: 11145 return LISTVIEW_CancelEditLabel(infoPtr); 11146 11147 case LVM_CREATEDRAGIMAGE: 11148 return (LRESULT)LISTVIEW_CreateDragImage(infoPtr, (INT)wParam, (LPPOINT)lParam); 11149 11150 case LVM_DELETEALLITEMS: 11151 return LISTVIEW_DeleteAllItems(infoPtr, FALSE); 11152 11153 case LVM_DELETECOLUMN: 11154 return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam); 11155 11156 case LVM_DELETEITEM: 11157 return LISTVIEW_DeleteItem(infoPtr, (INT)wParam); 11158 11159 case LVM_EDITLABELA: 11160 case LVM_EDITLABELW: 11161 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, 11162 uMsg == LVM_EDITLABELW); 11163 /* case LVM_ENABLEGROUPVIEW: */ 11164 11165 case LVM_ENSUREVISIBLE: 11166 return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam); 11167 11168 case LVM_FINDITEMW: 11169 return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam); 11170 11171 case LVM_FINDITEMA: 11172 return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam); 11173 11174 case LVM_GETBKCOLOR: 11175 return infoPtr->clrBk; 11176 11177 /* case LVM_GETBKIMAGE: */ 11178 11179 case LVM_GETCALLBACKMASK: 11180 return infoPtr->uCallbackMask; 11181 11182 case LVM_GETCOLUMNA: 11183 case LVM_GETCOLUMNW: 11184 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, 11185 uMsg == LVM_GETCOLUMNW); 11186 11187 case LVM_GETCOLUMNORDERARRAY: 11188 return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam); 11189 11190 case LVM_GETCOLUMNWIDTH: 11191 return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam); 11192 11193 case LVM_GETCOUNTPERPAGE: 11194 return LISTVIEW_GetCountPerPage(infoPtr); 11195 11196 case LVM_GETEDITCONTROL: 11197 return (LRESULT)infoPtr->hwndEdit; 11198 11199 case LVM_GETEXTENDEDLISTVIEWSTYLE: 11200 return infoPtr->dwLvExStyle; 11201 11202 /* case LVM_GETGROUPINFO: */ 11203 11204 /* case LVM_GETGROUPMETRICS: */ 11205 11206 case LVM_GETHEADER: 11207 return (LRESULT)infoPtr->hwndHeader; 11208 11209 case LVM_GETHOTCURSOR: 11210 return (LRESULT)infoPtr->hHotCursor; 11211 11212 case LVM_GETHOTITEM: 11213 return infoPtr->nHotItem; 11214 11215 case LVM_GETHOVERTIME: 11216 return infoPtr->dwHoverTime; 11217 11218 case LVM_GETIMAGELIST: 11219 return (LRESULT)LISTVIEW_GetImageList(infoPtr, (INT)wParam); 11220 11221 /* case LVM_GETINSERTMARK: */ 11222 11223 /* case LVM_GETINSERTMARKCOLOR: */ 11224 11225 /* case LVM_GETINSERTMARKRECT: */ 11226 11227 case LVM_GETISEARCHSTRINGA: 11228 case LVM_GETISEARCHSTRINGW: 11229 FIXME("LVM_GETISEARCHSTRING: unimplemented\n"); 11230 return FALSE; 11231 11232 case LVM_GETITEMA: 11233 case LVM_GETITEMW: 11234 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, uMsg == LVM_GETITEMW); 11235 11236 case LVM_GETITEMCOUNT: 11237 return infoPtr->nItemCount; 11238 11239 case LVM_GETITEMPOSITION: 11240 return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam); 11241 11242 case LVM_GETITEMRECT: 11243 return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam); 11244 11245 case LVM_GETITEMSPACING: 11246 return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam); 11247 11248 case LVM_GETITEMSTATE: 11249 return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam); 11250 11251 case LVM_GETITEMTEXTA: 11252 case LVM_GETITEMTEXTW: 11253 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, 11254 uMsg == LVM_GETITEMTEXTW); 11255 11256 case LVM_GETNEXTITEM: 11257 return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam)); 11258 11259 case LVM_GETNUMBEROFWORKAREAS: 11260 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n"); 11261 return 1; 11262 11263 case LVM_GETORIGIN: 11264 if (!lParam) return FALSE; 11265 if (infoPtr->uView == LV_VIEW_DETAILS || 11266 infoPtr->uView == LV_VIEW_LIST) return FALSE; 11267 LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam); 11268 return TRUE; 11269 11270 /* case LVM_GETOUTLINECOLOR: */ 11271 11272 /* case LVM_GETSELECTEDCOLUMN: */ 11273 11274 case LVM_GETSELECTEDCOUNT: 11275 return LISTVIEW_GetSelectedCount(infoPtr); 11276 11277 case LVM_GETSELECTIONMARK: 11278 return infoPtr->nSelectionMark; 11279 11280 case LVM_GETSTRINGWIDTHA: 11281 case LVM_GETSTRINGWIDTHW: 11282 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, 11283 uMsg == LVM_GETSTRINGWIDTHW); 11284 11285 case LVM_GETSUBITEMRECT: 11286 return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam); 11287 11288 case LVM_GETTEXTBKCOLOR: 11289 return infoPtr->clrTextBk; 11290 11291 case LVM_GETTEXTCOLOR: 11292 return infoPtr->clrText; 11293 11294 /* case LVM_GETTILEINFO: */ 11295 11296 /* case LVM_GETTILEVIEWINFO: */ 11297 11298 case LVM_GETTOOLTIPS: 11299 if( !infoPtr->hwndToolTip ) 11300 infoPtr->hwndToolTip = COMCTL32_CreateToolTip( hwnd ); 11301 return (LRESULT)infoPtr->hwndToolTip; 11302 11303 case LVM_GETTOPINDEX: 11304 return LISTVIEW_GetTopIndex(infoPtr); 11305 11306 case LVM_GETUNICODEFORMAT: 11307 return (infoPtr->notifyFormat == NFR_UNICODE); 11308 11309 case LVM_GETVIEW: 11310 return infoPtr->uView; 11311 11312 case LVM_GETVIEWRECT: 11313 return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam); 11314 11315 case LVM_GETWORKAREAS: 11316 FIXME("LVM_GETWORKAREAS: unimplemented\n"); 11317 return FALSE; 11318 11319 /* case LVM_HASGROUP: */ 11320 11321 case LVM_HITTEST: 11322 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, TRUE); 11323 11324 case LVM_INSERTCOLUMNA: 11325 case LVM_INSERTCOLUMNW: 11326 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, 11327 uMsg == LVM_INSERTCOLUMNW); 11328 11329 /* case LVM_INSERTGROUP: */ 11330 11331 /* case LVM_INSERTGROUPSORTED: */ 11332 11333 case LVM_INSERTITEMA: 11334 case LVM_INSERTITEMW: 11335 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, uMsg == LVM_INSERTITEMW); 11336 11337 /* case LVM_INSERTMARKHITTEST: */ 11338 11339 /* case LVM_ISGROUPVIEWENABLED: */ 11340 11341 case LVM_ISITEMVISIBLE: 11342 return LISTVIEW_IsItemVisible(infoPtr, (INT)wParam); 11343 11344 case LVM_MAPIDTOINDEX: 11345 return LISTVIEW_MapIdToIndex(infoPtr, (UINT)wParam); 11346 11347 case LVM_MAPINDEXTOID: 11348 return LISTVIEW_MapIndexToId(infoPtr, (INT)wParam); 11349 11350 /* case LVM_MOVEGROUP: */ 11351 11352 /* case LVM_MOVEITEMTOGROUP: */ 11353 11354 case LVM_REDRAWITEMS: 11355 return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam); 11356 11357 /* case LVM_REMOVEALLGROUPS: */ 11358 11359 /* case LVM_REMOVEGROUP: */ 11360 11361 case LVM_SCROLL: 11362 return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam); 11363 11364 case LVM_SETBKCOLOR: 11365 return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam); 11366 11367 /* case LVM_SETBKIMAGE: */ 11368 11369 case LVM_SETCALLBACKMASK: 11370 infoPtr->uCallbackMask = (UINT)wParam; 11371 return TRUE; 11372 11373 case LVM_SETCOLUMNA: 11374 case LVM_SETCOLUMNW: 11375 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, 11376 uMsg == LVM_SETCOLUMNW); 11377 11378 case LVM_SETCOLUMNORDERARRAY: 11379 return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam); 11380 11381 case LVM_SETCOLUMNWIDTH: 11382 return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, (short)LOWORD(lParam)); 11383 11384 case LVM_SETEXTENDEDLISTVIEWSTYLE: 11385 return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam); 11386 11387 /* case LVM_SETGROUPINFO: */ 11388 11389 /* case LVM_SETGROUPMETRICS: */ 11390 11391 case LVM_SETHOTCURSOR: 11392 return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam); 11393 11394 case LVM_SETHOTITEM: 11395 return LISTVIEW_SetHotItem(infoPtr, (INT)wParam); 11396 11397 case LVM_SETHOVERTIME: 11398 return LISTVIEW_SetHoverTime(infoPtr, (DWORD)lParam); 11399 11400 case LVM_SETICONSPACING: 11401 return LISTVIEW_SetIconSpacing(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam)); 11402 11403 case LVM_SETIMAGELIST: 11404 return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam); 11405 11406 /* case LVM_SETINFOTIP: */ 11407 11408 /* case LVM_SETINSERTMARK: */ 11409 11410 /* case LVM_SETINSERTMARKCOLOR: */ 11411 11412 case LVM_SETITEMA: 11413 case LVM_SETITEMW: 11414 { 11415 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE; 11416 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, (uMsg == LVM_SETITEMW)); 11417 } 11418 11419 case LVM_SETITEMCOUNT: 11420 return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam); 11421 11422 case LVM_SETITEMPOSITION: 11423 { 11424 POINT pt; 11425 pt.x = (short)LOWORD(lParam); 11426 pt.y = (short)HIWORD(lParam); 11427 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, &pt); 11428 } 11429 11430 case LVM_SETITEMPOSITION32: 11431 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, (POINT*)lParam); 11432 11433 case LVM_SETITEMSTATE: 11434 if (lParam == 0) return FALSE; 11435 return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam); 11436 11437 case LVM_SETITEMTEXTA: 11438 case LVM_SETITEMTEXTW: 11439 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, 11440 uMsg == LVM_SETITEMTEXTW); 11441 11442 /* case LVM_SETOUTLINECOLOR: */ 11443 11444 /* case LVM_SETSELECTEDCOLUMN: */ 11445 11446 case LVM_SETSELECTIONMARK: 11447 return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam); 11448 11449 case LVM_SETTEXTBKCOLOR: 11450 return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam); 11451 11452 case LVM_SETTEXTCOLOR: 11453 return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam); 11454 11455 /* case LVM_SETTILEINFO: */ 11456 11457 /* case LVM_SETTILEVIEWINFO: */ 11458 11459 /* case LVM_SETTILEWIDTH: */ 11460 11461 case LVM_SETTOOLTIPS: 11462 return (LRESULT)LISTVIEW_SetToolTips(infoPtr, (HWND)lParam); 11463 11464 case LVM_SETUNICODEFORMAT: 11465 return LISTVIEW_SetUnicodeFormat(infoPtr, wParam); 11466 11467 case LVM_SETVIEW: 11468 return LISTVIEW_SetView(infoPtr, wParam); 11469 11470 /* case LVM_SETWORKAREAS: */ 11471 11472 /* case LVM_SORTGROUPS: */ 11473 11474 case LVM_SORTITEMS: 11475 case LVM_SORTITEMSEX: 11476 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, wParam, 11477 uMsg == LVM_SORTITEMSEX); 11478 case LVM_SUBITEMHITTEST: 11479 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE); 11480 11481 case LVM_UPDATE: 11482 return LISTVIEW_Update(infoPtr, (INT)wParam); 11483 11484 case CCM_GETVERSION: 11485 return LISTVIEW_GetVersion(infoPtr); 11486 11487 case CCM_SETVERSION: 11488 return LISTVIEW_SetVersion(infoPtr, wParam); 11489 11490 case WM_CHAR: 11491 return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam ); 11492 11493 case WM_COMMAND: 11494 return LISTVIEW_Command(infoPtr, wParam, lParam); 11495 11496 case WM_NCCREATE: 11497 return LISTVIEW_NCCreate(hwnd, (LPCREATESTRUCTW)lParam); 11498 11499 case WM_CREATE: 11500 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam); 11501 11502 case WM_DESTROY: 11503 return LISTVIEW_Destroy(infoPtr); 11504 11505 case WM_ENABLE: 11506 return LISTVIEW_Enable(infoPtr); 11507 11508 case WM_ERASEBKGND: 11509 return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam); 11510 11511 case WM_GETDLGCODE: 11512 return DLGC_WANTCHARS | DLGC_WANTARROWS; 11513 11514 case WM_GETFONT: 11515 return (LRESULT)infoPtr->hFont; 11516 11517 case WM_HSCROLL: 11518 return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0); 11519 11520 case WM_KEYDOWN: 11521 return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam); 11522 11523 case WM_KILLFOCUS: 11524 return LISTVIEW_KillFocus(infoPtr); 11525 11526 case WM_LBUTTONDBLCLK: 11527 return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam)); 11528 11529 case WM_LBUTTONDOWN: 11530 return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam)); 11531 11532 case WM_LBUTTONUP: 11533 return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam)); 11534 11535 case WM_MOUSEMOVE: 11536 return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam)); 11537 11538 case WM_MOUSEHOVER: 11539 return LISTVIEW_MouseHover(infoPtr, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam)); 11540 11541 case WM_NCDESTROY: 11542 return LISTVIEW_NCDestroy(infoPtr); 11543 11544 case WM_NCPAINT: 11545 return LISTVIEW_NCPaint(infoPtr, (HRGN)wParam); 11546 11547 case WM_NOTIFY: 11548 return LISTVIEW_Notify(infoPtr, (LPNMHDR)lParam); 11549 11550 case WM_NOTIFYFORMAT: 11551 return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam); 11552 11553 case WM_PRINTCLIENT: 11554 return LISTVIEW_PrintClient(infoPtr, (HDC)wParam, (DWORD)lParam); 11555 11556 case WM_PAINT: 11557 return LISTVIEW_WMPaint(infoPtr, (HDC)wParam); 11558 11559 case WM_RBUTTONDBLCLK: 11560 return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam)); 11561 11562 case WM_RBUTTONDOWN: 11563 return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam)); 11564 11565 case WM_RBUTTONUP: 11566 return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam)); 11567 11568 case WM_SETCURSOR: 11569 return LISTVIEW_SetCursor(infoPtr, wParam, lParam); 11570 11571 case WM_SETFOCUS: 11572 return LISTVIEW_SetFocus(infoPtr, (HWND)wParam); 11573 11574 case WM_SETFONT: 11575 return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam); 11576 11577 case WM_SETREDRAW: 11578 return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam); 11579 11580 case WM_SHOWWINDOW: 11581 return LISTVIEW_ShowWindow(infoPtr, wParam, lParam); 11582 11583 case WM_SIZE: 11584 return LISTVIEW_Size(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam)); 11585 11586 case WM_STYLECHANGED: 11587 return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam); 11588 11589 case WM_STYLECHANGING: 11590 return LISTVIEW_StyleChanging(wParam, (LPSTYLESTRUCT)lParam); 11591 11592 case WM_SYSCOLORCHANGE: 11593 COMCTL32_RefreshSysColors(); 11594 if (infoPtr->bDefaultBkColor) 11595 { 11596 LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow); 11597 infoPtr->bDefaultBkColor = TRUE; 11598 LISTVIEW_InvalidateList(infoPtr); 11599 } 11600 return 0; 11601 11602 /* case WM_TIMER: */ 11603 case WM_THEMECHANGED: 11604 return LISTVIEW_ThemeChanged(infoPtr); 11605 11606 case WM_VSCROLL: 11607 return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0); 11608 11609 case WM_MOUSEWHEEL: 11610 if (wParam & (MK_SHIFT | MK_CONTROL)) 11611 return DefWindowProcW(hwnd, uMsg, wParam, lParam); 11612 return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam)); 11613 11614 case WM_WINDOWPOSCHANGED: 11615 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE)) 11616 { 11617 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE | 11618 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE); 11619 11620 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (infoPtr->uView == LV_VIEW_DETAILS)) 11621 { 11622 if (notify_measureitem(infoPtr)) LISTVIEW_InvalidateList(infoPtr); 11623 } 11624 11625 LISTVIEW_UpdateSize(infoPtr); 11626 LISTVIEW_UpdateScroll(infoPtr); 11627 } 11628 return DefWindowProcW(hwnd, uMsg, wParam, lParam); 11629 11630 /* case WM_WININICHANGE: */ 11631 11632 default: 11633 if ((uMsg >= WM_USER) && (uMsg < WM_APP) && !COMCTL32_IsReflectedMessage(uMsg)) 11634 ERR("unknown msg %04x wp=%08lx lp=%08lx\n", uMsg, wParam, lParam); 11635 11636 return DefWindowProcW(hwnd, uMsg, wParam, lParam); 11637 } 11638 11639 } 11640 11641 /*** 11642 * DESCRIPTION: 11643 * Registers the window class. 11644 * 11645 * PARAMETER(S): 11646 * None 11647 * 11648 * RETURN: 11649 * None 11650 */ 11651 void LISTVIEW_Register(void) 11652 { 11653 WNDCLASSW wndClass; 11654 11655 ZeroMemory(&wndClass, sizeof(WNDCLASSW)); 11656 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS; 11657 wndClass.lpfnWndProc = LISTVIEW_WindowProc; 11658 wndClass.cbClsExtra = 0; 11659 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *); 11660 wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW); 11661 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); 11662 wndClass.lpszClassName = WC_LISTVIEWW; 11663 RegisterClassW(&wndClass); 11664 } 11665 11666 /*** 11667 * DESCRIPTION: 11668 * Unregisters the window class. 11669 * 11670 * PARAMETER(S): 11671 * None 11672 * 11673 * RETURN: 11674 * None 11675 */ 11676 void LISTVIEW_Unregister(void) 11677 { 11678 UnregisterClassW(WC_LISTVIEWW, NULL); 11679 } 11680 11681 /*** 11682 * DESCRIPTION: 11683 * Handle any WM_COMMAND messages 11684 * 11685 * PARAMETER(S): 11686 * [I] infoPtr : valid pointer to the listview structure 11687 * [I] wParam : the first message parameter 11688 * [I] lParam : the second message parameter 11689 * 11690 * RETURN: 11691 * Zero. 11692 */ 11693 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam) 11694 { 11695 11696 TRACE("(%p %x %x %lx)\n", infoPtr, HIWORD(wParam), LOWORD(wParam), lParam); 11697 11698 if (!infoPtr->hwndEdit) return 0; 11699 11700 switch (HIWORD(wParam)) 11701 { 11702 case EN_UPDATE: 11703 { 11704 /* 11705 * Adjust the edit window size 11706 */ 11707 WCHAR buffer[1024]; 11708 HDC hdc = GetDC(infoPtr->hwndEdit); 11709 HFONT hFont, hOldFont = 0; 11710 RECT rect; 11711 SIZE sz; 11712 11713 if (!infoPtr->hwndEdit || !hdc) return 0; 11714 GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0])); 11715 GetWindowRect(infoPtr->hwndEdit, &rect); 11716 11717 /* Select font to get the right dimension of the string */ 11718 hFont = (HFONT)SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0); 11719 if (hFont) 11720 { 11721 hOldFont = SelectObject(hdc, hFont); 11722 } 11723 11724 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz)) 11725 { 11726 TEXTMETRICW textMetric; 11727 11728 /* Add Extra spacing for the next character */ 11729 GetTextMetricsW(hdc, &textMetric); 11730 sz.cx += (textMetric.tmMaxCharWidth * 2); 11731 11732 SetWindowPos(infoPtr->hwndEdit, NULL, 0, 0, sz.cx, 11733 rect.bottom - rect.top, SWP_DRAWFRAME | SWP_NOMOVE | SWP_NOZORDER); 11734 } 11735 if (hFont) 11736 SelectObject(hdc, hOldFont); 11737 11738 ReleaseDC(infoPtr->hwndEdit, hdc); 11739 11740 break; 11741 } 11742 case EN_KILLFOCUS: 11743 { 11744 LISTVIEW_CancelEditLabel(infoPtr); 11745 break; 11746 } 11747 11748 default: 11749 return SendMessageW (infoPtr->hwndNotify, WM_COMMAND, wParam, lParam); 11750 } 11751 11752 return 0; 11753 } Generated on Sun May 27 2012 04:17:01 for ReactOS by
1.7.6.1
|