Stroika Library 3.0d23
 
Loading...
Searching...
No Matches
LedItDocument.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
4
5#include "Stroika/Foundation/StroikaPreComp.h"
6
7#include <cctype>
8
9#if qStroika_Foundation_Common_Platform_MacOS
10#include <Finder.h>
11
12#include <LFile.h>
13#include <LMenu.h>
14#include <LMenuBar.h>
15#include <LPlaceHolder.h>
16#include <LPrintout.h>
17#include <LString.h>
18#include <LWindow.h>
19#include <UMemoryMgr.h>
20#include <UWindows.h>
21#endif
22#if defined(WIN32)
23
24#include <afxwin.h>
25#elif qStroika_FeatureSupported_XWindows
26#include <fcntl.h>
27#include <stdio.h>
28#include <sys/stat.h>
29#include <sys/types.h>
30#include <unistd.h>
31#endif
32
34#include "Stroika/Frameworks/Led/SpellCheckEngine_Basic.h"
35#include "Stroika/Frameworks/Led/StyledTextIO/StyledTextIO_LedNative.h"
36#include "Stroika/Frameworks/Led/StyledTextIO/StyledTextIO_PlainText.h"
37#if qStroika_Foundation_Common_Platform_MacOS
38#include "Stroika/Frameworks/Led/StyledTextIO/StyledTextIO_STYLText.h"
39#endif
40
41#if qStroika_Foundation_Common_Platform_MacOS
42#include "Stroika/Frameworks/Led/FilteredFilePicker.h"
43#elif qStroika_Foundation_Common_Platform_Windows
44#include "LedItControlItem.h"
45#include "LedItServerItem.h"
46#endif
47#include "LedItView.h"
48#include "Options.h"
49
50#include "LedItApplication.h"
51
52#include "LedItDocument.h"
53
54using namespace Stroika::Foundation;
55using namespace Stroika::Frameworks::Led;
56using namespace Stroika::Frameworks::Led::Platform;
57using namespace Stroika::Frameworks::Led::StyledTextIO;
58
59using Memory::MakeSharedPtr;
61
62#if qStroika_Foundation_Common_Platform_MacOS
63class LedItDocumentWindow : public LWindow {
64public:
65 LedItDocumentWindow (ResIDT inWINDid, UInt32 inAttributes, LCommander* inSuper)
66 : LWindow (inWINDid, inAttributes, inSuper)
67 {
68 sWindowList.push_back (this);
69 ::AppendMenu (::GetMenuHandle (kWindowsMenuID), "\pREPLACEME");
70 LMenu* windowMenu = LMenuBar::GetCurrentMenuBar ()->FetchMenu (kWindowsMenuID);
71 AssertNotNil (windowMenu);
72 size_t nMenuItems = ::CountMenuItems (windowMenu->GetMacMenuH ());
73 for (size_t i = 1; i <= nMenuItems; ++i) {
74 windowMenu->SetCommand (i, i - 1 + kBaseWindowCmd); // make first cmd = kBaseWindowCmd
75 }
76 }
77
78 ~LedItDocumentWindow ()
79 {
80 LWindow* w = this;
81 sWindowList.erase (std::find (sWindowList.begin (), sWindowList.end (), w));
82 LMenu* windowMenu = LMenuBar::GetCurrentMenuBar ()->FetchMenu (kWindowsMenuID);
83 AssertNotNil (windowMenu);
84 windowMenu->RemoveItem (1);
85 size_t nMenuItems = ::CountMenuItems (windowMenu->GetMacMenuH ());
86 for (size_t i = 1; i <= nMenuItems; ++i) {
87 windowMenu->SetCommand (i, i - 1 + kBaseWindowCmd); // make first cmd = kBaseWindowCmd
88 }
89 }
90
91 // make zoom produce a roughly page-size window
92 virtual void CalcStandardBoundsForScreen (const Rect& inScreenBounds, Rect& outStdBounds) const override
93 {
94 LWindow::CalcStandardBoundsForScreen (inScreenBounds, outStdBounds);
95 short winWidth = ::GetRectWidth (outStdBounds);
96 short desiredWidth = 8.5 * 72; // 8.5 inches by 72dpi
97 short newWidth = Led_Min (winWidth, desiredWidth);
98 outStdBounds.right = outStdBounds.left + newWidth;
99 }
100
101public:
102 static vector<LWindow*> sWindowList;
103};
104vector<LWindow*> LedItDocumentWindow::sWindowList;
105
106static FilteredSFPutDLog::TypeSpec sPutFileTypeList[] = {
107 {"HTML file", kTEXTFileType},
108 {"Led Rich Text Format", kLedPrivateDocumentFileType},
109 {"Microsoft Rich Text Format (RTF)", kTEXTFileType},
110 {"Text file", kTEXTFileType},
111};
112
113inline FileFormat MapPutFileTypeListIdxToFormat (size_t typeIndex)
114{
115 switch (typeIndex) {
116 case 0:
117 return (eHTMLFormat);
118 case 1:
119 return (eLedPrivateFormat);
120 case 2:
121 return (eRTFFormat);
122 case 3:
123 return (eTextFormat);
124 default:
125 Assert (false);
126 return (eTextFormat);
127 }
128}
129
130inline size_t MapPutFileFormatToTypeListIdx (FileFormat format)
131{
132 switch (format) {
133 case eHTMLFormat:
134 return (0);
135 case eLedPrivateFormat:
136 return (1);
137 case eRTFFormat:
138 return (2);
139 case eTextFormat:
140 return (3);
141 default:
142 Assert (false);
143 return (0);
144 }
145}
146
147inline OSType MapFormatToOSType (FileFormat fileFormat)
148{
149 switch (fileFormat) {
150 case eTextFormat:
151 return (kTEXTFileType);
152 case eLedPrivateFormat:
153 return (kLedPrivateDocumentFileType);
154 case eRTFFormat:
155 return (kTEXTFileType);
156 case eHTMLFormat:
157 return (kTEXTFileType);
158 case eUnknownFormat:
159 return (kLedPrivateDocumentFileType);
160 default:
161 return (kLedPrivateDocumentFileType);
162 }
163}
164
165/*
166 * I tried using the PowerPlant StCursor class, but it worked LOUSY. It kept getting
167 * its internal cache of what cursor was shown stuck, and stoped going busy!
168 *
169 * This is extremely primitive, and lacks many features I'd like (like not showing up
170 * unless needed - like if enuf time has past and looks like its gonna take longer).
171 *
172 * But it will do for now.... LGP 960702
173 *
174 * Maybe consider lifting the Stroika cursor support sometime in the future? LGP 960702
175 */
176class Led_BusyCursor {
177public:
178 Led_BusyCursor ()
179 {
180 ::SetCursor (*::GetCursor (watchCursor));
181 }
182 ~Led_BusyCursor ()
183 {
184 ::InitCursor ();
185 }
186};
187#endif
188
189#if qStroika_Foundation_Common_Platform_Windows
190// special exception handling just for MFC library implementation
191// copied here so I could clone MFC code as needed - not well understood - UGH!!! - LGP 951227
192#ifndef _AFX_OLD_EXCEPTIONS
193#define DELETE_EXCEPTION(e) \
194 do { \
195 e->Delete (); \
196 } while (0)
197#else //!_AFX_OLD_EXCEPTIONS
198#define DELETE_EXCEPTION(e)
199#endif //_AFX_OLD_EXCEPTIONS
200
201static void AppendFilterSuffix (CString& filter, OPENFILENAME& ofn, CString strFilterExt, CString strFilterName);
202static void AppendFilterSuffix (CString& filter, OPENFILENAME& ofn, CDocTemplate* pTemplate);
203
204#endif
205
206/*
207 ********************************************************************************
208 ******************************** LedItDocument *********************************
209 ********************************************************************************
210 */
211#if qStroika_Foundation_Common_Platform_Windows
212FileFormat LedItDocument::sHiddenDocOpenArg = eUnknownFormat; // See LedItDocument::OnOpenDocument ()
213
214IMPLEMENT_DYNCREATE (LedItDocument, COleServerDoc)
215
216BEGIN_MESSAGE_MAP (LedItDocument, COleServerDoc)
217ON_UPDATE_COMMAND_UI (ID_EDIT_PASTE_LINK, OnUpdatePasteLinkMenu)
218ON_UPDATE_COMMAND_UI (ID_OLE_EDIT_CONVERT, OnUpdateObjectVerbMenu)
219ON_COMMAND (ID_OLE_EDIT_CONVERT, OnEditConvert)
220ON_UPDATE_COMMAND_UI (ID_OLE_EDIT_LINKS, OnUpdateEditLinksMenu)
221ON_COMMAND (ID_OLE_EDIT_LINKS, OnEditLinks)
222ON_UPDATE_COMMAND_UI (ID_OLE_VERB_FIRST, OnUpdateObjectVerbMenu)
223ON_UPDATE_COMMAND_UI (ID_FILE_SAVE, OnUpdateFileSave)
224ON_COMMAND (ID_FILE_SAVE_COPY_AS, OnFileSaveCopyAs)
225END_MESSAGE_MAP ()
226
227BEGIN_DISPATCH_MAP (LedItDocument, COleServerDoc)
228END_DISPATCH_MAP ()
229
230// Note: we add support for IID_ILedIt to support typesafe binding
231// from VBA. This IID must match the GUID that is attached to the
232// dispinterface in the .ODL file.
233
234// {0FC00622-28BD-11CF-899C-00AA00580324}
235static const IID IID_ILedIt = {0xfc00622, 0x28bd, 0x11cf, {0x89, 0x9c, 0x0, 0xaa, 0x0, 0x58, 0x3, 0x24}};
236
237BEGIN_INTERFACE_MAP (LedItDocument, COleServerDoc)
238INTERFACE_PART (LedItDocument, IID_ILedIt, Dispatch)
239END_INTERFACE_MAP ()
240
241#endif
242
243#if qStroika_Foundation_Common_Platform_MacOS
244LedItDocument::LedItDocument (LCommander* inSuper, FileFormat format)
245 : LSingleDoc (inSuper)
246 ,
247#elif qStroika_Foundation_Common_Platform_Windows
248LedItDocument::LedItDocument ()
249 : COleServerDoc ()
250 ,
251#elif qStroika_FeatureSupported_XWindows
252LedItDocument::LedItDocument ()
253 :
254#endif
255 MarkerOwner ()
256 , fTextStore ()
257 , fRTFInfo ()
258 , fStyleDatabase ()
259 , fParagraphDatabase ()
260 , fHidableTextDatabase ()
261 , fCommandHandler (kMaxNumUndoLevels)
262 ,
263#if qStroika_Foundation_Common_Platform_MacOS
264 fFileFormat (format)
265 ,
266#elif qStroika_Foundation_Common_Platform_Windows || qStroika_FeatureSupported_XWindows
267 fFileFormat (eDefaultFormat)
268 ,
269#endif
270 fHTMLInfo ()
271#if qStroika_FeatureSupported_XWindows
272 , fPathName ()
273#endif
274#if qStroika_Foundation_Common_Platform_MacOS
275 , fTextView (NULL)
276#endif
277{
278#if qStroika_Foundation_Common_Platform_Windows
279 EnableAutomation ();
280 ::AfxOleLockApp ();
281#endif
282 fTextStore.AddMarkerOwner (this);
283 fStyleDatabase = MakeSharedPtr<StyleDatabaseRep> (fTextStore);
284 fParagraphDatabase = MakeSharedPtr<ParagraphDatabaseRep> (fTextStore);
285 fHidableTextDatabase = MakeSharedPtr<UniformHidableTextMarkerOwner> (fTextStore);
286}
287
288LedItDocument::~LedItDocument ()
289{
290#if qStroika_Foundation_Common_Platform_MacOS
291 if (mWindow != NULL) {
292 mWindow->PostAction (NULL); // Flush undo buffer
293 }
294
295 delete mWindow;
296 mWindow = NULL; // delete now (rather than letting base class) so
297// all links to our textstore/style dbase etc go away before our
298// DTOR is done...
299#endif
300 fTextStore.RemoveMarkerOwner (this);
301#if qStroika_Foundation_Common_Platform_Windows
302 ::AfxOleUnlockApp ();
303#endif
304}
305
306void LedItDocument::DidUpdateText (const UpdateInfo& updateInfo) noexcept
307{
308 if (updateInfo.fRealContentUpdate) {
309#if qStroika_Foundation_Common_Platform_MacOS
310 mIsModified = true;
311 SetUpdateCommandStatus (true);
312#elif qStroika_Foundation_Common_Platform_Windows
313 SetModifiedFlag ();
314#endif
315 }
316}
317
318TextStore* LedItDocument::PeekAtTextStore () const
319{
320 return &const_cast<LedItDocument*> (this)->fTextStore;
321}
322
323#if qStroika_FeatureSupported_XWindows
324void LedItDocument::LoadFromFile (const string& fileName, FileFormat fileFormat)
325{
326 Require (not fileName.empty ());
327 fPathName = fileName;
328 fFileFormat = fileFormat;
329
330// Now do actual reading stuff..
331#if qPrintGLIBTraceMessages
332 g_message ("DOING LedItDocument::LoadFromFile (path= '%s', format=%d)\n", fPathName.c_str (), fileFormat);
333#endif
334 size_t fileLen = 0;
335 StackBuffer<char> fileData{Memory::eUninitialized, fileLen};
336 int fd = ::open (fPathName.c_str (), O_RDONLY);
337 if (fd == -1) {
338 Execution::Throw (bad_alloc{});
339 }
340 fileLen = ::lseek (fd, 0, SEEK_END);
341 fileData.GrowToSize (fileLen);
342 try {
343 ::lseek (fd, 0, SEEK_SET);
344 if (::read (fd, fileData, fileLen) != int (fileLen)) {
345 Execution::Throw (bad_alloc{});
346 }
347 }
348 catch (...) {
349 ::close (fd);
350 throw;
351 }
352 ::close (fd);
353
354 StyledTextIOSrcStream_Memory source (fileData, fileLen);
355 WordProcessor::WordProcessorTextIOSinkStream sink (&fTextStore, fStyleDatabase, fParagraphDatabase, fHidableTextDatabase);
356
357ReRead:
358 switch (fFileFormat) {
359 case eTextFormat: {
360 StyledTextIOReader_PlainText textReader (&source, &sink);
361 textReader.Read ();
362 } break;
363
364 case eLedPrivateFormat: {
365 StyledTextIOReader_LedNativeFileFormat textReader (&source, &sink);
366 textReader.Read ();
367 } break;
368
369 case eRTFFormat: {
370 StyledTextIOReader_RTF textReader (&source, &sink, &fRTFInfo);
371 textReader.Read ();
372 } break;
373
374 case eHTMLFormat: {
375 StyledTextIOReader_HTML textReader (&source, &sink, &fHTMLInfo);
376 textReader.Read ();
377 } break;
378
379 case eUnknownFormat: {
380 /*
381 * Should enhance this unknown/format reading code to take into account file suffix in our guess.
382 * First look at file suffix. THAT takes precedence over guessing file format based on
383 * contents.
384 */
385 SDKString suffix = ExtractFileSuffix (fPathName);
386 if (suffix == ".rtf") {
387 fFileFormat = eRTFFormat;
388 goto ReRead;
389 }
390 if (suffix == ".htm" or suffix == ".html") {
391 fFileFormat = eHTMLFormat;
392 goto ReRead;
393 }
394 if (suffix == ".led") {
395 fFileFormat = eLedPrivateFormat;
396 goto ReRead;
397 }
398 if (suffix == ".txt") {
399 fFileFormat = eTextFormat;
400 goto ReRead;
401 }
402 // Try RTF
403 try {
404 StyledTextIOReader_RTF reader (&source, &sink, &fRTFInfo);
405 if (reader.QuickLookAppearsToBeRightFormat ()) {
406 fFileFormat = eRTFFormat;
407 goto ReRead;
408 }
409 }
410 catch (...) {
411 // ignore any errors, and proceed to next file type
412 }
413
414 // Try LedNativeFileFormat
415 try {
416 StyledTextIOReader_LedNativeFileFormat reader (&source, &sink);
417 if (reader.QuickLookAppearsToBeRightFormat ()) {
418 fFileFormat = eLedPrivateFormat;
419 goto ReRead;
420 }
421 }
422 catch (...) {
423 // ignore any errors, and proceed to next file type
424 }
425
426 // Try HTML
427 try {
428 StyledTextIOReader_HTML reader (&source, &sink);
429 if (reader.QuickLookAppearsToBeRightFormat ()) {
430 fFileFormat = eHTMLFormat;
431 goto ReRead;
432 }
433 }
434 catch (...) {
435 // ignore any errors, and proceed to next file type
436 }
437
438 // Nothing left todo but to read the text file as plain text, as best we can...
439 fFileFormat = eTextFormat;
440 goto ReRead;
441 } break;
442
443 default: {
444 Assert (false); // don't support reading that format (yet?)!
445 } break;
446 }
447 sink.Flush (); // explicit Flush () call - DTOR would have done it - but there exceptions get silently eaten - this will at least show them...
448
449#if 0
450 // Should do something like this for XWin too - but right now - no backpointer from Doc to View kept... LGP 2002-11-20
451 fTextView->SetEmptySelectionStyle ();
452#endif
453}
454
455void LedItDocument::Save ()
456{
457 // Now do actual reading stuff..
458 g_message ("DOING Save- '%s'\n", fPathName.c_str ());
459 Require (fFileFormat != eUnknownFormat); // We must have chosen a file format by now...
460
461 WordProcessor::WordProcessorTextIOSrcStream source (&fTextStore, fStyleDatabase, fParagraphDatabase, fHidableTextDatabase);
462 StyledTextIOWriterSinkStream_Memory sink;
463
464 switch (fFileFormat) {
465 case eTextFormat: {
466 StyledTextIOWriter_PlainText textWriter (&source, &sink);
467 textWriter.Write ();
468 } break;
469
470 case eRTFFormat: {
471 StyledTextIOWriter_RTF textWriter (&source, &sink, &fRTFInfo);
472 textWriter.Write ();
473 } break;
474
475 case eHTMLFormat: {
476 StyledTextIOWriter_HTML textWriter (&source, &sink, &fHTMLInfo);
477 textWriter.Write ();
478 } break;
479
480 case eLedPrivateFormat: {
481 StyledTextIOWriter_LedNativeFileFormat textWriter (&source, &sink);
482 textWriter.Write ();
483 } break;
484
485 default: {
486 Assert (false); // don't support writing that format (yet?)!
487 } break;
488 }
489 int fd = ::open (fPathName.c_str (), O_RDWR | O_CREAT, 0666);
490 if (fd == -1) {
491 Execution::Throw (bad_alloc{});
492 }
493 try {
494 ::lseek (fd, 0, SEEK_SET);
495 if (::write (fd, sink.PeekAtData (), sink.GetLength ()) != int (sink.GetLength ())) {
496 Execution::Throw (bad_alloc{});
497 }
498 }
499 catch (...) {
500 ::close (fd);
501 throw;
502 }
503 ::close (fd);
504}
505#endif
506
507#if qStroika_Foundation_Common_Platform_MacOS
508const vector<LWindow*>& LedItDocument::GetDocumentWindows ()
509{
510 return LedItDocumentWindow::sWindowList;
511}
512
513void LedItDocument::DoSaveHelper ()
514{
515 Led_BusyCursor busyCursor;
516
517 // write text
518 Execution::ThrowIfNull (mFile);
519 Execution::ThrowIfNull (mWindow);
520
521 mWindow->PostAction (NULL);
522
523 WordProcessor::WordProcessorTextIOSrcStream source (&fTextStore, fStyleDatabase, fParagraphDatabase, fHidableTextDatabase);
524
525 ThrowIfOSErr_ (::SetFPos (mFile->GetDataForkRefNum (), fsFromStart, 0));
526 StyledTextIOWriterSinkStream_FileDescriptor sink (mFile->GetDataForkRefNum ());
527
528 // Setup output buffer, but be sure there is plenty of RAM before doing so
529 {
530 char* cheeseBuf = NULL;
531 try {
532 cheeseBuf = new char[64 * 1024];
533 sink.SetBufferSize (64 * 1024);
534 delete[] cheeseBuf;
535 }
536 catch (...) {
537 delete[] cheeseBuf;
538 }
539 }
540
541 // Now actually write the file
542 switch (fFileFormat) {
543 case eTextFormat: {
544 StyledTextIOWriter_PlainText textWriter (&source, &sink);
545 textWriter.Write ();
546
547 {
548 short curResFile = ::CurResFile ();
549 try {
550 mFile->CreateNewFile (kApplicationSignature, kTEXTFileType, 0);
551 }
552 catch (...) {
553 }
554 try {
555 mFile->OpenResourceFork (fsRdWrPerm);
556 }
557 catch (...) {
558 }
559
560 if (mFile->GetResourceForkRefNum () != -1) {
561 ::UseResFile (mFile->GetResourceForkRefNum ());
562
563 try {
564 StScrpHandle macStyleHandle = (StScrpHandle)::Get1Resource ('styl', 128);
565 if (macStyleHandle != NULL) {
566 ::RemoveResource (Handle (macStyleHandle)); // remove old styl resource
567 }
568
569 vector<StyledInfoSummaryRecord> ledStyleRuns = fStyleDatabase->GetStyleInfo (0, fTextStore.GetLength ());
570 size_t nStyleRuns = ledStyleRuns.size ();
571 Assert (offsetof (StScrpRec, scrpStyleTab) == sizeof (short)); // thats why we add sizeof (short)
572 macStyleHandle = (StScrpHandle)::Led_DoNewHandle (sizeof (short) + nStyleRuns * sizeof (ScrpSTElement));
573 HLock (Handle (macStyleHandle));
574 (*macStyleHandle)->scrpNStyles = nStyleRuns;
575 StandardStyledTextImager::Convert (ledStyleRuns, (*macStyleHandle)->scrpStyleTab);
576 HUnlock (Handle (macStyleHandle));
577
578 ::AddResource (Handle (macStyleHandle), 'styl', 128, "\p");
579 ThrowIfResError_ ();
580 }
581 catch (...) {
582 ::UseResFile (curResFile);
583 mFile->CloseResourceFork ();
584 throw;
585 }
586
587 ::UseResFile (curResFile);
588 mFile->CloseResourceFork ();
589 }
590 }
591 } break;
592
593 case eRTFFormat: {
594 StyledTextIOWriter_RTF textWriter (&source, &sink);
595 textWriter.Write ();
596 } break;
597
598 case eHTMLFormat: {
599 StyledTextIOWriter_HTML textWriter (&source, &sink, &fHTMLInfo);
600 textWriter.Write ();
601 } break;
602
603 case eLedPrivateFormat: {
604 StyledTextIOWriter_LedNativeFileFormat textWriter (&source, &sink);
605 textWriter.Write ();
606 } break;
607
608 default: {
609 Assert (false);
610 } break;
611 }
612
613 sink.UpdateEOF ();
614}
615
616// Name a new, untitled document window
617//
618// Untitled windows start with "untitled", then "untitled 1",
619// "untitled 2", etc. Old numbers are reused, so there won't be
620// gaps in the numbering.
621//
622// This routine uses a STR# resource to store the "untitled" string,
623// which can be localized to different languages. The first string
624// is "untitled" and the second is "untitled " (trailing space),
625// which is used when appending a number to the name.
626void LedItDocument::NameNewDoc ()
627{
628 // Start with the default name ("untitled")
629 Str255 name;
630 ::GetIndString (name, STRx_Untitled, 1);
631
632 long num = 0;
633 while (UWindows::FindNamedWindow (name) != nil) {
634 // An existing window has the current name
635 // Increment counter and try again
636 ::GetIndString (name, STRx_Untitled, 2);
637 ++num;
638 Str15 numStr;
639 ::NumToString (num, numStr);
640 LString::AppendPStr (name, numStr);
641 }
642 AssertNotNull (mWindow);
643 mWindow->SetDescriptor (name); // Finally, set window title
644}
645
646void LedItDocument::OpenFile (const FSSpec& inFileSpec)
647{
648 // Create a new File object, read the entire File contents,
649 // put the contents into the text view, and set the Window
650 // title to the name of the File.
651 try {
652 Require (mFile == NULL);
653 mFile = new LFile (inFileSpec);
654
655 FInfo docFinderInfo;
656 ThrowIfError_ (::FSpGetFInfo (&inFileSpec, &docFinderInfo));
657 bool isStationary = docFinderInfo.fdFlags & kIsStationery;
658 bool openedReadOnly = false;
659 try {
660 mFile->OpenDataFork (fsRdWrPerm);
661 }
662 catch (...) {
663 openedReadOnly = true;
664 mFile->OpenDataFork (fsRdPerm);
665 }
666
667 DoReadCode ();
668
669 fTextView->SetEmptySelectionStyle ();
670
671 if (isStationary) {
672 NameNewDoc ();
673 }
674 else {
675 mWindow->SetDescriptor (inFileSpec.name);
676 }
677
678 if (openedReadOnly or isStationary) {
679 delete mFile;
680 mFile = NULL;
681 }
682 mIsSpecified = not(openedReadOnly or isStationary); // if we opened RO or stationary, then hitting save button should bring up save-dialog
683 mIsModified = isStationary; // if we stationary, then closing should bring up save-dialog
684 }
685 catch (...) {
686 // if an error opening the file, then delete the file object - in case its left open...
687 delete mFile;
688 mFile = NULL;
689 throw;
690 }
691}
692
693// Return whether the Document is has changed since the last save
694Boolean LedItDocument::IsModified ()
695{
696 return mIsModified;
697}
698
699// Save Document in the specified file with the specified file type
700//
701// If file type is fileType_Default, use the normal file type for
702// this document
703void LedItDocument::DoAESave (FSSpec& inFileSpec, OSType inFileType)
704{
705 delete mFile;
706 mFile = NULL;
707
708 mFile = new LFile (inFileSpec); // Make new file object
709
710 OSType fileType = MapFormatToOSType (fFileFormat); // Find proper file type
711 if (inFileType != fileType_Default) {
712 fileType = inFileType;
713 }
714
715 // Make new file on disk
716 mFile->CreateNewDataFile (kApplicationSignature, fileType, 0);
717 mFile->OpenDataFork (fsRdWrPerm);
718 DoSave (); // Write out data
719 // Change window name
720 mWindow->SetDescriptor (inFileSpec.name);
721 mIsSpecified = true; // Document now has a specified file
722}
723
724// Ask the user to save a Document and give it a name
725//
726// Returns false if the user cancels the operation
727Boolean LedItDocument::AskSaveAs (FSSpec& outFSSpec, Boolean inRecordIt)
728{
729 FilteredSFPutDLog filteredPicker (sPutFileTypeList, (sizeof sPutFileTypeList) / (sizeof sPutFileTypeList[0]));
730
731 Str255 defaultName;
732 GetDescriptor (defaultName);
733 size_t typeIndex = MapPutFileFormatToTypeListIdx (fFileFormat);
734 FSSpec fileResult;
735 bool replacingFile = false;
736 if (filteredPicker.PickFile (defaultName, &fileResult, &replacingFile, &typeIndex)) {
737 fFileFormat = MapPutFileTypeListIdxToFormat (typeIndex);
738
739 /*
740 * Close the file (in case were overwriting our original, so the delete below doesn't fail.
741 * Any error writing NEW file out, mark us as UNTITLED, and NOT associated with any file...
742 */
743 delete mFile;
744 mFile = NULL;
745 mIsSpecified = false;
746 try {
747 if (inRecordIt) {
748 SendAESaveAs (fileResult, fileType_Default, false);
749 }
750 if (replacingFile) { // Delete existing file
751 ThrowIfOSErr_ (::FSpDelete (&fileResult));
752 }
753 DoAESave (fileResult, fileType_Default);
754 outFSSpec = fileResult;
755 }
756 catch (...) {
757 delete mFile;
758 mFile = NULL;
759 mIsSpecified = false;
760 NameNewDoc ();
761 throw;
762 }
763 return true;
764 }
765 return false;
766}
767
768// Save the entire Document to its associated File (which must already exist)
769void LedItDocument::DoSave ()
770{
771 DoSaveHelper ();
772
773 FSSpec fileSpec;
774 mFile->GetSpecifier (fileSpec);
775 mWindow->SetDescriptor (fileSpec.name);
776
777 mIsModified = false;
778}
779
780// Revert the Document to the last saved version on disk
781void LedItDocument::DoRevert ()
782{
783 fTextStore.Replace (fTextStore.GetStart (), fTextStore.GetEnd (), LED_TCHAR_OF (""), 0);
784 fCommandHandler.Commit ();
785 DoReadCode ();
786 fCommandHandler.Commit ();
787 mIsModified = false;
788}
789
790// Print the contents of the Document
791void LedItDocument::DoPrint ()
792{
793 LPrintout* thePrintout = LPrintout::CreatePrintout (prto_TextDoc);
794 thePrintout->SetPrintSpec (mPrintSpec);
795 LPlaceHolder* textPlace = (LPlaceHolder*)thePrintout->FindPaneByID ('TBox');
796
797 LedItView editorView;
798 editorView.AddAttributes (textAttr_MultiStyle | textAttr_Editable | textAttr_Selectable | textAttr_WordWrap);
799 editorView.SpecifyTextStore (&fTextStore);
800 editorView.SetStyleDatabase (fStyleDatabase);
801 editorView.SetParagraphDatabase (fParagraphDatabase);
802 editorView.SetHidableTextDatabase (fHidableTextDatabase);
803 editorView.SetScrollBarType (Led_PPView::v, Led_PPView::eScrollBarNever);
804 editorView.SetScrollBarType (Led_PPView::h, Led_PPView::eScrollBarNever);
805 textPlace->InstallOccupant (&editorView, atNone);
806 editorView.FinishCreate ();
807 editorView.SetForceAllRowsShowing (false);
808
809 thePrintout->DoPrintJob ();
810 delete thePrintout;
811}
812
813void LedItDocument::PurgeUnneededMemory ()
814{
815 // If we are running out of memory, best to commit all our existing commands, to free some up.
816 fCommandHandler.Commit ();
817
818 // Also, we can punt cached linebreak etc information
819 AssertNotNull (fTextView);
820 fTextView->PurgeUnneededMemory ();
821}
822
823void LedItDocument::BuildDocWindow (const FSSpec* inFileSpec)
824{
825 Assert (mWindow == NULL);
826
827 // GetNewCWindow() seems to crash when we are nearly out of RAM in our local heap (rather
828 // than just returning NULL. So try to avoid that situation.
829 Led_CheckSomeLocalHeapRAMAvailable ();
830
831 mWindow = new LedItDocumentWindow (WIND_TextDoc,
832 windAttr_Regular | windAttr_CloseBox | windAttr_TitleBar | windAttr_Resizable | windAttr_SizeBox |
833 windAttr_Zoomable | windAttr_Enabled | windAttr_Targetable,
834 this);
835 mWindow->FinishCreate ();
836
837 // By default window created with a silly size. Don't know how todo this well within
838 // PowerPlant. Seems you have to specify size in WIND resource. But thats not what I want!
839 // Well, we'll just take matters into our own hands then...
840 {
841// Find GDevice containing largest portion of Window
842#if TARGET_CARBON
843 WindowPtr theWindPtr = ::GetWindowFromPort (mWindow->GetMacPort ());
844#else
845 WindowPtr theWindPtr = mWindow->GetMacPort ();
846#endif
847 Rect windowStructureRect = UWindows::GetWindowStructureRect (theWindPtr);
848 GDHandle dominantDevice = UWindows::FindDominantDevice (windowStructureRect);
849 Rect screenRect = (**dominantDevice).gdRect;
850 const int kSluffBetweenWindBottomAndScreenBottom = 10;
851 if (windowStructureRect.bottom + kSluffBetweenWindBottomAndScreenBottom < screenRect.bottom) {
852 Rect newWindBounds = UWindows::GetWindowContentRect (theWindPtr);
853 // being careful about size of title bar and rest of structure region, we want to incremnt content region size by
854 // how much we want to grow structure region.
855 newWindBounds.bottom += (screenRect.bottom - windowStructureRect.bottom - kSluffBetweenWindBottomAndScreenBottom);
856 mWindow->DoSetBounds (newWindBounds);
857 }
858 }
859
860 SDimension16 winSize;
861 mWindow->GetFrameSize (winSize);
862
863 {
864 Rect minMax;
865 minMax.top = 72; // minHeight
866 minMax.left = 72; // minWidth
867 minMax.bottom = 10 * 1024; // maxHeight
868 minMax.right = 10 * 1024; // maxWidth;
869 mWindow->SetMinMaxSize (minMax);
870 }
871
872 LedItView* editorView = new LedItView ();
873
874 editorView->AddAttributes (textAttr_MultiStyle | textAttr_Editable | textAttr_Selectable | textAttr_WordWrap);
875 editorView->SpecifyTextStore (&fTextStore);
876 editorView->SetStyleDatabase (fStyleDatabase);
877 editorView->SetParagraphDatabase (fParagraphDatabase);
878 editorView->SetHidableTextDatabase (fHidableTextDatabase);
879 editorView->SetCommandHandler (&fCommandHandler);
880 editorView->SetSpellCheckEngine (&LedItApplication::Get ().fSpellCheckEngine);
881 editorView->PutInside (mWindow);
882 editorView->PlaceInSuperFrameAt (0, 0, false);
883 editorView->ResizeFrameTo (winSize.width, winSize.height, false);
884 SBooleanRect editViewBindings;
885 editViewBindings.top = true;
886 editViewBindings.left = true;
887 editViewBindings.bottom = true;
888 editViewBindings.right = true;
889 editorView->SetFrameBinding (editViewBindings);
890 editorView->FinishCreate ();
891
892 fTextView = editorView;
893 mWindow->SetLatentSub (editorView); // make editorView get the focus when window shown...
894
895 if (inFileSpec == NULL) {
896 NameNewDoc (); // Set name of untitled window
897 }
898 else {
899 OpenFile (*inFileSpec); // Display contents of file in window
900 }
901
902 mWindow->Show ();
903}
904
905void LedItDocument::DoReadCode ()
906{
907 Led_BusyCursor busyCursor;
908
909 StScrpHandle styleInfo = NULL;
910 try {
911 short curResFile = ::CurResFile ();
912 mFile->OpenResourceFork (fsRdPerm);
913 ::UseResFile (mFile->GetResourceForkRefNum ());
914 styleInfo = (StScrpHandle)::Get1Resource ('styl', 128);
915 if (styleInfo != NULL) {
916 ::DetachResource ((Handle)styleInfo);
917 }
918 ::UseResFile (curResFile);
919 mFile->CloseResourceFork ();
920 }
921 catch (...) {
922 }
923 try {
924 ThrowIfOSErr_ (::SetFPos (mFile->GetDataForkRefNum (), fsFromStart, 0));
925 StyledTextIOSrcStream_FileDescriptor source (mFile->GetDataForkRefNum (), Handle (styleInfo));
926 WordProcessor::WordProcessorTextIOSinkStream sink (&fTextStore, fStyleDatabase, fParagraphDatabase, fHidableTextDatabase);
927
928 ReRead:
929 switch (fFileFormat) {
930 case eTextFormat: {
931 source.SetBufferSize (0); // no need for buffering with these readers
932 if (styleInfo == NULL) {
933 StyledTextIOReader_PlainText textReader (&source, &sink);
934 textReader.Read ();
935 }
936 else {
937 StyledTextIOReader_STYLText textReader (&source, &sink);
938 textReader.Read ();
939 }
940 } break;
941
942 case eRTFFormat: {
943 source.SetBufferSize (16 * 1024); // reads a byte at a time - buffering helps ALOT
944 StyledTextIOReader_RTF textReader (&source, &sink);
945 textReader.Read ();
946 } break;
947
948 case eHTMLFormat: {
949 source.SetBufferSize (16 * 1024); // reads a byte at a time - buffering helps ALOT
950 StyledTextIOReader_HTML textReader (&source, &sink, &fHTMLInfo);
951 textReader.Read ();
952 } break;
953
954 case eLedPrivateFormat: {
955 // unsure if buffering would help here or not - try both ways - LGP 960902
956 StyledTextIOReader_LedNativeFileFormat textReader (&source, &sink);
957 textReader.Read ();
958 } break;
959
960 default: {
961 /*
962 * Should enhance this unknown/format reading code to take into
963 * account file suffix in our guess. Also should take into account
964 * FINDER File-Type info.
965 */
966
967 // for unknown file format,
968 // first try RTF
969 // then Led private format
970 // first try HTML
971 // then try to read as plain text
972 source.SetBufferSize (1 * 1024); // most/all of these quicklook routines only examine the first 1K or so
973
974 // Try RTF
975 try {
976 StyledTextIOReader_RTF reader (&source, &sink);
977 if (reader.QuickLookAppearsToBeRightFormat ()) {
978 fFileFormat = eRTFFormat;
979 goto ReRead;
980 }
981 }
982 catch (...) {
983 // ignore any errors, and proceed to next file type
984 }
985
986 // Try LedNativeFileFormat
987 try {
988 StyledTextIOReader_LedNativeFileFormat reader (&source, &sink);
989 if (reader.QuickLookAppearsToBeRightFormat ()) {
990 fFileFormat = eLedPrivateFormat;
991 goto ReRead;
992 }
993 }
994 catch (...) {
995 // ignore any errors, and proceed to next file type
996 }
997
998 // Try HTML
999 try {
1000 StyledTextIOReader_HTML reader (&source, &sink);
1001 if (reader.QuickLookAppearsToBeRightFormat ()) {
1002 fFileFormat = eHTMLFormat;
1003 goto ReRead;
1004 }
1005 }
1006 catch (...) {
1007 // ignore any errors, and proceed to next file type
1008 }
1009
1010 // Nothing left todo but to read the text file as plain text, as best we can...
1011 fFileFormat = eTextFormat;
1012 goto ReRead;
1013 } break;
1014 }
1015 }
1016 catch (...) {
1017 if (styleInfo != NULL) {
1018 ::DisposeHandle ((Handle)styleInfo);
1019 }
1020 throw;
1021 }
1022 if (styleInfo != NULL) {
1023 ::DisposeHandle ((Handle)styleInfo);
1024 }
1025}
1026
1027Boolean LedItDocument::ObeyCommand (CommandT inCommand, void* ioParam)
1028{
1029 Boolean cmdHandled = true;
1030
1031 switch (inCommand) {
1032 case kCmdSaveACopyAs: {
1033 OnSaveACopyAsCommand ();
1034 } break;
1035
1036 default:
1037 cmdHandled = LSingleDoc::ObeyCommand (inCommand, ioParam);
1038 break;
1039 }
1040
1041 return cmdHandled;
1042}
1043
1044// Pass back status of a (menu) command
1045void LedItDocument::FindCommandStatus (CommandT inCommand, Boolean& outEnabled, Boolean& outUsesMark, UInt16& outMark, Str255 outName)
1046{
1047 outUsesMark = false;
1048 switch (inCommand) {
1049 case kCmdSaveACopyAs: {
1050 outEnabled = true;
1051 } break;
1052
1053 default:
1054 LSingleDoc::FindCommandStatus (inCommand, outEnabled, outUsesMark, outMark, outName);
1055 break;
1056 }
1057}
1058
1059void LedItDocument::OnSaveACopyAsCommand ()
1060{
1061 FilteredSFPutDLog filteredPicker (sPutFileTypeList, (sizeof sPutFileTypeList) / (sizeof sPutFileTypeList[0]));
1062
1063 Str255 defaultName;
1064 GetDescriptor (defaultName);
1065 size_t typeIndex = MapPutFileFormatToTypeListIdx (fFileFormat);
1066 FSSpec fileResult;
1067 bool replacingFile = false;
1068 if (filteredPicker.PickFile (defaultName, &fileResult, &replacingFile, &typeIndex)) {
1069 if (replacingFile) { // Delete existing file
1070 ThrowIfOSErr_ (::FSpDelete (&fileResult));
1071 }
1072
1073 FileFormat savedFileFormat = fFileFormat;
1074 LFile* savedFile = mFile;
1075
1076 fFileFormat = MapPutFileTypeListIdxToFormat (typeIndex);
1077 try {
1078 mFile = new LFile (fileResult); // Make a temporary file object
1079
1080 OSType fileType = MapFormatToOSType (fFileFormat); // Find proper file type
1081
1082 // Make new file on disk
1083 mFile->CreateNewDataFile (kApplicationSignature, fileType, 0);
1084 mFile->OpenDataFork (fsRdWrPerm);
1085 DoSaveHelper ();
1086 }
1087 catch (...) {
1088 delete mFile;
1089 mFile = savedFile;
1090 fFileFormat = savedFileFormat;
1091 throw;
1092 }
1093 fFileFormat = savedFileFormat;
1094 delete mFile;
1095 mFile = savedFile;
1096 }
1097}
1098#endif
1099
1100#if qStroika_Foundation_Common_Platform_Windows
1101BOOL LedItDocument::OnNewDocument ()
1102{
1103 fCommandHandler.Commit ();
1104 if (!COleServerDoc::OnNewDocument ()) {
1105 return FALSE;
1106 }
1107 fFileFormat = eDefaultFormat;
1108 fStyleDatabase = MakeSharedPtr<StyleDatabaseRep> (fTextStore);
1109 fParagraphDatabase = MakeSharedPtr<ParagraphDatabaseRep> (fTextStore);
1110 fHidableTextDatabase = MakeSharedPtr<UniformHidableTextMarkerOwner> (fTextStore);
1111 return TRUE;
1112}
1113
1114COleServerItem* LedItDocument::OnGetEmbeddedItem ()
1115{
1116 // OnGetEmbeddedItem is called by the framework to get the COleServerItem
1117 // that is associated with the document. It is only called when necessary.
1118 LedItServerItem* pItem = new LedItServerItem (this);
1119 ASSERT_VALID (pItem);
1120 return pItem;
1121}
1122
1123BOOL LedItDocument::DoSave (LPCTSTR lpszPathName, BOOL bReplace)
1124{
1125 FileFormat fileFormat = fFileFormat;
1126
1127 CString newName = lpszPathName;
1128 if (newName.IsEmpty ()) {
1129 CDocTemplate* pTemplate = GetDocTemplate ();
1130 ASSERT (pTemplate != NULL);
1131 newName = m_strPathName;
1132 if (bReplace && newName.IsEmpty ()) {
1133 newName = m_strTitle;
1134 // check for dubious filename
1135 int iBad = newName.FindOneOf (_T(" #%;/\\"));
1136 if (iBad != -1) {
1137 newName.ReleaseBuffer (iBad);
1138 }
1139#if 0
1140 // append the default suffix if there is one
1141 CString strExt;
1142 if (pTemplate->GetDocString(strExt, CDocTemplate::filterExt) &&
1143 !strExt.IsEmpty()) {
1144 ASSERT(strExt[0] == '.');
1145 newName += strExt;
1146 }
1147#endif
1148 }
1149
1150 if (bReplace) {
1151 if (not DoPromptSaveAsFileName (newName, &fileFormat)) {
1152 return false;
1153 }
1154 }
1155 else {
1156 if (not DoPromptSaveCopyAsFileName (newName, &fileFormat)) {
1157 return false;
1158 }
1159 }
1160 }
1161
1162 CWaitCursor wait;
1163
1164 // During the actual save, we must temporarily reset the fFileFormat field so we
1165 // know what format to write. Don't make the change permanent til much later, when the
1166 // write has succeeded, and we know that this was a save, and not a save-a-copy call.
1167 FileFormat realSavedDocFormat = fFileFormat;
1168 fFileFormat = fileFormat;
1169 try {
1170 if (!OnSaveDocument (newName)) {
1171 if (lpszPathName == NULL) {
1172 // be sure to delete the file
1173 TRY
1174 {
1175 CFile::Remove (newName);
1176 }
1177 CATCH_ALL (e)
1178 {
1179 TRACE0 ("Warning: failed to delete file after failed SaveAs.\n");
1180 DELETE_EXCEPTION (e);
1181 }
1182 END_CATCH_ALL
1183 }
1184 return FALSE;
1185 }
1186 }
1187 catch (...) {
1188 fFileFormat = realSavedDocFormat;
1189 throw;
1190 }
1191 fFileFormat = realSavedDocFormat;
1192
1193 // reset the title and change the document name
1194 if (bReplace) {
1195 SetPathName (newName);
1196 fFileFormat = fileFormat;
1197 }
1198
1199 return TRUE; // success
1200}
1201
1202void LedItDocument::Serialize (CArchive& ar)
1203{
1204 if (ar.IsStoring ()) {
1205 Require (fFileFormat != eUnknownFormat); // We must have chosen a file format by now...
1206
1207 WordProcessorTextIOSrcStream source{&fTextStore, fStyleDatabase, fParagraphDatabase, fHidableTextDatabase};
1208 StyledTextIOWriterSinkStream_Memory sink;
1209
1210 switch (fFileFormat) {
1211 case eTextFormat: {
1212 StyledTextIOWriter_PlainText textWriter{&source, &sink};
1213 textWriter.Write ();
1214 } break;
1215
1216 case eRTFFormat: {
1217 StyledTextIOWriter_RTF textWriter{&source, &sink, &fRTFInfo};
1218 textWriter.Write ();
1219 } break;
1220
1221 case eHTMLFormat: {
1222 StyledTextIOWriter_HTML textWriter{&source, &sink, &fHTMLInfo};
1223 textWriter.Write ();
1224 } break;
1225
1226 case eLedPrivateFormat: {
1227 StyledTextIOWriter_LedNativeFileFormat textWriter{&source, &sink};
1228 textWriter.Write ();
1229 } break;
1230
1231 default: {
1232 Assert (false); // don't support writing that format (yet?)!
1233 } break;
1234 }
1235 ar.Write ((char*)sink.PeekAtData (), static_cast<UINT> (sink.GetLength ()));
1236 }
1237 else {
1238 // NOTE - AS OF CHANGE FOR SPR#1552- this code for READING docs is probably not called
1239 // anymore. Should get eliminated/cleaned up in some future release...
1240 // -- LGP 2003-09-22
1241 {
1242 // Peculiar performance hack. Lots of parts of Led will do 'Refresh' to mark the
1243 // screen as needing redisplay. If done enuf times, these can be expensive. So Led is
1244 // 'clever' - and notices that if the entire screen is invalid, in need not do the elaborate
1245 // computations about what needs further invalidating. This initial call to Refresh () call
1246 // won't REALLY end up doing any extra redisplay, cuz we're reading in a new file, and would be
1247 // redisplaying the whole window anyhow. But it MIGHT save us a bit of time not calculating what
1248 // little bits of the screen to redisplay.
1249 // LGP 970619
1250 POSITION pos = GetFirstViewPosition ();
1251 for (CView* p = GetNextView (pos); p != NULL; p = GetNextView (pos)) {
1252 LedItView* v = dynamic_cast<LedItView*> (p);
1253 if (v != NULL) {
1254 v->Refresh ();
1255 }
1256 }
1257 }
1258 CFile* file = ar.GetFile ();
1259 ASSERT_VALID (file);
1260 DWORD nLen = static_cast<DWORD> (file->GetLength ()); // maybe should subtract current offset?
1261 StackBuffer<char> buf{Memory::eUninitialized, nLen};
1262 if (ar.Read (buf.data (), nLen) != nLen) {
1263 AfxThrowArchiveException (CArchiveException::endOfFile);
1264 }
1265 StyledTextIOSrcStream_Memory source{buf.data (), nLen};
1266 WordProcessorTextIOSinkStream sink{&fTextStore, fStyleDatabase, fParagraphDatabase, fHidableTextDatabase};
1267
1268 ReRead:
1269 switch (fFileFormat) {
1270 case eTextFormat: {
1271 StyledTextIOReader_PlainText textReader{&source, &sink};
1272 textReader.Read ();
1273 } break;
1274
1275 case eLedPrivateFormat: {
1276 LedItControlItem::DocContextDefiner tmp{this};
1277 StyledTextIOReader_LedNativeFileFormat textReader{&source, &sink};
1278 textReader.Read ();
1279 } break;
1280
1281 case eRTFFormat: {
1282 LedItControlItem::DocContextDefiner tmp{this};
1283 StyledTextIOReader_RTF textReader{&source, &sink, &fRTFInfo};
1284 textReader.Read ();
1285 } break;
1286
1287 case eHTMLFormat: {
1288 StyledTextIOReader_HTML textReader{&source, &sink, &fHTMLInfo};
1289 textReader.Read ();
1290 } break;
1291
1292 case eUnknownFormat: {
1293 /*
1294 * Should enhance this unknown/format reading code to take into account file suffix in our guess.
1295 */
1296
1297 // Try RTF
1298 try {
1299 StyledTextIOReader_RTF reader{&source, &sink, &fRTFInfo};
1300 if (reader.QuickLookAppearsToBeRightFormat ()) {
1301 fFileFormat = eRTFFormat;
1302 goto ReRead;
1303 }
1304 }
1305 catch (...) {
1306 // ignore any errors, and proceed to next file type
1307 }
1308
1309 // Try LedNativeFileFormat
1310 try {
1311 StyledTextIOReader_LedNativeFileFormat reader{&source, &sink};
1312 if (reader.QuickLookAppearsToBeRightFormat ()) {
1313 fFileFormat = eLedPrivateFormat;
1314 goto ReRead;
1315 }
1316 }
1317 catch (...) {
1318 // ignore any errors, and proceed to next file type
1319 }
1320
1321 // Try HTML
1322 try {
1323 StyledTextIOReader_HTML reader{&source, &sink};
1324 if (reader.QuickLookAppearsToBeRightFormat ()) {
1325 fFileFormat = eHTMLFormat;
1326 goto ReRead;
1327 }
1328 }
1329 catch (...) {
1330 // ignore any errors, and proceed to next file type
1331 }
1332
1333 // Nothing left todo but to read the text file as plain text, as best we can...
1334 fFileFormat = eTextFormat;
1335 goto ReRead;
1336 } break;
1337
1338 default: {
1339 Assert (false); // don't support reading that format (yet?)!
1340 } break;
1341 }
1342 sink.Flush (); // explicit Flush () call - DTOR would have done it - but there exceptions get silently eaten - this will at least show them...
1343 }
1344}
1345
1346BOOL LedItDocument::OnOpenDocument (LPCTSTR lpszPathName)
1347{
1348 {
1349 // Peculiar performance hack. Lots of parts of Led will do 'Refresh' to mark the
1350 // screen as needing redisplay. If done enuf times, these can be expensive. So Led is
1351 // 'clever' - and notices that if the entire screen is invalid, in need not do the elaborate
1352 // computations about what needs further invalidating. This initial call to Refresh () call
1353 // won't REALLY end up doing any extra redisplay, cuz we're reading in a new file, and would be
1354 // redisplaying the whole window anyhow. But it MIGHT save us a bit of time not calculating what
1355 // little bits of the screen to redisplay.
1356 // LGP 970619
1357 POSITION pos = GetFirstViewPosition ();
1358 for (CView* p = GetNextView (pos); p != NULL; p = GetNextView (pos)) {
1359 LedItView* v = dynamic_cast<LedItView*> (p);
1360 if (v != NULL) {
1361 v->Refresh ();
1362 }
1363 }
1364 }
1365
1366 fCommandHandler.Commit ();
1367 fStyleDatabase = MakeSharedPtr<StyleDatabaseRep> (fTextStore);
1368 fParagraphDatabase = MakeSharedPtr<ParagraphDatabaseRep> (fTextStore);
1369 fHidableTextDatabase = MakeSharedPtr<UniformHidableTextMarkerOwner> (fTextStore);
1370
1371 // Slight performance hack - get rid of existing style/etc dbases for the current view
1372 {
1373 POSITION pos = GetFirstViewPosition ();
1374 while (pos != NULL) {
1375 CView* pView = GetNextView (pos);
1376 pView->OnInitialUpdate ();
1377 }
1378 }
1379
1380 WordProcessor::WordProcessorFlavorPackageInternalizer internalizer (fTextStore, fStyleDatabase, fParagraphDatabase, fHidableTextDatabase);
1381
1382 Led_ClipFormat readFileFormat = kBadClipFormat; // defaults to GUESSING file format on READ (or based on file name)
1383
1384 /*
1385 * Because we need to vector through a bunch of layers of MFC code which doesn't pass along this format
1386 * inforamtion, we needed some hack to get it from where we knew the file type to here, where
1387 * we open the file. The application/DocMgr code made it hard to get/propagate the file type info
1388 * but we managed that. Were we dropped the ball was on the DocTemplate code. That looked too complex
1389 * to clone/redo to pass this info along, so when we get to the point of saying template->OpenDocument()
1390 * we first set this sHiddenDocOpenArg argument.
1391 */
1392 switch (sHiddenDocOpenArg) {
1393 case eTextFormat:
1394 readFileFormat = kTEXTClipFormat;
1395 break;
1396 case eLedPrivateFormat:
1397 readFileFormat = kLedPrivateClipFormat;
1398 break;
1399 case eRTFFormat:
1400 readFileFormat = kRTFClipFormat;
1401 break;
1402 case eHTMLFormat:
1403 readFileFormat = kHTMLClipFormat;
1404 break;
1405 }
1406
1407 internalizer.InternalizeFlavor_FILEData (lpszPathName, &readFileFormat, NULL, 0, fTextStore.GetEnd ());
1408
1409 // Set document file type based on the format READ from the disk...
1410 if (readFileFormat == kTEXTClipFormat) {
1411 fFileFormat = eTextFormat;
1412 }
1413 else if (readFileFormat == kLedPrivateClipFormat) {
1414 fFileFormat = eLedPrivateFormat;
1415 }
1416 else if (readFileFormat == kRTFClipFormat) {
1417 fFileFormat = eRTFFormat;
1418 }
1419 else if (readFileFormat == kHTMLClipFormat) {
1420 fFileFormat = eHTMLFormat;
1421 }
1422 else {
1423 // If we read some other format - then just set our file format to DEFAULT for the next write
1424 fFileFormat = eDefaultFormat;
1425 }
1426
1427 SetModifiedFlag (FALSE); // start off with doc unmodified
1428
1429 return true;
1430}
1431
1432void LedItDocument::OnUpdateFileSave (CCmdUI* pCmdUI)
1433{
1434 ASSERT_VALID (this);
1435 RequireNotNull (pCmdUI);
1436 // only enable save command if dirty, or no file name associated with this document
1437 pCmdUI->Enable (IsModified () or GetPathName ().GetLength () == 0);
1438}
1439
1440void LedItDocument::OnFileSaveCopyAs ()
1441{
1442 ASSERT_VALID (this);
1443 Assert (m_bRemember);
1444
1445 LPSTORAGE savedStorage = m_lpRootStg;
1446 m_lpRootStg = NULL;
1447
1448 FileFormat savedFileFormat = fFileFormat;
1449
1450 try {
1451 DoSave (NULL, false);
1452 }
1453 catch (...) {
1454 m_lpRootStg = savedStorage;
1455 m_bRemember = true;
1456 fFileFormat = savedFileFormat;
1457 throw;
1458 }
1459
1460 m_lpRootStg = savedStorage;
1461 m_bRemember = true;
1462 fFileFormat = savedFileFormat;
1463}
1464
1465void LedItDocument::DeleteContents ()
1466{
1467 fTextStore.Replace (fTextStore.GetStart (), fTextStore.GetEnd (), LED_TCHAR_OF (""), 0);
1468}
1469
1470bool LedItDocument::DoPromptSaveAsFileName (CString& fileName, FileFormat* fileFormat)
1471{
1472 RequireNotNull (fileFormat);
1473 return DoPromptFileName (fileName, AFX_IDS_SAVEFILE, false, OFN_HIDEREADONLY | OFN_PATHMUSTEXIST, fileFormat);
1474}
1475
1476bool LedItDocument::DoPromptSaveCopyAsFileName (CString& fileName, FileFormat* fileFormat)
1477{
1478 RequireNotNull (fileFormat);
1479 return DoPromptFileName (fileName, AFX_IDS_SAVEFILECOPY, false, OFN_HIDEREADONLY | OFN_PATHMUSTEXIST, fileFormat);
1480}
1481
1482bool LedItDocument::DoPromptOpenFileName (CString& fileName, FileFormat* fileFormat)
1483{
1484 RequireNotNull (fileFormat);
1485 return DoPromptFileName (fileName, AFX_IDS_OPENFILE, true, OFN_HIDEREADONLY | OFN_FILEMUSTEXIST, fileFormat);
1486}
1487
1488bool LedItDocument::DoPromptFileName (CString& fileName, UINT nIDSTitle, bool isOpenDialogCall, long fileDLogFlags, FileFormat* fileFormat)
1489{
1490 RequireNotNull (fileFormat);
1491 CFileDialog dlgFile{isOpenDialogCall};
1492
1493 CString title;
1494 Verify (title.LoadString (nIDSTitle));
1495
1496 dlgFile.m_ofn.Flags |= fileDLogFlags;
1497
1498 CString strFilter;
1499
1500 CDocTemplate* ledItPrivateDocFormatTemplate = NULL;
1501 {
1502 // do for all doc template
1503 POSITION pos = AfxGetApp ()->m_pDocManager->GetFirstDocTemplatePosition ();
1504 ledItPrivateDocFormatTemplate = AfxGetApp ()->m_pDocManager->GetNextDocTemplate (pos);
1505 }
1506 if (nIDSTitle == AFX_IDS_OPENFILE) {
1507 AppendFilterSuffix (strFilter, dlgFile.m_ofn, "*.htm;*.html;*.led;*.rtf;*.txt;", "All Recognized (*.htm;*.html;*.led;*.rtf;*.txt)");
1508 AppendFilterSuffix (strFilter, dlgFile.m_ofn, ".*", "All Files (*.*)");
1509 }
1510 AppendFilterSuffix (strFilter, dlgFile.m_ofn, "*.htm;*.html", "HTML file (*.htm;*.html)");
1511 AppendFilterSuffix (strFilter, dlgFile.m_ofn, ledItPrivateDocFormatTemplate);
1512 AppendFilterSuffix (strFilter, dlgFile.m_ofn, "*.rtf", "Microsoft Rich Text Format (*.rtf)");
1513 AppendFilterSuffix (strFilter, dlgFile.m_ofn, "*.txt", "Plain Text (*.txt)");
1514
1515 strFilter += (TCHAR)'\0'; // last string
1516
1517 dlgFile.m_ofn.lpstrFilter = strFilter;
1518 dlgFile.m_ofn.lpstrTitle = title;
1519
1520 // MAYBE SHOULD ELIMINATE TYPE-SUFFIX???
1521 dlgFile.m_ofn.lpstrFile = fileName.GetBuffer (_MAX_PATH);
1522
1523 if (nIDSTitle == AFX_IDS_OPENFILE) {
1524 dlgFile.m_ofn.nFilterIndex = 1; // default to "All Recognized"
1525 }
1526 else {
1527 FileFormat initialFormat = *fileFormat;
1528 if (initialFormat == eUnknownFormat) {
1529 initialFormat = eDefaultFormat;
1530 }
1531 switch (initialFormat) {
1532 case eHTMLFormat:
1533 dlgFile.m_ofn.nFilterIndex = 1;
1534 break;
1535 case eLedPrivateFormat:
1536 dlgFile.m_ofn.nFilterIndex = 2;
1537 break;
1538 case eRTFFormat:
1539 dlgFile.m_ofn.nFilterIndex = 3;
1540 break;
1541 case eTextFormat:
1542 dlgFile.m_ofn.nFilterIndex = 4;
1543 break;
1544 default:
1545 Assert (false);
1546 break;
1547 }
1548 }
1549 bool bResult = (dlgFile.DoModal () == IDOK);
1550 fileName.ReleaseBuffer ();
1551 if (bResult) {
1552 if (nIDSTitle == AFX_IDS_OPENFILE) {
1553 switch (dlgFile.m_ofn.nFilterIndex) {
1554 case 1:
1555 *fileFormat = eUnknownFormat;
1556 break;
1557 case 2:
1558 *fileFormat = eUnknownFormat;
1559 break;
1560 case 3:
1561 *fileFormat = eHTMLFormat;
1562 break;
1563 case 4:
1564 *fileFormat = eLedPrivateFormat;
1565 break;
1566 case 5:
1567 *fileFormat = eRTFFormat;
1568 break;
1569 case 6:
1570 *fileFormat = eTextFormat;
1571 break;
1572 default:
1573 *fileFormat = eUnknownFormat;
1574 break;
1575 }
1576 }
1577 else {
1578 switch (dlgFile.m_ofn.nFilterIndex) {
1579 case 1:
1580 *fileFormat = eHTMLFormat;
1581 break;
1582 case 2:
1583 *fileFormat = eLedPrivateFormat;
1584 break;
1585 case 3:
1586 *fileFormat = eRTFFormat;
1587 break;
1588 case 4:
1589 *fileFormat = eTextFormat;
1590 break;
1591 default:
1592 *fileFormat = eUnknownFormat;
1593 break;
1594 }
1595 }
1596
1597 // If saving a file, and user specified no extension, we add one. Not 100% sure this is the right way todo this.
1598 // But seems more often than not to be right... LGP 971019
1599 if (not(fileDLogFlags & OFN_FILEMUSTEXIST)) {
1600 if (dlgFile.GetFileExt () == "") {
1601 switch (*fileFormat) {
1602 case eTextFormat:
1603 fileName += ".txt";
1604 break;
1605 case eRTFFormat:
1606 fileName += ".rtf";
1607 break;
1608 case eHTMLFormat:
1609 fileName += ".html";
1610 break;
1611 case eLedPrivateFormat:
1612 fileName += ".led";
1613 break;
1614 }
1615 }
1616 }
1617 }
1618 return bResult;
1619}
1620
1621#if qStroika_Foundation_Debug_AssertionsChecked
1622void LedItDocument::AssertValid () const
1623{
1624 COleServerDoc::AssertValid ();
1625 fTextStore.Invariant ();
1626 //fStyleDatabase.Invariant (); Cannot do this cuz we're sometimes called at in-opportune times, while updating
1627 // the styles...called from MFC...
1628}
1629#endif
1630
1631#endif
1632
1633/*
1634 ********************************************************************************
1635 **************************** ExtractFileSuffix *********************************
1636 ********************************************************************************
1637 */
1638SDKString ExtractFileSuffix (const SDKString& from)
1639{
1640 size_t i = from.rfind ('.');
1641 if (i == SDKString::npos) {
1642 return SDKString{};
1643 }
1644 else {
1645 SDKString suffix = from.substr (i);
1646 for (size_t j = 0; j < suffix.length (); ++j) {
1647 if (isascii (suffix[j]) and isupper (suffix[j])) {
1648 suffix[j] = static_cast<Led_tChar> (tolower (suffix[j]));
1649 }
1650 }
1651 return suffix;
1652 }
1653}
1654
1655#if qStroika_Foundation_Common_Platform_Windows
1656/*
1657 ********************************************************************************
1658 ******************************** AppendFilterSuffix ****************************
1659 ********************************************************************************
1660 */
1661
1662static void AppendFilterSuffix (CString& filter, OPENFILENAME& ofn, CString strFilterExt, CString strFilterName)
1663{
1664 Require (not strFilterExt.IsEmpty ());
1665 Require (not strFilterName.IsEmpty ());
1666
1667 // add to filter
1668 filter += strFilterName;
1669 ASSERT (!filter.IsEmpty ()); // must have a file type name
1670 filter += (TCHAR)'\0'; // next string please
1671 filter += (TCHAR)'*';
1672 filter += strFilterExt;
1673 filter += (TCHAR)'\0'; // next string please
1674 ++ofn.nMaxCustFilter;
1675}
1676
1677static void AppendFilterSuffix (CString& filter, OPENFILENAME& ofn, CDocTemplate* pTemplate)
1678{
1679 ASSERT_VALID (pTemplate);
1680 ASSERT_KINDOF (CDocTemplate, pTemplate);
1681 CString strFilterExt;
1682 CString strFilterName;
1683 if (pTemplate->GetDocString (strFilterExt, CDocTemplate::filterExt) && !strFilterExt.IsEmpty () &&
1684 pTemplate->GetDocString (strFilterName, CDocTemplate::filterName) && !strFilterName.IsEmpty ()) {
1685 AppendFilterSuffix (filter, ofn, strFilterExt, strFilterName);
1686 }
1687}
1688
1689#endif
#define AssertNotNull(p)
Definition Assertions.h:334
#define RequireNotNull(p)
Definition Assertions.h:348
#define Verify(c)
Definition Assertions.h:420
Logically halfway between std::array and std::vector; Smart 'direct memory array' - which when needed...
basic_string< SDKChar > SDKString
Definition SDKString.h:38