todoctrl.cpp

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

CPP
2,301
字号
				rTree.bottom = (rCtrls.top - dlu.ToPixelsY(CTRLVSPACING));
			}
			
			dwm.MoveWindow(GetDlgItem(IDC_TASKLIST), rTree);

			// project name
			dwm.ResizeCtrl(this, IDC_PROJECTNAME, rTree.right - rProject.right, 0);
			
			// now iterate the visible controls settings their positions dynamically
			CRect rItem(rCtrls);
			rItem.bottom = rItem.top + dlu.ToPixelsY(CTRLHEIGHT);
		
			int nCol = 0;
			int nRightEdge = bVertComments ? rCtrls.right : rCtrls.right - dlu.ToPixelsY(CTRLHSPACING);
			
			for (int nCtrl = 0; nCtrl < NUM_CTRLITEMS; nCtrl++)
			{
				if (!bAllVisible && !IsColumnShowing(CTRLITEMS[nCtrl].nCol))
					continue;
				
				if (nCol >= nCols)
				{
					nCol = 0;
					rItem.OffsetRect(0, dlu.ToPixelsY(CTRLHEIGHT + CTRLVSPACING));
				}
				
				ReposControl(nCtrl, &dwm, &dlu, nCol, rItem.top, nRightEdge);
				
				nCol++;
			}
		}
		
		UpdateWindow();
	}
}

int CToDoCtrl::GetMinNonCommentSize()
{
	if (HasStyle(TDCS_VERTCOMMENTS))
	{
		// two field widths
		CDlgUnits dlu(*this);
		return dlu.ToPixelsX(2 * (CTRLENDOFFSET + CTRLHSPACING));
	}
	else
		return MINNONCOMMENTHEIGHT;
}

void CToDoCtrl::ReposComments(int cx, int cy, CDeferWndMove* pDWM, CRect& rComments /*out*/) 
{
	// validate comments size but don't change it
	int nCommentSize = HasStyle(TDCS_SHAREDCOMMENTSHEIGHT) ? s_nCommentsSize : m_nCommentsSize;
	
	if (HasStyle(TDCS_VERTCOMMENTS))
	{
		nCommentSize = min(nCommentSize, cx - GetMinNonCommentSize());
		rComments.top = 2;
		rComments.left = cx - nCommentSize;
	}
	else 
	{
		nCommentSize = min(nCommentSize, cy - GetMinNonCommentSize());
		rComments.left = 0;
		rComments.top = cy - nCommentSize;
	}
	rComments.right = cx; // always
	rComments.bottom = cy; // always

	CRect rComField(rComments);

	// horizontal splitter
	if (!HasStyle(TDCS_VERTCOMMENTS))
	{
		CRect rDiv(rComments);
		rDiv.bottom = rDiv.top + 2;
		pDWM->MoveWindow(GetDlgItem(IDC_HSPLITTER), rDiv);

		rComField.top = rDiv.bottom+ 4;
	}

	// label
	CRect rItem = pDWM->MoveCtrl(this, IDC_COMMENTSLABEL, rComField.left, rComField.top);

	// type combo to right of label
	if (m_mgrContent.GetNumContent() > 1)
		rItem = pDWM->MoveCtrl(this, IDC_COMMENTSTYPE, rItem.right + 4, rItem.top);

	// comments below combo
	rComField.top = rItem.bottom + 4;
	pDWM->MoveWindow(GetDlgItem(IDC_COMMENTS), rComField);
}

void CToDoCtrl::ReposControl(int nCtrl, CDeferWndMove* pDWM, const CDlgUnits* pDLU, int nCol, 
							 int nTop, int nClientRight)
{
	// the bottom of the ctrl is defined by its existing height
	CRect rItem(0, nTop, 0, nTop + ResizeCtrl(CTRLITEMS[nCtrl].nCtrlID).Height());
	
	rItem.left = pDLU->ToPixelsX(nCol * (CTRLENDOFFSET + CTRLHSPACING));
	rItem.right = rItem.left + pDLU->ToPixelsX(CTRLENDOFFSET);
				
	// move label
	CRect rLabel(rItem);
	rLabel.right = rLabel.left + pDLU->ToPixelsX(CTRLSTARTOFFSET - CTRLHSPACING);
				
	pDWM->MoveWindow(GetDlgItem(CTRLITEMS[nCtrl].nLabelID), rLabel);
				
	// move control
	CRect rCtrl(rItem);
	rCtrl.left += pDLU->ToPixelsX(CTRLSTARTOFFSET);
				
	// some special cases
	switch (CTRLITEMS[nCtrl].nCtrlID)
	{
	case IDC_PERCENT:
		{
			CRect rSpin = OffsetCtrl(IDC_PERCENTSPIN); // gets current pos
			rSpin.OffsetRect(rCtrl.right - rSpin.right, 0);
			rSpin.top = rCtrl.top;
			rSpin.bottom = rCtrl.bottom;
			pDWM->MoveWindow(&m_spinPercent, rSpin);
			
			rCtrl.right = rSpin.left + 2;
		}
		break;
		
	case IDC_FILEPATH:
		{
			// file path control can take as much space as is left
			rCtrl.right = nClientRight;
		}
		break;
		
	case IDC_ALLOCTO:
	case IDC_ALLOCBY:
	case IDC_STATUS:
	case IDC_CATEGORY:
	case IDC_PRIORITY:
	case IDC_VERSION:
		rCtrl.bottom += 200; // combo box drop height
		break;
	}
				
	pDWM->MoveWindow(GetDlgItem(CTRLITEMS[nCtrl].nCtrlID), rCtrl);
}

int CToDoCtrl::VisibleCtrlCount()
{
	if (!HasStyle(TDCS_SHOWCTRLSASCOLUMNS))
		return NUM_CTRLITEMS;
	
	// else
	int nVisibleCtrls = 0;
	
	for (int nCtrl = 0; nCtrl < NUM_CTRLITEMS; nCtrl++)
	{
		if (IsColumnShowing(CTRLITEMS[nCtrl].nCol))
			nVisibleCtrls++;
	}
	
	return nVisibleCtrls;
}

int CToDoCtrl::GetMinWidth()
{
	int nMinWidth = CDlgUnits(*this).ToPixelsX(2 * (CTRLENDOFFSET + CTRLHSPACING) + 1);	
	
	return nMinWidth;
}

// wrapper for CTreeCtrl
BOOL CToDoCtrl::SetCheckImageList(HIMAGELIST hImageList)
{
	m_hilDone = hImageList;
	
	if (HasStyle(TDCS_TREECHECKBOXES))
		TreeView_SetImageList(m_tree, m_hilDone, TVSIL_STATE);
	
	return TRUE;
}

void CToDoCtrl::OnTreeSelChanged(NMHDR* pNMHDR, LRESULT* pResult)
{
	LPNMTREEVIEW pNMTV = (LPNMTREEVIEW)pNMHDR;
	HTREEITEM hti = pNMTV->itemNew.hItem;
	
	*pResult = 0;
	
	// we don't support nothing having being selected unless there
	// are no items in the tree
	BOOL bCtrl = Misc::KeyIsPressed(VK_CONTROL) && (s_nExtendedSelection & HOTKEYF_CONTROL);
	BOOL bShift = Misc::KeyIsPressed(VK_SHIFT) && (s_nExtendedSelection & HOTKEYF_SHIFT);
	
	// cursor handled here
	// <shift>+cursor handled here
	// <ctrl>+cursor handled in PreTranslateMessage
	// <ctrl>+<shift>+cursor handled in PreTranslateMessage
	if (m_wKeyPress)
	{
		switch (m_wKeyPress)
		{
		case VK_NEXT:  
		case VK_DOWN:
		case VK_UP:
		case VK_PRIOR: 
		case VK_RIGHT:
		case VK_LEFT:
		case VK_HOME:
		case VK_END:
			if (!bCtrl)
			{
				Selection().RemoveAll();
				
				if (bShift)
					Selection().AddItems(Selection().GetAnchor(), hti);
				else
				{
					Selection().SetAnchor(hti);
					Selection().AddItem(hti);
				}
			}
			break;

		default:
			// else handle alphanum method of changing selection
			Selection().RemoveAll();
			Selection().SetAnchor(hti);
			Selection().AddItem(hti);
			break;
		}
		
		m_wKeyPress = 0;
	}
	// handle user clicking in the tree but not on an item
	// just set the focus back to the tree and do not update anything
	else if (hti == NULL)
	{
		hti = GetSelectedItem();

		if (hti)
			m_tree.SetItemState(hti, TVIS_SELECTED, TVIS_SELECTED);

		return;
	}
	
	UpdateControls(hti); // load newly selected item
	UpdateSelectedTaskPath();
}

void CToDoCtrl::UpdateSelectedTaskPath()
{
	// add the item path to the header
	if (HasStyle(TDCS_SHOWPATHINHEADER))
	{
		CEnString sHeader(IDS_TDC_TASK);
		
		if (GetSelectedCount() == 1)
		{
			CString sPath = GetSelectedTaskPath();
			
			if (!sPath.IsEmpty())
				sHeader.Format("%s [%s]", CEnString(IDS_TDC_TASK), sPath);
		}
		
		m_tree.SetGutterColumnHeaderTitle(TDCC_CLIENT, sHeader);
	}
}

CString CToDoCtrl::GetSelectedTaskPath()
{
	return m_tree.TCH().GetItemPath(GetSelectedItem(), 20);
}

void CToDoCtrl::UpdateControls(HTREEITEM hti)
{
	if (!hti)
		hti = GetSelectedItem();

	int nSelCount = GetSelectedCount();
	
	BOOL bMaximize = HasStyle(TDCS_MAXIMIZE);
	BOOL bEnable = (hti && !bMaximize);
	BOOL bIsParent = Selection().ItemsAreAllParents();
	BOOL bAveSubTaskCompletion = HasStyle(TDCS_AVERAGEPERCENTSUBCOMPLETION) && bIsParent;
	
	BOOL bShowCtrlsAsCols = HasStyle(TDCS_SHOWCTRLSASCOLUMNS);
	BOOL bReadOnly = IsReadOnly();
	
	BOOL bEditTime = !bIsParent || HasStyle(TDCS_ALLOWPARENTTIMETRACKING);
//	BOOL bEditCost = !bIsParent;
	BOOL bEditDue = (nSelCount >= 1);
	BOOL bIsClocking = m_data.IsTaskTimeTrackable(hti) && (GetSelectedTaskID() == m_dwTimeTrackTaskID);
	BOOL bEditPercent = !HasStyle(TDCS_AUTOCALCPERCENTDONE) && (nSelCount > 1 || !bAveSubTaskCompletion);
	BOOL bEditColor = !HasStyle(TDCS_COLORTEXTBYCATEGORY) && !HasStyle(TDCS_COLORTEXTBYPRIORITY);

	BOOL bEditComments = FALSE;
	CString sCommentsType(m_cfDefault);
	
	if (hti)
	{
		m_sTextComments = GetSelectedTaskComments();
		m_nPriority = (int)GetSelectedTaskPriority();
		m_nRisk = (int)GetSelectedTaskRisk();
		m_sAllocBy = GetSelectedTaskAllocBy();
		m_sStatus = GetSelectedTaskStatus();
		m_sFileRefPath = GetSelectedTaskFileRef();
		m_sExternalID = GetSelectedTaskExtID();
		m_sDepends = GetSelectedTaskDependency();
		m_sVersion = GetSelectedTaskVersion();
		m_crColour = GetSelectedTaskColor();

		// can only edit comments if the comments type for the task is available
		m_CustomComments = GetSelectedTaskCustomComments(sCommentsType);
		bEditComments = (m_mgrContent.FindContent(sCommentsType) != -1);
		
		// special cases
		GetSelectedTaskCategories(m_aCategory);
		GetSelectedTaskAllocTo(m_aAllocTo);

		if (bEditTime)
		{
			m_dTimeEstimate = GetSelectedTaskTimeEstimate(m_nTimeEstUnits);
			m_dTimeSpent = GetSelectedTaskTimeSpent(m_nTimeSpentUnits);
		}
		else
		{
			m_nTimeEstUnits = m_nTimeSpentUnits = s_tdDefault.nTimeEstUnits;
			m_dTimeEstimate = m_data.CalcTimeEstimate(hti, m_nTimeEstUnits);
			m_dTimeSpent = m_data.CalcTimeSpent(hti, m_nTimeEstUnits);
		}

//		if (bEditCost)
			m_dCost = GetSelectedTaskCost();
//		else
//			m_dCost = m_data.CalcCost(hti);
		
		// chess clock for time spent
		DWORD dwSelTaskID = GetSelectedTaskID();
		
		m_eTimeSpent.CheckButton(ID_TIMEEDITBTN, (dwSelTaskID == m_dwTimeTrackTaskID));
		m_eTimeSpent.EnableButton(ID_TIMEEDITBTN, bEditTime && bEnable && !bReadOnly && 
									nSelCount == 1 && m_data.IsTaskTimeTrackable(hti));

		// dependency link button
		m_eDependency.EnableButton(ID_DEPENDS_LINK, bEnable && !bReadOnly && !m_sDepends.IsEmpty());

		// percent done
		if (IsSelectedTaskDone())
			m_nPercentDone = 100;
		
		else if (bEditPercent)
			m_nPercentDone = GetSelectedTaskPercent();
		else
			m_nPercentDone = m_data.CalcPercentDone(hti);		
		
		// date controls need special handling too
		COleDateTime date;
		
		if (bEditDue)
			date = GetSelectedTaskDate(TDCD_DUE);
		else
			date = m_data.GetEarliestDueDate(hti, HasStyle(TDCS_USEEARLIESTDUEDATE));
				
		SetCtrlDate(m_dateDue, date);
		SetCtrlDate(m_dateDone, GetSelectedTaskDate(TDCD_DONE));
		SetCtrlDate(m_dateStart, GetSelectedTaskDate(TDCD_START));

		// recurrence
		GetSelectedTaskRecurrence(m_tRecurrence);
		m_eRecurrence.SetDefaultDate(date);
	}
	else // clear controls
	{
		m_sTextComments.Empty();
		m_CustomComments.Empty();
		m_nPriority = 0;
		m_nRisk = 0;
		m_sAllocBy.Empty();
		m_sStatus.Empty();
		m_sFileRefPath.Empty();
		m_sExternalID.Empty();
		m_sDepends.Empty();
		m_nPercentDone = 0;
		m_dTimeEstimate = m_dTimeSpent = 0;
		m_dCost = 0;
		m_tRecurrence = TDIRECURRENCE();
		m_sVersion.Empty();
		m_crColour = GetSysColor(COLOR_WINDOWTEXT);

		m_aCategory.RemoveAll();
		m_aAllocTo.RemoveAll();

		COleDateTime date;
		SetCtrlDate(m_dateDue, date);
		SetCtrlDate(m_dateDone, date);
		SetCtrlDate(m_dateStart, date);

		m_eTimeSpent.EnableButton(ID_TIMEEDITBTN, FALSE);
		m_eDependency.EnableButton(ID_DEPENDS_LINK, FALSE);
	}

	// recreate comments control if necessary
	// note: if more than one comments type is selected then sCommentsType
	// will be empty which will put the comments type combo in an
	// indeterminate state which is the desired effect since this requires
	// the user to reset the type before they can edit
	m_cfComments = sCommentsType;
	m_cbCommentsType.SetSelectedFormat(m_cfComments);

	if (!sCommentsType.IsEmpty())
		CreateContentControl();

	// update control content
	UpdateData(FALSE);
	UpdateComments(FALSE);
	
	// now enable/disable appropriate controls
	for (int nCtrl = 0; nCtrl < NUM_CTRLITEMS; nCtrl++)
	{
		CWnd* pCtrl = GetDlgItem(CTRLITEMS[nCtrl].nCtrlID);
		CWnd* pLabel = GetDlgItem(CTRLITEMS[nCtrl].nLabelID);
		
		if (!pCtrl || !pLabel)
			continue;
		
		BOOL bCtrlShowing = !bMaximize && (!bShowCtrlsAsCols || IsColumnShowing(CTRLITEMS[nCtrl].nCol));
		int nShowCtrl = bCtrlShowing ? SW_SHOW : SW_HIDE;
		
		// control state
		RT_CTRLSTATE nState = (!bEnable || !bCtrlShowing) ? RTCS_DISABLED : 
								(bReadOnly ? RTCS_READONLY : RTCS_ENABLED);
								
		// some additions and modifications
		switch (CTRLITEMS[nCtrl].nCtrlID)
		{
		case IDC_PERCENT:
			if (!bEditPercent && bEnable)
				nState = RTCS_READONLY;
			
			m_spinPercent.ShowWindow(nShowCtrl);
			SetControlState(m_spinPercent, nState);
			break;
			
		case IDC_TIMEEST:
			if (!bEditTime && bEnable)
				nState = RTCS_READONLY;
			break;
			
		case IDC_TIMESPENT:
			if ((!bEditTime || bIsClocking) && bEnable)
				nState = RTCS_READONLY;
			break;
			
// 		case IDC_COST:
// 			if (!bEditCost && bEnable)
// 				nState = RTCS_READONLY;
//			break;
			
 		case IDC_COLOUR:
 			if (!bEditColor)
				nState = RTCS_DISABLED;

⌨️ 快捷键说明

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