📄 treeview.c
字号:
/* Treeview control
*
* Copyright 1998 Eric Kohl <ekohl@abo.rhein-zeitung.de>
* Copyright 1998,1999 Alex Priem <alexp@sci.kun.nl>
* Copyright 1999 Sylvain St-Germain
* Copyright 2002 CodeWeavers, Aric Stewart
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*
* NOTES
*
* 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:
* missing notifications: NM_SETCURSOR, TVN_GETINFOTIP, TVN_KEYDOWN,
* TVN_SETDISPINFO, TVN_SINGLEEXPAND
*
* missing styles: TVS_FULLROWSELECT, TVS_INFOTIP, TVS_RTLREADING,
*
* missing item styles: TVIS_CUT, TVIS_EXPANDPARTIAL
*
* Make the insertion mark look right.
* Scroll (instead of repaint) as much as possible.
*/
#include "config.h"
#include "wine/port.h"
#include <assert.h>
#include <ctype.h>
#include <stdarg.h>
#include <string.h>
#include <limits.h>
#include <stdlib.h>
#define NONAMELESSUNION
#define NONAMELESSSTRUCT
#include "windef.h"
#include "winbase.h"
#include "wingdi.h"
#include "winuser.h"
#include "winnls.h"
#include "commctrl.h"
#include "comctl32.h"
#include "uxtheme.h"
#include "tmschema.h"
#include "wine/unicode.h"
#include "wine/debug.h"
/* internal structures */
typedef struct _TREEITEM /* HTREEITEM is a _TREEINFO *. */
{
UINT callbackMask;
UINT state;
UINT stateMask;
LPWSTR pszText;
int cchTextMax;
int iImage;
int iSelectedImage;
int cChildren;
LPARAM lParam;
int iIntegral; /* item height multiplier (1 is normal) */
int iLevel; /* indentation level:0=root level */
HTREEITEM parent; /* handle to parent or 0 if at root */
HTREEITEM firstChild; /* handle to first child or 0 if no child */
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;
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 tagTREEVIEW_INFO
{
HWND hwnd;
HWND hwndNotify; /* Owner window to send notifications to */
DWORD dwStyle;
HTREEITEM root;
UINT uInternalStatus;
INT Timer;
UINT uNumItems; /* number of valid TREEVIEW_ITEMs */
INT cdmode; /* last custom draw setting */
UINT uScrollTime; /* max. time for scrolling in milliseconds */
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 hotItem; /* handle currently under cursor, 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 */
HIMAGELIST dragList; /* Bitmap of dragged item */
LONG scrollX;
COLORREF clrBk;
COLORREF clrText;
COLORREF clrLine;
COLORREF clrInsertMark;
HFONT hFont;
HFONT hDefaultFont;
HFONT hBoldFont;
HFONT hUnderlineFont;
HCURSOR hcurHand;
HWND hwndToolTip;
HWND hwndEdit;
WNDPROC wpEditOrig; /* orig window proc for subclassing edit */
BOOL bIgnoreEditKillFocus;
BOOL bLabelChanged;
BOOL bNtfUnicode; /* TRUE if should send NOTIFY with W */
HIMAGELIST himlNormal;
int normalImageHeight;
int normalImageWidth;
HIMAGELIST himlState;
int stateImageHeight;
int stateImageWidth;
HDPA items;
DWORD lastKeyPressTimestamp; /* Added */
WPARAM charCode; /* Added */
INT nSearchParamLength; /* Added */
WCHAR szSearchParam[ MAX_PATH ]; /* Added */
} TREEVIEW_INFO;
/******** Defines that TREEVIEW_ProcessLetterKeys uses ****************/
#define KEY_DELAY 450
/* bitflags for infoPtr->uInternalStatus */
#define TV_HSCROLL 0x01 /* treeview too large to fit in window */
#define TV_VSCROLL 0x02 /* (horizontal/vertical) */
#define TV_LDRAG 0x04 /* Lbutton pushed to start drag */
#define TV_LDRAGGING 0x08 /* Lbutton pushed, mouse moved. */
#define TV_RDRAG 0x10 /* dito Rbutton */
#define TV_RDRAGGING 0x20
/* bitflags for infoPtr->timer */
#define TV_EDIT_TIMER 2
#define TV_EDIT_TIMER_SET 2
VOID TREEVIEW_Register (VOID);
VOID TREEVIEW_Unregister (VOID);
WINE_DEFAULT_DEBUG_CHANNEL(treeview);
#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)
static const WCHAR themeClass[] = { 'T','r','e','e','v','i','e','w',0 };
typedef VOID (*TREEVIEW_ItemEnumFunc)(TREEVIEW_INFO *, TREEVIEW_ITEM *,LPVOID);
static VOID TREEVIEW_Invalidate(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);
static INT TREEVIEW_NotifyFormat (TREEVIEW_INFO *infoPtr, HWND wParam, UINT lParam);
/* Random Utilities *****************************************************/
#ifndef NDEBUG
static inline void
TREEVIEW_VerifyTree(TREEVIEW_INFO *infoPtr)
{
(void)infoPtr;
}
#else
/* The definition is at the end of the file. */
static void TREEVIEW_VerifyTree(TREEVIEW_INFO *infoPtr);
#endif
/* 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 *)GetWindowLongPtrW(hwnd, 0);
}
/* 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);
}
/* Checks if item has changed and needs to be redrawn */
static inline BOOL item_changed (TREEVIEW_ITEM *tiOld, TREEVIEW_ITEM *tiNew, LPTVITEMEXW tvChange)
{
/* Number of children has changed */
if ((tvChange->mask & TVIF_CHILDREN) && (tiOld->cChildren != tiNew->cChildren))
return TRUE;
/* Image has changed and it's not a callback */
if ((tvChange->mask & TVIF_IMAGE) && (tiOld->iImage != tiNew->iImage) &&
tiNew->iImage != I_IMAGECALLBACK)
return TRUE;
/* Selected image has changed and it's not a callback */
if ((tvChange->mask & TVIF_SELECTEDIMAGE) && (tiOld->iSelectedImage != tiNew->iSelectedImage) &&
tiNew->iSelectedImage != I_IMAGECALLBACK)
return TRUE;
/* Text has changed and it's not a callback */
if ((tvChange->mask & TVIF_TEXT) && (tiOld->pszText != tiNew->pszText) &&
tiNew->pszText != LPSTR_TEXTCALLBACKW)
return TRUE;
/* Indent has changed */
if ((tvChange->mask & TVIF_INTEGRAL) && (tiOld->iIntegral != tiNew->iIntegral))
return TRUE;
/* Item state has changed */
if ((tvChange->mask & TVIF_STATE) && ((tiOld->state ^ tiNew->state) & tvChange->stateMask ))
return TRUE;
return FALSE;
}
/***************************************************************************
* This method checks that handle is an item for this tree.
*/
static BOOL
TREEVIEW_ValidItem(TREEVIEW_INFO *infoPtr, HTREEITEM handle)
{
if (TREEVIEW_GetItemIndex(infoPtr, handle) == -1)
{
TRACE("invalid item %p\n", handle);
return FALSE;
}
else
return TRUE;
}
static HFONT
TREEVIEW_CreateBoldFont(HFONT hOrigFont)
{
LOGFONTW font;
GetObjectW(hOrigFont, sizeof(font), &font);
font.lfWeight = FW_BOLD;
return CreateFontIndirectW(&font);
}
static HFONT
TREEVIEW_CreateUnderlineFont(HFONT hOrigFont)
{
LOGFONTW font;
GetObjectW(hOrigFont, sizeof(font), &font);
font.lfUnderline = TRUE;
return CreateFontIndirectW(&font);
}
static inline HFONT
TREEVIEW_FontForItem(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item)
{
if ((infoPtr->dwStyle & TVS_TRACKSELECT) && (item == infoPtr->hotItem))
return infoPtr->hUnderlineFont;
if (item->state & TVIS_BOLD)
return infoPtr->hBoldFont;
return 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_TEXTCALLBACKW) return "<callback>";
if (item->pszText == NULL) return "<null>";
return debugstr_w(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 non-hidden item in the list not
* considering the tree hierarchy.
*/
static TREEVIEW_ITEM *
TREEVIEW_GetPrevListItem(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *tvItem)
{
if (tvItem->prevSibling)
{
/* This item has a prevSibling, get the last item in the sibling's tree. */
TREEVIEW_ITEM *upItem = tvItem->prevSibling;
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;
}
}
/***************************************************************************
* 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)
{
assert(tvItem != NULL);
/*
* 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->nextSibling)
return tvItem->nextSibling;
/*
* Otherwise, get the parent's sibling.
*/
while (tvItem->parent)
{
tvItem = tvItem->parent;
if (tvItem->nextSibling)
return tvItem->nextSibling;
}
return NULL;
}
/***************************************************************************
* This method returns the nth item starting at the given item. It returns
* the last item (or first) we we run out of items.
*
* Will scroll backward if count is <0.
* forward if count is >0.
*/
static TREEVIEW_ITEM *
TREEVIEW_GetListItem(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *wineItem,
LONG count)
{
TREEVIEW_ITEM *(*next_item)(TREEVIEW_INFO *, TREEVIEW_ITEM *);
TREEVIEW_ITEM *previousItem;
assert(wineItem != NULL);
if (count > 0)
{
next_item = TREEVIEW_GetNextListItem;
}
else if (count < 0)
{
count = -count;
next_item = TREEVIEW_GetPrevListItem;
}
else
return wineItem;
do
{
previousItem = wineItem;
wineItem = next_item(infoPtr, wineItem);
} while (--count && wineItem != NULL);
return wineItem ? wineItem : previousItem;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -