ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

treeview.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 doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.