📄 xhtmltree.cpp
字号:
// XHtmlTree.cpp Version 1.6 - article available at www.codeproject.com
//
// Author: Hans Dietrich
// hdietrich@gmail.com
//
// History
// Version 1.6 - 2007 December 19
// - Bug fixes and enhancements; see CodeProject article for details
//
// Version 1.5 - 2007 November 7
// - Bug fixes and enhancements; see CodeProject article for details
//
// Version 1.4 - 2007 November 4
// - Bug fixes and enhancements; see CodeProject article for details
//
// Version 1.3 - 2007 October 16
// - Bug fixes and enhancements; see CodeProject article for details
//
// Version 1.2 - 2007 October 7
// - Bug fixes and enhancements; see CodeProject article for details
//
// Version 1.1 - 2007 October 6
// - Bug fixes and enhancements; see CodeProject article for details
//
// Version 1.0 - 2007 August 9
// - Initial public release
//
// License:
// This software is released into the public domain. You are free to use
// it in any way you like, except that you may not sell this source code.
//
// This software is provided "as is" with no expressed or implied warranty.
// I accept no liability for any damage or loss of business that this
// software may cause.
//
///////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#pragma warning(disable : 4786)
#include "XHtmlTree.h"
#include "XNamedColors.h"
#include "XHtmlDraw.h"
#include "CreateCheckboxImageList.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#ifndef __noop
#if _MSC_VER < 1300
#define __noop ((void)0)
#endif
#endif
#undef TRACE
#define TRACE __noop
//=============================================================================
// if you want to see the TRACE output, uncomment this line:
#include "XTrace.h"
//=============================================================================
//=============================================================================
// REGISTERED XHTMLTREE MESSAGES
//=============================================================================
UINT WM_XHTMLTREE_CHECKBOX_CLICKED = ::RegisterWindowMessage(_T("WM_XHTMLTREE_CHECKBOX_CLICKED"));
UINT WM_XHTMLTREE_ITEM_EXPANDED = ::RegisterWindowMessage(_T("WM_XHTMLTREE_ITEM_EXPANDED"));
UINT WM_XHTMLTREE_DISPLAY_TOOLTIP = ::RegisterWindowMessage(_T("WM_XHTMLTREE_DISPLAY_TOOLTIP"));
UINT WM_XHTMLTREE_INIT_TOOLTIP = ::RegisterWindowMessage(_T("WM_XHTMLTREE_INIT_TOOLTIP"));
#ifdef XHTMLDRAGDROP
UINT WM_XHTMLTREE_BEGIN_DRAG = ::RegisterWindowMessage(_T("WM_XHTMLTREE_BEGIN_DRAG"));
UINT WM_XHTMLTREE_END_DRAG = ::RegisterWindowMessage(_T("WM_XHTMLTREE_END_DRAG"));
UINT WM_XHTMLTREE_DROP_HOVER = ::RegisterWindowMessage(_T("WM_XHTMLTREE_DROP_HOVER"));
#endif // XHTMLDRAGDROP
#ifdef XHTMLTREE_DEMO
UINT WM_XHTMLTREE_SCROLL_SPEED = ::RegisterWindowMessage(_T("WM_XHTMLTREE_SCROLL_SPEED"));
#endif // XHTMLTREE_DEMO
#pragma warning(disable : 4996) // disable bogus deprecation warning
const UINT HOT_TIMER = 1;
const UINT LBUTTONDOWN_TIMER = 2;
const UINT CTRL_UP_TIMER = 3;
const UINT SHIFT_UP_TIMER = 4;
const UINT SELECT_TIMER = 5;
const UINT TOOLTIP_BASE_ID = 10000;
const DWORD MIN_HOVER_TIME = 1500; // 1.5 seconds
const int SCROLL_ZONE = 16; // pixels for scrolling
int XHTMLTREEDATA::nCount = 0;
//=============================================================================
BEGIN_MESSAGE_MAP(CXHtmlTree, CTreeCtrl)
//=============================================================================
//{{AFX_MSG_MAP(CXHtmlTree)
ON_WM_DESTROY()
ON_WM_ERASEBKGND()
ON_WM_MOUSEMOVE()
ON_WM_SYSCOLORCHANGE()
ON_WM_TIMER()
ON_WM_LBUTTONDOWN()
ON_WM_SIZE()
ON_WM_RBUTTONDOWN()
ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnCustomDraw)
ON_NOTIFY_REFLECT_EX(NM_CLICK, OnClick)
ON_NOTIFY_REFLECT_EX(NM_DBLCLK, OnDblclk)
ON_NOTIFY_REFLECT_EX(TVN_BEGINLABELEDIT, OnBeginlabeledit)
ON_NOTIFY_REFLECT_EX(TVN_ENDLABELEDIT, OnEndlabeledit)
//}}AFX_MSG_MAP
#ifdef XHTMLDRAGDROP
ON_NOTIFY_REFLECT(TVN_BEGINDRAG, OnBegindrag)
#endif // XHTMLDRAGDROP
#ifdef XHTMLTOOLTIPS
ON_NOTIFY(UDM_TOOLTIP_DISPLAY, NULL, OnDisplayTooltip)
#else
ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTW, 0, 0xFFFF, OnToolTipText)
ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA, 0, 0xFFFF, OnToolTipText)
#endif // XHTMLTOOLTIPS
ON_NOTIFY_REFLECT_EX(TVN_SELCHANGED, OnSelchanged)
ON_NOTIFY_REFLECT_EX(TVN_SELCHANGING, OnSelchanging)
END_MESSAGE_MAP()
//=============================================================================
CXHtmlTree::CXHtmlTree()
//=============================================================================
: m_bDestroyingTree(FALSE),
m_bFirstTime(TRUE),
m_bSmartCheck(FALSE),
m_bCheckBoxes(FALSE),
m_bSelectFollowsCheck(TRUE),
m_bReadOnly(FALSE),
m_bHtml(TRUE),
m_bStripHtml(FALSE),
m_bLogFont(FALSE),
m_bToolTip(FALSE),
m_bDragging(FALSE),
m_bAutoScroll(TRUE),
m_bImages(TRUE),
m_pToolTip(0),
m_hAnchorItem(0),
m_hHotItem(0),
m_hPreviousItem(0),
m_hItemButtonDown(0),
m_hPreviousDropItem(0),
m_nPadding(0),
m_nImageHeight(16),
m_nToolCount(0),
m_nDefaultTipWidth(0),
m_nScrollTime(0),
m_crCustomWindow(COLOR_NONE),
m_crCustomWindowText(COLOR_NONE),
m_nHorzPos(0),
m_dwDropHoverTime(0),
m_nNoDropCursor(0),
m_nDropCopyCursor(0),
m_nDropMoveCursor(0),
m_hNoDropCursor(0),
m_hDropCopyCursor(0),
m_hDropMoveCursor(0),
m_hPreviousCursor(0),
m_hCurrentCursor(0),
m_dwDragOps(XHTMLTREE_DO_DEFAULT)
{
TRACE(_T("in CXHtmlTree::CXHtmlTree\n"));
memset(&m_lf, 0, sizeof(m_lf));
SetColors();
m_hPreviousCursor = AfxGetApp()->LoadStandardCursor(IDC_ARROW);
}
//=============================================================================
CXHtmlTree::~CXHtmlTree()
//=============================================================================
{
if (m_pToolTip)
delete m_pToolTip;
m_pToolTip = 0;
if (m_StateImage.GetSafeHandle())
m_StateImage.DeleteImageList();
if (m_hNoDropCursor)
DestroyCursor(m_hNoDropCursor);
m_hNoDropCursor = NULL;
if (m_hDropCopyCursor)
DestroyCursor(m_hDropCopyCursor);
m_hDropCopyCursor = NULL;
if (m_hDropMoveCursor)
DestroyCursor(m_hDropMoveCursor);
m_hDropMoveCursor = NULL;
TRACE(_T("XHTMLTREEDATA::nCount=%d\n"), XHTMLTREEDATA::nCount);
}
//=============================================================================
CXHtmlTree& CXHtmlTree::Initialize(BOOL bCheckBoxes /*= FALSE*/,
BOOL bToolTip /*= FALSE*/)
//=============================================================================
{
TRACE(_T("in CXHtmlTree::Initialize\n"));
m_bDestroyingTree = TRUE;
DeleteAllItems();
if (m_pToolTip)
delete m_pToolTip;
m_pToolTip = 0;
SetImageList(NULL, TVSIL_STATE);
if (m_StateImage.GetSafeHandle())
m_StateImage.DeleteImageList();
m_bCheckBoxes = bCheckBoxes;
m_bToolTip = bToolTip;
m_bSmartCheck = FALSE;
m_bSelectFollowsCheck = TRUE;
m_bHtml = TRUE;
m_bLogFont = FALSE;
m_nPadding = 0;
m_nImageHeight = 16;
m_bFirstTime = TRUE;
m_hAnchorItem = 0;
m_hHotItem = 0;
memset(&m_lf, 0, sizeof(m_lf));
SetColors();
if (m_bToolTip)
{
TRACE(_T("creating tooltip\n"));
#ifdef XHTMLTOOLTIPS
m_pToolTip = new CPPToolTip;
#else
m_pToolTip = new CToolTipCtrl;
#endif // XHTMLTOOLTIPS
if (m_pToolTip)
{
m_pToolTip->Create(this);
}
}
if (m_bCheckBoxes)
CreateCheckboxImages();
m_bDestroyingTree = FALSE;
return *this;
}
//=============================================================================
// PreCreateWindow() is called when CXHtmlTree is used in a view.
//
BOOL CXHtmlTree::PreCreateWindow(CREATESTRUCT& cs)
//=============================================================================
{
TRACE(_T("in CXHtmlTree::PreCreateWindow\n"));
// style must include "no tooltips"
cs.style |= TVS_NOTOOLTIPS;
return CTreeCtrl::PreCreateWindow(cs);
}
//=============================================================================
void CXHtmlTree::PreSubclassWindow()
//=============================================================================
{
TRACE(_T("in CXHtmlTree::PreSubclassWindow\n"));
DWORD dwStyle = GetStyle();
if (dwStyle & TVS_CHECKBOXES)
m_bCheckBoxes = TRUE;
// these styles must not be set
ModifyStyle(TVS_CHECKBOXES, TVS_NOTOOLTIPS);
#ifdef XHTMLDRAGDROP
ModifyStyle(TVS_DISABLEDRAGDROP, 0);
#else
ModifyStyle(0, TVS_DISABLEDRAGDROP);
#endif // XHTMLDRAGDROP
if (m_bCheckBoxes)
CreateCheckboxImages();
CTreeCtrl::PreSubclassWindow();
}
//=============================================================================
BOOL CXHtmlTree::PreTranslateMessage(MSG* pMsg)
//=============================================================================
{
// allow edit control to receive messages, if
// label is being edited
if (GetEditControl() &&
((pMsg->message == WM_CHAR) ||
(pMsg->message == WM_KEYDOWN) ||
GetKeyState(VK_CONTROL)))
{
::TranslateMessage(pMsg);
::DispatchMessage(pMsg);
return TRUE;
}
if (m_pToolTip && IsWindow(m_pToolTip->m_hWnd))
{
m_pToolTip->RelayEvent(pMsg);
}
//=========================================================================
// WM_CHAR
//=========================================================================
if (pMsg->message == WM_CHAR)
{
if ((pMsg->wParam == VK_SPACE) && m_bCheckBoxes && !m_bReadOnly)
{
HTREEITEM hItem = GetSelectedItem();
XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);
if (pXTCD && !pXTCD->bSeparator) //+++1.6
{
SetCheck(hItem, !pXTCD->bChecked);
}
return TRUE;
}
}
//=========================================================================
// WM_KEYDOWN
//=========================================================================
if (pMsg->message == WM_KEYDOWN)
{
TRACE(_T("WM_KEYDOWN: lParam=0x%X\n"), pMsg->lParam);
#ifdef XHTMLDRAGDROP
//=====================================================================
// VK_ESCAPE while dragging
//=====================================================================
if ((pMsg->wParam == VK_ESCAPE) && m_bDragging)
{
TRACE(_T("ESC seen during drag\n"));
EndDragScroll();
SendRegisteredMessage(WM_XHTMLTREE_END_DRAG, 0, 0);
return TRUE;
}
//=====================================================================
// VK_CONTROL while dragging
//=====================================================================
if ((pMsg->wParam == VK_CONTROL) &&
(m_dwDragOps & XHTMLTREE_DO_CTRL_KEY))
{
// check if Ctrl key down for first time
if ((pMsg->lParam & 0x40000000) == 0)
{
if (IsOverItem() && m_bDragging)
{
SetDragCursor();
}
SetTimer(CTRL_UP_TIMER, 100, NULL);
}
}
//=====================================================================
// VK_SHIFT while dragging
//=====================================================================
if ((pMsg->wParam == VK_SHIFT) &&
(m_dwDragOps & XHTMLTREE_DO_SHIFT_KEY))
{
// check if Shift key down for first time
if ((pMsg->lParam & 0x40000000) == 0)
{
HTREEITEM hItem = IsOverItem();
if (hItem && m_bDragging)
{
if (IsSeparator(hItem)) //+++1.6
{
SelectDropTarget(NULL);
SetInsertMark(0, 0);
SetInsertMark(hItem, TRUE);
}
else
{
SetInsertMark(0, 0);
SelectDropTarget(hItem);
}
}
SetTimer(SHIFT_UP_TIMER, 100, NULL);
}
}
#endif // XHTMLDRAGDROP
//=====================================================================
// VK_RIGHT or VK_LEFT
//=====================================================================
if ((pMsg->wParam == VK_RIGHT) || (pMsg->wParam == VK_LEFT))
{
BOOL bRight = pMsg->wParam == VK_RIGHT;
HTREEITEM hItem = GetSelectedItem();
XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);
if (pXTCD && pXTCD->bEnabled && pXTCD->nChildren)
{
BOOL bExpanded = pXTCD->bExpanded;
BOOL bOldExpanded = pXTCD->bExpanded;
if (!bExpanded && bRight)
bExpanded = TRUE;
if (bExpanded && !bRight)
bExpanded = FALSE;
if (bOldExpanded != bExpanded)
{
Expand(hItem, bExpanded ? TVE_EXPAND : TVE_COLLAPSE);
return TRUE;
}
}
}
//=====================================================================
// VK_DOWN or VK_UP
//=====================================================================
if ((pMsg->wParam == VK_DOWN) || (pMsg->wParam == VK_UP))
{
BOOL bDown = pMsg->wParam == VK_DOWN;
HTREEITEM hItem = GetSelectedItem();
if (hItem)
{
HTREEITEM hItemNew = bDown ? GetNextVisibleItem(hItem) :
GetPrevVisibleItem(hItem);
XHTMLTREEDATA *pXTCD = NULL;
while (hItemNew)
{
pXTCD = GetItemDataStruct(hItemNew);
if (pXTCD && pXTCD->bEnabled)
break;
// next item is not enabled, just skip it
hItemNew = bDown ? GetNextVisibleItem(hItemNew) :
GetPrevVisibleItem(hItemNew);
}
if (hItemNew)
{
SelectItem(hItemNew);
return TRUE;
}
}
}
//=====================================================================
// VK_MULTIPLY
//=====================================================================
if (pMsg->wParam == VK_MULTIPLY)
{
HTREEITEM hItem = GetSelectedItem();
if (hItem)
{
ExpandBranch(hItem);
EnsureVisible(hItem);
SendMessage(WM_HSCROLL, SB_LEFT);
return TRUE;
}
}
//=====================================================================
// VK_SUBTRACT VK_ADD
//=====================================================================
UINT nCode = 0;
switch (pMsg->wParam)
{
default: break;
case VK_SUBTRACT: nCode = TVE_COLLAPSE; break;
case VK_ADD: nCode = TVE_EXPAND; break;
}
if (nCode)
{
HTREEITEM hItem = GetSelectedItem();
if (hItem)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -