// ========================================================================== // Class Implementation : COXChildFrameState // ========================================================================== // Source file : OXChildFrameState.cpp // Version: 9.3 // This software along with its related components, documentation and files ("The Libraries") // is © 1994-2007 The Code Project (1612916 Ontario Limited) and use of The Libraries is // governed by a software license agreement ("Agreement"). Copies of the Agreement are // available at The Code Project (www.codeproject.com), as part of the package you downloaded // to obtain this file, or directly from our office. For a copy of the license governing // this software, you may contact us at legalaffairs@codeproject.com, or by calling 416-849-8900. // ////////////////////////////////////////////////////////////////////////// #include "stdafx.h" #if (_MFC_VER < 0x0420) // This file uses classes that were introduced in MFC Version 4.2 // These classes are now officially documented by Microsoft, but did not exist in previous versions // Therefore this file will be completely excluded for older versions of MFC #pragma message("Warning : OXChildFrameState.cpp not included because MFC Version < 4.2") #else // The entire file #include "OXChildFrameState.h" #include "OXSplitterColRowState.h" #include "OXDocTemplateSpy.h" #ifdef _DEBUG #undef THIS_FILE static char BASED_CODE THIS_FILE[] = __FILE__; #endif const int COXChildFrameState::m_nSerializeSchemaVersion = 1; IMPLEMENT_SERIAL(COXChildFrameState, CObject, COXChildFrameState::m_nSerializeSchemaVersion | VERSIONABLE_SCHEMA) #define new DEBUG_NEW ///////////////////////////////////////////////////////////////////////////// // Definition of static members // Data members ------------------------------------------------------------- // protected: // WINDOWPLACEMENT m_framePlacement; // --- The placement of the frame window // CString m_sDocPath; // --- The full path name of the file associated with the document // CString m_sDocClassName; // CString m_sFrameClassName; // CString m_sViewClassName; // --- Name of the class of the document, frame window, view // The template containing this classes will be used to // reconstruct the previous state // BOOL m_bSaveSplitterPanes; // ---- Whether the properties of the splitter panes should be saved and restored // CObArray* m_pSplitterPanes; // --- The collection of the properties of the splitter panes // int m_nSerializeSchemaVersionLoad; // --- The schema version number that is read from archive // private: // Member functions --------------------------------------------------------- // public: COXChildFrameState::COXChildFrameState() : m_pSplitterPanes(NULL) { Initialize(); ASSERT_VALID(this); } BOOL COXChildFrameState::IsSplitterPaneIncluded() const { ASSERT_VALID(this); return m_bSaveSplitterPanes; } void COXChildFrameState::IncludeSplitterPane(BOOL bInclude /* = TRUE */) { ASSERT_VALID(this); m_bSaveSplitterPanes = bInclude; ASSERT_VALID(this); } BOOL COXChildFrameState::ComputeProperties(CFrameWnd* pFrameWnd) { ASSERT_VALID(this); // Get the position of the frame window ::ZeroMemory(&m_framePlacement, sizeof(WINDOWPLACEMENT)); m_framePlacement.length = sizeof(WINDOWPLACEMENT); if (pFrameWnd != NULL) { VERIFY(pFrameWnd->GetWindowPlacement(&m_framePlacement)); if(!pFrameWnd->IsWindowVisible()) { m_framePlacement.showCmd=SW_HIDE; } } // Get the document and view of this frame CDocument* pDoc = NULL; CView* pView = NULL; if (!GetDocView(pFrameWnd, pDoc, pView)) { TRACE0("COXChildFrameState::ComputeProperties : Failed to get the doc-view for this frame, failing\n"); return FALSE; } // Get the file path of the document if (pDoc != NULL) m_sDocPath = pDoc->GetPathName(); // Store the name of the classes of doc, frame and view if (pDoc != NULL) m_sDocClassName = pDoc->GetRuntimeClass()->m_lpszClassName; if (pFrameWnd != NULL) m_sFrameClassName = pFrameWnd->GetRuntimeClass()->m_lpszClassName; if (pView != NULL) m_sViewClassName = pView->GetRuntimeClass()->m_lpszClassName; EmptySplitterPanes(m_pSplitterPanes); if (m_bSaveSplitterPanes) ComputeSplitterPanes(pFrameWnd); ASSERT_VALID(this); return TRUE; } BOOL COXChildFrameState::ApplyProperties() const { ASSERT_VALID(this); CDocTemplate* pDocTemplate = NULL; CDocument* pDoc = NULL; CView* pView = NULL; CFrameWnd* pFrameWnd = NULL; // Create a new document with the info we have pDocTemplate = GetDocTemplate(m_sDocClassName, m_sFrameClassName, m_sViewClassName); if (pDocTemplate == NULL) { TRACE0("COXChildFrameState::ApplyProperties : Could not find the necessary doc template\n"); // Did you change the class name of the document ? // Did you remove the doc template from your program ? return FALSE; } // Open the document LPCTSTR pszDocPath = m_sDocPath; if (m_sDocPath.IsEmpty()) pszDocPath = NULL; if (!OpenDocument(pDocTemplate, pszDocPath, pDoc, pFrameWnd, pView)) { TRACE0("COXChildFrameState::ApplyProperties : Failed to open document, failing\n"); return FALSE; } // Restore the position of the frame window and show it ASSERT(pFrameWnd != NULL); VERIFY(pFrameWnd->SetWindowPlacement(&m_framePlacement)); if (m_bSaveSplitterPanes) ApplySplitterPanes(pFrameWnd); ASSERT_VALID(this); return TRUE; } void COXChildFrameState::Serialize(CArchive& ar) { ASSERT_VALID(this); // Check the version // (If version == -1, the version is unknown, this occurs when Serialize() is called directly) if (ar.IsLoading()) { m_nSerializeSchemaVersionLoad = (int)ar.GetObjectSchema(); if (m_nSerializeSchemaVersion < m_nSerializeSchemaVersionLoad) { TRACE1("COXChildFrameState::Serialize : Unexpected schema version : %i, throwing CArchiveException\n", m_nSerializeSchemaVersionLoad); AfxThrowArchiveException(CArchiveException::badSchema); } } // Call base class implementation CObject::Serialize(ar); // Serialize all data if (ar.IsStoring()) StoreProperties(ar); else LoadProperties(ar); ASSERT_VALID(this); } #ifdef _DEBUG void COXChildFrameState::AssertValid() const { CObject::AssertValid(); // The pointer object must always be valid and may never be NULL ASSERT_VALID(m_pSplitterPanes); } void COXChildFrameState::Dump(CDumpContext& dc) const { CObject::Dump(dc); } #endif //_DEBUG COXChildFrameState::~COXChildFrameState() { ASSERT_VALID(this); // Empty all lists EmptySplitterPanes(m_pSplitterPanes); // Delete dynamically created members delete m_pSplitterPanes; m_pSplitterPanes = NULL; } // protected: void COXChildFrameState::Initialize() // --- In : // --- Out : // --- Returns : // --- Effect : Initialized the data members of this object { ::ZeroMemory(&m_framePlacement, sizeof(WINDOWPLACEMENT)); m_bSaveSplitterPanes = TRUE; if (m_pSplitterPanes == NULL) m_pSplitterPanes = new CObArray(); EmptySplitterPanes(m_pSplitterPanes); m_nSerializeSchemaVersionLoad = -1; ASSERT_VALID(this); } BOOL COXChildFrameState::ComputeSplitterPanes(CFrameWnd* pFrameWnd) // --- In : // --- Out : // --- Returns : Whether it succeeded or not // --- Effect : Computes all the states of the splitter panes and adds them // to an internal collection { ASSERT_VALID(this); // Check for a CSplitterWnd child window CSplitterWnd* pSplitterWnd = GetSplitterWindow(pFrameWnd); if (pSplitterWnd == NULL) // ... No splitter window found : nothing to do return TRUE; BOOL bAllSucceeded = TRUE; int nRowColIndex = 0; // Calculate the row properties for (nRowColIndex = 0; nRowColIndex < pSplitterWnd->GetRowCount(); nRowColIndex++) { COXSplitterColRowState* pSplitterColRowState = new COXSplitterColRowState; if (pSplitterColRowState->ComputeProperties(pSplitterWnd, nRowColIndex, TRUE)) m_pSplitterPanes->Add(pSplitterColRowState); else { TRACE0("COXChildFrameState::ComputeSplitterPanes : Failed to compute properties (row)\n"); bAllSucceeded = FALSE; delete pSplitterColRowState; } } // Calculate the column properties for (nRowColIndex = 0; nRowColIndex < pSplitterWnd->GetColumnCount(); nRowColIndex++) { COXSplitterColRowState* pSplitterColRowState = new COXSplitterColRowState; if (pSplitterColRowState->ComputeProperties(pSplitterWnd, nRowColIndex, FALSE)) m_pSplitterPanes->Add(pSplitterColRowState); else { TRACE0("COXChildFrameState::ComputeSplitterPanes : Failed to compute properties (column)\n"); bAllSucceeded = FALSE; delete pSplitterColRowState; } } ASSERT_VALID(this); return bAllSucceeded; } BOOL COXChildFrameState::ApplySplitterPanes(CFrameWnd* pFrameWnd) const // --- In : // --- Out : // --- Returns : Whether it succeeded or not // --- Effect : Applies all the states of the splitter panes that are in the // internal collection { ASSERT_VALID(this); // Check for a CSplitterWnd child window CSplitterWnd* pSplitterWnd = GetSplitterWindow(pFrameWnd); if (pSplitterWnd == NULL) // ... No splitter window found : nothing to do return TRUE; BOOL bAllSucceeded = TRUE; int nSplitterColRowStateIndex = 0; COXSplitterColRowState* pSplitterColRowState = NULL; for(nSplitterColRowStateIndex = 0; nSplitterColRowStateIndex < m_pSplitterPanes->GetSize(); nSplitterColRowStateIndex++) { pSplitterColRowState = DYNAMIC_DOWNCAST(COXSplitterColRowState, m_pSplitterPanes->GetAt(nSplitterColRowStateIndex)); if (pSplitterColRowState != NULL) { if (!pSplitterColRowState->ApplyProperties(pSplitterWnd)) { TRACE0("COXChildFrameState::ApplySplitterPanes : Failed to apply properties\n"); bAllSucceeded = FALSE; } } } // Make the changes visible pSplitterWnd->RecalcLayout(); ASSERT_VALID(this); return bAllSucceeded; } void COXChildFrameState::StoreProperties(CArchive& ar) // --- In : ar : Archive used in serialization // --- Out : // --- Returns : // --- Effect : Stores the properties of this object to archive // This action may throw an exception on failure { ASSERT_VALID(this); ASSERT(ar.IsStoring()); // Some sanity checks ASSERT((m_bSaveSplitterPanes == FALSE) || (m_bSaveSplitterPanes == TRUE)); // ... Frame position ar << m_framePlacement.flags; ar << m_framePlacement.showCmd; ar << m_framePlacement.ptMinPosition; ar << m_framePlacement.ptMaxPosition; ar << m_framePlacement.rcNormalPosition; // ... Doc file path ar << m_sDocPath; // ... Class names ar << m_sDocClassName; ar << m_sFrameClassName; ar << m_sViewClassName; // ... Splitter panes ar << m_bSaveSplitterPanes; ar << m_pSplitterPanes; ASSERT_VALID(this); } void COXChildFrameState::LoadProperties(CArchive& ar) // --- In : ar : Archive used in serialization // --- Out : // --- Returns : // --- Effect : loads the properties of this object from archive // This action may throw an exception on failure { ASSERT_VALID(this); ASSERT(ar.IsLoading()); // ... Frame position ::ZeroMemory(&m_framePlacement, sizeof(WINDOWPLACEMENT)); m_framePlacement.length = sizeof(WINDOWPLACEMENT); ar >> m_framePlacement.flags; ar >> m_framePlacement.showCmd; ar >> m_framePlacement.ptMinPosition; ar >> m_framePlacement.ptMaxPosition; ar >> m_framePlacement.rcNormalPosition; // ... Doc file path ar >> m_sDocPath; // ... Class names ar >> m_sDocClassName; ar >> m_sFrameClassName; ar >> m_sViewClassName; // ... Splitter panes ar >> m_bSaveSplitterPanes; CObArray* pOldSplitterPanes= NULL; pOldSplitterPanes = m_pSplitterPanes; ar >> m_pSplitterPanes; EmptySplitterPanes(pOldSplitterPanes); delete pOldSplitterPanes; // Some sanity checks ASSERT((m_bSaveSplitterPanes == FALSE) || (m_bSaveSplitterPanes == TRUE)); ASSERT_VALID(this); } void COXChildFrameState::EmptySplitterPanes(CObArray* pSplitterPanes) // --- In : pSplitterPanes: Collection to use // --- Out : // --- Returns : // --- Effect : Clears the collection by deleting all its members and // clearing the collection itself { ASSERT_VALID(pSplitterPanes); int nSplitterColRowStateIndex = 0; CObject* pSplitterColRowState = NULL; for (nSplitterColRowStateIndex = 0; nSplitterColRowStateIndex < pSplitterPanes->GetSize(); nSplitterColRowStateIndex++) { pSplitterColRowState = pSplitterPanes->GetAt(nSplitterColRowStateIndex); delete pSplitterColRowState; pSplitterPanes->SetAt(nSplitterColRowStateIndex, NULL); } pSplitterPanes->RemoveAll(); } CView* COXChildFrameState::GetFirstView(CDocument* pDoc) // --- In : pDoc : A valid CDocument object // --- Out : // --- Returns : The first view attached to the specified document // --- Effect : { ASSERT(pDoc != NULL); CView * pView = NULL; POSITION viewPos = NULL; // Get the first view in the collection viewPos = pDoc->GetFirstViewPosition(); if (viewPos != NULL) { pView = pDoc->GetNextView(viewPos); ASSERT(pView != NULL); } #ifdef _DEBUG if (pView == NULL) TRACE0("COXChildFrameState::GetFirstView : No view found\n"); #endif // _DEBUG return pView; } BOOL COXChildFrameState::OpenDocument(CDocTemplate* pDocTemplate, LPCTSTR pszDocPath, CDocument*& pDoc, CFrameWnd*& pFrameWnd, CView*& pView) // --- In : pDocTemplate : The document template to use // pszDocPath : The file path of the document to open // --- Out : pDoc : The document that is opened // pFrameWnd : The associated MDI child frame window // pView : The associated view (the first one) // --- Returns : Whether it succeeded or not // --- Effect : Uses the specified doc template the open a document with the // spcified file path { ASSERT(pDocTemplate != NULL); // ... Initialize output parameters pDoc = NULL; pFrameWnd = NULL; pView = NULL; // ... Check whether the document has already been opened pDoc = SearchDocument(pszDocPath); if (pDoc != NULL) { // Document has already been opened, attach a new view to it pFrameWnd = pDocTemplate->CreateNewFrame(pDoc, NULL); if (pFrameWnd == NULL) { TRACE0("COXChildFrameState::OpenDocument : Failed to create new frame for existing document.\n"); pDoc = NULL; return FALSE; } // ... Frame is initially visible, this is necessary to send // WM_INITIALUPDATE to the view pDocTemplate->InitialUpdateFrame(pFrameWnd, pDoc, TRUE); pView = GetFirstView(pFrameWnd); ASSERT(pView != NULL); } else { // Document has not yet been opened, open it now // ... Frame is initially visible, this is necessary to send // WM_INITIALUPDATE to the view pDoc = pDocTemplate->OpenDocumentFile(pszDocPath, TRUE); if (pDoc == NULL) { TRACE0("COXChildFrameState::OpenDocument : Failed to open document, failing\n"); return FALSE; } pView = GetFirstView(pDoc); ASSERT(pView != NULL); pFrameWnd = pView->GetParentFrame(); ASSERT(pFrameWnd != NULL); } return TRUE; } CDocument* COXChildFrameState::SearchDocument(LPCTSTR pszDocPath) // --- In : pszDocPath : File path of a document // --- Out : // --- Returns : The open document with that file path or NULL otherwise // --- Effect : { if (pszDocPath == NULL) // ... Can only search for documents with a non-empty name return NULL; CDocument* pDoc = NULL; POSITION templatePos = NULL; CDocTemplate* pDocTemplate = NULL; templatePos = AfxGetApp()->GetFirstDocTemplatePosition(); while((pDoc == NULL) && (templatePos != NULL)) { pDocTemplate = AfxGetApp()->GetNextDocTemplate(templatePos); ASSERT(pDocTemplate != NULL); if (pDocTemplate->MatchDocType(pszDocPath, pDoc) != CDocTemplate::yesAlreadyOpen) pDoc = NULL; } return pDoc; } CView* COXChildFrameState::GetFirstView(CFrameWnd* pFrameWnd) // --- In : pFrameWnd : The frame window to use // --- Out : // --- Returns : The first view that is child of the specified MDI child frame window // --- Effect : { CView* pView = NULL; CWnd* pWnd = pFrameWnd->GetDescendantWindow(AFX_IDW_PANE_FIRST, TRUE); pView = DYNAMIC_DOWNCAST(CView, pWnd); if (pView == NULL) { pView = pFrameWnd->GetActiveView(); } return pView; } CSplitterWnd* COXChildFrameState::GetSplitterWindow(CFrameWnd* pFrameWnd) // --- In : pFrameWnd : The MDI child frame to use // --- Out : // --- Returns : The splitter window of the specified frame or NULL otherwise // --- Effect : When spliiter windows are nested, only the top level window is used { if (pFrameWnd == NULL) return NULL; // Search all children for a CSplitterWnd derived object HWND hChildWnd = NULL; CWnd* pChildWnd = NULL; CSplitterWnd* pSplitterWnd = NULL; hChildWnd = ::GetWindow(pFrameWnd->GetSafeHwnd(), GW_CHILD); pChildWnd = CWnd::FromHandlePermanent(hChildWnd); pSplitterWnd = DYNAMIC_DOWNCAST(CSplitterWnd, pChildWnd); while ((hChildWnd != NULL) && (pSplitterWnd == NULL)) { hChildWnd = ::GetWindow(hChildWnd, GW_HWNDNEXT); pChildWnd = CWnd::FromHandlePermanent(hChildWnd); pSplitterWnd = DYNAMIC_DOWNCAST(CSplitterWnd, pChildWnd); } return pSplitterWnd; } CDocTemplate* COXChildFrameState::GetDocTemplate(LPCTSTR pszDocName, LPCTSTR pszFrameWndName, LPCTSTR pszViewName) // --- In : pszDocName : Class name of the document // pszFrameWndName : Class name of the frame window // pszViewName : Class name of the view // --- Out : // --- Returns : The document template with the specified runtime classes for // document (required), frame (if found) and view (if found) // --- Effect : When no template is found that matches all three names, // one is searched that matches at least the docuemnt name // When none is found NULL is returned { // Iterate all the templates until we find one that matches the requested // doc - frame - view combination CDocTemplate* pDocTemplate = NULL; // ... The requested doc template if found CDocTemplate* pRequestedDocTemplate = NULL; // ... A doc template with the same doc class name CDocTemplate* pSimilarDocTemplate = NULL; COXDocTemplateSpy* pDocTemplateSpy = NULL; POSITION templatePos = NULL; templatePos = AfxGetApp()->GetFirstDocTemplatePosition(); while((pRequestedDocTemplate == NULL) && (templatePos != NULL)) { pDocTemplate = AfxGetApp()->GetNextDocTemplate(templatePos); ASSERT(pDocTemplate != NULL); // ... We cast the template to a COXDocTemplateSpy object to be able to // check the runtime classes pDocTemplateSpy = (COXDocTemplateSpy*)pDocTemplate; if (pDocTemplateSpy->CheckMatch(pszDocName, pszFrameWndName, pszViewName)) { pRequestedDocTemplate = pDocTemplate; } else if ((pSimilarDocTemplate == NULL) && pDocTemplateSpy->CheckMatch(pszDocName)) { pSimilarDocTemplate = pDocTemplate; } } if (pRequestedDocTemplate != NULL) return pRequestedDocTemplate; else { #ifdef _DEBUG if (pSimilarDocTemplate != NULL) TRACE0("COXChildFrameState::GetTemplate : Requested template not found, using other template with the same document class\n"); else TRACE0("COXChildFrameState::GetTemplate : No template found with the requested document class\n"); #endif // _DEBUG return pSimilarDocTemplate; } } BOOL COXChildFrameState::GetDocView(CFrameWnd* pFrameWnd, CDocument*& pDoc, CView*& pView) // --- In : pFrameWnd : The frame window to use // --- Out : pDoc : The document attached to this frame window // pView : The first view attached to this document // --- Returns : // --- Effect : { // Initialize return values pDoc = NULL; pView = NULL; // Check input parameter if (pFrameWnd == NULL) return FALSE; // First get the view (first pane or active) pView = GetFirstView(pFrameWnd); // Then get the document if (pView != NULL) pDoc = pView->GetDocument(); return ((pView != NULL) && (pDoc != NULL)); } // ========================================================================== void AFXAPI SerializeElements(CArchive& ar, COXChildFrameState** pChildFrameState, int nCount) { ASSERT(AfxIsValidAddress(pChildFrameState, sizeof(COXChildFrameState*) * nCount, ar.IsStoring())); if (ar.IsStoring()) { for (int i = 0; i < nCount; i++) { ASSERT_VALID(pChildFrameState[i]); ar << pChildFrameState[i]; } } else { for (int i = 0; i < nCount; i++) { ar >> pChildFrameState[i]; ASSERT_VALID(pChildFrameState[i]); } } } #endif // _MFC_VER // ==========================================================================