Stroika Library 3.0d18
 
Loading...
Searching...
No Matches
LedLineItDocument.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4
5#include "Stroika/Foundation/StroikaPreComp.h"
6
7#include <afxwin.h>
8
11#include "Stroika/Foundation/Characters/LineEndings.h"
13
14#include "Stroika/Frameworks/Led/FlavorPackage.h"
15
16#include "LedLineItServerItem.h"
17#include "Resource.h"
18
19#include "LedLineItDocument.h"
20
21using std::byte;
22
23using namespace Stroika::Foundation;
24using namespace Stroika::Foundation::Streams;
25using namespace Stroika::Frameworks::Led;
26
27using Stroika::Foundation::Characters::CodePagePrettyNameMapper;
29
30// special exception handling just for MFC library implementation
31// copied here so I could clone MFC code as needed - not well understood - UGH!!! - LGP 951227
32#ifndef _AFX_OLD_EXCEPTIONS
33#define DELETE_EXCEPTION(e) \
34 do { \
35 e->Delete (); \
36 } while (0)
37#else //!_AFX_OLD_EXCEPTIONS
38#define DELETE_EXCEPTION(e)
39#endif //_AFX_OLD_EXCEPTIONS
40
41static void AppendFilterSuffix (CString& filter, OPENFILENAME& ofn, CString strFilterExt, CString strFilterName);
42static void AppendFilterSuffix (CString& filter, OPENFILENAME& ofn, CDocTemplate* pTemplate);
43
44static SDKString MapCodePageToPrettyName (CodePage cp)
45{
46 switch (cp) {
47 case kAutomaticallyGuessCodePage:
48 return Led_SDK_TCHAROF ("Automaticly Detect");
49 default:
50 return CodePagePrettyNameMapper::GetName (cp);
51 }
52}
53
54static bool ShuffleToFront (vector<CodePage>* codePages, CodePage cp)
55{
56 vector<CodePage>::iterator i = std::find (codePages->begin (), codePages->end (), cp);
57 if (i != codePages->end ()) {
58 codePages->erase (i);
59 codePages->insert (codePages->begin (), cp);
60 return true;
61 }
62 return false;
63}
64
65class FileDialogWithCodePage : public CFileDialog {
66private:
67 using inherited = CFileDialog;
68
69public:
70 FileDialogWithCodePage (bool asOpenDialog, const vector<CodePage>& codePages, CodePage initialCodePage)
71 : CFileDialog{asOpenDialog}
72 , fCodePages{codePages}
73 , fCodePage{initialCodePage}
74 {
75 m_ofn.Flags |= OFN_ENABLETEMPLATE | OFN_EXPLORER;
76 m_ofn.lpTemplateName = m_lpszTemplateName = MAKEINTRESOURCE (kFileDialogAddOnID);
77 }
78
79 virtual BOOL OnInitDialog () override
80 {
81 inherited::OnInitDialog ();
82 fCodePageComboBox.SubclassWindow (::GetDlgItem (GetSafeHwnd (), kFileDialog_EncodingComboBox));
83 for (vector<CodePage>::const_iterator i = fCodePages.begin (); i != fCodePages.end (); ++i) {
84 fCodePageComboBox.AddString (MapCodePageToPrettyName (*i).c_str ());
85 if (*i == fCodePage) {
86 fCodePageComboBox.SetCurSel (static_cast<int> (i - fCodePages.begin ()));
87 }
88 }
89 return (true);
90 }
91
92 CodePage fCodePage;
93
94protected:
95 afx_msg void OnCodePageSelChange ()
96 {
97 int curSel = fCodePageComboBox.GetCurSel ();
98 if (curSel != CB_ERR) {
99 Assert (curSel < static_cast<int> (fCodePages.size ()));
100 fCodePage = fCodePages[curSel];
101 }
102 }
103
104private:
105 DECLARE_MESSAGE_MAP ()
106
107private:
108 vector<CodePage> fCodePages;
109 CComboBox fCodePageComboBox;
110};
111
112BEGIN_MESSAGE_MAP (FileDialogWithCodePage, CFileDialog)
113ON_CBN_SELCHANGE (kFileDialog_EncodingComboBox, OnCodePageSelChange)
114END_MESSAGE_MAP ()
115
116namespace {
117 class LineTooLongOnReadDialog : public CDialog {
118 public:
119 LineTooLongOnReadDialog (const SDKString& message, size_t breakCount)
120 : CDialog{kLineTooLongOnRead_DialogID}
121 , fMessage{message}
122 , fBreakCount{breakCount}
123 {
124 }
125 virtual BOOL OnInitDialog () override
126 {
127 BOOL result = CDialog::OnInitDialog ();
128 Led_CenterWindowInParent (m_hWnd);
129 SetDlgItemText (kLineTooLongOnRead_Dialog_MessageFieldID, fMessage.c_str ());
130 SetDlgItemInt (kLineTooLongOnRead_Dialog_BreakNumFieldID, static_cast<UINT> (fBreakCount));
131 return (result);
132 }
133 virtual void OnOK () override
134 {
135 size_t origBreakCount = fBreakCount;
136 BOOL trans = false;
137 fBreakCount = GetDlgItemInt (kLineTooLongOnRead_Dialog_BreakNumFieldID, &trans);
138 if (not trans) {
139 fBreakCount = origBreakCount;
140 }
141 CDialog::OnOK ();
142 }
143
144 private:
145 SDKString fMessage;
146
147 public:
148 size_t fBreakCount;
149
150 protected:
151 DECLARE_MESSAGE_MAP ()
152 };
153 BEGIN_MESSAGE_MAP (LineTooLongOnReadDialog, CDialog)
154 END_MESSAGE_MAP ()
155}
156
157/*
158 ********************************************************************************
159 **************************** LedLineItDocument *********************************
160 ********************************************************************************
161 */
162CodePage LedLineItDocument::sHiddenDocOpenArg = kIGNORECodePage;
163
164IMPLEMENT_DYNCREATE (LedLineItDocument, COleServerDoc)
165
166BEGIN_MESSAGE_MAP (LedLineItDocument, COleServerDoc)
167ON_UPDATE_COMMAND_UI (ID_EDIT_PASTE_LINK, OnUpdatePasteLinkMenu)
168ON_UPDATE_COMMAND_UI (ID_OLE_EDIT_CONVERT, OnUpdateObjectVerbMenu)
169ON_COMMAND (ID_OLE_EDIT_CONVERT, OnEditConvert)
170ON_UPDATE_COMMAND_UI (ID_OLE_EDIT_LINKS, OnUpdateEditLinksMenu)
171ON_COMMAND (ID_OLE_EDIT_LINKS, OnEditLinks)
172ON_UPDATE_COMMAND_UI (ID_OLE_VERB_FIRST, OnUpdateObjectVerbMenu)
173ON_UPDATE_COMMAND_UI (ID_FILE_SAVE, OnUpdateFileSave)
174ON_COMMAND (ID_FILE_SAVE_COPY_AS, OnFileSaveCopyAs)
175END_MESSAGE_MAP ()
176
177BEGIN_DISPATCH_MAP (LedLineItDocument, COleServerDoc)
178END_DISPATCH_MAP ()
179
180// Note: we add support for IID_ILedLineIt to support typesafe binding
181// from VBA. This IID must match the GUID that is attached to the
182// dispinterface in the .ODL file.
183
184// {0FC00622-28BD-11CF-899C-00AA00580324}
185static const IID IID_ILedLineIt = {0xfc00622, 0x28bd, 0x11cf, {0x89, 0x9c, 0x0, 0xaa, 0x0, 0x58, 0x3, 0x24}};
186
187BEGIN_INTERFACE_MAP (LedLineItDocument, COleServerDoc)
188INTERFACE_PART (LedLineItDocument, IID_ILedLineIt, Dispatch)
189END_INTERFACE_MAP ()
190
191LedLineItDocument::LedLineItDocument ()
192 : COleServerDoc{}
193 , MarkerOwner{}
194 , fTextStore{}
195 , fCommandHandler{kMaxNumUndoLevels}
196 , fCodePage{kDefaultNewDocCodePage}
197{
198 EnableAutomation ();
199 AfxOleLockApp ();
200
201 fTextStore.SetTextBreaker (shared_ptr<TextBreaks> (new TextBreaks_Basic_TextEditor ()));
202 fTextStore.AddMarkerOwner (this);
203}
204
205LedLineItDocument::~LedLineItDocument ()
206{
207 fTextStore.RemoveMarkerOwner (this);
208 AfxOleUnlockApp ();
209}
210
211void LedLineItDocument::DidUpdateText (const UpdateInfo& updateInfo) noexcept
212{
213 if (updateInfo.fRealContentUpdate) {
214 SetModifiedFlag ();
215 }
216}
217
218TextStore* LedLineItDocument::PeekAtTextStore () const
219{
220 return &const_cast<LedLineItDocument*> (this)->fTextStore;
221}
222
223BOOL LedLineItDocument::OnNewDocument ()
224{
225 fCommandHandler.Commit ();
226 if (!COleServerDoc::OnNewDocument ()) {
227 return FALSE;
228 }
229 return TRUE;
230}
231
232COleServerItem* LedLineItDocument::OnGetEmbeddedItem ()
233{
234 // OnGetEmbeddedItem is called by the framework to get the COleServerItem
235 // that is associated with the document. It is only called when necessary.
236 LedLineItServerItem* pItem = new LedLineItServerItem (this);
237 ASSERT_VALID (pItem);
238 return pItem;
239}
240
241BOOL LedLineItDocument::DoSave (LPCTSTR lpszPathName, BOOL bReplace)
242{
243 CodePage useCodePage = fCodePage;
244 CString newName = lpszPathName;
245 if (newName.IsEmpty ()) {
246 CDocTemplate* pTemplate = GetDocTemplate ();
247 ASSERT (pTemplate != NULL);
248 newName = m_strPathName;
249 if (bReplace && newName.IsEmpty ()) {
250 newName = m_strTitle;
251 // check for dubious filename
252 int iBad = newName.FindOneOf (_T(" #%;/\\"));
253 if (iBad != -1) {
254 newName.ReleaseBuffer (iBad);
255 }
256
257 // append .txt by default (SPR#1433) instead of grabbing from DocTemplate
258 if (not newName.IsEmpty ()) {
259 newName += _T (".txt");
260 }
261 }
262
263 if (bReplace) {
264 if (not DoPromptSaveAsFileName (&newName, &useCodePage)) {
265 return false;
266 }
267 }
268 else {
269 if (not DoPromptSaveCopyAsFileName (&newName, &useCodePage)) {
270 return false;
271 }
272 }
273 }
274
275 CWaitCursor wait;
276
277 CodePage savedCodePage = fCodePage;
278 fCodePage = useCodePage;
279 try {
280 if (!OnSaveDocument (newName)) {
281 if (lpszPathName == NULL) {
282 // be sure to delete the file
283 TRY
284 {
285 CFile::Remove (newName);
286 }
287 CATCH_ALL (e)
288 {
289 TRACE0 ("Warning: failed to delete file after failed SaveAs.\n");
290 DELETE_EXCEPTION (e);
291 }
292 END_CATCH_ALL
293 }
294 if (not bReplace) {
295 fCodePage = savedCodePage;
296 }
297 return FALSE;
298 }
299 if (not bReplace) {
300 fCodePage = savedCodePage;
301 }
302 }
303 catch (...) {
304 if (not bReplace) {
305 fCodePage = savedCodePage;
306 }
307 throw;
308 }
309
310 // reset the title and change the document name
311 if (bReplace) {
312 SetPathName (newName);
313 }
314
315 return TRUE; // success
316}
317
318BOOL LedLineItDocument::OnOpenDocument (LPCTSTR lpszPathName)
319{
320 /*
321 * Override this - instead of counting on default implementation - because the MFC version with
322 * Visual Studio.Net 2003 is buggy when opening read-only files (cuz they are already opened
323 * someplace else, or because they are on a CD). Do this simple override to
324 * get good/better reading files functionality.
325 *
326 * Note that this code being here probably means the READING side of the Serialize code is never
327 * executed (but leave it in there in case someone clones the code to use in another app, and there
328 * they use the serialize code).
329 *
330 * -- LGP 2003-09-22 - for SPR#1552.
331 *
332 * Also - later added further changes - such as using MyFlavorPackageInternalizer to breakup
333 * long lines, etc...
334 * -- LGP 2004-01-27
335 */
336 fCommandHandler.Commit ();
337
338 class MyFlavorPackageInternalizer : public FlavorPackageInternalizer {
339 private:
340 using inherited = FlavorPackageInternalizer;
341
342 public:
343 enum {
344 kMaxLineSize = 1024
345 };
346
347 public:
348 MyFlavorPackageInternalizer (TextStore& ts)
349 : inherited{ts}
350 , fBreakLongLines{false}
351 , fBreakWidths{kMaxLineSize}
352 {
353 }
354
355 public:
356 virtual void InternalizeFlavor_FILEGuessFormatsFromStartOfData ([[maybe_unused]] Led_ClipFormat* suggestedClipFormat,
357 optional<CodePage> suggestedCodePage, const byte* fileStart,
358 const byte* fileEnd) override
359 {
360 size_t curLineSize = 0;
361 size_t maxLineSize = 0;
362 for (const byte* p = fileStart; p != fileEnd; ++p) {
363 if (to_integer<char> (*p) == '\n' or to_integer<char> (*p) == '\r') {
364 curLineSize = 0;
365 }
366 else {
367 ++curLineSize;
368 }
369 maxLineSize = max (maxLineSize, curLineSize);
370 }
371 if (suggestedCodePage and (suggestedCodePage == Characters::WellKnownCodePages::kUNICODE_WIDE or
372 suggestedCodePage == Characters::WellKnownCodePages::kUNICODE_WIDE_BIGENDIAN)) {
373 maxLineSize /= 2; // because we'd be counting null-bytes between chars.
374 // Note this whole computation is VERY approximate - because its counting raw bytes of what could be
375 // encoded text. For example - if the text was SJIS or UTF-7 encoding of far-east text - this would
376 // greatly exagerate the estimated line size...
377 }
378
379 if (maxLineSize > kMaxLineSize) {
380 LineTooLongOnReadDialog dlg (
381 Characters::CString::Format (
382 Led_SDK_TCHAROF ("This file contains at least one very long line (approximately %d characters). This may reduce "
383 "editor performance, and make viewing the file awkward. Long lines can optionally be "
384 "automatically broken up if they exceed the 'Break at characer count' value below."),
385 maxLineSize / 100 * 100),
386 kMaxLineSize);
387 fBreakLongLines = (dlg.DoModal () == IDOK);
388 fBreakWidths = dlg.fBreakCount;
389 }
390 }
391
392 virtual bool InternalizeFlavor_FILEDataRawBytes (Led_ClipFormat* suggestedClipFormat, optional<CodePage> suggestedCodePage,
393 size_t from, size_t to, const void* rawBytes, size_t nRawBytes) override
394 {
395 Led_ClipFormat cf = (suggestedClipFormat == NULL or *suggestedClipFormat == kBadClipFormat) ? kTEXTClipFormat : *suggestedClipFormat;
396 Require (cf == kTEXTClipFormat);
397
398 fBreakWidths = max<size_t> (fBreakWidths, 1U); // assure a decent value given...
399
400 if (fBreakLongLines) {
402 using Characters::String;
403 Memory::BLOB rawBytesBLOB{span{reinterpret_cast<const byte*> (rawBytes), nRawBytes}};
404
405 String x = suggestedCodePage ? BinaryToText::Reader::New (rawBytesBLOB, CodeCvt<>{*suggestedCodePage}).ReadAll ()
406 : BinaryToText::Reader::New (rawBytesBLOB).ReadAll ();
407 x = x.NormalizeTextToNL ();
408 Led_tString tx = x.As<Led_tString> ();
409 size_t charsRead = tx.length ();
410 const auto fileData2 = span{tx};
411 {
412 StackBuffer<Led_tChar> patchedData{Memory::eUninitialized, charsRead + charsRead / fBreakWidths};
413 size_t curLineSize = 0;
414 size_t ourIdx = 0;
415 for (const Led_tChar* p = fileData2.data (); p != fileData2.data () + charsRead; ++p) {
416 if (*p == '\n' or *p == '\r') {
417 curLineSize = 0;
418 }
419 else {
420 ++curLineSize;
421 }
422 patchedData[ourIdx++] = *p;
423 if (curLineSize >= fBreakWidths) {
424 curLineSize = 0;
425 patchedData[ourIdx++] = '\n';
426 }
427 }
428 charsRead = ourIdx;
429 GetTextStore ().Replace (from, to, patchedData.data (), charsRead);
430 }
431 return true;
432 }
433 else {
434 return inherited::InternalizeFlavor_FILEDataRawBytes (suggestedClipFormat, suggestedCodePage, from, to, rawBytes, nRawBytes);
435 }
436 }
437
438 public:
439 bool fBreakLongLines;
440 size_t fBreakWidths;
441 };
442
443 MyFlavorPackageInternalizer internalizer{fTextStore};
444
445 CodePage useCodePage = fCodePage;
446 if (LedLineItDocument::sHiddenDocOpenArg != kIGNORECodePage) {
447 useCodePage = sHiddenDocOpenArg;
448 }
449 Led_ClipFormat cf = kTEXTClipFormat;
450 internalizer.InternalizeFlavor_FILEData (lpszPathName, &cf,
451 useCodePage == kAutomaticallyGuessCodePage ? optional<CodePage>{} : optional<CodePage>{useCodePage},
452 0, fTextStore.GetEnd ());
453 fCodePage = useCodePage; // use whatever codePage file was opened with... by default... for future saves
454
455 if (not internalizer.fBreakLongLines) {
456 SetModifiedFlag (FALSE); // start off with doc unmodified
457 }
458 return true;
459}
460
461void LedLineItDocument::Serialize (CArchive& ar)
462{
463 if (ar.IsStoring ()) {
464 constexpr size_t kBufSize = 8 * 1024;
465 Led_tChar buf[kBufSize];
466 size_t offset = 0;
467 size_t eob = fTextStore.GetLength ();
468 if (fCodePage == Characters::WellKnownCodePages::kUNICODE_WIDE or
469 fCodePage == Characters::WellKnownCodePages::kUNICODE_WIDE_BIGENDIAN or fCodePage == Characters::WellKnownCodePages::kUTF8) {
470 // write BOM
471 switch (fCodePage) {
472 case Characters::WellKnownCodePages::kUNICODE_WIDE:
473 ar.Write (Characters::GetByteOrderMark (Characters::UnicodeExternalEncodings::eUTF16).data (),
474 static_cast<UINT> (Characters::GetByteOrderMark (Characters::UnicodeExternalEncodings::eUTF16).size ()));
475 break;
476 case Characters::WellKnownCodePages::kUNICODE_WIDE_BIGENDIAN:
477 ar.Write (Characters::GetByteOrderMark (Characters::UnicodeExternalEncodings::eUTF16_BE).data (),
478 static_cast<UINT> (Characters::GetByteOrderMark (Characters::UnicodeExternalEncodings::eUTF16_BE).size ()));
479 break;
480 case Characters::WellKnownCodePages::kUTF8:
481 ar.Write (Characters::GetByteOrderMark (Characters::UnicodeExternalEncodings::eUTF8).data (),
482 static_cast<UINT> (Characters::GetByteOrderMark (Characters::UnicodeExternalEncodings::eUTF8).size ()));
483 break;
484 }
485 }
486 Characters::CodeCvt<Led_tChar> codeCvt{fCodePage};
487 while (offset < eob) {
488 size_t charsToWrite = min (kBufSize, eob - offset);
489 fTextStore.CopyOut (offset, charsToWrite, buf);
490 offset += charsToWrite;
491#if qStroika_Foundation_Common_Platform_Windows
492 Led_tChar buf2[2 * sizeof (buf)];
493#else
494 Led_tChar buf2[sizeof (buf)];
495#endif
496 charsToWrite = Characters::NLToNative<Led_tChar> (buf, charsToWrite, buf2, sizeof (buf2));
497 StackBuffer<byte> buf3_{Memory::eUninitialized, codeCvt.ComputeTargetByteBufferSize (span{buf, charsToWrite})};
498 auto toWrite = codeCvt.Characters2Bytes (span{buf, charsToWrite}, span{buf3_});
499 char* buffp = reinterpret_cast<char*> (buf3_.data ());
500 size_t nBytesToWrite = toWrite.size ();
501 ar.Write (buffp, static_cast<UINT> (nBytesToWrite));
502 }
503 }
504 else {
505 CFile* file = ar.GetFile ();
506 ASSERT_VALID (file);
507 DWORD nLen = static_cast<DWORD> (file->GetLength ()); // maybe should subtract current offset?
508 StackBuffer<char> buf{Memory::eUninitialized, nLen};
509 if (ar.Read (buf.data (), nLen) != nLen) {
510 AfxThrowArchiveException (CArchiveException::endOfFile);
511 }
512
513 CodePage useCodePage = fCodePage;
514 size_t bytesToStrip = 0;
515 optional<Characters::UnicodeExternalEncodings> useUnicodEncoding;
516 if (LedLineItDocument::sHiddenDocOpenArg != kIGNORECodePage) {
517 useCodePage = sHiddenDocOpenArg;
518 if (useCodePage == kAutomaticallyGuessCodePage) {
519 optional<tuple<Characters::UnicodeExternalEncodings, size_t>> n =
520 Characters::ReadByteOrderMark (span{reinterpret_cast<const byte*> (buf.data ()), nLen});
521 if (n) {
522 bytesToStrip = get<size_t> (*n);
523 useUnicodEncoding = get<Characters::UnicodeExternalEncodings> (*n);
524 }
525 }
526 }
527
529 CodeCvt<Led_tChar> codeCvt{useUnicodEncoding ? CodeCvt<Led_tChar>{*useUnicodEncoding}
530 : (useCodePage == kAutomaticallyGuessCodePage ? CodeCvt<Led_tChar>{locale{}}
531 : CodeCvt<Led_tChar>{useCodePage})};
532 StackBuffer<Led_tChar> result{Memory::eUninitialized,
533 codeCvt.ComputeTargetCharacterBufferSize (span{reinterpret_cast<const byte*> (buf.data ()), nLen}) + 1};
534 span<Led_tChar> n = codeCvt.Bytes2Characters (span{reinterpret_cast<const byte*> (buf.data ()), nLen}.subspan (bytesToStrip), span{result});
535 nLen = static_cast<DWORD> (n.size ());
536 result[nLen] = '\0'; // assure NUL-Term
537 Led_tChar* buffp = static_cast<Led_tChar*> (result);
538
539 nLen = static_cast<DWORD> (Characters::NormalizeTextToNL<Led_tChar> (buffp, nLen, buffp, nLen));
540 fTextStore.Replace (0, 0, buffp, nLen);
541
542 fCodePage = useCodePage; // whatever codepage I just used to read the doc should be the new codepage...
543 }
544}
545
546void LedLineItDocument::OnUpdateFileSave (CCmdUI* pCmdUI)
547{
548 ASSERT_VALID (this);
549 RequireNotNull (pCmdUI);
550 // only enable save command if dirty, or no file name associated with this document
551 pCmdUI->Enable (IsModified () or GetPathName ().GetLength () == 0);
552}
553
554void LedLineItDocument::OnFileSaveCopyAs ()
555{
556 ASSERT_VALID (this);
557 Assert (m_bRemember);
558
559 LPSTORAGE savedStorage = m_lpRootStg;
560 m_lpRootStg = NULL;
561
562 try {
563 DoSave (NULL, false);
564 }
565 catch (...) {
566 m_lpRootStg = savedStorage;
567 m_bRemember = true;
568 throw;
569 }
570
571 m_lpRootStg = savedStorage;
572 m_bRemember = true;
573}
574
575void LedLineItDocument::DeleteContents ()
576{
577 fTextStore.Replace (fTextStore.GetStart (), fTextStore.GetEnd (), LED_TCHAR_OF (""), 0);
578}
579
580bool LedLineItDocument::DoPromptSaveAsFileName (CString* fileName, CodePage* codePage)
581{
582 return DoPromptFileName (fileName, AFX_IDS_SAVEFILE, false, OFN_HIDEREADONLY | OFN_PATHMUSTEXIST, codePage);
583}
584
585bool LedLineItDocument::DoPromptSaveCopyAsFileName (CString* fileName, CodePage* codePage)
586{
587 return DoPromptFileName (fileName, AFX_IDS_SAVEFILECOPY, false, OFN_HIDEREADONLY | OFN_PATHMUSTEXIST, codePage);
588}
589
590bool LedLineItDocument::DoPromptOpenFileName (CString* fileName, CodePage* codePage)
591{
592 return DoPromptFileName (fileName, AFX_IDS_OPENFILE, true, OFN_HIDEREADONLY | OFN_FILEMUSTEXIST, codePage);
593}
594
595bool LedLineItDocument::DoPromptFileName (CString* fileName, UINT nIDSTitle, bool isOpenDialogCall, long fileDLogFlags, CodePage* codePage)
596{
597 vector<CodePage> codePages = CodePagesInstalled{}.GetAll ();
599 // We use these magic numbers internally here - just assure they don't conflict...
600 Assert (std::find (codePages.begin (), codePages.end (), kAutomaticallyGuessCodePage) == codePages.end ());
601 Assert (std::find (codePages.begin (), codePages.end (), kIGNORECodePage) == codePages.end ());
602 }
603
604#if 0
605 if (not isOpenDialogCall) {
606 /*
607 * Assure the given (argument) code page is in the list.
608 */
609 vector<CodePage>::iterator i = std::find (codePages.begin (), codePages.end (), *codePage);
610 if (i == codePages.end ()) {
611 codePages.push_back (*codePage);
612 }
613 }
614#endif
615 sort (codePages.begin (), codePages.end ());
616
617 /*
618 * Bring certain special code pages to the head of the list.
619 */
620 (void)ShuffleToFront (&codePages, Characters::WellKnownCodePages::kUNICODE_WIDE_BIGENDIAN);
621 (void)ShuffleToFront (&codePages, Characters::WellKnownCodePages::kUNICODE_WIDE);
622 (void)ShuffleToFront (&codePages, Characters::WellKnownCodePages::kUTF8);
623 if (isOpenDialogCall) {
624 codePages.insert (codePages.begin (), kAutomaticallyGuessCodePage);
625 }
626
627 FileDialogWithCodePage dlgFile (isOpenDialogCall, codePages, isOpenDialogCall ? kAutomaticallyGuessCodePage : *codePage);
628
629 CString title;
630 Verify (title.LoadString (nIDSTitle));
631
632 dlgFile.m_ofn.Flags |= fileDLogFlags;
633
634 CString strFilter;
635 AppendFilterSuffix (strFilter, dlgFile.m_ofn, ".txt", "Text Files (*.txt)");
636 AppendFilterSuffix (strFilter, dlgFile.m_ofn, ".*", "All Files (*.*)");
637
638 strFilter += (TCHAR)'\0'; // last string
639
640 dlgFile.m_ofn.lpstrFilter = strFilter;
641 dlgFile.m_ofn.lpstrTitle = title;
642
643 dlgFile.m_ofn.lpstrFile = fileName->GetBuffer (_MAX_PATH);
644
645 dlgFile.m_ofn.nFilterIndex = 2; // default to "All"
646 bool bResult = (dlgFile.DoModal () == IDOK);
647 fileName->ReleaseBuffer ();
648 *codePage = dlgFile.fCodePage;
649 return bResult;
650}
651
652#if qStroika_Foundation_Debug_AssertionsChecked
653void LedLineItDocument::AssertValid () const
654{
655 COleServerDoc::AssertValid ();
656 fTextStore.Invariant ();
657 //fStyleDatabase.Invariant (); Cannot do this cuz we're sometimes called at in-opportune times, while updating
658 // the styles...called from MFC...
659}
660#endif
661
662/*
663 ********************************************************************************
664 ******************************** AppendFilterSuffix ****************************
665 ********************************************************************************
666 */
667
668static void AppendFilterSuffix (CString& filter, OPENFILENAME& ofn, CString strFilterExt, CString strFilterName)
669{
670 Require (not strFilterExt.IsEmpty ());
671 Require (not strFilterName.IsEmpty ());
672
673 // add to filter
674 filter += strFilterName;
675 ASSERT (!filter.IsEmpty ()); // must have a file type name
676 filter += (TCHAR)'\0'; // next string please
677 filter += (TCHAR)'*';
678 filter += strFilterExt;
679 filter += (TCHAR)'\0'; // next string please
680 ofn.nMaxCustFilter++;
681}
682
683static void AppendFilterSuffix (CString& filter, OPENFILENAME& ofn, CDocTemplate* pTemplate)
684{
685 ASSERT_VALID (pTemplate);
686 ASSERT_KINDOF (CDocTemplate, pTemplate);
687 CString strFilterExt;
688 CString strFilterName;
689 if (pTemplate->GetDocString (strFilterExt, CDocTemplate::filterExt) && !strFilterExt.IsEmpty () &&
690 pTemplate->GetDocString (strFilterName, CDocTemplate::filterName) && !strFilterName.IsEmpty ()) {
691 AppendFilterSuffix (filter, ofn, strFilterExt, strFilterName);
692 }
693}
#define qStroika_Foundation_Debug_AssertionsChecked
The qStroika_Foundation_Debug_AssertionsChecked flag determines if assertions are checked and validat...
Definition Assertions.h:48
#define RequireNotNull(p)
Definition Assertions.h:347
#define Verify(c)
Definition Assertions.h:419
CodeCvt unifies byte <-> unicode conversions, vaguely inspired by (and wraps) std::codecvt,...
Definition CodeCvt.h:118
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
nonvirtual String NormalizeTextToNL() const
Definition String.cpp:1201
Logically halfway between std::array and std::vector; Smart 'direct memory array' - which when needed...
basic_string< SDKChar > SDKString
Definition SDKString.h:38
Ptr New(const InputStream::Ptr< byte > &src, optional< AutomaticCodeCvtFlags > codeCvtFlags={}, optional< SeekableFlag > seekable={}, ReadAhead readAhead=eReadAheadAllowed)
Create an InputStream::Ptr<Character> from the arguments (usually binary source) - which can be used ...