//---------------------------------------------------------------------------- // ObjectWindows // (C) Copyright 1993, 1994 by Borland International, All Rights Reserved // // Implements classes TDocument, TView, TWindowView //---------------------------------------------------------------------------- #pragma hdrignore SECTION #include #include #include #include #include DIAG_DECLARE_GROUP(OwlDocView); // General Doc/View diagnostic group #if !defined(SECTION) || SECTION == 1 const uint MinUniqueViewId = 0x8000; uint TView::NextViewId = MinUniqueViewId; int TDocument::UntitledIndex = 0; //---------------------------------------------------------------------------- // class TDocument // TDocument* TDocument::List::Next(const TDocument* doc) { return doc ? doc->NextDoc : DocList; } bool TDocument::List::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::List::Remove(TDocument* doc) { TDocument** ppdoc; for (ppdoc = &DocList; *ppdoc; ppdoc = &(*ppdoc)->NextDoc) { if (*ppdoc == doc) { *ppdoc = doc->NextDoc; return true; } } return false; } void TDocument::List::Destroy() { while (DocList) delete DocList; // removes it entry from destructor } TDocument::TDocument(TDocument* parent) : ParentDoc(parent), NextDoc(0), OpenMode(0), Title(0), Template(0), ViewList(0), Tag(0), StreamList(0), DocPath(0), DirtyFlag(false), Embedded(false) { if (parent) { DocManager = parent->DocManager; if (parent->ParentDoc == parent) {// a dummy parent ParentDoc = 0; DocManager->DocList.Insert(this); } else parent->ChildDoc.Insert(this); } else { TApplication* app = GetApplicationObject(); if ((DocManager = app->DocManager) == 0) THROW( TXOwl(IDS_NODOCMANAGER) ); // no doc manager to catch this one DocManager->DocList.Insert(this); } } TDocument::~TDocument() { if (ParentDoc == this) // a dummy doc return; ChildDoc.Destroy(); // destroy children first, otherwise unnecessary Close(); // force close (not virtual at this point!) // delete all attached views, data has already been flushed // SetTemplate(0); // unref tpl, prevent self autodelete when deleting views while (ViewList) delete ViewList; // ~View() calls DetachView, which removes it from list // delete all streams, should only be present if abort or coding error // while (StreamList) delete StreamList; // detach from parent and doc manager // if (ParentDoc) { ParentDoc->ChildDoc.Remove(this); } else { DocManager->PostEvent(dnClose, *this); DocManager->DocList.Remove(this); } delete [] Title; delete [] DocPath; } static char* PropNames[] = { "Document Class", // DocumentClass "Template Name", // TemplateName "View Count", // ViewCount "Storage Path", // StoragePath "Document Title", // DocTitle }; static int PropFlags[] = { pfGetText|pfConstant, // DocumentClass pfGetText, // TemplateName pfGetBinary|pfGetText, // ViewCount pfGetText|pfSettable, // StoragePath pfGetText|pfSettable, // DocTitle }; const char* TDocument::PropertyName(int index) { if (index <= PrevProperty) return 0; else if (index < NextProperty) return PropNames[index-PrevProperty-1]; else return 0; } int TDocument::PropertyFlags(int index) { if (index <= PrevProperty) return 0; else if (index < NextProperty) return PropFlags[index-PrevProperty-1]; else return 0; } int TDocument::FindProperty(const char far* name) { int i; for (i=0; i < NextProperty-PrevProperty-1; i++) if (strcmp(PropNames[i], name) == 0) return i+PrevProperty+1; return 0; } int TDocument::GetProperty(int prop, void far* dest, int textlen) { const char far* src; char numBuf[6]; switch (prop) { case DocumentClass: src = _TYPENAME(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++) ; if (!textlen) { *(int far*)dest = cnt; return sizeof(int); } wsprintf(numBuf, "%d", cnt); src = numBuf; break; } case StoragePath: src = DocPath; break; case DocTitle: src = Title; break; default: return 0; } if (!textlen) return 0; int srclen = src ? strlen(src) : 0; if (textlen > srclen) textlen = srclen; if (textlen) memcpy(dest, src, textlen); *((char far*)dest + textlen) = 0; return srclen; } bool TDocument::SetProperty(int prop, const void far* src) { switch (prop) { case DocTitle: SetTitle((const char far*)src); break; case StoragePath: return SetDocPath((const char far*)src); default: return false; } return true; } TDocument& TDocument::RootDocument() { TDocument* pdoc = this; while (pdoc->ParentDoc) pdoc = pdoc->ParentDoc; return *pdoc; } 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; } bool TDocument::SetDocPath(const char far* path) { // path has been set already // if (path && (path == DocPath)) return true; delete DocPath; DocPath = (path && *path) ? strnewdup(path) : 0; char title[256]; if (!DocPath || ::GetFileTitle(DocPath, title, sizeof title) != 0) { int len = DocManager->Application->LoadString(IDS_UNTITLED, title, sizeof title); if (DocManager->IsFlagSet(dmMDI)) wsprintf(title+len, "%d", ++UntitledIndex); } SetTitle(title); return true; // derived classes may validate path } void TDocument::SetTitle(const char far* title) { delete Title; Title = title ? strnewdup(title) : 0; ReindexFrames(); } bool TDocument::SetTemplate(TDocTemplate* tpl) { if (Template) DocManager->UnRefTemplate(*Template); if (tpl) DocManager->RefTemplate(*tpl); Template = tpl; return true; } 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) { seq += view->SetDocTitle(Title, seq); // increment if title displayed } } void TDocument::AttachStream(TStream& strm) { strm.NextStream = StreamList; StreamList = &strm; } void TDocument::DetachStream(TStream& strm) { TStream** plist = &StreamList; for ( ; *plist; plist = &(*plist)->NextStream) { if (*plist == &strm) { *plist = strm.NextStream; return; } } } TStream* TDocument::NextStream(const TStream* strm) { return strm ? strm->NextStream : StreamList; } TView* TDocument::NextView(const TView* view) { return view ? view->NextView : ViewList; } 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, (long)&view, &view); } TView* TDocument::InitView(TView* view) { if (!view) // test if new TView failed return 0; if (!view->IsOK()) { // test if constructor failed delete view; return 0; } DocManager->PostEvent(dnCreate, *view); if (!view->IsOK()) { // if failed in creation delete view; return 0; } ReindexFrames(); if (++TView::NextViewId < MinUniqueViewId) TView::NextViewId = MinUniqueViewId; return view; } bool TDocument::DetachView(TView& view) { TView** plist = &ViewList; for (; *plist; plist = &(*plist)->NextView) { if (*plist == &view) { DocManager->PostEvent(dnClose, view); *plist = view.NextView; NotifyViews(vnViewClosed, (long)&view, &view); if (!ViewList) { // all views for this doc are now gone if (Template && (Template->Flags & dtAutoDelete)) { if (IsOpen()) Close(); // close the document streams return view.IsOK(); // delete doc UNLESS failure in creation } } else { ReindexFrames(); } break; } } return false; } bool TDocument::Commit(bool force) { TDocument* pdoc = 0; while ((pdoc = ChildDoc.Next(pdoc)) != 0) { if (!pdoc->Commit(force)) return false; } // should we test here for DocPath==0 , or should caller have checked? // return NotifyViews(vnCommit, force); } bool TDocument::Revert(bool clear) { TDocument* pdoc = 0; while ((pdoc = ChildDoc.Next(pdoc)) != 0) { if (!pdoc->Revert(clear)) return false; } return NotifyViews(vnRevert, clear); } bool TDocument::NotifyViews(int event, long item, TView* exclude) { bool answer = true; TEventHandler::TEventInfo eventInfo(WM_OWLNOTIFY, event); TDocument* pdoc = 0; while ((pdoc = ChildDoc.Next(pdoc)) != 0) answer = ToBool(answer && pdoc->NotifyViews(event, item, exclude)); for (TView* view = ViewList; view != 0; view = view->NextView) if (view != exclude && view->Find(eventInfo)) answer = ToBool(answer && view->Dispatch(eventInfo, 0, item)); return answer; } 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 && view->Find(eventInfo) && view->Dispatch(eventInfo, 0, item)) return view; // return first acknowledger return 0; // if no takers } 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; } bool TDocument::HasFocus(HWND hwnd) { TDocument* pdoc = 0; while ((pdoc = ChildDoc.Next(pdoc)) != 0) if (pdoc->HasFocus(hwnd)) return true; return QueryViews(vnIsWindow, (long)hwnd) != 0; } 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 } bool TDocument::Close() { TDocument* pdoc = 0; while ((pdoc = ChildDoc.Next(pdoc)) != 0) if (!pdoc->Close()) return false; return true; } uint TDocument::PostError(uint sid, uint choice) { return DocManager->PostDocError(*this, sid, choice); } //---------------------------------------------------------------------------- // class TView // TView::TView(TDocument& doc) : Tag(0), ViewMenu(0) { ViewId = NextViewId; doc.AttachView(*this); } TView::~TView() { delete ViewMenu; if (Doc->DetachView(*this)) { delete Doc; } } void TView::SetViewMenu(TMenuDescr* menu) { delete ViewMenu; ViewMenu = menu; TDocTemplate* tpl = Doc->GetTemplate(); if (tpl && ViewMenu && *ViewMenu->GetModule() == *tpl->GetModule()) ViewMenu->SetModule(tpl->GetModule());// force same module alias as template } static char* TView_PropertyNames[] = { "View Class", // ViewClass "View Name", // ViewName }; static int TView_PropertyFlags[] = { pfGetText|pfConstant, // ViewClass pfGetText|pfConstant, // ViewName }; const char* TView::PropertyName(int index) { if (index <= PrevProperty) return 0; else if (index < NextProperty) return TView_PropertyNames[index-PrevProperty-1]; else return 0; } int TView::PropertyFlags(int index) { if (index <= PrevProperty) return 0; else if (index < NextProperty) return TView_PropertyFlags[index-PrevProperty-1]; else return 0; } int TView::FindProperty(const char far* name) { int i; for (i=0; i < NextProperty-PrevProperty-1; i++) if (strcmp(TView_PropertyNames[i], name) == 0) return i+PrevProperty+1; return 0; } int TView::GetProperty(int prop, void far* dest, int textlen) { const char far* src; switch (prop) { case ViewClass: src = _TYPENAME(this); break; case ViewName: src = GetViewName(); break; default: return 0; } if (!textlen) return 0; int srclen = src ? strlen(src) : 0; if (textlen > srclen) textlen = srclen; if (textlen) memcpy(dest, src, textlen); *((char far*)dest + textlen) = 0; return srclen; } //---------------------------------------------------------------------------- // class TWindowView // DEFINE_RESPONSE_TABLE1(TWindowView, TWindow) EV_VN_ISWINDOW, END_RESPONSE_TABLE; TWindowView::TWindowView(TDocument& doc, TWindow* parent) : TView(doc), TWindow(parent, 0, doc.GetDocManager().GetApplication()) { } // // Does a given HWND belong to this view? Yes if it is us, or a child of us // bool TWindowView::VnIsWindow(HWND hWnd) { return hWnd == HWindow || IsChild(hWnd); } //---------------------------------------------------------------------------- // class TDateTime (eventually) // int _OWLFUNC FormatFileTime(FILETIME* pft, void far* dest, int textlen) { char buf[40]; int len; if (!textlen) { *(FILETIME FAR*)dest = *pft; return sizeof(FILETIME); } #if defined(BI_PLAT_WIN32) SYSTEMTIME dt; FileTimeToSystemTime(pft, &dt); len = wsprintf(buf,"%d/%d/%d %02d:%02d:%02d.%02d", dt.wMonth, dt.wDay, dt.wYear, dt.wHour, dt.wMinute, dt.wSecond, dt.wMilliseconds/10); #else struct time filet; struct date filed; if (!(FileTimeToDateTime(pft, &filed, &filet))) return false; len = wsprintf(buf,"%d/%d/%d %02d:%02d:%02d.%02d", filed.da_mon,filed.da_day,filed.da_year, filet.ti_hour,filet.ti_min,filet.ti_sec,filet.ti_hund); #endif if (textlen > len) textlen = len; memcpy(dest, buf, textlen); *((char far*)dest + textlen) = 0; return len; } #if !defined(BI_PLAT_WIN32) // // Win32/OLE2 compatible FILETIME support for 16-bit applications // these functions to be incorporated into TDateTime class // int FormatDateTime(struct date filed, struct time filet, void far* dest, int textlen) { char buf[40]; if (!textlen) { DateTimeToFileTime(&filed, &filet, (FILETIME FAR*)dest); return sizeof(FILETIME); } int len = wsprintf(buf,"%d/%d/%d %02d:%02d:%02d.%02d", filed.da_mon,filed.da_day,filed.da_year, filet.ti_hour,filet.ti_min,filet.ti_sec,filet.ti_hund); if (textlen > len) textlen = len; memcpy(dest, buf, textlen); *((char far*)dest + textlen) = 0; return len; } // Convert date and time structures to 64-bit FILETIME structure // multiply seconds by 10^7 and adjust offset to Jan. 1, 1601 // adjustment from Jan.1, 1970 = 116,444,736,000,000,000 = 0x019DB1DED53E8000 // This code is for Win32 and OLE2 compatibility when using Win16 #include void DateTimeToFileTime(struct date* dosd, struct time* dost, FILETIME FAR* pft) { union { long udt; int w[2]; } u; u.udt = dostounix(dosd, dost); _BL = dost->ti_hund; _AX = u.w[0]; _CX = u.w[1]; asm mov di,200 asm mul di asm mov bh,0 asm add bx,bx asm add ax,bx asm adc dl,dh asm xchg ax,cx asm mov bx,dx asm mul di asm add bx,ax asm mov si,0 asm adc si,dx asm les di,pft asm mov ax,50000 asm xchg ax,cx asm mul cx asm add ax,0x8000 asm adc dx,0 asm mov es:[di],ax asm xchg ax,dx asm xchg ax,bx asm mul cx asm add ax,bx asm adc dx,0 asm add ax,0xD53E asm adc dx,0 asm mov es:[di+2],ax asm xchg ax,dx asm xchg ax,si asm mul cx asm add ax,si asm adc dx,0 asm add ax,0xB1DE asm adc dx,0x019D asm mov es:[di+4],ax asm mov es:[di+6],dx } // // Convert FILETIME structure to date and time structures // bool FileTimeToDateTime(FILETIME FAR* pft, struct date* dosd, struct time* dost) { union { long udt; int w[2]; } u; asm les di,pft asm mov dx,es:[di+6] asm mov ax,es:[di+4] asm sub ax,0xB1DE asm sbb dx,0x019D asm jnc OK return false; OK: asm mov cx,50000 asm div cx asm xchg bx,ax asm mov ax,es:[di+2] asm sub ax,0xD53E asm sbb dx,0 asm div cx asm xchg si,ax asm mov ax,es:[di] asm sub ax,(0x8000-5000) asm sbb dx,0 asm div cx asm mov dx,bx asm xchg ax,si asm mov cx,200 asm div cx asm xchg ax,si asm div cx asm shr dx,1 asm push dx u.w[0] = _AX; u.w[1] = _SI; unixtodos(u.udt, dosd, dost); asm pop ax dost->ti_hund = _AL; return true; } #endif // !defined(BI_PLAT_WIN32) #endif //---------------------------------------------------------------------------- #if !defined(SECTION) || SECTION == 2 IMPLEMENT_ABSTRACT_STREAMABLE(TView); void* TView::Streamer::Read(ipstream& is, uint32 /*version*/) const { TView* o = GetObject(); bool hasViewMenu = is.readByte(); if (hasViewMenu) { o->ViewMenu = new TMenuDescr; is >> *o->ViewMenu; } else o->ViewMenu = 0; is >> o->ViewId; is >> o->Doc; is >> o->NextView; return o; } void TView::Streamer::Write(opstream& os) const { TView* o = GetObject(); os.writeByte(int16(o->ViewMenu ? 1 : 0)); if (o->ViewMenu) os << *o->ViewMenu; os << o->ViewId; os << o->Doc; os << o->NextView; } IMPLEMENT_ABSTRACT_STREAMABLE(TDocument); 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; o->DocPath = is.freadString(); o->Title = is.freadString(); 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; os.fwriteString(o->DocPath); os.fwriteString(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 } IMPLEMENT_STREAMABLE2(TWindowView, TWindow, TView); void* TWindowView::Streamer::Read(ipstream& is, uint32 /*version*/) const { ReadBaseObject((TWindow*)GetObject(), is); ReadBaseObject((TView*)GetObject(), is); return GetObject(); } void TWindowView::Streamer::Write(opstream& os) const { WriteBaseObject((TWindow*)GetObject(), os); WriteBaseObject((TView*)GetObject(), os); } #endif