📄 statictreectrl.cpp
字号:
// 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 + -