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