⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 statictreectrl.cpp

📁 MiniCA V2.0版本源码。《小型CA系统V2.1含源码》发表以来
💻 CPP
📖 第 1 页 / 共 2 页
字号:
// StaticTreeCtrl.cpp : implementation file
//

#include "stdafx.h"
#include "..\..\minica.h"
#include "StaticTreeCtrl.h"
//#include "ContextMenu.h"
//#include "DLG_TreeNodeText.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CStaticTreeCtrl

CStaticTreeCtrl::CStaticTreeCtrl()
{
	m_pTopNode				= new CTreeNode();	// The tree top

	m_iIndent				= 16;				// Indentation for tree branches
	m_iPadding				= 4;				// Padding between tree and the control border

	m_bShowLines			= TRUE;				// Show lines by default
	m_bScrollBarMessage		= FALSE;			// Only relevant when calculating the scrollbar

	m_iDocHeight			= 0;				// A polite yet meaningless default

	m_crDefaultTextColor	= RGB(58,58,58);	// Some default
	m_crConnectingLines		= RGB(128,128,128);	// Some default

	m_bAudioOn				= TRUE;				// The context menu audio

	// Safeguards
	m_pSelected				= NULL;	
}

CStaticTreeCtrl::~CStaticTreeCtrl()
{
	DeleteNode( m_pTopNode );	// Delete all childs if there are any
	delete m_pTopNode;			// Delete top node
	m_pTopNode = NULL;
	
	m_Font.DeleteObject();

	if( m_bmpBackground.GetSafeHandle() != NULL )
		m_bmpBackground.DeleteObject();
}


BEGIN_MESSAGE_MAP(CStaticTreeCtrl, CStatic)
	//{{AFX_MSG_MAP(CStaticTreeCtrl)
	ON_WM_PAINT()
	ON_WM_SIZE()
	ON_WM_VSCROLL()
	ON_WM_LBUTTONUP()
	ON_WM_MOUSEWHEEL()
/*	ON_WM_CONTEXTMENU()
	ON_COMMAND(CM_INSERTCHILD, OnCM_InsertChild)
	ON_COMMAND(CM_INSERTSIBLING, OnCM_InsertSibling)
	ON_COMMAND(CM_DELETENODE, OnCM_DeleteNode)
	ON_COMMAND(CM_MODIFYNODETEXT, OnCM_ModifyNodeText)
	ON_COMMAND(CM_CHANGENODECOLOR, OnCM_ChangeNodeColor)
	ON_COMMAND(CM_TOGGLECONNECTINGLINES, OnCM_ToggleConnectingLines)
	ON_COMMAND(CM_SETCONNECTINGLINESCOLOR, OnCM_SetConnectingLinesColor)
	ON_COMMAND(CM_SETFONT, OnCM_SetFont)
	ON_COMMAND(CM_SETDEFAULTCOLOR, OnCM_SetDefaultColor)
	ON_COMMAND(CM_SETBACKGROUNDBITMAP, OnCM_SetBackgroundBitmap)
	ON_COMMAND(CM_TOGGLEMENUSOUND, OnCM_ToggleMenuSound)*/
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
//	PUBLIC METHODS
/////////////////////////////////////////////////////////////////////////////

CStaticTreeCtrl& CStaticTreeCtrl::SetTextFont( LONG nHeight, BOOL bBold, BOOL bItalic, const CString& csFaceName )
{
	CDC * pDC = GetDC();
	m_lgFont.lfHeight			= -MulDiv( nHeight, GetDeviceCaps( pDC->m_hDC, LOGPIXELSY ), 72 );
	m_lgFont.lfWidth			= 0;
	m_lgFont.lfEscapement		= 0;
	m_lgFont.lfOrientation		= 0;
	m_lgFont.lfWeight			= ( bBold )? FW_BOLD:FW_DONTCARE;
	m_lgFont.lfItalic			= (BYTE)( ( bItalic )? TRUE:FALSE );
	m_lgFont.lfUnderline		= FALSE;
	m_lgFont.lfStrikeOut		= FALSE;
	m_lgFont.lfCharSet			= DEFAULT_CHARSET;
	m_lgFont.lfOutPrecision		= OUT_DEFAULT_PRECIS;
	m_lgFont.lfClipPrecision	= CLIP_DEFAULT_PRECIS;
	m_lgFont.lfQuality			= DEFAULT_QUALITY;
	m_lgFont.lfPitchAndFamily	= DEFAULT_PITCH | FF_DONTCARE;

	STRCPY( m_lgFont.lfFaceName, csFaceName );

	if( m_Font.GetSafeHandle() != NULL )
		m_Font.DeleteObject();
	
	m_Font.CreateFontIndirect( &m_lgFont );

	// Calculate node height for this font
//	CDC		*pDC		= GetDC();
	int		iSaved		= pDC->SaveDC();
	CFont*	pOldFont	= pDC->SelectObject( &m_Font );

	// Calculate the height of this font with a character likely to be 'big'
	// and don't forget to add a little padding
	m_iLineHeight	= pDC->GetTextExtent( "X" ).cy + 4;

	pDC->SelectObject( pOldFont );
	pDC->RestoreDC( iSaved );
	ReleaseDC( pDC );

	return *this;
}

CStaticTreeCtrl& CStaticTreeCtrl::SetDefaultTextColor( COLORREF crText )
{
	m_crDefaultTextColor = crText;
	
	return *this;
}


HTREENODE CStaticTreeCtrl::InsertSibling(	HTREENODE pInsertAfter, const CString& csLabel,
											COLORREF crText /* = 0 */, BOOL bUseDefaultTextColor /* = TRUE */,
											BOOL bInvalidate /* = FALSE  */)
{
	ASSERT( pInsertAfter != NULL );	// Make sure the node exists
	
	HTREENODE pNewNode = new CTreeNode();

	pNewNode->csLabel	= csLabel;					// New node's label

	if( bUseDefaultTextColor )
		pNewNode->bUseDefaultTextColor = TRUE;		// Use the default text color
	else
		pNewNode->crText = crText;					// New node's text color

	pNewNode->pParent	= pInsertAfter->pParent;	// Nas the same parent

	// Insert the new node between pInsertAfter and its next sibling
	pNewNode->pSibling		= pInsertAfter->pSibling;
	pInsertAfter->pSibling	= pNewNode;

	// Repaint the control if so desired
	if( bInvalidate )
		Invalidate();
	
	return pNewNode;
}

HTREENODE CStaticTreeCtrl::InsertChild( HTREENODE pParent, const CString& csLabel,
										COLORREF crText /* = 0 */, BOOL bUseDefaultTextColor /* = TRUE */,
										BOOL bInvalidate /* = FALSE  */)
{
	ASSERT( pParent != NULL );	// Make sure the node exists
	
	if( pParent == HTOPNODE )	// Check for top node
		pParent = m_pTopNode;
	
	HTREENODE pNewNode = new CTreeNode();

	// Basic node information
	pNewNode->csLabel	= csLabel;	// New node's label

	if( bUseDefaultTextColor )
		pNewNode->bUseDefaultTextColor = TRUE;		// Use the default text color
	else
		pNewNode->crText = crText;					// New node's text color

	pNewNode->pParent	= pParent;	// New node's parent

	// Insert the new node as pParent's first child
	pNewNode->pSibling	= pParent->pChild;
	pParent->pChild		= pNewNode;

	// Repaint the control if so desired
	if( bInvalidate )
		Invalidate();
	
	return pNewNode;
}

void CStaticTreeCtrl::DeleteNode( HTREENODE pNode, BOOL bInvalidate /* = FALSE  */)
{
	ASSERT( pNode != NULL );	// Make sure the node exists

	// Don't delete the top node
	if( pNode == HTOPNODE )
		DeleteNode( m_pTopNode, bInvalidate );

	// Delete childs
	if( pNode->pChild != NULL )
		DeleteNodeRecursive( pNode->pChild );

	// If this node is not the top node, fix pointers in sibling list
	if( pNode != m_pTopNode )
	{
		HTREENODE pRunner = pNode->pParent;
		
		// If first child, set the parent pointer to the next sibling
		// Otherwise, find sibling before and set its sibling pointer to the node's sibling
		if( pRunner->pChild == pNode )
			pRunner->pChild = pNode->pSibling;
		else
		{
			pRunner = pRunner->pChild;

			// Loop until the next node is the one being deleted
			while( pRunner->pSibling != pNode )
				pRunner = pRunner->pSibling;

			pRunner->pSibling = pNode->pSibling;
		}

		delete pNode;

		pNode = NULL;
	}

	// Repaint the control if so desired
	if( bInvalidate )
		Invalidate();
}

void CStaticTreeCtrl::ToggleNode( HTREENODE pNode, BOOL bInvalidate /* = FALSE  */)
{
	ASSERT( pNode != NULL );

	pNode->bOpen = !( pNode->bOpen );

	if( bInvalidate )
		Invalidate();
}

void CStaticTreeCtrl::SetNodeColor( HTREENODE pNode, COLORREF crText, BOOL bInvalidate /* = FALSE  */)
{
	ASSERT( pNode != NULL );

	pNode->bUseDefaultTextColor	= FALSE;
	pNode->crText				= crText;

	if( bInvalidate )
		Invalidate();
}

void CStaticTreeCtrl::SetBackgroundBitmap( BOOL bInvalidate /* = FALSE  */)
{
	CFileDialog fd( TRUE, NULL, NULL, OFN_EXPLORER | OFN_FILEMUSTEXIST, _T("Bitmap Files (*.bmp)|*.bmp||"), this );

	// If the user clicked 'ok'
	if( fd.DoModal() == IDOK )
	{
		// If there is a bitmap already loaded, delete it
	    if( m_bmpBackground.GetSafeHandle() != NULL )
			m_bmpBackground.DeleteObject();
		
		// Load the bitmap from the file selected
		HBITMAP hBitmap =	(HBITMAP)LoadImage( NULL, fd.GetPathName(), IMAGE_BITMAP, 
												0, 0, LR_LOADFROMFILE | LR_CREATEDIBSECTION | LR_DEFAULTSIZE );

		// Attach it to the CBitmap object
		m_bmpBackground.Attach( hBitmap );

		// Repaint if so desired
		if( bInvalidate )
			Invalidate();
	}
}

/////////////////////////////////////////////////////////////////////////////
//	PROTECTED METHODS
/////////////////////////////////////////////////////////////////////////////

void CStaticTreeCtrl::DeleteNodeRecursive( HTREENODE pNode )
{
	if( pNode->pSibling != NULL )
		DeleteNodeRecursive( pNode->pSibling );

	if( pNode->pChild != NULL )
		DeleteNodeRecursive( pNode->pChild );

	delete pNode;

	pNode = NULL;
}

int CStaticTreeCtrl::DrawNodesRecursive( CDC* pDC, HTREENODE pNode, int x, int y, CRect rFrame )
{
	int		iDocHeight = 0;		// Total document height
	CRect	rNode;

	// The node's location and dimensions on screen
	rNode.left		= x;
	rNode.top		= y;
	rNode.right		= rFrame.right - m_iPadding;
	rNode.bottom	= y + m_iLineHeight;

	pNode->rNode.CopyRect( rNode );		// Record the rectangle

	COLORREF cr			= ( pNode->bUseDefaultTextColor )? m_crDefaultTextColor:pNode->crText;
	COLORREF crOldText	= pDC->SetTextColor( cr );

	// MULTILINE TEXT - begins
	CString	cs		= pNode->csLabel;
	int		iPos	= 0;

	// Draw text until there is nothing left to draw
	while( cs.GetLength() > 0 )
	{
		// Height of a line of text
		rNode.bottom = rNode.top + m_iLineHeight;

		// Find out how much text fits in one line
		iPos = HowMuchTextFits( pDC, rFrame.right - m_iPadding - rNode.left, cs );

		// Draw only if the node is visible
		if( rNode.bottom > 0 && rNode.top < rFrame.bottom )
			pDC->DrawText( cs.Left( iPos + 1 ), rNode, DT_LEFT | DT_SINGLELINE | DT_VCENTER );

		// Eliminate the text that has been already drawn
		cs = cs.Mid( iPos + 1 );

		// The node grows everytime another line of text is drawn
		pNode->rNode.UnionRect( pNode->rNode, rNode );

		// Move down the drawing rectangle for the next line of text
		rNode.top = rNode.bottom;
	}
	// MULTILINE TEXT - ends

	pDC->SetTextColor( crOldText );

	// If there are no child or siblings, then this branch is done
	if( pNode->pChild == NULL &&  pNode->pSibling == NULL )
		return pNode->rNode.Height();

	// If the node is open AND it has childs, then draw those
	if( pNode->bOpen && pNode->pChild != NULL )
		iDocHeight = DrawNodesRecursive( pDC, pNode->pChild, x + m_iIndent, y + pNode->rNode.Height(), rFrame );

	// If the node has siblings, then draw those
	if( pNode->pSibling != NULL )
		iDocHeight += DrawNodesRecursive( pDC, pNode->pSibling, x, y + pNode->rNode.Height() + iDocHeight, rFrame );

	return iDocHeight + pNode->rNode.Height();
}

int CStaticTreeCtrl::HowMuchTextFits( CDC* pDC, int iAvailableWidth, CString csText )
{
	int iValidSoFar = csText.GetLength() - 1;					// Assume the entire text fits

	// If the text's pixel width is larger than what's available
	if( pDC->GetTextExtent( csText ).cx > iAvailableWidth )
	{
		int iNextBlank	= 0;	// Position of the next blank in text
		int iPixelWidth	= 0;	// Text's pixel width

		// Loop until we can fit no more of the text
		while( iPixelWidth < iAvailableWidth )
		{
			iValidSoFar	= iNextBlank;							// Record the char pos so far
			iNextBlank	= csText.Find( ' ', iNextBlank + 1 );	// Advance one word at a time

			// Have reached the end of the string?
			if( iNextBlank == -1 )
				iNextBlank = csText.GetLength();

			// Calculate the new width
			iPixelWidth = pDC->GetTextExtent( csText.Left( iNextBlank ) ).cx;
		}
	}

	return iValidSoFar;
}

void CStaticTreeCtrl::DrawLinesRecursive( CDC* pDC, HTREENODE pNode )
{
	// Draw lines from childs if the node is open before drawing lines from this node
	if( pNode->bOpen && pNode->pChild != NULL )
		DrawLinesRecursive( pDC, pNode->pChild );

	// Where is the elbow joint of this connecting line?
	int iJointX	= pNode->rNode.left - m_iIndent - 6;
	int iJointY	= pNode->rNode.top + ( m_iLineHeight / 2 );

	// If the parent is not the top node, throw a connecting line to it
	if( pNode->pParent != m_pTopNode )
	{
		// How far up from the joint is the parent?
		int iDispY = iJointY - pNode->pParent->rNode.top - ( m_iLineHeight / 2 );
		
		// Use 1 pixel wide rectangles to draw lines
		pDC->FillSolidRect( iJointX, iJointY, m_iIndent, 1, m_crConnectingLines );	// Horizontal line
		pDC->FillSolidRect( iJointX, iJointY, 1, -iDispY, m_crConnectingLines );		// Vertical line
	}

	// Put a solid dot to mark a node
	pDC->FillSolidRect( iJointX + m_iIndent - 2, iJointY - 2, 5, 5, m_crConnectingLines );

	// Hollow out the dot if the node has no childs
	if( pNode->pChild == NULL )
		pDC->FillSolidRect( iJointX + m_iIndent - 1, iJointY - 1, 3, 3, RGB(255,255,255) );

	// Draw the next sibling if there are any
	if( pNode->pSibling != NULL )
		DrawLinesRecursive( pDC, pNode->pSibling );
}

void CStaticTreeCtrl::ResetScrollBar()
{
	// Flag to avoid a call from OnSize while resetting the scrollbar
	m_bScrollBarMessage = TRUE;

	CRect rFrame;

	GetClientRect( rFrame );

	// Need for scrollbars?
	if( rFrame.Height() > m_iDocHeight + 8 )
	{
		ShowScrollBar( SB_VERT, FALSE );	// Hide it

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -