// ConditionsView.cpp : implementation file // #include "stdafx.h" #include "logic_editor.h" #include "ConditionsView.h" #include "Logic_editorDoc.h" #include "MainFrm.h" #include "ChildFrm.h" #include "EditorFormView.h" #include "TMenu.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif #ifndef ID_MOVEASCHILD #define ID_MOVEASCHILD 32857 #endif #ifndef ID_MOVEASSIBLING #define ID_MOVEASSIBLING 32858 #endif #ifndef ID_COPYASSIBLING #define ID_COPYASSIBLING 32859 #endif #ifndef ID_COPYASCHILD #define ID_COPYASCHILD 32860 #endif ///////////////////////////////////////////////////////////////////////////// // CConditionsView IMPLEMENT_DYNCREATE(CConditionsView, CTreeView) CConditionsView::CConditionsView() { } CConditionsView::~CConditionsView() { } BEGIN_MESSAGE_MAP(CConditionsView, CTreeView) //{{AFX_MSG_MAP(CConditionsView) ON_NOTIFY_REFLECT(TVN_SELCHANGED, OnSelchanged) ON_WM_KILLFOCUS() ON_NOTIFY_REFLECT(TVN_BEGINRDRAG, OnBeginrdrag) ON_WM_MOUSEMOVE() ON_WM_RBUTTONUP() //}}AFX_MSG_MAP ON_COMMAND(ID_MOVEASCHILD, OnMoveAsChild) ON_COMMAND(ID_MOVEASSIBLING, OnMoveAsSibling) ON_COMMAND(ID_COPYASCHILD, OnCopyAsChild) ON_COMMAND(ID_COPYASSIBLING, OnCopyAsSibling) END_MESSAGE_MAP() // Creating Drag Image for a CTreeCtrl without images // Contribution of Pal K Tonder on www.codeguru.com // The method CreateDragImage is used during drag'n drop to create a drag image. The problem is that it only works for a CTreeCtrl with images. // The following method, CreateDragImageEx, checks whether the CTreeCtrl has a valid normal CImageList. If so, it just calls the standard CreateDragImage method and returns. // If, on the other hand, no valid CImageList is found, a bitmap is created based on the size of item rect, and the item text is drawn into it. Then a CImageList is // created and the bitmap is added to it. CImageList* CConditionsView::CreateDragImageEx(HTREEITEM hItem) { CTreeCtrl & tree = GetTreeCtrl(); if(tree.GetImageList(TVSIL_NORMAL) != NULL) return tree.CreateDragImage(hItem); CRect rect; tree.GetItemRect(hItem, rect, TRUE); rect.OffsetRect(-rect.left, -rect.top); // Create bitmap CClientDC dc (this); CDC memDC; if(!memDC.CreateCompatibleDC(&dc)) return NULL; CBitmap bitmap; if(!bitmap.CreateCompatibleBitmap(&dc, rect.Width(), rect.Height())) return NULL; CBitmap* pOldMemDCBitmap = memDC.SelectObject( &bitmap ); CFont* pOldFont = memDC.SelectObject(GetFont()); memDC.FillSolidRect(&rect, RGB(0, 255, 0)); // Here green is used as mask color memDC.SetTextColor(GetSysColor(COLOR_GRAYTEXT)); memDC.TextOut(rect.left, rect.top, tree.GetItemText(hItem)); memDC.SelectObject( pOldFont ); memDC.SelectObject( pOldMemDCBitmap ); // Create imagelist CImageList* pImageList = new CImageList; pImageList->Create(rect.Width(), rect.Height(), ILC_COLOR | ILC_MASK, 0, 1); pImageList->Add(&bitmap, RGB(0, 255, 0)); // Here green is used as mask color return pImageList; } ///////////////////////////////////////////////////////////////////////////// // CConditionsView drawing void CConditionsView::OnDraw(CDC* pDC) { CDocument* pDoc = GetDocument(); // TODO: add draw code here } ///////////////////////////////////////////////////////////////////////////// // CConditionsView diagnostics #ifdef _DEBUG void CConditionsView::AssertValid() const { CTreeView::AssertValid(); } void CConditionsView::Dump(CDumpContext& dc) const { CTreeView::Dump(dc); } #endif //_DEBUG void CConditionsView::expand(UINT nCode, HTREEITEM hItem) { HTREEITEM hElement; HTreeItemArray pile; CTreeCtrl &m_tree = GetTreeCtrl(); if (hItem == NULL) hElement = m_tree.GetRootItem(); else { m_tree.Expand(hItem,nCode); hElement = m_tree.GetChildItem(hItem); } pile.RemoveAll(); while ((hElement != NULL) || (pile.GetSize() != 0)) { while ((hElement == NULL) && (pile.GetSize()!=0)) { hElement = pile[pile.GetSize()-1]; pile.RemoveAt(pile.GetSize()-1); hElement = m_tree.GetNextSiblingItem(hElement); } if (hElement) { pile.Add(hElement); m_tree.Expand(hElement,nCode); hElement = m_tree.GetChildItem(hElement); } } } HMENU CConditionsView::GetRDragMenu() { CMenu menu; menu.CreatePopupMenu(); menu.AppendMenu( MF_STRING, ID_MOVEASCHILD, _T("Move As Child") ); menu.AppendMenu( MF_STRING, ID_MOVEASSIBLING, _T("Move As Sibling") ); menu.AppendMenu( MF_STRING, ID_COPYASCHILD, _T("Copy As Child") ); menu.AppendMenu( MF_STRING, ID_COPYASSIBLING, _T("Copy As Sibling") ); return menu.Detach(); } ///////////////////////////////////////////////////////////////////////////// // CConditionsView message handlers void CConditionsView::OnInitialUpdate() { CTreeView::OnInitialUpdate(); m_pSelectedCondition = NULL; m_pDragCondition = NULL; m_pSelectedNode = NULL; m_pDragImage = NULL; m_hitemDrag = NULL; m_hitemDrop = NULL; m_bRDragging = FALSE; m_bRDragCanceled = FALSE; } void CConditionsView::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint) { /** TO DO : REDRAW ONLY IF Some CONDITIONS have changed **/ SetRedraw(FALSE); CTreeView::OnUpdate(pSender,lHint,pHint); BOOL bSelection = FALSE; // get a pointer on associated document CLogic_editorDoc *pDoc = static_cast(GetDocument()); ASSERT_VALID(pDoc); CTreeCtrl &m_tree = GetTreeCtrl(); m_tree.DeleteAllItems(); m_mapItemToNode.RemoveAll(); TPositionList heap; HTREEITEM hItem = m_tree.InsertItem( "Conditions",0,0); // get the states map CCondition *pCondition; CString condName; POSITION pos = pDoc->m_conditions.GetStartPosition(); while (pos != NULL) { pDoc->m_conditions.GetNextAssoc( pos, condName, (void*&)pCondition ); hItem = m_tree.InsertItem( condName , m_tree.GetRootItem()); // if ( m_pSelectedCondition == pCondition && m_pSelectedNode == NULL) { m_tree.SelectItem( hItem ); bSelection = TRUE; } CConditionNode *pNode, *pNodeTemp; HTREEITEM parentHItem = hItem; POSITION nodePos = pCondition->m_ctConditionTree.GetHeadPosition(); while (nodePos != NULL) { pNode = pCondition->m_ctConditionTree.GetNext( nodePos ); if (pNode != NULL) { hItem = m_tree.InsertItem( pNode->getNodeAsString() , parentHItem); // memorize the association hItem/pNode in the map m_mapItemToNode.SetAt( hItem, pNode); if (m_pSelectedNode == pNode) { m_tree.SelectItem( hItem ); bSelection = TRUE; } for (;;) { // get the deepest first level node in the sub tree POSITION subPos = pNode->m_ctSubTree.GetHeadPosition(); while (subPos != NULL) { pNodeTemp = pNode->m_ctSubTree.GetNext(subPos); if (pNodeTemp != NULL) { pNode = pNodeTemp; hItem = m_tree.InsertItem( pNode->getNodeAsString() , hItem); m_mapItemToNode.SetAt( hItem, pNode); if (m_pSelectedNode == pNode) { m_tree.SelectItem( hItem ); bSelection = TRUE; } // push the position heap.AddTail( subPos ); subPos = pNode->m_ctSubTree.GetHeadPosition(); } } // get sibling if any, if none, get next node do { pNode = pNode->getParentNode(); hItem = m_tree.GetParentItem( hItem ); // pop subPos if( heap.IsEmpty() ) break; subPos = heap.GetTail(); heap.RemoveTail(); if (subPos != NULL) { pNodeTemp = pNode->m_ctSubTree.GetNext( subPos ); if (pNodeTemp != NULL) { pNode = pNodeTemp; hItem = m_tree.InsertItem( pNode->getNodeAsString() , hItem); // memorize the association hItem/pNode in the map m_mapItemToNode.SetAt( hItem, pNode); if (m_pSelectedNode == pNode) { m_tree.SelectItem( hItem ); bSelection = TRUE; } //push heap.AddTail( subPos ); subPos = pNode->m_ctSubTree.GetHeadPosition(); } else break; } } while (subPos == NULL); if (pNode == NULL) break; } } } } // expand all tree expand(/*TVE_EXPAND ,NULL*/ ); if (!bSelection) { m_pSelectedNode = NULL; m_pSelectedCondition = NULL; } else { // assure the selected item is visible (if any) hItem = m_tree.GetSelectedItem(); ASSERT (hItem != NULL); m_tree.EnsureVisible( hItem ); } SetRedraw(/*TRUE*/); } BOOL CConditionsView::PreCreateWindow(CREATESTRUCT& cs) { // modify arborescence style cs.style |=(TVS_HASLINES|TVS_LINESATROOT|TVS_HASBUTTONS/*|TVS_EDITLABELS*/); // cs.style &= ~TVS_DISABLEDRAGDROP; return CTreeView::PreCreateWindow(cs); } void CConditionsView::OnSelchanged(NMHDR* pNMHDR, LRESULT* pResult) { NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR; // m_pSelectedCondition = NULL; m_pSelectedNode = NULL; // get the selected item and update the good frame (open the condition edition page in the form too) CTreeCtrl &treeCtrl = GetTreeCtrl(); HTREEITEM hItem; hItem = treeCtrl.GetSelectedItem(); // if (hItem == treeCtrl.GetRootItem()) return; // get the node selected (if any) if ( ! m_mapItemToNode.Lookup(hItem, m_pSelectedNode) ) m_pSelectedNode = NULL; // get the condition (if any) while ( treeCtrl.GetParentItem( hItem ) != treeCtrl.GetRootItem() ) hItem = treeCtrl.GetParentItem( hItem ); CString condName = treeCtrl.GetItemText( hItem ); CLogic_editorDoc *pDoc = static_cast (GetDocument()); // to update the fields of the condition page in the form view CMainFrame *pFrame = (CMainFrame*)AfxGetApp()->m_pMainWnd; // Get the active MDI child window. CChildFrame *pChild = (CChildFrame *) pFrame->GetActiveFrame(); CEditorFormView *pFormView = static_cast ( pChild->m_wndSplitter.GetPane(0,1) ); CConditionPage *pCondPage = static_cast ( pFormView->m_pPropertySheet->GetPage(2) ); if ( pDoc->m_conditions.Lookup( condName, (void*&) m_pSelectedCondition) ) { pFormView->m_pPropertySheet->SetActivePage( pCondPage ); if ( pCondPage->m_pSelectedConditionNode != m_pSelectedNode || m_pSelectedNode == NULL) { pCondPage->m_pSelectedCondition = this->m_pSelectedCondition; pCondPage->m_pSelectedConditionNode = this->m_pSelectedNode; pCondPage->Update(); } } else { if (pCondPage->m_pSelectedCondition != NULL) { pCondPage->m_pSelectedConditionNode = this->m_pSelectedNode; // = NULL pCondPage->m_pSelectedCondition = this->m_pSelectedCondition; // = NULL pCondPage->Update(); } } this->SetFocus(); // *pResult = 0; } void CConditionsView::OnKillFocus(CWnd* pNewWnd) { // keep the current selection, so do not call CTreeView::OnKillFocus() // CTreeView::OnKillFocus(pNewWnd); } void CConditionsView::OnBeginrdrag(NMHDR* pNMHDR, LRESULT* pResult) { NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR; *pResult = 0; CTreeCtrl &tree = GetTreeCtrl(); CPoint ptAction; GetCursorPos(&ptAction); ScreenToClient(&ptAction); m_bRDragging = TRUE; m_bRDragCanceled = FALSE; m_hitemDrag = pNMTreeView->itemNew.hItem; m_hitemDrop = NULL; m_pDragImage = CreateDragImageEx(m_hitemDrag); // get the image list for dragging m_pDragImage->BeginDrag(0, CPoint(10,-15)); tree.SelectDropTarget(NULL); // to prevent image corruption. m_pDragImage->DragEnter(NULL, ptAction); SetCapture(); // get the drag condition HTREEITEM hItem = m_hitemDrag; // if (hItem == tree.GetRootItem()) return; // get the condition (if any) while ( tree.GetParentItem( hItem ) != tree.GetRootItem() ) hItem = tree.GetParentItem( hItem ); CString condName = tree.GetItemText( hItem ); CLogic_editorDoc *pDoc = static_cast (GetDocument()); pDoc->m_conditions.Lookup( condName, (void*&) m_pDragCondition); } void CConditionsView::OnMouseMove(UINT nFlags, CPoint point) { HTREEITEM hitem; UINT flags; CTreeCtrl &tree = GetTreeCtrl(); if (m_bRDragging) { POINT pt = point; ClientToScreen( &pt ); CImageList::DragMove(pt); if ((hitem = tree.HitTest(point, &flags)) != NULL) { CImageList::DragShowNolock(FALSE); tree.SelectDropTarget(hitem); m_hitemDrop = hitem; CImageList::DragShowNolock(TRUE); } } CTreeView::OnMouseMove(nFlags, point); } void CConditionsView::OnRButtonUp(UINT nFlags, CPoint point) { CTreeCtrl &tree = GetTreeCtrl(); if (m_bRDragging) { m_bRDragging = FALSE; CImageList::DragLeave(this); CImageList::EndDrag(); ReleaseCapture(); delete m_pDragImage; if( m_hitemDrag == m_hitemDrop ) return; CTMenu menu; menu.Attach( GetRDragMenu() ); menu.AddMenuTitle( tree.GetItemText(m_hitemDrag) ); // if the target is a condition and the dragged item is a node, do not allow '..as sibling' operations CConditionNode *pNode; if ( m_mapItemToNode.Lookup(m_hitemDrag, pNode) && ! m_mapItemToNode.Lookup(m_hitemDrop, pNode) ) { menu.EnableMenuItem( ID_MOVEASSIBLING, MF_GRAYED ); menu.EnableMenuItem( ID_COPYASSIBLING, MF_GRAYED ); } if( menu == NULL ) return; ClientToScreen( &point ); menu.TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, this); tree.SelectDropTarget(NULL); } else CTreeView::OnRButtonUp(nFlags, point); } void CConditionsView::OnMoveAsChild() { CTreeCtrl &tree = GetTreeCtrl(); // Check that the dragged item is not an ancestor HTREEITEM htiParent = m_hitemDrop; while( (htiParent = tree.GetParentItem( htiParent )) != NULL ) { if( htiParent == m_hitemDrag ) return; } CConditionNode* pTargetNode = NULL; CCondition * pTargetCond = NULL; CConditionNode *pDragNode; // get the drag object if ( m_mapItemToNode.Lookup( m_hitemDrag, pDragNode ) ) { CConditionNode *pParentNode = pDragNode->getParentNode(); // parent is a node if (pParentNode != NULL) { POSITION pos = pParentNode->m_ctSubTree.Find( pDragNode ); if (pos != NULL) { pParentNode->m_ctSubTree.RemoveAt( pos ); } else return; } // parent is a condition else { POSITION pos = m_pDragCondition->m_ctConditionTree.Find( pDragNode ); if (pos != NULL) { m_pDragCondition->m_ctConditionTree.RemoveAt( pos ); } else return; } } else return; CLogic_editorDoc *pDoc = static_cast (GetDocument()); // target is a node if ( m_mapItemToNode.Lookup( m_hitemDrop, pTargetNode ) ) { pTargetNode->m_ctSubTree.AddTail( pDragNode ); pDragNode->m_pParentNode = pTargetNode; } // target is a condition else { CString name = tree.GetItemText( m_hitemDrop ); if (pDoc->m_conditions.Lookup( name, (void*&)pTargetCond ) ) { pTargetCond->m_ctConditionTree.AddTail( pDragNode ); pDragNode->m_pParentNode = NULL; } else { AfxMessageBox("Error : root condition not found"); delete pDragNode; } } // for the moment, update all views, but moving branch may be faster pDoc->UpdateAllViews(NULL); /* Expand( m_hitemDrop, TVE_EXPAND ) ; HTREEITEM htiNew = CopyBranch( m_hitemDrag, m_hitemDrop, TVI_LAST ); DeleteItem(m_hitemDrag); SelectItem( htiNew ); */ } void CConditionsView::OnMoveAsSibling() { CTreeCtrl &tree = GetTreeCtrl(); if (m_hitemDrop == tree.GetRootItem() ) return; m_hitemDrop = tree.GetParentItem( m_hitemDrop); OnMoveAsChild(); } void CConditionsView::OnCopyAsChild() { CTreeCtrl &tree = GetTreeCtrl(); // Check that the dragged item is not an ancestor HTREEITEM htiParent = m_hitemDrop; while( (htiParent = tree.GetParentItem( htiParent )) != NULL ) { if( htiParent == m_hitemDrag ) return; } CConditionNode* pTargetNode = NULL; CCondition * pTargetCond = NULL; CConditionNode *pDragNode = NULL; CConditionNode *pNewNode = NULL; // get the drag object if ( m_mapItemToNode.Lookup( m_hitemDrag, pDragNode ) ) { pNewNode = new CConditionNode(*pDragNode); } else return; CLogic_editorDoc *pDoc = static_cast (GetDocument()); // target is a node if ( m_mapItemToNode.Lookup( m_hitemDrop, pTargetNode ) ) { pTargetNode->m_ctSubTree.AddTail( pNewNode ); pNewNode->m_pParentNode = pTargetNode; } // target is a condition else { CString name = tree.GetItemText( m_hitemDrop ); if (pDoc->m_conditions.Lookup( name, (void*&)pTargetCond ) ) { pTargetCond->m_ctConditionTree.AddTail( pNewNode ); pNewNode->m_pParentNode = NULL; } else { AfxMessageBox("Error : root condition not found"); delete pNewNode; } } // for the moment, update all views, but moving branch may be faster pDoc->UpdateAllViews(NULL); /* HTREEITEM htiNew = CopyBranch( m_hitemDrag, m_hitemDrop, TVI_LAST ); */ } void CConditionsView::OnCopyAsSibling() { CTreeCtrl &tree = GetTreeCtrl(); if (m_hitemDrop == tree.GetRootItem() ) return; m_hitemDrop = tree.GetParentItem( m_hitemDrop); OnCopyAsChild(); }