filteredtodoctrl.cpp

来自「管理项目进度工具的原代码」· C++ 代码 · 共 723 行 · 第 1/2 页

CPP
723
字号
// FilteredToDoCtrl.cpp: implementation of the CFilteredToDoCtrl class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "FilteredToDoCtrl.h"
#include "resource.h"

#include "..\shared\holdredraw.h"
#include "..\shared\datehelper.h"
#include "..\shared\enstring.h"

#include <math.h>

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

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CFilteredToDoCtrl::CFilteredToDoCtrl(CContentMgr& mgr, const CONTENTFORMAT& cfDefault) :
	CToDoCtrl(mgr, cfDefault), m_nHiddenCount(0)
{
}

CFilteredToDoCtrl::~CFilteredToDoCtrl()
{

}

int CFilteredToDoCtrl::GetAllTasks(CTaskFile& tasks) const
{
	if (!m_nHiddenCount) // nothing to merge in
		return CToDoCtrl::GetAllTasks(tasks);

	// else	merge back in any tasks that were previously removed
	// to make sure that changes to the visible tasks (esp structure)
	// are not overwritten by the hidden tasks we need to merge
	// from the visible tasks -> hidden tasks.
	CTaskFile tasksSrc;

	if (CToDoCtrl::GetAllTasks(tasksSrc))
	{
		tasks.Copy(m_tasksHidden);
		tasks.Merge(tasksSrc, TRUE, TRUE);
	}
	else // nothing showing so just copy hidden
		tasks.Copy(m_tasksHidden);

	return tasks.GetTaskCount();
}

HTREEITEM CFilteredToDoCtrl::SetAllTasks(const CTaskFile& tasks)
{
	HTREEITEM hti = NULL;

	if (HasFilter())
	{
		int nTaskCount = tasks.GetTaskCount();

		// this will contain the visible
		CTaskFile tasksShow;
		tasksShow.Copy(tasks);
		
		// this will contain the non-visible tasks. ie mutually exclusive
		m_tasksHidden.Copy(tasks);

		FilterTasks(m_tasksHidden, NULL, tasksShow, NULL);

		// work out how many are hidden
		m_nHiddenCount = nTaskCount - tasksShow.GetTaskCount();

		TRACE ("CFilteredToDoCtrl::SetAllTasks(%d showing, %d hidden)\n", 
				tasksShow.GetTaskCount(), m_tasksHidden.GetTaskCount());

		m_mgrPrompts.SetPrompt(m_tree, CEnString(IDS_TDC_FILTEREDTASKLISTPROMPT), TVM_GETCOUNT);

		hti = CToDoCtrl::SetAllTasks(tasksShow);
	}
	else
	{
		m_mgrPrompts.SetPrompt(m_tree, CEnString(IDS_TDC_TASKLISTPROMPT), TVM_GETCOUNT);

		m_tasksHidden.Reset();
		m_nHiddenCount = 0;
	
		hti = CToDoCtrl::SetAllTasks(tasks);
	}

	m_tree.Invalidate();
	return hti;
}

FILTER_TYPE CFilteredToDoCtrl::GetFilter(FTDCFILTER& filter) const
{
	filter = m_filter;
	return m_filter.nFilter;
}

void CFilteredToDoCtrl::SetFilter(const FTDCFILTER& filter)
{
	if (m_filter == filter)
		return; // no change

	m_filter = filter;
	RefreshFilter();
}
	
void CFilteredToDoCtrl::ClearFilter()
{
	if (HasFilter())
	{
		FTDCFILTER filter; // empty filter
		SetFilter(filter);
	}
}

UINT CFilteredToDoCtrl::GetTaskCount(UINT* pVisible) const
{
	int nVisible = CToDoCtrl::GetTaskCount();
	
	if (pVisible)
		*pVisible = nVisible;

	return (nVisible + m_nHiddenCount);
}

void CFilteredToDoCtrl::RefreshFilter() 
{
	CHoldRedraw hr(m_tree);
	
	CWaitCursor cursor;
	CTaskFile tasks;

	if (GetAllTasks(tasks))
	{
		// save and restore focus
		HWND hwndFocus = ::GetFocus();

		if (CToDoCtrl::GetTaskCount())
			SaveExpandedState();

		InitDueDate();
		
		HTREEITEM htiFirst = SetAllTasks(tasks);
		m_data.ResetCachedCalculations();

		// restore last expanded state
		HTREEITEM htiSel = LoadExpandedState(FALSE);
		
		if (!htiSel)
			htiSel = htiFirst;
		
		if (htiSel)
		{
			SelectItem(htiSel);
			m_tree.EnsureVisible(htiSel);
		}

		if (hwndFocus != ::GetFocus())
			::SetFocus(hwndFocus);

		// redo last sort
		if (GetSortBy() != TDC_SORTDISABLED)
		{
			m_bModSinceLastSort = TRUE; // so the sort won't switch directions
			Sort(GetSortBy());
		}
	}
}

void CFilteredToDoCtrl::FilterTasks(CTaskFile& tasksHide, HTASKITEM htHide, 
									CTaskFile& tasksShow, HTASKITEM htShow) 
{
	// iterate both lists at the same time
	HTASKITEM htchildHide = tasksHide.GetFirstTask(htHide);
	HTASKITEM htchildShow = tasksShow.GetFirstTask(htShow);

	while (htchildHide)
	{
		ASSERT (htchildShow);
		DWORD dwID = tasksShow.GetTaskID(htchildShow);

		// Note: because we will be hiding tasks from the underlying CToDoCtrl
		// we become responsible for validating m_dwNextUniqueID against the
		// actual IDs of the tasks
		m_dwNextUniqueID = max(m_dwNextUniqueID, dwID + 1); 		
		
		// save in case we later need to delete
		HTASKITEM htCacheHide = htchildHide;
		HTASKITEM htCacheShow = htchildShow;
		
		// if it has children then leave it in place but process children
		BOOL bHasChildren = (tasksShow.GetFirstTask(htchildShow) != NULL);
		
		// get next first in case we delete 
		htchildHide = tasksHide.GetNextTask(htchildHide);
		htchildShow = tasksShow.GetNextTask(htchildShow);
		
		if (bHasChildren)
		{
			// we need to handle done tasks in a special way if we have
			// the TDCS_TREATSUBCOMPLETEDASDONE style set because trying
			// to figure out whether to delete the parent after all the 
			// children have been deleted is too tricky. so if we're only
			// interested in incomplete tasks and we are sure that this
			// task is complete then we can deal with it right away
			if (m_filter.nFilter == FT_NOTDONE && HasStyle(TDCS_TREATSUBCOMPLETEDASDONE) &&
				IsTaskDone(tasksShow, htCacheShow))
			{
				tasksShow.DeleteTask(htCacheShow);
			}
			else // we go through all the tests
			{
				FilterTasks(tasksHide, htCacheHide, tasksShow, htCacheShow);
				
				// if the task previouly had children but now they've all 
				// be filtered out, decide whether the parent task needs to be 
				// deleted too
				BOOL bDelete = FALSE;
				
				if (bHasChildren && !tasksShow.GetFirstTask(htCacheShow))
				{
					bDelete = (MatchFilter(tasksShow, htCacheShow) != FTDC_MATCH);
				}

				if (bDelete)
					tasksShow.DeleteTask(htCacheShow);
				else
				{
					// task will exist in both lists so clear attributes from hidden
					// task to ensure that attributes are correctly merged back 
					// in from shown item (which may have been edited).
					// BUT make sure we don't delete the ID else the merge
					// will go horribly wrong
					tasksHide.DeleteTaskAttributes(htCacheHide);
					tasksHide.SetTaskID(htCacheHide, dwID); 
				}
			}
		}
		else // has no children so check for removal
		{
			// if the current item matches the filter then we delete
			// it from the 'hide' tasks because we will be showing
			// it, else we delete from the 'show' tasks because we
			// will be hiding it
			FTMATCHRESULT nMatch = MatchFilter(tasksHide, htCacheHide);
			
			if (nMatch == FTDC_MATCH)
				tasksHide.DeleteTask(htCacheHide);
			else
				tasksShow.DeleteTask(htCacheShow);
		}
	}
}

FTMATCHRESULT CFilteredToDoCtrl::MatchFilter(const CTaskFile& tasks, HTASKITEM ht) const
{
	FTMATCHRESULT nMatch = FTDC_NOMATCHSTATE;

	switch (m_filter.nFilter)
	{
	case FT_ALL:
		nMatch = FTDC_MATCH;
		break;

	case FT_DONE:
		if (IsTaskDone(tasks, ht))
			nMatch = FTDC_MATCH;
		break;

	case FT_NOTDONE:
		if (!IsTaskDone(tasks, ht))
			nMatch = FTDC_MATCH;
		break;

	default: // all the rest
		if (MatchDueDate(tasks, ht))
			nMatch = FTDC_MATCH;
		break;
	}

	// check other values if its a positive match
	if (nMatch == FTDC_MATCH)
	{
		if (!MatchCategory(tasks, ht))
			nMatch = FTDC_NOMATCHCATEGORY;

		else if (!MatchStatus(tasks, ht))
			nMatch = FTDC_NOMATCHSTATUS;

		else if (!MatchAllocTo(tasks, ht))
			nMatch = FTDC_NOMATCHALLOCTO;

		else if (!MatchAllocBy(tasks, ht))
			nMatch = FTDC_NOMATCHALLOCBY;

		else if (!MatchPriority(tasks, ht))
			nMatch = FTDC_NOMATCHPRIORITY;

		else if (!MatchRisk(tasks, ht))
			nMatch = FTDC_NOMATCHRISK;
	}

	return nMatch;
}

BOOL CFilteredToDoCtrl::MatchCategory(const CTaskFile& tasks, HTASKITEM ht) const
{
	if (m_filter.aCategories.GetSize())
	{
		CStringArray aCats;
		tasks.GetTaskCategories(ht, aCats);

		return m_filter.MatchCategories(aCats);
	}

	// else implicit match
	return TRUE;
}

BOOL CFilteredToDoCtrl::MatchStatus(const CTaskFile& tasks, HTASKITEM ht) const
{
	if (!m_filter.sStatus.IsEmpty())
		return (m_filter.sStatus.CompareNoCase(tasks.GetTaskStatus(ht)) == 0);

	// else match
	return TRUE;
}

BOOL CFilteredToDoCtrl::MatchAllocTo(const CTaskFile& tasks, HTASKITEM ht) const
{
	if (m_filter.aAllocTo.GetSize())
	{
		CStringArray aAllocTo;
		tasks.GetTaskAllocatedTo(ht, aAllocTo);

		return m_filter.MatchAllocTo(aAllocTo);
	}

	// else match
	return TRUE;
}

BOOL CFilteredToDoCtrl::MatchAllocBy(const CTaskFile& tasks, HTASKITEM ht) const
{
	if (!m_filter.sAllocBy.IsEmpty())
		return (m_filter.sAllocBy.CompareNoCase(tasks.GetTaskAllocatedBy(ht)) == 0);

	// else match
	return TRUE;
}

BOOL CFilteredToDoCtrl::MatchPriority(const CTaskFile& tasks, HTASKITEM ht) const
{
	if (m_filter.nPriority != FT_ANYPRIORITY)
	{
		/*

⌨️ 快捷键说明

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