📄 waitingtreectrl.cpp
字号:
// WaitingTreeCtrl.cpp : implementation file
//
/////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2000-2001 by Paolo Messina
// (http://www.geocities.com/ppescher - ppescher@yahoo.com)
//
// The contents of this file are subject to the Artistic License (the "License").
// You may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
// http://www.opensource.org/licenses/artistic-license.html
//
// If you find this code useful, credits would be nice!
//
/////////////////////////////////////////////////////////////////////////////
// TrueZq注释: UNREFERENCED_PARAMETER宏的作用是去掉编译警告->参数没有使用。
/////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "WaitingTreeCtrl.h"
#pragma warning(push)
#pragma warning(disable: 4201)
#include <mmsystem.h>
#pragma warning(pop)
#pragma comment(lib, "winmm.lib")
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
//IMPLEMENT_DYNCREATE(CWaitingTreeCtrl, CWnd)
/////////////////////////////////////////////////////////////////////////////
// CWaitingTreeCtrl
CWaitingTreeCtrl::CWaitingTreeCtrl()
{
m_bDrawSnapshot = FALSE;
m_sWaitMsg = _T("请稍候...");
m_bShowWaitMsg = FALSE;
m_hIconMsg = NULL; // default: blank icon
m_nTimerDelay = 0; // default: no timer
m_hRedrawEvent = NULL;
m_hTimerEvent = NULL;
m_hThread = NULL;
}
CWaitingTreeCtrl::~CWaitingTreeCtrl()
{
}
BEGIN_MESSAGE_MAP(CWaitingTreeCtrl, CTreeCtrl)
//{{AFX_MSG_MAP(CWaitingTreeCtrl)
ON_NOTIFY_REFLECT(TVN_ITEMEXPANDING, OnItemExpanding)
ON_NOTIFY_REFLECT(TVN_ITEMEXPANDED, OnItemExpanded)
ON_WM_ERASEBKGND()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CWaitingTreeCtrl message handlers
void CWaitingTreeCtrl::OnItemExpanding(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
if (pNMTreeView->action & TVE_EXPAND)
PreExpandItem(pNMTreeView->itemNew.hItem);
*pResult = 0;
}
void CWaitingTreeCtrl::OnItemExpanded(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
if (pNMTreeView->action & TVE_EXPAND)
{
ExpandItem(pNMTreeView->itemNew.hItem);
}
else if (pNMTreeView->action & TVE_COLLAPSE)
{
if (WantsRefresh(pNMTreeView->itemNew.hItem))
{
// delete child items
DeleteChildren(pNMTreeView->itemNew.hItem);
}
}
*pResult = 0;
}
void CWaitingTreeCtrl::PreAnimation(HTREEITEM hItemMsg)
{
UNREFERENCED_PARAMETER(hItemMsg);
}
void CWaitingTreeCtrl::PostAnimation()
{
}
void CWaitingTreeCtrl::DoAnimation(BOOL bTimerEvent, int iMaxSteps, int iStep)
{
UNREFERENCED_PARAMETER(bTimerEvent);
UNREFERENCED_PARAMETER(iMaxSteps);
UNREFERENCED_PARAMETER(iStep);
}
int CWaitingTreeCtrl::GetPopulationCount(int *piMaxSubItems)
{
if (piMaxSubItems != NULL)
*piMaxSubItems = m_iItemCount;
return m_iItemIndex;
}
void CWaitingTreeCtrl::SetPopulationCount(int iMaxSubItems, int iFirstSubItem)
{
m_iItemCount = iMaxSubItems;
m_iItemIndex = iFirstSubItem;
SetEvent(m_hRedrawEvent);
}
void CWaitingTreeCtrl::UpdatePopulation(int iSubItems)
{
m_iItemIndex = iSubItems;
SetEvent(m_hRedrawEvent);
}
void CWaitingTreeCtrl::IncreasePopulation(int iSubItemsToAdd)
{
m_iItemIndex += iSubItemsToAdd;
SetEvent(m_hRedrawEvent);
}
void CWaitingTreeCtrl::SetAnimationDelay(UINT nMilliseconds)
{
// if greater than zero, periodic DoAnimation() will be called
m_nTimerDelay = nMilliseconds;
}
DWORD WINAPI CWaitingTreeCtrl::AnimationThreadProc(LPVOID pThis)
{
CWaitingTreeCtrl* me = (CWaitingTreeCtrl*)pThis;
HANDLE events[2] = { me->m_hTimerEvent, me->m_hRedrawEvent };
while (!me->m_bAbortAnimation)
{
DWORD wait = WaitForMultipleObjects(2, events, FALSE, INFINITE);
if (me->m_bAbortAnimation || wait == WAIT_FAILED)
break;
if (wait == WAIT_OBJECT_0) // timer event
me->DoAnimation(TRUE, me->m_iItemCount, me->m_iItemIndex);
else // redraw event
me->DoAnimation(FALSE, me->m_iItemCount, me->m_iItemIndex);
}
return 0;
}
void CWaitingTreeCtrl::StartAnimation()
{
// user-defined setup
PreAnimation(m_hItemMsg);
// animation can go
m_bAbortAnimation = FALSE;
// automatic reset events, signaled
m_hTimerEvent = CreateEvent(NULL, FALSE, TRUE, NULL);
m_hRedrawEvent = CreateEvent(NULL, FALSE, TRUE, NULL);
// start animation thread
DWORD dwThreadID = 0;
m_hThread = CreateThread(NULL, 0, AnimationThreadProc, this,
THREAD_PRIORITY_HIGHEST, &dwThreadID);
// setup timer, if specified
if (m_nTimerDelay > 0)
m_nTimerID = (UINT)timeSetEvent(m_nTimerDelay, 5, (LPTIMECALLBACK)m_hTimerEvent,
0, TIME_PERIODIC | TIME_CALLBACK_EVENT_SET);
}
void CWaitingTreeCtrl::StopAnimation()
{
// stop and destroy timer
timeKillEvent(m_nTimerID);
// signal thread to terminate
m_bAbortAnimation = TRUE;
SetEvent(m_hRedrawEvent); // make sure it can see the signal
// wait thread termination
WaitForSingleObject(m_hThread, INFINITE);
// clean up
CloseHandle(m_hTimerEvent);
m_hTimerEvent = NULL;
CloseHandle(m_hRedrawEvent);
m_hRedrawEvent = NULL;
CloseHandle(m_hThread);
m_hThread = NULL;
// user-defined cleanup
PostAnimation();
}
void CWaitingTreeCtrl::PopulateRoot()
{
PreExpandItem(TVI_ROOT);
ExpandItem(TVI_ROOT);
// force update, don't scroll
SetRedraw(FALSE);
SCROLLINFO si;
GetScrollInfo(SB_HORZ, &si);
EnsureVisible(GetChildItem(TVI_ROOT));
SetScrollInfo(SB_HORZ, &si, FALSE);
SetRedraw();
}
void CWaitingTreeCtrl::PreExpandItem(HTREEITEM hItem)
{
if (!NeedsChildren(hItem))
{
if (WantsRefresh(hItem))
{
// delete child items before populating
DeleteChildren(hItem);
}
else
{
// doesn't want new items
m_hItemToPopulate = NULL;
return;
}
}
// if it wants new child items, go on
m_hItemToPopulate = hItem;
// fix redraw when expanded programatically
UpdateWindow();
// hide changes until it's expanded
SetRedraw(FALSE);
// add wait msg, to allow item expansion
// 插入“请稍候...”的一个临时节点
m_hItemMsg = InsertItem(m_sWaitMsg, m_hItemToPopulate);
// zero progress
m_iItemCount = 1;
m_iItemIndex = 0;
}
void CWaitingTreeCtrl::ExpandItem(HTREEITEM hItem)
{
if (m_hItemToPopulate == NULL)
return; // just expand, doesn't want new items
ASSERT(hItem == m_hItemToPopulate); // should never fail!!!
if (m_bShowWaitMsg)
{
// display wait msg now, make sure it's visible
SetRedraw();
EnsureVisible(m_hItemMsg);
UpdateWindow();
}
// setup animation thread, call PreAnimation
StartAnimation();
// draw icon
if (m_bShowWaitMsg)
DrawUserIcon();
// delay redraw after populating
SetRedraw(FALSE);
// take a snapshot of the background
TakeSnapshot();
// del temporary item (wait msg still shown)
DeleteItem(m_hItemMsg);
// fill in with sub items
BOOL bCheckChildren = PopulateItem(hItem);
// clean up animation thread, call PostAnimation
StopAnimation();
// change parent to reflect current children number
if (hItem != TVI_ROOT)
{
TVITEM item;
item.hItem = hItem;
item.mask = TVIF_HANDLE | TVIF_CHILDREN;
item.cChildren = NeedsChildren(hItem) ? 0 : 1;
if (bCheckChildren)
SetItem(&item);
else if (item.cChildren == 0)
// restore item's plus button if no children inserted
SetItemState(hItem, 0, TVIS_EXPANDED);
}
// clean up snapshot
DestroySnapshot();
// redraw now
SetRedraw(TRUE);
// scroll like in a standard expansion
HTREEITEM hChild = GetChildItem(hItem);
while (hChild != NULL && GetFirstVisibleItem() != hItem)
{
EnsureVisible(hChild);
hChild = GetNextSiblingItem(hChild);
}
}
BOOL CWaitingTreeCtrl::WantsRefresh(HTREEITEM hItem)
{
UNREFERENCED_PARAMETER(hItem);
// default implementation, no refresh
return FALSE;
}
BOOL CWaitingTreeCtrl::GetItemImageRect(HTREEITEM hItem, LPRECT pRect)
{
if (GetImageList(TVSIL_NORMAL) == NULL)
return FALSE; // no images
CRect rc;
// get item rect
if (!GetItemRect(hItem, &rc, TRUE))
return FALSE;
int cx = GetSystemMetrics(SM_CXSMICON);
int cy = GetSystemMetrics(SM_CYSMICON);
// move onto the icon space
int margin = (rc.Height()-cy)/2;
rc.OffsetRect(-cx-3 , margin);
rc.right = rc.left + cx; // make it square
rc.bottom = rc.top + cy; // make it square
*pRect = rc;
return TRUE;
}
void CWaitingTreeCtrl::DrawUserIcon()
{
// draw user defined icon
CRect rcIcon;
if (!GetItemImageRect(m_hItemMsg, &rcIcon))
return; // no image
// create background brush with current bg color (take rgb part only)
HBRUSH hBrush = CreateSolidBrush(GetBkColor() & 0x00FFFFFF);
CClientDC dc(this);
if (m_hIconMsg != NULL)
DrawIconEx(dc.GetSafeHdc(), rcIcon.left, rcIcon.top, m_hIconMsg,
rcIcon.Width(), rcIcon.Height(), 0, hBrush, DI_NORMAL);
else
FillRect(dc.GetSafeHdc(), &rcIcon, hBrush);
DeleteObject(hBrush);
}
void CWaitingTreeCtrl::SetWaitMessage(LPCTSTR pszText, HICON hIcon)
{
m_sWaitMsg = pszText;
m_hIconMsg = hIcon;
}
void CWaitingTreeCtrl::RefreshSubItems(HTREEITEM hParent)
{
if (hParent != TVI_ROOT && !ItemHasChildren(hParent))
return;
SetRedraw(FALSE);
DeleteChildren(hParent);
if (hParent == TVI_ROOT)
PopulateRoot();
else
{
PreExpandItem(hParent);
ExpandItem(hParent);
}
SetRedraw(TRUE);
}
// 是否需要子节点
// 如果已经有子节点,则不需要,返回 FALSE
inline BOOL CWaitingTreeCtrl::NeedsChildren(HTREEITEM hParent)
{
return (GetChildItem(hParent) == NULL);
}
void CWaitingTreeCtrl::DeleteChildren(HTREEITEM hParent)
{
HTREEITEM hChild = GetChildItem(hParent);
HTREEITEM hNext;
while (hChild != NULL)
{
hNext = GetNextSiblingItem(hChild);
DeleteItem(hChild);
hChild = hNext;
}
}
BOOL CWaitingTreeCtrl::OnEraseBkgnd(CDC* pDC)
{
if (!m_bDrawSnapshot)
return CTreeCtrl::OnEraseBkgnd(pDC);
DrawSnapshot(pDC);
SetEvent(m_hRedrawEvent);
return TRUE;
}
void CWaitingTreeCtrl::TakeSnapshot()
{
CClientDC dc(this);
CRect rcClient;
GetClientRect(&rcClient);
int width = rcClient.Width(), height = rcClient.Height();
// create the snapshot
CDC dcSnapshot;
dcSnapshot.CreateCompatibleDC(&dc);
m_bmpSnapshot.CreateCompatibleBitmap(&dc, width, height);
// copy the control's background
CBitmap* pOldBmp = dcSnapshot.SelectObject(&m_bmpSnapshot);
dcSnapshot.BitBlt(0, 0, width, height, &dc, 0, 0, SRCCOPY);
dcSnapshot.SelectObject(pOldBmp);
m_bDrawSnapshot = TRUE;
}
void CWaitingTreeCtrl::DrawSnapshot(CDC *pDC)
{
BITMAP bm;
m_bmpSnapshot.GetBitmap(&bm);
// prepare the snapshot
CDC dcSnapshot;
dcSnapshot.CreateCompatibleDC(pDC);
// copy to the control's background
CBitmap* pOldBmp = dcSnapshot.SelectObject(&m_bmpSnapshot);
pDC->BitBlt(0, 0, bm.bmWidth, bm.bmHeight, &dcSnapshot, 0, 0, SRCCOPY);
dcSnapshot.SelectObject(pOldBmp);
}
void CWaitingTreeCtrl::DestroySnapshot()
{
m_bmpSnapshot.DeleteObject();
m_bDrawSnapshot = FALSE;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -