Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygentreeview.c
Go to the documentation of this file.
00001 /* Treeview control 00002 * 00003 * Copyright 1998 Eric Kohl <ekohl@abo.rhein-zeitung.de> 00004 * Copyright 1998,1999 Alex Priem <alexp@sci.kun.nl> 00005 * Copyright 1999 Sylvain St-Germain 00006 * Copyright 2002 CodeWeavers, Aric Stewart 00007 * 00008 * This library is free software; you can redistribute it and/or 00009 * modify it under the terms of the GNU Lesser General Public 00010 * License as published by the Free Software Foundation; either 00011 * version 2.1 of the License, or (at your option) any later version. 00012 * 00013 * This library is distributed in the hope that it will be useful, 00014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00016 * Lesser General Public License for more details. 00017 * 00018 * You should have received a copy of the GNU Lesser General Public 00019 * License along with this library; if not, write to the Free Software 00020 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 00021 * 00022 * NOTES 00023 * 00024 * Note that TREEVIEW_INFO * and HTREEITEM are the same thing. 00025 * 00026 * Note2: If item's text == LPSTR_TEXTCALLBACKA we allocate buffer 00027 * of size TEXT_CALLBACK_SIZE in DoSetItem. 00028 * We use callbackMask to keep track of fields to be updated. 00029 * 00030 * TODO: 00031 * missing notifications: TVN_GETINFOTIP, TVN_KEYDOWN, 00032 * TVN_SETDISPINFO, TVN_SINGLEEXPAND 00033 * 00034 * missing styles: TVS_FULLROWSELECT, TVS_INFOTIP, TVS_RTLREADING, 00035 * 00036 * missing item styles: TVIS_EXPANDPARTIAL, TVIS_EX_FLAT, 00037 * TVIS_EX_DISABLED 00038 * 00039 * Make the insertion mark look right. 00040 * Scroll (instead of repaint) as much as possible. 00041 */ 00042 00043 #include "config.h" 00044 #include "wine/port.h" 00045 00046 #include <assert.h> 00047 #include <ctype.h> 00048 #include <stdarg.h> 00049 #include <string.h> 00050 #include <limits.h> 00051 #include <stdlib.h> 00052 00053 #define NONAMELESSUNION 00054 #define NONAMELESSSTRUCT 00055 #include "windef.h" 00056 #include "winbase.h" 00057 #include "wingdi.h" 00058 #include "winuser.h" 00059 #include "winnls.h" 00060 #include "commctrl.h" 00061 #include "comctl32.h" 00062 #include "uxtheme.h" 00063 #include "vssym32.h" 00064 #include "wine/unicode.h" 00065 #include "wine/debug.h" 00066 00067 WINE_DEFAULT_DEBUG_CHANNEL(treeview); 00068 00069 enum StateListType 00070 { 00071 OriginInternal, 00072 OriginUser 00073 }; 00074 00075 /* internal structures */ 00076 00077 typedef struct _TREEITEM /* HTREEITEM is a _TREEINFO *. */ 00078 { 00079 HTREEITEM parent; /* handle to parent or 0 if at root */ 00080 HTREEITEM nextSibling; /* handle to next item in list, 0 if last */ 00081 HTREEITEM firstChild; /* handle to first child or 0 if no child */ 00082 00083 UINT callbackMask; 00084 UINT state; 00085 UINT stateMask; 00086 LPWSTR pszText; 00087 int cchTextMax; 00088 int iImage; 00089 int iSelectedImage; 00090 int iExpandedImage; 00091 int cChildren; 00092 LPARAM lParam; 00093 int iIntegral; /* item height multiplier (1 is normal) */ 00094 int iLevel; /* indentation level:0=root level */ 00095 HTREEITEM lastChild; 00096 HTREEITEM prevSibling; /* handle to prev item in list, 0 if first */ 00097 RECT rect; 00098 LONG linesOffset; 00099 LONG stateOffset; 00100 LONG imageOffset; 00101 LONG textOffset; 00102 LONG textWidth; /* horizontal text extent for pszText */ 00103 LONG visibleOrder; /* visible ordering, 0 is first visible item */ 00104 } TREEVIEW_ITEM; 00105 00106 00107 typedef struct tagTREEVIEW_INFO 00108 { 00109 HWND hwnd; 00110 HWND hwndNotify; /* Owner window to send notifications to */ 00111 DWORD dwStyle; 00112 HTREEITEM root; 00113 UINT uInternalStatus; 00114 INT Timer; 00115 UINT uNumItems; /* number of valid TREEVIEW_ITEMs */ 00116 INT cdmode; /* last custom draw setting */ 00117 UINT uScrollTime; /* max. time for scrolling in milliseconds */ 00118 BOOL bRedraw; /* if FALSE we validate but don't redraw in TREEVIEW_Paint() */ 00119 00120 UINT uItemHeight; /* item height */ 00121 BOOL bHeightSet; 00122 00123 LONG clientWidth; /* width of control window */ 00124 LONG clientHeight; /* height of control window */ 00125 00126 LONG treeWidth; /* width of visible tree items */ 00127 LONG treeHeight; /* height of visible tree items */ 00128 00129 UINT uIndent; /* indentation in pixels */ 00130 HTREEITEM selectedItem; /* handle to selected item or 0 if none */ 00131 HTREEITEM hotItem; /* handle currently under cursor, 0 if none */ 00132 HTREEITEM focusedItem; /* item that was under the cursor when WM_LBUTTONDOWN was received */ 00133 HTREEITEM editItem; /* item being edited with builtin edit box */ 00134 00135 HTREEITEM firstVisible; /* handle to first visible item */ 00136 LONG maxVisibleOrder; 00137 HTREEITEM dropItem; /* handle to item selected by drag cursor */ 00138 HTREEITEM insertMarkItem; /* item after which insertion mark is placed */ 00139 BOOL insertBeforeorAfter; /* flag used by TVM_SETINSERTMARK */ 00140 HIMAGELIST dragList; /* Bitmap of dragged item */ 00141 LONG scrollX; 00142 COLORREF clrBk; 00143 COLORREF clrText; 00144 COLORREF clrLine; 00145 COLORREF clrInsertMark; 00146 HFONT hFont; 00147 HFONT hDefaultFont; 00148 HFONT hBoldFont; 00149 HFONT hUnderlineFont; 00150 HCURSOR hcurHand; 00151 HWND hwndToolTip; 00152 00153 HWND hwndEdit; 00154 WNDPROC wpEditOrig; /* orig window proc for subclassing edit */ 00155 BOOL bIgnoreEditKillFocus; 00156 BOOL bLabelChanged; 00157 00158 BOOL bNtfUnicode; /* TRUE if should send NOTIFY with W */ 00159 HIMAGELIST himlNormal; 00160 int normalImageHeight; 00161 int normalImageWidth; 00162 HIMAGELIST himlState; 00163 int stateImageHeight; 00164 int stateImageWidth; 00165 enum StateListType statehimlType; 00166 HDPA items; 00167 00168 DWORD lastKeyPressTimestamp; 00169 WPARAM charCode; 00170 INT nSearchParamLength; 00171 WCHAR szSearchParam[ MAX_PATH ]; 00172 } TREEVIEW_INFO; 00173 00174 00175 /******** Defines that TREEVIEW_ProcessLetterKeys uses ****************/ 00176 #define KEY_DELAY 450 00177 00178 /* bitflags for infoPtr->uInternalStatus */ 00179 00180 #define TV_HSCROLL 0x01 /* treeview too large to fit in window */ 00181 #define TV_VSCROLL 0x02 /* (horizontal/vertical) */ 00182 #define TV_LDRAG 0x04 /* Lbutton pushed to start drag */ 00183 #define TV_LDRAGGING 0x08 /* Lbutton pushed, mouse moved. */ 00184 #define TV_RDRAG 0x10 /* ditto Rbutton */ 00185 #define TV_RDRAGGING 0x20 00186 00187 /* bitflags for infoPtr->timer */ 00188 00189 #define TV_EDIT_TIMER 2 00190 #define TV_EDIT_TIMER_SET 2 00191 00192 #define TEXT_CALLBACK_SIZE 260 00193 00194 #define TREEVIEW_LEFT_MARGIN 8 00195 00196 #define MINIMUM_INDENT 19 00197 00198 #define CALLBACK_MASK_ALL (TVIF_TEXT|TVIF_CHILDREN|TVIF_IMAGE|TVIF_SELECTEDIMAGE) 00199 00200 #define STATEIMAGEINDEX(x) (((x) >> 12) & 0x0f) 00201 #define OVERLAYIMAGEINDEX(x) (((x) >> 8) & 0x0f) 00202 #define ISVISIBLE(x) ((x)->visibleOrder >= 0) 00203 00204 #define GETLINECOLOR(x) ((x) == CLR_DEFAULT ? comctl32_color.clrGrayText : (x)) 00205 #define GETBKCOLOR(x) ((x) == CLR_NONE ? comctl32_color.clrWindow : (x)) 00206 #define GETTXTCOLOR(x) ((x) == CLR_NONE ? comctl32_color.clrWindowText : (x)) 00207 #define GETINSCOLOR(x) ((x) == CLR_DEFAULT ? comctl32_color.clrBtnText : (x)) 00208 00209 static const WCHAR themeClass[] = { 'T','r','e','e','v','i','e','w',0 }; 00210 00211 00212 typedef VOID (*TREEVIEW_ItemEnumFunc)(TREEVIEW_INFO *, TREEVIEW_ITEM *,LPVOID); 00213 00214 00215 static VOID TREEVIEW_Invalidate(const TREEVIEW_INFO *, const TREEVIEW_ITEM *); 00216 00217 static LRESULT TREEVIEW_DoSelectItem(TREEVIEW_INFO *, INT, HTREEITEM, INT); 00218 static VOID TREEVIEW_SetFirstVisible(TREEVIEW_INFO *, TREEVIEW_ITEM *, BOOL); 00219 static LRESULT TREEVIEW_EnsureVisible(TREEVIEW_INFO *, HTREEITEM, BOOL); 00220 static LRESULT TREEVIEW_RButtonUp(const TREEVIEW_INFO *, const POINT *); 00221 static LRESULT TREEVIEW_EndEditLabelNow(TREEVIEW_INFO *infoPtr, BOOL bCancel); 00222 static VOID TREEVIEW_UpdateScrollBars(TREEVIEW_INFO *infoPtr); 00223 static LRESULT TREEVIEW_HScroll(TREEVIEW_INFO *, WPARAM); 00224 00225 /* Random Utilities *****************************************************/ 00226 static void TREEVIEW_VerifyTree(TREEVIEW_INFO *infoPtr); 00227 00228 /* Returns the treeview private data if hwnd is a treeview. 00229 * Otherwise returns an undefined value. */ 00230 static inline TREEVIEW_INFO * 00231 TREEVIEW_GetInfoPtr(HWND hwnd) 00232 { 00233 return (TREEVIEW_INFO *)GetWindowLongPtrW(hwnd, 0); 00234 } 00235 00236 /* Don't call this. Nothing wants an item index. */ 00237 static inline int 00238 TREEVIEW_GetItemIndex(const TREEVIEW_INFO *infoPtr, HTREEITEM handle) 00239 { 00240 return DPA_GetPtrIndex(infoPtr->items, handle); 00241 } 00242 00243 /* Checks if item has changed and needs to be redrawn */ 00244 static inline BOOL item_changed (const TREEVIEW_ITEM *tiOld, const TREEVIEW_ITEM *tiNew, 00245 const TVITEMEXW *tvChange) 00246 { 00247 /* Number of children has changed */ 00248 if ((tvChange->mask & TVIF_CHILDREN) && (tiOld->cChildren != tiNew->cChildren)) 00249 return TRUE; 00250 00251 /* Image has changed and it's not a callback */ 00252 if ((tvChange->mask & TVIF_IMAGE) && (tiOld->iImage != tiNew->iImage) && 00253 tiNew->iImage != I_IMAGECALLBACK) 00254 return TRUE; 00255 00256 /* Selected image has changed and it's not a callback */ 00257 if ((tvChange->mask & TVIF_SELECTEDIMAGE) && (tiOld->iSelectedImage != tiNew->iSelectedImage) && 00258 tiNew->iSelectedImage != I_IMAGECALLBACK) 00259 return TRUE; 00260 00261 if ((tvChange->mask & TVIF_EXPANDEDIMAGE) && (tiOld->iExpandedImage != tiNew->iExpandedImage) && 00262 tiNew->iExpandedImage != I_IMAGECALLBACK) 00263 return TRUE; 00264 00265 /* Text has changed and it's not a callback */ 00266 if ((tvChange->mask & TVIF_TEXT) && (tiOld->pszText != tiNew->pszText) && 00267 tiNew->pszText != LPSTR_TEXTCALLBACKW) 00268 return TRUE; 00269 00270 /* Indent has changed */ 00271 if ((tvChange->mask & TVIF_INTEGRAL) && (tiOld->iIntegral != tiNew->iIntegral)) 00272 return TRUE; 00273 00274 /* Item state has changed */ 00275 if ((tvChange->mask & TVIF_STATE) && ((tiOld->state ^ tiNew->state) & tvChange->stateMask )) 00276 return TRUE; 00277 00278 return FALSE; 00279 } 00280 00281 /*************************************************************************** 00282 * This method checks that handle is an item for this tree. 00283 */ 00284 static BOOL 00285 TREEVIEW_ValidItem(const TREEVIEW_INFO *infoPtr, HTREEITEM handle) 00286 { 00287 if (TREEVIEW_GetItemIndex(infoPtr, handle) == -1) 00288 { 00289 TRACE("invalid item %p\n", handle); 00290 return FALSE; 00291 } 00292 else 00293 return TRUE; 00294 } 00295 00296 static HFONT 00297 TREEVIEW_CreateBoldFont(HFONT hOrigFont) 00298 { 00299 LOGFONTW font; 00300 00301 GetObjectW(hOrigFont, sizeof(font), &font); 00302 font.lfWeight = FW_BOLD; 00303 return CreateFontIndirectW(&font); 00304 } 00305 00306 static HFONT 00307 TREEVIEW_CreateUnderlineFont(HFONT hOrigFont) 00308 { 00309 LOGFONTW font; 00310 00311 GetObjectW(hOrigFont, sizeof(font), &font); 00312 font.lfUnderline = TRUE; 00313 return CreateFontIndirectW(&font); 00314 } 00315 00316 static inline HFONT 00317 TREEVIEW_FontForItem(const TREEVIEW_INFO *infoPtr, const TREEVIEW_ITEM *item) 00318 { 00319 if ((infoPtr->dwStyle & TVS_TRACKSELECT) && (item == infoPtr->hotItem)) 00320 return infoPtr->hUnderlineFont; 00321 if (item->state & TVIS_BOLD) 00322 return infoPtr->hBoldFont; 00323 return infoPtr->hFont; 00324 } 00325 00326 /* for trace/debugging purposes only */ 00327 static const char * 00328 TREEVIEW_ItemName(const TREEVIEW_ITEM *item) 00329 { 00330 if (item == NULL) return "<null item>"; 00331 if (item->pszText == LPSTR_TEXTCALLBACKW) return "<callback>"; 00332 if (item->pszText == NULL) return "<null>"; 00333 return debugstr_w(item->pszText); 00334 } 00335 00336 /* An item is not a child of itself. */ 00337 static BOOL 00338 TREEVIEW_IsChildOf(const TREEVIEW_ITEM *parent, const TREEVIEW_ITEM *child) 00339 { 00340 do 00341 { 00342 child = child->parent; 00343 if (child == parent) return TRUE; 00344 } while (child != NULL); 00345 00346 return FALSE; 00347 } 00348 00349 00350 /* Tree Traversal *******************************************************/ 00351 00352 /*************************************************************************** 00353 * This method returns the last expanded sibling or child child item 00354 * of a tree node 00355 */ 00356 static TREEVIEW_ITEM * 00357 TREEVIEW_GetLastListItem(const TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item) 00358 { 00359 if (!item) return NULL; 00360 00361 while (item->lastChild) 00362 { 00363 if (item->state & TVIS_EXPANDED) 00364 item = item->lastChild; 00365 else 00366 break; 00367 } 00368 00369 if (item == infoPtr->root) 00370 return NULL; 00371 00372 return item; 00373 } 00374 00375 /*************************************************************************** 00376 * This method returns the previous non-hidden item in the list not 00377 * considering the tree hierarchy. 00378 */ 00379 static TREEVIEW_ITEM * 00380 TREEVIEW_GetPrevListItem(const TREEVIEW_INFO *infoPtr, const TREEVIEW_ITEM *tvItem) 00381 { 00382 if (tvItem->prevSibling) 00383 { 00384 /* This item has a prevSibling, get the last item in the sibling's tree. */ 00385 TREEVIEW_ITEM *upItem = tvItem->prevSibling; 00386 00387 if ((upItem->state & TVIS_EXPANDED) && upItem->lastChild != NULL) 00388 return TREEVIEW_GetLastListItem(infoPtr, upItem->lastChild); 00389 else 00390 return upItem; 00391 } 00392 else 00393 { 00394 /* this item does not have a prevSibling, get the parent */ 00395 return (tvItem->parent != infoPtr->root) ? tvItem->parent : NULL; 00396 } 00397 } 00398 00399 00400 /*************************************************************************** 00401 * This method returns the next physical item in the treeview not 00402 * considering the tree hierarchy. 00403 */ 00404 static TREEVIEW_ITEM * 00405 TREEVIEW_GetNextListItem(const TREEVIEW_INFO *infoPtr, const TREEVIEW_ITEM *tvItem) 00406 { 00407 /* 00408 * If this item has children and is expanded, return the first child 00409 */ 00410 if ((tvItem->state & TVIS_EXPANDED) && tvItem->firstChild != NULL) 00411 { 00412 return tvItem->firstChild; 00413 } 00414 00415 00416 /* 00417 * try to get the sibling 00418 */ 00419 if (tvItem->nextSibling) 00420 return tvItem->nextSibling; 00421 00422 /* 00423 * Otherwise, get the parent's sibling. 00424 */ 00425 while (tvItem->parent) 00426 { 00427 tvItem = tvItem->parent; 00428 00429 if (tvItem->nextSibling) 00430 return tvItem->nextSibling; 00431 } 00432 00433 return NULL; 00434 } 00435 00436 /*************************************************************************** 00437 * This method returns the nth item starting at the given item. It returns 00438 * the last item (or first) we we run out of items. 00439 * 00440 * Will scroll backward if count is <0. 00441 * forward if count is >0. 00442 */ 00443 static TREEVIEW_ITEM * 00444 TREEVIEW_GetListItem(const TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item, 00445 LONG count) 00446 { 00447 TREEVIEW_ITEM *(*next_item)(const TREEVIEW_INFO *, const TREEVIEW_ITEM *); 00448 TREEVIEW_ITEM *previousItem; 00449 00450 assert(item != NULL); 00451 00452 if (count > 0) 00453 { 00454 next_item = TREEVIEW_GetNextListItem; 00455 } 00456 else if (count < 0) 00457 { 00458 count = -count; 00459 next_item = TREEVIEW_GetPrevListItem; 00460 } 00461 else 00462 return item; 00463 00464 do 00465 { 00466 previousItem = item; 00467 item = next_item(infoPtr, item); 00468 00469 } while (--count && item != NULL); 00470 00471 00472 return item ? item : previousItem; 00473 } 00474 00475 /* Notifications ************************************************************/ 00476 00477 static INT get_notifycode(const TREEVIEW_INFO *infoPtr, INT code) 00478 { 00479 if (!infoPtr->bNtfUnicode) { 00480 switch (code) { 00481 case TVN_SELCHANGINGW: return TVN_SELCHANGINGA; 00482 case TVN_SELCHANGEDW: return TVN_SELCHANGEDA; 00483 case TVN_GETDISPINFOW: return TVN_GETDISPINFOA; 00484 case TVN_SETDISPINFOW: return TVN_SETDISPINFOA; 00485 case TVN_ITEMEXPANDINGW: return TVN_ITEMEXPANDINGA; 00486 case TVN_ITEMEXPANDEDW: return TVN_ITEMEXPANDEDA; 00487 case TVN_BEGINDRAGW: return TVN_BEGINDRAGA; 00488 case TVN_BEGINRDRAGW: return TVN_BEGINRDRAGA; 00489 case TVN_DELETEITEMW: return TVN_DELETEITEMA; 00490 case TVN_BEGINLABELEDITW: return TVN_BEGINLABELEDITA; 00491 case TVN_ENDLABELEDITW: return TVN_ENDLABELEDITA; 00492 case TVN_GETINFOTIPW: return TVN_GETINFOTIPA; 00493 } 00494 } 00495 return code; 00496 } 00497 00498 static inline BOOL 00499 TREEVIEW_SendRealNotify(const TREEVIEW_INFO *infoPtr, WPARAM wParam, LPNMHDR pnmh) 00500 { 00501 TRACE("wParam=%ld, lParam=%p\n", wParam, pnmh); 00502 return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, wParam, (LPARAM)pnmh); 00503 } 00504 00505 static BOOL 00506 TREEVIEW_SendSimpleNotify(const TREEVIEW_INFO *infoPtr, UINT code) 00507 { 00508 NMHDR nmhdr; 00509 HWND hwnd = infoPtr->hwnd; 00510 00511 TRACE("%d\n", code); 00512 nmhdr.hwndFrom = hwnd; 00513 nmhdr.idFrom = GetWindowLongPtrW(hwnd, GWLP_ID); 00514 nmhdr.code = get_notifycode(infoPtr, code); 00515 00516 return TREEVIEW_SendRealNotify(infoPtr, nmhdr.idFrom, &nmhdr); 00517 } 00518 00519 static VOID 00520 TREEVIEW_TVItemFromItem(const TREEVIEW_INFO *infoPtr, UINT mask, TVITEMW *tvItem, TREEVIEW_ITEM *item) 00521 { 00522 tvItem->mask = mask; 00523 tvItem->hItem = item; 00524 tvItem->state = item->state; 00525 tvItem->stateMask = 0; 00526 tvItem->iImage = item->iImage; 00527 tvItem->iSelectedImage = item->iSelectedImage; 00528 tvItem->cChildren = item->cChildren; 00529 tvItem->lParam = item->lParam; 00530 00531 if(mask & TVIF_TEXT) 00532 { 00533 if (!infoPtr->bNtfUnicode) 00534 { 00535 tvItem->cchTextMax = WideCharToMultiByte( CP_ACP, 0, item->pszText, -1, NULL, 0, NULL, NULL ); 00536 tvItem->pszText = Alloc (tvItem->cchTextMax); 00537 WideCharToMultiByte( CP_ACP, 0, item->pszText, -1, (LPSTR)tvItem->pszText, tvItem->cchTextMax, 0, 0 ); 00538 } 00539 else 00540 { 00541 tvItem->cchTextMax = item->cchTextMax; 00542 tvItem->pszText = item->pszText; 00543 } 00544 } 00545 else 00546 { 00547 tvItem->cchTextMax = 0; 00548 tvItem->pszText = NULL; 00549 } 00550 } 00551 00552 static BOOL 00553 TREEVIEW_SendTreeviewNotify(const TREEVIEW_INFO *infoPtr, UINT code, UINT action, 00554 UINT mask, HTREEITEM oldItem, HTREEITEM newItem) 00555 { 00556 HWND hwnd = infoPtr->hwnd; 00557 NMTREEVIEWW nmhdr; 00558 BOOL ret; 00559 00560 TRACE("code:%d action:%x olditem:%p newitem:%p\n", 00561 code, action, oldItem, newItem); 00562 00563 ZeroMemory(&nmhdr, sizeof(NMTREEVIEWW)); 00564 00565 nmhdr.hdr.hwndFrom = hwnd; 00566 nmhdr.hdr.idFrom = GetWindowLongPtrW(hwnd, GWLP_ID); 00567 nmhdr.hdr.code = get_notifycode(infoPtr, code); 00568 nmhdr.action = action; 00569 00570 if (oldItem) 00571 TREEVIEW_TVItemFromItem(infoPtr, mask, &nmhdr.itemOld, oldItem); 00572 00573 if (newItem) 00574 TREEVIEW_TVItemFromItem(infoPtr, mask, &nmhdr.itemNew, newItem); 00575 00576 nmhdr.ptDrag.x = 0; 00577 nmhdr.ptDrag.y = 0; 00578 00579 ret = TREEVIEW_SendRealNotify(infoPtr, nmhdr.hdr.idFrom, &nmhdr.hdr); 00580 if (!infoPtr->bNtfUnicode) 00581 { 00582 Free(nmhdr.itemOld.pszText); 00583 Free(nmhdr.itemNew.pszText); 00584 } 00585 return ret; 00586 } 00587 00588 static BOOL 00589 TREEVIEW_SendTreeviewDnDNotify(const TREEVIEW_INFO *infoPtr, UINT code, 00590 HTREEITEM dragItem, POINT pt) 00591 { 00592 HWND hwnd = infoPtr->hwnd; 00593 NMTREEVIEWW nmhdr; 00594 00595 TRACE("code:%d dragitem:%p\n", code, dragItem); 00596 00597 nmhdr.hdr.hwndFrom = hwnd; 00598 nmhdr.hdr.idFrom = GetWindowLongPtrW(hwnd, GWLP_ID); 00599 nmhdr.hdr.code = get_notifycode(infoPtr, code); 00600 nmhdr.action = 0; 00601 nmhdr.itemNew.mask = TVIF_STATE | TVIF_PARAM | TVIF_HANDLE; 00602 nmhdr.itemNew.hItem = dragItem; 00603 nmhdr.itemNew.state = dragItem->state; 00604 nmhdr.itemNew.lParam = dragItem->lParam; 00605 00606 nmhdr.ptDrag.x = pt.x; 00607 nmhdr.ptDrag.y = pt.y; 00608 00609 return TREEVIEW_SendRealNotify(infoPtr, nmhdr.hdr.idFrom, &nmhdr.hdr); 00610 } 00611 00612 00613 static BOOL 00614 TREEVIEW_SendCustomDrawNotify(const TREEVIEW_INFO *infoPtr, DWORD dwDrawStage, 00615 HDC hdc, RECT rc) 00616 { 00617 HWND hwnd = infoPtr->hwnd; 00618 NMTVCUSTOMDRAW nmcdhdr; 00619 LPNMCUSTOMDRAW nmcd; 00620 00621 TRACE("drawstage:%x hdc:%p\n", dwDrawStage, hdc); 00622 00623 nmcd = &nmcdhdr.nmcd; 00624 nmcd->hdr.hwndFrom = hwnd; 00625 nmcd->hdr.idFrom = GetWindowLongPtrW(hwnd, GWLP_ID); 00626 nmcd->hdr.code = NM_CUSTOMDRAW; 00627 nmcd->dwDrawStage = dwDrawStage; 00628 nmcd->hdc = hdc; 00629 nmcd->rc = rc; 00630 nmcd->dwItemSpec = 0; 00631 nmcd->uItemState = 0; 00632 nmcd->lItemlParam = 0; 00633 nmcdhdr.clrText = infoPtr->clrText; 00634 nmcdhdr.clrTextBk = infoPtr->clrBk; 00635 nmcdhdr.iLevel = 0; 00636 00637 return TREEVIEW_SendRealNotify(infoPtr, nmcd->hdr.idFrom, &nmcdhdr.nmcd.hdr); 00638 } 00639 00640 00641 00642 /* FIXME: need to find out when the flags in uItemState need to be set */ 00643 00644 static BOOL 00645 TREEVIEW_SendCustomDrawItemNotify(const TREEVIEW_INFO *infoPtr, HDC hdc, 00646 TREEVIEW_ITEM *item, UINT uItemDrawState, 00647 NMTVCUSTOMDRAW *nmcdhdr) 00648 { 00649 HWND hwnd = infoPtr->hwnd; 00650 LPNMCUSTOMDRAW nmcd; 00651 DWORD dwDrawStage; 00652 DWORD_PTR dwItemSpec; 00653 UINT uItemState; 00654 00655 dwDrawStage = CDDS_ITEM | uItemDrawState; 00656 dwItemSpec = (DWORD_PTR)item; 00657 uItemState = 0; 00658 if (item->state & TVIS_SELECTED) 00659 uItemState |= CDIS_SELECTED; 00660 if (item == infoPtr->selectedItem) 00661 uItemState |= CDIS_FOCUS; 00662 if (item == infoPtr->hotItem) 00663 uItemState |= CDIS_HOT; 00664 00665 nmcd = &nmcdhdr->nmcd; 00666 nmcd->hdr.hwndFrom = hwnd; 00667 nmcd->hdr.idFrom = GetWindowLongPtrW(hwnd, GWLP_ID); 00668 nmcd->hdr.code = NM_CUSTOMDRAW; 00669 nmcd->dwDrawStage = dwDrawStage; 00670 nmcd->hdc = hdc; 00671 nmcd->rc = item->rect; 00672 nmcd->dwItemSpec = dwItemSpec; 00673 nmcd->uItemState = uItemState; 00674 nmcd->lItemlParam = item->lParam; 00675 nmcdhdr->iLevel = item->iLevel; 00676 00677 TRACE("drawstage:%x hdc:%p item:%lx, itemstate:%x, lItemlParam:%lx\n", 00678 nmcd->dwDrawStage, nmcd->hdc, nmcd->dwItemSpec, 00679 nmcd->uItemState, nmcd->lItemlParam); 00680 00681 return TREEVIEW_SendRealNotify(infoPtr, nmcd->hdr.idFrom, &nmcdhdr->nmcd.hdr); 00682 } 00683 00684 static BOOL 00685 TREEVIEW_BeginLabelEditNotify(const TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *editItem) 00686 { 00687 HWND hwnd = infoPtr->hwnd; 00688 NMTVDISPINFOW tvdi; 00689 BOOL ret; 00690 00691 tvdi.hdr.hwndFrom = hwnd; 00692 tvdi.hdr.idFrom = GetWindowLongPtrW(hwnd, GWLP_ID); 00693 tvdi.hdr.code = get_notifycode(infoPtr, TVN_BEGINLABELEDITW); 00694 00695 TREEVIEW_TVItemFromItem(infoPtr, TVIF_HANDLE | TVIF_STATE | TVIF_PARAM | TVIF_TEXT, 00696 &tvdi.item, editItem); 00697 00698 ret = TREEVIEW_SendRealNotify(infoPtr, tvdi.hdr.idFrom, &tvdi.hdr); 00699 00700 if (!infoPtr->bNtfUnicode) 00701 Free(tvdi.item.pszText); 00702 00703 return ret; 00704 } 00705 00706 static void 00707 TREEVIEW_UpdateDispInfo(const TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item, 00708 UINT mask) 00709 { 00710 NMTVDISPINFOEXW callback; 00711 HWND hwnd = infoPtr->hwnd; 00712 00713 TRACE("mask=0x%x, callbackmask=0x%x\n", mask, item->callbackMask); 00714 mask &= item->callbackMask; 00715 00716 if (mask == 0) return; 00717 00718 callback.hdr.hwndFrom = hwnd; 00719 callback.hdr.idFrom = GetWindowLongPtrW(hwnd, GWLP_ID); 00720 callback.hdr.code = get_notifycode(infoPtr, TVN_GETDISPINFOW); 00721 00722 /* 'state' always contains valid value, as well as 'lParam'. 00723 * All other parameters are uninitialized. 00724 */ 00725 callback.item.pszText = item->pszText; 00726 callback.item.cchTextMax = item->cchTextMax; 00727 callback.item.mask = mask; 00728 callback.item.hItem = item; 00729 callback.item.state = item->state; 00730 callback.item.lParam = item->lParam; 00731 00732 /* If text is changed we need to recalculate textWidth */ 00733 if (mask & TVIF_TEXT) 00734 item->textWidth = 0; 00735 00736 TREEVIEW_SendRealNotify(infoPtr, callback.hdr.idFrom, &callback.hdr); 00737 TRACE("resulting code 0x%08x\n", callback.hdr.code); 00738 00739 /* It may have changed due to a call to SetItem. */ 00740 mask &= item->callbackMask; 00741 00742 if ((mask & TVIF_TEXT) && callback.item.pszText != item->pszText) 00743 { 00744 /* Instead of copying text into our buffer user specified his own */ 00745 if (!infoPtr->bNtfUnicode && (callback.hdr.code == TVN_GETDISPINFOA)) { 00746 LPWSTR newText; 00747 int buflen; 00748 int len = MultiByteToWideChar( CP_ACP, 0, 00749 (LPSTR)callback.item.pszText, -1, 00750 NULL, 0); 00751 buflen = max((len)*sizeof(WCHAR), TEXT_CALLBACK_SIZE); 00752 newText = ReAlloc(item->pszText, buflen); 00753 00754 TRACE("returned str %s, len=%d, buflen=%d\n", 00755 debugstr_a((LPSTR)callback.item.pszText), len, buflen); 00756 00757 if (newText) 00758 { 00759 item->pszText = newText; 00760 MultiByteToWideChar( CP_ACP, 0, 00761 (LPSTR)callback.item.pszText, -1, 00762 item->pszText, buflen/sizeof(WCHAR)); 00763 item->cchTextMax = buflen/sizeof(WCHAR); 00764 } 00765 /* If ReAlloc fails we have nothing to do, but keep original text */ 00766 } 00767 else { 00768 int len = max(lstrlenW(callback.item.pszText) + 1, 00769 TEXT_CALLBACK_SIZE); 00770 LPWSTR newText = ReAlloc(item->pszText, len); 00771 00772 TRACE("returned wstr %s, len=%d\n", 00773 debugstr_w(callback.item.pszText), len); 00774 00775 if (newText) 00776 { 00777 item->pszText = newText; 00778 strcpyW(item->pszText, callback.item.pszText); 00779 item->cchTextMax = len; 00780 } 00781 /* If ReAlloc fails we have nothing to do, but keep original text */ 00782 } 00783 } 00784 else if (mask & TVIF_TEXT) { 00785 /* User put text into our buffer, that is ok unless A string */ 00786 if (!infoPtr->bNtfUnicode && (callback.hdr.code == TVN_GETDISPINFOA)) { 00787 LPWSTR newText; 00788 int buflen; 00789 int len = MultiByteToWideChar( CP_ACP, 0, 00790 (LPSTR)callback.item.pszText, -1, 00791 NULL, 0); 00792 buflen = max((len)*sizeof(WCHAR), TEXT_CALLBACK_SIZE); 00793 newText = Alloc(buflen); 00794 00795 TRACE("same buffer str %s, len=%d, buflen=%d\n", 00796 debugstr_a((LPSTR)callback.item.pszText), len, buflen); 00797 00798 if (newText) 00799 { 00800 LPWSTR oldText = item->pszText; 00801 item->pszText = newText; 00802 MultiByteToWideChar( CP_ACP, 0, 00803 (LPSTR)callback.item.pszText, -1, 00804 item->pszText, buflen/sizeof(WCHAR)); 00805 item->cchTextMax = buflen/sizeof(WCHAR); 00806 Free(oldText); 00807 } 00808 } 00809 } 00810 00811 if (mask & TVIF_IMAGE) 00812 item->iImage = callback.item.iImage; 00813 00814 if (mask & TVIF_SELECTEDIMAGE) 00815 item->iSelectedImage = callback.item.iSelectedImage; 00816 00817 if (mask & TVIF_EXPANDEDIMAGE) 00818 item->iExpandedImage = callback.item.iExpandedImage; 00819 00820 if (mask & TVIF_CHILDREN) 00821 item->cChildren = callback.item.cChildren; 00822 00823 if (callback.item.mask & TVIF_STATE) 00824 { 00825 item->state &= ~callback.item.stateMask; 00826 item->state |= (callback.item.state & callback.item.stateMask); 00827 } 00828 00829 /* These members are now permanently set. */ 00830 if (callback.item.mask & TVIF_DI_SETITEM) 00831 item->callbackMask &= ~callback.item.mask; 00832 } 00833 00834 /*************************************************************************** 00835 * This function uses cChildren field to decide whether the item has 00836 * children or not. 00837 * Note: if this returns TRUE, the child items may not actually exist, 00838 * they could be virtual. 00839 * 00840 * Just use item->firstChild to check for physical children. 00841 */ 00842 static BOOL 00843 TREEVIEW_HasChildren(const TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item) 00844 { 00845 TREEVIEW_UpdateDispInfo(infoPtr, item, TVIF_CHILDREN); 00846 00847 return item->cChildren > 0; 00848 } 00849 00850 static INT TREEVIEW_NotifyFormat (TREEVIEW_INFO *infoPtr, HWND hwndFrom, UINT nCommand) 00851 { 00852 INT format; 00853 00854 TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom, nCommand); 00855 00856 if (nCommand != NF_REQUERY) return 0; 00857 00858 format = SendMessageW(hwndFrom, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwnd, NF_QUERY); 00859 TRACE("format=%d\n", format); 00860 00861 if (format != NFR_ANSI && format != NFR_UNICODE) return 0; 00862 00863 infoPtr->bNtfUnicode = (format == NFR_UNICODE); 00864 00865 return format; 00866 } 00867 00868 /* Item Position ********************************************************/ 00869 00870 /* Compute linesOffset, stateOffset, imageOffset, textOffset of an item. */ 00871 static VOID 00872 TREEVIEW_ComputeItemInternalMetrics(const TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item) 00873 { 00874 /* has TVS_LINESATROOT and (TVS_HASLINES|TVS_HASBUTTONS) */ 00875 BOOL lar = ((infoPtr->dwStyle & (TVS_LINESATROOT|TVS_HASLINES|TVS_HASBUTTONS)) 00876 > TVS_LINESATROOT); 00877 00878 item->linesOffset = infoPtr->uIndent * (lar ? item->iLevel : item->iLevel - 1) 00879 - infoPtr->scrollX; 00880 item->stateOffset = item->linesOffset + infoPtr->uIndent; 00881 item->imageOffset = item->stateOffset 00882 + (STATEIMAGEINDEX(item->state) ? infoPtr->stateImageWidth : 0); 00883 item->textOffset = item->imageOffset + infoPtr->normalImageWidth; 00884 } 00885 00886 static VOID 00887 TREEVIEW_ComputeTextWidth(const TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item, HDC hDC) 00888 { 00889 HDC hdc; 00890 HFONT hOldFont=0; 00891 SIZE sz; 00892 00893 /* DRAW's OM docker creates items like this */ 00894 if (item->pszText == NULL) 00895 { 00896 item->textWidth = 0; 00897 return; 00898 } 00899 00900 if (hDC != 0) 00901 { 00902 hdc = hDC; 00903 } 00904 else 00905 { 00906 hdc = GetDC(infoPtr->hwnd); 00907 hOldFont = SelectObject(hdc, TREEVIEW_FontForItem(infoPtr, item)); 00908 } 00909 00910 GetTextExtentPoint32W(hdc, item->pszText, strlenW(item->pszText), &sz); 00911 item->textWidth = sz.cx; 00912 00913 if (hDC == 0) 00914 { 00915 SelectObject(hdc, hOldFont); 00916 ReleaseDC(0, hdc); 00917 } 00918 } 00919 00920 static VOID 00921 TREEVIEW_ComputeItemRect(const TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item) 00922 { 00923 item->rect.top = infoPtr->uItemHeight * 00924 (item->visibleOrder - infoPtr->firstVisible->visibleOrder); 00925 00926 item->rect.bottom = item->rect.top 00927 + infoPtr->uItemHeight * item->iIntegral - 1; 00928 00929 item->rect.left = 0; 00930 item->rect.right = infoPtr->clientWidth; 00931 } 00932 00933 /* We know that only items after start need their order updated. */ 00934 static void 00935 TREEVIEW_RecalculateVisibleOrder(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *start) 00936 { 00937 TREEVIEW_ITEM *item; 00938 int order; 00939 00940 if (!start) 00941 { 00942 start = infoPtr->root->firstChild; 00943 order = 0; 00944 } 00945 else 00946 order = start->visibleOrder; 00947 00948 for (item = start; item != NULL; 00949 item = TREEVIEW_GetNextListItem(infoPtr, item)) 00950 { 00951 if (!ISVISIBLE(item) && order > 0) 00952 TREEVIEW_ComputeItemInternalMetrics(infoPtr, item); 00953 item->visibleOrder = order; 00954 order += item->iIntegral; 00955 } 00956 00957 infoPtr->maxVisibleOrder = order; 00958 00959 for (item = start; item != NULL; 00960 item = TREEVIEW_GetNextListItem(infoPtr, item)) 00961 { 00962 TREEVIEW_ComputeItemRect(infoPtr, item); 00963 } 00964 } 00965 00966 00967 /* Update metrics of all items in selected subtree. 00968 * root must be expanded 00969 */ 00970 static VOID 00971 TREEVIEW_UpdateSubTree(const TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *root) 00972 { 00973 TREEVIEW_ITEM *sibling; 00974 HDC hdc; 00975 HFONT hOldFont; 00976 00977 if (!root->firstChild || !(root->state & TVIS_EXPANDED)) 00978 return; 00979 00980 root->state &= ~TVIS_EXPANDED; 00981 sibling = TREEVIEW_GetNextListItem(infoPtr, root); 00982 root->state |= TVIS_EXPANDED; 00983 00984 hdc = GetDC(infoPtr->hwnd); 00985 hOldFont = SelectObject(hdc, infoPtr->hFont); 00986 00987 for (; root != sibling; 00988 root = TREEVIEW_GetNextListItem(infoPtr, root)) 00989 { 00990 TREEVIEW_ComputeItemInternalMetrics(infoPtr, root); 00991 00992 if (root->callbackMask & TVIF_TEXT) 00993 TREEVIEW_UpdateDispInfo(infoPtr, root, TVIF_TEXT); 00994 00995 if (root->textWidth == 0) 00996 { 00997 SelectObject(hdc, TREEVIEW_FontForItem(infoPtr, root)); 00998 TREEVIEW_ComputeTextWidth(infoPtr, root, hdc); 00999 } 01000 } 01001 01002 SelectObject(hdc, hOldFont); 01003 ReleaseDC(infoPtr->hwnd, hdc); 01004 } 01005 01006 /* Item Allocation **********************************************************/ 01007 01008 static TREEVIEW_ITEM * 01009 TREEVIEW_AllocateItem(const TREEVIEW_INFO *infoPtr) 01010 { 01011 TREEVIEW_ITEM *newItem = Alloc(sizeof(TREEVIEW_ITEM)); 01012 01013 if (!newItem) 01014 return NULL; 01015 01016 /* I_IMAGENONE would make more sense but this is neither what is 01017 * documented (MSDN doesn't specify) nor what Windows actually does 01018 * (it sets it to zero)... and I can so imagine an application using 01019 * inc/dec to toggle the images. */ 01020 newItem->iImage = 0; 01021 newItem->iSelectedImage = 0; 01022 newItem->iExpandedImage = (WORD)I_IMAGENONE; 01023 01024 if (DPA_InsertPtr(infoPtr->items, INT_MAX, newItem) == -1) 01025 { 01026 Free(newItem); 01027 return NULL; 01028 } 01029 01030 return newItem; 01031 } 01032 01033 /* Exact opposite of TREEVIEW_AllocateItem. In particular, it does not 01034 * free item->pszText. */ 01035 static void 01036 TREEVIEW_FreeItem(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item) 01037 { 01038 DPA_DeletePtr(infoPtr->items, DPA_GetPtrIndex(infoPtr->items, item)); 01039 if (infoPtr->selectedItem == item) 01040 infoPtr->selectedItem = NULL; 01041 if (infoPtr->hotItem == item) 01042 infoPtr->hotItem = NULL; 01043 if (infoPtr->focusedItem == item) 01044 infoPtr->focusedItem = NULL; 01045 if (infoPtr->firstVisible == item) 01046 infoPtr->firstVisible = NULL; 01047 if (infoPtr->dropItem == item) 01048 infoPtr->dropItem = NULL; 01049 if (infoPtr->insertMarkItem == item) 01050 infoPtr->insertMarkItem = NULL; 01051 Free(item); 01052 } 01053 01054 01055 /* Item Insertion *******************************************************/ 01056 01057 /*************************************************************************** 01058 * This method inserts newItem before sibling as a child of parent. 01059 * sibling can be NULL, but only if parent has no children. 01060 */ 01061 static void 01062 TREEVIEW_InsertBefore(TREEVIEW_ITEM *newItem, TREEVIEW_ITEM *sibling, 01063 TREEVIEW_ITEM *parent) 01064 { 01065 assert(parent != NULL); 01066 01067 if (sibling != NULL) 01068 { 01069 assert(sibling->parent == parent); 01070 01071 if (sibling->prevSibling != NULL) 01072 sibling->prevSibling->nextSibling = newItem; 01073 01074 newItem->prevSibling = sibling->prevSibling; 01075 sibling->prevSibling = newItem; 01076 } 01077 else 01078 newItem->prevSibling = NULL; 01079 01080 newItem->nextSibling = sibling; 01081 01082 if (parent->firstChild == sibling) 01083 parent->firstChild = newItem; 01084 01085 if (parent->lastChild == NULL) 01086 parent->lastChild = newItem; 01087 } 01088 01089 /*************************************************************************** 01090 * This method inserts newItem after sibling as a child of parent. 01091 * sibling can be NULL, but only if parent has no children. 01092 */ 01093 static void 01094 TREEVIEW_InsertAfter(TREEVIEW_ITEM *newItem, TREEVIEW_ITEM *sibling, 01095 TREEVIEW_ITEM *parent) 01096 { 01097 assert(parent != NULL); 01098 01099 if (sibling != NULL) 01100 { 01101 assert(sibling->parent == parent); 01102 01103 if (sibling->nextSibling != NULL) 01104 sibling->nextSibling->prevSibling = newItem; 01105 01106 newItem->nextSibling = sibling->nextSibling; 01107 sibling->nextSibling = newItem; 01108 } 01109 else 01110 newItem->nextSibling = NULL; 01111 01112 newItem->prevSibling = sibling; 01113 01114 if (parent->lastChild == sibling) 01115 parent->lastChild = newItem; 01116 01117 if (parent->firstChild == NULL) 01118 parent->firstChild = newItem; 01119 } 01120 01121 static BOOL 01122 TREEVIEW_DoSetItemT(const TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item, 01123 const TVITEMEXW *tvItem, BOOL isW) 01124 { 01125 UINT callbackClear = 0; 01126 UINT callbackSet = 0; 01127 01128 TRACE("item %p\n", item); 01129 /* Do this first in case it fails. */ 01130 if (tvItem->mask & TVIF_TEXT) 01131 { 01132 item->textWidth = 0; /* force width recalculation */ 01133 if (tvItem->pszText != LPSTR_TEXTCALLBACKW && tvItem->pszText != NULL) /* covers != TEXTCALLBACKA too, and undocumented: pszText of NULL also means TEXTCALLBACK */ 01134 { 01135 int len; 01136 LPWSTR newText; 01137 if (isW) 01138 len = lstrlenW(tvItem->pszText) + 1; 01139 else 01140 len = MultiByteToWideChar(CP_ACP, 0, (LPSTR)tvItem->pszText, -1, NULL, 0); 01141 01142 newText = ReAlloc(item->pszText, len * sizeof(WCHAR)); 01143 01144 if (newText == NULL) return FALSE; 01145 01146 callbackClear |= TVIF_TEXT; 01147 01148 item->pszText = newText; 01149 item->cchTextMax = len; 01150 if (isW) 01151 lstrcpynW(item->pszText, tvItem->pszText, len); 01152 else 01153 MultiByteToWideChar(CP_ACP, 0, (LPSTR)tvItem->pszText, -1, 01154 item->pszText, len); 01155 01156 TRACE("setting text %s, item %p\n", debugstr_w(item->pszText), item); 01157 } 01158 else 01159 { 01160 callbackSet |= TVIF_TEXT; 01161 01162 item->pszText = ReAlloc(item->pszText, 01163 TEXT_CALLBACK_SIZE * sizeof(WCHAR)); 01164 item->cchTextMax = TEXT_CALLBACK_SIZE; 01165 TRACE("setting callback, item %p\n", item); 01166 } 01167 } 01168 01169 if (tvItem->mask & TVIF_CHILDREN) 01170 { 01171 item->cChildren = tvItem->cChildren; 01172 01173 if (item->cChildren == I_CHILDRENCALLBACK) 01174 callbackSet |= TVIF_CHILDREN; 01175 else 01176 callbackClear |= TVIF_CHILDREN; 01177 } 01178 01179 if (tvItem->mask & TVIF_IMAGE) 01180 { 01181 item->iImage = tvItem->iImage; 01182 01183 if (item->iImage == I_IMAGECALLBACK) 01184 callbackSet |= TVIF_IMAGE; 01185 else 01186 callbackClear |= TVIF_IMAGE; 01187 } 01188 01189 if (tvItem->mask & TVIF_SELECTEDIMAGE) 01190 { 01191 item->iSelectedImage = tvItem->iSelectedImage; 01192 01193 if (item->iSelectedImage == I_IMAGECALLBACK) 01194 callbackSet |= TVIF_SELECTEDIMAGE; 01195 else 01196 callbackClear |= TVIF_SELECTEDIMAGE; 01197 } 01198 01199 if (tvItem->mask & TVIF_EXPANDEDIMAGE) 01200 { 01201 item->iExpandedImage = tvItem->iExpandedImage; 01202 01203 if (item->iExpandedImage == I_IMAGECALLBACK) 01204 callbackSet |= TVIF_EXPANDEDIMAGE; 01205 else 01206 callbackClear |= TVIF_EXPANDEDIMAGE; 01207 } 01208 01209 if (tvItem->mask & TVIF_PARAM) 01210 item->lParam = tvItem->lParam; 01211 01212 /* If the application sets TVIF_INTEGRAL without 01213 * supplying a TVITEMEX structure, it's toast. */ 01214 if (tvItem->mask & TVIF_INTEGRAL) 01215 item->iIntegral = tvItem->iIntegral; 01216 01217 if (tvItem->mask & TVIF_STATE) 01218 { 01219 TRACE("prevstate,state,mask:%x,%x,%x\n", item->state, tvItem->state, 01220 tvItem->stateMask); 01221 item->state &= ~tvItem->stateMask; 01222 item->state |= (tvItem->state & tvItem->stateMask); 01223 } 01224 01225 if (tvItem->mask & TVIF_STATEEX) 01226 { 01227 FIXME("New extended state: %x\n", tvItem->uStateEx); 01228 } 01229 01230 item->callbackMask |= callbackSet; 01231 item->callbackMask &= ~callbackClear; 01232 01233 return TRUE; 01234 } 01235 01236 /* Note that the new item is pre-zeroed. */ 01237 static LRESULT 01238 TREEVIEW_InsertItemT(TREEVIEW_INFO *infoPtr, const TVINSERTSTRUCTW *ptdi, BOOL isW) 01239 { 01240 const TVITEMEXW *tvItem = &ptdi->u.itemex; 01241 HTREEITEM insertAfter; 01242 TREEVIEW_ITEM *newItem, *parentItem; 01243 BOOL bTextUpdated = FALSE; 01244 01245 if (ptdi->hParent == TVI_ROOT || ptdi->hParent == 0) 01246 { 01247 parentItem = infoPtr->root; 01248 } 01249 else 01250 { 01251 parentItem = ptdi->hParent; 01252 01253 if (!TREEVIEW_ValidItem(infoPtr, parentItem)) 01254 { 01255 WARN("invalid parent %p\n", parentItem); 01256 return 0; 01257 } 01258 } 01259 01260 insertAfter = ptdi->hInsertAfter; 01261 01262 /* Validate this now for convenience. */ 01263 switch ((DWORD_PTR)insertAfter) 01264 { 01265 case (DWORD_PTR)TVI_FIRST: 01266 case (DWORD_PTR)TVI_LAST: 01267 case (DWORD_PTR)TVI_SORT: 01268 break; 01269 01270 default: 01271 if (!TREEVIEW_ValidItem(infoPtr, insertAfter) || 01272 insertAfter->parent != parentItem) 01273 { 01274 WARN("invalid insert after %p\n", insertAfter); 01275 insertAfter = TVI_LAST; 01276 } 01277 } 01278 01279 TRACE("parent %p position %p: %s\n", parentItem, insertAfter, 01280 (tvItem->mask & TVIF_TEXT) 01281 ? ((tvItem->pszText == LPSTR_TEXTCALLBACKW) ? "<callback>" 01282 : (isW ? debugstr_w(tvItem->pszText) : debugstr_a((LPSTR)tvItem->pszText))) 01283 : "<no label>"); 01284 01285 newItem = TREEVIEW_AllocateItem(infoPtr); 01286 if (newItem == NULL) 01287 return 0; 01288 01289 newItem->parent = parentItem; 01290 newItem->iIntegral = 1; 01291 newItem->visibleOrder = -1; 01292 01293 if (!TREEVIEW_DoSetItemT(infoPtr, newItem, tvItem, isW)) 01294 return 0; 01295 01296 /* After this point, nothing can fail. (Except for TVI_SORT.) */ 01297 01298 infoPtr->uNumItems++; 01299 01300 switch ((DWORD_PTR)insertAfter) 01301 { 01302 case (DWORD_PTR)TVI_FIRST: 01303 { 01304 TREEVIEW_ITEM *originalFirst = parentItem->firstChild; 01305 TREEVIEW_InsertBefore(newItem, parentItem->firstChild, parentItem); 01306 if (infoPtr->firstVisible == originalFirst) 01307 TREEVIEW_SetFirstVisible(infoPtr, newItem, TRUE); 01308 } 01309 break; 01310 01311 case (DWORD_PTR)TVI_LAST: 01312 TREEVIEW_InsertAfter(newItem, parentItem->lastChild, parentItem); 01313 break; 01314 01315 /* hInsertAfter names a specific item we want to insert after */ 01316 default: 01317 TREEVIEW_InsertAfter(newItem, insertAfter, insertAfter->parent); 01318 break; 01319 01320 case (DWORD_PTR)TVI_SORT: 01321 { 01322 TREEVIEW_ITEM *aChild; 01323 TREEVIEW_ITEM *previousChild = NULL; 01324 TREEVIEW_ITEM *originalFirst = parentItem->firstChild; 01325 BOOL bItemInserted = FALSE; 01326 01327 aChild = parentItem->firstChild; 01328 01329 bTextUpdated = TRUE; 01330 TREEVIEW_UpdateDispInfo(infoPtr, newItem, TVIF_TEXT); 01331 01332 /* Iterate the parent children to see where we fit in */ 01333 while (aChild != NULL) 01334 { 01335 INT comp; 01336 01337 TREEVIEW_UpdateDispInfo(infoPtr, aChild, TVIF_TEXT); 01338 comp = lstrcmpW(newItem->pszText, aChild->pszText); 01339 01340 if (comp < 0) /* we are smaller than the current one */ 01341 { 01342 TREEVIEW_InsertBefore(newItem, aChild, parentItem); 01343 if (infoPtr->firstVisible == originalFirst && 01344 aChild == originalFirst) 01345 TREEVIEW_SetFirstVisible(infoPtr, newItem, TRUE); 01346 bItemInserted = TRUE; 01347 break; 01348 } 01349 else if (comp > 0) /* we are bigger than the current one */ 01350 { 01351 previousChild = aChild; 01352 01353 /* This will help us to exit if there is no more sibling */ 01354 aChild = (aChild->nextSibling == 0) 01355 ? NULL 01356 : aChild->nextSibling; 01357 01358 /* Look at the next item */ 01359 continue; 01360 } 01361 else if (comp == 0) 01362 { 01363 /* 01364 * An item with this name is already existing, therefore, 01365 * we add after the one we found 01366 */ 01367 TREEVIEW_InsertAfter(newItem, aChild, parentItem); 01368 bItemInserted = TRUE; 01369 break; 01370 } 01371 } 01372 01373 /* 01374 * we reach the end of the child list and the item has not 01375 * yet been inserted, therefore, insert it after the last child. 01376 */ 01377 if ((!bItemInserted) && (aChild == NULL)) 01378 TREEVIEW_InsertAfter(newItem, previousChild, parentItem); 01379 01380 break; 01381 } 01382 } 01383 01384 01385 TRACE("new item %p; parent %p, mask %x\n", newItem, 01386 newItem->parent, tvItem->mask); 01387 01388 newItem->iLevel = newItem->parent->iLevel + 1; 01389 01390 if (newItem->parent->cChildren == 0) 01391 newItem->parent->cChildren = 1; 01392 01393 if (infoPtr->dwStyle & TVS_CHECKBOXES) 01394 { 01395 if (STATEIMAGEINDEX(newItem->state) == 0) 01396 newItem->state |= INDEXTOSTATEIMAGEMASK(1); 01397 } 01398 01399 if (infoPtr->firstVisible == NULL) 01400 infoPtr->firstVisible = newItem; 01401 01402 TREEVIEW_VerifyTree(infoPtr); 01403 01404 if (!infoPtr->bRedraw) return (LRESULT)newItem; 01405 01406 if (parentItem == infoPtr->root || 01407 (ISVISIBLE(parentItem) && parentItem->state & TVIS_EXPANDED)) 01408 { 01409 TREEVIEW_ITEM *item; 01410 TREEVIEW_ITEM *prev = TREEVIEW_GetPrevListItem(infoPtr, newItem); 01411 01412 TREEVIEW_RecalculateVisibleOrder(infoPtr, prev); 01413 TREEVIEW_ComputeItemInternalMetrics(infoPtr, newItem); 01414 01415 if (!bTextUpdated) 01416 TREEVIEW_UpdateDispInfo(infoPtr, newItem, TVIF_TEXT); 01417 01418 TREEVIEW_ComputeTextWidth(infoPtr, newItem, 0); 01419 TREEVIEW_UpdateScrollBars(infoPtr); 01420 /* 01421 * if the item was inserted in a visible part of the tree, 01422 * invalidate it, as well as those after it 01423 */ 01424 for (item = newItem; 01425 item != NULL; 01426 item = TREEVIEW_GetNextListItem(infoPtr, item)) 01427 TREEVIEW_Invalidate(infoPtr, item); 01428 } 01429 else 01430 { 01431 /* refresh treeview if newItem is the first item inserted under parentItem */ 01432 if (ISVISIBLE(parentItem) && newItem->prevSibling == newItem->nextSibling) 01433 { 01434 /* parent got '+' - update it */ 01435 TREEVIEW_Invalidate(infoPtr, parentItem); 01436 } 01437 } 01438 01439 return (LRESULT)newItem; 01440 } 01441 01442 /* Item Deletion ************************************************************/ 01443 static void 01444 TREEVIEW_RemoveItem(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item); 01445 01446 static void 01447 TREEVIEW_RemoveAllChildren(TREEVIEW_INFO *infoPtr, const TREEVIEW_ITEM *parentItem) 01448 { 01449 TREEVIEW_ITEM *kill = parentItem->firstChild; 01450 01451 while (kill != NULL) 01452 { 01453 TREEVIEW_ITEM *next = kill->nextSibling; 01454 01455 TREEVIEW_RemoveItem(infoPtr, kill); 01456 01457 kill = next; 01458 } 01459 01460 assert(parentItem->cChildren <= 0); /* I_CHILDRENCALLBACK or 0 */ 01461 assert(parentItem->firstChild == NULL); 01462 assert(parentItem->lastChild == NULL); 01463 } 01464 01465 static void 01466 TREEVIEW_UnlinkItem(const TREEVIEW_ITEM *item) 01467 { 01468 TREEVIEW_ITEM *parentItem = item->parent; 01469 01470 assert(item != NULL); 01471 assert(item->parent != NULL); /* i.e. it must not be the root */ 01472 01473 if (parentItem->firstChild == item) 01474 parentItem->firstChild = item->nextSibling; 01475 01476 if (parentItem->lastChild == item) 01477 parentItem->lastChild = item->prevSibling; 01478 01479 if (parentItem->firstChild == NULL && parentItem->lastChild == NULL 01480 && parentItem->cChildren > 0) 01481 parentItem->cChildren = 0; 01482 01483 if (item->prevSibling) 01484 item->prevSibling->nextSibling = item->nextSibling; 01485 01486 if (item->nextSibling) 01487 item->nextSibling->prevSibling = item->prevSibling; 01488 } 01489 01490 static void 01491 TREEVIEW_RemoveItem(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item) 01492 { 01493 TRACE("%p, (%s)\n", item, TREEVIEW_ItemName(item)); 01494 01495 if (item->firstChild) 01496 TREEVIEW_RemoveAllChildren(infoPtr, item); 01497 01498 TREEVIEW_SendTreeviewNotify(infoPtr, TVN_DELETEITEMW, TVC_UNKNOWN, 01499 TVIF_HANDLE | TVIF_PARAM, item, 0); 01500 01501 TREEVIEW_UnlinkItem(item); 01502 01503 infoPtr->uNumItems--; 01504 01505 if (item->pszText != LPSTR_TEXTCALLBACKW) 01506 Free(item->pszText); 01507 01508 TREEVIEW_FreeItem(infoPtr, item); 01509 } 01510 01511 01512 /* Empty out the tree. */ 01513 static void 01514 TREEVIEW_RemoveTree(TREEVIEW_INFO *infoPtr) 01515 { 01516 TREEVIEW_RemoveAllChildren(infoPtr, infoPtr->root); 01517 01518 assert(infoPtr->uNumItems == 0); /* root isn't counted in uNumItems */ 01519 } 01520 01521 static LRESULT 01522 TREEVIEW_DeleteItem(TREEVIEW_INFO *infoPtr, HTREEITEM item) 01523 { 01524 TREEVIEW_ITEM *newSelection = NULL; 01525 TREEVIEW_ITEM *newFirstVisible = NULL; 01526 TREEVIEW_ITEM *parent, *prev = NULL; 01527 BOOL visible = FALSE; 01528 01529 if (item == TVI_ROOT || !item) 01530 { 01531 TRACE("TVI_ROOT\n"); 01532 parent = infoPtr->root; 01533 newSelection = NULL; 01534 visible = TRUE; 01535 TREEVIEW_RemoveTree(infoPtr); 01536 } 01537 else 01538 { 01539 if (!TREEVIEW_ValidItem(infoPtr, item)) 01540 return FALSE; 01541 01542 TRACE("%p (%s)\n", item, TREEVIEW_ItemName(item)); 01543 parent = item->parent; 01544 01545 if (ISVISIBLE(item)) 01546 { 01547 prev = TREEVIEW_GetPrevListItem(infoPtr, item); 01548 visible = TRUE; 01549 } 01550 01551 if (infoPtr->selectedItem != NULL 01552 && (item == infoPtr->selectedItem 01553 || TREEVIEW_IsChildOf(item, infoPtr->selectedItem))) 01554 { 01555 if (item->nextSibling) 01556 newSelection = item->nextSibling; 01557 else if (item->parent != infoPtr->root) 01558 newSelection = item->parent; 01559 else 01560 newSelection = item->prevSibling; 01561 TRACE("newSelection = %p\n", newSelection); 01562 } 01563 01564 if (infoPtr->firstVisible == item) 01565 { 01566 if (item->nextSibling) 01567 newFirstVisible = item->nextSibling; 01568 else if (item->prevSibling) 01569 newFirstVisible = item->prevSibling; 01570 else if (item->parent != infoPtr->root) 01571 newFirstVisible = item->parent; 01572 TREEVIEW_SetFirstVisible(infoPtr, NULL, TRUE); 01573 } 01574 else 01575 newFirstVisible = infoPtr->firstVisible; 01576 01577 TREEVIEW_RemoveItem(infoPtr, item); 01578 } 01579 01580 /* Don't change if somebody else already has (infoPtr->selectedItem is cleared by FreeItem). */ 01581 if (!infoPtr->selectedItem && newSelection) 01582 { 01583 if (TREEVIEW_ValidItem(infoPtr, newSelection)) 01584 TREEVIEW_DoSelectItem(infoPtr, TVGN_CARET, newSelection, TVC_UNKNOWN); 01585 } 01586 01587 /* Validate insertMark dropItem. 01588 * hotItem ??? - used for comparison only. 01589 */ 01590 if (!TREEVIEW_ValidItem(infoPtr, infoPtr->insertMarkItem)) 01591 infoPtr->insertMarkItem = 0; 01592 01593 if (!TREEVIEW_ValidItem(infoPtr, infoPtr->dropItem)) 01594 infoPtr->dropItem = 0; 01595 01596 if (!TREEVIEW_ValidItem(infoPtr, newFirstVisible)) 01597 newFirstVisible = infoPtr->root->firstChild; 01598 01599 TREEVIEW_VerifyTree(infoPtr); 01600 01601 if (!infoPtr->bRedraw) return TRUE; 01602 01603 if (visible) 01604 { 01605 TREEVIEW_SetFirstVisible(infoPtr, newFirstVisible, TRUE); 01606 TREEVIEW_RecalculateVisibleOrder(infoPtr, prev); 01607 TREEVIEW_UpdateScrollBars(infoPtr); 01608 TREEVIEW_Invalidate(infoPtr, NULL); 01609 } 01610 else if (ISVISIBLE(parent) && !TREEVIEW_HasChildren(infoPtr, parent)) 01611 { 01612 /* parent lost '+/-' - update it */ 01613 TREEVIEW_Invalidate(infoPtr, parent); 01614 } 01615 01616 return TRUE; 01617 } 01618 01619 01620 /* Get/Set Messages *********************************************************/ 01621 static LRESULT 01622 TREEVIEW_SetRedraw(TREEVIEW_INFO* infoPtr, WPARAM wParam) 01623 { 01624 infoPtr->bRedraw = wParam ? TRUE : FALSE; 01625 01626 if (infoPtr->bRedraw) 01627 { 01628 TREEVIEW_UpdateSubTree(infoPtr, infoPtr->root); 01629 TREEVIEW_RecalculateVisibleOrder(infoPtr, NULL); 01630 TREEVIEW_UpdateScrollBars(infoPtr); 01631 TREEVIEW_Invalidate(infoPtr, NULL); 01632 } 01633 return 0; 01634 } 01635 01636 static LRESULT 01637 TREEVIEW_GetIndent(const TREEVIEW_INFO *infoPtr) 01638 { 01639 TRACE("\n"); 01640 return infoPtr->uIndent; 01641 } 01642 01643 static LRESULT 01644 TREEVIEW_SetIndent(TREEVIEW_INFO *infoPtr, UINT newIndent) 01645 { 01646 TRACE("\n"); 01647 01648 if (newIndent < MINIMUM_INDENT) 01649 newIndent = MINIMUM_INDENT; 01650 01651 if (infoPtr->uIndent != newIndent) 01652 { 01653 infoPtr->uIndent = newIndent; 01654 TREEVIEW_UpdateSubTree(infoPtr, infoPtr->root); 01655 TREEVIEW_UpdateScrollBars(infoPtr); 01656 TREEVIEW_Invalidate(infoPtr, NULL); 01657 } 01658 01659 return 0; 01660 } 01661 01662 01663 static LRESULT 01664 TREEVIEW_GetToolTips(const TREEVIEW_INFO *infoPtr) 01665 { 01666 TRACE("\n"); 01667 return (LRESULT)infoPtr->hwndToolTip; 01668 } 01669 01670 static LRESULT 01671 TREEVIEW_SetToolTips(TREEVIEW_INFO *infoPtr, HWND hwndTT) 01672 { 01673 HWND prevToolTip; 01674 01675 TRACE("\n"); 01676 prevToolTip = infoPtr->hwndToolTip; 01677 infoPtr->hwndToolTip = hwndTT; 01678 01679 return (LRESULT)prevToolTip; 01680 } 01681 01682 static LRESULT 01683 TREEVIEW_SetUnicodeFormat(TREEVIEW_INFO *infoPtr, BOOL fUnicode) 01684 { 01685 BOOL rc = infoPtr->bNtfUnicode; 01686 infoPtr->bNtfUnicode = fUnicode; 01687 return rc; 01688 } 01689 01690 static LRESULT 01691 TREEVIEW_GetUnicodeFormat(const TREEVIEW_INFO *infoPtr) 01692 { 01693 return infoPtr->bNtfUnicode; 01694 } 01695 01696 static LRESULT 01697 TREEVIEW_GetScrollTime(const TREEVIEW_INFO *infoPtr) 01698 { 01699 return infoPtr->uScrollTime; 01700 } 01701 01702 static LRESULT 01703 TREEVIEW_SetScrollTime(TREEVIEW_INFO *infoPtr, UINT uScrollTime) 01704 { 01705 UINT uOldScrollTime = infoPtr->uScrollTime; 01706 01707 infoPtr->uScrollTime = min(uScrollTime, 100); 01708 01709 return uOldScrollTime; 01710 } 01711 01712 01713 static LRESULT 01714 TREEVIEW_GetImageList(const TREEVIEW_INFO *infoPtr, WPARAM wParam) 01715 { 01716 TRACE("\n"); 01717 01718 switch (wParam) 01719 { 01720 case TVSIL_NORMAL: 01721 return (LRESULT)infoPtr->himlNormal; 01722 01723 case TVSIL_STATE: 01724 return (LRESULT)infoPtr->himlState; 01725 01726 default: 01727 return 0; 01728 } 01729 } 01730 01731 #define TVHEIGHT_MIN 16 01732 #define TVHEIGHT_FONT_ADJUST 3 /* 2 for focus border + 1 for margin some apps assume */ 01733 01734 /* Compute the natural height for items. */ 01735 static UINT 01736 TREEVIEW_NaturalHeight(const TREEVIEW_INFO *infoPtr) 01737 { 01738 TEXTMETRICW tm; 01739 HDC hdc = GetDC(0); 01740 HFONT hOldFont = SelectObject(hdc, infoPtr->hFont); 01741 UINT height; 01742 01743 /* Height is the maximum of: 01744 * 16 (a hack because our fonts are tiny), and 01745 * The text height + border & margin, and 01746 * The size of the normal image list 01747 */ 01748 GetTextMetricsW(hdc, &tm); 01749 SelectObject(hdc, hOldFont); 01750 ReleaseDC(0, hdc); 01751 01752 height = TVHEIGHT_MIN; 01753 if (height < tm.tmHeight + tm.tmExternalLeading + TVHEIGHT_FONT_ADJUST) 01754 height = tm.tmHeight + tm.tmExternalLeading + TVHEIGHT_FONT_ADJUST; 01755 if (height < infoPtr->normalImageHeight) 01756 height = infoPtr->normalImageHeight; 01757 01758 /* Round down, unless we support odd ("non even") heights. */ 01759 if (!(infoPtr->dwStyle & TVS_NONEVENHEIGHT)) 01760 height &= ~1; 01761 01762 return height; 01763 } 01764 01765 static LRESULT 01766 TREEVIEW_SetImageList(TREEVIEW_INFO *infoPtr, UINT type, HIMAGELIST himlNew) 01767 { 01768 HIMAGELIST himlOld = 0; 01769 int oldWidth = infoPtr->normalImageWidth; 01770 int oldHeight = infoPtr->normalImageHeight; 01771 01772 TRACE("%u,%p\n", type, himlNew); 01773 01774 switch (type) 01775 { 01776 case TVSIL_NORMAL: 01777 himlOld = infoPtr->himlNormal; 01778 infoPtr->himlNormal = himlNew; 01779 01780 if (himlNew) 01781 ImageList_GetIconSize(himlNew, &infoPtr->normalImageWidth, 01782 &infoPtr->normalImageHeight); 01783 else 01784 { 01785 infoPtr->normalImageWidth = 0; 01786 infoPtr->normalImageHeight = 0; 01787 } 01788 01789 break; 01790 01791 case TVSIL_STATE: 01792 himlOld = infoPtr->himlState; 01793 infoPtr->himlState = himlNew; 01794 01795 if (himlNew) 01796 { 01797 ImageList_GetIconSize(himlNew, &infoPtr->stateImageWidth, 01798 &infoPtr->stateImageHeight); 01799 infoPtr->statehimlType = OriginUser; 01800 } 01801 else 01802 { 01803 infoPtr->stateImageWidth = 0; 01804 infoPtr->stateImageHeight = 0; 01805 } 01806 01807 break; 01808 01809 default: 01810 ERR("unknown imagelist type %u\n", type); 01811 } 01812 01813 if (oldWidth != infoPtr->normalImageWidth || 01814 oldHeight != infoPtr->normalImageHeight) 01815 { 01816 BOOL bRecalcVisible = FALSE; 01817 01818 if (oldHeight != infoPtr->normalImageHeight && 01819 !infoPtr->bHeightSet) 01820 { 01821 infoPtr->uItemHeight = TREEVIEW_NaturalHeight(infoPtr); 01822 bRecalcVisible = TRUE; 01823 } 01824 01825 if (infoPtr->normalImageWidth > MINIMUM_INDENT && 01826 infoPtr->normalImageWidth != infoPtr->uIndent) 01827 { 01828 infoPtr->uIndent = infoPtr->normalImageWidth; 01829 bRecalcVisible = TRUE; 01830 } 01831 01832 if (bRecalcVisible) 01833 TREEVIEW_RecalculateVisibleOrder(infoPtr, NULL); 01834 01835 TREEVIEW_UpdateSubTree(infoPtr, infoPtr->root); 01836 TREEVIEW_UpdateScrollBars(infoPtr); 01837 } 01838 01839 TREEVIEW_Invalidate(infoPtr, NULL); 01840 01841 return (LRESULT)himlOld; 01842 } 01843 01844 static LRESULT 01845 TREEVIEW_SetItemHeight(TREEVIEW_INFO *infoPtr, INT newHeight) 01846 { 01847 INT prevHeight = infoPtr->uItemHeight; 01848 01849 TRACE("new=%d, old=%d\n", newHeight, prevHeight); 01850 if (newHeight == -1) 01851 { 01852 infoPtr->uItemHeight = TREEVIEW_NaturalHeight(infoPtr); 01853 infoPtr->bHeightSet = FALSE; 01854 } 01855 else 01856 { 01857 if (newHeight == 0) newHeight = 1; 01858 infoPtr->uItemHeight = newHeight; 01859 infoPtr->bHeightSet = TRUE; 01860 } 01861 01862 /* Round down, unless we support odd ("non even") heights. */ 01863 if (!(infoPtr->dwStyle & TVS_NONEVENHEIGHT) && infoPtr->uItemHeight != 1) 01864 { 01865 infoPtr->uItemHeight &= ~1; 01866 TRACE("after rounding=%d\n", infoPtr->uItemHeight); 01867 } 01868 01869 if (infoPtr->uItemHeight != prevHeight) 01870 { 01871 TREEVIEW_RecalculateVisibleOrder(infoPtr, NULL); 01872 TREEVIEW_UpdateScrollBars(infoPtr); 01873 TREEVIEW_Invalidate(infoPtr, NULL); 01874 } 01875 01876 return prevHeight; 01877 } 01878 01879 static LRESULT 01880 TREEVIEW_GetItemHeight(const TREEVIEW_INFO *infoPtr) 01881 { 01882 TRACE("\n"); 01883 return infoPtr->uItemHeight; 01884 } 01885 01886 01887 static LRESULT 01888 TREEVIEW_GetFont(const TREEVIEW_INFO *infoPtr) 01889 { 01890 TRACE("%p\n", infoPtr->hFont); 01891 return (LRESULT)infoPtr->hFont; 01892 } 01893 01894 01895 static INT CALLBACK 01896 TREEVIEW_ResetTextWidth(LPVOID pItem, LPVOID unused) 01897 { 01898 (void)unused; 01899 01900 ((TREEVIEW_ITEM *)pItem)->textWidth = 0; 01901 01902 return 1; 01903 } 01904 01905 static LRESULT 01906 TREEVIEW_SetFont(TREEVIEW_INFO *infoPtr, HFONT hFont, BOOL bRedraw) 01907 { 01908 UINT uHeight = infoPtr->uItemHeight; 01909 01910 TRACE("%p %i\n", hFont, bRedraw); 01911 01912 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont; 01913 01914 DeleteObject(infoPtr->hBoldFont); 01915 DeleteObject(infoPtr->hUnderlineFont); 01916 infoPtr->hBoldFont = TREEVIEW_CreateBoldFont(infoPtr->hFont); 01917 infoPtr->hUnderlineFont = TREEVIEW_CreateUnderlineFont(infoPtr->hFont); 01918 01919 if (!infoPtr->bHeightSet) 01920 infoPtr->uItemHeight = TREEVIEW_NaturalHeight(infoPtr); 01921 01922 if (uHeight != infoPtr->uItemHeight) 01923 TREEVIEW_RecalculateVisibleOrder(infoPtr, NULL); 01924 01925 DPA_EnumCallback(infoPtr->items, TREEVIEW_ResetTextWidth, 0); 01926 01927 TREEVIEW_UpdateSubTree(infoPtr, infoPtr->root); 01928 TREEVIEW_UpdateScrollBars(infoPtr); 01929 01930 if (bRedraw) 01931 TREEVIEW_Invalidate(infoPtr, NULL); 01932 01933 return 0; 01934 } 01935 01936 01937 static LRESULT 01938 TREEVIEW_GetLineColor(const TREEVIEW_INFO *infoPtr) 01939 { 01940 TRACE("\n"); 01941 return (LRESULT)infoPtr->clrLine; 01942 } 01943 01944 static LRESULT 01945 TREEVIEW_SetLineColor(TREEVIEW_INFO *infoPtr, COLORREF color) 01946 { 01947 COLORREF prevColor = infoPtr->clrLine; 01948 01949 TRACE("\n"); 01950 infoPtr->clrLine = color; 01951 return (LRESULT)prevColor; 01952 } 01953 01954 01955 static LRESULT 01956 TREEVIEW_GetTextColor(const TREEVIEW_INFO *infoPtr) 01957 { 01958 TRACE("\n"); 01959 return (LRESULT)infoPtr->clrText; 01960 } 01961 01962 static LRESULT 01963 TREEVIEW_SetTextColor(TREEVIEW_INFO *infoPtr, COLORREF color) 01964 { 01965 COLORREF prevColor = infoPtr->clrText; 01966 01967 TRACE("\n"); 01968 infoPtr->clrText = color; 01969 01970 if (infoPtr->clrText != prevColor) 01971 TREEVIEW_Invalidate(infoPtr, NULL); 01972 01973 return (LRESULT)prevColor; 01974 } 01975 01976 01977 static LRESULT 01978 TREEVIEW_GetBkColor(const TREEVIEW_INFO *infoPtr) 01979 { 01980 TRACE("\n"); 01981 return (LRESULT)infoPtr->clrBk; 01982 } 01983 01984 static LRESULT 01985 TREEVIEW_SetBkColor(TREEVIEW_INFO *infoPtr, COLORREF newColor) 01986 { 01987 COLORREF prevColor = infoPtr->clrBk; 01988 01989 TRACE("\n"); 01990 infoPtr->clrBk = newColor; 01991 01992 if (newColor != prevColor) 01993 TREEVIEW_Invalidate(infoPtr, NULL); 01994 01995 return (LRESULT)prevColor; 01996 } 01997 01998 01999 static LRESULT 02000 TREEVIEW_GetInsertMarkColor(const TREEVIEW_INFO *infoPtr) 02001 { 02002 TRACE("\n"); 02003 return (LRESULT)infoPtr->clrInsertMark; 02004 } 02005 02006 static LRESULT 02007 TREEVIEW_SetInsertMarkColor(TREEVIEW_INFO *infoPtr, COLORREF color) 02008 { 02009 COLORREF prevColor = infoPtr->clrInsertMark; 02010 02011 TRACE("%x\n", color); 02012 infoPtr->clrInsertMark = color; 02013 02014 return (LRESULT)prevColor; 02015 } 02016 02017 02018 static LRESULT 02019 TREEVIEW_SetInsertMark(TREEVIEW_INFO *infoPtr, BOOL wParam, HTREEITEM item) 02020 { 02021 TRACE("%d %p\n", wParam, item); 02022 02023 if (!TREEVIEW_ValidItem(infoPtr, item)) 02024 return 0; 02025 02026 infoPtr->insertBeforeorAfter = wParam; 02027 infoPtr->insertMarkItem = item; 02028 02029 TREEVIEW_Invalidate(infoPtr, NULL); 02030 02031 return 1; 02032 } 02033 02034 02035 /************************************************************************ 02036 * Some serious braindamage here. lParam is a pointer to both the 02037 * input HTREEITEM and the output RECT. 02038 */ 02039 static LRESULT 02040 TREEVIEW_GetItemRect(const TREEVIEW_INFO *infoPtr, BOOL fTextRect, LPRECT lpRect) 02041 { 02042 TREEVIEW_ITEM *item; 02043 const HTREEITEM *pItem = (HTREEITEM *)lpRect; 02044 02045 TRACE("\n"); 02046 02047 if (pItem == NULL) 02048 return FALSE; 02049 02050 item = *pItem; 02051 if (!TREEVIEW_ValidItem(infoPtr, item) || !ISVISIBLE(item)) 02052 return FALSE; 02053 02054 /* 02055 * If wParam is TRUE return the text size otherwise return 02056 * the whole item size 02057 */ 02058 if (fTextRect) 02059 { 02060 /* Windows does not send TVN_GETDISPINFO here. */ 02061 02062 lpRect->top = item->rect.top; 02063 lpRect->bottom = item->rect.bottom; 02064 02065 lpRect->left = item->textOffset; 02066 if (!item->textWidth) 02067 TREEVIEW_ComputeTextWidth(infoPtr, item, 0); 02068 02069 lpRect->right = item->textOffset + item->textWidth + 4; 02070 } 02071 else 02072 { 02073 *lpRect = item->rect; 02074 } 02075 02076 TRACE("%s [%s]\n", fTextRect ? "text" : "item", wine_dbgstr_rect(lpRect)); 02077 02078 return TRUE; 02079 } 02080 02081 static inline LRESULT 02082 TREEVIEW_GetVisibleCount(const TREEVIEW_INFO *infoPtr) 02083 { 02084 /* Surprise! This does not take integral height into account. */ 02085 TRACE("client=%d, item=%d\n", infoPtr->clientHeight, infoPtr->uItemHeight); 02086 return infoPtr->clientHeight / infoPtr->uItemHeight; 02087 } 02088 02089 02090 static LRESULT 02091 TREEVIEW_GetItemT(const TREEVIEW_INFO *infoPtr, LPTVITEMEXW tvItem, BOOL isW) 02092 { 02093 TREEVIEW_ITEM *item = tvItem->hItem; 02094 02095 if (!TREEVIEW_ValidItem(infoPtr, item)) 02096 return FALSE; 02097 02098 TREEVIEW_UpdateDispInfo(infoPtr, item, tvItem->mask); 02099 02100 if (tvItem->mask & TVIF_CHILDREN) 02101 { 02102 if (item->cChildren==I_CHILDRENCALLBACK) 02103 FIXME("I_CHILDRENCALLBACK not supported\n"); 02104 tvItem->cChildren = item->cChildren; 02105 } 02106 02107 if (tvItem->mask & TVIF_HANDLE) 02108 tvItem->hItem = item; 02109 02110 if (tvItem->mask & TVIF_IMAGE) 02111 tvItem->iImage = item->iImage; 02112 02113 if (tvItem->mask & TVIF_INTEGRAL) 02114 tvItem->iIntegral = item->iIntegral; 02115 02116 /* undocumented: (mask & TVIF_PARAM) ignored and lParam is always set */ 02117 tvItem->lParam = item->lParam; 02118 02119 if (tvItem->mask & TVIF_SELECTEDIMAGE) 02120 tvItem->iSelectedImage = item->iSelectedImage; 02121 02122 if (tvItem->mask & TVIF_EXPANDEDIMAGE) 02123 tvItem->iExpandedImage = item->iExpandedImage; 02124 02125 /* undocumented: stateMask and (state & TVIF_STATE) ignored, so state is always set */ 02126 tvItem->state = item->state; 02127 02128 if (tvItem->mask & TVIF_TEXT) 02129 { 02130 if (item->pszText == NULL) 02131 { 02132 if (tvItem->cchTextMax > 0) 02133 tvItem->pszText[0] = '\0'; 02134 } 02135 else if (isW) 02136 { 02137 if (item->pszText == LPSTR_TEXTCALLBACKW) 02138 { 02139 tvItem->pszText = LPSTR_TEXTCALLBACKW; 02140 FIXME(" GetItem called with LPSTR_TEXTCALLBACK\n"); 02141 } 02142 else 02143 { 02144 lstrcpynW(tvItem->pszText, item->pszText, tvItem->cchTextMax); 02145 } 02146 } 02147 else 02148 { 02149 if (item->pszText == LPSTR_TEXTCALLBACKW) 02150 { 02151 tvItem->pszText = (LPWSTR)LPSTR_TEXTCALLBACKA; 02152 FIXME(" GetItem called with LPSTR_TEXTCALLBACK\n"); 02153 } 02154 else 02155 { 02156 WideCharToMultiByte(CP_ACP, 0, item->pszText, -1, 02157 (LPSTR)tvItem->pszText, tvItem->cchTextMax, NULL, NULL); 02158 } 02159 } 02160 } 02161 02162 if (tvItem->mask & TVIF_STATEEX) 02163 { 02164 FIXME("Extended item state not supported, returning 0.\n"); 02165 tvItem->uStateEx = 0; 02166 } 02167 02168 TRACE("item <%p>, txt %p, img %d, mask %x\n", 02169 item, tvItem->pszText, tvItem->iImage, tvItem->mask); 02170 02171 return TRUE; 02172 } 02173 02174 /* Beware MSDN Library Visual Studio 6.0. It says -1 on failure, 0 on success, 02175 * which is wrong. */ 02176 static LRESULT 02177 TREEVIEW_SetItemT(TREEVIEW_INFO *infoPtr, const TVITEMEXW *tvItem, BOOL isW) 02178 { 02179 TREEVIEW_ITEM *item; 02180 TREEVIEW_ITEM originalItem; 02181 02182 item = tvItem->hItem; 02183 02184 TRACE("item %d,mask %x\n", TREEVIEW_GetItemIndex(infoPtr, item), 02185 tvItem->mask); 02186 02187 if (!TREEVIEW_ValidItem(infoPtr, item)) 02188 return FALSE; 02189 02190 /* store the original item values */ 02191 originalItem = *item; 02192 02193 if (!TREEVIEW_DoSetItemT(infoPtr, item, tvItem, isW)) 02194 return FALSE; 02195 02196 /* If the text or TVIS_BOLD was changed, and it is visible, recalculate. */ 02197 if ((tvItem->mask & TVIF_TEXT 02198 || (tvItem->mask & TVIF_STATE && tvItem->stateMask & TVIS_BOLD)) 02199 && ISVISIBLE(item)) 02200 { 02201 TREEVIEW_UpdateDispInfo(infoPtr, item, TVIF_TEXT); 02202 TREEVIEW_ComputeTextWidth(infoPtr, item, 0); 02203 } 02204 02205 if (tvItem->mask != 0 && ISVISIBLE(item)) 02206 { 02207 /* The refresh updates everything, but we can't wait until then. */ 02208 TREEVIEW_ComputeItemInternalMetrics(infoPtr, item); 02209 02210 /* if any of the item's values changed and it's not a callback, redraw the item */ 02211 if (item_changed(&originalItem, item, tvItem)) 02212 { 02213 if (tvItem->mask & TVIF_INTEGRAL) 02214 { 02215 TREEVIEW_RecalculateVisibleOrder(infoPtr, item); 02216 TREEVIEW_UpdateScrollBars(infoPtr); 02217 02218 TREEVIEW_Invalidate(infoPtr, NULL); 02219 } 02220 else 02221 { 02222 TREEVIEW_UpdateScrollBars(infoPtr); 02223 TREEVIEW_Invalidate(infoPtr, item); 02224 } 02225 } 02226 } 02227 02228 return TRUE; 02229 } 02230 02231 static LRESULT 02232 TREEVIEW_GetItemState(const TREEVIEW_INFO *infoPtr, HTREEITEM item, UINT mask) 02233 { 02234 TRACE("\n"); 02235 02236 if (!item || !TREEVIEW_ValidItem(infoPtr, item)) 02237 return 0; 02238 02239 return (item->state & mask); 02240 } 02241 02242 static LRESULT 02243 TREEVIEW_GetNextItem(const TREEVIEW_INFO *infoPtr, UINT which, HTREEITEM item) 02244 { 02245 TREEVIEW_ITEM *retval; 02246 02247 retval = 0; 02248 02249 /* handle all the global data here */ 02250 switch (which) 02251 { 02252 case TVGN_CHILD: /* Special case: child of 0 is root */ 02253 if (item) 02254 break; 02255 /* fall through */ 02256 case TVGN_ROOT: 02257 retval = infoPtr->root->firstChild; 02258 break; 02259 02260 case TVGN_CARET: 02261 retval = infoPtr->selectedItem; 02262 break; 02263 02264 case TVGN_FIRSTVISIBLE: 02265 retval = infoPtr->firstVisible; 02266 break; 02267 02268 case TVGN_DROPHILITE: 02269 retval = infoPtr->dropItem; 02270 break; 02271 02272 case TVGN_LASTVISIBLE: 02273 retval = TREEVIEW_GetLastListItem(infoPtr, infoPtr->root); 02274 break; 02275 } 02276 02277 if (retval) 02278 { 02279 TRACE("flags:%x, returns %p\n", which, retval); 02280 return (LRESULT)retval; 02281 } 02282 02283 if (item == TVI_ROOT) item = infoPtr->root; 02284 02285 if (!TREEVIEW_ValidItem(infoPtr, item)) 02286 return FALSE; 02287 02288 switch (which) 02289 { 02290 case TVGN_NEXT: 02291 retval = item->nextSibling; 02292 break; 02293 case TVGN_PREVIOUS: 02294 retval = item->prevSibling; 02295 break; 02296 case TVGN_PARENT: 02297 retval = (item->parent != infoPtr->root) ? item->parent : NULL; 02298 break; 02299 case TVGN_CHILD: 02300 retval = item->firstChild; 02301 break; 02302 case TVGN_NEXTVISIBLE: 02303 retval = TREEVIEW_GetNextListItem(infoPtr, item); 02304 break; 02305 case TVGN_PREVIOUSVISIBLE: 02306 retval = TREEVIEW_GetPrevListItem(infoPtr, item); 02307 break; 02308 default: 02309 TRACE("Unknown msg %x,item %p\n", which, item); 02310 break; 02311 } 02312 02313 TRACE("flags:%x, item %p;returns %p\n", which, item, retval); 02314 return (LRESULT)retval; 02315 } 02316 02317 02318 static LRESULT 02319 TREEVIEW_GetCount(const TREEVIEW_INFO *infoPtr) 02320 { 02321 TRACE(" %d\n", infoPtr->uNumItems); 02322 return (LRESULT)infoPtr->uNumItems; 02323 } 02324 02325 static VOID 02326 TREEVIEW_ToggleItemState(const TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item) 02327 { 02328 if (infoPtr->dwStyle & TVS_CHECKBOXES) 02329 { 02330 static const unsigned int state_table[] = { 0, 2, 1 }; 02331 02332 unsigned int state; 02333 02334 state = STATEIMAGEINDEX(item->state); 02335 TRACE("state:%x\n", state); 02336 item->state &= ~TVIS_STATEIMAGEMASK; 02337 02338 if (state < 3) 02339 state = state_table[state]; 02340 02341 item->state |= INDEXTOSTATEIMAGEMASK(state); 02342 02343 TRACE("state:%x\n", state); 02344 TREEVIEW_Invalidate(infoPtr, item); 02345 } 02346 } 02347 02348 02349 /* Painting *************************************************************/ 02350 02351 /* Draw the lines and expand button for an item. Also draws one section 02352 * of the line from item's parent to item's parent's next sibling. */ 02353 static void 02354 TREEVIEW_DrawItemLines(const TREEVIEW_INFO *infoPtr, HDC hdc, const TREEVIEW_ITEM *item) 02355 { 02356 LONG centerx, centery; 02357 BOOL lar = ((infoPtr->dwStyle 02358 & (TVS_LINESATROOT|TVS_HASLINES|TVS_HASBUTTONS)) 02359 > TVS_LINESATROOT); 02360 HBRUSH hbr, hbrOld; 02361 COLORREF clrBk = GETBKCOLOR(infoPtr->clrBk); 02362 02363 if (!lar && item->iLevel == 0) 02364 return; 02365 02366 hbr = CreateSolidBrush(clrBk); 02367 hbrOld = SelectObject(hdc, hbr); 02368 02369 centerx = (item->linesOffset + item->stateOffset) / 2; 02370 centery = (item->rect.top + item->rect.bottom) / 2; 02371 02372 if (infoPtr->dwStyle & TVS_HASLINES) 02373 { 02374 HPEN hOldPen, hNewPen; 02375 HTREEITEM parent; 02376 LOGBRUSH lb; 02377 02378 /* Get a dotted grey pen */ 02379 lb.lbStyle = BS_SOLID; 02380 lb.lbColor = GETLINECOLOR(infoPtr->clrLine); 02381 hNewPen = ExtCreatePen(PS_COSMETIC|PS_ALTERNATE, 1, &lb, 0, NULL); 02382 hOldPen = SelectObject(hdc, hNewPen); 02383 02384 /* Make sure the center is on a dot (using +2 instead 02385 * of +1 gives us pixel-by-pixel compat with native) */ 02386 centery = (centery + 2) & ~1; 02387 02388 MoveToEx(hdc, item->stateOffset, centery, NULL); 02389 LineTo(hdc, centerx - 1, centery); 02390 02391 if (item->prevSibling || item->parent != infoPtr->root) 02392 { 02393 MoveToEx(hdc, centerx, item->rect.top, NULL); 02394 LineTo(hdc, centerx, centery); 02395 } 02396 02397 if (item->nextSibling) 02398 { 02399 MoveToEx(hdc, centerx, centery, NULL); 02400 LineTo(hdc, centerx, item->rect.bottom + 1); 02401 } 02402 02403 /* Draw the line from our parent to its next sibling. */ 02404 parent = item->parent; 02405 while (parent != infoPtr->root) 02406 { 02407 int pcenterx = (parent->linesOffset + parent->stateOffset) / 2; 02408 02409 if (parent->nextSibling 02410 /* skip top-levels unless TVS_LINESATROOT */ 02411 && parent->stateOffset > parent->linesOffset) 02412 { 02413 MoveToEx(hdc, pcenterx, item->rect.top, NULL); 02414 LineTo(hdc, pcenterx, item->rect.bottom + 1); 02415 } 02416 02417 parent = parent->parent; 02418 } 02419 02420 SelectObject(hdc, hOldPen); 02421 DeleteObject(hNewPen); 02422 } 02423 02424 /* 02425 * Display the (+/-) signs 02426 */ 02427 02428 if (infoPtr->dwStyle & TVS_HASBUTTONS) 02429 { 02430 if (item->cChildren) 02431 { 02432 HTHEME theme = GetWindowTheme(infoPtr->hwnd); 02433 if (theme) 02434 { 02435 RECT glyphRect = item->rect; 02436 glyphRect.left = item->linesOffset; 02437 glyphRect.right = item->stateOffset; 02438 DrawThemeBackground (theme, hdc, TVP_GLYPH, 02439 (item->state & TVIS_EXPANDED) ? GLPS_OPENED : GLPS_CLOSED, 02440 &glyphRect, NULL); 02441 } 02442 else 02443 { 02444 LONG height = item->rect.bottom - item->rect.top; 02445 LONG width = item->stateOffset - item->linesOffset; 02446 LONG rectsize = min(height, width) / 4; 02447 /* plussize = ceil(rectsize * 3/4) */ 02448 LONG plussize = (rectsize + 1) * 3 / 4; 02449 02450 HPEN new_pen = CreatePen(PS_SOLID, 0, GETLINECOLOR(infoPtr->clrLine)); 02451 HPEN old_pen = SelectObject(hdc, new_pen); 02452 02453 Rectangle(hdc, centerx - rectsize - 1, centery - rectsize - 1, 02454 centerx + rectsize + 2, centery + rectsize + 2); 02455 02456 SelectObject(hdc, old_pen); 02457 DeleteObject(new_pen); 02458 02459 /* draw +/- signs with current text color */ 02460 new_pen = CreatePen(PS_SOLID, 0, GETTXTCOLOR(infoPtr->clrText)); 02461 old_pen = SelectObject(hdc, new_pen); 02462 02463 if (height < 18 || width < 18) 02464 { 02465 MoveToEx(hdc, centerx - plussize + 1, centery, NULL); 02466 LineTo(hdc, centerx + plussize, centery); 02467 02468 if (!(item->state & TVIS_EXPANDED) || 02469 (item->state & TVIS_EXPANDPARTIAL)) 02470 { 02471 MoveToEx(hdc, centerx, centery - plussize + 1, NULL); 02472 LineTo(hdc, centerx, centery + plussize); 02473 } 02474 } 02475 else 02476 { 02477 Rectangle(hdc, centerx - plussize + 1, centery - 1, 02478 centerx + plussize, centery + 2); 02479 02480 if (!(item->state & TVIS_EXPANDED) || 02481 (item->state & TVIS_EXPANDPARTIAL)) 02482 { 02483 Rectangle(hdc, centerx - 1, centery - plussize + 1, 02484 centerx + 2, centery + plussize); 02485 SetPixel(hdc, centerx - 1, centery, clrBk); 02486 SetPixel(hdc, centerx + 1, centery, clrBk); 02487 } 02488 } 02489 02490 SelectObject(hdc, old_pen); 02491 DeleteObject(new_pen); 02492 } 02493 } 02494 } 02495 SelectObject(hdc, hbrOld); 02496 DeleteObject(hbr); 02497 } 02498 02499 static void 02500 TREEVIEW_DrawItem(const TREEVIEW_INFO *infoPtr, HDC hdc, TREEVIEW_ITEM *item) 02501 { 02502 INT cditem; 02503 HFONT hOldFont; 02504 COLORREF oldTextColor, oldTextBkColor; 02505 int centery; 02506 BOOL inFocus = (GetFocus() == infoPtr->hwnd); 02507 NMTVCUSTOMDRAW nmcdhdr; 02508 02509 TREEVIEW_UpdateDispInfo(infoPtr, item, CALLBACK_MASK_ALL); 02510 02511 /* - If item is drop target or it is selected and window is in focus - 02512 * use blue background (COLOR_HIGHLIGHT). 02513 * - If item is selected, window is not in focus, but it has style 02514 * TVS_SHOWSELALWAYS - use grey background (COLOR_BTNFACE) 02515 * - Otherwise - use background color 02516 */ 02517 if ((item->state & TVIS_DROPHILITED) || ((item == infoPtr->focusedItem) && !(item->state & TVIS_SELECTED)) || 02518 ((item->state & TVIS_SELECTED) && (!infoPtr->focusedItem) && 02519 (inFocus || (infoPtr->dwStyle & TVS_SHOWSELALWAYS)))) 02520 { 02521 if ((item->state & TVIS_DROPHILITED) || inFocus) 02522 { 02523 nmcdhdr.clrTextBk = comctl32_color.clrHighlight; 02524 nmcdhdr.clrText = comctl32_color.clrHighlightText; 02525 } 02526 else 02527 { 02528 nmcdhdr.clrTextBk = comctl32_color.clrBtnFace; 02529 nmcdhdr.clrText = GETTXTCOLOR(infoPtr->clrText); 02530 } 02531 } 02532 else 02533 { 02534 nmcdhdr.clrTextBk = GETBKCOLOR(infoPtr->clrBk); 02535 if ((infoPtr->dwStyle & TVS_TRACKSELECT) && (item == infoPtr->hotItem)) 02536 nmcdhdr.clrText = comctl32_color.clrHighlight; 02537 else 02538 nmcdhdr.clrText = GETTXTCOLOR(infoPtr->clrText); 02539 } 02540 02541 hOldFont = SelectObject(hdc, TREEVIEW_FontForItem(infoPtr, item)); 02542 02543 /* The custom draw handler can query the text rectangle, 02544 * so get ready. */ 02545 /* should already be known, set to 0 when changed */ 02546 if (!item->textWidth) 02547 TREEVIEW_ComputeTextWidth(infoPtr, item, hdc); 02548 02549 cditem = 0; 02550 02551 if (infoPtr->cdmode & CDRF_NOTIFYITEMDRAW) 02552 { 02553 cditem = TREEVIEW_SendCustomDrawItemNotify 02554 (infoPtr, hdc, item, CDDS_ITEMPREPAINT, &nmcdhdr); 02555 TRACE("prepaint:cditem-app returns 0x%x\n", cditem); 02556 02557 if (cditem & CDRF_SKIPDEFAULT) 02558 { 02559 SelectObject(hdc, hOldFont); 02560 return; 02561 } 02562 } 02563 02564 if (cditem & CDRF_NEWFONT) 02565 TREEVIEW_ComputeTextWidth(infoPtr, item, hdc); 02566 02567 TREEVIEW_DrawItemLines(infoPtr, hdc, item); 02568 02569 /* Set colors. Custom draw handler can change these so we do this after it. */ 02570 oldTextColor = SetTextColor(hdc, nmcdhdr.clrText); 02571 oldTextBkColor = SetBkColor(hdc, nmcdhdr.clrTextBk); 02572 02573 centery = (item->rect.top + item->rect.bottom) / 2; 02574 02575 /* 02576 * Display the images associated with this item 02577 */ 02578 { 02579 INT imageIndex; 02580 02581 /* State images are displayed to the left of the Normal image 02582 * image number is in state; zero should be `display no image'. 02583 */ 02584 imageIndex = STATEIMAGEINDEX(item->state); 02585 02586 if (infoPtr->himlState && imageIndex) 02587 { 02588 ImageList_Draw(infoPtr->himlState, imageIndex, hdc, 02589 item->stateOffset, 02590 centery - infoPtr->stateImageHeight / 2, 02591 ILD_NORMAL); 02592 } 02593 02594 /* Now, draw the normal image; can be either selected, 02595 * non-selected or expanded image. 02596 */ 02597 02598 if ((item->state & TVIS_SELECTED) && (item->iSelectedImage >= 0)) 02599 { 02600 /* The item is currently selected */ 02601 imageIndex = item->iSelectedImage; 02602 } 02603 else if ((item->state & TVIS_EXPANDED) && (item->iExpandedImage != (WORD)I_IMAGENONE)) 02604 { 02605 /* The item is currently not selected but expanded */ 02606 imageIndex = item->iExpandedImage; 02607 } 02608 else 02609 { 02610 /* The item is not selected and not expanded */ 02611 imageIndex = item->iImage; 02612 } 02613 02614 if (infoPtr->himlNormal) 02615 { 02616 UINT style = item->state & TVIS_CUT ? ILD_SELECTED : ILD_NORMAL; 02617 02618 style |= item->state & TVIS_OVERLAYMASK; 02619 02620 ImageList_DrawEx(infoPtr->himlNormal, imageIndex, hdc, 02621 item->imageOffset, centery - infoPtr->normalImageHeight / 2, 02622 0, 0, infoPtr->clrBk, item->state & TVIS_CUT ? GETBKCOLOR(infoPtr->clrBk) : CLR_DEFAULT, 02623 style); 02624 } 02625 } 02626 02627 02628 /* 02629 * Display the text associated with this item 02630 */ 02631 02632 /* Don't paint item's text if it's being edited */ 02633 if (!infoPtr->hwndEdit || (infoPtr->selectedItem != item)) 02634 { 02635 if (item->pszText) 02636 { 02637 RECT rcText; 02638 UINT align; 02639 SIZE sz; 02640 02641 rcText.top = item->rect.top; 02642 rcText.bottom = item->rect.bottom; 02643 rcText.left = item->textOffset; 02644 rcText.right = rcText.left + item->textWidth + 4; 02645 02646 TRACE("drawing text %s at (%s)\n", 02647 debugstr_w(item->pszText), wine_dbgstr_rect(&rcText)); 02648 02649 /* Draw it */ 02650 GetTextExtentPoint32W(hdc, item->pszText, strlenW(item->pszText), &sz); 02651 02652 align = SetTextAlign(hdc, TA_LEFT | TA_TOP); 02653 ExtTextOutW(hdc, rcText.left + 2, (rcText.top + rcText.bottom - sz.cy) / 2, 02654 ETO_CLIPPED | ETO_OPAQUE, 02655 &rcText, 02656 item->pszText, 02657 lstrlenW(item->pszText), 02658 NULL); 02659 SetTextAlign(hdc, align); 02660 02661 /* Draw focus box around the selected item */ 02662 if ((item == infoPtr->selectedItem) && inFocus) 02663 { 02664 DrawFocusRect(hdc,&rcText); 02665 } 02666 } 02667 } 02668 02669 /* Draw insertion mark if necessary */ 02670 02671 if (infoPtr->insertMarkItem) 02672 TRACE("item:%d,mark:%p\n", 02673 TREEVIEW_GetItemIndex(infoPtr, item), 02674 infoPtr->insertMarkItem); 02675 02676 if (item == infoPtr->insertMarkItem) 02677 { 02678 HPEN hNewPen, hOldPen; 02679 int offset; 02680 int left, right; 02681 02682 hNewPen = CreatePen(PS_SOLID, 2, GETINSCOLOR(infoPtr->clrInsertMark)); 02683 hOldPen = SelectObject(hdc, hNewPen); 02684 02685 if (infoPtr->insertBeforeorAfter) 02686 offset = item->rect.bottom - 1; 02687 else 02688 offset = item->rect.top + 1; 02689 02690 left = item->textOffset - 2; 02691 right = item->textOffset + item->textWidth + 2; 02692 02693 MoveToEx(hdc, left, offset - 3, NULL); 02694 LineTo(hdc, left, offset + 4); 02695 02696 MoveToEx(hdc, left, offset, NULL); 02697 LineTo(hdc, right + 1, offset); 02698 02699 MoveToEx(hdc, right, offset + 3, NULL); 02700 LineTo(hdc, right, offset - 4); 02701 02702 SelectObject(hdc, hOldPen); 02703 DeleteObject(hNewPen); 02704 } 02705 02706 if (cditem & CDRF_NOTIFYPOSTPAINT) 02707 { 02708 cditem = TREEVIEW_SendCustomDrawItemNotify 02709 (infoPtr, hdc, item, CDDS_ITEMPOSTPAINT, &nmcdhdr); 02710 TRACE("postpaint:cditem-app returns 0x%x\n", cditem); 02711 } 02712 02713 /* Restore the hdc state */ 02714 SetTextColor(hdc, oldTextColor); 02715 SetBkColor(hdc, oldTextBkColor); 02716 SelectObject(hdc, hOldFont); 02717 } 02718 02719 /* Computes treeHeight and treeWidth and updates the scroll bars. 02720 */ 02721 static void 02722 TREEVIEW_UpdateScrollBars(TREEVIEW_INFO *infoPtr) 02723 { 02724 TREEVIEW_ITEM *item; 02725 HWND hwnd = infoPtr->hwnd; 02726 BOOL vert = FALSE; 02727 BOOL horz = FALSE; 02728 SCROLLINFO si; 02729 LONG scrollX = infoPtr->scrollX; 02730 02731 infoPtr->treeWidth = 0; 02732 infoPtr->treeHeight = 0; 02733 02734 /* We iterate through all visible items in order to get the tree height 02735 * and width */ 02736 item = infoPtr->root->firstChild; 02737 02738 while (item != NULL) 02739 { 02740 if (ISVISIBLE(item)) 02741 { 02742 /* actually we draw text at textOffset + 2 */ 02743 if (2+item->textOffset+item->textWidth > infoPtr->treeWidth) 02744 infoPtr->treeWidth = item->textOffset+item->textWidth+2; 02745 02746 /* This is scroll-adjusted, but we fix this below. */ 02747 infoPtr->treeHeight = item->rect.bottom; 02748 } 02749 02750 item = TREEVIEW_GetNextListItem(infoPtr, item); 02751 } 02752 02753 /* Fix the scroll adjusted treeHeight and treeWidth. */ 02754 if (infoPtr->root->firstChild) 02755 infoPtr->treeHeight -= infoPtr->root->firstChild->rect.top; 02756 02757 infoPtr->treeWidth += infoPtr->scrollX; 02758 02759 if (infoPtr->dwStyle & TVS_NOSCROLL) return; 02760 02761 /* Adding one scroll bar may take up enough space that it forces us 02762 * to add the other as well. */ 02763 if (infoPtr->treeHeight > infoPtr->clientHeight) 02764 { 02765 vert = TRUE; 02766 02767 if (infoPtr->treeWidth 02768 > infoPtr->clientWidth - GetSystemMetrics(SM_CXVSCROLL)) 02769 horz = TRUE; 02770 } 02771 else if (infoPtr->treeWidth > infoPtr->clientWidth || infoPtr->scrollX > 0) 02772 horz = TRUE; 02773 02774 if (!vert && horz && infoPtr->treeHeight 02775 > infoPtr->clientHeight - GetSystemMetrics(SM_CYVSCROLL)) 02776 vert = TRUE; 02777 02778 if (horz && (infoPtr->dwStyle & TVS_NOHSCROLL)) horz = FALSE; 02779 02780 si.cbSize = sizeof(SCROLLINFO); 02781 si.fMask = SIF_POS|SIF_RANGE|SIF_PAGE; 02782 si.nMin = 0; 02783 02784 if (vert) 02785 { 02786 si.nPage = TREEVIEW_GetVisibleCount(infoPtr); 02787 if ( si.nPage && NULL != infoPtr->firstVisible) 02788 { 02789 si.nPos = infoPtr->firstVisible->visibleOrder; 02790 si.nMax = infoPtr->maxVisibleOrder - 1; 02791 02792 SetScrollInfo(hwnd, SB_VERT, &si, TRUE); 02793 02794 if (!(infoPtr->uInternalStatus & TV_VSCROLL)) 02795 ShowScrollBar(hwnd, SB_VERT, TRUE); 02796 infoPtr->uInternalStatus |= TV_VSCROLL; 02797 } 02798 else 02799 { 02800 if (infoPtr->uInternalStatus & TV_VSCROLL) 02801 ShowScrollBar(hwnd, SB_VERT, FALSE); 02802 infoPtr->uInternalStatus &= ~TV_VSCROLL; 02803 } 02804 } 02805 else 02806 { 02807 if (infoPtr->uInternalStatus & TV_VSCROLL) 02808 ShowScrollBar(hwnd, SB_VERT, FALSE); 02809 infoPtr->uInternalStatus &= ~TV_VSCROLL; 02810 } 02811 02812 if (horz) 02813 { 02814 si.nPage = infoPtr->clientWidth; 02815 si.nPos = infoPtr->scrollX; 02816 si.nMax = infoPtr->treeWidth - 1; 02817 02818 if (si.nPos > si.nMax - max( si.nPage-1, 0 )) 02819 { 02820 si.nPos = si.nMax - max( si.nPage-1, 0 ); 02821 scrollX = si.nPos; 02822 } 02823 02824 if (!(infoPtr->uInternalStatus & TV_HSCROLL)) 02825 ShowScrollBar(hwnd, SB_HORZ, TRUE); 02826 infoPtr->uInternalStatus |= TV_HSCROLL; 02827 02828 SetScrollInfo(hwnd, SB_HORZ, &si, TRUE); 02829 TREEVIEW_HScroll(infoPtr, 02830 MAKEWPARAM(SB_THUMBPOSITION, scrollX)); 02831 } 02832 else 02833 { 02834 if (infoPtr->uInternalStatus & TV_HSCROLL) 02835 ShowScrollBar(hwnd, SB_HORZ, FALSE); 02836 infoPtr->uInternalStatus &= ~TV_HSCROLL; 02837 02838 scrollX = 0; 02839 if (infoPtr->scrollX != 0) 02840 { 02841 TREEVIEW_HScroll(infoPtr, 02842 MAKEWPARAM(SB_THUMBPOSITION, scrollX)); 02843 } 02844 } 02845 02846 if (!horz) 02847 infoPtr->uInternalStatus &= ~TV_HSCROLL; 02848 } 02849 02850 static void 02851 TREEVIEW_FillBkgnd(const TREEVIEW_INFO *infoPtr, HDC hdc, const RECT *rc) 02852 { 02853 HBRUSH hBrush; 02854 COLORREF clrBk = GETBKCOLOR(infoPtr->clrBk); 02855 02856 hBrush = CreateSolidBrush(clrBk); 02857 FillRect(hdc, rc, hBrush); 02858 DeleteObject(hBrush); 02859 } 02860 02861 /* CtrlSpy doesn't mention this, but CorelDRAW's object manager needs it. */ 02862 static LRESULT 02863 TREEVIEW_EraseBackground(const TREEVIEW_INFO *infoPtr, HDC hdc) 02864 { 02865 RECT rect; 02866 02867 TRACE("%p\n", infoPtr); 02868 02869 GetClientRect(infoPtr->hwnd, &rect); 02870 TREEVIEW_FillBkgnd(infoPtr, hdc, &rect); 02871 02872 return 1; 02873 } 02874 02875 static void 02876 TREEVIEW_Refresh(TREEVIEW_INFO *infoPtr, HDC hdc, const RECT *rc) 02877 { 02878 HWND hwnd = infoPtr->hwnd; 02879 RECT rect = *rc; 02880 TREEVIEW_ITEM *item; 02881 02882 if (infoPtr->clientHeight == 0 || infoPtr->clientWidth == 0) 02883 { 02884 TRACE("empty window\n"); 02885 return; 02886 } 02887 02888 infoPtr->cdmode = TREEVIEW_SendCustomDrawNotify(infoPtr, CDDS_PREPAINT, 02889 hdc, rect); 02890 02891 if (infoPtr->cdmode == CDRF_SKIPDEFAULT) 02892 { 02893 ReleaseDC(hwnd, hdc); 02894 return; 02895 } 02896 02897 for (item = infoPtr->root->firstChild; 02898 item != NULL; 02899 item = TREEVIEW_GetNextListItem(infoPtr, item)) 02900 { 02901 if (ISVISIBLE(item)) 02902 { 02903 /* Avoid unneeded calculations */ 02904 if (item->rect.top > rect.bottom) 02905 break; 02906 if (item->rect.bottom < rect.top) 02907 continue; 02908 02909 TREEVIEW_DrawItem(infoPtr, hdc, item); 02910 } 02911 } 02912 02913 // 02914 // This is correct, but is causes and infinite loop of WM_PAINT messages, resulting 02915 // in continuous painting of the scroll bar in reactos. Comment out until the real 02916 // bug is found 02917 // 02918 //TREEVIEW_UpdateScrollBars(infoPtr); 02919 02920 if (infoPtr->cdmode & CDRF_NOTIFYPOSTPAINT) 02921 infoPtr->cdmode = 02922 TREEVIEW_SendCustomDrawNotify(infoPtr, CDDS_POSTPAINT, hdc, rect); 02923 } 02924 02925 static inline void 02926 TREEVIEW_InvalidateItem(const TREEVIEW_INFO *infoPtr, const TREEVIEW_ITEM *item) 02927 { 02928 if (item) InvalidateRect(infoPtr->hwnd, &item->rect, TRUE); 02929 } 02930 02931 static void 02932 TREEVIEW_Invalidate(const TREEVIEW_INFO *infoPtr, const TREEVIEW_ITEM *item) 02933 { 02934 if (item) 02935 InvalidateRect(infoPtr->hwnd, &item->rect, TRUE); 02936 else 02937 InvalidateRect(infoPtr->hwnd, NULL, TRUE); 02938 } 02939 02940 static LRESULT 02941 TREEVIEW_Paint(TREEVIEW_INFO *infoPtr, HDC hdc_ref) 02942 { 02943 HDC hdc; 02944 PAINTSTRUCT ps; 02945 RECT rc; 02946 02947 TRACE("(%p %p)\n", infoPtr, hdc_ref); 02948 02949 if (hdc_ref) 02950 { 02951 hdc = hdc_ref; 02952 GetClientRect(infoPtr->hwnd, &rc); 02953 TREEVIEW_FillBkgnd(infoPtr, hdc, &rc); 02954 } 02955 else 02956 { 02957 hdc = BeginPaint(infoPtr->hwnd, &ps); 02958 rc = ps.rcPaint; 02959 if(ps.fErase) 02960 TREEVIEW_FillBkgnd(infoPtr, hdc, &rc); 02961 } 02962 02963 if(infoPtr->bRedraw) /* WM_SETREDRAW sets bRedraw */ 02964 TREEVIEW_Refresh(infoPtr, hdc, &rc); 02965 02966 if (!hdc_ref) 02967 EndPaint(infoPtr->hwnd, &ps); 02968 02969 return 0; 02970 } 02971 02972 static LRESULT 02973 TREEVIEW_PrintClient(TREEVIEW_INFO *infoPtr, HDC hdc, DWORD options) 02974 { 02975 FIXME("Partial Stub: (hdc=%p options=0x%08x)\n", hdc, options); 02976 02977 if ((options & PRF_CHECKVISIBLE) && !IsWindowVisible(infoPtr->hwnd)) 02978 return 0; 02979 02980 if (options & PRF_ERASEBKGND) 02981 TREEVIEW_EraseBackground(infoPtr, hdc); 02982 02983 if (options & PRF_CLIENT) 02984 { 02985 RECT rc; 02986 GetClientRect(infoPtr->hwnd, &rc); 02987 TREEVIEW_Refresh(infoPtr, hdc, &rc); 02988 } 02989 02990 return 0; 02991 } 02992 02993 /* Sorting **************************************************************/ 02994 02995 /*************************************************************************** 02996 * Forward the DPA local callback to the treeview owner callback 02997 */ 02998 static INT WINAPI 02999 TREEVIEW_CallBackCompare(const TREEVIEW_ITEM *first, const TREEVIEW_ITEM *second, 03000 const TVSORTCB *pCallBackSort) 03001 { 03002 /* Forward the call to the client-defined callback */ 03003 return pCallBackSort->lpfnCompare(first->lParam, 03004 second->lParam, 03005 pCallBackSort->lParam); 03006 } 03007 03008 /*************************************************************************** 03009 * Treeview native sort routine: sort on item text. 03010 */ 03011 static INT WINAPI 03012 TREEVIEW_SortOnName(TREEVIEW_ITEM *first, TREEVIEW_ITEM *second, 03013 const TREEVIEW_INFO *infoPtr) 03014 { 03015 TREEVIEW_UpdateDispInfo(infoPtr, first, TVIF_TEXT); 03016 TREEVIEW_UpdateDispInfo(infoPtr, second, TVIF_TEXT); 03017 03018 if(first->pszText && second->pszText) 03019 return lstrcmpiW(first->pszText, second->pszText); 03020 else if(first->pszText) 03021 return -1; 03022 else if(second->pszText) 03023 return 1; 03024 else 03025 return 0; 03026 } 03027 03028 /* Returns the number of physical children belonging to item. */ 03029 static INT 03030 TREEVIEW_CountChildren(const TREEVIEW_ITEM *item) 03031 { 03032 INT cChildren = 0; 03033 HTREEITEM hti; 03034 03035 for (hti = item->firstChild; hti != NULL; hti = hti->nextSibling) 03036 cChildren++; 03037 03038 return cChildren; 03039 } 03040 03041 /* Returns a DPA containing a pointer to each physical child of item in 03042 * sibling order. If item has no children, an empty DPA is returned. */ 03043 static HDPA 03044 TREEVIEW_BuildChildDPA(const TREEVIEW_ITEM *item) 03045 { 03046 HTREEITEM child; 03047 03048 HDPA list = DPA_Create(8); 03049 if (list == 0) return NULL; 03050 03051 for (child = item->firstChild; child != NULL; child = child->nextSibling) 03052 { 03053 if (DPA_InsertPtr(list, INT_MAX, child) == -1) 03054 { 03055 DPA_Destroy(list); 03056 return NULL; 03057 } 03058 } 03059 03060 return list; 03061 } 03062 03063 /*************************************************************************** 03064 * Setup the treeview structure with regards of the sort method 03065 * and sort the children of the TV item specified in lParam 03066 * fRecurse: currently unused. Should be zero. 03067 * parent: if pSort!=NULL, should equal pSort->hParent. 03068 * otherwise, item which child items are to be sorted. 03069 * pSort: sort method info. if NULL, sort on item text. 03070 * if non-NULL, sort on item's lParam content, and let the 03071 * application decide what that means. See also TVM_SORTCHILDRENCB. 03072 */ 03073 03074 static LRESULT 03075 TREEVIEW_Sort(TREEVIEW_INFO *infoPtr, HTREEITEM parent, 03076 LPTVSORTCB pSort) 03077 { 03078 INT cChildren; 03079 PFNDPACOMPARE pfnCompare; 03080 LPARAM lpCompare; 03081 03082 /* undocumented feature: TVI_ROOT or NULL means `sort the whole tree' */ 03083 if (parent == TVI_ROOT || parent == NULL) 03084 parent = infoPtr->root; 03085 03086 /* Check for a valid handle to the parent item */ 03087 if (!TREEVIEW_ValidItem(infoPtr, parent)) 03088 { 03089 ERR("invalid item hParent=%p\n", parent); 03090 return FALSE; 03091 } 03092 03093 if (pSort) 03094 { 03095 pfnCompare = (PFNDPACOMPARE)TREEVIEW_CallBackCompare; 03096 lpCompare = (LPARAM)pSort; 03097 } 03098 else 03099 { 03100 pfnCompare = (PFNDPACOMPARE)TREEVIEW_SortOnName; 03101 lpCompare = (LPARAM)infoPtr; 03102 } 03103 03104 cChildren = TREEVIEW_CountChildren(parent); 03105 03106 /* Make sure there is something to sort */ 03107 if (cChildren > 1) 03108 { 03109 /* TREEVIEW_ITEM rechaining */ 03110 INT count = 0; 03111 HTREEITEM item = 0; 03112 HTREEITEM nextItem = 0; 03113 HTREEITEM prevItem = 0; 03114 03115 HDPA sortList = TREEVIEW_BuildChildDPA(parent); 03116 03117 if (sortList == NULL) 03118 return FALSE; 03119 03120 /* let DPA sort the list */ 03121 DPA_Sort(sortList, pfnCompare, lpCompare); 03122 03123 /* The order of DPA entries has been changed, so fixup the 03124 * nextSibling and prevSibling pointers. */ 03125 03126 item = DPA_GetPtr(sortList, count++); 03127 while ((nextItem = DPA_GetPtr(sortList, count++)) != NULL) 03128 { 03129 /* link the two current item together */ 03130 item->nextSibling = nextItem; 03131 nextItem->prevSibling = item; 03132 03133 if (prevItem == NULL) 03134 { 03135 /* this is the first item, update the parent */ 03136 parent->firstChild = item; 03137 item->prevSibling = NULL; 03138 } 03139 else 03140 { 03141 /* fix the back chaining */ 03142 item->prevSibling = prevItem; 03143 } 03144 03145 /* get ready for the next one */ 03146 prevItem = item; 03147 item = nextItem; 03148 } 03149 03150 /* the last item is pointed to by item and never has a sibling */ 03151 item->nextSibling = NULL; 03152 parent->lastChild = item; 03153 03154 DPA_Destroy(sortList); 03155 03156 TREEVIEW_VerifyTree(infoPtr); 03157 03158 if (parent->state & TVIS_EXPANDED) 03159 { 03160 int visOrder = infoPtr->firstVisible->visibleOrder; 03161 03162 if (parent == infoPtr->root) 03163 TREEVIEW_RecalculateVisibleOrder(infoPtr, NULL); 03164 else 03165 TREEVIEW_RecalculateVisibleOrder(infoPtr, parent); 03166 03167 if (TREEVIEW_IsChildOf(parent, infoPtr->firstVisible)) 03168 { 03169 TREEVIEW_ITEM *item; 03170 03171 for (item = infoPtr->root->firstChild; item != NULL; 03172 item = TREEVIEW_GetNextListItem(infoPtr, item)) 03173 { 03174 if (item->visibleOrder == visOrder) 03175 break; 03176 } 03177 03178 if (!item) item = parent->firstChild; 03179 TREEVIEW_SetFirstVisible(infoPtr, item, FALSE); 03180 } 03181 03182 TREEVIEW_Invalidate(infoPtr, NULL); 03183 } 03184 03185 return TRUE; 03186 } 03187 return FALSE; 03188 } 03189 03190 03191 /*************************************************************************** 03192 * Setup the treeview structure with regards of the sort method 03193 * and sort the children of the TV item specified in lParam 03194 */ 03195 static LRESULT 03196 TREEVIEW_SortChildrenCB(TREEVIEW_INFO *infoPtr, LPTVSORTCB pSort) 03197 { 03198 return TREEVIEW_Sort(infoPtr, pSort->hParent, pSort); 03199 } 03200 03201 03202 /*************************************************************************** 03203 * Sort the children of the TV item specified in lParam. 03204 */ 03205 static LRESULT 03206 TREEVIEW_SortChildren(TREEVIEW_INFO *infoPtr, LPARAM lParam) 03207 { 03208 return TREEVIEW_Sort(infoPtr, (HTREEITEM)lParam, NULL); 03209 } 03210 03211 03212 /* Expansion/Collapse ***************************************************/ 03213 03214 static BOOL 03215 TREEVIEW_SendExpanding(const TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item, 03216 UINT action) 03217 { 03218 return !TREEVIEW_SendTreeviewNotify(infoPtr, TVN_ITEMEXPANDINGW, action, 03219 TVIF_HANDLE | TVIF_STATE | TVIF_PARAM 03220 | TVIF_IMAGE | TVIF_SELECTEDIMAGE, 03221 0, item); 03222 } 03223 03224 static VOID 03225 TREEVIEW_SendExpanded(const TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item, 03226 UINT action) 03227 { 03228 TREEVIEW_SendTreeviewNotify(infoPtr, TVN_ITEMEXPANDEDW, action, 03229 TVIF_HANDLE | TVIF_STATE | TVIF_PARAM 03230 | TVIF_IMAGE | TVIF_SELECTEDIMAGE, 03231 0, item); 03232 } 03233 03234 03235 /* This corresponds to TVM_EXPAND with TVE_COLLAPSE. 03236 * bRemoveChildren corresponds to TVE_COLLAPSERESET. */ 03237 static BOOL 03238 TREEVIEW_Collapse(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item, 03239 BOOL bRemoveChildren, BOOL bUser) 03240 { 03241 UINT action = TVE_COLLAPSE | (bRemoveChildren ? TVE_COLLAPSERESET : 0); 03242 BOOL bSetSelection, bSetFirstVisible; 03243 RECT scrollRect; 03244 LONG scrollDist = 0; 03245 TREEVIEW_ITEM *nextItem = NULL, *tmpItem; 03246 03247 TRACE("TVE_COLLAPSE %p %s\n", item, TREEVIEW_ItemName(item)); 03248 03249 if (!(item->state & TVIS_EXPANDED)) 03250 return FALSE; 03251 03252 if (bUser || !(item->state & TVIS_EXPANDEDONCE)) 03253 TREEVIEW_SendExpanding(infoPtr, item, action); 03254 03255 if (item->firstChild == NULL) 03256 return FALSE; 03257 03258 item->state &= ~TVIS_EXPANDED; 03259 03260 if (bUser || !(item->state & TVIS_EXPANDEDONCE)) 03261 TREEVIEW_SendExpanded(infoPtr, item, action); 03262 03263 bSetSelection = (infoPtr->selectedItem != NULL 03264 && TREEVIEW_IsChildOf(item, infoPtr->selectedItem)); 03265 03266 bSetFirstVisible = (infoPtr->firstVisible != NULL 03267 && TREEVIEW_IsChildOf(item, infoPtr->firstVisible)); 03268 03269 tmpItem = item; 03270 while (tmpItem) 03271 { 03272 if (tmpItem->nextSibling) 03273 { 03274 nextItem = tmpItem->nextSibling; 03275 break; 03276 } 03277 tmpItem = tmpItem->parent; 03278 } 03279 03280 if (nextItem) 03281 scrollDist = nextItem->rect.top; 03282 03283 if (bRemoveChildren) 03284 { 03285 INT old_cChildren = item->cChildren; 03286 TRACE("TVE_COLLAPSERESET\n"); 03287 item->state &= ~TVIS_EXPANDEDONCE; 03288 TREEVIEW_RemoveAllChildren(infoPtr, item); 03289 item->cChildren = old_cChildren; 03290 } 03291 03292 if (item->firstChild) 03293 { 03294 TREEVIEW_ITEM *i, *sibling; 03295 03296 sibling = TREEVIEW_GetNextListItem(infoPtr, item); 03297 03298 for (i = item->firstChild; i != sibling; 03299 i = TREEVIEW_GetNextListItem(infoPtr, i)) 03300 { 03301 i->visibleOrder = -1; 03302 } 03303 } 03304 03305 TREEVIEW_RecalculateVisibleOrder(infoPtr, item); 03306 03307 if (nextItem) 03308 scrollDist = -(scrollDist - nextItem->rect.top); 03309 03310 if (bSetSelection) 03311 { 03312 /* Don't call DoSelectItem, it sends notifications. */ 03313 if (TREEVIEW_ValidItem(infoPtr, infoPtr->selectedItem)) 03314 infoPtr->selectedItem->state &= ~TVIS_SELECTED; 03315 item->state |= TVIS_SELECTED; 03316 infoPtr->selectedItem = item; 03317 } 03318 03319 TREEVIEW_UpdateScrollBars(infoPtr); 03320 03321 scrollRect.left = 0; 03322 scrollRect.right = infoPtr->clientWidth; 03323 scrollRect.bottom = infoPtr->clientHeight; 03324 03325 if (nextItem) 03326 { 03327 scrollRect.top = nextItem->rect.top; 03328 03329 ScrollWindowEx (infoPtr->hwnd, 0, scrollDist, &scrollRect, &scrollRect, 03330 NULL, NULL, SW_ERASE | SW_INVALIDATE); 03331 TREEVIEW_Invalidate(infoPtr, item); 03332 } else { 03333 scrollRect.top = item->rect.top; 03334 InvalidateRect(infoPtr->hwnd, &scrollRect, TRUE); 03335 } 03336 03337 TREEVIEW_SetFirstVisible(infoPtr, 03338 bSetFirstVisible ? item : infoPtr->firstVisible, 03339 TRUE); 03340 03341 return TRUE; 03342 } 03343 03344 static BOOL 03345 TREEVIEW_Expand(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item, 03346 BOOL partial, BOOL user) 03347 { 03348 LONG scrollDist; 03349 LONG orgNextTop = 0; 03350 RECT scrollRect; 03351 TREEVIEW_ITEM *nextItem, *tmpItem; 03352 BOOL sendsNotifications; 03353 03354 TRACE("(%p, %p, partial=%d, %d\n", infoPtr, item, partial, user); 03355 03356 if (item->state & TVIS_EXPANDED) 03357 return TRUE; 03358 03359 tmpItem = item; nextItem = NULL; 03360 while (tmpItem) 03361 { 03362 if (tmpItem->nextSibling) 03363 { 03364 nextItem = tmpItem->nextSibling; 03365 break; 03366 } 03367 tmpItem = tmpItem->parent; 03368 } 03369 03370 if (nextItem) 03371 orgNextTop = nextItem->rect.top; 03372 03373 TRACE("TVE_EXPAND %p %s\n", item, TREEVIEW_ItemName(item)); 03374 03375 sendsNotifications = user || ((item->cChildren != 0) && 03376 !(item->state & TVIS_EXPANDEDONCE)); 03377 if (sendsNotifications) 03378 { 03379 if (!TREEVIEW_SendExpanding(infoPtr, item, TVE_EXPAND)) 03380 { 03381 TRACE(" TVN_ITEMEXPANDING returned TRUE, exiting...\n"); 03382 return FALSE; 03383 } 03384 } 03385 if (!item->firstChild) 03386 return FALSE; 03387 03388 item->state |= TVIS_EXPANDED; 03389 03390 if (partial) 03391 FIXME("TVE_EXPANDPARTIAL not implemented\n"); 03392 03393 if (ISVISIBLE(item)) 03394 { 03395 TREEVIEW_RecalculateVisibleOrder(infoPtr, item); 03396 TREEVIEW_UpdateSubTree(infoPtr, item); 03397 TREEVIEW_UpdateScrollBars(infoPtr); 03398 03399 scrollRect.left = 0; 03400 scrollRect.bottom = infoPtr->treeHeight; 03401 scrollRect.right = infoPtr->clientWidth; 03402 if (nextItem) 03403 { 03404 scrollDist = nextItem->rect.top - orgNextTop; 03405 scrollRect.top = orgNextTop; 03406 03407 ScrollWindowEx (infoPtr->hwnd, 0, scrollDist, &scrollRect, NULL, 03408 NULL, NULL, SW_ERASE | SW_INVALIDATE); 03409 TREEVIEW_Invalidate (infoPtr, item); 03410 } else { 03411 scrollRect.top = item->rect.top; 03412 InvalidateRect(infoPtr->hwnd, &scrollRect, FALSE); 03413 } 03414 03415 /* Scroll up so that as many children as possible are visible. 03416 * This fails when expanding causes an HScroll bar to appear, but we 03417 * don't know that yet, so the last item is obscured. */ 03418 if (item->firstChild != NULL) 03419 { 03420 int nChildren = item->lastChild->visibleOrder 03421 - item->firstChild->visibleOrder + 1; 03422 03423 int visible_pos = item->visibleOrder 03424 - infoPtr->firstVisible->visibleOrder; 03425 03426 int rows_below = TREEVIEW_GetVisibleCount(infoPtr) - visible_pos - 1; 03427 03428 if (visible_pos > 0 && nChildren > rows_below) 03429 { 03430 int scroll = nChildren - rows_below; 03431 03432 if (scroll > visible_pos) 03433 scroll = visible_pos; 03434 03435 if (scroll > 0) 03436 { 03437 TREEVIEW_ITEM *newFirstVisible 03438 = TREEVIEW_GetListItem(infoPtr, infoPtr->firstVisible, 03439 scroll); 03440 03441 03442 TREEVIEW_SetFirstVisible(infoPtr, newFirstVisible, TRUE); 03443 } 03444 } 03445 } 03446 } 03447 03448 if (sendsNotifications) { 03449 TREEVIEW_SendExpanded(infoPtr, item, TVE_EXPAND); 03450 item->state |= TVIS_EXPANDEDONCE; 03451 } 03452 03453 return TRUE; 03454 } 03455 03456 /* Handler for TVS_SINGLEEXPAND behaviour. Used on response 03457 to mouse messages and TVM_SELECTITEM. 03458 03459 selection - previously selected item, used to collapse a part of a tree 03460 item - new selected item 03461 */ 03462 static void TREEVIEW_SingleExpand(TREEVIEW_INFO *infoPtr, 03463 HTREEITEM selection, HTREEITEM item) 03464 { 03465 TREEVIEW_ITEM *SelItem; 03466 03467 if ((infoPtr->dwStyle & TVS_SINGLEEXPAND) == 0 || infoPtr->hwndEdit || !item) return; 03468 03469 TREEVIEW_SendTreeviewNotify(infoPtr, TVN_SINGLEEXPAND, TVC_UNKNOWN, TVIF_HANDLE | TVIF_PARAM, item, 0); 03470 03471 /* 03472 * Close the previous selection all the way to the root 03473 * as long as the new selection is not a child 03474 */ 03475 if(selection && (selection != item)) 03476 { 03477 BOOL closeit = TRUE; 03478 SelItem = item; 03479 03480 /* determine if the hitItem is a child of the currently selected item */ 03481 while(closeit && SelItem && TREEVIEW_ValidItem(infoPtr, SelItem) && 03482 (SelItem->parent != infoPtr->root)) 03483 { 03484 closeit = (SelItem != selection); 03485 SelItem = SelItem->parent; 03486 } 03487 03488 if(closeit) 03489 { 03490 if(TREEVIEW_ValidItem(infoPtr, selection)) 03491 SelItem = selection; 03492 03493 while(SelItem && (SelItem != item) && TREEVIEW_ValidItem(infoPtr, SelItem) && 03494 SelItem->parent != infoPtr->root) 03495 { 03496 TREEVIEW_Collapse(infoPtr, SelItem, FALSE, FALSE); 03497 SelItem = SelItem->parent; 03498 } 03499 } 03500 } 03501 03502 /* 03503 * Expand the current item 03504 */ 03505 TREEVIEW_Expand(infoPtr, item, FALSE, FALSE); 03506 } 03507 03508 static BOOL 03509 TREEVIEW_Toggle(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item, BOOL user) 03510 { 03511 TRACE("item=%p, user=%d\n", item, user); 03512 03513 if (item->state & TVIS_EXPANDED) 03514 return TREEVIEW_Collapse(infoPtr, item, FALSE, user); 03515 else 03516 return TREEVIEW_Expand(infoPtr, item, FALSE, user); 03517 } 03518 03519 static VOID 03520 TREEVIEW_ExpandAll(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item) 03521 { 03522 TREEVIEW_Expand(infoPtr, item, FALSE, TRUE); 03523 03524 for (item = item->firstChild; item != NULL; item = item->nextSibling) 03525 { 03526 if (TREEVIEW_HasChildren(infoPtr, item)) 03527 TREEVIEW_ExpandAll(infoPtr, item); 03528 } 03529 } 03530 03531 /* Note:If the specified item is the child of a collapsed parent item, 03532 the parent's list of child items is (recursively) expanded to reveal the 03533 specified item. This is mentioned for TREEVIEW_SelectItem; don't 03534 know if it also applies here. 03535 */ 03536 03537 static LRESULT 03538 TREEVIEW_ExpandMsg(TREEVIEW_INFO *infoPtr, UINT flag, HTREEITEM item) 03539 { 03540 if (!TREEVIEW_ValidItem(infoPtr, item)) 03541 return 0; 03542 03543 TRACE("For (%s) item:%d, flags 0x%x, state:%d\n", 03544 TREEVIEW_ItemName(item), TREEVIEW_GetItemIndex(infoPtr, item), 03545 flag, item->state); 03546 03547 switch (flag & TVE_TOGGLE) 03548 { 03549 case TVE_COLLAPSE: 03550 return TREEVIEW_Collapse(infoPtr, item, flag & TVE_COLLAPSERESET, 03551 FALSE); 03552 03553 case TVE_EXPAND: 03554 return TREEVIEW_Expand(infoPtr, item, flag & TVE_EXPANDPARTIAL, 03555 FALSE); 03556 03557 case TVE_TOGGLE: 03558 return TREEVIEW_Toggle(infoPtr, item, FALSE); 03559 03560 default: 03561 return 0; 03562 } 03563 } 03564 03565 /* Hit-Testing **********************************************************/ 03566 03567 static TREEVIEW_ITEM * 03568 TREEVIEW_HitTestPoint(const TREEVIEW_INFO *infoPtr, POINT pt) 03569 { 03570 TREEVIEW_ITEM *item; 03571 LONG row; 03572 03573 if (!infoPtr->firstVisible) 03574 return NULL; 03575 03576 row = pt.y / infoPtr->uItemHeight + infoPtr->firstVisible->visibleOrder; 03577 03578 for (item = infoPtr->firstVisible; item != NULL; 03579 item = TREEVIEW_GetNextListItem(infoPtr, item)) 03580 { 03581 if (row >= item->visibleOrder 03582 && row < item->visibleOrder + item->iIntegral) 03583 break; 03584 } 03585 03586 return item; 03587 } 03588 03589 static LRESULT 03590 TREEVIEW_HitTest(const TREEVIEW_INFO *infoPtr, LPTVHITTESTINFO lpht) 03591 { 03592 TREEVIEW_ITEM *item; 03593 RECT rect; 03594 UINT status; 03595 LONG x, y; 03596 03597 lpht->hItem = 0; 03598 GetClientRect(infoPtr->hwnd, &rect); 03599 status = 0; 03600 x = lpht->pt.x; 03601 y = lpht->pt.y; 03602 03603 if (x < rect.left) 03604 { 03605 status |= TVHT_TOLEFT; 03606 } 03607 else if (x > rect.right) 03608 { 03609 status |= TVHT_TORIGHT; 03610 } 03611 03612 if (y < rect.top) 03613 { 03614 status |= TVHT_ABOVE; 03615 } 03616 else if (y > rect.bottom) 03617 { 03618 status |= TVHT_BELOW; 03619 } 03620 03621 if (status) 03622 { 03623 lpht->flags = status; 03624 return 0; 03625 } 03626 03627 item = TREEVIEW_HitTestPoint(infoPtr, lpht->pt); 03628 if (!item) 03629 { 03630 lpht->flags = TVHT_NOWHERE; 03631 return 0; 03632 } 03633 03634 if (x >= item->textOffset + item->textWidth) 03635 { 03636 lpht->flags = TVHT_ONITEMRIGHT; 03637 } 03638 else if (x >= item->textOffset) 03639 { 03640 lpht->flags = TVHT_ONITEMLABEL; 03641 } 03642 else if (x >= item->imageOffset) 03643 { 03644 lpht->flags = TVHT_ONITEMICON; 03645 } 03646 else if (x >= item->stateOffset) 03647 { 03648 lpht->flags = TVHT_ONITEMSTATEICON; 03649 } 03650 else if (x >= item->linesOffset && infoPtr->dwStyle & TVS_HASBUTTONS) 03651 { 03652 lpht->flags = TVHT_ONITEMBUTTON; 03653 } 03654 else 03655 { 03656 lpht->flags = TVHT_ONITEMINDENT; 03657 } 03658 03659 lpht->hItem = item; 03660 TRACE("(%d,%d):result %x\n", lpht->pt.x, lpht->pt.y, lpht->flags); 03661 03662 return (LRESULT)item; 03663 } 03664 03665 /* Item Label Editing ***************************************************/ 03666 03667 static LRESULT 03668 TREEVIEW_GetEditControl(const TREEVIEW_INFO *infoPtr) 03669 { 03670 return (LRESULT)infoPtr->hwndEdit; 03671 } 03672 03673 static LRESULT CALLBACK 03674 TREEVIEW_Edit_SubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 03675 { 03676 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(GetParent(hwnd)); 03677 BOOL bCancel = FALSE; 03678 LRESULT rc; 03679 03680 switch (uMsg) 03681 { 03682 case WM_PAINT: 03683 TRACE("WM_PAINT start\n"); 03684 rc = CallWindowProcW(infoPtr->wpEditOrig, hwnd, uMsg, wParam, 03685 lParam); 03686 TRACE("WM_PAINT done\n"); 03687 return rc; 03688 03689 case WM_KILLFOCUS: 03690 if (infoPtr->bIgnoreEditKillFocus) 03691 return TRUE; 03692 break; 03693 03694 case WM_DESTROY: 03695 { 03696 WNDPROC editProc = infoPtr->wpEditOrig; 03697 infoPtr->wpEditOrig = 0; 03698 SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (DWORD_PTR)editProc); 03699 return CallWindowProcW(editProc, hwnd, uMsg, wParam, lParam); 03700 } 03701 03702 case WM_GETDLGCODE: 03703 return DLGC_WANTARROWS | DLGC_WANTALLKEYS; 03704 03705 case WM_KEYDOWN: 03706 if (wParam == VK_ESCAPE) 03707 { 03708 bCancel = TRUE; 03709 break; 03710 } 03711 else if (wParam == VK_RETURN) 03712 { 03713 break; 03714 } 03715 03716 /* fall through */ 03717 default: 03718 return CallWindowProcW(infoPtr->wpEditOrig, hwnd, uMsg, wParam, lParam); 03719 } 03720 03721 /* Processing TVN_ENDLABELEDIT message could kill the focus */ 03722 /* eg. Using a messagebox */ 03723 03724 infoPtr->bIgnoreEditKillFocus = TRUE; 03725 TREEVIEW_EndEditLabelNow(infoPtr, bCancel || !infoPtr->bLabelChanged); 03726 infoPtr->bIgnoreEditKillFocus = FALSE; 03727 03728 return 0; 03729 } 03730 03731 03732 /* should handle edit control messages here */ 03733 03734 static LRESULT 03735 TREEVIEW_Command(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam) 03736 { 03737 TRACE("code=%x, id=%x, handle=%lx\n", HIWORD(wParam), LOWORD(wParam), lParam); 03738 03739 switch (HIWORD(wParam)) 03740 { 03741 case EN_UPDATE: 03742 { 03743 /* 03744 * Adjust the edit window size 03745 */ 03746 WCHAR buffer[1024]; 03747 TREEVIEW_ITEM *editItem = infoPtr->editItem; 03748 HDC hdc = GetDC(infoPtr->hwndEdit); 03749 SIZE sz; 03750 HFONT hFont, hOldFont = 0; 03751 03752 TRACE("edit=%p\n", infoPtr->hwndEdit); 03753 03754 if (!IsWindow(infoPtr->hwndEdit) || !hdc) return FALSE; 03755 03756 infoPtr->bLabelChanged = TRUE; 03757 03758 GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0])); 03759 03760 /* Select font to get the right dimension of the string */ 03761 hFont = (HFONT)SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0); 03762 03763 if (hFont != 0) 03764 { 03765 hOldFont = SelectObject(hdc, hFont); 03766 } 03767 03768 if (GetTextExtentPoint32W(hdc, buffer, strlenW(buffer), &sz)) 03769 { 03770 TEXTMETRICW textMetric; 03771 03772 /* Add Extra spacing for the next character */ 03773 GetTextMetricsW(hdc, &textMetric); 03774 sz.cx += (textMetric.tmMaxCharWidth * 2); 03775 03776 sz.cx = max(sz.cx, textMetric.tmMaxCharWidth * 3); 03777 sz.cx = min(sz.cx, 03778 infoPtr->clientWidth - editItem->textOffset + 2); 03779 03780 SetWindowPos(infoPtr->hwndEdit, 03781 HWND_TOP, 03782 0, 03783 0, 03784 sz.cx, 03785 editItem->rect.bottom - editItem->rect.top + 3, 03786 SWP_NOMOVE | SWP_DRAWFRAME); 03787 } 03788 03789 if (hFont != 0) 03790 { 03791 SelectObject(hdc, hOldFont); 03792 } 03793 03794 ReleaseDC(infoPtr->hwnd, hdc); 03795 break; 03796 } 03797 case EN_KILLFOCUS: 03798 /* apparently we should respect passed handle value */ 03799 if (infoPtr->hwndEdit != (HWND)lParam) return FALSE; 03800 03801 TREEVIEW_EndEditLabelNow(infoPtr, FALSE); 03802 break; 03803 03804 default: 03805 return SendMessageW(infoPtr->hwndNotify, WM_COMMAND, wParam, lParam); 03806 } 03807 03808 return 0; 03809 } 03810 03811 static HWND 03812 TREEVIEW_EditLabel(TREEVIEW_INFO *infoPtr, HTREEITEM hItem) 03813 { 03814 HWND hwnd = infoPtr->hwnd; 03815 HWND hwndEdit; 03816 SIZE sz; 03817 HINSTANCE hinst = (HINSTANCE)GetWindowLongPtrW(hwnd, GWLP_HINSTANCE); 03818 HDC hdc; 03819 HFONT hOldFont=0; 03820 TEXTMETRICW textMetric; 03821 03822 TRACE("%p %p\n", hwnd, hItem); 03823 if (!TREEVIEW_ValidItem(infoPtr, hItem)) 03824 return NULL; 03825 03826 if (infoPtr->hwndEdit) 03827 return infoPtr->hwndEdit; 03828 03829 infoPtr->bLabelChanged = FALSE; 03830 03831 /* make edit item visible */ 03832 TREEVIEW_EnsureVisible(infoPtr, hItem, TRUE); 03833 03834 TREEVIEW_UpdateDispInfo(infoPtr, hItem, TVIF_TEXT); 03835 03836 hdc = GetDC(hwnd); 03837 /* Select the font to get appropriate metric dimensions */ 03838 if (infoPtr->hFont != 0) 03839 { 03840 hOldFont = SelectObject(hdc, infoPtr->hFont); 03841 } 03842 03843 /* Get string length in pixels */ 03844 if (hItem->pszText) 03845 GetTextExtentPoint32W(hdc, hItem->pszText, strlenW(hItem->pszText), 03846 &sz); 03847 else 03848 GetTextExtentPoint32A(hdc, "", 0, &sz); 03849 03850 /* Add Extra spacing for the next character */ 03851 GetTextMetricsW(hdc, &textMetric); 03852 sz.cx += (textMetric.tmMaxCharWidth * 2); 03853 03854 sz.cx = max(sz.cx, textMetric.tmMaxCharWidth * 3); 03855 sz.cx = min(sz.cx, infoPtr->clientWidth - hItem->textOffset + 2); 03856 03857 if (infoPtr->hFont != 0) 03858 { 03859 SelectObject(hdc, hOldFont); 03860 } 03861 03862 ReleaseDC(hwnd, hdc); 03863 03864 infoPtr->editItem = hItem; 03865 03866 hwndEdit = CreateWindowExW(WS_EX_LEFT, 03867 WC_EDITW, 03868 0, 03869 WS_CHILD | WS_BORDER | ES_AUTOHSCROLL | 03870 WS_CLIPSIBLINGS | ES_WANTRETURN | 03871 ES_LEFT, hItem->textOffset - 2, 03872 hItem->rect.top - 1, sz.cx + 3, 03873 hItem->rect.bottom - 03874 hItem->rect.top + 3, hwnd, 0, hinst, 0); 03875 /* FIXME: (HMENU)IDTVEDIT,pcs->hInstance,0); */ 03876 03877 infoPtr->hwndEdit = hwndEdit; 03878 03879 /* Get a 2D border. */ 03880 SetWindowLongW(hwndEdit, GWL_EXSTYLE, 03881 GetWindowLongW(hwndEdit, GWL_EXSTYLE) & ~WS_EX_CLIENTEDGE); 03882 SetWindowLongW(hwndEdit, GWL_STYLE, 03883 GetWindowLongW(hwndEdit, GWL_STYLE) | WS_BORDER); 03884 03885 SendMessageW(hwndEdit, WM_SETFONT, 03886 (WPARAM)TREEVIEW_FontForItem(infoPtr, hItem), FALSE); 03887 03888 infoPtr->wpEditOrig = (WNDPROC)SetWindowLongPtrW(hwndEdit, GWLP_WNDPROC, 03889 (DWORD_PTR) 03890 TREEVIEW_Edit_SubclassProc); 03891 if (hItem->pszText) 03892 SetWindowTextW(hwndEdit, hItem->pszText); 03893 03894 if (TREEVIEW_BeginLabelEditNotify(infoPtr, hItem)) 03895 { 03896 DestroyWindow(hwndEdit); 03897 infoPtr->hwndEdit = 0; 03898 infoPtr->editItem = NULL; 03899 return NULL; 03900 } 03901 03902 SetFocus(hwndEdit); 03903 SendMessageW(hwndEdit, EM_SETSEL, 0, -1); 03904 ShowWindow(hwndEdit, SW_SHOW); 03905 03906 return hwndEdit; 03907 } 03908 03909 03910 static LRESULT 03911 TREEVIEW_EndEditLabelNow(TREEVIEW_INFO *infoPtr, BOOL bCancel) 03912 { 03913 HWND hwnd = infoPtr->hwnd; 03914 TREEVIEW_ITEM *editedItem = infoPtr->editItem; 03915 NMTVDISPINFOW tvdi; 03916 BOOL bCommit; 03917 WCHAR tmpText[1024] = { '\0' }; 03918 WCHAR *newText = tmpText; 03919 int iLength = 0; 03920 03921 if (!IsWindow(infoPtr->hwndEdit)) return FALSE; 03922 03923 tvdi.hdr.hwndFrom = hwnd; 03924 tvdi.hdr.idFrom = GetWindowLongPtrW(hwnd, GWLP_ID); 03925 tvdi.hdr.code = get_notifycode(infoPtr, TVN_ENDLABELEDITW); 03926 tvdi.item.mask = 0; 03927 tvdi.item.hItem = editedItem; 03928 tvdi.item.state = editedItem->state; 03929 tvdi.item.lParam = editedItem->lParam; 03930 03931 if (!bCancel) 03932 { 03933 if (!infoPtr->bNtfUnicode) 03934 iLength = GetWindowTextA(infoPtr->hwndEdit, (LPSTR)tmpText, 1023); 03935 else 03936 iLength = GetWindowTextW(infoPtr->hwndEdit, tmpText, 1023); 03937 03938 if (iLength >= 1023) 03939 { 03940 ERR("Insufficient space to retrieve new item label\n"); 03941 } 03942 03943 tvdi.item.mask = TVIF_TEXT; 03944 tvdi.item.pszText = tmpText; 03945 tvdi.item.cchTextMax = iLength + 1; 03946 } 03947 else 03948 { 03949 tvdi.item.pszText = NULL; 03950 tvdi.item.cchTextMax = 0; 03951 } 03952 03953 bCommit = TREEVIEW_SendRealNotify(infoPtr, tvdi.hdr.idFrom, &tvdi.hdr); 03954 03955 if (!bCancel && bCommit) /* Apply the changes */ 03956 { 03957 if (!infoPtr->bNtfUnicode) 03958 { 03959 DWORD len = MultiByteToWideChar( CP_ACP, 0, (LPSTR)tmpText, -1, NULL, 0 ); 03960 newText = Alloc(len * sizeof(WCHAR)); 03961 MultiByteToWideChar( CP_ACP, 0, (LPSTR)tmpText, -1, newText, len ); 03962 iLength = len - 1; 03963 } 03964 03965 if (strcmpW(newText, editedItem->pszText) != 0) 03966 { 03967 WCHAR *ptr = ReAlloc(editedItem->pszText, sizeof(WCHAR)*(iLength + 1)); 03968 if (ptr == NULL) 03969 { 03970 ERR("OutOfMemory, cannot allocate space for label\n"); 03971 if(newText != tmpText) Free(newText); 03972 DestroyWindow(infoPtr->hwndEdit); 03973 infoPtr->hwndEdit = 0; 03974 infoPtr->editItem = NULL; 03975 return FALSE; 03976 } 03977 else 03978 { 03979 editedItem->pszText = ptr; 03980 editedItem->cchTextMax = iLength + 1; 03981 strcpyW(editedItem->pszText, newText); 03982 TREEVIEW_ComputeTextWidth(infoPtr, editedItem, 0); 03983 } 03984 } 03985 if(newText != tmpText) Free(newText); 03986 } 03987 03988 ShowWindow(infoPtr->hwndEdit, SW_HIDE); 03989 DestroyWindow(infoPtr->hwndEdit); 03990 infoPtr->hwndEdit = 0; 03991 infoPtr->editItem = NULL; 03992 return TRUE; 03993 } 03994 03995 static LRESULT 03996 TREEVIEW_HandleTimer(TREEVIEW_INFO *infoPtr, WPARAM wParam) 03997 { 03998 if (wParam != TV_EDIT_TIMER) 03999 { 04000 ERR("got unknown timer\n"); 04001 return 1; 04002 } 04003 04004 KillTimer(infoPtr->hwnd, TV_EDIT_TIMER); 04005 infoPtr->Timer &= ~TV_EDIT_TIMER_SET; 04006 04007 TREEVIEW_EditLabel(infoPtr, infoPtr->selectedItem); 04008 04009 return 0; 04010 } 04011 04012 04013 /* Mouse Tracking/Drag **************************************************/ 04014 04015 /*************************************************************************** 04016 * This is quite unusual piece of code, but that's how it's implemented in 04017 * Windows. 04018 */ 04019 static LRESULT 04020 TREEVIEW_TrackMouse(const TREEVIEW_INFO *infoPtr, POINT pt) 04021 { 04022 INT cxDrag = GetSystemMetrics(SM_CXDRAG); 04023 INT cyDrag = GetSystemMetrics(SM_CYDRAG); 04024 RECT r; 04025 MSG msg; 04026 04027 r.top = pt.y - cyDrag; 04028 r.left = pt.x - cxDrag; 04029 r.bottom = pt.y + cyDrag; 04030 r.right = pt.x + cxDrag; 04031 04032 SetCapture(infoPtr->hwnd); 04033 04034 while (1) 04035 { 04036 if (PeekMessageW(&msg, 0, 0, 0, PM_REMOVE | PM_NOYIELD)) 04037 { 04038 if (msg.message == WM_MOUSEMOVE) 04039 { 04040 pt.x = (short)LOWORD(msg.lParam); 04041 pt.y = (short)HIWORD(msg.lParam); 04042 if (PtInRect(&r, pt)) 04043 continue; 04044 else 04045 { 04046 ReleaseCapture(); 04047 return 1; 04048 } 04049 } 04050 else if (msg.message >= WM_LBUTTONDOWN && 04051 msg.message <= WM_RBUTTONDBLCLK) 04052 { 04053 if (msg.message == WM_RBUTTONUP) 04054 TREEVIEW_RButtonUp(infoPtr, &pt); 04055 break; 04056 } 04057 04058 DispatchMessageW(&msg); 04059 } 04060 04061 if (GetCapture() != infoPtr->hwnd) 04062 return 0; 04063 } 04064 04065 ReleaseCapture(); 04066 return 0; 04067 } 04068 04069 04070 static LRESULT 04071 TREEVIEW_LButtonDoubleClick(TREEVIEW_INFO *infoPtr, LPARAM lParam) 04072 { 04073 TREEVIEW_ITEM *item; 04074 TVHITTESTINFO hit; 04075 04076 TRACE("\n"); 04077 SetFocus(infoPtr->hwnd); 04078 04079 if (infoPtr->Timer & TV_EDIT_TIMER_SET) 04080 { 04081 /* If there is pending 'edit label' event - kill it now */ 04082 KillTimer(infoPtr->hwnd, TV_EDIT_TIMER); 04083 } 04084 04085 hit.pt.x = (short)LOWORD(lParam); 04086 hit.pt.y = (short)HIWORD(lParam); 04087 04088 item = (TREEVIEW_ITEM *)TREEVIEW_HitTest(infoPtr, &hit); 04089 if (!item) 04090 return 0; 04091 TRACE("item %d\n", TREEVIEW_GetItemIndex(infoPtr, item)); 04092 04093 if (TREEVIEW_SendSimpleNotify(infoPtr, NM_DBLCLK) == FALSE) 04094 { /* FIXME! */ 04095 switch (hit.flags) 04096 { 04097 case TVHT_ONITEMRIGHT: 04098 /* FIXME: we should not have sent NM_DBLCLK in this case. */ 04099 break; 04100 04101 case TVHT_ONITEMINDENT: 04102 if (!(infoPtr->dwStyle & TVS_HASLINES)) 04103 { 04104 break; 04105 } 04106 else 04107 { 04108 int level = hit.pt.x / infoPtr->uIndent; 04109 if (!(infoPtr->dwStyle & TVS_LINESATROOT)) level++; 04110 04111 while (item->iLevel > level) 04112 { 04113 item = item->parent; 04114 } 04115 04116 /* fall through */ 04117 } 04118 04119 case TVHT_ONITEMLABEL: 04120 case TVHT_ONITEMICON: 04121 case TVHT_ONITEMBUTTON: 04122 TREEVIEW_Toggle(infoPtr, item, TRUE); 04123 break; 04124 04125 case TVHT_ONITEMSTATEICON: 04126 if (infoPtr->dwStyle & TVS_CHECKBOXES) 04127 TREEVIEW_ToggleItemState(infoPtr, item); 04128 else 04129 TREEVIEW_Toggle(infoPtr, item, TRUE); 04130 break; 04131 } 04132 } 04133 return TRUE; 04134 } 04135 04136 04137 static LRESULT 04138 TREEVIEW_LButtonDown(TREEVIEW_INFO *infoPtr, LPARAM lParam) 04139 { 04140 HWND hwnd = infoPtr->hwnd; 04141 TVHITTESTINFO ht; 04142 BOOL bTrack, bDoLabelEdit; 04143 04144 /* If Edit control is active - kill it and return. 04145 * The best way to do it is to set focus to itself. 04146 * Edit control subclassed procedure will automatically call 04147 * EndEditLabelNow. 04148 */ 04149 if (infoPtr->hwndEdit) 04150 { 04151 SetFocus(hwnd); 04152 return 0; 04153 } 04154 04155 ht.pt.x = (short)LOWORD(lParam); 04156 ht.pt.y = (short)HIWORD(lParam); 04157 04158 TREEVIEW_HitTest(infoPtr, &ht); 04159 TRACE("item %d\n", TREEVIEW_GetItemIndex(infoPtr, ht.hItem)); 04160 04161 /* update focusedItem and redraw both items */ 04162 if(ht.hItem && (ht.flags & TVHT_ONITEM)) 04163 { 04164 infoPtr->focusedItem = ht.hItem; 04165 TREEVIEW_InvalidateItem(infoPtr, infoPtr->focusedItem); 04166 TREEVIEW_InvalidateItem(infoPtr, infoPtr->selectedItem); 04167 } 04168 04169 bTrack = (ht.flags & TVHT_ONITEM) 04170 && !(infoPtr->dwStyle & TVS_DISABLEDRAGDROP); 04171 04172 /* 04173 * If the style allows editing and the node is already selected 04174 * and the click occurred on the item label... 04175 */ 04176 bDoLabelEdit = (infoPtr->dwStyle & TVS_EDITLABELS) && 04177 (ht.flags & TVHT_ONITEMLABEL) && (infoPtr->selectedItem == ht.hItem); 04178 04179 /* Send NM_CLICK right away */ 04180 if (!bTrack) 04181 if (TREEVIEW_SendSimpleNotify(infoPtr, NM_CLICK)) 04182 goto setfocus; 04183 04184 if (ht.flags & TVHT_ONITEMBUTTON) 04185 { 04186 TREEVIEW_Toggle(infoPtr, ht.hItem, TRUE); 04187 goto setfocus; 04188 } 04189 else if (bTrack) 04190 { /* if TREEVIEW_TrackMouse == 1 dragging occurred and the cursor left the dragged item's rectangle */ 04191 if (TREEVIEW_TrackMouse(infoPtr, ht.pt)) 04192 { 04193 TREEVIEW_SendTreeviewDnDNotify(infoPtr, TVN_BEGINDRAGW, ht.hItem, ht.pt); 04194 infoPtr->dropItem = ht.hItem; 04195 04196 /* clean up focusedItem as we dragged and won't select this item */ 04197 if(infoPtr->focusedItem) 04198 { 04199 /* refresh the item that was focused */ 04200 TREEVIEW_InvalidateItem(infoPtr, infoPtr->focusedItem); 04201 infoPtr->focusedItem = NULL; 04202 04203 /* refresh the selected item to return the filled background */ 04204 TREEVIEW_InvalidateItem(infoPtr, infoPtr->selectedItem); 04205 } 04206 04207 return 0; 04208 } 04209 } 04210 04211 if (bTrack && TREEVIEW_SendSimpleNotify(infoPtr, NM_CLICK)) 04212 goto setfocus; 04213 04214 if (bDoLabelEdit) 04215 { 04216 if (infoPtr->Timer & TV_EDIT_TIMER_SET) 04217 KillTimer(hwnd, TV_EDIT_TIMER); 04218 04219 SetTimer(hwnd, TV_EDIT_TIMER, GetDoubleClickTime(), 0); 04220 infoPtr->Timer |= TV_EDIT_TIMER_SET; 04221 } 04222 else if (ht.flags & (TVHT_ONITEMICON|TVHT_ONITEMLABEL)) /* select the item if the hit was inside of the icon or text */ 04223 { 04224 TREEVIEW_ITEM *selection = infoPtr->selectedItem; 04225 04226 /* Select the current item */ 04227 TREEVIEW_DoSelectItem(infoPtr, TVGN_CARET, ht.hItem, TVC_BYMOUSE); 04228 TREEVIEW_SingleExpand(infoPtr, selection, ht.hItem); 04229 } 04230 else if (ht.flags & TVHT_ONITEMSTATEICON) 04231 { 04232 /* TVS_CHECKBOXES requires us to toggle the current state */ 04233 if (infoPtr->dwStyle & TVS_CHECKBOXES) 04234 TREEVIEW_ToggleItemState(infoPtr, ht.hItem); 04235 } 04236 04237 setfocus: 04238 SetFocus(hwnd); 04239 return 0; 04240 } 04241 04242 04243 static LRESULT 04244 TREEVIEW_RButtonDown(TREEVIEW_INFO *infoPtr, LPARAM lParam) 04245 { 04246 TVHITTESTINFO ht; 04247 04248 if (infoPtr->hwndEdit) 04249 { 04250 SetFocus(infoPtr->hwnd); 04251 return 0; 04252 } 04253 04254 ht.pt.x = (short)LOWORD(lParam); 04255 ht.pt.y = (short)HIWORD(lParam); 04256 04257 TREEVIEW_HitTest(infoPtr, &ht); 04258 04259 if (TREEVIEW_TrackMouse(infoPtr, ht.pt)) 04260 { 04261 if (ht.hItem) 04262 { 04263 TREEVIEW_SendTreeviewDnDNotify(infoPtr, TVN_BEGINRDRAGW, ht.hItem, ht.pt); 04264 infoPtr->dropItem = ht.hItem; 04265 } 04266 } 04267 else 04268 { 04269 SetFocus(infoPtr->hwnd); 04270 TREEVIEW_SendSimpleNotify(infoPtr, NM_RCLICK); 04271 } 04272 04273 return 0; 04274 } 04275 04276 static LRESULT 04277 TREEVIEW_RButtonUp(const TREEVIEW_INFO *infoPtr, const POINT *pPt) 04278 { 04279 TVHITTESTINFO ht; 04280 04281 ht.pt = *pPt; 04282 04283 TREEVIEW_HitTest(infoPtr, &ht); 04284 04285 if (ht.hItem) 04286 { 04287 /* Change to screen coordinate for WM_CONTEXTMENU */ 04288 ClientToScreen(infoPtr->hwnd, &ht.pt); 04289 04290 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */ 04291 SendMessageW(infoPtr->hwnd, WM_CONTEXTMENU, 04292 (WPARAM)infoPtr->hwnd, MAKELPARAM(ht.pt.x, ht.pt.y)); 04293 } 04294 return 0; 04295 } 04296 04297 04298 static LRESULT 04299 TREEVIEW_CreateDragImage(TREEVIEW_INFO *infoPtr, LPARAM lParam) 04300 { 04301 TREEVIEW_ITEM *dragItem = (HTREEITEM)lParam; 04302 INT cx, cy; 04303 HDC hdc, htopdc; 04304 HWND hwtop; 04305 HBITMAP hbmp, hOldbmp; 04306 SIZE size; 04307 RECT rc; 04308 HFONT hOldFont; 04309 04310 TRACE("\n"); 04311 04312 if (!(infoPtr->himlNormal)) 04313 return 0; 04314 04315 if (!dragItem || !TREEVIEW_ValidItem(infoPtr, dragItem)) 04316 return 0; 04317 04318 TREEVIEW_UpdateDispInfo(infoPtr, dragItem, TVIF_TEXT); 04319 04320 hwtop = GetDesktopWindow(); 04321 htopdc = GetDC(hwtop); 04322 hdc = CreateCompatibleDC(htopdc); 04323 04324 hOldFont = SelectObject(hdc, infoPtr->hFont); 04325 04326 if (dragItem->pszText) 04327 GetTextExtentPoint32W(hdc, dragItem->pszText, strlenW(dragItem->pszText), 04328 &size); 04329 else 04330 GetTextExtentPoint32A(hdc, "", 0, &size); 04331 04332 TRACE("%d %d %s\n", size.cx, size.cy, debugstr_w(dragItem->pszText)); 04333 hbmp = CreateCompatibleBitmap(htopdc, size.cx, size.cy); 04334 hOldbmp = SelectObject(hdc, hbmp); 04335 04336 ImageList_GetIconSize(infoPtr->himlNormal, &cx, &cy); 04337 size.cx += cx; 04338 if (cy > size.cy) 04339 size.cy = cy; 04340 04341 infoPtr->dragList = ImageList_Create(size.cx, size.cy, ILC_COLOR, 10, 10); 04342 ImageList_Draw(infoPtr->himlNormal, dragItem->iImage, hdc, 0, 0, 04343 ILD_NORMAL); 04344 04345 /* 04346 ImageList_GetImageInfo (infoPtr->himlNormal, dragItem->hItem, &iminfo); 04347 ImageList_AddMasked (infoPtr->dragList, iminfo.hbmImage, CLR_DEFAULT); 04348 */ 04349 04350 /* draw item text */ 04351 04352 SetRect(&rc, cx, 0, size.cx, size.cy); 04353 04354 if (dragItem->pszText) 04355 DrawTextW(hdc, dragItem->pszText, strlenW(dragItem->pszText), &rc, 04356 DT_LEFT); 04357 04358 SelectObject(hdc, hOldFont); 04359 SelectObject(hdc, hOldbmp); 04360 04361 ImageList_Add(infoPtr->dragList, hbmp, 0); 04362 04363 DeleteDC(hdc); 04364 DeleteObject(hbmp); 04365 ReleaseDC(hwtop, htopdc); 04366 04367 return (LRESULT)infoPtr->dragList; 04368 } 04369 04370 /* Selection ************************************************************/ 04371 04372 static LRESULT 04373 TREEVIEW_DoSelectItem(TREEVIEW_INFO *infoPtr, INT action, HTREEITEM newSelect, 04374 INT cause) 04375 { 04376 TREEVIEW_ITEM *prevSelect; 04377 04378 assert(newSelect == NULL || TREEVIEW_ValidItem(infoPtr, newSelect)); 04379 04380 TRACE("Entering item %p (%s), flag %x, cause %x, state %d\n", 04381 newSelect, TREEVIEW_ItemName(newSelect), action, cause, 04382 newSelect ? newSelect->state : 0); 04383 04384 /* reset and redraw focusedItem if focusedItem was set so we don't */ 04385 /* have to worry about the previously focused item when we set a new one */ 04386 TREEVIEW_InvalidateItem(infoPtr, infoPtr->focusedItem); 04387 infoPtr->focusedItem = NULL; 04388 04389 switch (action) 04390 { 04391 case TVGN_CARET|TVSI_NOSINGLEEXPAND: 04392 FIXME("TVSI_NOSINGLEEXPAND specified.\n"); 04393 /* Fall through */ 04394 case TVGN_CARET: 04395 prevSelect = infoPtr->selectedItem; 04396 04397 if (prevSelect == newSelect) { 04398 TREEVIEW_EnsureVisible(infoPtr, infoPtr->selectedItem, FALSE); 04399 break; 04400 } 04401 04402 if (TREEVIEW_SendTreeviewNotify(infoPtr, 04403 TVN_SELCHANGINGW, 04404 cause, 04405 TVIF_TEXT | TVIF_HANDLE | TVIF_STATE | TVIF_PARAM, 04406 prevSelect, 04407 newSelect)) 04408 return FALSE; 04409 04410 if (prevSelect) 04411 prevSelect->state &= ~TVIS_SELECTED; 04412 if (newSelect) 04413 newSelect->state |= TVIS_SELECTED; 04414 04415 infoPtr->selectedItem = newSelect; 04416 04417 TREEVIEW_EnsureVisible(infoPtr, infoPtr->selectedItem, FALSE); 04418 04419 TREEVIEW_InvalidateItem(infoPtr, prevSelect); 04420 TREEVIEW_InvalidateItem(infoPtr, newSelect); 04421 04422 TREEVIEW_SendTreeviewNotify(infoPtr, 04423 TVN_SELCHANGEDW, 04424 cause, 04425 TVIF_TEXT | TVIF_HANDLE | TVIF_STATE | TVIF_PARAM, 04426 prevSelect, 04427 newSelect); 04428 break; 04429 04430 case TVGN_DROPHILITE: 04431 prevSelect = infoPtr->dropItem; 04432 04433 if (prevSelect) 04434 prevSelect->state &= ~TVIS_DROPHILITED; 04435 04436 infoPtr->dropItem = newSelect; 04437 04438 if (newSelect) 04439 newSelect->state |= TVIS_DROPHILITED; 04440 04441 TREEVIEW_Invalidate(infoPtr, prevSelect); 04442 TREEVIEW_Invalidate(infoPtr, newSelect); 04443 break; 04444 04445 case TVGN_FIRSTVISIBLE: 04446 if (newSelect != NULL) 04447 { 04448 TREEVIEW_EnsureVisible(infoPtr, newSelect, FALSE); 04449 TREEVIEW_SetFirstVisible(infoPtr, newSelect, TRUE); 04450 TREEVIEW_Invalidate(infoPtr, NULL); 04451 } 04452 break; 04453 } 04454 04455 TRACE("Leaving state %d\n", newSelect ? newSelect->state : 0); 04456 return TRUE; 04457 } 04458 04459 /* FIXME: handle NM_KILLFOCUS etc */ 04460 static LRESULT 04461 TREEVIEW_SelectItem(TREEVIEW_INFO *infoPtr, INT wParam, HTREEITEM item) 04462 { 04463 TREEVIEW_ITEM *selection = infoPtr->selectedItem; 04464 04465 if (item && !TREEVIEW_ValidItem(infoPtr, item)) 04466 return FALSE; 04467 04468 TRACE("%p (%s) %d\n", item, TREEVIEW_ItemName(item), wParam); 04469 04470 if (!TREEVIEW_DoSelectItem(infoPtr, wParam, item, TVC_UNKNOWN)) 04471 return FALSE; 04472 04473 TREEVIEW_SingleExpand(infoPtr, selection, item); 04474 04475 return TRUE; 04476 } 04477 04478 /************************************************************************* 04479 * TREEVIEW_ProcessLetterKeys 04480 * 04481 * Processes keyboard messages generated by pressing the letter keys 04482 * on the keyboard. 04483 * What this does is perform a case insensitive search from the 04484 * current position with the following quirks: 04485 * - If two chars or more are pressed in quick succession we search 04486 * for the corresponding string (e.g. 'abc'). 04487 * - If there is a delay we wipe away the current search string and 04488 * restart with just that char. 04489 * - If the user keeps pressing the same character, whether slowly or 04490 * fast, so that the search string is entirely composed of this 04491 * character ('aaaaa' for instance), then we search for first item 04492 * that starting with that character. 04493 * - If the user types the above character in quick succession, then 04494 * we must also search for the corresponding string ('aaaaa'), and 04495 * go to that string if there is a match. 04496 * 04497 * RETURNS 04498 * 04499 * Zero. 04500 * 04501 * BUGS 04502 * 04503 * - The current implementation has a list of characters it will 04504 * accept and it ignores everything else. In particular it will 04505 * ignore accentuated characters which seems to match what 04506 * Windows does. But I'm not sure it makes sense to follow 04507 * Windows there. 04508 * - We don't sound a beep when the search fails. 04509 * - The search should start from the focused item, not from the selected 04510 * item. One reason for this is to allow for multiple selections in trees. 04511 * But currently infoPtr->focusedItem does not seem very usable. 04512 * 04513 * SEE ALSO 04514 * 04515 * TREEVIEW_ProcessLetterKeys 04516 */ 04517 static INT TREEVIEW_ProcessLetterKeys(TREEVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData) 04518 { 04519 HTREEITEM nItem; 04520 HTREEITEM endidx,idx; 04521 TVITEMEXW item; 04522 WCHAR buffer[MAX_PATH]; 04523 DWORD timestamp,elapsed; 04524 04525 /* simple parameter checking */ 04526 if (!charCode || !keyData) return 0; 04527 04528 /* only allow the valid WM_CHARs through */ 04529 if (!isalnum(charCode) && 04530 charCode != '.' && charCode != '`' && charCode != '!' && 04531 charCode != '@' && charCode != '#' && charCode != '$' && 04532 charCode != '%' && charCode != '^' && charCode != '&' && 04533 charCode != '*' && charCode != '(' && charCode != ')' && 04534 charCode != '-' && charCode != '_' && charCode != '+' && 04535 charCode != '=' && charCode != '\\'&& charCode != ']' && 04536 charCode != '}' && charCode != '[' && charCode != '{' && 04537 charCode != '/' && charCode != '?' && charCode != '>' && 04538 charCode != '<' && charCode != ',' && charCode != '~') 04539 return 0; 04540 04541 /* compute how much time elapsed since last keypress */ 04542 timestamp = GetTickCount(); 04543 if (timestamp > infoPtr->lastKeyPressTimestamp) { 04544 elapsed=timestamp-infoPtr->lastKeyPressTimestamp; 04545 } else { 04546 elapsed=infoPtr->lastKeyPressTimestamp-timestamp; 04547 } 04548 04549 /* update the search parameters */ 04550 infoPtr->lastKeyPressTimestamp=timestamp; 04551 if (elapsed < KEY_DELAY) { 04552 if (infoPtr->nSearchParamLength < sizeof(infoPtr->szSearchParam) / sizeof(WCHAR)) { 04553 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode; 04554 } 04555 if (infoPtr->charCode != charCode) { 04556 infoPtr->charCode=charCode=0; 04557 } 04558 } else { 04559 infoPtr->charCode=charCode; 04560 infoPtr->szSearchParam[0]=charCode; 04561 infoPtr->nSearchParamLength=1; 04562 /* Redundant with the 1 char string */ 04563 charCode=0; 04564 } 04565 04566 /* and search from the current position */ 04567 nItem=NULL; 04568 if (infoPtr->selectedItem != NULL) { 04569 endidx=infoPtr->selectedItem; 04570 /* if looking for single character match, 04571 * then we must always move forward 04572 */ 04573 if (infoPtr->nSearchParamLength == 1) 04574 idx=TREEVIEW_GetNextListItem(infoPtr,endidx); 04575 else 04576 idx=endidx; 04577 } else { 04578 endidx=NULL; 04579 idx=infoPtr->root->firstChild; 04580 } 04581 do { 04582 /* At the end point, sort out wrapping */ 04583 if (idx == NULL) { 04584 04585 /* If endidx is null, stop at the last item (ie top to bottom) */ 04586 if (endidx == NULL) 04587 break; 04588 04589 /* Otherwise, start again at the very beginning */ 04590 idx=infoPtr->root->firstChild; 04591 04592 /* But if we are stopping on the first child, end now! */ 04593 if (idx == endidx) break; 04594 } 04595 04596 /* get item */ 04597 ZeroMemory(&item, sizeof(item)); 04598 item.mask = TVIF_TEXT; 04599 item.hItem = idx; 04600 item.pszText = buffer; 04601 item.cchTextMax = sizeof(buffer); 04602 TREEVIEW_GetItemT( infoPtr, &item, TRUE ); 04603 04604 /* check for a match */ 04605 if (strncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) { 04606 nItem=idx; 04607 break; 04608 } else if ( (charCode != 0) && (nItem == NULL) && 04609 (nItem != infoPtr->selectedItem) && 04610 (strncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) { 04611 /* This would work but we must keep looking for a longer match */ 04612 nItem=idx; 04613 } 04614 idx=TREEVIEW_GetNextListItem(infoPtr,idx); 04615 } while (idx != endidx); 04616 04617 if (nItem != NULL) { 04618 if (TREEVIEW_DoSelectItem(infoPtr, TVGN_CARET, nItem, TVC_BYKEYBOARD)) { 04619 TREEVIEW_EnsureVisible(infoPtr, nItem, FALSE); 04620 } 04621 } 04622 04623 return 0; 04624 } 04625 04626 /* Scrolling ************************************************************/ 04627 04628 static LRESULT 04629 TREEVIEW_EnsureVisible(TREEVIEW_INFO *infoPtr, HTREEITEM item, BOOL bHScroll) 04630 { 04631 int viscount; 04632 BOOL hasFirstVisible = infoPtr->firstVisible != NULL; 04633 HTREEITEM newFirstVisible = NULL; 04634 int visible_pos = -1; 04635 04636 if (!TREEVIEW_ValidItem(infoPtr, item)) 04637 return FALSE; 04638 04639 if (!ISVISIBLE(item)) 04640 { 04641 /* Expand parents as necessary. */ 04642 HTREEITEM parent; 04643 04644 /* see if we are trying to ensure that root is visible */ 04645 if((item != infoPtr->root) && TREEVIEW_ValidItem(infoPtr, item)) 04646 parent = item->parent; 04647 else 04648 parent = item; /* this item is the topmost item */ 04649 04650 while (parent != infoPtr->root) 04651 { 04652 if (!(parent->state & TVIS_EXPANDED)) 04653 TREEVIEW_Expand(infoPtr, parent, FALSE, FALSE); 04654 04655 parent = parent->parent; 04656 } 04657 } 04658 04659 viscount = TREEVIEW_GetVisibleCount(infoPtr); 04660 04661 TRACE("%p (%s) %d - %d viscount(%d)\n", item, TREEVIEW_ItemName(item), item->visibleOrder, 04662 hasFirstVisible ? infoPtr->firstVisible->visibleOrder : -1, viscount); 04663 04664 if (hasFirstVisible) 04665 visible_pos = item->visibleOrder - infoPtr->firstVisible->visibleOrder; 04666 04667 if (visible_pos < 0) 04668 { 04669 /* item is before the start of the list: put it at the top. */ 04670 newFirstVisible = item; 04671 } 04672 else if (visible_pos >= viscount 04673 /* Sometimes, before we are displayed, GVC is 0, causing us to 04674 * spuriously scroll up. */ 04675 && visible_pos > 0 && !(infoPtr->dwStyle & TVS_NOSCROLL) ) 04676 { 04677 /* item is past the end of the list. */ 04678 int scroll = visible_pos - viscount; 04679 04680 newFirstVisible = TREEVIEW_GetListItem(infoPtr, infoPtr->firstVisible, 04681 scroll + 1); 04682 } 04683 04684 if (bHScroll) 04685 { 04686 /* Scroll window so item's text is visible as much as possible */ 04687 /* Calculation of amount of extra space is taken from EditLabel code */ 04688 INT pos, x; 04689 TEXTMETRICW textMetric; 04690 HDC hdc = GetWindowDC(infoPtr->hwnd); 04691 04692 x = item->textWidth; 04693 04694 GetTextMetricsW(hdc, &textMetric); 04695 ReleaseDC(infoPtr->hwnd, hdc); 04696 04697 x += (textMetric.tmMaxCharWidth * 2); 04698 x = max(x, textMetric.tmMaxCharWidth * 3); 04699 04700 if (item->textOffset < 0) 04701 pos = item->textOffset; 04702 else if (item->textOffset + x > infoPtr->clientWidth) 04703 { 04704 if (x > infoPtr->clientWidth) 04705 pos = item->textOffset; 04706 else 04707 pos = item->textOffset + x - infoPtr->clientWidth; 04708 } 04709 else 04710 pos = 0; 04711 04712 TREEVIEW_HScroll(infoPtr, MAKEWPARAM(SB_THUMBPOSITION, infoPtr->scrollX + pos)); 04713 } 04714 04715 if (newFirstVisible != NULL && newFirstVisible != infoPtr->firstVisible) 04716 { 04717 TREEVIEW_SetFirstVisible(infoPtr, newFirstVisible, TRUE); 04718 04719 return TRUE; 04720 } 04721 04722 return FALSE; 04723 } 04724 04725 static VOID 04726 TREEVIEW_SetFirstVisible(TREEVIEW_INFO *infoPtr, 04727 TREEVIEW_ITEM *newFirstVisible, 04728 BOOL bUpdateScrollPos) 04729 { 04730 int gap_size; 04731 04732 TRACE("%p: %s\n", newFirstVisible, TREEVIEW_ItemName(newFirstVisible)); 04733 04734 if (newFirstVisible != NULL) 04735 { 04736 /* Prevent an empty gap from appearing at the bottom... */ 04737 gap_size = TREEVIEW_GetVisibleCount(infoPtr) 04738 - infoPtr->maxVisibleOrder + newFirstVisible->visibleOrder; 04739 04740 if (gap_size > 0) 04741 { 04742 newFirstVisible = TREEVIEW_GetListItem(infoPtr, newFirstVisible, 04743 -gap_size); 04744 04745 /* ... unless we just don't have enough items. */ 04746 if (newFirstVisible == NULL) 04747 newFirstVisible = infoPtr->root->firstChild; 04748 } 04749 } 04750 04751 if (infoPtr->firstVisible != newFirstVisible) 04752 { 04753 if (infoPtr->firstVisible == NULL || newFirstVisible == NULL) 04754 { 04755 infoPtr->firstVisible = newFirstVisible; 04756 TREEVIEW_Invalidate(infoPtr, NULL); 04757 } 04758 else 04759 { 04760 TREEVIEW_ITEM *item; 04761 int scroll = infoPtr->uItemHeight * 04762 (infoPtr->firstVisible->visibleOrder 04763 - newFirstVisible->visibleOrder); 04764 04765 infoPtr->firstVisible = newFirstVisible; 04766 04767 for (item = infoPtr->root->firstChild; item != NULL; 04768 item = TREEVIEW_GetNextListItem(infoPtr, item)) 04769 { 04770 item->rect.top += scroll; 04771 item->rect.bottom += scroll; 04772 } 04773 04774 if (bUpdateScrollPos) 04775 SetScrollPos(infoPtr->hwnd, SB_VERT, 04776 newFirstVisible->visibleOrder, TRUE); 04777 04778 ScrollWindowEx(infoPtr->hwnd, 0, scroll, NULL, NULL, NULL, NULL, SW_ERASE | SW_INVALIDATE); 04779 } 04780 } 04781 } 04782 04783 /************************************************************************ 04784 * VScroll is always in units of visible items. i.e. we always have a 04785 * visible item aligned to the top of the control. (Unless we have no 04786 * items at all.) 04787 */ 04788 static LRESULT 04789 TREEVIEW_VScroll(TREEVIEW_INFO *infoPtr, WPARAM wParam) 04790 { 04791 TREEVIEW_ITEM *oldFirstVisible = infoPtr->firstVisible; 04792 TREEVIEW_ITEM *newFirstVisible = NULL; 04793 04794 int nScrollCode = LOWORD(wParam); 04795 04796 TRACE("wp %lx\n", wParam); 04797 04798 if (!(infoPtr->uInternalStatus & TV_VSCROLL)) 04799 return 0; 04800 04801 if (!oldFirstVisible) 04802 { 04803 assert(infoPtr->root->firstChild == NULL); 04804 return 0; 04805 } 04806 04807 switch (nScrollCode) 04808 { 04809 case SB_TOP: 04810 newFirstVisible = infoPtr->root->firstChild; 04811 break; 04812 04813 case SB_BOTTOM: 04814 newFirstVisible = TREEVIEW_GetLastListItem(infoPtr, infoPtr->root); 04815 break; 04816 04817 case SB_LINEUP: 04818 newFirstVisible = TREEVIEW_GetPrevListItem(infoPtr, oldFirstVisible); 04819 break; 04820 04821 case SB_LINEDOWN: 04822 newFirstVisible = TREEVIEW_GetNextListItem(infoPtr, oldFirstVisible); 04823 break; 04824 04825 case SB_PAGEUP: 04826 newFirstVisible = TREEVIEW_GetListItem(infoPtr, oldFirstVisible, 04827 -max(1, TREEVIEW_GetVisibleCount(infoPtr))); 04828 break; 04829 04830 case SB_PAGEDOWN: 04831 newFirstVisible = TREEVIEW_GetListItem(infoPtr, oldFirstVisible, 04832 max(1, TREEVIEW_GetVisibleCount(infoPtr))); 04833 break; 04834 04835 case SB_THUMBTRACK: 04836 case SB_THUMBPOSITION: 04837 newFirstVisible = TREEVIEW_GetListItem(infoPtr, 04838 infoPtr->root->firstChild, 04839 (LONG)(SHORT)HIWORD(wParam)); 04840 break; 04841 04842 case SB_ENDSCROLL: 04843 return 0; 04844 } 04845 04846 if (newFirstVisible != NULL) 04847 { 04848 if (newFirstVisible != oldFirstVisible) 04849 TREEVIEW_SetFirstVisible(infoPtr, newFirstVisible, 04850 nScrollCode != SB_THUMBTRACK); 04851 else if (nScrollCode == SB_THUMBPOSITION) 04852 SetScrollPos(infoPtr->hwnd, SB_VERT, 04853 newFirstVisible->visibleOrder, TRUE); 04854 } 04855 04856 return 0; 04857 } 04858 04859 static LRESULT 04860 TREEVIEW_HScroll(TREEVIEW_INFO *infoPtr, WPARAM wParam) 04861 { 04862 int maxWidth; 04863 int scrollX = infoPtr->scrollX; 04864 int nScrollCode = LOWORD(wParam); 04865 04866 TRACE("wp %lx\n", wParam); 04867 04868 if (!(infoPtr->uInternalStatus & TV_HSCROLL)) 04869 return FALSE; 04870 04871 maxWidth = infoPtr->treeWidth - infoPtr->clientWidth; 04872 /* shall never occur */ 04873 if (maxWidth <= 0) 04874 { 04875 scrollX = 0; 04876 goto scroll; 04877 } 04878 04879 switch (nScrollCode) 04880 { 04881 case SB_LINELEFT: 04882 scrollX -= infoPtr->uItemHeight; 04883 break; 04884 case SB_LINERIGHT: 04885 scrollX += infoPtr->uItemHeight; 04886 break; 04887 case SB_PAGELEFT: 04888 scrollX -= infoPtr->clientWidth; 04889 break; 04890 case SB_PAGERIGHT: 04891 scrollX += infoPtr->clientWidth; 04892 break; 04893 04894 case SB_THUMBTRACK: 04895 case SB_THUMBPOSITION: 04896 scrollX = (int)(SHORT)HIWORD(wParam); 04897 break; 04898 04899 case SB_ENDSCROLL: 04900 return 0; 04901 } 04902 04903 if (scrollX > maxWidth) 04904 scrollX = maxWidth; 04905 else if (scrollX < 0) 04906 scrollX = 0; 04907 04908 scroll: 04909 if (scrollX != infoPtr->scrollX) 04910 { 04911 TREEVIEW_ITEM *item; 04912 LONG scroll_pixels = infoPtr->scrollX - scrollX; 04913 04914 for (item = infoPtr->root->firstChild; item != NULL; 04915 item = TREEVIEW_GetNextListItem(infoPtr, item)) 04916 { 04917 item->linesOffset += scroll_pixels; 04918 item->stateOffset += scroll_pixels; 04919 item->imageOffset += scroll_pixels; 04920 item->textOffset += scroll_pixels; 04921 } 04922 04923 ScrollWindow(infoPtr->hwnd, scroll_pixels, 0, NULL, NULL); 04924 infoPtr->scrollX = scrollX; 04925 UpdateWindow(infoPtr->hwnd); 04926 } 04927 04928 if (nScrollCode != SB_THUMBTRACK) 04929 SetScrollPos(infoPtr->hwnd, SB_HORZ, scrollX, TRUE); 04930 04931 return 0; 04932 } 04933 04934 static LRESULT 04935 TREEVIEW_MouseWheel(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam) 04936 { 04937 short gcWheelDelta; 04938 UINT pulScrollLines = 3; 04939 04940 if (wParam & (MK_SHIFT | MK_CONTROL)) 04941 return DefWindowProcW(infoPtr->hwnd, WM_MOUSEWHEEL, wParam, lParam); 04942 04943 if (infoPtr->firstVisible == NULL) 04944 return TRUE; 04945 04946 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES, 0, &pulScrollLines, 0); 04947 04948 gcWheelDelta = -(short)HIWORD(wParam); 04949 pulScrollLines *= (gcWheelDelta / WHEEL_DELTA); 04950 04951 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines) 04952 { 04953 int newDy = infoPtr->firstVisible->visibleOrder + pulScrollLines; 04954 int maxDy = infoPtr->maxVisibleOrder; 04955 04956 if (newDy > maxDy) 04957 newDy = maxDy; 04958 04959 if (newDy < 0) 04960 newDy = 0; 04961 04962 TREEVIEW_VScroll(infoPtr, MAKEWPARAM(SB_THUMBPOSITION, newDy)); 04963 } 04964 return TRUE; 04965 } 04966 04967 /* Create/Destroy *******************************************************/ 04968 04969 static void 04970 TREEVIEW_InitCheckboxes(TREEVIEW_INFO *infoPtr) 04971 { 04972 RECT rc; 04973 HBITMAP hbm, hbmOld; 04974 HDC hdc, hdcScreen; 04975 int nIndex; 04976 04977 infoPtr->himlState = ImageList_Create(16, 16, ILC_COLOR | ILC_MASK, 3, 0); 04978 infoPtr->statehimlType = OriginInternal; 04979 04980 hdcScreen = GetDC(0); 04981 04982 hdc = CreateCompatibleDC(hdcScreen); 04983 hbm = CreateCompatibleBitmap(hdcScreen, 48, 16); 04984 hbmOld = SelectObject(hdc, hbm); 04985 04986 SetRect(&rc, 0, 0, 48, 16); 04987 FillRect(hdc, &rc, (HBRUSH)(COLOR_WINDOW+1)); 04988 04989 SetRect(&rc, 18, 2, 30, 14); 04990 DrawFrameControl(hdc, &rc, DFC_BUTTON, 04991 DFCS_BUTTONCHECK|DFCS_FLAT); 04992 04993 SetRect(&rc, 34, 2, 46, 14); 04994 DrawFrameControl(hdc, &rc, DFC_BUTTON, 04995 DFCS_BUTTONCHECK|DFCS_FLAT|DFCS_CHECKED); 04996 04997 SelectObject(hdc, hbmOld); 04998 nIndex = ImageList_AddMasked(infoPtr->himlState, hbm, 04999 comctl32_color.clrWindow); 05000 TRACE("checkbox index %d\n", nIndex); 05001 05002 DeleteObject(hbm); 05003 DeleteDC(hdc); 05004 ReleaseDC(0, hdcScreen); 05005 05006 infoPtr->stateImageWidth = 16; 05007 infoPtr->stateImageHeight = 16; 05008 } 05009 05010 static LRESULT 05011 TREEVIEW_Create(HWND hwnd, const CREATESTRUCTW *lpcs) 05012 { 05013 RECT rcClient; 05014 TREEVIEW_INFO *infoPtr; 05015 LOGFONTW lf; 05016 05017 TRACE("wnd %p, style %x\n", hwnd, GetWindowLongW(hwnd, GWL_STYLE)); 05018 05019 infoPtr = Alloc(sizeof(TREEVIEW_INFO)); 05020 05021 if (infoPtr == NULL) 05022 { 05023 ERR("could not allocate info memory!\n"); 05024 return 0; 05025 } 05026 05027 SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr); 05028 05029 infoPtr->hwnd = hwnd; 05030 infoPtr->dwStyle = GetWindowLongW(hwnd, GWL_STYLE); 05031 infoPtr->Timer = 0; 05032 infoPtr->uNumItems = 0; 05033 infoPtr->cdmode = 0; 05034 infoPtr->uScrollTime = 300; /* milliseconds */ 05035 infoPtr->bRedraw = TRUE; 05036 05037 GetClientRect(hwnd, &rcClient); 05038 05039 /* No scroll bars yet. */ 05040 infoPtr->clientWidth = rcClient.right; 05041 infoPtr->clientHeight = rcClient.bottom; 05042 infoPtr->uInternalStatus = 0; 05043 05044 infoPtr->treeWidth = 0; 05045 infoPtr->treeHeight = 0; 05046 05047 infoPtr->uIndent = MINIMUM_INDENT; 05048 infoPtr->selectedItem = NULL; 05049 infoPtr->focusedItem = NULL; 05050 infoPtr->hotItem = NULL; 05051 infoPtr->editItem = NULL; 05052 infoPtr->firstVisible = NULL; 05053 infoPtr->maxVisibleOrder = 0; 05054 infoPtr->dropItem = NULL; 05055 infoPtr->insertMarkItem = NULL; 05056 infoPtr->insertBeforeorAfter = 0; 05057 /* dragList */ 05058 05059 infoPtr->scrollX = 0; 05060 05061 infoPtr->clrBk = CLR_NONE; /* use system color */ 05062 infoPtr->clrText = CLR_NONE; /* use system color */ 05063 infoPtr->clrLine = CLR_DEFAULT; 05064 infoPtr->clrInsertMark = CLR_DEFAULT; 05065 05066 /* hwndToolTip */ 05067 05068 infoPtr->hwndEdit = NULL; 05069 infoPtr->wpEditOrig = NULL; 05070 infoPtr->bIgnoreEditKillFocus = FALSE; 05071 infoPtr->bLabelChanged = FALSE; 05072 05073 infoPtr->himlNormal = NULL; 05074 infoPtr->himlState = NULL; 05075 infoPtr->normalImageWidth = 0; 05076 infoPtr->normalImageHeight = 0; 05077 infoPtr->stateImageWidth = 0; 05078 infoPtr->stateImageHeight = 0; 05079 05080 infoPtr->items = DPA_Create(16); 05081 05082 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, sizeof(lf), &lf, 0); 05083 infoPtr->hFont = infoPtr->hDefaultFont = CreateFontIndirectW(&lf); 05084 infoPtr->hBoldFont = TREEVIEW_CreateBoldFont(infoPtr->hFont); 05085 infoPtr->hUnderlineFont = TREEVIEW_CreateUnderlineFont(infoPtr->hFont); 05086 infoPtr->hcurHand = LoadCursorW(NULL, (LPWSTR)IDC_HAND); 05087 05088 infoPtr->uItemHeight = TREEVIEW_NaturalHeight(infoPtr); 05089 05090 infoPtr->root = TREEVIEW_AllocateItem(infoPtr); 05091 infoPtr->root->state = TVIS_EXPANDED; 05092 infoPtr->root->iLevel = -1; 05093 infoPtr->root->visibleOrder = -1; 05094 05095 infoPtr->hwndNotify = lpcs->hwndParent; 05096 infoPtr->hwndToolTip = 0; 05097 05098 infoPtr->bNtfUnicode = IsWindowUnicode (hwnd); 05099 05100 /* Determine what type of notify should be issued */ 05101 /* sets infoPtr->bNtfUnicode */ 05102 TREEVIEW_NotifyFormat(infoPtr, infoPtr->hwndNotify, NF_REQUERY); 05103 05104 if (!(infoPtr->dwStyle & TVS_NOTOOLTIPS)) 05105 infoPtr->hwndToolTip = CreateWindowExW(0, TOOLTIPS_CLASSW, NULL, WS_POPUP, 05106 CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 05107 hwnd, 0, 0, 0); 05108 05109 if (infoPtr->dwStyle & TVS_CHECKBOXES) 05110 TREEVIEW_InitCheckboxes(infoPtr); 05111 05112 /* Make sure actual scrollbar state is consistent with uInternalStatus */ 05113 ShowScrollBar(hwnd, SB_VERT, FALSE); 05114 ShowScrollBar(hwnd, SB_HORZ, FALSE); 05115 05116 OpenThemeData (hwnd, themeClass); 05117 05118 return 0; 05119 } 05120 05121 05122 static LRESULT 05123 TREEVIEW_Destroy(TREEVIEW_INFO *infoPtr) 05124 { 05125 TRACE("\n"); 05126 05127 /* free item data */ 05128 TREEVIEW_RemoveTree(infoPtr); 05129 /* root isn't freed with other items */ 05130 TREEVIEW_FreeItem(infoPtr, infoPtr->root); 05131 DPA_Destroy(infoPtr->items); 05132 05133 /* tool tip is automatically destroyed: we are its owner */ 05134 05135 /* Restore original wndproc */ 05136 if (infoPtr->hwndEdit) 05137 SetWindowLongPtrW(infoPtr->hwndEdit, GWLP_WNDPROC, 05138 (DWORD_PTR)infoPtr->wpEditOrig); 05139 05140 CloseThemeData (GetWindowTheme (infoPtr->hwnd)); 05141 05142 if (infoPtr->statehimlType == OriginInternal) 05143 ImageList_Destroy(infoPtr->himlState); 05144 /* Deassociate treeview from the window before doing anything drastic. */ 05145 SetWindowLongPtrW(infoPtr->hwnd, 0, 0); 05146 05147 DeleteObject(infoPtr->hDefaultFont); 05148 DeleteObject(infoPtr->hBoldFont); 05149 DeleteObject(infoPtr->hUnderlineFont); 05150 Free(infoPtr); 05151 05152 return 0; 05153 } 05154 05155 /* Miscellaneous Messages ***********************************************/ 05156 05157 static LRESULT 05158 TREEVIEW_ScrollKeyDown(TREEVIEW_INFO *infoPtr, WPARAM key) 05159 { 05160 static const struct 05161 { 05162 unsigned char code; 05163 } 05164 scroll[] = 05165 { 05166 #define SCROLL_ENTRY(dir, code) { ((dir) << 7) | (code) } 05167 SCROLL_ENTRY(SB_VERT, SB_PAGEUP), /* VK_PRIOR */ 05168 SCROLL_ENTRY(SB_VERT, SB_PAGEDOWN), /* VK_NEXT */ 05169 SCROLL_ENTRY(SB_VERT, SB_BOTTOM), /* VK_END */ 05170 SCROLL_ENTRY(SB_VERT, SB_TOP), /* VK_HOME */ 05171 SCROLL_ENTRY(SB_HORZ, SB_LINEUP), /* VK_LEFT */ 05172 SCROLL_ENTRY(SB_VERT, SB_LINEUP), /* VK_UP */ 05173 SCROLL_ENTRY(SB_HORZ, SB_LINEDOWN), /* VK_RIGHT */ 05174 SCROLL_ENTRY(SB_VERT, SB_LINEDOWN) /* VK_DOWN */ 05175 #undef SCROLL_ENTRY 05176 }; 05177 05178 if (key >= VK_PRIOR && key <= VK_DOWN) 05179 { 05180 unsigned char code = scroll[key - VK_PRIOR].code; 05181 05182 (((code & (1 << 7)) == (SB_HORZ << 7)) 05183 ? TREEVIEW_HScroll 05184 : TREEVIEW_VScroll)(infoPtr, code & 0x7F); 05185 } 05186 05187 return 0; 05188 } 05189 05190 /************************************************************************ 05191 * TREEVIEW_KeyDown 05192 * 05193 * VK_UP Move selection to the previous non-hidden item. 05194 * VK_DOWN Move selection to the next non-hidden item. 05195 * VK_HOME Move selection to the first item. 05196 * VK_END Move selection to the last item. 05197 * VK_LEFT If expanded then collapse, otherwise move to parent. 05198 * VK_RIGHT If collapsed then expand, otherwise move to first child. 05199 * VK_ADD Expand. 05200 * VK_SUBTRACT Collapse. 05201 * VK_MULTIPLY Expand all. 05202 * VK_PRIOR Move up GetVisibleCount items. 05203 * VK_NEXT Move down GetVisibleCount items. 05204 * VK_BACK Move to parent. 05205 * CTRL-Left,Right,Up,Down,PgUp,PgDown,Home,End: Scroll without changing selection 05206 */ 05207 static LRESULT 05208 TREEVIEW_KeyDown(TREEVIEW_INFO *infoPtr, WPARAM wParam) 05209 { 05210 /* If it is non-NULL and different, it will be selected and visible. */ 05211 TREEVIEW_ITEM *newSelection = NULL; 05212 05213 TREEVIEW_ITEM *prevItem = infoPtr->selectedItem; 05214 05215 TRACE("%lx\n", wParam); 05216 05217 if (prevItem == NULL) 05218 return FALSE; 05219 05220 if (GetAsyncKeyState(VK_CONTROL) & 0x8000) 05221 return TREEVIEW_ScrollKeyDown(infoPtr, wParam); 05222 05223 switch (wParam) 05224 { 05225 case VK_UP: 05226 newSelection = TREEVIEW_GetPrevListItem(infoPtr, prevItem); 05227 if (!newSelection) 05228 newSelection = infoPtr->root->firstChild; 05229 break; 05230 05231 case VK_DOWN: 05232 newSelection = TREEVIEW_GetNextListItem(infoPtr, prevItem); 05233 break; 05234 05235 case VK_HOME: 05236 newSelection = infoPtr->root->firstChild; 05237 break; 05238 05239 case VK_END: 05240 newSelection = TREEVIEW_GetLastListItem(infoPtr, infoPtr->root); 05241 break; 05242 05243 case VK_LEFT: 05244 if (prevItem->state & TVIS_EXPANDED) 05245 { 05246 TREEVIEW_Collapse(infoPtr, prevItem, FALSE, TRUE); 05247 } 05248 else if (prevItem->parent != infoPtr->root) 05249 { 05250 newSelection = prevItem->parent; 05251 } 05252 break; 05253 05254 case VK_RIGHT: 05255 if (TREEVIEW_HasChildren(infoPtr, prevItem)) 05256 { 05257 if (!(prevItem->state & TVIS_EXPANDED)) 05258 TREEVIEW_Expand(infoPtr, prevItem, FALSE, TRUE); 05259 else 05260 { 05261 newSelection = prevItem->firstChild; 05262 } 05263 } 05264 05265 break; 05266 05267 case VK_MULTIPLY: 05268 TREEVIEW_ExpandAll(infoPtr, prevItem); 05269 break; 05270 05271 case VK_ADD: 05272 if (!(prevItem->state & TVIS_EXPANDED)) 05273 TREEVIEW_Expand(infoPtr, prevItem, FALSE, TRUE); 05274 break; 05275 05276 case VK_SUBTRACT: 05277 if (prevItem->state & TVIS_EXPANDED) 05278 TREEVIEW_Collapse(infoPtr, prevItem, FALSE, TRUE); 05279 break; 05280 05281 case VK_PRIOR: 05282 newSelection 05283 = TREEVIEW_GetListItem(infoPtr, prevItem, 05284 -TREEVIEW_GetVisibleCount(infoPtr)); 05285 break; 05286 05287 case VK_NEXT: 05288 newSelection 05289 = TREEVIEW_GetListItem(infoPtr, prevItem, 05290 TREEVIEW_GetVisibleCount(infoPtr)); 05291 break; 05292 05293 case VK_BACK: 05294 newSelection = prevItem->parent; 05295 if (newSelection == infoPtr->root) 05296 newSelection = NULL; 05297 break; 05298 05299 case VK_SPACE: 05300 if (infoPtr->dwStyle & TVS_CHECKBOXES) 05301 TREEVIEW_ToggleItemState(infoPtr, prevItem); 05302 break; 05303 } 05304 05305 if (newSelection && newSelection != prevItem) 05306 { 05307 if (TREEVIEW_DoSelectItem(infoPtr, TVGN_CARET, newSelection, 05308 TVC_BYKEYBOARD)) 05309 { 05310 TREEVIEW_EnsureVisible(infoPtr, newSelection, FALSE); 05311 } 05312 } 05313 05314 return FALSE; 05315 } 05316 05317 static LRESULT 05318 TREEVIEW_MouseLeave (TREEVIEW_INFO * infoPtr) 05319 { 05320 /* remove hot effect from item */ 05321 TREEVIEW_InvalidateItem(infoPtr, infoPtr->hotItem); 05322 infoPtr->hotItem = NULL; 05323 05324 return 0; 05325 } 05326 05327 static LRESULT 05328 TREEVIEW_MouseMove (TREEVIEW_INFO * infoPtr, LPARAM lParam) 05329 { 05330 POINT pt; 05331 TRACKMOUSEEVENT trackinfo; 05332 TREEVIEW_ITEM * item; 05333 05334 if (!(infoPtr->dwStyle & TVS_TRACKSELECT)) return 0; 05335 05336 /* fill in the TRACKMOUSEEVENT struct */ 05337 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT); 05338 trackinfo.dwFlags = TME_QUERY; 05339 trackinfo.hwndTrack = infoPtr->hwnd; 05340 05341 /* call _TrackMouseEvent to see if we are currently tracking for this hwnd */ 05342 _TrackMouseEvent(&trackinfo); 05343 05344 /* Make sure tracking is enabled so we receive a WM_MOUSELEAVE message */ 05345 if(!(trackinfo.dwFlags & TME_LEAVE)) 05346 { 05347 trackinfo.dwFlags = TME_LEAVE; /* notify upon leaving */ 05348 trackinfo.hwndTrack = infoPtr->hwnd; 05349 /* do it as fast as possible, minimal systimer latency will be used */ 05350 trackinfo.dwHoverTime = 1; 05351 05352 /* call TRACKMOUSEEVENT so we receive a WM_MOUSELEAVE message */ 05353 /* and can properly deactivate the hot item */ 05354 _TrackMouseEvent(&trackinfo); 05355 } 05356 05357 pt.x = (short)LOWORD(lParam); 05358 pt.y = (short)HIWORD(lParam); 05359 05360 item = TREEVIEW_HitTestPoint(infoPtr, pt); 05361 05362 if (item != infoPtr->hotItem) 05363 { 05364 /* redraw old hot item */ 05365 TREEVIEW_InvalidateItem(infoPtr, infoPtr->hotItem); 05366 infoPtr->hotItem = item; 05367 /* redraw new hot item */ 05368 TREEVIEW_InvalidateItem(infoPtr, infoPtr->hotItem); 05369 } 05370 05371 return 0; 05372 } 05373 05374 /* Draw themed border */ 05375 static BOOL TREEVIEW_NCPaint (const TREEVIEW_INFO *infoPtr, HRGN region, LPARAM lParam) 05376 { 05377 HTHEME theme = GetWindowTheme (infoPtr->hwnd); 05378 HDC dc; 05379 RECT r; 05380 HRGN cliprgn; 05381 int cxEdge = GetSystemMetrics (SM_CXEDGE), 05382 cyEdge = GetSystemMetrics (SM_CYEDGE); 05383 05384 if (!theme) 05385 return DefWindowProcW (infoPtr->hwnd, WM_NCPAINT, (WPARAM)region, lParam); 05386 05387 GetWindowRect(infoPtr->hwnd, &r); 05388 05389 cliprgn = CreateRectRgn (r.left + cxEdge, r.top + cyEdge, 05390 r.right - cxEdge, r.bottom - cyEdge); 05391 if (region != (HRGN)1) 05392 CombineRgn (cliprgn, cliprgn, region, RGN_AND); 05393 OffsetRect(&r, -r.left, -r.top); 05394 05395 dc = GetDCEx(infoPtr->hwnd, region, DCX_WINDOW|DCX_INTERSECTRGN); 05396 OffsetRect(&r, -r.left, -r.top); 05397 05398 if (IsThemeBackgroundPartiallyTransparent (theme, 0, 0)) 05399 DrawThemeParentBackground(infoPtr->hwnd, dc, &r); 05400 DrawThemeBackground (theme, dc, 0, 0, &r, 0); 05401 ReleaseDC(infoPtr->hwnd, dc); 05402 05403 /* Call default proc to get the scrollbars etc. painted */ 05404 DefWindowProcW (infoPtr->hwnd, WM_NCPAINT, (WPARAM)cliprgn, 0); 05405 05406 return TRUE; 05407 } 05408 05409 static LRESULT 05410 TREEVIEW_Notify(const TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam) 05411 { 05412 LPNMHDR lpnmh = (LPNMHDR)lParam; 05413 05414 if (lpnmh->code == PGN_CALCSIZE) { 05415 LPNMPGCALCSIZE lppgc = (LPNMPGCALCSIZE)lParam; 05416 05417 if (lppgc->dwFlag == PGF_CALCWIDTH) { 05418 lppgc->iWidth = infoPtr->treeWidth; 05419 TRACE("got PGN_CALCSIZE, returning horz size = %d, client=%d\n", 05420 infoPtr->treeWidth, infoPtr->clientWidth); 05421 } 05422 else { 05423 lppgc->iHeight = infoPtr->treeHeight; 05424 TRACE("got PGN_CALCSIZE, returning vert size = %d, client=%d\n", 05425 infoPtr->treeHeight, infoPtr->clientHeight); 05426 } 05427 return 0; 05428 } 05429 return DefWindowProcW(infoPtr->hwnd, WM_NOTIFY, wParam, lParam); 05430 } 05431 05432 static LRESULT 05433 TREEVIEW_Size(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam) 05434 { 05435 if (wParam == SIZE_RESTORED) 05436 { 05437 infoPtr->clientWidth = (short)LOWORD(lParam); 05438 infoPtr->clientHeight = (short)HIWORD(lParam); 05439 05440 TREEVIEW_RecalculateVisibleOrder(infoPtr, NULL); 05441 TREEVIEW_SetFirstVisible(infoPtr, infoPtr->firstVisible, TRUE); 05442 TREEVIEW_UpdateScrollBars(infoPtr); 05443 } 05444 else 05445 { 05446 FIXME("WM_SIZE flag %lx %lx not handled\n", wParam, lParam); 05447 } 05448 05449 TREEVIEW_Invalidate(infoPtr, NULL); 05450 return 0; 05451 } 05452 05453 static void TREEVIEW_ResetImageStateIndex(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item) 05454 { 05455 TREEVIEW_ITEM *child = item->firstChild; 05456 05457 item->state &= ~TVIS_STATEIMAGEMASK; 05458 item->state |= INDEXTOSTATEIMAGEMASK(1); 05459 05460 while (child) 05461 { 05462 TREEVIEW_ITEM *next = child->nextSibling; 05463 TREEVIEW_ResetImageStateIndex(infoPtr, child); 05464 child = next; 05465 } 05466 } 05467 05468 static LRESULT 05469 TREEVIEW_StyleChanged(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam) 05470 { 05471 TRACE("(%lx %lx)\n", wParam, lParam); 05472 05473 if (wParam == GWL_STYLE) 05474 { 05475 DWORD dwNewStyle = ((LPSTYLESTRUCT)lParam)->styleNew; 05476 05477 if ((infoPtr->dwStyle ^ dwNewStyle) & TVS_CHECKBOXES) 05478 { 05479 if (dwNewStyle & TVS_CHECKBOXES) 05480 { 05481 TREEVIEW_InitCheckboxes(infoPtr); 05482 TRACE("checkboxes enabled\n"); 05483 05484 /* set all items to state image index 1 */ 05485 TREEVIEW_ResetImageStateIndex(infoPtr, infoPtr->root); 05486 } 05487 else 05488 { 05489 FIXME("tried to disable checkboxes\n"); 05490 } 05491 } 05492 05493 if ((infoPtr->dwStyle ^ dwNewStyle) & TVS_NOTOOLTIPS) 05494 { 05495 if (infoPtr->dwStyle & TVS_NOTOOLTIPS) 05496 { 05497 infoPtr->hwndToolTip = COMCTL32_CreateToolTip(infoPtr->hwnd); 05498 TRACE("tooltips enabled\n"); 05499 } 05500 else 05501 { 05502 DestroyWindow(infoPtr->hwndToolTip); 05503 infoPtr->hwndToolTip = 0; 05504 TRACE("tooltips disabled\n"); 05505 } 05506 } 05507 05508 infoPtr->dwStyle = dwNewStyle; 05509 } 05510 05511 TREEVIEW_UpdateSubTree(infoPtr, infoPtr->root); 05512 TREEVIEW_UpdateScrollBars(infoPtr); 05513 TREEVIEW_Invalidate(infoPtr, NULL); 05514 05515 return 0; 05516 } 05517 05518 static LRESULT 05519 TREEVIEW_SetCursor(const TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam) 05520 { 05521 POINT pt; 05522 TREEVIEW_ITEM * item; 05523 NMMOUSE nmmouse; 05524 05525 GetCursorPos(&pt); 05526 ScreenToClient(infoPtr->hwnd, &pt); 05527 05528 item = TREEVIEW_HitTestPoint(infoPtr, pt); 05529 05530 memset(&nmmouse, 0, sizeof(nmmouse)); 05531 nmmouse.hdr.hwndFrom = infoPtr->hwnd; 05532 nmmouse.hdr.idFrom = GetWindowLongPtrW(infoPtr->hwnd, GWLP_ID); 05533 nmmouse.hdr.code = NM_SETCURSOR; 05534 if (item) 05535 { 05536 nmmouse.dwItemSpec = (DWORD_PTR)item; 05537 nmmouse.dwItemData = item->lParam; 05538 } 05539 nmmouse.pt.x = 0; 05540 nmmouse.pt.y = 0; 05541 nmmouse.dwHitInfo = lParam; 05542 if (TREEVIEW_SendRealNotify(infoPtr, nmmouse.hdr.idFrom, &nmmouse.hdr)) 05543 return 0; 05544 05545 if (item && (infoPtr->dwStyle & TVS_TRACKSELECT)) 05546 { 05547 SetCursor(infoPtr->hcurHand); 05548 return 0; 05549 } 05550 else 05551 return DefWindowProcW(infoPtr->hwnd, WM_SETCURSOR, wParam, lParam); 05552 } 05553 05554 static LRESULT 05555 TREEVIEW_SetFocus(TREEVIEW_INFO *infoPtr) 05556 { 05557 TRACE("\n"); 05558 05559 if (!infoPtr->selectedItem) 05560 { 05561 TREEVIEW_DoSelectItem(infoPtr, TVGN_CARET, infoPtr->firstVisible, 05562 TVC_UNKNOWN); 05563 } 05564 05565 TREEVIEW_Invalidate(infoPtr, infoPtr->selectedItem); 05566 TREEVIEW_SendSimpleNotify(infoPtr, NM_SETFOCUS); 05567 return 0; 05568 } 05569 05570 static LRESULT 05571 TREEVIEW_KillFocus(const TREEVIEW_INFO *infoPtr) 05572 { 05573 TRACE("\n"); 05574 05575 TREEVIEW_Invalidate(infoPtr, infoPtr->selectedItem); 05576 UpdateWindow(infoPtr->hwnd); 05577 TREEVIEW_SendSimpleNotify(infoPtr, NM_KILLFOCUS); 05578 return 0; 05579 } 05580 05581 /* update theme after a WM_THEMECHANGED message */ 05582 static LRESULT TREEVIEW_ThemeChanged(const TREEVIEW_INFO *infoPtr) 05583 { 05584 HTHEME theme = GetWindowTheme (infoPtr->hwnd); 05585 CloseThemeData (theme); 05586 OpenThemeData (infoPtr->hwnd, themeClass); 05587 return 0; 05588 } 05589 05590 05591 static LRESULT WINAPI 05592 TREEVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 05593 { 05594 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd); 05595 05596 TRACE("hwnd %p msg %04x wp=%08lx lp=%08lx\n", hwnd, uMsg, wParam, lParam); 05597 05598 if (infoPtr) TREEVIEW_VerifyTree(infoPtr); 05599 else 05600 { 05601 if (uMsg == WM_CREATE) 05602 TREEVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam); 05603 else 05604 goto def; 05605 } 05606 05607 switch (uMsg) 05608 { 05609 case TVM_CREATEDRAGIMAGE: 05610 return TREEVIEW_CreateDragImage(infoPtr, lParam); 05611 05612 case TVM_DELETEITEM: 05613 return TREEVIEW_DeleteItem(infoPtr, (HTREEITEM)lParam); 05614 05615 case TVM_EDITLABELA: 05616 case TVM_EDITLABELW: 05617 return (LRESULT)TREEVIEW_EditLabel(infoPtr, (HTREEITEM)lParam); 05618 05619 case TVM_ENDEDITLABELNOW: 05620 return TREEVIEW_EndEditLabelNow(infoPtr, (BOOL)wParam); 05621 05622 case TVM_ENSUREVISIBLE: 05623 return TREEVIEW_EnsureVisible(infoPtr, (HTREEITEM)lParam, TRUE); 05624 05625 case TVM_EXPAND: 05626 return TREEVIEW_ExpandMsg(infoPtr, (UINT)wParam, (HTREEITEM)lParam); 05627 05628 case TVM_GETBKCOLOR: 05629 return TREEVIEW_GetBkColor(infoPtr); 05630 05631 case TVM_GETCOUNT: 05632 return TREEVIEW_GetCount(infoPtr); 05633 05634 case TVM_GETEDITCONTROL: 05635 return TREEVIEW_GetEditControl(infoPtr); 05636 05637 case TVM_GETIMAGELIST: 05638 return TREEVIEW_GetImageList(infoPtr, wParam); 05639 05640 case TVM_GETINDENT: 05641 return TREEVIEW_GetIndent(infoPtr); 05642 05643 case TVM_GETINSERTMARKCOLOR: 05644 return TREEVIEW_GetInsertMarkColor(infoPtr); 05645 05646 case TVM_GETISEARCHSTRINGA: 05647 FIXME("Unimplemented msg TVM_GETISEARCHSTRINGA\n"); 05648 return 0; 05649 05650 case TVM_GETISEARCHSTRINGW: 05651 FIXME("Unimplemented msg TVM_GETISEARCHSTRINGW\n"); 05652 return 0; 05653 05654 case TVM_GETITEMA: 05655 case TVM_GETITEMW: 05656 return TREEVIEW_GetItemT(infoPtr, (LPTVITEMEXW)lParam, 05657 uMsg == TVM_GETITEMW); 05658 case TVM_GETITEMHEIGHT: 05659 return TREEVIEW_GetItemHeight(infoPtr); 05660 05661 case TVM_GETITEMRECT: 05662 return TREEVIEW_GetItemRect(infoPtr, (BOOL)wParam, (LPRECT)lParam); 05663 05664 case TVM_GETITEMSTATE: 05665 return TREEVIEW_GetItemState(infoPtr, (HTREEITEM)wParam, (UINT)lParam); 05666 05667 case TVM_GETLINECOLOR: 05668 return TREEVIEW_GetLineColor(infoPtr); 05669 05670 case TVM_GETNEXTITEM: 05671 return TREEVIEW_GetNextItem(infoPtr, (UINT)wParam, (HTREEITEM)lParam); 05672 05673 case TVM_GETSCROLLTIME: 05674 return TREEVIEW_GetScrollTime(infoPtr); 05675 05676 case TVM_GETTEXTCOLOR: 05677 return TREEVIEW_GetTextColor(infoPtr); 05678 05679 case TVM_GETTOOLTIPS: 05680 return TREEVIEW_GetToolTips(infoPtr); 05681 05682 case TVM_GETUNICODEFORMAT: 05683 return TREEVIEW_GetUnicodeFormat(infoPtr); 05684 05685 case TVM_GETVISIBLECOUNT: 05686 return TREEVIEW_GetVisibleCount(infoPtr); 05687 05688 case TVM_HITTEST: 05689 return TREEVIEW_HitTest(infoPtr, (LPTVHITTESTINFO)lParam); 05690 05691 case TVM_INSERTITEMA: 05692 case TVM_INSERTITEMW: 05693 return TREEVIEW_InsertItemT(infoPtr, (LPTVINSERTSTRUCTW)lParam, 05694 uMsg == TVM_INSERTITEMW); 05695 case TVM_SELECTITEM: 05696 return TREEVIEW_SelectItem(infoPtr, (INT)wParam, (HTREEITEM)lParam); 05697 05698 case TVM_SETBKCOLOR: 05699 return TREEVIEW_SetBkColor(infoPtr, (COLORREF)lParam); 05700 05701 case TVM_SETIMAGELIST: 05702 return TREEVIEW_SetImageList(infoPtr, wParam, (HIMAGELIST)lParam); 05703 05704 case TVM_SETINDENT: 05705 return TREEVIEW_SetIndent(infoPtr, (UINT)wParam); 05706 05707 case TVM_SETINSERTMARK: 05708 return TREEVIEW_SetInsertMark(infoPtr, (BOOL)wParam, (HTREEITEM)lParam); 05709 05710 case TVM_SETINSERTMARKCOLOR: 05711 return TREEVIEW_SetInsertMarkColor(infoPtr, (COLORREF)lParam); 05712 05713 case TVM_SETITEMA: 05714 case TVM_SETITEMW: 05715 return TREEVIEW_SetItemT(infoPtr, (LPTVITEMEXW)lParam, 05716 uMsg == TVM_SETITEMW); 05717 case TVM_SETLINECOLOR: 05718 return TREEVIEW_SetLineColor(infoPtr, (COLORREF)lParam); 05719 05720 case TVM_SETITEMHEIGHT: 05721 return TREEVIEW_SetItemHeight(infoPtr, (INT)(SHORT)wParam); 05722 05723 case TVM_SETSCROLLTIME: 05724 return TREEVIEW_SetScrollTime(infoPtr, (UINT)wParam); 05725 05726 case TVM_SETTEXTCOLOR: 05727 return TREEVIEW_SetTextColor(infoPtr, (COLORREF)lParam); 05728 05729 case TVM_SETTOOLTIPS: 05730 return TREEVIEW_SetToolTips(infoPtr, (HWND)wParam); 05731 05732 case TVM_SETUNICODEFORMAT: 05733 return TREEVIEW_SetUnicodeFormat(infoPtr, (BOOL)wParam); 05734 05735 case TVM_SORTCHILDREN: 05736 return TREEVIEW_SortChildren(infoPtr, lParam); 05737 05738 case TVM_SORTCHILDRENCB: 05739 return TREEVIEW_SortChildrenCB(infoPtr, (LPTVSORTCB)lParam); 05740 05741 case WM_CHAR: 05742 return TREEVIEW_ProcessLetterKeys(infoPtr, wParam, lParam); 05743 05744 case WM_COMMAND: 05745 return TREEVIEW_Command(infoPtr, wParam, lParam); 05746 05747 case WM_DESTROY: 05748 return TREEVIEW_Destroy(infoPtr); 05749 05750 /* WM_ENABLE */ 05751 05752 case WM_ERASEBKGND: 05753 return TREEVIEW_EraseBackground(infoPtr, (HDC)wParam); 05754 05755 case WM_GETDLGCODE: 05756 return DLGC_WANTARROWS | DLGC_WANTCHARS; 05757 05758 case WM_GETFONT: 05759 return TREEVIEW_GetFont(infoPtr); 05760 05761 case WM_HSCROLL: 05762 return TREEVIEW_HScroll(infoPtr, wParam); 05763 05764 case WM_KEYDOWN: 05765 return TREEVIEW_KeyDown(infoPtr, wParam); 05766 05767 case WM_KILLFOCUS: 05768 return TREEVIEW_KillFocus(infoPtr); 05769 05770 case WM_LBUTTONDBLCLK: 05771 return TREEVIEW_LButtonDoubleClick(infoPtr, lParam); 05772 05773 case WM_LBUTTONDOWN: 05774 return TREEVIEW_LButtonDown(infoPtr, lParam); 05775 05776 /* WM_MBUTTONDOWN */ 05777 05778 case WM_MOUSELEAVE: 05779 return TREEVIEW_MouseLeave(infoPtr); 05780 05781 case WM_MOUSEMOVE: 05782 return TREEVIEW_MouseMove(infoPtr, lParam); 05783 05784 case WM_NCLBUTTONDOWN: 05785 if (infoPtr->hwndEdit) 05786 SetFocus(infoPtr->hwnd); 05787 goto def; 05788 05789 case WM_NCPAINT: 05790 return TREEVIEW_NCPaint (infoPtr, (HRGN)wParam, lParam); 05791 05792 case WM_NOTIFY: 05793 return TREEVIEW_Notify(infoPtr, wParam, lParam); 05794 05795 case WM_NOTIFYFORMAT: 05796 return TREEVIEW_NotifyFormat(infoPtr, (HWND)wParam, (UINT)lParam); 05797 05798 case WM_PRINTCLIENT: 05799 return TREEVIEW_PrintClient(infoPtr, (HDC)wParam, lParam); 05800 05801 case WM_PAINT: 05802 return TREEVIEW_Paint(infoPtr, (HDC)wParam); 05803 05804 case WM_RBUTTONDOWN: 05805 return TREEVIEW_RButtonDown(infoPtr, lParam); 05806 05807 case WM_SETCURSOR: 05808 return TREEVIEW_SetCursor(infoPtr, wParam, lParam); 05809 05810 case WM_SETFOCUS: 05811 return TREEVIEW_SetFocus(infoPtr); 05812 05813 case WM_SETFONT: 05814 return TREEVIEW_SetFont(infoPtr, (HFONT)wParam, (BOOL)lParam); 05815 05816 case WM_SETREDRAW: 05817 return TREEVIEW_SetRedraw(infoPtr, wParam); 05818 05819 case WM_SIZE: 05820 return TREEVIEW_Size(infoPtr, wParam, lParam); 05821 05822 case WM_STYLECHANGED: 05823 return TREEVIEW_StyleChanged(infoPtr, wParam, lParam); 05824 05825 case WM_SYSCOLORCHANGE: 05826 COMCTL32_RefreshSysColors(); 05827 return 0; 05828 05829 /* WM_SYSKEYDOWN */ 05830 05831 case WM_TIMER: 05832 return TREEVIEW_HandleTimer(infoPtr, wParam); 05833 05834 case WM_THEMECHANGED: 05835 return TREEVIEW_ThemeChanged (infoPtr); 05836 05837 case WM_VSCROLL: 05838 return TREEVIEW_VScroll(infoPtr, wParam); 05839 05840 /* WM_WININICHANGE */ 05841 05842 case WM_MOUSEWHEEL: 05843 return TREEVIEW_MouseWheel(infoPtr, wParam, lParam); 05844 05845 case WM_DRAWITEM: 05846 TRACE("drawItem\n"); 05847 goto def; 05848 05849 default: 05850 /* This mostly catches MFC and Delphi messages. :( */ 05851 if ((uMsg >= WM_USER) && (uMsg < WM_APP) && !COMCTL32_IsReflectedMessage(uMsg)) 05852 TRACE("Unknown msg %04x wp=%08lx lp=%08lx\n", uMsg, wParam, lParam); 05853 def: 05854 return DefWindowProcW(hwnd, uMsg, wParam, lParam); 05855 } 05856 } 05857 05858 05859 /* Class Registration ***************************************************/ 05860 05861 VOID 05862 TREEVIEW_Register(void) 05863 { 05864 WNDCLASSW wndClass; 05865 05866 TRACE("\n"); 05867 05868 ZeroMemory(&wndClass, sizeof(WNDCLASSW)); 05869 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS; 05870 wndClass.lpfnWndProc = TREEVIEW_WindowProc; 05871 wndClass.cbClsExtra = 0; 05872 wndClass.cbWndExtra = sizeof(TREEVIEW_INFO *); 05873 05874 wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW); 05875 wndClass.hbrBackground = 0; 05876 wndClass.lpszClassName = WC_TREEVIEWW; 05877 05878 RegisterClassW(&wndClass); 05879 } 05880 05881 05882 VOID 05883 TREEVIEW_Unregister(void) 05884 { 05885 UnregisterClassW(WC_TREEVIEWW, NULL); 05886 } 05887 05888 05889 /* Tree Verification ****************************************************/ 05890 05891 static inline void 05892 TREEVIEW_VerifyChildren(TREEVIEW_INFO *infoPtr, const TREEVIEW_ITEM *item); 05893 05894 static inline void TREEVIEW_VerifyItemCommon(TREEVIEW_INFO *infoPtr, 05895 const TREEVIEW_ITEM *item) 05896 { 05897 assert(infoPtr != NULL); 05898 assert(item != NULL); 05899 05900 /* both NULL, or both non-null */ 05901 assert((item->firstChild == NULL) == (item->lastChild == NULL)); 05902 05903 assert(item->firstChild != item); 05904 assert(item->lastChild != item); 05905 05906 if (item->firstChild) 05907 { 05908 assert(item->firstChild->parent == item); 05909 assert(item->firstChild->prevSibling == NULL); 05910 } 05911 05912 if (item->lastChild) 05913 { 05914 assert(item->lastChild->parent == item); 05915 assert(item->lastChild->nextSibling == NULL); 05916 } 05917 05918 assert(item->nextSibling != item); 05919 if (item->nextSibling) 05920 { 05921 assert(item->nextSibling->parent == item->parent); 05922 assert(item->nextSibling->prevSibling == item); 05923 } 05924 05925 assert(item->prevSibling != item); 05926 if (item->prevSibling) 05927 { 05928 assert(item->prevSibling->parent == item->parent); 05929 assert(item->prevSibling->nextSibling == item); 05930 } 05931 } 05932 05933 static inline void 05934 TREEVIEW_VerifyItem(TREEVIEW_INFO *infoPtr, const TREEVIEW_ITEM *item) 05935 { 05936 assert(item != NULL); 05937 05938 assert(item->parent != NULL); 05939 assert(item->parent != item); 05940 assert(item->iLevel == item->parent->iLevel + 1); 05941 05942 assert(DPA_GetPtrIndex(infoPtr->items, item) != -1); 05943 05944 TREEVIEW_VerifyItemCommon(infoPtr, item); 05945 05946 TREEVIEW_VerifyChildren(infoPtr, item); 05947 } 05948 05949 static inline void 05950 TREEVIEW_VerifyChildren(TREEVIEW_INFO *infoPtr, const TREEVIEW_ITEM *item) 05951 { 05952 const TREEVIEW_ITEM *child; 05953 assert(item != NULL); 05954 05955 for (child = item->firstChild; child != NULL; child = child->nextSibling) 05956 TREEVIEW_VerifyItem(infoPtr, child); 05957 } 05958 05959 static inline void 05960 TREEVIEW_VerifyRoot(TREEVIEW_INFO *infoPtr) 05961 { 05962 TREEVIEW_ITEM *root = infoPtr->root; 05963 05964 assert(root != NULL); 05965 assert(root->iLevel == -1); 05966 assert(root->parent == NULL); 05967 assert(root->prevSibling == NULL); 05968 05969 TREEVIEW_VerifyItemCommon(infoPtr, root); 05970 05971 TREEVIEW_VerifyChildren(infoPtr, root); 05972 } 05973 05974 static void 05975 TREEVIEW_VerifyTree(TREEVIEW_INFO *infoPtr) 05976 { 05977 if (!TRACE_ON(treeview)) return; 05978 05979 assert(infoPtr != NULL); 05980 TREEVIEW_VerifyRoot(infoPtr); 05981 } Generated on Mon May 28 2012 04:17:25 for ReactOS by
1.7.6.1
|