// 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)
{
Expand(hItem, nCode);
return TRUE; // skip default processing
}
}
}
return CTreeCtrl::PreTranslateMessage(pMsg);
}
//=============================================================================
void CXHtmlTree::OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult)
//=============================================================================
{
NMTVCUSTOMDRAW* pCD = reinterpret_cast<NMTVCUSTOMDRAW*>(pNMHDR);
CDC* pDC = CDC::FromHandle(pCD->nmcd.hdc);
HTREEITEM hItem = reinterpret_cast<HTREEITEM> (pCD->nmcd.dwItemSpec);
// Take the default processing unless we set this to something else below.
*pResult = CDRF_DODEFAULT;
// First thing - check the draw stage. If it's the control's prepaint
// stage, then tell Windows we want messages for every item.
//=========================================================================
if (pCD->nmcd.dwDrawStage == CDDS_PREPAINT) // before the painting cycle begins
//=========================================================================
{
*pResult = CDRF_NOTIFYITEMDRAW /*| CDRF_NOTIFYPOSTPAINT*/;
}
//=========================================================================
else if (pCD->nmcd.dwDrawStage == CDDS_ITEMPREPAINT) // before an item is drawn
//=========================================================================
{
pCD->clrText = pCD->clrTextBk; // don't want default drawing -
// set text color = background color
if (hItem)
{
CRect rectItem1;
GetItemRect(hItem, &rectItem1, FALSE); // get rect for item
if (!IsBadRect(rectItem1))
{
CBrush brush(m_crWindow);
pDC->FillRect(&rectItem1, &brush); // erase entire background
}
}
*pResult = CDRF_NOTIFYPOSTPAINT | CDRF_NEWFONT;
}
//=========================================================================
else if (pCD->nmcd.dwDrawStage == CDDS_ITEMPOSTPAINT) // after an item has been drawn
//=========================================================================
{
// by doing the drawing at this stage we avoid having to draw lines, etc.
if (m_bFirstTime)
{
if (m_bToolTip)
CreateToolTipsForTree();
CImageList *pImageList = GetImageList(TVSIL_NORMAL);
if (!pImageList) //+++1.5
{
TRACE(_T("WARNING no image list, setting m_bImages to FALSE\n"));
m_bImages = FALSE;
}
}
m_bFirstTime = FALSE;
CRect rectItem;
GetItemRect(hItem, &rectItem, FALSE); // get rect for entire item
CRect rectText;
GetItemRect(hItem, &rectText, TRUE); // get rect for text
rectText.right = rectItem.right;
XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);
// set up colors
COLORREF crText = m_crWindowText;
COLORREF crAnchorText = m_crAnchorText;
COLORREF crBackground = m_crWindow;
COLORREF crTextBackground = m_crWindow;
BOOL bEnabled = TRUE;
HTREEITEM hSelected = GetSelectedItem();
if (pXTCD)
{
// try to use colors specified for this item
pXTCD->ds.bIgnoreColorTag = FALSE;
crText = pXTCD->ds.crText;
crTextBackground = pXTCD->ds.crTextBackground;
crBackground = pXTCD->ds.crBackground;
//TRACE(_T("crText=%08X crBkgnd=%08X ~~~~~\n"), crText, crBackground);
bEnabled = pXTCD->bEnabled;
if ((hItem == hSelected) ||
(GetItemState(hItem, TVIF_STATE) & TVIS_DROPHILITED))
{
crTextBackground = m_crHighlight;
}
else
{
//crTextBackground = COLOR_NONE;
}
if (!bEnabled)
{
crText = crAnchorText = m_crGrayText;
pXTCD->ds.bIgnoreColorTag = TRUE;
}
else if ((hItem == hSelected) ||
(GetItemState(hItem, TVIF_STATE) & TVIS_DROPHILITED))
{
crText = crAnchorText = m_crHighlightText;
pXTCD->ds.bIgnoreColorTag = TRUE;
}
else
{
}
}
if (crBackground == COLOR_NONE)
crBackground = m_crWindow;
if (pXTCD && pXTCD->bSeparator) //+++1.6
{
if (crText == COLOR_NONE)
crText = m_crSeparator;
if (hItem == hSelected)
crBackground = m_crHighlight;
DrawSeparator(pDC, hItem, crText, crBackground, rectText);
}
else
{
if (crText == COLOR_NONE)
crText = m_crWindowText;
CString strText = GetItemText(hItem);
BOOL bContainsHtml = FALSE;
// check for html tag and char entity
if (strText.FindOneOf(_T("<&")) >= 0)
bContainsHtml = TRUE;
if (m_bStripHtml && bContainsHtml)
strText = GetItemText(hItem, TRUE);
#ifdef XHTMLHTML
if (m_bHtml && bContainsHtml)
DrawItemTextHtml(pDC, hItem, strText, crText, crTextBackground,
crBackground, crAnchorText, rectText);
else
#endif // XHTMLHTML
DrawItemText(pDC, hItem, strText, crText, crTextBackground, crBackground, rectText);
}
//*pResult = CDRF_SKIPDEFAULT; // We've painted everything.
}
}
//=============================================================================
BOOL CXHtmlTree::SelectItem(HTREEITEM hItem)
//=============================================================================
{
HTREEITEM hPrevItemSel = GetSelectedItem();
if (hItem == hPrevItemSel)
return TRUE;
XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);
if (pXTCD && pXTCD->bEnabled)
{
NMTREEVIEW nmtv = { 0 };
nmtv.hdr.hwndFrom = m_hWnd;
nmtv.hdr.idFrom = GetDlgCtrlID();
nmtv.hdr.code = TVN_SELCHANGED;
nmtv.itemNew.hItem = hItem;
CWnd *pWnd = GetParent();
if (!pWnd)
pWnd = GetOwner();
if (pWnd && ::IsWindow(pWnd->m_hWnd))
{
pWnd->SendMessage(WM_NOTIFY, GetDlgCtrlID(), (LPARAM)&nmtv);
}
}
else
{
if (hPrevItemSel)
hItem = hPrevItemSel;
else
return TRUE;
}
return CTreeCtrl::SelectItem(hItem);
}
//=============================================================================
BOOL CXHtmlTree::IsSelected(HTREEITEM hItem)
//=============================================================================
{
BOOL rc = FALSE;
if (hItem == GetSelectedItem())
rc = TRUE;
return rc;
}
//=============================================================================
BOOL CXHtmlTree::IsEnabled(HTREEITEM hItem)
//=============================================================================
{
BOOL rc = FALSE;
XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);
if (pXTCD)
{
rc = pXTCD->bEnabled;
}
return rc;
}
//=============================================================================
BOOL CXHtmlTree::IsExpanded(HTREEITEM hItem)
//=============================================================================
{
BOOL rc = FALSE;
XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);
if (pXTCD)
{
rc = pXTCD->bExpanded && ItemHasChildren(hItem);
}
return rc;
}
//=============================================================================
BOOL CXHtmlTree::IsSeparator(HTREEITEM hItem) //+++1.6
//=============================================================================
{
BOOL rc = FALSE;
XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);
if (pXTCD)
{
rc = pXTCD->bSeparator;
}
return rc;
}
//=============================================================================
BOOL CXHtmlTree::GetItemPath(HTREEITEM hItem, CStringArray& sa, CPtrArray& items)
//=============================================================================
{
BOOL rc = FALSE;
sa.RemoveAll();
items.RemoveAll();
if (hItem == NULL)
hItem = GetRootItem();
if (hItem)
{
CStringArray path;
CPtrArray htreeitems;
// get the path in reverse order
while (hItem)
{
CString strText = GetItemText(hItem);
#ifdef XHTMLHTML
// remove html tags
CXHtmlDraw hd;
TCHAR s[200];
hd.GetPlainText(strText, s, sizeof(s)/sizeof(TCHAR)-1);
strText = s;
#endif // XHTMLHTML
path.Add(strText);
htreeitems.Add(hItem);
hItem = GetParentItem(hItem);
}
int n = (int) path.GetSize();
if (n)
{
// return path in correct order
for (int i = n-1; i >= 0 ; i--)
{
sa.Add(path[i]);
items.Add(htreeitems[i]);
}
rc = TRUE;
}
}
return rc;
}
//=============================================================================
int CXHtmlTree::GetDefaultTipWidth()
//=============================================================================
{
int nWidth = 200;
if (m_nDefaultTipWidth == 0)
{
// no default width specified, use a heuristic
CWnd *pWnd = GetParent();
if (!pWnd)
pWnd = GetOwner();
if (pWnd && ::IsWindow(pWnd->m_hWnd))
{
CRect rectParent;
pWnd->GetWindowRect(&rectParent);
CRect rectTree;
GetWindowRect(&rectTree);
int nWidthTree = (3 * rectTree.Width()) / 4;
int nWidthParent = rectParent.Width() / 2;
nWidth = (nWidthTree < 200) ? nWidthParent : nWidthTree;
}
}
else
{
nWidth = m_nDefaultTipWidth;
}
return nWidth;
}
//=============================================================================
void CXHtmlTree::CreateToolTipsForTree()
//=============================================================================
{
if ((m_pToolTip == 0) || (!IsWindow(m_pToolTip->m_hWnd)))
return;
#ifdef XHTMLTOOLTIPS
m_pToolTip->SetNotify(TRUE);
#endif // XHTMLTOOLTIPS
m_pToolTip->SetMaxTipWidth(GetDefaultTipWidth());
// first delete all existing tools
int nCount = m_nToolCount; //m_pToolTip->GetToolCount();
for (int j = 0; j < nCount; j++)
m_pToolTip->DelTool(this, TOOLTIP_BASE_ID+j);
m_nToolCount = 0;
CRect rect;
GetClientRect(rect);
CRect rectItem;
GetItemRect(GetFirstVisibleItem(), &rectItem, FALSE);
ASSERT(!IsBadRect(rectItem));
rect.top = 0;
rect.bottom = rect.top + rectItem.Height()-1;
UINT n = GetVisibleCount();
// loop to add a tool for each visible item
UINT i = 0;
for (i = 0; i < n; i++)
{
#ifdef XHTMLTOOLTIPS
PPTOOLTIP_INFO ti;
ti.nBehaviour = PPTOOLTIP_MULTIPLE_SHOW;
ti.nIDTool = 123;
ti.rectBounds = rect;
ti.sTooltip = "";
ti.nMask = PPTOOLTIP_MASK_BEHAVIOUR;
m_pToolTip->AddTool(this, ti);
#else
m_pToolTip->AddTool(this, LPSTR_TEXTCALLBACK, rect, TOOLTIP_BASE_ID+i);
#endif // XHTMLTOOLTIPS
rect.top = rect.bottom+1;
rect.bottom = rect.top + rectItem.Height()-1;
}
m_nToolCount = i;
if (m_pToolTip)
{
// allow parent to perform custom initializatio of tooltip
SendRegisteredMessage(WM_XHTMLTREE_INIT_TOOLTIP, 0, (LPARAM)m_pToolTip);
}
}
//=============================================================================
// GetNormalImageWidth
// returns: width - if image is specified
// -width - TV_NOIMAGE is specified for this item
// 0 - no image list
int CXHtmlTree::GetNormalImageWidth(HTREEITEM hItem)
//=============================================================================
{
int nWidth = 0;
CImageList *pImageList = GetImageList(TVSIL_NORMAL);
if (pImageList && hItem)
{
// there is an image list
int nImage = TV_NOIMAGE;
int nSelectedImage = TV_NOIMAGE;
GetItemImage(hItem, nImage, nSelectedImage);
IMAGEINFO ii = { 0 };
if (pImageList->GetImageInfo(0, &ii)) // use first image width
{
nWidth = ii.rcImage.right - ii.rcImage.left;
}
if (nImage == TV_NOIMAGE)
nWidth = -nWidth;
}
return nWidth;
}
//=============================================================================
BOOL CXHtmlTree::CreateCheckboxImages()
//=============================================================================
{
CDC *pDC = GetDC();
ASSERT(pDC);
BOOL rc = HDCheckboxImageList::CreateCheckboxImageList(pDC, m_StateImage,
m_nImageHeight, m_crWindow);
ReleaseDC(pDC);
SetImageList(&m_StateImage, TVSIL_STATE);
return rc;
}
//=============================================================================
int CXHtmlTree::DrawItemText(CDC *pDC,
HTREEITEM hItem,
LPCTSTR lpszText,
COLORREF crText,
COLORREF crTextBackground,
COLORREF crBackground,
CRect& rect)
//=============================================================================
{
ASSERT(pDC);
ASSERT(hItem);
if (!pDC || !hItem)
{
TRACE(_T("ERROR bad parameters\n"));
return 0;
}
if (IsBadRect(rect))
{
return 0;
}
int nWidth = 0;
CRect rectText(rect);
pDC->FillSolidRect(&rectText, crBackground);
CString str = lpszText;
//TRACE(_T("CXHtmlTree::DrawItemText: crText=%08X crBkgnd=%08X <%s> ++++++\n"), crText, crBackground, str);
XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);
if (pXTCD && !str.IsEmpty())
{
UINT uFormat = DT_VCENTER | DT_SINGLELINE | DT_LEFT | DT_NOPREFIX;
CFont *pOldFont = NULL;
CFont font;
CFont *pFont = pDC->GetCurrentFont();
if (pFont)
{
LOGFONT lf;
pFont->GetLogFont(&lf);
lf.lfWeight = pXTCD->ds.bBold ? FW_BOLD : FW_NORMAL;
lf.lfUnderline = (BYTE) pXTCD->ds.bUnderline;
lf.lfItalic = (BYTE) pXTCD->ds.bItalic;
lf.lfStrikeOut = (BYTE) pXTCD->ds.bStrikeThrough;
font.CreateFontIndirect(&lf);
pOldFont = pDC->SelectObject(&font);
}
pDC->SetTextColor(crText);
if (crTextBackground == COLOR_NONE)
pDC->SetBkColor(crBackground);
else
pDC->SetBkColor(crTextBackground);
CRect rectOut(rectText);
pDC->DrawText(str, &rectOut, uFormat | DT_CALCRECT);
pDC->DrawText(str, &rectOut, uFormat);
rectOut.InflateRect(m_nPadding, 0);
nWidth = rectOut.right;
pXTCD->ds.nRightX = rectOut.right;
if (pOldFont)
pDC->SelectObject(pOldFont);
}
return nWidth;
}
#ifdef XHTMLHTML
//=============================================================================
int CXHtmlTree::DrawItemTextHtml(CDC *pDC,
HTREEITEM hItem,
LPCTSTR lpszText,
COLORREF crText,
COLORREF crTextBackground,
COLORREF crBackground,
COLORREF crAnchorText,
CRect& rect)
//=============================================================================
{
ASSERT(pDC);
ASSERT(hItem);
if (!pDC || !hItem)
{
TRACE(_T("ERROR bad parameters\n"));
return 0;
}
if (IsBadRect(rect))
{
return 0;
}
XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);
if (!pXTCD)
{
TRACE(_T("ERROR no XHTMLTREEDATA\n"));
return 0;
}
COLORREF crTextOld, crTextBackgroundOld, crBackgroundOld, crAnchorTextOld;
crTextOld = pXTCD->ds.crText;
crTextBackgroundOld = pXTCD->ds.crTextBackground;
crBackgroundOld = pXTCD->ds.crBackground;
crAnchorTextOld = pXTCD->ds.crAnchorText;
pXTCD->ds.crText = crText;
pXTCD->ds.crTextBackground = crTextBackground;
pXTCD->ds.crBackground = crBackground;
pXTCD->ds.crAnchorText = crAnchorText;
pXTCD->ds.rect = rect;
pXTCD->ds.bUseEllipsis = FALSE;
if (m_bLogFont)
{
pXTCD->ds.bLogFont = TRUE;
memcpy(&pXTCD->ds.lf, &m_lf, sizeof(LOGFONT));
}
CString strText = lpszText;//GetItemText(hItem);
TRACE(_T("in CXHtmlTree::DrawItemTextHtml: <%s> crText=%06X crBk=%06X\n"), strText, crText, crBackground);
CXHtmlDraw htmldraw;
int nWidth = htmldraw.Draw(pDC->m_hDC, strText, &pXTCD->ds, hItem == m_hAnchorItem);
pXTCD->ds.crText = crTextOld;
pXTCD->ds.crTextBackground = crTextBackgroundOld;
pXTCD->ds.crBackground = crBackgroundOld;
pXTCD->ds.crAnchorText = crAnchorTextOld;
return nWidth;
}
#endif // XHTMLHTML
//=============================================================================
int CXHtmlTree::DrawSeparator(CDC *pDC, //+++1.6
HTREEITEM hItem,
COLORREF crText,
COLORREF crBackground,
CRect& rect)
//=============================================================================
{
ASSERT(pDC);
ASSERT(hItem);
if (!pDC || !hItem)
{
TRACE(_T("ERROR bad parameters\n"));
return 0;
}
if (IsBadRect(rect))
{
return 0;
}
int nWidth = 0;
CRect rectSep(rect);
pDC->FillSolidRect(&rectSep, crBackground);
//TRACE(_T("CXHtmlTree::DrawItemText: crText=%08X crBkgnd=%08X <%s> ++++++\n"), crText, crBackground, str);
XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);
if (pXTCD)
{
CPen pen(PS_SOLID, 1, crText);
CPen * pOldPen = pDC->SelectObject(&pen);
TRACE(_T("drawing separator\n"));
rectSep.right -= 1;
rectSep.left -= 2;
rectSep.top += rectSep.Height()/2;
pDC->MoveTo(rectSep.left, rectSep.top);
pDC->LineTo(rectSep.right, rectSep.top);
pDC->SelectObject(pOldPen);
nWidth = rectSep.right;
pXTCD->ds.nRightX = rectSep.right;
}
return nWidth;
}
//=============================================================================
void CXHtmlTree::OnDestroy()
//=============================================================================
{
TRACE(_T("in CXHtmlTree::OnDestroy\n"));
DeleteMap();
CTreeCtrl::OnDestroy();
}
//=============================================================================
void CXHtmlTree::DeleteMap()
//=============================================================================
{
BOOL bOldDestroyingTree = m_bDestroyingTree;
m_bDestroyingTree = TRUE;
int n = (int)m_DataMap.GetCount();
POSITION pos = m_DataMap.GetStartPosition();
HTREEITEM hItem = 0;
XHTMLTREEDATA *pXTCD = NULL;
if (n > 0)
{
do
{
m_DataMap.GetNextAssoc(pos, hItem, pXTCD);
if (hItem && pXTCD)
delete pXTCD;
n--;
} while (pos != NULL);
}
ASSERT(n == 0);
m_DataMap.RemoveAll();
m_bDestroyingTree = bOldDestroyingTree;
}
//=============================================================================
// GetNextItem - Get next item in sequence (as if tree was completely expanded)
// see http://www.codeguru.com/Cpp/controls/treeview/treetraversal/article.php/c645
// hItem - The reference item
// Returns - The item immediately below the reference item
HTREEITEM CXHtmlTree::GetNextItem(HTREEITEM hItem)
//=============================================================================
{
HTREEITEM hItemNext = NULL;
ASSERT(hItem);
if (hItem)
{
if (ItemHasChildren(hItem))
{
hItemNext = GetChildItem(hItem); // first child
}
if (hItemNext == NULL)
{
// return next sibling item - go up the tree to find
// a parent's sibling if needed.
while ((hItemNext = GetNextSiblingItem(hItem)) == NULL)
{
if ((hItem = GetParentItem(hItem)) == NULL)
return NULL;
}
}
}
return hItemNext;
}
//=============================================================================
// GetNextItem - Get previous item as if outline was completely expanded
// Returns - The item immediately above the reference item
// hItem - The reference item
HTREEITEM CXHtmlTree::GetPrevItem(HTREEITEM hItem)
//=============================================================================
{
HTREEITEM hItemPrev;
hItemPrev = GetPrevSiblingItem(hItem);
if (hItemPrev == NULL)
hItemPrev = GetParentItem(hItem);
else
hItemPrev = GetLastItem(hItemPrev);
return hItemPrev;
}
//=============================================================================
// GetLastItem - Gets last item in the branch
// Returns - Last item
// hItem - Node identifying the branch. NULL will
// return the last item in outine
HTREEITEM CXHtmlTree::GetLastItem(HTREEITEM hItem)
//=============================================================================
{
// Last child of the last child of the last child ...
HTREEITEM hItemNext;
if (hItem == NULL)
{
// Get the last item at the top level
hItemNext = GetRootItem();
while (hItemNext)
{
hItem = hItemNext;
hItemNext = GetNextSiblingItem(hItemNext);
}
}
while (ItemHasChildren(hItem))
{
hItemNext = GetChildItem(hItem);
while (hItemNext)
{
hItem = hItemNext;
hItemNext = GetNextSiblingItem(hItemNext);
}
}
return hItem;
}
//=============================================================================
// FindItem - Finds an item that contains the search string
//
// http://www.codeguru.com/cpp/controls/treeview/treetraversal/article.php/c673/
//
// Returns - Handle to the item or NULL
//
// str - String to search for
// bCaseSensitive - Should the search be case sensitive
// bDownDir - Search direction - TRUE for down
// bWholeWord - True if search should match whole words
// hItem - Item to start searching from. NULL for
// currently selected item
HTREEITEM CXHtmlTree::FindItem(CString &str,
BOOL bCaseSensitive /*= FALSE*/,
BOOL bDownDir /*= TRUE*/,
BOOL bWholeWord /*= FALSE*/,
BOOL bWrap /* = TRUE */,
HTREEITEM hItem /*= NULL*/)
//=============================================================================
{
int lenSearchStr = str.GetLength();
if (lenSearchStr == 0)
return NULL;
HTREEITEM hItemSel = hItem ? hItem : GetSelectedItem();
HTREEITEM hItemCur = bDownDir ? GetNextItem(hItemSel) : GetPrevItem(hItemSel);
CString sSearch = str;
if (hItemCur == NULL)
{
if (bDownDir)
hItemCur = GetRootItem();
else
hItemCur = GetLastItem(NULL);
}
if (!bCaseSensitive)
sSearch.MakeLower();
while (hItemCur && (hItemCur != hItemSel))
{
CString sItemText = GetItemText(hItemCur);
#ifdef XHTMLHTML
TCHAR s[200];
// remove html tags
CXHtmlDraw hd;
hd.GetPlainText(sItemText, s, sizeof(s)/sizeof(TCHAR)-1);
sItemText = s;
#endif // XHTMLHTML
if (!bCaseSensitive)
sItemText.MakeLower();
int n = 0;
while ((n = sItemText.Find(sSearch)) != -1)
{
// search string found
if (bWholeWord)
{
// check preceding char
if (n != 0)
{
if (isalpha(sItemText[n-1]) ||
sItemText[n-1] == '_')
{
// Not whole word
sItemText = sItemText.Right(sItemText.GetLength() -
n - lenSearchStr);
continue;
}
}
// check succeeding char
if (sItemText.GetLength() > (n + lenSearchStr) &&
(isalpha(sItemText[n+lenSearchStr]) ||
sItemText[n+lenSearchStr] == '_' ))
{
// Not whole word
sItemText = sItemText.Right(sItemText.GetLength()
- n - sSearch.GetLength());
continue;
}
}
if (IsFindValid( hItemCur))
return hItemCur;
else
break;
}
hItemCur = bDownDir ? GetNextItem(hItemCur) : GetPrevItem(hItemCur);
if ((hItemCur == NULL) && !bWrap)
break;
if ((hItemCur == NULL) && (hItemSel != NULL)) // wrap only if there
// is a selected item
{
if (bDownDir)
hItemCur = GetRootItem();
else
hItemCur = GetLastItem(NULL);
}
}
return NULL;
}
//=============================================================================
// IsFindValid - Virtual function used by FindItem to allow this
// function to filter the result of FindItem
// Returns - True if item matches the criteria
// Arg - Handle of the item
BOOL CXHtmlTree::IsFindValid(HTREEITEM)
//=============================================================================
{
return TRUE;
}
//=============================================================================
void CXHtmlTree::RedrawItem(HTREEITEM hItem)
//=============================================================================
{
if (hItem)
{
CRect rect;
GetItemRect(hItem, &rect, FALSE);
InvalidateRect(&rect, FALSE);
UpdateWindow();
}
}
//=============================================================================
// OnMouseMove - handle link underlining and checkbox hot state
void CXHtmlTree::OnMouseMove(UINT nFlags, CPoint point)
//=============================================================================
{
// hItem will be non-zero if the cursor is anywhere over a valid item
UINT flags = 0;
HTREEITEM hItem = HitTest(point, &flags);
#ifdef XHTMLDRAGDROP
if (!m_bDragging && !IsLeftButtonUp() && IsSeparator(hItem))
{
// we must send TVN_BEGINDRAG to ourself, since separator has no text
NMTREEVIEW nmtv = { 0 };
nmtv.hdr.hwndFrom = m_hWnd;
nmtv.hdr.idFrom = GetDlgCtrlID();
nmtv.hdr.code = TVN_BEGINDRAG;
nmtv.itemNew.hItem = hItem;
SendMessage(WM_NOTIFY, 0, (LPARAM)&nmtv);
}
else if (m_bDragging)
{
CPoint cursor_point(point);
ClientToScreen(&cursor_point);
LRESULT lResult = 0; // allow drop if lResult is 0
BOOL bCopyDrag = IsDragCopy();
if (hItem)
{
// allow parent to decide whether to permit drag
XHTMLTREEDRAGMSGDATA dragdata = { 0 };
dragdata.hItem = m_hItemButtonDown;
dragdata.hAfter = hItem;
dragdata.bCopyDrag = bCopyDrag;
lResult = SendRegisteredMessage(WM_XHTMLTREE_DROP_HOVER,
m_hItemButtonDown, (LPARAM)&dragdata);
}
// Check to see if the drag is over an item in the tree
if (hItem && !lResult)
{
if (m_hPreviousDropItem != hItem)
{
SetDragCursor();
SetInsertMark(0, 0); // remove previous insert mark
TRACE(_T("Drag target item 0x%X\n"), hItem);
m_hPreviousDropItem = hItem;
m_dwDropHoverTime = GetTickCount();
// check if Shift key down
if (GetBit(m_dwDragOps, XHTMLTREE_DO_SHIFT_KEY) &&
(GetAsyncKeyState(VK_SHIFT) < 0))
{
TRACE(_T("VK_SHIFT down\n"));
if (IsSeparator(hItem)) //+++1.6
{
SelectDropTarget(NULL);
SetInsertMark(hItem, TRUE);
}
else
{
SelectDropTarget(hItem);
}
}
else
{
SetInsertMark(hItem, TRUE);
SelectDropTarget(NULL);
}
}
}
else // not over an item
{
SetInsertMark(0, 0); // remove insert mark
m_hPreviousDropItem = 0;
if (m_hNoDropCursor)
SetCursor(m_hNoDropCursor); // set to no-drop cursor
else
SetCursor(AfxGetApp()->LoadStandardCursor(IDC_NO)); // set to no-drop cursor
}
SetFocus();
}
else
#endif // XHTMLDRAGDROP
if (hItem)
{
// if mouse is on a different item, or has moved off state icon
if ((m_hHotItem && (m_hHotItem != hItem)) ||
(m_hHotItem && ((flags & TVHT_ONITEMSTATEICON) == 0)))
{
int nState = GetStateImage(m_hHotItem);
// a hot item can only be UNCHECKED, CHECKED, or CHECKED_TRISTATE
nState &= ~HDCheckboxImageList::HOT_INDEX;
// remove hot from previous hot item
SetItemState(m_hHotItem, INDEXTOSTATEIMAGEMASK(nState), TVIS_STATEIMAGEMASK);
m_hHotItem = NULL;
}
if ((m_hHotItem == NULL) && (flags & TVHT_ONITEMSTATEICON))
{
TRACE(_T("cursor over state image\n"));
int nState = GetStateImage(hItem);
if ((nState & HDCheckboxImageList::DISABLED_INDEX) == 0) // is it enabled?
{
// a hot item can only be UNCHECKED, CHECKED, or CHECKED_TRISTATE
nState |= HDCheckboxImageList::HOT_INDEX;
// remove hot from previous hot item
SetItemState(hItem, INDEXTOSTATEIMAGEMASK(nState), TVIS_STATEIMAGEMASK);
m_hHotItem = hItem;
SetTimer(HOT_TIMER, 100, NULL); // timer in case mouse leaves window
}
}
CRect rect;
BOOL bOverAnchor = IsOverAnchor(hItem, point, &rect);
//TRACE(_T("bOverAnchor=%d\n"), bOverAnchor);
#ifdef XHTMLHTML
if (bOverAnchor)
m_Links.SetLinkCursor();
#endif // XHTMLHTML
if ((m_hAnchorItem && (m_hAnchorItem != hItem)) ||
(m_hAnchorItem && !bOverAnchor))
{
TRACE(_T("removing anchor 0x%X-----\n"), m_hAnchorItem);
GetItemRect(m_hAnchorItem, &rect, FALSE); // note: must get entire
// rect, since text might
// be shifted left
m_hAnchorItem = NULL;
InvalidateRect(&rect, FALSE);
}
else if ((m_hAnchorItem == NULL) && bOverAnchor)
{
TRACE(_T("adding anchor 0x%X-----\n"), hItem);
m_hAnchorItem = hItem;
TRACE(_T("mouse over anchor 0x%X-----\n"), hItem);
GetItemRect(hItem, &rect, FALSE); // note: must get entire
// rect, since text might
// be shifted left
InvalidateRect(&rect, FALSE);
SetTimer(HOT_TIMER, 80, NULL); // timer in case mouse leaves window
}
}
CTreeCtrl::OnMouseMove(nFlags, point);
}
//=============================================================================
HTREEITEM CXHtmlTree::IsOverItem(LPPOINT lpPoint /*= NULL*/)
//=============================================================================
{
CPoint point;
if (lpPoint)
{
point = *lpPoint;
}
else
{
::GetCursorPos(&point);
ScreenToClient(&point);
}
UINT flags = 0;
HTREEITEM hItem = HitTest(point, &flags);
return hItem;
}
//=============================================================================
BOOL CXHtmlTree::IsOverAnchor(HTREEITEM hItem, CPoint point, CRect *pRect /*= NULL*/)
//=============================================================================
{
BOOL rc = FALSE;
CRect rect(0,0,0,0);
XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);
if (pXTCD && pXTCD->ds.bHasAnchor && pXTCD->bEnabled)
{
GetItemRect(hItem, &rect, TRUE);
// set rect to anchor boundaries
rect.left = pXTCD->ds.rectAnchor.left;
rect.right = pXTCD->ds.rectAnchor.right;
if (rect.PtInRect(point))
{
rc = TRUE;
}
}
if (pRect)
*pRect = rect;
return rc;
}
//=============================================================================
HCURSOR CXHtmlTree::SetCursor(HCURSOR hCursor)
//=============================================================================
{
if (hCursor == m_hCurrentCursor)
return m_hCurrentCursor;
m_hCurrentCursor = hCursor;
TRACE(_T("calling ::SetCursor %X\n"), hCursor);
return ::SetCursor(hCursor);
}
//=============================================================================
BOOL CXHtmlTree::IsLeftButtonUp()
//=============================================================================
{
BOOL rc = FALSE;
SHORT state = 0;
if (GetSystemMetrics(SM_SWAPBUTTON)) // check if buttons have been swapped
state = GetAsyncKeyState(VK_RBUTTON); // buttons swapped, get right button state
else
state = GetAsyncKeyState(VK_LBUTTON);
// if the most significant bit is set, the button is down
if (state >= 0)
rc = TRUE;
return rc;
}
//=============================================================================
// OnTimer - check if mouse has left, turn off underlining and hot state
void CXHtmlTree::OnTimer(UINT nIDEvent)
//=============================================================================
{
CPoint point;
::GetCursorPos(&point);
ScreenToClient(&point);
if (nIDEvent == HOT_TIMER)
{
// if mouse has left window, turn off hot and anchor highlighting
CRect rectClient;
GetClientRect(&rectClient);
if (!rectClient.PtInRect(point))
{
KillTimer(nIDEvent);
// mouse has left the window
if (m_hHotItem)
{
int nState = GetStateImage(m_hHotItem);
// a hot item can only be UNCHECKED, CHECKED, or CHECKED_TRISTATE
nState &= ~HDCheckboxImageList::HOT_INDEX;
// remove hot from previous hot item
SetItemState(m_hHotItem, INDEXTOSTATEIMAGEMASK(nState), TVIS_STATEIMAGEMASK);
m_hHotItem = NULL;
}
if (m_hAnchorItem)
{
// remove underline
CRect rectAnchor;
GetItemRect(m_hAnchorItem, &rectAnchor, FALSE);
m_hAnchorItem = NULL;
InvalidateRect(&rectAnchor, TRUE);
}
}
}
else if (nIDEvent == LBUTTONDOWN_TIMER) // timer set by WM_LBUTTONDOWN
{
HTREEITEM hItem = IsOverItem(&point);
if (IsLeftButtonUp())
{
TRACE(_T("mouse button is up >>>>>\n"));
KillTimer(nIDEvent);
HTREEITEM hItemSelected = GetSelectedItem();
#ifdef XHTMLDRAGDROP
if (m_bDragging) // case 1: user is dragging
{
if (hItem &&
(hItem != m_hItemButtonDown) &&
(!IsChildNodeOf(hItem, m_hItemButtonDown)))
{
HTREEITEM hAfter = hItem;
HTREEITEM hParent = GetParentItem(m_hItemButtonDown);
HTREEITEM hNewParent = GetParentItem(hAfter);
TRACE(_T("hParent=%X\n"), hParent);
// check if Shift key down
if ((m_dwDragOps & XHTMLTREE_DO_SHIFT_KEY) &&
(GetAsyncKeyState(VK_SHIFT) < 0) &&
!IsSeparator(hAfter)) //+++1.6
{
TRACE(_T("VK_SHIFT down, creating child item\n"));
hNewParent = hAfter;
hAfter = TVI_LAST;
}
else if (hParent == hAfter)
{
// dropping on parent
hNewParent = hParent;
hAfter = TVI_FIRST;
}
else if (ItemHasChildren(hAfter) && IsExpanded(hAfter)) //+++1.6
{
// dropping on node that is expanded
hNewParent = hAfter;
hAfter = TVI_FIRST;
}
else if (hNewParent == 0) //+++1.6
{
// multiple roots
}
onItemDropped(m_hItemButtonDown,hNewParent);
//StartMoveBranch(m_hItemButtonDown, hNewParent, hAfter);
}
else
{
TRACE(_T("ERROR can't drop on %X\n"), hItem);
SendRegisteredMessage(WM_XHTMLTREE_END_DRAG, 0, 0);
}
EndDragScroll();
}
else
#endif // XHTMLDRAGDROP
if (hItem && (hItem == hItemSelected)
&& !IsSeparator(hItem)) // case 2: user wants to edit a label
{
// clicking on a selected item
CEdit *pEdit = GetEditControl();
CRect rect;
GetItemRect(hItem, &rect, TRUE);
if (rect.PtInRect(point))
{
TRACE(_T("sending TVM_EDITLABEL\n"));
// click on item text, begin edit
SendMessage(TVM_EDITLABEL, 0, (LPARAM)hItem);
}
else if (pEdit && IsWindow(pEdit->m_hWnd))
{
TRACE(_T("sending WM_CLOSE to edit box\n"));
// click outside item text, end edit
pEdit->SendMessage(WM_CLOSE);
}
}
}
#ifdef XHTMLDRAGDROP
else // left button is down
{
//TRACE(_T("mouse button is down >>>>>\n"));
if (m_bDragging)
{
// check how long we've been hovering over same item
if (hItem && (hItem == m_hPreviousDropItem))
{
if (GetBit(m_dwDragOps, XHTMLTREE_DO_AUTO_EXPAND))
{
// still over same item
if ((GetTickCount() - m_dwDropHoverTime) > MIN_HOVER_TIME)
{
// min hover time has passed, expand node if it has children
Expand(hItem, TVE_EXPAND);
}
}
}
if (m_bAutoScroll)
AutoScroll(hItem);
}
}
#endif // XHTMLDRAGDROP
}
#ifdef XHTMLDRAGDROP
else if (nIDEvent == CTRL_UP_TIMER)
{
// check if Ctrl key down
if (!IsCtrlDown())
{
TRACE(_T("VK_CONTROL up\n"));
KillTimer(nIDEvent);
if (IsOverItem() && m_bDragging)
{
SetDragCursor();
}
}
}
else if (nIDEvent == SHIFT_UP_TIMER)
{
if ((m_dwDragOps & XHTMLTREE_DO_SHIFT_KEY) &&
(GetAsyncKeyState(VK_SHIFT) >= 0))
{
TRACE(_T("VK_SHIFT up\n"));
KillTimer(nIDEvent);
HTREEITEM hItem = IsOverItem();
if (hItem)
SetInsertMark(hItem, TRUE);
SelectDropTarget(NULL);
}
}
else if (nIDEvent == SELECT_TIMER) // timer set by WM_LBUTTONDOWN
{
KillTimer(nIDEvent);
HTREEITEM hItem = IsOverItem();
if (hItem)
{
SelectItem(hItem);
}
}
#endif // XHTMLDRAGDROP
CTreeCtrl::OnTimer(nIDEvent);
}
//=============================================================================
BOOL CXHtmlTree::OnEraseBkgnd(CDC* pDC)
//=============================================================================
{
CRect rectClientx;
GetClientRect(&rectClientx);
pDC->FillSolidRect(rectClientx, m_crWindow);
return TRUE;
}
//=============================================================================
CXHtmlTree& CXHtmlTree::SetCheck(HTREEITEM hItem, BOOL fCheck /*= TRUE*/)
//=============================================================================
{
ASSERT(hItem);
if (hItem && m_bCheckBoxes)
{
TRACE(_T("in CXHtmlTree::SetCheck: %d <%s>\n"), fCheck, GetItemText(hItem));
XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);
if (pXTCD && pXTCD->bEnabled && !pXTCD->bSeparator) //+++1.6
{
BOOL bOldChecked = pXTCD->bChecked;
pXTCD->bChecked = fCheck;
UINT nState = GetStateImage(hItem);
//TRACE(_T("nState=0x%X nOldState=0x%X ~~~~~\n"), nState, nOldState);
SetItemState(hItem, INDEXTOSTATEIMAGEMASK(nState), TVIS_STATEIMAGEMASK);
if (m_bSmartCheck && (bOldChecked != fCheck))
{
HTREEITEM hParent = hItem;
int nCount = 0;
if (fCheck)
nCount = pXTCD->nChildren - pXTCD->nChecked + 1;//bOldCheck ? 1 : 0;
else
nCount = -(pXTCD->nChecked + 1);//bOldCheck ? 1 : 0);
SetCheckChildren(hItem, fCheck);
// find all parents, adjust their checked counts
TRACE(_T("starting nCount=%d\n"), nCount);
while ((hParent = GetParentItem(hParent)) != NULL)
{
nCount = SetCheckParent(hParent, nCount);
}
}
SendRegisteredMessage(WM_XHTMLTREE_CHECKBOX_CLICKED, hItem, fCheck);
}
}
return *this;
}
//=============================================================================
void CXHtmlTree::SetHotItem(HTREEITEM hItem, UINT nFlags)
//=============================================================================
{
if (m_hHotItem && (m_hHotItem != hItem))
{
int nState = GetStateImage(m_hHotItem);
// a hot item can only be UNCHECKED, CHECKED, or CHECKED_TRISTATE
nState &= ~HDCheckboxImageList::HOT_INDEX;
// remove hot from previous hot item
SetItemState(m_hHotItem, INDEXTOSTATEIMAGEMASK(nState), TVIS_STATEIMAGEMASK);
m_hHotItem = NULL;
}
if (hItem && (m_hHotItem == NULL) && (nFlags & TVHT_ONITEMSTATEICON))
{
TRACE(_T("cursor over state image\n"));
int nState = GetStateImage(hItem);
// a hot item can only be UNCHECKED, CHECKED, or CHECKED_TRISTATE
nState |= HDCheckboxImageList::HOT_INDEX;
// remove hot from previous hot item
SetItemState(hItem, INDEXTOSTATEIMAGEMASK(nState), TVIS_STATEIMAGEMASK);
m_hHotItem = hItem;
}
}
//=============================================================================
LRESULT CXHtmlTree::SendRegisteredMessage(UINT nMessage,
HTREEITEM hItem,
LPARAM lParam /*= 0*/)
//=============================================================================
{
LRESULT lResult = 0;
CWnd *pWnd = GetParent();
if (!pWnd)
pWnd = GetOwner();
if (pWnd && ::IsWindow(pWnd->m_hWnd))
{
XHTMLTREEMSGDATA msgdata = { 0 };
msgdata.hCtrl = m_hWnd;
msgdata.nCtrlId = GetDlgCtrlID();
msgdata.hItem = hItem;
lResult = pWnd->SendMessage(nMessage, (WPARAM)&msgdata, lParam);
}
return lResult;
}
//=============================================================================
int CXHtmlTree::GetStateImage(HTREEITEM hItem)
//=============================================================================
{
int nState = 0;
ASSERT(hItem);
XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);
if (pXTCD)
{
if (m_bSmartCheck && (pXTCD->nChildren != 0))
{
if (pXTCD->nChecked == 0)
nState = UNCHECKED;
else if (pXTCD->nChecked == (pXTCD->nChildren - pXTCD->nSeparators)) //+++1.6
nState = CHECKED;
else
nState = TRISTATE;
}
else
{
if (pXTCD->bChecked)
nState = CHECKED;
else
nState = UNCHECKED;
}
if (!pXTCD->bEnabled)
nState |= HDCheckboxImageList::DISABLED_INDEX;
}
TRACE(_T("GetStateImage returning %d ~~~~~\n"), nState);
return nState;
}
//=============================================================================
int CXHtmlTree::SetCheckParent(HTREEITEM hItem, int nCount)
//=============================================================================
{
TRACE(_T("in CXHtmlTree::SetCheckParent: nCount=%d <%s>\n"), nCount, GetItemText(hItem));
ASSERT(hItem);
int nState = 0;
XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);
if (pXTCD)
{
TRACE(_T("pXTCD->nChecked=%d pXTCD->nChildren=%d \n"), pXTCD->nChecked, pXTCD->nChildren);
pXTCD->nChecked += nCount;
if (pXTCD->nChecked < 0)
pXTCD->nChecked = 0;
BOOL bOldCheck = pXTCD->bChecked;
if (pXTCD->nChecked == (pXTCD->nChildren - pXTCD->nSeparators)) //+++1.6
pXTCD->bChecked = TRUE;
else
pXTCD->bChecked = FALSE;
if (pXTCD->bChecked != bOldCheck)
nCount += pXTCD->bChecked ? 1 : -1;
nState = GetStateImage(hItem);
SetItemState(hItem, INDEXTOSTATEIMAGEMASK(nState), TVIS_STATEIMAGEMASK);
TRACE(_T("nState=%d\n"), nState);
}
return nCount;
}
//=============================================================================
CXHtmlTree& CXHtmlTree::SetCheckChildren(HTREEITEM hItem, BOOL fCheck)
//=============================================================================
{
TRACE(_T("in CXHtmlTree::SetCheckChildren\n"));
// first set item state for this item
SetItemStateChildren(hItem, fCheck);
HTREEITEM hNext = GetChildItem(hItem);
// loop to set item state for children
while (hNext)
{
TRACE(_T("SetCheckChildren: %d <%s>\n"), fCheck, GetItemText(hNext));
// recurse into children
if (ItemHasChildren(hNext))
SetCheckChildren(hNext, fCheck);
SetItemStateChildren(hNext, fCheck);
hNext = GetNextItem(hNext, TVGN_NEXT);
}
return *this;
}
//=============================================================================
CXHtmlTree& CXHtmlTree::SetItemStateChildren(HTREEITEM hItem, BOOL fCheck)
//=============================================================================
{
TRACE(_T("in CXHtmlTree::SetItemStateChildren\n"));
XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);
if (pXTCD && pXTCD->bEnabled)
{
int nState = GetStateImage(hItem);
if (pXTCD->bSeparator) //+++1.6
{
nState = 0;
}
else
{
int nStateHot = nState & HDCheckboxImageList::HOT_INDEX; // save hot
int nStateDisabled = nState & HDCheckboxImageList::DISABLED_INDEX; // save disabled
nState &= ~(HDCheckboxImageList::HOT_INDEX |
HDCheckboxImageList::DISABLED_INDEX); // remove hot & disabled
pXTCD->bChecked = fCheck;
if (fCheck)
{
pXTCD->nChecked = pXTCD->nChildren - pXTCD->nSeparators; //+++1.6
if (pXTCD->nChecked < 0)
pXTCD->nChecked = 0;
nState = CHECKED;
}
else
{
pXTCD->nChecked = 0;
nState = UNCHECKED;
}
nState |= nStateHot; // restore hot
nState |= nStateDisabled; // restore disabled
TRACE(_T("setting state to %d\n"), nState);
}
SetItemState(hItem, INDEXTOSTATEIMAGEMASK(nState), TVIS_STATEIMAGEMASK);
}
return *this;
}
//=============================================================================
BOOL CXHtmlTree::EnableItem(HTREEITEM hItem, BOOL bEnabled)
//=============================================================================
{
BOOL rc = TRUE;
ASSERT(hItem);
XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);
if (pXTCD)
{
rc = pXTCD->bEnabled;
pXTCD->bEnabled = bEnabled;
int nState = GetStateImage(hItem);
if (bEnabled)
nState &= ~HDCheckboxImageList::DISABLED_INDEX;
else
nState |= HDCheckboxImageList::DISABLED_INDEX;
// set enabled state
SetItemState(hItem, INDEXTOSTATEIMAGEMASK(nState), TVIS_STATEIMAGEMASK);
}
return rc;
}
//=============================================================================
BOOL CXHtmlTree::EnableBranch(HTREEITEM hItem, BOOL bEnabled)
//=============================================================================
{
BOOL rc = TRUE;
if (hItem && !IsSeparator(hItem)) //+++1.6
{
rc = EnableItem(hItem, bEnabled);
hItem = GetChildItem(hItem);
if (hItem)
{
do
{
EnableBranch(hItem, bEnabled);
} while ((hItem = GetNextSiblingItem(hItem)) != NULL);
}
}
return rc; // return state of first item
}
//=============================================================================
BOOL CXHtmlTree::GetCheck(HTREEITEM hItem)
//=============================================================================
{
BOOL rc = FALSE;
ASSERT(hItem);
if (m_bCheckBoxes && hItem)
{
XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);
if (pXTCD)
{
if (m_bSmartCheck)
{
if (pXTCD->nChildren == 0)
{
rc = pXTCD->bChecked;
}
else
{
if (pXTCD->nChecked == 0)
rc = FALSE; // no children are checked
else if (pXTCD->nChecked == (pXTCD->nChildren - pXTCD->nSeparators)) //+++1.6
rc = TRUE; // all children are checked
else
rc = FALSE; // not all children are checked
}
}
else
{
rc = pXTCD->bChecked;
}
}
}
return rc;
}
//=============================================================================
HTREEITEM CXHtmlTree::GetFirstCheckedItem()
//=============================================================================
{
if (m_bCheckBoxes)
{
for (HTREEITEM hItem = GetRootItem();
hItem != NULL;
hItem = GetNextItem(hItem))
{
if (GetCheck(hItem))
return hItem;
}
}
return NULL;
}
//=============================================================================
HTREEITEM CXHtmlTree::GetNextCheckedItem(HTREEITEM hItem)
//=============================================================================
{
if (m_bCheckBoxes)
{
for (hItem = GetNextItem(hItem);
hItem != NULL;
hItem = GetNextItem(hItem))
{
if (GetCheck(hItem))
return hItem;
}
}
return NULL;
}
//=============================================================================
HTREEITEM CXHtmlTree::GetPrevCheckedItem(HTREEITEM hItem)
//=============================================================================
{
if (m_bCheckBoxes)
{
for (hItem = GetPrevItem(hItem);
hItem != NULL;
hItem = GetPrevItem(hItem))
{
if (GetCheck(hItem))
return hItem;
}
}
return NULL;
}
//=============================================================================
BOOL CXHtmlTree::DeleteItem(HTREEITEM hItem)
//=============================================================================
{
TRACE(_T("in CXHtmlTree::DeleteItem\n"));
BOOL bOldDestroyingTree = m_bDestroyingTree;
if (hItem && ItemHasChildren(hItem))
{
DeleteBranch(hItem);
}
else if (hItem)
{
XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);
if (pXTCD)
{
if (pXTCD->bChecked)
m_nDeletedChecked++;
HTREEITEM hParent = hItem;
// find all parents, decrement their children counts,
// adjust their checked counts and separator counts
while ((hParent = GetParentItem(hParent)) != NULL)
{
IncrementChildren(hParent, -1);
if (pXTCD->bChecked)
SetCheckParent(hParent, -1);
if (pXTCD->bSeparator) //+++1.6
IncrementSeparators(hParent, -1);
}
m_bDestroyingTree = TRUE;
m_DataMap.RemoveKey(hItem);
delete pXTCD;
}
}
m_bDestroyingTree = bOldDestroyingTree;
m_nDeleted++;
return CTreeCtrl::DeleteItem(hItem);
}
//=============================================================================
void CXHtmlTree::DeleteBranch(HTREEITEM hItem)
//=============================================================================
{
if (hItem)
{
HTREEITEM hChild = GetChildItem(hItem);
while (hChild)
{
// recursively delete all the items
HTREEITEM hNext = GetNextSiblingItem(hChild);
DeleteBranch(hChild);
hChild = hNext;
}
DeleteItem(hItem);
}
}
//=============================================================================
CString CXHtmlTree::GetItemNote(HTREEITEM hItem, BOOL bStripHtml /*= FALSE*/)
//=============================================================================
{
CString strNote = _T("");
XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);
if (pXTCD && pXTCD->pszNote)
{
strNote = pXTCD->pszNote;
if (bStripHtml)
{
#ifdef XHTMLHTML
// remove html tags
CXHtmlDraw hd;
int n = strNote.GetLength();
if (n > 3) // minimum html string
{
TCHAR *s = new TCHAR [n + 16];
hd.GetPlainText(strNote, s, n+4);
strNote = s;
delete [] s;
}
#endif // XHTMLHTML
}
}
return strNote;
}
//=============================================================================
int CXHtmlTree::GetItemNoteWidth(HTREEITEM hItem)
//=============================================================================
{
int nWidth = 0;
XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);
if (pXTCD && pXTCD->pszNote)
{
nWidth = pXTCD->nTipWidth;
}
return nWidth;
}
//=============================================================================
CXHtmlTree& CXHtmlTree::SetItemNote(HTREEITEM hItem,
LPCTSTR lpszNote,
int nTipWidth /*= 0*/)
//=============================================================================
{
ASSERT(hItem);
ASSERT(lpszNote);
if (hItem && lpszNote)
{
XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);
if (pXTCD)
{
if (pXTCD->pszNote)
delete [] pXTCD->pszNote;
pXTCD->pszNote = NULL;
size_t len = _tcslen(lpszNote);
pXTCD->pszNote = new TCHAR [len + 4];
if (pXTCD->pszNote)
{
memset(pXTCD->pszNote, 0, len+4);
_tcsncpy(pXTCD->pszNote, lpszNote, len+1);
pXTCD->nTipWidth = nTipWidth;
}
}
}
return *this;
}
//=============================================================================
HTREEITEM CXHtmlTree::InsertItem(LPTVINSERTSTRUCT lpInsertStruct,
XHTMLTREEDATA * pData /*= NULL*/)
//=============================================================================
{
XHTMLTREEDATA *pXTCD = new XHTMLTREEDATA;
ASSERT(pXTCD);
if (!pXTCD)
{
ASSERT(FALSE);
return 0;
}
if (pData)
{
// copy user items for XHTMLTREEDATA
pXTCD->bChecked = pData->bChecked;
pXTCD->bEnabled = pData->bEnabled;
pXTCD->bSeparator = pData->bSeparator; //+++1.6
// copy user items for XHTMLDRAWSTRUCT
pXTCD->ds.crText = pData->ds.crText;
pXTCD->ds.crAnchorText = pData->ds.crAnchorText;
pXTCD->ds.crBackground = pData->ds.crBackground;
pXTCD->ds.crTextBackground = pData->ds.crTextBackground;
pXTCD->ds.bIgnoreColorTag = pData->ds.bIgnoreColorTag;
pXTCD->ds.bTransparent = pData->ds.bTransparent;
pXTCD->ds.bBold = pData->ds.bBold;
pXTCD->ds.bItalic = pData->ds.bItalic;
pXTCD->ds.bUnderline = pData->ds.bUnderline;
pXTCD->ds.bStrikeThrough = pData->ds.bStrikeThrough;
pXTCD->ds.bUseEllipsis = pData->ds.bUseEllipsis;
pXTCD->ds.bLogFont = pData->ds.bLogFont;
pXTCD->ds.uFormat = pData->ds.uFormat;
pXTCD->ds.lf = pData->ds.lf;
}
pXTCD->hTreeCtrl = m_hWnd;
TVINSERTSTRUCT tvis;
memcpy(&tvis, lpInsertStruct, sizeof(TVINSERTSTRUCT));
if (!m_bImages)
{
tvis.item.iImage = TV_NOIMAGE;
tvis.item.iSelectedImage = TV_NOIMAGE; //+++1.5
}
tvis.item.mask |= TVIF_STATE;
int nState = UNCHECKED;
if (pXTCD->bChecked && m_bCheckBoxes)
nState = CHECKED;
if (!pXTCD->bEnabled)
nState |= HDCheckboxImageList::DISABLED_INDEX;
tvis.item.state = INDEXTOSTATEIMAGEMASK(nState);
tvis.item.stateMask = TVIS_STATEIMAGEMASK;
CString strText = tvis.item.pszText;
if (m_bStripHtml)
{
#ifdef XHTMLHTML
// remove html tags
CXHtmlDraw hd;
int n = strText.GetLength();
if (n > 3) // minimum html string
{
TCHAR *s = new TCHAR [n + 16];
hd.GetPlainText(strText, s, n+4);
strText = s;
delete [] s;
}
#endif // XHTMLHTML
}
TRACE(_T("inserting <%s>\n"), strText);
tvis.item.pszText = strText.LockBuffer();
HTREEITEM hItem = CTreeCtrl::InsertItem(&tvis);
ASSERT(hItem);
strText.UnlockBuffer();
if (hItem)
{
m_DataMap.SetAt(hItem, pXTCD);
TRACE(_T("count=%d\n"), m_DataMap.GetCount());
if (m_bSmartCheck)
{
HTREEITEM hParent = hItem;
// find all parents, increment their children counts,
// adjust their checked counts
int nCount = pXTCD->bChecked ? 1 : 0;
while ((hParent = GetParentItem(hParent)) != NULL)
{
IncrementChildren(hParent);
nCount = SetCheckParent(hParent, nCount);
}
}
}
return hItem;
}
//=============================================================================
HTREEITEM CXHtmlTree::InsertItem(UINT nMask,
LPCTSTR lpszItem,
int nImage,
int nSelectedImage,
UINT nState,
UINT nStateMask,
LPARAM lParam,
HTREEITEM hParent,
HTREEITEM hInsertAfter)
//=============================================================================
{
TVINSERTSTRUCT tvis = { 0 };
tvis.item.mask = nMask;
tvis.item.pszText = (LPTSTR)lpszItem;
tvis.item.iImage = nImage;
tvis.item.iSelectedImage = nSelectedImage;
tvis.item.state = nState;
tvis.item.stateMask = nStateMask;
tvis.item.lParam = lParam;
tvis.hParent = hParent;
tvis.hInsertAfter = hInsertAfter;
return InsertItem(&tvis);
}
//=============================================================================
HTREEITEM CXHtmlTree::InsertItem(LPCTSTR lpszItem,
HTREEITEM hParent /*= TVI_ROOT*/,
HTREEITEM hInsertAfter /*= TVI_LAST*/)
//=============================================================================
{
TVINSERTSTRUCT tvis = { 0 };
tvis.item.mask = TVIF_TEXT;
tvis.item.pszText = (LPTSTR)lpszItem;
tvis.hParent = hParent;
tvis.hInsertAfter = hInsertAfter;
return InsertItem(&tvis);
}
//=============================================================================
HTREEITEM CXHtmlTree::InsertItem(LPCTSTR lpszItem,
int nImage,
int nSelectedImage,
HTREEITEM hParent /*= TVI_ROOT*/,
HTREEITEM hInsertAfter /*= TVI_LAST*/)
//=============================================================================
{
TVINSERTSTRUCT tvis = { 0 };
tvis.item.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
tvis.item.pszText = (LPTSTR)lpszItem;
tvis.item.iImage = nImage;
tvis.item.iSelectedImage = nSelectedImage;
tvis.hParent = hParent;
tvis.hInsertAfter = hInsertAfter;
return InsertItem(&tvis);
}
//=============================================================================
HTREEITEM CXHtmlTree::InsertSeparator(HTREEITEM hItem) //+++1.6
//=============================================================================
{
TRACE(_T("in CXHtmlTree::InsertSeparator\n"));
HTREEITEM hAfter = hItem;
HTREEITEM hParent = GetParentItem(hItem);
HTREEITEM hNewParent = GetParentItem(hItem);
TRACE(_T("hParent=%X\n"), hParent);
if (hParent == hAfter)
{
// dropping on parent
hNewParent = hParent;
hAfter = TVI_FIRST;
}
else if (ItemHasChildren(hAfter) && IsExpanded(hAfter))
{
// dropping on node that is expanded
hNewParent = hAfter;
hAfter = TVI_FIRST;
}
TVINSERTSTRUCT tvis = { 0 };
tvis.item.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
tvis.item.pszText = _T("");
tvis.item.iImage = TV_NOIMAGE;
tvis.item.iSelectedImage = TV_NOIMAGE;
tvis.hParent = hNewParent;
tvis.hInsertAfter = hAfter;
HTREEITEM hSep = InsertItem(&tvis);
XHTMLTREEDATA *pXTCD = GetItemDataStruct(hSep);
if (pXTCD)
{
pXTCD->bSeparator = TRUE;
//SetItemTextColor(hSep, RGB(255,0,0));
}
SetItemState(hSep, INDEXTOSTATEIMAGEMASK(0), TVIS_STATEIMAGEMASK);
// increment separator count in parents
hParent = hSep;
while ((hParent = GetParentItem(hParent)) != NULL)
{
IncrementSeparators(hParent, 1);
}
return hSep;
}
//=============================================================================
int CXHtmlTree::IncrementChildren(HTREEITEM hItem, int n /*= 1*/)
//=============================================================================
{
int nChildren = 0;
ASSERT(hItem);
XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);
if (pXTCD)
{
pXTCD->nChildren += n;
if (pXTCD->nChildren < 0)
pXTCD->nChildren = 0;
nChildren = pXTCD->nChildren;
}
return nChildren;
}
//=============================================================================
int CXHtmlTree::IncrementSeparators(HTREEITEM hItem, int n /*= 1*/) //+++1.6
//=============================================================================
{
int nSeparators = 0;
ASSERT(hItem);
XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);
if (pXTCD)
{
pXTCD->nSeparators += n;
if (pXTCD->nSeparators < 0)
pXTCD->nSeparators = 0;
nSeparators = pXTCD->nSeparators;
}
return nSeparators;
}
//=============================================================================
XHTMLTREEDATA * CXHtmlTree::GetItemDataStruct(HTREEITEM hItem)
//=============================================================================
{
XHTMLTREEDATA *pXTCD = NULL;
if (hItem && !m_bDestroyingTree)
{
m_DataMap.Lookup(hItem, pXTCD);
}
return pXTCD;
}
#ifdef XHTMLTOOLTIPS
//=============================================================================
// OnDisplayTooltip - display CPPToolTip
void CXHtmlTree::OnDisplayTooltip(NMHDR * pNMHDR, LRESULT * pResult)
//=============================================================================
{
TRACE(_T("in CXHtmlTree::OnDisplayTooltip\n"));
*pResult = 0;
NM_PPTOOLTIP_DISPLAY * pNotify = (NM_PPTOOLTIP_DISPLAY*)pNMHDR;
CString strToolTip = _T("");
if (PreDisplayToolTip(FALSE, strToolTip))
{
// no limitation on text length
TRACE(_T("setting tooltip to <%s>\n"), strToolTip);
// change the tooltip's text
pNotify->ti->sTooltip = strToolTip;
}
}
#else
//=============================================================================
BOOL CXHtmlTree::OnToolTipText(UINT /*id*/, NMHDR * pNMHDR, LRESULT * pResult)
//=============================================================================
{
TRACE(_T("in CXHtmlTree::OnToolTipText\n"));
UINT nID = (UINT)pNMHDR->idFrom;
// check if this is the automatic tooltip of the control
if (nID == 0)
return TRUE; // do not allow display of automatic tooltip,
// or our tooltip will disappear
// handle both ANSI and UNICODE versions of the message
TOOLTIPTEXTA* pTTTA = (TOOLTIPTEXTA*)pNMHDR;
TOOLTIPTEXTW* pTTTW = (TOOLTIPTEXTW*)pNMHDR;
*pResult = 0;
CString strToolTip = _T("");
if (PreDisplayToolTip(TRUE, strToolTip))
{
// copy item text (up to 80 characters worth, limitation
// of the TOOLTIPTEXT structure) into the TOOLTIPTEXT
// structure's szText member
strToolTip = strToolTip.Mid(0, sizeof(pTTTA->szText)-2);
#ifndef _UNICODE
if (pNMHDR->code == TTN_NEEDTEXTA)
lstrcpyn(pTTTA->szText, strToolTip, sizeof(pTTTA->szText));
else
_mbstowcsz(pTTTW->szText, strToolTip, sizeof(pTTTW->szText)/sizeof(TCHAR));
#else
if (pNMHDR->code == TTN_NEEDTEXTA)
_wcstombsz(pTTTA->szText, strToolTip, sizeof(pTTTA->szText));
else
lstrcpyn(pTTTW->szText, strToolTip, sizeof(pTTTW->szText)/sizeof(TCHAR));
#endif // _UNICODE
}
return FALSE; // we didn't handle the message, let the
// framework continue propagating the message
}
#endif // XHTMLTOOLTIPS
//=============================================================================
// PreDisplayToolTip - returns TRUE if tooltip should be displayed
BOOL CXHtmlTree::PreDisplayToolTip(BOOL bAlwaysRemoveHtml, CString& strToolTip)
//=============================================================================
{
BOOL rc = FALSE;
if (m_bDragging)
return rc;
strToolTip = _T("");
// get the mouse position
const MSG* pMessage;
pMessage = GetCurrentMessage();
ASSERT(pMessage);
CPoint point;
point = pMessage->pt; // get the point from the message
ScreenToClient(&point); // convert the point's coords to be relative
// to this control
// see if the point falls on a tree item
UINT flags = 0;
HTREEITEM hItem = HitTest(point, &flags);
if (IsSeparator(hItem)) //+++1.6
return FALSE; // no tooltip for separator
if (hItem && (flags & TVHT_ONITEM))
{
// it did fall on an item
TRACE(_T("in PreDisplayToolTip: mouse on item %X\n"), hItem);
XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);
if (pXTCD)
{
// get rect for item text
CRect rectClient;
GetClientRect(&rectClient);
CRect rectText;
GetItemRect(hItem, &rectText, TRUE); // get rect for text
rectText.right = pXTCD->ds.nRightX;
//TRACE(_T("nRightX = %d\n"), pXTCD->ds.nRightX);
//TRACERECT(rectText);
//TRACERECT(rectClient);
strToolTip = GetItemNote(hItem);
BOOL bNote = !strToolTip.IsEmpty();
// check if text rect falls entirely inside client rect of control
if (bNote || (rectText.right > (rectClient.right - 3)))
{
TRACE(_T("note or overflow\n"));
// check if parent wants to display this tooltip --
// if lResult is not zero, don't display
LRESULT lResult = 0;
if (m_pToolTip)
{
lResult = SendRegisteredMessage(WM_XHTMLTREE_DISPLAY_TOOLTIP,
hItem, (LPARAM)m_pToolTip);
}
if (!lResult)
{
// get note again - this allows parent to modify note
// before it is displayed
BOOL bStripHtml = bAlwaysRemoveHtml || !bNote;
strToolTip = GetItemNote(hItem, bStripHtml);
if (strToolTip.IsEmpty())
strToolTip = GetItemText(hItem, bStripHtml);
rc = TRUE;
// set tip width regardless of whether there is a note
int nTipWidth = GetItemNoteWidth(hItem);
if (nTipWidth == 0)
{
// no note width specified, use a heuristic
nTipWidth = GetDefaultTipWidth();
}
if (nTipWidth && m_pToolTip && IsWindow(m_pToolTip->m_hWnd))
m_pToolTip->SetMaxTipWidth(nTipWidth);
}
}
}
}
return rc;
}
//=============================================================================
void CXHtmlTree::OnSysColorChange()
//=============================================================================
{
CTreeCtrl::OnSysColorChange();
SetColors();
}
//=============================================================================
COLORREF CXHtmlTree::GetItemTextBkColor(HTREEITEM hItem)
//=============================================================================
{
COLORREF rc = 0;
XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);
if (pXTCD)
rc = pXTCD->ds.crTextBackground;
return rc;
}
//=============================================================================
COLORREF CXHtmlTree::GetItemTextColor(HTREEITEM hItem)
//=============================================================================
{
COLORREF rc = 0;
XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);
if (pXTCD)
rc = pXTCD->ds.crText;
return rc;
}
//=============================================================================
BOOL CXHtmlTree::SetItemText(HTREEITEM hItem, LPCTSTR lpszItem) //+++1.6
//=============================================================================
{
if (IsSeparator(hItem))
return FALSE;
return CTreeCtrl::SetItemText(hItem, lpszItem);
}
//=============================================================================
COLORREF CXHtmlTree::SetItemTextBkColor(HTREEITEM hItem, COLORREF rgb)
//=============================================================================
{
COLORREF rc = 0;
XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);
if (pXTCD)
{
rc = pXTCD->ds.crTextBackground;
pXTCD->ds.crTextBackground = rgb;
}
return rc;
}
//=============================================================================
COLORREF CXHtmlTree::SetItemTextColor(HTREEITEM hItem, COLORREF rgb)
//=============================================================================
{
COLORREF rc = 0;
XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);
if (pXTCD)
{
rc = pXTCD->ds.crText;
pXTCD->ds.crText = rgb;
}
return rc;
}
//=============================================================================
COLORREF CXHtmlTree::SetBkColor(COLORREF rgb)
//=============================================================================
{
COLORREF old = m_crWindow;
if (rgb == COLOR_NONE)
rgb = GetSysColor(COLOR_WINDOW);
m_crCustomWindow = m_crWindow = rgb;
return old;
}
//=============================================================================
COLORREF CXHtmlTree::SetTextColor(COLORREF rgb)
//=============================================================================
{
COLORREF old = m_crWindowText;
if (rgb == COLOR_NONE)
rgb = GetSysColor(COLOR_WINDOWTEXT);
m_crCustomWindowText = m_crWindowText = rgb;
return old;
}
//=============================================================================
COLORREF CXHtmlTree::SetInsertMarkColor(COLORREF rgb)
//=============================================================================
{
if (rgb == COLOR_NONE)
rgb = GetSysColor(COLOR_HIGHLIGHT);
m_crInsertMark = rgb;
return CTreeCtrl::SetInsertMarkColor(rgb);
}
//=============================================================================
CXHtmlTree& CXHtmlTree::SetSeparatorColor(COLORREF rgb) //+++1.6
//=============================================================================
{
if (rgb == COLOR_NONE)
rgb = GetSysColor(COLOR_GRAYTEXT);
m_crSeparator = rgb;
return *this;
}
//=============================================================================
void CXHtmlTree::SetColors()
//=============================================================================
{
m_crWindow = GetSysColor(COLOR_WINDOW);
m_crWindowText = GetSysColor(COLOR_WINDOWTEXT);
m_crAnchorText = RGB(0,0,255);
m_crGrayText = GetSysColor(COLOR_GRAYTEXT);
m_crHighlight = GetSysColor(COLOR_HIGHLIGHT);
m_crHighlightText = GetSysColor(COLOR_HIGHLIGHTTEXT);
m_crInsertMark = GetSysColor(COLOR_HIGHLIGHT);
m_crSeparator = GetSysColor(COLOR_GRAYTEXT); //+++1.6
if (m_crCustomWindow != COLOR_NONE)
m_crWindow = m_crCustomWindow;
if (m_crCustomWindowText != COLOR_NONE)
m_crWindowText = m_crCustomWindowText;
}
//=============================================================================
BOOL CXHtmlTree::EnableWindow(BOOL bEnable /*= TRUE*/)
//=============================================================================
{
BOOL rc = CTreeCtrl::EnableWindow(bEnable);
if (bEnable)
{
if (m_crCustomWindow != COLOR_NONE)
m_crWindow = m_crCustomWindow;
else
m_crWindow = GetSysColor(COLOR_WINDOW);
if (m_crCustomWindowText != COLOR_NONE)
m_crWindowText = m_crCustomWindowText;
else
m_crWindowText = GetSysColor(COLOR_WINDOWTEXT);
}
else
{
m_crWindow = GetDisabledColor(GetSysColor(COLOR_WINDOW));
m_crWindowText = GetSysColor(COLOR_GRAYTEXT);
}
return rc;
}
//=============================================================================
COLORREF CXHtmlTree::GetDisabledColor(COLORREF color)
//=============================================================================
{
BYTE r = GetRValue(color);
BYTE g = GetGValue(color);
BYTE b = GetBValue(color);
const BYTE disabled_value = 10;
r = (r >= disabled_value) ? (BYTE)(r - disabled_value) : r;
g = (g >= disabled_value) ? (BYTE)(g - disabled_value) : g;
b = (b >= disabled_value) ? (BYTE)(b - disabled_value) : b;
return RGB(r, g, b);
}
//=============================================================================
CXHtmlTree& CXHtmlTree::SetLogfont(LOGFONT * pLogFont)
//=============================================================================
{
ASSERT(pLogFont);
if (pLogFont)
m_lf = *pLogFont;
return *this;
}
//=============================================================================
BOOL CXHtmlTree::GetItemBold(HTREEITEM hItem)
//=============================================================================
{
BOOL rc = FALSE;
XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);
if (pXTCD)
rc = pXTCD->ds.bBold;
return rc;
}
//=============================================================================
BOOL CXHtmlTree::SetItemBold(HTREEITEM hItem, BOOL bBold)
//=============================================================================
{
BOOL rc = FALSE;
XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);
if (pXTCD)
{
rc = pXTCD->ds.bBold;
pXTCD->ds.bBold = bBold;
}
return rc;
}
//=============================================================================
BOOL CXHtmlTree::DeleteAllItems()
//=============================================================================
{
// avoid unneeded selchange notifications
SelectItem(NULL);
CollapseAll();
BOOL bOldDestroyingTree = m_bDestroyingTree;
m_bDestroyingTree = TRUE;
BOOL rc = CTreeCtrl::DeleteAllItems();
DeleteMap();
m_bDestroyingTree = bOldDestroyingTree;
return rc;
}
//=============================================================================
void CXHtmlTree::CollapseAll()
//=============================================================================
{
HTREEITEM hItemRoot = GetRootItem();
HTREEITEM hItem = hItemRoot;
if (hItem)
{
do
{
CollapseBranch(hItem);
} while ((hItem = GetNextSiblingItem(hItem)) != NULL);
SelectItem(hItemRoot);
SendMessage(WM_HSCROLL, SB_LEFT);
UpdateWindow();
}
}
//=============================================================================
void CXHtmlTree::ExpandAll()
//=============================================================================
{
HTREEITEM hItemSel = GetSelectedItem();
if (!hItemSel)
hItemSel = GetRootItem();
if (hItemSel)
{
HTREEITEM hItem = GetRootItem(); // must start with root for best performance
SetRedraw(FALSE);
do
{
ExpandBranch(hItem);
} while ((hItem = GetNextSiblingItem(hItem)) != NULL);
SelectItem(hItemSel);
SetScrollPos(SB_VERT, 0);
EnsureVisible(hItemSel);
SendMessage(WM_HSCROLL, SB_LEFT);
SetRedraw(TRUE);
}
}
//=============================================================================
void CXHtmlTree::ExpandBranch(HTREEITEM hItem)
//=============================================================================
{
if (hItem && ItemHasChildren(hItem))
{
Expand(hItem, TVE_EXPAND);
hItem = GetChildItem(hItem);
if (hItem)
{
do
{
ExpandBranch(hItem);
} while ((hItem = GetNextSiblingItem(hItem)) != NULL);
}
}
}
//=============================================================================
void CXHtmlTree::CollapseBranch(HTREEITEM hItem)
//=============================================================================
{
if (hItem && ItemHasChildren(hItem))
{
Expand(hItem, TVE_COLLAPSE);
hItem = GetChildItem(hItem);
if (hItem && ItemHasChildren(hItem))
{
do
{
CollapseBranch(hItem);
} while ((hItem = GetNextSiblingItem(hItem)) != NULL);
}
}
}
//=============================================================================
BOOL CXHtmlTree::Expand(HTREEITEM hItem, UINT nCode)
//=============================================================================
{
if (hItem && ItemHasChildren(hItem))
{
XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);
if (pXTCD && pXTCD->bEnabled)
{
BOOL bOldExpanded = pXTCD->bExpanded;
if (nCode == TVE_COLLAPSE || nCode == TVE_COLLAPSERESET)
{
pXTCD->bExpanded = FALSE;
}
else if (nCode == TVE_EXPAND)
{
pXTCD->bExpanded = TRUE;
}
else if (nCode == TVE_TOGGLE)
{
if (bOldExpanded)
pXTCD->bExpanded = FALSE;
else
pXTCD->bExpanded = TRUE;
}
else
{
TRACE(_T("ERROR bad nCode=%u\n"), nCode);
}
if (pXTCD->bExpanded)
pXTCD->bHasBeenExpanded = TRUE;
if (pXTCD->bExpanded != bOldExpanded)
SendRegisteredMessage(WM_XHTMLTREE_ITEM_EXPANDED, hItem,
pXTCD->bExpanded);
}
}
TRACE(_T("calling CTreeCtrl::Expand()\n"));
BOOL rc = CTreeCtrl::Expand(hItem, nCode);
return rc;
}
//=============================================================================
BOOL CXHtmlTree::GetHasBeenExpanded(HTREEITEM hItem)
//=============================================================================
{
BOOL rc = FALSE;
XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);
if (pXTCD)
{
rc = pXTCD->bHasBeenExpanded;
}
return rc;
}
//=============================================================================
void CXHtmlTree::CheckAll(BOOL bCheck)
//=============================================================================
{
if (m_bCheckBoxes)
{
if (m_bSmartCheck)
{
// check all root-level items
HTREEITEM hRoot = GetRootItem();
while (hRoot)
{
SetCheck(hRoot, bCheck);
hRoot = GetNextItem(hRoot, TVGN_NEXT); // get next root item
}
}
else
{
// check all items
HTREEITEM hItem = GetRootItem();
while (hItem)
{
SetCheck(hItem, bCheck);
hItem = GetNextItem(hItem); // get next sequential item
}
}
}
}
//=============================================================================
int CXHtmlTree::GetCheckedCount()
//=============================================================================
{
int rc = 0;
if (!m_bCheckBoxes)
return 0;
for (HTREEITEM hItem = GetRootItem();
hItem != NULL;
hItem = GetNextItem(hItem))
{
if (GetCheck(hItem))
rc++;
}
return rc;
}
//=============================================================================
int CXHtmlTree::GetChildrenCheckedCount(HTREEITEM hItem)
//=============================================================================
{
int rc = 0;
if (!m_bCheckBoxes)
return 0;
if (!hItem)
hItem = GetRootItem();
if (hItem && m_bSmartCheck)
{
XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);
if (pXTCD)
{
rc = pXTCD->nChecked;
}
}
else if (hItem && ItemHasChildren(hItem))
{
HTREEITEM hChild = GetChildItem(hItem);
if (hChild)
{
do
{
if (GetCheck(hChild))
rc++;
rc += GetChildrenCheckedCount(hChild);
} while ((hChild = GetNextSiblingItem(hChild)) != NULL);
}
}
return rc;
}
//=============================================================================
int CXHtmlTree::GetChildrenDisabledCount(HTREEITEM hItem)
//=============================================================================
{
int rc = 0;
if (!hItem)
hItem = GetRootItem();
if (hItem && ItemHasChildren(hItem))
{
HTREEITEM hChild = GetChildItem(hItem);
if (hChild)
{
do
{
if (!IsEnabled(hChild))
rc++;
rc += GetChildrenDisabledCount(hChild);
} while ((hChild = GetNextSiblingItem(hChild)) != NULL);
}
}
return rc;
}
//=============================================================================
int CXHtmlTree::GetChildrenCount(HTREEITEM hItem)
//=============================================================================
{
int rc = 0;
if (!hItem)
hItem = GetRootItem();
if (hItem && m_bSmartCheck)
{
XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);
if (pXTCD)
{
rc = pXTCD->nChildren;
}
}
else if (hItem && ItemHasChildren(hItem))
{
HTREEITEM hChild = GetChildItem(hItem);
if (hChild)
{
do
{
rc++;
rc += GetChildrenCount(hChild);
} while ((hChild = GetNextSiblingItem(hChild)) != NULL);
}
}
return rc;
}
//=============================================================================
int CXHtmlTree::GetSeparatorCount(HTREEITEM hItem) //+++1.6
//=============================================================================
{
int rc = 0;
if (!hItem)
hItem = GetRootItem();
XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);
if (pXTCD)
{
rc = pXTCD->nSeparators;
}
return rc;
}
//=============================================================================
CString CXHtmlTree::GetItemText(HTREEITEM hItem, BOOL bStripHtml /*= FALSE*/) const
//=============================================================================
{
CString strText = _T("");
if (hItem)
{
strText = CTreeCtrl::GetItemText(hItem);
if (bStripHtml)
{
#ifdef XHTMLHTML
// remove html tags
CXHtmlDraw hd;
int n = strText.GetLength();
if (n > 3) // minimum html string
{
TCHAR *s = new TCHAR [n + 16];
hd.GetPlainText(strText, s, n+4);
strText = s;
delete [] s;
}
#endif // XHTMLHTML
}
}
return strText;
}
//=============================================================================
void CXHtmlTree::OnLButtonDown(UINT nFlags, CPoint point)
//=============================================================================
{
TRACE(_T("in CXHtmlTree::OnLButtonDown\n"));
UINT uFlags = 0;
HTREEITEM hItem = HitTest(point, &uFlags);
HTREEITEM hItemSelected = GetSelectedItem();
CEdit *pEdit = GetEditControl();
if (hItem && (TVHT_ONITEMBUTTON & uFlags))
{
TRACE(_T("TVHT_ONITEMBUTTON\n"));
if (pEdit && IsWindow(pEdit->m_hWnd))
{
TRACE(_T("sending WM_CLOSE to edit box\n"));
pEdit->SendMessage(WM_CLOSE);
}
XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);
if (pXTCD && !pXTCD->bEnabled)
return;
if (pXTCD && pXTCD->bEnabled && ItemHasChildren(hItem))
{
BOOL bExpanded = !pXTCD->bExpanded;
Expand(hItem, bExpanded ? TVE_EXPAND : TVE_COLLAPSE);
return;
}
}
else if (hItem && (TVHT_ONITEMSTATEICON & uFlags))
{
TRACE(_T("TVHT_ONITEMSTATEICON\n"));
// click on checkbox
}
else if (hItem && IsSeparator(hItem)) //+++1.6
{
if (hItem != hItemSelected)
SelectItem(hItem);
m_hItemButtonDown = hItem;
TRACE(_T("setting LBUTTONDOWN_TIMER >>>>>\n"));
SetTimer(LBUTTONDOWN_TIMER, 100, NULL);
return;
}
else if (hItem && (hItem == hItemSelected))
{
// item was already selected, so catch the button up state -
// this will be start of edit. We don't need to check if disabled,
// because disabled items can't be selected
//
// NOTE: we use timer because we don't always get WM_LBUTTONUP message.
//
m_hItemButtonDown = hItem;
TRACE(_T("setting LBUTTONDOWN_TIMER >>>>>\n"));
SetTimer(LBUTTONDOWN_TIMER, 100, NULL);
#ifndef XHTMLDRAGDROP
// We disable the return to prevent edit box opening on button down -
// it should open only on button up.
// For drag & drop, we need to call base function to allow
// OnBegindrag() to be called.
return;
#endif // XHTMLDRAGDROP
}
#ifdef XHTMLDRAGDROP
else if (hItem && (hItem != hItemSelected))
{
SetTimer(SELECT_TIMER, 50, NULL);
}
#endif // XHTMLDRAGDROP
TRACE(_T("calling CTreeCtrl::OnLButtonDown\n"));
CTreeCtrl::OnLButtonDown(nFlags, point);
}
//=============================================================================
void CXHtmlTree::OnRButtonDown(UINT nFlags, CPoint point)
//=============================================================================
{
#ifdef XHTMLDRAGDROP
EndDragScroll();
SendRegisteredMessage(WM_XHTMLTREE_END_DRAG, 0, 0);
#endif // XHTMLDRAGDROP
CTreeCtrl::OnRButtonDown(nFlags, point);
}
//=============================================================================
// OnClick - handle clicking on checkbox and link
BOOL CXHtmlTree::OnClick(NMHDR* /*pNMHDR*/, LRESULT* pResult)
//=============================================================================
{
TRACE(_T("in CXHtmlTree::OnClick\n"));
BOOL rc = FALSE; // allow parent to handle
CPoint point;
::GetCursorPos(&point);
ScreenToClient(&point);
UINT flags = 0;
HTREEITEM hItem = HitTest(point, &flags);
TRACE(_T("in CXHtmlTree::OnClick: hItem=%X\n"), hItem);
XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);
if (pXTCD) // will not be NULL if hItem is valid
{
if (pXTCD->bSeparator) //+++1.6
{
return TRUE; // can't set checkbox if separator
}
else if (flags & TVHT_ONITEMSTATEICON)
{
TRACE(_T("click on checkbox\n"));
if (pXTCD->bEnabled && !m_bReadOnly)
{
SetCheck(hItem, !pXTCD->bChecked);
//BOOL bCheck = GetCheck(hItem);
//TRACE(_T("item %s checked\n"), bCheck ? _T("is") : _T("is not"));
if (m_bSelectFollowsCheck)
SelectItem(hItem);
int nState = GetStateImage(hItem);
nState |= HDCheckboxImageList::HOT_INDEX;
SetItemState(hItem, INDEXTOSTATEIMAGEMASK(nState), TVIS_STATEIMAGEMASK);
m_hHotItem = hItem;
}
else
{
rc = TRUE;
}
}
else
{
TRACE(_T("not on checkbox\n"));
// can't use TVHT_ONITEMLABEL because text might be shifted left
if (pXTCD->ds.bHasAnchor && pXTCD->bEnabled)
{
if (IsOverAnchor(hItem, point))
{
#ifdef XHTMLHTML
TRACE(_T("click on link\n"));
m_Links.GotoURL(pXTCD->ds.pszAnchor, SW_SHOW, (LPARAM)(UINT_PTR)hItem);
#endif // XHTMLHTML
}
else
{
TRACE(_T("not in link rect\n"));
}
}
else if (!pXTCD->bEnabled)
{
TRACE(_T("click on disabled item\n"));
rc = TRUE;
}
}
}
*pResult = 0;
return rc;
}
//=============================================================================
BOOL CXHtmlTree::OnDblclk(NMHDR* /*pNMHDR*/, LRESULT* pResult)
//=============================================================================
{
TRACE(_T("in CXHtmlTree::OnDblclk\n"));
BOOL rc = FALSE; // allow parent to handle
CPoint point;
::GetCursorPos(&point);
ScreenToClient(&point);
UINT flags = 0;
HTREEITEM hItem = HitTest(point, &flags);
TRACE(_T("in CXHtmlTree::OnDblclk: hItem=%X\n"), hItem);
if (hItem && (flags & (TVHT_ONITEM | TVHT_ONITEMBUTTON | TVHT_ONITEMSTATEICON)))
{
XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);
if (pXTCD && !pXTCD->bEnabled)
{
TRACE(_T("double click on disabled item\n"));
rc = TRUE; // don't allow default processing
}
else if (pXTCD && pXTCD->bEnabled && ItemHasChildren(hItem))
{
pXTCD->bExpanded = !pXTCD->bExpanded;
pXTCD->bHasBeenExpanded = TRUE;
SendRegisteredMessage(WM_XHTMLTREE_ITEM_EXPANDED, hItem,
pXTCD->bExpanded);
}
}
*pResult = rc;
return rc;
}
//=============================================================================
BOOL CXHtmlTree::OnSelchanged(NMHDR* pNMHDR, LRESULT* pResult)
//=============================================================================
{
BOOL rc = FALSE; // allow parent to handle
NMTREEVIEW* pNMTreeView = (NMTREEVIEW*)pNMHDR;
HTREEITEM hItem = pNMTreeView->itemNew.hItem;
HTREEITEM hOldItem = pNMTreeView->itemOld.hItem;
CString strItem = GetItemText(hItem);
TRACE(_T("in CXHtmlTree::OnSelchanged: <%s>\n"), strItem);
XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);
if (pXTCD && !pXTCD->bEnabled)
{
if (hOldItem)
{
SelectItem(hOldItem);
}
rc = TRUE;
}
else
{
if (hItem == m_hPreviousItem)
rc = TRUE;
}
if (!rc)
m_hPreviousItem = hItem;
*pResult = rc;
return rc;
}
//=============================================================================
BOOL CXHtmlTree::OnSelchanging(NMHDR* pNMHDR, LRESULT* pResult)
//=============================================================================
{
BOOL rc = FALSE; // allow parent to handle
NMTREEVIEW* pNMTreeView = (NMTREEVIEW*)pNMHDR;
HTREEITEM hItem = pNMTreeView->itemNew.hItem;
CString strItem = GetItemText(hItem);
TRACE(_T("in CXHtmlTree::OnSelchanging: <%s>\n"), strItem);
XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);
if (pXTCD && !pXTCD->bEnabled)
{
rc = TRUE;
}
*pResult = rc;
return rc;
}
//=============================================================================
void CXHtmlTree::OnSize(UINT nType, int cx, int cy)
//=============================================================================
{
TRACE(_T("in CXHtmlTree::OnSize\n"));
CTreeCtrl::OnSize(nType, cx, cy);
m_bFirstTime = TRUE; // this will cause tooltips to be re-created
}
//=============================================================================
int CXHtmlTree::GetIndentLevel(HTREEITEM hItem)
//=============================================================================
{
int nIndent = 0;
while ((hItem = GetParentItem(hItem)) != NULL)
nIndent++;
return nIndent;
}
#ifdef XHTMLXML
//=============================================================================
BOOL CXHtmlTree::ConvertBuffer(const BYTE * inbuf,
DWORD inlen, // in bytes
BYTE ** outbuf,
DWORD& outlen, // in bytes
ConvertAction eConvertAction /*= NoConvertAction*/)
//=============================================================================
{
BOOL rc = FALSE;
ASSERT(inbuf);
ASSERT(inlen != 0);
outlen = 0;
if (inbuf && (inlen != 0))
{
TRACE(_T("eConvertAction=%d\n"), eConvertAction);
// copy buffer to ensure it's null terminated
BYTE * buf = new BYTE [inlen+16];
memset(buf, 0, inlen+16);
memcpy(buf, inbuf, inlen);
if (eConvertAction == ConvertToUnicode)
{
TRACE(_T("converting to unicode\n"));
int wlen = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)buf, -1, NULL, 0);
TRACE(_T("wlen=%d\n"), wlen);
WCHAR *pszText = new WCHAR [wlen+16];
memset(pszText, 0, (wlen+16) * sizeof(WCHAR));
MultiByteToWideChar(CP_ACP, 0, (LPCSTR)buf, -1, pszText, wlen+2);
outlen = (DWORD)wcslen(pszText) * sizeof(WCHAR); // bytes
*outbuf = (BYTE*) pszText;
delete [] buf;
}
else if (eConvertAction == ConvertToAnsi)
{
TRACE(_T("converting to ansi\n"));
LPCWSTR wp = (LPCWSTR) buf;
int alen = WideCharToMultiByte(CP_ACP, 0, wp, -1, NULL, 0, NULL, NULL);
TRACE(_T("alen=%d\n"), alen);
char *pszText = new char [alen+16];
memset(pszText, 0, alen+16);
WideCharToMultiByte(CP_ACP, 0, wp, -1, pszText, alen+2, NULL, NULL);
outlen = (DWORD)strlen(pszText); // bytes
*outbuf = (BYTE*) pszText;
delete [] buf;
}
else
{
// no conversion
outlen = inlen; // bytes
*outbuf = buf;
}
TRACE(_T("outlen=%d\n"), outlen);
rc = TRUE;
}
return rc;
}
//=============================================================================
BOOL CXHtmlTree::LoadXmlFromBuffer(const BYTE * pBuf,
DWORD len, // bytes
ConvertAction eConvertAction)
//=============================================================================
{
BOOL rc = FALSE;
ASSERT(pBuf);
if (pBuf)
{
DWORD outlen = 0;
BYTE * outbuf = 0;
rc = ConvertBuffer(pBuf, len, &outbuf, outlen,
eConvertAction);
if (rc)
{
CString strXML = (TCHAR *) outbuf;
delete [] outbuf;
// remove endofline and tabs
strXML.Remove(_T('\n'));
strXML.Remove(_T('\r'));
strXML.Remove(_T('\t'));
m_nXmlCount = 0;
CXmlDocument xml;
BOOL ret = xml.Parse(strXML);
TRACE(_T("Parse returned %d\n"), ret);
if (ret) // useless, always returns TRUE
{
TRACE(_T("loading root +++++\n"));
HTREEITEM hRoot = InsertXmlItem(xml.GetRootElement(), 0);
if (hRoot)
m_nXmlCount++;
TRACE(_T("loading children +++++\n"));
LoadXml(xml, xml.GetRootElement(), hRoot, m_nXmlCount);
rc = TRUE;
}
else
{
TRACE(_T("ERROR - Parse failed\n"));
}
}
}
return rc;
}
//=============================================================================
BOOL CXHtmlTree::LoadXmlFromResource(HINSTANCE hInstance,
LPCTSTR lpszResId,
LPCTSTR lpszResType,
ConvertAction eConvertAction)
//=============================================================================
{
BOOL rc = FALSE;
ASSERT(lpszResId);
ASSERT(lpszResType);
if (lpszResId && lpszResType)
{
TCHAR *pszRes = NULL;
// is this a resource name string or an id?
if (HIWORD(lpszResId) == 0)
{
// id
pszRes = MAKEINTRESOURCE(LOWORD((UINT)(UINT_PTR)lpszResId));
}
else
{
// string
pszRes = (TCHAR *)lpszResId;
TRACE(_T("pszRes=%s\n"), pszRes);
}
HRSRC hrsrc = FindResource(hInstance, pszRes, lpszResType);
_ASSERTE(hrsrc);
if (hrsrc)
{
DWORD dwSize = SizeofResource(hInstance, hrsrc); // in bytes
TRACE(_T("dwSize=%d\n"), dwSize);
HGLOBAL hglob = LoadResource(hInstance, hrsrc);
_ASSERTE(hglob);
if (hglob)
{
LPVOID lplock = LockResource(hglob);
_ASSERTE(lplock);
if (lplock)
{
BYTE *pBuf = new BYTE [dwSize+16];
ASSERT(pBuf);
if (pBuf)
{
memcpy(pBuf, lplock, dwSize);
pBuf[dwSize] = 0;
pBuf[dwSize+1] = 0;
pBuf[dwSize+2] = 0;
LoadXmlFromBuffer(pBuf, dwSize, eConvertAction);
delete [] pBuf;
rc = TRUE;
}
}
}
}
}
return rc;
}
//=============================================================================
BOOL CXHtmlTree::LoadXmlFromFile(LPCTSTR lpszFile, ConvertAction eConvertAction)
//=============================================================================
{
TRACE(_T("in CXHtmlTree::LoadXmlFromFile: %s\n"), lpszFile);
BOOL rc = FALSE;
DWORD dwFileSize = 0;
HANDLE hFile = NULL;
ASSERT(lpszFile);
if (lpszFile)
{
hFile = CreateFile(lpszFile,
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
TRACE(_T("ERROR - CreateFile failed\n"));
hFile = 0;
}
else
{
dwFileSize = GetFileSize(hFile, NULL);
if ((dwFileSize != INVALID_FILE_SIZE))
{
BYTE * pBuf = new BYTE [dwFileSize+16];
ASSERT(pBuf);
if (pBuf)
{
DWORD dwBytesRead = 0;
BOOL bRet = ReadFile(hFile,
(LPVOID) pBuf,
dwFileSize,
&dwBytesRead,
NULL);
if (bRet)
{
pBuf[dwFileSize] = 0;
pBuf[dwFileSize+1] = 0;
pBuf[dwFileSize+2] = 0;
LoadXmlFromBuffer(pBuf, dwFileSize,
eConvertAction);
rc = TRUE;
}
else
{
TRACE(_T("ERROR - ReadFile failed\n"));
dwFileSize = 0;
}
delete [] pBuf;
}
else
{
TRACE(_T("ERROR - failed to allocate memory\n"));
dwFileSize = 0;
}
}
else
{
TRACE(_T("ERROR - GetFileSize failed\n"));
dwFileSize = 0;
}
}
}
else
{
TRACE(_T("ERROR - bad parameter\n"));
}
if (hFile)
CloseHandle(hFile);
return rc;
}
//=============================================================================
HTREEITEM CXHtmlTree::InsertXmlItem(CXmlElement *pElement, HTREEITEM hParent)
//=============================================================================
{
TRACE(_T("in CXHtmlTree::InsertXmlItem\n"));
HTREEITEM hItem = 0;
ASSERT(pElement);
if (pElement)
{
CString strName = pElement->GetValue(_T("name"));
TRACE(_T("InsertXmlItem: <%s>\n"), strName);
// hack to stop display
if (/*!strName.IsEmpty() &&*/ strName.Compare(_T("...")) != 0)
{
strName.TrimLeft();
strName.TrimRight();
#if 0 // -----------------------------------------------------------
if (strName.GetLength() <= 0)
{
TRACE(_T("ERROR - name is empty\n"));
ASSERT(FALSE);
}
#endif // -----------------------------------------------------------
XHTMLTREEDATA xhtd;
CString strSeparator = pElement->GetValue(_T("separator")); //+++1.6
if (!strSeparator.IsEmpty())
xhtd.bSeparator = _ttoi(strSeparator);
CString strChecked = pElement->GetValue(_T("checked"));
if (!strChecked.IsEmpty() && !xhtd.bSeparator)
xhtd.bChecked = _ttoi(strChecked);
CString strEnabled = pElement->GetValue(_T("enabled"));
if (!strEnabled.IsEmpty() && !xhtd.bSeparator)
xhtd.bEnabled = _ttoi(strEnabled);
TRACE(_T("name=<%s>, <%s>, <%s> \n"), strName, strChecked, strEnabled);
TRACE(_T("bChecked=%d bEnabled=%d bSeparator=%d\n"), xhtd.bChecked, xhtd.bEnabled, xhtd.bSeparator);
// get colors - must be named colors, e.g. "red"
CString strTextColor = pElement->GetValue(_T("text-color"));
CString strBackgroundColor = pElement->GetValue(_T("text-background-color"));
if (!strTextColor.IsEmpty())
{
CXNamedColors crText(strTextColor);
xhtd.ds.crText = crText.GetRGB();
}
if (!strBackgroundColor.IsEmpty())
{
CXNamedColors crBackground(strBackgroundColor);
xhtd.ds.crTextBackground = crBackground.GetRGB();
}
int nImage = -1;
CString strImage = pElement->GetValue(_T("image"));
if (!strImage.IsEmpty() && !xhtd.bSeparator)
{
nImage = _ttoi(strImage);
CImageList *pImageList = GetImageList(TVSIL_NORMAL);
if (pImageList) //+++1.5
{
int nCount = pImageList->GetImageCount();
if (nImage >= nCount)
{
TRACE(_T("ERROR - invalid image\n"));
ASSERT(FALSE);
nImage = -1;
}
}
else
{
TRACE(_T("WARNING no image list, setting m_bImages to FALSE\n"));
m_bImages = FALSE;
}
}
//static int nItem = 1;
// line not empty - add item
TVINSERTSTRUCT tvis = { 0 };
if (!xhtd.bSeparator)
{
tvis.item.mask = TVIF_TEXT;
tvis.item.cchTextMax = strName.GetLength();
tvis.item.pszText = strName.LockBuffer();
}
tvis.hParent = hParent;
tvis.item.iImage = TV_NOIMAGE;
tvis.item.iSelectedImage = TV_NOIMAGE;
tvis.item.mask |= TVIF_IMAGE | TVIF_SELECTEDIMAGE;
//tvis.item.mask |= TVIF_PARAM;
//tvis.item.lParam = nItem++; // for debugging
if (m_bImages && (nImage != -1) && !xhtd.bSeparator) //+++1.5
{
tvis.item.iImage = tvis.item.iSelectedImage = nImage;
}
hItem = InsertItem(&tvis, &xhtd);
ASSERT(hItem);
if (!xhtd.bSeparator)
strName.UnlockBuffer();
if (hItem)
{
if (xhtd.bSeparator)
{
SetItemState(hItem, INDEXTOSTATEIMAGEMASK(0), TVIS_STATEIMAGEMASK);
// increment separator count in parents
HTREEITEM hParentSep = hItem;
while ((hParentSep = GetParentItem(hParentSep)) != NULL)
{
IncrementSeparators(hParentSep, 1);
}
}
else
{
CString strNote = pElement->GetValue(_T("note"));
int nNoteWidth = 0;
CString strNoteWidth = pElement->GetValue(_T("note-width"));
if (!strNoteWidth.IsEmpty())
nNoteWidth = _ttoi(strNoteWidth);
if (!strNote.IsEmpty())
SetItemNote(hItem, strNote, nNoteWidth);
}
}
}
}
return hItem;
}
//=============================================================================
BOOL CXHtmlTree::LoadXml(CXmlDocument& xml,
CXmlElement *pElement,
HTREEITEM hParent,
int& nCount)
//=============================================================================
{
BOOL rc = TRUE;
ASSERT(pElement);
if (pElement)
{
CXmlElement *pChild = xml.GetFirstChild(pElement);
while (pChild != NULL)
{
HTREEITEM hItem = InsertXmlItem(pChild, hParent);
if (!hItem)
{
// found '...'
rc = FALSE;
break;
}
nCount++;
rc = LoadXml(xml, pChild, hItem, nCount);
if (!rc)
break;
pChild = xml.GetNextSibling(pElement);
}
}
return rc;
}
//=============================================================================
CString CXHtmlTree::GetXmlText(HTREEITEM hItem, LPCTSTR lpszElem)
//=============================================================================
{
ASSERT(lpszElem);
CString strElem = lpszElem;
XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);
CString s = _T("");
s = GetItemText(hItem);
s.Replace(_T("&"), _T("&"));
s.Replace(_T("<"), _T("<"));
s.Replace(_T(">"), _T(">"));
s.Replace(_T("\""), _T("""));
CString strXml = _T("");
strXml.Format(_T("<%s name=\"%s\""), strElem, s);
if (pXTCD)
{
s.Format(_T(" separator=\"%s\""), pXTCD->bSeparator ? _T("1") : _T("0"));
strXml += s;
s.Format(_T(" checked=\"%s\""), pXTCD->bChecked ? _T("1") : _T("0"));
strXml += s;
s.Format(_T(" enabled=\"%s\""), pXTCD->bEnabled ? _T("1") : _T("0"));
strXml += s;
if (pXTCD->ds.crText != COLOR_NONE)
{
TCHAR szColor[50];
szColor[0] = _T('\0');
CXNamedColors nc(pXTCD->ds.crText);
if (nc.IsKnownColor())
nc.GetName(szColor, sizeof(szColor)/sizeof(TCHAR)-1);
else
nc.GetHex(szColor, sizeof(szColor)/sizeof(TCHAR)-1);
if (szColor[0] != _T('\0'))
{
s.Format(_T(" text-color=\"%s\""), szColor);
strXml += s;
}
}
if (pXTCD->ds.crTextBackground != COLOR_NONE)
{
TCHAR szColor[50];
szColor[0] = _T('\0');
CXNamedColors nc(pXTCD->ds.crTextBackground);
if (nc.IsKnownColor())
nc.GetName(szColor, sizeof(szColor)/sizeof(TCHAR)-1);
else
nc.GetHex(szColor, sizeof(szColor)/sizeof(TCHAR)-1);
if (szColor[0] != _T('\0'))
{
s.Format(_T(" text-background-color=\"%s\""), szColor);
strXml += s;
}
}
}
int nImage, nSelectedImage;
GetItemImage(hItem, nImage, nSelectedImage);
if (nImage != TV_NOIMAGE)
{
s.Format(_T(" image=\"%d\""), nImage);
strXml += s;
}
s = GetItemNote(hItem);
if (!s.IsEmpty())
{
s.Replace(_T("&"), _T("&"));
s.Replace(_T("<"), _T("<"));
s.Replace(_T(">"), _T(">"));
s.Replace(_T("\""), _T("""));
strXml += _T(" note=\"");
strXml += s;
strXml += _T("\"");
int nNoteWidth = 0;
if (pXTCD)
nNoteWidth = pXTCD->nTipWidth;
if (nNoteWidth)
{
s.Format(_T(" note-width=\"%d\""), nNoteWidth);
strXml += s;
}
}
return strXml;
}
//=============================================================================
BOOL CXHtmlTree::SaveXml(HTREEITEM hItem,
LPCTSTR lpszFile,
BOOL bSaveAsUTF16) // useful only on VS2005
//=============================================================================
{
BOOL rc = FALSE;
ASSERT(lpszFile);
if (lpszFile)
{
FILE *f = NULL;
#if _MSC_VER >= 1400
if (bSaveAsUTF16)
f = _tfopen(lpszFile, _T("w, ccs=UTF-16LE"));
else
#else
UNUSED_ALWAYS(bSaveAsUTF16);
#endif
f = _tfopen(lpszFile, _T("w"));
if (f && hItem)
{
_ftprintf(f, _T("<?xml version=\"1.0\" encoding=\"ISO-8859-15\"?>\n"));
int nPrevIndent = -1;
int nIndent = 0;
int nLevel = 1;
BOOL bInItem = FALSE;
CString ws = _T("");
CString strText = _T("");
HTREEITEM hNext = hItem;
TCHAR *elem = _T("root");
while (hNext)
{
strText = GetXmlText(hNext, elem);
elem = _T("item");
nIndent = GetIndentLevel(hNext);
if (nIndent > nPrevIndent)
{
if (bInItem)
_ftprintf(f, _T(">\n"));
ws = _T("");
for (int i = 0; i < nIndent; i++)
ws += _T('\t');
_ftprintf(f, _T("%s%s"), ws, strText);
bInItem = TRUE;
nLevel++;
}
else if (nIndent < nPrevIndent)
{
_ftprintf(f, _T("/>\n"), ws);
ws = _T("");
while (nPrevIndent > nIndent)
{
nPrevIndent--;
ws = _T("");
for (int i = 0; i < nPrevIndent; i++)
ws += _T('\t');
_ftprintf(f, _T("%s</item>\n"), ws);
}
_ftprintf(f, _T("%s%s"), ws, strText);
bInItem = TRUE;
}
else
{
ws = _T("");
for (int i = 0; i < nIndent; i++)
ws += _T('\t');
if (bInItem)
//_ftprintf(f, _T("%s</item>\n"), ws);
_ftprintf(f, _T("/>\n"));
_ftprintf(f, _T("%s%s"), ws, strText);
bInItem = TRUE;
}
nPrevIndent = nIndent;
hNext = GetNextItem(hNext);
}
_ftprintf(f, _T("/>\n"));
nIndent--;
while (nIndent > 0)
{
ws = _T("");
for (int i = 0; i < nIndent; i++)
ws += _T('\t');
_ftprintf(f, _T("%s</item>\n"), ws);
nIndent--;
}
_ftprintf(f, _T("</root>\n"));
fflush(f);
fclose(f);
rc = TRUE;
}
}
return rc;
}
#endif // XHTMLXML
//=============================================================================
LRESULT CXHtmlTree::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
//=============================================================================
{
LRESULT rc = CTreeCtrl::WindowProc(message, wParam, lParam);
return rc;
}
//=============================================================================
// This function does special processing for bTextOnly=TRUE, since
// the text rect is shifted left when there is no image.
//
BOOL CXHtmlTree::GetItemRect(HTREEITEM hItem, LPRECT lpRect, BOOL bTextOnly)
//=============================================================================
{
BOOL rc = CTreeCtrl::GetItemRect(hItem, lpRect, bTextOnly);
if (bTextOnly)
{
int width = GetNormalImageWidth(hItem); // get width of normal image
if (width < 0)
{
// TV_NOIMAGE specified, shift text to the left
lpRect->left += width;
lpRect->right += width;
}
XHTMLTREEDATA *pXTCD = GetItemDataStruct(hItem);
if (pXTCD)
{
lpRect->right = pXTCD->ds.nRightX;
}
//TRACERECT(*lpRect);
}
return rc;
}
//=============================================================================
BOOL CXHtmlTree::OnBeginlabeledit(NMHDR* /*pNMHDR*/, LRESULT* pResult)
//=============================================================================
{
*pResult = 0;
HTREEITEM hItem = IsOverItem();
TRACE(_T("in CXHtmlTree::OnBeginlabeledit: %X\n"), hItem);
if (hItem && IsSeparator(hItem)) //+++1.6
{
*pResult = 1; // separator item, don't allow edit
}
else if (m_bReadOnly)
{
*pResult = 1; // tree is read-only, don't allow edit
}
else
{
m_nHorzPos = GetScrollPos(SB_HORZ); // save initial scroll position
}
return (BOOL)*pResult; // 0 = allow parent to handle
}
//=============================================================================
BOOL CXHtmlTree::OnEndlabeledit(NMHDR* /*pNMHDR*/, LRESULT* pResult)
//=============================================================================
{
TRACE(_T("in CXHtmlTree::OnEndlabeledit\n"));
*pResult = 0;
if ((m_nHorzPos == 0) && (GetScrollPos(SB_HORZ) != 0))
SendMessage(WM_HSCROLL, SB_LEFT); // editing caused tree to be
// scrolled, so scroll it back
return 0; // 0 = allow parent to handle
}
//=============================================================================
// following code contributed by David McMinn
BOOL CXHtmlTree::OnCommand(WPARAM wParam, LPARAM lParam)
//=============================================================================
{
CEdit *pEdit = GetEditControl();
if (((HIWORD(wParam) == EN_SETFOCUS) || (HIWORD(wParam) == EN_CHANGE)) &&
pEdit &&
(pEdit->GetSafeHwnd() == (HWND)lParam))
{
AdjustEditRect();
}
return CTreeCtrl::OnCommand(wParam, lParam);
}
//=============================================================================
// AdjustEditRect() moves the in-place edit box to line up with item text
//
void CXHtmlTree::AdjustEditRect()
//=============================================================================
{
CEdit *pEdit = GetEditControl();
if (pEdit && IsWindow(pEdit->m_hWnd))
{
HTREEITEM hItemEdit = GetSelectedItem();
if (hItemEdit)
{
// adjust position of edit box for all items, even
// those with image
CRect rectClient;
GetClientRect(&rectClient);
CRect rectEdit;
GetItemRect(hItemEdit, &rectEdit, TRUE);
rectEdit.InflateRect(1, 1); // allow for border
rectEdit.OffsetRect(-1, -1); // overlay existing text
if (rectEdit.left < 0)
rectEdit.left = 0;
rectEdit.right = rectClient.right - 1; // extend edit box full width
// of tree control
pEdit->MoveWindow(&rectEdit);
}
}
}
#ifdef XHTMLDRAGDROP
//=============================================================================
BOOL CXHtmlTree::IsCtrlDown()
//=============================================================================
{
BOOL rc = FALSE;
// always return FALSE if XHTMLTREE_DO_CTRL_KEY is not set
if (m_dwDragOps & XHTMLTREE_DO_CTRL_KEY)
if (GetAsyncKeyState(VK_CONTROL) < 0)
rc = TRUE;
return rc;
}
//=============================================================================
BOOL CXHtmlTree::IsDragCopy()
//
// returns TRUE if copy, FALSE if move
//
// Ctrl key COPY_DRAG flag Action
// ----------+------------------+--------
// up | false | move
// down | false | copy
// up | true | copy
// down | true | move
//=============================================================================
{
BOOL rc = FALSE;
BOOL bCopyDrag = GetBit(m_dwDragOps, XHTMLTREE_DO_COPY_DRAG);
BOOL bCtrlDown = IsCtrlDown();
if (bCtrlDown && !bCopyDrag) // Ctrl down, !bCopyDrag: copy
rc = TRUE;
else if (!bCtrlDown && bCopyDrag) // Ctrl up, bCopyDrag: copy
rc = TRUE;
return rc;
}
//=============================================================================
HCURSOR CXHtmlTree::GetDragCursor()
//=============================================================================
{
BOOL bCopyDrag = IsDragCopy();
HCURSOR hCursor = bCopyDrag ? m_hDropCopyCursor : m_hDropMoveCursor;
return hCursor;
}
//=============================================================================
void CXHtmlTree::SetDragCursor()
//=============================================================================
{
HCURSOR hCursor = GetDragCursor();
if (hCursor)
SetCursor(hCursor);
else
SetCursor(m_hPreviousCursor);
}
//=============================================================================
void CXHtmlTree::OnBegindrag(NMHDR* pNMHDR, LRESULT* pResult)
//=============================================================================
{
ASSERT(!m_bDragging);
NMTREEVIEW* pNMTreeView = (NMTREEVIEW*)pNMHDR;
HTREEITEM hItem = pNMTreeView->itemNew.hItem;
ASSERT(hItem);
if (!hItem)
return;
BOOL bCopyDrag = IsDragCopy();
#ifdef _DEBUG
CString strText = GetItemText(hItem);
TRACE(_T("in CXHtmlTree::OnBegindrag: %s bCopyDrag=%d\n"), strText, bCopyDrag);
#endif
// allow parent to decide whether to permit drag
XHTMLTREEDRAGMSGDATA dragdata = { 0 };
dragdata.hItem = hItem;
dragdata.bCopyDrag = bCopyDrag;
LRESULT lResult = SendRegisteredMessage(WM_XHTMLTREE_BEGIN_DRAG, hItem,
(LPARAM)&dragdata);
if (lResult)
{
return;
}
m_hItemButtonDown = hItem;
TRACE(_T("setting LBUTTONDOWN_TIMER >>>>>\n"));
SetTimer(LBUTTONDOWN_TIMER, 100, NULL);
m_bDragging = TRUE;
if (m_nNoDropCursor && (m_hNoDropCursor == NULL))
m_hNoDropCursor = AfxGetApp()->LoadCursor(m_nNoDropCursor);
if (m_nDropCopyCursor && (m_hDropCopyCursor == NULL))
m_hDropCopyCursor = AfxGetApp()->LoadCursor(m_nDropCopyCursor);
if (m_nDropMoveCursor && (m_hDropMoveCursor == NULL))
m_hDropMoveCursor = AfxGetApp()->LoadCursor(m_nDropMoveCursor);
SetDragCursor();
m_dwDropHoverTime = GetTickCount();
SetInsertMarkColor(m_crInsertMark);
m_hPreviousDropItem = NULL;
SetCapture();
SetFocus();
*pResult = 0;
}
//=============================================================================
// Determines if hItem is a child of hitemSuspectedParent
// Called in OnTimer to prevent the case where an item is attempted
// to be made a child of its own child.
//
BOOL CXHtmlTree::IsChildNodeOf(HTREEITEM hItem, HTREEITEM hitemSuspectedParent)
//=============================================================================
{
do
{
if (hItem == hitemSuspectedParent)
break;
}
while ((hItem = GetParentItem(hItem)) != NULL);
return (hItem != NULL);
}
//=============================================================================
// CopyItem - Copies an item to a new location
// Returns - Handle of the new item
// hItem - Item to be copied
// hNewParent - Handle of the parent for new item
// hAfter - Item after which the new item should be created
HTREEITEM CXHtmlTree::MoveItem(HTREEITEM hItem,
HTREEITEM hNewParent,
HTREEITEM hAfter /*= TVI_LAST*/)
//=============================================================================
{
HTREEITEM hNewItem = NULL;
XHTMLTREEDATA *pXTCD = NULL;
if (hItem)
pXTCD = GetItemDataStruct(hItem);
if (pXTCD)
{
TVINSERTSTRUCT tvis;
CString strText = _T("");
// get information of the source item
tvis.item.hItem = hItem;
tvis.item.mask = TVIF_CHILDREN | TVIF_HANDLE | TVIF_PARAM |
TVIF_IMAGE | TVIF_SELECTEDIMAGE;
GetItem(&tvis.item);
strText = GetItemText(hItem);
tvis.item.cchTextMax = strText.GetLength();
tvis.item.pszText = strText.LockBuffer();
// insert the item at proper location
tvis.hParent = hNewParent;
tvis.hInsertAfter = hAfter;
tvis.item.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_TEXT | TVIF_PARAM;
hNewItem = InsertItem(&tvis, pXTCD);
strText.UnlockBuffer();
if (pXTCD->bSeparator) //+++1.6
{
SetItemState(hNewItem, INDEXTOSTATEIMAGEMASK(0), TVIS_STATEIMAGEMASK);
// increment separator count
HTREEITEM hParent = hNewItem;
while ((hParent = GetParentItem(hParent)) != NULL)
{
IncrementSeparators(hParent, 1);
}
}
#if 0 // -----------------------------------------------------------
SetItemState(hNewItem, GetItemState(hItem, TVIS_STATEIMAGEMASK),
TVIS_STATEIMAGEMASK);
#endif // -----------------------------------------------------------
}
return hNewItem;
}
//=============================================================================
// MoveBranch - Moves all items in a branch to a new location
// Returns - The new branch node
// hBranch - The node that starts the branch
// hNewParent - Handle of the parent for new branch
// hAfter - Item after which the new branch should be created
HTREEITEM CXHtmlTree::MoveBranch(HTREEITEM hBranch,
HTREEITEM hNewParent,
HTREEITEM hAfter /*= TVI_LAST*/)
//=============================================================================
{
HTREEITEM hChild;
HTREEITEM hNewItem = MoveItem(hBranch, hNewParent, hAfter);
HTREEITEM hNext = GetChildItem(hBranch);
hChild = hNext;
while (hChild)
{
// recursively transfer all the items
MoveBranch(hChild, hNewItem);
hNext = GetNextSiblingItem(hChild);
hChild = hNext;
}
return hNewItem;
}
//=============================================================================
BOOL CXHtmlTree::StartMoveBranch(HTREEITEM hItem,
HTREEITEM hNewParent,
HTREEITEM hAfter /*= TVI_LAST*/)
//=============================================================================
{
BOOL bCopyDrag = IsDragCopy();
TRACE(_T("in CXHtmlTree::StartMoveBranch: %s %X: new parent=%X after=%X\n"),
bCopyDrag ? _T("copying") : _T("moving"), hItem, hNewParent, hAfter);
BOOL rc = FALSE;
ASSERT(hItem);
if (hItem)
{
// allow parent to decide whether to permit drag
XHTMLTREEDRAGMSGDATA dragdata = { 0 };
dragdata.hItem = hItem;
dragdata.hNewParent = hNewParent;
dragdata.hAfter = hAfter;
dragdata.bCopyDrag = bCopyDrag;
LRESULT lResult = SendRegisteredMessage(WM_XHTMLTREE_END_DRAG, hItem,
(LPARAM)&dragdata);
rc = lResult == 0; // allow drop if lResult is 0
if (rc)
{
MoveBranch(hItem, hNewParent, hAfter);
if (!bCopyDrag)
{
// moving, so delete original item
m_nDeleted = 0;
m_nDeletedChecked = 0;
DeleteBranch(hItem); // this was a move, not a copy
TRACE(_T("m_nDeleted=%d m_nDeletedChecked=%d\n"), m_nDeleted, m_nDeletedChecked);
}
}
}
return rc;
}
//=============================================================================
void CXHtmlTree::AutoScroll(HTREEITEM hItem)
//=============================================================================
{
DWORD dwSpeedFlags = XHTMLTREE_DO_SCROLL_NORMAL | XHTMLTREE_DO_SCROLL_FAST;
#ifdef _DEBUG
if ((m_dwDragOps & dwSpeedFlags) == dwSpeedFlags)
{
TRACE(_T("ERROR - only one speed flag should be set\n"));
ASSERT(FALSE);
}
#endif // _DEBUG
if ((m_dwDragOps & dwSpeedFlags) == 0)
{
// no speed flags, don't scroll
return;
}
if (m_dwDragOps & XHTMLTREE_DO_SCROLL_NORMAL)
{
// scroll every other time, about 5 times a second
if ((++m_nScrollTime & 1) == 0)
return;
}
int n = 0;
if (hItem)
{
int nScrollZone = GetItemHeight() + 5;
CPoint point;
GetCursorPos(&point); // screen coords
CRect rect;
GetClientRect(&rect);
ClientToScreen(&rect); // screen coords
int nDistance = 0;
BOOL bUp = TRUE;
if (point.y < (rect.top + nScrollZone))
{
nDistance = point.y - rect.top;
bUp = TRUE; // up
}
else if (point.y > (rect.bottom - nScrollZone))
{
nDistance = rect.bottom - point.y;
bUp = FALSE; // down
}
if (nDistance > 0)
{
SetInsertMark(0, 0); // remove previous insert mark
if (nDistance > ((nScrollZone/3)*2))
{
// in region farthest from border, scroll slow
SendMessage(WM_VSCROLL, bUp ? SB_LINEUP : SB_LINEDOWN);
n = 1;
}
else if (nDistance > (nScrollZone/3))
{
// in middle region, scroll faster
SendMessage(WM_VSCROLL, bUp ? SB_LINEUP : SB_LINEDOWN);
SendMessage(WM_VSCROLL, bUp ? SB_LINEUP : SB_LINEDOWN);
n = 2;
}
else
{
// in region closest to border, scroll very fast
SendMessage(WM_VSCROLL, bUp ? SB_PAGEUP : SB_PAGEDOWN);
n = 3;
}
ScreenToClient(&point);
HTREEITEM hItem = IsOverItem(&point);
if (hItem)
{
if ((m_dwDragOps & XHTMLTREE_DO_SHIFT_KEY) &&
(GetAsyncKeyState(VK_SHIFT) < 0))
{
TRACE(_T("VK_SHIFT down\n"));
if (IsSeparator(hItem)) //+++1.6
{
SelectDropTarget(NULL);
SetInsertMark(hItem, TRUE);
}
else
{
SelectDropTarget(hItem);
}
}
else
{
SetInsertMark(hItem, TRUE);
SelectDropTarget(NULL);
}
}
}
}
#ifdef XHTMLTREE_DEMO
CWnd *pWnd = GetParent();
if (!pWnd)
pWnd = GetOwner();
if (pWnd && ::IsWindow(pWnd->m_hWnd))
pWnd->SendMessage(WM_XHTMLTREE_SCROLL_SPEED, n, 0);
#endif // XHTMLTREE_DEMO
}
#endif // XHTMLDRAGDROP
//=============================================================================
void CXHtmlTree::EndDragScroll()
//=============================================================================
{
#ifdef XHTMLDRAGDROP
TRACE(_T("in CXHtmlTree::EndDragScroll\n"));
KillTimer(LBUTTONDOWN_TIMER);
KillTimer(SHIFT_UP_TIMER);
SetInsertMark(0, 0);
m_nScrollTime = 0;
if (m_bAutoScroll)
AutoScroll(NULL);
SelectDropTarget(NULL);
ReleaseCapture();
m_bDragging = FALSE;
SetCursor(m_hPreviousCursor);
#endif // XHTMLDRAGDROP
}