// /// Destruct this DocManager. Close all open documents (views close with them), /// and delete all non-static doc templates /// /// Destroys a TDocManager object removes attached documents templates. The /// constructor resets TDocTemplate::DocTemplateStaticHead to point to the head of /// the static template list. // TDocManager::~TDocManager() { // Iterate through document list, closing and deleting each // TDocument* doc; while ((doc = DocList.Next(0)) != 0) { if (doc->IsOpen()) doc->Close(); // NOTE: deleting the document deletes all attached views, and unlinks // the document from the docmanager's document list // delete doc; // Flush (dispatch) any pending MDI-Child-destroy messages // GetApplication()->PumpWaitingMessages(); } // Reset the 'Docmanager' pointer of static templates and delete // dynamic ones... // while (TemplateList) { TDocTemplate* tpl = TemplateList; TemplateList = tpl->GetNextTemplate(); if (tpl->IsStatic()) tpl->SetDocManager(0); else delete tpl; } }
// /// Used only after streaming in the doc manager, EvWakeUp allows for the windows to /// be created after the streaming has occurred. // void TDocManager::EvWakeUp() { TDocument* doc = 0; while ((doc = DocList.Next(doc)) != 0) doc->ReindexFrames(); }
// /// Performs the reverse of Commit() and cancels any changes made to the document /// since the last commit. If clear is true, data is not reloaded for views. Revert /// also checks all child documents and cancels any changes if all children return /// true. When a file is closed, the document manager calls either Commit() or Revert. /// Returns true if the operation is successful. // bool TDocument::Revert(bool clear) { TDocument* pdoc = 0; while ((pdoc = ChildDoc.Next(pdoc)) != 0) { if (!pdoc->Revert(clear)) return false; } return NotifyViews(vnRevert, clear); }
// /// Checks to see if all child documents can be closed before closing the current /// document. If any child returns false, returns false and aborts the process. If /// all children return true, EvCanClose calls TDocManager::FlushDoc for each /// document. If FlushDoc finds that the document is dirty, it displays a message /// asking the user to save the document, discard any changes, or cancel the /// operation. If the document is not dirty and CanClose returns true, EvCanClose /// returns true. // bool TDocManager::EvCanClose() { TDocument* doc = 0; while ((doc = DocList.Next(doc)) != 0) { if (!doc->CanClose()) // normally calls back to FlushDoc() return false; } return true; }
// /// Return pointer to this document or one of its child documents if the spcecified /// window parameter is a view associated with the document. /// \note Unlike 'HasFocus', this method allows you to distinguish whether the /// document with focus is a child document. // TDocument* TDocument::DocWithFocus(HWND hWnd) { TDocument* pdoc = 0; while ((pdoc = ChildDoc.Next(pdoc)) != 0) if (pdoc->DocWithFocus(hWnd)) return pdoc; return QueryViews(vnIsWindow, (long)hWnd) ? this : 0; }
// /// Checks to see if all child documents can be closed before closing the current /// document. If any child returns false, CanClose returns false and aborts the /// process. If all children return true, calls TDocManager::FlushDoc. If FlushDoc /// finds that the document has been changed but not saved, it displays a message /// asking the user to either save the document, discard any changes, or cancel the /// operation. If the document has not been changed and all children's CanClose /// functions return true, this CanClose function returns true. // bool TDocument::CanClose() { TDocument* pdoc = 0; while ((pdoc = ChildDoc.Next(pdoc)) != 0) if (!pdoc->CanClose()) return false; return DocManager->FlushDoc(*this); // do the UI in the doc manager }
// /// Closes the document but does not delete or detach the document. Before closing /// the document, Close checks any child documents and tries to close them before /// closing the parent document. Even if you write your own Close function, call /// TDocument's version to ensure that all child documents are checked before the /// parent document is closed. // bool TDocument::Close() { TDocument* pdoc = 0; while ((pdoc = ChildDoc.Next(pdoc)) != 0) if (!pdoc->Close()) return false; return true; }
void TDocManager::FileRevert() { TDocument* doc = GetCurrentDoc(); if (doc && doc->GetDocPath()) { if (!doc->IsDirty()) { PostDocError(*doc, IDS_NOTCHANGED); return; } doc->Revert(); } }
// /// If the document can be closed it is closed. // void TDocManager::FileClose() { TDocument* doc = GetCurrentDoc(); if (doc && doc->CanClose()) { // normally calls back to FlushDoc() if (!doc->Close()) PostDocError(*doc, IDS_UNABLECLOSE); else delete doc; } WARNX(OwlDocView, !doc, 0, _T("FileClose invoked with no current doc")); }
// /// Saves the current data to storage. When a file is closed, the document manager /// calls either Commit or Revert. If force is true, all data is written to storage. /// Commit checks any child documents and commits their changes to storage also. /// Before the current data is saved, all child documents must return true. If all /// child documents return true, Commit flushes the views for operations that /// occurred since the last time the view was checked. After all data for the /// document is updated and saved, Commit returns true. // bool TDocument::Commit(bool force) { TDocument* pdoc = 0; while ((pdoc = ChildDoc.Next(pdoc)) != 0) { if (!pdoc->Commit(force)) return false; } WARNX(OwlDocView, !DocPath, 0, _T("Commit(): 0 DocPath!")); return NotifyViews(vnCommit, force); }
// /// Returns true if the document or one of its views has changed but has not been /// saved. // bool TDocument::IsDirty() { if (DirtyFlag) return true; TDocument* pdoc = 0; while ((pdoc = ChildDoc.Next(pdoc)) != 0) if (pdoc->IsDirty()) return true; return QueryViews(vnIsDirty) != 0; }
// /// SelectAnySave() is called to get a template. Then the Commit() function is called /// for the current document. // void TDocManager::FileSaveAs() { TDocument* doc = GetCurrentDoc(); if (doc) { TDocTemplate* tpl = SelectAnySave(*doc, true); if (tpl) { if (tpl != doc->Template) doc->SetTemplate(tpl); // replace existing template if (doc->Commit(true)) // force rewrite to new path PostEvent(dnRename, *doc); // WM_OWLDOCUMENT } } }
// /// Returns a pointer to the TDocument object if the specified document /// is currently opened and manager by the DocManager. Returns 0 otherwise. // TDocument* TDocManager::FindDocument(LPCTSTR path) { TDocument* doc = 0; while ((doc = DocList.Next(doc)) != 0) if (path) { if (doc->GetDocPath() && _tcscmp(doc->GetDocPath(), path) == 0) break; } else { if (doc->GetDocPath() == 0) break; } return doc; }
void TDocManager::FileSave() { TDocument* doc = GetCurrentDoc(); if (doc) { if (!doc->GetDocPath()) { CmFileSaveAs(); return; } if (!(Mode & dmSaveEnable) && !doc->IsDirty()) { PostDocError(*doc, IDS_NOTCHANGED); return; } doc->Commit(); // No force of write here since is just to same file } }
void TDocument::Streamer::Write(opstream& os) const { TDocument* o = GetObject(); while (!o->CanClose()) // can't permit cancel here ; os << o->OpenMode; _USES_CONVERSION; os.fwriteString(_W2A(o->DocPath)); os.fwriteString(_W2A(o->Title)); os << o->Template; // templates already streamed, must be so if static os << o->ParentDoc; os << o->ViewList; // each view streams out the next os << TView::NextViewId; // insure that this static var gets set on reload }
// /// Notifies the views of the current document and the views of any child documents /// of a change. In contrast to QueryViews, NotifyViews sends notification of an /// event to all views and returns true if all views returned a true result. The /// event, EV_OWLNOTIFY, is sent with an event code, which is private to the /// particular document and view class, and a long argument, which can be cast /// appropriately to the actual type passed in the argument of the response /// function. // bool TDocument::NotifyViews(int event, long item, TView* exclude) { bool answer = true; TDocument* pdoc = 0; while ((pdoc = ChildDoc.Next(pdoc)) != 0) answer = (answer && pdoc->NotifyViews(event, item, exclude)); TEventHandler::TEventInfo eventInfo(WM_OWLNOTIFY, event); for (TView* view = ViewList; view != 0; view = view->NextView) if (view != exclude && view->Find(eventInfo)) answer = (answer && (view->Dispatch(eventInfo, 0, item) != 0)); return answer; }
// /// Overrideable method invoked just before the DocumentManager creates a new /// document. The default behaviour is to close and delete the current /// document if we're in SDI mode. // bool TDocManager::CreatingDoc(TDocTemplate* /*tpl*/) { if (Mode & dmSDI) { TDocument* doc = DocList.Next(0); if (doc) { if (!doc->CanClose()) return false; if (!doc->Close()) { PostDocError(*doc, IDS_UNABLECLOSE); return false; } delete doc; } } return true; }
// /// Displays FileSave dialog prompting the user to select a file name for savinng the document. /// Filters out read-only files. // bool TDocManager::SelectSave(TDocument& doc) { TDocTemplate* tpl = doc.GetTemplate(); if (!tpl || !tpl->GetFileFilter()) return false; tchar filepath[_MAX_PATH]; if (doc.GetDocPath()) ::_tcscpy(filepath, doc.GetDocPath()); else filepath[0] = 0; // no initial file path int index = SelectDocPath(&tpl, 1, filepath, COUNTOF(filepath), 0, true, &doc); return index ? doc.SetDocPath(filepath) : false; }
// /// Queries the views of the current document and the views of any child documents /// about a specified event, but stops at the first view that returns true. In /// contrast to NotifyViews(), QueryViews returns a pointer to the first view that /// responded to an event with a true result. The event, EV_OWLNOTIFY, is sent with /// an event code (which is private to the particular document and view class) and a /// long argument (which can be cast appropriately to the actual type passed in the /// argument of the response function). // TView* TDocument::QueryViews(int event, long item, TView* exclude) { TView* view; TDocument* pdoc = 0; while ((pdoc = ChildDoc.Next(pdoc)) != 0) if ((view = pdoc->QueryViews(event, item, exclude)) != 0) return view; TEventHandler::TEventInfo eventInfo(WM_OWLNOTIFY, event); for (view = ViewList; view != 0; view = view->NextView) { if (view != exclude) { if (view->Find(eventInfo)) { if (view->Dispatch(eventInfo, 0, item)) { return view; // Return first acknowledger } } } } return 0; }
// /// CreateDoc creates a document based on the directory path and the specified /// template. The flags parameter contains one of the document template constants /// that determines how the document is created. // TDocument* TDocManager::CreateDoc(TDocTemplate* tpl, LPCTSTR path, TDocument* parent, long flags) { if (!tpl) { TRACEX(OwlDocView, 0, _T("CreateDoc(): NULL template specified!")); return 0; } // Creation step 0: Inform docmanager that we're about to create a document // and allow docmanager to veto // if (!CreatingDoc(tpl)) { TRACEX(OwlDocView, 1, _T("CreateDoc(): Creation vetoed.")); return 0; } // Creation step 1: Construct the document, passing in the parent document // Put together a dummy parent document if no parent doc was specified in // order to allow us to pass in the DocManager pointer hidden in the parent // doc // TDocument* doc; if (!parent){ TDocument td(this); doc = tpl->ConstructDoc(&td); } else doc = tpl->ConstructDoc(parent); if (!doc) { TRACEX(OwlDocView, 0, _T("CreateDoc(): ConstructDoc call failed")); return 0; } // Creation step2: Initialize the document // doc->SetTemplate(tpl); return InitDoc(doc, path, flags); }
// /// Calls TWindow::GetFocus() to determine the window with the focus. Searches the /// list of documents and returns the document that contains the view with the /// focus. Returns 0 if no document has a view with focus. // TDocument* TDocManager::GetCurrentDoc() { PRECONDITION(GetApplication()); PRECONDITION(GetApplication()->GetMainWindow()); HWND hWnd = GetApplication()->GetMainWindow()->GetCommandTarget(); TDocument* doc = 0; #if defined(OLD_DOCVIEW) // !BB This older implementation of GetCurrentDoc relies on the // !BB document's HasFocus method which does not allow 'GetCurrentDoc' // !BB to return child documents.... // !BB // !BB This obviously causes some problems (for example, closing a view // !BB associated with a child document closes the whole document and // !BB all their associated views!). // !BB // !BB However is there code that relies on this behaviour - Investigate // !BB if (hWnd && ::IsWindow(hWnd)) { while ((doc = DocList.Next(doc)) != 0 && !doc->HasFocus(hWnd)) ; } #else if (hWnd && ::IsWindow(hWnd)) { while ((doc = DocList.Next(doc)) != 0 ) { TDocument* childDoc = doc->DocWithFocus(hWnd); if (childDoc) { doc = childDoc; break; } } } #endif return doc; }
int TDocManager::GetViewTemplates(TDocTemplate** tplList, int size, TDocument& doc) #endif { // Check for no registered templates // if (!TemplateList) { TRACEX(OwlDocView, 0, _T("GetViewTemplates(): No registered templates!")); return 0; } // Grab a list of templates for creating views // int tplCount = 0; for (TDocTemplate* tpl = TemplateList; tpl; tpl = tpl->GetNextTemplate()) { if (tpl->IsMyKindOfDoc(doc)) { // Don't grab the same view more than once // LPCTSTR viewName = tpl->GetViewName(); int index; for (index = 0; index < tplCount; index++) { if (tplList[index]->GetViewName() == viewName) break; } // Skip a view if the document already has one and the template // specifies 'SingleView'. // if (tpl->IsFlagSet(dtSingleView)) { TView* pview = 0; while ((pview = doc.NextView(pview)) != 0) if (tpl->IsMyKindOfView(*pview)) index = -1; } // Store the template if we have a match... // if (index == tplCount) { CHECK(tplList); CHECK(tplCount < size); tplList[tplCount++] = tpl; } } } return tplCount; }
// /// Displays a message box with the error message passed as a string resource ID in /// sid. By default, the message box contains either an OK push button or a question /// mark icon. If an error message can't be found, PostDocError displays a "Message /// not found" message. choice can be one or more of the MB_Xxxx message style /// constants. This function can be overridden. /// /// Returns an integer /// identifying the MessageBox option (push-button) selected by the user. // uint TDocManager::PostDocError(TDocument& doc, uint sid, uint choice) { PRECONDITION(GetApplication()); PRECONDITION(GetApplication()->GetMainWindow()); tchar buf[256]; if (GetApplication()->LoadString(sid, buf, sizeof(buf) / sizeof(tchar)) == 0) #if BI_MSG_LANGUAGE == 0x0411 _stprintf(buf, "エラー: 文字列 ID %u が見つかりません", sid); #else _stprintf(buf, _T("Error: Message [string ID %u] not found"), sid); #endif if (choice != MB_OK) choice |= MB_ICONQUESTION; return GetApplication()->GetMainWindow()->MessageBox(buf, doc.GetTitle(), choice); }
// /// Given a list of templates, prompts the user to select one of the templates to use for the file to be /// opened. Returns the template index used for the selection, or 0 if unsuccessful. /// For a file open operation, save is false. For a file save operation, save is /// true. This function can be overridden to provide a customized user interface. /// /// \note This is Windows-specific, using the system-provided file open dialog box // int TDocManager::SelectDocPath(TDocTemplate** tpllist, int tplcount, LPTSTR path, int buflen, long flags, bool save, TDocument* doc) { // Compute length of description(s) and filter(s) // int len = GetTemplateDescription(tpllist, tplcount); // Put together a string of description and filters // TAPointer<tchar> filtbuf(new tchar[++len]); GetTemplateDescription(tpllist, tplcount, filtbuf, len); // Find the (default) template to select // int index, count; TDocument* curDoc = save ? (doc ? doc : GetCurrentDoc()) : 0; CHECK(!save || curDoc); if (save) { for (index = count = 0; count < tplcount; count++) { if (tpllist[count] == curDoc->GetTemplate()) { index = count; break; } } } else { for (index = count = 0; count < tplcount; count++) { if (tpllist[count]->IsFlagSet(dtSelected)) { index = count; break; } } } // Initialize data structure used for launching Common Dialog // flags = (tpllist[index]->GetFlags() | flags); flags &= 0x000FFFFF; // Clear Doc/View related flags. flags |= AdditionalFileDialogFlags; // Add extended flags, e.g. OFN_ENABLESIZING. flags &= ~dtProhibited; // Clear unsupported flags, e.g. OFN_ENABLETEMPLATE. TDvOpenSaveData data(flags, // flags filtbuf, // filter CONST_CAST(LPTSTR, tpllist[index]->GetDirectory()), // initDir. CONST_CAST(LPTSTR, tpllist[index]->GetDefaultExt()), // defExt. index ? index+1 : 0, // filterIndex tpllist, // template list tplcount); // template count //--- Sirma (Krasi) { LPCTSTR fName = path && *path ? path : (doc ? doc->GetTitle() : 0); if (fName && *fName) _tcsncpy(data.FileName, fName, buflen); } //--- Sirma (Krasi) --- // Execute dialog // int result; TWindow* parent = GetApplication()->GetMainWindow(); if (save) result = TDvFileSaveDialog(parent, data).Execute(); else result = TDvFileOpenDialog(parent, data).Execute(); // Dialog was cancelled! // if (result != IDOK) { WARNX(OwlDocView, data.Error != 0, 0, _T("Common dialog error: ") << \ data.Error << _T(" in SelectDocPath()") ); return 0; } // !BB // Here, there's a major dilemma! How, do we know the user did not // mislead us? For example, the user may have selected the *wrong* // template for saving a particular document... This is crucial when // saving documents!!! // // Update templates to 'remember' the template last used // for (count = 0; count < tplcount; count++) { if (count == index-1) tpllist[count]->SetFlag(dtSelected); else tpllist[count]->ClearFlag(dtSelected); } // Update selected template with directory // // // !BB Yeah! Where's the meat? // Copy file name over // CHECK(path); if (data.FileName && data.FileName[0]) { WARNX(OwlDocView, ::_tcslen(data.FileName) < (size_t)buflen, 0, _T("SelectDocPath: Specified buffer is too small")); _tcsncpy(path, data.FileName, buflen); } else *path = 0; // Return index [1-based] of selected template // CHECK(data.FilterIndex >= 1); CHECK(data.FilterIndex <= tplcount); return data.FilterIndex; }
// /// Command enabler for CmFileSave. // void TDocManager::CeFileSave(TCommandEnabler& ce) { TDocument* doc = GetCurrentDoc(); ce.Enable(doc && (doc->IsDirty() || (Mode & dmSaveEnable))); }
// /// Command enabler for CmFileRevert. // void TDocManager::CeFileRevert(TCommandEnabler& ce) { TDocument* doc = GetCurrentDoc(); ce.Enable(doc && doc->IsDirty() && doc->GetDocPath()); }
// /// Method invoked when specified document is about to be closed. /// Updates the document with any changes and prompts the user for confirmation of /// updates. /// Returns 'true' if DocManager should proceed with the closing /// stages of the document, or 'false' otherwise. // bool TDocManager::FlushDoc(TDocument& doc) { while (doc.IsDirty()) { int saveOrNot = doc.IsEmbedded() ? IDYES : PostDocError(doc, IDS_DOCCHANGED, MB_YESNOCANCEL); switch (saveOrNot) { case IDYES: // Prompt the user for filename in save-as situation // if (!doc.IsEmbedded() && doc.GetDocPath() == 0) { // !BB // !BB It does not make sense to invoke SelectAnySave // !BB with false here... This would allow the user // !BB to switch to any available template when saving the // !BB document. In other words, a user would be allowed to // !BB save a .TXT file as a .PTS file although they are // !BB not related whatsoever... // !BB // !BB I'm switching this to use true - let me know if you // !BB know of a reason for the prior behaviour. // !BB #if defined(OLD_DOCVIEW) TDocTemplate* tpl = SelectAnySave(doc, false); #else TDocTemplate* tpl = SelectAnySave(doc, true); #endif if (!tpl) continue; // !BB // !BB The following is suspicious: Is there a reason // !BB to allow the user to switch the template in the first // !BB place?? OK, if everyone agrees that same TDocument-derived // !BB type implies compatible document, that would be OK. // !BB However, that's not what we've encouraged. Our own // !BB examples use the same TFileDocument for incompatible // !BB document types. Hence, if an app. has a // !BB TBitmapView/TFileDocument and a TTextView/TFileDocument pair, // !BB the following would allow the user to save a text file as a .BMP // !BB Ack!! // !BB If the following is really the intent, then DV users must be // !BB conscious that they will more often than not be using TFileDocument- // !BB derived documents as a method to specify compatible and incompatible // !BB document types. // if (tpl != doc.Template) doc.SetTemplate(tpl); } if (doc.Commit()) return true; continue; case IDNO: if (doc.Revert(true)) return true; return false; case IDCANCEL: return false; } } return true; }