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