Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
Marker.inl
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#include "TextStore.h"
5
6namespace Stroika::Frameworks::Led {
7
8 namespace Private_ {
9 void SetMarkerRange_ (TextStore& textstore, Marker* marker, size_t start, size_t end) noexcept;
10 void PreRemoveMarker_ (TextStore& textstore, Marker* marker);
11 void RemoveMarkers_ (TextStore& textstore, Marker* const markerArray[], size_t markerCount);
12 }
13
14 /*
15 ********************************************************************************
16 ********************************* Led::MarkerOwner *****************************
17 ********************************************************************************
18 */
19 /*
20 @METHOD: MarkerOwner::GetTextStore
21 @DESCRIPTION: <p>This is a trivial wrapper on @'MarkerOwner::PeekAtTextStore' which asserts that
22 PeekAtTextStore () didn't return nullptr, and then returns its result dereferences. Use this for
23 clarity sake when you are sure the MarkerOwner must have an associated TextStore.</p>
24 */
25 inline TextStore& MarkerOwner::GetTextStore () const
26 {
27 TextStore* ts = PeekAtTextStore ();
28 EnsureNotNull (ts);
29 return *ts;
30 }
31 /*
32 @METHOD: MarkerOwner::AboutToUpdateText
33 @DESCRIPTION: <p>This method is called by a TextStore when text is being updated for all registered MarkerOwners
34 (see @'TextStore::AddMarkerOwner'). You can throw an exception to prevent text from being updated.</p>
35 */
36 inline void MarkerOwner::AboutToUpdateText (const UpdateInfo& /*updateInfo*/)
37 {
38 }
39 /*
40 @METHOD: MarkerOwner::EarlyDidUpdateText
41 @DESCRIPTION: <p>This method is called by a @'TextStore' <em>just</em>after a text update has taken place (and before the <em>DidUpdateText</em> calls take place).
42 You can use this call just to set private data in your objects - but make no calls out (til the DidUpdateText). This is just so that
43 in some rare cases - you can tell if your getting a callback between your AboutToUpdate() call and your DidUpdateText () call - so you can
44 tell if the text is truely updated yet.</p>
45 */
46 inline void MarkerOwner::EarlyDidUpdateText (const UpdateInfo& /*updateInfo*/) noexcept
47 {
48 }
49 /*
50 @METHOD: MarkerOwner::DidUpdateText
51 @DESCRIPTION: <p>This method is called by a @'TextStore' after a text update has taken place. You can use this to update
52 some appropriate data structures based on the change. NB: an exception <em>cannot</em> be raised by this method, or
53 any overrides of it.</p>
54 */
55 inline void MarkerOwner::DidUpdateText (const UpdateInfo& /*updateInfo*/) noexcept
56 {
57 }
58
59 /*
60 @METHOD: Marker::AboutToUpdateText
61 @DESCRIPTION: <p>Notification callback which is called by a @'TextStore' when the text is updated. You override this
62 if you want todo something special when the text marked by this particular marker is changed. You can throw
63 from this method to prevent the update from actually taking place.</p>
64 */
65 inline void Marker::AboutToUpdateText (const UpdateInfo& /*updateInfo*/)
66 {
67 }
68 /*
69 @METHOD: Marker::DidUpdateText
70 @DESCRIPTION: <p>Notification callback which is called by a @'TextStore' after the text is updated. You override this
71 if you want todo something special when the text marked by this particular marker is changed. You cannot throw
72 from this routine. Use @'Marker::AboutToUpdateText' to preflight, and assure any DidUpdateText method calls will
73 work without a hitch.</p>
74 */
75 inline void Marker::DidUpdateText (const UpdateInfo& /*updateInfo*/) noexcept
76 {
77 }
78 /*
79 @METHOD: Marker::GetStart
80 @DESCRIPTION: <p>Returns the marker position of the start (lhs) of the marker.</p>
81 <p>It is illegal to call this
82 if the marker is not currently added to a @'TextStore'.</p>
83 */
84 inline size_t Marker::GetStart () const
85 {
86 AssertNotNull (fTextStoreHook);
87 return fTextStoreHook->GetStart ();
88 }
89 /*
90 @METHOD: Marker::GetEnd
91 @DESCRIPTION: <p>Returns the marker position of the end (rhs) of the marker.</p>
92 <p>It is illegal to call this
93 if the marker is not currently added to a @'TextStore'.</p>
94 */
95 inline size_t Marker::GetEnd () const
96 {
97 AssertNotNull (fTextStoreHook);
98 return fTextStoreHook->GetEnd ();
99 }
100 /*
101 @METHOD: Marker::GetLength
102 @DESCRIPTION: <p>Return the length - in @'Led_tChar's - of the marker span. This value can be zero. But if it
103 ever goes to zero, Led will never re-inflate the marker. You must reset its bounds manually via @'Marker::SetRange' ().</p>
104 */
105 inline size_t Marker::GetLength () const
106 {
107 AssertNotNull (fTextStoreHook);
108 return fTextStoreHook->GetLength ();
109 }
110 /*
111 @METHOD: Marker::GetOwner
112 @DESCRIPTION: <p>Return the current marker owner. Unlike most Marker methods, this <em>can</em> be called when the
113 marker hasn't yet been added to a @'TextStore'. It just returns nullptr in that case. Note, it can return nullptr anyhow,
114 as that is a valid value to specify in @'TextStore::AddMarker' ().</p>
115 */
116 inline MarkerOwner* Marker::GetOwner () const
117 {
118 // fTextStoreHook CAN be nullptr here if we don't yet have a marker owner!
119 return (fTextStoreHook == nullptr ? nullptr : fTextStoreHook->GetOwner ());
120 }
121 /*
122 @METHOD: Marker::GetRange
123 @DESCRIPTION: <p>Return the start and end position of the marker. You must set its bounds
124 via @'Marker::SetRange' ().</p>
125 */
126 inline void Marker::GetRange (size_t* start, size_t* end) const
127 {
128 RequireNotNull (start);
129 RequireNotNull (end);
130 RequireNotNull (fTextStoreHook);
131 fTextStoreHook->GetStartEnd (start, end);
132 }
133
134 /*
135 ********************************************************************************
136 ********************* Led::MarkerOwner::UpdateInfo *****************************
137 ********************************************************************************
138 */
139 inline MarkerOwner::UpdateInfo::UpdateInfo (size_t from, size_t to, const Led_tChar* withWhat, size_t withWhatCharCount,
140 bool textModified, bool realContentUpdate)
141 : fReplaceFrom{from}
142 , fReplaceTo{to}
143 , fTextInserted{withWhat}
144 , fTextLength{withWhatCharCount}
145 , fTextModified{textModified}
146 , fRealContentUpdate{realContentUpdate}
147 {
148 }
149 inline size_t MarkerOwner::UpdateInfo::GetResultingRHS () const
150 {
151 return fTextModified ? (fReplaceFrom + fTextLength) : fReplaceTo;
152 }
153
154 /*
155 ********************************************************************************
156 ********************** Led::MarkerMortuary<MARKER> *****************************
157 ********************************************************************************
158 */
159 template <typename MARKER>
160 inline MarkerMortuary<MARKER>::~MarkerMortuary ()
161 {
162 Assert (fMarkersToBeDeleted.size () == 0); // these better be deleted by now!
163 }
164 template <typename MARKER>
165 /*
166 @METHOD: MarkerMortuary<MARKER>::AccumulateMarkerForDeletion
167 @DESCRIPTION: <p>Since Led can remove large numbers of markers at a time much faster than it can
168 remove a single one, we have this special interface to accumulate markers to be
169 deleted, and delete them all at once. This doesn't ususally <em>NEED</em> to be used. But it sometimes
170 does <em>Need</em> to be used to avoid the case where a marker is kept in a list (say back on the stack in
171 some caller for example), and we decide it must be deleted, but we don't know for sure if it
172 will be accessed from that stack-based list again.</p>
173 <p>After a call to this routine, the marker is effectively dead and removed
174 from OUR marker list, but it is not 'delete' d. And the call to RemoveMarker</p>
175 is postponed til '@'MarkerMortuary<MARKER>::FinalizeMarkerDeletions ()'.
176 <p>NB: It is illegal to accumulate a marker for deletion twice (detected error). And greatly
177 discouraged using it afterwards.</p>
178 */
179 inline void MarkerMortuary<MARKER>::AccumulateMarkerForDeletion (MARKER* m)
180 {
181 RequireNotNull (m);
182 Require (IndexOf (fMarkersToBeDeleted, m) == kBadIndex);
184 if (fMarkersToBeDeleted.size () != 0) {
185 RequireNotNull (static_cast<Marker*> (fMarkersToBeDeleted[0])->GetOwner ());
186 RequireNotNull (static_cast<Marker*> (fMarkersToBeDeleted[0])->GetOwner ()->PeekAtTextStore ());
187 RequireNotNull (static_cast<Marker*> (m)->GetOwner ());
188 RequireNotNull (static_cast<Marker*> (m)->GetOwner ()->PeekAtTextStore ());
189 Require (static_cast<Marker*> (fMarkersToBeDeleted[0])->GetOwner ()->PeekAtTextStore () ==
190 static_cast<Marker*> (m)->GetOwner ()->PeekAtTextStore ());
191 }
192 }
193
194 // Added for SPR#0822 - see for details
195 Private_::PreRemoveMarker_ (static_cast<Marker*> (m)->GetOwner ()->GetTextStore (), m);
196
197 // NB: fMarkersToBeDeleted SB a linked list, so we don't need todo any mem allocations
198 // and don't need to worry about failing to allocate memory here!!!
199 fMarkersToBeDeleted.push_back (m);
200 }
201 template <typename MARKER>
202 /*
203 @METHOD: MarkerMortuary<MARKER>::SafeAccumulateMarkerForDeletion
204 @DESCRIPTION: <p>Like @'MarkerMortuary<MARKER>::AccumulateMarkerForDeletion', but its OK to add a marker more than once.</p>
205 */
206 void MarkerMortuary<MARKER>::SafeAccumulateMarkerForDeletion (MARKER* m)
207 {
208 RequireNotNull (m);
209 if (IndexOf (fMarkersToBeDeleted, m) == kBadIndex) {
210 AccumulateMarkerForDeletion (m);
211 }
212 }
213 template <typename MARKER>
214 /*
215 @METHOD: MarkerMortuary<MARKER>::FinalizeMarkerDeletions
216 @DESCRIPTION: <p>Call anytime, but preferably after we've finished doing a bunch of marker deletions.
217 See @'MarkerMortuary<MARKER>::AccumulateMarkerForDeletion' for more information.</p>
218 */
219 void MarkerMortuary<MARKER>::FinalizeMarkerDeletions () noexcept
220 {
221 if (fMarkersToBeDeleted.size () != 0) {
222 MarkerOwner* owner = static_cast<Marker*> (fMarkersToBeDeleted[0])->GetOwner ();
223 AssertNotNull (owner);
224 TextStore& textStore = owner->GetTextStore ();
225
226 // NB: No exceptions can happen in any of this - all these deletes allocate no memory (LGP 950415)
227 MARKER* const* markersToBeDeleted_ = &fMarkersToBeDeleted.front ();
228 Marker* const* markersToBeDeleted = (Marker* const*)markersToBeDeleted_; // need cast - but safe - cuz array of MARKER* and looking for Marker* - safe cuz const array!
229 Private_::RemoveMarkers_ (textStore, markersToBeDeleted, fMarkersToBeDeleted.size ());
230 for (size_t i = 0; i < fMarkersToBeDeleted.size (); ++i) {
231 delete fMarkersToBeDeleted[i];
232 }
233 fMarkersToBeDeleted.clear ();
234 }
235 }
236 template <typename MARKER>
237 /*
238 @METHOD: MarkerMortuary<MARKER>::IsEmpty
239 @DESCRIPTION: <p>Mostly used for assertions. Checks all accumulated markers have been finalized.</p>
240 */
241 inline bool MarkerMortuary<MARKER>::IsEmpty () const noexcept
242 {
243 return fMarkersToBeDeleted.size () == 0;
244 }
245
246 /*
247 ********************************************************************************
248 ********************************** Led::Contains *******************************
249 ********************************************************************************
250 */
251 /*
252 @METHOD: Contains
253 @DESCRIPTION: <p>Four overloads. Basically this tests if the first marker is contained in the second.
254 </p>
255 <p>Note that Led doesn't treat the beginning and ending of a marker symmetrically. It
256 is considered legitimate containment if the marker start positions are equal, but <em>not</em>
257 if the marker end positions are equal</p>
258 */
259 inline bool Contains (const Marker& containedMarker, const Marker& containerMarker)
260 {
261 size_t containerStart;
262 size_t containerEnd;
263 containerMarker.GetRange (&containerStart, &containerEnd);
264
265 size_t containedStart;
266 size_t containedEnd;
267 containedMarker.GetRange (&containedStart, &containedEnd);
268 return ((containedStart >= containerStart) and (containerEnd >= containedEnd));
269 }
270 inline bool Contains (size_t containedMarkerStart, size_t containedMarkerEnd, const Marker& containerMarker)
271 {
272 Assert (containedMarkerStart <= containedMarkerEnd);
273 size_t containerStart;
274 size_t containerEnd;
275 containerMarker.GetRange (&containerStart, &containerEnd);
276 return ((containedMarkerStart >= containerStart) and (containerEnd >= containedMarkerEnd));
277 }
278 inline bool Contains (const Marker& marker, size_t charPos)
279 {
280 size_t start;
281 size_t end;
282 marker.GetRange (&start, &end);
283 return (charPos >= start and charPos < end);
284 }
285 inline bool Contains (size_t containedMarkerStart, size_t containedMarkerEnd, size_t charPos)
286 {
287 return (charPos >= containedMarkerStart and charPos < containedMarkerEnd);
288 }
289
290 /*
291 ********************************************************************************
292 ******************************** Led::TempMarker *******************************
293 ********************************************************************************
294 */
295 inline size_t TempMarker::GetStart () const
296 {
297 return fMarker.GetStart ();
298 }
299 inline size_t TempMarker::GetEnd () const
300 {
301 return fMarker.GetEnd ();
302 }
303 inline size_t TempMarker::GetLength () const
304 {
305 return fMarker.GetLength ();
306 }
307 inline void TempMarker::GetLocation (size_t* start, size_t* end) const
308 {
309 fMarker.GetRange (start, end);
310 }
311
312 /*
313 ********************************************************************************
314 **************** Led::TemporaryMarkerSlideDown<MARKER> *************************
315 ********************************************************************************
316 */
317 template <typename MARKER>
318 TemporaryMarkerSlideDown<MARKER>::TemporaryMarkerSlideDown (TextStore& ts, const vector<MARKER*>& m, ptrdiff_t slideBy)
319 : fTextStore (ts)
320 , fMarkers (m)
321 , fSlideBy (slideBy)
322 {
323 for (auto i = fMarkers.begin (); i != fMarkers.end (); ++i) {
324 size_t start = 0;
325 size_t end = 0;
326 (*i)->GetRange (&start, &end);
327 Private_::SetMarkerRange_ (fTextStore, (*i), start + fSlideBy, end + fSlideBy);
328 }
329 }
330 template <typename MARKER>
331 TemporaryMarkerSlideDown<MARKER>::~TemporaryMarkerSlideDown ()
332 {
333 for (auto i = fMarkers.begin (); i != fMarkers.end (); ++i) {
334 size_t start = 0;
335 size_t end = 0;
336 (*i)->GetRange (&start, &end);
337 Private_::SetMarkerRange_ (fTextStore, (*i), start - fSlideBy, end - fSlideBy);
338 }
339 }
340
341}
#define AssertNotNull(p)
Definition Assertions.h:333
#define EnsureNotNull(p)
Definition Assertions.h:340
#define qStroika_Foundation_Debug_AssertionsChecked
The qStroika_Foundation_Debug_AssertionsChecked flag determines if assertions are checked and validat...
Definition Assertions.h:48
#define RequireNotNull(p)
Definition Assertions.h:347