blob: cf04d4bcbe98c33be5534366458bbd4ea5b76199 [file] [log] [blame]
Alexandre Julliardd30dfd21998-09-27 18:28:36 +00001/* Treeview control
Alexandre Julliard642d3131998-07-12 19:29:36 +00002 *
Alexandre Julliardd30dfd21998-09-27 18:28:36 +00003 * Copyright 1998 Eric Kohl <ekohl@abo.rhein-zeitung.de>
Alex Priemd2d7f961999-01-30 13:02:30 +00004 * Copyright 1998,1999 Alex Priem <alexp@sci.kun.nl>
Sylvain St.Germain02300ae1999-03-16 10:38:44 +00005 * Copyright 1999 Sylvain St-Germain
Huw Davies5693ecd2003-11-26 03:37:30 +00006 * Copyright 2002 CodeWeavers, Aric Stewart
Alexandre Julliard642d3131998-07-12 19:29:36 +00007 *
Alexandre Julliard0799c1a2002-03-09 23:29:33 +00008 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
Dimitrie O. Pauna46b7fb2005-03-23 10:24:42 +000022 * NOTES
23 *
Chris Morgan5f9fd772000-09-13 20:27:30 +000024 * Note that TREEVIEW_INFO * and HTREEITEM are the same thing.
25 *
26 * Note2: All items always! have valid (allocated) pszText field.
27 * If item's text == LPSTR_TEXTCALLBACKA we allocate buffer
28 * of size TEXT_CALLBACK_SIZE in DoSetItem.
29 * We use callbackMask to keep track of fields to be updated.
Alexandre Julliard642d3131998-07-12 19:29:36 +000030 *
31 * TODO:
Chris Morgan5f9fd772000-09-13 20:27:30 +000032 * missing notifications: NM_SETCURSOR, TVN_GETINFOTIP, TVN_KEYDOWN,
33 * TVN_SETDISPINFO, TVN_SINGLEEXPAND
Alexandre Julliardd30dfd21998-09-27 18:28:36 +000034 *
Huw Davies7e1d83e2004-05-02 04:20:06 +000035 * missing styles: TVS_FULLROWSELECT, TVS_INFOTIP, TVS_RTLREADING,
Chris Morgan5f9fd772000-09-13 20:27:30 +000036 *
37 * missing item styles: TVIS_CUT, TVIS_EXPANDPARTIAL
38 *
39 * Make the insertion mark look right.
40 * Scroll (instead of repaint) as much as possible.
Alexandre Julliard642d3131998-07-12 19:29:36 +000041 */
42
Rolf Kalbermatter6c799302002-12-16 22:43:58 +000043#include "config.h"
44#include "wine/port.h"
45
Chris Morgan5f9fd772000-09-13 20:27:30 +000046#include <assert.h>
François Gouget2ad98552001-02-12 18:08:08 +000047#include <ctype.h>
Alexandre Julliarde37c6e12003-09-05 23:08:26 +000048#include <stdarg.h>
Marcus Meissnerd09136f1998-10-31 12:06:53 +000049#include <string.h>
Chris Morgan5f9fd772000-09-13 20:27:30 +000050#include <limits.h>
Alexandre Julliard908464d2000-11-01 03:11:12 +000051#include <stdlib.h>
52
Dimitrie O. Paun297f3d82003-01-07 20:36:20 +000053#define NONAMELESSUNION
54#define NONAMELESSSTRUCT
Alexandre Julliarde37c6e12003-09-05 23:08:26 +000055#include "windef.h"
Marcus Meissner317af321999-02-17 13:51:06 +000056#include "winbase.h"
Sylvain St.Germain90245261999-04-10 16:35:30 +000057#include "wingdi.h"
Alexandre Julliarde37c6e12003-09-05 23:08:26 +000058#include "winuser.h"
59#include "winnls.h"
Alexandre Julliard642d3131998-07-12 19:29:36 +000060#include "commctrl.h"
Alex Prieme6e71ad1999-11-28 19:50:01 +000061#include "comctl32.h"
Frank Richter171fea02005-07-28 10:15:10 +000062#include "uxtheme.h"
63#include "tmschema.h"
Huw Davies5693ecd2003-11-26 03:37:30 +000064#include "wine/unicode.h"
Alexandre Julliard0799c1a2002-03-09 23:29:33 +000065#include "wine/debug.h"
Brian Litzingerf64e2741998-10-22 14:47:41 +000066
Alexandre Julliard70c9e092000-08-09 00:41:17 +000067/* internal structures */
68
Chris Morgan5f9fd772000-09-13 20:27:30 +000069typedef struct _TREEITEM /* HTREEITEM is a _TREEINFO *. */
70{
71 UINT callbackMask;
Alexandre Julliard70c9e092000-08-09 00:41:17 +000072 UINT state;
73 UINT stateMask;
Huw Davies5693ecd2003-11-26 03:37:30 +000074 LPWSTR pszText;
Alexandre Julliard70c9e092000-08-09 00:41:17 +000075 int cchTextMax;
76 int iImage;
77 int iSelectedImage;
78 int cChildren;
79 LPARAM lParam;
Chris Morgan5f9fd772000-09-13 20:27:30 +000080 int iIntegral; /* item height multiplier (1 is normal) */
Alexandre Julliard70c9e092000-08-09 00:41:17 +000081 int iLevel; /* indentation level:0=root level */
Andreas Mohr8bc7f162002-02-27 01:34:08 +000082 HTREEITEM parent; /* handle to parent or 0 if at root */
83 HTREEITEM firstChild; /* handle to first child or 0 if no child */
Chris Morgan5f9fd772000-09-13 20:27:30 +000084 HTREEITEM lastChild;
85 HTREEITEM prevSibling; /* handle to prev item in list, 0 if first */
86 HTREEITEM nextSibling; /* handle to next item in list, 0 if last */
Alexandre Julliard70c9e092000-08-09 00:41:17 +000087 RECT rect;
Chris Morgan5f9fd772000-09-13 20:27:30 +000088 LONG linesOffset;
89 LONG stateOffset;
90 LONG imageOffset;
91 LONG textOffset;
92 LONG textWidth; /* horizontal text extent for pszText */
93 LONG visibleOrder; /* visible ordering, 0 is first visible item */
Alexandre Julliard70c9e092000-08-09 00:41:17 +000094} TREEVIEW_ITEM;
95
96
Chris Morgan5f9fd772000-09-13 20:27:30 +000097typedef struct tagTREEVIEW_INFO
Alexandre Julliard70c9e092000-08-09 00:41:17 +000098{
Chris Morgan5f9fd772000-09-13 20:27:30 +000099 HWND hwnd;
Uwe Bonnes4bfc2a82000-10-17 00:29:18 +0000100 HWND hwndNotify; /* Owner window to send notifications to */
Chris Morgan5f9fd772000-09-13 20:27:30 +0000101 DWORD dwStyle;
102 HTREEITEM root;
103 UINT uInternalStatus;
Alexandre Julliard70c9e092000-08-09 00:41:17 +0000104 INT Timer;
105 UINT uNumItems; /* number of valid TREEVIEW_ITEMs */
Alexandre Julliard70c9e092000-08-09 00:41:17 +0000106 INT cdmode; /* last custom draw setting */
Susan Farley036ae0b2000-10-13 20:13:38 +0000107 UINT uScrollTime; /* max. time for scrolling in milliseconds */
Jon Griffithsf479f102004-09-28 02:35:48 +0000108 BOOL bRedraw; /* if FALSE we validate but don't redraw in TREEVIEW_Paint() */
Chris Morgan5f9fd772000-09-13 20:27:30 +0000109
110 UINT uItemHeight; /* item height */
111 BOOL bHeightSet;
112
113 LONG clientWidth; /* width of control window */
114 LONG clientHeight; /* height of control window */
115
116 LONG treeWidth; /* width of visible tree items */
117 LONG treeHeight; /* height of visible tree items */
118
Alexandre Julliard70c9e092000-08-09 00:41:17 +0000119 UINT uIndent; /* indentation in pixels */
120 HTREEITEM selectedItem; /* handle to selected item or 0 if none */
Alexandre Julliard70c9e092000-08-09 00:41:17 +0000121 HTREEITEM hotItem; /* handle currently under cursor, 0 if none */
Chris Morgan5f9fd772000-09-13 20:27:30 +0000122 HTREEITEM focusedItem; /* item that was under the cursor when WM_LBUTTONDOWN was received */
123
Alexandre Julliard70c9e092000-08-09 00:41:17 +0000124 HTREEITEM firstVisible; /* handle to first visible item */
Chris Morgan5f9fd772000-09-13 20:27:30 +0000125 LONG maxVisibleOrder;
Alexandre Julliard70c9e092000-08-09 00:41:17 +0000126 HTREEITEM dropItem; /* handle to item selected by drag cursor */
127 HTREEITEM insertMarkItem; /* item after which insertion mark is placed */
Chris Morgan5f9fd772000-09-13 20:27:30 +0000128 BOOL insertBeforeorAfter; /* flag used by TVM_SETINSERTMARK */
Alexandre Julliard70c9e092000-08-09 00:41:17 +0000129 HIMAGELIST dragList; /* Bitmap of dragged item */
Chris Morgan5f9fd772000-09-13 20:27:30 +0000130 LONG scrollX;
131 COLORREF clrBk;
Alexandre Julliard70c9e092000-08-09 00:41:17 +0000132 COLORREF clrText;
133 COLORREF clrLine;
Chris Morgan5f9fd772000-09-13 20:27:30 +0000134 COLORREF clrInsertMark;
Alexandre Julliard70c9e092000-08-09 00:41:17 +0000135 HFONT hFont;
Huw Davies3138b4a2004-11-21 15:41:08 +0000136 HFONT hDefaultFont;
Alexandre Julliard70c9e092000-08-09 00:41:17 +0000137 HFONT hBoldFont;
Robert Shearman1e92f4a2004-12-17 18:52:04 +0000138 HFONT hUnderlineFont;
139 HCURSOR hcurHand;
Alexandre Julliard70c9e092000-08-09 00:41:17 +0000140 HWND hwndToolTip;
Chris Morgan5f9fd772000-09-13 20:27:30 +0000141
Alexandre Julliard70c9e092000-08-09 00:41:17 +0000142 HWND hwndEdit;
Chris Morgan5f9fd772000-09-13 20:27:30 +0000143 WNDPROC wpEditOrig; /* orig window proc for subclassing edit */
144 BOOL bIgnoreEditKillFocus;
145 BOOL bLabelChanged;
146
Guy L. Albertellib4994d12002-03-20 01:29:04 +0000147 BOOL bNtfUnicode; /* TRUE if should send NOTIFY with W */
Chris Morgan5f9fd772000-09-13 20:27:30 +0000148 HIMAGELIST himlNormal;
149 int normalImageHeight;
150 int normalImageWidth;
Alexandre Julliard70c9e092000-08-09 00:41:17 +0000151 HIMAGELIST himlState;
Chris Morgan5f9fd772000-09-13 20:27:30 +0000152 int stateImageHeight;
153 int stateImageWidth;
154 HDPA items;
François Gouget2ad98552001-02-12 18:08:08 +0000155
156 DWORD lastKeyPressTimestamp; /* Added */
157 WPARAM charCode; /* Added */
158 INT nSearchParamLength; /* Added */
Huw Davies5693ecd2003-11-26 03:37:30 +0000159 WCHAR szSearchParam[ MAX_PATH ]; /* Added */
Alexandre Julliard70c9e092000-08-09 00:41:17 +0000160} TREEVIEW_INFO;
161
162
François Gouget2ad98552001-02-12 18:08:08 +0000163/******** Defines that TREEVIEW_ProcessLetterKeys uses ****************/
164#define KEY_DELAY 450
Chris Morgan5f9fd772000-09-13 20:27:30 +0000165
Alexandre Julliard70c9e092000-08-09 00:41:17 +0000166/* bitflags for infoPtr->uInternalStatus */
167
168#define TV_HSCROLL 0x01 /* treeview too large to fit in window */
169#define TV_VSCROLL 0x02 /* (horizontal/vertical) */
170#define TV_LDRAG 0x04 /* Lbutton pushed to start drag */
Andreas Mohr8bc7f162002-02-27 01:34:08 +0000171#define TV_LDRAGGING 0x08 /* Lbutton pushed, mouse moved. */
Alexandre Julliard70c9e092000-08-09 00:41:17 +0000172#define TV_RDRAG 0x10 /* dito Rbutton */
Vincent Béron9a624912002-05-31 23:06:46 +0000173#define TV_RDRAGGING 0x20
Alexandre Julliard70c9e092000-08-09 00:41:17 +0000174
175/* bitflags for infoPtr->timer */
176
Alexandre Julliard70c9e092000-08-09 00:41:17 +0000177#define TV_EDIT_TIMER 2
Chris Morgan5f9fd772000-09-13 20:27:30 +0000178#define TV_EDIT_TIMER_SET 2
Alexandre Julliardd30dfd21998-09-27 18:28:36 +0000179
180
Chris Morgan5f9fd772000-09-13 20:27:30 +0000181VOID TREEVIEW_Register (VOID);
182VOID TREEVIEW_Unregister (VOID);
Alexandre Julliardd30dfd21998-09-27 18:28:36 +0000183
184
Alexandre Julliard0799c1a2002-03-09 23:29:33 +0000185WINE_DEFAULT_DEBUG_CHANNEL(treeview);
Alexandre Julliardd30dfd21998-09-27 18:28:36 +0000186
Alex Priemd2d7f961999-01-30 13:02:30 +0000187
Chris Morgan5f9fd772000-09-13 20:27:30 +0000188#define TEXT_CALLBACK_SIZE 260
189
190#define TREEVIEW_LEFT_MARGIN 8
191
192#define MINIMUM_INDENT 19
193
194#define CALLBACK_MASK_ALL (TVIF_TEXT|TVIF_CHILDREN|TVIF_IMAGE|TVIF_SELECTEDIMAGE)
195
196#define STATEIMAGEINDEX(x) (((x) >> 12) & 0x0f)
197#define OVERLAYIMAGEINDEX(x) (((x) >> 8) & 0x0f)
198#define ISVISIBLE(x) ((x)->visibleOrder >= 0)
199
200
Frank Richter171fea02005-07-28 10:15:10 +0000201static const WCHAR themeClass[] = { 'T','r','e','e','v','i','e','w',0 };
202
203
Chris Morgan5f9fd772000-09-13 20:27:30 +0000204typedef VOID (*TREEVIEW_ItemEnumFunc)(TREEVIEW_INFO *, TREEVIEW_ITEM *,LPVOID);
205
206
Susan Farley8d8c87b2000-10-25 20:30:58 +0000207static VOID TREEVIEW_Invalidate(TREEVIEW_INFO *, TREEVIEW_ITEM *);
Chris Morgan5f9fd772000-09-13 20:27:30 +0000208
209static LRESULT TREEVIEW_DoSelectItem(TREEVIEW_INFO *, INT, HTREEITEM, INT);
210static VOID TREEVIEW_SetFirstVisible(TREEVIEW_INFO *, TREEVIEW_ITEM *, BOOL);
211static LRESULT TREEVIEW_EnsureVisible(TREEVIEW_INFO *, HTREEITEM, BOOL);
212static LRESULT TREEVIEW_RButtonUp(TREEVIEW_INFO *, LPPOINT);
213static LRESULT TREEVIEW_EndEditLabelNow(TREEVIEW_INFO *infoPtr, BOOL bCancel);
214static VOID TREEVIEW_UpdateScrollBars(TREEVIEW_INFO *infoPtr);
215static LRESULT TREEVIEW_HScroll(TREEVIEW_INFO *, WPARAM);
Dimitrie O. Paund2e881d2002-11-27 20:15:49 +0000216static INT TREEVIEW_NotifyFormat (TREEVIEW_INFO *infoPtr, HWND wParam, UINT lParam);
Chris Morgan5f9fd772000-09-13 20:27:30 +0000217
218
219/* Random Utilities *****************************************************/
220
Susan Farley8d8c87b2000-10-25 20:30:58 +0000221#ifndef NDEBUG
Chris Morgan5f9fd772000-09-13 20:27:30 +0000222static inline void
223TREEVIEW_VerifyTree(TREEVIEW_INFO *infoPtr)
Alexandre Julliardd30dfd21998-09-27 18:28:36 +0000224{
Chris Morgan5f9fd772000-09-13 20:27:30 +0000225 (void)infoPtr;
226}
227#else
228/* The definition is at the end of the file. */
229static void TREEVIEW_VerifyTree(TREEVIEW_INFO *infoPtr);
230#endif
Alexandre Julliardd30dfd21998-09-27 18:28:36 +0000231
Chris Morgan5f9fd772000-09-13 20:27:30 +0000232/* Returns the treeview private data if hwnd is a treeview.
233 * Otherwise returns an undefined value. */
234static TREEVIEW_INFO *
235TREEVIEW_GetInfoPtr(HWND hwnd)
236{
Robert Shearmancdb263e2004-08-25 17:33:01 +0000237 return (TREEVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
Chris Morgan5f9fd772000-09-13 20:27:30 +0000238}
Sylvain St.Germain90245261999-04-10 16:35:30 +0000239
Chris Morgan5f9fd772000-09-13 20:27:30 +0000240/* Don't call this. Nothing wants an item index. */
241static inline int
242TREEVIEW_GetItemIndex(TREEVIEW_INFO *infoPtr, HTREEITEM handle)
243{
244 assert(infoPtr != NULL);
245
246 return DPA_GetPtrIndex(infoPtr->items, handle);
Alexandre Julliardd30dfd21998-09-27 18:28:36 +0000247}
248
Vitaliy Margolen71be0942005-04-18 10:20:51 +0000249/* Checks if item has changed and needs to be redrawn */
250static inline BOOL item_changed (TREEVIEW_ITEM *tiOld, TREEVIEW_ITEM *tiNew, LPTVITEMEXW tvChange)
251{
252 /* Number of children has changed */
253 if ((tvChange->mask & TVIF_CHILDREN) && (tiOld->cChildren != tiNew->cChildren))
254 return TRUE;
255
256 /* Image has changed and it's not a callback */
257 if ((tvChange->mask & TVIF_IMAGE) && (tiOld->iImage != tiNew->iImage) &&
258 tiNew->iImage != I_IMAGECALLBACK)
259 return TRUE;
260
261 /* Selected image has changed and it's not a callback */
262 if ((tvChange->mask & TVIF_SELECTEDIMAGE) && (tiOld->iSelectedImage != tiNew->iSelectedImage) &&
263 tiNew->iSelectedImage != I_IMAGECALLBACK)
264 return TRUE;
265
266 /* Text has changed and it's not a callback */
267 if ((tvChange->mask & TVIF_TEXT) && (tiOld->pszText != tiNew->pszText) &&
268 tiNew->pszText != LPSTR_TEXTCALLBACKW)
269 return TRUE;
270
271 /* Indent has changed */
272 if ((tvChange->mask & TVIF_INTEGRAL) && (tiOld->iIntegral != tiNew->iIntegral))
273 return TRUE;
274
275 /* Item state has changed */
276 if ((tvChange->mask & TVIF_STATE) && ((tiOld->state ^ tiNew->state) & tvChange->stateMask ))
277 return TRUE;
278
279 return FALSE;
280}
281
Sylvain St.Germain49c1ca21999-03-18 17:30:44 +0000282/***************************************************************************
Chris Morgan5f9fd772000-09-13 20:27:30 +0000283 * This method checks that handle is an item for this tree.
Sylvain St.Germain49c1ca21999-03-18 17:30:44 +0000284 */
Chris Morgan5f9fd772000-09-13 20:27:30 +0000285static BOOL
286TREEVIEW_ValidItem(TREEVIEW_INFO *infoPtr, HTREEITEM handle)
Alexandre Julliardd30dfd21998-09-27 18:28:36 +0000287{
Chris Morgan5f9fd772000-09-13 20:27:30 +0000288 if (TREEVIEW_GetItemIndex(infoPtr, handle) == -1)
289 {
290 TRACE("invalid item %p\n", handle);
291 return FALSE;
292 }
293 else
294 return TRUE;
295}
Alexandre Julliardd30dfd21998-09-27 18:28:36 +0000296
Chris Morgan5f9fd772000-09-13 20:27:30 +0000297static HFONT
298TREEVIEW_CreateBoldFont(HFONT hOrigFont)
299{
Huw Davies3138b4a2004-11-21 15:41:08 +0000300 LOGFONTW font;
Alexandre Julliardd30dfd21998-09-27 18:28:36 +0000301
Huw Davies3138b4a2004-11-21 15:41:08 +0000302 GetObjectW(hOrigFont, sizeof(font), &font);
Chris Morgan5f9fd772000-09-13 20:27:30 +0000303 font.lfWeight = FW_BOLD;
Huw Davies3138b4a2004-11-21 15:41:08 +0000304 return CreateFontIndirectW(&font);
Chris Morgan5f9fd772000-09-13 20:27:30 +0000305}
Alexandre Julliardd30dfd21998-09-27 18:28:36 +0000306
Robert Shearman1e92f4a2004-12-17 18:52:04 +0000307static HFONT
308TREEVIEW_CreateUnderlineFont(HFONT hOrigFont)
309{
310 LOGFONTW font;
311
312 GetObjectW(hOrigFont, sizeof(font), &font);
313 font.lfUnderline = TRUE;
314 return CreateFontIndirectW(&font);
315}
316
Chris Morgan5f9fd772000-09-13 20:27:30 +0000317static inline HFONT
318TREEVIEW_FontForItem(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item)
319{
Robert Shearman1e92f4a2004-12-17 18:52:04 +0000320 if ((infoPtr->dwStyle & TVS_TRACKSELECT) && (item == infoPtr->hotItem))
321 return infoPtr->hUnderlineFont;
322 if (item->state & TVIS_BOLD)
323 return infoPtr->hBoldFont;
324 return infoPtr->hFont;
Chris Morgan5f9fd772000-09-13 20:27:30 +0000325}
326
327/* for trace/debugging purposes only */
328static const char *
329TREEVIEW_ItemName(TREEVIEW_ITEM *item)
330{
331 if (item == NULL) return "<null item>";
Huw Davies5693ecd2003-11-26 03:37:30 +0000332 if (item->pszText == LPSTR_TEXTCALLBACKW) return "<callback>";
Chris Morgan5f9fd772000-09-13 20:27:30 +0000333 if (item->pszText == NULL) return "<null>";
Huw Davies5693ecd2003-11-26 03:37:30 +0000334 return debugstr_w(item->pszText);
Chris Morgan5f9fd772000-09-13 20:27:30 +0000335}
336
337/* An item is not a child of itself. */
338static BOOL
339TREEVIEW_IsChildOf(TREEVIEW_ITEM *parent, TREEVIEW_ITEM *child)
340{
341 do
342 {
343 child = child->parent;
344 if (child == parent) return TRUE;
345 } while (child != NULL);
346
347 return FALSE;
348}
349
350
351/* Tree Traversal *******************************************************/
352
353/***************************************************************************
354 * This method returns the last expanded sibling or child child item
355 * of a tree node
356 */
357static TREEVIEW_ITEM *
358TREEVIEW_GetLastListItem(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *wineItem)
359{
360 if (!wineItem)
361 return NULL;
362
363 while (wineItem->lastChild)
364 {
365 if (wineItem->state & TVIS_EXPANDED)
366 wineItem = wineItem->lastChild;
367 else
368 break;
369 }
370
371 if (wineItem == infoPtr->root)
372 return NULL;
373
374 return wineItem;
Sylvain St.Germain49c1ca21999-03-18 17:30:44 +0000375}
376
377/***************************************************************************
Vincent Béron9a624912002-05-31 23:06:46 +0000378 * This method returns the previous non-hidden item in the list not
Sylvain St.Germain49c1ca21999-03-18 17:30:44 +0000379 * considering the tree hierarchy.
380 */
Chris Morgan5f9fd772000-09-13 20:27:30 +0000381static TREEVIEW_ITEM *
382TREEVIEW_GetPrevListItem(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *tvItem)
Sylvain St.Germain49c1ca21999-03-18 17:30:44 +0000383{
Chris Morgan5f9fd772000-09-13 20:27:30 +0000384 if (tvItem->prevSibling)
385 {
386 /* This item has a prevSibling, get the last item in the sibling's tree. */
387 TREEVIEW_ITEM *upItem = tvItem->prevSibling;
Sylvain St.Germain49c1ca21999-03-18 17:30:44 +0000388
Chris Morgan5f9fd772000-09-13 20:27:30 +0000389 if ((upItem->state & TVIS_EXPANDED) && upItem->lastChild != NULL)
390 return TREEVIEW_GetLastListItem(infoPtr, upItem->lastChild);
391 else
392 return upItem;
393 }
394 else
395 {
396 /* this item does not have a prevSibling, get the parent */
397 return (tvItem->parent != infoPtr->root) ? tvItem->parent : NULL;
398 }
Alexandre Julliardd30dfd21998-09-27 18:28:36 +0000399}
400
Alex Priem65b637b1998-12-10 09:54:17 +0000401
Sylvain St.Germain49c1ca21999-03-18 17:30:44 +0000402/***************************************************************************
Vincent Béron9a624912002-05-31 23:06:46 +0000403 * This method returns the next physical item in the treeview not
Sylvain St.Germain49c1ca21999-03-18 17:30:44 +0000404 * considering the tree hierarchy.
405 */
Chris Morgan5f9fd772000-09-13 20:27:30 +0000406static TREEVIEW_ITEM *
407TREEVIEW_GetNextListItem(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *tvItem)
Alexandre Julliardd30dfd21998-09-27 18:28:36 +0000408{
Chris Morgan5f9fd772000-09-13 20:27:30 +0000409 assert(tvItem != NULL);
Alexandre Julliardd30dfd21998-09-27 18:28:36 +0000410
Vincent Béron9a624912002-05-31 23:06:46 +0000411 /*
Chris Morgan5f9fd772000-09-13 20:27:30 +0000412 * If this item has children and is expanded, return the first child
413 */
414 if ((tvItem->state & TVIS_EXPANDED) && tvItem->firstChild != NULL)
415 {
416 return tvItem->firstChild;
417 }
Alex Priem65b637b1998-12-10 09:54:17 +0000418
Sylvain St.Germain99b118a1999-03-25 10:55:01 +0000419
Chris Morgan5f9fd772000-09-13 20:27:30 +0000420 /*
421 * try to get the sibling
422 */
423 if (tvItem->nextSibling)
424 return tvItem->nextSibling;
Alexandre Julliardd30dfd21998-09-27 18:28:36 +0000425
Chris Morgan5f9fd772000-09-13 20:27:30 +0000426 /*
427 * Otherwise, get the parent's sibling.
428 */
429 while (tvItem->parent)
430 {
431 tvItem = tvItem->parent;
Alexandre Julliardd30dfd21998-09-27 18:28:36 +0000432
Chris Morgan5f9fd772000-09-13 20:27:30 +0000433 if (tvItem->nextSibling)
434 return tvItem->nextSibling;
435 }
436
437 return NULL;
Alexandre Julliardd30dfd21998-09-27 18:28:36 +0000438}
439
Sylvain St.Germain49c1ca21999-03-18 17:30:44 +0000440/***************************************************************************
Vincent Béron9a624912002-05-31 23:06:46 +0000441 * This method returns the nth item starting at the given item. It returns
Sylvain St.Germain49c1ca21999-03-18 17:30:44 +0000442 * the last item (or first) we we run out of items.
443 *
444 * Will scroll backward if count is <0.
445 * forward if count is >0.
446 */
Chris Morgan5f9fd772000-09-13 20:27:30 +0000447static TREEVIEW_ITEM *
448TREEVIEW_GetListItem(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *wineItem,
449 LONG count)
Alexandre Julliardd30dfd21998-09-27 18:28:36 +0000450{
Chris Morgan5f9fd772000-09-13 20:27:30 +0000451 TREEVIEW_ITEM *(*next_item)(TREEVIEW_INFO *, TREEVIEW_ITEM *);
452 TREEVIEW_ITEM *previousItem;
Alex Priemd2d7f961999-01-30 13:02:30 +0000453
Chris Morgan5f9fd772000-09-13 20:27:30 +0000454 assert(wineItem != NULL);
Alexandre Julliardd30dfd21998-09-27 18:28:36 +0000455
Chris Morgan5f9fd772000-09-13 20:27:30 +0000456 if (count > 0)
Sylvain St.Germain49c1ca21999-03-18 17:30:44 +0000457 {
Chris Morgan5f9fd772000-09-13 20:27:30 +0000458 next_item = TREEVIEW_GetNextListItem;
Sylvain St.Germain49c1ca21999-03-18 17:30:44 +0000459 }
Chris Morgan5f9fd772000-09-13 20:27:30 +0000460 else if (count < 0)
461 {
462 count = -count;
463 next_item = TREEVIEW_GetPrevListItem;
464 }
465 else
466 return wineItem;
Sylvain St.Germain49c1ca21999-03-18 17:30:44 +0000467
Chris Morgan5f9fd772000-09-13 20:27:30 +0000468 do
469 {
470 previousItem = wineItem;
471 wineItem = next_item(infoPtr, wineItem);
Sylvain St.Germain49c1ca21999-03-18 17:30:44 +0000472
Chris Morgan5f9fd772000-09-13 20:27:30 +0000473 } while (--count && wineItem != NULL);
474
475
476 return wineItem ? wineItem : previousItem;
Alexandre Julliardd30dfd21998-09-27 18:28:36 +0000477}
Alex Priemd2d7f961999-01-30 13:02:30 +0000478
Chris Morgan5f9fd772000-09-13 20:27:30 +0000479/* Notifications ************************************************************/
480
Dimitrie O. Paun02af5502002-10-29 21:25:35 +0000481static INT get_notifycode(TREEVIEW_INFO *infoPtr, INT code)
482{
Dimitrie O. Paun4bbd7b32002-10-31 00:54:53 +0000483 if (!infoPtr->bNtfUnicode) {
484 switch (code) {
485 case TVN_SELCHANGINGW: return TVN_SELCHANGINGA;
486 case TVN_SELCHANGEDW: return TVN_SELCHANGEDA;
487 case TVN_GETDISPINFOW: return TVN_GETDISPINFOA;
488 case TVN_SETDISPINFOW: return TVN_SETDISPINFOA;
489 case TVN_ITEMEXPANDINGW: return TVN_ITEMEXPANDINGA;
490 case TVN_ITEMEXPANDEDW: return TVN_ITEMEXPANDEDA;
491 case TVN_BEGINDRAGW: return TVN_BEGINDRAGA;
492 case TVN_BEGINRDRAGW: return TVN_BEGINRDRAGA;
493 case TVN_DELETEITEMW: return TVN_DELETEITEMA;
494 case TVN_BEGINLABELEDITW: return TVN_BEGINLABELEDITA;
495 case TVN_ENDLABELEDITW: return TVN_ENDLABELEDITA;
496 case TVN_GETINFOTIPW: return TVN_GETINFOTIPA;
497 }
Dimitrie O. Paun02af5502002-10-29 21:25:35 +0000498 }
499 return code;
500}
501
Guy L. Albertellib4994d12002-03-20 01:29:04 +0000502static LRESULT
503TREEVIEW_SendRealNotify(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
504{
Dimitrie O. Paund2e881d2002-11-27 20:15:49 +0000505 TRACE("wParam=%d, lParam=%ld\n", wParam, lParam);
506 return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, wParam, lParam);
Guy L. Albertellib4994d12002-03-20 01:29:04 +0000507}
508
Chris Morgan5f9fd772000-09-13 20:27:30 +0000509static BOOL
510TREEVIEW_SendSimpleNotify(TREEVIEW_INFO *infoPtr, UINT code)
Alex Priemd2d7f961999-01-30 13:02:30 +0000511{
Chris Morgan5f9fd772000-09-13 20:27:30 +0000512 NMHDR nmhdr;
513 HWND hwnd = infoPtr->hwnd;
Alex Priemd2d7f961999-01-30 13:02:30 +0000514
Dimitrie O. Paun02af5502002-10-29 21:25:35 +0000515 TRACE("%d\n", code);
Chris Morgan5f9fd772000-09-13 20:27:30 +0000516 nmhdr.hwndFrom = hwnd;
Robert Shearmancdb263e2004-08-25 17:33:01 +0000517 nmhdr.idFrom = GetWindowLongPtrW(hwnd, GWLP_ID);
Dimitrie O. Paun02af5502002-10-29 21:25:35 +0000518 nmhdr.code = get_notifycode(infoPtr, code);
Alex Priemd2d7f961999-01-30 13:02:30 +0000519
Guy L. Albertellib4994d12002-03-20 01:29:04 +0000520 return (BOOL)TREEVIEW_SendRealNotify(infoPtr,
521 (WPARAM)nmhdr.idFrom, (LPARAM)&nmhdr);
Alex Priemd2d7f961999-01-30 13:02:30 +0000522}
Alexandre Julliardd30dfd21998-09-27 18:28:36 +0000523
Chris Morgan5f9fd772000-09-13 20:27:30 +0000524static VOID
Huw Davies5693ecd2003-11-26 03:37:30 +0000525TREEVIEW_TVItemFromItem(TREEVIEW_INFO *infoPtr, UINT mask, TVITEMW *tvItem, TREEVIEW_ITEM *item)
Chris Morgan5f9fd772000-09-13 20:27:30 +0000526{
527 tvItem->mask = mask;
528 tvItem->hItem = item;
529 tvItem->state = item->state;
530 tvItem->stateMask = 0;
531 tvItem->iImage = item->iImage;
Chris Morgan5f9fd772000-09-13 20:27:30 +0000532 tvItem->iSelectedImage = item->iSelectedImage;
533 tvItem->cChildren = item->cChildren;
534 tvItem->lParam = item->lParam;
Guy L. Albertellib4994d12002-03-20 01:29:04 +0000535
Huw Davies5693ecd2003-11-26 03:37:30 +0000536 if(mask & TVIF_TEXT)
537 {
538 if (!infoPtr->bNtfUnicode)
539 {
540 tvItem->cchTextMax = WideCharToMultiByte( CP_ACP, 0, item->pszText, -1, NULL, 0, NULL, NULL );
541 tvItem->pszText = Alloc (tvItem->cchTextMax);
542 WideCharToMultiByte( CP_ACP, 0, item->pszText, -1, (LPSTR)tvItem->pszText, tvItem->cchTextMax, 0, 0 );
Guy L. Albertellib4994d12002-03-20 01:29:04 +0000543 }
Huw Davies5693ecd2003-11-26 03:37:30 +0000544 else
545 {
546 tvItem->cchTextMax = item->cchTextMax;
547 tvItem->pszText = item->pszText;
548 }
Guy L. Albertellib4994d12002-03-20 01:29:04 +0000549 }
550 else
Huw Davies5693ecd2003-11-26 03:37:30 +0000551 {
552 tvItem->cchTextMax = 0;
553 tvItem->pszText = NULL;
554 }
Chris Morgan5f9fd772000-09-13 20:27:30 +0000555}
556
557static BOOL
558TREEVIEW_SendTreeviewNotify(TREEVIEW_INFO *infoPtr, UINT code, UINT action,
559 UINT mask, HTREEITEM oldItem, HTREEITEM newItem)
560{
561 HWND hwnd = infoPtr->hwnd;
Huw Davies5693ecd2003-11-26 03:37:30 +0000562 NMTREEVIEWW nmhdr;
Guy L. Albertellib4994d12002-03-20 01:29:04 +0000563 BOOL ret;
Chris Morgan5f9fd772000-09-13 20:27:30 +0000564
Dimitrie O. Paun02af5502002-10-29 21:25:35 +0000565 TRACE("code:%d action:%x olditem:%p newitem:%p\n",
Chris Morgan5f9fd772000-09-13 20:27:30 +0000566 code, action, oldItem, newItem);
567
568 ZeroMemory(&nmhdr, sizeof(NMTREEVIEWA));
569
570 nmhdr.hdr.hwndFrom = hwnd;
Robert Shearmancdb263e2004-08-25 17:33:01 +0000571 nmhdr.hdr.idFrom = GetWindowLongPtrW(hwnd, GWLP_ID);
Dimitrie O. Paun02af5502002-10-29 21:25:35 +0000572 nmhdr.hdr.code = get_notifycode(infoPtr, code);
Chris Morgan5f9fd772000-09-13 20:27:30 +0000573 nmhdr.action = action;
574
575 if (oldItem)
Guy L. Albertellib4994d12002-03-20 01:29:04 +0000576 TREEVIEW_TVItemFromItem(infoPtr, mask, &nmhdr.itemOld, oldItem);
Chris Morgan5f9fd772000-09-13 20:27:30 +0000577
578 if (newItem)
Guy L. Albertellib4994d12002-03-20 01:29:04 +0000579 TREEVIEW_TVItemFromItem(infoPtr, mask, &nmhdr.itemNew, newItem);
Chris Morgan5f9fd772000-09-13 20:27:30 +0000580
581 nmhdr.ptDrag.x = 0;
582 nmhdr.ptDrag.y = 0;
583
Guy L. Albertellib4994d12002-03-20 01:29:04 +0000584 ret = (BOOL)TREEVIEW_SendRealNotify(infoPtr,
Huw Davies5693ecd2003-11-26 03:37:30 +0000585 (WPARAM)nmhdr.hdr.idFrom,
Chris Morgan5f9fd772000-09-13 20:27:30 +0000586 (LPARAM)&nmhdr);
Huw Davies5693ecd2003-11-26 03:37:30 +0000587 if (!infoPtr->bNtfUnicode)
588 {
Dimitrie O. Paun7de279a2003-09-22 21:32:33 +0000589 Free(nmhdr.itemOld.pszText);
590 Free(nmhdr.itemNew.pszText);
Guy L. Albertellib4994d12002-03-20 01:29:04 +0000591 }
592 return ret;
Chris Morgan5f9fd772000-09-13 20:27:30 +0000593}
594
595static BOOL
596TREEVIEW_SendTreeviewDnDNotify(TREEVIEW_INFO *infoPtr, UINT code,
597 HTREEITEM dragItem, POINT pt)
598{
599 HWND hwnd = infoPtr->hwnd;
Huw Davies5693ecd2003-11-26 03:37:30 +0000600 NMTREEVIEWW nmhdr;
Chris Morgan5f9fd772000-09-13 20:27:30 +0000601
Dimitrie O. Paun02af5502002-10-29 21:25:35 +0000602 TRACE("code:%d dragitem:%p\n", code, dragItem);
Chris Morgan5f9fd772000-09-13 20:27:30 +0000603
604 nmhdr.hdr.hwndFrom = hwnd;
Robert Shearmancdb263e2004-08-25 17:33:01 +0000605 nmhdr.hdr.idFrom = GetWindowLongPtrW(hwnd, GWLP_ID);
Dimitrie O. Paun02af5502002-10-29 21:25:35 +0000606 nmhdr.hdr.code = get_notifycode(infoPtr, code);
Chris Morgan5f9fd772000-09-13 20:27:30 +0000607 nmhdr.action = 0;
608 nmhdr.itemNew.mask = TVIF_STATE | TVIF_PARAM | TVIF_HANDLE;
609 nmhdr.itemNew.hItem = dragItem;
610 nmhdr.itemNew.state = dragItem->state;
611 nmhdr.itemNew.lParam = dragItem->lParam;
612
613 nmhdr.ptDrag.x = pt.x;
614 nmhdr.ptDrag.y = pt.y;
615
Guy L. Albertellib4994d12002-03-20 01:29:04 +0000616 return (BOOL)TREEVIEW_SendRealNotify(infoPtr,
Huw Davies5693ecd2003-11-26 03:37:30 +0000617 (WPARAM)nmhdr.hdr.idFrom,
Chris Morgan5f9fd772000-09-13 20:27:30 +0000618 (LPARAM)&nmhdr);
619}
620
621
622static BOOL
623TREEVIEW_SendCustomDrawNotify(TREEVIEW_INFO *infoPtr, DWORD dwDrawStage,
624 HDC hdc, RECT rc)
625{
626 HWND hwnd = infoPtr->hwnd;
627 NMTVCUSTOMDRAW nmcdhdr;
628 LPNMCUSTOMDRAW nmcd;
629
Michael Stefaniuc353529b2002-10-23 22:19:10 +0000630 TRACE("drawstage:%lx hdc:%p\n", dwDrawStage, hdc);
Chris Morgan5f9fd772000-09-13 20:27:30 +0000631
632 nmcd = &nmcdhdr.nmcd;
633 nmcd->hdr.hwndFrom = hwnd;
Robert Shearmancdb263e2004-08-25 17:33:01 +0000634 nmcd->hdr.idFrom = GetWindowLongPtrW(hwnd, GWLP_ID);
Chris Morgan5f9fd772000-09-13 20:27:30 +0000635 nmcd->hdr.code = NM_CUSTOMDRAW;
636 nmcd->dwDrawStage = dwDrawStage;
637 nmcd->hdc = hdc;
638 nmcd->rc = rc;
639 nmcd->dwItemSpec = 0;
640 nmcd->uItemState = 0;
641 nmcd->lItemlParam = 0;
642 nmcdhdr.clrText = infoPtr->clrText;
643 nmcdhdr.clrTextBk = infoPtr->clrBk;
644 nmcdhdr.iLevel = 0;
645
Guy L. Albertellib4994d12002-03-20 01:29:04 +0000646 return (BOOL)TREEVIEW_SendRealNotify(infoPtr,
Huw Davies5693ecd2003-11-26 03:37:30 +0000647 (WPARAM)nmcd->hdr.idFrom,
Chris Morgan5f9fd772000-09-13 20:27:30 +0000648 (LPARAM)&nmcdhdr);
649}
650
651
652
653/* FIXME: need to find out when the flags in uItemState need to be set */
654
655static BOOL
656TREEVIEW_SendCustomDrawItemNotify(TREEVIEW_INFO *infoPtr, HDC hdc,
Vitaliy Margolen71be0942005-04-18 10:20:51 +0000657 TREEVIEW_ITEM *wineItem, UINT uItemDrawState,
658 NMTVCUSTOMDRAW *nmcdhdr)
Chris Morgan5f9fd772000-09-13 20:27:30 +0000659{
660 HWND hwnd = infoPtr->hwnd;
Chris Morgan5f9fd772000-09-13 20:27:30 +0000661 LPNMCUSTOMDRAW nmcd;
Frank Richter9e570912005-08-30 10:07:17 +0000662 DWORD dwDrawStage;
663 DWORD_PTR dwItemSpec;
Chris Morgan5f9fd772000-09-13 20:27:30 +0000664 UINT uItemState;
665 INT retval;
666
667 dwDrawStage = CDDS_ITEM | uItemDrawState;
Frank Richter9e570912005-08-30 10:07:17 +0000668 dwItemSpec = (DWORD_PTR)wineItem;
Chris Morgan5f9fd772000-09-13 20:27:30 +0000669 uItemState = 0;
670 if (wineItem->state & TVIS_SELECTED)
671 uItemState |= CDIS_SELECTED;
672 if (wineItem == infoPtr->selectedItem)
673 uItemState |= CDIS_FOCUS;
674 if (wineItem == infoPtr->hotItem)
675 uItemState |= CDIS_HOT;
676
Vitaliy Margolen71be0942005-04-18 10:20:51 +0000677 nmcd = &nmcdhdr->nmcd;
Chris Morgan5f9fd772000-09-13 20:27:30 +0000678 nmcd->hdr.hwndFrom = hwnd;
Robert Shearmancdb263e2004-08-25 17:33:01 +0000679 nmcd->hdr.idFrom = GetWindowLongPtrW(hwnd, GWLP_ID);
Chris Morgan5f9fd772000-09-13 20:27:30 +0000680 nmcd->hdr.code = NM_CUSTOMDRAW;
681 nmcd->dwDrawStage = dwDrawStage;
682 nmcd->hdc = hdc;
683 nmcd->rc = wineItem->rect;
684 nmcd->dwItemSpec = dwItemSpec;
685 nmcd->uItemState = uItemState;
686 nmcd->lItemlParam = wineItem->lParam;
Vitaliy Margolen71be0942005-04-18 10:20:51 +0000687 nmcdhdr->iLevel = wineItem->iLevel;
Chris Morgan5f9fd772000-09-13 20:27:30 +0000688
Michael Stefaniuc353529b2002-10-23 22:19:10 +0000689 TRACE("drawstage:%lx hdc:%p item:%lx, itemstate:%x, lItemlParam:%lx\n",
Chris Morgan5f9fd772000-09-13 20:27:30 +0000690 nmcd->dwDrawStage, nmcd->hdc, nmcd->dwItemSpec,
691 nmcd->uItemState, nmcd->lItemlParam);
692
Guy L. Albertellib4994d12002-03-20 01:29:04 +0000693 retval = TREEVIEW_SendRealNotify(infoPtr,
Huw Davies5693ecd2003-11-26 03:37:30 +0000694 (WPARAM)nmcd->hdr.idFrom,
Vitaliy Margolen71be0942005-04-18 10:20:51 +0000695 (LPARAM)nmcdhdr);
Chris Morgan5f9fd772000-09-13 20:27:30 +0000696
Chris Morgan5f9fd772000-09-13 20:27:30 +0000697 return (BOOL)retval;
698}
699
700static BOOL
701TREEVIEW_BeginLabelEditNotify(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *editItem)
702{
703 HWND hwnd = infoPtr->hwnd;
Huw Davies5693ecd2003-11-26 03:37:30 +0000704 NMTVDISPINFOW tvdi;
Guy L. Albertellib4994d12002-03-20 01:29:04 +0000705 BOOL ret;
Chris Morgan5f9fd772000-09-13 20:27:30 +0000706
707 tvdi.hdr.hwndFrom = hwnd;
Robert Shearmancdb263e2004-08-25 17:33:01 +0000708 tvdi.hdr.idFrom = GetWindowLongPtrW(hwnd, GWLP_ID);
Dimitrie O. Paun4bbd7b32002-10-31 00:54:53 +0000709 tvdi.hdr.code = get_notifycode(infoPtr, TVN_BEGINLABELEDITW);
Chris Morgan5f9fd772000-09-13 20:27:30 +0000710
Huw Davies5693ecd2003-11-26 03:37:30 +0000711 TREEVIEW_TVItemFromItem(infoPtr, TVIF_HANDLE | TVIF_STATE | TVIF_PARAM | TVIF_TEXT,
712 &tvdi.item, editItem);
Chris Morgan5f9fd772000-09-13 20:27:30 +0000713
Huw Davies5693ecd2003-11-26 03:37:30 +0000714 ret = (BOOL)TREEVIEW_SendRealNotify(infoPtr, tvdi.hdr.idFrom, (LPARAM)&tvdi);
715
716 if (!infoPtr->bNtfUnicode)
717 Free(tvdi.item.pszText);
718
Guy L. Albertellib4994d12002-03-20 01:29:04 +0000719 return ret;
Chris Morgan5f9fd772000-09-13 20:27:30 +0000720}
Alexandre Julliardd30dfd21998-09-27 18:28:36 +0000721
722static void
Chris Morgan5f9fd772000-09-13 20:27:30 +0000723TREEVIEW_UpdateDispInfo(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *wineItem,
724 UINT mask)
Alexandre Julliardd30dfd21998-09-27 18:28:36 +0000725{
Huw Davies5693ecd2003-11-26 03:37:30 +0000726 NMTVDISPINFOW callback;
Chris Morgan5f9fd772000-09-13 20:27:30 +0000727 HWND hwnd = infoPtr->hwnd;
Alexandre Julliardd30dfd21998-09-27 18:28:36 +0000728
Huw Davies5693ecd2003-11-26 03:37:30 +0000729 TRACE("mask %x callbackMask %x\n", mask, wineItem->callbackMask);
Maxime Bellengéfb394422003-08-18 19:46:31 +0000730 mask &= wineItem->callbackMask;
Alexandre Julliardd30dfd21998-09-27 18:28:36 +0000731
Chris Morgan5f9fd772000-09-13 20:27:30 +0000732 if (mask == 0) return;
Alex Priemd2d7f961999-01-30 13:02:30 +0000733
Chris Morgan5f9fd772000-09-13 20:27:30 +0000734 callback.hdr.hwndFrom = hwnd;
Robert Shearmancdb263e2004-08-25 17:33:01 +0000735 callback.hdr.idFrom = GetWindowLongPtrW(hwnd, GWLP_ID);
Dimitrie O. Paun4bbd7b32002-10-31 00:54:53 +0000736 callback.hdr.code = get_notifycode(infoPtr, TVN_GETDISPINFOW);
Alex Priemd2d7f961999-01-30 13:02:30 +0000737
Chris Morgan5f9fd772000-09-13 20:27:30 +0000738 /* 'state' always contains valid value, as well as 'lParam'.
739 * All other parameters are uninitialized.
740 */
741 callback.item.pszText = wineItem->pszText;
742 callback.item.cchTextMax = wineItem->cchTextMax;
743 callback.item.mask = mask;
744 callback.item.hItem = wineItem;
745 callback.item.state = wineItem->state;
746 callback.item.lParam = wineItem->lParam;
747
748 /* If text is changed we need to recalculate textWidth */
749 if (mask & TVIF_TEXT)
750 wineItem->textWidth = 0;
751
Guy L. Albertellib4994d12002-03-20 01:29:04 +0000752 TREEVIEW_SendRealNotify(infoPtr,
753 (WPARAM)callback.hdr.idFrom, (LPARAM)&callback);
Chris Morgan5f9fd772000-09-13 20:27:30 +0000754
Vincent Béron9a624912002-05-31 23:06:46 +0000755 /* It may have changed due to a call to SetItem. */
Chris Morgan5f9fd772000-09-13 20:27:30 +0000756 mask &= wineItem->callbackMask;
757
758 if ((mask & TVIF_TEXT) && callback.item.pszText != wineItem->pszText)
759 {
760 /* Instead of copying text into our buffer user specified its own */
Huw Davies5693ecd2003-11-26 03:37:30 +0000761 if (!infoPtr->bNtfUnicode) {
Guy L. Albertellib4994d12002-03-20 01:29:04 +0000762 LPWSTR newText;
763 int buflen;
Huw Davies5693ecd2003-11-26 03:37:30 +0000764 int len = MultiByteToWideChar( CP_ACP, 0,
765 (LPSTR)callback.item.pszText, -1,
766 NULL, 0);
767 buflen = max((len)*sizeof(WCHAR), TEXT_CALLBACK_SIZE);
Dimitrie O. Paun7de279a2003-09-22 21:32:33 +0000768 newText = (LPWSTR)ReAlloc(wineItem->pszText, buflen);
Chris Morgan5f9fd772000-09-13 20:27:30 +0000769
Huw Davies5693ecd2003-11-26 03:37:30 +0000770 TRACE("returned str %s, len=%d, buflen=%d\n",
771 debugstr_a((LPSTR)callback.item.pszText), len, buflen);
Guy L. Albertellib4994d12002-03-20 01:29:04 +0000772
773 if (newText)
774 {
Huw Davies5693ecd2003-11-26 03:37:30 +0000775 wineItem->pszText = newText;
776 MultiByteToWideChar( CP_ACP, 0,
777 (LPSTR)callback.item.pszText, -1,
Marcus Meissnerbd7ec9a2005-09-12 22:07:53 +0000778 wineItem->pszText, buflen/sizeof(WCHAR));
779 wineItem->cchTextMax = buflen/sizeof(WCHAR);
Guy L. Albertellib4994d12002-03-20 01:29:04 +0000780 }
781 /* If ReAlloc fails we have nothing to do, but keep original text */
Chris Morgan5f9fd772000-09-13 20:27:30 +0000782 }
Guy L. Albertellib4994d12002-03-20 01:29:04 +0000783 else {
Huw Davies5693ecd2003-11-26 03:37:30 +0000784 int len = max(lstrlenW(callback.item.pszText) + 1,
Guy L. Albertellib4994d12002-03-20 01:29:04 +0000785 TEXT_CALLBACK_SIZE);
Huw Davies5693ecd2003-11-26 03:37:30 +0000786 LPWSTR newText = ReAlloc(wineItem->pszText, len);
Chris Morgan5f9fd772000-09-13 20:27:30 +0000787
Huw Davies5693ecd2003-11-26 03:37:30 +0000788 TRACE("returned wstr %s, len=%d\n",
789 debugstr_w(callback.item.pszText), len);
Guy L. Albertellib4994d12002-03-20 01:29:04 +0000790
791 if (newText)
792 {
793 wineItem->pszText = newText;
Huw Davies5693ecd2003-11-26 03:37:30 +0000794 strcpyW(wineItem->pszText, callback.item.pszText);
Guy L. Albertellib4994d12002-03-20 01:29:04 +0000795 wineItem->cchTextMax = len;
796 }
797 /* If ReAlloc fails we have nothing to do, but keep original text */
798 }
799 }
800 else if (mask & TVIF_TEXT) {
Huw Davies5693ecd2003-11-26 03:37:30 +0000801 /* User put text into our buffer, that is ok unless A string */
802 if (!infoPtr->bNtfUnicode) {
Guy L. Albertellib4994d12002-03-20 01:29:04 +0000803 LPWSTR newText;
Huw Davies5693ecd2003-11-26 03:37:30 +0000804 LPWSTR oldText = NULL;
Guy L. Albertellib4994d12002-03-20 01:29:04 +0000805 int buflen;
Huw Davies5693ecd2003-11-26 03:37:30 +0000806 int len = MultiByteToWideChar( CP_ACP, 0,
807 (LPSTR)callback.item.pszText, -1,
808 NULL, 0);
809 buflen = max((len)*sizeof(WCHAR), TEXT_CALLBACK_SIZE);
Dimitrie O. Paun7de279a2003-09-22 21:32:33 +0000810 newText = (LPWSTR)Alloc(buflen);
Guy L. Albertellib4994d12002-03-20 01:29:04 +0000811
Huw Davies5693ecd2003-11-26 03:37:30 +0000812 TRACE("same buffer str %s, len=%d, buflen=%d\n",
813 debugstr_a((LPSTR)callback.item.pszText), len, buflen);
Guy L. Albertellib4994d12002-03-20 01:29:04 +0000814
815 if (newText)
816 {
817 oldText = wineItem->pszText;
Huw Davies5693ecd2003-11-26 03:37:30 +0000818 wineItem->pszText = newText;
819 MultiByteToWideChar( CP_ACP, 0,
820 (LPSTR)callback.item.pszText, -1,
Marcus Meissnerbd7ec9a2005-09-12 22:07:53 +0000821 wineItem->pszText, buflen/sizeof(WCHAR));
822 wineItem->cchTextMax = buflen/sizeof(WCHAR);
Guy L. Albertellib4994d12002-03-20 01:29:04 +0000823 if (oldText)
Dimitrie O. Paun7de279a2003-09-22 21:32:33 +0000824 Free(oldText);
Guy L. Albertellib4994d12002-03-20 01:29:04 +0000825 }
826 }
827 }
Vincent Béron9a624912002-05-31 23:06:46 +0000828
Chris Morgan5f9fd772000-09-13 20:27:30 +0000829 if (mask & TVIF_IMAGE)
830 wineItem->iImage = callback.item.iImage;
831
832 if (mask & TVIF_SELECTEDIMAGE)
833 wineItem->iSelectedImage = callback.item.iSelectedImage;
834
835 if (mask & TVIF_CHILDREN)
836 wineItem->cChildren = callback.item.cChildren;
837
838 /* These members are now permanently set. */
839 if (callback.item.mask & TVIF_DI_SETITEM)
840 wineItem->callbackMask &= ~callback.item.mask;
841}
842
843/***************************************************************************
844 * This function uses cChildren field to decide whether the item has
845 * children or not.
846 * Note: if this returns TRUE, the child items may not actually exist,
847 * they could be virtual.
848 *
849 * Just use wineItem->firstChild to check for physical children.
850 */
851static BOOL
852TREEVIEW_HasChildren(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *wineItem)
853{
854 TREEVIEW_UpdateDispInfo(infoPtr, wineItem, TVIF_CHILDREN);
855
856 return wineItem->cChildren > 0;
857}
858
859
860/* Item Position ********************************************************/
861
862/* Compute linesOffset, stateOffset, imageOffset, textOffset of an item. */
863static VOID
864TREEVIEW_ComputeItemInternalMetrics(TREEVIEW_INFO *infoPtr,
865 TREEVIEW_ITEM *item)
866{
867 /* Same effect, different optimisation. */
868#if 0
869 BOOL lar = ((infoPtr->dwStyle & TVS_LINESATROOT)
870 && (infoPtr->dwStyle & (TVS_HASLINES|TVS_HASBUTTONS)));
871#else
872 BOOL lar = ((infoPtr->dwStyle
873 & (TVS_LINESATROOT|TVS_HASLINES|TVS_HASBUTTONS))
874 > TVS_LINESATROOT);
875#endif
876
877 item->linesOffset = infoPtr->uIndent * (item->iLevel + lar - 1)
878 - infoPtr->scrollX;
879 item->stateOffset = item->linesOffset + infoPtr->uIndent;
880 item->imageOffset = item->stateOffset
881 + (STATEIMAGEINDEX(item->state) ? infoPtr->stateImageWidth : 0);
882 item->textOffset = item->imageOffset + infoPtr->normalImageWidth;
883}
884
885static VOID
886TREEVIEW_ComputeTextWidth(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item, HDC hDC)
887{
888 HDC hdc;
889 HFONT hOldFont=0;
890 SIZE sz;
891
892 /* DRAW's OM docker creates items like this */
893 if (item->pszText == NULL)
894 {
895 item->textWidth = 0;
896 return;
897 }
898
Chris Morgan5f9fd772000-09-13 20:27:30 +0000899 if (hDC != 0)
900 {
901 hdc = hDC;
902 }
903 else
904 {
905 hdc = GetDC(infoPtr->hwnd);
906 hOldFont = SelectObject(hdc, TREEVIEW_FontForItem(infoPtr, item));
907 }
908
Huw Davies5693ecd2003-11-26 03:37:30 +0000909 GetTextExtentPoint32W(hdc, item->pszText, strlenW(item->pszText), &sz);
Chris Morgan5f9fd772000-09-13 20:27:30 +0000910 item->textWidth = sz.cx;
911
912 if (hDC == 0)
913 {
914 SelectObject(hdc, hOldFont);
915 ReleaseDC(0, hdc);
916 }
917}
918
919static VOID
920TREEVIEW_ComputeItemRect(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item)
921{
922 item->rect.top = infoPtr->uItemHeight *
923 (item->visibleOrder - infoPtr->firstVisible->visibleOrder);
924
925 item->rect.bottom = item->rect.top
Susan Farley036ae0b2000-10-13 20:13:38 +0000926 + infoPtr->uItemHeight * item->iIntegral - 1;
Chris Morgan5f9fd772000-09-13 20:27:30 +0000927
928 item->rect.left = 0;
929 item->rect.right = infoPtr->clientWidth;
930}
931
932/* We know that only items after start need their order updated. */
933static void
934TREEVIEW_RecalculateVisibleOrder(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *start)
935{
936 TREEVIEW_ITEM *item;
937 int order;
938
939 if (!start)
940 {
941 start = infoPtr->root->firstChild;
942 order = 0;
943 }
944 else
945 order = start->visibleOrder;
946
947 for (item = start; item != NULL;
948 item = TREEVIEW_GetNextListItem(infoPtr, item))
949 {
950 item->visibleOrder = order;
951 order += item->iIntegral;
952 }
953
954 infoPtr->maxVisibleOrder = order;
955
956 for (item = start; item != NULL;
957 item = TREEVIEW_GetNextListItem(infoPtr, item))
958 {
959 TREEVIEW_ComputeItemRect(infoPtr, item);
960 }
961}
962
963
964/* Update metrics of all items in selected subtree.
965 * root must be expanded
966 */
967static VOID
968TREEVIEW_UpdateSubTree(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *root)
969{
970 TREEVIEW_ITEM *sibling;
971 HDC hdc;
972 HFONT hOldFont;
973
974 if (!root->firstChild || !(root->state & TVIS_EXPANDED))
975 return;
976
977 root->state &= ~TVIS_EXPANDED;
978 sibling = TREEVIEW_GetNextListItem(infoPtr, root);
979 root->state |= TVIS_EXPANDED;
980
981 hdc = GetDC(infoPtr->hwnd);
982 hOldFont = SelectObject(hdc, infoPtr->hFont);
983
984 for (; root != sibling;
985 root = TREEVIEW_GetNextListItem(infoPtr, root))
986 {
987 TREEVIEW_ComputeItemInternalMetrics(infoPtr, root);
988
989 if (root->callbackMask & TVIF_TEXT)
990 TREEVIEW_UpdateDispInfo(infoPtr, root, TVIF_TEXT);
991
992 if (root->textWidth == 0)
993 {
994 SelectObject(hdc, TREEVIEW_FontForItem(infoPtr, root));
995 TREEVIEW_ComputeTextWidth(infoPtr, root, hdc);
996 }
997 }
998
999 SelectObject(hdc, hOldFont);
1000 ReleaseDC(infoPtr->hwnd, hdc);
1001}
1002
1003/* Item Allocation **********************************************************/
1004
1005static TREEVIEW_ITEM *
1006TREEVIEW_AllocateItem(TREEVIEW_INFO *infoPtr)
1007{
Dimitrie O. Paun7de279a2003-09-22 21:32:33 +00001008 TREEVIEW_ITEM *newItem = Alloc(sizeof(TREEVIEW_ITEM));
Chris Morgan5f9fd772000-09-13 20:27:30 +00001009
1010 if (!newItem)
1011 return NULL;
1012
Robert Shearman6a365e02005-03-21 10:51:14 +00001013 newItem->iImage = -1;
1014 newItem->iSelectedImage = -1;
1015
Chris Morgan5f9fd772000-09-13 20:27:30 +00001016 if (DPA_InsertPtr(infoPtr->items, INT_MAX, newItem) == -1)
1017 {
Dimitrie O. Paun7de279a2003-09-22 21:32:33 +00001018 Free(newItem);
Chris Morgan5f9fd772000-09-13 20:27:30 +00001019 return NULL;
1020 }
1021
1022 return newItem;
1023}
1024
1025/* Exact opposite of TREEVIEW_AllocateItem. In particular, it does not
1026 * free item->pszText. */
1027static void
1028TREEVIEW_FreeItem(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item)
1029{
1030 DPA_DeletePtr(infoPtr->items, DPA_GetPtrIndex(infoPtr->items, item));
Dimitrie O. Paun7de279a2003-09-22 21:32:33 +00001031 Free(item);
Gerard Patelede98812001-08-20 18:00:54 +00001032 if (infoPtr->selectedItem == item)
1033 infoPtr->selectedItem = NULL;
Adam Gundya9822592003-05-02 20:15:36 +00001034 if (infoPtr->hotItem == item)
1035 infoPtr->hotItem = NULL;
1036 if (infoPtr->focusedItem == item)
1037 infoPtr->focusedItem = NULL;
1038 if (infoPtr->firstVisible == item)
1039 infoPtr->firstVisible = NULL;
1040 if (infoPtr->dropItem == item)
1041 infoPtr->dropItem = NULL;
1042 if (infoPtr->insertMarkItem == item)
1043 infoPtr->insertMarkItem = NULL;
Chris Morgan5f9fd772000-09-13 20:27:30 +00001044}
1045
1046
1047/* Item Insertion *******************************************************/
1048
1049/***************************************************************************
1050 * This method inserts newItem before sibling as a child of parent.
1051 * sibling can be NULL, but only if parent has no children.
1052 */
1053static void
1054TREEVIEW_InsertBefore(TREEVIEW_ITEM *newItem, TREEVIEW_ITEM *sibling,
1055 TREEVIEW_ITEM *parent)
1056{
1057 assert(newItem != NULL);
1058 assert(parent != NULL);
1059
1060 if (sibling != NULL)
1061 {
1062 assert(sibling->parent == parent);
1063
1064 if (sibling->prevSibling != NULL)
1065 sibling->prevSibling->nextSibling = newItem;
1066
1067 newItem->prevSibling = sibling->prevSibling;
1068 sibling->prevSibling = newItem;
1069 }
1070 else
1071 newItem->prevSibling = NULL;
1072
1073 newItem->nextSibling = sibling;
1074
1075 if (parent->firstChild == sibling)
1076 parent->firstChild = newItem;
1077
1078 if (parent->lastChild == NULL)
1079 parent->lastChild = newItem;
1080}
1081
1082/***************************************************************************
1083 * This method inserts newItem after sibling as a child of parent.
1084 * sibling can be NULL, but only if parent has no children.
1085 */
1086static void
1087TREEVIEW_InsertAfter(TREEVIEW_ITEM *newItem, TREEVIEW_ITEM *sibling,
1088 TREEVIEW_ITEM *parent)
1089{
1090 assert(newItem != NULL);
1091 assert(parent != NULL);
1092
1093 if (sibling != NULL)
1094 {
1095 assert(sibling->parent == parent);
1096
1097 if (sibling->nextSibling != NULL)
1098 sibling->nextSibling->prevSibling = newItem;
1099
1100 newItem->nextSibling = sibling->nextSibling;
1101 sibling->nextSibling = newItem;
1102 }
1103 else
1104 newItem->nextSibling = NULL;
1105
1106 newItem->prevSibling = sibling;
1107
1108 if (parent->lastChild == sibling)
1109 parent->lastChild = newItem;
1110
1111 if (parent->firstChild == NULL)
1112 parent->firstChild = newItem;
1113}
1114
1115static BOOL
Huw Davies6cfbf892003-11-27 01:11:01 +00001116TREEVIEW_DoSetItemT(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *wineItem,
1117 const TVITEMEXW *tvItem, BOOL isW)
Chris Morgan5f9fd772000-09-13 20:27:30 +00001118{
1119 UINT callbackClear = 0;
1120 UINT callbackSet = 0;
1121
Huw Davies5693ecd2003-11-26 03:37:30 +00001122 TRACE("item %p\n", wineItem);
Chris Morgan5f9fd772000-09-13 20:27:30 +00001123 /* Do this first in case it fails. */
1124 if (tvItem->mask & TVIF_TEXT)
1125 {
Gerard Patelf6d3d012001-04-09 18:28:47 +00001126 wineItem->textWidth = 0; /* force width recalculation */
Huw Davies6cfbf892003-11-27 01:11:01 +00001127 if (tvItem->pszText != LPSTR_TEXTCALLBACKW) /* covers != TEXTCALLBACKA too */
Chris Morgan5f9fd772000-09-13 20:27:30 +00001128 {
Huw Davies6cfbf892003-11-27 01:11:01 +00001129 int len;
1130 LPWSTR newText;
1131 if (isW)
1132 len = lstrlenW(tvItem->pszText) + 1;
1133 else
1134 len = MultiByteToWideChar(CP_ACP, 0, (LPSTR)tvItem->pszText, -1, NULL, 0);
Jon Griffithsf479f102004-09-28 02:35:48 +00001135
Huw Davies6cfbf892003-11-27 01:11:01 +00001136 newText = ReAlloc(wineItem->pszText, len * sizeof(WCHAR));
Chris Morgan5f9fd772000-09-13 20:27:30 +00001137
Huw Davies6cfbf892003-11-27 01:11:01 +00001138 if (newText == NULL) return FALSE;
Chris Morgan5f9fd772000-09-13 20:27:30 +00001139
Huw Davies6cfbf892003-11-27 01:11:01 +00001140 callbackClear |= TVIF_TEXT;
Chris Morgan5f9fd772000-09-13 20:27:30 +00001141
Huw Davies6cfbf892003-11-27 01:11:01 +00001142 wineItem->pszText = newText;
1143 wineItem->cchTextMax = len;
1144 if (isW)
1145 lstrcpynW(wineItem->pszText, tvItem->pszText, len);
1146 else
1147 MultiByteToWideChar(CP_ACP, 0, (LPSTR)tvItem->pszText, -1,
1148 wineItem->pszText, len);
1149
1150 TRACE("setting text %s, item %p\n", debugstr_w(wineItem->pszText), wineItem);
1151 }
Chris Morgan5f9fd772000-09-13 20:27:30 +00001152 else
1153 {
1154 callbackSet |= TVIF_TEXT;
1155
Dimitrie O. Paun7de279a2003-09-22 21:32:33 +00001156 wineItem->pszText = ReAlloc(wineItem->pszText,
Huw Davies6cfbf892003-11-27 01:11:01 +00001157 TEXT_CALLBACK_SIZE * sizeof(WCHAR));
Chris Morgan5f9fd772000-09-13 20:27:30 +00001158 wineItem->cchTextMax = TEXT_CALLBACK_SIZE;
Huw Davies6cfbf892003-11-27 01:11:01 +00001159 TRACE("setting callback, item %p\n", wineItem);
Chris Morgan5f9fd772000-09-13 20:27:30 +00001160 }
1161 }
1162
1163 if (tvItem->mask & TVIF_CHILDREN)
1164 {
1165 wineItem->cChildren = tvItem->cChildren;
1166
1167 if (wineItem->cChildren == I_CHILDRENCALLBACK)
1168 callbackSet |= TVIF_CHILDREN;
1169 else
1170 callbackClear |= TVIF_CHILDREN;
1171 }
1172
1173 if (tvItem->mask & TVIF_IMAGE)
1174 {
1175 wineItem->iImage = tvItem->iImage;
1176
1177 if (wineItem->iImage == I_IMAGECALLBACK)
1178 callbackSet |= TVIF_IMAGE;
1179 else
1180 callbackClear |= TVIF_IMAGE;
1181 }
1182
1183 if (tvItem->mask & TVIF_SELECTEDIMAGE)
1184 {
1185 wineItem->iSelectedImage = tvItem->iSelectedImage;
1186
1187 if (wineItem->iSelectedImage == I_IMAGECALLBACK)
1188 callbackSet |= TVIF_SELECTEDIMAGE;
1189 else
1190 callbackClear |= TVIF_SELECTEDIMAGE;
1191 }
1192
1193 if (tvItem->mask & TVIF_PARAM)
1194 wineItem->lParam = tvItem->lParam;
1195
1196 /* If the application sets TVIF_INTEGRAL without
1197 * supplying a TVITEMEX structure, it's toast. */
1198 if (tvItem->mask & TVIF_INTEGRAL)
1199 wineItem->iIntegral = tvItem->iIntegral;
1200
1201 if (tvItem->mask & TVIF_STATE)
1202 {
1203 TRACE("prevstate,state,mask:%x,%x,%x\n", wineItem->state, tvItem->state,
1204 tvItem->stateMask);
1205 wineItem->state &= ~tvItem->stateMask;
1206 wineItem->state |= (tvItem->state & tvItem->stateMask);
1207 }
1208
1209 wineItem->callbackMask |= callbackSet;
1210 wineItem->callbackMask &= ~callbackClear;
1211
1212 return TRUE;
1213}
1214
1215/* Note that the new item is pre-zeroed. */
1216static LRESULT
Huw Davies6cfbf892003-11-27 01:11:01 +00001217TREEVIEW_InsertItemT(TREEVIEW_INFO *infoPtr, const TVINSERTSTRUCTW *ptdi, BOOL isW)
Chris Morgan5f9fd772000-09-13 20:27:30 +00001218{
Alexandre Julliard502f6c52004-01-07 00:47:35 +00001219 const TVITEMEXW *tvItem = &ptdi->u.itemex;
Chris Morgan5f9fd772000-09-13 20:27:30 +00001220 HTREEITEM insertAfter;
1221 TREEVIEW_ITEM *newItem, *parentItem;
1222 BOOL bTextUpdated = FALSE;
1223
1224 if (ptdi->hParent == TVI_ROOT || ptdi->hParent == 0)
1225 {
1226 parentItem = infoPtr->root;
1227 }
1228 else
1229 {
1230 parentItem = ptdi->hParent;
1231
1232 if (!TREEVIEW_ValidItem(infoPtr, parentItem))
1233 {
1234 WARN("invalid parent %p\n", parentItem);
1235 return (LRESULT)(HTREEITEM)NULL;
1236 }
1237 }
1238
1239 insertAfter = ptdi->hInsertAfter;
1240
1241 /* Validate this now for convenience. */
Frank Richter9e570912005-08-30 10:07:17 +00001242 switch ((DWORD_PTR)insertAfter)
Chris Morgan5f9fd772000-09-13 20:27:30 +00001243 {
Frank Richter9e570912005-08-30 10:07:17 +00001244 case (DWORD_PTR)TVI_FIRST:
1245 case (DWORD_PTR)TVI_LAST:
1246 case (DWORD_PTR)TVI_SORT:
Chris Morgan5f9fd772000-09-13 20:27:30 +00001247 break;
1248
1249 default:
1250 if (!TREEVIEW_ValidItem(infoPtr, insertAfter) ||
1251 insertAfter->parent != parentItem)
1252 {
1253 WARN("invalid insert after %p\n", insertAfter);
1254 insertAfter = TVI_LAST;
1255 }
1256 }
1257
Huw Davies5693ecd2003-11-26 03:37:30 +00001258 TRACE("parent %p position %p: %s\n", parentItem, insertAfter,
Chris Morgan5f9fd772000-09-13 20:27:30 +00001259 (tvItem->mask & TVIF_TEXT)
Huw Davies5693ecd2003-11-26 03:37:30 +00001260 ? ((tvItem->pszText == LPSTR_TEXTCALLBACKW) ? "<callback>"
Huw Daviesd9558db2004-02-12 20:06:04 +00001261 : (isW ? debugstr_w(tvItem->pszText) : debugstr_a((LPSTR)tvItem->pszText)))
Chris Morgan5f9fd772000-09-13 20:27:30 +00001262 : "<no label>");
1263
1264 newItem = TREEVIEW_AllocateItem(infoPtr);
1265 if (newItem == NULL)
1266 return (LRESULT)(HTREEITEM)NULL;
1267
1268 newItem->parent = parentItem;
1269 newItem->iIntegral = 1;
1270
Huw Davies6cfbf892003-11-27 01:11:01 +00001271 if (!TREEVIEW_DoSetItemT(infoPtr, newItem, tvItem, isW))
Chris Morgan5f9fd772000-09-13 20:27:30 +00001272 return (LRESULT)(HTREEITEM)NULL;
1273
1274 /* After this point, nothing can fail. (Except for TVI_SORT.) */
1275
1276 infoPtr->uNumItems++;
1277
Frank Richter9e570912005-08-30 10:07:17 +00001278 switch ((DWORD_PTR)insertAfter)
Chris Morgan5f9fd772000-09-13 20:27:30 +00001279 {
Frank Richter9e570912005-08-30 10:07:17 +00001280 case (DWORD_PTR)TVI_FIRST:
Adam Gundya9822592003-05-02 20:15:36 +00001281 {
1282 TREEVIEW_ITEM *originalFirst = parentItem->firstChild;
1283 TREEVIEW_InsertBefore(newItem, parentItem->firstChild, parentItem);
1284 if (infoPtr->firstVisible == originalFirst)
1285 TREEVIEW_SetFirstVisible(infoPtr, newItem, TRUE);
1286 }
Chris Morgan5f9fd772000-09-13 20:27:30 +00001287 break;
1288
Frank Richter9e570912005-08-30 10:07:17 +00001289 case (DWORD_PTR)TVI_LAST:
Chris Morgan5f9fd772000-09-13 20:27:30 +00001290 TREEVIEW_InsertAfter(newItem, parentItem->lastChild, parentItem);
1291 break;
1292
1293 /* hInsertAfter names a specific item we want to insert after */
1294 default:
1295 TREEVIEW_InsertAfter(newItem, insertAfter, insertAfter->parent);
1296 break;
1297
Frank Richter9e570912005-08-30 10:07:17 +00001298 case (DWORD_PTR)TVI_SORT:
Chris Morgan5f9fd772000-09-13 20:27:30 +00001299 {
1300 TREEVIEW_ITEM *aChild;
1301 TREEVIEW_ITEM *previousChild = NULL;
1302 BOOL bItemInserted = FALSE;
1303
1304 aChild = parentItem->firstChild;
1305
1306 bTextUpdated = TRUE;
1307 TREEVIEW_UpdateDispInfo(infoPtr, newItem, TVIF_TEXT);
1308
1309 /* Iterate the parent children to see where we fit in */
1310 while (aChild != NULL)
1311 {
1312 INT comp;
1313
1314 TREEVIEW_UpdateDispInfo(infoPtr, aChild, TVIF_TEXT);
Huw Davies5693ecd2003-11-26 03:37:30 +00001315 comp = lstrcmpW(newItem->pszText, aChild->pszText);
Chris Morgan5f9fd772000-09-13 20:27:30 +00001316
1317 if (comp < 0) /* we are smaller than the current one */
1318 {
1319 TREEVIEW_InsertBefore(newItem, aChild, parentItem);
1320 bItemInserted = TRUE;
1321 break;
Alexandre Julliardd30dfd21998-09-27 18:28:36 +00001322 }
Chris Morgan5f9fd772000-09-13 20:27:30 +00001323 else if (comp > 0) /* we are bigger than the current one */
1324 {
1325 previousChild = aChild;
Alexandre Julliardd30dfd21998-09-27 18:28:36 +00001326
Chris Morgan5f9fd772000-09-13 20:27:30 +00001327 /* This will help us to exit if there is no more sibling */
1328 aChild = (aChild->nextSibling == 0)
Vincent Béron9a624912002-05-31 23:06:46 +00001329 ? NULL
Chris Morgan5f9fd772000-09-13 20:27:30 +00001330 : aChild->nextSibling;
1331
1332 /* Look at the next item */
1333 continue;
1334 }
1335 else if (comp == 0)
1336 {
Vincent Béron9a624912002-05-31 23:06:46 +00001337 /*
1338 * An item with this name is already existing, therefore,
1339 * we add after the one we found
Chris Morgan5f9fd772000-09-13 20:27:30 +00001340 */
1341 TREEVIEW_InsertAfter(newItem, aChild, parentItem);
1342 bItemInserted = TRUE;
1343 break;
1344 }
1345 }
1346
Vincent Béron9a624912002-05-31 23:06:46 +00001347 /*
Susan Farley036ae0b2000-10-13 20:13:38 +00001348 * we reach the end of the child list and the item has not
Chris Morgan5f9fd772000-09-13 20:27:30 +00001349 * yet been inserted, therefore, insert it after the last child.
1350 */
1351 if ((!bItemInserted) && (aChild == NULL))
1352 TREEVIEW_InsertAfter(newItem, previousChild, parentItem);
1353
1354 break;
1355 }
1356 }
1357
1358
1359 TRACE("new item %p; parent %p, mask %x\n", newItem,
1360 newItem->parent, tvItem->mask);
1361
1362 newItem->iLevel = newItem->parent->iLevel + 1;
1363
1364 if (newItem->parent->cChildren == 0)
1365 newItem->parent->cChildren = 1;
1366
1367 if (infoPtr->dwStyle & TVS_CHECKBOXES)
1368 {
1369 if (STATEIMAGEINDEX(newItem->state) == 0)
1370 newItem->state |= INDEXTOSTATEIMAGEMASK(1);
1371 }
1372
1373 if (infoPtr->firstVisible == NULL)
1374 infoPtr->firstVisible = newItem;
1375
1376 TREEVIEW_VerifyTree(infoPtr);
1377
1378 if (parentItem == infoPtr->root ||
1379 (ISVISIBLE(parentItem) && parentItem->state & TVIS_EXPANDED))
1380 {
Susan Farley8d8c87b2000-10-25 20:30:58 +00001381 TREEVIEW_ITEM *item;
Chris Morgan5f9fd772000-09-13 20:27:30 +00001382 TREEVIEW_ITEM *prev = TREEVIEW_GetPrevListItem(infoPtr, newItem);
1383
1384 TREEVIEW_RecalculateVisibleOrder(infoPtr, prev);
1385 TREEVIEW_ComputeItemInternalMetrics(infoPtr, newItem);
1386
1387 if (!bTextUpdated)
1388 TREEVIEW_UpdateDispInfo(infoPtr, newItem, TVIF_TEXT);
1389
1390 TREEVIEW_ComputeTextWidth(infoPtr, newItem, 0);
1391 TREEVIEW_UpdateScrollBars(infoPtr);
Susan Farley8d8c87b2000-10-25 20:30:58 +00001392 /*
Vincent Béron9a624912002-05-31 23:06:46 +00001393 * if the item was inserted in a visible part of the tree,
Susan Farley8d8c87b2000-10-25 20:30:58 +00001394 * invalidate it, as well as those after it
1395 */
1396 for (item = newItem;
1397 item != NULL;
1398 item = TREEVIEW_GetNextListItem(infoPtr, item))
1399 TREEVIEW_Invalidate(infoPtr, item);
Chris Morgan5f9fd772000-09-13 20:27:30 +00001400 }
1401 else
1402 {
1403 newItem->visibleOrder = -1;
1404
1405 /* refresh treeview if newItem is the first item inserted under parentItem */
1406 if (ISVISIBLE(parentItem) && newItem->prevSibling == newItem->nextSibling)
1407 {
1408 /* parent got '+' - update it */
Susan Farley8d8c87b2000-10-25 20:30:58 +00001409 TREEVIEW_Invalidate(infoPtr, parentItem);
Chris Morgan5f9fd772000-09-13 20:27:30 +00001410 }
1411 }
1412
1413 return (LRESULT)newItem;
Alexandre Julliardd30dfd21998-09-27 18:28:36 +00001414}
1415
Chris Morgan5f9fd772000-09-13 20:27:30 +00001416/* Item Deletion ************************************************************/
1417static void
1418TREEVIEW_RemoveItem(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *wineItem);
1419
1420static void
1421TREEVIEW_RemoveAllChildren(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *parentItem)
1422{
1423 TREEVIEW_ITEM *kill = parentItem->firstChild;
1424
1425 while (kill != NULL)
1426 {
1427 TREEVIEW_ITEM *next = kill->nextSibling;
1428
1429 TREEVIEW_RemoveItem(infoPtr, kill);
1430
1431 kill = next;
1432 }
1433
1434 assert(parentItem->cChildren <= 0); /* I_CHILDRENCALLBACK or 0 */
1435 assert(parentItem->firstChild == NULL);
1436 assert(parentItem->lastChild == NULL);
1437}
1438
1439static void
1440TREEVIEW_UnlinkItem(TREEVIEW_ITEM *item)
1441{
1442 TREEVIEW_ITEM *parentItem = item->parent;
1443
1444 assert(item != NULL);
1445 assert(item->parent != NULL); /* i.e. it must not be the root */
1446
1447 if (parentItem->firstChild == item)
1448 parentItem->firstChild = item->nextSibling;
1449
1450 if (parentItem->lastChild == item)
1451 parentItem->lastChild = item->prevSibling;
1452
1453 if (parentItem->firstChild == NULL && parentItem->lastChild == NULL
1454 && parentItem->cChildren > 0)
1455 parentItem->cChildren = 0;
1456
1457 if (item->prevSibling)
1458 item->prevSibling->nextSibling = item->nextSibling;
1459
1460 if (item->nextSibling)
1461 item->nextSibling->prevSibling = item->prevSibling;
1462}
1463
1464static void
1465TREEVIEW_RemoveItem(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *wineItem)
1466{
1467 TRACE("%p, (%s)\n", wineItem, TREEVIEW_ItemName(wineItem));
1468
Dimitrie O. Paund2e881d2002-11-27 20:15:49 +00001469 TREEVIEW_SendTreeviewNotify(infoPtr, TVN_DELETEITEMW, TVC_UNKNOWN,
1470 TVIF_HANDLE | TVIF_PARAM, wineItem, 0);
Chris Morgan5f9fd772000-09-13 20:27:30 +00001471
1472 if (wineItem->firstChild)
1473 TREEVIEW_RemoveAllChildren(infoPtr, wineItem);
1474
1475 TREEVIEW_UnlinkItem(wineItem);
1476
1477 infoPtr->uNumItems--;
1478
Huw Davies5693ecd2003-11-26 03:37:30 +00001479 if (wineItem->pszText && wineItem->pszText != LPSTR_TEXTCALLBACKW)
Dimitrie O. Paun7de279a2003-09-22 21:32:33 +00001480 Free(wineItem->pszText);
Chris Morgan5f9fd772000-09-13 20:27:30 +00001481
1482 TREEVIEW_FreeItem(infoPtr, wineItem);
1483}
1484
1485
1486/* Empty out the tree. */
1487static void
1488TREEVIEW_RemoveTree(TREEVIEW_INFO *infoPtr)
1489{
1490 TREEVIEW_RemoveAllChildren(infoPtr, infoPtr->root);
1491
1492 assert(infoPtr->uNumItems == 0); /* root isn't counted in uNumItems */
1493}
1494
1495static LRESULT
1496TREEVIEW_DeleteItem(TREEVIEW_INFO *infoPtr, HTREEITEM wineItem)
1497{
Huw Davies84e75472004-01-21 22:16:02 +00001498 TREEVIEW_ITEM *newSelection = NULL;
Chris Morgan5f9fd772000-09-13 20:27:30 +00001499 TREEVIEW_ITEM *newFirstVisible = NULL;
1500 TREEVIEW_ITEM *parent, *prev = NULL;
1501 BOOL visible = FALSE;
1502
1503 if (wineItem == TVI_ROOT)
1504 {
1505 TRACE("TVI_ROOT\n");
1506 parent = infoPtr->root;
1507 newSelection = NULL;
1508 visible = TRUE;
1509 TREEVIEW_RemoveTree(infoPtr);
1510 }
1511 else
1512 {
1513 if (!TREEVIEW_ValidItem(infoPtr, wineItem))
1514 return FALSE;
1515
1516 TRACE("%p (%s)\n", wineItem, TREEVIEW_ItemName(wineItem));
1517 parent = wineItem->parent;
1518
1519 if (ISVISIBLE(wineItem))
1520 {
1521 prev = TREEVIEW_GetPrevListItem(infoPtr, wineItem);
1522 visible = TRUE;
1523 }
1524
1525 if (infoPtr->selectedItem != NULL
1526 && (wineItem == infoPtr->selectedItem
1527 || TREEVIEW_IsChildOf(wineItem, infoPtr->selectedItem)))
1528 {
1529 if (wineItem->nextSibling)
1530 newSelection = wineItem->nextSibling;
1531 else if (wineItem->parent != infoPtr->root)
1532 newSelection = wineItem->parent;
Huw Davies84e75472004-01-21 22:16:02 +00001533 else
1534 newSelection = wineItem->prevSibling;
1535 TRACE("newSelection = %p\n", newSelection);
Chris Morgan5f9fd772000-09-13 20:27:30 +00001536 }
1537
1538 if (infoPtr->firstVisible == wineItem)
1539 {
1540 if (wineItem->nextSibling)
1541 newFirstVisible = wineItem->nextSibling;
1542 else if (wineItem->prevSibling)
1543 newFirstVisible = wineItem->prevSibling;
1544 else if (wineItem->parent != infoPtr->root)
1545 newFirstVisible = wineItem->parent;
Adam Gundya9822592003-05-02 20:15:36 +00001546 TREEVIEW_SetFirstVisible(infoPtr, NULL, TRUE);
Chris Morgan5f9fd772000-09-13 20:27:30 +00001547 }
1548 else
1549 newFirstVisible = infoPtr->firstVisible;
1550
1551 TREEVIEW_RemoveItem(infoPtr, wineItem);
1552 }
1553
Huw Davies84e75472004-01-21 22:16:02 +00001554 /* Don't change if somebody else already has (infoPtr->selectedItem is cleared by FreeItem). */
1555 if (!infoPtr->selectedItem && newSelection)
Chris Morgan5f9fd772000-09-13 20:27:30 +00001556 {
1557 if (TREEVIEW_ValidItem(infoPtr, newSelection))
1558 TREEVIEW_DoSelectItem(infoPtr, TVGN_CARET, newSelection, TVC_UNKNOWN);
Chris Morgan5f9fd772000-09-13 20:27:30 +00001559 }
1560
1561 /* Validate insertMark dropItem.
Susan Farley036ae0b2000-10-13 20:13:38 +00001562 * hotItem ??? - used for comparison only.
Chris Morgan5f9fd772000-09-13 20:27:30 +00001563 */
1564 if (!TREEVIEW_ValidItem(infoPtr, infoPtr->insertMarkItem))
1565 infoPtr->insertMarkItem = 0;
1566
1567 if (!TREEVIEW_ValidItem(infoPtr, infoPtr->dropItem))
1568 infoPtr->dropItem = 0;
1569
1570 if (!TREEVIEW_ValidItem(infoPtr, newFirstVisible))
1571 newFirstVisible = infoPtr->root->firstChild;
1572
1573 TREEVIEW_VerifyTree(infoPtr);
1574
1575
1576 if (visible)
1577 {
Chris Morgan5f9fd772000-09-13 20:27:30 +00001578 TREEVIEW_SetFirstVisible(infoPtr, newFirstVisible, TRUE);
Adam Gundya9822592003-05-02 20:15:36 +00001579 TREEVIEW_RecalculateVisibleOrder(infoPtr, prev);
Chris Morgan5f9fd772000-09-13 20:27:30 +00001580 TREEVIEW_UpdateScrollBars(infoPtr);
Susan Farley8d8c87b2000-10-25 20:30:58 +00001581 TREEVIEW_Invalidate(infoPtr, NULL);
Chris Morgan5f9fd772000-09-13 20:27:30 +00001582 }
1583 else if (ISVISIBLE(parent) && !TREEVIEW_HasChildren(infoPtr, parent))
1584 {
1585 /* parent lost '+/-' - update it */
Susan Farley8d8c87b2000-10-25 20:30:58 +00001586 TREEVIEW_Invalidate(infoPtr, parent);
Chris Morgan5f9fd772000-09-13 20:27:30 +00001587 }
1588
1589 return TRUE;
1590}
1591
1592
1593/* Get/Set Messages *********************************************************/
1594static LRESULT
1595TREEVIEW_SetRedraw(TREEVIEW_INFO* infoPtr, WPARAM wParam, LPARAM lParam)
1596{
1597 if(wParam)
1598 infoPtr->bRedraw = TRUE;
1599 else
1600 infoPtr->bRedraw = FALSE;
Alexandre Julliardd30dfd21998-09-27 18:28:36 +00001601
1602 return 0;
Alexandre Julliarda0d77311998-09-13 16:32:00 +00001603}
1604
Alexandre Julliarda0d77311998-09-13 16:32:00 +00001605static LRESULT
Chris Morgan5f9fd772000-09-13 20:27:30 +00001606TREEVIEW_GetIndent(TREEVIEW_INFO *infoPtr)
Alexandre Julliard829fe321998-07-26 14:27:39 +00001607{
Chris Morgan5f9fd772000-09-13 20:27:30 +00001608 TRACE("\n");
1609 return infoPtr->uIndent;
1610}
Alexandre Julliard829fe321998-07-26 14:27:39 +00001611
Chris Morgan5f9fd772000-09-13 20:27:30 +00001612static LRESULT
1613TREEVIEW_SetIndent(TREEVIEW_INFO *infoPtr, UINT newIndent)
1614{
1615 TRACE("\n");
Alexandre Julliard829fe321998-07-26 14:27:39 +00001616
Chris Morgan5f9fd772000-09-13 20:27:30 +00001617 if (newIndent < MINIMUM_INDENT)
1618 newIndent = MINIMUM_INDENT;
1619
1620 if (infoPtr->uIndent != newIndent)
1621 {
1622 infoPtr->uIndent = newIndent;
1623 TREEVIEW_UpdateSubTree(infoPtr, infoPtr->root);
1624 TREEVIEW_UpdateScrollBars(infoPtr);
Susan Farley8d8c87b2000-10-25 20:30:58 +00001625 TREEVIEW_Invalidate(infoPtr, NULL);
Alexandre Julliarda0d77311998-09-13 16:32:00 +00001626 }
1627
Chris Morgan5f9fd772000-09-13 20:27:30 +00001628 return 0;
Alexandre Julliard829fe321998-07-26 14:27:39 +00001629}
1630
1631
Alexandre Julliard829fe321998-07-26 14:27:39 +00001632static LRESULT
Chris Morgan5f9fd772000-09-13 20:27:30 +00001633TREEVIEW_GetToolTips(TREEVIEW_INFO *infoPtr)
Alexandre Julliard829fe321998-07-26 14:27:39 +00001634{
Chris Morgan5f9fd772000-09-13 20:27:30 +00001635 TRACE("\n");
Michael Stefaniuc025c0b72002-09-06 19:41:17 +00001636 return (LRESULT)infoPtr->hwndToolTip;
Alexandre Julliardd30dfd21998-09-27 18:28:36 +00001637}
1638
1639static LRESULT
Chris Morgan5f9fd772000-09-13 20:27:30 +00001640TREEVIEW_SetToolTips(TREEVIEW_INFO *infoPtr, HWND hwndTT)
Alexandre Julliardd30dfd21998-09-27 18:28:36 +00001641{
Chris Morgan5f9fd772000-09-13 20:27:30 +00001642 HWND prevToolTip;
Alex Prieme74f2191999-07-18 18:31:05 +00001643
Chris Morgan5f9fd772000-09-13 20:27:30 +00001644 TRACE("\n");
1645 prevToolTip = infoPtr->hwndToolTip;
1646 infoPtr->hwndToolTip = hwndTT;
1647
Michael Stefaniuc025c0b72002-09-06 19:41:17 +00001648 return (LRESULT)prevToolTip;
Chris Morgan5f9fd772000-09-13 20:27:30 +00001649}
1650
Huw Davies5693ecd2003-11-26 03:37:30 +00001651static LRESULT
1652TREEVIEW_SetUnicodeFormat(TREEVIEW_INFO *infoPtr, BOOL fUnicode)
1653{
1654 BOOL rc = infoPtr->bNtfUnicode;
1655 infoPtr->bNtfUnicode = fUnicode;
1656 return rc;
1657}
1658
1659static LRESULT
1660TREEVIEW_GetUnicodeFormat(TREEVIEW_INFO *infoPtr)
1661{
1662 return infoPtr->bNtfUnicode;
1663}
Chris Morgan5f9fd772000-09-13 20:27:30 +00001664
1665static LRESULT
1666TREEVIEW_GetScrollTime(TREEVIEW_INFO *infoPtr)
1667{
1668 return infoPtr->uScrollTime;
Alex Prieme74f2191999-07-18 18:31:05 +00001669}
1670
1671static LRESULT
Chris Morgan5f9fd772000-09-13 20:27:30 +00001672TREEVIEW_SetScrollTime(TREEVIEW_INFO *infoPtr, UINT uScrollTime)
Alex Prieme74f2191999-07-18 18:31:05 +00001673{
Chris Morgan5f9fd772000-09-13 20:27:30 +00001674 UINT uOldScrollTime = infoPtr->uScrollTime;
Alex Prieme74f2191999-07-18 18:31:05 +00001675
Chris Morgan5f9fd772000-09-13 20:27:30 +00001676 infoPtr->uScrollTime = min(uScrollTime, 100);
1677
1678 return uOldScrollTime;
1679}
1680
1681
1682static LRESULT
1683TREEVIEW_GetImageList(TREEVIEW_INFO *infoPtr, WPARAM wParam)
1684{
1685 TRACE("\n");
1686
1687 switch (wParam)
1688 {
1689 case (WPARAM)TVSIL_NORMAL:
1690 return (LRESULT)infoPtr->himlNormal;
1691
1692 case (WPARAM)TVSIL_STATE:
1693 return (LRESULT)infoPtr->himlState;
1694
1695 default:
1696 return 0;
1697 }
Alex Prieme74f2191999-07-18 18:31:05 +00001698}
1699
Jon Griffithsf479f102004-09-28 02:35:48 +00001700#define TVHEIGHT_MIN 16
1701#define TVHEIGHT_FONT_ADJUST 3 /* 2 for focus border + 1 for margin some apps assume */
1702
1703/* Compute the natural height for items. */
1704static UINT
1705TREEVIEW_NaturalHeight(TREEVIEW_INFO *infoPtr)
1706{
1707 TEXTMETRICW tm;
1708 HDC hdc = GetDC(0);
1709 HFONT hOldFont = SelectObject(hdc, infoPtr->hFont);
1710 UINT height;
1711
1712 /* Height is the maximum of:
1713 * 16 (a hack because our fonts are tiny), and
1714 * The text height + border & margin, and
1715 * The size of the normal image list
1716 */
1717 GetTextMetricsW(hdc, &tm);
1718 SelectObject(hdc, hOldFont);
1719 ReleaseDC(0, hdc);
1720
1721 height = TVHEIGHT_MIN;
1722 if (height < tm.tmHeight + tm.tmExternalLeading + TVHEIGHT_FONT_ADJUST)
1723 height = tm.tmHeight + tm.tmExternalLeading + TVHEIGHT_FONT_ADJUST;
1724 if (height < infoPtr->normalImageHeight)
1725 height = infoPtr->normalImageHeight;
1726 return height;
1727}
1728
Alex Prieme74f2191999-07-18 18:31:05 +00001729static LRESULT
Chris Morgan5f9fd772000-09-13 20:27:30 +00001730TREEVIEW_SetImageList(TREEVIEW_INFO *infoPtr, WPARAM wParam, HIMAGELIST himlNew)
Alex Prieme6e71ad1999-11-28 19:50:01 +00001731{
Chris Morgan5f9fd772000-09-13 20:27:30 +00001732 HIMAGELIST himlOld = 0;
1733 int oldWidth = infoPtr->normalImageWidth;
1734 int oldHeight = infoPtr->normalImageHeight;
Alex Priemd2d7f961999-01-30 13:02:30 +00001735
1736
Chris Morgan5f9fd772000-09-13 20:27:30 +00001737 TRACE("%x,%p\n", wParam, himlNew);
Alex Priemd2d7f961999-01-30 13:02:30 +00001738
Chris Morgan5f9fd772000-09-13 20:27:30 +00001739 switch (wParam)
1740 {
1741 case (WPARAM)TVSIL_NORMAL:
1742 himlOld = infoPtr->himlNormal;
1743 infoPtr->himlNormal = himlNew;
Sylvain St.Germain99b118a1999-03-25 10:55:01 +00001744
Chris Morgan5f9fd772000-09-13 20:27:30 +00001745 if (himlNew != NULL)
1746 ImageList_GetIconSize(himlNew, &infoPtr->normalImageWidth,
1747 &infoPtr->normalImageHeight);
1748 else
1749 {
1750 infoPtr->normalImageWidth = 0;
1751 infoPtr->normalImageHeight = 0;
Alex Priemd2d7f961999-01-30 13:02:30 +00001752 }
Sylvain St.Germain02300ae1999-03-16 10:38:44 +00001753
Chris Morgan5f9fd772000-09-13 20:27:30 +00001754 break;
Sylvain St.Germain99b118a1999-03-25 10:55:01 +00001755
Chris Morgan5f9fd772000-09-13 20:27:30 +00001756 case (WPARAM)TVSIL_STATE:
1757 himlOld = infoPtr->himlState;
1758 infoPtr->himlState = himlNew;
Sylvain St.Germain02300ae1999-03-16 10:38:44 +00001759
Chris Morgan5f9fd772000-09-13 20:27:30 +00001760 if (himlNew != NULL)
1761 ImageList_GetIconSize(himlNew, &infoPtr->stateImageWidth,
1762 &infoPtr->stateImageHeight);
1763 else
1764 {
1765 infoPtr->stateImageWidth = 0;
1766 infoPtr->stateImageHeight = 0;
Alex Prieme6e71ad1999-11-28 19:50:01 +00001767 }
1768
Chris Morgan5f9fd772000-09-13 20:27:30 +00001769 break;
1770 }
Alex Priemd2d7f961999-01-30 13:02:30 +00001771
Chris Morgan5f9fd772000-09-13 20:27:30 +00001772 if (oldWidth != infoPtr->normalImageWidth ||
1773 oldHeight != infoPtr->normalImageHeight)
Alexandre Julliard15d9a602000-08-01 23:31:17 +00001774 {
Francois Gouget67aa8582004-10-05 18:15:29 +00001775 BOOL bRecalcVisible = FALSE;
Jon Griffithsf479f102004-09-28 02:35:48 +00001776
1777 if (oldHeight != infoPtr->normalImageHeight &&
1778 !infoPtr->bHeightSet)
1779 {
1780 infoPtr->uItemHeight = TREEVIEW_NaturalHeight(infoPtr);
Francois Gouget67aa8582004-10-05 18:15:29 +00001781 bRecalcVisible = TRUE;
Jon Griffithsf479f102004-09-28 02:35:48 +00001782 }
1783
1784 if (infoPtr->normalImageWidth > MINIMUM_INDENT &&
1785 infoPtr->normalImageWidth != infoPtr->uIndent)
1786 {
1787 infoPtr->uIndent = infoPtr->normalImageWidth;
Francois Gouget67aa8582004-10-05 18:15:29 +00001788 bRecalcVisible = TRUE;
Jon Griffithsf479f102004-09-28 02:35:48 +00001789 }
1790
Francois Gouget67aa8582004-10-05 18:15:29 +00001791 if (bRecalcVisible)
Jon Griffithsf479f102004-09-28 02:35:48 +00001792 TREEVIEW_RecalculateVisibleOrder(infoPtr, NULL);
1793
Chris Morgan5f9fd772000-09-13 20:27:30 +00001794 TREEVIEW_UpdateSubTree(infoPtr, infoPtr->root);
1795 TREEVIEW_UpdateScrollBars(infoPtr);
1796 }
Alexandre Julliard15d9a602000-08-01 23:31:17 +00001797
Susan Farley8d8c87b2000-10-25 20:30:58 +00001798 TREEVIEW_Invalidate(infoPtr, NULL);
Chris Morgan5f9fd772000-09-13 20:27:30 +00001799
1800 return (LRESULT)himlOld;
1801}
1802
Chris Morgan5f9fd772000-09-13 20:27:30 +00001803static LRESULT
1804TREEVIEW_SetItemHeight(TREEVIEW_INFO *infoPtr, INT newHeight)
1805{
1806 INT prevHeight = infoPtr->uItemHeight;
1807
Francois Gouget0edbaf72005-11-10 12:14:56 +00001808 TRACE("%d\n", newHeight);
Chris Morgan5f9fd772000-09-13 20:27:30 +00001809 if (newHeight == -1)
1810 {
1811 infoPtr->uItemHeight = TREEVIEW_NaturalHeight(infoPtr);
1812 infoPtr->bHeightSet = FALSE;
Alexandre Julliard15d9a602000-08-01 23:31:17 +00001813 }
1814 else
1815 {
Chris Morgan5f9fd772000-09-13 20:27:30 +00001816 infoPtr->uItemHeight = newHeight;
1817 infoPtr->bHeightSet = TRUE;
Alexandre Julliard15d9a602000-08-01 23:31:17 +00001818 }
Alexandre Julliardd30dfd21998-09-27 18:28:36 +00001819
Chris Morgan5f9fd772000-09-13 20:27:30 +00001820 /* Round down, unless we support odd ("non even") heights. */
1821 if (!(infoPtr->dwStyle) & TVS_NONEVENHEIGHT)
1822 infoPtr->uItemHeight &= ~1;
David Grant977392c1999-11-07 05:36:44 +00001823
Chris Morgan5f9fd772000-09-13 20:27:30 +00001824 if (infoPtr->uItemHeight != prevHeight)
1825 {
1826 TREEVIEW_RecalculateVisibleOrder(infoPtr, NULL);
1827 TREEVIEW_UpdateScrollBars(infoPtr);
Susan Farley8d8c87b2000-10-25 20:30:58 +00001828 TREEVIEW_Invalidate(infoPtr, NULL);
Chris Morgan5f9fd772000-09-13 20:27:30 +00001829 }
1830
1831 return prevHeight;
Alexandre Julliardd30dfd21998-09-27 18:28:36 +00001832}
1833
Alex Prieme74f2191999-07-18 18:31:05 +00001834static LRESULT
Chris Morgan5f9fd772000-09-13 20:27:30 +00001835TREEVIEW_GetItemHeight(TREEVIEW_INFO *infoPtr)
Alex Prieme74f2191999-07-18 18:31:05 +00001836{
Alex Prieme6e71ad1999-11-28 19:50:01 +00001837 TRACE("\n");
Chris Morgan5f9fd772000-09-13 20:27:30 +00001838 return infoPtr->uItemHeight;
Alex Prieme74f2191999-07-18 18:31:05 +00001839}
1840
Alexandre Julliardd30dfd21998-09-27 18:28:36 +00001841
Chris Morgan5f9fd772000-09-13 20:27:30 +00001842static LRESULT
1843TREEVIEW_GetFont(TREEVIEW_INFO *infoPtr)
Alexandre Julliardd30dfd21998-09-27 18:28:36 +00001844{
Michael Stefaniuc353529b2002-10-23 22:19:10 +00001845 TRACE("%p\n", infoPtr->hFont);
Michael Stefaniucf3d18932002-10-23 20:19:22 +00001846 return (LRESULT)infoPtr->hFont;
Chris Morgan5f9fd772000-09-13 20:27:30 +00001847}
Susan Farley1da10fd2000-08-01 22:01:11 +00001848
Chris Morgan5f9fd772000-09-13 20:27:30 +00001849
1850static INT CALLBACK
Alexandre Julliardf5cb3dd2003-09-17 20:15:21 +00001851TREEVIEW_ResetTextWidth(LPVOID pItem, LPVOID unused)
Chris Morgan5f9fd772000-09-13 20:27:30 +00001852{
1853 (void)unused;
1854
1855 ((TREEVIEW_ITEM *)pItem)->textWidth = 0;
1856
1857 return 1;
1858}
1859
1860static LRESULT
1861TREEVIEW_SetFont(TREEVIEW_INFO *infoPtr, HFONT hFont, BOOL bRedraw)
1862{
1863 UINT uHeight = infoPtr->uItemHeight;
1864
Michael Stefaniuc353529b2002-10-23 22:19:10 +00001865 TRACE("%p %i\n", hFont, bRedraw);
Chris Morgan5f9fd772000-09-13 20:27:30 +00001866
Huw Davies3138b4a2004-11-21 15:41:08 +00001867 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
Chris Morgan5f9fd772000-09-13 20:27:30 +00001868
1869 DeleteObject(infoPtr->hBoldFont);
1870 infoPtr->hBoldFont = TREEVIEW_CreateBoldFont(infoPtr->hFont);
Robert Shearman1e92f4a2004-12-17 18:52:04 +00001871 infoPtr->hUnderlineFont = TREEVIEW_CreateUnderlineFont(infoPtr->hFont);
Chris Morgan5f9fd772000-09-13 20:27:30 +00001872
1873 if (!infoPtr->bHeightSet)
1874 infoPtr->uItemHeight = TREEVIEW_NaturalHeight(infoPtr);
1875
1876 if (uHeight != infoPtr->uItemHeight)
1877 TREEVIEW_RecalculateVisibleOrder(infoPtr, NULL);
1878
1879 DPA_EnumCallback(infoPtr->items, TREEVIEW_ResetTextWidth, 0);
1880
1881 TREEVIEW_UpdateSubTree(infoPtr, infoPtr->root);
1882 TREEVIEW_UpdateScrollBars(infoPtr);
1883
1884 if (bRedraw)
Susan Farley8d8c87b2000-10-25 20:30:58 +00001885 TREEVIEW_Invalidate(infoPtr, NULL);
Chris Morgan5f9fd772000-09-13 20:27:30 +00001886
1887 return 0;
1888}
1889
1890
1891static LRESULT
1892TREEVIEW_GetLineColor(TREEVIEW_INFO *infoPtr)
1893{
1894 TRACE("\n");
1895 return (LRESULT)infoPtr->clrLine;
1896}
1897
1898static LRESULT
1899TREEVIEW_SetLineColor(TREEVIEW_INFO *infoPtr, COLORREF color)
1900{
1901 COLORREF prevColor = infoPtr->clrLine;
Alexandre Julliardd30dfd21998-09-27 18:28:36 +00001902
Alexandre Julliarda099a551999-06-12 15:45:58 +00001903 TRACE("\n");
Chris Morgan5f9fd772000-09-13 20:27:30 +00001904 infoPtr->clrLine = color;
1905 return (LRESULT)prevColor;
Alexandre Julliardd30dfd21998-09-27 18:28:36 +00001906}
1907
1908
Eric Kohlb7b4aed2000-06-03 21:05:47 +00001909static LRESULT
Chris Morgan5f9fd772000-09-13 20:27:30 +00001910TREEVIEW_GetTextColor(TREEVIEW_INFO *infoPtr)
Eric Kohlb7b4aed2000-06-03 21:05:47 +00001911{
Chris Morgan5f9fd772000-09-13 20:27:30 +00001912 TRACE("\n");
1913 return (LRESULT)infoPtr->clrText;
Eric Kohlb7b4aed2000-06-03 21:05:47 +00001914}
1915
Alexandre Julliardd30dfd21998-09-27 18:28:36 +00001916static LRESULT
Chris Morgan5f9fd772000-09-13 20:27:30 +00001917TREEVIEW_SetTextColor(TREEVIEW_INFO *infoPtr, COLORREF color)
Alexandre Julliardd30dfd21998-09-27 18:28:36 +00001918{
Chris Morgan5f9fd772000-09-13 20:27:30 +00001919 COLORREF prevColor = infoPtr->clrText;
Alexandre Julliardd30dfd21998-09-27 18:28:36 +00001920
Chris Morgan5f9fd772000-09-13 20:27:30 +00001921 TRACE("\n");
1922 infoPtr->clrText = color;
Huw D M Davies0c3594a2000-04-13 15:56:09 +00001923
Chris Morgan5f9fd772000-09-13 20:27:30 +00001924 if (infoPtr->clrText != prevColor)
Susan Farley8d8c87b2000-10-25 20:30:58 +00001925 TREEVIEW_Invalidate(infoPtr, NULL);
Huw D M Davies0c3594a2000-04-13 15:56:09 +00001926
Chris Morgan5f9fd772000-09-13 20:27:30 +00001927 return (LRESULT)prevColor;
Alexandre Julliard829fe321998-07-26 14:27:39 +00001928}
1929
1930
1931static LRESULT
Chris Morgan5f9fd772000-09-13 20:27:30 +00001932TREEVIEW_GetBkColor(TREEVIEW_INFO *infoPtr)
Alexandre Julliard829fe321998-07-26 14:27:39 +00001933{
Chris Morgan5f9fd772000-09-13 20:27:30 +00001934 TRACE("\n");
1935 return (LRESULT)infoPtr->clrBk;
Alexandre Julliardd30dfd21998-09-27 18:28:36 +00001936}
Alexandre Julliard829fe321998-07-26 14:27:39 +00001937
Chris Morgan5f9fd772000-09-13 20:27:30 +00001938static LRESULT
1939TREEVIEW_SetBkColor(TREEVIEW_INFO *infoPtr, COLORREF newColor)
1940{
1941 COLORREF prevColor = infoPtr->clrBk;
1942
1943 TRACE("\n");
1944 infoPtr->clrBk = newColor;
1945
1946 if (newColor != prevColor)
Susan Farley8d8c87b2000-10-25 20:30:58 +00001947 TREEVIEW_Invalidate(infoPtr, NULL);
Chris Morgan5f9fd772000-09-13 20:27:30 +00001948
1949 return (LRESULT)prevColor;
1950}
1951
1952
1953static LRESULT
1954TREEVIEW_GetInsertMarkColor(TREEVIEW_INFO *infoPtr)
1955{
1956 TRACE("\n");
1957 return (LRESULT)infoPtr->clrInsertMark;
1958}
1959
1960static LRESULT
1961TREEVIEW_SetInsertMarkColor(TREEVIEW_INFO *infoPtr, COLORREF color)
1962{
1963 COLORREF prevColor = infoPtr->clrInsertMark;
1964
1965 TRACE("%lx\n", color);
1966 infoPtr->clrInsertMark = color;
1967
1968 return (LRESULT)prevColor;
1969}
1970
1971
1972static LRESULT
1973TREEVIEW_SetInsertMark(TREEVIEW_INFO *infoPtr, BOOL wParam, HTREEITEM item)
1974{
1975 TRACE("%d %p\n", wParam, item);
1976
1977 if (!TREEVIEW_ValidItem(infoPtr, item))
1978 return 0;
1979
1980 infoPtr->insertBeforeorAfter = wParam;
1981 infoPtr->insertMarkItem = item;
1982
Susan Farley8d8c87b2000-10-25 20:30:58 +00001983 TREEVIEW_Invalidate(infoPtr, NULL);
Chris Morgan5f9fd772000-09-13 20:27:30 +00001984
1985 return 1;
1986}
1987
1988
1989/************************************************************************
1990 * Some serious braindamage here. lParam is a pointer to both the
1991 * input HTREEITEM and the output RECT.
Sylvain St.Germain99b118a1999-03-25 10:55:01 +00001992 */
Chris Morgan5f9fd772000-09-13 20:27:30 +00001993static LRESULT
1994TREEVIEW_GetItemRect(TREEVIEW_INFO *infoPtr, BOOL fTextRect, LPRECT lpRect)
Sylvain St.Germain99b118a1999-03-25 10:55:01 +00001995{
Chris Morgan5f9fd772000-09-13 20:27:30 +00001996 TREEVIEW_ITEM *wineItem;
1997 const HTREEITEM *pItem = (HTREEITEM *)lpRect;
Alexandre Julliard829fe321998-07-26 14:27:39 +00001998
Chris Morgan5f9fd772000-09-13 20:27:30 +00001999 TRACE("\n");
Sylvain St.Germain99b118a1999-03-25 10:55:01 +00002000 /*
Chris Morgan5f9fd772000-09-13 20:27:30 +00002001 * validate parameters
2002 */
2003 if (pItem == NULL)
2004 return FALSE;
2005
2006 wineItem = *pItem;
2007 if (!TREEVIEW_ValidItem(infoPtr, wineItem) || !ISVISIBLE(wineItem))
2008 return FALSE;
2009
Vincent Béron9a624912002-05-31 23:06:46 +00002010 /*
2011 * If wParam is TRUE return the text size otherwise return
2012 * the whole item size
Chris Morgan5f9fd772000-09-13 20:27:30 +00002013 */
2014 if (fTextRect)
2015 {
2016 /* Windows does not send TVN_GETDISPINFO here. */
2017
2018 lpRect->top = wineItem->rect.top;
2019 lpRect->bottom = wineItem->rect.bottom;
2020
2021 lpRect->left = wineItem->textOffset;
2022 lpRect->right = wineItem->textOffset + wineItem->textWidth;
2023 }
2024 else
2025 {
2026 *lpRect = wineItem->rect;
2027 }
2028
Dan Kegel0fd521f2003-01-08 21:09:25 +00002029 TRACE("%s [L:%ld R:%ld T:%ld B:%ld]\n", fTextRect ? "text" : "item",
Chris Morgan5f9fd772000-09-13 20:27:30 +00002030 lpRect->left, lpRect->right, lpRect->top, lpRect->bottom);
2031
2032 return TRUE;
Sylvain St.Germain99b118a1999-03-25 10:55:01 +00002033}
2034
Chris Morgan5f9fd772000-09-13 20:27:30 +00002035static inline LRESULT
2036TREEVIEW_GetVisibleCount(TREEVIEW_INFO *infoPtr)
2037{
2038 /* Suprise! This does not take integral height into account. */
2039 return infoPtr->clientHeight / infoPtr->uItemHeight;
2040}
2041
2042
2043static LRESULT
Huw Davies96b90572003-11-26 22:26:19 +00002044TREEVIEW_GetItemT(TREEVIEW_INFO *infoPtr, LPTVITEMEXW tvItem, BOOL isW)
Chris Morgan5f9fd772000-09-13 20:27:30 +00002045{
2046 TREEVIEW_ITEM *wineItem;
2047
2048 wineItem = tvItem->hItem;
2049 if (!TREEVIEW_ValidItem(infoPtr, wineItem))
2050 return FALSE;
2051
2052 TREEVIEW_UpdateDispInfo(infoPtr, wineItem, tvItem->mask);
2053
2054 if (tvItem->mask & TVIF_CHILDREN)
Huw Davies96b90572003-11-26 22:26:19 +00002055 {
Filip Navara317d6c52004-09-23 22:51:29 +00002056 if (wineItem->cChildren==I_CHILDRENCALLBACK)
Huw Davies96b90572003-11-26 22:26:19 +00002057 FIXME("I_CHILDRENCALLBACK not supported\n");
Chris Morgan5f9fd772000-09-13 20:27:30 +00002058 tvItem->cChildren = wineItem->cChildren;
Huw Davies96b90572003-11-26 22:26:19 +00002059 }
Chris Morgan5f9fd772000-09-13 20:27:30 +00002060
2061 if (tvItem->mask & TVIF_HANDLE)
2062 tvItem->hItem = wineItem;
2063
2064 if (tvItem->mask & TVIF_IMAGE)
2065 tvItem->iImage = wineItem->iImage;
2066
2067 if (tvItem->mask & TVIF_INTEGRAL)
2068 tvItem->iIntegral = wineItem->iIntegral;
2069
2070 /* undocumented: windows ignores TVIF_PARAM and
2071 * * always sets lParam
2072 */
2073 tvItem->lParam = wineItem->lParam;
2074
2075 if (tvItem->mask & TVIF_SELECTEDIMAGE)
2076 tvItem->iSelectedImage = wineItem->iSelectedImage;
2077
Huw Davies96b90572003-11-26 22:26:19 +00002078 if (tvItem->mask & TVIF_STATE)
Mike Hearnb27587d2003-03-26 23:35:50 +00002079 /* Careful here - Windows ignores the stateMask when you get the state
2080 That contradicts the documentation, but makes more common sense, masking
2081 retrieval in this way seems overkill */
2082 tvItem->state = wineItem->state;
Chris Morgan5f9fd772000-09-13 20:27:30 +00002083
2084 if (tvItem->mask & TVIF_TEXT)
Huw Davies96b90572003-11-26 22:26:19 +00002085 {
2086 if (isW)
2087 {
2088 if (wineItem->pszText == LPSTR_TEXTCALLBACKW)
2089 {
2090 tvItem->pszText = LPSTR_TEXTCALLBACKW;
2091 FIXME(" GetItem called with LPSTR_TEXTCALLBACK\n");
2092 }
2093 else
2094 {
2095 lstrcpynW(tvItem->pszText, wineItem->pszText, tvItem->cchTextMax);
2096 }
2097 }
2098 else
2099 {
2100 if (wineItem->pszText == LPSTR_TEXTCALLBACKW)
2101 {
2102 tvItem->pszText = (LPWSTR)LPSTR_TEXTCALLBACKA;
2103 FIXME(" GetItem called with LPSTR_TEXTCALLBACK\n");
2104 }
2105 else
2106 {
2107 WideCharToMultiByte(CP_ACP, 0, wineItem->pszText, -1,
2108 (LPSTR)tvItem->pszText, tvItem->cchTextMax, NULL, NULL);
2109 }
2110 }
2111 }
Chris Morgan5f9fd772000-09-13 20:27:30 +00002112 TRACE("item <%p>, txt %p, img %p, mask %x\n",
2113 wineItem, tvItem->pszText, &tvItem->iImage, tvItem->mask);
2114
2115 return TRUE;
2116}
2117
2118/* Beware MSDN Library Visual Studio 6.0. It says -1 on failure, 0 on success,
2119 * which is wrong. */
2120static LRESULT
Huw Davies6cfbf892003-11-27 01:11:01 +00002121TREEVIEW_SetItemT(TREEVIEW_INFO *infoPtr, LPTVITEMEXW tvItem, BOOL isW)
Chris Morgan5f9fd772000-09-13 20:27:30 +00002122{
2123 TREEVIEW_ITEM *wineItem;
Chris Morgan36c8db82000-09-22 20:48:15 +00002124 TREEVIEW_ITEM originalItem;
Chris Morgan5f9fd772000-09-13 20:27:30 +00002125
2126 wineItem = tvItem->hItem;
Chris Morgan36c8db82000-09-22 20:48:15 +00002127
Chris Morgan5f9fd772000-09-13 20:27:30 +00002128 TRACE("item %d,mask %x\n", TREEVIEW_GetItemIndex(infoPtr, wineItem),
2129 tvItem->mask);
2130
2131 if (!TREEVIEW_ValidItem(infoPtr, wineItem))
2132 return FALSE;
Jon Griffithsf479f102004-09-28 02:35:48 +00002133
Mike Hearn044a3532003-03-26 01:22:10 +00002134 /* store the orignal item values */
2135 originalItem = *wineItem;
Chris Morgan5f9fd772000-09-13 20:27:30 +00002136
Huw Davies6cfbf892003-11-27 01:11:01 +00002137 if (!TREEVIEW_DoSetItemT(infoPtr, wineItem, tvItem, isW))
Chris Morgan5f9fd772000-09-13 20:27:30 +00002138 return FALSE;
2139
2140 /* If the text or TVIS_BOLD was changed, and it is visible, recalculate. */
2141 if ((tvItem->mask & TVIF_TEXT
2142 || (tvItem->mask & TVIF_STATE && tvItem->stateMask & TVIS_BOLD))
2143 && ISVISIBLE(wineItem))
2144 {
2145 TREEVIEW_UpdateDispInfo(infoPtr, wineItem, TVIF_TEXT);
2146 TREEVIEW_ComputeTextWidth(infoPtr, wineItem, 0);
2147 }
2148
2149 if (tvItem->mask != 0 && ISVISIBLE(wineItem))
2150 {
2151 /* The refresh updates everything, but we can't wait until then. */
2152 TREEVIEW_ComputeItemInternalMetrics(infoPtr, wineItem);
2153
Vitaliy Margolen71be0942005-04-18 10:20:51 +00002154 /* if any of the item's values changed and it's not a callback, redraw the item */
2155 if (item_changed(&originalItem, wineItem, tvItem))
Chris Morgan36c8db82000-09-22 20:48:15 +00002156 {
2157 if (tvItem->mask & TVIF_INTEGRAL)
2158 {
2159 TREEVIEW_RecalculateVisibleOrder(infoPtr, wineItem);
2160 TREEVIEW_UpdateScrollBars(infoPtr);
Chris Morgan5f9fd772000-09-13 20:27:30 +00002161
Susan Farley8d8c87b2000-10-25 20:30:58 +00002162 TREEVIEW_Invalidate(infoPtr, NULL);
Chris Morgan36c8db82000-09-22 20:48:15 +00002163 }
2164 else
2165 {
2166 TREEVIEW_UpdateScrollBars(infoPtr);
Susan Farley8d8c87b2000-10-25 20:30:58 +00002167 TREEVIEW_Invalidate(infoPtr, wineItem);
Chris Morgan36c8db82000-09-22 20:48:15 +00002168 }
2169 }
Chris Morgan5f9fd772000-09-13 20:27:30 +00002170 }
2171
2172 return TRUE;
2173}
2174
2175static LRESULT
Chris Morgan5f9fd772000-09-13 20:27:30 +00002176TREEVIEW_GetItemState(TREEVIEW_INFO *infoPtr, HTREEITEM wineItem, UINT mask)
2177{
2178 TRACE("\n");
2179
2180 if (!wineItem || !TREEVIEW_ValidItem(infoPtr, wineItem))
2181 return 0;
2182
2183 return (wineItem->state & mask);
2184}
2185
2186static LRESULT
2187TREEVIEW_GetNextItem(TREEVIEW_INFO *infoPtr, UINT which, HTREEITEM wineItem)
2188{
2189 TREEVIEW_ITEM *retval;
2190
2191 retval = 0;
2192
2193 /* handle all the global data here */
2194 switch (which)
2195 {
2196 case TVGN_CHILD: /* Special case: child of 0 is root */
2197 if (wineItem)
2198 break;
2199 /* fall through */
2200 case TVGN_ROOT:
2201 retval = infoPtr->root->firstChild;
2202 break;
2203
2204 case TVGN_CARET:
2205 retval = infoPtr->selectedItem;
2206 break;
2207
2208 case TVGN_FIRSTVISIBLE:
2209 retval = infoPtr->firstVisible;
2210 break;
2211
2212 case TVGN_DROPHILITE:
2213 retval = infoPtr->dropItem;
2214 break;
2215
2216 case TVGN_LASTVISIBLE:
2217 retval = TREEVIEW_GetLastListItem(infoPtr, infoPtr->root);
2218 break;
2219 }
2220
2221 if (retval)
2222 {
2223 TRACE("flags:%x, returns %p\n", which, retval);
2224 return (LRESULT)retval;
2225 }
2226
Bill Medland32ca21ca2001-12-11 00:15:11 +00002227 if (wineItem == TVI_ROOT) wineItem = infoPtr->root;
2228
Chris Morgan5f9fd772000-09-13 20:27:30 +00002229 if (!TREEVIEW_ValidItem(infoPtr, wineItem))
2230 return FALSE;
2231
2232 switch (which)
2233 {
2234 case TVGN_NEXT:
2235 retval = wineItem->nextSibling;
2236 break;
2237 case TVGN_PREVIOUS:
2238 retval = wineItem->prevSibling;
2239 break;
2240 case TVGN_PARENT:
2241 retval = (wineItem->parent != infoPtr->root) ? wineItem->parent : NULL;
2242 break;
2243 case TVGN_CHILD:
2244 retval = wineItem->firstChild;
2245 break;
2246 case TVGN_NEXTVISIBLE:
2247 retval = TREEVIEW_GetNextListItem(infoPtr, wineItem);
2248 break;
2249 case TVGN_PREVIOUSVISIBLE:
2250 retval = TREEVIEW_GetPrevListItem(infoPtr, wineItem);
2251 break;
2252 default:
2253 TRACE("Unknown msg %x,item %p\n", which, wineItem);
2254 break;
2255 }
2256
2257 TRACE("flags:%x, item %p;returns %p\n", which, wineItem, retval);
2258 return (LRESULT)retval;
2259}
2260
2261
2262static LRESULT
2263TREEVIEW_GetCount(TREEVIEW_INFO *infoPtr)
2264{
2265 TRACE(" %d\n", infoPtr->uNumItems);
2266 return (LRESULT)infoPtr->uNumItems;
2267}
2268
2269static VOID
2270TREEVIEW_ToggleItemState(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item)
2271{
2272 if (infoPtr->dwStyle & TVS_CHECKBOXES)
2273 {
2274 static const unsigned int state_table[] = { 0, 2, 1 };
2275
2276 unsigned int state;
2277
2278 state = STATEIMAGEINDEX(item->state);
2279 TRACE("state:%x\n", state);
2280 item->state &= ~TVIS_STATEIMAGEMASK;
2281
2282 if (state < 3)
2283 state = state_table[state];
2284
2285 item->state |= INDEXTOSTATEIMAGEMASK(state);
2286
2287 TRACE("state:%x\n", state);
Susan Farley8d8c87b2000-10-25 20:30:58 +00002288 TREEVIEW_Invalidate(infoPtr, item);
Chris Morgan5f9fd772000-09-13 20:27:30 +00002289 }
2290}
2291
2292
2293/* Painting *************************************************************/
2294
2295/* Draw the lines and expand button for an item. Also draws one section
2296 * of the line from item's parent to item's parent's next sibling. */
2297static void
2298TREEVIEW_DrawItemLines(TREEVIEW_INFO *infoPtr, HDC hdc, TREEVIEW_ITEM *item)
2299{
2300 LONG centerx, centery;
2301 BOOL lar = ((infoPtr->dwStyle
2302 & (TVS_LINESATROOT|TVS_HASLINES|TVS_HASBUTTONS))
2303 > TVS_LINESATROOT);
Vitaliy Margolen71be0942005-04-18 10:20:51 +00002304 HBRUSH hbr, hbrOld;
Chris Morgan5f9fd772000-09-13 20:27:30 +00002305
2306 if (!lar && item->iLevel == 0)
2307 return;
2308
Vitaliy Margolen71be0942005-04-18 10:20:51 +00002309 hbr = CreateSolidBrush(infoPtr->clrBk);
2310 hbrOld = SelectObject(hdc, hbr);
2311
Chris Morgan5f9fd772000-09-13 20:27:30 +00002312 centerx = (item->linesOffset + item->stateOffset) / 2;
2313 centery = (item->rect.top + item->rect.bottom) / 2;
2314
2315 if (infoPtr->dwStyle & TVS_HASLINES)
2316 {
2317 HPEN hOldPen, hNewPen;
2318 HTREEITEM parent;
2319
Vincent Béron9a624912002-05-31 23:06:46 +00002320 /*
Chris Morgan5f9fd772000-09-13 20:27:30 +00002321 * Get a dotted grey pen
2322 */
Francois Gouget7203ee62000-10-31 02:05:11 +00002323 hNewPen = CreatePen(PS_ALTERNATE, 0, infoPtr->clrLine);
Chris Morgan5f9fd772000-09-13 20:27:30 +00002324 hOldPen = SelectObject(hdc, hNewPen);
2325
2326 MoveToEx(hdc, item->stateOffset, centery, NULL);
2327 LineTo(hdc, centerx - 1, centery);
2328
2329 if (item->prevSibling || item->parent != infoPtr->root)
2330 {
2331 MoveToEx(hdc, centerx, item->rect.top, NULL);
2332 LineTo(hdc, centerx, centery);
2333 }
2334
2335 if (item->nextSibling)
2336 {
2337 MoveToEx(hdc, centerx, centery, NULL);
2338 LineTo(hdc, centerx, item->rect.bottom + 1);
2339 }
2340
2341 /* Draw the line from our parent to its next sibling. */
2342 parent = item->parent;
2343 while (parent != infoPtr->root)
2344 {
2345 int pcenterx = (parent->linesOffset + parent->stateOffset) / 2;
2346
2347 if (parent->nextSibling
2348 /* skip top-levels unless TVS_LINESATROOT */
2349 && parent->stateOffset > parent->linesOffset)
2350 {
2351 MoveToEx(hdc, pcenterx, item->rect.top, NULL);
2352 LineTo(hdc, pcenterx, item->rect.bottom + 1);
2353 }
2354
2355 parent = parent->parent;
2356 }
2357
2358 SelectObject(hdc, hOldPen);
2359 DeleteObject(hNewPen);
2360 }
2361
Vincent Béron9a624912002-05-31 23:06:46 +00002362 /*
Chris Morgan5f9fd772000-09-13 20:27:30 +00002363 * Display the (+/-) signs
2364 */
2365
2366 if (infoPtr->dwStyle & TVS_HASBUTTONS)
2367 {
2368 if (item->cChildren)
2369 {
Frank Richter171fea02005-07-28 10:15:10 +00002370 HTHEME theme = GetWindowTheme(infoPtr->hwnd);
2371 if (theme)
2372 {
2373 RECT glyphRect = item->rect;
2374 glyphRect.left = item->linesOffset;
2375 glyphRect.right = item->stateOffset;
2376 DrawThemeBackground (theme, hdc, TVP_GLYPH,
2377 (item->state & TVIS_EXPANDED) ? GLPS_OPENED : GLPS_CLOSED,
2378 &glyphRect, NULL);
2379 }
2380 else
2381 {
2382 LONG height = item->rect.bottom - item->rect.top;
2383 LONG width = item->stateOffset - item->linesOffset;
2384 LONG rectsize = min(height, width) / 4;
2385 /* plussize = ceil(rectsize * 3/4) */
2386 LONG plussize = (rectsize + 1) * 3 / 4;
2387
2388 HPEN hNewPen = CreatePen(PS_SOLID, 0, infoPtr->clrLine);
2389 HPEN hOldPen = SelectObject(hdc, hNewPen);
2390
2391 Rectangle(hdc, centerx - rectsize - 1, centery - rectsize - 1,
2392 centerx + rectsize + 2, centery + rectsize + 2);
2393
2394 SelectObject(hdc, hOldPen);
2395 DeleteObject(hNewPen);
2396
2397 if (height < 18 || width < 18)
2398 {
2399 MoveToEx(hdc, centerx - plussize + 1, centery, NULL);
2400 LineTo(hdc, centerx + plussize, centery);
2401
2402 if (!(item->state & TVIS_EXPANDED))
2403 {
2404 MoveToEx(hdc, centerx, centery - plussize + 1, NULL);
2405 LineTo(hdc, centerx, centery + plussize);
2406 }
2407 }
2408 else
2409 {
2410 Rectangle(hdc, centerx - plussize + 1, centery - 1,
2411 centerx + plussize, centery + 2);
2412
2413 if (!(item->state & TVIS_EXPANDED))
2414 {
2415 Rectangle(hdc, centerx - 1, centery - plussize + 1,
2416 centerx + 2, centery + plussize);
2417 SetPixel(hdc, centerx - 1, centery, infoPtr->clrBk);
2418 SetPixel(hdc, centerx + 1, centery, infoPtr->clrBk);
2419 }
2420 }
2421 }
Chris Morgan5f9fd772000-09-13 20:27:30 +00002422 }
2423 }
Vitaliy Margolen71be0942005-04-18 10:20:51 +00002424 SelectObject(hdc, hbrOld);
2425 DeleteObject(hbr);
Chris Morgan5f9fd772000-09-13 20:27:30 +00002426}
2427
2428static void
2429TREEVIEW_DrawItem(TREEVIEW_INFO *infoPtr, HDC hdc, TREEVIEW_ITEM *wineItem)
2430{
2431 INT cditem;
2432 HFONT hOldFont;
Vitaliy Margolen71be0942005-04-18 10:20:51 +00002433 COLORREF oldTextColor, oldTextBkColor;
Chris Morgan5f9fd772000-09-13 20:27:30 +00002434 int centery;
Vitaliy Margolen71be0942005-04-18 10:20:51 +00002435 BOOL inFocus = (GetFocus() == infoPtr->hwnd);
2436 NMTVCUSTOMDRAW nmcdhdr;
Chris Morgan5f9fd772000-09-13 20:27:30 +00002437
2438 TREEVIEW_UpdateDispInfo(infoPtr, wineItem, CALLBACK_MASK_ALL);
2439
Vitaliy Margolen71be0942005-04-18 10:20:51 +00002440 /* - If item is drop target or it is selected and window is in focus -
2441 * use blue background (COLOR_HIGHLIGHT).
2442 * - If item is selected, window is not in focus, but it has style
2443 * TVS_SHOWSELALWAYS - use grey background (COLOR_BTNFACE)
2444 * - Otherwise - use background color
2445 */
2446 if ((wineItem->state & TVIS_DROPHILITED) || ((wineItem == infoPtr->focusedItem) && !(wineItem->state & TVIS_SELECTED)) ||
2447 ((wineItem->state & TVIS_SELECTED) && (!infoPtr->focusedItem) &&
2448 (inFocus || (infoPtr->dwStyle & TVS_SHOWSELALWAYS))))
2449 {
2450 if ((wineItem->state & TVIS_DROPHILITED) || inFocus)
2451 {
2452 nmcdhdr.clrTextBk = GetSysColor(COLOR_HIGHLIGHT);
2453 nmcdhdr.clrText = GetSysColor(COLOR_HIGHLIGHTTEXT);
2454 }
2455 else
2456 {
2457 nmcdhdr.clrTextBk = GetSysColor(COLOR_BTNFACE);
2458 if (infoPtr->clrText == -1)
2459 nmcdhdr.clrText = GetSysColor(COLOR_WINDOWTEXT);
2460 else
2461 nmcdhdr.clrText = infoPtr->clrText;
2462 }
2463 }
2464 else
2465 {
2466 nmcdhdr.clrTextBk = infoPtr->clrBk;
2467 if ((infoPtr->dwStyle & TVS_TRACKSELECT) && (wineItem == infoPtr->hotItem))
2468 nmcdhdr.clrText = comctl32_color.clrHighlight;
2469 else if (infoPtr->clrText == -1)
2470 nmcdhdr.clrText = GetSysColor(COLOR_WINDOWTEXT);
2471 else
2472 nmcdhdr.clrText = infoPtr->clrText;
2473 }
2474
2475 hOldFont = SelectObject(hdc, TREEVIEW_FontForItem(infoPtr, wineItem));
2476
Chris Morgan5f9fd772000-09-13 20:27:30 +00002477 /* The custom draw handler can query the text rectangle,
2478 * so get ready. */
Martijn Vernooij2c2db4b2005-02-22 19:31:01 +00002479 /* should already be known, set to 0 when changed */
2480 if (!wineItem->textWidth)
2481 TREEVIEW_ComputeTextWidth(infoPtr, wineItem, hdc);
Chris Morgan5f9fd772000-09-13 20:27:30 +00002482
2483 cditem = 0;
2484
2485 if (infoPtr->cdmode & CDRF_NOTIFYITEMDRAW)
2486 {
2487 cditem = TREEVIEW_SendCustomDrawItemNotify
Vitaliy Margolen71be0942005-04-18 10:20:51 +00002488 (infoPtr, hdc, wineItem, CDDS_ITEMPREPAINT, &nmcdhdr);
Chris Morgan5f9fd772000-09-13 20:27:30 +00002489 TRACE("prepaint:cditem-app returns 0x%x\n", cditem);
2490
2491 if (cditem & CDRF_SKIPDEFAULT)
2492 {
2493 SelectObject(hdc, hOldFont);
2494 return;
2495 }
2496 }
2497
2498 if (cditem & CDRF_NEWFONT)
2499 TREEVIEW_ComputeTextWidth(infoPtr, wineItem, hdc);
2500
2501 TREEVIEW_DrawItemLines(infoPtr, hdc, wineItem);
2502
Vitaliy Margolen71be0942005-04-18 10:20:51 +00002503 /* Set colors. Custom draw handler can change these so we do this after it. */
2504 oldTextColor = SetTextColor(hdc, nmcdhdr.clrText);
2505 oldTextBkColor = SetBkColor(hdc, nmcdhdr.clrTextBk);
2506
Chris Morgan5f9fd772000-09-13 20:27:30 +00002507 centery = (wineItem->rect.top + wineItem->rect.bottom) / 2;
2508
Vincent Béron9a624912002-05-31 23:06:46 +00002509 /*
Chris Morgan5f9fd772000-09-13 20:27:30 +00002510 * Display the images associated with this item
2511 */
2512 {
2513 INT imageIndex;
2514
2515 /* State images are displayed to the left of the Normal image
2516 * image number is in state; zero should be `display no image'.
2517 */
2518 imageIndex = STATEIMAGEINDEX(wineItem->state);
2519
2520 if (infoPtr->himlState && imageIndex)
2521 {
2522 ImageList_Draw(infoPtr->himlState, imageIndex, hdc,
2523 wineItem->stateOffset,
2524 centery - infoPtr->stateImageHeight / 2,
2525 ILD_NORMAL);
2526 }
2527
2528 /* Now, draw the normal image; can be either selected or
Vincent Béron9a624912002-05-31 23:06:46 +00002529 * non-selected image.
Chris Morgan5f9fd772000-09-13 20:27:30 +00002530 */
2531
Robert Shearman6a365e02005-03-21 10:51:14 +00002532 if ((wineItem->state & TVIS_SELECTED) && (wineItem->iSelectedImage >= 0))
Chris Morgan5f9fd772000-09-13 20:27:30 +00002533 {
Andreas Mohr8bc7f162002-02-27 01:34:08 +00002534 /* The item is currently selected */
Chris Morgan5f9fd772000-09-13 20:27:30 +00002535 imageIndex = wineItem->iSelectedImage;
2536 }
2537 else
2538 {
2539 /* The item is not selected */
2540 imageIndex = wineItem->iImage;
2541 }
2542
2543 if (infoPtr->himlNormal)
2544 {
2545 int ovlIdx = wineItem->state & TVIS_OVERLAYMASK;
2546
2547 ImageList_Draw(infoPtr->himlNormal, imageIndex, hdc,
2548 wineItem->imageOffset,
2549 centery - infoPtr->normalImageHeight / 2,
2550 ILD_NORMAL | ovlIdx);
2551 }
2552 }
2553
2554
Vincent Béron9a624912002-05-31 23:06:46 +00002555 /*
Chris Morgan5f9fd772000-09-13 20:27:30 +00002556 * Display the text associated with this item
2557 */
2558
2559 /* Don't paint item's text if it's being edited */
2560 if (!infoPtr->hwndEdit || (infoPtr->selectedItem != wineItem))
2561 {
2562 if (wineItem->pszText)
2563 {
Chris Morgan5f9fd772000-09-13 20:27:30 +00002564 RECT rcText;
2565
Chris Morgan5f9fd772000-09-13 20:27:30 +00002566 rcText.top = wineItem->rect.top;
2567 rcText.bottom = wineItem->rect.bottom;
2568 rcText.left = wineItem->textOffset;
2569 rcText.right = rcText.left + wineItem->textWidth + 4;
2570
Vitaliy Margolen71be0942005-04-18 10:20:51 +00002571 TRACE("drawing text %s at (%ld,%ld)-(%ld,%ld)\n",
2572 debugstr_w(wineItem->pszText),
2573 rcText.left, rcText.top, rcText.right, rcText.bottom);
Chris Morgan5f9fd772000-09-13 20:27:30 +00002574
Vitaliy Margolen71be0942005-04-18 10:20:51 +00002575 /* Draw it */
2576 ExtTextOutW(hdc, rcText.left + 2, rcText.top + 1,
2577 ETO_CLIPPED | ETO_OPAQUE,
2578 &rcText,
2579 wineItem->pszText,
2580 lstrlenW(wineItem->pszText),
2581 NULL);
2582
Francois Gouget7203ee62000-10-31 02:05:11 +00002583 /* Draw the box around the selected item */
Chris Morgan5f9fd772000-09-13 20:27:30 +00002584 if ((wineItem == infoPtr->selectedItem) && inFocus)
2585 {
Francois Gouget7203ee62000-10-31 02:05:11 +00002586 DrawFocusRect(hdc,&rcText);
Chris Morgan5f9fd772000-09-13 20:27:30 +00002587 }
2588
Chris Morgan5f9fd772000-09-13 20:27:30 +00002589 }
2590 }
2591
2592 /* Draw insertion mark if necessary */
2593
2594 if (infoPtr->insertMarkItem)
Frank Richter9e570912005-08-30 10:07:17 +00002595 TRACE("item:%d,mark:%p\n",
Chris Morgan5f9fd772000-09-13 20:27:30 +00002596 TREEVIEW_GetItemIndex(infoPtr, wineItem),
Frank Richter9e570912005-08-30 10:07:17 +00002597 infoPtr->insertMarkItem);
Chris Morgan5f9fd772000-09-13 20:27:30 +00002598
2599 if (wineItem == infoPtr->insertMarkItem)
2600 {
2601 HPEN hNewPen, hOldPen;
2602 int offset;
2603 int left, right;
2604
2605 hNewPen = CreatePen(PS_SOLID, 2, infoPtr->clrInsertMark);
2606 hOldPen = SelectObject(hdc, hNewPen);
2607
2608 if (infoPtr->insertBeforeorAfter)
2609 offset = wineItem->rect.bottom - 1;
2610 else
2611 offset = wineItem->rect.top + 1;
2612
2613 left = wineItem->textOffset - 2;
2614 right = wineItem->textOffset + wineItem->textWidth + 2;
2615
2616 MoveToEx(hdc, left, offset - 3, NULL);
2617 LineTo(hdc, left, offset + 4);
2618
2619 MoveToEx(hdc, left, offset, NULL);
2620 LineTo(hdc, right + 1, offset);
2621
2622 MoveToEx(hdc, right, offset + 3, NULL);
2623 LineTo(hdc, right, offset - 4);
2624
2625 SelectObject(hdc, hOldPen);
2626 DeleteObject(hNewPen);
2627 }
2628
2629 if (cditem & CDRF_NOTIFYPOSTPAINT)
2630 {
2631 cditem = TREEVIEW_SendCustomDrawItemNotify
Vitaliy Margolen71be0942005-04-18 10:20:51 +00002632 (infoPtr, hdc, wineItem, CDDS_ITEMPOSTPAINT, &nmcdhdr);
Chris Morgan5f9fd772000-09-13 20:27:30 +00002633 TRACE("postpaint:cditem-app returns 0x%x\n", cditem);
2634 }
2635
Vitaliy Margolen71be0942005-04-18 10:20:51 +00002636 /* Restore the hdc state */
2637 SetTextColor(hdc, oldTextColor);
2638 SetBkColor(hdc, oldTextBkColor);
Chris Morgan5f9fd772000-09-13 20:27:30 +00002639 SelectObject(hdc, hOldFont);
2640}
2641
2642/* Computes treeHeight and treeWidth and updates the scroll bars.
2643 */
2644static void
2645TREEVIEW_UpdateScrollBars(TREEVIEW_INFO *infoPtr)
2646{
2647 TREEVIEW_ITEM *wineItem;
2648 HWND hwnd = infoPtr->hwnd;
2649 BOOL vert = FALSE;
2650 BOOL horz = FALSE;
2651 SCROLLINFO si;
2652 LONG scrollX = infoPtr->scrollX;
2653
2654 infoPtr->treeWidth = 0;
2655 infoPtr->treeHeight = 0;
2656
2657 /* We iterate through all visible items in order to get the tree height
2658 * and width */
2659 wineItem = infoPtr->root->firstChild;
2660
2661 while (wineItem != NULL)
2662 {
2663 if (ISVISIBLE(wineItem))
2664 {
2665 /* actually we draw text at textOffset + 2 */
2666 if (2+wineItem->textOffset+wineItem->textWidth > infoPtr->treeWidth)
2667 infoPtr->treeWidth = wineItem->textOffset+wineItem->textWidth+2;
2668
2669 /* This is scroll-adjusted, but we fix this below. */
2670 infoPtr->treeHeight = wineItem->rect.bottom;
2671 }
2672
2673 wineItem = TREEVIEW_GetNextListItem(infoPtr, wineItem);
2674 }
2675
2676 /* Fix the scroll adjusted treeHeight and treeWidth. */
2677 if (infoPtr->root->firstChild)
2678 infoPtr->treeHeight -= infoPtr->root->firstChild->rect.top;
2679
2680 infoPtr->treeWidth += infoPtr->scrollX;
2681
Guy L. Albertellib4994d12002-03-20 01:29:04 +00002682 if (infoPtr->dwStyle & TVS_NOSCROLL) return;
2683
Chris Morgan5f9fd772000-09-13 20:27:30 +00002684 /* Adding one scroll bar may take up enough space that it forces us
2685 * to add the other as well. */
2686 if (infoPtr->treeHeight > infoPtr->clientHeight)
2687 {
2688 vert = TRUE;
2689
2690 if (infoPtr->treeWidth
2691 > infoPtr->clientWidth - GetSystemMetrics(SM_CXVSCROLL))
2692 horz = TRUE;
2693 }
2694 else if (infoPtr->treeWidth > infoPtr->clientWidth)
2695 horz = TRUE;
2696
2697 if (!vert && horz && infoPtr->treeHeight
2698 > infoPtr->clientHeight - GetSystemMetrics(SM_CYVSCROLL))
2699 vert = TRUE;
2700
Guy L. Albertellib4994d12002-03-20 01:29:04 +00002701 if (horz && (infoPtr->dwStyle & TVS_NOHSCROLL)) horz = FALSE;
2702
Chris Morgan5f9fd772000-09-13 20:27:30 +00002703 si.cbSize = sizeof(SCROLLINFO);
2704 si.fMask = SIF_POS|SIF_RANGE|SIF_PAGE;
2705 si.nMin = 0;
2706
2707 if (vert)
2708 {
Chris Morgan5f9fd772000-09-13 20:27:30 +00002709 si.nPage = TREEVIEW_GetVisibleCount(infoPtr);
Steve Lustbadera7528852004-03-01 23:10:52 +00002710 if ( si.nPage && NULL != infoPtr->firstVisible)
Adam Gundy292d8422003-03-06 22:41:59 +00002711 {
2712 si.nPos = infoPtr->firstVisible->visibleOrder;
2713 si.nMax = infoPtr->maxVisibleOrder - 1;
Chris Morgan5f9fd772000-09-13 20:27:30 +00002714
Adam Gundy292d8422003-03-06 22:41:59 +00002715 SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
Susan Farley4dc7f4c2000-10-31 01:02:05 +00002716
Adam Gundy292d8422003-03-06 22:41:59 +00002717 if (!(infoPtr->uInternalStatus & TV_VSCROLL))
2718 ShowScrollBar(hwnd, SB_VERT, TRUE);
2719 infoPtr->uInternalStatus |= TV_VSCROLL;
2720 }
2721 else
2722 {
2723 if (infoPtr->uInternalStatus & TV_VSCROLL)
2724 ShowScrollBar(hwnd, SB_VERT, FALSE);
2725 infoPtr->uInternalStatus &= ~TV_VSCROLL;
2726 }
Chris Morgan5f9fd772000-09-13 20:27:30 +00002727 }
2728 else
2729 {
2730 if (infoPtr->uInternalStatus & TV_VSCROLL)
2731 ShowScrollBar(hwnd, SB_VERT, FALSE);
Chris Morgan5f9fd772000-09-13 20:27:30 +00002732 infoPtr->uInternalStatus &= ~TV_VSCROLL;
2733 }
2734
2735 if (horz)
2736 {
Chris Morgan5f9fd772000-09-13 20:27:30 +00002737 si.nPage = infoPtr->clientWidth;
2738 si.nPos = infoPtr->scrollX;
2739 si.nMax = infoPtr->treeWidth - 1;
2740
2741 if (si.nPos > si.nMax - max( si.nPage-1, 0 ))
2742 {
2743 si.nPos = si.nMax - max( si.nPage-1, 0 );
2744 scrollX = si.nPos;
2745 }
2746
Susan Farley4dc7f4c2000-10-31 01:02:05 +00002747 if (!(infoPtr->uInternalStatus & TV_HSCROLL))
2748 ShowScrollBar(hwnd, SB_HORZ, TRUE);
2749 infoPtr->uInternalStatus |= TV_HSCROLL;
2750
Chris Morgan5f9fd772000-09-13 20:27:30 +00002751 SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);
2752 }
2753 else
2754 {
2755 if (infoPtr->uInternalStatus & TV_HSCROLL)
2756 ShowScrollBar(hwnd, SB_HORZ, FALSE);
Susan Farley4dc7f4c2000-10-31 01:02:05 +00002757 infoPtr->uInternalStatus &= ~TV_HSCROLL;
Chris Morgan5f9fd772000-09-13 20:27:30 +00002758
2759 scrollX = 0;
2760 }
2761
2762 if (infoPtr->scrollX != scrollX)
2763 {
2764 TREEVIEW_HScroll(infoPtr,
2765 MAKEWPARAM(SB_THUMBPOSITION, scrollX));
2766 }
2767
2768 if (!horz)
2769 infoPtr->uInternalStatus &= ~TV_HSCROLL;
2770}
2771
2772/* CtrlSpy doesn't mention this, but CorelDRAW's object manager needs it. */
2773static LRESULT
2774TREEVIEW_EraseBackground(TREEVIEW_INFO *infoPtr, HDC hDC)
2775{
2776 HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk);
2777 RECT rect;
2778
2779 GetClientRect(infoPtr->hwnd, &rect);
2780 FillRect(hDC, &rect, hBrush);
2781 DeleteObject(hBrush);
2782
2783 return 1;
2784}
2785
2786static void
2787TREEVIEW_Refresh(TREEVIEW_INFO *infoPtr, HDC hdc, RECT *rc)
2788{
2789 HWND hwnd = infoPtr->hwnd;
2790 RECT rect = *rc;
2791 TREEVIEW_ITEM *wineItem;
2792
2793 if (infoPtr->clientHeight == 0 || infoPtr->clientWidth == 0)
2794 {
2795 TRACE("empty window\n");
2796 return;
2797 }
2798
2799 infoPtr->cdmode = TREEVIEW_SendCustomDrawNotify(infoPtr, CDDS_PREPAINT,
2800 hdc, rect);
2801
2802 if (infoPtr->cdmode == CDRF_SKIPDEFAULT)
2803 {
2804 ReleaseDC(hwnd, hdc);
2805 return;
2806 }
2807
2808 for (wineItem = infoPtr->root->firstChild;
2809 wineItem != NULL;
2810 wineItem = TREEVIEW_GetNextListItem(infoPtr, wineItem))
2811 {
2812 if (ISVISIBLE(wineItem))
2813 {
2814 /* Avoid unneeded calculations */
2815 if (wineItem->rect.top > rect.bottom)
2816 break;
2817 if (wineItem->rect.bottom < rect.top)
2818 continue;
2819
2820 TREEVIEW_DrawItem(infoPtr, hdc, wineItem);
2821 }
2822 }
2823
2824 TREEVIEW_UpdateScrollBars(infoPtr);
2825
2826 if (infoPtr->cdmode & CDRF_NOTIFYPOSTPAINT)
2827 infoPtr->cdmode =
2828 TREEVIEW_SendCustomDrawNotify(infoPtr, CDDS_POSTPAINT, hdc, rect);
2829}
2830
2831static void
Susan Farley8d8c87b2000-10-25 20:30:58 +00002832TREEVIEW_Invalidate(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item)
Chris Morgan5f9fd772000-09-13 20:27:30 +00002833{
2834 if (item != NULL)
2835 InvalidateRect(infoPtr->hwnd, &item->rect, TRUE);
Susan Farley8d8c87b2000-10-25 20:30:58 +00002836 else
2837 InvalidateRect(infoPtr->hwnd, NULL, TRUE);
Chris Morgan5f9fd772000-09-13 20:27:30 +00002838}
2839
2840static LRESULT
2841TREEVIEW_Paint(TREEVIEW_INFO *infoPtr, WPARAM wParam)
2842{
2843 HDC hdc;
2844 PAINTSTRUCT ps;
2845 RECT rc;
2846
2847 TRACE("\n");
2848
2849 if (wParam)
2850 {
2851 hdc = (HDC)wParam;
Alex Villacís Lasso4082e9a2005-11-08 13:11:48 +00002852 GetClientRect(infoPtr->hwnd, &rc);
2853 TREEVIEW_EraseBackground(infoPtr, hdc);
Chris Morgan5f9fd772000-09-13 20:27:30 +00002854 }
2855 else
2856 {
2857 hdc = BeginPaint(infoPtr->hwnd, &ps);
2858 rc = ps.rcPaint;
2859 }
2860
2861 if(infoPtr->bRedraw) /* WM_SETREDRAW sets bRedraw */
2862 TREEVIEW_Refresh(infoPtr, hdc, &rc);
2863
2864 if (!wParam)
2865 EndPaint(infoPtr->hwnd, &ps);
2866
2867 return 0;
2868}
2869
2870
2871/* Sorting **************************************************************/
2872
Sylvain St.Germain99b118a1999-03-25 10:55:01 +00002873/***************************************************************************
Sylvain St.Germain99b118a1999-03-25 10:55:01 +00002874 * Forward the DPA local callback to the treeview owner callback
2875 */
Chris Morgan5f9fd772000-09-13 20:27:30 +00002876static INT WINAPI
2877TREEVIEW_CallBackCompare(TREEVIEW_ITEM *first, TREEVIEW_ITEM *second, LPTVSORTCB pCallBackSort)
Sylvain St.Germain99b118a1999-03-25 10:55:01 +00002878{
Chris Morgan5f9fd772000-09-13 20:27:30 +00002879 /* Forward the call to the client-defined callback */
2880 return pCallBackSort->lpfnCompare(first->lParam,
2881 second->lParam,
2882 pCallBackSort->lParam);
Sylvain St.Germain99b118a1999-03-25 10:55:01 +00002883}
2884
2885/***************************************************************************
Alex Prieme5ea09c1999-07-10 10:19:48 +00002886 * Treeview native sort routine: sort on item text.
2887 */
Chris Morgan5f9fd772000-09-13 20:27:30 +00002888static INT WINAPI
2889TREEVIEW_SortOnName(TREEVIEW_ITEM *first, TREEVIEW_ITEM *second,
2890 TREEVIEW_INFO *infoPtr)
Alex Prieme5ea09c1999-07-10 10:19:48 +00002891{
Chris Morgan5f9fd772000-09-13 20:27:30 +00002892 TREEVIEW_UpdateDispInfo(infoPtr, first, TVIF_TEXT);
2893 TREEVIEW_UpdateDispInfo(infoPtr, second, TVIF_TEXT);
Alex Prieme5ea09c1999-07-10 10:19:48 +00002894
Huw Davies5693ecd2003-11-26 03:37:30 +00002895 if(first->pszText && second->pszText)
2896 return lstrcmpiW(first->pszText, second->pszText);
2897 else if(first->pszText)
2898 return -1;
2899 else if(second->pszText)
2900 return 1;
2901 else
2902 return 0;
Chris Morgan5f9fd772000-09-13 20:27:30 +00002903}
2904
2905/* Returns the number of physical children belonging to item. */
2906static INT
2907TREEVIEW_CountChildren(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item)
2908{
2909 INT cChildren = 0;
2910 HTREEITEM hti;
2911
2912 for (hti = item->firstChild; hti != NULL; hti = hti->nextSibling)
2913 cChildren++;
2914
2915 return cChildren;
2916}
2917
2918/* Returns a DPA containing a pointer to each physical child of item in
2919 * sibling order. If item has no children, an empty DPA is returned. */
2920static HDPA
2921TREEVIEW_BuildChildDPA(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item)
2922{
2923 HTREEITEM child = item->firstChild;
2924
2925 HDPA list = DPA_Create(8);
2926 if (list == 0) return NULL;
2927
2928 for (child = item->firstChild; child != NULL; child = child->nextSibling)
2929 {
2930 if (DPA_InsertPtr(list, INT_MAX, child) == -1)
2931 {
2932 DPA_Destroy(list);
2933 return NULL;
Alex Prieme5ea09c1999-07-10 10:19:48 +00002934 }
Chris Morgan5f9fd772000-09-13 20:27:30 +00002935 }
Alex Prieme5ea09c1999-07-10 10:19:48 +00002936
Chris Morgan5f9fd772000-09-13 20:27:30 +00002937 return list;
Alex Prieme5ea09c1999-07-10 10:19:48 +00002938}
2939
2940/***************************************************************************
Sylvain St.Germain99b118a1999-03-25 10:55:01 +00002941 * Setup the treeview structure with regards of the sort method
2942 * and sort the children of the TV item specified in lParam
Alex Prieme5ea09c1999-07-10 10:19:48 +00002943 * fRecurse: currently unused. Should be zero.
2944 * parent: if pSort!=NULL, should equal pSort->hParent.
2945 * otherwise, item which child items are to be sorted.
2946 * pSort: sort method info. if NULL, sort on item text.
2947 * if non-NULL, sort on item's lParam content, and let the
2948 * application decide what that means. See also TVM_SORTCHILDRENCB.
Sylvain St.Germain99b118a1999-03-25 10:55:01 +00002949 */
Alex Prieme5ea09c1999-07-10 10:19:48 +00002950
Chris Morgan5f9fd772000-09-13 20:27:30 +00002951static LRESULT
2952TREEVIEW_Sort(TREEVIEW_INFO *infoPtr, BOOL fRecurse, HTREEITEM parent,
2953 LPTVSORTCB pSort)
Sylvain St.Germain99b118a1999-03-25 10:55:01 +00002954{
Chris Morgan5f9fd772000-09-13 20:27:30 +00002955 INT cChildren;
2956 PFNDPACOMPARE pfnCompare;
2957 LPARAM lpCompare;
Sylvain St.Germain99b118a1999-03-25 10:55:01 +00002958
Tim Segallefa2b0c2003-02-25 03:59:12 +00002959 /* undocumented feature: TVI_ROOT or NULL means `sort the whole tree' */
2960 if (parent == TVI_ROOT || parent == NULL)
Chris Morgan5f9fd772000-09-13 20:27:30 +00002961 parent = infoPtr->root;
Sylvain St.Germain99b118a1999-03-25 10:55:01 +00002962
Chris Morgan5f9fd772000-09-13 20:27:30 +00002963 /* Check for a valid handle to the parent item */
2964 if (!TREEVIEW_ValidItem(infoPtr, parent))
Sylvain St.Germain99b118a1999-03-25 10:55:01 +00002965 {
Frank Richter9e570912005-08-30 10:07:17 +00002966 ERR("invalid item hParent=%p\n", parent);
Chris Morgan5f9fd772000-09-13 20:27:30 +00002967 return FALSE;
Sylvain St.Germain99b118a1999-03-25 10:55:01 +00002968 }
2969
Chris Morgan5f9fd772000-09-13 20:27:30 +00002970 if (pSort)
2971 {
2972 pfnCompare = (PFNDPACOMPARE)TREEVIEW_CallBackCompare;
2973 lpCompare = (LPARAM)pSort;
2974 }
2975 else
2976 {
2977 pfnCompare = (PFNDPACOMPARE)TREEVIEW_SortOnName;
2978 lpCompare = (LPARAM)infoPtr;
2979 }
Sylvain St.Germain99b118a1999-03-25 10:55:01 +00002980
Chris Morgan5f9fd772000-09-13 20:27:30 +00002981 cChildren = TREEVIEW_CountChildren(infoPtr, parent);
Sylvain St.Germain99b118a1999-03-25 10:55:01 +00002982
Chris Morgan5f9fd772000-09-13 20:27:30 +00002983 /* Make sure there is something to sort */
2984 if (cChildren > 1)
2985 {
2986 /* TREEVIEW_ITEM rechaining */
2987 INT count = 0;
2988 HTREEITEM item = 0;
2989 HTREEITEM nextItem = 0;
2990 HTREEITEM prevItem = 0;
2991
2992 HDPA sortList = TREEVIEW_BuildChildDPA(infoPtr, parent);
2993
2994 if (sortList == NULL)
2995 return FALSE;
2996
2997 /* let DPA sort the list */
2998 DPA_Sort(sortList, pfnCompare, lpCompare);
2999
3000 /* The order of DPA entries has been changed, so fixup the
3001 * nextSibling and prevSibling pointers. */
3002
3003 item = (HTREEITEM)DPA_GetPtr(sortList, count++);
3004 while ((nextItem = (HTREEITEM)DPA_GetPtr(sortList, count++)) != NULL)
3005 {
3006 /* link the two current item toghether */
3007 item->nextSibling = nextItem;
3008 nextItem->prevSibling = item;
3009
3010 if (prevItem == NULL)
3011 {
3012 /* this is the first item, update the parent */
3013 parent->firstChild = item;
3014 item->prevSibling = NULL;
3015 }
3016 else
3017 {
3018 /* fix the back chaining */
3019 item->prevSibling = prevItem;
3020 }
3021
3022 /* get ready for the next one */
3023 prevItem = item;
3024 item = nextItem;
3025 }
3026
3027 /* the last item is pointed to by item and never has a sibling */
3028 item->nextSibling = NULL;
3029 parent->lastChild = item;
3030
3031 DPA_Destroy(sortList);
3032
3033 TREEVIEW_VerifyTree(infoPtr);
3034
3035 if (parent->state & TVIS_EXPANDED)
3036 {
3037 int visOrder = infoPtr->firstVisible->visibleOrder;
3038
Aric Stewartcf94bf82000-11-02 20:09:43 +00003039 if (parent == infoPtr->root)
3040 TREEVIEW_RecalculateVisibleOrder(infoPtr, NULL);
3041 else
3042 TREEVIEW_RecalculateVisibleOrder(infoPtr, parent);
Chris Morgan5f9fd772000-09-13 20:27:30 +00003043
3044 if (TREEVIEW_IsChildOf(parent, infoPtr->firstVisible))
3045 {
3046 TREEVIEW_ITEM *item;
3047
3048 for (item = infoPtr->root->firstChild; item != NULL;
3049 item = TREEVIEW_GetNextListItem(infoPtr, item))
3050 {
3051 if (item->visibleOrder == visOrder)
3052 break;
3053 }
3054
Dmitry Timoshkovd6872af2003-11-13 20:49:00 +00003055 if (!item) item = parent->firstChild;
Chris Morgan5f9fd772000-09-13 20:27:30 +00003056 TREEVIEW_SetFirstVisible(infoPtr, item, FALSE);
3057 }
3058
Susan Farley8d8c87b2000-10-25 20:30:58 +00003059 TREEVIEW_Invalidate(infoPtr, NULL);
Chris Morgan5f9fd772000-09-13 20:27:30 +00003060 }
3061
3062 return TRUE;
3063 }
3064 return FALSE;
Sylvain St.Germain99b118a1999-03-25 10:55:01 +00003065}
Alexandre Julliard829fe321998-07-26 14:27:39 +00003066
3067
Alex Prieme5ea09c1999-07-10 10:19:48 +00003068/***************************************************************************
3069 * Setup the treeview structure with regards of the sort method
3070 * and sort the children of the TV item specified in lParam
3071 */
Chris Morgan5f9fd772000-09-13 20:27:30 +00003072static LRESULT
3073TREEVIEW_SortChildrenCB(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPTVSORTCB pSort)
Alex Prieme5ea09c1999-07-10 10:19:48 +00003074{
Chris Morgan5f9fd772000-09-13 20:27:30 +00003075 return TREEVIEW_Sort(infoPtr, wParam, pSort->hParent, pSort);
Alex Prieme5ea09c1999-07-10 10:19:48 +00003076}
3077
3078
3079/***************************************************************************
3080 * Sort the children of the TV item specified in lParam.
3081 */
Chris Morgan5f9fd772000-09-13 20:27:30 +00003082static LRESULT
3083TREEVIEW_SortChildren(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
Alex Prieme5ea09c1999-07-10 10:19:48 +00003084{
Chris Morgan5f9fd772000-09-13 20:27:30 +00003085 return TREEVIEW_Sort(infoPtr, (BOOL)wParam, (HTREEITEM)lParam, NULL);
Alex Prieme5ea09c1999-07-10 10:19:48 +00003086}
3087
3088
Chris Morgan5f9fd772000-09-13 20:27:30 +00003089/* Expansion/Collapse ***************************************************/
Alex Prieme5ea09c1999-07-10 10:19:48 +00003090
Chris Morgan5f9fd772000-09-13 20:27:30 +00003091static BOOL
3092TREEVIEW_SendExpanding(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *wineItem,
3093 UINT action)
Alexandre Julliardd30dfd21998-09-27 18:28:36 +00003094{
Dimitrie O. Paun4bbd7b32002-10-31 00:54:53 +00003095 return !TREEVIEW_SendTreeviewNotify(infoPtr, TVN_ITEMEXPANDINGW, action,
Chris Morgan5f9fd772000-09-13 20:27:30 +00003096 TVIF_HANDLE | TVIF_STATE | TVIF_PARAM
3097 | TVIF_IMAGE | TVIF_SELECTEDIMAGE,
3098 0, wineItem);
3099}
Alexandre Julliardd30dfd21998-09-27 18:28:36 +00003100
Chris Morgan5f9fd772000-09-13 20:27:30 +00003101static VOID
3102TREEVIEW_SendExpanded(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *wineItem,
3103 UINT action)
3104{
Dimitrie O. Paun4bbd7b32002-10-31 00:54:53 +00003105 TREEVIEW_SendTreeviewNotify(infoPtr, TVN_ITEMEXPANDEDW, action,
Chris Morgan5f9fd772000-09-13 20:27:30 +00003106 TVIF_HANDLE | TVIF_STATE | TVIF_PARAM
3107 | TVIF_IMAGE | TVIF_SELECTEDIMAGE,
3108 0, wineItem);
Alexandre Julliardd30dfd21998-09-27 18:28:36 +00003109}
Eric Kohlf9b58071999-07-31 13:09:40 +00003110
3111
Chris Morgan5f9fd772000-09-13 20:27:30 +00003112/* This corresponds to TVM_EXPAND with TVE_COLLAPSE.
3113 * bRemoveChildren corresponds to TVE_COLLAPSERESET. */
3114static BOOL
3115TREEVIEW_Collapse(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *wineItem,
3116 BOOL bRemoveChildren, BOOL bUser)
Francois Boisvert56c36e31999-07-24 12:14:51 +00003117{
Chris Morgan5f9fd772000-09-13 20:27:30 +00003118 UINT action = TVE_COLLAPSE | (bRemoveChildren ? TVE_COLLAPSERESET : 0);
3119 BOOL bSetSelection, bSetFirstVisible;
Martijn Vernooij2c2db4b2005-02-22 19:31:01 +00003120 RECT scrollRect;
3121 LONG scrollDist = 0;
3122 TREEVIEW_ITEM *nextItem = NULL, *tmpItem;
Alexandre Julliardd30dfd21998-09-27 18:28:36 +00003123
Chris Morgan5f9fd772000-09-13 20:27:30 +00003124 TRACE("TVE_COLLAPSE %p %s\n", wineItem, TREEVIEW_ItemName(wineItem));
Francois Boisvert56c36e31999-07-24 12:14:51 +00003125
Martin Fuchsbb40b302003-07-21 22:04:42 +00003126 if (!(wineItem->state & TVIS_EXPANDED))
Chris Morgan5f9fd772000-09-13 20:27:30 +00003127 return FALSE;
Francois Boisvert56c36e31999-07-24 12:14:51 +00003128
Huw Davies4a8e3392003-11-21 21:30:20 +00003129 if (bUser || !(wineItem->state & TVIS_EXPANDEDONCE))
Chris Morgan5f9fd772000-09-13 20:27:30 +00003130 TREEVIEW_SendExpanding(infoPtr, wineItem, action);
Francois Boisvert56c36e31999-07-24 12:14:51 +00003131
Martin Fuchsbb40b302003-07-21 22:04:42 +00003132 if (wineItem->firstChild == NULL)
3133 return FALSE;
3134
Chris Morgan5f9fd772000-09-13 20:27:30 +00003135 wineItem->state &= ~TVIS_EXPANDED;
3136
Huw Davies4a8e3392003-11-21 21:30:20 +00003137 if (bUser || !(wineItem->state & TVIS_EXPANDEDONCE))
Chris Morgan5f9fd772000-09-13 20:27:30 +00003138 TREEVIEW_SendExpanded(infoPtr, wineItem, action);
3139
3140 bSetSelection = (infoPtr->selectedItem != NULL
3141 && TREEVIEW_IsChildOf(wineItem, infoPtr->selectedItem));
3142
3143 bSetFirstVisible = (infoPtr->firstVisible != NULL
3144 && TREEVIEW_IsChildOf(wineItem, infoPtr->firstVisible));
3145
Martijn Vernooij2c2db4b2005-02-22 19:31:01 +00003146 tmpItem = wineItem;
3147 while (tmpItem)
3148 {
3149 if (tmpItem->nextSibling)
3150 {
3151 nextItem = tmpItem->nextSibling;
3152 break;
3153 }
3154 tmpItem = tmpItem->parent;
3155 }
3156
3157 if (nextItem)
3158 scrollDist = nextItem->rect.top;
3159
Chris Morgan5f9fd772000-09-13 20:27:30 +00003160 if (bRemoveChildren)
Francois Boisvert56c36e31999-07-24 12:14:51 +00003161 {
Huw Davies4a8e3392003-11-21 21:30:20 +00003162 INT old_cChildren = wineItem->cChildren;
Chris Morgan5f9fd772000-09-13 20:27:30 +00003163 TRACE("TVE_COLLAPSERESET\n");
3164 wineItem->state &= ~TVIS_EXPANDEDONCE;
3165 TREEVIEW_RemoveAllChildren(infoPtr, wineItem);
Huw Davies4a8e3392003-11-21 21:30:20 +00003166 wineItem->cChildren = old_cChildren;
Chris Morgan5f9fd772000-09-13 20:27:30 +00003167 }
3168
3169 if (wineItem->firstChild)
3170 {
3171 TREEVIEW_ITEM *item, *sibling;
3172
3173 sibling = TREEVIEW_GetNextListItem(infoPtr, wineItem);
3174
3175 for (item = wineItem->firstChild; item != sibling;
3176 item = TREEVIEW_GetNextListItem(infoPtr, item))
Francois Boisvert56c36e31999-07-24 12:14:51 +00003177 {
Chris Morgan5f9fd772000-09-13 20:27:30 +00003178 item->visibleOrder = -1;
Francois Boisvert56c36e31999-07-24 12:14:51 +00003179 }
3180 }
3181
Chris Morgan5f9fd772000-09-13 20:27:30 +00003182 TREEVIEW_RecalculateVisibleOrder(infoPtr, wineItem);
Francois Boisvert56c36e31999-07-24 12:14:51 +00003183
Martijn Vernooij2c2db4b2005-02-22 19:31:01 +00003184 if (nextItem)
3185 scrollDist = -(scrollDist - nextItem->rect.top);
Francois Boisvert56c36e31999-07-24 12:14:51 +00003186
Chris Morgan5f9fd772000-09-13 20:27:30 +00003187 if (bSetSelection)
Francois Boisvert56c36e31999-07-24 12:14:51 +00003188 {
Chris Morgan5f9fd772000-09-13 20:27:30 +00003189 /* Don't call DoSelectItem, it sends notifications. */
3190 if (TREEVIEW_ValidItem(infoPtr, infoPtr->selectedItem))
3191 infoPtr->selectedItem->state &= ~TVIS_SELECTED;
3192 wineItem->state |= TVIS_SELECTED;
3193 infoPtr->selectedItem = wineItem;
Francois Boisvert56c36e31999-07-24 12:14:51 +00003194 }
3195
Chris Morgan5f9fd772000-09-13 20:27:30 +00003196 TREEVIEW_UpdateScrollBars(infoPtr);
Martijn Vernooij2c2db4b2005-02-22 19:31:01 +00003197
3198 scrollRect.left = 0;
3199 scrollRect.right = infoPtr->clientWidth;
3200 scrollRect.bottom = infoPtr->clientHeight;
3201
3202 if (nextItem)
3203 {
3204 scrollRect.top = nextItem->rect.top;
3205
3206 ScrollWindowEx (infoPtr->hwnd, 0, scrollDist, &scrollRect, NULL,
3207 NULL, NULL, SW_ERASE | SW_INVALIDATE);
3208 TREEVIEW_Invalidate(infoPtr, wineItem);
3209 } else {
3210 scrollRect.top = wineItem->rect.top;
3211 InvalidateRect(infoPtr->hwnd, &scrollRect, TRUE);
3212 }
3213
3214 TREEVIEW_SetFirstVisible(infoPtr,
3215 bSetFirstVisible ? wineItem : infoPtr->firstVisible,
3216 TRUE);
Alexandre Julliard829fe321998-07-26 14:27:39 +00003217
Alexandre Julliard829fe321998-07-26 14:27:39 +00003218 return TRUE;
3219}
3220
Alexandre Julliarda3960291999-02-26 11:11:13 +00003221static BOOL
Chris Morgan5f9fd772000-09-13 20:27:30 +00003222TREEVIEW_Expand(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *wineItem,
3223 BOOL bExpandPartial, BOOL bUser)
Alexandre Julliard642d3131998-07-12 19:29:36 +00003224{
Martijn Vernooij2c2db4b2005-02-22 19:31:01 +00003225 LONG scrollDist;
3226 LONG orgNextTop = 0;
3227 RECT scrollRect;
3228 TREEVIEW_ITEM *nextItem, *tmpItem;
3229
Chris Morgan5f9fd772000-09-13 20:27:30 +00003230 TRACE("\n");
Alexandre Julliard642d3131998-07-12 19:29:36 +00003231
Martin Fuchsbb40b302003-07-21 22:04:42 +00003232 if (wineItem->state & TVIS_EXPANDED)
Martin Fuchs5307b3f2004-04-14 04:27:59 +00003233 return TRUE;
Alexandre Julliard642d3131998-07-12 19:29:36 +00003234
Martijn Vernooij2c2db4b2005-02-22 19:31:01 +00003235 tmpItem = wineItem; nextItem = NULL;
3236 while (tmpItem)
3237 {
3238 if (tmpItem->nextSibling)
3239 {
3240 nextItem = tmpItem->nextSibling;
3241 break;
3242 }
3243 tmpItem = tmpItem->parent;
3244 }
3245
3246 if (nextItem)
3247 orgNextTop = nextItem->rect.top;
3248
Chris Morgan5f9fd772000-09-13 20:27:30 +00003249 TRACE("TVE_EXPAND %p %s\n", wineItem, TREEVIEW_ItemName(wineItem));
Alexandre Julliard829fe321998-07-26 14:27:39 +00003250
Adam Gundyc2d90612003-10-15 21:01:54 +00003251 if (bUser || ((wineItem->cChildren != 0) &&
3252 !(wineItem->state & TVIS_EXPANDEDONCE)))
Chris Morgan5f9fd772000-09-13 20:27:30 +00003253 {
3254 if (!TREEVIEW_SendExpanding(infoPtr, wineItem, TVE_EXPAND))
3255 {
3256 TRACE(" TVN_ITEMEXPANDING returned TRUE, exiting...\n");
3257 return FALSE;
Alex Priemd2d7f961999-01-30 13:02:30 +00003258 }
Alex Priemd2d7f961999-01-30 13:02:30 +00003259
Martin Fuchsbb40b302003-07-21 22:04:42 +00003260 if (!wineItem->firstChild)
3261 return FALSE;
3262
Chris Morgan5f9fd772000-09-13 20:27:30 +00003263 wineItem->state |= TVIS_EXPANDED;
3264 TREEVIEW_SendExpanded(infoPtr, wineItem, TVE_EXPAND);
3265 wineItem->state |= TVIS_EXPANDEDONCE;
3266 }
3267 else
3268 {
Martin Fuchsbb40b302003-07-21 22:04:42 +00003269 if (!wineItem->firstChild)
3270 return FALSE;
3271
Chris Morgan5f9fd772000-09-13 20:27:30 +00003272 /* this item has already been expanded */
3273 wineItem->state |= TVIS_EXPANDED;
3274 }
3275
3276 if (bExpandPartial)
3277 FIXME("TVE_EXPANDPARTIAL not implemented\n");
3278
3279 TREEVIEW_RecalculateVisibleOrder(infoPtr, wineItem);
3280 TREEVIEW_UpdateSubTree(infoPtr, wineItem);
3281 TREEVIEW_UpdateScrollBars(infoPtr);
3282
Martijn Vernooij2c2db4b2005-02-22 19:31:01 +00003283 scrollRect.left = 0;
3284 scrollRect.bottom = infoPtr->treeHeight;
3285 scrollRect.right = infoPtr->clientWidth;
3286 if (nextItem)
3287 {
3288 scrollDist = nextItem->rect.top - orgNextTop;
3289 scrollRect.top = orgNextTop;
3290
3291 ScrollWindowEx (infoPtr->hwnd, 0, scrollDist, &scrollRect, NULL,
3292 NULL, NULL, SW_ERASE | SW_INVALIDATE);
3293 TREEVIEW_Invalidate (infoPtr, wineItem);
3294 } else {
3295 scrollRect.top = wineItem->rect.top;
3296 InvalidateRect(infoPtr->hwnd, &scrollRect, FALSE);
3297 }
3298
Chris Morgan5f9fd772000-09-13 20:27:30 +00003299 /* Scroll up so that as many children as possible are visible.
Francois Gouget9d589ac2005-01-04 20:39:54 +00003300 * This fails when expanding causes an HScroll bar to appear, but we
Chris Morgan5f9fd772000-09-13 20:27:30 +00003301 * don't know that yet, so the last item is obscured. */
3302 if (wineItem->firstChild != NULL)
3303 {
3304 int nChildren = wineItem->lastChild->visibleOrder
3305 - wineItem->firstChild->visibleOrder + 1;
3306
3307 int visible_pos = wineItem->visibleOrder
3308 - infoPtr->firstVisible->visibleOrder;
3309
3310 int rows_below = TREEVIEW_GetVisibleCount(infoPtr) - visible_pos - 1;
3311
3312 if (visible_pos > 0 && nChildren > rows_below)
3313 {
3314 int scroll = nChildren - rows_below;
3315
3316 if (scroll > visible_pos)
3317 scroll = visible_pos;
3318
3319 if (scroll > 0)
3320 {
3321 TREEVIEW_ITEM *newFirstVisible
3322 = TREEVIEW_GetListItem(infoPtr, infoPtr->firstVisible,
3323 scroll);
3324
3325
3326 TREEVIEW_SetFirstVisible(infoPtr, newFirstVisible, TRUE);
3327 }
3328 }
3329 }
3330
Chris Morgan5f9fd772000-09-13 20:27:30 +00003331 return TRUE;
Alex Priemd2d7f961999-01-30 13:02:30 +00003332}
3333
Alexandre Julliarda3960291999-02-26 11:11:13 +00003334static BOOL
Chris Morgan5f9fd772000-09-13 20:27:30 +00003335TREEVIEW_Toggle(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *wineItem, BOOL bUser)
Alex Priemd2d7f961999-01-30 13:02:30 +00003336{
Chris Morgan5f9fd772000-09-13 20:27:30 +00003337 TRACE("\n");
Alex Priemd2d7f961999-01-30 13:02:30 +00003338
Chris Morgan5f9fd772000-09-13 20:27:30 +00003339 if (wineItem->state & TVIS_EXPANDED)
3340 return TREEVIEW_Collapse(infoPtr, wineItem, FALSE, bUser);
3341 else
3342 return TREEVIEW_Expand(infoPtr, wineItem, FALSE, bUser);
Alex Priemd2d7f961999-01-30 13:02:30 +00003343}
3344
Chris Morgan5f9fd772000-09-13 20:27:30 +00003345static VOID
3346TREEVIEW_ExpandAll(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item)
Alex Priemd2d7f961999-01-30 13:02:30 +00003347{
Chris Morgan5f9fd772000-09-13 20:27:30 +00003348 TREEVIEW_Expand(infoPtr, item, FALSE, TRUE);
Alex Priemd2d7f961999-01-30 13:02:30 +00003349
Chris Morgan5f9fd772000-09-13 20:27:30 +00003350 for (item = item->firstChild; item != NULL; item = item->nextSibling)
3351 {
3352 if (TREEVIEW_HasChildren(infoPtr, item))
3353 TREEVIEW_ExpandAll(infoPtr, item);
3354 }
Alex Priemd2d7f961999-01-30 13:02:30 +00003355}
3356
Alex Priemd2d7f961999-01-30 13:02:30 +00003357/* Note:If the specified item is the child of a collapsed parent item,
Vincent Béron9a624912002-05-31 23:06:46 +00003358 the parent's list of child items is (recursively) expanded to reveal the
3359 specified item. This is mentioned for TREEVIEW_SelectItem; don't
Alex Priemd2d7f961999-01-30 13:02:30 +00003360 know if it also applies here.
3361*/
3362
Alexandre Julliardd30dfd21998-09-27 18:28:36 +00003363static LRESULT
Chris Morgan5f9fd772000-09-13 20:27:30 +00003364TREEVIEW_ExpandMsg(TREEVIEW_INFO *infoPtr, UINT flag, HTREEITEM wineItem)
Alexandre Julliardd30dfd21998-09-27 18:28:36 +00003365{
Chris Morgan5f9fd772000-09-13 20:27:30 +00003366 if (!TREEVIEW_ValidItem(infoPtr, wineItem))
3367 return 0;
Sylvain St.Germain90245261999-04-10 16:35:30 +00003368
Chris Morgan5f9fd772000-09-13 20:27:30 +00003369 TRACE("For (%s) item:%d, flags %x, state:%d\n",
3370 TREEVIEW_ItemName(wineItem), flag,
3371 TREEVIEW_GetItemIndex(infoPtr, wineItem), wineItem->state);
Alexandre Julliard767e6f61998-08-09 12:47:43 +00003372
Chris Morgan5f9fd772000-09-13 20:27:30 +00003373 switch (flag & TVE_TOGGLE)
3374 {
3375 case TVE_COLLAPSE:
3376 return TREEVIEW_Collapse(infoPtr, wineItem, flag & TVE_COLLAPSERESET,
3377 FALSE);
Alex Priemd2d7f961999-01-30 13:02:30 +00003378
Chris Morgan5f9fd772000-09-13 20:27:30 +00003379 case TVE_EXPAND:
3380 return TREEVIEW_Expand(infoPtr, wineItem, flag & TVE_EXPANDPARTIAL,
3381 FALSE);
Alex Priem23dc2e61999-07-15 14:30:55 +00003382
Chris Morgan5f9fd772000-09-13 20:27:30 +00003383 case TVE_TOGGLE:
3384 return TREEVIEW_Toggle(infoPtr, wineItem, TRUE);
Alexandre Julliard767e6f61998-08-09 12:47:43 +00003385
Chris Morgan5f9fd772000-09-13 20:27:30 +00003386 default:
3387 return 0;
3388 }
Sylvain St.Germain02300ae1999-03-16 10:38:44 +00003389
Chris Morgan5f9fd772000-09-13 20:27:30 +00003390#if 0
3391 TRACE("Exiting, Item %p state is now %d...\n", wineItem, wineItem->state);
3392#endif
Alexandre Julliardd30dfd21998-09-27 18:28:36 +00003393}
Alexandre Julliard767e6f61998-08-09 12:47:43 +00003394
Chris Morgan5f9fd772000-09-13 20:27:30 +00003395/* Hit-Testing **********************************************************/
Alexandre Julliard767e6f61998-08-09 12:47:43 +00003396
Alex Priemd2d7f961999-01-30 13:02:30 +00003397static TREEVIEW_ITEM *
Chris Morgan5f9fd772000-09-13 20:27:30 +00003398TREEVIEW_HitTestPoint(TREEVIEW_INFO *infoPtr, POINT pt)
Alexandre Julliardd30dfd21998-09-27 18:28:36 +00003399{
Chris Morgan5f9fd772000-09-13 20:27:30 +00003400 TREEVIEW_ITEM *wineItem;
3401 LONG row;
Alexandre Julliard767e6f61998-08-09 12:47:43 +00003402
Chris Morgan5f9fd772000-09-13 20:27:30 +00003403 if (!infoPtr->firstVisible)
Alex Priemd2d7f961999-01-30 13:02:30 +00003404 return NULL;
3405
Chris Morgan5f9fd772000-09-13 20:27:30 +00003406 row = pt.y / infoPtr->uItemHeight + infoPtr->firstVisible->visibleOrder;
Alex Priemd2d7f961999-01-30 13:02:30 +00003407
Chris Morgan5f9fd772000-09-13 20:27:30 +00003408 for (wineItem = infoPtr->firstVisible; wineItem != NULL;
3409 wineItem = TREEVIEW_GetNextListItem(infoPtr, wineItem))
David Black2b441912000-05-14 23:50:00 +00003410 {
Chris Morgan5f9fd772000-09-13 20:27:30 +00003411 if (row >= wineItem->visibleOrder
3412 && row < wineItem->visibleOrder + wineItem->iIntegral)
3413 break;
Sylvain St.Germain90245261999-04-10 16:35:30 +00003414 }
David Black2b441912000-05-14 23:50:00 +00003415
Chris Morgan5f9fd772000-09-13 20:27:30 +00003416 return wineItem;
3417}
Sylvain St.Germain90245261999-04-10 16:35:30 +00003418
Chris Morgan5f9fd772000-09-13 20:27:30 +00003419static LRESULT
3420TREEVIEW_HitTest(TREEVIEW_INFO *infoPtr, LPTVHITTESTINFO lpht)
3421{
3422 TREEVIEW_ITEM *wineItem;
3423 RECT rect;
3424 UINT status;
3425 LONG x, y;
Sylvain St.Germain90245261999-04-10 16:35:30 +00003426
Chris Morgan5f9fd772000-09-13 20:27:30 +00003427 lpht->hItem = 0;
3428 GetClientRect(infoPtr->hwnd, &rect);
3429 status = 0;
3430 x = lpht->pt.x;
3431 y = lpht->pt.y;
David Black2b441912000-05-14 23:50:00 +00003432
Chris Morgan5f9fd772000-09-13 20:27:30 +00003433 if (x < rect.left)
Sylvain St.Germain90245261999-04-10 16:35:30 +00003434 {
Chris Morgan5f9fd772000-09-13 20:27:30 +00003435 status |= TVHT_TOLEFT;
3436 }
3437 else if (x > rect.right)
3438 {
3439 status |= TVHT_TORIGHT;
3440 }
David Black2b441912000-05-14 23:50:00 +00003441
Chris Morgan5f9fd772000-09-13 20:27:30 +00003442 if (y < rect.top)
3443 {
3444 status |= TVHT_ABOVE;
3445 }
3446 else if (y > rect.bottom)
3447 {
3448 status |= TVHT_BELOW;
3449 }
3450
3451 if (status)
3452 {
3453 lpht->flags = status;
3454 return (LRESULT)(HTREEITEM)NULL;
3455 }
3456
3457 wineItem = TREEVIEW_HitTestPoint(infoPtr, lpht->pt);
3458 if (!wineItem)
3459 {
3460 lpht->flags = TVHT_NOWHERE;
3461 return (LRESULT)(HTREEITEM)NULL;
3462 }
3463
3464 if (x >= wineItem->textOffset + wineItem->textWidth)
3465 {
3466 lpht->flags = TVHT_ONITEMRIGHT;
3467 }
3468 else if (x >= wineItem->textOffset)
3469 {
3470 lpht->flags = TVHT_ONITEMLABEL;
3471 }
3472 else if (x >= wineItem->imageOffset)
3473 {
3474 lpht->flags = TVHT_ONITEMICON;
3475 }
3476 else if (x >= wineItem->stateOffset)
3477 {
3478 lpht->flags = TVHT_ONITEMSTATEICON;
3479 }
3480 else if (x >= wineItem->linesOffset && infoPtr->dwStyle & TVS_HASBUTTONS)
3481 {
3482 lpht->flags = TVHT_ONITEMBUTTON;
Sylvain St.Germain90245261999-04-10 16:35:30 +00003483 }
3484 else
3485 {
Chris Morgan5f9fd772000-09-13 20:27:30 +00003486 lpht->flags = TVHT_ONITEMINDENT;
Sylvain St.Germain90245261999-04-10 16:35:30 +00003487 }
Chris Morgan5f9fd772000-09-13 20:27:30 +00003488
3489 lpht->hItem = wineItem;
3490 TRACE("(%ld,%ld):result %x\n", lpht->pt.x, lpht->pt.y, lpht->flags);
3491
3492 return (LRESULT)wineItem;
3493}
3494
3495/* Item Label Editing ***************************************************/
3496
3497static LRESULT
3498TREEVIEW_GetEditControl(TREEVIEW_INFO *infoPtr)
3499{
Michael Stefaniuc025c0b72002-09-06 19:41:17 +00003500 return (LRESULT)infoPtr->hwndEdit;
Chris Morgan5f9fd772000-09-13 20:27:30 +00003501}
3502
3503static LRESULT CALLBACK
3504TREEVIEW_Edit_SubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
3505{
Dimitrie O. Paun6443ee52003-11-20 04:19:59 +00003506 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(GetParent(hwnd));
Chris Morgan5f9fd772000-09-13 20:27:30 +00003507 BOOL bCancel = FALSE;
Dimitrie O. Paun6443ee52003-11-20 04:19:59 +00003508 LRESULT rc;
Chris Morgan5f9fd772000-09-13 20:27:30 +00003509
3510 switch (uMsg)
David Black2b441912000-05-14 23:50:00 +00003511 {
Chris Morgan5f9fd772000-09-13 20:27:30 +00003512 case WM_PAINT:
Dimitrie O. Paun6443ee52003-11-20 04:19:59 +00003513 TRACE("WM_PAINT start\n");
Huw Davies5693ecd2003-11-26 03:37:30 +00003514 rc = CallWindowProcW(infoPtr->wpEditOrig, hwnd, uMsg, wParam,
Chris Morgan5f9fd772000-09-13 20:27:30 +00003515 lParam);
Dimitrie O. Paun6443ee52003-11-20 04:19:59 +00003516 TRACE("WM_PAINT done\n");
3517 return rc;
David Black2b441912000-05-14 23:50:00 +00003518
Chris Morgan5f9fd772000-09-13 20:27:30 +00003519 case WM_KILLFOCUS:
Chris Morgan5f9fd772000-09-13 20:27:30 +00003520 if (infoPtr->bIgnoreEditKillFocus)
3521 return TRUE;
Chris Morgan5f9fd772000-09-13 20:27:30 +00003522 break;
David Black2b441912000-05-14 23:50:00 +00003523
Chris Morgan5f9fd772000-09-13 20:27:30 +00003524 case WM_GETDLGCODE:
3525 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
Sylvain St.Germain90245261999-04-10 16:35:30 +00003526
Chris Morgan5f9fd772000-09-13 20:27:30 +00003527 case WM_KEYDOWN:
3528 if (wParam == (WPARAM)VK_ESCAPE)
3529 {
3530 bCancel = TRUE;
3531 break;
3532 }
3533 else if (wParam == (WPARAM)VK_RETURN)
3534 {
3535 break;
3536 }
Aric Stewart0406a082000-05-11 00:07:44 +00003537
Chris Morgan5f9fd772000-09-13 20:27:30 +00003538 /* fall through */
3539 default:
Huw Davies5693ecd2003-11-26 03:37:30 +00003540 return CallWindowProcW(infoPtr->wpEditOrig, hwnd, uMsg, wParam, lParam);
Chris Morgan5f9fd772000-09-13 20:27:30 +00003541 }
Aric Stewart0406a082000-05-11 00:07:44 +00003542
Chris Morgan5f9fd772000-09-13 20:27:30 +00003543 /* Processing TVN_ENDLABELEDIT message could kill the focus */
3544 /* eg. Using a messagebox */
Alexandre Julliard829fe321998-07-26 14:27:39 +00003545
Chris Morgan5f9fd772000-09-13 20:27:30 +00003546 infoPtr->bIgnoreEditKillFocus = TRUE;
3547 TREEVIEW_EndEditLabelNow(infoPtr, bCancel || !infoPtr->bLabelChanged);
3548 infoPtr->bIgnoreEditKillFocus = FALSE;
Alexandre Julliard829fe321998-07-26 14:27:39 +00003549
Sylvain St.Germain90245261999-04-10 16:35:30 +00003550 return 0;
Chris Morgan5f9fd772000-09-13 20:27:30 +00003551}
Sylvain St.Germain90245261999-04-10 16:35:30 +00003552
Chris Morgan5f9fd772000-09-13 20:27:30 +00003553
3554/* should handle edit control messages here */
3555
3556static LRESULT
3557TREEVIEW_Command(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
3558{
3559 TRACE("%x %ld\n", wParam, lParam);
3560
3561 switch (HIWORD(wParam))
3562 {
3563 case EN_UPDATE:
3564 {
Vincent Béron9a624912002-05-31 23:06:46 +00003565 /*
3566 * Adjust the edit window size
Chris Morgan5f9fd772000-09-13 20:27:30 +00003567 */
Huw Davies5693ecd2003-11-26 03:37:30 +00003568 WCHAR buffer[1024];
Chris Morgan5f9fd772000-09-13 20:27:30 +00003569 TREEVIEW_ITEM *editItem = infoPtr->selectedItem;
3570 HDC hdc = GetDC(infoPtr->hwndEdit);
3571 SIZE sz;
3572 int len;
3573 HFONT hFont, hOldFont = 0;
3574
3575 infoPtr->bLabelChanged = TRUE;
3576
Huw Davies5693ecd2003-11-26 03:37:30 +00003577 len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer));
Chris Morgan5f9fd772000-09-13 20:27:30 +00003578
3579 /* Select font to get the right dimension of the string */
Huw Davies5693ecd2003-11-26 03:37:30 +00003580 hFont = (HFONT)SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
3581
Chris Morgan5f9fd772000-09-13 20:27:30 +00003582 if (hFont != 0)
3583 {
3584 hOldFont = SelectObject(hdc, hFont);
3585 }
3586
Huw Davies5693ecd2003-11-26 03:37:30 +00003587 if (GetTextExtentPoint32W(hdc, buffer, strlenW(buffer), &sz))
Chris Morgan5f9fd772000-09-13 20:27:30 +00003588 {
Huw Davies5693ecd2003-11-26 03:37:30 +00003589 TEXTMETRICW textMetric;
Chris Morgan5f9fd772000-09-13 20:27:30 +00003590
3591 /* Add Extra spacing for the next character */
Huw Davies5693ecd2003-11-26 03:37:30 +00003592 GetTextMetricsW(hdc, &textMetric);
Chris Morgan5f9fd772000-09-13 20:27:30 +00003593 sz.cx += (textMetric.tmMaxCharWidth * 2);
3594
3595 sz.cx = max(sz.cx, textMetric.tmMaxCharWidth * 3);
3596 sz.cx = min(sz.cx,
3597 infoPtr->clientWidth - editItem->textOffset + 2);
3598
3599 SetWindowPos(infoPtr->hwndEdit,
3600 HWND_TOP,
3601 0,
3602 0,
3603 sz.cx,
3604 editItem->rect.bottom - editItem->rect.top + 3,
3605 SWP_NOMOVE | SWP_DRAWFRAME);
3606 }
3607
3608 if (hFont != 0)
3609 {
3610 SelectObject(hdc, hOldFont);
3611 }
3612
3613 ReleaseDC(infoPtr->hwnd, hdc);
3614 break;
3615 }
3616
3617 default:
Huw Davies5693ecd2003-11-26 03:37:30 +00003618 return SendMessageW(infoPtr->hwndNotify, WM_COMMAND, wParam, lParam);
Chris Morgan5f9fd772000-09-13 20:27:30 +00003619 }
3620
Sylvain St.Germain90245261999-04-10 16:35:30 +00003621 return 0;
Chris Morgan5f9fd772000-09-13 20:27:30 +00003622}
Sylvain St.Germain90245261999-04-10 16:35:30 +00003623
Chris Morgan5f9fd772000-09-13 20:27:30 +00003624static HWND
Huw Davies5693ecd2003-11-26 03:37:30 +00003625TREEVIEW_EditLabel(TREEVIEW_INFO *infoPtr, HTREEITEM hItem)
Chris Morgan5f9fd772000-09-13 20:27:30 +00003626{
3627 HWND hwnd = infoPtr->hwnd;
3628 HWND hwndEdit;
3629 SIZE sz;
3630 TREEVIEW_ITEM *editItem = hItem;
Robert Shearmancdb263e2004-08-25 17:33:01 +00003631 HINSTANCE hinst = (HINSTANCE)GetWindowLongPtrW(hwnd, GWLP_HINSTANCE);
Chris Morgan5f9fd772000-09-13 20:27:30 +00003632 HDC hdc;
3633 HFONT hOldFont=0;
Huw Davies5693ecd2003-11-26 03:37:30 +00003634 TEXTMETRICW textMetric;
Francois Gouget52c08322004-04-20 01:12:17 +00003635 static const WCHAR EditW[] = {'E','d','i','t',0};
Chris Morgan5f9fd772000-09-13 20:27:30 +00003636
Frank Richter9e570912005-08-30 10:07:17 +00003637 TRACE("%p %p\n", hwnd, hItem);
Chris Morgan5f9fd772000-09-13 20:27:30 +00003638 if (!TREEVIEW_ValidItem(infoPtr, editItem))
Francois Gougetd2667a42002-12-02 18:10:57 +00003639 return NULL;
Chris Morgan5f9fd772000-09-13 20:27:30 +00003640
3641 if (infoPtr->hwndEdit)
3642 return infoPtr->hwndEdit;
3643
3644 infoPtr->bLabelChanged = FALSE;
3645
3646 /* Make sure that edit item is selected */
3647 TREEVIEW_DoSelectItem(infoPtr, TVGN_CARET, hItem, TVC_UNKNOWN);
3648 TREEVIEW_EnsureVisible(infoPtr, hItem, TRUE);
3649
3650 TREEVIEW_UpdateDispInfo(infoPtr, editItem, TVIF_TEXT);
3651
3652 hdc = GetDC(hwnd);
3653 /* Select the font to get appropriate metric dimensions */
3654 if (infoPtr->hFont != 0)
Aric Stewart4b40a7d2000-05-24 21:02:50 +00003655 {
Chris Morgan5f9fd772000-09-13 20:27:30 +00003656 hOldFont = SelectObject(hdc, infoPtr->hFont);
Aric Stewart4b40a7d2000-05-24 21:02:50 +00003657 }
Aric Stewart4b40a7d2000-05-24 21:02:50 +00003658
Susan Farley036ae0b2000-10-13 20:13:38 +00003659 /* Get string length in pixels */
Huw Davies5693ecd2003-11-26 03:37:30 +00003660 GetTextExtentPoint32W(hdc, editItem->pszText, strlenW(editItem->pszText),
Chris Morgan5f9fd772000-09-13 20:27:30 +00003661 &sz);
Sylvain St.Germain90245261999-04-10 16:35:30 +00003662
Susan Farley036ae0b2000-10-13 20:13:38 +00003663 /* Add Extra spacing for the next character */
Huw Davies5693ecd2003-11-26 03:37:30 +00003664 GetTextMetricsW(hdc, &textMetric);
Chris Morgan5f9fd772000-09-13 20:27:30 +00003665 sz.cx += (textMetric.tmMaxCharWidth * 2);
3666
3667 sz.cx = max(sz.cx, textMetric.tmMaxCharWidth * 3);
3668 sz.cx = min(sz.cx, infoPtr->clientWidth - editItem->textOffset + 2);
3669
3670 if (infoPtr->hFont != 0)
Sylvain St.Germain90245261999-04-10 16:35:30 +00003671 {
Chris Morgan5f9fd772000-09-13 20:27:30 +00003672 SelectObject(hdc, hOldFont);
Sylvain St.Germain90245261999-04-10 16:35:30 +00003673 }
Sylvain St.Germain90245261999-04-10 16:35:30 +00003674
Chris Morgan5f9fd772000-09-13 20:27:30 +00003675 ReleaseDC(hwnd, hdc);
Huw Davies5693ecd2003-11-26 03:37:30 +00003676 hwndEdit = CreateWindowExW(WS_EX_LEFT,
3677 EditW,
Chris Morgan5f9fd772000-09-13 20:27:30 +00003678 0,
3679 WS_CHILD | WS_BORDER | ES_AUTOHSCROLL |
3680 WS_CLIPSIBLINGS | ES_WANTRETURN |
3681 ES_LEFT, editItem->textOffset - 2,
3682 editItem->rect.top - 1, sz.cx + 3,
3683 editItem->rect.bottom -
3684 editItem->rect.top + 3, hwnd, 0, hinst, 0);
3685/* FIXME: (HMENU)IDTVEDIT,pcs->hInstance,0); */
Alex Prieme6e71ad1999-11-28 19:50:01 +00003686
Chris Morgan5f9fd772000-09-13 20:27:30 +00003687 infoPtr->hwndEdit = hwndEdit;
3688
3689 /* Get a 2D border. */
Huw Davies5693ecd2003-11-26 03:37:30 +00003690 SetWindowLongW(hwndEdit, GWL_EXSTYLE,
3691 GetWindowLongW(hwndEdit, GWL_EXSTYLE) & ~WS_EX_CLIENTEDGE);
3692 SetWindowLongW(hwndEdit, GWL_STYLE,
3693 GetWindowLongW(hwndEdit, GWL_STYLE) | WS_BORDER);
Chris Morgan5f9fd772000-09-13 20:27:30 +00003694
Huw Davies5693ecd2003-11-26 03:37:30 +00003695 SendMessageW(hwndEdit, WM_SETFONT,
Michael Stefaniucf3d18932002-10-23 20:19:22 +00003696 (WPARAM)TREEVIEW_FontForItem(infoPtr, editItem), FALSE);
Chris Morgan5f9fd772000-09-13 20:27:30 +00003697
Robert Shearmancdb263e2004-08-25 17:33:01 +00003698 infoPtr->wpEditOrig = (WNDPROC)SetWindowLongPtrW(hwndEdit, GWLP_WNDPROC,
3699 (DWORD_PTR)
Chris Morgan5f9fd772000-09-13 20:27:30 +00003700 TREEVIEW_Edit_SubclassProc);
3701
3702 if (TREEVIEW_BeginLabelEditNotify(infoPtr, editItem))
3703 {
3704 DestroyWindow(hwndEdit);
3705 infoPtr->hwndEdit = 0;
Francois Gougetd2667a42002-12-02 18:10:57 +00003706 return NULL;
Chris Morgan5f9fd772000-09-13 20:27:30 +00003707 }
3708
3709 infoPtr->selectedItem = hItem;
Huw Davies5693ecd2003-11-26 03:37:30 +00003710 SetWindowTextW(hwndEdit, editItem->pszText);
Chris Morgan5f9fd772000-09-13 20:27:30 +00003711 SetFocus(hwndEdit);
Huw Davies5693ecd2003-11-26 03:37:30 +00003712 SendMessageW(hwndEdit, EM_SETSEL, 0, -1);
Chris Morgan5f9fd772000-09-13 20:27:30 +00003713 ShowWindow(hwndEdit, SW_SHOW);
3714
3715 return hwndEdit;
3716}
3717
3718
3719static LRESULT
3720TREEVIEW_EndEditLabelNow(TREEVIEW_INFO *infoPtr, BOOL bCancel)
3721{
3722 HWND hwnd = infoPtr->hwnd;
3723 TREEVIEW_ITEM *editedItem = infoPtr->selectedItem;
Huw Davies5693ecd2003-11-26 03:37:30 +00003724 NMTVDISPINFOW tvdi;
Chris Morgan5f9fd772000-09-13 20:27:30 +00003725 BOOL bCommit;
Huw Davies5693ecd2003-11-26 03:37:30 +00003726 WCHAR tmpText[1024] = { '\0' };
3727 WCHAR *newText = tmpText;
Chris Morgan5f9fd772000-09-13 20:27:30 +00003728 int iLength = 0;
3729
3730 if (!infoPtr->hwndEdit)
3731 return FALSE;
3732
3733 tvdi.hdr.hwndFrom = hwnd;
Robert Shearmancdb263e2004-08-25 17:33:01 +00003734 tvdi.hdr.idFrom = GetWindowLongPtrW(hwnd, GWLP_ID);
Dimitrie O. Paun4bbd7b32002-10-31 00:54:53 +00003735 tvdi.hdr.code = get_notifycode(infoPtr, TVN_ENDLABELEDITW);
Chris Morgan5f9fd772000-09-13 20:27:30 +00003736 tvdi.item.mask = 0;
3737 tvdi.item.hItem = editedItem;
3738 tvdi.item.state = editedItem->state;
3739 tvdi.item.lParam = editedItem->lParam;
3740
3741 if (!bCancel)
3742 {
Huw Davies5693ecd2003-11-26 03:37:30 +00003743 if (!infoPtr->bNtfUnicode)
3744 iLength = GetWindowTextA(infoPtr->hwndEdit, (LPSTR)tmpText, 1023);
3745 else
3746 iLength = GetWindowTextW(infoPtr->hwndEdit, tmpText, 1023);
Chris Morgan5f9fd772000-09-13 20:27:30 +00003747
3748 if (iLength >= 1023)
3749 {
Andreas Mohr6e256c722002-10-25 19:09:02 +00003750 ERR("Insufficient space to retrieve new item label\n");
Chris Morgan5f9fd772000-09-13 20:27:30 +00003751 }
3752
Huw Davies5693ecd2003-11-26 03:37:30 +00003753 tvdi.item.mask = TVIF_TEXT;
Chris Morgan5f9fd772000-09-13 20:27:30 +00003754 tvdi.item.pszText = tmpText;
3755 tvdi.item.cchTextMax = iLength + 1;
3756 }
3757 else
3758 {
3759 tvdi.item.pszText = NULL;
3760 tvdi.item.cchTextMax = 0;
3761 }
3762
Guy L. Albertellib4994d12002-03-20 01:29:04 +00003763 bCommit = (BOOL)TREEVIEW_SendRealNotify(infoPtr,
Chris Morgan5f9fd772000-09-13 20:27:30 +00003764 (WPARAM)tvdi.hdr.idFrom, (LPARAM)&tvdi);
3765
3766 if (!bCancel && bCommit) /* Apply the changes */
3767 {
Huw Davies5693ecd2003-11-26 03:37:30 +00003768 if (!infoPtr->bNtfUnicode)
3769 {
3770 DWORD len = MultiByteToWideChar( CP_ACP, 0, (LPSTR)tmpText, -1, NULL, 0 );
3771 newText = Alloc(len * sizeof(WCHAR));
3772 MultiByteToWideChar( CP_ACP, 0, (LPSTR)tmpText, -1, newText, len );
3773 iLength = len - 1;
3774 }
3775
3776 if (strcmpW(newText, editedItem->pszText) != 0)
3777 {
3778 if (NULL == ReAlloc(editedItem->pszText, iLength + 1))
3779 {
3780 ERR("OutOfMemory, cannot allocate space for label\n");
3781 DestroyWindow(infoPtr->hwndEdit);
3782 infoPtr->hwndEdit = 0;
3783 return FALSE;
3784 }
3785 else
3786 {
3787 editedItem->cchTextMax = iLength + 1;
3788 strcpyW(editedItem->pszText, newText);
3789 }
3790 }
3791 if(newText != tmpText) Free(newText);
Chris Morgan5f9fd772000-09-13 20:27:30 +00003792 }
3793
3794 ShowWindow(infoPtr->hwndEdit, SW_HIDE);
3795 DestroyWindow(infoPtr->hwndEdit);
3796 infoPtr->hwndEdit = 0;
3797 return TRUE;
3798}
3799
3800static LRESULT
3801TREEVIEW_HandleTimer(TREEVIEW_INFO *infoPtr, WPARAM wParam)
3802{
3803 if (wParam != TV_EDIT_TIMER)
3804 {
3805 ERR("got unknown timer\n");
3806 return 1;
3807 }
3808
3809 KillTimer(infoPtr->hwnd, TV_EDIT_TIMER);
3810 infoPtr->Timer &= ~TV_EDIT_TIMER_SET;
3811
Huw Davies5693ecd2003-11-26 03:37:30 +00003812 TREEVIEW_EditLabel(infoPtr, infoPtr->selectedItem);
Chris Morgan5f9fd772000-09-13 20:27:30 +00003813
3814 return 0;
3815}
3816
3817
3818/* Mouse Tracking/Drag **************************************************/
3819
3820/***************************************************************************
3821 * This is quite unusual piece of code, but that's how it's implemented in
3822 * Windows.
3823 */
3824static LRESULT
3825TREEVIEW_TrackMouse(TREEVIEW_INFO *infoPtr, POINT pt)
3826{
3827 INT cxDrag = GetSystemMetrics(SM_CXDRAG);
3828 INT cyDrag = GetSystemMetrics(SM_CYDRAG);
3829 RECT r;
3830 MSG msg;
3831
3832 r.top = pt.y - cyDrag;
3833 r.left = pt.x - cxDrag;
3834 r.bottom = pt.y + cyDrag;
3835 r.right = pt.x + cxDrag;
3836
3837 SetCapture(infoPtr->hwnd);
3838
3839 while (1)
3840 {
Dimitrie O. Pauna46b7fb2005-03-23 10:24:42 +00003841 if (PeekMessageW(&msg, 0, 0, 0, PM_REMOVE | PM_NOYIELD))
Chris Morgan5f9fd772000-09-13 20:27:30 +00003842 {
3843 if (msg.message == WM_MOUSEMOVE)
3844 {
Alexandre Julliard9d615962003-09-17 04:28:28 +00003845 pt.x = (short)LOWORD(msg.lParam);
3846 pt.y = (short)HIWORD(msg.lParam);
Chris Morgan5f9fd772000-09-13 20:27:30 +00003847 if (PtInRect(&r, pt))
3848 continue;
3849 else
3850 {
3851 ReleaseCapture();
3852 return 1;
Alex Prieme6e71ad1999-11-28 19:50:01 +00003853 }
Chris Morgan5f9fd772000-09-13 20:27:30 +00003854 }
3855 else if (msg.message >= WM_LBUTTONDOWN &&
3856 msg.message <= WM_RBUTTONDBLCLK)
3857 {
3858 if (msg.message == WM_RBUTTONUP)
3859 TREEVIEW_RButtonUp(infoPtr, &pt);
3860 break;
3861 }
3862
Dimitrie O. Pauna46b7fb2005-03-23 10:24:42 +00003863 DispatchMessageW(&msg);
Chris Morgan5f9fd772000-09-13 20:27:30 +00003864 }
3865
3866 if (GetCapture() != infoPtr->hwnd)
3867 return 0;
3868 }
3869
3870 ReleaseCapture();
3871 return 0;
Alex Priemd2d7f961999-01-30 13:02:30 +00003872}
3873
Alexandre Julliardd30dfd21998-09-27 18:28:36 +00003874
3875static LRESULT
Chris Morgan5f9fd772000-09-13 20:27:30 +00003876TREEVIEW_LButtonDoubleClick(TREEVIEW_INFO *infoPtr, LPARAM lParam)
Alexandre Julliardd30dfd21998-09-27 18:28:36 +00003877{
Chris Morgan5f9fd772000-09-13 20:27:30 +00003878 TREEVIEW_ITEM *wineItem;
3879 TVHITTESTINFO hit;
Alexandre Julliardd30dfd21998-09-27 18:28:36 +00003880
Chris Morgan5f9fd772000-09-13 20:27:30 +00003881 TRACE("\n");
3882 SetFocus(infoPtr->hwnd);
Alex Priemd2d7f961999-01-30 13:02:30 +00003883
Chris Morgan5f9fd772000-09-13 20:27:30 +00003884 if (infoPtr->Timer & TV_EDIT_TIMER_SET)
3885 {
3886 /* If there is pending 'edit label' event - kill it now */
3887 KillTimer(infoPtr->hwnd, TV_EDIT_TIMER);
3888 }
Alex Priemd2d7f961999-01-30 13:02:30 +00003889
Alexandre Julliard9d615962003-09-17 04:28:28 +00003890 hit.pt.x = (short)LOWORD(lParam);
3891 hit.pt.y = (short)HIWORD(lParam);
Alexandre Julliardd30dfd21998-09-27 18:28:36 +00003892
Chris Morgan5f9fd772000-09-13 20:27:30 +00003893 wineItem = (TREEVIEW_ITEM *)TREEVIEW_HitTest(infoPtr, &hit);
3894 if (!wineItem)
Alex Priemd2d7f961999-01-30 13:02:30 +00003895 return 0;
Susan Farley036ae0b2000-10-13 20:13:38 +00003896 TRACE("item %d\n", TREEVIEW_GetItemIndex(infoPtr, wineItem));
Alex Priemd2d7f961999-01-30 13:02:30 +00003897
Chris Morgan5f9fd772000-09-13 20:27:30 +00003898 if (TREEVIEW_SendSimpleNotify(infoPtr, NM_DBLCLK) == FALSE)
3899 { /* FIXME! */
3900 switch (hit.flags)
3901 {
3902 case TVHT_ONITEMRIGHT:
Susan Farley036ae0b2000-10-13 20:13:38 +00003903 /* FIXME: we should not have sent NM_DBLCLK in this case. */
Chris Morgan5f9fd772000-09-13 20:27:30 +00003904 break;
3905
3906 case TVHT_ONITEMINDENT:
3907 if (!(infoPtr->dwStyle & TVS_HASLINES))
3908 {
3909 break;
3910 }
3911 else
3912 {
3913 int level = hit.pt.x / infoPtr->uIndent;
3914 if (!(infoPtr->dwStyle & TVS_LINESATROOT)) level++;
3915
3916 while (wineItem->iLevel > level)
3917 {
3918 wineItem = wineItem->parent;
3919 }
3920
3921 /* fall through */
3922 }
3923
3924 case TVHT_ONITEMLABEL:
3925 case TVHT_ONITEMICON:
3926 case TVHT_ONITEMBUTTON:
3927 TREEVIEW_Toggle(infoPtr, wineItem, TRUE);
3928 break;
3929
3930 case TVHT_ONITEMSTATEICON:
3931 if (infoPtr->dwStyle & TVS_CHECKBOXES)
3932 TREEVIEW_ToggleItemState(infoPtr, wineItem);
3933 else
3934 TREEVIEW_Toggle(infoPtr, wineItem, TRUE);
3935 break;
3936 }
3937 }
3938 return TRUE;
Alex Priemd2d7f961999-01-30 13:02:30 +00003939}
3940
3941
3942static LRESULT
Chris Morgan5f9fd772000-09-13 20:27:30 +00003943TREEVIEW_LButtonDown(TREEVIEW_INFO *infoPtr, LPARAM lParam)
Alex Priemd2d7f961999-01-30 13:02:30 +00003944{
Chris Morgan5f9fd772000-09-13 20:27:30 +00003945 HWND hwnd = infoPtr->hwnd;
3946 TVHITTESTINFO ht;
Robert Shearman847c81d2004-09-28 19:17:17 +00003947 BOOL bTrack, bDoLabelEdit;
Chris Morgan5f9fd772000-09-13 20:27:30 +00003948 HTREEITEM tempItem;
Alex Priem07be70b1999-11-21 00:47:43 +00003949
Chris Morgan5f9fd772000-09-13 20:27:30 +00003950 /* If Edit control is active - kill it and return.
3951 * The best way to do it is to set focus to itself.
3952 * Edit control subclassed procedure will automatically call
3953 * EndEditLabelNow.
3954 */
3955 if (infoPtr->hwndEdit)
3956 {
3957 SetFocus(hwnd);
3958 return 0;
3959 }
Alex Priemd2d7f961999-01-30 13:02:30 +00003960
Alexandre Julliard9d615962003-09-17 04:28:28 +00003961 ht.pt.x = (short)LOWORD(lParam);
3962 ht.pt.y = (short)HIWORD(lParam);
Alex Priemd2d7f961999-01-30 13:02:30 +00003963
Chris Morgan5f9fd772000-09-13 20:27:30 +00003964 TREEVIEW_HitTest(infoPtr, &ht);
Susan Farley036ae0b2000-10-13 20:13:38 +00003965 TRACE("item %d\n", TREEVIEW_GetItemIndex(infoPtr, ht.hItem));
Alex Priemd2d7f961999-01-30 13:02:30 +00003966
Chris Morgan5f9fd772000-09-13 20:27:30 +00003967 /* update focusedItem and redraw both items */
3968 if(ht.hItem && (ht.flags & TVHT_ONITEM))
3969 {
3970 infoPtr->focusedItem = ht.hItem;
3971 InvalidateRect(hwnd, &(((HTREEITEM)(ht.hItem))->rect), TRUE);
3972
3973 if(infoPtr->selectedItem)
3974 InvalidateRect(hwnd, &(infoPtr->selectedItem->rect), TRUE);
3975 }
3976
3977 bTrack = (ht.flags & TVHT_ONITEM)
3978 && !(infoPtr->dwStyle & TVS_DISABLEDRAGDROP);
3979
Robert Shearman847c81d2004-09-28 19:17:17 +00003980 /*
3981 * If the style allows editing and the node is already selected
3982 * and the click occurred on the item label...
3983 */
3984 bDoLabelEdit = (infoPtr->dwStyle & TVS_EDITLABELS) &&
3985 (ht.flags & TVHT_ONITEMLABEL) && (infoPtr->selectedItem == ht.hItem);
3986
Chris Morgan5f9fd772000-09-13 20:27:30 +00003987 /* Send NM_CLICK right away */
3988 if (!bTrack)
3989 if (TREEVIEW_SendSimpleNotify(infoPtr, NM_CLICK))
3990 goto setfocus;
3991
3992 if (ht.flags & TVHT_ONITEMBUTTON)
3993 {
3994 TREEVIEW_Toggle(infoPtr, ht.hItem, TRUE);
3995 goto setfocus;
3996 }
3997 else if (bTrack)
Andreas Mohr8bc7f162002-02-27 01:34:08 +00003998 { /* if TREEVIEW_TrackMouse == 1 dragging occurred and the cursor left the dragged item's rectangle */
Chris Morgan5f9fd772000-09-13 20:27:30 +00003999 if (TREEVIEW_TrackMouse(infoPtr, ht.pt))
4000 {
Dimitrie O. Paun4bbd7b32002-10-31 00:54:53 +00004001 TREEVIEW_SendTreeviewDnDNotify(infoPtr, TVN_BEGINDRAGW, ht.hItem, ht.pt);
Chris Morgan5f9fd772000-09-13 20:27:30 +00004002 infoPtr->dropItem = ht.hItem;
4003
Susan Farley036ae0b2000-10-13 20:13:38 +00004004 /* clean up focusedItem as we dragged and won't select this item */
Chris Morgan5f9fd772000-09-13 20:27:30 +00004005 if(infoPtr->focusedItem)
4006 {
4007 /* refresh the item that was focused */
4008 tempItem = infoPtr->focusedItem;
4009 infoPtr->focusedItem = 0;
4010 InvalidateRect(infoPtr->hwnd, &tempItem->rect, TRUE);
4011
4012 /* refresh the selected item to return the filled background */
4013 InvalidateRect(infoPtr->hwnd, &(infoPtr->selectedItem->rect), TRUE);
4014 }
4015
4016 return 0;
4017 }
4018 }
4019
Mike Hearnd0300d92003-03-27 00:00:42 +00004020 if (bTrack && TREEVIEW_SendSimpleNotify(infoPtr, NM_CLICK))
Chris Morgan5f9fd772000-09-13 20:27:30 +00004021 goto setfocus;
4022
Robert Shearman847c81d2004-09-28 19:17:17 +00004023 if (bDoLabelEdit)
Chris Morgan5f9fd772000-09-13 20:27:30 +00004024 {
4025 if (infoPtr->Timer & TV_EDIT_TIMER_SET)
4026 KillTimer(hwnd, TV_EDIT_TIMER);
4027
4028 SetTimer(hwnd, TV_EDIT_TIMER, GetDoubleClickTime(), 0);
4029 infoPtr->Timer |= TV_EDIT_TIMER_SET;
4030 }
Maxime Bellengé3db30e32003-08-19 00:57:08 +00004031 else if (ht.flags & (TVHT_ONITEMICON|TVHT_ONITEMLABEL)) /* select the item if the hit was inside of the icon or text */
Chris Morgan5f9fd772000-09-13 20:27:30 +00004032 {
4033 /*
4034 * if we are TVS_SINGLEEXPAND then we want this single click to
4035 * do a bunch of things.
4036 */
4037 if((infoPtr->dwStyle & TVS_SINGLEEXPAND) &&
4038 (infoPtr->hwndEdit == 0))
4039 {
4040 TREEVIEW_ITEM *SelItem;
4041
4042 /*
4043 * Send the notification
4044 */
Dimitrie O. Paund2e881d2002-11-27 20:15:49 +00004045 TREEVIEW_SendTreeviewNotify(infoPtr, TVN_SINGLEEXPAND, TVC_UNKNOWN, TVIF_HANDLE | TVIF_PARAM, ht.hItem, 0);
Chris Morgan36c8db82000-09-22 20:48:15 +00004046
Chris Morgan5f9fd772000-09-13 20:27:30 +00004047 /*
4048 * Close the previous selection all the way to the root
4049 * as long as the new selection is not a child
4050 */
Chris Morgan5f9fd772000-09-13 20:27:30 +00004051 if((infoPtr->selectedItem)
4052 && (infoPtr->selectedItem != ht.hItem))
4053 {
4054 BOOL closeit = TRUE;
4055 SelItem = ht.hItem;
4056
Susan Farley036ae0b2000-10-13 20:13:38 +00004057 /* determine if the hitItem is a child of the currently selected item */
Chris Morgan36c8db82000-09-22 20:48:15 +00004058 while(closeit && SelItem && TREEVIEW_ValidItem(infoPtr, SelItem) && (SelItem != infoPtr->root))
Chris Morgan5f9fd772000-09-13 20:27:30 +00004059 {
4060 closeit = (SelItem != infoPtr->selectedItem);
Chris Morgan36c8db82000-09-22 20:48:15 +00004061 SelItem = SelItem->parent;
Chris Morgan5f9fd772000-09-13 20:27:30 +00004062 }
4063
4064 if(closeit)
4065 {
4066 if(TREEVIEW_ValidItem(infoPtr, infoPtr->selectedItem))
4067 SelItem = infoPtr->selectedItem;
4068
Chris Morgan36c8db82000-09-22 20:48:15 +00004069 while(SelItem && (SelItem != ht.hItem) && TREEVIEW_ValidItem(infoPtr, SelItem) && (SelItem != infoPtr->root))
Chris Morgan5f9fd772000-09-13 20:27:30 +00004070 {
Chris Morgan36c8db82000-09-22 20:48:15 +00004071 TREEVIEW_Collapse(infoPtr, SelItem, FALSE, FALSE);
4072 SelItem = SelItem->parent;
Chris Morgan5f9fd772000-09-13 20:27:30 +00004073 }
4074 }
4075 }
4076
4077 /*
4078 * Expand the current item
4079 */
4080 TREEVIEW_Expand(infoPtr, ht.hItem, TVE_TOGGLE, FALSE);
4081 }
Chris Morgan36c8db82000-09-22 20:48:15 +00004082
4083 /* Select the current item */
4084 TREEVIEW_DoSelectItem(infoPtr, TVGN_CARET, ht.hItem, TVC_BYMOUSE);
Chris Morgan5f9fd772000-09-13 20:27:30 +00004085 }
4086 else if (ht.flags & TVHT_ONITEMSTATEICON)
4087 {
4088 /* TVS_CHECKBOXES requires us to toggle the current state */
4089 if (infoPtr->dwStyle & TVS_CHECKBOXES)
4090 TREEVIEW_ToggleItemState(infoPtr, ht.hItem);
4091 }
4092
4093 setfocus:
4094 SetFocus(hwnd);
4095 return 0;
4096}
4097
4098
4099static LRESULT
4100TREEVIEW_RButtonDown(TREEVIEW_INFO *infoPtr, LPARAM lParam)
4101{
4102 TVHITTESTINFO ht;
4103
4104 if (infoPtr->hwndEdit)
4105 {
4106 SetFocus(infoPtr->hwnd);
4107 return 0;
4108 }
4109
Alexandre Julliard9d615962003-09-17 04:28:28 +00004110 ht.pt.x = (short)LOWORD(lParam);
4111 ht.pt.y = (short)HIWORD(lParam);
Chris Morgan5f9fd772000-09-13 20:27:30 +00004112
4113 TREEVIEW_HitTest(infoPtr, &ht);
4114
4115 if (TREEVIEW_TrackMouse(infoPtr, ht.pt))
4116 {
4117 if (ht.hItem)
4118 {
Dimitrie O. Paun4bbd7b32002-10-31 00:54:53 +00004119 TREEVIEW_SendTreeviewDnDNotify(infoPtr, TVN_BEGINRDRAGW, ht.hItem, ht.pt);
Chris Morgan5f9fd772000-09-13 20:27:30 +00004120 infoPtr->dropItem = ht.hItem;
4121 }
4122 }
4123 else
4124 {
4125 SetFocus(infoPtr->hwnd);
4126 TREEVIEW_SendSimpleNotify(infoPtr, NM_RCLICK);
4127 }
4128
4129 return 0;
4130}
4131
4132static LRESULT
4133TREEVIEW_RButtonUp(TREEVIEW_INFO *infoPtr, LPPOINT pPt)
4134{
Chris Morgan5f9fd772000-09-13 20:27:30 +00004135 return 0;
4136}
4137
4138
4139static LRESULT
4140TREEVIEW_CreateDragImage(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
4141{
4142 TREEVIEW_ITEM *dragItem = (HTREEITEM)lParam;
4143 INT cx, cy;
4144 HDC hdc, htopdc;
4145 HWND hwtop;
4146 HBITMAP hbmp, hOldbmp;
4147 SIZE size;
4148 RECT rc;
4149 HFONT hOldFont;
4150
4151 TRACE("\n");
4152
4153 if (!(infoPtr->himlNormal))
4154 return 0;
4155
4156 if (!dragItem || !TREEVIEW_ValidItem(infoPtr, dragItem))
4157 return 0;
4158
4159 TREEVIEW_UpdateDispInfo(infoPtr, dragItem, TVIF_TEXT);
4160
4161 hwtop = GetDesktopWindow();
4162 htopdc = GetDC(hwtop);
4163 hdc = CreateCompatibleDC(htopdc);
4164
4165 hOldFont = SelectObject(hdc, infoPtr->hFont);
Huw Davies5693ecd2003-11-26 03:37:30 +00004166 GetTextExtentPoint32W(hdc, dragItem->pszText, strlenW(dragItem->pszText),
Chris Morgan5f9fd772000-09-13 20:27:30 +00004167 &size);
Huw Davies5693ecd2003-11-26 03:37:30 +00004168 TRACE("%ld %ld %s %d\n", size.cx, size.cy, debugstr_w(dragItem->pszText),
4169 strlenW(dragItem->pszText));
Chris Morgan5f9fd772000-09-13 20:27:30 +00004170 hbmp = CreateCompatibleBitmap(htopdc, size.cx, size.cy);
4171 hOldbmp = SelectObject(hdc, hbmp);
4172
4173 ImageList_GetIconSize(infoPtr->himlNormal, &cx, &cy);
4174 size.cx += cx;
4175 if (cy > size.cy)
4176 size.cy = cy;
4177
4178 infoPtr->dragList = ImageList_Create(size.cx, size.cy, ILC_COLOR, 10, 10);
4179 ImageList_Draw(infoPtr->himlNormal, dragItem->iImage, hdc, 0, 0,
4180 ILD_NORMAL);
Alex Priemd2d7f961999-01-30 13:02:30 +00004181
4182/*
4183 ImageList_GetImageInfo (infoPtr->himlNormal, dragItem->hItem, &iminfo);
4184 ImageList_AddMasked (infoPtr->dragList, iminfo.hbmImage, CLR_DEFAULT);
4185*/
4186
4187/* draw item text */
4188
Chris Morgan5f9fd772000-09-13 20:27:30 +00004189 SetRect(&rc, cx, 0, size.cx, size.cy);
Huw Davies5693ecd2003-11-26 03:37:30 +00004190 DrawTextW(hdc, dragItem->pszText, strlenW(dragItem->pszText), &rc,
Chris Morgan5f9fd772000-09-13 20:27:30 +00004191 DT_LEFT);
4192 SelectObject(hdc, hOldFont);
4193 SelectObject(hdc, hOldbmp);
Alex Priemd2d7f961999-01-30 13:02:30 +00004194
Chris Morgan5f9fd772000-09-13 20:27:30 +00004195 ImageList_Add(infoPtr->dragList, hbmp, 0);
Alex Priemd2d7f961999-01-30 13:02:30 +00004196
Chris Morgan5f9fd772000-09-13 20:27:30 +00004197 DeleteDC(hdc);
4198 DeleteObject(hbmp);
4199 ReleaseDC(hwtop, htopdc);
Alex Priemd2d7f961999-01-30 13:02:30 +00004200
Chris Morgan5f9fd772000-09-13 20:27:30 +00004201 return (LRESULT)infoPtr->dragList;
Alex Priemd2d7f961999-01-30 13:02:30 +00004202}
4203
Chris Morgan5f9fd772000-09-13 20:27:30 +00004204/* Selection ************************************************************/
Alex Priemd2d7f961999-01-30 13:02:30 +00004205
Alex Priemd2d7f961999-01-30 13:02:30 +00004206static LRESULT
Chris Morgan5f9fd772000-09-13 20:27:30 +00004207TREEVIEW_DoSelectItem(TREEVIEW_INFO *infoPtr, INT action, HTREEITEM newSelect,
4208 INT cause)
Alex Priemd2d7f961999-01-30 13:02:30 +00004209{
Chris Morgan5f9fd772000-09-13 20:27:30 +00004210 TREEVIEW_ITEM *prevSelect;
4211 RECT rcFocused;
Alex Priemd2d7f961999-01-30 13:02:30 +00004212
Chris Morgan5f9fd772000-09-13 20:27:30 +00004213 assert(newSelect == NULL || TREEVIEW_ValidItem(infoPtr, newSelect));
Alexandre Julliardd30dfd21998-09-27 18:28:36 +00004214
Chris Morgan5f9fd772000-09-13 20:27:30 +00004215 TRACE("Entering item %p (%s), flag %x, cause %x, state %d\n",
4216 newSelect, TREEVIEW_ItemName(newSelect), action, cause,
4217 newSelect ? newSelect->state : 0);
Sylvain St.Germain90245261999-04-10 16:35:30 +00004218
Chris Morgan5f9fd772000-09-13 20:27:30 +00004219 /* reset and redraw focusedItem if focusedItem was set so we don't */
4220 /* have to worry about the previously focused item when we set a new one */
4221 if(infoPtr->focusedItem)
4222 {
4223 rcFocused = (infoPtr->focusedItem)->rect;
4224 infoPtr->focusedItem = 0;
4225 InvalidateRect(infoPtr->hwnd, &rcFocused, TRUE);
4226 }
Alexandre Julliardd30dfd21998-09-27 18:28:36 +00004227
Chris Morgan5f9fd772000-09-13 20:27:30 +00004228 switch (action)
4229 {
4230 case TVGN_CARET:
4231 prevSelect = infoPtr->selectedItem;
Sylvain St.Germain90245261999-04-10 16:35:30 +00004232
Krzysztof Foltman112df732005-06-13 11:38:55 +00004233 if (prevSelect == newSelect) {
4234 TREEVIEW_EnsureVisible(infoPtr, infoPtr->selectedItem, FALSE);
4235 break;
4236 }
4237
Chris Morgan5f9fd772000-09-13 20:27:30 +00004238 if (TREEVIEW_SendTreeviewNotify(infoPtr,
Dimitrie O. Paun4bbd7b32002-10-31 00:54:53 +00004239 TVN_SELCHANGINGW,
Chris Morgan5f9fd772000-09-13 20:27:30 +00004240 cause,
4241 TVIF_HANDLE | TVIF_STATE | TVIF_PARAM,
4242 prevSelect,
4243 newSelect))
4244 return FALSE;
Sylvain St.Germain90245261999-04-10 16:35:30 +00004245
Chris Morgan5f9fd772000-09-13 20:27:30 +00004246 if (prevSelect)
4247 prevSelect->state &= ~TVIS_SELECTED;
4248 if (newSelect)
4249 newSelect->state |= TVIS_SELECTED;
Sylvain St.Germain90245261999-04-10 16:35:30 +00004250
Chris Morgan5f9fd772000-09-13 20:27:30 +00004251 infoPtr->selectedItem = newSelect;
Sylvain St.Germain90245261999-04-10 16:35:30 +00004252
Chris Morgan5f9fd772000-09-13 20:27:30 +00004253 TREEVIEW_EnsureVisible(infoPtr, infoPtr->selectedItem, FALSE);
Sylvain St.Germain90245261999-04-10 16:35:30 +00004254
Vitaliy Margolen3b3574a2005-04-14 13:56:12 +00004255 if (prevSelect)
4256 TREEVIEW_Invalidate(infoPtr, prevSelect);
4257 if (newSelect)
4258 TREEVIEW_Invalidate(infoPtr, newSelect);
4259
Chris Morgan5f9fd772000-09-13 20:27:30 +00004260 TREEVIEW_SendTreeviewNotify(infoPtr,
Dimitrie O. Paun4bbd7b32002-10-31 00:54:53 +00004261 TVN_SELCHANGEDW,
Chris Morgan5f9fd772000-09-13 20:27:30 +00004262 cause,
4263 TVIF_HANDLE | TVIF_STATE | TVIF_PARAM,
4264 prevSelect,
4265 newSelect);
Chris Morgan5f9fd772000-09-13 20:27:30 +00004266 break;
Sylvain St.Germain90245261999-04-10 16:35:30 +00004267
Chris Morgan5f9fd772000-09-13 20:27:30 +00004268 case TVGN_DROPHILITE:
4269 prevSelect = infoPtr->dropItem;
Sylvain St.Germain90245261999-04-10 16:35:30 +00004270
Chris Morgan5f9fd772000-09-13 20:27:30 +00004271 if (prevSelect)
4272 prevSelect->state &= ~TVIS_DROPHILITED;
Sylvain St.Germain90245261999-04-10 16:35:30 +00004273
Chris Morgan5f9fd772000-09-13 20:27:30 +00004274 infoPtr->dropItem = newSelect;
Sylvain St.Germain90245261999-04-10 16:35:30 +00004275
Chris Morgan5f9fd772000-09-13 20:27:30 +00004276 if (newSelect)
4277 newSelect->state |= TVIS_DROPHILITED;
Sylvain St.Germain90245261999-04-10 16:35:30 +00004278
Susan Farley8d8c87b2000-10-25 20:30:58 +00004279 TREEVIEW_Invalidate(infoPtr, prevSelect);
4280 TREEVIEW_Invalidate(infoPtr, newSelect);
Chris Morgan5f9fd772000-09-13 20:27:30 +00004281 break;
Sylvain St.Germain90245261999-04-10 16:35:30 +00004282
Chris Morgan5f9fd772000-09-13 20:27:30 +00004283 case TVGN_FIRSTVISIBLE:
Evan Deaubl20c93c22005-03-14 10:51:06 +00004284 if (newSelect != NULL)
4285 {
4286 TREEVIEW_EnsureVisible(infoPtr, newSelect, FALSE);
4287 TREEVIEW_SetFirstVisible(infoPtr, newSelect, TRUE);
4288 TREEVIEW_Invalidate(infoPtr, NULL);
4289 }
Chris Morgan5f9fd772000-09-13 20:27:30 +00004290 break;
4291 }
Sylvain St.Germain90245261999-04-10 16:35:30 +00004292
Chris Morgan5f9fd772000-09-13 20:27:30 +00004293 TRACE("Leaving state %d\n", newSelect ? newSelect->state : 0);
4294 return TRUE;
Alexandre Julliardd30dfd21998-09-27 18:28:36 +00004295}
4296
Chris Morgan5f9fd772000-09-13 20:27:30 +00004297/* FIXME: handle NM_KILLFOCUS etc */
Alex Priemd2d7f961999-01-30 13:02:30 +00004298static LRESULT
Chris Morgan5f9fd772000-09-13 20:27:30 +00004299TREEVIEW_SelectItem(TREEVIEW_INFO *infoPtr, INT wParam, HTREEITEM item)
Alex Priemd2d7f961999-01-30 13:02:30 +00004300{
Chris Morgan5f9fd772000-09-13 20:27:30 +00004301 if (item != NULL && !TREEVIEW_ValidItem(infoPtr, item))
4302 return FALSE;
4303
4304 TRACE("%p (%s) %d\n", item, TREEVIEW_ItemName(item), wParam);
4305
4306 if (!TREEVIEW_DoSelectItem(infoPtr, wParam, item, TVC_UNKNOWN))
4307 return FALSE;
4308
4309 return TRUE;
Alex Priemd2d7f961999-01-30 13:02:30 +00004310}
4311
François Gouget2ad98552001-02-12 18:08:08 +00004312/*************************************************************************
4313 * TREEVIEW_ProcessLetterKeys
4314 *
Vincent Béron9a624912002-05-31 23:06:46 +00004315 * Processes keyboard messages generated by pressing the letter keys
François Gouget2ad98552001-02-12 18:08:08 +00004316 * on the keyboard.
Vincent Béron9a624912002-05-31 23:06:46 +00004317 * What this does is perform a case insensitive search from the
François Gouget2ad98552001-02-12 18:08:08 +00004318 * current position with the following quirks:
Vincent Béron9a624912002-05-31 23:06:46 +00004319 * - If two chars or more are pressed in quick succession we search
François Gouget2ad98552001-02-12 18:08:08 +00004320 * for the corresponding string (e.g. 'abc').
Vincent Béron9a624912002-05-31 23:06:46 +00004321 * - If there is a delay we wipe away the current search string and
François Gouget2ad98552001-02-12 18:08:08 +00004322 * restart with just that char.
Vincent Béron9a624912002-05-31 23:06:46 +00004323 * - If the user keeps pressing the same character, whether slowly or
4324 * fast, so that the search string is entirely composed of this
4325 * character ('aaaaa' for instance), then we search for first item
François Gouget2ad98552001-02-12 18:08:08 +00004326 * that starting with that character.
Vincent Béron9a624912002-05-31 23:06:46 +00004327 * - If the user types the above character in quick succession, then
4328 * we must also search for the corresponding string ('aaaaa'), and
François Gouget2ad98552001-02-12 18:08:08 +00004329 * go to that string if there is a match.
4330 *
4331 * RETURNS
4332 *
4333 * Zero.
4334 *
4335 * BUGS
4336 *
Vincent Béron9a624912002-05-31 23:06:46 +00004337 * - The current implementation has a list of characters it will
4338 * accept and it ignores averything else. In particular it will
4339 * ignore accentuated characters which seems to match what
4340 * Windows does. But I'm not sure it makes sense to follow
François Gouget2ad98552001-02-12 18:08:08 +00004341 * Windows there.
4342 * - We don't sound a beep when the search fails.
Vincent Béron9a624912002-05-31 23:06:46 +00004343 * - The search should start from the focused item, not from the selected
François Gouget2ad98552001-02-12 18:08:08 +00004344 * item. One reason for this is to allow for multiple selections in trees.
4345 * But currently infoPtr->focusedItem does not seem very usable.
4346 *
4347 * SEE ALSO
4348 *
4349 * TREEVIEW_ProcessLetterKeys
4350 */
4351static INT TREEVIEW_ProcessLetterKeys(
4352 HWND hwnd, /* handle to the window */
4353 WPARAM charCode, /* the character code, the actual character */
4354 LPARAM keyData /* key data */
4355 )
4356{
4357 TREEVIEW_INFO *infoPtr;
4358 HTREEITEM nItem;
4359 HTREEITEM endidx,idx;
Huw Davies5693ecd2003-11-26 03:37:30 +00004360 TVITEMEXW item;
4361 WCHAR buffer[MAX_PATH];
François Gouget2ad98552001-02-12 18:08:08 +00004362 DWORD timestamp,elapsed;
4363
4364 /* simple parameter checking */
4365 if (!hwnd || !charCode || !keyData)
4366 return 0;
4367
Robert Shearmancdb263e2004-08-25 17:33:01 +00004368 infoPtr=(TREEVIEW_INFO*)GetWindowLongPtrW(hwnd, 0);
François Gouget2ad98552001-02-12 18:08:08 +00004369 if (!infoPtr)
4370 return 0;
4371
4372 /* only allow the valid WM_CHARs through */
4373 if (!isalnum(charCode) &&
4374 charCode != '.' && charCode != '`' && charCode != '!' &&
4375 charCode != '@' && charCode != '#' && charCode != '$' &&
4376 charCode != '%' && charCode != '^' && charCode != '&' &&
4377 charCode != '*' && charCode != '(' && charCode != ')' &&
4378 charCode != '-' && charCode != '_' && charCode != '+' &&
4379 charCode != '=' && charCode != '\\'&& charCode != ']' &&
4380 charCode != '}' && charCode != '[' && charCode != '{' &&
4381 charCode != '/' && charCode != '?' && charCode != '>' &&
4382 charCode != '<' && charCode != ',' && charCode != '~')
4383 return 0;
4384
4385 /* compute how much time elapsed since last keypress */
4386 timestamp = GetTickCount();
4387 if (timestamp > infoPtr->lastKeyPressTimestamp) {
4388 elapsed=timestamp-infoPtr->lastKeyPressTimestamp;
4389 } else {
4390 elapsed=infoPtr->lastKeyPressTimestamp-timestamp;
4391 }
4392
4393 /* update the search parameters */
4394 infoPtr->lastKeyPressTimestamp=timestamp;
4395 if (elapsed < KEY_DELAY) {
Huw Davies5693ecd2003-11-26 03:37:30 +00004396 if (infoPtr->nSearchParamLength < sizeof(infoPtr->szSearchParam) / sizeof(WCHAR)) {
François Gouget2ad98552001-02-12 18:08:08 +00004397 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
4398 }
4399 if (infoPtr->charCode != charCode) {
4400 infoPtr->charCode=charCode=0;
4401 }
4402 } else {
4403 infoPtr->charCode=charCode;
4404 infoPtr->szSearchParam[0]=charCode;
4405 infoPtr->nSearchParamLength=1;
4406 /* Redundant with the 1 char string */
4407 charCode=0;
4408 }
4409
4410 /* and search from the current position */
4411 nItem=NULL;
4412 if (infoPtr->selectedItem != NULL) {
4413 endidx=infoPtr->selectedItem;
4414 /* if looking for single character match,
4415 * then we must always move forward
4416 */
4417 if (infoPtr->nSearchParamLength == 1)
4418 idx=TREEVIEW_GetNextListItem(infoPtr,endidx);
4419 else
4420 idx=endidx;
4421 } else {
4422 endidx=NULL;
4423 idx=infoPtr->root->firstChild;
4424 }
4425 do {
Jason Edmeades06c7c902005-03-23 11:58:58 +00004426 /* At the end point, sort out wrapping */
François Gouget2ad98552001-02-12 18:08:08 +00004427 if (idx == NULL) {
Jason Edmeades06c7c902005-03-23 11:58:58 +00004428
4429 /* If endidx is null, stop at the last item (ie top to bottom) */
François Gouget2ad98552001-02-12 18:08:08 +00004430 if (endidx == NULL)
4431 break;
Jason Edmeades06c7c902005-03-23 11:58:58 +00004432
4433 /* Otherwise, start again at the very beginning */
François Gouget2ad98552001-02-12 18:08:08 +00004434 idx=infoPtr->root->firstChild;
Jason Edmeades06c7c902005-03-23 11:58:58 +00004435
4436 /* But if we are stopping on the first child, end now! */
4437 if (idx == endidx) break;
François Gouget2ad98552001-02-12 18:08:08 +00004438 }
4439
4440 /* get item */
4441 ZeroMemory(&item, sizeof(item));
4442 item.mask = TVIF_TEXT;
4443 item.hItem = idx;
4444 item.pszText = buffer;
4445 item.cchTextMax = sizeof(buffer);
Huw Davies96b90572003-11-26 22:26:19 +00004446 TREEVIEW_GetItemT( infoPtr, &item, TRUE );
François Gouget2ad98552001-02-12 18:08:08 +00004447
4448 /* check for a match */
Huw Davies5693ecd2003-11-26 03:37:30 +00004449 if (strncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
François Gouget2ad98552001-02-12 18:08:08 +00004450 nItem=idx;
4451 break;
4452 } else if ( (charCode != 0) && (nItem == NULL) &&
4453 (nItem != infoPtr->selectedItem) &&
Huw Davies5693ecd2003-11-26 03:37:30 +00004454 (strncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
François Gouget2ad98552001-02-12 18:08:08 +00004455 /* This would work but we must keep looking for a longer match */
4456 nItem=idx;
4457 }
4458 idx=TREEVIEW_GetNextListItem(infoPtr,idx);
4459 } while (idx != endidx);
4460
4461 if (nItem != NULL) {
4462 if (TREEVIEW_DoSelectItem(infoPtr, TVGN_CARET, nItem, TVC_BYKEYBOARD)) {
4463 TREEVIEW_EnsureVisible(infoPtr, nItem, FALSE);
4464 }
4465 }
4466
4467 return 0;
4468}
4469
Chris Morgan5f9fd772000-09-13 20:27:30 +00004470/* Scrolling ************************************************************/
Alex Priemd2d7f961999-01-30 13:02:30 +00004471
Alex Priem374a0a81998-12-18 15:56:48 +00004472static LRESULT
Chris Morgan5f9fd772000-09-13 20:27:30 +00004473TREEVIEW_EnsureVisible(TREEVIEW_INFO *infoPtr, HTREEITEM item, BOOL bHScroll)
Alex Priem374a0a81998-12-18 15:56:48 +00004474{
Ulrich Czekalla1afef2c2003-11-27 00:55:18 +00004475 int viscount;
4476 BOOL hasFirstVisible = infoPtr->firstVisible != NULL;
Chris Morgan5f9fd772000-09-13 20:27:30 +00004477 HTREEITEM newFirstVisible = NULL;
Ulrich Czekalla1afef2c2003-11-27 00:55:18 +00004478 int visible_pos = -1;
Alex Priem374a0a81998-12-18 15:56:48 +00004479
Chris Morgan5f9fd772000-09-13 20:27:30 +00004480 if (!TREEVIEW_ValidItem(infoPtr, item))
4481 return FALSE;
4482
4483 if (!ISVISIBLE(item))
4484 {
4485 /* Expand parents as necessary. */
Chris Morgan36c8db82000-09-22 20:48:15 +00004486 HTREEITEM parent;
4487
4488 /* see if we are trying to ensure that root is vislble */
4489 if((item != infoPtr->root) && TREEVIEW_ValidItem(infoPtr, item))
4490 parent = item->parent;
4491 else
4492 parent = item; /* this item is the topmost item */
Chris Morgan5f9fd772000-09-13 20:27:30 +00004493
4494 while (parent != infoPtr->root)
4495 {
4496 if (!(parent->state & TVIS_EXPANDED))
4497 TREEVIEW_Expand(infoPtr, parent, FALSE, FALSE);
4498
4499 parent = parent->parent;
4500 }
4501 }
4502
Ulrich Czekalla1afef2c2003-11-27 00:55:18 +00004503 viscount = TREEVIEW_GetVisibleCount(infoPtr);
Chris Morgan5f9fd772000-09-13 20:27:30 +00004504
Jon Griffithsf479f102004-09-28 02:35:48 +00004505 TRACE("%p (%s) %ld - %ld viscount(%d)\n", item, TREEVIEW_ItemName(item), item->visibleOrder,
Ulrich Czekalla1afef2c2003-11-27 00:55:18 +00004506 hasFirstVisible ? infoPtr->firstVisible->visibleOrder : -1, viscount);
4507
4508 if (hasFirstVisible)
4509 visible_pos = item->visibleOrder - infoPtr->firstVisible->visibleOrder;
Chris Morgan5f9fd772000-09-13 20:27:30 +00004510
4511 if (visible_pos < 0)
4512 {
4513 /* item is before the start of the list: put it at the top. */
4514 newFirstVisible = item;
4515 }
Ulrich Czekalla1afef2c2003-11-27 00:55:18 +00004516 else if (visible_pos >= viscount
Chris Morgan5f9fd772000-09-13 20:27:30 +00004517 /* Sometimes, before we are displayed, GVC is 0, causing us to
4518 * spuriously scroll up. */
Huw Davies7e1d83e2004-05-02 04:20:06 +00004519 && visible_pos > 0 && !(infoPtr->dwStyle & TVS_NOSCROLL) )
Chris Morgan5f9fd772000-09-13 20:27:30 +00004520 {
4521 /* item is past the end of the list. */
Ulrich Czekalla1afef2c2003-11-27 00:55:18 +00004522 int scroll = visible_pos - viscount;
Chris Morgan5f9fd772000-09-13 20:27:30 +00004523
4524 newFirstVisible = TREEVIEW_GetListItem(infoPtr, infoPtr->firstVisible,
4525 scroll + 1);
4526 }
4527
4528 if (bHScroll)
4529 {
4530 /* Scroll window so item's text is visible as much as possible */
4531 /* Calculation of amount of extra space is taken from EditLabel code */
4532 INT pos, x;
Huw Davies5693ecd2003-11-26 03:37:30 +00004533 TEXTMETRICW textMetric;
Chris Morgan5f9fd772000-09-13 20:27:30 +00004534 HDC hdc = GetWindowDC(infoPtr->hwnd);
4535
4536 x = item->textWidth;
4537
Huw Davies5693ecd2003-11-26 03:37:30 +00004538 GetTextMetricsW(hdc, &textMetric);
Chris Morgan5f9fd772000-09-13 20:27:30 +00004539 ReleaseDC(infoPtr->hwnd, hdc);
4540
4541 x += (textMetric.tmMaxCharWidth * 2);
4542 x = max(x, textMetric.tmMaxCharWidth * 3);
4543
4544 if (item->textOffset < 0)
4545 pos = item->textOffset;
4546 else if (item->textOffset + x > infoPtr->clientWidth)
4547 {
4548 if (x > infoPtr->clientWidth)
4549 pos = item->textOffset;
4550 else
4551 pos = item->textOffset + x - infoPtr->clientWidth;
4552 }
4553 else
4554 pos = 0;
4555
4556 TREEVIEW_HScroll(infoPtr, MAKEWPARAM(SB_THUMBPOSITION, infoPtr->scrollX + pos));
4557 }
4558
4559 if (newFirstVisible != NULL && newFirstVisible != infoPtr->firstVisible)
4560 {
4561 TREEVIEW_SetFirstVisible(infoPtr, newFirstVisible, TRUE);
4562
4563 return TRUE;
4564 }
4565
4566 return FALSE;
4567}
4568
4569static VOID
4570TREEVIEW_SetFirstVisible(TREEVIEW_INFO *infoPtr,
4571 TREEVIEW_ITEM *newFirstVisible,
4572 BOOL bUpdateScrollPos)
4573{
4574 int gap_size;
4575
4576 TRACE("%p: %s\n", newFirstVisible, TREEVIEW_ItemName(newFirstVisible));
4577
4578 if (newFirstVisible != NULL)
4579 {
4580 /* Prevent an empty gap from appearing at the bottom... */
4581 gap_size = TREEVIEW_GetVisibleCount(infoPtr)
4582 - infoPtr->maxVisibleOrder + newFirstVisible->visibleOrder;
4583
4584 if (gap_size > 0)
4585 {
4586 newFirstVisible = TREEVIEW_GetListItem(infoPtr, newFirstVisible,
4587 -gap_size);
4588
4589 /* ... unless we just don't have enough items. */
4590 if (newFirstVisible == NULL)
4591 newFirstVisible = infoPtr->root->firstChild;
4592 }
4593 }
4594
4595 if (infoPtr->firstVisible != newFirstVisible)
4596 {
4597 if (infoPtr->firstVisible == NULL || newFirstVisible == NULL)
4598 {
4599 infoPtr->firstVisible = newFirstVisible;
Susan Farley8d8c87b2000-10-25 20:30:58 +00004600 TREEVIEW_Invalidate(infoPtr, NULL);
Chris Morgan5f9fd772000-09-13 20:27:30 +00004601 }
4602 else
4603 {
4604 TREEVIEW_ITEM *item;
4605 int scroll = infoPtr->uItemHeight *
4606 (infoPtr->firstVisible->visibleOrder
4607 - newFirstVisible->visibleOrder);
4608
4609 infoPtr->firstVisible = newFirstVisible;
4610
4611 for (item = infoPtr->root->firstChild; item != NULL;
4612 item = TREEVIEW_GetNextListItem(infoPtr, item))
4613 {
4614 item->rect.top += scroll;
4615 item->rect.bottom += scroll;
4616 }
4617
4618 if (bUpdateScrollPos)
4619 SetScrollPos(infoPtr->hwnd, SB_VERT,
4620 newFirstVisible->visibleOrder, TRUE);
4621
Vitaliy Margolenba5d2672004-09-13 19:17:35 +00004622 ScrollWindowEx(infoPtr->hwnd, 0, scroll, NULL, NULL, NULL, NULL, SW_ERASE | SW_INVALIDATE);
Chris Morgan5f9fd772000-09-13 20:27:30 +00004623 }
4624 }
4625}
4626
4627/************************************************************************
4628 * VScroll is always in units of visible items. i.e. we always have a
4629 * visible item aligned to the top of the control. (Unless we have no
4630 * items at all.)
4631 */
4632static LRESULT
4633TREEVIEW_VScroll(TREEVIEW_INFO *infoPtr, WPARAM wParam)
4634{
4635 TREEVIEW_ITEM *oldFirstVisible = infoPtr->firstVisible;
4636 TREEVIEW_ITEM *newFirstVisible = NULL;
4637
4638 int nScrollCode = LOWORD(wParam);
4639
4640 TRACE("wp %x\n", wParam);
4641
4642 if (!(infoPtr->uInternalStatus & TV_VSCROLL))
4643 return 0;
4644
4645 if (infoPtr->hwndEdit)
4646 SetFocus(infoPtr->hwnd);
4647
4648 if (!oldFirstVisible)
4649 {
4650 assert(infoPtr->root->firstChild == NULL);
4651 return 0;
4652 }
4653
4654 switch (nScrollCode)
4655 {
4656 case SB_TOP:
4657 newFirstVisible = infoPtr->root->firstChild;
4658 break;
4659
4660 case SB_BOTTOM:
4661 newFirstVisible = TREEVIEW_GetLastListItem(infoPtr, infoPtr->root);
4662 break;
4663
4664 case SB_LINEUP:
4665 newFirstVisible = TREEVIEW_GetPrevListItem(infoPtr, oldFirstVisible);
4666 break;
4667
4668 case SB_LINEDOWN:
4669 newFirstVisible = TREEVIEW_GetNextListItem(infoPtr, oldFirstVisible);
4670 break;
4671
4672 case SB_PAGEUP:
4673 newFirstVisible = TREEVIEW_GetListItem(infoPtr, oldFirstVisible,
4674 -max(1, TREEVIEW_GetVisibleCount(infoPtr)));
4675 break;
4676
4677 case SB_PAGEDOWN:
4678 newFirstVisible = TREEVIEW_GetListItem(infoPtr, oldFirstVisible,
4679 max(1, TREEVIEW_GetVisibleCount(infoPtr)));
4680 break;
4681
4682 case SB_THUMBTRACK:
4683 case SB_THUMBPOSITION:
4684 newFirstVisible = TREEVIEW_GetListItem(infoPtr,
4685 infoPtr->root->firstChild,
Alexandre Julliard83f52d12000-09-26 22:20:14 +00004686 (LONG)(SHORT)HIWORD(wParam));
Chris Morgan5f9fd772000-09-13 20:27:30 +00004687 break;
4688
4689 case SB_ENDSCROLL:
4690 return 0;
4691 }
4692
4693 if (newFirstVisible != NULL)
4694 {
4695 if (newFirstVisible != oldFirstVisible)
4696 TREEVIEW_SetFirstVisible(infoPtr, newFirstVisible,
4697 nScrollCode != SB_THUMBTRACK);
4698 else if (nScrollCode == SB_THUMBPOSITION)
4699 SetScrollPos(infoPtr->hwnd, SB_VERT,
4700 newFirstVisible->visibleOrder, TRUE);
4701 }
4702
4703 return 0;
Alex Priem374a0a81998-12-18 15:56:48 +00004704}
4705
4706static LRESULT
Chris Morgan5f9fd772000-09-13 20:27:30 +00004707TREEVIEW_HScroll(TREEVIEW_INFO *infoPtr, WPARAM wParam)
Alex Priem374a0a81998-12-18 15:56:48 +00004708{
Chris Morgan5f9fd772000-09-13 20:27:30 +00004709 int maxWidth;
4710 int scrollX = infoPtr->scrollX;
4711 int nScrollCode = LOWORD(wParam);
Alex Priem374a0a81998-12-18 15:56:48 +00004712
Chris Morgan5f9fd772000-09-13 20:27:30 +00004713 TRACE("wp %x\n", wParam);
Alex Priem374a0a81998-12-18 15:56:48 +00004714
Chris Morgan5f9fd772000-09-13 20:27:30 +00004715 if (!(infoPtr->uInternalStatus & TV_HSCROLL))
4716 return FALSE;
Alex Priem374a0a81998-12-18 15:56:48 +00004717
Chris Morgan5f9fd772000-09-13 20:27:30 +00004718 if (infoPtr->hwndEdit)
4719 SetFocus(infoPtr->hwnd);
Alex Priemd2d7f961999-01-30 13:02:30 +00004720
Chris Morgan5f9fd772000-09-13 20:27:30 +00004721 maxWidth = infoPtr->treeWidth - infoPtr->clientWidth;
Susan Farley036ae0b2000-10-13 20:13:38 +00004722 /* shall never occur */
Chris Morgan5f9fd772000-09-13 20:27:30 +00004723 if (maxWidth <= 0)
4724 {
4725 scrollX = 0;
4726 goto scroll;
4727 }
Alex Priem374a0a81998-12-18 15:56:48 +00004728
Chris Morgan5f9fd772000-09-13 20:27:30 +00004729 switch (nScrollCode)
4730 {
4731 case SB_LINELEFT:
4732 scrollX -= infoPtr->uItemHeight;
4733 break;
4734 case SB_LINERIGHT:
4735 scrollX += infoPtr->uItemHeight;
4736 break;
4737 case SB_PAGELEFT:
4738 scrollX -= infoPtr->clientWidth;
4739 break;
4740 case SB_PAGERIGHT:
4741 scrollX += infoPtr->clientWidth;
4742 break;
Alex Priem374a0a81998-12-18 15:56:48 +00004743
Chris Morgan5f9fd772000-09-13 20:27:30 +00004744 case SB_THUMBTRACK:
4745 case SB_THUMBPOSITION:
Alexandre Julliard83f52d12000-09-26 22:20:14 +00004746 scrollX = (int)(SHORT)HIWORD(wParam);
Chris Morgan5f9fd772000-09-13 20:27:30 +00004747 break;
Alex Priem374a0a81998-12-18 15:56:48 +00004748
Chris Morgan5f9fd772000-09-13 20:27:30 +00004749 case SB_ENDSCROLL:
4750 return 0;
4751 }
Alexandre Julliardd30dfd21998-09-27 18:28:36 +00004752
Chris Morgan5f9fd772000-09-13 20:27:30 +00004753 if (scrollX > maxWidth)
4754 scrollX = maxWidth;
4755 else if (scrollX < 0)
4756 scrollX = 0;
Alexandre Julliardd30dfd21998-09-27 18:28:36 +00004757
Chris Morgan5f9fd772000-09-13 20:27:30 +00004758scroll:
4759 if (scrollX != infoPtr->scrollX)
4760 {
4761 TREEVIEW_ITEM *item;
4762 LONG scroll_pixels = infoPtr->scrollX - scrollX;
Vincent Béron9a624912002-05-31 23:06:46 +00004763
Chris Morgan5f9fd772000-09-13 20:27:30 +00004764 for (item = infoPtr->root->firstChild; item != NULL;
4765 item = TREEVIEW_GetNextListItem(infoPtr, item))
4766 {
4767 item->linesOffset += scroll_pixels;
4768 item->stateOffset += scroll_pixels;
4769 item->imageOffset += scroll_pixels;
4770 item->textOffset += scroll_pixels;
4771 }
Alexandre Julliardd30dfd21998-09-27 18:28:36 +00004772
Chris Morgan5f9fd772000-09-13 20:27:30 +00004773 ScrollWindow(infoPtr->hwnd, scroll_pixels, 0, NULL, NULL);
4774 infoPtr->scrollX = scrollX;
4775 UpdateWindow(infoPtr->hwnd);
4776 }
Alexandre Julliardd30dfd21998-09-27 18:28:36 +00004777
Chris Morgan5f9fd772000-09-13 20:27:30 +00004778 if (nScrollCode != SB_THUMBTRACK)
4779 SetScrollPos(infoPtr->hwnd, SB_HORZ, scrollX, TRUE);
4780
4781 return 0;
Alexandre Julliardd30dfd21998-09-27 18:28:36 +00004782}
4783
4784static LRESULT
Chris Morgan5f9fd772000-09-13 20:27:30 +00004785TREEVIEW_MouseWheel(TREEVIEW_INFO *infoPtr, WPARAM wParam)
Alexandre Julliardd30dfd21998-09-27 18:28:36 +00004786{
Chris Morgan5f9fd772000-09-13 20:27:30 +00004787 short gcWheelDelta;
Stephane Lussier30316dd2000-04-19 16:45:30 +00004788 UINT pulScrollLines = 3;
4789
Chris Morgan5f9fd772000-09-13 20:27:30 +00004790 if (infoPtr->firstVisible == NULL)
4791 return TRUE;
Stephane Lussier30316dd2000-04-19 16:45:30 +00004792
Chris Morgan5f9fd772000-09-13 20:27:30 +00004793 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES, 0, &pulScrollLines, 0);
4794
4795 gcWheelDelta = -(short)HIWORD(wParam);
Stephane Lussier30316dd2000-04-19 16:45:30 +00004796 pulScrollLines *= (gcWheelDelta / WHEEL_DELTA);
4797
4798 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
4799 {
Chris Morgan5f9fd772000-09-13 20:27:30 +00004800 int newDy = infoPtr->firstVisible->visibleOrder + pulScrollLines;
4801 int maxDy = infoPtr->maxVisibleOrder;
Stephane Lussier30316dd2000-04-19 16:45:30 +00004802
Chris Morgan5f9fd772000-09-13 20:27:30 +00004803 if (newDy > maxDy)
4804 newDy = maxDy;
Stephane Lussier30316dd2000-04-19 16:45:30 +00004805
Chris Morgan5f9fd772000-09-13 20:27:30 +00004806 if (newDy < 0)
4807 newDy = 0;
Stephane Lussier30316dd2000-04-19 16:45:30 +00004808
Chris Morgan5f9fd772000-09-13 20:27:30 +00004809 TREEVIEW_VScroll(infoPtr, MAKEWPARAM(SB_THUMBPOSITION, newDy));
Stephane Lussier30316dd2000-04-19 16:45:30 +00004810 }
Chris Morgan5f9fd772000-09-13 20:27:30 +00004811 return TRUE;
Stephane Lussier30316dd2000-04-19 16:45:30 +00004812}
Alexandre Julliardd30dfd21998-09-27 18:28:36 +00004813
Chris Morgan5f9fd772000-09-13 20:27:30 +00004814/* Create/Destroy *******************************************************/
4815
Sylvain St.Germain49c1ca21999-03-18 17:30:44 +00004816static LRESULT
Dimitrie O. Paunc5940432003-11-20 22:04:13 +00004817TREEVIEW_Create(HWND hwnd, const CREATESTRUCTW *lpcs)
Vincent Béron9a624912002-05-31 23:06:46 +00004818{
Jon Griffiths3772c732004-09-13 23:22:30 +00004819 static const WCHAR szDisplayW[] = { 'D','I','S','P','L','A','Y','\0' };
Chris Morgan5f9fd772000-09-13 20:27:30 +00004820 RECT rcClient;
4821 TREEVIEW_INFO *infoPtr;
Huw Davies3138b4a2004-11-21 15:41:08 +00004822 LOGFONTW lf;
Sylvain St.Germain49c1ca21999-03-18 17:30:44 +00004823
Huw Davies5693ecd2003-11-26 03:37:30 +00004824 TRACE("wnd %p, style %lx\n", hwnd, GetWindowLongW(hwnd, GWL_STYLE));
Sylvain St.Germain90245261999-04-10 16:35:30 +00004825
Dimitrie O. Paun7de279a2003-09-22 21:32:33 +00004826 infoPtr = (TREEVIEW_INFO *)Alloc(sizeof(TREEVIEW_INFO));
Vincent Béron9a624912002-05-31 23:06:46 +00004827
Chris Morgan5f9fd772000-09-13 20:27:30 +00004828 if (infoPtr == NULL)
4829 {
4830 ERR("could not allocate info memory!\n");
4831 return 0;
4832 }
Alex Priemd2d7f961999-01-30 13:02:30 +00004833
Robert Shearmancdb263e2004-08-25 17:33:01 +00004834 SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr);
Stephane Lussier30316dd2000-04-19 16:45:30 +00004835
Chris Morgan5f9fd772000-09-13 20:27:30 +00004836 infoPtr->hwnd = hwnd;
Huw Davies5693ecd2003-11-26 03:37:30 +00004837 infoPtr->dwStyle = GetWindowLongW(hwnd, GWL_STYLE);
Chris Morgan5f9fd772000-09-13 20:27:30 +00004838 infoPtr->Timer = 0;
4839 infoPtr->uNumItems = 0;
4840 infoPtr->cdmode = 0;
4841 infoPtr->uScrollTime = 300; /* milliseconds */
4842 infoPtr->bRedraw = TRUE;
4843
4844 GetClientRect(hwnd, &rcClient);
4845
4846 /* No scroll bars yet. */
4847 infoPtr->clientWidth = rcClient.right;
4848 infoPtr->clientHeight = rcClient.bottom;
Robert Shearman1e92f4a2004-12-17 18:52:04 +00004849 infoPtr->uInternalStatus = 0;
Chris Morgan5f9fd772000-09-13 20:27:30 +00004850
4851 infoPtr->treeWidth = 0;
4852 infoPtr->treeHeight = 0;
4853
Jon Griffithsf479f102004-09-28 02:35:48 +00004854 infoPtr->uIndent = MINIMUM_INDENT;
Chris Morgan5f9fd772000-09-13 20:27:30 +00004855 infoPtr->selectedItem = 0;
4856 infoPtr->focusedItem = 0;
Martijn Vernooij2c2db4b2005-02-22 19:31:01 +00004857 infoPtr->hotItem = 0;
Chris Morgan5f9fd772000-09-13 20:27:30 +00004858 infoPtr->firstVisible = 0;
4859 infoPtr->maxVisibleOrder = 0;
4860 infoPtr->dropItem = 0;
4861 infoPtr->insertMarkItem = 0;
4862 infoPtr->insertBeforeorAfter = 0;
4863 /* dragList */
4864
4865 infoPtr->scrollX = 0;
4866
4867 infoPtr->clrBk = GetSysColor(COLOR_WINDOW);
4868 infoPtr->clrText = -1; /* use system color */
4869 infoPtr->clrLine = RGB(128, 128, 128);
4870 infoPtr->clrInsertMark = GetSysColor(COLOR_BTNTEXT);
4871
4872 /* hwndToolTip */
4873
4874 infoPtr->hwndEdit = 0;
4875 infoPtr->wpEditOrig = NULL;
4876 infoPtr->bIgnoreEditKillFocus = FALSE;
4877 infoPtr->bLabelChanged = FALSE;
4878
4879 infoPtr->himlNormal = NULL;
4880 infoPtr->himlState = NULL;
4881 infoPtr->normalImageWidth = 0;
4882 infoPtr->normalImageHeight = 0;
4883 infoPtr->stateImageWidth = 0;
4884 infoPtr->stateImageHeight = 0;
4885
4886 infoPtr->items = DPA_Create(16);
4887
Huw Davies3138b4a2004-11-21 15:41:08 +00004888 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, sizeof(lf), &lf, 0);
4889 infoPtr->hFont = infoPtr->hDefaultFont = CreateFontIndirectW(&lf);
Chris Morgan5f9fd772000-09-13 20:27:30 +00004890 infoPtr->hBoldFont = TREEVIEW_CreateBoldFont(infoPtr->hFont);
Robert Shearman1e92f4a2004-12-17 18:52:04 +00004891 infoPtr->hUnderlineFont = TREEVIEW_CreateUnderlineFont(infoPtr->hFont);
4892 infoPtr->hcurHand = LoadCursorW(NULL, (LPWSTR)IDC_HAND);
Chris Morgan5f9fd772000-09-13 20:27:30 +00004893
4894 infoPtr->uItemHeight = TREEVIEW_NaturalHeight(infoPtr);
4895
4896 infoPtr->root = TREEVIEW_AllocateItem(infoPtr);
4897 infoPtr->root->state = TVIS_EXPANDED;
4898 infoPtr->root->iLevel = -1;
4899 infoPtr->root->visibleOrder = -1;
4900
Dimitrie O. Paunc5940432003-11-20 22:04:13 +00004901 infoPtr->hwndNotify = lpcs->hwndParent;
Chris Morgan5f9fd772000-09-13 20:27:30 +00004902#if 0
Huw Davies5693ecd2003-11-26 03:37:30 +00004903 infoPtr->bTransparent = ( GetWindowLongW( hwnd, GWL_STYLE) & TBSTYLE_FLAT);
Chris Morgan5f9fd772000-09-13 20:27:30 +00004904#endif
4905
4906 infoPtr->hwndToolTip = 0;
Guy L. Albertellib4994d12002-03-20 01:29:04 +00004907
Huw Davies5693ecd2003-11-26 03:37:30 +00004908 infoPtr->bNtfUnicode = IsWindowUnicode (hwnd);
Guy L. Albertellib4994d12002-03-20 01:29:04 +00004909
4910 /* Determine what type of notify should be issued */
4911 /* sets infoPtr->bNtfUnicode */
Dimitrie O. Paund2e881d2002-11-27 20:15:49 +00004912 TREEVIEW_NotifyFormat(infoPtr, infoPtr->hwndNotify, NF_REQUERY);
Guy L. Albertellib4994d12002-03-20 01:29:04 +00004913
Chris Morgan5f9fd772000-09-13 20:27:30 +00004914 if (!(infoPtr->dwStyle & TVS_NOTOOLTIPS))
4915 infoPtr->hwndToolTip = COMCTL32_CreateToolTip(hwnd);
4916
4917 if (infoPtr->dwStyle & TVS_CHECKBOXES)
4918 {
4919 RECT rc;
4920 HBITMAP hbm, hbmOld;
Maxime Bellengécb1c1122003-08-23 23:18:48 +00004921 HDC hdc,hdcScreen;
Chris Morgan5f9fd772000-09-13 20:27:30 +00004922 int nIndex;
4923
4924 infoPtr->himlState =
4925 ImageList_Create(16, 16, ILC_COLOR | ILC_MASK, 3, 0);
4926
Jon Griffiths3772c732004-09-13 23:22:30 +00004927 hdcScreen = CreateDCW(szDisplayW, NULL, NULL, NULL);
Maxime Bellengécb1c1122003-08-23 23:18:48 +00004928
4929 /* Create a coloured bitmap compatible with the screen depth
4930 because checkboxes are not black&white */
4931 hdc = CreateCompatibleDC(hdcScreen);
4932 hbm = CreateCompatibleBitmap(hdcScreen, 48, 16);
Chris Morgan5f9fd772000-09-13 20:27:30 +00004933 hbmOld = SelectObject(hdc, hbm);
4934
4935 rc.left = 0; rc.top = 0;
4936 rc.right = 48; rc.bottom = 16;
4937 FillRect(hdc, &rc, (HBRUSH)(COLOR_WINDOW+1));
4938
4939 rc.left = 18; rc.top = 2;
4940 rc.right = 30; rc.bottom = 14;
4941 DrawFrameControl(hdc, &rc, DFC_BUTTON,
4942 DFCS_BUTTONCHECK|DFCS_FLAT);
4943
4944 rc.left = 34; rc.right = 46;
4945 DrawFrameControl(hdc, &rc, DFC_BUTTON,
4946 DFCS_BUTTONCHECK|DFCS_FLAT|DFCS_CHECKED);
4947
Maxime Bellengécb1c1122003-08-23 23:18:48 +00004948 SelectObject(hdc, hbmOld);
Chris Morgan5f9fd772000-09-13 20:27:30 +00004949 nIndex = ImageList_AddMasked(infoPtr->himlState, hbm,
4950 GetSysColor(COLOR_WINDOW));
Maxime Bellengécb1c1122003-08-23 23:18:48 +00004951 TRACE("checkbox index %d\n", nIndex);
4952
Chris Morgan5f9fd772000-09-13 20:27:30 +00004953 DeleteObject(hbm);
4954 DeleteDC(hdc);
Maxime Bellengécb1c1122003-08-23 23:18:48 +00004955 DeleteDC(hdcScreen);
Chris Morgan5f9fd772000-09-13 20:27:30 +00004956
4957 infoPtr->stateImageWidth = 16;
4958 infoPtr->stateImageHeight = 16;
4959 }
Robert Shearman1e92f4a2004-12-17 18:52:04 +00004960
4961 /* Make sure actual scrollbar state is consistent with uInternalStatus */
4962 ShowScrollBar(hwnd, SB_VERT, FALSE);
4963 ShowScrollBar(hwnd, SB_HORZ, FALSE);
Frank Richter171fea02005-07-28 10:15:10 +00004964
4965 OpenThemeData (hwnd, themeClass);
Robert Shearman1e92f4a2004-12-17 18:52:04 +00004966
Alexandre Julliard642d3131998-07-12 19:29:36 +00004967 return 0;
4968}
4969
4970
Chris Morgan5f9fd772000-09-13 20:27:30 +00004971static LRESULT
4972TREEVIEW_Destroy(TREEVIEW_INFO *infoPtr)
4973{
4974 TRACE("\n");
4975
4976 TREEVIEW_RemoveTree(infoPtr);
4977
4978 /* tool tip is automatically destroyed: we are its owner */
4979
Andreas Mohr8bc7f162002-02-27 01:34:08 +00004980 /* Restore original wndproc */
Chris Morgan5f9fd772000-09-13 20:27:30 +00004981 if (infoPtr->hwndEdit)
Robert Shearmancdb263e2004-08-25 17:33:01 +00004982 SetWindowLongPtrW(infoPtr->hwndEdit, GWLP_WNDPROC,
4983 (DWORD_PTR)infoPtr->wpEditOrig);
Chris Morgan5f9fd772000-09-13 20:27:30 +00004984
Frank Richter171fea02005-07-28 10:15:10 +00004985 CloseThemeData (GetWindowTheme (infoPtr->hwnd));
4986
Chris Morgan5f9fd772000-09-13 20:27:30 +00004987 /* Deassociate treeview from the window before doing anything drastic. */
Robert Shearmancdb263e2004-08-25 17:33:01 +00004988 SetWindowLongPtrW(infoPtr->hwnd, 0, (DWORD_PTR)NULL);
Chris Morgan5f9fd772000-09-13 20:27:30 +00004989
Frank Richter171fea02005-07-28 10:15:10 +00004990
Huw Davies3138b4a2004-11-21 15:41:08 +00004991 DeleteObject(infoPtr->hDefaultFont);
Chris Morgan5f9fd772000-09-13 20:27:30 +00004992 DeleteObject(infoPtr->hBoldFont);
Robert Shearman1e92f4a2004-12-17 18:52:04 +00004993 DeleteObject(infoPtr->hUnderlineFont);
Dimitrie O. Paun7de279a2003-09-22 21:32:33 +00004994 Free(infoPtr);
Chris Morgan5f9fd772000-09-13 20:27:30 +00004995
4996 return 0;
4997}
4998
4999/* Miscellaneous Messages ***********************************************/
5000
5001static LRESULT
5002TREEVIEW_ScrollKeyDown(TREEVIEW_INFO *infoPtr, WPARAM key)
5003{
5004 static const struct
5005 {
5006 unsigned char code;
5007 }
5008 scroll[] =
5009 {
5010#define SCROLL_ENTRY(dir, code) { ((dir) << 7) | (code) }
5011 SCROLL_ENTRY(SB_VERT, SB_PAGEUP), /* VK_PRIOR */
5012 SCROLL_ENTRY(SB_VERT, SB_PAGEDOWN), /* VK_NEXT */
5013 SCROLL_ENTRY(SB_VERT, SB_BOTTOM), /* VK_END */
5014 SCROLL_ENTRY(SB_VERT, SB_TOP), /* VK_HOME */
5015 SCROLL_ENTRY(SB_HORZ, SB_LINEUP), /* VK_LEFT */
5016 SCROLL_ENTRY(SB_VERT, SB_LINEUP), /* VK_UP */
5017 SCROLL_ENTRY(SB_HORZ, SB_LINEDOWN), /* VK_RIGHT */
5018 SCROLL_ENTRY(SB_VERT, SB_LINEDOWN) /* VK_DOWN */
5019#undef SCROLL_ENTRY
5020 };
5021
5022 if (key >= VK_PRIOR && key <= VK_DOWN)
5023 {
5024 unsigned char code = scroll[key - VK_PRIOR].code;
5025
5026 (((code & (1 << 7)) == (SB_HORZ << 7))
5027 ? TREEVIEW_HScroll
5028 : TREEVIEW_VScroll)(infoPtr, code & 0x7F);
5029 }
5030
5031 return 0;
5032}
5033
5034/************************************************************************
5035 * TREEVIEW_KeyDown
5036 *
5037 * VK_UP Move selection to the previous non-hidden item.
5038 * VK_DOWN Move selection to the next non-hidden item.
5039 * VK_HOME Move selection to the first item.
5040 * VK_END Move selection to the last item.
5041 * VK_LEFT If expanded then collapse, otherwise move to parent.
5042 * VK_RIGHT If collapsed then expand, otherwise move to first child.
5043 * VK_ADD Expand.
5044 * VK_SUBTRACT Collapse.
5045 * VK_MULTIPLY Expand all.
5046 * VK_PRIOR Move up GetVisibleCount items.
5047 * VK_NEXT Move down GetVisibleCount items.
5048 * VK_BACK Move to parent.
5049 * CTRL-Left,Right,Up,Down,PgUp,PgDown,Home,End: Scroll without changing selection
5050 */
5051static LRESULT
5052TREEVIEW_KeyDown(TREEVIEW_INFO *infoPtr, WPARAM wParam)
5053{
5054 /* If it is non-NULL and different, it will be selected and visible. */
5055 TREEVIEW_ITEM *newSelection = NULL;
5056
5057 TREEVIEW_ITEM *prevItem = infoPtr->selectedItem;
5058
5059 TRACE("%x\n", wParam);
5060
5061 if (prevItem == NULL)
5062 return FALSE;
5063
5064 if (GetAsyncKeyState(VK_CONTROL) & 0x8000)
5065 return TREEVIEW_ScrollKeyDown(infoPtr, wParam);
5066
5067 switch (wParam)
5068 {
5069 case VK_UP:
5070 newSelection = TREEVIEW_GetPrevListItem(infoPtr, prevItem);
5071 if (!newSelection)
5072 newSelection = infoPtr->root->firstChild;
5073 break;
5074
5075 case VK_DOWN:
5076 newSelection = TREEVIEW_GetNextListItem(infoPtr, prevItem);
5077 break;
5078
5079 case VK_HOME:
5080 newSelection = infoPtr->root->firstChild;
5081 break;
5082
5083 case VK_END:
5084 newSelection = TREEVIEW_GetLastListItem(infoPtr, infoPtr->root);
5085 break;
5086
5087 case VK_LEFT:
5088 if (prevItem->state & TVIS_EXPANDED)
5089 {
5090 TREEVIEW_Collapse(infoPtr, prevItem, FALSE, TRUE);
5091 }
5092 else if (prevItem->parent != infoPtr->root)
5093 {
5094 newSelection = prevItem->parent;
5095 }
5096 break;
5097
5098 case VK_RIGHT:
5099 if (TREEVIEW_HasChildren(infoPtr, prevItem))
5100 {
5101 if (!(prevItem->state & TVIS_EXPANDED))
5102 TREEVIEW_Expand(infoPtr, prevItem, FALSE, TRUE);
5103 else
5104 {
5105 newSelection = prevItem->firstChild;
5106 }
5107 }
5108
5109 break;
5110
5111 case VK_MULTIPLY:
5112 TREEVIEW_ExpandAll(infoPtr, prevItem);
5113 break;
5114
5115 case VK_ADD:
5116 if (!(prevItem->state & TVIS_EXPANDED))
5117 TREEVIEW_Expand(infoPtr, prevItem, FALSE, TRUE);
5118 break;
5119
5120 case VK_SUBTRACT:
5121 if (prevItem->state & TVIS_EXPANDED)
5122 TREEVIEW_Collapse(infoPtr, prevItem, FALSE, TRUE);
5123 break;
5124
5125 case VK_PRIOR:
5126 newSelection
5127 = TREEVIEW_GetListItem(infoPtr, prevItem,
5128 -TREEVIEW_GetVisibleCount(infoPtr));
5129 break;
5130
5131 case VK_NEXT:
5132 newSelection
5133 = TREEVIEW_GetListItem(infoPtr, prevItem,
5134 TREEVIEW_GetVisibleCount(infoPtr));
5135 break;
5136
5137 case VK_BACK:
5138 newSelection = prevItem->parent;
5139 if (newSelection == infoPtr->root)
5140 newSelection = NULL;
5141 break;
5142
5143 case VK_SPACE:
5144 if (infoPtr->dwStyle & TVS_CHECKBOXES)
5145 TREEVIEW_ToggleItemState(infoPtr, prevItem);
5146 break;
5147 }
5148
5149 if (newSelection && newSelection != prevItem)
5150 {
5151 if (TREEVIEW_DoSelectItem(infoPtr, TVGN_CARET, newSelection,
5152 TVC_BYKEYBOARD))
5153 {
5154 TREEVIEW_EnsureVisible(infoPtr, newSelection, FALSE);
5155 }
5156 }
5157
5158 return FALSE;
5159}
5160
5161static LRESULT
Robert Shearman1e92f4a2004-12-17 18:52:04 +00005162TREEVIEW_MouseLeave (TREEVIEW_INFO * infoPtr)
5163{
5164 if (infoPtr->hotItem)
5165 {
5166 /* remove hot effect from item */
5167 InvalidateRect(infoPtr->hwnd, &infoPtr->hotItem->rect, TRUE);
5168 infoPtr->hotItem = NULL;
5169 }
5170 return 0;
5171}
5172
5173static LRESULT
5174TREEVIEW_MouseMove (TREEVIEW_INFO * infoPtr, WPARAM wParam, LPARAM lParam)
5175{
5176 POINT pt;
5177 TRACKMOUSEEVENT trackinfo;
5178 TREEVIEW_ITEM * item;
5179
5180 /* fill in the TRACKMOUSEEVENT struct */
5181 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
5182 trackinfo.dwFlags = TME_QUERY;
5183 trackinfo.hwndTrack = infoPtr->hwnd;
5184 trackinfo.dwHoverTime = HOVER_DEFAULT;
5185
5186 /* call _TrackMouseEvent to see if we are currently tracking for this hwnd */
5187 _TrackMouseEvent(&trackinfo);
5188
5189 /* Make sure tracking is enabled so we receive a WM_MOUSELEAVE message */
5190 if(!(trackinfo.dwFlags & TME_LEAVE))
5191 {
5192 trackinfo.dwFlags = TME_LEAVE; /* notify upon leaving */
5193
5194 /* call TRACKMOUSEEVENT so we receive a WM_MOUSELEAVE message */
5195 /* and can properly deactivate the hot item */
5196 _TrackMouseEvent(&trackinfo);
5197 }
5198
5199 pt.x = (INT)LOWORD(lParam);
5200 pt.y = (INT)HIWORD(lParam);
5201
5202 item = TREEVIEW_HitTestPoint(infoPtr, pt);
5203
5204 if (item != infoPtr->hotItem)
5205 {
5206 /* redraw old hot item */
5207 if (infoPtr->hotItem)
5208 InvalidateRect(infoPtr->hwnd, &infoPtr->hotItem->rect, TRUE);
5209 infoPtr->hotItem = item;
5210 /* redraw new hot item */
5211 if (infoPtr->hotItem)
5212 InvalidateRect(infoPtr->hwnd, &infoPtr->hotItem->rect, TRUE);
5213 }
5214
5215 return 0;
5216}
5217
Frank Richter171fea02005-07-28 10:15:10 +00005218/* Draw themed border */
5219static BOOL nc_paint (TREEVIEW_INFO *infoPtr, HRGN region)
5220{
5221 HTHEME theme = GetWindowTheme (infoPtr->hwnd);
5222 HDC dc;
5223 RECT r;
5224 HRGN cliprgn;
5225 int cxEdge = GetSystemMetrics (SM_CXEDGE),
5226 cyEdge = GetSystemMetrics (SM_CYEDGE);
5227
5228 if (!theme) return FALSE;
5229
5230 GetWindowRect(infoPtr->hwnd, &r);
5231
5232 cliprgn = CreateRectRgn (r.left + cxEdge, r.top + cyEdge,
5233 r.right - cxEdge, r.bottom - cyEdge);
5234 if (region != (HRGN)1)
5235 CombineRgn (cliprgn, cliprgn, region, RGN_AND);
5236 OffsetRect(&r, -r.left, -r.top);
5237
5238 dc = GetDCEx(infoPtr->hwnd, region, DCX_WINDOW|DCX_INTERSECTRGN);
5239 OffsetRect(&r, -r.left, -r.top);
5240
5241 if (IsThemeBackgroundPartiallyTransparent (theme, 0, 0))
5242 DrawThemeParentBackground(infoPtr->hwnd, dc, &r);
5243 DrawThemeBackground (theme, dc, 0, 0, &r, 0);
5244 ReleaseDC(infoPtr->hwnd, dc);
5245
5246 /* Call default proc to get the scrollbars etc. painted */
5247 DefWindowProcW (infoPtr->hwnd, WM_NCPAINT, (WPARAM)cliprgn, 0);
5248
5249 return TRUE;
5250}
5251
Robert Shearman1e92f4a2004-12-17 18:52:04 +00005252static LRESULT
Guy L. Albertellibe73d102001-08-13 20:08:21 +00005253TREEVIEW_Notify(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
5254{
5255 LPNMHDR lpnmh = (LPNMHDR)lParam;
5256
5257 if (lpnmh->code == PGN_CALCSIZE) {
5258 LPNMPGCALCSIZE lppgc = (LPNMPGCALCSIZE)lParam;
5259
5260 if (lppgc->dwFlag == PGF_CALCWIDTH) {
5261 lppgc->iWidth = infoPtr->treeWidth;
Vincent Béron9a624912002-05-31 23:06:46 +00005262 TRACE("got PGN_CALCSIZE, returning horz size = %ld, client=%ld\n",
Guy L. Albertellibe73d102001-08-13 20:08:21 +00005263 infoPtr->treeWidth, infoPtr->clientWidth);
5264 }
5265 else {
5266 lppgc->iHeight = infoPtr->treeHeight;
Vincent Béron9a624912002-05-31 23:06:46 +00005267 TRACE("got PGN_CALCSIZE, returning vert size = %ld, client=%ld\n",
Guy L. Albertellibe73d102001-08-13 20:08:21 +00005268 infoPtr->treeHeight, infoPtr->clientHeight);
5269 }
5270 return 0;
5271 }
Dimitrie O. Pauna46b7fb2005-03-23 10:24:42 +00005272 return DefWindowProcW(infoPtr->hwnd, WM_NOTIFY, wParam, lParam);
Guy L. Albertellibe73d102001-08-13 20:08:21 +00005273}
5274
Dimitrie O. Paund2e881d2002-11-27 20:15:49 +00005275static INT TREEVIEW_NotifyFormat (TREEVIEW_INFO *infoPtr, HWND hwndFrom, UINT nCommand)
Guy L. Albertellib4994d12002-03-20 01:29:04 +00005276{
Dimitrie O. Paund2e881d2002-11-27 20:15:49 +00005277 INT format;
Jon Griffithsf479f102004-09-28 02:35:48 +00005278
Dimitrie O. Paund2e881d2002-11-27 20:15:49 +00005279 TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom, nCommand);
Guy L. Albertellib4994d12002-03-20 01:29:04 +00005280
Dimitrie O. Paund2e881d2002-11-27 20:15:49 +00005281 if (nCommand != NF_REQUERY) return 0;
Jon Griffithsf479f102004-09-28 02:35:48 +00005282
Dimitrie O. Paund2e881d2002-11-27 20:15:49 +00005283 format = SendMessageW(hwndFrom, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwnd, NF_QUERY);
5284 TRACE("format=%d\n", format);
5285
5286 if (format != NFR_ANSI && format != NFR_UNICODE) return 0;
Jon Griffithsf479f102004-09-28 02:35:48 +00005287
Dimitrie O. Paund2e881d2002-11-27 20:15:49 +00005288 infoPtr->bNtfUnicode = (format == NFR_UNICODE);
Jon Griffithsf479f102004-09-28 02:35:48 +00005289
Dimitrie O. Paund2e881d2002-11-27 20:15:49 +00005290 return format;
Guy L. Albertellib4994d12002-03-20 01:29:04 +00005291}
Jon Griffithsf479f102004-09-28 02:35:48 +00005292
Guy L. Albertellib4994d12002-03-20 01:29:04 +00005293static LRESULT
Chris Morgan5f9fd772000-09-13 20:27:30 +00005294TREEVIEW_Size(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
5295{
5296 if (wParam == SIZE_RESTORED)
5297 {
Alexandre Julliard9d615962003-09-17 04:28:28 +00005298 infoPtr->clientWidth = (short)LOWORD(lParam);
5299 infoPtr->clientHeight = (short)HIWORD(lParam);
Chris Morgan5f9fd772000-09-13 20:27:30 +00005300
5301 TREEVIEW_RecalculateVisibleOrder(infoPtr, NULL);
5302 TREEVIEW_SetFirstVisible(infoPtr, infoPtr->firstVisible, TRUE);
5303 TREEVIEW_UpdateScrollBars(infoPtr);
5304 }
5305 else
5306 {
5307 FIXME("WM_SIZE flag %x %lx not handled\n", wParam, lParam);
5308 }
5309
Susan Farley8d8c87b2000-10-25 20:30:58 +00005310 TREEVIEW_Invalidate(infoPtr, NULL);
Chris Morgan5f9fd772000-09-13 20:27:30 +00005311 return 0;
5312}
5313
5314static LRESULT
5315TREEVIEW_StyleChanged(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
5316{
5317 TRACE("(%x %lx)\n", wParam, lParam);
5318
5319 if (wParam == GWL_STYLE)
5320 {
5321 DWORD dwNewStyle = ((LPSTYLESTRUCT)lParam)->styleNew;
5322
5323 /* we have to take special care about tooltips */
5324 if ((infoPtr->dwStyle ^ dwNewStyle) & TVS_NOTOOLTIPS)
5325 {
5326 if (infoPtr->dwStyle & TVS_NOTOOLTIPS)
5327 {
5328 infoPtr->hwndToolTip = COMCTL32_CreateToolTip(infoPtr->hwnd);
5329 TRACE("\n");
5330 }
5331 else
5332 {
5333 DestroyWindow(infoPtr->hwndToolTip);
5334 infoPtr->hwndToolTip = 0;
5335 }
5336 }
5337
5338 infoPtr->dwStyle = dwNewStyle;
5339 }
5340
5341 TREEVIEW_UpdateSubTree(infoPtr, infoPtr->root);
5342 TREEVIEW_UpdateScrollBars(infoPtr);
Susan Farley8d8c87b2000-10-25 20:30:58 +00005343 TREEVIEW_Invalidate(infoPtr, NULL);
Chris Morgan5f9fd772000-09-13 20:27:30 +00005344
5345 return 0;
5346}
5347
5348static LRESULT
Robert Shearman1e92f4a2004-12-17 18:52:04 +00005349TREEVIEW_SetCursor(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
5350{
5351 POINT pt;
5352 TREEVIEW_ITEM * item;
5353
5354 GetCursorPos(&pt);
5355 ScreenToClient(infoPtr->hwnd, &pt);
5356
5357 item = TREEVIEW_HitTestPoint(infoPtr, pt);
5358
5359 /* FIXME: send NM_SETCURSOR */
5360
5361 if (item && (infoPtr->dwStyle & TVS_TRACKSELECT))
5362 {
5363 SetCursor(infoPtr->hcurHand);
5364 return 0;
5365 }
5366 else
5367 return DefWindowProcW(infoPtr->hwnd, WM_SETCURSOR, wParam, lParam);
5368}
5369
5370static LRESULT
Chris Morgan5f9fd772000-09-13 20:27:30 +00005371TREEVIEW_SetFocus(TREEVIEW_INFO *infoPtr)
5372{
5373 TRACE("\n");
5374
5375 if (!infoPtr->selectedItem)
5376 {
5377 TREEVIEW_DoSelectItem(infoPtr, TVGN_CARET, infoPtr->firstVisible,
5378 TVC_UNKNOWN);
5379 }
5380
Susan Farley8d8c87b2000-10-25 20:30:58 +00005381 TREEVIEW_Invalidate(infoPtr, infoPtr->selectedItem);
Vitaliy Margolen3b3574a2005-04-14 13:56:12 +00005382 TREEVIEW_SendSimpleNotify(infoPtr, NM_SETFOCUS);
Chris Morgan5f9fd772000-09-13 20:27:30 +00005383 return 0;
5384}
5385
5386static LRESULT
5387TREEVIEW_KillFocus(TREEVIEW_INFO *infoPtr)
5388{
5389 TRACE("\n");
5390
Susan Farley8d8c87b2000-10-25 20:30:58 +00005391 TREEVIEW_Invalidate(infoPtr, infoPtr->selectedItem);
Vitaliy Margolen3b3574a2005-04-14 13:56:12 +00005392 UpdateWindow(infoPtr->hwnd);
5393 TREEVIEW_SendSimpleNotify(infoPtr, NM_KILLFOCUS);
Chris Morgan5f9fd772000-09-13 20:27:30 +00005394 return 0;
5395}
5396
Frank Richter171fea02005-07-28 10:15:10 +00005397/* update theme after a WM_THEMECHANGED message */
5398static LRESULT theme_changed (TREEVIEW_INFO* infoPtr)
5399{
5400 HTHEME theme = GetWindowTheme (infoPtr->hwnd);
5401 CloseThemeData (theme);
5402 OpenThemeData (infoPtr->hwnd, themeClass);
5403 return 0;
5404}
5405
Chris Morgan5f9fd772000-09-13 20:27:30 +00005406
5407static LRESULT WINAPI
5408TREEVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
5409{
5410 TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
Dmitry Timoshkovd6872af2003-11-13 20:49:00 +00005411
5412 TRACE("hwnd %p msg %04x wp=%08x lp=%08lx\n", hwnd, uMsg, wParam, lParam);
5413
Chris Morgan5f9fd772000-09-13 20:27:30 +00005414 if (infoPtr) TREEVIEW_VerifyTree(infoPtr);
5415 else
5416 {
5417 if (uMsg == WM_CREATE)
Dimitrie O. Paunc5940432003-11-20 22:04:13 +00005418 TREEVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
Chris Morgan5f9fd772000-09-13 20:27:30 +00005419 else
5420 goto def;
5421 }
5422
5423 switch (uMsg)
5424 {
5425 case TVM_CREATEDRAGIMAGE:
5426 return TREEVIEW_CreateDragImage(infoPtr, wParam, lParam);
5427
5428 case TVM_DELETEITEM:
5429 return TREEVIEW_DeleteItem(infoPtr, (HTREEITEM)lParam);
5430
5431 case TVM_EDITLABELA:
Huw Davies5693ecd2003-11-26 03:37:30 +00005432 return (LRESULT)TREEVIEW_EditLabel(infoPtr, (HTREEITEM)lParam);
Chris Morgan5f9fd772000-09-13 20:27:30 +00005433
5434 case TVM_EDITLABELW:
Huw Davies5693ecd2003-11-26 03:37:30 +00005435 return (LRESULT)TREEVIEW_EditLabel(infoPtr, (HTREEITEM)lParam);
Chris Morgan5f9fd772000-09-13 20:27:30 +00005436
5437 case TVM_ENDEDITLABELNOW:
5438 return TREEVIEW_EndEditLabelNow(infoPtr, (BOOL)wParam);
5439
5440 case TVM_ENSUREVISIBLE:
5441 return TREEVIEW_EnsureVisible(infoPtr, (HTREEITEM)lParam, TRUE);
5442
5443 case TVM_EXPAND:
5444 return TREEVIEW_ExpandMsg(infoPtr, (UINT)wParam, (HTREEITEM)lParam);
5445
5446 case TVM_GETBKCOLOR:
5447 return TREEVIEW_GetBkColor(infoPtr);
5448
5449 case TVM_GETCOUNT:
5450 return TREEVIEW_GetCount(infoPtr);
5451
5452 case TVM_GETEDITCONTROL:
5453 return TREEVIEW_GetEditControl(infoPtr);
5454
5455 case TVM_GETIMAGELIST:
5456 return TREEVIEW_GetImageList(infoPtr, wParam);
5457
5458 case TVM_GETINDENT:
5459 return TREEVIEW_GetIndent(infoPtr);
5460
5461 case TVM_GETINSERTMARKCOLOR:
5462 return TREEVIEW_GetInsertMarkColor(infoPtr);
5463
5464 case TVM_GETISEARCHSTRINGA:
5465 FIXME("Unimplemented msg TVM_GETISEARCHSTRINGA\n");
5466 return 0;
5467
5468 case TVM_GETISEARCHSTRINGW:
5469 FIXME("Unimplemented msg TVM_GETISEARCHSTRINGW\n");
5470 return 0;
5471
5472 case TVM_GETITEMA:
Huw Davies96b90572003-11-26 22:26:19 +00005473 return TREEVIEW_GetItemT(infoPtr, (LPTVITEMEXW)lParam, FALSE);
Chris Morgan5f9fd772000-09-13 20:27:30 +00005474
5475 case TVM_GETITEMW:
Huw Davies96b90572003-11-26 22:26:19 +00005476 return TREEVIEW_GetItemT(infoPtr, (LPTVITEMEXW)lParam, TRUE);
Chris Morgan5f9fd772000-09-13 20:27:30 +00005477
5478 case TVM_GETITEMHEIGHT:
5479 return TREEVIEW_GetItemHeight(infoPtr);
5480
5481 case TVM_GETITEMRECT:
5482 return TREEVIEW_GetItemRect(infoPtr, (BOOL)wParam, (LPRECT)lParam);
5483
5484 case TVM_GETITEMSTATE:
5485 return TREEVIEW_GetItemState(infoPtr, (HTREEITEM)wParam, (UINT)lParam);
5486
5487 case TVM_GETLINECOLOR:
5488 return TREEVIEW_GetLineColor(infoPtr);
5489
5490 case TVM_GETNEXTITEM:
5491 return TREEVIEW_GetNextItem(infoPtr, (UINT)wParam, (HTREEITEM)lParam);
5492
5493 case TVM_GETSCROLLTIME:
5494 return TREEVIEW_GetScrollTime(infoPtr);
5495
5496 case TVM_GETTEXTCOLOR:
5497 return TREEVIEW_GetTextColor(infoPtr);
5498
5499 case TVM_GETTOOLTIPS:
5500 return TREEVIEW_GetToolTips(infoPtr);
5501
5502 case TVM_GETUNICODEFORMAT:
Huw Davies5693ecd2003-11-26 03:37:30 +00005503 return TREEVIEW_GetUnicodeFormat(infoPtr);
Chris Morgan5f9fd772000-09-13 20:27:30 +00005504
5505 case TVM_GETVISIBLECOUNT:
5506 return TREEVIEW_GetVisibleCount(infoPtr);
5507
5508 case TVM_HITTEST:
5509 return TREEVIEW_HitTest(infoPtr, (LPTVHITTESTINFO)lParam);
5510
5511 case TVM_INSERTITEMA:
Huw Davies6cfbf892003-11-27 01:11:01 +00005512 return TREEVIEW_InsertItemT(infoPtr, (LPTVINSERTSTRUCTW)lParam, FALSE);
Chris Morgan5f9fd772000-09-13 20:27:30 +00005513
5514 case TVM_INSERTITEMW:
Huw Davies6cfbf892003-11-27 01:11:01 +00005515 return TREEVIEW_InsertItemT(infoPtr, (LPTVINSERTSTRUCTW)lParam, TRUE);
Chris Morgan5f9fd772000-09-13 20:27:30 +00005516
5517 case TVM_SELECTITEM:
5518 return TREEVIEW_SelectItem(infoPtr, (INT)wParam, (HTREEITEM)lParam);
5519
5520 case TVM_SETBKCOLOR:
5521 return TREEVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
5522
5523 case TVM_SETIMAGELIST:
5524 return TREEVIEW_SetImageList(infoPtr, wParam, (HIMAGELIST)lParam);
5525
5526 case TVM_SETINDENT:
5527 return TREEVIEW_SetIndent(infoPtr, (UINT)wParam);
5528
5529 case TVM_SETINSERTMARK:
5530 return TREEVIEW_SetInsertMark(infoPtr, (BOOL)wParam, (HTREEITEM)lParam);
5531
5532 case TVM_SETINSERTMARKCOLOR:
5533 return TREEVIEW_SetInsertMarkColor(infoPtr, (COLORREF)lParam);
5534
5535 case TVM_SETITEMA:
Huw Davies6cfbf892003-11-27 01:11:01 +00005536 return TREEVIEW_SetItemT(infoPtr, (LPTVITEMEXW)lParam, FALSE);
Chris Morgan5f9fd772000-09-13 20:27:30 +00005537
5538 case TVM_SETITEMW:
Huw Davies6cfbf892003-11-27 01:11:01 +00005539 return TREEVIEW_SetItemT(infoPtr, (LPTVITEMEXW)lParam, TRUE);
Chris Morgan5f9fd772000-09-13 20:27:30 +00005540
5541 case TVM_SETLINECOLOR:
5542 return TREEVIEW_SetLineColor(infoPtr, (COLORREF)lParam);
5543
5544 case TVM_SETITEMHEIGHT:
5545 return TREEVIEW_SetItemHeight(infoPtr, (INT)(SHORT)wParam);
5546
5547 case TVM_SETSCROLLTIME:
5548 return TREEVIEW_SetScrollTime(infoPtr, (UINT)wParam);
5549
5550 case TVM_SETTEXTCOLOR:
5551 return TREEVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
5552
5553 case TVM_SETTOOLTIPS:
5554 return TREEVIEW_SetToolTips(infoPtr, (HWND)wParam);
5555
5556 case TVM_SETUNICODEFORMAT:
Huw Davies5693ecd2003-11-26 03:37:30 +00005557 return TREEVIEW_SetUnicodeFormat(infoPtr, (BOOL)wParam);
Chris Morgan5f9fd772000-09-13 20:27:30 +00005558
5559 case TVM_SORTCHILDREN:
5560 return TREEVIEW_SortChildren(infoPtr, wParam, lParam);
5561
5562 case TVM_SORTCHILDRENCB:
5563 return TREEVIEW_SortChildrenCB(infoPtr, wParam, (LPTVSORTCB)lParam);
5564
François Gouget2ad98552001-02-12 18:08:08 +00005565 case WM_CHAR:
5566 return TREEVIEW_ProcessLetterKeys( hwnd, wParam, lParam );
Chris Morgan5f9fd772000-09-13 20:27:30 +00005567
5568 case WM_COMMAND:
5569 return TREEVIEW_Command(infoPtr, wParam, lParam);
5570
5571 case WM_DESTROY:
5572 return TREEVIEW_Destroy(infoPtr);
5573
5574 /* WM_ENABLE */
5575
5576 case WM_ERASEBKGND:
5577 return TREEVIEW_EraseBackground(infoPtr, (HDC)wParam);
5578
5579 case WM_GETDLGCODE:
5580 return DLGC_WANTARROWS | DLGC_WANTCHARS;
5581
5582 case WM_GETFONT:
5583 return TREEVIEW_GetFont(infoPtr);
5584
5585 case WM_HSCROLL:
5586 return TREEVIEW_HScroll(infoPtr, wParam);
5587
5588 case WM_KEYDOWN:
5589 return TREEVIEW_KeyDown(infoPtr, wParam);
5590
5591 case WM_KILLFOCUS:
5592 return TREEVIEW_KillFocus(infoPtr);
5593
5594 case WM_LBUTTONDBLCLK:
5595 return TREEVIEW_LButtonDoubleClick(infoPtr, lParam);
5596
5597 case WM_LBUTTONDOWN:
5598 return TREEVIEW_LButtonDown(infoPtr, lParam);
5599
5600 /* WM_MBUTTONDOWN */
5601
Robert Shearman1e92f4a2004-12-17 18:52:04 +00005602 case WM_MOUSELEAVE:
5603 return TREEVIEW_MouseLeave(infoPtr);
5604
5605 case WM_MOUSEMOVE:
Martijn Vernooij2c2db4b2005-02-22 19:31:01 +00005606 if (infoPtr->dwStyle & TVS_TRACKSELECT)
5607 return TREEVIEW_MouseMove(infoPtr, wParam, lParam);
5608 else
5609 return 0;
Chris Morgan5f9fd772000-09-13 20:27:30 +00005610
Frank Richter171fea02005-07-28 10:15:10 +00005611 case WM_NCPAINT:
5612 if (nc_paint (infoPtr, (HRGN)wParam))
5613 return 0;
5614 goto def;
5615
Guy L. Albertellibe73d102001-08-13 20:08:21 +00005616 case WM_NOTIFY:
5617 return TREEVIEW_Notify(infoPtr, wParam, lParam);
Chris Morgan5f9fd772000-09-13 20:27:30 +00005618
Guy L. Albertellib4994d12002-03-20 01:29:04 +00005619 case WM_NOTIFYFORMAT:
Dimitrie O. Paund2e881d2002-11-27 20:15:49 +00005620 return TREEVIEW_NotifyFormat(infoPtr, (HWND)wParam, (UINT)lParam);
Chris Morgan5f9fd772000-09-13 20:27:30 +00005621
Michael Kaufmanne9310da2005-11-08 12:52:35 +00005622 case WM_PRINTCLIENT:
Chris Morgan5f9fd772000-09-13 20:27:30 +00005623 case WM_PAINT:
5624 return TREEVIEW_Paint(infoPtr, wParam);
5625
Chris Morgan5f9fd772000-09-13 20:27:30 +00005626 case WM_RBUTTONDOWN:
5627 return TREEVIEW_RButtonDown(infoPtr, lParam);
5628
Robert Shearman1e92f4a2004-12-17 18:52:04 +00005629 case WM_SETCURSOR:
5630 return TREEVIEW_SetCursor(infoPtr, wParam, lParam);
5631
Chris Morgan5f9fd772000-09-13 20:27:30 +00005632 case WM_SETFOCUS:
5633 return TREEVIEW_SetFocus(infoPtr);
5634
5635 case WM_SETFONT:
5636 return TREEVIEW_SetFont(infoPtr, (HFONT)wParam, (BOOL)lParam);
5637
5638 case WM_SETREDRAW:
5639 return TREEVIEW_SetRedraw(infoPtr, wParam, lParam);
5640
5641 case WM_SIZE:
5642 return TREEVIEW_Size(infoPtr, wParam, lParam);
5643
5644 case WM_STYLECHANGED:
5645 return TREEVIEW_StyleChanged(infoPtr, wParam, lParam);
5646
5647 /* WM_SYSCOLORCHANGE */
5648
5649 /* WM_SYSKEYDOWN */
5650
5651 case WM_TIMER:
5652 return TREEVIEW_HandleTimer(infoPtr, wParam);
5653
Frank Richter171fea02005-07-28 10:15:10 +00005654 case WM_THEMECHANGED:
5655 return theme_changed (infoPtr);
5656
Chris Morgan5f9fd772000-09-13 20:27:30 +00005657 case WM_VSCROLL:
5658 return TREEVIEW_VScroll(infoPtr, wParam);
5659
5660 /* WM_WININICHANGE */
5661
5662 case WM_MOUSEWHEEL:
5663 if (wParam & (MK_SHIFT | MK_CONTROL))
5664 goto def;
5665 return TREEVIEW_MouseWheel(infoPtr, wParam);
5666
5667 case WM_DRAWITEM:
5668 TRACE("drawItem\n");
5669 goto def;
5670
5671 default:
5672 /* This mostly catches MFC and Delphi messages. :( */
Guy L. Albertelli23739a32002-07-16 01:23:59 +00005673 if ((uMsg >= WM_USER) && (uMsg < WM_APP))
Chris Morgan5f9fd772000-09-13 20:27:30 +00005674 TRACE("Unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
5675def:
Dimitrie O. Pauna46b7fb2005-03-23 10:24:42 +00005676 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
Chris Morgan5f9fd772000-09-13 20:27:30 +00005677 }
Chris Morgan5f9fd772000-09-13 20:27:30 +00005678}
5679
5680
5681/* Class Registration ***************************************************/
5682
Eric Kohl9d8e8641998-10-24 10:49:27 +00005683VOID
Chris Morgan5f9fd772000-09-13 20:27:30 +00005684TREEVIEW_Register(void)
Alexandre Julliard642d3131998-07-12 19:29:36 +00005685{
Dimitrie O. Pauna46b7fb2005-03-23 10:24:42 +00005686 WNDCLASSW wndClass;
Alexandre Julliard642d3131998-07-12 19:29:36 +00005687
Alexandre Julliarda099a551999-06-12 15:45:58 +00005688 TRACE("\n");
Alexandre Julliard767e6f61998-08-09 12:47:43 +00005689
Dimitrie O. Pauna46b7fb2005-03-23 10:24:42 +00005690 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
Chris Morgan5f9fd772000-09-13 20:27:30 +00005691 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
Robert Shearman8d28e032004-11-06 03:49:03 +00005692 wndClass.lpfnWndProc = TREEVIEW_WindowProc;
Chris Morgan5f9fd772000-09-13 20:27:30 +00005693 wndClass.cbClsExtra = 0;
5694 wndClass.cbWndExtra = sizeof(TREEVIEW_INFO *);
5695
Dimitrie O. Pauna46b7fb2005-03-23 10:24:42 +00005696 wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
Alexandre Julliard642d3131998-07-12 19:29:36 +00005697 wndClass.hbrBackground = 0;
Dimitrie O. Pauna46b7fb2005-03-23 10:24:42 +00005698 wndClass.lpszClassName = WC_TREEVIEWW;
Chris Morgan5f9fd772000-09-13 20:27:30 +00005699
Dimitrie O. Pauna46b7fb2005-03-23 10:24:42 +00005700 RegisterClassW(&wndClass);
Alexandre Julliard642d3131998-07-12 19:29:36 +00005701}
5702
Eric Kohl9d8e8641998-10-24 10:49:27 +00005703
5704VOID
Chris Morgan5f9fd772000-09-13 20:27:30 +00005705TREEVIEW_Unregister(void)
Eric Kohl9d8e8641998-10-24 10:49:27 +00005706{
Dimitrie O. Pauna46b7fb2005-03-23 10:24:42 +00005707 UnregisterClassW(WC_TREEVIEWW, NULL);
Eric Kohl9d8e8641998-10-24 10:49:27 +00005708}
5709
Francois Boisvert56c36e31999-07-24 12:14:51 +00005710
Chris Morgan5f9fd772000-09-13 20:27:30 +00005711/* Tree Verification ****************************************************/
Francois Boisvert56c36e31999-07-24 12:14:51 +00005712
Susan Farley8d8c87b2000-10-25 20:30:58 +00005713#ifdef NDEBUG
Chris Morgan5f9fd772000-09-13 20:27:30 +00005714static inline void
5715TREEVIEW_VerifyChildren(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item);
5716
5717static inline void TREEVIEW_VerifyItemCommon(TREEVIEW_INFO *infoPtr,
5718 TREEVIEW_ITEM *item)
5719{
5720 assert(infoPtr != NULL);
5721 assert(item != NULL);
5722
5723 /* both NULL, or both non-null */
5724 assert((item->firstChild == NULL) == (item->lastChild == NULL));
5725
5726 assert(item->firstChild != item);
5727 assert(item->lastChild != item);
5728
5729 if (item->firstChild)
5730 {
5731 assert(item->firstChild->parent == item);
5732 assert(item->firstChild->prevSibling == NULL);
5733 }
5734
5735 if (item->lastChild)
5736 {
5737 assert(item->lastChild->parent == item);
5738 assert(item->lastChild->nextSibling == NULL);
5739 }
5740
5741 assert(item->nextSibling != item);
5742 if (item->nextSibling)
5743 {
5744 assert(item->nextSibling->parent == item->parent);
5745 assert(item->nextSibling->prevSibling == item);
5746 }
5747
5748 assert(item->prevSibling != item);
5749 if (item->prevSibling)
5750 {
5751 assert(item->prevSibling->parent == item->parent);
5752 assert(item->prevSibling->nextSibling == item);
5753 }
5754}
5755
5756static inline void
5757TREEVIEW_VerifyItem(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item)
5758{
5759 assert(item != NULL);
5760
5761 assert(item->parent != NULL);
5762 assert(item->parent != item);
5763 assert(item->iLevel == item->parent->iLevel + 1);
5764
5765 assert(DPA_GetPtrIndex(infoPtr->items, item) != -1);
5766
5767 TREEVIEW_VerifyItemCommon(infoPtr, item);
5768
5769 TREEVIEW_VerifyChildren(infoPtr, item);
5770}
5771
5772static inline void
5773TREEVIEW_VerifyChildren(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item)
5774{
5775 TREEVIEW_ITEM *child;
5776 assert(item != NULL);
5777
5778 for (child = item->firstChild; child != NULL; child = child->nextSibling)
5779 TREEVIEW_VerifyItem(infoPtr, child);
5780}
5781
5782static inline void
5783TREEVIEW_VerifyRoot(TREEVIEW_INFO *infoPtr)
5784{
5785 TREEVIEW_ITEM *root = infoPtr->root;
5786
5787 assert(root != NULL);
5788 assert(root->iLevel == -1);
5789 assert(root->parent == NULL);
5790 assert(root->prevSibling == NULL);
5791
5792 TREEVIEW_VerifyItemCommon(infoPtr, root);
5793
5794 TREEVIEW_VerifyChildren(infoPtr, root);
5795}
5796
5797static void
5798TREEVIEW_VerifyTree(TREEVIEW_INFO *infoPtr)
5799{
5800 assert(infoPtr != NULL);
5801
5802 TREEVIEW_VerifyRoot(infoPtr);
5803}
5804#endif