// XTPTrackControl.cpp : implementation of the CXTPTrackControl class. // // This file is a part of the XTREME REPORTCONTROL MFC class library. // (c)1998-2012 Codejock Software, All Rights Reserved. // // THIS SOURCE FILE IS THE PROPERTY OF CODEJOCK SOFTWARE AND IS NOT TO BE // RE-DISTRIBUTED BY ANY MEANS WHATSOEVER WITHOUT THE EXPRESSED WRITTEN // CONSENT OF CODEJOCK SOFTWARE. // // THIS SOURCE CODE CAN ONLY BE USED UNDER THE TERMS AND CONDITIONS OUTLINED // IN THE XTREME TOOLKIT PRO LICENSE AGREEMENT. CODEJOCK SOFTWARE GRANTS TO // YOU (ONE SOFTWARE DEVELOPER) THE LIMITED RIGHT TO USE THIS SOFTWARE ON A // SINGLE COMPUTER. // // CONTACT INFORMATION: // support@codejock.com // http://www.codejock.com // ///////////////////////////////////////////////////////////////////////////// #include "StdAfx.h" #include #include "Common/Resource.h" #include "Common/XTPDrawHelpers.h" #include "Common/XTPPropExchange.h" #include "Common/XTPImageManager.h" #include "Common/XTPCustomHeap.h" #include "Common/XTPSystemHelpers.h" #include "Common/XTPSmartPtrInternalT.h" #include "Common/XTPColorManager.h" #include "../XTPReportDefines.h" #include "../XTPReportControl.h" #include "../XTPReportHeader.h" #include "../XTPReportColumn.h" #include "../XTPReportColumns.h" #include "../XTPReportRow.h" #include "../XTPReportRecord.h" #include "../XTPReportRecordItem.h" #include "../XTPReportRows.h" #include "../XTPReportSections.h" #include "../XTPReportSection.h" #include "../XTPReportPaintManager.h" #include "../XTPReportHitTestInfo.h" #include "XTPTrackBlock.h" #include "XTPTrackControl.h" #include "XTPTrackControlItem.h" #include "XTPTrackHeader.h" #include "XTPTrackPaintManager.h" #include "XTPTrackUndoManager.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif BEGIN_MESSAGE_MAP(CXTPTrackControl,CXTPReportControl) //{{AFX_MSG_MAP(CXTPTrackControl) ON_WM_KEYDOWN() ON_WM_LBUTTONDOWN() ON_WM_GETDLGCODE() //}}AFX_MSG_MAP END_MESSAGE_MAP() CXTPTrackControl::CXTPTrackControl() { RegisterWindowClass(); m_nTimeLinePosition = 0; m_nWorkAreaMin = 0; m_nWorkAreaMax = 100; m_nTimeLineMin = 0; m_nTimeLineMax = 100; m_nViewPortMin = 0; m_nViewPortMax = 100; m_bScaleOnResize = TRUE; m_nSnapMargin = 5; m_bSnapToBlocks = FALSE; m_bSnapToMarkers = TRUE; m_bFlexibleDrag = FALSE; m_nTrackColumnIndex = -1; m_bAllowBlockRemove = TRUE; m_bAllowBlockScale = TRUE; m_bAllowBlockMove = TRUE; m_rcSelectedArea.SetRectEmpty(); m_hMoveCursor = AfxGetApp()->LoadCursor(XTP_IDC_HAND); m_hResizeCursor = AfxGetApp()->LoadStandardCursor(IDC_SIZEWE); m_strTimeFormat = _T("%d"); m_pMarkers = new CXTPTrackMarkers(this); m_nOldTrackWidth = 0; m_bShowWorkArea = TRUE; m_bShowTimeLinePosition = TRUE; SetPaintManager(new CXTPTrackPaintManager()); SetReportHeader(new CXTPTrackHeader(this, m_pColumns)); m_pUndoManager = new CXTPTrackUndoManager(); m_pSelectedBlocks = new CXTPTrackSelectedBlocks(); } CXTPTrackControl::~CXTPTrackControl() { m_pUndoManager->InternalRelease(); m_pMarkers->InternalRelease(); m_pSelectedBlocks->InternalRelease(); } BOOL CXTPTrackControl::RegisterWindowClass(HINSTANCE hInstance) { return XTPDrawHelpers()->RegisterWndClass(hInstance, XTPTRACKCTRL_CLASSNAME, CS_DBLCLKS); } LRESULT CXTPTrackControl::SendMessageToParent(UINT nMessage, CXTPTrackBlock* pBlock, CXTPTrackMarker* pMarker) const { if (!IsWindow(m_hWnd)) return 0; XTP_NM_TRACKCONTROL nmgv; nmgv.pBlock = pBlock; nmgv.pMarker = pMarker; return SendNotifyMessage(nMessage, (NMHDR*)&nmgv); } void CXTPTrackControl::SetViewPort(int nViewPortMin, int nViewPortMax) { if (nViewPortMin < m_nTimeLineMin) { nViewPortMax = m_nTimeLineMin + nViewPortMax - nViewPortMin; nViewPortMin = m_nTimeLineMin; } if (nViewPortMax > m_nTimeLineMax) { nViewPortMin = m_nTimeLineMax - (nViewPortMax - nViewPortMin); nViewPortMax = m_nTimeLineMax; } m_nOldTrackWidth = 0; m_nViewPortMin = nViewPortMin; m_nViewPortMax = nViewPortMax; RedrawControl(); SendMessageToParent(XTP_NM_TRACK_SLIDERCHANGED); } int CXTPTrackControl::GetTrackColumnIndex() const { if (m_nTrackColumnIndex != -1) return m_nTrackColumnIndex; return m_pColumns->GetCount() - 1; } int CXTPTrackControl::GetTrackOffset() const { CXTPReportColumn* pColumn = m_pColumns->Find(GetTrackColumnIndex()); ASSERT(pColumn && pColumn->IsVisible()); return pColumn->GetRect().left; } void CXTPTrackControl::SetTimeLinePosition(int nTimeLinePos) { if (m_nTimeLinePosition != nTimeLinePos) { m_nTimeLinePosition = nTimeLinePos; //Main Marker RedrawControl(); } } void CXTPTrackControl::OnDraw(CDC* pDC) { AdjustTrackColumnWidth(); CXTPReportControl::OnDraw(pDC); } void CXTPTrackControl::AdjustTrackColumnWidth() { CXTPReportColumn* pColumn = GetTrackColumn(); if (pColumn && pColumn->IsVisible()) { int nTrackWidth = max(1, pColumn->GetWidth() - 14); if (m_bScaleOnResize) { m_nOldTrackWidth = 0; } else if (!m_bScaleOnResize && nTrackWidth > 1) { if (m_nOldTrackWidth != 0 ) { double nViewPortMax = m_nViewPortMin + nTrackWidth * m_nOldTrackViewPortRange / m_nOldTrackWidth; if (nViewPortMax > m_nTimeLineMax) nViewPortMax = m_nTimeLineMax; if (fabs(m_nViewPortMax - nViewPortMax) > 1e-6) { m_nViewPortMax = nViewPortMax; SendMessageToParent(XTP_NM_TRACK_SLIDERCHANGED); } } if (m_nOldTrackWidth == 0) { m_nOldTrackWidth = nTrackWidth; m_nOldTrackViewPortRange = m_nViewPortMax - m_nViewPortMin; } } } } int CXTPTrackControl::DrawRows(CDC* pDC, CRect& rcClient, int y, CXTPReportRows *pRows, int nTopRow, int nColumnFrom, int nColumnTo, int *pnHeight) { int nIndex = CXTPReportControl::DrawRows(pDC, rcClient, y, pRows, nTopRow, nColumnFrom, nColumnTo, pnHeight); CXTPReportColumn *pColumn = GetTrackColumn(); if (!pColumn || !pColumn->IsVisible()) { return nIndex; } CRect rcTrack = pColumn->GetRect(); if (m_bShowTimeLinePosition) { int nPos = PositionToTrack(GetTimeLinePosition()); if (nPos >= rcTrack.left && nPos <= rcTrack.right) { pDC->FillSolidRect(CRect(nPos, m_pSectionBody->GetRect().top, nPos + 1, m_pSectionBody->GetRect().bottom + 10), RGB(205, 0, 0)); } } if (!m_rcSelectedArea.IsRectEmpty()) { CRect rcSelectedArea = m_rcSelectedArea; rcSelectedArea.top = max(rcSelectedArea.top, m_pSectionBody->GetRect().top); COLORREF clrSelectedArea = GetTrackPaintManager()->m_clrSelectedArea; LPDWORD lpBits; HBITMAP hBitmap = CXTPImageManager::Create32BPPDIBSection(pDC->GetSafeHdc(), 1, 1, (LPBYTE*)&lpBits); if (hBitmap) { CXTPCompatibleDC dc(pDC, hBitmap); lpBits[0] = RGB(GetBValue(clrSelectedArea), GetGValue(clrSelectedArea), GetRValue(clrSelectedArea)); XTPImageManager()->AlphaBlend2(pDC->GetSafeHdc(), rcSelectedArea, dc, CRect(0, 0, 1, 1), 100); //XTPImageManager()->DoAlphaBlend(pDC->GetSafeHdc(), rcSelectedArea, dc, CRect(0, 0, 1, 1)); DeleteObject(hBitmap); } pDC->Draw3dRect(rcSelectedArea, clrSelectedArea, clrSelectedArea); } return nIndex; } void CXTPTrackControl::AdjustLayout() { /* ------------------------------ | Group by | ------------------------------ | Time line | ------------------------------ | Header | ------------------------------ | Sections | ------------------------------ | Footer | ------------------------------ */ if (NULL == GetSafeHwnd()) return; if (m_bAdjustLayoutRunning) //guard to prevent the recursion similar to OnSize function return; m_bAdjustLayoutRunning = TRUE; CXTPClientRect rcClient(this); CWindowDC dc(this); int nHeaderWidth = m_rcHeaderArea.Width(); CXTPReportHeader *pHeader = GetReportHeader(); CXTPReportColumn *pTrack = GetTrackColumn(); int nHeightGroupBy = 0; int nHeightHeader = 0; int nHeightFooter = 0; int nHeightTimeLine = 0; if (NULL != pHeader && m_bGroupByEnabled) { nHeightGroupBy = pHeader->GetGroupByHeight(); } if (pTrack && pTrack->IsVisible()) { nHeightTimeLine = 30; } if (m_bHeaderVisible) { nHeightHeader = GetPaintManager()->GetHeaderHeight(this, &dc); } if (m_bFooterVisible) { nHeightFooter = GetPaintManager()->GetFooterHeight(this, &dc); } // Group by rect m_rcGroupByArea.SetRect(0, 0, rcClient.Width(), nHeightGroupBy); // Time line m_rcTimelineArea.SetRect(0, m_rcGroupByArea.bottom, rcClient.Width(), m_rcGroupByArea.bottom + nHeightTimeLine); // Header m_rcHeaderArea.SetRect(0, m_rcTimelineArea.bottom, rcClient.Width(), m_rcTimelineArea.bottom + nHeightHeader); // Sections int nHeightSections = rcClient.Height() - nHeightGroupBy - nHeightTimeLine - nHeightHeader - nHeightFooter; CRect rcSections(0, m_rcHeaderArea.bottom, rcClient.Width(), m_rcHeaderArea.bottom+nHeightSections); m_pSections->AdjustLayout(&dc, rcSections); // Footer m_rcFooterArea.SetRect(0, rcClient.Height() - nHeightFooter, rcClient.Width(), rcClient.Height()); if (nHeaderWidth != m_rcHeaderArea.Width() && pHeader) { pHeader->AdjustColumnsWidth(m_rcHeaderArea.Width()); } m_bAdjustLayoutRunning = FALSE; } CXTPReportColumn* CXTPTrackControl::GetTrackColumn() const { CXTPReportColumn* pColumn = m_pColumns->Find(GetTrackColumnIndex()); return pColumn; } int CXTPTrackControl::PositionToTrack(int nPosition) { CXTPReportColumn* pColumn = GetTrackColumn(); CRect rcTrack = pColumn->GetRect(); rcTrack.DeflateRect(7, 0); return int(rcTrack.left + (nPosition - m_nViewPortMin) * rcTrack.Width() / (m_nViewPortMax - m_nViewPortMin)); } int CXTPTrackControl::TrackToPosition(int nTrack) const { CXTPReportColumn* pColumn = GetTrackColumn(); CRect rcTrack = pColumn->GetRect(); rcTrack.DeflateRect(7, 0); return int(m_nViewPortMin + (nTrack - rcTrack.left) * (m_nViewPortMax - m_nViewPortMin) / rcTrack.Width()); } void CXTPTrackControl::DoPropExchange(CXTPPropExchange* pPX) { CXTPReportControl::DoPropExchange(pPX); PX_Int(pPX, _T("TimeScaleMin"), m_nTimeLineMin); PX_Int(pPX, _T("TimeScaleMax"), m_nTimeLineMax); //PX_Int(pPX, _T("ViewPortMin"), m_nViewPortMin); //PX_Int(pPX, _T("ViewPortMax"), m_nViewPortMax); PX_Int(pPX, _T("WorkAreaMin"), m_nWorkAreaMin); PX_Int(pPX, _T("WorkAreaMax"), m_nWorkAreaMax); PX_Int(pPX, _T("TimeLinePos"), m_nTimeLinePosition); PX_Int(pPX, _T("TrackColumnIndex"), m_nTrackColumnIndex, -1); CXTPPropExchangeSection secMarkers(pPX->GetSection(_T("Markers"))); m_pMarkers->DoPropExchange(&secMarkers); } void CXTPTrackControl::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) { CXTPReportColumn* pColumn = GetTrackColumn(); if (!pColumn || !pColumn->IsVisible()) { CXTPReportControl::OnKeyDown(nChar, nRepCnt, nFlags); return; } if ((GetKeyState(VK_CONTROL) < 0 || GetKeyState(VK_SHIFT) < 0) && (nChar == VK_LEFT || nChar == VK_RIGHT || nChar == VK_UP || nChar == VK_DOWN)) { CreateDragBlocks(); if (m_arrDragBlocks.GetSize() == 0) return; BOOL bResize = m_bAllowBlockScale && GetKeyState(VK_SHIFT) < 0 ? 2 : 0; if (!bResize && !m_bAllowBlockMove) return; XTPDrawHelpers()->KeyToLayout(this, nChar); CRect rcBlock = m_arrDragBlocks[m_arrDragBlocks.GetSize() - 1].pBlock->GetRect(); CPoint pt = rcBlock.CenterPoint(); ClientToScreen(&pt); m_ptStartDrag = pt; if (nChar == VK_LEFT) pt.x -= 20; if (nChar == VK_RIGHT) pt.x += 20; if (nChar == VK_UP) pt.y -= 20; if (nChar == VK_DOWN) pt.y += 20; OnMoveBlock(pt, bResize); RedrawControl(); UpdateWindow(); rcBlock = m_arrDragBlocks[m_arrDragBlocks.GetSize() - 1].pBlock->GetRect(); pt = rcBlock.CenterPoint(); if (bResize) pt.x = rcBlock.right - 2; ClientToScreen(&pt); SetCursorPos(pt.x, pt.y); ReleaseDragBlocks(); return; } if (nChar == VK_RIGHT && !m_bFocusSubItems) { int nDelta = TrackToPosition(10) - TrackToPosition(0); if (IsLayoutRTL()) nDelta = -nDelta; SetViewPort(int(m_nViewPortMin + nDelta), int(m_nViewPortMax + nDelta)); return; } if (nChar == VK_LEFT && !m_bFocusSubItems) { int nDelta = TrackToPosition(10) - TrackToPosition(0); if (IsLayoutRTL()) nDelta = -nDelta; SetViewPort(int(m_nViewPortMin - nDelta), int(m_nViewPortMax - nDelta)); return; } if (nChar == VK_TAB) { CXTPTrackBlock* pOldSelectedBlock = m_pSelectedBlocks->GetAt(m_pSelectedBlocks->GetCount() - 1); CXTPTrackBlock* pNewSelectedBlock = NULL; if ((GetKeyState(VK_SHIFT) < 0)) // Back { int nOldPosition = pOldSelectedBlock ? pOldSelectedBlock->GetPosition() : INT_MAX; for (int i = pOldSelectedBlock == 0 ? GetRows()->m_nFocusedRow : GetRows()->GetCount() - 1; i >= 0; i--) { CXTPReportRow* pRow = GetRows()->GetAt(i); if (!pRow || !pRow->GetRecord()) continue; CXTPTrackControlItem* pTrack = DYNAMIC_DOWNCAST(CXTPTrackControlItem, pRow->GetRecord()->GetItem(GetTrackColumnIndex())); if (!pTrack || pTrack->GetBlockCount() == 0) continue; if (pOldSelectedBlock == NULL || pOldSelectedBlock->GetItem() == pTrack) { for (int j = 0; j < pTrack->GetBlockCount(); j++) { CXTPTrackBlock* pBlock = pTrack->GetBlock(j); if (pBlock->IsLocked()) continue; if (pBlock->GetPosition() >= nOldPosition) continue; if (pNewSelectedBlock == NULL || pBlock->GetPosition() > pNewSelectedBlock->GetPosition()) pNewSelectedBlock = pBlock; } if (pNewSelectedBlock) break; nOldPosition = INT_MAX; pOldSelectedBlock = 0; } } } else { int nOldPosition = pOldSelectedBlock ? pOldSelectedBlock->GetPosition() : -1; for (int i = pOldSelectedBlock == 0 ? GetRows()->m_nFocusedRow : 0; i < GetRows()->GetCount(); i++) { CXTPReportRow* pRow = GetRows()->GetAt(i); if (!pRow || !pRow->GetRecord()) continue; CXTPTrackControlItem* pTrack = DYNAMIC_DOWNCAST(CXTPTrackControlItem, pRow->GetRecord()->GetItem(GetTrackColumnIndex())); if (!pTrack || pTrack->GetBlockCount() == 0) continue; if (pOldSelectedBlock == NULL || pOldSelectedBlock->GetItem() == pTrack) { for (int j = 0; j < pTrack->GetBlockCount(); j++) { CXTPTrackBlock* pBlock = pTrack->GetBlock(j); if (pBlock->IsLocked()) continue; if (pBlock->GetPosition() <= nOldPosition) continue; if (pNewSelectedBlock == NULL || pBlock->GetPosition() < pNewSelectedBlock->GetPosition()) pNewSelectedBlock = pBlock; } if (pNewSelectedBlock) break; nOldPosition = -1; pOldSelectedBlock = 0; } } } if (pNewSelectedBlock != NULL) { m_pSelectedBlocks->RemoveAll(); m_pSelectedBlocks->Add(pNewSelectedBlock); SendMessageToParent(XTP_NM_TRACK_SELECTEDBLOCKSCHANGED); EnsureVisible(pNewSelectedBlock); } RedrawControl(); return; } CXTPReportControl::OnKeyDown(nChar, nRepCnt, nFlags); } UINT CXTPTrackControl::OnGetDlgCode() { return DLGC_WANTTAB | DLGC_WANTARROWS | DLGC_WANTCHARS; } void CXTPTrackControl::EnsureVisible(CXTPTrackBlock* pBlock) { CXTPReportRecord* pRecord = pBlock->GetItem()->GetRecord(); CXTPReportRow* pRow = GetRows()->Find(pRecord); if (!pRow) return; CXTPReportControl::EnsureVisible(pRow); CXTPReportControl::EnsureVisible(GetTrackColumn()); CXTPReportColumn* pColumn = GetTrackColumn(); if (!pColumn || !pColumn->IsVisible()) { return; } if (m_nViewPortMin >= pBlock->GetPosition() + pBlock->GetLength()) { SetViewPort(pBlock->GetPosition(), pBlock->GetPosition() + int(m_nViewPortMax - m_nViewPortMin)); } else if (m_nViewPortMax <= pBlock->GetPosition()) { int nViewPortMin = pBlock->GetPosition() + pBlock->GetLength() - int(m_nViewPortMax - m_nViewPortMin); if (nViewPortMin < m_nTimeLineMin) nViewPortMin = m_nTimeLineMin; SetViewPort(nViewPortMin, nViewPortMin + int(m_nViewPortMax - m_nViewPortMin)); } } ////////////////////////////////////////////////////////////////////////// // int CXTPTrackControl::SnapPosition(int nPosition) { CXTPTrackControl* pControl = this; int nMargin = pControl->GetSnapMargin(); if (nMargin == 0) return nPosition; int nScreenPosition = pControl->PositionToTrack(nPosition); if (pControl->m_bSnapToMarkers) { if (abs(nScreenPosition - pControl->PositionToTrack(pControl->GetTimeLinePosition())) <= nMargin) { return pControl->GetTimeLinePosition(); } for (int i = 0; i < pControl->GetMarkers()->GetCount(); i++) { if (abs(nScreenPosition - pControl->PositionToTrack(pControl->GetMarkers()->GetAt(i)->GetPosition())) <= nMargin) { return pControl->GetMarkers()->GetAt(i)->GetPosition(); } } } if (pControl->m_bSnapToBlocks) { CXTPReportScreenRows *pScreenRows = pControl->GetRows()->GetScreenRows(); for (int i = 0; i < pScreenRows->GetSize(); i++) { CXTPReportRow* pRow = pScreenRows->GetAt(i); if (!pRow || !pRow->GetRecord()) continue; CXTPTrackControlItem* pTrack = DYNAMIC_DOWNCAST(CXTPTrackControlItem, pRow->GetRecord()->GetItem(GetTrackColumnIndex())); if (!pTrack) continue; for (int j = 0; j < pTrack->GetBlockCount(); j++) { CXTPTrackBlock* pBlock = pTrack->GetBlock(j); if (pBlock->m_bDragging) continue; if (abs(nScreenPosition - pControl->PositionToTrack(pBlock->GetPosition())) <= nMargin) { return pBlock->GetPosition(); } if (abs(nScreenPosition - pControl->PositionToTrack(pBlock->GetPosition() + pBlock->GetLength())) <= nMargin) { return pBlock->GetPosition() + pBlock->GetLength(); } } } } return nPosition; } void CXTPTrackControl::OnMoveBlock(CPoint pt, BOOL bResize) { CXTPTrackControl* pControl = this; pControl->BeginUpdate(); CArray arrBlocks; CArray arrItems; int i; for (i = 0; i < m_arrDragBlocks.GetSize(); i++) { CXTPTrackBlock* pBlock = m_arrDragBlocks[i].pBlock; if (bResize && !pBlock->IsResizable()) continue; CXTPTrackControlItem* pOldTrack = pBlock->GetItem(); CRect rc = m_arrDragBlocks[i].rcOrigin; rc.OffsetRect(pt - m_ptStartDrag); CPoint ptHit = CPoint(0, (rc.top + rc.bottom) / 2); CXTPReportRow* pRow = CXTPReportControl::HitTest(ptHit); CXTPTrackControlItem* pNewTrack = 0; if (!bResize && pControl->m_bAllowBlockRemove) { if (pRow && pRow->GetRecord()) { pNewTrack = DYNAMIC_DOWNCAST(CXTPTrackControlItem, pRow->GetRecord()->GetItem(GetTrackColumnIndex())); } } else { pNewTrack = pBlock->GetItem(); } if (pNewTrack && !pNewTrack->IsLocked()) { if (pBlock->GetItem() != pNewTrack) { pBlock->InternalAddRef(); pBlock->Remove(); pNewTrack->Add(pBlock); } int nOffet = -(pControl->TrackToPosition(m_ptStartDrag.x) - pControl->TrackToPosition(pt.x)); if (IsLayoutRTL()) nOffet = -nOffet; int nPosition = pBlock->m_nMRUPosition; int nLength = pBlock->m_nLength; if (bResize == 1) { int nLeft = m_arrDragBlocks[i].nOldPos; int nRight =m_arrDragBlocks[i].nOldLength + m_arrDragBlocks[i].nOldPos; nLeft = max(pControl->GetTimeLineMin(), SnapPosition(nLeft + nOffet)); if (nRight - nLeft < pBlock->GetMinLength()) nLeft = nRight - pBlock->GetMinLength(); else if (nRight - nLeft > pBlock->GetMaxLength()) nLeft = nRight - pBlock->GetMaxLength(); nPosition = nLeft; nLength = nRight - nLeft; } else if (bResize == 2) { int nLeft = m_arrDragBlocks[i].nOldPos; int nRight =m_arrDragBlocks[i].nOldLength + m_arrDragBlocks[i].nOldPos; nRight = min(pControl->GetTimeLineMax(), SnapPosition(nRight + nOffet)); if (nRight - nLeft < pBlock->GetMinLength()) nRight = nLeft + pBlock->GetMinLength(); else if (nRight - nLeft > pBlock->GetMaxLength()) nRight = nLeft + pBlock->GetMaxLength(); nLength = nRight - nLeft; } else { int nRight = min(pControl->GetTimeLineMax(), SnapPosition(m_arrDragBlocks[i].nOldPos + m_arrDragBlocks[i].nOldLength + nOffet)); int nLeft = max(pControl->GetTimeLineMin(), SnapPosition(nRight - m_arrDragBlocks[i].nOldLength)); nPosition = nLeft; } pBlock->SetPosition(nPosition); pBlock->SetLength(nLength); pBlock->m_nLastDragTime = (int)time(0); arrBlocks.Add(pBlock); if (pOldTrack) { arrItems.Add(pOldTrack); } if (pNewTrack != pOldTrack) { arrItems.Add(pNewTrack); } } } for (i = (int)arrBlocks.GetSize() - 1; i >= 0; i--) { CXTPTrackBlock* pBlock = arrBlocks[i]; if (!pControl->m_bFlexibleDrag) { pBlock->GetItem()->AdjustBlockPosition(pBlock, bResize); } pControl->SendMessageToParent(XTP_NM_TRACK_BLOCKCHANGED, pBlock); } for (i = 0; i < arrItems.GetSize(); i++) { CXTPTrackControlItem* pItem = arrItems[i]; pItem->RecalcLayout(); } EndUpdate(); } void CXTPTrackControl::CreateDragBlocks() { m_arrDragBlocks.RemoveAll(); int i; for (i = 0; i < m_pSelectedBlocks->GetCount(); i++) { DRAGBLOCK block; block.pBlock = (CXTPTrackBlock*)m_pSelectedBlocks->GetAt(i); if (block.pBlock->IsLocked()) continue; block.nOldPos = block.pBlock->GetPosition(); block.nOldLength = block.pBlock->GetLength(); block.rcOrigin = block.pBlock->GetRect(); block.pBlock->m_bDragging = TRUE; m_arrDragBlocks.Add(block); } } void CXTPTrackControl::ReleaseDragBlocks() { for (int i = 0; i < m_arrDragBlocks.GetSize(); i++) { m_arrDragBlocks[i].pBlock->m_bDragging = FALSE; } } void CXTPTrackControl::StartDragBlocks(BOOL bResize) { m_pUndoManager->StartGroup(); // set capture to the window which received this message SetCapture(); CPoint pt(0, 0); GetCursorPos(&pt); m_ptStartDrag = pt; CreateDragBlocks(); ::SetCursor(bResize ? m_hResizeCursor : m_hMoveCursor); // get messages until capture lost or cancelled/accepted while (::GetCapture() == m_hWnd) { MSG msg; if (!::GetMessage(&msg, NULL, 0, 0)) { AfxPostQuitMessage((int)msg.wParam); break; } if (msg.message == WM_LBUTTONUP) break; else if (msg.message == WM_MOUSEMOVE && pt != msg.pt) { pt = msg.pt; OnMoveBlock(pt, bResize); } else if (msg.message == WM_KEYDOWN) { if (msg.wParam == VK_ESCAPE) { break; } } else DispatchMessage(&msg); } ReleaseCapture(); ReleaseDragBlocks(); m_pUndoManager->EndGroup(); } void CXTPTrackControl::OnMoveSelection(CPoint pt) { CXTPTrackControl* pControl = this; CRect rc(m_ptStartDrag, pt); rc.NormalizeRect(); pControl->ScreenToClient(&rc); int sz = pControl->m_pSelectedBlocks->GetCount(); pControl->m_pSelectedBlocks->RemoveAll(); CXTPReportScreenRows *pScreenRows = pControl->GetRows()->GetScreenRows(); for (int i = 0; i < pScreenRows->GetSize(); i++) { CXTPReportRow* pRow = pScreenRows->GetAt(i); if (!pRow->GetRecord()) continue; if (pRow->GetRect().top >= rc.bottom || pRow->GetRect().bottom <= rc.top) continue; CXTPTrackControlItem* pTrack = DYNAMIC_DOWNCAST(CXTPTrackControlItem, pRow->GetRecord()->GetItem(GetTrackColumnIndex())); if (!pTrack) continue; for (int j = 0; j < pTrack->GetBlockCount(); j++) { CXTPTrackBlock* pBlock = pTrack->GetBlock(j); if (pBlock->IsLocked()) continue; if (CRect().IntersectRect(pBlock->GetRect(), rc)) { pControl->m_pSelectedBlocks->Add(pBlock); } } } if (sz != pControl->m_pSelectedBlocks->GetCount()) { pControl->SendMessageToParent(XTP_NM_TRACK_SELECTEDBLOCKSCHANGED); } pControl->m_rcSelectedArea = rc; pControl->RedrawControl(); } void CXTPTrackControl::StartDragSelection() { // set capture to the window which received this message SetCapture(); CPoint pt(0, 0); GetCursorPos(&pt); m_ptStartDrag = pt; // get messages until capture lost or canceled/accepted while (::GetCapture() == m_hWnd) { MSG msg; if (!::GetMessage(&msg, NULL, 0, 0)) { AfxPostQuitMessage((int)msg.wParam); break; } if (msg.message == WM_LBUTTONUP) break; else if (msg.message == WM_MOUSEMOVE && pt != msg.pt) { pt = msg.pt; OnMoveSelection(pt); } else if (msg.message == WM_KEYDOWN) { if (msg.wParam == VK_ESCAPE) { break; } } else DispatchMessage(&msg); } ReleaseCapture(); m_rcSelectedArea.SetRectEmpty(); RedrawControl(); } void CXTPTrackControl::OnLButtonDown(UINT nFlags, CPoint point) { if (m_pSectionBody->GetRect().PtInRect(point)) { if (CXTPReportControl::HitTest(point) == NULL) { if (m_pSelectedBlocks->GetCount()) { m_pSelectedBlocks->RemoveAll(); SendMessageToParent(XTP_NM_TRACK_SELECTEDBLOCKSCHANGED); } StartDragSelection(); return; } } CXTPReportControl::OnLButtonDown(nFlags, point); } void CXTPTrackControl::Populate() { CXTPReportControl::Populate(); if (m_pUndoManager) m_pUndoManager->Clear(); } BOOL CXTPTrackControl::HitTest(CPoint pt, CXTPReportHitTestInfo *pInfo) const { if (NULL == pInfo) { return FALSE; } pInfo->m_pColumn = NULL; pInfo->m_pRow = NULL; pInfo->m_pItem = NULL; pInfo->m_pBlock = NULL; pInfo->m_htCode = xtpReportHitTestUnknown; pInfo->m_iTrackPosition = 0; if (m_pSections->GetRect().PtInRect(pt)) { pInfo->m_pRow = CXTPReportControl::HitTest(pt); if (pInfo->m_pRow) { pInfo->m_pItem = pInfo->m_pRow->HitTest(pt, NULL, &pInfo->m_pColumn); if (pInfo->m_pItem && pInfo->m_pItem->IsKindOf(RUNTIME_CLASS(CXTPTrackControlItem))) { CXTPTrackControlItem *pItem = DYNAMIC_DOWNCAST(CXTPTrackControlItem, pInfo->m_pItem); if (pItem) { CXTPTrackBlock *pBlock = pItem->HitTest(pt); pInfo->m_iTrackPosition = TrackToPosition(pt.x); if (NULL != pBlock) { pInfo->m_htCode = xtpReportHitTestBlock; pInfo->m_pBlock = pBlock; } } } } } if (xtpReportHitTestUnknown == pInfo->m_htCode) { return CXTPReportControl::HitTest(pt, pInfo); } else { return TRUE; } }