//----------------------------------------------------------------------------
// ObjectWindows
// Copyright (c) 1993, 1996 by Borland International, All Rights Reserved
//
/// \file
/// Implementation of class TDocument
//----------------------------------------------------------------------------
#include <owl/pch.h>
#include <owl/docmanag.h>
#include <owl/appdict.h>
#include <owl/docview.rh>
#include <string.h>
#include <stdio.h>
 
#if defined(__BORLANDC__)
# pragma option -w-ccc // Disable "Condition is always true/false"
#endif
 
namespace owl {
 
OWL_DIAGINFO;
DIAG_DECLARE_GROUP(OwlDocView);        // General Doc/View diagnostic group
 
// !BB Is provided by DocManager to avoid sharing
//    of index between multiple applications.
// int TDocument::UntitledIndex = 0;
 
 
//
/// Although you do not create a TDocument object directly, you must call the
/// constructor when you create a derived class. parent points to the parent of the
/// new document. If no parent exists, parent is 0.
//
TDocument::TDocument(TDocument* parent)
:
  Tag(0),
  DirtyFlag(false),
  Embedded(false),
  ParentDoc(parent),
  NextDoc(0),
  OpenMode(0),
  Title(0),
  Template(0),
  ViewList(0),
  StreamList(0),
  DocPath(0)
{
  if (parent) {
    DocManager = parent->DocManager;
 
    // Handle case of 'dummy' parent [parent parented to itself] which was
    // created by the docmanager simply to pass itself to us
    //
    if (parent->ParentDoc == parent) {
      ParentDoc = 0;
      CHECK(DocManager);
      DocManager->DocList.Insert(this);
    }
    else
      parent->ChildDoc.Insert(this);
  }
  else {
    TApplication* app = OWLGetAppDictionary().GetApplication(0);
    CHECK(app);
    DocManager = app->GetDocManager();
    if (!DocManager)
      TXOwl::Raise(IDS_NODOCMANAGER);  // No doc manager to catch this one
    DocManager->DocList.Insert(this);
  }
}
 
//
/// Deletes a TDocument object. Normally, Close is called first. TDocument's
/// destructor destroys all children and closes all open streams. If this is the
/// last document that used the template, it closes the object's template and any
/// associated views, deletes the object's stream, and removes itself from the
/// parent's list of children if a parent exists. If there is no parent, it removes
/// itself from the document manager's document list.
//
TDocument::~TDocument()
{
  // Is this a dummy document?
  //
  if (ParentDoc == this)
    return;
 
  // Unref template - prevents self autodelete when deleting views, including
  // views in child documents
  //
  SetTemplate(0);
 
  DestroyChildren();
 
  DestroyViews();
 
  // Delete all streams, should only be present if abort or coding error
  //
  while (StreamList) {
    TRACEX(OwlDocView, 0, _T("~TDocument(): StreamList not 0!"));
    delete StreamList;
  }
 
  // Detach from parent and doc manager
  //
  if (ParentDoc) {
    ParentDoc->ChildDoc.Remove(this);
  }
  else {
    const auto ok = DocManager != nullptr;
    WARN(!ok, _T("TDocument::~TDocument: Terminating due to failed precondition."));
    if (!ok) std::terminate();
 
    DocManager->PostEvent(dnClose, *this); // WM_OWLDOCUMENT
    DocManager->DocList.Remove(this);
  }
 
  delete[] Title;
  delete[] DocPath;
}
 
//
//
//
static const LPCTSTR PropNames[] = {
  _T("Document Class"),  // DocumentClass
  _T("Template Name"),   // TemplateName
  _T("View Count"),      // ViewCount
  _T("Storage Path"),    // StoragePath
  _T("Document Title"),  // DocTitle
};
 
//
//
//
static int PropFlags[] = {
  pfGetText|pfConstant,  // DocumentClass
  pfGetText,             // TemplateName
  pfGetBinary|pfGetText, // ViewCount
  pfGetText|pfSettable,  // StoragePath
  pfGetText|pfSettable,  // DocTitle
};
 
//
/// Returns the name of the property given the index value (index).
//
LPCTSTR
TDocument::PropertyName(int index)
{
  if (index <= PrevProperty) {
    TRACEX(OwlDocView, 0, _T("PropertyName(): index <= PrevProperty!"));
    return 0;
  }
  else if (index < NextProperty)
    return PropNames[index-PrevProperty-1];
  else {
    TRACEX(OwlDocView, 0, _T("PropertyName(): index >= PrevProperty!"));
    return 0;
  }
}
 
//
/// Returns the attributes of a specified property given the index (index) of the
/// property whose attributes you want to retrieve.
//
int
TDocument::PropertyFlags(int index)
{
  if (index <= PrevProperty) {
    TRACEX(OwlDocView, 0, _T("PropertyFlags(): index <= PrevProperty!"));
    return 0;
  }
  else if (index < NextProperty)
    return PropFlags[index-PrevProperty-1];
  else {
    TRACEX(OwlDocView, 0, _T("PropertyFlags(): index >= PrevProperty!"));
    return 0;
  }
}
 
//
/// Gets the property index, given the property name (name). Returns either the
/// integer index number that corresponds to the name or 0 if the name isn't found
/// in the list of properties.
//
int
TDocument::FindProperty(LPCTSTR name)
{
  PRECONDITION(name != nullptr);
  int i;
  for (i=0; i < NextProperty-PrevProperty-1; i++)
    if (_tcscmp(PropNames[i], name) == 0)
      return i+PrevProperty+1;
 
  TRACEX(OwlDocView, 0, _T("FindProperty: Index of [") << name << _T("] not found") );
  return 0;
}
 
//
/// Retrieves the property identified by the given index.
/// If the requested property is text, then `dest` should point to a text buffer, and `textlen`
/// should specify the maximum number of characters the buffer can hold, excluding the terminating
/// null-character, i.e. the buffer must have room for (`textlen` + 1) characters.
///
/// If the requested property is numerical, then it may be requested either as text or in its
/// binary form. To request the property as text, pass a text buffer as described above. To request
/// the property in binary form, `dest` should point to storage of sufficent size, and `textlen`
/// should be zero.
///
/// Non-text properties without textual representation, e.g. file handles, may only be requested
/// in binary form, i.e. `dest` must point to sufficient storage, and `textlen` must be zero.
///
/// \return If the parameter `textlen` is non-zero, which means that the property is requested in
/// string form, the function returns the length of the string, i.e. the character count excluding
/// the terminating null-character. If the parameter `textlen` is zero, which means that property
/// is requested in binary form, the return value is the size of the data in bytes.
///
/// If the property is text, and `textlen` is zero, the function fails and returns 0. The function
/// also fails and returns 0 if `textlen` is non-zero and the property requested can not be
/// expressed as text. It also returns 0 if the property is not defined.
///
/// \sa TDocument::TDocProp
//
int
TDocument::GetProperty(int index, void * dest, int textlen)
{
  LPCTSTR src;
  tchar   numBuf[15];
  switch (index) {
    case DocumentClass: {
        _USES_CONVERSION;
        src = _A2W(_OBJ_FULLTYPENAME(this));
      }
      break;
 
    case TemplateName:
      src = Template ? Template->GetDescription() : 0;
      break;
 
    case ViewCount: {
      int cnt;
      TView* view;
      for (view=ViewList, cnt=0; view != 0; view=view->NextView, cnt++)
        ; // Do nothing
      if (!textlen) {
        *(int *)dest = cnt;
        return sizeof(int);
      }
      wsprintf(numBuf, _T("%d"), cnt);
      src = numBuf;
      break;
    }
 
    case StoragePath:
      src = DocPath;
      break;
 
    case DocTitle:
      src = Title;
      break;
 
    default:
      TRACEX(OwlDocView, 0, _T("GetProperty(): invalid property [")
             << index << _T("] specified!") );
      return 0;
  }
 
  if (!textlen) {
    TRACEX(OwlDocView, 0, _T("GetProperty(): 0-Length buffer specified!"));
    return 0;
  }
  int srclen = src ? static_cast<int>(::_tcslen(src)) : 0;
  if (textlen > srclen)
    textlen = srclen;
  if (textlen)
    memcpy(dest, src, textlen*sizeof(tchar));
  *((LPTSTR)dest + textlen) = 0;
  return srclen;
}
 
//
/// Sets the value of the property, given index, the index value of the property,
/// and src, the data type (either binary or text) to which the property must be
/// set.
//
bool
TDocument::SetProperty(int prop, const void * src)
{
  switch (prop) {
    case DocTitle:
      SetTitle((LPCTSTR)src);
      break;
 
    case StoragePath:
      return SetDocPath((LPCTSTR)src);
 
    default:
      TRACEX(OwlDocView, 0, _T("SetProperty(): invalid prop [") << prop <<\
            _T("] specified!"));
      return false;
  }
  return true;
}
 
//
/// Destroy children first if we have any. Then force close here as a last
/// resort if derived classes have not done so. Since we have destructed down
/// to a TDocument by now, derived closes will not be called.
//
void TDocument::DestroyChildren()
{
  ChildDoc.Destroy();
  Close();
}
 
//
/// Destroys the views attached to this document.
//
void TDocument::DestroyViews()
{
  // NOTE: View's destructor invokes 'DetachView' which removes it from
  //       the list
  //
  while (ViewList)
    delete ViewList;
}
 
//
/// Returns the this pointer as the root document.
//
TDocument&
TDocument::RootDocument()
{
  TDocument* pdoc = this;
  while (pdoc->ParentDoc)
    pdoc = pdoc->ParentDoc;
  return *pdoc;
}
 
//
/// Sets the current document manager to the argument dm.
//
void
TDocument::SetDocManager(TDocManager& dm)
{
  if (!ParentDoc) {
    if (DocManager)  // test needed for TDocManager::Streamer::Read()
      DocManager->DocList.Remove(this);
    dm.DocList.Insert(this);
  }
  DocManager = &dm;
}
 
//
/// Sets the document path for Open and Save operations.
//
bool
TDocument::SetDocPath(LPCTSTR path)
{
  delete[] DocPath;
  DocPath = (path && *path) ? strnewdup(path) : 0;
 
  tchar title[_MAX_PATH] = _T("Unknown");  // Never used - but just in case!
  if (!DocPath || GetFileTitle(DocPath, title, COUNTOF(title)) != 0) {
    CHECK(DocManager);
    CHECK(DocManager->GetApplication());
 
    int len = DocManager->GetApplication()->LoadString(IDS_UNTITLED,
                                                       title, COUNTOF(title));
    if (DocManager->IsFlagSet(dmMDI))
      wsprintf(title+len, _T("%d"), ++(DocManager->GetUntitledIndex()));
  }
  SetTitle(title);
  return true;  // derived classes may validate path
}
 
//
/// Sets the title of the document.
//
void
TDocument::SetTitle(LPCTSTR title)
{
  if (!Title || title!=Title)
  {    //This check allows calls like SetTitle(GetTitle()) (which is indirect way of calling ReindexFrames)
    delete[] Title;
    Title = title ? strnewdup(title) : 0;
  }
  ReindexFrames();
}
 
//
/// Sets the document template. However, if the template type is incompatible with
/// the file, the document manager will refuse to save the file as this template
/// type.
//
bool
TDocument::SetTemplate(TDocTemplate* tpl)
{
  if (Template) {
    CHECK(DocManager);
    DocManager->UnRefTemplate(*Template);
  }
  if (tpl) {
    CHECK(DocManager);
    DocManager->RefTemplate(*tpl);
  }
  Template = tpl;
  return true;
}
 
//
/// Force view title and index update.
//
void
TDocument::ReindexFrames()
{
  TView* view;
  int seq;
 
  for (seq = -1, view = ViewList; view != 0; view = view->NextView) {
    seq -= view->SetDocTitle(Title, seq);  // decrement if title displayed
    if (seq == -3)   // need only check if more than one title displayed
      break;
  }
  if (seq == -1)
    return;
  seq = (seq == -2 ? 0 : 1);
  for (view = ViewList; view != 0; view = view->NextView)
    {
    //DLN: added this if condition to avoid PRECONDITIONs in debug build
    // which occur if program closed by closing main window
    if ( view->GetWindow() && view->GetWindow()->GetHandle() )
    seq += view->SetDocTitle(Title, seq);  // increment if title displayed
  }
}
 
//
/// Called from TStream's constructor, AttachStream attaches a stream to the current
/// document.
//
void
TDocument::AttachStream(TStream& strm)
{
  strm.NextStream = StreamList;
  StreamList = &strm;
}
 
//
/// Called from TStream's destructor, DetachStream detaches the stream from the
/// current document.
//
void
TDocument::DetachStream(TStream& strm)
{
  TStream** plist = &StreamList;
  for ( ; *plist; plist = &(*plist)->NextStream) {
    if (*plist == &strm) {
      *plist = strm.NextStream;
      return;
    }
  }
}
 
//
/// Gets the next entry in the stream. Holds 0 if none exists.
//
TStream*
TDocument::NextStream(const TStream* strm)
{
  return strm ? strm->NextStream : StreamList;
}
 
//
/// Gets the next view in the list of views. Holds 0 if none exists.
//
TView*
TDocument::NextView(const TView* view)
{
  return view ? view->NextView : ViewList;
}
 
//
/// Called from TView constructor.
//
void
TDocument::AttachView(TView& view)
{
  TView** ppview;
 
  for (ppview = &ViewList; *ppview; ppview = &(*ppview)->NextView)
    ;
  *ppview = &view;    // insert at end of list
  view.NextView = 0;
  view.Doc = this;
  NotifyViews(vnViewOpened, reinterpret_cast<TParam2>(&view), &view);
}
 
//
/// Initializes the view. Notifies others a view is created by posting the dnCreate
/// event.
//
TView*
TDocument::InitView(TView* view)
{
  if (!view) {
    TRACEX(OwlDocView, 0, _T("InitView(): 0 view specified!"));
    return 0;
  }
  if (!view->IsOK()) {
    TRACEX(OwlDocView, 0, _T("InitView(): Invalid view object specified!"));
    delete view;
    return 0;
  }
 
  CHECK(DocManager);
  DocManager->PostEvent(dnCreate, *view); // WM_OWLVIEW
 
  if (!view->IsOK()) {
    TRACEX(OwlDocView, 0, _T("InitView(): Invalid view object post dnCreate!"));
    delete view;
    return 0;
  }
 
  ReindexFrames();
  TView::BumpNextViewId();
 
  return view;
}
 
//
/// DetachView is invoked from TView's destructor so that the view can detach
/// itself from this document. True is returned if the detachment is successful
/// indicating that this document should be deleted.
//
bool
TDocument::DetachView(TView& view)
{
  TView** plist = &ViewList;
  for (; *plist; plist = &(*plist)->NextView) {
    if (*plist == &view) {
 
      // Found the view, now detach it and notify app and other views
      //
      DocManager->PostEvent(dnClose, view); // WM_OWLVIEW
      *plist = view.NextView;
      NotifyViews(vnViewClosed, reinterpret_cast<TParam2>(&view), &view);
 
      // Cleanup doc if last view was just closed and dtAutoDelete
      // or dtAutoOpen is set. dtAutoOpen will cause an autoclose, while
      // dtAutoDelete will delete this doc also.
      //
      if (!ViewList) {
        if (Template && ((Template->Flags & dtAutoDelete) ||
                         (Template->Flags & dtAutoOpen))) {
          // Close document streams
          //
          if (IsOpen())
            Close();
 
          // Returning true will cause ~TView to delete document. Using
          // 'view.IsOK()' caters for cases where TView's construction failed.
          //
          return (Template->Flags & dtAutoDelete) && view.IsOK();
        }
      }
      else {
        ReindexFrames();
      }
      break;
    }
  }
  return false;
}
 
//
/// 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);
}
 
//
/// 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);
}
 
//
/// Notifies the views of this document, and the views of any child documents, of a change.
///
/// The notification message, WM_OWLNOTIFY, is sent with the given eventId, which is
/// private to the particular document class, and a single generic argument, which will be
/// cast to the actual type of the parameter of the response function. If the optional
/// parameter `exclude` is set, the appointed view will not be notified.
///
/// In contrast to QueryViews, NotifyViews sends the notification message to all views,
/// regardless of the return values. NotifyViews returns `true`, if and only if all views
/// return `true`. If a view does not handle the notification, then a `true` return value is
/// assumed. In other words, NotifyViews will return `false` only if a view handles the
/// notification and explicitly returns `false`. In any case, all views are notified.
//
bool
TDocument::NotifyViews(int eventId, TParam2 param, TView* exclude)
{
  bool answer = true;
 
  for (auto d = ChildDoc.Next(nullptr); d; d = ChildDoc.Next(d))
  {
    bool r = d->NotifyViews(eventId, param, exclude);
    answer = answer && r;
  }
 
  TEventHandler::TEventInfo eventInfo(WM_OWLNOTIFY, eventId);
  for (TView* view = ViewList; view != 0; view = view->NextView)
  {
    if (view == exclude) continue;
 
    if (view->Find(eventInfo))
    {
      bool r = view->Dispatch(eventInfo, 0, param) != 0;
      answer = answer && r;
    }
  }
  return answer;
}
 
//
/// Notifies the views of this document of a change. Does not notify views of child documents.
///
/// The notification message, WM_OWLNOTIFY, is sent with the given eventId, which is
/// private to the particular document class, and a single generic argument, which will be
/// cast to the actual type of the parameter of the response function. If the optional
/// parameter `exclude` is set, the appointed view will not be notified.
///
/// In contrast to QueryViews, NotifyOwnViews sends the notification message to all views,
/// regardless of the return values. NotifyOwnViews returns `true`, if and only if all views
/// return `true`. If a view does not handle the notification, then a `true` return value is
/// assumed. In other words, NotifyOwnViews will return `false` only if a view handles the
/// notification and explicitly returns `false`. In any case, all views are notified.
//
bool
TDocument::NotifyOwnViews(int eventId, TParam2 param, TView* exclude)
{
  bool answer = true;
 
  TEventHandler::TEventInfo eventInfo(WM_OWLNOTIFY, eventId);
  for (TView* view = ViewList; view != 0; view = view->NextView)
  {
    if (view == exclude) continue;
 
    if (view->Find(eventInfo))
    {
      bool r = view->Dispatch(eventInfo, 0, param) != 0;
      answer = answer && r;
    }
  }
  return answer;
}
 
//
/// Queries the views of the current document, and the views of any child documents,
/// about a specified event.
///
/// The notification message, WM_OWLNOTIFY, is sent with the given eventId, which is
/// private to the particular document class, and a single generic argument, which will be
/// cast to the actual type of the parameter of the response function. If the optional
/// parameter `exclude` is set, the appointed view will not be queried.
///
/// In contrast to NotifyViews, the query stops at the first view that returns `true`, and
/// the pointer to the view is returned. If no view returns `true`, `nullptr` is returned.
//
TView*
TDocument::QueryViews(int eventId, TParam2 param, TView* exclude)
{
  TView* view;
  TDocument* pdoc = 0;
  while ((pdoc = ChildDoc.Next(pdoc)) != 0)
    if ((view = pdoc->QueryViews(eventId, param, exclude)) != 0)
      return view;
 
  TEventHandler::TEventInfo eventInfo(WM_OWLNOTIFY, eventId);
  for (view = ViewList; view != 0; view = view->NextView)
  {
    if (view == exclude) continue;
 
    if (view->Find(eventInfo))
      if (view->Dispatch(eventInfo, 0, param))
        return view; // Return first acknowledger
  }
  return 0;
}
 
//
/// 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;
}
 
//
/// Used by the document manager, HasFocus returns true if this document's view has
/// focus. hwnd is a handle to the document. to determine if the document contains a
/// view with a focus.
//
bool
TDocument::HasFocus(HWND hWnd)
{
  return DocWithFocus(hWnd) != 0;
}
 
//
/// 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, reinterpret_cast<TParam2>(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;
}
 
//
/// Posts the error message passed as a string resource ID in sid. choice is one or
/// more of the MB_Xxxx style constants.
//
uint
TDocument::PostError(uint sid, uint choice)
{
  return DocManager->PostDocError(*this, sid, choice);
}
 
//----------------------------------------------------------------------------
 
//
//
//
TDocument*
TDocument::TList::Next(const TDocument* doc)
{
  return doc ? doc->NextDoc : DocList;
}
 
//
//
//
bool
TDocument::TList::Insert(TDocument* doc)
{
  TDocument* pdoc;
  for (pdoc = DocList; pdoc; pdoc = pdoc->NextDoc)
    if (pdoc == doc)
      return false;
  doc->NextDoc = DocList;
  DocList = doc;
  return true;
}
 
//
//
//
bool
TDocument::TList::Remove(TDocument* doc)
{
  TDocument** ppdoc;
  for (ppdoc = &DocList; *ppdoc; ppdoc = &(*ppdoc)->NextDoc) {
    if (*ppdoc == doc) {
      *ppdoc = doc->NextDoc;
      return true;
    }
  }
  return false;
}
 
//
//
//
bool TDocument::TList::Contains(TDocument* doc)
{
  for (TDocument* pDoc = DocList; pDoc; pDoc = pDoc->NextDoc)
    if (pDoc == doc)
      return true;
  return false;
}
 
//
//
//
void
TDocument::TList::Destroy()
{
  while (DocList)
    delete DocList;   // removes it entry from destructor
}
 
 
IMPLEMENT_ABSTRACT_STREAMABLE(TDocument);
 
#if OWL_PERSISTENT_STREAMS
 
//
//
//
void*
TDocument::Streamer::Read(ipstream& is, uint32 /*version*/) const
{
  TDocument* o = GetObject();
 
  o->NextDoc = 0;
  o->StreamList = 0;
  o->DocManager = 0;
  o->DirtyFlag = false;
 
  is >> o->OpenMode;
#if defined(UNICODE)
  _USES_CONVERSION;
  char * docPath = is.freadString();
  char * title = is.freadString();
 
  o->DocPath = _A2W(docPath);
  o->Title   = _A2W(title);
 
  delete[] docPath;
  delete[] title;
#else
  o->DocPath = is.freadString();
  o->Title   = is.freadString();
#endif
  is >> o->Template;  // static templates must have been already streamed
  is >> o->ParentDoc;
  o->ViewList = 0;    // must init, does not get set until after view streamed
  is >> o->ViewList;
 
  is >> TView::NextViewId;  // static, but must get set by at least 1 document
 
  return o;
}
 
//
//
//
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
}
 
#endif
 
} // OWL namespace
 

V1004 The 'app' pointer was used unsafely after it was verified against nullptr. Check lines: 64, 65.

V1004 The 'src' pointer was used unsafely after it was verified against nullptr. Check lines: 271, 275.