Merged main Wine changes into Corel's treeview control rewritten by
Serge Ivanov and Andrew Lewycky. Fixed item focus behavior to match
Windows. Fixed item selection when un/expanding items. Implemented
WM_SETREDRAW. Added Corel's COMCTL32_CreateToolTip() helper function
to commctrl.c.
diff --git a/dlls/comctl32/comctl32.h b/dlls/comctl32/comctl32.h
index 93bdb0b..9cb48e0 100644
--- a/dlls/comctl32/comctl32.h
+++ b/dlls/comctl32/comctl32.h
@@ -7,6 +7,9 @@
*
*/
+#ifndef __WINE_COMCTL32_H
+#define __WINE_COMCTL32_H
+
extern HMODULE COMCTL32_hModule;
/* Property sheet / Wizard */
@@ -62,3 +65,7 @@
#define IDT_CHECK 401
+/* Internal function */
+HWND COMCTL32_CreateToolTip (HWND);
+
+#endif /* __WINE_COMCTL32_H */
diff --git a/dlls/comctl32/commctrl.c b/dlls/comctl32/commctrl.c
index 52a183d..1c0fc2e 100644
--- a/dlls/comctl32/commctrl.c
+++ b/dlls/comctl32/commctrl.c
@@ -1150,3 +1150,45 @@
{
COMCTL32_uiLang = uiLang;
}
+
+
+/***********************************************************************
+ * COMCTL32_CreateToolTip [NOT AN API]
+ *
+ * Creates a tooltip for the control specified in hwnd and does all
+ * necessary setup and notifications.
+ *
+ * PARAMS
+ * hwndOwner [I] Handle to the window that will own the tool tip.
+ *
+ * RETURNS
+ * Success: Handle of tool tip window.
+ * Failure: NULL
+ */
+HWND
+COMCTL32_CreateToolTip(HWND hwndOwner)
+{
+ HWND hwndToolTip;
+
+ hwndToolTip = CreateWindowExA(0, TOOLTIPS_CLASSA, NULL, 0,
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ CW_USEDEFAULT, CW_USEDEFAULT, hwndOwner,
+ 0, 0, 0);
+
+ /* Send NM_TOOLTIPSCREATED notification */
+ if (hwndToolTip)
+ {
+ NMTOOLTIPSCREATED nmttc;
+
+ nmttc.hdr.hwndFrom = hwndOwner;
+ nmttc.hdr.idFrom = GetWindowLongA(hwndOwner, GWL_ID);
+ nmttc.hdr.code = NM_TOOLTIPSCREATED;
+ nmttc.hwndToolTips = hwndToolTip;
+
+ SendMessageA(GetParent(hwndOwner), WM_NOTIFY,
+ (WPARAM)GetWindowLongA(hwndOwner, GWL_ID),
+ (LPARAM)&nmttc);
+ }
+
+ return hwndToolTip;
+}
diff --git a/dlls/comctl32/treeview.c b/dlls/comctl32/treeview.c
index 17b29c5..ea451fd 100644
--- a/dlls/comctl32/treeview.c
+++ b/dlls/comctl32/treeview.c
@@ -4,70 +4,40 @@
* Copyright 1998,1999 Alex Priem <alexp@sci.kun.nl>
* Copyright 1999 Sylvain St-Germain
*
+ * Note that TREEVIEW_INFO * and HTREEITEM are the same thing.
+ *
+ * Note2: All items always! have valid (allocated) pszText field.
+ * If item's text == LPSTR_TEXTCALLBACKA we allocate buffer
+ * of size TEXT_CALLBACK_SIZE in DoSetItem.
+ * We use callbackMask to keep track of fields to be updated.
*
* TODO:
- * Using DPA to store the item ptr would be good.
- * Node label edition is implemented but something happened in wine in the
- * two last weeks of march 99 that broke it.
- * refreshtreeview:
- -small array containing info about positions.
- -better implementation of RefreshItem:
- 1) draw lines between parents
- 2) draw items
- 3) draw lines from parent<->items.
- -implement partial drawing?
- * -drag&drop: TVM_CREATEDRAGIMAGE should create drag bitmap.
- * -scrollbars: horizontal scrollbar doesn't work.
- * -Unicode messages
- * -check custom draw
- * -I_CHILDRENCALLBACK
- * FIXME: check fontsize. (uRealItemHeight)
- * test focusItem (redraw in different color)
- uHotItem
- Edit: needs timer
- better implementation.
- * WM_HSCROLL is broken.
- * use separate routine to get item text/image.
- *
- * Separate drawing/calculation.
+ * missing notifications: NM_SETCURSOR, TVN_GETINFOTIP, TVN_KEYDOWN,
+ * TVN_SETDISPINFO, TVN_SINGLEEXPAND
*
- * FIXMEs (for personal use)
- Expand: -ctlmacro expands twice ->toggle.
- -DblClick: ctlmacro.exe's NM_DBLCLK seems to go wrong (returns FALSE).
- -treehelper: stack corruption makes big window.
-
+ * missing styles: TVS_FULLROWSELECT, TVS_INFOTIP, TVS_NOSCROLL,
+ * TVS_RTLREADING, TVS_TRACKSELECT
+ *
+ * missing item styles: TVIS_CUT, TVIS_EXPANDPARTIAL
+ *
+ * Make the insertion mark look right.
+ * Scroll (instead of repaint) as much as possible.
*/
-
+#include <assert.h>
#include <string.h>
+#include <limits.h>
#include "winbase.h"
#include "wingdi.h"
#include "commctrl.h"
#include "comctl32.h"
#include "debugtools.h"
-DEFAULT_DEBUG_CHANNEL(treeview);
-
-/* ffs should be in <string.h>. */
-
-/* Defines, since they do not need to return previous state, and nr
- * has no side effects in this file.
- */
-#define tv_test_bit(nr,bf) (((LPBYTE)bf)[nr>>3]&(1<<(nr&7)))
-#define tv_set_bit(nr,bf) ((LPBYTE)bf)[nr>>3]|=(1<<(nr&7))
-#define tv_clear_bit(nr,bf) ((LPBYTE)bf)[nr>>3]&=~(1<<(nr&7))
-
-#define MINIMUM_INDENT 10
-#define TV_REFRESH_DELAY 100 /* 100 ms delay between two refreshes */
-#define TV_DEFAULTITEMHEIGHT 16
-#define TVITEM_ALLOC 16 /* default nr of items to allocate at first try */
-
-
/* internal structures */
-typedef struct {
- UINT mask;
- HTREEITEM hItem;
+typedef struct _TREEITEM /* HTREEITEM is a _TREEINFO *. */
+{
+ UINT callbackMask;
UINT state;
UINT stateMask;
LPSTR pszText;
@@ -76,69 +46,80 @@
int iSelectedImage;
int cChildren;
LPARAM lParam;
- int iIntegral;
+ int iIntegral; /* item height multiplier (1 is normal) */
int iLevel; /* indentation level:0=root level */
- COLORREF clrText;
HTREEITEM parent; /* handle to parent or 0 if at root*/
HTREEITEM firstChild; /* handle to first child or 0 if no child*/
- HTREEITEM sibling; /* handle to next item in list, 0 if last */
- HTREEITEM upsibling; /* handle to previous item in list, 0 if first */
- int visible;
+ HTREEITEM lastChild;
+ HTREEITEM prevSibling; /* handle to prev item in list, 0 if first */
+ HTREEITEM nextSibling; /* handle to next item in list, 0 if last */
RECT rect;
- RECT text;
- RECT expandBox; /* expand box (+/-) coordinate */
- RECT bitmap;
- RECT statebitmap;
+ LONG linesOffset;
+ LONG stateOffset;
+ LONG imageOffset;
+ LONG textOffset;
+ LONG textWidth; /* horizontal text extent for pszText */
+ LONG visibleOrder; /* visible ordering, 0 is first visible item */
} TREEVIEW_ITEM;
-typedef struct
+typedef struct tagTREEVIEW_INFO
{
- UINT uInternalStatus;
- UINT bAutoSize; /* merge with uInternalStatus */
+ HWND hwnd;
+ DWORD dwStyle;
+ HTREEITEM root;
+ UINT uInternalStatus;
INT Timer;
UINT uNumItems; /* number of valid TREEVIEW_ITEMs */
- UINT uNumPtrsAlloced;
- HTREEITEM uMaxHandle; /* needed for delete_item */
- HTREEITEM TopRootItem; /* handle to first item in treeview */
INT cdmode; /* last custom draw setting */
UINT uScrollTime; /* max. time for scrolling in milliseconds*/
- UINT uItemHeight; /* item height, -1 for default item height */
- UINT uRealItemHeight;/* current item height in pixels */
- UINT uVisibleHeight; /* visible height of treeview in pixels */
- UINT uTotalHeight; /* total height of treeview in pixels */
- UINT uVisibleWidth;
- UINT uTotalWidth;
+ BOOL bRedraw; /* if FALSE we validate but don't redraw in TREEVIEW_Paint() */
+
+ UINT uItemHeight; /* item height */
+ BOOL bHeightSet;
+
+ LONG clientWidth; /* width of control window */
+ LONG clientHeight; /* height of control window */
+
+ LONG treeWidth; /* width of visible tree items */
+ LONG treeHeight; /* height of visible tree items */
+
UINT uIndent; /* indentation in pixels */
HTREEITEM selectedItem; /* handle to selected item or 0 if none */
- HTREEITEM focusItem; /* handle to item that has focus, 0 if none */
HTREEITEM hotItem; /* handle currently under cursor, 0 if none */
- HTREEITEM editItem; /* handle to item currently editted, 0 if none */
+ HTREEITEM focusedItem; /* item that was under the cursor when WM_LBUTTONDOWN was received */
+
HTREEITEM firstVisible; /* handle to first visible item */
+ LONG maxVisibleOrder;
HTREEITEM dropItem; /* handle to item selected by drag cursor */
HTREEITEM insertMarkItem; /* item after which insertion mark is placed */
- BOOL insertBeforeorAfter; /* flag used by TVM_SETINSERTMARK */
+ BOOL insertBeforeorAfter; /* flag used by TVM_SETINSERTMARK */
HIMAGELIST dragList; /* Bitmap of dragged item */
- INT cx,cy; /* current x/y place in list */
- COLORREF clrBk;
+ LONG scrollX;
+ COLORREF clrBk;
COLORREF clrText;
COLORREF clrLine;
- COLORREF clrInsertMark;
+ COLORREF clrInsertMark;
HFONT hFont;
HFONT hBoldFont;
HWND hwndToolTip;
+
HWND hwndEdit;
- WNDPROC wpEditOrig; /* needed for subclassing edit control */
- HIMAGELIST himlNormal;
+ WNDPROC wpEditOrig; /* orig window proc for subclassing edit */
+ BOOL bIgnoreEditKillFocus;
+ BOOL bLabelChanged;
+
+ HIMAGELIST himlNormal;
+ int normalImageHeight;
+ int normalImageWidth;
HIMAGELIST himlState;
- LPTVSORTCB pCallBackSort; /* ptr to TVSORTCB struct for callback sorting */
- TREEVIEW_ITEM *items; /* itemlist */
- INT *freeList; /* bitmap indicating which elements are valid */
- /* 1=valid, 0=free; */
- /* size of list= uNumPtrsAlloced/32 */
+ int stateImageHeight;
+ int stateImageWidth;
+ HDPA items;
} TREEVIEW_INFO;
+
/* bitflags for infoPtr->uInternalStatus */
#define TV_HSCROLL 0x01 /* treeview too large to fit in window */
@@ -150,136 +131,178 @@
/* bitflags for infoPtr->timer */
-#define TV_REFRESH_TIMER 1
#define TV_EDIT_TIMER 2
-#define TV_REFRESH_TIMER_SET 1
-#define TV_EDIT_TIMER_SET 2
-
-#define TREEVIEW_GetInfoPtr(hwnd) \
- ((TREEVIEW_INFO *) GetWindowLongA( hwnd, 0))
-
-#define FOCUS_BORDER 3
-
-static BOOL
-TREEVIEW_SendSimpleNotify (HWND hwnd, UINT code);
-static BOOL
-TREEVIEW_SendTreeviewNotify (HWND hwnd, UINT code, UINT action,
- HTREEITEM oldItem, HTREEITEM newItem);
-static BOOL
-TREEVIEW_SendTreeviewDnDNotify (HWND hwnd, UINT code, HTREEITEM dragItem,
- POINT pt);
-static BOOL
-TREEVIEW_SendDispInfoNotify (HWND hwnd, TREEVIEW_ITEM *wineItem,
- UINT code, UINT what);
-static BOOL
-TREEVIEW_SendCustomDrawNotify (HWND hwnd, DWORD dwDrawStage, HDC hdc,
- RECT rc);
-static BOOL
-TREEVIEW_SendCustomDrawItemNotify (HWND hwnd, HDC hdc,
- TREEVIEW_ITEM *tvItem, UINT uItemDrawState);
-static LRESULT
-TREEVIEW_DoSelectItem (HWND hwnd, INT action, HTREEITEM newSelect, INT cause);
-static void
-TREEVIEW_Refresh (HWND hwnd, HDC hdc);
-
-static LRESULT CALLBACK
-TREEVIEW_Edit_SubclassProc (HWND hwnd, UINT uMsg, WPARAM wParam,
- LPARAM lParam);
-
-static LRESULT
-TREEVIEW_EditLabelA (HWND hwnd, WPARAM wParam, LPARAM lParam);
-
-static LRESULT
-TREEVIEW_EndEditLabelNow (HWND hwnd, WPARAM wParam, LPARAM lParam);
+#define TV_EDIT_TIMER_SET 2
+VOID TREEVIEW_Register (VOID);
+VOID TREEVIEW_Unregister (VOID);
-/* helper functions. Work with the assumption that validity of operands
- is checked beforehand, and that tree state is valid. */
+DEFAULT_DEBUG_CHANNEL(treeview);
-/* FIXME: MS documentation says `GetNextVisibleItem' returns NULL
- if not successfull. Probably only applies to dereferencing infoPtr
- (i.e. we are offered a valid treeview structure)
- and not whether there is a next `visible' child.
- FIXME: check other failures.
- */
-/***************************************************************************
- * This method returns the TREEVIEW_ITEM object given the handle
- */
-static TREEVIEW_ITEM* TREEVIEW_ValidItem(
- TREEVIEW_INFO *infoPtr,
- HTREEITEM handle)
+#define TEXT_CALLBACK_SIZE 260
+
+#define TREEVIEW_LEFT_MARGIN 8
+
+#define MINIMUM_INDENT 19
+
+#define CALLBACK_MASK_ALL (TVIF_TEXT|TVIF_CHILDREN|TVIF_IMAGE|TVIF_SELECTEDIMAGE)
+
+#define STATEIMAGEINDEX(x) (((x) >> 12) & 0x0f)
+#define OVERLAYIMAGEINDEX(x) (((x) >> 8) & 0x0f)
+#define ISVISIBLE(x) ((x)->visibleOrder >= 0)
+
+
+typedef VOID (*TREEVIEW_ItemEnumFunc)(TREEVIEW_INFO *, TREEVIEW_ITEM *,LPVOID);
+
+
+static VOID TREEVIEW_QueueRefresh(TREEVIEW_INFO *);
+static VOID TREEVIEW_QueueItemRefresh(TREEVIEW_INFO *, TREEVIEW_ITEM *);
+
+static LRESULT TREEVIEW_DoSelectItem(TREEVIEW_INFO *, INT, HTREEITEM, INT);
+static VOID TREEVIEW_SetFirstVisible(TREEVIEW_INFO *, TREEVIEW_ITEM *, BOOL);
+static LRESULT TREEVIEW_EnsureVisible(TREEVIEW_INFO *, HTREEITEM, BOOL);
+static LRESULT TREEVIEW_RButtonUp(TREEVIEW_INFO *, LPPOINT);
+static LRESULT TREEVIEW_EndEditLabelNow(TREEVIEW_INFO *infoPtr, BOOL bCancel);
+static VOID TREEVIEW_UpdateScrollBars(TREEVIEW_INFO *infoPtr);
+static LRESULT TREEVIEW_HScroll(TREEVIEW_INFO *, WPARAM);
+
+
+/* Random Utilities *****************************************************/
+
+#ifdef NDEBUG
+static inline void
+TREEVIEW_VerifyTree(TREEVIEW_INFO *infoPtr)
{
- if ((!handle) || (handle>infoPtr->uMaxHandle))
- return NULL;
+ (void)infoPtr;
+}
+#else
+/* The definition is at the end of the file. */
+static void TREEVIEW_VerifyTree(TREEVIEW_INFO *infoPtr);
+#endif
- if (tv_test_bit ((INT)handle, infoPtr->freeList))
- return NULL;
+/* Returns the treeview private data if hwnd is a treeview.
+ * Otherwise returns an undefined value. */
+static TREEVIEW_INFO *
+TREEVIEW_GetInfoPtr(HWND hwnd)
+{
+ return (TREEVIEW_INFO *)GetWindowLongA(hwnd, 0);
+}
- return &infoPtr->items[(INT)handle];
+/* Don't call this. Nothing wants an item index. */
+static inline int
+TREEVIEW_GetItemIndex(TREEVIEW_INFO *infoPtr, HTREEITEM handle)
+{
+ assert(infoPtr != NULL);
+
+ return DPA_GetPtrIndex(infoPtr->items, handle);
}
/***************************************************************************
- * This method returns the last expanded child item of a tree node
+ * This method checks that handle is an item for this tree.
*/
-static TREEVIEW_ITEM *TREEVIEW_GetLastListItem(
- TREEVIEW_INFO *infoPtr,
- TREEVIEW_ITEM *tvItem)
+static BOOL
+TREEVIEW_ValidItem(TREEVIEW_INFO *infoPtr, HTREEITEM handle)
{
- TREEVIEW_ITEM *wineItem = tvItem;
+ if (TREEVIEW_GetItemIndex(infoPtr, handle) == -1)
+ {
+ TRACE("invalid item %p\n", handle);
+ return FALSE;
+ }
+ else
+ return TRUE;
+}
- /*
- * Get this item last sibling
- */
- while (wineItem->sibling)
- wineItem=& infoPtr->items [(INT)wineItem->sibling];
+static HFONT
+TREEVIEW_CreateBoldFont(HFONT hOrigFont)
+{
+ LOGFONTA font;
- /*
- * If the last sibling has expanded children, restart.
- */
- if ( ( wineItem->cChildren > 0 ) && ( wineItem->state & TVIS_EXPANDED) )
- return TREEVIEW_GetLastListItem(
- infoPtr,
- &(infoPtr->items[(INT)wineItem->firstChild]));
+ GetObjectA(hOrigFont, sizeof(font), &font);
+ font.lfWeight = FW_BOLD;
+ return CreateFontIndirectA(&font);
+}
- return wineItem;
+static inline HFONT
+TREEVIEW_FontForItem(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item)
+{
+ return (item->state & TVIS_BOLD) ? infoPtr->hBoldFont : infoPtr->hFont;
+}
+
+/* for trace/debugging purposes only */
+static const char *
+TREEVIEW_ItemName(TREEVIEW_ITEM *item)
+{
+ if (item == NULL) return "<null item>";
+ if (item->pszText == LPSTR_TEXTCALLBACKA) return "<callback>";
+ if (item->pszText == NULL) return "<null>";
+ return item->pszText;
+}
+
+/* An item is not a child of itself. */
+static BOOL
+TREEVIEW_IsChildOf(TREEVIEW_ITEM *parent, TREEVIEW_ITEM *child)
+{
+ do
+ {
+ child = child->parent;
+ if (child == parent) return TRUE;
+ } while (child != NULL);
+
+ return FALSE;
+}
+
+
+/* Tree Traversal *******************************************************/
+
+/***************************************************************************
+ * This method returns the last expanded sibling or child child item
+ * of a tree node
+ */
+static TREEVIEW_ITEM *
+TREEVIEW_GetLastListItem(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *wineItem)
+{
+ if (!wineItem)
+ return NULL;
+
+ while (wineItem->lastChild)
+ {
+ if (wineItem->state & TVIS_EXPANDED)
+ wineItem = wineItem->lastChild;
+ else
+ break;
+ }
+
+ if (wineItem == infoPtr->root)
+ return NULL;
+
+ return wineItem;
}
/***************************************************************************
- * This method returns the previous physical item in the list not
+ * This method returns the previous non-hidden item in the list not
* considering the tree hierarchy.
*/
-static TREEVIEW_ITEM *TREEVIEW_GetPrevListItem(
- TREEVIEW_INFO *infoPtr,
- TREEVIEW_ITEM *tvItem)
+static TREEVIEW_ITEM *
+TREEVIEW_GetPrevListItem(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *tvItem)
{
- if (tvItem->upsibling)
- {
- /*
- * This item has a upsibling, get the last item. Since, GetLastListItem
- * first looks at siblings, we must feed it with the first child.
- */
- TREEVIEW_ITEM *upItem = &infoPtr->items[(INT)tvItem->upsibling];
-
- if ( ( upItem->cChildren > 0 ) && ( upItem->state & TVIS_EXPANDED) )
- return TREEVIEW_GetLastListItem(
- infoPtr,
- &infoPtr->items[(INT)upItem->firstChild]);
- else
- return upItem;
- }
- else
- {
- /*
- * this item does not have a upsibling, get the parent
- */
- if (tvItem->parent)
- return &infoPtr->items[(INT)tvItem->parent];
- }
+ if (tvItem->prevSibling)
+ {
+ /* This item has a prevSibling, get the last item in the sibling's tree. */
+ TREEVIEW_ITEM *upItem = tvItem->prevSibling;
- return NULL;
+ if ((upItem->state & TVIS_EXPANDED) && upItem->lastChild != NULL)
+ return TREEVIEW_GetLastListItem(infoPtr, upItem->lastChild);
+ else
+ return upItem;
+ }
+ else
+ {
+ /* this item does not have a prevSibling, get the parent */
+ return (tvItem->parent != infoPtr->root) ? tvItem->parent : NULL;
+ }
}
@@ -287,36 +310,38 @@
* This method returns the next physical item in the treeview not
* considering the tree hierarchy.
*/
-static TREEVIEW_ITEM *TREEVIEW_GetNextListItem(
- TREEVIEW_INFO *infoPtr,
- TREEVIEW_ITEM *tvItem)
+static TREEVIEW_ITEM *
+TREEVIEW_GetNextListItem(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *tvItem)
{
- TREEVIEW_ITEM *wineItem = NULL;
+ assert(tvItem != NULL);
- /*
- * If this item has children and is expanded, return the first child
- */
- if ((tvItem->firstChild) && (tvItem->state & TVIS_EXPANDED))
- return (& infoPtr->items[(INT)tvItem->firstChild]);
+ /*
+ * If this item has children and is expanded, return the first child
+ */
+ if ((tvItem->state & TVIS_EXPANDED) && tvItem->firstChild != NULL)
+ {
+ return tvItem->firstChild;
+ }
- /*
- * try to get the sibling
- */
- if (tvItem->sibling)
- return (& infoPtr->items[(INT)tvItem->sibling]);
+ /*
+ * try to get the sibling
+ */
+ if (tvItem->nextSibling)
+ return tvItem->nextSibling;
- /*
- * Otherwise, get the parent's sibling.
- */
- wineItem=tvItem;
- while (wineItem->parent) {
- wineItem=& infoPtr->items [(INT)wineItem->parent];
- if (wineItem->sibling)
- return (& infoPtr->items [(INT)wineItem->sibling]);
- }
+ /*
+ * Otherwise, get the parent's sibling.
+ */
+ while (tvItem->parent)
+ {
+ tvItem = tvItem->parent;
- return NULL;
+ if (tvItem->nextSibling)
+ return tvItem->nextSibling;
+ }
+
+ return NULL;
}
/***************************************************************************
@@ -326,1422 +351,2347 @@
* Will scroll backward if count is <0.
* forward if count is >0.
*/
-static TREEVIEW_ITEM *TREEVIEW_GetListItem(
- TREEVIEW_INFO *infoPtr,
- TREEVIEW_ITEM *tvItem,
- LONG count)
+static TREEVIEW_ITEM *
+TREEVIEW_GetListItem(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *wineItem,
+ LONG count)
{
- TREEVIEW_ITEM *previousItem = NULL;
- TREEVIEW_ITEM *wineItem = tvItem;
- LONG iter = 0;
+ TREEVIEW_ITEM *(*next_item)(TREEVIEW_INFO *, TREEVIEW_ITEM *);
+ TREEVIEW_ITEM *previousItem;
- if (count > 0)
- {
- /* Find count item downward */
- while ((iter++ < count) && (wineItem != NULL))
- {
- /* Keep a pointer to the previous in case we ask for more than we got */
- previousItem = wineItem;
- wineItem = TREEVIEW_GetNextListItem(infoPtr, wineItem);
- }
+ assert(wineItem != NULL);
- if (wineItem == NULL)
- wineItem = previousItem;
- }
- else if (count < 0)
- {
- /* Find count item upward */
- while ((iter-- > count) && (wineItem != NULL))
+ if (count > 0)
{
- /* Keep a pointer to the previous in case we ask for more than we got */
- previousItem = wineItem;
- wineItem = TREEVIEW_GetPrevListItem(infoPtr, wineItem);
+ next_item = TREEVIEW_GetNextListItem;
}
+ else if (count < 0)
+ {
+ count = -count;
+ next_item = TREEVIEW_GetPrevListItem;
+ }
+ else
+ return wineItem;
- if (wineItem == NULL)
- wineItem = previousItem;
- }
- else
- wineItem = NULL;
+ do
+ {
+ previousItem = wineItem;
+ wineItem = next_item(infoPtr, wineItem);
- return wineItem;
+ } while (--count && wineItem != NULL);
+
+
+ return wineItem ? wineItem : previousItem;
}
-
-/***************************************************************************
- * This method
- */
-static void TREEVIEW_RemoveAllChildren(
- HWND hwnd,
- TREEVIEW_ITEM *parentItem)
+/* Notifications ************************************************************/
+
+static BOOL
+TREEVIEW_SendSimpleNotify(TREEVIEW_INFO *infoPtr, UINT code)
{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
- TREEVIEW_ITEM *killItem;
- INT kill;
-
- kill=(INT)parentItem->firstChild;
- while (kill) {
- tv_set_bit ( kill, infoPtr->freeList);
- killItem=& infoPtr->items[kill];
- if (killItem->pszText!=LPSTR_TEXTCALLBACKA)
- COMCTL32_Free (killItem->pszText);
- TREEVIEW_SendTreeviewNotify (hwnd, TVN_DELETEITEMA, 0, (HTREEITEM)kill, 0);
- if (killItem->firstChild)
- TREEVIEW_RemoveAllChildren (hwnd, killItem);
- kill=(INT)killItem->sibling;
- }
+ NMHDR nmhdr;
+ HWND hwnd = infoPtr->hwnd;
- if (parentItem->cChildren>0) {
- infoPtr->uNumItems -= parentItem->cChildren;
- parentItem->firstChild = 0;
- parentItem->cChildren = 0;
- }
+ TRACE("%x\n", code);
+ nmhdr.hwndFrom = hwnd;
+ nmhdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
+ nmhdr.code = code;
+ return (BOOL)SendMessageA(GetParent(hwnd), WM_NOTIFY,
+ (WPARAM)nmhdr.idFrom, (LPARAM)&nmhdr);
}
+static VOID
+TREEVIEW_TVItemFromItem(UINT mask, TVITEMA *tvItem, TREEVIEW_ITEM *item)
+{
+ tvItem->mask = mask;
+ tvItem->hItem = item;
+ tvItem->state = item->state;
+ tvItem->stateMask = 0;
+ tvItem->iImage = item->iImage;
+ tvItem->pszText = item->pszText;
+ tvItem->cchTextMax = item->cchTextMax;
+ tvItem->iImage = item->iImage;
+ tvItem->iSelectedImage = item->iSelectedImage;
+ tvItem->cChildren = item->cChildren;
+ tvItem->lParam = item->lParam;
+}
+
+static BOOL
+TREEVIEW_SendTreeviewNotify(TREEVIEW_INFO *infoPtr, UINT code, UINT action,
+ UINT mask, HTREEITEM oldItem, HTREEITEM newItem)
+{
+ HWND hwnd = infoPtr->hwnd;
+ NMTREEVIEWA nmhdr;
+
+ TRACE("code:%x action:%x olditem:%p newitem:%p\n",
+ code, action, oldItem, newItem);
+
+ ZeroMemory(&nmhdr, sizeof(NMTREEVIEWA));
+
+ nmhdr.hdr.hwndFrom = hwnd;
+ nmhdr.hdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
+ nmhdr.hdr.code = code;
+ nmhdr.action = action;
+
+ if (oldItem)
+ TREEVIEW_TVItemFromItem(mask, &nmhdr.itemOld, oldItem);
+
+ if (newItem)
+ TREEVIEW_TVItemFromItem(mask, &nmhdr.itemNew, newItem);
+
+ nmhdr.ptDrag.x = 0;
+ nmhdr.ptDrag.y = 0;
+
+ return (BOOL)SendMessageA(GetParent(hwnd), WM_NOTIFY,
+ (WPARAM)GetWindowLongA(hwnd, GWL_ID),
+ (LPARAM)&nmhdr);
+}
+
+static BOOL
+TREEVIEW_SendTreeviewDnDNotify(TREEVIEW_INFO *infoPtr, UINT code,
+ HTREEITEM dragItem, POINT pt)
+{
+ HWND hwnd = infoPtr->hwnd;
+ NMTREEVIEWA nmhdr;
+
+ TRACE("code:%x dragitem:%p\n", code, dragItem);
+
+ nmhdr.hdr.hwndFrom = hwnd;
+ nmhdr.hdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
+ nmhdr.hdr.code = code;
+ nmhdr.action = 0;
+ nmhdr.itemNew.mask = TVIF_STATE | TVIF_PARAM | TVIF_HANDLE;
+ nmhdr.itemNew.hItem = dragItem;
+ nmhdr.itemNew.state = dragItem->state;
+ nmhdr.itemNew.lParam = dragItem->lParam;
+
+ nmhdr.ptDrag.x = pt.x;
+ nmhdr.ptDrag.y = pt.y;
+
+ return (BOOL)SendMessageA(GetParent(hwnd), WM_NOTIFY,
+ (WPARAM)GetWindowLongA(hwnd, GWL_ID),
+ (LPARAM)&nmhdr);
+}
+
+
+static BOOL
+TREEVIEW_SendCustomDrawNotify(TREEVIEW_INFO *infoPtr, DWORD dwDrawStage,
+ HDC hdc, RECT rc)
+{
+ HWND hwnd = infoPtr->hwnd;
+ NMTVCUSTOMDRAW nmcdhdr;
+ LPNMCUSTOMDRAW nmcd;
+
+ TRACE("drawstage:%lx hdc:%x\n", dwDrawStage, hdc);
+
+ nmcd = &nmcdhdr.nmcd;
+ nmcd->hdr.hwndFrom = hwnd;
+ nmcd->hdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
+ nmcd->hdr.code = NM_CUSTOMDRAW;
+ nmcd->dwDrawStage = dwDrawStage;
+ nmcd->hdc = hdc;
+ nmcd->rc = rc;
+ nmcd->dwItemSpec = 0;
+ nmcd->uItemState = 0;
+ nmcd->lItemlParam = 0;
+ nmcdhdr.clrText = infoPtr->clrText;
+ nmcdhdr.clrTextBk = infoPtr->clrBk;
+ nmcdhdr.iLevel = 0;
+
+ return (BOOL)SendMessageA(GetParent(hwnd), WM_NOTIFY,
+ (WPARAM)GetWindowLongA(hwnd, GWL_ID),
+ (LPARAM)&nmcdhdr);
+}
+
+
+
+/* FIXME: need to find out when the flags in uItemState need to be set */
+
+static BOOL
+TREEVIEW_SendCustomDrawItemNotify(TREEVIEW_INFO *infoPtr, HDC hdc,
+ TREEVIEW_ITEM *wineItem, UINT uItemDrawState)
+{
+ HWND hwnd = infoPtr->hwnd;
+ NMTVCUSTOMDRAW nmcdhdr;
+ LPNMCUSTOMDRAW nmcd;
+ DWORD dwDrawStage, dwItemSpec;
+ UINT uItemState;
+ INT retval;
+
+ dwDrawStage = CDDS_ITEM | uItemDrawState;
+ dwItemSpec = (DWORD)wineItem;
+ uItemState = 0;
+ if (wineItem->state & TVIS_SELECTED)
+ uItemState |= CDIS_SELECTED;
+ if (wineItem == infoPtr->selectedItem)
+ uItemState |= CDIS_FOCUS;
+ if (wineItem == infoPtr->hotItem)
+ uItemState |= CDIS_HOT;
+
+ nmcd = &nmcdhdr.nmcd;
+ nmcd->hdr.hwndFrom = hwnd;
+ nmcd->hdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
+ nmcd->hdr.code = NM_CUSTOMDRAW;
+ nmcd->dwDrawStage = dwDrawStage;
+ nmcd->hdc = hdc;
+ nmcd->rc = wineItem->rect;
+ nmcd->dwItemSpec = dwItemSpec;
+ nmcd->uItemState = uItemState;
+ nmcd->lItemlParam = wineItem->lParam;
+ nmcdhdr.clrText = infoPtr->clrText;
+ nmcdhdr.clrTextBk = infoPtr->clrBk;
+ nmcdhdr.iLevel = wineItem->iLevel;
+
+ TRACE("drawstage:%lx hdc:%x item:%lx, itemstate:%x, lItemlParam:%lx\n",
+ nmcd->dwDrawStage, nmcd->hdc, nmcd->dwItemSpec,
+ nmcd->uItemState, nmcd->lItemlParam);
+
+ retval = SendMessageA(GetParent(hwnd), WM_NOTIFY,
+ (WPARAM)GetWindowLongA(hwnd, GWL_ID),
+ (LPARAM)&nmcdhdr);
+
+ infoPtr->clrText = nmcdhdr.clrText;
+ infoPtr->clrBk = nmcdhdr.clrTextBk;
+ return (BOOL)retval;
+}
+
+static BOOL
+TREEVIEW_BeginLabelEditNotify(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *editItem)
+{
+ HWND hwnd = infoPtr->hwnd;
+ NMTVDISPINFOA tvdi;
+
+ tvdi.hdr.hwndFrom = hwnd;
+ tvdi.hdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
+ tvdi.hdr.code = TVN_BEGINLABELEDITA;
+
+ tvdi.item.mask = TVIF_HANDLE | TVIF_STATE | TVIF_PARAM | TVIF_TEXT;
+ tvdi.item.hItem = editItem;
+ tvdi.item.state = editItem->state;
+ tvdi.item.lParam = editItem->lParam;
+ tvdi.item.pszText = editItem->pszText;
+ tvdi.item.cchTextMax = editItem->cchTextMax;
+
+ return SendMessageA(GetParent(hwnd), WM_NOTIFY, tvdi.hdr.idFrom,
+ (LPARAM)&tvdi);
+}
static void
-TREEVIEW_RemoveItem (HWND hwnd, TREEVIEW_ITEM *wineItem)
-
+TREEVIEW_UpdateDispInfo(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *wineItem,
+ UINT mask)
{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
- TREEVIEW_ITEM *parentItem, *upsiblingItem, *siblingItem;
- INT iItem;
+ NMTVDISPINFOA callback;
+ HWND hwnd = infoPtr->hwnd;
- iItem=(INT)wineItem->hItem;
- tv_set_bit(iItem,infoPtr->freeList);
- infoPtr->uNumItems--;
- parentItem=NULL;
- if (wineItem->pszText!=LPSTR_TEXTCALLBACKA)
- COMCTL32_Free (wineItem->pszText);
+ mask &= wineItem->callbackMask;
- TREEVIEW_SendTreeviewNotify (hwnd, TVN_DELETEITEMA, 0, (HTREEITEM)iItem, 0);
+ if (mask == 0) return;
- if (wineItem->firstChild)
- TREEVIEW_RemoveAllChildren (hwnd,wineItem);
+ callback.hdr.hwndFrom = hwnd;
+ callback.hdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
+ callback.hdr.code = TVN_GETDISPINFOA;
- if (wineItem->parent) {
- parentItem=& infoPtr->items [(INT)wineItem->parent];
- switch (parentItem->cChildren) {
- case I_CHILDRENCALLBACK:
- FIXME("we don't handle I_CHILDRENCALLBACK yet\n");
- break;
- case 1:
- parentItem->cChildren=0;
- parentItem->firstChild=0;
- return;
- default:
- parentItem->cChildren--;
- if ((INT)parentItem->firstChild==iItem)
- parentItem->firstChild=wineItem->sibling;
+ /* 'state' always contains valid value, as well as 'lParam'.
+ * All other parameters are uninitialized.
+ */
+ callback.item.pszText = wineItem->pszText;
+ callback.item.cchTextMax = wineItem->cchTextMax;
+ callback.item.mask = mask;
+ callback.item.hItem = wineItem;
+ callback.item.state = wineItem->state;
+ callback.item.lParam = wineItem->lParam;
+
+ /* If text is changed we need to recalculate textWidth */
+ if (mask & TVIF_TEXT)
+ wineItem->textWidth = 0;
+
+ SendMessageA(GetParent(hwnd), WM_NOTIFY, callback.hdr.idFrom, (LPARAM)&callback);
+
+ /* It may have changed due to a call to SetItem. */
+ mask &= wineItem->callbackMask;
+
+ if ((mask & TVIF_TEXT) && callback.item.pszText != wineItem->pszText)
+ {
+ /* Instead of copying text into our buffer user specified its own */
+ int len = max(lstrlenA(callback.item.pszText) + 1, TEXT_CALLBACK_SIZE);
+ LPSTR newText = COMCTL32_ReAlloc(wineItem->pszText, len);
+
+ if (newText)
+ {
+ wineItem->pszText = newText;
+ strcpy(wineItem->pszText, callback.item.pszText);
+ wineItem->cchTextMax = len;
+ }
+ /* If ReAlloc fails we have nothing to do, but keep original text */
+ }
+
+ if (mask & TVIF_IMAGE)
+ wineItem->iImage = callback.item.iImage;
+
+ if (mask & TVIF_SELECTEDIMAGE)
+ wineItem->iSelectedImage = callback.item.iSelectedImage;
+
+ if (mask & TVIF_CHILDREN)
+ wineItem->cChildren = callback.item.cChildren;
+
+ /* These members are now permanently set. */
+ if (callback.item.mask & TVIF_DI_SETITEM)
+ wineItem->callbackMask &= ~callback.item.mask;
+}
+
+/***************************************************************************
+ * This function uses cChildren field to decide whether the item has
+ * children or not.
+ * Note: if this returns TRUE, the child items may not actually exist,
+ * they could be virtual.
+ *
+ * Just use wineItem->firstChild to check for physical children.
+ */
+static BOOL
+TREEVIEW_HasChildren(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *wineItem)
+{
+ TREEVIEW_UpdateDispInfo(infoPtr, wineItem, TVIF_CHILDREN);
+
+ return wineItem->cChildren > 0;
+}
+
+
+/* Item Position ********************************************************/
+
+/* Compute linesOffset, stateOffset, imageOffset, textOffset of an item. */
+static VOID
+TREEVIEW_ComputeItemInternalMetrics(TREEVIEW_INFO *infoPtr,
+ TREEVIEW_ITEM *item)
+{
+ /* Same effect, different optimisation. */
+#if 0
+ BOOL lar = ((infoPtr->dwStyle & TVS_LINESATROOT)
+ && (infoPtr->dwStyle & (TVS_HASLINES|TVS_HASBUTTONS)));
+#else
+ BOOL lar = ((infoPtr->dwStyle
+ & (TVS_LINESATROOT|TVS_HASLINES|TVS_HASBUTTONS))
+ > TVS_LINESATROOT);
+#endif
+
+ item->linesOffset = infoPtr->uIndent * (item->iLevel + lar - 1)
+ - infoPtr->scrollX;
+ item->stateOffset = item->linesOffset + infoPtr->uIndent;
+ item->imageOffset = item->stateOffset
+ + (STATEIMAGEINDEX(item->state) ? infoPtr->stateImageWidth : 0);
+ item->textOffset = item->imageOffset + infoPtr->normalImageWidth;
+}
+
+static VOID
+TREEVIEW_ComputeTextWidth(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item, HDC hDC)
+{
+ HDC hdc;
+ HFONT hOldFont=0;
+ SIZE sz;
+
+ /* DRAW's OM docker creates items like this */
+ if (item->pszText == NULL)
+ {
+ item->textWidth = 0;
+ return;
+ }
+
+ if (item->textWidth != 0 && !(item->callbackMask & TVIF_TEXT))
+ return;
+
+ if (hDC != 0)
+ {
+ hdc = hDC;
+ }
+ else
+ {
+ hdc = GetDC(infoPtr->hwnd);
+ hOldFont = SelectObject(hdc, TREEVIEW_FontForItem(infoPtr, item));
+ }
+
+ GetTextExtentPoint32A(hdc, item->pszText, strlen(item->pszText), &sz);
+ item->textWidth = sz.cx;
+
+ if (hDC == 0)
+ {
+ SelectObject(hdc, hOldFont);
+ ReleaseDC(0, hdc);
+ }
+}
+
+static VOID
+TREEVIEW_ComputeItemRect(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item)
+{
+ item->rect.top = infoPtr->uItemHeight *
+ (item->visibleOrder - infoPtr->firstVisible->visibleOrder);
+
+ item->rect.bottom = item->rect.top
+ + infoPtr->uItemHeight * item->iIntegral;
+
+ item->rect.left = 0;
+ item->rect.right = infoPtr->clientWidth;
+}
+
+/* We know that only items after start need their order updated. */
+static void
+TREEVIEW_RecalculateVisibleOrder(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *start)
+{
+ TREEVIEW_ITEM *item;
+ int order;
+
+ if (!start)
+ {
+ start = infoPtr->root->firstChild;
+ order = 0;
+ }
+ else
+ order = start->visibleOrder;
+
+ for (item = start; item != NULL;
+ item = TREEVIEW_GetNextListItem(infoPtr, item))
+ {
+ item->visibleOrder = order;
+ order += item->iIntegral;
+ }
+
+ infoPtr->maxVisibleOrder = order;
+
+ for (item = start; item != NULL;
+ item = TREEVIEW_GetNextListItem(infoPtr, item))
+ {
+ TREEVIEW_ComputeItemRect(infoPtr, item);
+ }
+}
+
+
+/* Update metrics of all items in selected subtree.
+ * root must be expanded
+ */
+static VOID
+TREEVIEW_UpdateSubTree(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *root)
+{
+ TREEVIEW_ITEM *sibling;
+ HDC hdc;
+ HFONT hOldFont;
+
+ if (!root->firstChild || !(root->state & TVIS_EXPANDED))
+ return;
+
+ root->state &= ~TVIS_EXPANDED;
+ sibling = TREEVIEW_GetNextListItem(infoPtr, root);
+ root->state |= TVIS_EXPANDED;
+
+ hdc = GetDC(infoPtr->hwnd);
+ hOldFont = SelectObject(hdc, infoPtr->hFont);
+
+ for (; root != sibling;
+ root = TREEVIEW_GetNextListItem(infoPtr, root))
+ {
+ TREEVIEW_ComputeItemInternalMetrics(infoPtr, root);
+
+ if (root->callbackMask & TVIF_TEXT)
+ TREEVIEW_UpdateDispInfo(infoPtr, root, TVIF_TEXT);
+
+ if (root->textWidth == 0)
+ {
+ SelectObject(hdc, TREEVIEW_FontForItem(infoPtr, root));
+ TREEVIEW_ComputeTextWidth(infoPtr, root, hdc);
+ }
+ }
+
+ SelectObject(hdc, hOldFont);
+ ReleaseDC(infoPtr->hwnd, hdc);
+}
+
+/* Item Allocation **********************************************************/
+
+static TREEVIEW_ITEM *
+TREEVIEW_AllocateItem(TREEVIEW_INFO *infoPtr)
+{
+ TREEVIEW_ITEM *newItem = COMCTL32_Alloc(sizeof(TREEVIEW_ITEM));
+
+ if (!newItem)
+ return NULL;
+
+ if (DPA_InsertPtr(infoPtr->items, INT_MAX, newItem) == -1)
+ {
+ COMCTL32_Free(newItem);
+ return NULL;
+ }
+
+ return newItem;
+}
+
+/* Exact opposite of TREEVIEW_AllocateItem. In particular, it does not
+ * free item->pszText. */
+static void
+TREEVIEW_FreeItem(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item)
+{
+ DPA_DeletePtr(infoPtr->items, DPA_GetPtrIndex(infoPtr->items, item));
+ COMCTL32_Free(item);
+}
+
+
+/* Item Insertion *******************************************************/
+
+/***************************************************************************
+ * This method inserts newItem before sibling as a child of parent.
+ * sibling can be NULL, but only if parent has no children.
+ */
+static void
+TREEVIEW_InsertBefore(TREEVIEW_ITEM *newItem, TREEVIEW_ITEM *sibling,
+ TREEVIEW_ITEM *parent)
+{
+ assert(newItem != NULL);
+ assert(parent != NULL);
+
+ if (sibling != NULL)
+ {
+ assert(sibling->parent == parent);
+
+ if (sibling->prevSibling != NULL)
+ sibling->prevSibling->nextSibling = newItem;
+
+ newItem->prevSibling = sibling->prevSibling;
+ sibling->prevSibling = newItem;
+ }
+ else
+ newItem->prevSibling = NULL;
+
+ newItem->nextSibling = sibling;
+
+ if (parent->firstChild == sibling)
+ parent->firstChild = newItem;
+
+ if (parent->lastChild == NULL)
+ parent->lastChild = newItem;
+}
+
+/***************************************************************************
+ * This method inserts newItem after sibling as a child of parent.
+ * sibling can be NULL, but only if parent has no children.
+ */
+static void
+TREEVIEW_InsertAfter(TREEVIEW_ITEM *newItem, TREEVIEW_ITEM *sibling,
+ TREEVIEW_ITEM *parent)
+{
+ assert(newItem != NULL);
+ assert(parent != NULL);
+
+ if (sibling != NULL)
+ {
+ assert(sibling->parent == parent);
+
+ if (sibling->nextSibling != NULL)
+ sibling->nextSibling->prevSibling = newItem;
+
+ newItem->nextSibling = sibling->nextSibling;
+ sibling->nextSibling = newItem;
+ }
+ else
+ newItem->nextSibling = NULL;
+
+ newItem->prevSibling = sibling;
+
+ if (parent->lastChild == sibling)
+ parent->lastChild = newItem;
+
+ if (parent->firstChild == NULL)
+ parent->firstChild = newItem;
+}
+
+static BOOL
+TREEVIEW_DoSetItem(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *wineItem,
+ const TVITEMEXA *tvItem)
+{
+ UINT callbackClear = 0;
+ UINT callbackSet = 0;
+
+ /* Do this first in case it fails. */
+ if (tvItem->mask & TVIF_TEXT)
+ {
+ if (tvItem->pszText != LPSTR_TEXTCALLBACKA)
+ {
+ int len = lstrlenA(tvItem->pszText) + 1;
+ LPSTR newText = COMCTL32_ReAlloc(wineItem->pszText, len);
+
+ if (newText == NULL) return FALSE;
+
+ callbackClear |= TVIF_TEXT;
+
+ wineItem->pszText = newText;
+ wineItem->cchTextMax = len;
+ lstrcpynA(wineItem->pszText, tvItem->pszText, len);
+ }
+ else
+ {
+ callbackSet |= TVIF_TEXT;
+
+ wineItem->pszText = COMCTL32_ReAlloc(wineItem->pszText,
+ TEXT_CALLBACK_SIZE);
+ wineItem->cchTextMax = TEXT_CALLBACK_SIZE;
+ }
+ }
+
+ if (tvItem->mask & TVIF_CHILDREN)
+ {
+ wineItem->cChildren = tvItem->cChildren;
+
+ if (wineItem->cChildren == I_CHILDRENCALLBACK)
+ callbackSet |= TVIF_CHILDREN;
+ else
+ callbackClear |= TVIF_CHILDREN;
+ }
+
+ if (tvItem->mask & TVIF_IMAGE)
+ {
+ wineItem->iImage = tvItem->iImage;
+
+ if (wineItem->iImage == I_IMAGECALLBACK)
+ callbackSet |= TVIF_IMAGE;
+ else
+ callbackClear |= TVIF_IMAGE;
+ }
+
+ if (tvItem->mask & TVIF_SELECTEDIMAGE)
+ {
+ wineItem->iSelectedImage = tvItem->iSelectedImage;
+
+ if (wineItem->iSelectedImage == I_IMAGECALLBACK)
+ callbackSet |= TVIF_SELECTEDIMAGE;
+ else
+ callbackClear |= TVIF_SELECTEDIMAGE;
+ }
+
+ if (tvItem->mask & TVIF_PARAM)
+ wineItem->lParam = tvItem->lParam;
+
+ /* If the application sets TVIF_INTEGRAL without
+ * supplying a TVITEMEX structure, it's toast. */
+ if (tvItem->mask & TVIF_INTEGRAL)
+ wineItem->iIntegral = tvItem->iIntegral;
+
+ if (tvItem->mask & TVIF_STATE)
+ {
+ TRACE("prevstate,state,mask:%x,%x,%x\n", wineItem->state, tvItem->state,
+ tvItem->stateMask);
+ wineItem->state &= ~tvItem->stateMask;
+ wineItem->state |= (tvItem->state & tvItem->stateMask);
+ }
+
+ wineItem->callbackMask |= callbackSet;
+ wineItem->callbackMask &= ~callbackClear;
+
+ return TRUE;
+}
+
+/* Note that the new item is pre-zeroed. */
+static LRESULT
+TREEVIEW_InsertItemA(TREEVIEW_INFO *infoPtr, LPARAM lParam)
+{
+ const TVINSERTSTRUCTA *ptdi = (LPTVINSERTSTRUCTA) lParam;
+ const TVITEMEXA *tvItem = &ptdi->DUMMYUNIONNAME.itemex;
+ HTREEITEM insertAfter;
+ TREEVIEW_ITEM *newItem, *parentItem;
+ BOOL bTextUpdated = FALSE;
+
+ if (ptdi->hParent == TVI_ROOT || ptdi->hParent == 0)
+ {
+ parentItem = infoPtr->root;
+ }
+ else
+ {
+ parentItem = ptdi->hParent;
+
+ if (!TREEVIEW_ValidItem(infoPtr, parentItem))
+ {
+ WARN("invalid parent %p\n", parentItem);
+ return (LRESULT)(HTREEITEM)NULL;
+ }
+ }
+
+ insertAfter = ptdi->hInsertAfter;
+
+ /* Validate this now for convenience. */
+ switch ((DWORD)insertAfter)
+ {
+ case (DWORD)TVI_FIRST:
+ case (DWORD)TVI_LAST:
+ case (DWORD)TVI_SORT:
+ break;
+
+ default:
+ if (!TREEVIEW_ValidItem(infoPtr, insertAfter) ||
+ insertAfter->parent != parentItem)
+ {
+ WARN("invalid insert after %p\n", insertAfter);
+ insertAfter = TVI_LAST;
+ }
+ }
+
+ TRACE("parent %p position %p: %s\n", parentItem, insertAfter,
+ (tvItem->mask & TVIF_TEXT)
+ ? ((tvItem->pszText == LPSTR_TEXTCALLBACKA) ? "<callback>"
+ : tvItem->pszText)
+ : "<no label>");
+
+ newItem = TREEVIEW_AllocateItem(infoPtr);
+ if (newItem == NULL)
+ return (LRESULT)(HTREEITEM)NULL;
+
+ newItem->parent = parentItem;
+ newItem->iIntegral = 1;
+
+ if (!TREEVIEW_DoSetItem(infoPtr, newItem, tvItem))
+ return (LRESULT)(HTREEITEM)NULL;
+
+ /* After this point, nothing can fail. (Except for TVI_SORT.) */
+
+ infoPtr->uNumItems++;
+
+ switch ((DWORD)insertAfter)
+ {
+ case (DWORD)TVI_FIRST:
+ TREEVIEW_InsertBefore(newItem, parentItem->firstChild, parentItem);
+ if (infoPtr->firstVisible == parentItem->firstChild)
+ TREEVIEW_SetFirstVisible(infoPtr, newItem, TRUE);
+ break;
+
+ case (DWORD)TVI_LAST:
+ TREEVIEW_InsertAfter(newItem, parentItem->lastChild, parentItem);
+ break;
+
+ /* hInsertAfter names a specific item we want to insert after */
+ default:
+ TREEVIEW_InsertAfter(newItem, insertAfter, insertAfter->parent);
+ break;
+
+ case (DWORD)TVI_SORT:
+ {
+ TREEVIEW_ITEM *aChild;
+ TREEVIEW_ITEM *previousChild = NULL;
+ BOOL bItemInserted = FALSE;
+
+ aChild = parentItem->firstChild;
+
+ bTextUpdated = TRUE;
+ TREEVIEW_UpdateDispInfo(infoPtr, newItem, TVIF_TEXT);
+
+ /* Iterate the parent children to see where we fit in */
+ while (aChild != NULL)
+ {
+ INT comp;
+
+ TREEVIEW_UpdateDispInfo(infoPtr, aChild, TVIF_TEXT);
+ comp = lstrcmpA(newItem->pszText, aChild->pszText);
+
+ if (comp < 0) /* we are smaller than the current one */
+ {
+ TREEVIEW_InsertBefore(newItem, aChild, parentItem);
+ bItemInserted = TRUE;
+ break;
}
- }
+ else if (comp > 0) /* we are bigger than the current one */
+ {
+ previousChild = aChild;
- if (iItem==(INT)infoPtr->TopRootItem)
- infoPtr->TopRootItem=(HTREEITEM)wineItem->sibling;
- if (wineItem->upsibling) {
- upsiblingItem=& infoPtr->items [(INT)wineItem->upsibling];
- upsiblingItem->sibling=wineItem->sibling;
- }
- if (wineItem->sibling) {
- siblingItem=& infoPtr->items [(INT)wineItem->sibling];
- siblingItem->upsibling=wineItem->upsibling;
- }
+ /* This will help us to exit if there is no more sibling */
+ aChild = (aChild->nextSibling == 0)
+ ? NULL
+ : aChild->nextSibling;
+
+ /* Look at the next item */
+ continue;
+ }
+ else if (comp == 0)
+ {
+ /*
+ * An item with this name is already existing, therefore,
+ * we add after the one we found
+ */
+ TREEVIEW_InsertAfter(newItem, aChild, parentItem);
+ bItemInserted = TRUE;
+ break;
+ }
+ }
+
+ /*
+ * we reach the end of the child list and the item as not
+ * yet been inserted, therefore, insert it after the last child.
+ */
+ if ((!bItemInserted) && (aChild == NULL))
+ TREEVIEW_InsertAfter(newItem, previousChild, parentItem);
+
+ break;
+ }
+ }
+
+
+ TRACE("new item %p; parent %p, mask %x\n", newItem,
+ newItem->parent, tvItem->mask);
+
+ newItem->iLevel = newItem->parent->iLevel + 1;
+
+ if (newItem->parent->cChildren == 0)
+ newItem->parent->cChildren = 1;
+
+ if (infoPtr->dwStyle & TVS_CHECKBOXES)
+ {
+ if (STATEIMAGEINDEX(newItem->state) == 0)
+ newItem->state |= INDEXTOSTATEIMAGEMASK(1);
+ }
+
+ if (infoPtr->firstVisible == NULL)
+ infoPtr->firstVisible = newItem;
+
+ TREEVIEW_VerifyTree(infoPtr);
+
+ if (parentItem == infoPtr->root ||
+ (ISVISIBLE(parentItem) && parentItem->state & TVIS_EXPANDED))
+ {
+ TREEVIEW_ITEM *prev = TREEVIEW_GetPrevListItem(infoPtr, newItem);
+
+ TREEVIEW_RecalculateVisibleOrder(infoPtr, prev);
+ TREEVIEW_ComputeItemInternalMetrics(infoPtr, newItem);
+
+ if (!bTextUpdated)
+ TREEVIEW_UpdateDispInfo(infoPtr, newItem, TVIF_TEXT);
+
+ TREEVIEW_ComputeTextWidth(infoPtr, newItem, 0);
+ TREEVIEW_UpdateScrollBars(infoPtr);
+ TREEVIEW_QueueRefresh(infoPtr);
+ }
+ else
+ {
+ newItem->visibleOrder = -1;
+
+ /* refresh treeview if newItem is the first item inserted under parentItem */
+ if (ISVISIBLE(parentItem) && newItem->prevSibling == newItem->nextSibling)
+ {
+ /* parent got '+' - update it */
+ TREEVIEW_QueueItemRefresh(infoPtr, parentItem);
+ }
+ }
+
+ return (LRESULT)newItem;
}
-
-
-
-/* Note:TREEVIEW_RemoveTree doesn't remove infoPtr itself */
-
-static void TREEVIEW_RemoveTree (HWND hwnd)
-
-{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
- TREEVIEW_ITEM *killItem;
- int i;
-
- for (i = 1; i <= (INT)infoPtr->uMaxHandle; i++)
- if (!tv_test_bit (i, infoPtr->freeList)) {
- killItem = &infoPtr->items[i];
- if (killItem->pszText != LPSTR_TEXTCALLBACKA)
- COMCTL32_Free (killItem->pszText);
- TREEVIEW_SendTreeviewNotify(hwnd, TVN_DELETEITEMA, 0,
- killItem->hItem, 0);
- }
- if (infoPtr->uNumPtrsAlloced) {
- COMCTL32_Free (infoPtr->items);
- COMCTL32_Free (infoPtr->freeList);
- infoPtr->uNumItems = 0;
- infoPtr->uNumPtrsAlloced = 0;
- infoPtr->uMaxHandle = 0;
- infoPtr->TopRootItem = 0;
- }
-}
-
-
-
-
-
-
-
static LRESULT
-TREEVIEW_GetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
+TREEVIEW_InsertItemW(TREEVIEW_INFO *infoPtr, LPARAM lParam)
{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
+ TVINSERTSTRUCTW *tvisW;
+ TVINSERTSTRUCTA tvisA;
+ LRESULT lRes;
- TRACE("\n");
+ tvisW = (LPTVINSERTSTRUCTW) lParam;
- if ((INT)wParam == TVSIL_NORMAL)
- return (LRESULT) infoPtr->himlNormal;
- if ((INT)wParam == TVSIL_STATE)
- return (LRESULT) infoPtr->himlState;
+ tvisA.hParent = tvisW->hParent;
+ tvisA.hInsertAfter = tvisW->hInsertAfter;
+
+ tvisA.DUMMYUNIONNAME.item.mask = tvisW->DUMMYUNIONNAME.item.mask;
+ tvisA.DUMMYUNIONNAME.item.hItem = tvisW->DUMMYUNIONNAME.item.hItem;
+ tvisA.DUMMYUNIONNAME.item.state = tvisW->DUMMYUNIONNAME.item.state;
+ tvisA.DUMMYUNIONNAME.item.stateMask = tvisW->DUMMYUNIONNAME.item.stateMask;
+ tvisA.DUMMYUNIONNAME.item.cchTextMax =
+ tvisW->DUMMYUNIONNAME.item.cchTextMax;
+
+ if (tvisW->DUMMYUNIONNAME.item.pszText)
+ {
+ if (tvisW->DUMMYUNIONNAME.item.pszText != LPSTR_TEXTCALLBACKW)
+ {
+ int len = lstrlenW(tvisW->DUMMYUNIONNAME.item.pszText) + 1;
+
+ tvisA.DUMMYUNIONNAME.item.pszText = COMCTL32_Alloc(len);
+ lstrcpyWtoA(tvisA.DUMMYUNIONNAME.item.pszText,
+ tvisW->DUMMYUNIONNAME.item.pszText);
+ }
+ else
+ {
+ tvisA.DUMMYUNIONNAME.item.pszText = LPSTR_TEXTCALLBACKA;
+ tvisA.DUMMYUNIONNAME.item.cchTextMax = 0;
+ }
+ }
+
+ tvisA.DUMMYUNIONNAME.item.iImage = tvisW->DUMMYUNIONNAME.item.iImage;
+ tvisA.DUMMYUNIONNAME.item.iSelectedImage =
+ tvisW->DUMMYUNIONNAME.item.iSelectedImage;
+ tvisA.DUMMYUNIONNAME.item.cChildren = tvisW->DUMMYUNIONNAME.item.cChildren;
+ tvisA.DUMMYUNIONNAME.item.lParam = tvisW->DUMMYUNIONNAME.item.lParam;
+
+ lRes = TREEVIEW_InsertItemA(infoPtr, (LPARAM)&tvisA);
+
+ if (tvisA.DUMMYUNIONNAME.item.pszText != LPSTR_TEXTCALLBACKA)
+ {
+ COMCTL32_Free(tvisA.DUMMYUNIONNAME.item.pszText);
+ }
+
+ return lRes;
+
+}
+
+
+/* Item Deletion ************************************************************/
+static void
+TREEVIEW_RemoveItem(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *wineItem);
+
+static void
+TREEVIEW_RemoveAllChildren(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *parentItem)
+{
+ TREEVIEW_ITEM *kill = parentItem->firstChild;
+
+ while (kill != NULL)
+ {
+ TREEVIEW_ITEM *next = kill->nextSibling;
+
+ TREEVIEW_RemoveItem(infoPtr, kill);
+
+ kill = next;
+ }
+
+ assert(parentItem->cChildren <= 0); /* I_CHILDRENCALLBACK or 0 */
+ assert(parentItem->firstChild == NULL);
+ assert(parentItem->lastChild == NULL);
+}
+
+static void
+TREEVIEW_UnlinkItem(TREEVIEW_ITEM *item)
+{
+ TREEVIEW_ITEM *parentItem = item->parent;
+
+ assert(item != NULL);
+ assert(item->parent != NULL); /* i.e. it must not be the root */
+
+ if (parentItem->firstChild == item)
+ parentItem->firstChild = item->nextSibling;
+
+ if (parentItem->lastChild == item)
+ parentItem->lastChild = item->prevSibling;
+
+ if (parentItem->firstChild == NULL && parentItem->lastChild == NULL
+ && parentItem->cChildren > 0)
+ parentItem->cChildren = 0;
+
+ if (item->prevSibling)
+ item->prevSibling->nextSibling = item->nextSibling;
+
+ if (item->nextSibling)
+ item->nextSibling->prevSibling = item->prevSibling;
+}
+
+static void
+TREEVIEW_RemoveItem(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *wineItem)
+{
+ TRACE("%p, (%s)\n", wineItem, TREEVIEW_ItemName(wineItem));
+
+ TREEVIEW_SendTreeviewNotify(infoPtr, TVN_DELETEITEMA,
+ TVIF_HANDLE | TVIF_PARAM, 0, wineItem, 0);
+
+ if (wineItem->firstChild)
+ TREEVIEW_RemoveAllChildren(infoPtr, wineItem);
+
+ TREEVIEW_UnlinkItem(wineItem);
+
+ infoPtr->uNumItems--;
+
+ if (wineItem->pszText != LPSTR_TEXTCALLBACKA)
+ COMCTL32_Free(wineItem->pszText);
+
+ TREEVIEW_FreeItem(infoPtr, wineItem);
+}
+
+
+/* Empty out the tree. */
+static void
+TREEVIEW_RemoveTree(TREEVIEW_INFO *infoPtr)
+{
+ TREEVIEW_RemoveAllChildren(infoPtr, infoPtr->root);
+
+ assert(infoPtr->uNumItems == 0); /* root isn't counted in uNumItems */
+}
+
+static LRESULT
+TREEVIEW_DeleteItem(TREEVIEW_INFO *infoPtr, HTREEITEM wineItem)
+{
+ TREEVIEW_ITEM *oldSelection = infoPtr->selectedItem;
+ TREEVIEW_ITEM *newSelection = oldSelection;
+ TREEVIEW_ITEM *newFirstVisible = NULL;
+ TREEVIEW_ITEM *parent, *prev = NULL;
+ BOOL visible = FALSE;
+
+ if (wineItem == TVI_ROOT)
+ {
+ TRACE("TVI_ROOT\n");
+ parent = infoPtr->root;
+ newSelection = NULL;
+ visible = TRUE;
+ TREEVIEW_RemoveTree(infoPtr);
+ }
+ else
+ {
+ if (!TREEVIEW_ValidItem(infoPtr, wineItem))
+ return FALSE;
+
+ TRACE("%p (%s)\n", wineItem, TREEVIEW_ItemName(wineItem));
+ parent = wineItem->parent;
+
+ if (ISVISIBLE(wineItem))
+ {
+ prev = TREEVIEW_GetPrevListItem(infoPtr, wineItem);
+ visible = TRUE;
+ }
+
+ if (infoPtr->selectedItem != NULL
+ && (wineItem == infoPtr->selectedItem
+ || TREEVIEW_IsChildOf(wineItem, infoPtr->selectedItem)))
+ {
+ if (wineItem->nextSibling)
+ newSelection = wineItem->nextSibling;
+ else if (wineItem->parent != infoPtr->root)
+ newSelection = wineItem->parent;
+ }
+
+ if (infoPtr->firstVisible == wineItem)
+ {
+ if (wineItem->nextSibling)
+ newFirstVisible = wineItem->nextSibling;
+ else if (wineItem->prevSibling)
+ newFirstVisible = wineItem->prevSibling;
+ else if (wineItem->parent != infoPtr->root)
+ newFirstVisible = wineItem->parent;
+ }
+ else
+ newFirstVisible = infoPtr->firstVisible;
+
+ TREEVIEW_RemoveItem(infoPtr, wineItem);
+ }
+
+ /* Don't change if somebody else already has. */
+ if (oldSelection == infoPtr->selectedItem)
+ {
+ if (TREEVIEW_ValidItem(infoPtr, newSelection))
+ TREEVIEW_DoSelectItem(infoPtr, TVGN_CARET, newSelection, TVC_UNKNOWN);
+ else
+ infoPtr->selectedItem = 0;
+ }
+
+ /* Validate insertMark dropItem.
+ * hotItem ??? - used for comparision only.
+ */
+ if (!TREEVIEW_ValidItem(infoPtr, infoPtr->insertMarkItem))
+ infoPtr->insertMarkItem = 0;
+
+ if (!TREEVIEW_ValidItem(infoPtr, infoPtr->dropItem))
+ infoPtr->dropItem = 0;
+
+ if (!TREEVIEW_ValidItem(infoPtr, newFirstVisible))
+ newFirstVisible = infoPtr->root->firstChild;
+
+ TREEVIEW_VerifyTree(infoPtr);
+
+
+ if (visible)
+ {
+ TREEVIEW_RecalculateVisibleOrder(infoPtr, prev);
+ TREEVIEW_SetFirstVisible(infoPtr, newFirstVisible, TRUE);
+ TREEVIEW_UpdateScrollBars(infoPtr);
+ TREEVIEW_QueueRefresh(infoPtr);
+ }
+ else if (ISVISIBLE(parent) && !TREEVIEW_HasChildren(infoPtr, parent))
+ {
+ /* parent lost '+/-' - update it */
+ TREEVIEW_QueueItemRefresh(infoPtr, parent);
+ }
+
+ return TRUE;
+}
+
+
+/* Get/Set Messages *********************************************************/
+static LRESULT
+TREEVIEW_SetRedraw(TREEVIEW_INFO* infoPtr, WPARAM wParam, LPARAM lParam)
+{
+ if(wParam)
+ infoPtr->bRedraw = TRUE;
+ else
+ infoPtr->bRedraw = FALSE;
return 0;
}
static LRESULT
-TREEVIEW_SetImageList (HWND hwnd, WPARAM wParam, LPARAM lParam)
+TREEVIEW_GetIndent(TREEVIEW_INFO *infoPtr)
{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
- HIMAGELIST himlTemp;
+ TRACE("\n");
+ return infoPtr->uIndent;
+}
- TRACE("%x,%lx\n", wParam, lParam);
- switch ((INT)wParam) {
- case TVSIL_NORMAL:
- himlTemp = infoPtr->himlNormal;
- infoPtr->himlNormal = (HIMAGELIST)lParam;
- return (LRESULT)himlTemp;
+static LRESULT
+TREEVIEW_SetIndent(TREEVIEW_INFO *infoPtr, UINT newIndent)
+{
+ TRACE("\n");
- case TVSIL_STATE:
- himlTemp = infoPtr->himlState;
- infoPtr->himlState = (HIMAGELIST)lParam;
- return (LRESULT)himlTemp;
+ if (newIndent < MINIMUM_INDENT)
+ newIndent = MINIMUM_INDENT;
+
+ if (infoPtr->uIndent != newIndent)
+ {
+ infoPtr->uIndent = newIndent;
+ TREEVIEW_UpdateSubTree(infoPtr, infoPtr->root);
+ TREEVIEW_UpdateScrollBars(infoPtr);
+ TREEVIEW_QueueRefresh(infoPtr);
}
- return (LRESULT)NULL;
+ return 0;
}
-
static LRESULT
-TREEVIEW_SetItemHeight (HWND hwnd, WPARAM wParam)
+TREEVIEW_GetToolTips(TREEVIEW_INFO *infoPtr)
{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
- INT cx,cy,prevHeight=infoPtr->uItemHeight;
-
- TRACE("\n");
- if (wParam==-1) {
- infoPtr->uItemHeight=-1;
- return prevHeight;
- }
-
- ImageList_GetIconSize (infoPtr->himlNormal, &cx, &cy);
-
- if (wParam>cy) cy=wParam;
- infoPtr->uItemHeight=cy;
-
- if (!( GetWindowLongA( hwnd, GWL_STYLE) & TVS_NONEVENHEIGHT))
- infoPtr->uItemHeight = (INT) wParam & 0xfffffffe;
- return prevHeight;
+ TRACE("\n");
+ return infoPtr->hwndToolTip;
}
static LRESULT
-TREEVIEW_GetItemHeight (HWND hwnd)
+TREEVIEW_SetToolTips(TREEVIEW_INFO *infoPtr, HWND hwndTT)
{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
-
- TRACE("\n");
- return infoPtr->uItemHeight;
-}
-
-static LRESULT
-TREEVIEW_GetLineColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
-{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
+ HWND prevToolTip;
- TRACE("\n");
- return (LRESULT) infoPtr->clrLine;
+ TRACE("\n");
+ prevToolTip = infoPtr->hwndToolTip;
+ infoPtr->hwndToolTip = hwndTT;
+
+ return prevToolTip;
+}
+
+
+static LRESULT
+TREEVIEW_GetScrollTime(TREEVIEW_INFO *infoPtr)
+{
+ return infoPtr->uScrollTime;
}
static LRESULT
-TREEVIEW_SetLineColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
+TREEVIEW_SetScrollTime(TREEVIEW_INFO *infoPtr, UINT uScrollTime)
{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
- COLORREF prevColor=infoPtr->clrLine;
+ UINT uOldScrollTime = infoPtr->uScrollTime;
- TRACE("\n");
- infoPtr->clrLine=(COLORREF) lParam;
- return (LRESULT) prevColor;
+ infoPtr->uScrollTime = min(uScrollTime, 100);
+
+ return uOldScrollTime;
+}
+
+
+static LRESULT
+TREEVIEW_GetImageList(TREEVIEW_INFO *infoPtr, WPARAM wParam)
+{
+ TRACE("\n");
+
+ switch (wParam)
+ {
+ case (WPARAM)TVSIL_NORMAL:
+ return (LRESULT)infoPtr->himlNormal;
+
+ case (WPARAM)TVSIL_STATE:
+ return (LRESULT)infoPtr->himlState;
+
+ default:
+ return 0;
+ }
}
static LRESULT
-TREEVIEW_GetInsertMarkColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
+TREEVIEW_SetImageList(TREEVIEW_INFO *infoPtr, WPARAM wParam, HIMAGELIST himlNew)
{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
-
- TRACE("\n");
- return (LRESULT) infoPtr->clrInsertMark;
-}
-
-static LRESULT
-TREEVIEW_SetInsertMarkColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
-{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
- COLORREF prevColor=infoPtr->clrInsertMark;
-
- TRACE("%d %ld\n",wParam,lParam);
- infoPtr->clrInsertMark=(COLORREF) lParam;
- return (LRESULT) prevColor;
-}
-
-static LRESULT
-TREEVIEW_SetInsertMark (HWND hwnd, WPARAM wParam, LPARAM lParam)
-{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
-
- FIXME("%d %ld\n",wParam,lParam);
- if (!TREEVIEW_ValidItem (infoPtr, (HTREEITEM)lParam)) return 0;
- FIXME("%d %ld\n",wParam,lParam);
-
- infoPtr->insertBeforeorAfter=(BOOL) wParam;
- infoPtr->insertMarkItem=(HTREEITEM) lParam;
-
- InvalidateRect(hwnd, NULL, FALSE);
-
- return 1;
-}
-
-static LRESULT
-TREEVIEW_SetTextColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
-{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
- COLORREF prevColor=infoPtr->clrText;
-
- TRACE("\n");
- infoPtr->clrText=(COLORREF) lParam;
- return (LRESULT) prevColor;
-}
-
-static LRESULT
-TREEVIEW_GetBkColor (HWND hwnd)
-{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
-
- TRACE("\n");
- return (LRESULT) infoPtr->clrBk;
-}
-
-static LRESULT
-TREEVIEW_SetBkColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
-{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
- COLORREF prevColor=infoPtr->clrBk;
-
- TRACE("\n");
- infoPtr->clrBk=(COLORREF) lParam;
- return (LRESULT) prevColor;
-}
-
-static LRESULT
-TREEVIEW_GetTextColor (HWND hwnd)
-{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
-
- TRACE("\n");
- return (LRESULT) infoPtr->clrText;
-}
+ HIMAGELIST himlOld = 0;
+ int oldWidth = infoPtr->normalImageWidth;
+ int oldHeight = infoPtr->normalImageHeight;
-/* cdmode: custom draw mode as received from app. in first NMCUSTOMDRAW
- notification */
+ TRACE("%x,%p\n", wParam, himlNew);
-#define TREEVIEW_LEFT_MARGIN 8
+ switch (wParam)
+ {
+ case (WPARAM)TVSIL_NORMAL:
+ himlOld = infoPtr->himlNormal;
+ infoPtr->himlNormal = himlNew;
-
-static void
-TREEVIEW_DrawItem (HWND hwnd, HDC hdc, TREEVIEW_ITEM *wineItem)
-{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
- DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
- INT center,xpos,cx,cy, cditem;
- HFONT hOldFont;
- UINT uTextJustify = DT_LEFT;
- RECT r;
-
-
- if (wineItem->state & TVIS_BOLD)
- hOldFont = SelectObject (hdc, infoPtr->hBoldFont);
- else
- hOldFont = SelectObject (hdc, infoPtr->hFont);
-
- cditem=0;
- TRACE ("cdmode:%x\n",infoPtr->cdmode);
- if (infoPtr->cdmode & CDRF_NOTIFYITEMDRAW) {
- cditem=TREEVIEW_SendCustomDrawItemNotify
- (hwnd, hdc, wineItem, CDDS_ITEMPREPAINT);
- TRACE("prepaint:cditem-app returns 0x%x\n",cditem);
-
- if (cditem & CDRF_SKIPDEFAULT)
- return;
+ if (himlNew != NULL)
+ ImageList_GetIconSize(himlNew, &infoPtr->normalImageWidth,
+ &infoPtr->normalImageHeight);
+ else
+ {
+ infoPtr->normalImageWidth = 0;
+ infoPtr->normalImageHeight = 0;
}
- /*
- * Set drawing starting points
- */
- r = wineItem->rect; /* this item rectangle */
- center = (r.top+r.bottom)/2; /* this item vertical center */
- xpos = r.left + TREEVIEW_LEFT_MARGIN;/* horizontal starting point */
+ break;
- /*
- * Display the tree hierarchy
- */
- if ( dwStyle & TVS_HASLINES)
- {
- /*
- * Write links to parent node
- * we draw the L starting from the child to the parent
- *
- * points[0] is attached to the current item
- * points[1] is the L corner
- * points[2] is attached to the parent or the up sibling
- */
- if ( dwStyle & TVS_LINESATROOT)
- {
- TREEVIEW_ITEM *upNode = NULL;
- BOOL hasParentOrSibling = TRUE;
- RECT upRect = {0,0,0,0};
- HPEN hOldPen, hNewPen;
- POINT points[3];
- /*
- * determine the target location of the line at root, either be linked
- * to the up sibling or to the parent node.
- */
- if (wineItem->upsibling)
- upNode = TREEVIEW_ValidItem (infoPtr, wineItem->upsibling);
- else if (wineItem->parent)
- upNode = TREEVIEW_ValidItem (infoPtr, wineItem->parent);
- else
- hasParentOrSibling = FALSE;
+ case (WPARAM)TVSIL_STATE:
+ himlOld = infoPtr->himlState;
+ infoPtr->himlState = himlNew;
- if (upNode)
- upRect = upNode->rect;
-
- if ( wineItem->iLevel == 0 )
- {
- points[2].x = points[1].x = upRect.left+8;
- points[0].x = points[2].x + 10;
- points[2].y = upRect.bottom-3;
- points[1].y = points[0].y = center;
- }
- else
- {
- points[2].x = points[1].x = 8 + (20*wineItem->iLevel);
- points[2].y = ( upNode->cChildren == 0) ?
- upRect.top : /* is linked to the "L" above */
- ( wineItem->upsibling != NULL) ?
- upRect.bottom-3: /* is linked to an icon */
- upRect.bottom+1; /* is linked to a +/- box */
- points[1].y = points[0].y = center;
- points[0].x = points[1].x + 10;
- }
-
- /*
- * Get a dotted pen
- */
- hNewPen = CreatePen(PS_DOT, 0, infoPtr->clrLine);
- hOldPen = SelectObject( hdc, hNewPen );
-
- if (hasParentOrSibling)
- Polyline (hdc,points,3);
- else
- Polyline (hdc,points,2);
-
- DeleteObject(hNewPen);
- SelectObject(hdc, hOldPen);
- }
- }
-
- /*
- * Display the (+/-) signs
- */
- if (wineItem->iLevel != 0)/* update position only for non root node */
- xpos+=(5*wineItem->iLevel);
-
- if (( dwStyle & TVS_HASBUTTONS) && ( dwStyle & TVS_HASLINES))
- {
- if ( (wineItem->cChildren) ||
- (wineItem->cChildren == I_CHILDRENCALLBACK))
- {
- /* Setup expand box coordinate to facilitate the LMBClick handling */
- wineItem->expandBox.left = xpos-4;
- wineItem->expandBox.top = center-4;
- wineItem->expandBox.right = xpos+5;
- wineItem->expandBox.bottom = center+5;
-
- Rectangle (
- hdc,
- wineItem->expandBox.left,
- wineItem->expandBox.top ,
- wineItem->expandBox.right,
- wineItem->expandBox.bottom);
-
- MoveToEx (hdc, xpos-2, center, NULL);
- LineTo (hdc, xpos+3, center);
-
- if (!(wineItem->state & TVIS_EXPANDED)) {
- MoveToEx (hdc, xpos, center-2, NULL);
- LineTo (hdc, xpos, center+3);
- }
- }
- }
-
- /*
- * Display the image associated with this item
- */
- xpos += 13; /* update position */
- if (wineItem->mask & (TVIF_IMAGE|TVIF_SELECTEDIMAGE)) {
- INT imageIndex;
- HIMAGELIST *himlp = NULL;
-
- /* State images are displayed to the left of the Normal image
- * image number is in state; zero should be `display no image'.
- * FIXME: that last sentence looks like it needs some checking.
- */
- if (infoPtr->himlState)
- himlp=&infoPtr->himlState;
- imageIndex=wineItem->state>>12;
- imageIndex++; /* yeah, right */
- TRACE ("imindex:%d\n",imageIndex);
- if ((himlp) && (imageIndex))
- {
- imageIndex--; /* see FIXME */
- ImageList_Draw ( *himlp, imageIndex, hdc, xpos-2, r.top+1, ILD_NORMAL);
- ImageList_GetIconSize (*himlp, &cx, &cy);
- wineItem->statebitmap.left=xpos-2;
- wineItem->statebitmap.right=xpos-2+cx;
- wineItem->statebitmap.top=r.top+1;
- wineItem->statebitmap.bottom=r.top+1+cy;
- xpos+=cx;
- }
-
- /* Now, draw the normal image; can be either selected or
- * non-selected image.
- */
-
- himlp=NULL;
- if (infoPtr->himlNormal)
- himlp=&infoPtr->himlNormal; /* get the image list */
-
- imageIndex = wineItem->iImage;
- if ( (wineItem->state & TVIS_SELECTED) &&
- (wineItem->iSelectedImage)) {
-
- /* The item is curently selected */
- if (wineItem->iSelectedImage == I_IMAGECALLBACK)
- TREEVIEW_SendDispInfoNotify
- (hwnd, wineItem, TVN_GETDISPINFOA, TVIF_SELECTEDIMAGE);
-
- imageIndex = wineItem->iSelectedImage;
- } else {
- /* The item is not selected */
- if (wineItem->iImage == I_IMAGECALLBACK)
- TREEVIEW_SendDispInfoNotify
- (hwnd, wineItem, TVN_GETDISPINFOA, TVIF_IMAGE);
-
- imageIndex = wineItem->iImage;
- }
-
- if (himlp)
- {
- int ovlIdx = 0;
-
- if(wineItem->stateMask & TVIS_OVERLAYMASK)
- ovlIdx = wineItem->state & TVIS_OVERLAYMASK;
-
- ImageList_Draw ( *himlp, imageIndex, hdc, xpos-2, r.top+1, ILD_NORMAL|ovlIdx);
- ImageList_GetIconSize (*himlp, &cx, &cy);
- wineItem->bitmap.left=xpos-2;
- wineItem->bitmap.right=xpos-2+cx;
- wineItem->bitmap.top=r.top+1;
- wineItem->bitmap.bottom=r.top+1+cy;
- xpos+=cx;
- }
- }
-
-
- /*
- * Display the text associated with this item
- */
- r.left=xpos;
- if ((wineItem->mask & TVIF_TEXT) && (wineItem->pszText))
- {
- COLORREF oldTextColor = 0;
- INT oldBkMode;
- HBRUSH hbrBk = 0;
- BOOL inFocus = GetFocus() == hwnd;
-
- TREEVIEW_ITEM tmpItem;
- char buf[128];
-
- if (wineItem->pszText == LPSTR_TEXTCALLBACKA)
- {
- tmpItem.hItem = wineItem->hItem;
- tmpItem.state = wineItem->state;
- tmpItem.lParam = wineItem->lParam;
- tmpItem.pszText = buf;
- tmpItem.cchTextMax = sizeof(buf);
-
- TREEVIEW_SendDispInfoNotify(hwnd, &tmpItem, TVN_GETDISPINFOA, TVIF_TEXT);
- }
-
- r.left += 3;
- r.right -= 3;
-
- wineItem->text.left = r.left;
- wineItem->text.right = r.right;
- wineItem->text.top = r.top;
- wineItem->text.bottom= r.bottom;
-
- oldBkMode = SetBkMode(hdc, TRANSPARENT);
-
- /* - If item is drop target or it is selected and window is in focus -
- * use blue background (COLOR_HIGHLIGHT).
- * - If item is selected, window is not in focus, but it has style
- * TVS_SHOWSELALWAYS - use grey background (COLOR_BTNFACE)
- * - Otherwise - don't fill background
- */
- if ((wineItem->state & TVIS_DROPHILITED) ||
- ((wineItem->state & TVIS_SELECTED) &&
- (inFocus || (GetWindowLongA( hwnd, GWL_STYLE) & TVS_SHOWSELALWAYS))))
- {
- if ((wineItem->state & TVIS_DROPHILITED) || inFocus)
- {
- hbrBk = CreateSolidBrush(GetSysColor( COLOR_HIGHLIGHT));
- oldTextColor = SetTextColor(hdc, GetSysColor( COLOR_HIGHLIGHTTEXT));
- }
- else
- {
- hbrBk = CreateSolidBrush(GetSysColor( COLOR_BTNFACE));
-
- if (infoPtr->clrText == -1)
- oldTextColor = SetTextColor(hdc, GetSysColor( COLOR_WINDOWTEXT));
- else
- oldTextColor = SetTextColor(hdc, infoPtr->clrText);
- }
- }
- else
- {
- if (infoPtr->clrText == -1)
- oldTextColor = SetTextColor(hdc, GetSysColor( COLOR_WINDOWTEXT));
- else
- oldTextColor = SetTextColor(hdc, infoPtr->clrText);
- }
-
- if (wineItem->pszText != LPSTR_TEXTCALLBACKA)
- tmpItem.pszText = wineItem->pszText;
-
- /* Obtain the text coordinate */
- DrawTextA (
- hdc,
- tmpItem.pszText,
- lstrlenA(tmpItem.pszText),
- &wineItem->text,
- uTextJustify | DT_VCENTER | DT_SINGLELINE | DT_CALCRECT | DT_NOPREFIX);
-
- /* We need to reset it to items height */
- wineItem->text.top = r.top;
- wineItem->text.bottom = r.bottom;
- wineItem->text.right += 4; /* This is extra for focus rectangle */
-
- if (hbrBk)
- {
- FillRect(hdc, &wineItem->text, hbrBk);
- DeleteObject(hbrBk);
- }
-
- wineItem->text.left += 2;
-
- /* Draw it */
- DrawTextA ( hdc,
- tmpItem.pszText,
- lstrlenA(tmpItem.pszText),
- &wineItem->text,
- uTextJustify | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX);
-
- wineItem->text.left -=2;
-
- /* Restore the hdc state */
- SetTextColor( hdc, oldTextColor);
-
- /* Draw the box arround the selected item */
- if (wineItem->state & TVIS_SELECTED && inFocus)
- {
- HPEN hNewPen = CreatePen(PS_DOT, 0, GetSysColor(COLOR_WINDOWTEXT) );
- HPEN hOldPen = SelectObject( hdc, hNewPen );
- INT rop = SetROP2(hdc, R2_XORPEN);
- POINT points[5];
-
- points[4].x = points[0].x = wineItem->text.left;
- points[4].y = points[0].y = wineItem->text.top;
- points[1].x = wineItem->text.right-1 ;
- points[1].y = wineItem->text.top;
- points[2].x = wineItem->text.right-1;
- points[2].y = wineItem->text.bottom-1;
- points[3].x = wineItem->text.left;
- points[3].y = wineItem->text.bottom-1;
-
- Polyline (hdc,points,5);
-
- SetROP2(hdc, rop);
- DeleteObject(hNewPen);
- SelectObject(hdc, hOldPen);
- }
-
- if (oldBkMode != TRANSPARENT)
- SetBkMode(hdc, oldBkMode);
- }
-
- /* Draw insertion mark if necessary */
-
- if (infoPtr->insertMarkItem)
- TRACE ("item:%d,mark:%d\n", (int)wineItem->hItem,
- (int) infoPtr->insertMarkItem);
- if (wineItem->hItem==infoPtr->insertMarkItem) {
- HPEN hNewPen, hOldPen;
- int offset;
-
- hNewPen = CreatePen(PS_SOLID, 2, infoPtr->clrInsertMark);
- hOldPen = SelectObject( hdc, hNewPen );
-
- if (infoPtr->insertBeforeorAfter)
- offset=wineItem->text.top+1;
- else
- offset=wineItem->text.bottom-1;
-
- MoveToEx (hdc, wineItem->text.left, offset-3, NULL);
- LineTo (hdc, wineItem->text.left, offset+3);
-
- MoveToEx (hdc, wineItem->text.left, offset, NULL);
- LineTo (hdc, r.right-2, offset);
-
- MoveToEx (hdc, r.right-2, offset+3, NULL);
- LineTo (hdc, r.right-2, offset-3);
-
- DeleteObject(hNewPen);
-
- SelectObject(hdc, hOldPen);
+ if (himlNew != NULL)
+ ImageList_GetIconSize(himlNew, &infoPtr->stateImageWidth,
+ &infoPtr->stateImageHeight);
+ else
+ {
+ infoPtr->stateImageWidth = 0;
+ infoPtr->stateImageHeight = 0;
}
- if (cditem & CDRF_NOTIFYPOSTPAINT) {
- cditem=TREEVIEW_SendCustomDrawItemNotify
- (hwnd, hdc, wineItem, CDDS_ITEMPOSTPAINT);
- TRACE("postpaint:cditem-app returns 0x%x\n",cditem);
- }
+ break;
+ }
- SelectObject (hdc, hOldFont);
-}
-
-static LRESULT
-TREEVIEW_GetItemRect (HWND hwnd, WPARAM wParam, LPARAM lParam)
-{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
- TREEVIEW_ITEM *wineItem;
- HTREEITEM *iItem;
- LPRECT lpRect = (LPRECT)lParam;
-
- TRACE("\n");
- /*
- * validate parameters
- */
- if (lpRect == NULL)
- return FALSE;
-
- if (infoPtr->Timer & TV_REFRESH_TIMER_SET) {
- InvalidateRect(hwnd, NULL, FALSE);
- }
-
-
- /*
- * retrieve the item ptr
- */
- iItem = (HTREEITEM *) lParam;
- wineItem = TREEVIEW_ValidItem (infoPtr, *iItem);
- if ((!wineItem) || (!wineItem->visible))
- return FALSE;
-
- /*
- * If wParam is TRUE return the text size otherwise return
- * the whole item size
- */
- if ((INT) wParam) {
- lpRect->left = wineItem->text.left;
- lpRect->right = wineItem->text.right;
- lpRect->bottom = wineItem->text.bottom;
- lpRect->top = wineItem->text.top;
- } else {
- lpRect->left = wineItem->rect.left;
- lpRect->right = wineItem->rect.right;
- lpRect->bottom = wineItem->rect.bottom;
- lpRect->top = wineItem->rect.top;
- }
-
- TRACE("[L:%d R:%d T:%d B:%d]\n",
- lpRect->left,lpRect->right,
- lpRect->top,lpRect->bottom);
-
- return TRUE;
-}
-
-static LRESULT
-TREEVIEW_GetVisibleCount (HWND hwnd, WPARAM wParam, LPARAM lParam)
-
-{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
-
- return (LRESULT) infoPtr->uVisibleHeight / infoPtr->uRealItemHeight;
-}
-
-
-
-static LRESULT
-TREEVIEW_SetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
-{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
- TREEVIEW_ITEM *wineItem;
- TVITEMEXA *tvItem;
- INT iItem,len;
-
- tvItem=(LPTVITEMEXA) lParam;
- iItem=(INT)tvItem->hItem;
- TRACE("item %d,mask %x\n",iItem,tvItem->mask);
-
- wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
- if (!wineItem) return FALSE;
-
- if (tvItem->mask & TVIF_CHILDREN) {
- wineItem->cChildren=tvItem->cChildren;
- }
-
- if (tvItem->mask & TVIF_IMAGE) {
- wineItem->iImage=tvItem->iImage;
- }
-
- if (tvItem->mask & TVIF_INTEGRAL) {
- wineItem->iIntegral=tvItem->iIntegral;
- }
-
- if (tvItem->mask & TVIF_PARAM) {
- wineItem->lParam=tvItem->lParam;
- }
-
- if (tvItem->mask & TVIF_SELECTEDIMAGE) {
- wineItem->iSelectedImage=tvItem->iSelectedImage;
- }
-
- if (tvItem->mask & TVIF_STATE) {
- TRACE ("prevstate,state,mask:%x,%x,%x\n",wineItem->state,tvItem->state,
-tvItem->stateMask);
- wineItem->state&= ~tvItem->stateMask;
- wineItem->state|= (tvItem->state & tvItem->stateMask);
- wineItem->stateMask|= tvItem->stateMask;
- }
-
- if (tvItem->mask & TVIF_TEXT)
- {
- if (tvItem->pszText!=LPSTR_TEXTCALLBACKA)
+ if (oldWidth != infoPtr->normalImageWidth ||
+ oldHeight != infoPtr->normalImageHeight)
{
- len=lstrlenA (tvItem->pszText) + 1;
- if (len>wineItem->cchTextMax)
- {
- wineItem->pszText= COMCTL32_ReAlloc (wineItem->pszText, len);
- wineItem->cchTextMax = len;
- }
+ TREEVIEW_UpdateSubTree(infoPtr, infoPtr->root);
+ TREEVIEW_UpdateScrollBars(infoPtr);
+ }
- lstrcpynA (wineItem->pszText, tvItem->pszText,len);
+ TREEVIEW_QueueRefresh(infoPtr);
+
+ return (LRESULT)himlOld;
+}
+
+/* Compute the natural height (based on the font size) for items. */
+static UINT
+TREEVIEW_NaturalHeight(TREEVIEW_INFO *infoPtr)
+{
+ TEXTMETRICA tm;
+ HDC hdc = GetDC(0);
+ HFONT hOldFont = SelectObject(hdc, infoPtr->hFont);
+
+ GetTextMetricsA(hdc, &tm);
+
+ SelectObject(hdc, hOldFont);
+ ReleaseDC(0, hdc);
+
+ /* The 16 is a hack because our fonts are tiny. */
+ return max(16, tm.tmHeight + tm.tmExternalLeading);
+}
+
+static LRESULT
+TREEVIEW_SetItemHeight(TREEVIEW_INFO *infoPtr, INT newHeight)
+{
+ INT prevHeight = infoPtr->uItemHeight;
+
+ TRACE("%d \n", newHeight);
+ if (newHeight == -1)
+ {
+ infoPtr->uItemHeight = TREEVIEW_NaturalHeight(infoPtr);
+ infoPtr->bHeightSet = FALSE;
}
else
{
- if (wineItem->cchTextMax)
- {
- COMCTL32_Free (wineItem->pszText);
- wineItem->cchTextMax=0;
- }
- wineItem->pszText=LPSTR_TEXTCALLBACKA;
+ infoPtr->uItemHeight = newHeight;
+ infoPtr->bHeightSet = TRUE;
}
- }
- wineItem->mask |= tvItem->mask;
+ /* Round down, unless we support odd ("non even") heights. */
+ if (!(infoPtr->dwStyle) & TVS_NONEVENHEIGHT)
+ infoPtr->uItemHeight &= ~1;
- return TRUE;
+ if (infoPtr->uItemHeight != prevHeight)
+ {
+ TREEVIEW_RecalculateVisibleOrder(infoPtr, NULL);
+ TREEVIEW_UpdateScrollBars(infoPtr);
+ TREEVIEW_QueueRefresh(infoPtr);
+ }
+
+ return prevHeight;
}
static LRESULT
-TREEVIEW_GetItemState (HWND hwnd, WPARAM wParam, LPARAM lParam)
-
+TREEVIEW_GetItemHeight(TREEVIEW_INFO *infoPtr)
{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
- TREEVIEW_ITEM *wineItem;
-
TRACE("\n");
- wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)wParam);
- if (!wineItem) return 0;
-
- return (wineItem->state & lParam);
+ return infoPtr->uItemHeight;
}
-
-
-static void
-TREEVIEW_Refresh (HWND hwnd, HDC hdc)
+static LRESULT
+TREEVIEW_GetFont(TREEVIEW_INFO *infoPtr)
{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
- TEXTMETRICA tm;
- HBRUSH hbrBk;
- HFONT hOldFont;
+ TRACE("%x\n", infoPtr->hFont);
+ return infoPtr->hFont;
+}
- RECT rect;
- INT iItem, indent, x, y, height, itemHeight;
- INT viewtop,viewbottom,viewleft,viewright;
- TREEVIEW_ITEM *wineItem, *prevItem;
+
+static INT CALLBACK
+TREEVIEW_ResetTextWidth(LPVOID pItem, DWORD unused)
+{
+ (void)unused;
+
+ ((TREEVIEW_ITEM *)pItem)->textWidth = 0;
+
+ return 1;
+}
+
+static LRESULT
+TREEVIEW_SetFont(TREEVIEW_INFO *infoPtr, HFONT hFont, BOOL bRedraw)
+{
+ UINT uHeight = infoPtr->uItemHeight;
+
+ TRACE("%x %i\n", hFont, bRedraw);
+
+ infoPtr->hFont = hFont ? hFont : GetStockObject(SYSTEM_FONT);
+
+ DeleteObject(infoPtr->hBoldFont);
+ infoPtr->hBoldFont = TREEVIEW_CreateBoldFont(infoPtr->hFont);
+
+ if (!infoPtr->bHeightSet)
+ infoPtr->uItemHeight = TREEVIEW_NaturalHeight(infoPtr);
+
+ if (uHeight != infoPtr->uItemHeight)
+ TREEVIEW_RecalculateVisibleOrder(infoPtr, NULL);
+
+ DPA_EnumCallback(infoPtr->items, TREEVIEW_ResetTextWidth, 0);
+
+ TREEVIEW_UpdateSubTree(infoPtr, infoPtr->root);
+ TREEVIEW_UpdateScrollBars(infoPtr);
+
+ if (bRedraw)
+ TREEVIEW_QueueRefresh(infoPtr);
+
+ return 0;
+}
+
+
+static LRESULT
+TREEVIEW_GetLineColor(TREEVIEW_INFO *infoPtr)
+{
+ TRACE("\n");
+ return (LRESULT)infoPtr->clrLine;
+}
+
+static LRESULT
+TREEVIEW_SetLineColor(TREEVIEW_INFO *infoPtr, COLORREF color)
+{
+ COLORREF prevColor = infoPtr->clrLine;
TRACE("\n");
-
-
- if (infoPtr->Timer & TV_REFRESH_TIMER_SET) {
- KillTimer (hwnd, TV_REFRESH_TIMER);
- infoPtr->Timer &= ~TV_REFRESH_TIMER_SET;
- }
-
-
- GetClientRect (hwnd, &rect);
- if ((rect.left >= rect.right) || (rect.top >= rect.bottom)) return;
-
- infoPtr->cdmode=TREEVIEW_SendCustomDrawNotify(hwnd,CDDS_PREPAINT,hdc,rect);
-
- if (infoPtr->cdmode==CDRF_SKIPDEFAULT) return;
-
- infoPtr->uVisibleHeight= rect.bottom-rect.top + 1;
- infoPtr->uVisibleWidth= rect.right-rect.left + 1;
-
- viewtop=infoPtr->cy;
- viewbottom=infoPtr->cy + rect.bottom-rect.top;
- viewleft=infoPtr->cx;
- viewright=infoPtr->cx + rect.right-rect.left;
-
- TRACE("[%d %d %d %d]\n",viewtop,viewbottom,viewleft,viewright);
-
- /* draw background */
-
- hbrBk = CreateSolidBrush (infoPtr->clrBk);
- FillRect(hdc, &rect, hbrBk);
- DeleteObject(hbrBk);
-
- ImageList_GetIconSize (infoPtr->himlNormal, &x, &itemHeight);
- if (infoPtr->uItemHeight>itemHeight)
- itemHeight=infoPtr->uItemHeight;
-
- // assume that bold and normal fonts have same height
- hOldFont = SelectObject (hdc, infoPtr->hBoldFont);
- GetTextMetricsA (hdc, &tm);
- if ((tm.tmHeight + tm.tmExternalLeading + FOCUS_BORDER) > itemHeight)
- itemHeight=tm.tmHeight + tm.tmExternalLeading + FOCUS_BORDER;
- SelectObject (hdc, hOldFont);
-
- infoPtr->uRealItemHeight=itemHeight;
-
- iItem=(INT)infoPtr->TopRootItem;
- infoPtr->firstVisible=0;
- wineItem=NULL;
- indent=0;
- x=y=0;
-
- while (iItem) {
- prevItem=wineItem;
- wineItem= & infoPtr->items[iItem];
- wineItem->iLevel=indent;
-
-/* FIXME: remove this in later stage */
-/*
- if (wineItem->pszText!=LPSTR_TEXTCALLBACK32A)
- TRACE (treeview, "%d %d [%d %d %d %d] (%s)\n",y,x,
- wineItem->rect.top, wineItem->rect.bottom,
- wineItem->rect.left, wineItem->rect.right,
- wineItem->pszText);
- else
- TRACE (treeview, "%d [%d %d %d %d] (CALLBACK)\n",
- wineItem->hItem,
- wineItem->rect.top, wineItem->rect.bottom,
- wineItem->rect.left, wineItem->rect.right);
-*/
-
- height=itemHeight * wineItem->iIntegral;
- if ((y >= viewtop) && (y <= viewbottom) &&
- (x >= viewleft ) && (x <= viewright)) {
- wineItem->visible = TRUE;
- wineItem->rect.top = y - infoPtr->cy + rect.top;
- wineItem->rect.bottom = wineItem->rect.top + height-1;
- wineItem->rect.left = x - infoPtr->cx + rect.left;
- wineItem->rect.right = rect.right;
- if (!infoPtr->firstVisible)
- infoPtr->firstVisible=wineItem->hItem;
- TREEVIEW_DrawItem (hwnd, hdc, wineItem);
- }
- else {
- wineItem->visible = FALSE;
- wineItem->rect.left = wineItem->rect.top = 0;
- wineItem->rect.right= wineItem->rect.bottom = 0;
- wineItem->text.left = wineItem->text.top = 0;
- wineItem->text.right= wineItem->text.bottom = 0;
- }
-
- /* look up next item */
-
- if ((wineItem->firstChild) && (wineItem->state & TVIS_EXPANDED)) {
- iItem=(INT)wineItem->firstChild;
- indent++;
- x+=infoPtr->uIndent;
- if (x>infoPtr->uTotalWidth)
- infoPtr->uTotalWidth=x;
- }
- else {
- iItem=(INT)wineItem->sibling;
- while ((!iItem) && (indent>0)) {
- indent--;
- x-=infoPtr->uIndent;
- wineItem=&infoPtr->items[(INT)wineItem->parent];
- iItem=(INT)wineItem->sibling;
- }
- }
- y +=height;
- } /* while */
-
-/* FIXME: infoPtr->uTotalWidth should also take item label into account */
-/* FIXME: or should query item sizes (ie check CDRF_NEWFONT) */
-
- infoPtr->uTotalHeight=y;
- if (y >= (viewbottom-viewtop)) {
- if (!(infoPtr->uInternalStatus & TV_VSCROLL))
- ShowScrollBar (hwnd, SB_VERT, TRUE);
- infoPtr->uInternalStatus |=TV_VSCROLL;
- SetScrollRange (hwnd, SB_VERT, 0,
- y - infoPtr->uVisibleHeight, FALSE);
- SetScrollPos (hwnd, SB_VERT, infoPtr->cy, TRUE);
- }
- else {
- if (infoPtr->uInternalStatus & TV_VSCROLL)
- ShowScrollBar (hwnd, SB_VERT, FALSE);
- infoPtr->uInternalStatus &= ~TV_VSCROLL;
- }
-
-
- if (infoPtr->cdmode & CDRF_NOTIFYPOSTPAINT)
- infoPtr->cdmode=TREEVIEW_SendCustomDrawNotify
- (hwnd, CDDS_POSTPAINT, hdc, rect);
-
- TRACE("done\n");
-}
-
-
-static LRESULT
-TREEVIEW_HandleTimer (HWND hwnd, WPARAM wParam, LPARAM lParam)
-{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
-
- TRACE(" %d\n",wParam);
-
- switch (wParam) {
- case TV_REFRESH_TIMER:
- KillTimer (hwnd, TV_REFRESH_TIMER);
- infoPtr->Timer &= ~TV_REFRESH_TIMER_SET;
- InvalidateRect(hwnd, NULL, FALSE);
- return 0;
- case TV_EDIT_TIMER:
- KillTimer (hwnd, TV_EDIT_TIMER);
- infoPtr->Timer &= ~TV_EDIT_TIMER_SET;
- return 0;
- default:
- ERR("got unknown timer\n");
- }
-
- return 1;
-}
-
-
-static void
-TREEVIEW_QueueRefresh (HWND hwnd)
-
-{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
-
- TRACE("\n");
- if (infoPtr->Timer & TV_REFRESH_TIMER_SET) {
- KillTimer (hwnd, TV_REFRESH_TIMER);
- }
-
- SetTimer (hwnd, TV_REFRESH_TIMER, TV_REFRESH_DELAY, 0);
- infoPtr->Timer|=TV_REFRESH_TIMER_SET;
-}
-
-
-
-static LRESULT
-TREEVIEW_GetItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
-{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
- LPTVITEMEXA tvItem;
- TREEVIEW_ITEM *wineItem;
- INT iItem;
-
- tvItem=(LPTVITEMEXA) lParam;
- iItem=(INT)tvItem->hItem;
-
- wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
- if (!wineItem) return FALSE;
-
- if (tvItem->mask & TVIF_CHILDREN) {
- if (TVIF_CHILDREN==I_CHILDRENCALLBACK)
- FIXME("I_CHILDRENCALLBACK not supported\n");
- tvItem->cChildren=wineItem->cChildren;
- }
-
- if (tvItem->mask & TVIF_HANDLE) {
- tvItem->hItem=wineItem->hItem;
- }
-
- if (tvItem->mask & TVIF_IMAGE) {
- tvItem->iImage=wineItem->iImage;
- }
-
- if (tvItem->mask & TVIF_INTEGRAL) {
- tvItem->iIntegral=wineItem->iIntegral;
- }
-
- /* undocumented: windows ignores TVIF_PARAM and
- * always sets lParam
- */
- tvItem->lParam=wineItem->lParam;
-
- if (tvItem->mask & TVIF_SELECTEDIMAGE) {
- tvItem->iSelectedImage=wineItem->iSelectedImage;
- }
-
- if (tvItem->mask & TVIF_STATE) {
- tvItem->state=wineItem->state & tvItem->stateMask;
- }
-
- if (tvItem->mask & TVIF_TEXT) {
- if (wineItem->pszText == LPSTR_TEXTCALLBACKA) {
- tvItem->pszText = LPSTR_TEXTCALLBACKA; /* FIXME:send notification? */
- ERR(" GetItem called with LPSTR_TEXTCALLBACK\n");
- }
- else if (wineItem->pszText) {
- lstrcpynA (tvItem->pszText, wineItem->pszText, tvItem->cchTextMax);
- }
- }
-
- TRACE("item %d<%p>, txt %p, img %p, action %x\n",
- iItem, tvItem, tvItem->pszText, &tvItem->iImage, tvItem->mask);
-
- return TRUE;
+ infoPtr->clrLine = color;
+ return (LRESULT)prevColor;
}
static LRESULT
-TREEVIEW_GetItemW (HWND hwnd, WPARAM wParam, LPARAM lParam)
+TREEVIEW_GetTextColor(TREEVIEW_INFO *infoPtr)
{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
- LPTVITEMEXA tvItem;
- TREEVIEW_ITEM *wineItem;
- INT iItem;
-
- tvItem=(LPTVITEMEXA) lParam;
- iItem=(INT)tvItem->hItem;
-
- wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
- if (!wineItem) return FALSE;
-
- if (tvItem->mask & TVIF_CHILDREN) {
- if (TVIF_CHILDREN==I_CHILDRENCALLBACK)
- FIXME("I_CHILDRENCALLBACK not supported\n");
- tvItem->cChildren=wineItem->cChildren;
- }
-
- if (tvItem->mask & TVIF_HANDLE) {
- tvItem->hItem=wineItem->hItem;
- }
-
- if (tvItem->mask & TVIF_IMAGE) {
- tvItem->iImage=wineItem->iImage;
- }
-
- if (tvItem->mask & TVIF_INTEGRAL) {
- tvItem->iIntegral=wineItem->iIntegral;
- }
-
- /* undocumented: windows ignores TVIF_PARAM and
- * always sets lParam
- */
- tvItem->lParam=wineItem->lParam;
-
- if (tvItem->mask & TVIF_SELECTEDIMAGE) {
- tvItem->iSelectedImage=wineItem->iSelectedImage;
- }
-
- if (tvItem->mask & TVIF_STATE) {
- tvItem->state=wineItem->state & tvItem->stateMask;
- }
-
-#if 0
- if (tvItem->mask & TVIF_TEXT) {
- if (wineItem->pszText == LPSTR_TEXTCALLBACKW) {
- tvItem->pszText = LPSTR_TEXTCALLBACKW; /* FIXME:send notification? */
- ERR(" GetItem called with LPSTR_TEXTCALLBACK\n");
- }
- else if (wineItem->pszText) {
- lstrcpynAtoW (tvItem->pszText, wineItem->pszText, tvItem->cchTextMax);
- }
- }
-#endif
- wineItem->pszText = NULL;
-
- TRACE("item %d<%p>, txt %p, img %p, action %x\n",
- iItem, tvItem, tvItem->pszText, &tvItem->iImage, tvItem->mask);
-
- return TRUE;
+ TRACE("\n");
+ return (LRESULT)infoPtr->clrText;
}
-
-
-/* FIXME: check implementation of TVGN_NEXT/TVGN_NEXTVISIBLE */
-
static LRESULT
-TREEVIEW_GetNextItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
-
+TREEVIEW_SetTextColor(TREEVIEW_INFO *infoPtr, COLORREF color)
{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
- TREEVIEW_ITEM *wineItem, *returnItem;
- INT iItem = (INT)lParam, retval = 0, flag = (INT)wParam;
+ COLORREF prevColor = infoPtr->clrText;
- switch (flag) {
- case TVGN_ROOT:
- retval = (INT)infoPtr->TopRootItem;
- break;
+ TRACE("\n");
+ infoPtr->clrText = color;
- case TVGN_CARET:
- retval = (INT)infoPtr->selectedItem;
- break;
+ if (infoPtr->clrText != prevColor)
+ TREEVIEW_QueueRefresh(infoPtr);
- case TVGN_FIRSTVISIBLE: /* FIXME:we should only recalculate, not redraw */
- InvalidateRect(hwnd, NULL, FALSE);
- retval = (INT)infoPtr->firstVisible;
- break;
-
- case TVGN_DROPHILITE:
- retval = (INT)infoPtr->dropItem;
- break;
-
- case TVGN_NEXT:
- wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
- retval = wineItem ? (INT)wineItem->sibling : 0;
- break;
-
- case TVGN_PREVIOUS:
- wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
- retval = wineItem ? (INT)wineItem->upsibling : 0;
- break;
-
- case TVGN_PARENT:
- wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
- retval = wineItem ? (INT)wineItem->parent : 0;
- break;
-
- case TVGN_CHILD:
- wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
- retval = wineItem ? (INT)wineItem->firstChild : 0;
- break;
-
- case TVGN_LASTVISIBLE:
- if((wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem))) {
- returnItem = TREEVIEW_GetLastListItem (infoPtr,wineItem);
- retval = returnItem ? (INT)returnItem->hItem : 0;
- }
- break;
-
- case TVGN_NEXTVISIBLE:
- if((wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem))) {
- returnItem = TREEVIEW_GetNextListItem (infoPtr,wineItem);
- retval = returnItem ? (INT)returnItem->hItem : 0;
- }
- break;
-
- case TVGN_PREVIOUSVISIBLE:
- if((wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem))) {
- returnItem = TREEVIEW_GetPrevListItem (infoPtr, wineItem);
- retval = returnItem ? (INT)returnItem->hItem : 0;
- }
- break;
-
- default:
- FIXME("Unknown msg %x,item %x\n", flag,iItem);
- break;
- }
-
- TRACE("flags %x, item %d returns %d\n", flag, iItem, retval);
- return retval;
+ return (LRESULT)prevColor;
}
static LRESULT
-TREEVIEW_GetCount (HWND hwnd, WPARAM wParam, LPARAM lParam)
+TREEVIEW_GetBkColor(TREEVIEW_INFO *infoPtr)
{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
-
- TRACE(" %d\n",infoPtr->uNumItems);
- return (LRESULT) infoPtr->uNumItems;
+ TRACE("\n");
+ return (LRESULT)infoPtr->clrBk;
}
-/***************************************************************************
- * This method does the chaining of the insertion of a treeview item
- * before an item.
- * If parent is NULL, we're inserting at the root of the list.
+static LRESULT
+TREEVIEW_SetBkColor(TREEVIEW_INFO *infoPtr, COLORREF newColor)
+{
+ COLORREF prevColor = infoPtr->clrBk;
+
+ TRACE("\n");
+ infoPtr->clrBk = newColor;
+
+ if (newColor != prevColor)
+ TREEVIEW_QueueRefresh(infoPtr);
+
+ return (LRESULT)prevColor;
+}
+
+
+static LRESULT
+TREEVIEW_GetInsertMarkColor(TREEVIEW_INFO *infoPtr)
+{
+ TRACE("\n");
+ return (LRESULT)infoPtr->clrInsertMark;
+}
+
+static LRESULT
+TREEVIEW_SetInsertMarkColor(TREEVIEW_INFO *infoPtr, COLORREF color)
+{
+ COLORREF prevColor = infoPtr->clrInsertMark;
+
+ TRACE("%lx\n", color);
+ infoPtr->clrInsertMark = color;
+
+ return (LRESULT)prevColor;
+}
+
+
+static LRESULT
+TREEVIEW_SetInsertMark(TREEVIEW_INFO *infoPtr, BOOL wParam, HTREEITEM item)
+{
+ TRACE("%d %p\n", wParam, item);
+
+ if (!TREEVIEW_ValidItem(infoPtr, item))
+ return 0;
+
+ infoPtr->insertBeforeorAfter = wParam;
+ infoPtr->insertMarkItem = item;
+
+ TREEVIEW_QueueRefresh(infoPtr);
+
+ return 1;
+}
+
+
+/************************************************************************
+ * Some serious braindamage here. lParam is a pointer to both the
+ * input HTREEITEM and the output RECT.
*/
-static void TREEVIEW_InsertBefore(
- TREEVIEW_INFO *infoPtr,
- TREEVIEW_ITEM *newItem,
- TREEVIEW_ITEM *sibling,
- TREEVIEW_ITEM *parent)
+static LRESULT
+TREEVIEW_GetItemRect(TREEVIEW_INFO *infoPtr, BOOL fTextRect, LPRECT lpRect)
{
- HTREEITEM siblingHandle = 0;
- HTREEITEM upSiblingHandle = 0;
- TREEVIEW_ITEM *upSibling = NULL;
+ TREEVIEW_ITEM *wineItem;
+ const HTREEITEM *pItem = (HTREEITEM *)lpRect;
- if (newItem == NULL)
- ERR("NULL newItem, impossible condition\n");
-
- if (sibling != NULL) /* Insert before this sibling for this parent */
- {
- /* Store the new item sibling up sibling and sibling tem handle */
- siblingHandle = sibling->hItem;
- upSiblingHandle = sibling->upsibling;
- /* As well as a pointer to the upsibling sibling object */
- if ( (INT)sibling->upsibling != 0 )
- upSibling = &infoPtr->items[(INT)sibling->upsibling];
-
- /* Adjust the sibling pointer */
- sibling->upsibling = newItem->hItem;
-
- /* Adjust the new item pointers */
- newItem->upsibling = upSiblingHandle;
- newItem->sibling = siblingHandle;
-
- /* Adjust the up sibling pointer */
- if ( upSibling != NULL )
- upSibling->sibling = newItem->hItem;
- else
- /* this item is the first child of this parent, adjust parent pointers */
- if (parent)
- parent->firstChild = newItem->hItem;
- else
- infoPtr->TopRootItem= newItem->hItem;
- }
- else /* Insert as first child of this parent */
- if (parent)
- parent->firstChild = newItem->hItem;
-}
-
-/***************************************************************************
- * This method does the chaining of the insertion of a treeview item
- * after an item.
- * If parent is NULL, we're inserting at the root of the list.
- */
-static void TREEVIEW_InsertAfter(
- TREEVIEW_INFO *infoPtr,
- TREEVIEW_ITEM *newItem,
- TREEVIEW_ITEM *upSibling,
- TREEVIEW_ITEM *parent)
-{
- HTREEITEM upSiblingHandle = 0;
- HTREEITEM siblingHandle = 0;
- TREEVIEW_ITEM *sibling = NULL;
-
-
- if (newItem == NULL)
- ERR("NULL newItem, impossible condition\n");
-
- if (upSibling != NULL) /* Insert after this upsibling for this parent */
- {
- /* Store the new item up sibling and sibling item handle */
- upSiblingHandle = upSibling->hItem;
- siblingHandle = upSibling->sibling;
- /* As well as a pointer to the upsibling sibling object */
- if ( (INT)upSibling->sibling != 0 )
- sibling = &infoPtr->items[(INT)upSibling->sibling];
-
- /* Adjust the up sibling pointer */
- upSibling->sibling = newItem->hItem;
-
- /* Adjust the new item pointers */
- newItem->upsibling = upSiblingHandle;
- newItem->sibling = siblingHandle;
-
- /* Adjust the sibling pointer */
- if ( sibling != NULL )
- sibling->upsibling = newItem->hItem;
+ TRACE("\n");
/*
- else
- newItem is the last of the level, nothing else to do
- */
- }
- else /* Insert as first child of this parent */
- if (parent)
- parent->firstChild = newItem->hItem;
+ * validate parameters
+ */
+ if (pItem == NULL)
+ return FALSE;
+
+ wineItem = *pItem;
+ if (!TREEVIEW_ValidItem(infoPtr, wineItem) || !ISVISIBLE(wineItem))
+ return FALSE;
+
+ /*
+ * If wParam is TRUE return the text size otherwise return
+ * the whole item size
+ */
+ if (fTextRect)
+ {
+ /* Windows does not send TVN_GETDISPINFO here. */
+
+ lpRect->top = wineItem->rect.top;
+ lpRect->bottom = wineItem->rect.bottom;
+
+ lpRect->left = wineItem->textOffset;
+ lpRect->right = wineItem->textOffset + wineItem->textWidth;
+ }
+ else
+ {
+ *lpRect = wineItem->rect;
+ }
+
+ TRACE("%s [L:%d R:%d T:%d B:%d]\n", fTextRect ? "text" : "item",
+ lpRect->left, lpRect->right, lpRect->top, lpRect->bottom);
+
+ return TRUE;
}
+static inline LRESULT
+TREEVIEW_GetVisibleCount(TREEVIEW_INFO *infoPtr)
+{
+ /* Suprise! This does not take integral height into account. */
+ return infoPtr->clientHeight / infoPtr->uItemHeight;
+}
+
+
+static LRESULT
+TREEVIEW_GetItemA(TREEVIEW_INFO *infoPtr, LPTVITEMEXA tvItem)
+{
+ TREEVIEW_ITEM *wineItem;
+
+ wineItem = tvItem->hItem;
+ if (!TREEVIEW_ValidItem(infoPtr, wineItem))
+ return FALSE;
+
+ TREEVIEW_UpdateDispInfo(infoPtr, wineItem, tvItem->mask);
+
+ if (tvItem->mask & TVIF_CHILDREN)
+ tvItem->cChildren = wineItem->cChildren;
+
+ if (tvItem->mask & TVIF_HANDLE)
+ tvItem->hItem = wineItem;
+
+ if (tvItem->mask & TVIF_IMAGE)
+ tvItem->iImage = wineItem->iImage;
+
+ if (tvItem->mask & TVIF_INTEGRAL)
+ tvItem->iIntegral = wineItem->iIntegral;
+
+ /* undocumented: windows ignores TVIF_PARAM and
+ * * always sets lParam
+ */
+ tvItem->lParam = wineItem->lParam;
+
+ if (tvItem->mask & TVIF_SELECTEDIMAGE)
+ tvItem->iSelectedImage = wineItem->iSelectedImage;
+
+ if (tvItem->mask & TVIF_STATE)
+ tvItem->state = wineItem->state & tvItem->stateMask;
+
+ if (tvItem->mask & TVIF_TEXT)
+ lstrcpynA(tvItem->pszText, wineItem->pszText, tvItem->cchTextMax);
+
+ TRACE("item <%p>, txt %p, img %p, mask %x\n",
+ wineItem, tvItem->pszText, &tvItem->iImage, tvItem->mask);
+
+ return TRUE;
+}
+
+/* Beware MSDN Library Visual Studio 6.0. It says -1 on failure, 0 on success,
+ * which is wrong. */
+static LRESULT
+TREEVIEW_SetItemA(TREEVIEW_INFO *infoPtr, LPTVITEMEXA tvItem)
+{
+ TREEVIEW_ITEM *wineItem;
+
+ wineItem = tvItem->hItem;
+ TRACE("item %d,mask %x\n", TREEVIEW_GetItemIndex(infoPtr, wineItem),
+ tvItem->mask);
+
+ if (!TREEVIEW_ValidItem(infoPtr, wineItem))
+ return FALSE;
+
+ if (!TREEVIEW_DoSetItem(infoPtr, wineItem, tvItem))
+ return FALSE;
+
+ /* If the text or TVIS_BOLD was changed, and it is visible, recalculate. */
+ if ((tvItem->mask & TVIF_TEXT
+ || (tvItem->mask & TVIF_STATE && tvItem->stateMask & TVIS_BOLD))
+ && ISVISIBLE(wineItem))
+ {
+ TREEVIEW_UpdateDispInfo(infoPtr, wineItem, TVIF_TEXT);
+ TREEVIEW_ComputeTextWidth(infoPtr, wineItem, 0);
+ }
+
+ if (tvItem->mask != 0 && ISVISIBLE(wineItem))
+ {
+ /* The refresh updates everything, but we can't wait until then. */
+ TREEVIEW_ComputeItemInternalMetrics(infoPtr, wineItem);
+
+ if (tvItem->mask & TVIF_INTEGRAL)
+ {
+ TREEVIEW_RecalculateVisibleOrder(infoPtr, wineItem);
+ TREEVIEW_UpdateScrollBars(infoPtr);
+
+ TREEVIEW_QueueRefresh(infoPtr);
+ }
+ else
+ {
+ TREEVIEW_UpdateScrollBars(infoPtr);
+ TREEVIEW_QueueItemRefresh(infoPtr, wineItem);
+ }
+ }
+
+ return TRUE;
+}
+
+static LRESULT
+TREEVIEW_GetItemW(TREEVIEW_INFO *infoPtr, LPTVITEMEXA tvItem)
+{
+ TREEVIEW_ITEM *wineItem;
+ INT iItem;
+ iItem = (INT)tvItem->hItem;
+
+ wineItem = tvItem->hItem;
+ if(!TREEVIEW_ValidItem (infoPtr, wineItem))
+ return FALSE;
+
+ TREEVIEW_UpdateDispInfo(infoPtr, wineItem, tvItem->mask);
+
+ if (tvItem->mask & TVIF_CHILDREN) {
+ if (TVIF_CHILDREN==I_CHILDRENCALLBACK)
+ FIXME("I_CHILDRENCALLBACK not supported\n");
+ tvItem->cChildren = wineItem->cChildren;
+ }
+
+ if (tvItem->mask & TVIF_HANDLE) {
+ tvItem->hItem = wineItem;
+ }
+ if (tvItem->mask & TVIF_IMAGE) {
+ tvItem->iImage = wineItem->iImage;
+ }
+ if (tvItem->mask & TVIF_INTEGRAL) {
+ tvItem->iIntegral = wineItem->iIntegral;
+ }
+ /* undocumented: windows ignores TVIF_PARAM and
+ * always sets lParam */
+ tvItem->lParam = wineItem->lParam;
+ if (tvItem->mask & TVIF_SELECTEDIMAGE) {
+ tvItem->iSelectedImage = wineItem->iSelectedImage;
+ }
+ if (tvItem->mask & TVIF_STATE) {
+ tvItem->state = wineItem->state & tvItem->stateMask;
+ }
+#if 0
+ if (tvItem->mask & TVIF_TEXT) {
+ if (wineItem->pszText == LPSTR_TEXTCALLBACKW) {
+ tvItem->pszText = LPSTR_TEXTCALLBACKW; /* FIXME:send notification? */
+ ERR(" GetItem called with LPSTR_TEXTCALLBACK\n");
+ }
+ else if (wineItem->pszText) {
+ lstrcpynAtoW(tvItem->pszText, wineItem->pszText, tvItem->cchTextMax);
+ }
+ }
+#endif
+ wineItem->pszText = NULL;
+ TRACE("item %d<%p>, txt %p, img %p, action %x\n",
+ iItem, tvItem, tvItem->pszText, &tvItem->iImage, tvItem->mask);
+ return TRUE;
+}
+
+static LRESULT
+TREEVIEW_GetItemState(TREEVIEW_INFO *infoPtr, HTREEITEM wineItem, UINT mask)
+{
+ TRACE("\n");
+
+ if (!wineItem || !TREEVIEW_ValidItem(infoPtr, wineItem))
+ return 0;
+
+ return (wineItem->state & mask);
+}
+
+static LRESULT
+TREEVIEW_GetNextItem(TREEVIEW_INFO *infoPtr, UINT which, HTREEITEM wineItem)
+{
+ TREEVIEW_ITEM *retval;
+
+ retval = 0;
+
+ /* handle all the global data here */
+ switch (which)
+ {
+ case TVGN_CHILD: /* Special case: child of 0 is root */
+ if (wineItem)
+ break;
+ /* fall through */
+ case TVGN_ROOT:
+ retval = infoPtr->root->firstChild;
+ break;
+
+ case TVGN_CARET:
+ retval = infoPtr->selectedItem;
+ break;
+
+ case TVGN_FIRSTVISIBLE:
+ retval = infoPtr->firstVisible;
+ break;
+
+ case TVGN_DROPHILITE:
+ retval = infoPtr->dropItem;
+ break;
+
+ case TVGN_LASTVISIBLE:
+ retval = TREEVIEW_GetLastListItem(infoPtr, infoPtr->root);
+ break;
+ }
+
+ if (retval)
+ {
+ TRACE("flags:%x, returns %p\n", which, retval);
+ return (LRESULT)retval;
+ }
+
+ if (!TREEVIEW_ValidItem(infoPtr, wineItem))
+ return FALSE;
+
+ switch (which)
+ {
+ case TVGN_NEXT:
+ retval = wineItem->nextSibling;
+ break;
+ case TVGN_PREVIOUS:
+ retval = wineItem->prevSibling;
+ break;
+ case TVGN_PARENT:
+ retval = (wineItem->parent != infoPtr->root) ? wineItem->parent : NULL;
+ break;
+ case TVGN_CHILD:
+ retval = wineItem->firstChild;
+ break;
+ case TVGN_NEXTVISIBLE:
+ retval = TREEVIEW_GetNextListItem(infoPtr, wineItem);
+ break;
+ case TVGN_PREVIOUSVISIBLE:
+ retval = TREEVIEW_GetPrevListItem(infoPtr, wineItem);
+ break;
+ default:
+ TRACE("Unknown msg %x,item %p\n", which, wineItem);
+ break;
+ }
+
+ TRACE("flags:%x, item %p;returns %p\n", which, wineItem, retval);
+ return (LRESULT)retval;
+}
+
+
+static LRESULT
+TREEVIEW_GetCount(TREEVIEW_INFO *infoPtr)
+{
+ TRACE(" %d\n", infoPtr->uNumItems);
+ return (LRESULT)infoPtr->uNumItems;
+}
+
+static VOID
+TREEVIEW_ToggleItemState(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item)
+{
+ if (infoPtr->dwStyle & TVS_CHECKBOXES)
+ {
+ static const unsigned int state_table[] = { 0, 2, 1 };
+
+ unsigned int state;
+
+ state = STATEIMAGEINDEX(item->state);
+ TRACE("state:%x\n", state);
+ item->state &= ~TVIS_STATEIMAGEMASK;
+
+ if (state < 3)
+ state = state_table[state];
+
+ item->state |= INDEXTOSTATEIMAGEMASK(state);
+
+ TRACE("state:%x\n", state);
+ TREEVIEW_QueueItemRefresh(infoPtr, item);
+ }
+}
+
+
+/* Painting *************************************************************/
+
+/* Draw the lines and expand button for an item. Also draws one section
+ * of the line from item's parent to item's parent's next sibling. */
+static void
+TREEVIEW_DrawItemLines(TREEVIEW_INFO *infoPtr, HDC hdc, TREEVIEW_ITEM *item)
+{
+ LONG centerx, centery;
+ BOOL lar = ((infoPtr->dwStyle
+ & (TVS_LINESATROOT|TVS_HASLINES|TVS_HASBUTTONS))
+ > TVS_LINESATROOT);
+
+ if (!lar && item->iLevel == 0)
+ return;
+
+ centerx = (item->linesOffset + item->stateOffset) / 2;
+ centery = (item->rect.top + item->rect.bottom) / 2;
+
+ if (infoPtr->dwStyle & TVS_HASLINES)
+ {
+ HPEN hOldPen, hNewPen;
+ HTREEITEM parent;
+
+ /*
+ * Get a dotted grey pen
+ */
+ hNewPen = CreatePen(PS_DOT, 0, infoPtr->clrLine);
+ hOldPen = SelectObject(hdc, hNewPen);
+
+ MoveToEx(hdc, item->stateOffset, centery, NULL);
+ LineTo(hdc, centerx - 1, centery);
+
+ if (item->prevSibling || item->parent != infoPtr->root)
+ {
+ MoveToEx(hdc, centerx, item->rect.top, NULL);
+ LineTo(hdc, centerx, centery);
+ }
+
+ if (item->nextSibling)
+ {
+ MoveToEx(hdc, centerx, centery, NULL);
+ LineTo(hdc, centerx, item->rect.bottom + 1);
+ }
+
+ /* Draw the line from our parent to its next sibling. */
+ parent = item->parent;
+ while (parent != infoPtr->root)
+ {
+ int pcenterx = (parent->linesOffset + parent->stateOffset) / 2;
+
+ if (parent->nextSibling
+ /* skip top-levels unless TVS_LINESATROOT */
+ && parent->stateOffset > parent->linesOffset)
+ {
+ MoveToEx(hdc, pcenterx, item->rect.top, NULL);
+ LineTo(hdc, pcenterx, item->rect.bottom + 1);
+ }
+
+ parent = parent->parent;
+ }
+
+ SelectObject(hdc, hOldPen);
+ DeleteObject(hNewPen);
+ }
+
+ /*
+ * Display the (+/-) signs
+ */
+
+ if (infoPtr->dwStyle & TVS_HASBUTTONS)
+ {
+ if (item->cChildren)
+ {
+ LONG height = item->rect.bottom - item->rect.top;
+ LONG width = item->stateOffset - item->linesOffset;
+ LONG rectsize = min(height, width) / 4;
+ /* plussize = ceil(rectsize * 3/4) */
+ LONG plussize = (rectsize + 1) * 3 / 4;
+
+ HPEN hNewPen = CreatePen(PS_SOLID, 0, infoPtr->clrLine);
+ HPEN hOldPen = SelectObject(hdc, hNewPen);
+ HBRUSH hbr = CreateSolidBrush(infoPtr->clrBk);
+ HBRUSH hbrOld = SelectObject(hdc, hbr);
+
+ Rectangle(hdc, centerx - rectsize, centery - rectsize,
+ centerx + rectsize + 1, centery + rectsize + 1);
+
+ SelectObject(hdc, hbrOld);
+ DeleteObject(hbr);
+
+ SelectObject(hdc, hOldPen);
+ DeleteObject(hNewPen);
+
+ MoveToEx(hdc, centerx - plussize + 1, centery, NULL);
+ LineTo(hdc, centerx + plussize, centery);
+
+ if (!(item->state & TVIS_EXPANDED))
+ {
+ MoveToEx(hdc, centerx, centery - plussize + 1, NULL);
+ LineTo(hdc, centerx, centery + plussize);
+ }
+ }
+ }
+}
+
+static void
+TREEVIEW_DrawItem(TREEVIEW_INFO *infoPtr, HDC hdc, TREEVIEW_ITEM *wineItem)
+{
+ INT cditem;
+ HFONT hOldFont;
+ int centery;
+
+ hOldFont = SelectObject(hdc, TREEVIEW_FontForItem(infoPtr, wineItem));
+
+ TREEVIEW_UpdateDispInfo(infoPtr, wineItem, CALLBACK_MASK_ALL);
+
+ /* The custom draw handler can query the text rectangle,
+ * so get ready. */
+ TREEVIEW_ComputeTextWidth(infoPtr, wineItem, hdc);
+
+ cditem = 0;
+
+ if (infoPtr->cdmode & CDRF_NOTIFYITEMDRAW)
+ {
+ cditem = TREEVIEW_SendCustomDrawItemNotify
+ (infoPtr, hdc, wineItem, CDDS_ITEMPREPAINT);
+ TRACE("prepaint:cditem-app returns 0x%x\n", cditem);
+
+ if (cditem & CDRF_SKIPDEFAULT)
+ {
+ SelectObject(hdc, hOldFont);
+ return;
+ }
+ }
+
+ if (cditem & CDRF_NEWFONT)
+ TREEVIEW_ComputeTextWidth(infoPtr, wineItem, hdc);
+
+ TREEVIEW_DrawItemLines(infoPtr, hdc, wineItem);
+
+ centery = (wineItem->rect.top + wineItem->rect.bottom) / 2;
+
+ /*
+ * Display the images associated with this item
+ */
+ {
+ INT imageIndex;
+
+ /* State images are displayed to the left of the Normal image
+ * image number is in state; zero should be `display no image'.
+ */
+ imageIndex = STATEIMAGEINDEX(wineItem->state);
+
+ if (infoPtr->himlState && imageIndex)
+ {
+ ImageList_Draw(infoPtr->himlState, imageIndex, hdc,
+ wineItem->stateOffset,
+ centery - infoPtr->stateImageHeight / 2,
+ ILD_NORMAL);
+ }
+
+ /* Now, draw the normal image; can be either selected or
+ * non-selected image.
+ */
+
+ if ((wineItem->state & TVIS_SELECTED) && (wineItem->iSelectedImage))
+ {
+ /* The item is curently selected */
+ imageIndex = wineItem->iSelectedImage;
+ }
+ else
+ {
+ /* The item is not selected */
+ imageIndex = wineItem->iImage;
+ }
+
+ if (infoPtr->himlNormal)
+ {
+ int ovlIdx = wineItem->state & TVIS_OVERLAYMASK;
+
+ ImageList_Draw(infoPtr->himlNormal, imageIndex, hdc,
+ wineItem->imageOffset,
+ centery - infoPtr->normalImageHeight / 2,
+ ILD_NORMAL | ovlIdx);
+ }
+ }
+
+
+ /*
+ * Display the text associated with this item
+ */
+
+ /* Don't paint item's text if it's being edited */
+ if (!infoPtr->hwndEdit || (infoPtr->selectedItem != wineItem))
+ {
+ if (wineItem->pszText)
+ {
+ COLORREF oldTextColor = 0;
+ INT oldBkMode;
+ HBRUSH hbrBk = 0;
+ BOOL inFocus = (GetFocus() == infoPtr->hwnd);
+ RECT rcText;
+
+ oldBkMode = SetBkMode(hdc, TRANSPARENT);
+
+ /* - If item is drop target or it is selected and window is in focus -
+ * use blue background (COLOR_HIGHLIGHT).
+ * - If item is selected, window is not in focus, but it has style
+ * TVS_SHOWSELALWAYS - use grey background (COLOR_BTNFACE)
+ * - Otherwise - don't fill background
+ */
+ if ((wineItem->state & TVIS_DROPHILITED) || ((wineItem == infoPtr->focusedItem) && !(wineItem->state & TVIS_SELECTED)) ||
+ ((wineItem->state & TVIS_SELECTED) && (!infoPtr->focusedItem) &&
+ (inFocus || (infoPtr->dwStyle & TVS_SHOWSELALWAYS))))
+ {
+ if ((wineItem->state & TVIS_DROPHILITED) || inFocus)
+ {
+ hbrBk = CreateSolidBrush(GetSysColor(COLOR_HIGHLIGHT));
+ oldTextColor =
+ SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
+ }
+ else
+ {
+ hbrBk = CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
+
+ if (infoPtr->clrText == -1)
+ oldTextColor =
+ SetTextColor(hdc, GetSysColor(COLOR_WINDOWTEXT));
+ else
+ oldTextColor = SetTextColor(hdc, infoPtr->clrText);
+ }
+ }
+ else
+ {
+ if (infoPtr->clrText == -1)
+ oldTextColor =
+ SetTextColor(hdc, GetSysColor(COLOR_WINDOWTEXT));
+ else
+ oldTextColor = SetTextColor(hdc, infoPtr->clrText);
+ }
+
+ rcText.top = wineItem->rect.top;
+ rcText.bottom = wineItem->rect.bottom;
+ rcText.left = wineItem->textOffset;
+ rcText.right = rcText.left + wineItem->textWidth + 4;
+
+ if (hbrBk)
+ {
+ FillRect(hdc, &rcText, hbrBk);
+ DeleteObject(hbrBk);
+ }
+
+ /* Draw the box arround the selected item */
+ if ((wineItem == infoPtr->selectedItem) && inFocus)
+ {
+ HPEN hNewPen = CreatePen(PS_DOT, 0,
+ GetSysColor(COLOR_WINDOWTEXT));
+ HPEN hOldPen = SelectObject(hdc, hNewPen);
+ INT rop = SetROP2(hdc, R2_XORPEN);
+ POINT points[5];
+
+ points[4].x = points[0].x = rcText.left;
+ points[4].y = points[0].y = rcText.top;
+ points[1].x = rcText.right - 1;
+ points[1].y = rcText.top;
+ points[2].x = rcText.right - 1;
+ points[2].y = rcText.bottom - 1;
+ points[3].x = rcText.left;
+ points[3].y = rcText.bottom - 1;
+
+ Polyline(hdc, points, 5);
+
+ SetROP2(hdc, rop);
+ SelectObject(hdc, hOldPen);
+ DeleteObject(hNewPen);
+ }
+
+ rcText.left += 2;
+ rcText.right -= 2;
+
+ /* Draw it */
+ DrawTextA(hdc,
+ wineItem->pszText,
+ lstrlenA(wineItem->pszText),
+ &rcText,
+ DT_CENTER | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX);
+
+ /* Restore the hdc state */
+ SetTextColor(hdc, oldTextColor);
+
+ if (oldBkMode != TRANSPARENT)
+ SetBkMode(hdc, oldBkMode);
+ }
+ }
+
+ /* Draw insertion mark if necessary */
+
+ if (infoPtr->insertMarkItem)
+ TRACE("item:%d,mark:%d\n",
+ TREEVIEW_GetItemIndex(infoPtr, wineItem),
+ (int)infoPtr->insertMarkItem);
+
+ if (wineItem == infoPtr->insertMarkItem)
+ {
+ HPEN hNewPen, hOldPen;
+ int offset;
+ int left, right;
+
+ hNewPen = CreatePen(PS_SOLID, 2, infoPtr->clrInsertMark);
+ hOldPen = SelectObject(hdc, hNewPen);
+
+ if (infoPtr->insertBeforeorAfter)
+ offset = wineItem->rect.bottom - 1;
+ else
+ offset = wineItem->rect.top + 1;
+
+ left = wineItem->textOffset - 2;
+ right = wineItem->textOffset + wineItem->textWidth + 2;
+
+ MoveToEx(hdc, left, offset - 3, NULL);
+ LineTo(hdc, left, offset + 4);
+
+ MoveToEx(hdc, left, offset, NULL);
+ LineTo(hdc, right + 1, offset);
+
+ MoveToEx(hdc, right, offset + 3, NULL);
+ LineTo(hdc, right, offset - 4);
+
+ SelectObject(hdc, hOldPen);
+ DeleteObject(hNewPen);
+ }
+
+ if (cditem & CDRF_NOTIFYPOSTPAINT)
+ {
+ cditem = TREEVIEW_SendCustomDrawItemNotify
+ (infoPtr, hdc, wineItem, CDDS_ITEMPOSTPAINT);
+ TRACE("postpaint:cditem-app returns 0x%x\n", cditem);
+ }
+
+ SelectObject(hdc, hOldFont);
+}
+
+/* Computes treeHeight and treeWidth and updates the scroll bars.
+ */
+static void
+TREEVIEW_UpdateScrollBars(TREEVIEW_INFO *infoPtr)
+{
+ TREEVIEW_ITEM *wineItem;
+ HWND hwnd = infoPtr->hwnd;
+ BOOL vert = FALSE;
+ BOOL horz = FALSE;
+ SCROLLINFO si;
+ LONG scrollX = infoPtr->scrollX;
+
+ infoPtr->treeWidth = 0;
+ infoPtr->treeHeight = 0;
+
+ /* We iterate through all visible items in order to get the tree height
+ * and width */
+ wineItem = infoPtr->root->firstChild;
+
+ while (wineItem != NULL)
+ {
+ if (ISVISIBLE(wineItem))
+ {
+ /* actually we draw text at textOffset + 2 */
+ if (2+wineItem->textOffset+wineItem->textWidth > infoPtr->treeWidth)
+ infoPtr->treeWidth = wineItem->textOffset+wineItem->textWidth+2;
+
+ /* This is scroll-adjusted, but we fix this below. */
+ infoPtr->treeHeight = wineItem->rect.bottom;
+ }
+
+ wineItem = TREEVIEW_GetNextListItem(infoPtr, wineItem);
+ }
+
+ /* Fix the scroll adjusted treeHeight and treeWidth. */
+ if (infoPtr->root->firstChild)
+ infoPtr->treeHeight -= infoPtr->root->firstChild->rect.top;
+
+ infoPtr->treeWidth += infoPtr->scrollX;
+
+ /* Adding one scroll bar may take up enough space that it forces us
+ * to add the other as well. */
+ if (infoPtr->treeHeight > infoPtr->clientHeight)
+ {
+ vert = TRUE;
+
+ if (infoPtr->treeWidth
+ > infoPtr->clientWidth - GetSystemMetrics(SM_CXVSCROLL))
+ horz = TRUE;
+ }
+ else if (infoPtr->treeWidth > infoPtr->clientWidth)
+ horz = TRUE;
+
+ if (!vert && horz && infoPtr->treeHeight
+ > infoPtr->clientHeight - GetSystemMetrics(SM_CYVSCROLL))
+ vert = TRUE;
+
+ si.cbSize = sizeof(SCROLLINFO);
+ si.fMask = SIF_POS|SIF_RANGE|SIF_PAGE;
+ si.nMin = 0;
+
+ if (vert)
+ {
+ infoPtr->uInternalStatus |= TV_VSCROLL;
+
+ si.nPage = TREEVIEW_GetVisibleCount(infoPtr);
+ si.nPos = infoPtr->firstVisible->visibleOrder;
+ si.nMax = infoPtr->maxVisibleOrder - 1;
+
+ SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
+ }
+ else
+ {
+ if (infoPtr->uInternalStatus & TV_VSCROLL)
+ ShowScrollBar(hwnd, SB_VERT, FALSE);
+
+ infoPtr->uInternalStatus &= ~TV_VSCROLL;
+ }
+
+ if (horz)
+ {
+ infoPtr->uInternalStatus |= TV_HSCROLL;
+
+ si.nPage = infoPtr->clientWidth;
+ si.nPos = infoPtr->scrollX;
+ si.nMax = infoPtr->treeWidth - 1;
+
+ if (si.nPos > si.nMax - max( si.nPage-1, 0 ))
+ {
+ si.nPos = si.nMax - max( si.nPage-1, 0 );
+ scrollX = si.nPos;
+ }
+
+ SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);
+ }
+ else
+ {
+ if (infoPtr->uInternalStatus & TV_HSCROLL)
+ ShowScrollBar(hwnd, SB_HORZ, FALSE);
+
+ scrollX = 0;
+ }
+
+ if (infoPtr->scrollX != scrollX)
+ {
+ TREEVIEW_HScroll(infoPtr,
+ MAKEWPARAM(SB_THUMBPOSITION, scrollX));
+ }
+
+ if (!horz)
+ infoPtr->uInternalStatus &= ~TV_HSCROLL;
+}
+
+/* CtrlSpy doesn't mention this, but CorelDRAW's object manager needs it. */
+static LRESULT
+TREEVIEW_EraseBackground(TREEVIEW_INFO *infoPtr, HDC hDC)
+{
+ HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk);
+ RECT rect;
+
+ GetClientRect(infoPtr->hwnd, &rect);
+ FillRect(hDC, &rect, hBrush);
+ DeleteObject(hBrush);
+
+ return 1;
+}
+
+static void
+TREEVIEW_Refresh(TREEVIEW_INFO *infoPtr, HDC hdc, RECT *rc)
+{
+ HWND hwnd = infoPtr->hwnd;
+ RECT rect = *rc;
+ TREEVIEW_ITEM *wineItem;
+
+ if (infoPtr->clientHeight == 0 || infoPtr->clientWidth == 0)
+ {
+ TRACE("empty window\n");
+ return;
+ }
+
+ infoPtr->cdmode = TREEVIEW_SendCustomDrawNotify(infoPtr, CDDS_PREPAINT,
+ hdc, rect);
+
+ if (infoPtr->cdmode == CDRF_SKIPDEFAULT)
+ {
+ ReleaseDC(hwnd, hdc);
+ return;
+ }
+
+ for (wineItem = infoPtr->root->firstChild;
+ wineItem != NULL;
+ wineItem = TREEVIEW_GetNextListItem(infoPtr, wineItem))
+ {
+ if (ISVISIBLE(wineItem))
+ {
+ /* Avoid unneeded calculations */
+ if (wineItem->rect.top > rect.bottom)
+ break;
+ if (wineItem->rect.bottom < rect.top)
+ continue;
+
+ TREEVIEW_DrawItem(infoPtr, hdc, wineItem);
+ }
+ }
+
+ TREEVIEW_UpdateScrollBars(infoPtr);
+
+ if (infoPtr->cdmode & CDRF_NOTIFYPOSTPAINT)
+ infoPtr->cdmode =
+ TREEVIEW_SendCustomDrawNotify(infoPtr, CDDS_POSTPAINT, hdc, rect);
+}
+
+static void
+TREEVIEW_QueueRefresh(TREEVIEW_INFO *infoPtr)
+{
+ InvalidateRect(infoPtr->hwnd, NULL, TRUE);
+}
+
+/* It be that item->rect is out of date. If so, we invalidate the wrong area,
+ * but then whoever updates item->rect knows that they must invalidate after
+ * correcting it. */
+static void
+TREEVIEW_QueueItemRefresh(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item)
+{
+ if (item != NULL)
+ InvalidateRect(infoPtr->hwnd, &item->rect, TRUE);
+}
+
+static LRESULT
+TREEVIEW_Paint(TREEVIEW_INFO *infoPtr, WPARAM wParam)
+{
+ HDC hdc;
+ PAINTSTRUCT ps;
+ RECT rc;
+
+ TRACE("\n");
+
+ if (wParam)
+ {
+ hdc = (HDC)wParam;
+ GetUpdateRect(infoPtr->hwnd, &rc, TRUE);
+ }
+ else
+ {
+ hdc = BeginPaint(infoPtr->hwnd, &ps);
+ rc = ps.rcPaint;
+ }
+
+ if(infoPtr->bRedraw) /* WM_SETREDRAW sets bRedraw */
+ TREEVIEW_Refresh(infoPtr, hdc, &rc);
+
+ if (!wParam)
+ EndPaint(infoPtr->hwnd, &ps);
+
+ return 0;
+}
+
+
+/* Sorting **************************************************************/
+
/***************************************************************************
* Forward the DPA local callback to the treeview owner callback
*/
-static INT WINAPI TREEVIEW_CallBackCompare(
- LPVOID first,
- LPVOID second,
- LPARAM tvInfoPtr)
+static INT WINAPI
+TREEVIEW_CallBackCompare(TREEVIEW_ITEM *first, TREEVIEW_ITEM *second, LPTVSORTCB pCallBackSort)
{
- /* Forward the call to the client define callback */
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr((HWND)tvInfoPtr);
- return (infoPtr->pCallBackSort->lpfnCompare)(
- ((TREEVIEW_ITEM*)first)->lParam,
- ((TREEVIEW_ITEM*)second)->lParam,
- infoPtr->pCallBackSort->lParam);
+ /* Forward the call to the client-defined callback */
+ return pCallBackSort->lpfnCompare(first->lParam,
+ second->lParam,
+ pCallBackSort->lParam);
}
/***************************************************************************
* Treeview native sort routine: sort on item text.
*/
-static INT WINAPI TREEVIEW_SortOnName (
- LPVOID first,
- LPVOID second,
- LPARAM tvInfoPtr)
+static INT WINAPI
+TREEVIEW_SortOnName(TREEVIEW_ITEM *first, TREEVIEW_ITEM *second,
+ TREEVIEW_INFO *infoPtr)
{
- HWND hwnd=(HWND) tvInfoPtr;
- char *txt1, *txt2;
- TREEVIEW_ITEM *item;
+ TREEVIEW_UpdateDispInfo(infoPtr, first, TVIF_TEXT);
+ TREEVIEW_UpdateDispInfo(infoPtr, second, TVIF_TEXT);
-
- item=(TREEVIEW_ITEM *) first;
- if (item->pszText==LPSTR_TEXTCALLBACKA) {
- TREEVIEW_SendDispInfoNotify (hwnd, item, TVN_GETDISPINFOA, TVIF_TEXT);
+ return strcasecmp(first->pszText, second->pszText);
+}
+
+/* Returns the number of physical children belonging to item. */
+static INT
+TREEVIEW_CountChildren(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item)
+{
+ INT cChildren = 0;
+ HTREEITEM hti;
+
+ for (hti = item->firstChild; hti != NULL; hti = hti->nextSibling)
+ cChildren++;
+
+ return cChildren;
+}
+
+/* Returns a DPA containing a pointer to each physical child of item in
+ * sibling order. If item has no children, an empty DPA is returned. */
+static HDPA
+TREEVIEW_BuildChildDPA(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item)
+{
+ HTREEITEM child = item->firstChild;
+
+ HDPA list = DPA_Create(8);
+ if (list == 0) return NULL;
+
+ for (child = item->firstChild; child != NULL; child = child->nextSibling)
+ {
+ if (DPA_InsertPtr(list, INT_MAX, child) == -1)
+ {
+ DPA_Destroy(list);
+ return NULL;
}
- txt1=item->pszText;
+ }
- item=(TREEVIEW_ITEM *) second;
- if (item->pszText==LPSTR_TEXTCALLBACKA) {
- TREEVIEW_SendDispInfoNotify (hwnd, item, TVN_GETDISPINFOA, TVIF_TEXT);
- }
- txt2=item->pszText;
-
- return -strcmp (txt1,txt2);
+ return list;
}
/***************************************************************************
@@ -1755,109 +2705,116 @@
* application decide what that means. See also TVM_SORTCHILDRENCB.
*/
-static LRESULT WINAPI TREEVIEW_Sort (
- HWND hwnd,
- BOOL fRecurse,
- HTREEITEM parent,
- LPTVSORTCB pSort
- )
+static LRESULT
+TREEVIEW_Sort(TREEVIEW_INFO *infoPtr, BOOL fRecurse, HTREEITEM parent,
+ LPTVSORTCB pSort)
{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
- TREEVIEW_ITEM *sortMe = NULL; /* Node for which we sort the children */
+ INT cChildren;
+ PFNDPACOMPARE pfnCompare;
+ LPARAM lpCompare;
- /* Obtain the TVSORTBC struct */
- infoPtr->pCallBackSort = pSort;
+ /* undocumented feature: TVI_ROOT means `sort the whole tree' */
+ if (parent == TVI_ROOT)
+ parent = infoPtr->root;
- /* undocumented feature: TVI_ROOT means `sort the whole tree' */
-
- if (parent==TVI_ROOT)
- parent=infoPtr->TopRootItem;
-
- /* Check for a valid handle to the parent item */
- if (!TREEVIEW_ValidItem(infoPtr, parent))
- {
- ERR ("invalid item hParent=%x\n", (INT)parent);
- return FALSE;
- }
-
- /* Obtain the parent node to sort */
- sortMe = &infoPtr->items[ (INT)parent ];
-
- /* Make sure there is something to sort */
- if ( sortMe->cChildren > 1 )
- {
- /* pointer organization */
- HDPA sortList = DPA_Create(sortMe->cChildren);
- HTREEITEM itemHandle = sortMe->firstChild;
- TREEVIEW_ITEM *itemPtr = & infoPtr->items[ (INT)itemHandle ];
-
- /* TREEVIEW_ITEM rechaining */
- INT count = 0;
- VOID *item = 0;
- VOID *nextItem = 0;
- VOID *prevItem = 0;
-
- /* Build the list of item to sort */
- do
+ /* Check for a valid handle to the parent item */
+ if (!TREEVIEW_ValidItem(infoPtr, parent))
{
- DPA_InsertPtr(
- sortList, /* the list */
- sortMe->cChildren+1, /* force the insertion to be an append */
- itemPtr); /* the ptr to store */
-
- /* Get the next sibling */
- itemHandle = itemPtr->sibling;
- itemPtr = & infoPtr->items[ (INT)itemHandle ];
- } while ( itemHandle != NULL );
-
- /* let DPA perform the sort activity */
- if (pSort)
- DPA_Sort(
- sortList, /* what */
- TREEVIEW_CallBackCompare, /* how */
- hwnd); /* owner */
- else
- DPA_Sort (
- sortList, /* what */
- TREEVIEW_SortOnName, /* how */
- hwnd); /* owner */
-
- /*
- * Reorganized TREEVIEW_ITEM structures.
- * Note that we know we have at least two elements.
- */
-
- /* Get the first item and get ready to start... */
- item = DPA_GetPtr(sortList, count++);
- while ( (nextItem = DPA_GetPtr(sortList, count++)) != NULL )
- {
- /* link the two current item toghether */
- ((TREEVIEW_ITEM*)item)->sibling = ((TREEVIEW_ITEM*)nextItem)->hItem;
- ((TREEVIEW_ITEM*)nextItem)->upsibling = ((TREEVIEW_ITEM*)item)->hItem;
-
- if (prevItem == NULL) /* this is the first item, update the parent */
- {
- sortMe->firstChild = ((TREEVIEW_ITEM*)item)->hItem;
- ((TREEVIEW_ITEM*)item)->upsibling = NULL;
- }
- else /* fix the back chaining */
- {
- ((TREEVIEW_ITEM*)item)->upsibling = ((TREEVIEW_ITEM*)prevItem)->hItem;
- }
-
- /* get ready for the next one */
- prevItem = item;
- item = nextItem;
+ ERR("invalid item hParent=%x\n", (INT)parent);
+ return FALSE;
}
- /* the last item is pointed to by item and never has a sibling */
- ((TREEVIEW_ITEM*)item)->sibling = NULL;
+ if (pSort)
+ {
+ pfnCompare = (PFNDPACOMPARE)TREEVIEW_CallBackCompare;
+ lpCompare = (LPARAM)pSort;
+ }
+ else
+ {
+ pfnCompare = (PFNDPACOMPARE)TREEVIEW_SortOnName;
+ lpCompare = (LPARAM)infoPtr;
+ }
- DPA_Destroy(sortList);
+ cChildren = TREEVIEW_CountChildren(infoPtr, parent);
- return TRUE;
- }
- return FALSE;
+ /* Make sure there is something to sort */
+ if (cChildren > 1)
+ {
+ /* TREEVIEW_ITEM rechaining */
+ INT count = 0;
+ HTREEITEM item = 0;
+ HTREEITEM nextItem = 0;
+ HTREEITEM prevItem = 0;
+
+ HDPA sortList = TREEVIEW_BuildChildDPA(infoPtr, parent);
+
+ if (sortList == NULL)
+ return FALSE;
+
+ /* let DPA sort the list */
+ DPA_Sort(sortList, pfnCompare, lpCompare);
+
+ /* The order of DPA entries has been changed, so fixup the
+ * nextSibling and prevSibling pointers. */
+
+ item = (HTREEITEM)DPA_GetPtr(sortList, count++);
+ while ((nextItem = (HTREEITEM)DPA_GetPtr(sortList, count++)) != NULL)
+ {
+ /* link the two current item toghether */
+ item->nextSibling = nextItem;
+ nextItem->prevSibling = item;
+
+ if (prevItem == NULL)
+ {
+ /* this is the first item, update the parent */
+ parent->firstChild = item;
+ item->prevSibling = NULL;
+ }
+ else
+ {
+ /* fix the back chaining */
+ item->prevSibling = prevItem;
+ }
+
+ /* get ready for the next one */
+ prevItem = item;
+ item = nextItem;
+ }
+
+ /* the last item is pointed to by item and never has a sibling */
+ item->nextSibling = NULL;
+ parent->lastChild = item;
+
+ DPA_Destroy(sortList);
+
+ TREEVIEW_VerifyTree(infoPtr);
+
+ if (parent->state & TVIS_EXPANDED)
+ {
+ int visOrder = infoPtr->firstVisible->visibleOrder;
+
+ TREEVIEW_RecalculateVisibleOrder(infoPtr, parent);
+
+ if (TREEVIEW_IsChildOf(parent, infoPtr->firstVisible))
+ {
+ TREEVIEW_ITEM *item;
+
+ for (item = infoPtr->root->firstChild; item != NULL;
+ item = TREEVIEW_GetNextListItem(infoPtr, item))
+ {
+ if (item->visibleOrder == visOrder)
+ break;
+ }
+
+ TREEVIEW_SetFirstVisible(infoPtr, item, FALSE);
+ }
+
+ TREEVIEW_QueueRefresh(infoPtr);
+ }
+
+ return TRUE;
+ }
+ return FALSE;
}
@@ -1865,1058 +2822,213 @@
* Setup the treeview structure with regards of the sort method
* and sort the children of the TV item specified in lParam
*/
-static LRESULT WINAPI TREEVIEW_SortChildrenCB(
- HWND hwnd,
- WPARAM wParam,
- LPARAM lParam
- )
+static LRESULT
+TREEVIEW_SortChildrenCB(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPTVSORTCB pSort)
{
- LPTVSORTCB pSort=(LPTVSORTCB) lParam;
-
- return TREEVIEW_Sort (hwnd, wParam, pSort->hParent, pSort);
+ return TREEVIEW_Sort(infoPtr, wParam, pSort->hParent, pSort);
}
/***************************************************************************
* Sort the children of the TV item specified in lParam.
*/
-static LRESULT WINAPI TREEVIEW_SortChildren (
- HWND hwnd,
- WPARAM wParam,
- LPARAM lParam)
+static LRESULT
+TREEVIEW_SortChildren(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
{
- return TREEVIEW_Sort (hwnd, (BOOL) wParam, (HTREEITEM) lParam, NULL);
+ return TREEVIEW_Sort(infoPtr, (BOOL)wParam, (HTREEITEM)lParam, NULL);
}
+/* Expansion/Collapse ***************************************************/
-/* the method used below isn't the most memory-friendly, but it avoids
- a lot of memory reallocations */
-
-/* BTW: we waste handle 0; 0 is not an allowed handle. */
-
-static LRESULT
-TREEVIEW_InsertItemA (HWND hwnd, WPARAM wParam, LPARAM lParam)
-
+static BOOL
+TREEVIEW_SendExpanding(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *wineItem,
+ UINT action)
{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
- TVINSERTSTRUCTA *ptdi;
- TVITEMEXA *tvItem;
- TREEVIEW_ITEM *wineItem, *parentItem, *prevsib, *sibItem;
- INT iItem,listItems,i,len;
-
- /* Item to insert */
- ptdi = (LPTVINSERTSTRUCTA) lParam;
+ return !TREEVIEW_SendTreeviewNotify(infoPtr, TVN_ITEMEXPANDINGA, action,
+ TVIF_HANDLE | TVIF_STATE | TVIF_PARAM
+ | TVIF_IMAGE | TVIF_SELECTEDIMAGE,
+ 0, wineItem);
+}
- /* check if memory is available */
-
- if (infoPtr->uNumPtrsAlloced==0) {
- infoPtr->items = COMCTL32_Alloc (TVITEM_ALLOC*sizeof (TREEVIEW_ITEM));
- infoPtr->freeList= COMCTL32_Alloc ((1+(TVITEM_ALLOC>>5)) * sizeof (INT));
- infoPtr->uNumPtrsAlloced=TVITEM_ALLOC;
- infoPtr->TopRootItem=(HTREEITEM)1;
- }
-
- /*
- * Reallocate contiguous space for items
- */
- if (infoPtr->uNumItems == (infoPtr->uNumPtrsAlloced-1) ) {
- TREEVIEW_ITEM *oldItems = infoPtr->items;
- INT *oldfreeList = infoPtr->freeList;
-
- infoPtr->uNumPtrsAlloced*=2;
- infoPtr->items = COMCTL32_Alloc (infoPtr->uNumPtrsAlloced*sizeof (TREEVIEW_ITEM));
- infoPtr->freeList= COMCTL32_Alloc ((1+(infoPtr->uNumPtrsAlloced>>5))*sizeof (INT));
-
- memcpy (&infoPtr->items[0], &oldItems[0],
- infoPtr->uNumPtrsAlloced/2 * sizeof(TREEVIEW_ITEM));
- memcpy (&infoPtr->freeList[0], &oldfreeList[0],
- (infoPtr->uNumPtrsAlloced>>6) * sizeof(INT));
-
- COMCTL32_Free (oldItems);
- COMCTL32_Free (oldfreeList);
- }
-
- /*
- * Reset infoPtr structure with new stat according to current TV picture
- */
- iItem=0;
- infoPtr->uNumItems++;
- if ((INT)infoPtr->uMaxHandle==(infoPtr->uNumItems-1)) {
- iItem=infoPtr->uNumItems;
- infoPtr->uMaxHandle = (HTREEITEM)((INT)infoPtr->uMaxHandle + 1);
- } else { /* check freelist */
- for (i=0; i<=infoPtr->uNumPtrsAlloced>>5; i++) {
- if (infoPtr->freeList[i]) {
- iItem=ffs (infoPtr->freeList[i])-1;
- tv_clear_bit(iItem,&infoPtr->freeList[i]);
- iItem+=i<<5;
- break;
- }
- }
- }
-
- if (TRACE_ON(treeview)) {
- for (i=0; i<=infoPtr->uNumPtrsAlloced>>5; i++)
- TRACE("%8x\n",infoPtr->freeList[i]);
- }
-
- if (!iItem) ERR("Argh -- can't find free item.\n");
-
- /*
- * Find the parent item of the new item
- */
- tvItem= & ptdi->DUMMYUNIONNAME.itemex;
- wineItem=& infoPtr->items[iItem];
-
- if ((ptdi->hParent==TVI_ROOT) || (ptdi->hParent==0)) {
- parentItem = NULL;
- wineItem->parent = 0;
- sibItem = &infoPtr->items [(INT)infoPtr->TopRootItem];
- listItems = infoPtr->uNumItems;
- }
- else {
- parentItem = &infoPtr->items[(INT)ptdi->hParent];
-
- /* Do the insertion here it if it's the only item of this parent */
- if (!parentItem->firstChild)
- parentItem->firstChild=(HTREEITEM)iItem;
-
- wineItem->parent = ptdi->hParent;
- sibItem = &infoPtr->items [(INT)parentItem->firstChild];
- listItems = parentItem->cChildren;
- parentItem->cChildren++;
- }
-
-
- /* NOTE: I am moving some setup of the wineItem object that was initialy
- * done at the end of the function since some of the values are
- * required by the Callback sorting
- */
-
- if (tvItem->mask & TVIF_TEXT)
- {
- /*
- * Setup the item text stuff here since it's required by the Sort method
- * when the insertion are ordered
- */
- if (tvItem->pszText!=LPSTR_TEXTCALLBACKA)
- {
- TRACE("(%p,%s)\n", &tvItem->pszText, tvItem->pszText);
- len = lstrlenA (tvItem->pszText)+1;
- wineItem->pszText= COMCTL32_Alloc (len+1);
- strcpy (wineItem->pszText, tvItem->pszText);
- wineItem->cchTextMax=len;
- }
- else
- {
- TRACE("LPSTR_TEXTCALLBACK\n");
- wineItem->pszText = LPSTR_TEXTCALLBACKA;
- wineItem->cchTextMax = 0;
- }
- }
-
- if (tvItem->mask & TVIF_PARAM)
- wineItem->lParam=tvItem->lParam;
-
-
- wineItem->upsibling=0; /* needed in case we're the first item in a list */
- wineItem->sibling=0;
- wineItem->firstChild=0;
- wineItem->hItem=(HTREEITEM)iItem;
-
- if (listItems!=0) {
- prevsib=NULL;
-
- switch ((DWORD) ptdi->hInsertAfter) {
- case (DWORD) TVI_FIRST:
- if (sibItem==wineItem) break;
- if (wineItem->parent) {
- wineItem->sibling=parentItem->firstChild;
- parentItem->firstChild=(HTREEITEM)iItem;
- } else {
- wineItem->sibling=infoPtr->TopRootItem;
- infoPtr->TopRootItem=(HTREEITEM)iItem;
- }
- sibItem->upsibling=(HTREEITEM)iItem;
- break;
-
- case (DWORD) TVI_SORT:
- if (sibItem==wineItem)
- /*
- * This item is the first child of the level and it
- * has already been inserted
- */
- break;
- else
- {
- TREEVIEW_ITEM *aChild;
-
-
- TREEVIEW_ITEM *previousChild = NULL;
- BOOL bItemInserted = FALSE;
-
- if (parentItem)
- aChild = &infoPtr->items[(INT)parentItem->firstChild];
- else
- aChild = &infoPtr->items[(INT)infoPtr->TopRootItem];
-
- /* lookup the text if using LPSTR_TEXTCALLBACKs */
- if (wineItem->pszText==LPSTR_TEXTCALLBACKA) {
- TREEVIEW_SendDispInfoNotify (hwnd, wineItem, TVN_GETDISPINFOA, TVIF_TEXT);
- }
-
- /* Iterate the parent children to see where we fit in */
- while ( aChild != NULL )
- {
- INT comp;
-
- /* lookup the text if using LPSTR_TEXTCALLBACKs */
- if (aChild->pszText==LPSTR_TEXTCALLBACKA) {
- TREEVIEW_SendDispInfoNotify (hwnd, aChild, TVN_GETDISPINFOA, TVIF_TEXT);
- }
-
- comp = strcmp(wineItem->pszText, aChild->pszText);
- if ( comp < 0 ) /* we are smaller than the current one */
- {
- TREEVIEW_InsertBefore(infoPtr, wineItem, aChild, parentItem);
- bItemInserted = TRUE;
- break;
- }
- else if ( comp > 0 ) /* we are bigger than the current one */
- {
- previousChild = aChild;
- aChild = (aChild->sibling == 0) /* This will help us to exit */
- ? NULL /* if there is no more sibling */
- : &infoPtr->items[(INT)aChild->sibling];
-
- /* Look at the next item */
- continue;
- }
- else if ( comp == 0 )
- {
- /*
- * An item with this name is already existing, therefore,
- * we add after the one we found
- */
- TREEVIEW_InsertAfter(infoPtr, wineItem, aChild, parentItem);
- bItemInserted = TRUE;
- break;
- }
- }
-
- /*
- * we reach the end of the child list and the item as not
- * yet been inserted, therefore, insert it after the last child.
- */
- if ( (! bItemInserted ) && (aChild == NULL) )
- TREEVIEW_InsertAfter(infoPtr, wineItem, previousChild, parentItem);
-
- break;
- }
-
-
- case (DWORD) TVI_LAST:
- if (sibItem==wineItem) break;
- while (sibItem->sibling) {
- prevsib=sibItem;
- sibItem=&infoPtr->items [(INT)sibItem->sibling];
- }
- sibItem->sibling=(HTREEITEM)iItem;
- wineItem->upsibling=sibItem->hItem;
- break;
- default:
- {
- TREEVIEW_ITEM *localsibItem = sibItem;
- while ((localsibItem->sibling) &&
- (localsibItem->hItem!=ptdi->hInsertAfter))
- {
- prevsib=localsibItem;
- localsibItem=&infoPtr->items [(INT)localsibItem->sibling];
- }
- if (localsibItem->hItem!=ptdi->hInsertAfter) {
- WARN("tried to insert item after nonexisting handle %d treating as TVI_LAST.\n",
- (INT) ptdi->hInsertAfter);
- /*
- * retry placing it last
- */
- if (sibItem==wineItem) break;
- while (sibItem->sibling) {
- prevsib=sibItem;
- sibItem=&infoPtr->items [(INT)sibItem->sibling];
- }
- sibItem->sibling=(HTREEITEM)iItem;
- wineItem->upsibling=sibItem->hItem;
- break;
- }
- prevsib=localsibItem;
- if (localsibItem->sibling) {
- localsibItem=&infoPtr->items [(INT)localsibItem->sibling];
- localsibItem->upsibling=(HTREEITEM)iItem;
- wineItem->sibling=localsibItem->hItem;
- }
- prevsib->sibling=(HTREEITEM)iItem;
- wineItem->upsibling=prevsib->hItem;
- break;
- }
- }
- }
-
-
-/* Fill in info structure */
-
- TRACE("new item %d; parent %d, mask %x\n", iItem,
- (INT)wineItem->parent,tvItem->mask);
-
- wineItem->mask=tvItem->mask;
- wineItem->iIntegral=1;
-
- if (tvItem->mask & TVIF_CHILDREN) {
- wineItem->cChildren=tvItem->cChildren;
- if (tvItem->cChildren==I_CHILDRENCALLBACK)
- FIXME(" I_CHILDRENCALLBACK not supported\n");
- }
-
- wineItem->expandBox.left = 0; /* Initialize the expandBox */
- wineItem->expandBox.top = 0;
- wineItem->expandBox.right = 0;
- wineItem->expandBox.bottom = 0;
-
- if (tvItem->mask & TVIF_IMAGE)
- wineItem->iImage=tvItem->iImage;
-
- /* If the application sets TVIF_INTEGRAL without
- supplying a TVITEMEX structure, it's toast */
-
- if (tvItem->mask & TVIF_INTEGRAL)
- wineItem->iIntegral=tvItem->iIntegral;
-
- if (tvItem->mask & TVIF_SELECTEDIMAGE)
- wineItem->iSelectedImage=tvItem->iSelectedImage;
-
- if (tvItem->mask & TVIF_STATE) {
- TRACE("item state: %x ->%x\n", wineItem->state, tvItem->state);
- TRACE("statemask: %x ->%x\n", wineItem->stateMask, tvItem->stateMask);
- wineItem->state=tvItem->state;
- wineItem->stateMask=tvItem->stateMask;
- }
-
- TREEVIEW_QueueRefresh (hwnd);
-
- return (LRESULT) iItem;
+static VOID
+TREEVIEW_SendExpanded(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *wineItem,
+ UINT action)
+{
+ TREEVIEW_SendTreeviewNotify(infoPtr, TVN_ITEMEXPANDEDA, action,
+ TVIF_HANDLE | TVIF_STATE | TVIF_PARAM
+ | TVIF_IMAGE | TVIF_SELECTEDIMAGE,
+ 0, wineItem);
}
-static LRESULT
-TREEVIEW_InsertItemW(HWND hwnd, WPARAM wParam, LPARAM lParam)
+/* This corresponds to TVM_EXPAND with TVE_COLLAPSE.
+ * bRemoveChildren corresponds to TVE_COLLAPSERESET. */
+static BOOL
+TREEVIEW_Collapse(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *wineItem,
+ BOOL bRemoveChildren, BOOL bUser)
{
- TVINSERTSTRUCTW *tvisW;
- TVINSERTSTRUCTA tvisA;
- LRESULT lRes;
+ UINT action = TVE_COLLAPSE | (bRemoveChildren ? TVE_COLLAPSERESET : 0);
+ BOOL bSetSelection, bSetFirstVisible;
- tvisW = (LPTVINSERTSTRUCTW)lParam;
+ TRACE("TVE_COLLAPSE %p %s\n", wineItem, TREEVIEW_ItemName(wineItem));
- tvisA.hParent = tvisW->hParent;
- tvisA.hInsertAfter = tvisW->hInsertAfter;
+ if (!(wineItem->state & TVIS_EXPANDED) || wineItem->firstChild == NULL)
+ return FALSE;
- tvisA.DUMMYUNIONNAME.item.mask = tvisW->DUMMYUNIONNAME.item.mask;
- tvisA.DUMMYUNIONNAME.item.hItem = tvisW->DUMMYUNIONNAME.item.hItem;
- tvisA.DUMMYUNIONNAME.item.state = tvisW->DUMMYUNIONNAME.item.state;
- tvisA.DUMMYUNIONNAME.item.stateMask = tvisW->DUMMYUNIONNAME.item.stateMask;
- tvisA.DUMMYUNIONNAME.item.cchTextMax = tvisW->DUMMYUNIONNAME.item.cchTextMax;
+ if (bUser)
+ TREEVIEW_SendExpanding(infoPtr, wineItem, action);
- if(tvisW->DUMMYUNIONNAME.item.pszText)
+ wineItem->state &= ~TVIS_EXPANDED;
+
+ if (bUser)
+ TREEVIEW_SendExpanded(infoPtr, wineItem, action);
+
+ bSetSelection = (infoPtr->selectedItem != NULL
+ && TREEVIEW_IsChildOf(wineItem, infoPtr->selectedItem));
+
+ bSetFirstVisible = (infoPtr->firstVisible != NULL
+ && TREEVIEW_IsChildOf(wineItem, infoPtr->firstVisible));
+
+ if (bRemoveChildren)
{
- if (tvisW->DUMMYUNIONNAME.item.pszText!=LPSTR_TEXTCALLBACKW)
- {
- int len = lstrlenW (tvisW->DUMMYUNIONNAME.item.pszText)+1;
- tvisA.DUMMYUNIONNAME.item.pszText = COMCTL32_Alloc (len);
- lstrcpyWtoA (tvisA.DUMMYUNIONNAME.item.pszText,
- tvisW->DUMMYUNIONNAME.item.pszText );
- }
- else
+ TRACE("TVE_COLLAPSERESET\n");
+ wineItem->state &= ~TVIS_EXPANDEDONCE;
+ TREEVIEW_RemoveAllChildren(infoPtr, wineItem);
+ }
+
+ if (wineItem->firstChild)
+ {
+ TREEVIEW_ITEM *item, *sibling;
+
+ sibling = TREEVIEW_GetNextListItem(infoPtr, wineItem);
+
+ for (item = wineItem->firstChild; item != sibling;
+ item = TREEVIEW_GetNextListItem(infoPtr, item))
{
- tvisA.DUMMYUNIONNAME.item.pszText = LPSTR_TEXTCALLBACKA;
- tvisA.DUMMYUNIONNAME.item.cchTextMax = 0;
+ item->visibleOrder = -1;
}
}
- tvisA.DUMMYUNIONNAME.item.iImage = tvisW->DUMMYUNIONNAME.item.iImage;
- tvisA.DUMMYUNIONNAME.item.iSelectedImage = tvisW->DUMMYUNIONNAME.item.iSelectedImage;
- tvisA.DUMMYUNIONNAME.item.cChildren = tvisW->DUMMYUNIONNAME.item.cChildren;
- tvisA.DUMMYUNIONNAME.item.lParam = tvisW->DUMMYUNIONNAME.item.lParam;
+ TREEVIEW_RecalculateVisibleOrder(infoPtr, wineItem);
- lRes = TREEVIEW_InsertItemA(hwnd,wParam,(LPARAM)&tvisA);
+ TREEVIEW_SetFirstVisible(infoPtr, bSetFirstVisible ? wineItem
+ : infoPtr->firstVisible, TRUE);
- if (tvisA.DUMMYUNIONNAME.item.pszText!=LPSTR_TEXTCALLBACKA)
+ if (bSetSelection)
{
- COMCTL32_Free(tvisA.DUMMYUNIONNAME.item.pszText);
+ /* Don't call DoSelectItem, it sends notifications. */
+ if (TREEVIEW_ValidItem(infoPtr, infoPtr->selectedItem))
+ infoPtr->selectedItem->state &= ~TVIS_SELECTED;
+ wineItem->state |= TVIS_SELECTED;
+ infoPtr->selectedItem = wineItem;
+
+ TREEVIEW_EnsureVisible(infoPtr, wineItem, FALSE);
}
- return lRes;
-
-}
-
-
-static LRESULT
-TREEVIEW_DeleteItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
-{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
- INT iItem;
- TREEVIEW_ITEM *wineItem;
-
- TRACE("item = %08lx\n", lParam);
-
- if (lParam == (INT)TVI_ROOT) {
- TREEVIEW_RemoveTree (hwnd);
- } else {
- iItem= (INT) lParam;
- wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)iItem);
- if (!wineItem) return FALSE;
-
- if (wineItem->pszText==LPSTR_TEXTCALLBACKA)
- TRACE("LPSTR_TEXTCALLBACK\n");
- else
- TRACE("%s\n",wineItem->pszText);
- TREEVIEW_RemoveItem (hwnd, wineItem);
- }
-
- TREEVIEW_QueueRefresh (hwnd);
-
- return TRUE;
-}
-
-
-
-static LRESULT
-TREEVIEW_GetIndent (HWND hwnd)
-{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
-
- TRACE("\n");
- return infoPtr->uIndent;
-}
-
-static LRESULT
-TREEVIEW_SetIndent (HWND hwnd, WPARAM wParam)
-{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
- INT newIndent;
-
- TRACE("\n");
- newIndent=(INT) wParam;
- if (newIndent < MINIMUM_INDENT) newIndent=MINIMUM_INDENT;
- infoPtr->uIndent=newIndent;
-
- return 0;
-}
-
-static LRESULT
-TREEVIEW_GetToolTips (HWND hwnd)
-
-{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
-
- TRACE("\n");
- return infoPtr->hwndToolTip;
-}
-
-
-static LRESULT
-TREEVIEW_SetToolTips (HWND hwnd, WPARAM wParam)
-
-{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
- HWND prevToolTip;
-
- TRACE("\n");
- prevToolTip=infoPtr->hwndToolTip;
- infoPtr->hwndToolTip= (HWND) wParam;
-
- return prevToolTip;
-}
-
-
-static LRESULT CALLBACK
-TREEVIEW_GetEditControl (HWND hwnd)
-
-{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
-
- return infoPtr->hwndEdit;
-}
-
-LRESULT CALLBACK
-TREEVIEW_Edit_SubclassProc (HWND hwnd, UINT uMsg, WPARAM wParam,
- LPARAM lParam)
-{
- switch (uMsg)
- {
- case WM_ERASEBKGND:
- {
- RECT rc;
- HDC hdc = (HDC) wParam;
- GetClientRect (hwnd, &rc);
- Rectangle (hdc, rc.left, rc.top, rc.right, rc.bottom);
- return -1;
- }
-
- case WM_GETDLGCODE:
- {
- return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
- }
-
- case WM_KEYDOWN:
- if (wParam == VK_ESCAPE)
-{
- TREEVIEW_EndEditLabelNow(GetParent(hwnd), (WPARAM)TRUE, 0);
- return 1;
-}
- else if (wParam == VK_RETURN)
- TREEVIEW_EndEditLabelNow(GetParent(hwnd), (WPARAM)FALSE, 0);
- break;
-
-
-
- default:
- {
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(GetParent(hwnd));
- if (infoPtr!=NULL)
- return CallWindowProcA (infoPtr->wpEditOrig, hwnd, uMsg, wParam, lParam);
- else
- break;
-
- }
- }
-
- return 0;
-}
-
-
-/* should handle edit control messages here */
-
-static LRESULT
-TREEVIEW_Command (HWND hwnd, WPARAM wParam, LPARAM lParam)
-
-{
- TRACE("%x %ld\n",wParam, lParam);
-
- switch (HIWORD(wParam))
- {
- case EN_UPDATE:
- {
- /*
- * Adjust the edit window size
- */
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
- TREEVIEW_ITEM *editItem = TREEVIEW_ValidItem(infoPtr, infoPtr->editItem);
- INT iLength = GetWindowTextLengthA(infoPtr->hwndEdit);
- HDC hdc = GetDC(infoPtr->hwndEdit);
- TEXTMETRICA tm;
-
- if ( GetTextMetricsA(hdc, &tm) )
- {
- LONG newWidth = (iLength * tm.tmAveCharWidth) + 15;
-
- SetWindowPos (
- infoPtr->hwndEdit,
- HWND_TOP,
- editItem->text.left - 2,
- editItem->text.top - 1,
- newWidth,
- editItem->text.bottom - editItem->text.top + 3,
- SWP_DRAWFRAME );
- }
- ReleaseDC(hwnd, hdc);
-
- break;
- }
-
- case EN_KILLFOCUS:
-/* TREEVIEW_EndEditLabelNow(hwnd, (WPARAM)FALSE, 0);
-*/
- break;
-
- default:
- return SendMessageA (GetParent (hwnd), WM_COMMAND, wParam, lParam);
- }
-
- return 0;
-}
-
-static LRESULT
-TREEVIEW_Size (HWND hwnd, WPARAM wParam, LPARAM lParam)
-
-{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
-
- if (infoPtr->bAutoSize)
- {
- infoPtr->bAutoSize = FALSE;
- return 0;
- }
- infoPtr->bAutoSize = TRUE;
-
- if (wParam == SIZE_RESTORED)
- {
- infoPtr->uTotalWidth = LOWORD (lParam);
- infoPtr->uTotalHeight = HIWORD (lParam);
- } else {
- FIXME("WM_SIZE flag %x %lx not handled\n", wParam, lParam);
- }
-
- TREEVIEW_QueueRefresh (hwnd);
- return 0;
-}
-
-
-
-static LRESULT
-TREEVIEW_StyleChanged (HWND hwnd, WPARAM wParam, LPARAM lParam)
-{
- TRACE("(%x %lx)\n",wParam,lParam);
-
- InvalidateRect(hwnd, NULL, FALSE);
-
- return 0;
-}
-
-static LRESULT
-TREEVIEW_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
-{
- TREEVIEW_INFO *infoPtr;
- DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
- LOGFONTA logFont;
- TEXTMETRICA tm;
- HDC hdc;
-
- TRACE("wnd %x, style %lx\n",hwnd,dwStyle);
- /* allocate memory for info structure */
- infoPtr = (TREEVIEW_INFO *) COMCTL32_Alloc (sizeof(TREEVIEW_INFO));
-
- SetWindowLongA( hwnd, 0, (DWORD)infoPtr);
-
- if (infoPtr == NULL) {
- ERR("could not allocate info memory!\n");
- return 0;
- }
-
- if ((TREEVIEW_INFO*) GetWindowLongA( hwnd, 0) != infoPtr) {
- ERR("pointer assignment error!\n");
- return 0;
- }
-
- hdc=GetDC (hwnd);
-
- /* set default settings */
- infoPtr->uInternalStatus=0;
- infoPtr->uNumItems=0;
- infoPtr->clrBk = GetSysColor (COLOR_WINDOW);
- infoPtr->clrText = GetSysColor (COLOR_WINDOWTEXT);
- infoPtr->clrLine = GetSysColor (COLOR_WINDOWTEXT);
- infoPtr->clrInsertMark = GetSysColor (COLOR_BTNTEXT);
- infoPtr->cy = 0;
- infoPtr->cx = 0;
- infoPtr->uIndent = 15;
- infoPtr->himlNormal = NULL;
- infoPtr->himlState = NULL;
- infoPtr->uItemHeight = -1;
- GetTextMetricsA (hdc, &tm);
- infoPtr->hFont = GetStockObject (DEFAULT_GUI_FONT);
- GetObjectA (infoPtr->hFont, sizeof (LOGFONTA), &logFont);
- logFont.lfWeight=FW_BOLD;
- infoPtr->hBoldFont = CreateFontIndirectA (&logFont);
-
- infoPtr->items = NULL;
- infoPtr->selectedItem=0;
- infoPtr->clrText=-1; /* use system color */
- infoPtr->dropItem=0;
- infoPtr->insertMarkItem=0;
- infoPtr->insertBeforeorAfter=0;
- infoPtr->pCallBackSort=NULL;
- infoPtr->uScrollTime = 300; /* milliseconds */
- infoPtr->wpEditOrig = NULL; /* we haven't subclassed anything yet */
-
- infoPtr->hwndToolTip=0;
- if (!(dwStyle & TVS_NOTOOLTIPS)) { /* Create tooltip control */
- TTTOOLINFOA ti;
-
- infoPtr->hwndToolTip =
- CreateWindowExA (0, TOOLTIPS_CLASSA, NULL, 0,
- CW_USEDEFAULT, CW_USEDEFAULT,
- CW_USEDEFAULT, CW_USEDEFAULT,
- hwnd, 0, 0, 0);
-
- /* Send NM_TOOLTIPSCREATED notification */
- if (infoPtr->hwndToolTip) {
- NMTOOLTIPSCREATED nmttc;
-
- nmttc.hdr.hwndFrom = hwnd;
- nmttc.hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
- nmttc.hdr.code = NM_TOOLTIPSCREATED;
- nmttc.hwndToolTips = infoPtr->hwndToolTip;
-
- SendMessageA (GetParent (hwnd), WM_NOTIFY,
- (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmttc);
- }
-
- ZeroMemory (&ti, sizeof(TTTOOLINFOA));
- ti.cbSize = sizeof(TTTOOLINFOA);
- ti.uFlags = TTF_IDISHWND | TTF_TRACK | TTF_TRANSPARENT ;
- ti.hwnd = hwnd;
- ti.uId = 0;
- ti.lpszText = "Test"; /* LPSTR_TEXTCALLBACK; */
- SetRectEmpty (&ti.rect);
-
- SendMessageA (infoPtr->hwndToolTip, TTM_ADDTOOLA, 0, (LPARAM)&ti);
- }
-
- infoPtr->hwndEdit = CreateWindowExA (
- WS_EX_LEFT,
- "EDIT",
- 0,
- WS_CHILD | WS_BORDER | ES_AUTOHSCROLL |
- ES_WANTRETURN | ES_LEFT,
- 0, 0, 0, 0,
- hwnd,
- 0,0,0); /* FIXME: (HMENU)IDTVEDIT,pcs->hInstance,0);*/
-
- SendMessageA ( infoPtr->hwndEdit, WM_SETFONT, infoPtr->hFont, FALSE);
- infoPtr->wpEditOrig = (WNDPROC)SetWindowLongA (
- infoPtr->hwndEdit,
- GWL_WNDPROC,
- (LONG) TREEVIEW_Edit_SubclassProc);
-
- if (dwStyle & TVS_CHECKBOXES) {
- HBITMAP hbmLoad;
- int nIndex;
-
- infoPtr->himlState =
- ImageList_Create (16, 16,ILC_COLOR|ILC_MASK, 15, 1);
-
- hbmLoad = LoadBitmapA (COMCTL32_hModule, MAKEINTRESOURCEA(IDT_CHECK));
- TRACE ("%x\n",hbmLoad);
- nIndex = ImageList_AddMasked (infoPtr->himlState, hbmLoad, CLR_DEFAULT);
- TRACE ("%d\n",nIndex);
- DeleteObject (hbmLoad);
- }
- ReleaseDC (hwnd, hdc);
- return 0;
-}
-
-
-
-static LRESULT
-TREEVIEW_Destroy (HWND hwnd)
-{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
-
- TRACE("\n");
- TREEVIEW_RemoveTree (hwnd);
- SetWindowLongA (hwnd, 0, (DWORD)NULL);
-
- if (infoPtr->Timer & TV_REFRESH_TIMER_SET)
- KillTimer (hwnd, TV_REFRESH_TIMER);
- if (infoPtr->hwndToolTip)
- DestroyWindow (infoPtr->hwndToolTip);
-
- COMCTL32_Free (infoPtr);
- return 0;
-}
-
-
-static LRESULT
-TREEVIEW_Paint (HWND hwnd, WPARAM wParam, LPARAM lParam)
-{
- HDC hdc;
- PAINTSTRUCT ps;
-
- TRACE("\n");
- hdc = wParam==0 ? BeginPaint (hwnd, &ps) : (HDC)wParam;
- TREEVIEW_Refresh (hwnd, hdc);
- if(!wParam) EndPaint (hwnd, &ps);
- TRACE("done\n");
-
- return 0;
-}
-
-static LRESULT
-TREEVIEW_SetFocus (HWND hwnd, WPARAM wParam, LPARAM lParam)
-{
- TREEVIEW_SendSimpleNotify (hwnd, NM_SETFOCUS);
- InvalidateRect(hwnd, NULL, FALSE);
- return 0;
-}
-
-static LRESULT
-TREEVIEW_KillFocus (HWND hwnd, WPARAM wParam, LPARAM lParam)
-{
- TREEVIEW_SendSimpleNotify (hwnd, NM_KILLFOCUS);
- InvalidateRect(hwnd, NULL, FALSE);
- return 0;
-}
-
-static LRESULT
-TREEVIEW_EraseBackground (HWND hwnd, WPARAM wParam, LPARAM lParam)
-{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
- HBRUSH hBrush = CreateSolidBrush (infoPtr->clrBk);
- RECT rect;
+ TREEVIEW_UpdateScrollBars(infoPtr);
+ TREEVIEW_QueueRefresh(infoPtr);
- TRACE("\n");
- GetClientRect (hwnd, &rect);
- FillRect ((HDC)wParam, &rect, hBrush);
- DeleteObject (hBrush);
return TRUE;
}
-
-
-
-
-
-/* Notifications */
-
-
-
-
-
static BOOL
-TREEVIEW_SendSimpleNotify (HWND hwnd, UINT code)
+TREEVIEW_Expand(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *wineItem,
+ BOOL bExpandPartial, BOOL bUser)
{
- NMHDR nmhdr;
+ TRACE("\n");
- TRACE("%x\n",code);
- nmhdr.hwndFrom = hwnd;
- nmhdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
- nmhdr.code = code;
+ if (!TREEVIEW_HasChildren(infoPtr, wineItem)
+ || wineItem->state & TVIS_EXPANDED)
+ return FALSE;
- return (BOOL) SendMessageA (GetParent (hwnd), WM_NOTIFY,
- (WPARAM)nmhdr.idFrom, (LPARAM)&nmhdr);
-}
+ TRACE("TVE_EXPAND %p %s\n", wineItem, TREEVIEW_ItemName(wineItem));
-
-
-static BOOL
-TREEVIEW_SendTreeviewNotify (HWND hwnd, UINT code, UINT action,
- HTREEITEM oldItem, HTREEITEM newItem)
-
-{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
- NMTREEVIEWA nmhdr;
- TREEVIEW_ITEM *wineItem;
-
- TRACE("code:%x action:%x olditem:%x newitem:%x\n",
- code,action,(INT)oldItem,(INT)newItem);
- nmhdr.hdr.hwndFrom = hwnd;
- nmhdr.hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
- nmhdr.hdr.code = code;
- nmhdr.action = action;
- if (oldItem) {
- wineItem=& infoPtr->items[(INT)oldItem];
- nmhdr.itemOld.mask = wineItem->mask;
- nmhdr.itemOld.hItem = wineItem->hItem;
- nmhdr.itemOld.state = wineItem->state;
- nmhdr.itemOld.stateMask = wineItem->stateMask;
- nmhdr.itemOld.iImage = wineItem->iImage;
- nmhdr.itemOld.pszText = wineItem->pszText;
- nmhdr.itemOld.cchTextMax= wineItem->cchTextMax;
- nmhdr.itemOld.iImage = wineItem->iImage;
- nmhdr.itemOld.iSelectedImage = wineItem->iSelectedImage;
- nmhdr.itemOld.cChildren = wineItem->cChildren;
- nmhdr.itemOld.lParam = wineItem->lParam;
- }
-
- if (newItem) {
- wineItem=& infoPtr->items[(INT)newItem];
- nmhdr.itemNew.mask = wineItem->mask;
- nmhdr.itemNew.hItem = wineItem->hItem;
- nmhdr.itemNew.state = wineItem->state;
- nmhdr.itemNew.stateMask = wineItem->stateMask;
- nmhdr.itemNew.iImage = wineItem->iImage;
- nmhdr.itemNew.pszText = wineItem->pszText;
- nmhdr.itemNew.cchTextMax= wineItem->cchTextMax;
- nmhdr.itemNew.iImage = wineItem->iImage;
- nmhdr.itemNew.iSelectedImage = wineItem->iSelectedImage;
- nmhdr.itemNew.cChildren = wineItem->cChildren;
- nmhdr.itemNew.lParam = wineItem->lParam;
- }
-
- nmhdr.ptDrag.x = 0;
- nmhdr.ptDrag.y = 0;
-
- return (BOOL)SendMessageA (GetParent (hwnd), WM_NOTIFY,
- (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmhdr);
-
-}
-
-static BOOL
-TREEVIEW_SendTreeviewDnDNotify (HWND hwnd, UINT code, HTREEITEM dragItem,
- POINT pt)
-{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
- NMTREEVIEWA nmhdr;
- TREEVIEW_ITEM *wineItem;
-
- TRACE("code:%x dragitem:%x\n", code,(INT)dragItem);
-
- nmhdr.hdr.hwndFrom = hwnd;
- nmhdr.hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
- nmhdr.hdr.code = code;
- nmhdr.action = 0;
- wineItem=& infoPtr->items[(INT)dragItem];
- nmhdr.itemNew.mask = wineItem->mask;
- nmhdr.itemNew.hItem = wineItem->hItem;
- nmhdr.itemNew.state = wineItem->state;
- nmhdr.itemNew.lParam = wineItem->lParam;
-
- nmhdr.ptDrag.x = pt.x;
- nmhdr.ptDrag.y = pt.y;
-
- return (BOOL)SendMessageA (GetParent (hwnd), WM_NOTIFY,
- (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmhdr);
-
-}
-
-
-
-static BOOL
-TREEVIEW_SendDispInfoNotify (HWND hwnd, TREEVIEW_ITEM *wineItem,
- UINT code, UINT what)
-{
- NMTVDISPINFOA tvdi;
- BOOL retval;
- char *buf;
-
- TRACE("item %d, action %x, state %d\n",
- (INT)wineItem->hItem,
- what,
- (INT)wineItem->state);
-
- tvdi.hdr.hwndFrom = hwnd;
- tvdi.hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
- tvdi.hdr.code = code;
- tvdi.item.mask = what;
- tvdi.item.hItem = wineItem->hItem;
- tvdi.item.state = wineItem->state;
- tvdi.item.lParam = wineItem->lParam;
- tvdi.item.pszText = COMCTL32_Alloc (128*sizeof(char));
- tvdi.item.cchTextMax = 128;
- buf = tvdi.item.pszText;
-
- retval=(BOOL)SendMessageA (
- GetParent(hwnd),
- WM_NOTIFY,
- (WPARAM)tvdi.hdr.idFrom,
- (LPARAM)&tvdi);
-
- if (what & TVIF_TEXT) {
- wineItem->pszText = tvdi.item.pszText;
- if (buf==tvdi.item.pszText) {
- wineItem->cchTextMax = 128;
- } else {
- TRACE("user-supplied buffer\n");
- COMCTL32_Free (buf);
- wineItem->cchTextMax = 0;
- }
+ if (bUser || !(wineItem->state & TVIS_EXPANDEDONCE))
+ {
+ if (!TREEVIEW_SendExpanding(infoPtr, wineItem, TVE_EXPAND))
+ {
+ TRACE(" TVN_ITEMEXPANDING returned TRUE, exiting...\n");
+ return FALSE;
}
- if (what & TVIF_SELECTEDIMAGE)
- wineItem->iSelectedImage = tvdi.item.iSelectedImage;
- if (what & TVIF_IMAGE)
- wineItem->iImage = tvdi.item.iImage;
- if (what & TVIF_CHILDREN)
- wineItem->cChildren = tvdi.item.cChildren;
- return retval;
+ wineItem->state |= TVIS_EXPANDED;
+ TREEVIEW_SendExpanded(infoPtr, wineItem, TVE_EXPAND);
+ wineItem->state |= TVIS_EXPANDEDONCE;
+ }
+ else
+ {
+ /* this item has already been expanded */
+ wineItem->state |= TVIS_EXPANDED;
+ }
+
+ if (bExpandPartial)
+ FIXME("TVE_EXPANDPARTIAL not implemented\n");
+
+ TREEVIEW_RecalculateVisibleOrder(infoPtr, wineItem);
+ TREEVIEW_UpdateSubTree(infoPtr, wineItem);
+ TREEVIEW_UpdateScrollBars(infoPtr);
+
+ /* Scroll up so that as many children as possible are visible.
+ * This looses when expanding causes an HScroll bar to appear, but we
+ * don't know that yet, so the last item is obscured. */
+ if (wineItem->firstChild != NULL)
+ {
+ int nChildren = wineItem->lastChild->visibleOrder
+ - wineItem->firstChild->visibleOrder + 1;
+
+ int visible_pos = wineItem->visibleOrder
+ - infoPtr->firstVisible->visibleOrder;
+
+ int rows_below = TREEVIEW_GetVisibleCount(infoPtr) - visible_pos - 1;
+
+ if (visible_pos > 0 && nChildren > rows_below)
+ {
+ int scroll = nChildren - rows_below;
+
+ if (scroll > visible_pos)
+ scroll = visible_pos;
+
+ if (scroll > 0)
+ {
+ TREEVIEW_ITEM *newFirstVisible
+ = TREEVIEW_GetListItem(infoPtr, infoPtr->firstVisible,
+ scroll);
+
+
+ TREEVIEW_SetFirstVisible(infoPtr, newFirstVisible, TRUE);
+ }
+ }
+ }
+
+ TREEVIEW_QueueRefresh(infoPtr);
+
+ return TRUE;
}
-
-
static BOOL
-TREEVIEW_SendCustomDrawNotify (HWND hwnd, DWORD dwDrawStage, HDC hdc,
- RECT rc)
+TREEVIEW_Toggle(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *wineItem, BOOL bUser)
{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
- NMTVCUSTOMDRAW nmcdhdr;
- LPNMCUSTOMDRAW nmcd;
+ TRACE("\n");
- TRACE("drawstage:%lx hdc:%x\n", dwDrawStage, hdc);
-
- nmcd= & nmcdhdr.nmcd;
- nmcd->hdr.hwndFrom = hwnd;
- nmcd->hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
- nmcd->hdr.code = NM_CUSTOMDRAW;
- nmcd->dwDrawStage= dwDrawStage;
- nmcd->hdc = hdc;
- nmcd->rc.left = rc.left;
- nmcd->rc.right = rc.right;
- nmcd->rc.bottom = rc.bottom;
- nmcd->rc.top = rc.top;
- nmcd->dwItemSpec = 0;
- nmcd->uItemState = 0;
- nmcd->lItemlParam= 0;
- nmcdhdr.clrText = infoPtr->clrText;
- nmcdhdr.clrTextBk= infoPtr->clrBk;
- nmcdhdr.iLevel = 0;
-
- return (BOOL)SendMessageA (GetParent (hwnd), WM_NOTIFY,
- (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmcdhdr);
-
+ if (wineItem->state & TVIS_EXPANDED)
+ return TREEVIEW_Collapse(infoPtr, wineItem, FALSE, bUser);
+ else
+ return TREEVIEW_Expand(infoPtr, wineItem, FALSE, bUser);
}
-
-
-/* FIXME: need to find out when the flags in uItemState need to be set */
-
-static BOOL
-TREEVIEW_SendCustomDrawItemNotify (HWND hwnd, HDC hdc,
- TREEVIEW_ITEM *wineItem, UINT uItemDrawState)
+static VOID
+TREEVIEW_ExpandAll(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item)
{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
- NMTVCUSTOMDRAW nmcdhdr;
- LPNMCUSTOMDRAW nmcd;
- DWORD dwDrawStage,dwItemSpec;
- UINT uItemState;
- INT retval;
-
- dwDrawStage=CDDS_ITEM | uItemDrawState;
- dwItemSpec=(DWORD)wineItem->hItem;
- uItemState=0;
- if (wineItem->hItem==infoPtr->selectedItem) uItemState|=CDIS_SELECTED;
- if (wineItem->hItem==infoPtr->focusItem) uItemState|=CDIS_FOCUS;
- if (wineItem->hItem==infoPtr->hotItem) uItemState|=CDIS_HOT;
+ TREEVIEW_Expand(infoPtr, item, FALSE, TRUE);
- nmcd= & nmcdhdr.nmcd;
- nmcd->hdr.hwndFrom = hwnd;
- nmcd->hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
- nmcd->hdr.code = NM_CUSTOMDRAW;
- nmcd->dwDrawStage= dwDrawStage;
- nmcd->hdc = hdc;
- nmcd->rc.left = wineItem->rect.left;
- nmcd->rc.right = wineItem->rect.right;
- nmcd->rc.bottom = wineItem->rect.bottom;
- nmcd->rc.top = wineItem->rect.top;
- nmcd->dwItemSpec = dwItemSpec;
- nmcd->uItemState = uItemState;
- nmcd->lItemlParam= wineItem->lParam;
- nmcdhdr.clrText = infoPtr->clrText;
- nmcdhdr.clrTextBk= infoPtr->clrBk;
- nmcdhdr.iLevel = wineItem->iLevel;
-
- TRACE("drawstage:%lx hdc:%x item:%lx, itemstate:%x, lItemlParam:%lx\n",
- nmcd->dwDrawStage, nmcd->hdc, nmcd->dwItemSpec,
- nmcd->uItemState, nmcd->lItemlParam);
-
- retval=SendMessageA (GetParent (hwnd), WM_NOTIFY,
- (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmcdhdr);
-
- infoPtr->clrText=nmcdhdr.clrText;
- infoPtr->clrBk =nmcdhdr.clrTextBk;
- return (BOOL) retval;
+ for (item = item->firstChild; item != NULL; item = item->nextSibling)
+ {
+ if (TREEVIEW_HasChildren(infoPtr, item))
+ TREEVIEW_ExpandAll(infoPtr, item);
+ }
}
-
-
/* Note:If the specified item is the child of a collapsed parent item,
the parent's list of child items is (recursively) expanded to reveal the
specified item. This is mentioned for TREEVIEW_SelectItem; don't
@@ -2924,716 +3036,832 @@
*/
static LRESULT
-TREEVIEW_Expand (HWND hwnd, WPARAM wParam, LPARAM lParam)
+TREEVIEW_ExpandMsg(TREEVIEW_INFO *infoPtr, UINT flag, HTREEITEM wineItem)
{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
- TREEVIEW_ITEM *wineItem;
- UINT flag;
- INT expand;
-
- flag = (UINT) wParam;
- expand = (INT) lParam;
+ if (!TREEVIEW_ValidItem(infoPtr, wineItem))
+ return 0;
- wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)expand);
+ TRACE("For (%s) item:%d, flags %x, state:%d\n",
+ TREEVIEW_ItemName(wineItem), flag,
+ TREEVIEW_GetItemIndex(infoPtr, wineItem), wineItem->state);
- if (!wineItem)
- return 0;
- if (!wineItem->cChildren)
- return 0;
+ switch (flag & TVE_TOGGLE)
+ {
+ case TVE_COLLAPSE:
+ return TREEVIEW_Collapse(infoPtr, wineItem, flag & TVE_COLLAPSERESET,
+ FALSE);
- if (wineItem->pszText==LPSTR_TEXTCALLBACKA)
- TRACE ("For item %d, flags %d, state %d\n",
- expand, flag, wineItem->state);
- else
- TRACE("For (%s) item:%d, flags %x, state:%d\n",
- wineItem->pszText, flag, expand, wineItem->state);
+ case TVE_EXPAND:
+ return TREEVIEW_Expand(infoPtr, wineItem, flag & TVE_EXPANDPARTIAL,
+ FALSE);
- if (wineItem->cChildren==I_CHILDRENCALLBACK) {
- FIXME("we don't handle I_CHILDRENCALLBACK yet\n");
- return 0;
- }
+ case TVE_TOGGLE:
+ return TREEVIEW_Toggle(infoPtr, wineItem, TRUE);
- if (flag == TVE_TOGGLE) { /* FIXME: check exact behaviour here */
- flag &= ~TVE_TOGGLE; /* ie: bitwise ops or 'case' ops */
- if (wineItem->state & TVIS_EXPANDED)
- flag |= TVE_COLLAPSE;
- else
- flag |= TVE_EXPAND;
- }
+ default:
+ return 0;
+ }
- switch (flag)
- {
- case TVE_COLLAPSERESET:
- TRACE(" case TVE_COLLAPSERESET\n");
- if (!wineItem->state & TVIS_EXPANDED)
- return 0;
-
- wineItem->state &= ~(TVIS_EXPANDEDONCE | TVIS_EXPANDED);
- TREEVIEW_RemoveAllChildren (hwnd, wineItem);
- break;
-
- case TVE_COLLAPSE:
- TRACE(" case TVE_COLLAPSE\n");
- if (!wineItem->state & TVIS_EXPANDED)
- return 0;
-
- wineItem->state &= ~TVIS_EXPANDED;
- break;
-
- case TVE_EXPAND:
- TRACE(" case TVE_EXPAND\n");
- if (wineItem->state & TVIS_EXPANDED)
- return 0;
-
- TRACE(" is not expanded...\n");
-
- if (!(wineItem->state & TVIS_EXPANDEDONCE))
- {
- TRACE(" and has never been expanded...\n");
- wineItem->state |= TVIS_EXPANDED;
-
- /* this item has never been expanded */
- if (TREEVIEW_SendTreeviewNotify (
- hwnd,
- TVN_ITEMEXPANDINGA,
- TVE_EXPAND,
- 0,
- (HTREEITEM)expand))
- {
- TRACE(" TVN_ITEMEXPANDINGA returned TRUE, exiting...\n");
- return FALSE;
- }
-
- /* FIXME
- * Since the TVN_ITEMEXPANDINGA message may has caused the parent to
- * insert new items which in turn may have cause items placeholder
- * reallocation, I reassign the current item pointer so we have
- * something valid to work with...
- * However, this should not be necessary,
- * investigation required in TREEVIEW_InsertItemA
- */
- wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)expand);
- if (! wineItem)
- {
- ERR(
- "Catastrophic situation, cannot retrieve item #%d\n",
- expand);
- return FALSE;
- }
-
- wineItem->state |= TVIS_EXPANDEDONCE;
- TRACE(" TVN_ITEMEXPANDINGA sent...\n");
-
- TREEVIEW_SendTreeviewNotify (
- hwnd,
- TVN_ITEMEXPANDEDA,
- TVE_EXPAND,
- 0,
- (HTREEITEM)expand);
-
- TRACE(" TVN_ITEMEXPANDEDA sent...\n");
-
- }
- else
- {
- /* this item has already been expanded */
- wineItem->state |= TVIS_EXPANDED;
- }
- break;
-
- case TVE_EXPANDPARTIAL:
- TRACE(" case TVE_EXPANDPARTIAL\n");
- FIXME("TVE_EXPANDPARTIAL not implemented\n");
- wineItem->state ^=TVIS_EXPANDED;
- wineItem->state |=TVIS_EXPANDEDONCE;
- break;
- }
-
- TRACE("Exiting, Item %d state is now %d...\n",
- expand,
- wineItem->state);
-
- TREEVIEW_QueueRefresh (hwnd);
- return TRUE;
+#if 0
+ TRACE("Exiting, Item %p state is now %d...\n", wineItem, wineItem->state);
+#endif
}
-
+/* Hit-Testing **********************************************************/
static TREEVIEW_ITEM *
-TREEVIEW_HitTestPoint (HWND hwnd, POINT pt)
+TREEVIEW_HitTestPoint(TREEVIEW_INFO *infoPtr, POINT pt)
{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
- TREEVIEW_ITEM *wineItem;
- RECT rect;
+ TREEVIEW_ITEM *wineItem;
+ LONG row;
- GetClientRect (hwnd, &rect);
-
- if (!infoPtr->firstVisible) return NULL;
-
- wineItem=&infoPtr->items [(INT)infoPtr->firstVisible];
-
- while ((wineItem!=NULL) && (pt.y > wineItem->rect.bottom))
- wineItem=TREEVIEW_GetNextListItem (infoPtr,wineItem);
-
- if (!wineItem)
+ if (!infoPtr->firstVisible)
return NULL;
- return wineItem;
-}
+ row = pt.y / infoPtr->uItemHeight + infoPtr->firstVisible->visibleOrder;
-
-
-
-static LRESULT
-TREEVIEW_HitTest (HWND hwnd, LPARAM lParam)
-{
- LPTVHITTESTINFO lpht=(LPTVHITTESTINFO) lParam;
- TREEVIEW_ITEM *wineItem;
- RECT rect;
- UINT status,x,y;
-
- GetClientRect (hwnd, &rect);
- status=0;
- x=lpht->pt.x;
- y=lpht->pt.y;
- if (x < rect.left) status|=TVHT_TOLEFT;
- if (x > rect.right) status|=TVHT_TORIGHT;
- if (y < rect.top ) status|=TVHT_ABOVE;
- if (y > rect.bottom) status|=TVHT_BELOW;
-
- if (status) {
- lpht->flags=status;
- return 0;
- }
-
- wineItem=TREEVIEW_HitTestPoint (hwnd, lpht->pt);
- if (!wineItem) {
- lpht->flags=TVHT_NOWHERE;
- return 0;
- }
-
- lpht->flags=0;
-
- if (x < wineItem->expandBox.left) {
- lpht->flags |= TVHT_ONITEMINDENT;
- goto done;
- }
- if ( PtInRect ( &wineItem->expandBox, lpht->pt)) {
- lpht->flags |= TVHT_ONITEMBUTTON;
- goto done;
- }
- if ( PtInRect ( &wineItem->bitmap, lpht->pt)) {
- lpht->flags |= TVHT_ONITEMICON;
- goto done;
- }
- if ( PtInRect ( &wineItem->statebitmap, lpht->pt)) {
- lpht->flags |= TVHT_ONITEMSTATEICON;
- goto done;
- }
- if ( PtInRect ( &wineItem->text, lpht->pt)) {
- lpht->flags |= TVHT_ONITEMLABEL;
- goto done;
- }
-
- lpht->flags|=TVHT_ONITEMRIGHT;
-
-
-done:
- lpht->hItem=wineItem->hItem;
- TRACE ("(%ld,%ld):result %x\n",lpht->pt.x,lpht->pt.y,lpht->flags);
-
- return (LRESULT) wineItem->hItem;
-}
-
-static LRESULT
-TREEVIEW_EditLabelA (HWND hwnd, WPARAM wParam, LPARAM lParam)
-{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
- TREEVIEW_ITEM *wineItem;
-
- /*
- * If the style allow editing...
- */
- if ( GetWindowLongA( hwnd, GWL_STYLE) & TVS_EDITLABELS )
- {
-
- if ( infoPtr->editItem == 0 ) /* If we are not curently editing */
+ for (wineItem = infoPtr->firstVisible; wineItem != NULL;
+ wineItem = TREEVIEW_GetNextListItem(infoPtr, wineItem))
{
- wineItem = TREEVIEW_ValidItem(infoPtr,(HTREEITEM) lParam);
- if ( wineItem == NULL )
- {
- ERR("Cannot get valid TREEVIEW_ITEM for lParam\n");
- return 0;
- }
-
- TRACE("Edit started for %s.\n", wineItem->pszText);
- infoPtr->editItem = wineItem->hItem;
-
-
- /*
- * It is common practice for a windows program to get this
- * edit control and then subclass it. It is assumed that a
- * new edit control is given every time.
- *
- * As a result some programs really mess up the edit control
- * so we need to destory our old edit control and create a new
- * one. Recycling would be nice but we would need to reset
- * everything. So recreating may just be easyier
- *
- */
- DestroyWindow(infoPtr->hwndEdit);
- infoPtr->hwndEdit = CreateWindowExA (
- WS_EX_LEFT,
- "EDIT",
- 0,
- WS_CHILD | WS_BORDER | ES_AUTOHSCROLL |
- ES_WANTRETURN | ES_LEFT,
- 0, 0, 0, 0,
- hwnd,
- 0,0,0); /* FIXME: (HMENU)IDTVEDIT,pcs->hInstance,0);*/
-
- SetWindowLongA (
- infoPtr->hwndEdit,
- GWL_WNDPROC,
- (LONG) TREEVIEW_Edit_SubclassProc);
-
-
- SendMessageA ( infoPtr->hwndEdit, WM_SETFONT, infoPtr->hFont, FALSE);
-
- SetWindowTextA( infoPtr->hwndEdit, wineItem->pszText );
- SendMessageA ( infoPtr->hwndEdit, EM_SETSEL, 0, -1 );
-
- /*
- ** NOTE: this must be after the edit control is created
- ** (according to TVN_BEGINLABELEDITA docs), before position is set.
- */
- if ( TREEVIEW_SendDispInfoNotify( /* Return true to cancel edition */
- hwnd,
- wineItem,
- TVN_BEGINLABELEDITA,
- 0))
- {
- /*
- ** FIXME: Is this right, should we return a handle even though edit was cancelled?
- */
- TRACE("Edit cancelled by TVN_BEGINLABELEDITA for %s.\n", wineItem->pszText);
-
- TREEVIEW_EndEditLabelNow(hwnd, (WPARAM)TRUE, 0);
-
- return 0;
- }
-
- SetWindowPos (
- infoPtr->hwndEdit,
- HWND_TOP,
- wineItem->text.left - 2,
- wineItem->text.top - 1,
- wineItem->text.right - wineItem->text.left + 20 ,
- wineItem->text.bottom - wineItem->text.top + 3,
- SWP_DRAWFRAME );
-
- SetFocus ( infoPtr->hwndEdit);
- ShowWindow ( infoPtr->hwndEdit, SW_SHOW);
- }
- }
- else
- {
- /*
- ** return NULL since we cannot edit this.
- */
-
- return 0;
- }
-
- return infoPtr->hwndEdit;
-}
-
-static LRESULT
-TREEVIEW_EndEditLabelNow (HWND hwnd, WPARAM wParam, LPARAM lParam)
-{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
- TREEVIEW_ITEM *editedItem = TREEVIEW_ValidItem (infoPtr, infoPtr->editItem);
- BOOL bRevert = (BOOL)wParam;
- NMTVDISPINFOA tvdi;
- RECT itemRect;
- LPSTR oldText;
- LPSTR newText;
- int iLength;
-
- /*
- ** NOTE: we have to get the new text before calling TVN_ENDLABELEDITA
- ** since some apps(eg regedit, win98) validate the text in TVN_ENDLABELEDITA.
- */
-
- oldText = editedItem->pszText;
-
- if ( !bRevert ) /* wParam is set to true to cancel the edition */
- {
-
- iLength = GetWindowTextLengthA( infoPtr->hwndEdit );
- if (iLength == 0)
- {
- WARN("Zero length string for new label(not changing).");
- bRevert=TRUE;
+ if (row >= wineItem->visibleOrder
+ && row < wineItem->visibleOrder + wineItem->iIntegral)
+ break;
}
- newText = COMCTL32_Alloc( iLength+1 );
- if( newText == NULL )
- {
- ERR("OutOfMemory, cannot allocate space for label");
- return FALSE;
- }
- GetWindowTextA( infoPtr->hwndEdit, newText, iLength+1);
- }
- else
- {
- newText=NULL;
- }
+ return wineItem;
+}
+static LRESULT
+TREEVIEW_HitTest(TREEVIEW_INFO *infoPtr, LPTVHITTESTINFO lpht)
+{
+ TREEVIEW_ITEM *wineItem;
+ RECT rect;
+ UINT status;
+ LONG x, y;
- /*
- * notify our parent with the new string(or NULL if wParam==TRUE)
- */
- tvdi.hdr.hwndFrom = hwnd;
- tvdi.hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
- tvdi.hdr.code = TVN_ENDLABELEDITA;
- tvdi.item.hItem = editedItem->hItem;
- tvdi.item.lParam = editedItem->lParam;
- tvdi.item.mask = TVIF_TEXT|TVIF_HANDLE|TVIF_PARAM;
- tvdi.item.pszText = newText;
+ lpht->hItem = 0;
+ GetClientRect(infoPtr->hwnd, &rect);
+ status = 0;
+ x = lpht->pt.x;
+ y = lpht->pt.y;
- if(!SendMessageA ( /* return false to cancel edition */
- GetParent(hwnd),
- WM_NOTIFY,
- (WPARAM)tvdi.hdr.idFrom,
- (LPARAM)&tvdi))
- {
- if( newText == NULL ) /*we are supposed to ignore the return if (and pszText==NULL), MSDOCs */
- bRevert=TRUE;
- }
-
- if (oldText != LPSTR_TEXTCALLBACKA)
- {
-
- if( bRevert )
+ if (x < rect.left)
{
- if( newText != NULL )
- COMCTL32_Free(newText);
+ status |= TVHT_TOLEFT;
+ }
+ else if (x > rect.right)
+ {
+ status |= TVHT_TORIGHT;
+ }
- editedItem->pszText=oldText; /* revert back to the old label */
+ if (y < rect.top)
+ {
+ status |= TVHT_ABOVE;
+ }
+ else if (y > rect.bottom)
+ {
+ status |= TVHT_BELOW;
+ }
+
+ if (status)
+ {
+ lpht->flags = status;
+ return (LRESULT)(HTREEITEM)NULL;
+ }
+
+ wineItem = TREEVIEW_HitTestPoint(infoPtr, lpht->pt);
+ if (!wineItem)
+ {
+ lpht->flags = TVHT_NOWHERE;
+ return (LRESULT)(HTREEITEM)NULL;
+ }
+
+ if (x >= wineItem->textOffset + wineItem->textWidth)
+ {
+ lpht->flags = TVHT_ONITEMRIGHT;
+ }
+ else if (x >= wineItem->textOffset)
+ {
+ lpht->flags = TVHT_ONITEMLABEL;
+ }
+ else if (x >= wineItem->imageOffset)
+ {
+ lpht->flags = TVHT_ONITEMICON;
+ }
+ else if (x >= wineItem->stateOffset)
+ {
+ lpht->flags = TVHT_ONITEMSTATEICON;
+ }
+ else if (x >= wineItem->linesOffset && infoPtr->dwStyle & TVS_HASBUTTONS)
+ {
+ lpht->flags = TVHT_ONITEMBUTTON;
}
else
{
- COMCTL32_Free(oldText);
-
- editedItem->pszText=newText; /* use the new label */
+ lpht->flags = TVHT_ONITEMINDENT;
}
- }
- else if( newText!=NULL )
- {
- /*
- ** Is really this necessary? shouldnt an app update its internal data in TVN_ENDLABELEDITA?
- */
- if( !bRevert )
+
+ lpht->hItem = wineItem;
+ TRACE("(%ld,%ld):result %x\n", lpht->pt.x, lpht->pt.y, lpht->flags);
+
+ return (LRESULT)wineItem;
+}
+
+/* Item Label Editing ***************************************************/
+
+static LRESULT
+TREEVIEW_GetEditControl(TREEVIEW_INFO *infoPtr)
+{
+ return infoPtr->hwndEdit;
+}
+
+static LRESULT CALLBACK
+TREEVIEW_Edit_SubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ TREEVIEW_INFO *infoPtr;
+ BOOL bCancel = FALSE;
+
+ switch (uMsg)
{
- /*
- * This is a callback string so we need
- * to inform the parent that the string
- * has changed
- *
- */
- tvdi.hdr.hwndFrom = hwnd;
- tvdi.hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
- tvdi.hdr.code = TVN_SETDISPINFOA;
- tvdi.item.mask = TVIF_TEXT;
- tvdi.item.pszText = newText;
+ case WM_PAINT:
+ {
+ LRESULT rc;
+ TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(GetParent(hwnd));
- SendMessageA (
- GetParent(hwnd),
- WM_NOTIFY,
- (WPARAM)tvdi.hdr.idFrom,
- (LPARAM)&tvdi);
+ TRACE("WM_PAINT start\n");
+ rc = CallWindowProcA(infoPtr->wpEditOrig, hwnd, uMsg, wParam,
+ lParam);
+ TRACE("WM_PAINT done\n");
+ return rc;
+ }
+ case WM_KILLFOCUS:
+ {
+ TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(GetParent(hwnd));
+ if (infoPtr->bIgnoreEditKillFocus)
+ return TRUE;
+
+ break;
}
- COMCTL32_Free(newText);
- }
-
+ case WM_GETDLGCODE:
+ return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
- ShowWindow(infoPtr->hwndEdit, SW_HIDE);
- EnableWindow(infoPtr->hwndEdit, FALSE);
+ case WM_KEYDOWN:
+ if (wParam == (WPARAM)VK_ESCAPE)
+ {
+ bCancel = TRUE;
+ break;
+ }
+ else if (wParam == (WPARAM)VK_RETURN)
+ {
+ break;
+ }
- /* update the window to eliminate fragments and the like */
- TreeView_GetItemRect(hwnd,infoPtr->editItem,&itemRect,FALSE);
- RedrawWindow(hwnd,&itemRect,0,RDW_ERASE|RDW_INVALIDATE|RDW_UPDATENOW);
+ /* fall through */
+ default:
+ {
+ TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(GetParent(hwnd));
- infoPtr->editItem = 0;
+ return CallWindowProcA(infoPtr->wpEditOrig, hwnd, uMsg, wParam,
+ lParam);
+ }
+ }
- return !bRevert; /* return true if label edit succesful, otherwise false */
-}
+ /* Processing TVN_ENDLABELEDIT message could kill the focus */
+ /* eg. Using a messagebox */
+ infoPtr = TREEVIEW_GetInfoPtr(GetParent(hwnd));
+ infoPtr->bIgnoreEditKillFocus = TRUE;
+ TREEVIEW_EndEditLabelNow(infoPtr, bCancel || !infoPtr->bLabelChanged);
+ infoPtr->bIgnoreEditKillFocus = FALSE;
-
-static LRESULT
-TREEVIEW_LButtonDoubleClick (HWND hwnd, WPARAM wParam, LPARAM lParam)
-{
- TREEVIEW_ITEM *wineItem;
- POINT pt;
-
- TRACE("\n");
- pt.x = (INT)LOWORD(lParam);
- pt.y = (INT)HIWORD(lParam);
- SetFocus (hwnd);
-
- wineItem=TREEVIEW_HitTestPoint (hwnd, pt);
- if (!wineItem) return 0;
- TRACE("item %d \n",(INT)wineItem->hItem);
-
- if (TREEVIEW_SendSimpleNotify (hwnd, NM_DBLCLK)!=TRUE) { /* FIXME!*/
- TREEVIEW_Expand (hwnd, (WPARAM) TVE_TOGGLE, (LPARAM) wineItem->hItem);
- }
- return TRUE;
-}
-
-
-static LRESULT
-TREEVIEW_LButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
-{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
- INT iItem;
- TVHITTESTINFO ht;
-
- ht.pt.x = (INT)LOWORD(lParam);
- ht.pt.y = (INT)HIWORD(lParam);
-
- SetFocus (hwnd);
- iItem=TREEVIEW_HitTest (hwnd, (LPARAM) &ht);
- TRACE("item %d \n",iItem);
-
- if (ht.flags & TVHT_ONITEMBUTTON) {
- TREEVIEW_Expand (hwnd, (WPARAM) TVE_TOGGLE, (LPARAM) iItem);
- }
- else
- {
- infoPtr->uInternalStatus|=TV_LDRAG;
- }
-
- return 0;
-}
-
-static LRESULT
-TREEVIEW_LButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
-{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
- INT iItem;
- TREEVIEW_ITEM *wineItem;
- TVHITTESTINFO ht;
- DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
-
- ht.pt.x = (INT)LOWORD(lParam);
- ht.pt.y = (INT)HIWORD(lParam);
-
- TRACE("\n");
-
- /* Return true to cancel default behaviour */
- if ( TREEVIEW_SendSimpleNotify (hwnd, NM_CLICK) )
return 0;
+}
- /* Get the item */
- iItem = TREEVIEW_HitTest (hwnd, (LPARAM) &ht);
- TRACE ("%d\n",iItem);
- if (!iItem)
+
+/* should handle edit control messages here */
+
+static LRESULT
+TREEVIEW_Command(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
+{
+ TRACE("%x %ld\n", wParam, lParam);
+
+ switch (HIWORD(wParam))
+ {
+ case EN_UPDATE:
+ {
+ /*
+ * Adjust the edit window size
+ */
+ char buffer[1024];
+ TREEVIEW_ITEM *editItem = infoPtr->selectedItem;
+ HDC hdc = GetDC(infoPtr->hwndEdit);
+ SIZE sz;
+ int len;
+ HFONT hFont, hOldFont = 0;
+
+ infoPtr->bLabelChanged = TRUE;
+
+ len = GetWindowTextA(infoPtr->hwndEdit, buffer, sizeof(buffer));
+
+ /* Select font to get the right dimension of the string */
+ hFont = SendMessageA(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
+ if (hFont != 0)
+ {
+ hOldFont = SelectObject(hdc, hFont);
+ }
+
+ if (GetTextExtentPoint32A(hdc, buffer, strlen(buffer), &sz))
+ {
+ TEXTMETRICA textMetric;
+
+ /* Add Extra spacing for the next character */
+ GetTextMetricsA(hdc, &textMetric);
+ sz.cx += (textMetric.tmMaxCharWidth * 2);
+
+ sz.cx = max(sz.cx, textMetric.tmMaxCharWidth * 3);
+ sz.cx = min(sz.cx,
+ infoPtr->clientWidth - editItem->textOffset + 2);
+
+ SetWindowPos(infoPtr->hwndEdit,
+ HWND_TOP,
+ 0,
+ 0,
+ sz.cx,
+ editItem->rect.bottom - editItem->rect.top + 3,
+ SWP_NOMOVE | SWP_DRAWFRAME);
+ }
+
+ if (hFont != 0)
+ {
+ SelectObject(hdc, hOldFont);
+ }
+
+ ReleaseDC(infoPtr->hwnd, hdc);
+ break;
+ }
+
+ default:
+ return SendMessageA(GetParent(infoPtr->hwnd), WM_COMMAND, wParam, lParam);
+ }
+
return 0;
+}
- wineItem = TREEVIEW_ValidItem(infoPtr, (HTREEITEM)iItem);
-
- /*
- * if we are TVS_SINGLEEXPAND then we want this single click to
- * do a bunch of things.
- */
- if ((dwStyle & TVS_SINGLEEXPAND)&&
- ( ht.flags & (TVHT_ONITEMLABEL | TVHT_ONITEMICON))&&
- ( infoPtr->editItem == 0 ))
- {
- TREEVIEW_ITEM *SelItem;
- /*
- * Send the notification
- */
- TREEVIEW_SendTreeviewNotify (hwnd,TVN_SINGLEEXPAND,0,
- (HTREEITEM)iItem,0);
- /*
- * Close the previous selection all the way to the root
- * as long as the new selection is not a child
- */
-
- if ((infoPtr->selectedItem)&&(infoPtr->selectedItem != (HTREEITEM)iItem))
+static HWND
+TREEVIEW_EditLabelA(TREEVIEW_INFO *infoPtr, HTREEITEM hItem)
+{
+ HWND hwnd = infoPtr->hwnd;
+ HWND hwndEdit;
+ SIZE sz;
+ TREEVIEW_ITEM *editItem = hItem;
+ HINSTANCE hinst = GetWindowLongA(hwnd, GWL_HINSTANCE);
+ HDC hdc;
+ HFONT hOldFont=0;
+ TEXTMETRICA textMetric;
+
+ TRACE("%x %p\n", (unsigned)hwnd, hItem);
+ if (!TREEVIEW_ValidItem(infoPtr, editItem))
+ return (HWND)NULL;
+
+ if (infoPtr->hwndEdit)
+ return infoPtr->hwndEdit;
+
+ infoPtr->bLabelChanged = FALSE;
+
+ /* Make sure that edit item is selected */
+ TREEVIEW_DoSelectItem(infoPtr, TVGN_CARET, hItem, TVC_UNKNOWN);
+ TREEVIEW_EnsureVisible(infoPtr, hItem, TRUE);
+
+ TREEVIEW_UpdateDispInfo(infoPtr, editItem, TVIF_TEXT);
+
+ hdc = GetDC(hwnd);
+ /* Select the font to get appropriate metric dimensions */
+ if (infoPtr->hFont != 0)
{
- BOOL closeit = TRUE;
- SelItem = wineItem;
-
- while (closeit && SelItem)
- {
- closeit = (SelItem->hItem != infoPtr->selectedItem);
- SelItem = TREEVIEW_ValidItem(infoPtr,SelItem->parent);
- }
-
- if (closeit)
- {
- SelItem = TREEVIEW_ValidItem(infoPtr,infoPtr->selectedItem);
- while ((SelItem)&&(SelItem->hItem != wineItem->hItem))
- {
- TREEVIEW_Expand (hwnd,(WPARAM)TVE_COLLAPSE,(LPARAM)SelItem->hItem);
- SelItem = TREEVIEW_ValidItem(infoPtr,SelItem->parent);
- }
- }
+ hOldFont = SelectObject(hdc, infoPtr->hFont);
}
-
- /*
- * Expand the current item
- */
- TREEVIEW_Expand (hwnd, (WPARAM) TVE_TOGGLE, (LPARAM) wineItem->hItem);
- }
- infoPtr->uInternalStatus &= ~(TV_LDRAG | TV_LDRAGGING);
+ /*Get String Lenght in pixels */
+ GetTextExtentPoint32A(hdc, editItem->pszText, strlen(editItem->pszText),
+ &sz);
- /*
- * If the style allow editing and the node is already selected
- * and the click occured on the item label...
- */
- if ( ( GetWindowLongA( hwnd, GWL_STYLE) & TVS_EDITLABELS ) &&
- ( wineItem->state & TVIS_SELECTED ) &&
- ( ht.flags & TVHT_ONITEMLABEL ))
- {
- if ( infoPtr->editItem == 0 ) /* If we are not curently editing */
+ /*Add Extra spacing for the next character */
+ GetTextMetricsA(hdc, &textMetric);
+ sz.cx += (textMetric.tmMaxCharWidth * 2);
+
+ sz.cx = max(sz.cx, textMetric.tmMaxCharWidth * 3);
+ sz.cx = min(sz.cx, infoPtr->clientWidth - editItem->textOffset + 2);
+
+ if (infoPtr->hFont != 0)
{
- if( SendMessageA(hwnd, TVM_EDITLABELA, 0, (LPARAM)iItem) == 0)
- return 0;
+ SelectObject(hdc, hOldFont);
}
- }
- else if ( infoPtr->editItem != 0 ) /* If we are curently editing */
- {
- TREEVIEW_EndEditLabelNow(hwnd, (WPARAM)FALSE, 0);
- }
- else if ( ht.flags & (TVHT_ONITEMLABEL | TVHT_ONITEMICON))
- {
- TREEVIEW_DoSelectItem ( hwnd, TVGN_CARET, (HTREEITEM)iItem, TVC_BYMOUSE);
- }
- if (ht.flags & TVHT_ONITEMSTATEICON) {
+ ReleaseDC(hwnd, hdc);
+ hwndEdit = CreateWindowExA(WS_EX_LEFT,
+ "EDIT",
+ 0,
+ WS_CHILD | WS_BORDER | ES_AUTOHSCROLL |
+ WS_CLIPSIBLINGS | ES_WANTRETURN |
+ ES_LEFT, editItem->textOffset - 2,
+ editItem->rect.top - 1, sz.cx + 3,
+ editItem->rect.bottom -
+ editItem->rect.top + 3, hwnd, 0, hinst, 0);
+/* FIXME: (HMENU)IDTVEDIT,pcs->hInstance,0); */
-
- if (dwStyle & TVS_CHECKBOXES) { /* TVS_CHECKBOXES requires _us_ */
- int state; /* to toggle the current state */
- state=1-(wineItem->state>>12);
- TRACE ("state:%x\n", state);
- wineItem->state&= ~TVIS_STATEIMAGEMASK;
- wineItem->state|=state<<12;
- TRACE ("state:%x\n", wineItem->state);
- TREEVIEW_QueueRefresh (hwnd);
+ infoPtr->hwndEdit = hwndEdit;
+
+ /* Get a 2D border. */
+ SetWindowLongA(hwndEdit, GWL_EXSTYLE,
+ GetWindowLongA(hwndEdit, GWL_EXSTYLE) & ~WS_EX_CLIENTEDGE);
+ SetWindowLongA(hwndEdit, GWL_STYLE,
+ GetWindowLongA(hwndEdit, GWL_STYLE) | WS_BORDER);
+
+ SendMessageA(hwndEdit, WM_SETFONT, TREEVIEW_FontForItem(infoPtr, editItem),
+ FALSE);
+
+ infoPtr->wpEditOrig = (WNDPROC)SetWindowLongA(hwndEdit, GWL_WNDPROC,
+ (DWORD)
+ TREEVIEW_Edit_SubclassProc);
+
+ if (TREEVIEW_BeginLabelEditNotify(infoPtr, editItem))
+ {
+ DestroyWindow(hwndEdit);
+ infoPtr->hwndEdit = 0;
+ return (HWND)NULL;
+ }
+
+ infoPtr->selectedItem = hItem;
+ SetWindowTextA(hwndEdit, editItem->pszText);
+ SetFocus(hwndEdit);
+ SendMessageA(hwndEdit, EM_SETSEL, 0, -1);
+ ShowWindow(hwndEdit, SW_SHOW);
+
+ return hwndEdit;
+}
+
+
+static LRESULT
+TREEVIEW_EndEditLabelNow(TREEVIEW_INFO *infoPtr, BOOL bCancel)
+{
+ HWND hwnd = infoPtr->hwnd;
+ TREEVIEW_ITEM *editedItem = infoPtr->selectedItem;
+ NMTVDISPINFOA tvdi;
+ BOOL bCommit;
+ char tmpText[1024] = { '\0' };
+ int iLength = 0;
+
+ if (!infoPtr->hwndEdit)
+ return FALSE;
+
+ tvdi.hdr.hwndFrom = hwnd;
+ tvdi.hdr.idFrom = GetWindowLongA(hwnd, GWL_ID);
+ tvdi.hdr.code = TVN_ENDLABELEDITA;
+ tvdi.item.mask = 0;
+ tvdi.item.hItem = editedItem;
+ tvdi.item.state = editedItem->state;
+ tvdi.item.lParam = editedItem->lParam;
+
+ if (!bCancel)
+ {
+ iLength = GetWindowTextA(infoPtr->hwndEdit, tmpText, 1023);
+
+ if (iLength >= 1023)
+ {
+ ERR("Insuficient space to retrieve new item label.");
+ }
+
+ tvdi.item.pszText = tmpText;
+ tvdi.item.cchTextMax = iLength + 1;
+ }
+ else
+ {
+ tvdi.item.pszText = NULL;
+ tvdi.item.cchTextMax = 0;
+ }
+
+ bCommit = (BOOL)SendMessageA(GetParent(hwnd),
+ WM_NOTIFY,
+ (WPARAM)tvdi.hdr.idFrom, (LPARAM)&tvdi);
+
+ if (!bCancel && bCommit) /* Apply the changes */
+ {
+ if (strcmp(tmpText, editedItem->pszText) != 0)
+ {
+ if (NULL == COMCTL32_ReAlloc(editedItem->pszText, iLength + 1))
+ {
+ ERR("OutOfMemory, cannot allocate space for label");
+ DestroyWindow(infoPtr->hwndEdit);
+ infoPtr->hwndEdit = 0;
+ return FALSE;
+ }
+ else
+ {
+ editedItem->cchTextMax = iLength + 1;
+ lstrcpyA(editedItem->pszText, tmpText);
+ }
+ }
+ }
+
+ ShowWindow(infoPtr->hwndEdit, SW_HIDE);
+ DestroyWindow(infoPtr->hwndEdit);
+ infoPtr->hwndEdit = 0;
+ return TRUE;
+}
+
+static LRESULT
+TREEVIEW_HandleTimer(TREEVIEW_INFO *infoPtr, WPARAM wParam)
+{
+ if (wParam != TV_EDIT_TIMER)
+ {
+ ERR("got unknown timer\n");
+ return 1;
+ }
+
+ KillTimer(infoPtr->hwnd, TV_EDIT_TIMER);
+ infoPtr->Timer &= ~TV_EDIT_TIMER_SET;
+
+ TREEVIEW_EditLabelA(infoPtr, infoPtr->selectedItem);
+
+ return 0;
+}
+
+
+/* Mouse Tracking/Drag **************************************************/
+
+/***************************************************************************
+ * This is quite unusual piece of code, but that's how it's implemented in
+ * Windows.
+ */
+static LRESULT
+TREEVIEW_TrackMouse(TREEVIEW_INFO *infoPtr, POINT pt)
+{
+ INT cxDrag = GetSystemMetrics(SM_CXDRAG);
+ INT cyDrag = GetSystemMetrics(SM_CYDRAG);
+ RECT r;
+ MSG msg;
+
+ r.top = pt.y - cyDrag;
+ r.left = pt.x - cxDrag;
+ r.bottom = pt.y + cyDrag;
+ r.right = pt.x + cxDrag;
+
+ SetCapture(infoPtr->hwnd);
+
+ while (1)
+ {
+ if (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE | PM_NOYIELD))
+ {
+ if (msg.message == WM_MOUSEMOVE)
+ {
+ pt.x = (LONG)(INT16)LOWORD(msg.lParam);
+ pt.y = (LONG)(INT16)HIWORD(msg.lParam);
+ if (PtInRect(&r, pt))
+ continue;
+ else
+ {
+ ReleaseCapture();
+ return 1;
}
- }
- return 0;
+ }
+ else if (msg.message >= WM_LBUTTONDOWN &&
+ msg.message <= WM_RBUTTONDBLCLK)
+ {
+ if (msg.message == WM_RBUTTONUP)
+ TREEVIEW_RButtonUp(infoPtr, &pt);
+ break;
+ }
+
+ DispatchMessageA(&msg);
+ }
+
+ if (GetCapture() != infoPtr->hwnd)
+ return 0;
+ }
+
+ ReleaseCapture();
+ return 0;
}
static LRESULT
-TREEVIEW_RButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
+TREEVIEW_LButtonDoubleClick(TREEVIEW_INFO *infoPtr, LPARAM lParam)
{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
+ TREEVIEW_ITEM *wineItem;
+ TVHITTESTINFO hit;
- TRACE("\n");
- infoPtr->uInternalStatus|=TV_RDRAG;
- return 0;
-}
+ TRACE("\n");
+ SetFocus(infoPtr->hwnd);
-static LRESULT
-TREEVIEW_RButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
-{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
+ if (infoPtr->Timer & TV_EDIT_TIMER_SET)
+ {
+ /* If there is pending 'edit label' event - kill it now */
+ KillTimer(infoPtr->hwnd, TV_EDIT_TIMER);
+ }
- TRACE("\n");
- if (TREEVIEW_SendSimpleNotify (hwnd, NM_RCLICK)) return 0;
- infoPtr->uInternalStatus&= ~(TV_RDRAG | TV_RDRAGGING);
- return 0;
-}
+ hit.pt.x = (LONG)(INT16)LOWORD(lParam);
+ hit.pt.y = (LONG)(INT16)HIWORD(lParam);
-
-static LRESULT
-TREEVIEW_MouseMove (HWND hwnd, WPARAM wParam, LPARAM lParam)
-{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
- TREEVIEW_ITEM *hotItem;
- POINT pt;
-
- pt.x=(INT) LOWORD (lParam);
- pt.y=(INT) HIWORD (lParam);
- hotItem=TREEVIEW_HitTestPoint (hwnd, pt);
- if (!hotItem) return 0;
- infoPtr->focusItem=hotItem->hItem;
-
- if ( GetWindowLongA( hwnd, GWL_STYLE) & TVS_DISABLEDRAGDROP) return 0;
-
- if (infoPtr->uInternalStatus & TV_LDRAG) {
- TREEVIEW_SendTreeviewDnDNotify (hwnd, TVN_BEGINDRAGA, hotItem->hItem, pt);
- infoPtr->uInternalStatus &= ~TV_LDRAG;
- infoPtr->uInternalStatus |= TV_LDRAGGING;
- infoPtr->dropItem=hotItem->hItem;
+ wineItem = (TREEVIEW_ITEM *)TREEVIEW_HitTest(infoPtr, &hit);
+ if (!wineItem)
return 0;
- }
+ TRACE("item %d \n", TREEVIEW_GetItemIndex(infoPtr, wineItem));
- if (infoPtr->uInternalStatus & TV_RDRAG) {
- TREEVIEW_SendTreeviewDnDNotify (hwnd, TVN_BEGINRDRAGA, hotItem->hItem, pt);
- infoPtr->uInternalStatus &= ~TV_RDRAG;
- infoPtr->uInternalStatus |= TV_RDRAGGING;
- infoPtr->dropItem=hotItem->hItem;
- return 0;
- }
-
- return 0;
+ if (TREEVIEW_SendSimpleNotify(infoPtr, NM_DBLCLK) == FALSE)
+ { /* FIXME! */
+ switch (hit.flags)
+ {
+ case TVHT_ONITEMRIGHT:
+ /* FIXME: we should not have send NM_DBLCLK in this case. */
+ break;
+
+ case TVHT_ONITEMINDENT:
+ if (!(infoPtr->dwStyle & TVS_HASLINES))
+ {
+ break;
+ }
+ else
+ {
+ int level = hit.pt.x / infoPtr->uIndent;
+ if (!(infoPtr->dwStyle & TVS_LINESATROOT)) level++;
+
+ while (wineItem->iLevel > level)
+ {
+ wineItem = wineItem->parent;
+ }
+
+ /* fall through */
+ }
+
+ case TVHT_ONITEMLABEL:
+ case TVHT_ONITEMICON:
+ case TVHT_ONITEMBUTTON:
+ TREEVIEW_Toggle(infoPtr, wineItem, TRUE);
+ break;
+
+ case TVHT_ONITEMSTATEICON:
+ if (infoPtr->dwStyle & TVS_CHECKBOXES)
+ TREEVIEW_ToggleItemState(infoPtr, wineItem);
+ else
+ TREEVIEW_Toggle(infoPtr, wineItem, TRUE);
+ break;
+ }
+ }
+ return TRUE;
}
static LRESULT
-TREEVIEW_CreateDragImage (HWND hwnd, WPARAM wParam, LPARAM lParam)
+TREEVIEW_LButtonDown(TREEVIEW_INFO *infoPtr, LPARAM lParam)
{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
- TREEVIEW_ITEM *dragItem;
- INT cx,cy;
- HDC hdc,htopdc;
- HWND hwtop;
- HBITMAP hbmp,hOldbmp;
- SIZE size;
- RECT rc;
- HFONT hOldFont;
- char *itemtxt;
-
- TRACE("\n");
- if (!(infoPtr->himlNormal)) return 0;
- dragItem=TREEVIEW_ValidItem (infoPtr, (HTREEITEM) lParam);
-
- if (!dragItem) return 0;
+ HWND hwnd = infoPtr->hwnd;
+ TVHITTESTINFO ht;
+ BOOL bTrack;
+ HTREEITEM tempItem;
- if (dragItem->pszText==LPSTR_TEXTCALLBACKA) {
- TREEVIEW_SendDispInfoNotify (hwnd, dragItem, TVN_GETDISPINFOA, TVIF_TEXT);
- }
- itemtxt=dragItem->pszText;
+ /* If Edit control is active - kill it and return.
+ * The best way to do it is to set focus to itself.
+ * Edit control subclassed procedure will automatically call
+ * EndEditLabelNow.
+ */
+ if (infoPtr->hwndEdit)
+ {
+ SetFocus(hwnd);
+ return 0;
+ }
- hwtop=GetDesktopWindow ();
- htopdc= GetDC (hwtop);
- hdc=CreateCompatibleDC (htopdc);
-
- hOldFont=SelectObject (hdc, infoPtr->hFont);
- GetTextExtentPoint32A (hdc, itemtxt, lstrlenA (itemtxt), &size);
- TRACE("%d %d %s %d\n",size.cx,size.cy,itemtxt,lstrlenA(itemtxt));
- hbmp=CreateCompatibleBitmap (htopdc, size.cx, size.cy);
- hOldbmp=SelectObject (hdc, hbmp);
+ ht.pt.x = (LONG)(INT16)LOWORD(lParam);
+ ht.pt.y = (LONG)(INT16)HIWORD(lParam);
- ImageList_GetIconSize (infoPtr->himlNormal, &cx, &cy);
- size.cx+=cx;
- if (cy>size.cy) size.cy=cy;
+ TREEVIEW_HitTest(infoPtr, &ht);
+ TRACE("item %d \n", TREEVIEW_GetItemIndex(infoPtr, ht.hItem));
- infoPtr->dragList=ImageList_Create (size.cx, size.cy, ILC_COLOR, 10, 10);
- ImageList_Draw (infoPtr->himlNormal, dragItem->iImage, hdc, 0, 0, ILD_NORMAL);
+ /* update focusedItem and redraw both items */
+ if(ht.hItem && (ht.flags & TVHT_ONITEM))
+ {
+ infoPtr->focusedItem = ht.hItem;
+ InvalidateRect(hwnd, &(((HTREEITEM)(ht.hItem))->rect), TRUE);
+
+ if(infoPtr->selectedItem)
+ InvalidateRect(hwnd, &(infoPtr->selectedItem->rect), TRUE);
+ }
+
+ bTrack = (ht.flags & TVHT_ONITEM)
+ && !(infoPtr->dwStyle & TVS_DISABLEDRAGDROP);
+
+ /* Send NM_CLICK right away */
+ if (!bTrack)
+ if (TREEVIEW_SendSimpleNotify(infoPtr, NM_CLICK))
+ goto setfocus;
+
+ if (ht.flags & TVHT_ONITEMBUTTON)
+ {
+ TREEVIEW_Toggle(infoPtr, ht.hItem, TRUE);
+ goto setfocus;
+ }
+ else if (bTrack)
+ { /* if TREEVIEW_TrackMouse == 1 dragging occured and the cursor left the dragged item's rectangle */
+ if (TREEVIEW_TrackMouse(infoPtr, ht.pt))
+ {
+ TREEVIEW_SendTreeviewDnDNotify(infoPtr, TVN_BEGINDRAGA, ht.hItem,
+ ht.pt);
+ infoPtr->dropItem = ht.hItem;
+
+ /* clean up focuedItem as we dragged and won't select this item */
+ if(infoPtr->focusedItem)
+ {
+ /* refresh the item that was focused */
+ tempItem = infoPtr->focusedItem;
+ infoPtr->focusedItem = 0;
+ InvalidateRect(infoPtr->hwnd, &tempItem->rect, TRUE);
+
+ /* refresh the selected item to return the filled background */
+ InvalidateRect(infoPtr->hwnd, &(infoPtr->selectedItem->rect), TRUE);
+ }
+
+ return 0;
+ }
+ }
+
+ if (TREEVIEW_SendSimpleNotify(infoPtr, NM_CLICK))
+ goto setfocus;
+
+ /*
+ * If the style allow editing and the node is already selected
+ * and the click occured on the item label...
+ */
+ if ((infoPtr->dwStyle & TVS_EDITLABELS) &&
+ (ht.flags & TVHT_ONITEMLABEL) && (infoPtr->selectedItem == ht.hItem))
+ {
+ if (infoPtr->Timer & TV_EDIT_TIMER_SET)
+ KillTimer(hwnd, TV_EDIT_TIMER);
+
+ SetTimer(hwnd, TV_EDIT_TIMER, GetDoubleClickTime(), 0);
+ infoPtr->Timer |= TV_EDIT_TIMER_SET;
+ }
+ else if (ht.flags & TVHT_ONITEM) /* select the item if the hit was inside of the icon or text */
+ {
+ /*
+ * if we are TVS_SINGLEEXPAND then we want this single click to
+ * do a bunch of things.
+ */
+ if((infoPtr->dwStyle & TVS_SINGLEEXPAND) &&
+ (infoPtr->hwndEdit == 0))
+ {
+ TREEVIEW_ITEM *SelItem;
+
+ /*
+ * Send the notification
+ */
+ TREEVIEW_SendTreeviewNotify(infoPtr, TVN_SINGLEEXPAND, TVIF_HANDLE | TVIF_PARAM,
+ 0, ht.hItem, 0);
+ /*
+ * Close the previous selection all the way to the root
+ * as long as the new selection is not a child
+ */
+
+ if((infoPtr->selectedItem)
+ && (infoPtr->selectedItem != ht.hItem))
+ {
+ BOOL closeit = TRUE;
+ SelItem = ht.hItem;
+
+ while(closeit && SelItem)
+ {
+ closeit = (SelItem != infoPtr->selectedItem);
+
+ if(TREEVIEW_ValidItem(infoPtr, SelItem->parent))
+ SelItem = SelItem->parent;
+ }
+
+ if(closeit)
+ {
+ if(TREEVIEW_ValidItem(infoPtr, infoPtr->selectedItem))
+ SelItem = infoPtr->selectedItem;
+
+ while((SelItem) && (SelItem != ht.hItem))
+ {
+ TREEVIEW_Expand(infoPtr, SelItem, (WPARAM)TVE_COLLAPSE,
+ FALSE);
+ if(TREEVIEW_ValidItem(infoPtr, SelItem->parent))
+ SelItem = SelItem->parent;
+ }
+ }
+ }
+
+ /*
+ * Expand the current item
+ */
+ TREEVIEW_Expand(infoPtr, ht.hItem, TVE_TOGGLE, FALSE);
+ }
+ else
+ TREEVIEW_DoSelectItem(infoPtr, TVGN_CARET, ht.hItem, TVC_BYMOUSE);
+ }
+ else if (ht.flags & TVHT_ONITEMSTATEICON)
+ {
+ /* TVS_CHECKBOXES requires us to toggle the current state */
+ if (infoPtr->dwStyle & TVS_CHECKBOXES)
+ TREEVIEW_ToggleItemState(infoPtr, ht.hItem);
+ }
+
+ setfocus:
+ SetFocus(hwnd);
+ return 0;
+}
+
+
+static LRESULT
+TREEVIEW_RButtonDown(TREEVIEW_INFO *infoPtr, LPARAM lParam)
+{
+ TVHITTESTINFO ht;
+
+ if (infoPtr->hwndEdit)
+ {
+ SetFocus(infoPtr->hwnd);
+ return 0;
+ }
+
+ ht.pt.x = (LONG)(INT16)LOWORD(lParam);
+ ht.pt.y = (LONG)(INT16)HIWORD(lParam);
+
+ TREEVIEW_HitTest(infoPtr, &ht);
+
+ if (TREEVIEW_TrackMouse(infoPtr, ht.pt))
+ {
+ if (ht.hItem)
+ {
+ TREEVIEW_SendTreeviewDnDNotify(infoPtr, TVN_BEGINRDRAGA, ht.hItem,
+ ht.pt);
+ infoPtr->dropItem = ht.hItem;
+ }
+ }
+ else
+ {
+ SetFocus(infoPtr->hwnd);
+ TREEVIEW_SendSimpleNotify(infoPtr, NM_RCLICK);
+ }
+
+ return 0;
+}
+
+static LRESULT
+TREEVIEW_RButtonUp(TREEVIEW_INFO *infoPtr, LPPOINT pPt)
+{
+ HWND hwnd = infoPtr->hwnd;
+ POINT pt = *pPt;
+
+ /* Change to screen coordinate for WM_CONTEXTMENU */
+ ClientToScreen(hwnd, &pt);
+
+ SendMessageA(hwnd, WM_CONTEXTMENU, (WPARAM)hwnd, MAKELPARAM(pt.x, pt.y));
+ return 0;
+}
+
+
+static LRESULT
+TREEVIEW_CreateDragImage(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
+{
+ TREEVIEW_ITEM *dragItem = (HTREEITEM)lParam;
+ INT cx, cy;
+ HDC hdc, htopdc;
+ HWND hwtop;
+ HBITMAP hbmp, hOldbmp;
+ SIZE size;
+ RECT rc;
+ HFONT hOldFont;
+
+ TRACE("\n");
+
+ if (!(infoPtr->himlNormal))
+ return 0;
+
+ if (!dragItem || !TREEVIEW_ValidItem(infoPtr, dragItem))
+ return 0;
+
+ TREEVIEW_UpdateDispInfo(infoPtr, dragItem, TVIF_TEXT);
+
+ hwtop = GetDesktopWindow();
+ htopdc = GetDC(hwtop);
+ hdc = CreateCompatibleDC(htopdc);
+
+ hOldFont = SelectObject(hdc, infoPtr->hFont);
+ GetTextExtentPoint32A(hdc, dragItem->pszText, lstrlenA(dragItem->pszText),
+ &size);
+ TRACE("%d %d %s %d\n", size.cx, size.cy, dragItem->pszText,
+ lstrlenA(dragItem->pszText));
+ hbmp = CreateCompatibleBitmap(htopdc, size.cx, size.cy);
+ hOldbmp = SelectObject(hdc, hbmp);
+
+ ImageList_GetIconSize(infoPtr->himlNormal, &cx, &cy);
+ size.cx += cx;
+ if (cy > size.cy)
+ size.cy = cy;
+
+ infoPtr->dragList = ImageList_Create(size.cx, size.cy, ILC_COLOR, 10, 10);
+ ImageList_Draw(infoPtr->himlNormal, dragItem->iImage, hdc, 0, 0,
+ ILD_NORMAL);
/*
ImageList_GetImageInfo (infoPtr->himlNormal, dragItem->hItem, &iminfo);
@@ -3642,759 +3870,1231 @@
/* draw item text */
- SetRect (&rc, cx, 0, size.cx,size.cy);
- DrawTextA (hdc, itemtxt, lstrlenA (itemtxt), &rc, DT_LEFT);
- SelectObject (hdc, hOldFont);
- SelectObject (hdc, hOldbmp);
+ SetRect(&rc, cx, 0, size.cx, size.cy);
+ DrawTextA(hdc, dragItem->pszText, lstrlenA(dragItem->pszText), &rc,
+ DT_LEFT);
+ SelectObject(hdc, hOldFont);
+ SelectObject(hdc, hOldbmp);
- ImageList_Add (infoPtr->dragList, hbmp, 0);
+ ImageList_Add(infoPtr->dragList, hbmp, 0);
- DeleteDC (hdc);
- DeleteObject (hbmp);
- ReleaseDC (hwtop, htopdc);
+ DeleteDC(hdc);
+ DeleteObject(hbmp);
+ ReleaseDC(hwtop, htopdc);
- return (LRESULT)infoPtr->dragList;
+ return (LRESULT)infoPtr->dragList;
}
+/* Selection ************************************************************/
static LRESULT
-TREEVIEW_DoSelectItem (HWND hwnd, INT action, HTREEITEM newSelect, INT cause)
-
+TREEVIEW_DoSelectItem(TREEVIEW_INFO *infoPtr, INT action, HTREEITEM newSelect,
+ INT cause)
{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
- TREEVIEW_ITEM *prevItem,*wineItem;
- INT prevSelect;
+ TREEVIEW_ITEM *prevSelect;
+ RECT rcFocused;
- wineItem = TREEVIEW_ValidItem (infoPtr, (HTREEITEM)newSelect);
+ assert(newSelect == NULL || TREEVIEW_ValidItem(infoPtr, newSelect));
- TRACE("Entering item %d, flag %x, cause %x, state %d\n",
- (INT)newSelect,
- action,
- cause,
- wineItem->state);
+ TRACE("Entering item %p (%s), flag %x, cause %x, state %d\n",
+ newSelect, TREEVIEW_ItemName(newSelect), action, cause,
+ newSelect ? newSelect->state : 0);
- if ( (wineItem) && (wineItem->parent))
- {
- /*
- * If the item has a collapse parent expand the parent so he
- * can expose the item
- */
- TREEVIEW_ITEM *parentItem = TREEVIEW_ValidItem (infoPtr, wineItem->parent);
- if ( !(parentItem->state & TVIS_EXPANDED))
- TREEVIEW_Expand (hwnd, TVE_EXPAND, (LPARAM) wineItem->parent);
- }
+ /* reset and redraw focusedItem if focusedItem was set so we don't */
+ /* have to worry about the previously focused item when we set a new one */
+ if(infoPtr->focusedItem)
+ {
+ rcFocused = (infoPtr->focusedItem)->rect;
+ infoPtr->focusedItem = 0;
+ InvalidateRect(infoPtr->hwnd, &rcFocused, TRUE);
+ }
- switch (action)
- {
- case TVGN_CARET:
- prevSelect=(INT)infoPtr->selectedItem;
+ switch (action)
+ {
+ case TVGN_CARET:
+ prevSelect = infoPtr->selectedItem;
- if ((HTREEITEM)prevSelect==newSelect)
- return FALSE;
+ if (prevSelect == newSelect)
+ return FALSE;
- prevItem= TREEVIEW_ValidItem (infoPtr, (HTREEITEM)prevSelect);
+ if (TREEVIEW_SendTreeviewNotify(infoPtr,
+ TVN_SELCHANGINGA,
+ cause,
+ TVIF_HANDLE | TVIF_STATE | TVIF_PARAM,
+ prevSelect,
+ newSelect))
+ return FALSE;
- if (newSelect)
- if (TREEVIEW_SendTreeviewNotify(
- hwnd,
- TVN_SELCHANGINGA,
- cause,
- (HTREEITEM)prevSelect,
- (HTREEITEM)newSelect))
- return FALSE; /* FIXME: OK? */
-
- if (prevItem)
- prevItem->state &= ~TVIS_SELECTED;
- if (wineItem)
- wineItem->state |= TVIS_SELECTED;
+ if (prevSelect)
+ prevSelect->state &= ~TVIS_SELECTED;
+ if (newSelect)
+ newSelect->state |= TVIS_SELECTED;
- infoPtr->selectedItem=(HTREEITEM)newSelect;
+ infoPtr->selectedItem = newSelect;
- TREEVIEW_SendTreeviewNotify(
- hwnd,
- TVN_SELCHANGEDA,
- cause,
- (HTREEITEM)prevSelect,
- (HTREEITEM)newSelect);
+ TREEVIEW_EnsureVisible(infoPtr, infoPtr->selectedItem, FALSE);
- break;
+ TREEVIEW_SendTreeviewNotify(infoPtr,
+ TVN_SELCHANGEDA,
+ cause,
+ TVIF_HANDLE | TVIF_STATE | TVIF_PARAM,
+ prevSelect,
+ newSelect);
+ TREEVIEW_QueueItemRefresh(infoPtr, prevSelect);
+ TREEVIEW_QueueItemRefresh(infoPtr, newSelect);
+ break;
- case TVGN_DROPHILITE:
- prevItem= TREEVIEW_ValidItem (infoPtr, infoPtr->dropItem);
+ case TVGN_DROPHILITE:
+ prevSelect = infoPtr->dropItem;
- if (prevItem)
- prevItem->state &= ~TVIS_DROPHILITED;
+ if (prevSelect)
+ prevSelect->state &= ~TVIS_DROPHILITED;
- infoPtr->dropItem=(HTREEITEM)newSelect;
+ infoPtr->dropItem = newSelect;
- if (wineItem)
- wineItem->state |=TVIS_DROPHILITED;
+ if (newSelect)
+ newSelect->state |= TVIS_DROPHILITED;
- break;
+ TREEVIEW_QueueItemRefresh(infoPtr, prevSelect);
+ TREEVIEW_QueueItemRefresh(infoPtr, newSelect);
+ break;
- case TVGN_FIRSTVISIBLE:
- FIXME("FIRSTVISIBLE not implemented\n");
- break;
- }
-
- TREEVIEW_QueueRefresh (hwnd);
+ case TVGN_FIRSTVISIBLE:
+ TREEVIEW_EnsureVisible(infoPtr, newSelect, FALSE);
+ TREEVIEW_SetFirstVisible(infoPtr, newSelect, TRUE);
+ TREEVIEW_QueueRefresh(infoPtr);
+ break;
+ }
- TRACE("Leaving state %d\n", wineItem->state);
- return TRUE;
+ TRACE("Leaving state %d\n", newSelect ? newSelect->state : 0);
+ return TRUE;
}
-/* FIXME: handle NM_KILLFocus etc */
+/* FIXME: handle NM_KILLFOCUS etc */
static LRESULT
-TREEVIEW_SelectItem (HWND hwnd, WPARAM wParam, LPARAM lParam)
-
+TREEVIEW_SelectItem(TREEVIEW_INFO *infoPtr, INT wParam, HTREEITEM item)
{
- return TREEVIEW_DoSelectItem (hwnd, wParam, (HTREEITEM) lParam, TVC_UNKNOWN);
+ if (item != NULL && !TREEVIEW_ValidItem(infoPtr, item))
+ return FALSE;
+
+ TRACE("%p (%s) %d\n", item, TREEVIEW_ItemName(item), wParam);
+
+ if (!TREEVIEW_DoSelectItem(infoPtr, wParam, item, TVC_UNKNOWN))
+ return FALSE;
+
+ return TRUE;
}
+/* Scrolling ************************************************************/
-
-
static LRESULT
-TREEVIEW_GetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
-
+TREEVIEW_EnsureVisible(TREEVIEW_INFO *infoPtr, HTREEITEM item, BOOL bHScroll)
{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
+ HTREEITEM newFirstVisible = NULL;
+ int visible_pos;
- TRACE("%x\n",infoPtr->hFont);
- return infoPtr->hFont;
+ if (!TREEVIEW_ValidItem(infoPtr, item))
+ return FALSE;
+
+ if (!ISVISIBLE(item))
+ {
+ /* Expand parents as necessary. */
+ HTREEITEM parent = item->parent;
+
+ while (parent != infoPtr->root)
+ {
+ if (!(parent->state & TVIS_EXPANDED))
+ TREEVIEW_Expand(infoPtr, parent, FALSE, FALSE);
+
+ parent = parent->parent;
+ }
+ }
+
+ TRACE("%p (%s) %ld - %ld\n", item, TREEVIEW_ItemName(item), item->visibleOrder,
+ infoPtr->firstVisible->visibleOrder);
+
+ visible_pos = item->visibleOrder - infoPtr->firstVisible->visibleOrder;
+
+ if (visible_pos < 0)
+ {
+ /* item is before the start of the list: put it at the top. */
+ newFirstVisible = item;
+ }
+ else if (visible_pos >= TREEVIEW_GetVisibleCount(infoPtr)
+ /* Sometimes, before we are displayed, GVC is 0, causing us to
+ * spuriously scroll up. */
+ && visible_pos > 0)
+ {
+ /* item is past the end of the list. */
+ int scroll = visible_pos - TREEVIEW_GetVisibleCount(infoPtr);
+
+ newFirstVisible = TREEVIEW_GetListItem(infoPtr, infoPtr->firstVisible,
+ scroll + 1);
+ }
+
+ if (bHScroll)
+ {
+ /* Scroll window so item's text is visible as much as possible */
+ /* Calculation of amount of extra space is taken from EditLabel code */
+ INT pos, x;
+ TEXTMETRICA textMetric;
+ HDC hdc = GetWindowDC(infoPtr->hwnd);
+
+ x = item->textWidth;
+
+ GetTextMetricsA(hdc, &textMetric);
+ ReleaseDC(infoPtr->hwnd, hdc);
+
+ x += (textMetric.tmMaxCharWidth * 2);
+ x = max(x, textMetric.tmMaxCharWidth * 3);
+
+ if (item->textOffset < 0)
+ pos = item->textOffset;
+ else if (item->textOffset + x > infoPtr->clientWidth)
+ {
+ if (x > infoPtr->clientWidth)
+ pos = item->textOffset;
+ else
+ pos = item->textOffset + x - infoPtr->clientWidth;
+ }
+ else
+ pos = 0;
+
+ TREEVIEW_HScroll(infoPtr, MAKEWPARAM(SB_THUMBPOSITION, infoPtr->scrollX + pos));
+ }
+
+ if (newFirstVisible != NULL && newFirstVisible != infoPtr->firstVisible)
+ {
+ TREEVIEW_SetFirstVisible(infoPtr, newFirstVisible, TRUE);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static VOID
+TREEVIEW_SetFirstVisible(TREEVIEW_INFO *infoPtr,
+ TREEVIEW_ITEM *newFirstVisible,
+ BOOL bUpdateScrollPos)
+{
+ int gap_size;
+
+ TRACE("%p: %s\n", newFirstVisible, TREEVIEW_ItemName(newFirstVisible));
+
+ if (newFirstVisible != NULL)
+ {
+ /* Prevent an empty gap from appearing at the bottom... */
+ gap_size = TREEVIEW_GetVisibleCount(infoPtr)
+ - infoPtr->maxVisibleOrder + newFirstVisible->visibleOrder;
+
+ if (gap_size > 0)
+ {
+ newFirstVisible = TREEVIEW_GetListItem(infoPtr, newFirstVisible,
+ -gap_size);
+
+ /* ... unless we just don't have enough items. */
+ if (newFirstVisible == NULL)
+ newFirstVisible = infoPtr->root->firstChild;
+ }
+ }
+
+ if (infoPtr->firstVisible != newFirstVisible)
+ {
+ if (infoPtr->firstVisible == NULL || newFirstVisible == NULL)
+ {
+ infoPtr->firstVisible = newFirstVisible;
+ TREEVIEW_QueueRefresh(infoPtr);
+ }
+ else
+ {
+ TREEVIEW_ITEM *item;
+ int scroll = infoPtr->uItemHeight *
+ (infoPtr->firstVisible->visibleOrder
+ - newFirstVisible->visibleOrder);
+
+ infoPtr->firstVisible = newFirstVisible;
+
+ for (item = infoPtr->root->firstChild; item != NULL;
+ item = TREEVIEW_GetNextListItem(infoPtr, item))
+ {
+ item->rect.top += scroll;
+ item->rect.bottom += scroll;
+ }
+
+ if (bUpdateScrollPos)
+ SetScrollPos(infoPtr->hwnd, SB_VERT,
+ newFirstVisible->visibleOrder, TRUE);
+
+ ScrollWindow(infoPtr->hwnd, 0, scroll, NULL, NULL);
+ UpdateWindow(infoPtr->hwnd);
+ }
+ }
+}
+
+/************************************************************************
+ * VScroll is always in units of visible items. i.e. we always have a
+ * visible item aligned to the top of the control. (Unless we have no
+ * items at all.)
+ */
+static LRESULT
+TREEVIEW_VScroll(TREEVIEW_INFO *infoPtr, WPARAM wParam)
+{
+ TREEVIEW_ITEM *oldFirstVisible = infoPtr->firstVisible;
+ TREEVIEW_ITEM *newFirstVisible = NULL;
+
+ int nScrollCode = LOWORD(wParam);
+
+ TRACE("wp %x\n", wParam);
+
+ if (!(infoPtr->uInternalStatus & TV_VSCROLL))
+ return 0;
+
+ if (infoPtr->hwndEdit)
+ SetFocus(infoPtr->hwnd);
+
+ if (!oldFirstVisible)
+ {
+ assert(infoPtr->root->firstChild == NULL);
+ return 0;
+ }
+
+ switch (nScrollCode)
+ {
+ case SB_TOP:
+ newFirstVisible = infoPtr->root->firstChild;
+ break;
+
+ case SB_BOTTOM:
+ newFirstVisible = TREEVIEW_GetLastListItem(infoPtr, infoPtr->root);
+ break;
+
+ case SB_LINEUP:
+ newFirstVisible = TREEVIEW_GetPrevListItem(infoPtr, oldFirstVisible);
+ break;
+
+ case SB_LINEDOWN:
+ newFirstVisible = TREEVIEW_GetNextListItem(infoPtr, oldFirstVisible);
+ break;
+
+ case SB_PAGEUP:
+ newFirstVisible = TREEVIEW_GetListItem(infoPtr, oldFirstVisible,
+ -max(1, TREEVIEW_GetVisibleCount(infoPtr)));
+ break;
+
+ case SB_PAGEDOWN:
+ newFirstVisible = TREEVIEW_GetListItem(infoPtr, oldFirstVisible,
+ max(1, TREEVIEW_GetVisibleCount(infoPtr)));
+ break;
+
+ case SB_THUMBTRACK:
+ case SB_THUMBPOSITION:
+ newFirstVisible = TREEVIEW_GetListItem(infoPtr,
+ infoPtr->root->firstChild,
+ (LONG)(INT16)HIWORD(wParam));
+ break;
+
+ case SB_ENDSCROLL:
+ return 0;
+ }
+
+ if (newFirstVisible != NULL)
+ {
+ if (newFirstVisible != oldFirstVisible)
+ TREEVIEW_SetFirstVisible(infoPtr, newFirstVisible,
+ nScrollCode != SB_THUMBTRACK);
+ else if (nScrollCode == SB_THUMBPOSITION)
+ SetScrollPos(infoPtr->hwnd, SB_VERT,
+ newFirstVisible->visibleOrder, TRUE);
+ }
+
+ return 0;
}
static LRESULT
-TREEVIEW_SetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
-
+TREEVIEW_HScroll(TREEVIEW_INFO *infoPtr, WPARAM wParam)
{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
- TEXTMETRICA tm;
- LOGFONTA logFont;
- HFONT hFont, hOldFont;
- INT height;
- HDC hdc;
+ int maxWidth;
+ int scrollX = infoPtr->scrollX;
+ int nScrollCode = LOWORD(wParam);
- TRACE("%x %lx\n",wParam, lParam);
-
- infoPtr->hFont = (HFONT)wParam;
+ TRACE("wp %x\n", wParam);
- hFont = infoPtr->hFont ? infoPtr->hFont : GetStockObject (SYSTEM_FONT);
+ if (!(infoPtr->uInternalStatus & TV_HSCROLL))
+ return FALSE;
- GetObjectA (infoPtr->hFont, sizeof (LOGFONTA), &logFont);
- logFont.lfWeight=FW_BOLD;
- infoPtr->hBoldFont = CreateFontIndirectA (&logFont);
+ if (infoPtr->hwndEdit)
+ SetFocus(infoPtr->hwnd);
- hdc = GetDC (0);
- hOldFont = SelectObject (hdc, hFont);
- GetTextMetricsA (hdc, &tm);
- height= tm.tmHeight + tm.tmExternalLeading + FOCUS_BORDER;
- if (height>infoPtr->uRealItemHeight)
- infoPtr->uRealItemHeight=height;
- SelectObject (hdc, hOldFont);
- ReleaseDC (0, hdc);
+ maxWidth = infoPtr->treeWidth - infoPtr->clientWidth;
+ /* shall never occure */
+ if (maxWidth <= 0)
+ {
+ scrollX = 0;
+ goto scroll;
+ }
- if (lParam)
- TREEVIEW_QueueRefresh (hwnd);
-
- return 0;
-}
+ switch (nScrollCode)
+ {
+ case SB_LINELEFT:
+ scrollX -= infoPtr->uItemHeight;
+ break;
+ case SB_LINERIGHT:
+ scrollX += infoPtr->uItemHeight;
+ break;
+ case SB_PAGELEFT:
+ scrollX -= infoPtr->clientWidth;
+ break;
+ case SB_PAGERIGHT:
+ scrollX += infoPtr->clientWidth;
+ break;
+ case SB_THUMBTRACK:
+ case SB_THUMBPOSITION:
+ scrollX = (int)(INT16)HIWORD(wParam);
+ break;
+ case SB_ENDSCROLL:
+ return 0;
+ }
-static LRESULT
-TREEVIEW_VScroll (HWND hwnd, WPARAM wParam, LPARAM lParam)
+ if (scrollX > maxWidth)
+ scrollX = maxWidth;
+ else if (scrollX < 0)
+ scrollX = 0;
-{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
- WORD nVisibleItems;
- int maxHeight;
+scroll:
+ if (scrollX != infoPtr->scrollX)
+ {
+ TREEVIEW_ITEM *item;
+ LONG scroll_pixels = infoPtr->scrollX - scrollX;
+
+ for (item = infoPtr->root->firstChild; item != NULL;
+ item = TREEVIEW_GetNextListItem(infoPtr, item))
+ {
+ item->linesOffset += scroll_pixels;
+ item->stateOffset += scroll_pixels;
+ item->imageOffset += scroll_pixels;
+ item->textOffset += scroll_pixels;
+ }
- TRACE("wp %x, lp %lx\n", wParam, lParam);
- if (!infoPtr->uInternalStatus & TV_VSCROLL) return FALSE;
+ ScrollWindow(infoPtr->hwnd, scroll_pixels, 0, NULL, NULL);
+ infoPtr->scrollX = scrollX;
+ UpdateWindow(infoPtr->hwnd);
+ }
- switch (LOWORD (wParam)) {
- case SB_LINEUP:
- if (!infoPtr->cy) return FALSE;
- infoPtr->cy -= infoPtr->uRealItemHeight;
- if (infoPtr->cy < 0) infoPtr->cy=0;
- break;
- case SB_LINEDOWN:
- nVisibleItems = infoPtr->uVisibleHeight / infoPtr->uRealItemHeight;
- maxHeight=infoPtr->uTotalHeight - nVisibleItems * infoPtr->uRealItemHeight;
- if (infoPtr->cy >= maxHeight) return FALSE;
- infoPtr->cy += infoPtr->uRealItemHeight;
- if (infoPtr->cy >= maxHeight)
- infoPtr->cy = maxHeight;
- break;
- case SB_PAGEUP:
- if (!infoPtr->cy) return FALSE;
- infoPtr->cy -= infoPtr->uVisibleHeight;
- if (infoPtr->cy < 0) infoPtr->cy=0;
- break;
- case SB_PAGEDOWN:
- nVisibleItems = infoPtr->uVisibleHeight / infoPtr->uRealItemHeight;
- maxHeight=infoPtr->uTotalHeight - nVisibleItems * infoPtr->uRealItemHeight;
- if (infoPtr->cy == maxHeight) return FALSE;
- infoPtr->cy += infoPtr->uVisibleHeight;
- if (infoPtr->cy >= maxHeight)
- infoPtr->cy = maxHeight;
- break;
- case SB_THUMBTRACK:
- infoPtr->cy = HIWORD (wParam);
- break;
-
- }
-
- TREEVIEW_QueueRefresh (hwnd);
- return TRUE;
+ if (nScrollCode != SB_THUMBTRACK)
+ SetScrollPos(infoPtr->hwnd, SB_HORZ, scrollX, TRUE);
+
+ return 0;
}
static LRESULT
-TREEVIEW_HScroll (HWND hwnd, WPARAM wParam, LPARAM lParam)
+TREEVIEW_MouseWheel(TREEVIEW_INFO *infoPtr, WPARAM wParam)
{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
- int maxWidth;
-
- TRACE("wp %lx, lp %x\n", lParam, wParam);
-
- if (!infoPtr->uInternalStatus & TV_HSCROLL) return FALSE;
-
- switch (LOWORD (wParam)) {
- case SB_LINEUP:
- if (!infoPtr->cx) return FALSE;
- infoPtr->cx -= infoPtr->uRealItemHeight;
- if (infoPtr->cx < 0) infoPtr->cx=0;
- break;
- case SB_LINEDOWN:
- maxWidth=infoPtr->uTotalWidth-infoPtr->uVisibleWidth;
- if (infoPtr->cx == maxWidth) return FALSE;
- infoPtr->cx += infoPtr->uRealItemHeight; /*FIXME */
- if (infoPtr->cx > maxWidth)
- infoPtr->cx = maxWidth;
- break;
- case SB_PAGEUP:
- if (!infoPtr->cx) return FALSE;
- infoPtr->cx -= infoPtr->uVisibleWidth;
- if (infoPtr->cx < 0) infoPtr->cx=0;
- break;
- case SB_PAGEDOWN:
- maxWidth=infoPtr->uTotalWidth-infoPtr->uVisibleWidth;
- if (infoPtr->cx == maxWidth) return FALSE;
- infoPtr->cx += infoPtr->uVisibleWidth;
- if (infoPtr->cx > maxWidth)
- infoPtr->cx = maxWidth;
- break;
- case SB_THUMBTRACK:
- infoPtr->cx = HIWORD (wParam);
- break;
-
- }
-
- TREEVIEW_QueueRefresh (hwnd);
- return TRUE;
-}
-
-static LRESULT TREEVIEW_MouseWheel (HWND hwnd, WPARAM wParam, LPARAM lParam)
-{
-
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
- short gcWheelDelta = 0;
+ short gcWheelDelta;
UINT pulScrollLines = 3;
- SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
+ if (infoPtr->firstVisible == NULL)
+ return TRUE;
- gcWheelDelta -= (short) HIWORD(wParam);
+ SystemParametersInfoW(SPI_GETWHEELSCROLLLINES, 0, &pulScrollLines, 0);
+
+ gcWheelDelta = -(short)HIWORD(wParam);
pulScrollLines *= (gcWheelDelta / WHEEL_DELTA);
if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
{
- int wheelDy = pulScrollLines * infoPtr->uRealItemHeight;
- int newDy = infoPtr->cy + wheelDy;
- int maxDy = infoPtr->uTotalHeight - infoPtr->uVisibleHeight;
+ int newDy = infoPtr->firstVisible->visibleOrder + pulScrollLines;
+ int maxDy = infoPtr->maxVisibleOrder;
- if (newDy > maxDy) newDy = maxDy;
- if (newDy < 0) newDy = 0;
+ if (newDy > maxDy)
+ newDy = maxDy;
- if (newDy == infoPtr->cy) return TRUE;
+ if (newDy < 0)
+ newDy = 0;
- TREEVIEW_VScroll(hwnd, MAKEWPARAM(SB_THUMBTRACK,newDy),0);
+ TREEVIEW_VScroll(infoPtr, MAKEWPARAM(SB_THUMBPOSITION, newDy));
}
- return TRUE;
+ return TRUE;
}
+/* Create/Destroy *******************************************************/
+
static LRESULT
-TREEVIEW_KeyDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
-{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
- HTREEITEM hNewSelection = 0;
- INT scrollNeeds = -1;
- INT cyChangeNeeds = -1;
- INT prevSelect = (INT)infoPtr->selectedItem;
+TREEVIEW_Create(HWND hwnd)
+{
+ RECT rcClient;
+ TREEVIEW_INFO *infoPtr;
- TREEVIEW_ITEM *prevItem =
- (prevSelect != 0 ) ?
- TREEVIEW_ValidItem (infoPtr, (HTREEITEM)prevSelect) :
- NULL;
+ TRACE("wnd %x, style %lx\n", hwnd, GetWindowLongA(hwnd, GWL_STYLE));
- TREEVIEW_ITEM *newItem = NULL;
-
- TRACE("%x %lx\n",wParam, lParam);
-
- if (prevSelect == 0)
- return FALSE;
-
- switch (wParam) {
- case VK_UP:
- newItem=TREEVIEW_GetPrevListItem (infoPtr, prevItem);
-
- if (!newItem)
- newItem=& infoPtr->items[(INT)infoPtr->TopRootItem];
-
- hNewSelection = newItem->hItem;
-
- if (! newItem->visible)
- scrollNeeds = SB_LINEUP;
- break;
-
- case VK_DOWN:
- newItem=TREEVIEW_GetNextListItem (infoPtr, prevItem);
-
- if (!newItem)
- newItem=prevItem;
-
- hNewSelection = newItem->hItem;
-
- if (! newItem->visible)
- scrollNeeds = SB_LINEDOWN;
-
- break;
-
- case VK_HOME:
- newItem = &infoPtr->items[(INT)infoPtr->TopRootItem];
- hNewSelection = newItem->hItem;
- cyChangeNeeds = 0;
- break;
-
- case VK_END:
- newItem = &infoPtr->items[(INT)infoPtr->TopRootItem];
- newItem = TREEVIEW_GetLastListItem (infoPtr, newItem);
- hNewSelection = newItem->hItem;
-
- if (! newItem->visible)
- cyChangeNeeds = infoPtr->uTotalHeight-infoPtr->uVisibleHeight;
-
- break;
-
- case VK_LEFT:
- if ( (prevItem->cChildren > 0) && (prevItem->state & TVIS_EXPANDED) )
- {
- TREEVIEW_Expand(hwnd, TVE_COLLAPSE, prevSelect );
- }
- else if ((INT)prevItem->parent)
- {
- newItem = (& infoPtr->items[(INT)prevItem->parent]);
- if (! newItem->visible)
- /* FIXME find a way to make this item the first visible... */
- newItem = NULL;
-
- hNewSelection = newItem->hItem;
- }
-
- break;
-
- case VK_RIGHT:
- if ( ( prevItem->cChildren > 0) ||
- ( prevItem->cChildren == I_CHILDRENCALLBACK))
- {
- if (! (prevItem->state & TVIS_EXPANDED))
- TREEVIEW_Expand(hwnd, TVE_EXPAND, prevSelect );
- else
- {
- newItem = (& infoPtr->items[(INT)prevItem->firstChild]);
- hNewSelection = newItem->hItem;
- }
- }
-
- break;
-
- case VK_ADD:
- if (! (prevItem->state & TVIS_EXPANDED))
- TREEVIEW_Expand(hwnd, TVE_EXPAND, prevSelect );
- break;
-
- case VK_SUBTRACT:
- if (prevItem->state & TVIS_EXPANDED)
- TREEVIEW_Expand(hwnd, TVE_COLLAPSE, prevSelect );
- break;
-
- case VK_PRIOR:
-
- newItem=TREEVIEW_GetListItem(
- infoPtr,
- prevItem,
- -1*(TREEVIEW_GetVisibleCount(hwnd,0,0)-3));
- if (!newItem)
- newItem=prevItem;
-
- hNewSelection = newItem->hItem;
-
- if (! newItem->visible)
- scrollNeeds = SB_PAGEUP;
-
- break;
-
- case VK_NEXT:
- newItem=TREEVIEW_GetListItem(
- infoPtr,
- prevItem,
- TREEVIEW_GetVisibleCount(hwnd,0,0)-3);
-
- if (!newItem)
- newItem=prevItem;
-
- hNewSelection = newItem->hItem;
-
- if (! newItem->visible)
- scrollNeeds = SB_PAGEDOWN;
-
- break;
-
- case VK_BACK:
-
- case VK_RETURN:
-
- default:
- FIXME("%x not implemented\n", wParam);
- break;
- }
-
- if (hNewSelection)
- {
-/*
- This works but does not send notification...
-
- prevItem->state &= ~TVIS_SELECTED;
- newItem->state |= TVIS_SELECTED;
- infoPtr->selectedItem = hNewSelection;
- TREEVIEW_QueueRefresh (hwnd);
-*/
-
- if ( TREEVIEW_DoSelectItem(
- hwnd,
- TVGN_CARET,
- (HTREEITEM)hNewSelection,
- TVC_BYKEYBOARD))
- {
- /* If selection change is allowed for the new item, perform scrolling */
- if (scrollNeeds != -1)
- TREEVIEW_VScroll(hwnd, scrollNeeds, 0);
-
- if (cyChangeNeeds != -1)
- infoPtr->cy = cyChangeNeeds;
-
- /* FIXME: Something happen in the load the in the two weeks before
- april 1st 1999 which makes this SetFocus mandatory otherwise, the focus
- is lost... However the SetFocus should not be required...*/
-
- SetFocus(hwnd);
- }
- }
-
- return FALSE;
-}
-
-
-static LRESULT
-TREEVIEW_GetScrollTime (HWND hwnd)
-{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
-
- return infoPtr->uScrollTime;
-}
-
-
-static LRESULT
-TREEVIEW_SetScrollTime (HWND hwnd, UINT uScrollTime)
-{
- TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
- UINT uOldScrollTime = infoPtr->uScrollTime;
-
- infoPtr->uScrollTime = min (uScrollTime, 100);
-
- return uOldScrollTime;
-}
-
-
-static LRESULT WINAPI
-TREEVIEW_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
-{
- TREEVIEW_INFO *infoPtr;
- if (uMsg==WM_CREATE)
- return TREEVIEW_Create (hwnd, wParam, lParam);
-
- if (!(infoPtr = TREEVIEW_GetInfoPtr(hwnd)))
- return DefWindowProcA (hwnd, uMsg, wParam, lParam);
-
- switch (uMsg) {
-
- case TVM_INSERTITEMA:
- return TREEVIEW_InsertItemA (hwnd, wParam, lParam);
-
- case TVM_INSERTITEMW:
- return TREEVIEW_InsertItemW(hwnd,wParam,lParam);;
-
- case TVM_DELETEITEM:
- return TREEVIEW_DeleteItem (hwnd, wParam, lParam);
-
- case TVM_EXPAND:
- return TREEVIEW_Expand (hwnd, wParam, lParam);
-
- case TVM_GETITEMRECT:
- return TREEVIEW_GetItemRect (hwnd, wParam, lParam);
-
- case TVM_GETCOUNT:
- return TREEVIEW_GetCount (hwnd, wParam, lParam);
-
- case TVM_GETINDENT:
- return TREEVIEW_GetIndent (hwnd);
-
- case TVM_SETINDENT:
- return TREEVIEW_SetIndent (hwnd, wParam);
-
- case TVM_GETIMAGELIST:
- return TREEVIEW_GetImageList (hwnd, wParam, lParam);
-
- case TVM_SETIMAGELIST:
- return TREEVIEW_SetImageList (hwnd, wParam, lParam);
-
- case TVM_GETNEXTITEM:
- return TREEVIEW_GetNextItem (hwnd, wParam, lParam);
-
- case TVM_SELECTITEM:
- return TREEVIEW_SelectItem (hwnd, wParam, lParam);
-
- case TVM_GETITEMA:
- return TREEVIEW_GetItemA (hwnd, wParam, lParam);
-
- case TVM_GETITEMW:
- return TREEVIEW_GetItemW (hwnd, wParam, lParam);
-
- case TVM_SETITEMA:
- return TREEVIEW_SetItemA (hwnd, wParam, lParam);
-
- case TVM_SETITEMW:
- FIXME("Unimplemented msg TVM_SETITEMW\n");
- return 0;
-
- case TVM_EDITLABELA:
- return TREEVIEW_EditLabelA(hwnd, wParam, lParam);
-
- case TVM_EDITLABELW:
- FIXME("Unimplemented msg TVM_EDITLABELW \n");
- return 0;
-
- case TVM_GETEDITCONTROL:
- return TREEVIEW_GetEditControl (hwnd);
-
- case TVM_GETVISIBLECOUNT:
- return TREEVIEW_GetVisibleCount (hwnd, wParam, lParam);
-
- case TVM_HITTEST:
- return TREEVIEW_HitTest (hwnd, lParam);
-
- case TVM_CREATEDRAGIMAGE:
- return TREEVIEW_CreateDragImage (hwnd, wParam, lParam);
-
- case TVM_SORTCHILDREN:
- return TREEVIEW_SortChildren (hwnd, wParam, lParam);
-
- case TVM_ENSUREVISIBLE:
- FIXME("Unimplemented msg TVM_ENSUREVISIBLE\n");
- return 0;
-
- case TVM_SORTCHILDRENCB:
- return TREEVIEW_SortChildrenCB(hwnd, wParam, lParam);
-
- case TVM_ENDEDITLABELNOW:
- if (infoPtr->editItem)
- return TREEVIEW_EndEditLabelNow (hwnd, wParam, lParam);
-
- case TVM_GETISEARCHSTRINGA:
- FIXME("Unimplemented msg TVM_GETISEARCHSTRINGA\n");
- return 0;
-
- case TVM_GETISEARCHSTRINGW:
- FIXME("Unimplemented msg TVM_GETISEARCHSTRINGW\n");
- return 0;
-
- case TVM_GETTOOLTIPS:
- return TREEVIEW_GetToolTips (hwnd);
-
- case TVM_SETTOOLTIPS:
- return TREEVIEW_SetToolTips (hwnd, wParam);
-
- case TVM_SETINSERTMARK:
- return TREEVIEW_SetInsertMark (hwnd,wParam, lParam);
-
- case TVM_SETITEMHEIGHT:
- return TREEVIEW_SetItemHeight (hwnd, wParam);
-
- case TVM_GETITEMHEIGHT:
- return TREEVIEW_GetItemHeight (hwnd);
-
- case TVM_SETBKCOLOR:
- return TREEVIEW_SetBkColor (hwnd, wParam, lParam);
-
- case TVM_SETTEXTCOLOR:
- return TREEVIEW_SetTextColor (hwnd, wParam, lParam);
-
- case TVM_GETBKCOLOR:
- return TREEVIEW_GetBkColor (hwnd);
-
- case TVM_GETTEXTCOLOR:
- return TREEVIEW_GetTextColor (hwnd);
-
- case TVM_SETSCROLLTIME:
- return TREEVIEW_SetScrollTime (hwnd, (UINT)wParam);
-
- case TVM_GETSCROLLTIME:
- return TREEVIEW_GetScrollTime (hwnd);
-
- case TVM_GETITEMSTATE:
- return TREEVIEW_GetItemState (hwnd,wParam, lParam);
-
- case TVM_GETLINECOLOR:
- return TREEVIEW_GetLineColor (hwnd,wParam, lParam);
-
- case TVM_SETLINECOLOR:
- return TREEVIEW_SetLineColor (hwnd,wParam, lParam);
-
- case TVM_SETINSERTMARKCOLOR:
- return TREEVIEW_SetInsertMarkColor (hwnd,wParam, lParam);
-
- case TVM_GETINSERTMARKCOLOR:
- return TREEVIEW_GetInsertMarkColor (hwnd,wParam, lParam);
-
- case TVM_SETUNICODEFORMAT:
- FIXME("Unimplemented msg TVM_SETUNICODEFORMAT\n");
- return 0;
-
- case TVM_GETUNICODEFORMAT:
- FIXME("Unimplemented msg TVM_GETUNICODEFORMAT\n");
- return 0;
-
- case WM_COMMAND:
- return TREEVIEW_Command (hwnd, wParam, lParam);
-
- case WM_DESTROY:
- return TREEVIEW_Destroy (hwnd);
-
-/* case WM_ENABLE: */
-
- case WM_ERASEBKGND:
- return TREEVIEW_EraseBackground (hwnd, wParam, lParam);
-
- case WM_GETDLGCODE:
- return DLGC_WANTARROWS | DLGC_WANTCHARS;
-
- case WM_PAINT:
- return TREEVIEW_Paint (hwnd, wParam, lParam);
-
- case WM_GETFONT:
- return TREEVIEW_GetFont (hwnd, wParam, lParam);
-
- case WM_SETFONT:
- return TREEVIEW_SetFont (hwnd, wParam, lParam);
-
- case WM_KEYDOWN:
- return TREEVIEW_KeyDown (hwnd, wParam, lParam);
-
- case WM_SETFOCUS:
- return TREEVIEW_SetFocus (hwnd, wParam, lParam);
-
- case WM_KILLFOCUS:
- return TREEVIEW_KillFocus (hwnd, wParam, lParam);
-
- case WM_LBUTTONDOWN:
- return TREEVIEW_LButtonDown (hwnd, wParam, lParam);
-
- case WM_LBUTTONUP:
- return TREEVIEW_LButtonUp (hwnd, wParam, lParam);
-
- case WM_LBUTTONDBLCLK:
- return TREEVIEW_LButtonDoubleClick (hwnd, wParam, lParam);
-
- case WM_RBUTTONDOWN:
- return TREEVIEW_RButtonDown (hwnd, wParam, lParam);
-
- case WM_RBUTTONUP:
- return TREEVIEW_RButtonUp (hwnd, wParam, lParam);
-
- case WM_MOUSEMOVE:
- return TREEVIEW_MouseMove (hwnd, wParam, lParam);
-
- case WM_STYLECHANGED:
- return TREEVIEW_StyleChanged (hwnd, wParam, lParam);
-
-/* case WM_SYSCOLORCHANGE: */
-/* case WM_SETREDRAW: */
-
- case WM_TIMER:
- return TREEVIEW_HandleTimer (hwnd, wParam, lParam);
+ infoPtr = (TREEVIEW_INFO *)COMCTL32_Alloc(sizeof(TREEVIEW_INFO));
- case WM_SIZE:
- return TREEVIEW_Size (hwnd, wParam,lParam);
+ if (infoPtr == NULL)
+ {
+ ERR("could not allocate info memory!\n");
+ return 0;
+ }
- case WM_HSCROLL:
- return TREEVIEW_HScroll (hwnd, wParam, lParam);
- case WM_VSCROLL:
- return TREEVIEW_VScroll (hwnd, wParam, lParam);
-
- case WM_MOUSEWHEEL:
- if (wParam & (MK_SHIFT | MK_CONTROL))
- return DefWindowProcA( hwnd, uMsg, wParam, lParam );
- return TREEVIEW_MouseWheel (hwnd, wParam, lParam);
+ SetWindowLongA(hwnd, 0, (DWORD)infoPtr);
- case WM_DRAWITEM:
- TRACE ("drawItem\n");
- return DefWindowProcA (hwnd, uMsg, wParam, lParam);
-
- default:
- if (uMsg >= WM_USER)
- FIXME("Unknown msg %04x wp=%08x lp=%08lx\n",
- uMsg, wParam, lParam);
- return DefWindowProcA (hwnd, uMsg, wParam, lParam);
- }
+ infoPtr->hwnd = hwnd;
+ infoPtr->dwStyle = GetWindowLongA(hwnd, GWL_STYLE);
+ infoPtr->uInternalStatus = 0;
+ infoPtr->Timer = 0;
+ infoPtr->uNumItems = 0;
+ infoPtr->cdmode = 0;
+ infoPtr->uScrollTime = 300; /* milliseconds */
+ infoPtr->bRedraw = TRUE;
+
+ GetClientRect(hwnd, &rcClient);
+
+ /* No scroll bars yet. */
+ infoPtr->clientWidth = rcClient.right;
+ infoPtr->clientHeight = rcClient.bottom;
+
+ infoPtr->treeWidth = 0;
+ infoPtr->treeHeight = 0;
+
+ infoPtr->uIndent = 19;
+ infoPtr->selectedItem = 0;
+ infoPtr->focusedItem = 0;
+ /* hotItem? */
+ infoPtr->firstVisible = 0;
+ infoPtr->maxVisibleOrder = 0;
+ infoPtr->dropItem = 0;
+ infoPtr->insertMarkItem = 0;
+ infoPtr->insertBeforeorAfter = 0;
+ /* dragList */
+
+ infoPtr->scrollX = 0;
+
+ infoPtr->clrBk = GetSysColor(COLOR_WINDOW);
+ infoPtr->clrText = -1; /* use system color */
+ infoPtr->clrLine = RGB(128, 128, 128);
+ infoPtr->clrInsertMark = GetSysColor(COLOR_BTNTEXT);
+
+ /* hwndToolTip */
+
+ infoPtr->hwndEdit = 0;
+ infoPtr->wpEditOrig = NULL;
+ infoPtr->bIgnoreEditKillFocus = FALSE;
+ infoPtr->bLabelChanged = FALSE;
+
+ infoPtr->himlNormal = NULL;
+ infoPtr->himlState = NULL;
+ infoPtr->normalImageWidth = 0;
+ infoPtr->normalImageHeight = 0;
+ infoPtr->stateImageWidth = 0;
+ infoPtr->stateImageHeight = 0;
+
+ infoPtr->items = DPA_Create(16);
+
+ infoPtr->hFont = GetStockObject(DEFAULT_GUI_FONT);
+ infoPtr->hBoldFont = TREEVIEW_CreateBoldFont(infoPtr->hFont);
+
+ infoPtr->uItemHeight = TREEVIEW_NaturalHeight(infoPtr);
+
+ infoPtr->root = TREEVIEW_AllocateItem(infoPtr);
+ infoPtr->root->state = TVIS_EXPANDED;
+ infoPtr->root->iLevel = -1;
+ infoPtr->root->visibleOrder = -1;
+
+#if 0
+ infoPtr->hwndNotify = GetParent32 (hwnd);
+ infoPtr->bTransparent = ( GetWindowLongA( hwnd, GWL_STYLE) & TBSTYLE_FLAT);
+#endif
+
+ infoPtr->hwndToolTip = 0;
+ if (!(infoPtr->dwStyle & TVS_NOTOOLTIPS))
+ infoPtr->hwndToolTip = COMCTL32_CreateToolTip(hwnd);
+
+ if (infoPtr->dwStyle & TVS_CHECKBOXES)
+ {
+ RECT rc;
+ HBITMAP hbm, hbmOld;
+ HDC hdc;
+ int nIndex;
+
+ infoPtr->himlState =
+ ImageList_Create(16, 16, ILC_COLOR | ILC_MASK, 3, 0);
+
+ hdc = CreateCompatibleDC(0);
+ hbm = CreateCompatibleBitmap(hdc, 48, 16);
+ hbmOld = SelectObject(hdc, hbm);
+
+ rc.left = 0; rc.top = 0;
+ rc.right = 48; rc.bottom = 16;
+ FillRect(hdc, &rc, (HBRUSH)(COLOR_WINDOW+1));
+
+ rc.left = 18; rc.top = 2;
+ rc.right = 30; rc.bottom = 14;
+ DrawFrameControl(hdc, &rc, DFC_BUTTON,
+ DFCS_BUTTONCHECK|DFCS_FLAT);
+
+ rc.left = 34; rc.right = 46;
+ DrawFrameControl(hdc, &rc, DFC_BUTTON,
+ DFCS_BUTTONCHECK|DFCS_FLAT|DFCS_CHECKED);
+
+ nIndex = ImageList_AddMasked(infoPtr->himlState, hbm,
+ GetSysColor(COLOR_WINDOW));
+ TRACE("chckbox index %d\n", nIndex);
+ SelectObject(hdc, hbmOld);
+ DeleteObject(hbm);
+ DeleteDC(hdc);
+
+ infoPtr->stateImageWidth = 16;
+ infoPtr->stateImageHeight = 16;
+ }
return 0;
}
+static LRESULT
+TREEVIEW_Destroy(TREEVIEW_INFO *infoPtr)
+{
+ TRACE("\n");
+
+ TREEVIEW_RemoveTree(infoPtr);
+
+ /* tool tip is automatically destroyed: we are its owner */
+
+ /* Restore original windproc. */
+ if (infoPtr->hwndEdit)
+ SetWindowLongA(infoPtr->hwndEdit, GWL_WNDPROC,
+ (LONG)infoPtr->wpEditOrig);
+
+ /* Deassociate treeview from the window before doing anything drastic. */
+ SetWindowLongA(infoPtr->hwnd, 0, (LONG)NULL);
+
+ DeleteObject(infoPtr->hBoldFont);
+ COMCTL32_Free(infoPtr);
+
+ return 0;
+}
+
+/* Miscellaneous Messages ***********************************************/
+
+static LRESULT
+TREEVIEW_ScrollKeyDown(TREEVIEW_INFO *infoPtr, WPARAM key)
+{
+ static const struct
+ {
+ unsigned char code;
+ }
+ scroll[] =
+ {
+#define SCROLL_ENTRY(dir, code) { ((dir) << 7) | (code) }
+ SCROLL_ENTRY(SB_VERT, SB_PAGEUP), /* VK_PRIOR */
+ SCROLL_ENTRY(SB_VERT, SB_PAGEDOWN), /* VK_NEXT */
+ SCROLL_ENTRY(SB_VERT, SB_BOTTOM), /* VK_END */
+ SCROLL_ENTRY(SB_VERT, SB_TOP), /* VK_HOME */
+ SCROLL_ENTRY(SB_HORZ, SB_LINEUP), /* VK_LEFT */
+ SCROLL_ENTRY(SB_VERT, SB_LINEUP), /* VK_UP */
+ SCROLL_ENTRY(SB_HORZ, SB_LINEDOWN), /* VK_RIGHT */
+ SCROLL_ENTRY(SB_VERT, SB_LINEDOWN) /* VK_DOWN */
+#undef SCROLL_ENTRY
+ };
+
+ if (key >= VK_PRIOR && key <= VK_DOWN)
+ {
+ unsigned char code = scroll[key - VK_PRIOR].code;
+
+ (((code & (1 << 7)) == (SB_HORZ << 7))
+ ? TREEVIEW_HScroll
+ : TREEVIEW_VScroll)(infoPtr, code & 0x7F);
+ }
+
+ return 0;
+}
+
+/************************************************************************
+ * TREEVIEW_KeyDown
+ *
+ * VK_UP Move selection to the previous non-hidden item.
+ * VK_DOWN Move selection to the next non-hidden item.
+ * VK_HOME Move selection to the first item.
+ * VK_END Move selection to the last item.
+ * VK_LEFT If expanded then collapse, otherwise move to parent.
+ * VK_RIGHT If collapsed then expand, otherwise move to first child.
+ * VK_ADD Expand.
+ * VK_SUBTRACT Collapse.
+ * VK_MULTIPLY Expand all.
+ * VK_PRIOR Move up GetVisibleCount items.
+ * VK_NEXT Move down GetVisibleCount items.
+ * VK_BACK Move to parent.
+ * CTRL-Left,Right,Up,Down,PgUp,PgDown,Home,End: Scroll without changing selection
+ */
+static LRESULT
+TREEVIEW_KeyDown(TREEVIEW_INFO *infoPtr, WPARAM wParam)
+{
+ /* If it is non-NULL and different, it will be selected and visible. */
+ TREEVIEW_ITEM *newSelection = NULL;
+
+ TREEVIEW_ITEM *prevItem = infoPtr->selectedItem;
+
+ TRACE("%x\n", wParam);
+
+ if (prevItem == NULL)
+ return FALSE;
+
+ if (GetAsyncKeyState(VK_CONTROL) & 0x8000)
+ return TREEVIEW_ScrollKeyDown(infoPtr, wParam);
+
+ switch (wParam)
+ {
+ case VK_UP:
+ newSelection = TREEVIEW_GetPrevListItem(infoPtr, prevItem);
+ if (!newSelection)
+ newSelection = infoPtr->root->firstChild;
+ break;
+
+ case VK_DOWN:
+ newSelection = TREEVIEW_GetNextListItem(infoPtr, prevItem);
+ break;
+
+ case VK_HOME:
+ newSelection = infoPtr->root->firstChild;
+ break;
+
+ case VK_END:
+ newSelection = TREEVIEW_GetLastListItem(infoPtr, infoPtr->root);
+ break;
+
+ case VK_LEFT:
+ if (prevItem->state & TVIS_EXPANDED)
+ {
+ TREEVIEW_Collapse(infoPtr, prevItem, FALSE, TRUE);
+ }
+ else if (prevItem->parent != infoPtr->root)
+ {
+ newSelection = prevItem->parent;
+ }
+ break;
+
+ case VK_RIGHT:
+ if (TREEVIEW_HasChildren(infoPtr, prevItem))
+ {
+ if (!(prevItem->state & TVIS_EXPANDED))
+ TREEVIEW_Expand(infoPtr, prevItem, FALSE, TRUE);
+ else
+ {
+ newSelection = prevItem->firstChild;
+ }
+ }
+
+ break;
+
+ case VK_MULTIPLY:
+ TREEVIEW_ExpandAll(infoPtr, prevItem);
+ break;
+
+ case VK_ADD:
+ if (!(prevItem->state & TVIS_EXPANDED))
+ TREEVIEW_Expand(infoPtr, prevItem, FALSE, TRUE);
+ break;
+
+ case VK_SUBTRACT:
+ if (prevItem->state & TVIS_EXPANDED)
+ TREEVIEW_Collapse(infoPtr, prevItem, FALSE, TRUE);
+ break;
+
+ case VK_PRIOR:
+ newSelection
+ = TREEVIEW_GetListItem(infoPtr, prevItem,
+ -TREEVIEW_GetVisibleCount(infoPtr));
+ break;
+
+ case VK_NEXT:
+ newSelection
+ = TREEVIEW_GetListItem(infoPtr, prevItem,
+ TREEVIEW_GetVisibleCount(infoPtr));
+ break;
+
+ case VK_BACK:
+ newSelection = prevItem->parent;
+ if (newSelection == infoPtr->root)
+ newSelection = NULL;
+ break;
+
+ case VK_SPACE:
+ if (infoPtr->dwStyle & TVS_CHECKBOXES)
+ TREEVIEW_ToggleItemState(infoPtr, prevItem);
+ break;
+ }
+
+ if (newSelection && newSelection != prevItem)
+ {
+ if (TREEVIEW_DoSelectItem(infoPtr, TVGN_CARET, newSelection,
+ TVC_BYKEYBOARD))
+ {
+ TREEVIEW_EnsureVisible(infoPtr, newSelection, FALSE);
+ }
+ }
+
+ return FALSE;
+}
+
+static LRESULT
+TREEVIEW_Size(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
+{
+ if (wParam == SIZE_RESTORED)
+ {
+ infoPtr->clientWidth = (LONG)(INT16)LOWORD(lParam);
+ infoPtr->clientHeight = (LONG)(INT16)HIWORD(lParam);
+
+ TREEVIEW_RecalculateVisibleOrder(infoPtr, NULL);
+ TREEVIEW_SetFirstVisible(infoPtr, infoPtr->firstVisible, TRUE);
+ TREEVIEW_UpdateScrollBars(infoPtr);
+ }
+ else
+ {
+ FIXME("WM_SIZE flag %x %lx not handled\n", wParam, lParam);
+ }
+
+ TREEVIEW_QueueRefresh(infoPtr);
+ return 0;
+}
+
+static LRESULT
+TREEVIEW_StyleChanged(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
+{
+ TRACE("(%x %lx)\n", wParam, lParam);
+
+ if (wParam == GWL_STYLE)
+ {
+ DWORD dwNewStyle = ((LPSTYLESTRUCT)lParam)->styleNew;
+
+ /* we have to take special care about tooltips */
+ if ((infoPtr->dwStyle ^ dwNewStyle) & TVS_NOTOOLTIPS)
+ {
+ if (infoPtr->dwStyle & TVS_NOTOOLTIPS)
+ {
+ infoPtr->hwndToolTip = COMCTL32_CreateToolTip(infoPtr->hwnd);
+ TRACE("\n");
+ }
+ else
+ {
+ DestroyWindow(infoPtr->hwndToolTip);
+ infoPtr->hwndToolTip = 0;
+ }
+ }
+
+ infoPtr->dwStyle = dwNewStyle;
+ }
+
+ TREEVIEW_UpdateSubTree(infoPtr, infoPtr->root);
+ TREEVIEW_UpdateScrollBars(infoPtr);
+ TREEVIEW_QueueRefresh(infoPtr);
+
+ return 0;
+}
+
+static LRESULT
+TREEVIEW_SetFocus(TREEVIEW_INFO *infoPtr)
+{
+ TRACE("\n");
+
+ if (!infoPtr->selectedItem)
+ {
+ TREEVIEW_DoSelectItem(infoPtr, TVGN_CARET, infoPtr->firstVisible,
+ TVC_UNKNOWN);
+ }
+
+ TREEVIEW_SendSimpleNotify(infoPtr, NM_SETFOCUS);
+ TREEVIEW_QueueItemRefresh(infoPtr, infoPtr->selectedItem);
+ return 0;
+}
+
+static LRESULT
+TREEVIEW_KillFocus(TREEVIEW_INFO *infoPtr)
+{
+ TRACE("\n");
+
+ TREEVIEW_SendSimpleNotify(infoPtr, NM_KILLFOCUS);
+ TREEVIEW_QueueItemRefresh(infoPtr, infoPtr->selectedItem);
+ return 0;
+}
+
+
+static LRESULT WINAPI
+TREEVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ TREEVIEW_INFO *infoPtr = TREEVIEW_GetInfoPtr(hwnd);
+ if (infoPtr) TREEVIEW_VerifyTree(infoPtr);
+ else
+ {
+ if (uMsg == WM_CREATE)
+ TREEVIEW_Create(hwnd);
+ else
+ goto def;
+ }
+
+ switch (uMsg)
+ {
+ case TVM_CREATEDRAGIMAGE:
+ return TREEVIEW_CreateDragImage(infoPtr, wParam, lParam);
+
+ case TVM_DELETEITEM:
+ return TREEVIEW_DeleteItem(infoPtr, (HTREEITEM)lParam);
+
+ case TVM_EDITLABELA:
+ return TREEVIEW_EditLabelA(infoPtr, (HTREEITEM)lParam);
+
+ case TVM_EDITLABELW:
+ FIXME("Unimplemented msg TVM_EDITLABELW\n");
+ return 0;
+
+ case TVM_ENDEDITLABELNOW:
+ return TREEVIEW_EndEditLabelNow(infoPtr, (BOOL)wParam);
+
+ case TVM_ENSUREVISIBLE:
+ return TREEVIEW_EnsureVisible(infoPtr, (HTREEITEM)lParam, TRUE);
+
+ case TVM_EXPAND:
+ return TREEVIEW_ExpandMsg(infoPtr, (UINT)wParam, (HTREEITEM)lParam);
+
+ case TVM_GETBKCOLOR:
+ return TREEVIEW_GetBkColor(infoPtr);
+
+ case TVM_GETCOUNT:
+ return TREEVIEW_GetCount(infoPtr);
+
+ case TVM_GETEDITCONTROL:
+ return TREEVIEW_GetEditControl(infoPtr);
+
+ case TVM_GETIMAGELIST:
+ return TREEVIEW_GetImageList(infoPtr, wParam);
+
+ case TVM_GETINDENT:
+ return TREEVIEW_GetIndent(infoPtr);
+
+ case TVM_GETINSERTMARKCOLOR:
+ return TREEVIEW_GetInsertMarkColor(infoPtr);
+
+ case TVM_GETISEARCHSTRINGA:
+ FIXME("Unimplemented msg TVM_GETISEARCHSTRINGA\n");
+ return 0;
+
+ case TVM_GETISEARCHSTRINGW:
+ FIXME("Unimplemented msg TVM_GETISEARCHSTRINGW\n");
+ return 0;
+
+ case TVM_GETITEMA:
+ return TREEVIEW_GetItemA(infoPtr, (LPTVITEMEXA)lParam);
+
+ case TVM_GETITEMW:
+ return TREEVIEW_GetItemW(infoPtr, (LPTVITEMEXA)lParam);
+
+ case TVM_GETITEMHEIGHT:
+ return TREEVIEW_GetItemHeight(infoPtr);
+
+ case TVM_GETITEMRECT:
+ return TREEVIEW_GetItemRect(infoPtr, (BOOL)wParam, (LPRECT)lParam);
+
+ case TVM_GETITEMSTATE:
+ return TREEVIEW_GetItemState(infoPtr, (HTREEITEM)wParam, (UINT)lParam);
+
+ case TVM_GETLINECOLOR:
+ return TREEVIEW_GetLineColor(infoPtr);
+
+ case TVM_GETNEXTITEM:
+ return TREEVIEW_GetNextItem(infoPtr, (UINT)wParam, (HTREEITEM)lParam);
+
+ case TVM_GETSCROLLTIME:
+ return TREEVIEW_GetScrollTime(infoPtr);
+
+ case TVM_GETTEXTCOLOR:
+ return TREEVIEW_GetTextColor(infoPtr);
+
+ case TVM_GETTOOLTIPS:
+ return TREEVIEW_GetToolTips(infoPtr);
+
+ case TVM_GETUNICODEFORMAT:
+ FIXME("Unimplemented msg TVM_GETUNICODEFORMAT\n");
+ return 0;
+
+ case TVM_GETVISIBLECOUNT:
+ return TREEVIEW_GetVisibleCount(infoPtr);
+
+ case TVM_HITTEST:
+ return TREEVIEW_HitTest(infoPtr, (LPTVHITTESTINFO)lParam);
+
+ case TVM_INSERTITEMA:
+ return TREEVIEW_InsertItemA(infoPtr, lParam);
+
+ case TVM_INSERTITEMW:
+ return TREEVIEW_InsertItemW(infoPtr, lParam);
+
+ case TVM_SELECTITEM:
+ return TREEVIEW_SelectItem(infoPtr, (INT)wParam, (HTREEITEM)lParam);
+
+ case TVM_SETBKCOLOR:
+ return TREEVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
+
+ case TVM_SETIMAGELIST:
+ return TREEVIEW_SetImageList(infoPtr, wParam, (HIMAGELIST)lParam);
+
+ case TVM_SETINDENT:
+ return TREEVIEW_SetIndent(infoPtr, (UINT)wParam);
+
+ case TVM_SETINSERTMARK:
+ return TREEVIEW_SetInsertMark(infoPtr, (BOOL)wParam, (HTREEITEM)lParam);
+
+ case TVM_SETINSERTMARKCOLOR:
+ return TREEVIEW_SetInsertMarkColor(infoPtr, (COLORREF)lParam);
+
+ case TVM_SETITEMA:
+ return TREEVIEW_SetItemA(infoPtr, (LPTVITEMEXA)lParam);
+
+ case TVM_SETITEMW:
+ FIXME("Unimplemented msg TVM_SETITEMW\n");
+ return 0;
+
+ case TVM_SETLINECOLOR:
+ return TREEVIEW_SetLineColor(infoPtr, (COLORREF)lParam);
+
+ case TVM_SETITEMHEIGHT:
+ return TREEVIEW_SetItemHeight(infoPtr, (INT)(SHORT)wParam);
+
+ case TVM_SETSCROLLTIME:
+ return TREEVIEW_SetScrollTime(infoPtr, (UINT)wParam);
+
+ case TVM_SETTEXTCOLOR:
+ return TREEVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
+
+ case TVM_SETTOOLTIPS:
+ return TREEVIEW_SetToolTips(infoPtr, (HWND)wParam);
+
+ case TVM_SETUNICODEFORMAT:
+ FIXME("Unimplemented msg TVM_SETUNICODEFORMAT\n");
+ return 0;
+
+ case TVM_SORTCHILDREN:
+ return TREEVIEW_SortChildren(infoPtr, wParam, lParam);
+
+ case TVM_SORTCHILDRENCB:
+ return TREEVIEW_SortChildrenCB(infoPtr, wParam, (LPTVSORTCB)lParam);
+
+ /* WM_CHAR */
+
+ case WM_COMMAND:
+ return TREEVIEW_Command(infoPtr, wParam, lParam);
+
+ case WM_DESTROY:
+ return TREEVIEW_Destroy(infoPtr);
+
+ /* WM_ENABLE */
+
+ case WM_ERASEBKGND:
+ return TREEVIEW_EraseBackground(infoPtr, (HDC)wParam);
+
+ case WM_GETDLGCODE:
+ return DLGC_WANTARROWS | DLGC_WANTCHARS;
+
+ case WM_GETFONT:
+ return TREEVIEW_GetFont(infoPtr);
+
+ case WM_HSCROLL:
+ return TREEVIEW_HScroll(infoPtr, wParam);
+
+ case WM_KEYDOWN:
+ return TREEVIEW_KeyDown(infoPtr, wParam);
+
+ case WM_KILLFOCUS:
+ return TREEVIEW_KillFocus(infoPtr);
+
+ case WM_LBUTTONDBLCLK:
+ return TREEVIEW_LButtonDoubleClick(infoPtr, lParam);
+
+ case WM_LBUTTONDOWN:
+ return TREEVIEW_LButtonDown(infoPtr, lParam);
+
+ /* WM_MBUTTONDOWN */
+
+ /* WM_MOUSEMOVE */
+
+ /* WM_NOTIFY */
+
+ /* WM_NOTIFYFORMAT */
+
+ case WM_PAINT:
+ return TREEVIEW_Paint(infoPtr, wParam);
+
+ /* WM_PRINTCLIENT */
+
+ case WM_RBUTTONDOWN:
+ return TREEVIEW_RButtonDown(infoPtr, lParam);
+
+ case WM_SETFOCUS:
+ return TREEVIEW_SetFocus(infoPtr);
+
+ case WM_SETFONT:
+ return TREEVIEW_SetFont(infoPtr, (HFONT)wParam, (BOOL)lParam);
+
+ case WM_SETREDRAW:
+ return TREEVIEW_SetRedraw(infoPtr, wParam, lParam);
+
+ case WM_SIZE:
+ return TREEVIEW_Size(infoPtr, wParam, lParam);
+
+ case WM_STYLECHANGED:
+ return TREEVIEW_StyleChanged(infoPtr, wParam, lParam);
+
+ /* WM_SYSCOLORCHANGE */
+
+ /* WM_SYSKEYDOWN */
+
+ case WM_TIMER:
+ return TREEVIEW_HandleTimer(infoPtr, wParam);
+
+ case WM_VSCROLL:
+ return TREEVIEW_VScroll(infoPtr, wParam);
+
+ /* WM_WININICHANGE */
+
+ case WM_MOUSEWHEEL:
+ if (wParam & (MK_SHIFT | MK_CONTROL))
+ goto def;
+ return TREEVIEW_MouseWheel(infoPtr, wParam);
+
+ case WM_DRAWITEM:
+ TRACE("drawItem\n");
+ goto def;
+
+ default:
+ /* This mostly catches MFC and Delphi messages. :( */
+ if (uMsg >= WM_USER)
+ TRACE("Unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
+def:
+ return DefWindowProcA(hwnd, uMsg, wParam, lParam);
+ }
+ return 0;
+}
+
+
+/* Class Registration ***************************************************/
+
VOID
-TREEVIEW_Register (void)
+TREEVIEW_Register(void)
{
WNDCLASSA wndClass;
TRACE("\n");
- ZeroMemory (&wndClass, sizeof(WNDCLASSA));
- wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
- wndClass.lpfnWndProc = (WNDPROC)TREEVIEW_WindowProc;
- wndClass.cbClsExtra = 0;
- wndClass.cbWndExtra = sizeof(TREEVIEW_INFO *);
- wndClass.hCursor = LoadCursorA (0, IDC_ARROWA);
+ ZeroMemory(&wndClass, sizeof(WNDCLASSA));
+ wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
+ wndClass.lpfnWndProc = (WNDPROC)TREEVIEW_WindowProc;
+ wndClass.cbClsExtra = 0;
+ wndClass.cbWndExtra = sizeof(TREEVIEW_INFO *);
+
+ wndClass.hCursor = LoadCursorA(0, IDC_ARROWA);
wndClass.hbrBackground = 0;
wndClass.lpszClassName = WC_TREEVIEWA;
-
- RegisterClassA (&wndClass);
+
+ RegisterClassA(&wndClass);
}
VOID
-TREEVIEW_Unregister (void)
+TREEVIEW_Unregister(void)
{
- UnregisterClassA (WC_TREEVIEWA, (HINSTANCE)NULL);
+ UnregisterClassA(WC_TREEVIEWA, (HINSTANCE) NULL);
}
+/* Tree Verification ****************************************************/
+#ifndef NDEBUG
+static inline void
+TREEVIEW_VerifyChildren(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item);
+
+static inline void TREEVIEW_VerifyItemCommon(TREEVIEW_INFO *infoPtr,
+ TREEVIEW_ITEM *item)
+{
+ assert(infoPtr != NULL);
+ assert(item != NULL);
+
+ /* both NULL, or both non-null */
+ assert((item->firstChild == NULL) == (item->lastChild == NULL));
+
+ assert(item->firstChild != item);
+ assert(item->lastChild != item);
+
+ if (item->firstChild)
+ {
+ assert(item->firstChild->parent == item);
+ assert(item->firstChild->prevSibling == NULL);
+ }
+
+ if (item->lastChild)
+ {
+ assert(item->lastChild->parent == item);
+ assert(item->lastChild->nextSibling == NULL);
+ }
+
+ assert(item->nextSibling != item);
+ if (item->nextSibling)
+ {
+ assert(item->nextSibling->parent == item->parent);
+ assert(item->nextSibling->prevSibling == item);
+ }
+
+ assert(item->prevSibling != item);
+ if (item->prevSibling)
+ {
+ assert(item->prevSibling->parent == item->parent);
+ assert(item->prevSibling->nextSibling == item);
+ }
+}
+
+static inline void
+TREEVIEW_VerifyItem(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item)
+{
+ assert(item != NULL);
+
+ assert(item->parent != NULL);
+ assert(item->parent != item);
+ assert(item->iLevel == item->parent->iLevel + 1);
+
+ assert(DPA_GetPtrIndex(infoPtr->items, item) != -1);
+
+ TREEVIEW_VerifyItemCommon(infoPtr, item);
+
+ TREEVIEW_VerifyChildren(infoPtr, item);
+}
+
+static inline void
+TREEVIEW_VerifyChildren(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item)
+{
+ TREEVIEW_ITEM *child;
+ assert(item != NULL);
+
+ for (child = item->firstChild; child != NULL; child = child->nextSibling)
+ TREEVIEW_VerifyItem(infoPtr, child);
+}
+
+static inline void
+TREEVIEW_VerifyRoot(TREEVIEW_INFO *infoPtr)
+{
+ TREEVIEW_ITEM *root = infoPtr->root;
+
+ assert(root != NULL);
+ assert(root->iLevel == -1);
+ assert(root->parent == NULL);
+ assert(root->prevSibling == NULL);
+
+ TREEVIEW_VerifyItemCommon(infoPtr, root);
+
+ TREEVIEW_VerifyChildren(infoPtr, root);
+}
+
+static void
+TREEVIEW_VerifyTree(TREEVIEW_INFO *infoPtr)
+{
+ assert(infoPtr != NULL);
+
+ TREEVIEW_VerifyRoot(infoPtr);
+}
+#endif