Stroika Library 3.0d23x
 
Loading...
Searching...
No Matches
StandardStyledTextInteractor.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
4#include "Stroika/Frameworks/StroikaPreComp.h"
5
6#include "Stroika/Foundation/DataExchange/BadFormatException.h"
9#include "Stroika/Frameworks/Led/StyledTextEmbeddedObjects.h"
10#include "Stroika/Frameworks/Led/StyledTextIO/StyledTextIO_HTML.h"
11#include "Stroika/Frameworks/Led/StyledTextIO/StyledTextIO_RTF.h"
12
13#include "StandardStyledTextInteractor.h"
14
15#if qIncludeLedNativeFileFormatSupportInStandardStyledTextInteractor
16#include "Stroika/Frameworks/Led/StyledTextIO/Led_StyledTextIO_LedNative.h"
17#endif
18
19using std::byte;
20
21using namespace Stroika::Foundation;
22
23using Memory::MakeSharedPtr;
24
25using namespace Stroika::Frameworks;
26using namespace Stroika::Frameworks::Led;
27using namespace Stroika::Frameworks::Led::StyledTextIO;
28
29/*
30 ********************************************************************************
31 ************************** StandardStyledTextIOSrcStream ***********************
32 ********************************************************************************
33 */
34StandardStyledTextIOSrcStream::StandardStyledTextIOSrcStream (TextStore* textStore, const shared_ptr<AbstractStyleDatabaseRep>& textStyleDatabase,
35 size_t selectionStart, size_t selectionEnd)
36 : fTextStore (textStore)
37 , fStyleRunDatabase (textStyleDatabase)
38 , fCurOffset (selectionStart)
39 , fSelStart (selectionStart)
40 , fSelEnd (selectionEnd)
41{
42 RequireNotNull (textStore);
43 Require (textStyleDatabase.get () != nullptr);
44 Require (fSelStart >= 0);
45 Require (fSelEnd >= 0);
46 fSelEnd = min (fSelEnd, textStore->GetEnd ());
47}
48
49#if qStroika_Frameworks_Led_SupportGDI
50StandardStyledTextIOSrcStream::StandardStyledTextIOSrcStream (StandardStyledTextImager* textImager, size_t selectionStart, size_t selectionEnd)
51 : StandardStyledTextIOSrcStream{textImager->PeekAtTextStore (), textImager->GetStyleDatabase (), selectionStart, selectionEnd}
52{
53}
54#endif
55
56size_t StandardStyledTextIOSrcStream::readNTChars (Led_tChar* intoBuf, size_t maxTChars)
57{
58 AssertNotNull (intoBuf);
59 size_t bytesToRead = min (maxTChars, fSelEnd - fCurOffset);
60 Assert (bytesToRead <= maxTChars);
61 fTextStore->CopyOut (fCurOffset, bytesToRead, intoBuf);
62 fCurOffset += bytesToRead;
63 return bytesToRead;
64}
65
66size_t StandardStyledTextIOSrcStream::current_offset () const
67{
68 return fCurOffset - fSelStart;
69}
70
71void StandardStyledTextIOSrcStream::seek_to (size_t to)
72{
73 Require (to >= 0);
74 to += fSelStart;
75 to = min (to, fSelEnd);
76 fCurOffset = to;
77 Ensure (fCurOffset >= fSelStart);
78 Ensure (fCurOffset <= fSelEnd);
79}
80
81size_t StandardStyledTextIOSrcStream::GetTotalTextLength () const
82{
83 Assert (fSelEnd >= fSelStart);
84 return (fSelEnd - fSelStart);
85}
86
87vector<StyledInfoSummaryRecord> StandardStyledTextIOSrcStream::GetStyleInfo (size_t from, size_t len) const
88{
89 size_t effectiveFrom = from + fSelStart;
90#if qStroika_Foundation_Debug_AssertionsChecked
91 size_t effectiveTo = effectiveFrom + len;
92#endif
93 Require (effectiveFrom >= fSelStart);
94 Require (effectiveFrom <= fSelEnd);
95#if qStroika_Foundation_Debug_AssertionsChecked
96 Require (effectiveTo >= fSelStart);
97 Require (effectiveTo <= fSelEnd);
98#endif
99 return fStyleRunDatabase->GetStyleInfo (effectiveFrom, len);
100}
101
102#if qStroika_Frameworks_Led_SupportGDI
103vector<SimpleEmbeddedObjectStyleMarker*> StandardStyledTextIOSrcStream::CollectAllEmbeddingMarkersInRange (size_t from, size_t to) const
104{
105 size_t effectiveFrom = from + fSelStart;
106 size_t effectiveTo = to + fSelStart;
107 Require (effectiveFrom >= fSelStart);
108 Require (effectiveFrom <= fSelEnd);
109 Require (effectiveTo >= fSelStart);
110 Require (effectiveTo <= fSelEnd);
111
112 MarkersOfATypeMarkerSink2Vector<SimpleEmbeddedObjectStyleMarker> result;
113 AssertNotNull (fTextStore);
114 fTextStore->CollectAllMarkersInRangeInto (effectiveFrom, effectiveTo, TextStore::kAnyMarkerOwner, result);
115 return result.fResult;
116}
117#endif
118
119StandardStyledTextIOSrcStream::Table* StandardStyledTextIOSrcStream::GetTableAt (size_t /*at*/) const
120{
121 return nullptr;
122}
123
124void StandardStyledTextIOSrcStream::SummarizeFontAndColorTable (set<SDKString>* fontNames, set<Color>* colorsUsed) const
125{
126 if (fontNames != nullptr or colorsUsed != nullptr) {
127 size_t totalTextLength = GetTotalTextLength ();
128 vector<StyledInfoSummaryRecord> styleRuns;
129 if (totalTextLength != 0) {
130 styleRuns = GetStyleInfo (0, totalTextLength);
131 }
132 for (auto i = styleRuns.begin (); i != styleRuns.end (); ++i) {
133 if (fontNames != nullptr) {
134 fontNames->insert ((*i).GetFontName ());
135 }
136 if (colorsUsed != nullptr) {
137 colorsUsed->insert ((*i).GetTextColor ());
138 }
139 }
140 }
141}
142
143size_t StandardStyledTextIOSrcStream::GetEmbeddingMarkerPosOffset () const
144{
145 return fSelStart;
146}
147
148/*
149 ********************************************************************************
150 ************************* StandardStyledTextIOSinkStream ***********************
151 ********************************************************************************
152 */
153StandardStyledTextIOSinkStream::StandardStyledTextIOSinkStream (TextStore* textStore, const shared_ptr<AbstractStyleDatabaseRep>& textStyleDatabase,
154 size_t insertionStart)
155 : fTextStore{textStore}
156 , fStyleRunDatabase{textStyleDatabase}
157 , fOriginalStart{insertionStart}
158 , fInsertionStart{insertionStart}
159{
160 RequireNotNull (textStore);
161 Require (textStyleDatabase.get () != nullptr);
162}
163
164StandardStyledTextIOSinkStream::~StandardStyledTextIOSinkStream ()
165{
166 try {
167 Flush ();
168 }
169 catch (...) {
170 // ignore, cuz cannot fail out of DTOR
171 }
172}
173
174size_t StandardStyledTextIOSinkStream::current_offset () const
175{
176 return fInsertionStart - fOriginalStart;
177}
178
179void StandardStyledTextIOSinkStream::AppendText (const Led_tChar* text, size_t nTChars, const FontSpecification* fontSpec)
180{
181 RequireNotNull (text);
182 AssertNotNull (fTextStore);
183
184 // If caching, append the text to an array. Coun't on the array to have efficient
185 // growing properties (does for MSVC50 - grows by factor of two, so log-n append times).
186 fCachedText.insert (fCachedText.end (), text, text + nTChars);
187
188 /*
189 * Keep track of the style changes that need to be applied later. If there is NO spec specified, use the one from
190 * the previous section. Check for 'nTChars == 0' - don't append empty info-summary records.
191 */
192 if (nTChars != 0) {
193 if (fontSpec == nullptr) {
194 if (fSavedStyleInfo.size () == 0) {
195 fSavedStyleInfo.push_back (StyledInfoSummaryRecord (fStyleRunDatabase->GetStyleInfo (fOriginalStart, 0)[0], nTChars));
196 }
197 else {
198 fSavedStyleInfo.back ().fLength += nTChars;
199 }
200 }
201 else {
202 fSavedStyleInfo.push_back (StyledInfoSummaryRecord (*fontSpec, nTChars));
203 }
204 }
205 fInsertionStart += nTChars;
206}
207
208void StandardStyledTextIOSinkStream::ApplyStyle (size_t from, size_t to, const vector<StyledInfoSummaryRecord>& styleRuns)
209{
210 Require (from <= to);
211 if (GetCachedTextSize () != 0) {
212 Flush ();
213 }
214 fStyleRunDatabase->SetStyleInfo (fOriginalStart + from, to - from, styleRuns);
215}
216
217FontSpecification StandardStyledTextIOSinkStream::GetDefaultFontSpec () const
218{
219 return GetStaticDefaultFont ();
220}
221
222#if qStroika_Frameworks_Led_SupportGDI
223void StandardStyledTextIOSinkStream::InsertEmbeddingForExistingSentinel (SimpleEmbeddedObjectStyleMarker* embedding, size_t at)
224{
225 RequireNotNull (embedding);
226 if (GetCachedTextSize () != 0) {
227 Flush ();
228 }
229 size_t effectiveFrom = fOriginalStart + at;
230 Led_tChar testSentinel;
231 AssertNotNull (fTextStore);
232 fTextStore->CopyOut (effectiveFrom, 1, &testSentinel);
233 if (testSentinel != kEmbeddingSentinelChar) {
234 Execution::Throw (DataExchange::BadFormatException::kThe);
235 }
236 Stroika::Frameworks::Led::InsertEmbeddingForExistingSentinel (embedding, *fTextStore, effectiveFrom, fStyleRunDatabase.get ());
237}
238
239void StandardStyledTextIOSinkStream::AppendEmbedding (SimpleEmbeddedObjectStyleMarker* embedding)
240{
241 RequireNotNull (embedding);
242 AssertNotNull (fTextStore);
243 if (GetCachedTextSize () != 0) {
244 Flush ();
245 }
246 AddEmbedding (embedding, *fTextStore, fInsertionStart, fStyleRunDatabase.get ());
247 ++fInsertionStart;
248}
249#endif
250
251void StandardStyledTextIOSinkStream::AppendSoftLineBreak ()
252{
253 // Bogus implementation - usually overriden...
254 AppendText (LED_TCHAR_OF ("\n"), 1, nullptr);
255}
256
257void StandardStyledTextIOSinkStream::InsertMarker (Marker* m, size_t at, size_t length, MarkerOwner* markerOwner)
258{
259 Require (at <= current_offset ());
260 RequireNotNull (m);
261 RequireNotNull (markerOwner);
262 AssertNotNull (fTextStore);
263 if (GetCachedTextSize () != 0) {
264 Flush ();
265 }
266 {
267 TextStore::SimpleUpdater u (*fTextStore, fOriginalStart + at, fOriginalStart + at + length);
268 fTextStore->AddMarker (m, fOriginalStart + at, length, markerOwner);
269 }
270}
271
272void StandardStyledTextIOSinkStream::Flush ()
273{
274 if (GetCachedTextSize () != 0) {
275 AssertNotNull (fTextStore);
276 size_t dataSize = fCachedText.size ();
277 size_t whereToInsert = fInsertionStart - dataSize;
278 fTextStore->Replace (whereToInsert, whereToInsert, Traversal::Iterator2Pointer (fCachedText.begin ()), dataSize);
279 fCachedText.clear ();
280
281 // Flush the cached style info
282 fStyleRunDatabase->SetStyleInfo (whereToInsert, dataSize, fSavedStyleInfo.size (), Traversal::Iterator2Pointer (fSavedStyleInfo.begin ()));
283 fSavedStyleInfo.clear ();
284 }
285 Ensure (fSavedStyleInfo.size () == 0);
286}
287
288void StandardStyledTextIOSinkStream::PushContext (TextStore* ts, const shared_ptr<AbstractStyleDatabaseRep>& textStyleDatabase, size_t insertionStart)
289{
290 Require (GetCachedTextSize () == 0); // must flush before setting/popping context
291
292 Context c;
293 c.fTextStore = fTextStore;
294 c.fStyleRunDatabase = fStyleRunDatabase;
295 c.fInsertionStart = fInsertionStart;
296 c.fOriginalStart = fOriginalStart;
297 fSavedContexts.push_back (c);
298 fTextStore = ts;
299 fStyleRunDatabase = textStyleDatabase;
300 fInsertionStart = insertionStart;
301 fOriginalStart = insertionStart;
302}
303
304void StandardStyledTextIOSinkStream::PopContext ()
305{
306 Require (GetCachedTextSize () == 0); // must flush before setting/popping context
307 Require (not fSavedContexts.empty ());
308 fTextStore = fSavedContexts.back ().fTextStore;
309 fStyleRunDatabase = fSavedContexts.back ().fStyleRunDatabase;
310 fInsertionStart = fSavedContexts.back ().fInsertionStart;
311 fOriginalStart = fSavedContexts.back ().fOriginalStart;
312 fSavedContexts.pop_back ();
313}
314
315#if qStroika_Frameworks_Led_SupportGDI
316
317#if qStroika_Foundation_Common_Platform_MacOS
318const Led_ClipFormat Led::kLedPrivateClipFormat = 'LedP';
319const Led_ClipFormat Led::kRTFClipFormat = 'RTF ';
320const Led_ClipFormat Led::kHTMLClipFormat = 'HTML';
321#elif qStroika_Foundation_Common_Platform_Windows
322const TCHAR kLedPrivateClipTypeName[] = _T ("Led Rich Text Format");
323const Led_ClipFormat Led::kLedPrivateClipFormat = static_cast<Led_ClipFormat> (::RegisterClipboardFormat (kLedPrivateClipTypeName));
324const TCHAR kRTFClipTypeName[] = _T ("Rich Text Format");
325const Led_ClipFormat Led::kRTFClipFormat = static_cast<Led_ClipFormat> (::RegisterClipboardFormat (kRTFClipTypeName));
326const TCHAR kHTMLClipTypeName[] = _T ("HTML"); /// MAYBE A BAD NAME - SEE IF ANY WINDOWS STANDARD NAME???
327const Led_ClipFormat Led::kHTMLClipFormat = static_cast<Led_ClipFormat> (::RegisterClipboardFormat (kHTMLClipTypeName));
328#elif qStroika_FeatureSupported_XWindows
329// Toolkit-specific code (e.g. Led_Gtk<>) must reset these to good values. Cannot be constants
330// and cannot be filled in here, cuz we require a DISPLAY object to register the contants on.
331Led_ClipFormat kLedPrivateClipFormat = 0;
332Led_ClipFormat kRTFClipFormat = 0;
333Led_ClipFormat kHTMLClipFormat = 0;
334#endif
335
336/*
337 ********************************************************************************
338 *************************** StandardStyledTextInteractor ***********************
339 ********************************************************************************
340 */
341StandardStyledTextInteractor::CommandNames StandardStyledTextInteractor::sCommandNames = StandardStyledTextInteractor::MakeDefaultCommandNames ();
342
343StandardStyledTextInteractor::StandardStyledTextInteractor ()
344 : fEmptySelectionStyleSuppressMode (false)
345 , fEmptySelectionStyle (GetStaticDefaultFont ())
346{
347}
348
349StandardStyledTextInteractor::CommandNames StandardStyledTextInteractor::MakeDefaultCommandNames ()
350{
351 StandardStyledTextInteractor::CommandNames cmdNames;
352 cmdNames.fFontChangeCommandName = Led_SDK_TCHAROF ("Font Change");
353 return cmdNames;
354}
355
356void StandardStyledTextInteractor::HookLosingTextStore ()
357{
358 TextInteractor::HookLosingTextStore ();
359 StandardStyledTextImager::HookLosingTextStore ();
360 HookLosingTextStore_ ();
361}
362
363void StandardStyledTextInteractor::HookLosingTextStore_ ()
364{
365 // Remove all embeddings...
366 vector<SimpleEmbeddedObjectStyleMarker*> embeddings = CollectAllEmbeddingMarkersInRange (0, GetLength ());
367 for (size_t i = 0; i < embeddings.size (); ++i) {
368 SimpleEmbeddedObjectStyleMarker* e = embeddings[i];
369 AssertNotNull (e); // all embeddings returned must be non-null
370 DISABLE_COMPILER_MSC_WARNING_START (28182)
371 if (e->GetOwner () == this) {
372 PeekAtTextStore ()->RemoveMarker (e);
373 delete e;
374 }
375 DISABLE_COMPILER_MSC_WARNING_END (28182)
376 }
377}
378
379void StandardStyledTextInteractor::HookGainedNewTextStore ()
380{
381 TextInteractor::HookGainedNewTextStore ();
382 StandardStyledTextImager::HookGainedNewTextStore ();
383 HookGainedNewTextStore_ ();
384}
385
386void StandardStyledTextInteractor::HookGainedNewTextStore_ ()
387{
388}
389
390/*
391@METHOD: StandardStyledTextInteractor::SetDefaultFont
392@DESCRIPTION: <p>Override @'TextImager::SetDefaultFont' to provide a moderately sensible interpretation
393 of that method.</p>
394 <p>Note that this definition is significantly different than in Led 3.0. To get
395 the Led 3.0 behavior, you should call InteractiveSetFont ()</p>
396 */
397void StandardStyledTextInteractor::SetDefaultFont (const IncrementalFontSpecification& defaultFont)
398{
399 size_t selStart = GetSelectionStart ();
400 size_t selEnd = GetSelectionEnd ();
401 if (selStart == selEnd) {
402 size_t selLength = selEnd - selStart;
403 FontSpecification newEmptySelectionStyle = fEmptySelectionStyle;
404 newEmptySelectionStyle.MergeIn (defaultFont);
405 SetStyleInfo (selStart, selLength, defaultFont);
406 SetEmptySelectionStyle (newEmptySelectionStyle);
407 Refresh ();
408 }
409 StandardStyledTextImager::SetDefaultFont (defaultFont);
410}
411
412/*
413@METHOD: StandardStyledTextInteractor::InteractiveSetFont
414@DESCRIPTION: <p>Applies the given incremental font
415 specification to the current selection. If the selection is empty - it stores the specification in
416 a special variable used to represent the font of the empty selection. Then when the user types, the
417 newly typed characters are in that font.</p>
418 <p>This function is meant to implement the underlying semantics of the standard word-processor
419 font/style selection menu.</p>
420 <p>As such, it is also entered by name in the command UNDO list. If you do <em>not</em> want your
421 font changes treated that way, use @'StandardStyledTextImager::SetStyleInfo'.</p>
422 <p>Note that this functionality USED to be provided by @'StandardStyledTextInteractor::SetDefaultFont'
423 (before Led 3.1a4) - but that is now obsolete. @'TextImager::SetDefaultFont' just sets a default font
424 object associated with the imager, and that has little or no effect when used with this class.
425 */
426void StandardStyledTextInteractor::InteractiveSetFont (const IncrementalFontSpecification& defaultFont)
427{
428 InteractiveModeUpdater iuMode (*this);
429 RequireNotNull (PeekAtTextStore ()); // Must specify TextStore before calling this, or any routine that calls it.
430
431 BreakInGroupedCommands ();
432
433 UndoableContextHelper undoContext (*this, GetCommandNames ().fFontChangeCommandName, false);
434 {
435 /*
436 * NB: The SetStyleInfo() call will notify markers via AboutTo/DidUpdate, so long as that code
437 * percieves there is any change. But if the selection is empty, no style runs will change!
438 *
439 * Similarly, if there is no selection, SetEmptySelectionStyle () will take care of the notification
440 * of change.
441 */
442 size_t selLength = undoContext.GetUndoRegionEnd () - undoContext.GetUndoRegionStart ();
443 FontSpecification newEmptySelectionStyle = fEmptySelectionStyle;
444 newEmptySelectionStyle.MergeIn (defaultFont);
445 SetStyleInfo (undoContext.GetUndoRegionStart (), selLength, defaultFont);
446 SetEmptySelectionStyle (newEmptySelectionStyle);
447 Refresh ();
448 }
449 undoContext.CommandComplete ();
450}
451
452IncrementalFontSpecification StandardStyledTextInteractor::GetContinuousStyleInfo (size_t from, size_t nTChars) const
453{
454 if (nTChars == 0 and from == GetSelectionStart ()) {
455 vector<StyledInfoSummaryRecord> summaryInfo;
456 summaryInfo.push_back (StyledInfoSummaryRecord (fEmptySelectionStyle, 0));
457 return (GetContinuousStyleInfo_ (summaryInfo));
458 }
459 else {
460 return StandardStyledTextImager::GetContinuousStyleInfo (from, nTChars);
461 }
462}
463
464void StandardStyledTextInteractor::DidUpdateText (const UpdateInfo& updateInfo) noexcept
465{
466 TextInteractor::DidUpdateText (updateInfo);
467 StandardStyledTextImager::DidUpdateText (updateInfo);
468 /*
469 * SPR#1171 - note that we cannot call SetEmptySelectionStyle () here because it is TOO aggressive.
470 * changes occur all the time (including setting the font in an empty selection to italics or something
471 * which trigger a DidUpdate call. Easier and safer to just do the SetEmptySelectionStyle () in the few
472 * places where I notice its needed.
473 */
474}
475
476bool StandardStyledTextInteractor::ShouldEnablePasteCommand () const
477{
478 if (TextInteractor::ShouldEnablePasteCommand ()) {
479 return true;
480 }
481 if (Led_ClipboardObjectAcquire::FormatAvailable (kLedPrivateClipFormat)) {
482 return true;
483 }
484 if (Led_ClipboardObjectAcquire::FormatAvailable (kRTFClipFormat)) {
485 return true;
486 }
487
488 const vector<EmbeddedObjectCreatorRegistry::Assoc>& types = EmbeddedObjectCreatorRegistry::Get ().GetAssocList ();
489 for (size_t i = 0; i < types.size (); ++i) {
490 EmbeddedObjectCreatorRegistry::Assoc assoc = types[i];
491 bool clipAvailForAll = true;
492 for (size_t j = 0; j < assoc.fFormatTagCount; ++j) {
493 if (not Led_ClipboardObjectAcquire::FormatAvailable (assoc.GetIthFormat (j))) {
494 clipAvailForAll = false;
495 break;
496 }
497 }
498 if (clipAvailForAll) {
499 return true;
500 }
501 }
502 return false;
503}
504
505bool StandardStyledTextInteractor::CanAcceptFlavor (Led_ClipFormat clipFormat) const
506{
507 if (TextInteractor::CanAcceptFlavor (clipFormat)) {
508 return true;
509 }
510 if (clipFormat == kLedPrivateClipFormat) {
511 return true;
512 }
513 if (clipFormat == kRTFClipFormat) {
514 return true;
515 }
516
517 const vector<EmbeddedObjectCreatorRegistry::Assoc>& types = EmbeddedObjectCreatorRegistry::Get ().GetAssocList ();
518 for (size_t i = 0; i < types.size (); ++i) {
519 EmbeddedObjectCreatorRegistry::Assoc assoc = types[i];
520
521 // This may sometimes false-posative - since we may (in priciple) require several other formats at the
522 // same time. But this will at least return true whenever we CAN accept the format...
523 // Maybe we should redesign/reimplement this API for a future release? LGP 960416
524 for (size_t j = 0; j < assoc.fFormatTagCount; ++j) {
525 if (assoc.GetIthFormat (j) == clipFormat) {
526 return true;
527 }
528 }
529 }
530 return false;
531}
532
533void StandardStyledTextInteractor::HookStyleDatabaseChanged ()
534{
535 StandardStyledTextImager::HookStyleDatabaseChanged ();
536 if (PeekAtTextStore () != nullptr) {
537 // if no TextStore - no problem - TextInteractor::HookGainedTextStore () will recreate these.
538 SetExternalizer (MakeDefaultExternalizer ());
539 SetInternalizer (MakeDefaultInternalizer ());
540 }
541}
542
543shared_ptr<FlavorPackageInternalizer> StandardStyledTextInteractor::MakeDefaultInternalizer ()
544{
545 return MakeSharedPtr<StyledTextFlavorPackageInternalizer> (GetTextStore (), GetStyleDatabase ());
546}
547
548shared_ptr<FlavorPackageExternalizer> StandardStyledTextInteractor::MakeDefaultExternalizer ()
549{
550 return MakeSharedPtr<StyledTextFlavorPackageExternalizer> (GetTextStore (), GetStyleDatabase ());
551}
552
553/*
554@METHOD: StandardStyledTextInteractor::ProcessSimpleClick
555@ACCESS: protected
556@DESCRIPTION: <p>Override @'TextInteractor::ProcessSimpleClick' to handle embeddings.</p>
557*/
558bool StandardStyledTextInteractor::ProcessSimpleClick (Led_Point clickedAt, unsigned clickCount, bool extendSelection, size_t* dragAnchor)
559{
560 RequireNotNull (dragAnchor);
561 size_t clickedOnChar = GetCharAtWindowLocation (clickedAt);
562 Led_Rect charRect = GetCharWindowLocation (clickedOnChar);
563
564 // Only if click is on an embedding character cell, and fully within it (not in case just past it as at when at
565 // end of line) - then we look at if it needs special processing
566 //
567 // Actually - better to check that the click isn't too near the edges of the embedding,
568 // cuz then its hard to click and make an insertion point in between two embeddings.
569 // So only do this click-selects somewhere near the middle of the embedding.
570 Led_Rect tstClickRect = charRect;
571 const DistanceType kHMargin = 3;
572 tstClickRect.left += kHMargin;
573 tstClickRect.right -= kHMargin;
574
575 if (tstClickRect.Contains (clickedAt)) {
576 vector<SimpleEmbeddedObjectStyleMarker*> embeddingList = CollectAllEmbeddingMarkersInRange (clickedOnChar, clickedOnChar + 1);
577 Assert (embeddingList.size () == 0 or embeddingList.size () == 1);
578
579 if (embeddingList.size () == 1) {
580 SimpleEmbeddedObjectStyleMarker* embedding = embeddingList[0];
581 AssertMember (embedding, SimpleEmbeddedObjectStyleMarker);
582 switch (clickCount) {
583 case 1: {
584 if (not extendSelection) {
585 SetSelection (clickedOnChar, clickedOnChar + 1);
586 // select the entire embedding, and then process the rest as usual...
587 if (clickCount == 1) {
588 // In this case - it really doesn't matter if we pick the LHS or RHS of the embedding
589 // as the drag anchor...
590 *dragAnchor = clickedOnChar;
591 }
592 return embedding->HandleClick (clickedAt, 1);
593 }
594 } break;
595
596 case 2: {
597 // If we dbl-click on an embedding, then be sure it is still selected, and then try to open it.
598 if (not extendSelection) {
599 SetSelection (clickedOnChar, clickedOnChar + 1);
600 // DO OPEN? Anyhow - even if no open, the treating the object as a word makes good sense...
601 if (clickCount == 1) {
602 // In this case - it really doesn't matter if we pick the LHS or RHS of the embedding
603 // as the drag anchor...
604 *dragAnchor = clickedOnChar;
605 }
606 return embedding->HandleClick (clickedAt, 2);
607 }
608 } break;
609
610 default: {
611 // others are ignored
612 } break;
613 }
614 }
615 }
616 return TextInteractor::ProcessSimpleClick (clickedAt, clickCount, extendSelection, dragAnchor);
617}
618
619void StandardStyledTextInteractor::WhileSimpleMouseTracking (Led_Point newMousePos, size_t dragAnchor)
620{
621 size_t clickedOnChar = GetCharAtWindowLocation (newMousePos);
622 size_t oldSelStart = GetSelectionStart ();
623 size_t oldSelEnd = GetSelectionEnd ();
624
625 /*
626 * If the drag anchor is coincident with the LHS or RHS of the clicked on character and the selection length
627 * is one (we clicked on an embedding) - then just eat that mousetracking - and prevent the selection from
628 * changing.
629 */
630 if ((clickedOnChar == dragAnchor or clickedOnChar + 1 == dragAnchor) and (oldSelEnd - oldSelStart == 1)) {
631 vector<SimpleEmbeddedObjectStyleMarker*> embeddingList = CollectAllEmbeddingMarkersInRange (clickedOnChar, clickedOnChar + 1);
632 if (embeddingList.size () == 1) {
633 [[maybe_unused]] SimpleEmbeddedObjectStyleMarker* embedding = embeddingList[0];
634 AssertMember (embedding, SimpleEmbeddedObjectStyleMarker);
635 return;
636 }
637 }
638 TextInteractor::WhileSimpleMouseTracking (newMousePos, dragAnchor);
639}
640
641void StandardStyledTextInteractor::InteractiveReplace (const Led_tChar* withWhat, size_t withWhatCharCount, UpdateMode updateMode)
642{
643 UpdateMode useUpdateMode = updateMode == eImmediateUpdate ? eDelayedUpdate : updateMode;
644 Assert (not fEmptySelectionStyleSuppressMode);
645 fEmptySelectionStyleSuppressMode = true;
646 try {
647 TextInteractor::InteractiveReplace (withWhat, withWhatCharCount, useUpdateMode);
648 fEmptySelectionStyleSuppressMode = false;
649 }
650 catch (...) {
651 fEmptySelectionStyleSuppressMode = false;
652 throw;
653 }
654 if (updateMode == eImmediateUpdate) {
655 Update ();
656 }
657}
658
659void StandardStyledTextInteractor::SetSelection (size_t start, size_t end)
660{
661 bool changed = (GetSelectionStart () != start) or (GetSelectionEnd () != end);
662 TextInteractor::SetSelection (start, end);
663 if (changed) {
664 StandardStyledTextInteractor::SetSelection_ (start, end);
665 }
666}
667
668void StandardStyledTextInteractor::SetSelection_ ([[maybe_unused]] size_t start, [[maybe_unused]] size_t end)
669{
670 // SetEmptySelectionStyle () assumes selection already set - uses set one - assure that we're called
671 // at the right time and that it already HAS been set
672 Require (start == GetSelectionStart ());
673 Require (end == GetSelectionEnd ());
674 if (not fEmptySelectionStyleSuppressMode) {
675 SetEmptySelectionStyle ();
676 }
677}
678
679/*
680@METHOD: StandardStyledTextInteractor::GetEmptySelectionStyle
681@DESCRIPTION: <p>Return the style applied to newly typed text (or interactively entered text) at the current
682 selection. This font defaults to the same as the surrounding text. But can be changed under user-control,
683 in order to implement the usual semantics of a font / style menu.</p>
684 <p>See @'StandardStyledTextInteractor::SetEmptySelectionStyle'.</p>
685*/
686FontSpecification StandardStyledTextInteractor::GetEmptySelectionStyle () const
687{
688 return fEmptySelectionStyle;
689}
690
691/*
692@METHOD: StandardStyledTextInteractor::SetEmptySelectionStyle_OVLD
693@DESCRIPTION: <p>Same as @'StandardStyledTextInteractor::SetEmptySelectionStyle' but always grabs style info
694 from the surrounding text (called from @'StandardStyledTextInteractor::SetSelection_').</p>
695 <p>See @'StandardStyledTextInteractor::GetEmptySelectionStyle'.</p>
696*/
697void StandardStyledTextInteractor::SetEmptySelectionStyle ()
698{
699 size_t start = 0;
700 size_t end = 0;
701 GetSelection (&start, &end);
702 if (fLeftSideOfSelectionInteresting) {
703 if (start < GetTextStore ().GetEnd ()) {
704 fEmptySelectionStyle = GetStyleInfo (start);
705 }
706 }
707 else {
708 fEmptySelectionStyle = GetStyleInfo (FindPreviousCharacter (end));
709 }
710 if (end == GetEnd ()) {
711 SetStyleInfo (GetEnd (), 1, fEmptySelectionStyle);
712 }
713}
714
715/*
716@METHOD: StandardStyledTextInteractor::SetEmptySelectionStyle
717@DESCRIPTION: <p>Set the @'FontSpecification' applied to newly typed text (or interactively entered text) at the current
718 selection. This font defaults to the same as the surrounding text. But can be changed under user-control,
719 in order to implement the usual semantics of a font / style menu.</p>
720 <p>Note: if the selection is currently empty, this routine will make appropriate AboutToUpdate/DidUpdate calls to
721 notifie anyone interested of the change (so - for example - the cached font metrics of text can change).</p>
722 <p>See @'StandardStyledTextInteractor::GetEmptySelectionStyle'.</p>
723*/
724void StandardStyledTextInteractor::SetEmptySelectionStyle (FontSpecification newEmptyFontSpec)
725{
726 if (fEmptySelectionStyle != newEmptyFontSpec) {
727 /*
728 * If we change the empty style selection, this change can affect menus etc (since they show
729 * the currently selected font which - with an empty selection is really just 'fEmptySelectionStyle').
730 *
731 * Also - this can change the size of the current row of text (when you change the font of the empty style to make
732 * it big - even if you type no characters - the row immediately gets bigger).
733 */
734 size_t selStart = 0;
735 size_t selEnd = 0;
736 GetSelection (&selStart, &selEnd);
737 if (selStart == selEnd) {
738 {
739 // not sure if we really need this scoping operator - just added to assure preserving semeantics - for now - LGP 2003-03-16
740 TextStore::SimpleUpdater updater (GetTextStore (), selStart, selEnd, false);
741 fEmptySelectionStyle = newEmptyFontSpec;
742 }
743 if (selEnd == GetEnd ()) {
744 SetStyleInfo (GetEnd (), 1, fEmptySelectionStyle);
745 }
746 }
747 }
748}
749
750bool StandardStyledTextInteractor::InteractiveReplaceEarlyPostReplaceHook (size_t withWhatCharCount)
751{
752 Assert (GetSelectionStart () >= withWhatCharCount);
753 if (withWhatCharCount == 1) {
754 // If we just typed a single extra char - apply our fEmptySelectionStyle to that extra char typed. Return true iff
755 // that caused a font-style change.
756 size_t charAt = FindPreviousCharacter (GetSelectionStart ()); // start of char we just added
757 FontSpecification prevStyle = GetStyleInfo (charAt);
758 if (prevStyle != fEmptySelectionStyle) {
759 SetStyleInfo (charAt, withWhatCharCount, IncrementalFontSpecification (fEmptySelectionStyle));
760 return true;
761 }
762 }
763 return false;
764}
765
766vector<SimpleEmbeddedObjectStyleMarker*> StandardStyledTextInteractor::CollectAllEmbeddingMarkersInRange (size_t from, size_t to) const
767{
768 /*
769 * Walk through all the markers in existence, and throw away all but our
770 * SimpleEmbeddedObjectStyleMarker markers. This is an inefficient approach. It would be far
771 * faster to keep a linked, or doubly linked list of all these guys.
772 * But this approach saves a bit of memory, and til we see this as a problem, lets just
773 * live with it.
774 */
775 MarkersOfATypeMarkerSink2Vector<SimpleEmbeddedObjectStyleMarker> result;
776 GetTextStore ().CollectAllMarkersInRangeInto (from, to, TextStore::kAnyMarkerOwner, result);
777 return result.fResult;
778}
779
780InteractiveReplaceCommand::SavedTextRep* StandardStyledTextInteractor::InteractiveUndoHelperMakeTextRep (size_t regionStart, size_t regionEnd,
781 size_t selStart, size_t selEnd)
782{
783 if (regionStart == regionEnd) {
784 // optimization, cuz these are smaller
785 return new EmptySelStyleTextRep{this, selStart, selEnd};
786 }
787 else {
788 return TextInteractor::InteractiveUndoHelperMakeTextRep (regionStart, regionEnd, selStart, selEnd);
789 }
790}
791
792/*
793 ********************************************************************************
794 ************************* StyledTextFlavorPackageInternalizer ******************
795 ********************************************************************************
796 */
797using StyledTextFlavorPackageInternalizer = StandardStyledTextInteractor::StyledTextFlavorPackageInternalizer;
798StyledTextFlavorPackageInternalizer::StyledTextFlavorPackageInternalizer (TextStore& ts, const shared_ptr<AbstractStyleDatabaseRep>& styleDatabase)
799 : inherited (ts)
800 , fStyleDatabase (styleDatabase)
801{
802}
803
804void StyledTextFlavorPackageInternalizer::InternalizeFlavor_FILEGuessFormatsFromName (filesystem::path fileName, Led_ClipFormat* suggestedClipFormat,
805 optional<CodePage> suggestedCodePage)
806{
807 inherited::InternalizeFlavor_FILEGuessFormatsFromName (fileName, suggestedClipFormat, suggestedCodePage);
808
809#if qStroika_Foundation_Common_Platform_MacOS
810// Should add code here to grab file-type from OS. If called from XXX - then thats already done, but in case
811// called from elsewhere...
812#elif qStroika_Foundation_Common_Platform_Windows
813 if (suggestedClipFormat != nullptr and *suggestedClipFormat == kBadClipFormat) {
814 TCHAR drive[_MAX_DRIVE];
815 TCHAR dir[_MAX_DIR];
816 TCHAR fname[_MAX_FNAME];
817 TCHAR ext[_MAX_EXT];
818 ::_tsplitpath_s (fileName.native ().c_str (), drive, dir, fname, ext);
819 if (::_tcsicmp (ext, Led_SDK_TCHAROF (".rtf")) == 0) {
820 *suggestedClipFormat = kRTFClipFormat;
821 }
822 else if (::_tcsicmp (ext, Led_SDK_TCHAROF (".htm")) == 0) {
823 *suggestedClipFormat = kHTMLClipFormat;
824 }
825 else if (::_tcsicmp (ext, Led_SDK_TCHAROF (".html")) == 0) {
826 *suggestedClipFormat = kHTMLClipFormat;
827 }
828 else if (::_tcsicmp (ext, Led_SDK_TCHAROF (".led")) == 0) {
829 *suggestedClipFormat = kLedPrivateClipFormat;
830 }
831 }
832#endif
833}
834
835void StyledTextFlavorPackageInternalizer::InternalizeFlavor_FILEGuessFormatsFromStartOfData (Led_ClipFormat* suggestedClipFormat,
836 optional<CodePage> suggestedCodePage,
837 const byte* fileStart, const byte* fileEnd)
838{
839 inherited::InternalizeFlavor_FILEGuessFormatsFromStartOfData (suggestedClipFormat, suggestedCodePage, fileStart, fileEnd);
840 if (suggestedClipFormat != nullptr) {
841 if (*suggestedClipFormat == kBadClipFormat) {
842 {
843 StyledTextIOSrcStream_Memory source (fileStart, fileEnd - fileStart);
844 StyledTextIOReader_RTF reader (&source, nullptr);
845 if (reader.QuickLookAppearsToBeRightFormat ()) {
846 *suggestedClipFormat = kRTFClipFormat;
847 return;
848 }
849 }
850
851 {
852 StyledTextIOSrcStream_Memory source (fileStart, fileEnd - fileStart);
853 StyledTextIOReader_HTML reader (&source, nullptr);
854 if (reader.QuickLookAppearsToBeRightFormat ()) {
855 *suggestedClipFormat = kHTMLClipFormat;
856 return;
857 }
858 }
859
860#if qIncludeLedNativeFileFormatSupportInStandardStyledTextInteractor
861 {
862 StyledTextIOSrcStream_Memory source (fileStart, fileEnd - fileStart);
863 StyledTextIOReader_LedNativeFileFormat reader (&source, nullptr);
864 if (reader.QuickLookAppearsToBeRightFormat ()) {
865 *suggestedClipFormat = kLedPrivateClipFormat;
866 return;
867 }
868 }
869#endif
870 }
871 }
872}
873
874bool StyledTextFlavorPackageInternalizer::InternalizeBestFlavor (ReaderFlavorPackage& flavorPackage, size_t from, size_t to)
875{
876 Require (from <= GetTextStore ().GetEnd ());
877 Require (to <= GetTextStore ().GetEnd ());
878 Require (from <= to);
879
880 if (InternalizeFlavor_RTF (flavorPackage, from, to)) {
881 return true;
882 }
883 else if (InternalizeFlavor_HTML (flavorPackage, from, to)) {
884 return true;
885 }
886#if qIncludeLedNativeFileFormatSupportInStandardStyledTextInteractor
887 else if (InternalizeFlavor_Native (flavorPackage, from, to)) {
888 return true;
889 }
890#endif
891#if qStroika_Foundation_Common_Platform_Windows
892 // A bit of a hack. MSIE 3.0 generates both FILE and DIB objects on the clip
893 // for when we drag out pictures. This allows us to grab the dibs in that case.
894 // I just hope it doesn't cause too much trouble for other cases. For Led 2.2, we
895 // must completely rewrite this code and find a better way to choose what to
896 // grab out of a clip package...
897 // LGP 961101
898 else if (flavorPackage.GetFlavorAvailable (CF_DIB) and InternalizeFlavor_OtherRegisteredEmbedding (flavorPackage, from, to)) {
899 return true;
900 }
901#endif
902 else if (InternalizeFlavor_FILE (flavorPackage, from, to)) {
903 return true;
904 }
905 else if (InternalizeFlavor_OtherRegisteredEmbedding (flavorPackage, from, to)) {
906 return true;
907 }
908#if qStroika_Foundation_Common_Platform_MacOS
909 else if (InternalizeFlavor_STYLAndTEXT (flavorPackage, from, to)) {
910 return true;
911 }
912#endif
913 else if (InternalizeFlavor_TEXT (flavorPackage, from, to)) {
914 return true;
915 }
916 return false;
917}
918
919#if qStroika_Foundation_Common_Platform_MacOS
920bool StyledTextFlavorPackageInternalizer::InternalizeFlavor_STYLAndTEXT (ReaderFlavorPackage& flavorPackage, size_t from, size_t to)
921{
922 size_t pasteStart = from;
923 size_t pasteEnd = to;
924 Assert (pasteEnd >= pasteStart);
925
926 TempMarker newSel (GetTextStore (), pasteStart + 1, pasteStart + 1);
927 if (inherited::InternalizeFlavor_TEXT (flavorPackage, pasteStart, pasteEnd)) {
928 if (flavorPackage.GetFlavorAvailable ('styl')) {
929 size_t length = flavorPackage.GetFlavorSize ('styl');
930 Memory::StackBuffer<char> buf{Memory::eUninitialized, length};
931 length = flavorPackage.ReadFlavorData ('styl', length, buf);
932 Assert (newSel.GetStart () >= pasteStart + 1);
933 size_t pasteEndXXX = newSel.GetStart () - 1;
934 Assert (pasteEndXXX >= pasteStart);
935 StScrpRec* styleRecords = reinterpret_cast<StScrpRec*> (static_cast<char*> (buf));
936 vector<StyledInfoSummaryRecord> ledStyleInfo = StandardStyledTextImager::Convert (styleRecords->scrpStyleTab, styleRecords->scrpNStyles);
937 fStyleDatabase->SetStyleInfo (pasteStart, pasteEndXXX - pasteStart, ledStyleInfo);
938 }
939
940 // Even if we have no STYL info, we did already paste the text in, and that would be next
941 // on our list to try anyhow...
942 return true;
943 }
944 return false;
945}
946#endif
947
948#if qIncludeLedNativeFileFormatSupportInStandardStyledTextInteractor
949bool StyledTextFlavorPackageInternalizer::InternalizeFlavor_Native (ReaderFlavorPackage& flavorPackage, size_t from, size_t to)
950{
951 if (flavorPackage.GetFlavorAvailable (kLedPrivateClipFormat)) {
952 size_t length = flavorPackage.GetFlavorSize (kLedPrivateClipFormat);
953 Memory::StackBuffer<char> buf{Memory::eUninitialized, length};
954 length = flavorPackage.ReadFlavorData (kLedPrivateClipFormat, length, buf);
955
956 size_t start = from;
957 size_t end = to;
958 Assert (end >= start);
959
960 GetTextStore ().Replace (start, end, LED_TCHAR_OF (""), 0); // clear current selection before insert
961 {
962 // Be sure these guys in scope like this so caches get flusehd before we update cursor position
963 StyledTextIOSrcStream_Memory source (buf, length);
964 unique_ptr<StandardStyledTextIOSinkStream> sink (mkStandardStyledTextIOSinkStream (start));
965 StyledTextIOReader_LedNativeFileFormat textReader (&source, sink.get ());
966 textReader.Read ();
967 sink->Flush (); // would be called implcitly in DTOR, but call like this so exceptions get propagates...
968 }
969 return true;
970 }
971 else {
972 return false;
973 }
974}
975#endif
976
977bool StyledTextFlavorPackageInternalizer::InternalizeFlavor_RTF (ReaderFlavorPackage& flavorPackage, size_t from, size_t to)
978{
979 if (flavorPackage.GetFlavorAvailable (kRTFClipFormat)) {
980 size_t length = flavorPackage.GetFlavorSize (kRTFClipFormat);
981 if (length == 0) {
982 // Bizare - but Word2000 seems to sometimes return a zero-length RTF entity when you insert a BitMap ActiveX control into the WP, and
983 // try to copy it... LGP 2000/04/26
984 return false;
985 }
986 Memory::StackBuffer<char> buf{Memory::eUninitialized, length};
987 length = flavorPackage.ReadFlavorData (kRTFClipFormat, length, buf.data ());
988
989 size_t start = from;
990 size_t end = to;
991 Assert (end >= start);
992
993 GetTextStore ().Replace (start, end, LED_TCHAR_OF (""), 0); // clear current selection before insert
994 {
995 // Be sure these guys in scope like this so caches get flusehd before we update cursor position
996 StyledTextIOSrcStream_Memory source{buf.data (), length};
997 unique_ptr<StandardStyledTextIOSinkStream> sink{mkStandardStyledTextIOSinkStream (start)};
998 StyledTextIOReader_RTF textReader{&source, sink.get ()};
999 textReader.Read ();
1000 sink->Flush (); // would be called implcitly in DTOR, but call like this so exceptions get propagates...
1001 }
1002 return true;
1003 }
1004 else {
1005 return false;
1006 }
1007}
1008
1009bool StyledTextFlavorPackageInternalizer::InternalizeFlavor_HTML (ReaderFlavorPackage& flavorPackage, size_t from, size_t to)
1010{
1011 if (flavorPackage.GetFlavorAvailable (kHTMLClipFormat)) {
1012 size_t length = flavorPackage.GetFlavorSize (kHTMLClipFormat);
1013 Memory::StackBuffer<char> buf{Memory::eUninitialized, length};
1014 length = flavorPackage.ReadFlavorData (kHTMLClipFormat, length, buf.data ());
1015
1016 size_t start = from;
1017 size_t end = to;
1018 Assert (end >= start);
1019
1020 GetTextStore ().Replace (start, end, LED_TCHAR_OF (""), 0); // clear current selection before insert
1021 {
1022 // Be sure these guys in scope like this so caches get flusehd before we update cursor position
1023 StyledTextIOSrcStream_Memory source{buf.data (), length};
1024 unique_ptr<StandardStyledTextIOSinkStream> sink{mkStandardStyledTextIOSinkStream (start)};
1025 StyledTextIOReader_HTML textReader{&source, sink.get ()};
1026 textReader.Read ();
1027 sink->Flush (); // would be called implcitly in DTOR, but call like this so exceptions get propagates...
1028 }
1029 return true;
1030 }
1031 else {
1032 return false;
1033 }
1034}
1035
1036bool StyledTextFlavorPackageInternalizer::InternalizeFlavor_OtherRegisteredEmbedding (ReaderFlavorPackage& flavorPackage, size_t from, size_t to)
1037{
1038 const vector<EmbeddedObjectCreatorRegistry::Assoc>& types = EmbeddedObjectCreatorRegistry::Get ().GetAssocList ();
1039 for (size_t i = 0; i < types.size (); ++i) {
1040 EmbeddedObjectCreatorRegistry::Assoc assoc = types[i];
1041 bool clipAvailForAll = (assoc.fFormatTagCount != 0);
1042 for (size_t j = 0; j < assoc.fFormatTagCount; ++j) {
1043 if (not flavorPackage.GetFlavorAvailable (assoc.GetIthFormat (j))) {
1044 clipAvailForAll = false;
1045 break;
1046 }
1047 }
1048 if (clipAvailForAll) {
1049 SimpleEmbeddedObjectStyleMarker* objMarker = (assoc.fReadFromFlavorPackage) (flavorPackage);
1050 {
1051 size_t pasteStart = from;
1052 size_t pasteEnd = to;
1053 Assert (pasteEnd >= pasteStart);
1054
1055 GetTextStore ().Replace (pasteStart, pasteEnd, &kEmbeddingSentinelChar, 1); // clear current selection and put in embedding character
1056
1057 {
1058 // add marker, and do DID_UPDATE stuff so cached metrics and rowheights get refreshed...
1059 TextStore::SimpleUpdater updater (GetTextStore (), pasteStart, pasteStart + 1);
1060 GetTextStore ().AddMarker (objMarker, pasteStart, 1, fStyleDatabase.get ());
1061 }
1062 }
1063
1064 return true;
1065 }
1066 }
1067 return false;
1068}
1069
1070/*
1071@METHOD: StandardStyledTextInteractor::StyledTextFlavorPackageInternalizer::mkStandardStyledTextIOSinkStream
1072@DESCRIPTION: <p>Hook function so that the various Externalize_XXX methods in
1073 @'StandardStyledTextInteractor::StyledTextFlavorPackageInternalizer' can use a dynamicly typed
1074 SinkStream. So - for example - the externalize methods include paragraph info.</p>
1075*/
1076StandardStyledTextIOSinkStream* StyledTextFlavorPackageInternalizer::mkStandardStyledTextIOSinkStream (size_t insertionStart)
1077{
1078 return new StandardStyledTextIOSinkStream (PeekAtTextStore (), fStyleDatabase, insertionStart);
1079}
1080
1081/*
1082 ********************************************************************************
1083 ************************* StyledTextFlavorPackageExternalizer ******************
1084 ********************************************************************************
1085 */
1086using StyledTextFlavorPackageExternalizer = StandardStyledTextInteractor::StyledTextFlavorPackageExternalizer;
1087StyledTextFlavorPackageExternalizer::StyledTextFlavorPackageExternalizer (TextStore& ts, const shared_ptr<AbstractStyleDatabaseRep>& styleDatabase)
1088 : inherited (ts)
1089 , fStyleDatabase (styleDatabase)
1090{
1091}
1092
1093void StyledTextFlavorPackageExternalizer::ExternalizeFlavors (WriterFlavorPackage& flavorPackage, size_t from, size_t to)
1094{
1095 size_t start = from;
1096 size_t end = to;
1097 Require (start >= 0);
1098 Require (end <= GetTextStore ().GetEnd ());
1099 Require (start <= end);
1100
1101 /*
1102 * Enumerate in fidelity order flavors to copy...
1103 */
1104
1105 // Directly write out singly selected embedding
1106 {
1107 MarkersOfATypeMarkerSink2Vector<SimpleEmbeddedObjectStyleMarker> embeddings;
1108 GetTextStore ().CollectAllMarkersInRangeInto (from, to, TextStore::kAnyMarkerOwner, embeddings);
1109 if ((embeddings.fResult.size () == 1) and (start + 1 == end)) {
1110 /*
1111 * If we have some embedding like a pict or whatever selected, no reason
1112 * to copy to clip anything else.
1113 */
1114 ExternalizeFlavor_SingleSelectedEmbedding (flavorPackage, embeddings.fResult[0]);
1115#if 0
1116 // WELL, this trick (of returning here) is ALMOST right.
1117 // At least for the time being, some 'unknown' embeddings get externalized in
1118 // some format NOBODY - including LED will recognize (eg rtf pictures).
1119 // This isn't fatal, except
1120 // in a few rare cases. For example, when we try to
1121 return;
1122#endif
1123 }
1124 }
1125
1126 // not sure we should do if single selected???? -LGP 961014
1127 // If NOT, we must be careful about case of unknown RTF embeddings!!! - See SPR# 0397
1128 ExternalizeFlavor_RTF (flavorPackage, start, end);
1129
1130 ExternalizeFlavor_TEXT (flavorPackage, start, end);
1131
1132#if qStroika_Foundation_Common_Platform_MacOS
1133 ExternalizeFlavor_STYL (flavorPackage, start, end);
1134#endif
1135}
1136
1137void StyledTextFlavorPackageExternalizer::ExternalizeBestFlavor (WriterFlavorPackage& flavorPackage, size_t from, size_t to)
1138{
1139 ExternalizeFlavor_RTF (flavorPackage, from, to);
1140}
1141
1142#if qStroika_Foundation_Common_Platform_MacOS
1143void StyledTextFlavorPackageExternalizer::ExternalizeFlavor_STYL (WriterFlavorPackage& flavorPackage, size_t from, size_t to)
1144{
1145 Require (from <= to);
1146 Require (to <= GetTextStore ().GetEnd ());
1147 size_t length = to - from;
1148
1149 vector<StyledInfoSummaryRecord> ledStyleRuns = fStyleDatabase->GetStyleInfo (from, length);
1150 size_t nStyleRuns = ledStyleRuns.size ();
1151
1152 Assert (offsetof (StScrpRec, scrpStyleTab) == sizeof (short)); // thats why we add sizeof (short)
1153
1154 size_t nBytes = sizeof (short) + nStyleRuns * sizeof (ScrpSTElement);
1155 Memory::StackBuffer<char> buf{nBytes};
1156 StScrpPtr stylePtr = (StScrpPtr)(char*)buf;
1157
1158 stylePtr->scrpNStyles = nStyleRuns;
1159 StandardStyledTextImager::Convert (ledStyleRuns, stylePtr->scrpStyleTab);
1160 flavorPackage.AddFlavorData ('styl', nBytes, stylePtr);
1161}
1162#endif
1163
1164#if qIncludeLedNativeFileFormatSupportInStandardStyledTextInteractor
1165void StyledTextFlavorPackageExternalizer::ExternalizeFlavor_Native (WriterFlavorPackage& flavorPackage, size_t from, size_t to)
1166{
1167 Require (from <= to);
1168 Require (to <= GetTextStore ().GetEnd ());
1169 unique_ptr<StandardStyledTextIOSrcStream> source (mkStandardStyledTextIOSrcStream (from, to));
1170 StyledTextIOWriterSinkStream_Memory sink;
1171 StyledTextIOWriter_LedNativeFileFormat textWriter (source.get (), &sink);
1172 textWriter.Write ();
1173 flavorPackage.AddFlavorData (kLedPrivateClipFormat, sink.GetLength (), sink.PeekAtData ());
1174}
1175#endif
1176
1177void StyledTextFlavorPackageExternalizer::ExternalizeFlavor_RTF (WriterFlavorPackage& flavorPackage, size_t from, size_t to)
1178{
1179 Require (from <= to);
1180 Require (to <= GetTextStore ().GetEnd ());
1181 unique_ptr<StandardStyledTextIOSrcStream> source{mkStandardStyledTextIOSrcStream (from, to)};
1182 StyledTextIOWriterSinkStream_Memory sink;
1183 StyledTextIOWriter_RTF textWriter{source.get (), &sink};
1184 textWriter.Write ();
1185 flavorPackage.AddFlavorData (kRTFClipFormat, sink.GetLength (), sink.PeekAtData ());
1186}
1187
1188void StyledTextFlavorPackageExternalizer::ExternalizeFlavor_SingleSelectedEmbedding (WriterFlavorPackage& flavorPackage,
1189 SimpleEmbeddedObjectStyleMarker* embedding)
1190{
1191 RequireNotNull (embedding);
1192 embedding->ExternalizeFlavors (flavorPackage);
1193}
1194
1195/*
1196@METHOD: StandardStyledTextInteractor::StyledTextFlavorPackageExternalizer::mkStandardStyledTextIOSrcStream
1197@DESCRIPTION: <p>Hook function so that the various Internalize_XXX methods in
1198 @'StandardStyledTextInteractor::StyledTextFlavorPackageExternalizer' can use a dynamicly typed
1199 SinkStream. So - for example - the internalize methods include paragraph info.</p>
1200*/
1201StandardStyledTextIOSrcStream* StyledTextFlavorPackageExternalizer::mkStandardStyledTextIOSrcStream (size_t selectionStart, size_t selectionEnd)
1202{
1203 return new StandardStyledTextIOSrcStream (PeekAtTextStore (), fStyleDatabase, selectionStart, selectionEnd);
1204}
1205
1206/*
1207 ********************************************************************************
1208 ***************************** EmptySelStyleTextRep *****************************
1209 ********************************************************************************
1210 */
1211using EmptySelStyleTextRep = StandardStyledTextInteractor::EmptySelStyleTextRep;
1212
1213EmptySelStyleTextRep::EmptySelStyleTextRep (StandardStyledTextInteractor* interactor, size_t selStart, size_t selEnd)
1214 : inherited{selStart, selEnd}
1215 , fSavedStyle{interactor->fEmptySelectionStyle}
1216{
1217}
1218
1219size_t EmptySelStyleTextRep::GetLength () const
1220{
1221 return 0;
1222}
1223
1224void EmptySelStyleTextRep::InsertSelf (TextInteractor* interactor, size_t at, size_t nBytesToOverwrite)
1225{
1226 RequireNotNull (interactor);
1227 interactor->Replace (at, at + nBytesToOverwrite, LED_TCHAR_OF (""), 0);
1228
1229 StandardStyledTextInteractor* si = dynamic_cast<StandardStyledTextInteractor*> (interactor);
1230 RequireNotNull (si); // cannot DO with one type, and UNDO with another!
1231
1232 si->SetEmptySelectionStyle (fSavedStyle);
1233}
1234#endif
#define AssertNotNull(p)
Definition Assertions.h:333
#define RequireNotNull(p)
Definition Assertions.h:347
#define AssertMember(p, c)
Definition Assertions.h:312
Logically halfway between std::array and std::vector; Smart 'direct memory array' - which when needed...
virtual size_t GetFlavorSize(Led_ClipFormat clipFormat) const =0
void Throw(T &&e2Throw)
identical to builtin C++ 'throw' except that it does helpful, type dependent DbgTrace() messages firs...
Definition Throw.inl:43