// svrnode.cpp : implementation of the CServerNode class // // This is a part of the Microsoft Foundation Classes C++ library. // Copyright (C) 1992-1995 Microsoft Corporation // All rights reserved. // // This source code is only intended as a supplement to the // Microsoft Foundation Classes Reference and Microsoft // QuickHelp and/or WinHelp documentation provided with the library. // See these sources for detailed information regarding the // Microsoft Foundation Classes product. #include "stdafx.h" #include // for CSharedFile #include // for _alloca #include "$$root$$.h" #include "svritem.h" #include "svrdoc.h" #include "svrview.h" #ifdef _DEBUG #undef THIS_FILE static char BASED_CODE THIS_FILE[] = __FILE__; #endif ///////////////////////////////////////////////////////////////////////////// // Local dialogs used for implementation class CChangeNameDlg : public CDialog { // Construction public: CChangeNameDlg(CWnd* pParent = NULL); // Dialog Data //{{AFX_DATA(CChangeNameDlg) enum { IDD = IDD_CHANGE_NAME }; int m_shape; CString m_strDescription; CString m_strLinkKey; CEdit m_editDescription; CButton m_btnOK; //}}AFX_DATA // Implementation protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support virtual BOOL OnInitDialog(); // Generated message map functions //{{AFX_MSG(CChangeNameDlg) afx_msg void OnChange(); //}}AFX_MSG DECLARE_MESSAGE_MAP() }; class CAddNodeDlg : public CDialog { // Construction public: CAddNodeDlg(CWnd* pParent = NULL); // standard constructor // Dialog Data //{{AFX_DATA(CAddNodeDlg) enum { IDD = IDD_ADD_NODE }; CString m_strDescription; CString m_strLinkKey; int m_shape; CEdit m_editDescription; CButton m_btnOK; //}}AFX_DATA // Implementation protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support virtual BOOL OnInitDialog(); // Generated message map functions //{{AFX_MSG(CAddNodeDlg) afx_msg void OnChange(); //}}AFX_MSG DECLARE_MESSAGE_MAP() }; ///////////////////////////////////////////////////////////////////////////// // CServerNode IMPLEMENT_SERIAL(CServerNode, CObject, 0); CServerNode::CServerNode(CServerDoc* pServerDoc) { m_shape = shapeRect; m_bHideChildren = FALSE; m_pDocument = pServerDoc; m_pServerItem = NULL; // will get connected if need be } // Special "new" operator for root node CServerNode* CServerNode::CreateRootNode(CServerDoc* pDoc) { CServerNode* pRoot = NULL; TRY { pRoot = new CServerNode(pDoc); pRoot->InitRootNode(); } CATCH(CException, e) { delete pRoot; pRoot = NULL; } END_CATCH return pRoot; } // Special constructor for root node void CServerNode::InitRootNode() { DeleteChildNodes(); // initalized root node has no children m_strDescription = "Root node"; m_strLinkKey = "%ROOT%"; // link name for root UpdateItemName(); } BOOL CServerNode::FindAndDelete(CServerNode* pNode) { POSITION pos = m_listChild.GetHeadPosition(); while (pos != NULL) { POSITION posPrev = pos; CServerNode *pChild = (CServerNode*)m_listChild.GetNext(pos); if (pChild == pNode) { m_listChild.RemoveAt(posPrev); delete pNode; return TRUE; } if (pChild->FindAndDelete(pNode) == TRUE) return TRUE; } return FALSE; } CServerNode* CServerNode::FindNode(LPCTSTR pszItemName) { // return this node if it matches! if (GetItemName() == pszItemName) return this; POSITION pos = m_listChild.GetHeadPosition(); while (pos != NULL) { CServerNode* pChild = (CServerNode*)m_listChild.GetNext(pos); if (pChild->GetItemName() == pszItemName) return pChild; // recurse down the tree pChild = pChild->FindNode(pszItemName); if (pChild != NULL) return pChild; } return NULL; } void CServerNode::DeleteChildNodes() { // delete child nodes POSITION pos = m_listChild.GetHeadPosition(); while (pos != NULL) delete m_listChild.GetNext(pos); m_listChild.RemoveAll(); } CServerNode::~CServerNode() { // if we are the root of a document, clear it if (GetDocument()->m_pRoot == this) GetDocument()->m_pRoot = NULL; // if we are attached to a server item, detach from it if (m_pServerItem != NULL) m_pServerItem->m_pServerNode = NULL; // delete all of the child nodes of this node DeleteChildNodes(); } BOOL CServerNode::IsChild(const CServerNode* pPotentialChild) const { if (pPotentialChild == this) return TRUE; POSITION pos = m_listChild.GetHeadPosition(); while (pos != NULL) { CServerNode* pNode = (CServerNode*)m_listChild.GetNext(pos); if (pNode->IsChild(pPotentialChild)) return TRUE; } return FALSE; } ///////////////////////////////////////////////////////////////////////////// // CServerNode bounding rect helpers #define CX_INSET 4 #define CY_INSET 2 // calc node size for arbitrary dc and return // (result is in logical units for the given DC) void CServerNode::CalcNodeSize(CDC* pDC, CSize& sizeNode) { ASSERT(pDC != NULL); sizeNode = pDC->GetTextExtent(m_strDescription, m_strDescription.GetLength()); sizeNode += CSize(CX_INSET * 2, CY_INSET * 2); } // calculate bounding rect using pointer to member for each node void CServerNode::CalcBounding(CDC* pDC, CPoint& ptStart, CSize& sizeMax) { ASSERT(sizeMax.cx >= 0 && sizeMax.cy >= 0); ASSERT(ptStart.x >= 0 && ptStart.y >= 0); CSize sizeNode; CalcNodeSize(pDC, sizeNode); ptStart.y += sizeNode.cy + CY_SEPARATOR; if (ptStart.y > sizeMax.cy) sizeMax.cy = ptStart.y; if (ptStart.x + sizeNode.cx > sizeMax.cx) sizeMax.cx = ptStart.x + sizeNode.cx; ptStart.x += CX_INDENT; // add in the kids if (!m_bHideChildren) { POSITION pos = m_listChild.GetHeadPosition(); while (pos != NULL) { CServerNode* pNode = (CServerNode*)m_listChild.GetNext(pos); pNode->CalcBounding(pDC, ptStart, sizeMax); } } ptStart.x -= CX_INDENT; } $$IF(WANTS_TEXTVIEW) ///////////////////////////////////////////////////////////////////////////// // CSTextView drawing helpers BOOL CServerNode::GetNodeText(CString *strBuf, int TAB) { *strBuf+= m_strDescription ; //Insert a return *strBuf+= "\r\n" ; return TRUE; } int CServerNode::GetTreeText(CString * strBuf, int TAB) { ASSERT(strBuf != NULL); // Indent if we need to. for(int i = 0; i< TAB; i++) *strBuf+= "\t" ; GetNodeText(strBuf, TAB); if (m_listChild.GetCount() == 0 || m_bHideChildren) return TAB; // nothing more to draw // draw the kids POSITION pos = m_listChild.GetHeadPosition(); while (pos != NULL) { CServerNode *pChild = (CServerNode*)m_listChild.GetNext(pos); // draw the child node pChild->GetTreeText(strBuf, TAB +1); } return TAB; } $$ENDIF ///////////////////////////////////////////////////////////////////////////// // CServerNode drawing helpers BOOL CServerNode::Draw(CDC* pDC, CPoint pt, BOOL bSelected, CSize sizeNode) { CRect rect(pt, sizeNode); if (!pDC->RectVisible(&rect)) return TRUE; // draw a the bounding shape BOOL bOK = TRUE; CPen* pOldPen = NULL; CPen penHilite; int nPenStyle = (m_bHideChildren) ? PS_DASH : PS_SOLID; COLORREF crPen = (bSelected) ? RGB(255,0,0) : RGB(0,0,0); if (penHilite.CreatePen(nPenStyle, 0, crPen)) pOldPen = pDC->SelectObject(&penHilite); switch (m_shape) { case shapeRect: bOK = pDC->Rectangle(rect); break; case shapeRound: bOK = pDC->RoundRect(rect, CPoint(7, 7)); break; case shapeOval: // really just a rounder rounded rect bOK = pDC->RoundRect(rect, CPoint(14, 14)); break; default: TRACE1("Error: unknown shape %d\n", m_shape); bOK = FALSE; break; } if (pOldPen != NULL) pDC->SelectObject(pOldPen); if (!bOK) return FALSE; // inset a bit rect.InflateRect(-CX_INSET, -CY_INSET); if (!pDC->ExtTextOut(rect.left, rect.top, ETO_OPAQUE, rect, m_strDescription, m_strDescription.GetLength(), NULL)) return FALSE; // all OK return TRUE; } int CServerNode::DrawTree(CDC* pDC, CPoint ptStart, CServerNode* pNodeSel) { ASSERT(pDC != NULL); ASSERT(ptStart.x >= 0 && ptStart.y >= 0); // root node starts here int cyDrawn = 0; CSize sizeNode; CalcNodeSize(pDC, sizeNode); Draw(pDC, ptStart, this == pNodeSel, sizeNode); cyDrawn += sizeNode.cy + CY_SEPARATOR; if (m_listChild.GetCount() == 0 || m_bHideChildren) return cyDrawn; // nothing more to draw // indent for the kids CPoint ptKid(ptStart.x + CX_INDENT, ptStart.y + cyDrawn); // draw the kids int yMid = 0; POSITION pos = m_listChild.GetHeadPosition(); while (pos != NULL) { CServerNode *pChild = (CServerNode*)m_listChild.GetNext(pos); // add the little line yMid = ptKid.y + (sizeNode.cy)/2; pDC->MoveTo(ptStart.x + CX_BACKDENT, yMid); pDC->LineTo(ptKid.x, yMid); // draw the child node int cyKid = pChild->DrawTree(pDC, ptKid, pNodeSel); if (cyKid == -1) return -1; // error cyDrawn += cyKid; ptKid.y += cyKid; } ASSERT(ptKid.y == ptStart.y + cyDrawn); // draw the connecting line (down to last yMid) ASSERT(yMid != 0); int xLine = ptStart.x + CX_BACKDENT; pDC->MoveTo(xLine, ptStart.y + sizeNode.cy); pDC->LineTo(xLine, yMid); return cyDrawn; } // Serializes to file or to embedded OLE stream void CServerNode::Serialize(CArchive& ar) { CObject::Serialize(ar); if (ar.IsStoring()) { ar << (WORD)m_shape; ASSERT(m_strLinkKey.IsEmpty() || m_strDescription != m_strLinkKey); if (m_pServerItem != NULL) ar << m_pServerItem->GetItemName(); else ar << m_strLinkKey; ar << m_strDescription; ar << (WORD)m_bHideChildren; } else { // get back-pointer to document m_pDocument = (CServerDoc*)ar.m_pDocument; WORD wTemp; ar >> wTemp; m_shape = (EShape)wTemp; if (m_shape >= shapeMax) m_shape = shapeRect; ar >> m_strLinkKey >> m_strDescription; UpdateItemName(); ar >> wTemp; m_bHideChildren = (BOOL)wTemp; } m_listChild.Serialize(ar); } ///////////////////////////////////////////////////////////////////////////// // CServerNode support for CF_TEXT format void CServerNode::SaveAsText(CArchive& ar, int nLevel) { ASSERT(ar.IsStoring()); // indent node text char szTabs[MAX_LEVEL]; int nTabs = min(nLevel, MAX_LEVEL); memset(szTabs, '\t', nTabs); ar.Write(szTabs, nTabs); // write node text itself #ifdef _UNICODE int nLen = m_strDescription.GetLength(); char* psz = (char*)_alloca(nLen+1); wcstombs(psz, m_strDescription, nLen+1); ar.Write(psz, nLen); #else ar.Write(m_strDescription, m_strDescription.GetLength()*sizeof(TCHAR)); #endif // terminate line with CRLF static char BASED_CODE szCRLF[] = "\r\n"; ar.Write(szCRLF, sizeof(szCRLF)-1); // write kids POSITION pos = m_listChild.GetHeadPosition(); while (pos != NULL) { CServerNode* pNode = (CServerNode*)m_listChild.GetNext(pos); pNode->SaveAsText(ar, nLevel+1); } } void CServerNode::UpdateItemName() { // empty link key if same as description (it is redundant) if (m_strLinkKey == m_strDescription) m_strLinkKey.Empty(); // update link name in item if attached if (m_pServerItem != NULL) { CString* pstr; if (m_strLinkKey.IsEmpty()) pstr = &m_strDescription; else pstr = &m_strLinkKey; m_pServerItem->SetItemName(*pstr); // empty link key since it is redundant with the server item m_strLinkKey.Empty(); } } const CString& CServerNode::GetItemName() { // use item name in server item if connected if (m_pServerItem != NULL) return m_pServerItem->GetItemName(); // use description or link key depending on state of link key else if (m_strLinkKey.IsEmpty()) return m_strDescription; else return m_strLinkKey; } BOOL CServerNode::PromptChangeNode() { CChangeNameDlg dlg; dlg.m_strDescription = m_strDescription; dlg.m_strLinkKey = GetItemName(); if (dlg.m_strLinkKey == dlg.m_strDescription) dlg.m_strLinkKey.Empty(); // leave empty for tracking link name dlg.m_shape = m_shape; if (dlg.DoModal() == IDOK) { m_strDescription = dlg.m_strDescription; m_strLinkKey = dlg.m_strLinkKey; UpdateItemName(); // syncronize names as appropriate m_shape = (EShape)dlg.m_shape; return TRUE; } return FALSE; } //////////////////////////////////////////////////////////////// // Creating child nodes // Special constructor for child node CServerNode* CServerNode::CreateChildNode(LPCTSTR lpszDescription) { CServerNode* pNew = NULL; TRY { pNew = new CServerNode(GetDocument()); pNew->m_strDescription = lpszDescription; // both item name and description start out the same // add as the last child m_listChild.AddTail(pNew); } CATCH (CException, e) { delete pNew; pNew = NULL; } END_CATCH return pNew; } CServerNode* CServerNode::PromptNewChildNode() { CAddNodeDlg dlg; dlg.m_shape = (EShape)shapeRect; if (dlg.DoModal() != IDOK) return NULL; CServerNode* pNew = CreateChildNode(dlg.m_strDescription); if (pNew == NULL) AfxThrowMemoryException(); pNew->m_shape = (EShape)dlg.m_shape; pNew->m_strLinkKey = dlg.m_strLinkKey; pNew->UpdateItemName(); return pNew; } CServerNode* CServerNode::GetNext(CServerNode* pNode, BOOL bInit) { static BOOL bFound; if (bInit) bFound = FALSE; if (pNode == this) bFound = TRUE; if (!m_bHideChildren) { POSITION pos = m_listChild.GetHeadPosition(); while (pos != NULL) { CServerNode* pChild = (CServerNode*)m_listChild.GetNext(pos); if (bFound) return pChild; pChild = pChild->GetNext(pNode, FALSE); if (pChild != NULL) return pChild; } } // if reached top and last level return original if (bInit) return pNode; else return NULL; } CServerNode* CServerNode::GetPrev(CServerNode* pNode,BOOL bInit) { static CServerNode* pPrev; if (bInit) pPrev = this; if (pNode == this) return pPrev; pPrev = this; if (!m_bHideChildren) { POSITION pos = m_listChild.GetHeadPosition(); while (pos != NULL) { CServerNode* pCur = (CServerNode*)m_listChild.GetNext(pos); CServerNode* pChild = pCur->GetPrev(pNode, FALSE); if (pChild != NULL) return pChild; } } if (bInit) return pPrev; else return NULL; } ///////////////////////////////////////////////////////////////////////////// // CChangeNameDlg dialog CChangeNameDlg::CChangeNameDlg(CWnd* pParent) : CDialog(CChangeNameDlg::IDD, pParent) { //{{AFX_DATA_INIT(CChangeNameDlg) //}}AFX_DATA_INIT } BOOL CChangeNameDlg::OnInitDialog() { CDialog::OnInitDialog(); OnChange(); // after DDX return TRUE; } void CChangeNameDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CChangeNameDlg) DDX_CBIndex(pDX, IDC_COMBO1, m_shape); DDX_Text(pDX, IDC_EDIT1, m_strDescription); DDX_Text(pDX, IDC_EDIT2, m_strLinkKey); DDX_Control(pDX, IDC_EDIT1, m_editDescription); DDX_Control(pDX, IDOK, m_btnOK); //}}AFX_DATA_MAP } BEGIN_MESSAGE_MAP(CChangeNameDlg, CDialog) //{{AFX_MSG_MAP(CChangeNameDlg) ON_EN_CHANGE(IDC_EDIT1, OnChange) //}}AFX_MSG_MAP END_MESSAGE_MAP() void CChangeNameDlg::OnChange() { if (m_btnOK.m_hWnd) m_btnOK.EnableWindow(m_editDescription.GetWindowTextLength() > 0); } ///////////////////////////////////////////////////////////////////////////// // CAddNodeDlg dialog CAddNodeDlg::CAddNodeDlg(CWnd* pParent /*=NULL*/) : CDialog(CAddNodeDlg::IDD, pParent) { //{{AFX_DATA_INIT(CAddNodeDlg) //}}AFX_DATA_INIT } BOOL CAddNodeDlg::OnInitDialog() { CDialog::OnInitDialog(); OnChange(); // after DDX return TRUE; } void CAddNodeDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CAddNodeDlg) DDX_Text(pDX, IDC_EDIT1, m_strDescription); DDX_Text(pDX, IDC_EDIT2, m_strLinkKey); DDX_CBIndex(pDX, IDC_COMBO1, m_shape); DDX_Control(pDX, IDC_EDIT1, m_editDescription); DDX_Control(pDX, IDOK, m_btnOK); //}}AFX_DATA_MAP } BEGIN_MESSAGE_MAP(CAddNodeDlg, CDialog) //{{AFX_MSG_MAP(CAddNodeDlg) ON_EN_CHANGE(IDC_EDIT1, OnChange) //}}AFX_MSG_MAP END_MESSAGE_MAP() void CAddNodeDlg::OnChange() { if (m_btnOK.m_hWnd) m_btnOK.EnableWindow(m_editDescription.GetWindowTextLength() > 0); } ////////////////////////////////////////////////////////////////////////////// // CServerItem construction CServerItem::CServerItem(CServerDoc* pServerDoc, CServerNode* pServerNode) : COleServerItem(pServerDoc, TRUE) { // need to attach this server item to the node in the document ASSERT_VALID(pServerNode); m_pServerNode = pServerNode; pServerNode->m_pServerItem = this; pServerNode->UpdateItemName(); // support CF_TEXT format GetDataSource()->DelayRenderFileData(CF_TEXT); } CServerItem::~CServerItem() { if (m_pServerNode != NULL) { m_pServerNode->m_pServerItem = NULL; m_pServerNode->m_strLinkKey = GetItemName(); m_pServerNode->UpdateItemName(); } } ////////////////////////////////////////////////////////////////////////////// // CServerItem verb handling void CServerItem::OnOpen() { if (IsLinkedItem() && m_pServerNode != NULL) { // for linked items, try to select this node in the document CServerDoc* pDoc = GetDocument(); ASSERT(pDoc != NULL); POSITION pos = pDoc->GetFirstViewPosition(); ASSERT(pos != NULL); CServerView* pView = (CServerView*)pDoc->GetNextView(pos); ASSERT(pView->IsKindOf(RUNTIME_CLASS(CServerView))); pView->SetSelection(m_pServerNode); pView->ScrollToItem(m_pServerNode, TRUE); } COleServerItem::OnOpen(); } ///////////////////////////////////////////////////////////////////////////// // CServerItem drawing BOOL CServerItem::OnGetExtent(DVASPECT dwDrawAspect, CSize& rSize) { if (dwDrawAspect != DVASPECT_CONTENT) return COleServerItem::OnGetExtent(dwDrawAspect, rSize); // determine extent based on screen dc rSize = CSize(0, 0); if (m_pServerNode != NULL) { CClientDC dc(NULL); dc.SetMapMode(MM_ANISOTROPIC); CServerDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); CPoint ptStart(CX_MARGIN, CY_MARGIN); pDoc->CalcBounding(&dc, m_pServerNode, ptStart, rSize); rSize.cx += CX_MARGIN; dc.LPtoHIMETRIC(&rSize); // convert pixels to HIMETRIC } return TRUE; } BOOL CServerItem::OnDraw(CDC* pDC, CSize& rSize) { if (m_pServerNode != NULL) { // determine extent of this item in the document CServerDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); rSize = CSize(0, 0); CPoint ptStart(CX_MARGIN, CY_MARGIN); pDoc->CalcBounding(pDC, m_pServerNode, ptStart, rSize); rSize.cx += CX_MARGIN; // prepare to draw and remember the extent in himetric units pDC->SetWindowOrg(0, 0); pDC->SetWindowExt(rSize); pDC->SetViewportExt(rSize); // Note: only affects the m_hAttribDC pDC->LPtoHIMETRIC(&rSize); // convert pixels to HIMETRIC // CServerDoc::DrawTree selects and releases font automatically pDoc->DrawTree(pDC, ptStart, NULL, m_pServerNode); } return TRUE; } ///////////////////////////////////////////////////////////////////////////// // CServerItem clipboard handling void CServerItem::Serialize(CArchive& ar) { // serialize font info from document (so it looks like a document) CServerDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); pDoc->SerializeFontInfo(ar); // serialize node info ASSERT_VALID(m_pServerNode); m_pServerNode->Serialize(ar); } BOOL CServerItem::OnRenderFileData(LPFORMATETC lpFormatEtc, CFile* pFile) { ASSERT(lpFormatEtc != NULL); if (lpFormatEtc->cfFormat != CF_TEXT) return COleServerItem::OnRenderFileData(lpFormatEtc, pFile); BOOL bResult = FALSE; if (m_pServerNode != NULL) { TRY { // save as text CArchive ar(pFile, CArchive::store); m_pServerNode->SaveAsText(ar, 0); ar << (BYTE)'\0'; // terminate with NUL character bResult = TRUE; } END_TRY } return bResult; } // OnGetClipboardData is used by CopyToClipboard and DoDragDrop COleDataSource* CServerItem::OnGetClipboardData(BOOL bIncludeLink, LPPOINT pptOffset, LPSIZE pSize) { ASSERT_VALID(this); if (m_pServerNode == NULL) return NULL; COleDataSource* pDataSource = new COleDataSource; TRY { GetNativeClipboardData(pDataSource); GetClipboardData(pDataSource, bIncludeLink, pptOffset, pSize); } CATCH_ALL(e) { delete pDataSource; THROW_LAST(); } END_CATCH_ALL ASSERT_VALID(pDataSource); return pDataSource; } void CServerItem::GetNativeClipboardData(COleDataSource *pDataSource) { ASSERT_VALID(this); ASSERT(CServerDoc::m_cfPrivate != NULL); // Create a shared file and associate a CArchive with it CSharedFile file; CArchive ar(&file,CArchive::store); // Serialize selected objects to the archive m_pServerNode->Serialize(ar); ar.Close(); // put on local format instead of or in addation to pDataSource->CacheGlobalData(CServerDoc::m_cfPrivate,file.Detach()); } /////////////////////////////////////////////////////////////////////////////