#include "ET++.ph" #ifdef __GNUG__ #pragma implementation #endif #include "Document.h" #include "Class.h" #include "ImageItem.h" #include "FileDialog.h" #include "System.h" #include "CommandProcessor.h" #include "WindowSystem.h" #include "Env.h" #include "ProgressFilter.h" #include "OrdColl.h" #include "FileData.h" #include "MenuBar.h" #include "Menu.h" #include "CmdNo.h" #include "String.h" #include "Application.h" #include "Alert_e.h" #include "TypeMatcher.h" #include "Converter.h" #include "ET_stdio.h" //---- FormatSaveFileDialog ---------------------------------------------------- const int cIdFormat = cIdFirstUser + 130; class FormatSaveFileDialog : public FileDialog { public: FormatSaveFileDialog(const char *title= "File Dialog"); ~FormatSaveFileDialog(); int ShowInWindow2(Clipper*, EvtHandler *eh, Object *op, Converter **dflt); void Control(int id, int p, void *v); VObject *MakePart(int which); protected: Converter **defaultconverter; PopupButton *converterPopup; OrdCollection *converters, *converteritems; int defaultformatid; }; FormatSaveFileDialog::FormatSaveFileDialog(const char *tit) : FileDialog(tit) { defaultconverter= 0; converterPopup= 0; converters= 0; converteritems= 0; } FormatSaveFileDialog::~FormatSaveFileDialog() { SafeDelete(converters); } int FormatSaveFileDialog::ShowInWindow2(Clipper *cl, EvtHandler *eh, Object *op, Converter **dflt) { defaultconverter= dflt; converters= Converter::CollectOutputConverters(op); converteritems= new OrdCollection; Iter next(converters); register Converter *cv; defaultformatid= cIdFormat; for (int i= 0; cv= (Converter*) next(); i++) { converteritems->Add(new MenuButtonItem(cIdFormat+i, cv->AsString())); if (*defaultconverter == cv) defaultformatid= cIdFormat+i; } if (converterPopup) { converterPopup->SetCollection(converteritems); converterPopup->SetValue(defaultformatid, FALSE); } return FileDialog::ShowInWindow(eFDWrite, cl, eh, "Save with format:"); } VObject *FormatSaveFileDialog::MakePart(int which) { if (which == eFDEditTitlePart) { converterPopup= new PopupButton(cIdFormat, cIdNone, new Menu("Output Formats", FALSE, TRUE)); converterPopup->SetCollection(converteritems); converterPopup->SetValue(defaultformatid, FALSE); return new Form(cIdNone, (VObjAlign)(eVObjVBase+eVObjHExpand), gPoint10, FileDialog::MakePart(eFDEditTitlePart), converterPopup, 0 ); } return FileDialog::MakePart(which); } void FormatSaveFileDialog::Control(int i, int p, void *v) { if (i == cIdFormat) { Converter *c= (Converter*) converters->At((int)v-cIdFormat); if (c) { *defaultconverter= c; if (gDebug) fprintf(stderr, "FormatSaveFileDialog::Control: converter == %s\n", (*defaultconverter)->AsString()); } return; } FileDialog::Control(i, p, v); } //------------------------------------------------------------------------------ extern SmartBitmap gFileIcon; static FileDialog *loadDialog, *saveDialog; static FormatSaveFileDialog *formatSaveDialog; ONEXIT(Document) { SafeDelete(loadDialog); SafeDelete(saveDialog); SafeDelete(formatSaveDialog); } //---- Document ---------------------------------------------------------------- NewAbstractMetaImpl(Document,Manager, (T(docType), TP(docData))); Document::Document(const Symbol &dt) : docType(dt), Manager("???") { docData= new FileData((char*)0); SetName(GetBaseName()); SetOnDismiss(eMgrIconize); } Document::~Document() { SafeDelete(docData); } CommandProcessor *Document::MakeCmdProcessor() { int n= Env::GetValue("Document.UndoLevel", 1); if (n == 1) return new SingleCommandProcessor; //if (n > 100) return new UnboundedCommandProcessor; //return new BoundedCommandProcessor(n); } FileDialog *Document::MakeFileDialog(FileDialogType) { return gApplication->MakeFileDialog(); } const char *Document::GetBaseName() { return docData->ShortName(); } u_long Document::UniqueId() { return docData->UniqueId(); } bool Document::IsUntitled() { return docData->IsUntitled(); } bool Document::IsConverted() { Object *op= ReturnObjectToStore(); if (op) { Converter *converter= ((FileData*)docData)->GetConverter(); if (converter == 0 || !converter->CanConvertTo(op)) return TRUE; return FALSE; } if (GetDocumentType() == cDocTypeAscii && docData->IsAscii()) return FALSE; return docData->Type() != GetDocumentType(); } void Document::SetData(Data *fd) { if (fd && fd != docData) { SafeDelete(docData); docData= (Data*) fd->DeepClone(); } SetName(GetBaseName()); } VObject *Document::DoMakeIconContent() { return DoMakeIcon(GetName()); } VObject *Document::DoMakeIcon(const char*) { return new ImageItem(docData->GetBitmap()); } //---- Menus ------------------------------------------------------------------- MenuBar *Document::DoMakeMenuBar() { MenuBar *mb= new MenuBar; mb->AddMenuRight(MakeMenu(cHELPMENU)); mb->AddMenu(MakeMenu(cFILEMENU)); mb->AddMenu(MakeMenu(cEDITMENU)); mb->AddMenu(MakeMenu(cAPPMENU)); return mb; } Menu *Document::MakeMenu(int menuId) { Menu *m; switch (menuId) { case cFILEMENU: m= new Menu(cFILEMENU, "File"); m->AppendItems( "New@N", cNEW, "Open ...@O", cOPEN, "-", "Load ...@L", cLOAD, "Save@S", cSAVE, "Save As ...", cSAVEAS, "Revert", cREVERT, "Close@W", cCLOSE, "-", "Print ...@P", cPRINT, "-", "Quit@Q", cQUIT, 0); break; default: m= Manager::MakeMenu(menuId); } return m; } void Document::DoSetupMenu(Menu *m) { Manager::DoSetupMenu(m); GetCmdP()->DoSetupMenu(m); if (Modified()) { m->EnableItem(cSAVE); if (!IsUntitled()) m->EnableItem(cREVERT); } m->EnableItems(cLOAD, cSAVEAS, cPRINT, 0); } Command *Document::DoMenuCommand(int cmd) { switch(cmd) { case cSAVE: Save(); break; case cSAVEAS: SaveAs(); break; case cLOAD: Load(); break; case cREVERT: Revert(); break; case cCLOSE: Close(); break; default: return Manager::DoMenuCommand(cmd); } return gNoChanges; } void Document::CollectParts(Collection *col) { Manager::CollectParts(col); if (docData) col->Add(docData); } void Document::ExtCommand(int c, char *req, char *args, int l, char *&rc, int &rl) { if (strcmp(req, "load") == 0) Load(args); else if (strcmp(req, "save") == 0) Save(); else Manager::ExtCommand(c, req, args, l, rc, rl); } bool Document::Modified() { return GetCmdP()->Modified(); } bool Document::SavedChanges() { if (Modified()) { switch (ShowAlert(eAlertCaution, "Save changes to @B%s @P?", docData->ShortName())) { case cIdYes: if (Save()) return TRUE; return FALSE; // save cancelled -> cancel all case cIdNo: GetCmdP()->Finish(); return TRUE; case cIdCancel: return FALSE; } } return TRUE; } bool Document::Load(const char *filename, bool unique) { if (filename) { Data *d= new FileData(filename); bool b= LoadData(d, unique); delete d; return b; } return LoadData(0, unique); } bool Document::LoadData(Data *data, bool unique) { if (! SavedChanges()) return FALSE; if (data == 0) { if (loadDialog == 0) loadDialog= MakeFileDialog(eFDTypeRead); if (loadDialog->ShowInWindow(eFDRead, GetWindow(), this) != cIdOk) return FALSE; data= loadDialog->GetData(); } if (CanLoad(data)) { if (data->UniqueId() == 0) { ShowAlert(eAlertNote, "something wrong with @B%s @P\n%s", data->ShortName(), gSystem->GetErrorStr()); return FALSE; } if (unique) { Document *doc= (Document*) gApplication->FindManager((int)data->UniqueId()); if (doc && !DoFileIsAlreadyOpen(doc, data->FullName())) return FALSE; } AboutToLoad(data); gProgress->Start(form("Reading: %s", data->ShortName()), (int) data->SizeHint()); bool ok= DoReadData(data); if (gProgress->Stop()) ok= FALSE; // ProgressDialog was cancelled if (ok) SetData(data); gWindowSystem->Update(); } else gApplication->OpenDocument(data->FullName()); return TRUE; } void Document::AboutToLoad(Data*) { } bool Document::Close() // return TRUE if OK { if (IsOpen()) { if (SavedChanges()) return Manager::Close(); //return TRUE; } return FALSE; } Object *Document::ReturnObjectToStore() { return 0; } bool Document::Save() { if (Modified() && (IsUntitled() || IsConverted())) return SaveAs(); if (! Modified()) { ShowAlert(eAlertNote, "No changes since last save"); return TRUE; } if (! docData->IsWritable()) { ShowAlert(eAlertNote, "Document @B%s @Pis not writable\n%s", docData->ShortName(), gSystem->GetErrorStr()); return FALSE; } Store(); return TRUE; } bool Document::SaveAs() { Object *op= ReturnObjectToStore(); FileDialog *fd; if (op) { Converter *converter= ((FileData*)docData)->GetConverter(); if (formatSaveDialog == 0) formatSaveDialog= new FormatSaveFileDialog; if (formatSaveDialog->ShowInWindow2(GetWindow(), this, op, &converter) != cIdOk) return FALSE; ((FileData*)docData)->SetConverter(converter); fd= formatSaveDialog; } else { if (saveDialog == 0) saveDialog= MakeFileDialog(eFDTypeWrite); if (saveDialog->ShowInWindow(eFDWrite, GetWindow(), this) != cIdOk) return FALSE; fd= saveDialog; } ((FileData*)docData)->SetName(fd->FileName()); SetName(GetBaseName()); if (! docData->IsWritable()) { ShowAlert(eAlertNote, "Document @B%s @Pis not writable\n%s", docData->ShortName(), gSystem->GetErrorStr()); return FALSE; } Store(); return TRUE; } void Document::Revert() { if (Modified() && ShowAlert(eAlertCaution, "Discard all changes of @B%s @P?", GetBaseName()) == cIdYes) { GetCmdP()->Finish(); Load(docData->FullName(), FALSE); } } //---- Load/Store Documents ----------------------------------------------------- bool Document::CanLoad(Data *data) { if (GetDocumentType() == data->Type()) return TRUE; if (GetDocumentType() == cDocTypeAscii && data->IsAscii()) return TRUE; return FALSE; } bool Document::DoFileIsAlreadyOpen(Document *shown, const char *nm) { ShowAlert(eAlertNote, "Document @B%s @Pis already open", nm); if (shown != this) shown->Show(); return FALSE; } long Document::SizeHintForWriting() { return 0; } void Document::Store() { bool ok= FALSE; Object *op; GetCmdP()->Finish(); long sizehint= SizeHintForWriting(); if (sizehint > 0) gProgress->Start(form("Writing: %s", GetBaseName()), sizehint*2); docData->StartUpdate(); if (sizehint > 0) gProgress->Inc(sizehint); if (op= ReturnObjectToStore()) { Converter *converter= ((FileData*)docData)->GetConverter(); if (converter) { if (gDebug) fprintf(stderr, "Document::Store: converter == %s\n", converter->AsString()); ok= converter->ConvertTo(docData, op); } } else { StreamBuf *sb= docData->GetStreamBufForWriting(); if (sizehint > 0) { StreamBuf *osb= sb; sb= new ProgressFilter; sb->SetBaseStream(osb); } OStream to(sb, TRUE); ok= DoWrite(to, docData); if (ok) to << '\n'; } docData->EndUpdate(ok, !Env::GetValue("Document.MakeBackup", FALSE)); if (sizehint > 0) gProgress->Stop(); } bool Document::DoWrite(OStream& to, Data*) { to << cMagic SP << GetDocumentType().AsString() SP << gApplication->ProgramName() NL; return TRUE; } bool Document::DoReadData(Data *data) { IStream from(data->GetStreamBufForReading(), 0, TRUE); return DoRead(from, data); } bool Document::DoRead(IStream &from, Data*) { char c; // overread magic cookie while (from.get(c)) if (c == '\n') break; return TRUE; }