Stroika Library 3.0d25
 
Loading...
Searching...
No Matches
SimpleTextImager.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
4#include "Stroika/Frameworks/StroikaPreComp.h"
5
6#include <limits.h>
7
8#include "Stroika/Frameworks/Led/LineBasedPartition.h"
9#include "Stroika/Frameworks/Led/Support.h"
10
11#include "SimpleTextImager.h"
12
13using namespace Stroika::Foundation;
14using namespace Stroika::Frameworks;
15using namespace Stroika::Frameworks::Led;
16
17using PartitionMarker = Partition::PartitionMarker;
18#if qStroika_Frameworks_Led_SupportGDI
19using PartitionPtr = PartitioningTextImager::PartitionPtr;
20
21/*
22 ********************************************************************************
23 ********************** SimpleTextImager::MyPartitionWatcher ********************
24 ********************************************************************************
25 */
26void SimpleTextImager::MyPartitionWatcher::Init (PartitionPtr partition, SimpleTextImager* imager)
27{
28 fImager = imager;
29 if (partition.get () != nullptr) {
30 partition->AddPartitionWatcher (this);
31 }
32}
33
34void SimpleTextImager::MyPartitionWatcher::UnInit (PartitionPtr partition)
35{
36 fImager = NULL;
37 if (partition.get () != nullptr) {
38 partition->RemovePartitionWatcher (this);
39 }
40}
41
42void SimpleTextImager::MyPartitionWatcher::AboutToSplit (PartitionMarker* /*pm*/, size_t /*at*/, void** /*infoRecord*/) const noexcept
43{
44 AssertNotNull (fImager);
45}
46
47void SimpleTextImager::MyPartitionWatcher::DidSplit (void* /*infoRecord*/) const noexcept
48{
49 AssertNotNull (fImager);
50 fImager->InvalidateTotalRowsInWindow ();
51}
52
53void SimpleTextImager::MyPartitionWatcher::AboutToCoalece (PartitionMarker* pm, void** infoRecord) const noexcept
54{
55 AssertNotNull (fImager);
56
57 /*
58 * If we are deleting the current marker on the top of the screen -
59 * we must update that marker to refer to its following marker.
60 */
61 PartitionMarker* newTopLine = NULL;
62 if (pm == fImager->fTopLinePartitionMarkerInWindow) {
63 if (pm->GetNext () == NULL) {
64 newTopLine = fImager->fTopLinePartitionMarkerInWindow->GetPrevious ();
65 }
66 else {
67 newTopLine = fImager->fTopLinePartitionMarkerInWindow->GetNext ();
68 }
69 AssertNotNull (newTopLine);
70 }
71
72 *infoRecord = newTopLine;
73}
74
75void SimpleTextImager::MyPartitionWatcher::DidCoalece (void* infoRecord) const noexcept
76{
77 AssertNotNull (fImager);
78
79 PartitionMarker* newTopLine = reinterpret_cast<PartitionMarker*> (infoRecord);
80
81 // If we must change the top-row - then do so NOW - cuz NOW its safe to call GetRowCount ()
82 if (newTopLine != NULL) {
83 fImager->SetTopRowInWindow_ (RowReference (newTopLine));
84 }
85 AssertNotNull (fImager->fTopLinePartitionMarkerInWindow); // can't delete last one...
86 AssertMember (fImager->fTopLinePartitionMarkerInWindow, PartitionMarker);
87 fImager->InvalidateTotalRowsInWindow ();
88}
89
90/*
91 ********************************************************************************
92 ********************************* SimpleTextImager *****************************
93 ********************************************************************************
94 */
95SimpleTextImager::SimpleTextImager ()
96 : fICreatedPartition{false}
97 , fMyPartitionWatcher{}
98 , fRowHeight{DistanceType (-1)}
99 , fInterlineSpace{0}
100 , fTopLinePartitionMarkerInWindow{NULL}
101 , fTotalRowsInWindow{0} // value must be computed
102{
103}
104
105SimpleTextImager::~SimpleTextImager ()
106{
107 Assert (fTopLinePartitionMarkerInWindow == NULL);
108}
109
110void SimpleTextImager::HookLosingTextStore ()
111{
112 inherited::HookLosingTextStore ();
113 HookLosingTextStore_ ();
114}
115
116void SimpleTextImager::HookLosingTextStore_ ()
117{
118 InvalidateTotalRowsInWindow ();
119 fTopLinePartitionMarkerInWindow = NULL;
120 // Only if we created the partition should we delete it. If it was set by external SetPartition () call, don't unset it here.
121 if (fICreatedPartition) {
122 fMyPartitionWatcher.UnInit (GetPartition ());
123 fICreatedPartition = false;
124 inherited::SetPartition (NULL); // Call inherited so we avoid creating a new partition - PeekAtTextStore () still non-NULL, but soon to be NULL
125 }
126}
127
128void SimpleTextImager::HookGainedNewTextStore ()
129{
130 inherited::HookGainedNewTextStore ();
131 HookGainedNewTextStore_ ();
132}
133
134void SimpleTextImager::HookGainedNewTextStore_ ()
135{
136 Require (fTopLinePartitionMarkerInWindow == NULL);
137 if (GetPartition ().get () == nullptr) {
138 SetPartition (NULL); // sets default one in since we have a textstore...
139 }
140 Assert (fTopLinePartitionMarkerInWindow == NULL);
141 fTopLinePartitionMarkerInWindow = GetFirstPartitionMarker ();
142 AssureWholeWindowUsedIfNeeded ();
143 InvalidateScrollBarParameters (); // even if we don't change the top row, we might change enuf about the text to change sbar
144}
145
146/*
147@METHOD: SimpleTextImager::SetPartition
148@DESCRIPTION: <p>See @'PartitioningTextImager::SetPartition'. Same, but if called with NULL (and we
149 have a TextStore) then make a default partition.</p>
150*/
151void SimpleTextImager::SetPartition (const PartitionPtr& partitionPtr)
152{
153 fMyPartitionWatcher.UnInit (GetPartition ());
154 inherited::SetPartition (partitionPtr);
155 fICreatedPartition = false;
156 if (GetPartition ().get () == nullptr and PeekAtTextStore () != NULL) {
157 inherited::SetPartition (PartitionPtr (new LineBasedPartition (GetTextStore ())));
158 fICreatedPartition = true;
159 }
160 fMyPartitionWatcher.Init (GetPartition (), this);
161}
162
163PartitioningTextImager::PartitionPtr SimpleTextImager::MakeDefaultPartition () const
164{
165 return PartitionPtr{new LineBasedPartition (GetTextStore ())};
166}
167
168/*
169@METHOD: SimpleTextImager::ReCalcRowHeight
170@DESCRIPTION: <p>Called by @'SimpleTextImager::GetRowHeight' on an as-needed basis (when invalidated by
171 @'SimpleTextImager::InvalidateRowHeight'). All rows in a SimpleTextImager are the same height.
172 By default this simple uses MeasureSegmentHeight_ (GetDefaultFont (), 0, 0) to establish the per-row
173 height. You can override this to provide whatever height you wish. But anything smaller will look cut-off.</p>
174*/
175DistanceType SimpleTextImager::ReCalcRowHeight () const
176{
177 return MeasureSegmentHeight_ (GetDefaultFont (), 0, 0);
178}
179
180DistanceType SimpleTextImager::MeasureSegmentHeight (size_t /*from*/, size_t /*to*/) const
181{
182 //return (MeasureSegmentHeight_ (GetDefaultFont (), from, to));
183 return GetRowHeight ();
184}
185
186DistanceType SimpleTextImager::MeasureSegmentBaseLine (size_t /*from*/, size_t /*to*/) const
187{
188 // If the user specifies an unusually large row-height, by default make this whitespace become above the
189 // baseline, and not below.
190 DistanceType rh = GetRowHeight ();
191 DistanceType sbrh = MeasureSegmentHeight_ (GetDefaultFont (), 0, 0);
192 DistanceType bl = MeasureSegmentBaseLine_ (GetDefaultFont (), 0, 0);
193 if (rh > sbrh) {
194 bl += (rh - sbrh);
195 }
196 return bl;
197}
198
199bool SimpleTextImager::GetIthRowReferenceFromHere (RowReference* adjustMeInPlace, ptrdiff_t ith) const
200{
201 for (; ith > 0; --ith) {
202 if (not GetNextRowReference (adjustMeInPlace)) {
203 return false;
204 }
205 }
206 for (; ith < 0; ++ith) {
207 if (not GetPreviousRowReference (adjustMeInPlace)) {
208 return false;
209 }
210 }
211 return true;
212}
213
214size_t SimpleTextImager::GetRowNumber (RowReference rowRef) const
215{
216 // NB: This routine is VERY EXPENSIVE, if the text above the given row has not yet been wrapped, since
217 // it forces a wrap. This is meant only to be a convenient code-saver in implementing rownumber based
218 // APIs - even though their use is discouraged...
219 size_t rowNumber = 0;
220 AssertNotNull (rowRef.GetPartitionMarker ());
221 for (PartitionMarker* cur = rowRef.GetPartitionMarker ()->GetPrevious (); cur != NULL; cur = cur->GetPrevious ()) {
222 rowNumber += 1;
223 }
224 return rowNumber;
225}
226
227size_t SimpleTextImager::CountRowDifference (RowReference lhs, RowReference rhs) const
228{
229 /*
230 * See which row reference comes before the other, and then can from one TO the
231 * other. Sadly - this forces the wrapping of all that text in between.
232 *
233 * Note - this CAN be expensive if the two row references are far apart, as it requires
234 * wrapping all the text in-between.
235 */
236 PartitionMarker* lhsPM = lhs.GetPartitionMarker ();
237 PartitionMarker* rhsPM = rhs.GetPartitionMarker ();
238 size_t lhsMarkerStart = lhsPM->GetStart ();
239 size_t rhsMarkerStart = rhsPM->GetStart ();
240 bool leftSmaller = ((lhsMarkerStart < rhsMarkerStart) or ((lhsMarkerStart == rhsMarkerStart)));
241 RowReference firstRowRef = leftSmaller ? lhs : rhs;
242 RowReference lastRowRef = leftSmaller ? rhs : lhs;
243
244 size_t rowsGoneBy = 0;
245 for (RowReference cur = firstRowRef; cur != lastRowRef; ++rowsGoneBy) {
246 [[maybe_unused]] bool result = GetIthRowReferenceFromHere (&cur, 1);
247 Assert (result);
248 }
249 return rowsGoneBy;
250}
251
252size_t SimpleTextImager::GetTopRowInWindow () const
253{
254 // NB: Use of this function is discouraged as it is inefficent in the presence of word-wrapping
255 return (GetRowNumber (GetTopRowReferenceInWindow ()));
256}
257
258size_t SimpleTextImager::GetTotalRowsInWindow () const
259{
260 return GetTotalRowsInWindow_ ();
261}
262
263size_t SimpleTextImager::GetLastRowInWindow () const
264{
265 // NB: Use of this function is discouraged as it is inefficent in the presence of word-wrapping
266 return GetRowNumber (GetLastRowReferenceInWindow ());
267}
268
269void SimpleTextImager::SetTopRowInWindow (size_t newTopRow)
270{
271 // NB: Use of this function is discouraged as it is inefficent in the presence of word-wrapping
272 Assert (newTopRow >= 0);
273
274 SetTopRowInWindow (GetIthRowReference (newTopRow));
275
276 Assert (GetTopRowInWindow () == newTopRow); // Since a SetTopRowInWindow() was called - all the
277 // intervening lines have been wrapped anyhow - may
278 // as well check we have our definitions straight...
279}
280
281void SimpleTextImager::AssureWholeWindowUsedIfNeeded ()
282{
283 SetTopRowInWindow (GetTopRowReferenceInWindow ());
284}
285
286size_t SimpleTextImager::GetMarkerPositionOfStartOfWindow () const
287{
288 return (GetStartOfRow (GetTopRowReferenceInWindow ()));
289}
290
291size_t SimpleTextImager::GetMarkerPositionOfEndOfWindow () const
292{
293 return GetEndOfRow (GetLastRowReferenceInWindow ());
294}
295
296size_t SimpleTextImager::GetMarkerPositionOfStartOfLastRowOfWindow () const
297{
298 return GetStartOfRow (GetLastRowReferenceInWindow ());
299}
300
301ptrdiff_t SimpleTextImager::CalculateRowDeltaFromCharDeltaFromTopOfWindow (long deltaChars) const
302{
303 Assert (long (GetMarkerPositionOfStartOfWindow ()) >= 0 - deltaChars);
304 size_t pos = long (GetMarkerPositionOfStartOfWindow ()) + deltaChars;
305 RowReference targetRow = GetRowReferenceContainingPosition (pos);
306 size_t rowDiff = CountRowDifference (targetRow, GetTopRowReferenceInWindow ());
307 return (deltaChars >= 0) ? rowDiff : -long (rowDiff);
308}
309
310ptrdiff_t SimpleTextImager::CalculateCharDeltaFromRowDeltaFromTopOfWindow (ptrdiff_t deltaRows) const
311{
312 RowReference row = GetIthRowReferenceFromHere (GetTopRowReferenceInWindow (), deltaRows);
313 return long (GetStartOfRow (row)) - long (GetMarkerPositionOfStartOfWindow ());
314}
315
316void SimpleTextImager::ScrollByIfRoom (ptrdiff_t downByRows)
317{
318 RowReference newTopRow = GetTopRowReferenceInWindow ();
319 (void)GetIthRowReferenceFromHere (&newTopRow, downByRows); // ignore result cuz we did say - IF-ROOM!
320 SetTopRowInWindow (newTopRow);
321}
322
323/*
324@METHOD: SimpleTextImager::ScrollSoShowing
325@DESCRIPTION: <p>Implement @'TextImager::ScrollSoShowing' API.</p>
326*/
327void SimpleTextImager::ScrollSoShowing (size_t markerPos, size_t andTryToShowMarkerPos)
328{
329 Assert (markerPos >= 0);
330 Assert (markerPos <= GetLength ()); // Allow any marker position (not just character?)
331 Assert (fTotalRowsInWindow == 0 or fTotalRowsInWindow == ComputeRowsThatWouldFitInWindowWithTopRow (GetTopRowReferenceInWindow ()));
332
333 if (andTryToShowMarkerPos == 0) { // special flag indicating we don't care...
334 andTryToShowMarkerPos = markerPos;
335 }
336 Assert (andTryToShowMarkerPos >= 0);
337 Assert (andTryToShowMarkerPos <= GetLength ()); // Allow any marker position (not just character?)
338
339 /*
340 * First check and see if the given position is within the current window
341 * If so, do nothing. If it isn't, then try making the given selection
342 * the first row (this later strategy is subject to chanage - but its
343 * a plausible, and easy to implement start).
344 */
345 size_t startOfWindow = GetMarkerPositionOfStartOfWindow ();
346 size_t endOfWindow = GetMarkerPositionOfEndOfWindow ();
347 if (markerPos >= startOfWindow and markerPos < endOfWindow and andTryToShowMarkerPos >= startOfWindow and andTryToShowMarkerPos < endOfWindow) {
348 ScrollSoShowingHHelper (markerPos, andTryToShowMarkerPos);
349 return; // nothing (vertical) changed...
350 }
351
352 RowReference originalTop = GetTopRowReferenceInWindow ();
353
354 /*
355 * Now things are a little complicated. We want to show both ends of the
356 * selection - if we can. But - if we must show only one end or the other, we
357 * make sure we show the 'markerPos' end.
358 *
359 * We also would probably (maybe?) like to minimize the amount we scroll-by.
360 * that is - if we only need to show one more line, don't scroll by
361 * more than that.
362 */
363
364 /*
365 * First get us to a RowReference which is close to where we will eventually end up. That way, and calls
366 * we do which will require word-wrapping (stuff to count rows) will only get applied to rows with a good
367 * liklihood of needing to be wrapped anyhow.
368 */
369 RowReference newTop = originalTop;
370
371 {
372 // Speed hack recomended by Remo (roughly) - SPR#0553
373 // if our target is closer to 0, then to the current window top, start at 0.
374 // Similarly if we are closer to the end ( 0 .. M .. E } ==> M + (E-M)/2 ==> (E+M)/2
375 if (markerPos < newTop.GetPartitionMarker ()->GetStart () / 2) {
376 newTop = GetRowReferenceContainingPosition (0);
377 }
378 else if (markerPos > (GetEnd () + newTop.GetPartitionMarker ()->GetEnd ()) / 2) {
379 newTop = GetRowReferenceContainingPosition (GetEnd ());
380 }
381 }
382
383 while (markerPos < newTop.GetPartitionMarker ()->GetStart ()) {
384 newTop = RowReference (newTop.GetPartitionMarker ()->GetPrevious ());
385 }
386 // only try scrolling down at all if we don't already fit in the window - cuz otherwise - we could artificially
387 // scroll when not needed.
388 if (not PositionWouldFitInWindowWithThisTopRow (markerPos, newTop)) {
389 while (markerPos > newTop.GetPartitionMarker ()->GetEnd ()) {
390 if (newTop.GetPartitionMarker ()->GetNext () == NULL) {
391 // could be going to row IN last line
392 break;
393 }
394 newTop = RowReference (newTop.GetPartitionMarker ()->GetNext ());
395 }
396 Assert (Contains (markerPos, markerPos, *newTop.GetPartitionMarker ()));
397 }
398
399 /*
400 * At this point, we have a newTop which is CLOSE to where it will end up. We now adjust the
401 * newTop to ASSURE that markerPos is shown.
402 */
403 while (markerPos < GetStartOfRow (newTop) and GetPreviousRowReference (&newTop))
404 ;
405 while (not PositionWouldFitInWindowWithThisTopRow (markerPos, newTop) and GetNextRowReference (&newTop))
406 ;
407
408 // At this point our main desired position should be visible
409 Assert (markerPos >= GetStartOfRow (newTop));
410 Assert (PositionWouldFitInWindowWithThisTopRow (markerPos, newTop));
411
412 /*
413 * Now - try to adjust the newTop so that the 'andTryToShowMarkerPos' is also
414 * shown. But - BE CAREFUL WE PRESERVE VISIBILITY OF 'markerPos'!!!
415 */
416 while (not PositionWouldFitInWindowWithThisTopRow (andTryToShowMarkerPos, newTop)) {
417 RowReference trailNewTop = newTop;
418 if (andTryToShowMarkerPos < GetStartOfRow (trailNewTop)) {
419 if (not GetPreviousRowReference (&trailNewTop)) {
420 break;
421 }
422 }
423 else {
424 if (not GetNextRowReference (&trailNewTop)) {
425 break;
426 }
427 }
428 if (PositionWouldFitInWindowWithThisTopRow (markerPos, trailNewTop)) {
429 newTop = trailNewTop;
430 }
431 else {
432 break;
433 }
434 }
435
436 /*
437 * Now - see if we've moved our 'newTop' by more than a certain threshold. If YES - then we may as well scroll
438 * so that the new region of interest is CENTERED in the window.
439 */
440 const unsigned kRowMoveThreshold = 1;
441 if (CountRowDifference (originalTop, newTop) > kRowMoveThreshold) {
442 bool mustPreserveSecondPos = PositionWouldFitInWindowWithThisTopRow (andTryToShowMarkerPos, newTop);
443
444 // Now try to center the region of interest. Center by number of rows - not height of pixels. Height of pixels
445 // might be better - but I think this is slightly easier - and probably just as good most of the time.
446 size_t topMarkerPos = min (markerPos, andTryToShowMarkerPos);
447 size_t botMarkerPos = max (markerPos, andTryToShowMarkerPos);
448 size_t numRowsAbove = CountRowDifference (newTop, GetRowReferenceContainingPosition (topMarkerPos));
449 size_t rowsInWindow = ComputeRowsThatWouldFitInWindowWithTopRow (newTop);
450 RowReference lastRowInWindow = GetIthRowReferenceFromHere (newTop, rowsInWindow - 1);
451 size_t numRowsBelow = CountRowDifference (lastRowInWindow, GetRowReferenceContainingPosition (botMarkerPos));
452
453 size_t numRowsToSpare = numRowsAbove + numRowsBelow;
454
455 // to to make numRowsAbove = 1/2 of numRowsToSpare
456 RowReference trailNewTop = newTop;
457 GetIthRowReferenceFromHere (&trailNewTop, int (numRowsAbove) - int (numRowsToSpare / 2));
458 if (PositionWouldFitInWindowWithThisTopRow (markerPos, trailNewTop) and
459 (not mustPreserveSecondPos or PositionWouldFitInWindowWithThisTopRow (andTryToShowMarkerPos, trailNewTop))) {
460 newTop = trailNewTop;
461 }
462 }
463
464 SetTopRowInWindow (newTop); // This handles any notification of scrolling/update of sbars etc...
465
466 Assert (GetMarkerPositionOfStartOfWindow () <= markerPos and markerPos <= GetMarkerPositionOfEndOfWindow ());
467
468 /*
469 * Must call this AFTER we've done some VERTICAL scrolling - cuz the vertical scrolling could have affected the MaxHPOS.
470 */
471 ScrollSoShowingHHelper (markerPos, andTryToShowMarkerPos);
472}
473
474void SimpleTextImager::SetTopRowInWindow (RowReference row)
475{
476 if (GetForceAllRowsShowing ()) {
477 row = AdjustPotentialTopRowReferenceSoWholeWindowUsed (row);
478 }
479 if (row != GetTopRowReferenceInWindow ()) {
480 SetTopRowInWindow_ (row);
481 InvalidateScrollBarParameters ();
482 }
483}
484
485void SimpleTextImager::Draw (const Led_Rect& subsetToDraw, bool printing)
486{
487 Invariant ();
488
489 Led_Rect rowsLeftToDrawRect = GetWindowRect ();
490
491 Tablet_Acquirer tablet_ (this);
492 Tablet* tablet = tablet_;
493 AssertNotNull (tablet);
494
495/*
496 * Save old font/pen/brush info here, and restore - even in the presence of exceptions -
497 * on the way out. That way - the drawsegment code need not worry about restoring
498 * these things.
499 */
500#if qStroika_Platform_Windows
501 GDI_Obj_Selector pen (tablet, ::GetStockObject (NULL_PEN));
502 GDI_Obj_Selector brush (tablet, ::GetStockObject (NULL_BRUSH));
503#endif
504
505 /*
506 * Do this AFTER the save of colors above cuz no need in preserving that crap for
507 * offscreen bitmap we cons up here on the fly.
508 */
509 OffscreenTablet thisIsOurNewOST;
510 if (GetImageUsingOffscreenBitmaps () and not printing) {
511 thisIsOurNewOST.Setup (tablet_);
512 }
513
514 try {
515 size_t totalRowsInWindow = GetTotalRowsInWindow_ ();
516 RowReference topRowInWindow = GetTopRowReferenceInWindow ();
517 size_t rowsLeftInWindow = totalRowsInWindow;
518 for (PartitionMarker* pm = topRowInWindow.GetPartitionMarker (); rowsLeftInWindow != 0; pm = pm->GetNext ()) {
519 Assert (pm != NULL);
520 size_t startSubRow = 0;
521 size_t maxSubRow = 0;
522 maxSubRow = rowsLeftInWindow - 1 + startSubRow;
523 size_t rowsDrawn = 0;
524 DrawPartitionElement (pm, startSubRow, maxSubRow, tablet, (GetImageUsingOffscreenBitmaps () and not printing) ? &thisIsOurNewOST : NULL,
525 printing, subsetToDraw, &rowsLeftToDrawRect, &rowsDrawn);
526 Assert (rowsLeftInWindow >= rowsDrawn);
527 rowsLeftInWindow -= rowsDrawn;
528 }
529
530 /*
531 * Now erase to the end of the page.
532 */
533 Assert (tablet == tablet_); // Draw to screen directly past here...
534 {
535 Led_Rect eraser = GetWindowRect ();
536 eraser.top = rowsLeftToDrawRect.top; // only from here down...
537 eraser.bottom = subsetToDraw.bottom; // cuz image rect may not cover what it used to, and never any need to
538
539 if (eraser.top > eraser.bottom) {
540 eraser.bottom = eraser.top;
541 }
542
543 // SEE IF WE CAN TIGHTEN THIS TEST A BIT MORE, SO WHEN NO PIXELS WILL BE DRAWN, WE DONT BOTHER
544 // IN OTHER WORDS, CHANGE A COUPLE <= to < - LGP 970315
545
546 // QUICKIE INTERSECT TEST
547 if (((eraser.top >= subsetToDraw.top and eraser.top <= subsetToDraw.bottom) or
548 (eraser.bottom >= subsetToDraw.top and eraser.bottom <= subsetToDraw.bottom)) and
549 (eraser.GetHeight () > 0 and eraser.GetWidth () > 0)) {
550
551 if (GetImageUsingOffscreenBitmaps () and not printing) {
552 tablet = thisIsOurNewOST.PrepareRect (eraser);
553 }
554 EraseBackground (tablet, eraser, printing);
555#if 0
556 // Do we want to hilight the section after the end of the last row displayed if the selection continues onto
557 // the next window? Somehow, I think it ends up looking schlocky. Leave off for now...
558 size_t hilightStart = GetSelectionStart ();
559 size_t hilightEnd = GetSelectionEnd ();
560 size_t end = GetMarkerPositionOfEndOfWindow ();
561 bool segmentHilightedAtEnd = GetSelectionShown () and (hilightStart < end) and (end <= hilightEnd);
562 if (not printing and segmentHilightedAtEnd) {
563 HilightARectangle (tablet, eraser);
564 }
565#endif
566 if (GetImageUsingOffscreenBitmaps () and not printing) {
567 /*
568 * Blast offscreen bitmap onto the screen.
569 */
570 thisIsOurNewOST.BlastBitmapToOrigTablet ();
571 tablet = tablet_; // don't use offscreen tablet past here... Draw to screen directly!!!
572 }
573 }
574 }
575 }
576 catch (...) {
577 throw;
578 }
579}
580
581/*
582@METHOD: SimpleTextImager::DrawPartitionElement
583@DESCRIPTION: <p></p>
584*/
585void SimpleTextImager::DrawPartitionElement (PartitionMarker* pm, size_t startSubRow, size_t /*maxSubRow*/, Tablet* tablet, OffscreenTablet* offscreenTablet,
586 bool printing, const Led_Rect& subsetToDraw, Led_Rect* remainingDrawArea, size_t* rowsDrawn)
587{
588 RequireNotNull (pm);
589 RequireNotNull (remainingDrawArea);
590 RequireNotNull (rowsDrawn);
591
592 size_t start = pm->GetStart ();
593 size_t end = pm->GetEnd ();
594
595 Assert (end <= GetLength () + 1);
596 if (end == GetLength () + 1) {
597 --end; // don't include bogus char at end of buffer
598 }
599
600 Tablet* savedTablet = tablet;
601 size_t endSubRow = 0;
602 *rowsDrawn = 0;
603
604 for (size_t subRow = startSubRow; subRow <= endSubRow; ++subRow) {
605 Led_Rect currentRowRect = *remainingDrawArea;
606 currentRowRect.bottom = currentRowRect.top + GetRowHeight ();
607 DistanceType interlineSpace = GetInterLineSpace ();
608 if ((currentRowRect.bottom + CoordinateType (interlineSpace) > subsetToDraw.top) and (currentRowRect.top < subsetToDraw.bottom)) {
609
610 /*
611 * patch start/end/len to take into account rows...
612 */
613 size_t rowStart = start;
614 size_t rowEnd = end;
615
616 // rowEnd is just BEFORE the terminating NL - if any...
617 {
618 Assert (rowEnd < GetEnd () + 1);
619 size_t prevToEnd = FindPreviousCharacter (rowEnd);
620 if (prevToEnd >= rowStart and prevToEnd < GetEnd ()) {
621 Led_tChar lastChar;
622 CopyOut (prevToEnd, 1, &lastChar);
623 if (RemoveMappedDisplayCharacters (&lastChar, 1) == 0) {
624 rowEnd = prevToEnd;
625 }
626 }
627 }
628
629 TextLayoutBlock_Copy rowText = GetTextLayoutBlock (rowStart, rowEnd);
630
631 if (offscreenTablet != NULL) {
632 tablet = offscreenTablet->PrepareRect (currentRowRect, interlineSpace);
633 }
634
635 {
636 Led_Rect invalidRowRect = currentRowRect;
637 invalidRowRect.left = subsetToDraw.left;
638 invalidRowRect.right = subsetToDraw.right;
639 // PERHAPS should also intersect the 'subsetToDraw' top/bottom with this invalidRowRect to get better logical clipping?
640 // Maybe important for tables!!!
641 // LGP 2002-09-22
642 // DrawRow (RowReference (pm), tablet, currentRowRect, invalidRowRect, rowText, rowStart, rowEnd, printing);
643 DrawRow (tablet, currentRowRect, invalidRowRect, rowText, rowStart, rowEnd, printing);
644 }
645
646 /*
647 * Now erase/draw any interline space.
648 */
649 if (interlineSpace != 0) {
650 size_t hilightStart = GetSelectionStart ();
651 size_t hilightEnd = GetSelectionEnd ();
652 bool segmentHilightedAtEnd = GetSelectionShown () and (hilightStart < rowEnd) and (rowEnd <= hilightEnd);
653 if (pm->GetNext () == NULL) {
654 segmentHilightedAtEnd = false; // last row always contains no NL - so no invert off to the right...
655 }
656 DrawInterLineSpace (interlineSpace, tablet, currentRowRect.bottom, segmentHilightedAtEnd, printing);
657 }
658
659 if (offscreenTablet != NULL) {
660 /*
661 * Blast offscreen bitmap onto the screen.
662 */
663 offscreenTablet->BlastBitmapToOrigTablet ();
664 tablet = savedTablet; // don't use offscreen tablet past here... Draw to screen directly!!!
665 }
666 }
667
668 remainingDrawArea->top = currentRowRect.bottom + interlineSpace;
669 ++(*rowsDrawn);
670 }
671}
672
673DistanceType SimpleTextImager::ComputeMaxHScrollPos () const
674{
675 DistanceType maxHWidth = 0;
676 {
677 /*
678 * Figure the largest amount we might need to scroll given the current windows contents.
679 * But take into account where we've scrolled so far, and never invalidate that
680 * scroll amount. Always leave at least as much layout-width as needed to
681 * preserve the current scroll-to position.
682 */
683 DistanceType width = CalculateLongestRowInWindowPixelWidth ();
684 if (GetHScrollPos () != 0) {
685 width = max (width, GetHScrollPos () + GetWindowRect ().GetWidth ());
686 }
687 maxHWidth = max<DistanceType> (width, 1);
688 }
689 DistanceType wWidth = GetWindowRect ().GetWidth ();
690 if (maxHWidth > wWidth) {
691 return maxHWidth - wWidth;
692 }
693 else {
694 return 0;
695 }
696}
697
698Led_Rect SimpleTextImager::GetCharLocation (size_t afterPosition) const
699{
700 return (GetCharLocationRowRelative (afterPosition, RowReference (GetFirstPartitionMarker ())));
701}
702
703Led_Rect SimpleTextImager::GetCharWindowLocation (size_t afterPosition) const
704{
705 Led_Point windowOrigin = GetWindowRect ().GetOrigin () - Led_Point (0, GetHScrollPos ());
706 return windowOrigin + GetCharLocationRowRelative (afterPosition, GetTopRowReferenceInWindow (), GetTotalRowsInWindow_ ());
707}
708
709size_t SimpleTextImager::GetCharAtLocation (const Led_Point& where) const
710{
711 return GetCharAtLocationRowRelative (where, RowReference (GetFirstPartitionMarker ()));
712}
713
714size_t SimpleTextImager::GetCharAtWindowLocation (const Led_Point& where) const
715{
716 Led_Point windowOrigin = GetWindowRect ().GetOrigin () - Led_Point (0, GetHScrollPos ());
717 return GetCharAtLocationRowRelative (where - windowOrigin, GetTopRowReferenceInWindow (), GetTotalRowsInWindow_ ());
718}
719
720size_t SimpleTextImager::GetStartOfRow (size_t rowNumber) const
721{
722 // NB: Use of routines using rowNumbers force word-wrap, and so can be quite slow.
723 // Routines using RowReferences often perform MUCH better
724 return (GetStartOfRow (GetIthRowReference (rowNumber)));
725}
726
727size_t SimpleTextImager::GetStartOfRowContainingPosition (size_t charPosition) const
728{
729 return GetStartOfRow (GetRowReferenceContainingPosition (charPosition));
730}
731
732size_t SimpleTextImager::GetEndOfRow (size_t rowNumber) const
733{
734 // NB: Use of routines using rowNumbers force word-wrap, and so can be quite slow.
735 // Routines using RowReferences often perform MUCH better
736 return GetEndOfRow (GetIthRowReference (rowNumber));
737}
738
739size_t SimpleTextImager::GetEndOfRowContainingPosition (size_t charPosition) const
740{
741 return GetEndOfRow (GetRowReferenceContainingPosition (charPosition));
742}
743
744size_t SimpleTextImager::GetRealEndOfRow (size_t rowNumber) const
745{
746 // NB: Use of routines using rowNumbers force word-wrap, and so can be quite slow.
747 // Routines using RowReferences often perform MUCH better
748 return GetRealEndOfRow (GetIthRowReference (rowNumber));
749}
750
751size_t SimpleTextImager::GetRealEndOfRowContainingPosition (size_t charPosition) const
752{
753 return GetRealEndOfRow (GetRowReferenceContainingPosition (charPosition));
754}
755
756size_t SimpleTextImager::GetStartOfRow (RowReference row) const
757{
758 PartitionMarker* cur = row.GetPartitionMarker ();
759 AssertNotNull (cur);
760 return cur->GetStart ();
761}
762
763size_t SimpleTextImager::GetEndOfRow (RowReference row) const
764{
765 size_t markerEnd = GetRealEndOfRow (row);
766 // Be careful about NL at end. If we end with an NL, then don't count that.
767 // And for the last PM - it contains a bogus empty character. Don't count
768 // that either.
769 Assert (markerEnd <= GetLength () + 1);
770 if (markerEnd == GetLength () + 1) {
771 return (GetLength ());
772 }
773 size_t prevToEnd = FindPreviousCharacter (markerEnd);
774 if (prevToEnd >= GetStartOfRow (row)) {
775 Led_tChar lastChar;
776 CopyOut (prevToEnd, 1, &lastChar);
777 if (RemoveMappedDisplayCharacters (&lastChar, 1) == 0) {
778 return prevToEnd;
779 }
780 }
781 return markerEnd;
782}
783
784size_t SimpleTextImager::GetRealEndOfRow (RowReference row) const
785{
786 PartitionMarker* cur = row.GetPartitionMarker ();
787 AssertNotNull (cur);
788 Assert (cur->GetEnd () > 0);
789 size_t markerEnd = cur->GetEnd ();
790 Assert (markerEnd <= GetLength () + 1);
791 return markerEnd;
792}
793
794SimpleTextImager::RowReference SimpleTextImager::GetRowReferenceContainingPosition (size_t charPosition) const
795{
796 Require (charPosition >= 0);
797 Require (charPosition <= GetEnd ());
798 PartitionMarker* pm = GetPartitionMarkerContainingPosition (charPosition);
799 AssertNotNull (pm);
800 return (RowReference{pm});
801}
802
803size_t SimpleTextImager::GetRowContainingPosition (size_t charPosition) const
804{
805 return GetRowNumber (GetRowReferenceContainingPosition (charPosition));
806}
807
808size_t SimpleTextImager::GetRowCount () const
809{
810 // NB: This is an expensive routine because it forces a word-wrap on all the text!
811 size_t rowCount = 0;
812 for (PartitionMarker* cur = GetFirstPartitionMarker (); cur != NULL; cur = cur->GetNext ()) {
813 [[maybe_unused]] PartitionMarker* pm = cur;
814 AssertNotNull (pm);
815 AssertMember (pm, PartitionMarker);
816 rowCount += 1;
817 }
818 return (rowCount);
819}
820
821Led_Rect SimpleTextImager::GetCharLocationRowRelativeByPosition (size_t afterPosition, size_t positionOfTopRow, size_t maxRowsToCheck) const
822{
823 return GetCharLocationRowRelative (afterPosition, GetRowReferenceContainingPosition (positionOfTopRow), maxRowsToCheck);
824}
825
826DistanceType SimpleTextImager::GetRowHeight (size_t /*rowNumber*/) const
827{
828 return (GetRowHeight ());
829}
830
831/*
832@METHOD: SimpleTextImager::GetRowRelativeBaselineOfRowContainingPosition
833@DESCRIPTION: <p>Override/implement @'TextImager::GetRowRelativeBaselineOfRowContainingPosition'.</p>
834*/
835DistanceType SimpleTextImager::GetRowRelativeBaselineOfRowContainingPosition (size_t charPosition) const
836{
837 RowReference thisRow = GetRowReferenceContainingPosition (charPosition);
838 size_t startOfRow = GetStartOfRow (thisRow);
839 size_t endOfRow = GetEndOfRow (thisRow);
840 return MeasureSegmentBaseLine (startOfRow, endOfRow);
841}
842
843void SimpleTextImager::GetStableTypingRegionContaingMarkerRange (size_t fromMarkerPos, size_t toMarkerPos, size_t* expandedFromMarkerPos,
844 size_t* expandedToMarkerPos) const
845{
846 AssertNotNull (expandedFromMarkerPos);
847 AssertNotNull (expandedToMarkerPos);
848 Assert (fromMarkerPos >= 0);
849 Assert (fromMarkerPos <= toMarkerPos);
850 Assert (toMarkerPos <= GetEnd ());
851
852 size_t curTopRowRelativeRowNumber = 0;
853
854 RowReference curRow = GetTopRowReferenceInWindow ();
855 do {
856 PartitionMarker* cur = curRow.GetPartitionMarker ();
857 AssertNotNull (cur);
858 size_t start = cur->GetStart ();
859 size_t end = cur->GetEnd ();
860
861 // For the last partition marker - we are including a BOGUS character past the end of the buffer.
862 // We don't want to return that. But otherwise - it is OK to return the NL at the end of the
863 // other lines (though perhaps that is unnecceary).... LGP 950210
864 if (cur->GetNext () == NULL) {
865 --end;
866 }
867
868 //size_t len = end - start;
869
870 // If we are strictly before the first row, we won't appear later...
871 if (curTopRowRelativeRowNumber == 0 and (fromMarkerPos < start)) {
872 break;
873 }
874
875 ++curTopRowRelativeRowNumber;
876
877 if (Contains (*cur, fromMarkerPos) and Contains (*cur, toMarkerPos)) {
878 (*expandedFromMarkerPos) = start;
879 (*expandedToMarkerPos) = end;
880 Assert ((*expandedFromMarkerPos) >= 0);
881 Assert ((*expandedFromMarkerPos) <= (*expandedToMarkerPos));
882 Assert ((*expandedToMarkerPos) <= GetEnd ());
883 return;
884 }
885
886 if (curTopRowRelativeRowNumber >= GetTotalRowsInWindow_ ()) {
887 break; // though this might allow is to go too far - no matter. We'd return
888 // the same result anyhow. And the extra overhead in counter rows
889 // as opposed to lines doesn't offset the overhead in counting a few
890 // extra lines - besides - this is simpler...
891 }
892 } while (GetNextRowReference (&curRow));
893
894 (*expandedFromMarkerPos) = 0;
895 (*expandedToMarkerPos) = GetEnd ();
896}
897
898DistanceType SimpleTextImager::GetHeightOfRows (size_t startingRow, size_t rowCount) const
899{
900 return (GetHeightOfRows (GetIthRowReference (startingRow), rowCount));
901}
902
903DistanceType SimpleTextImager::GetHeightOfRows (RowReference startingRow, size_t rowCount) const
904{
905 DistanceType height = 0;
906 for (RowReference curRow = startingRow; rowCount > 0; --rowCount) {
907 PartitionMarker* curPM = curRow.GetPartitionMarker ();
908 height += GetRowHeight ();
909 height += GetInterLineSpace (curPM);
910 (void)GetNextRowReference (&curRow);
911 }
912 return height;
913}
914
915/*
916@METHOD: SimpleTextImager::SetInterLineSpace
917@DESCRIPTION: <p>Set the interline space associated for the entire text buffer. Call the
918 no-arg version of @'SimpleTextImager::GetInterLineSpace' to get the currently set value.</p>
919*/
920void SimpleTextImager::SetInterLineSpace (DistanceType interlineSpace)
921{
922 fInterlineSpace = interlineSpace;
923}
924
925/*
926@METHOD: SimpleTextImager::GetInterLineSpace
927@DESCRIPTION: <p>Get the interline space associated with a particular partition element. This method
928 is virtual so that subclassers can provide a different interline space for each partition element.
929 But the default behavior is to simply use the global - buffer value - specified by
930 @'SimpleTextImager::SetInterLineSpace' and returned by the no-arg version of
931 @'SimpleTextImager::GetInterLineSpace'.</p>
932*/
933DistanceType SimpleTextImager::GetInterLineSpace (PartitionMarker* /*pm*/) const
934{
935 return GetInterLineSpace ();
936}
937
938/*
939@METHOD: SimpleTextImager::ChangedInterLineSpace
940@DESCRIPTION: <p>If you override the virtual, one-arg version of @'SimpleTextImager::GetInterLineSpace', you must
941 call this method whenever the interline space value changes, for a given PM. This method will clear any caches
942 associated with that PM. And refresh the screen, as needed (in some subclass).</p>
943 <p>See the one-arg @'SimpleTextImager::GetInterLineSpace' for more details.</p>
944*/
945void SimpleTextImager::ChangedInterLineSpace (PartitionMarker* /*pm*/)
946{
947 // for now - we do nothing. Not sure there are any caches - at this point.
948}
949
950void SimpleTextImager::SetDefaultFont (const IncrementalFontSpecification& defaultFont)
951{
952 // TextImager::SetDefaultFont_ (defaultFont);
953 inherited::SetDefaultFont (defaultFont);
954 try {
955 TabletChangedMetrics ();
956 }
957 catch (NotFullyInitialized&) {
958 // ignore this - no harm...
959 }
960 catch (...) {
961 throw;
962 }
963}
964
965void SimpleTextImager::DidUpdateText (const UpdateInfo& updateInfo) noexcept
966{
967 InvalidateTotalRowsInWindow ();
968 inherited::DidUpdateText (updateInfo);
969 AssertNotNull (fTopLinePartitionMarkerInWindow);
970 AssureWholeWindowUsedIfNeeded ();
971 InvalidateScrollBarParameters (); // even if we don't change the top row, we might change enuf about the text to change sbar
972}
973
974void SimpleTextImager::SetWindowRect (const Led_Rect& windowRect)
975{
976 bool heightChanged = GetWindowRect ().GetHeight () != windowRect.GetHeight ();
977 inherited::SetWindowRect (windowRect);
978 if (heightChanged and PeekAtTextStore () != NULL) {
979 InvalidateTotalRowsInWindow ();
980 AssureWholeWindowUsedIfNeeded ();
981 InvalidateScrollBarParameters ();
982 }
983}
984
985void SimpleTextImager::InvalidateAllCaches ()
986{
987 inherited::InvalidateAllCaches ();
988 InvalidateRowHeight ();
989 if (GetPartition ().get () != nullptr) {
990 // Invalidate totalRows at END of this procedure - after invalidating scrollbar params and assurewholewindowusedifneeded () - cuz
991 // InvalidateScrollBarParameters/AssureWholeWindowUsedIfNeeded can revalidate some cache values...
992 InvalidateScrollBarParameters ();
993 AssureWholeWindowUsedIfNeeded ();
994 InvalidateTotalRowsInWindow ();
995 }
996}
997
998SimpleTextImager::RowReference SimpleTextImager::AdjustPotentialTopRowReferenceSoWholeWindowUsed (const RowReference& potentialTopRow)
999{
1000 /*
1001 * This check is always safe, but probably not a worthwhile optimization, except that it avoids
1002 * some problems about initializing the top-row-reference before we've got a valid
1003 * tablet setup to use.
1004 */
1005 if (potentialTopRow.GetPartitionMarker ()->GetPrevious () == NULL) {
1006 return potentialTopRow;
1007 }
1008
1009 CoordinateType windowHeight = GetWindowRect ().GetHeight ();
1010 CoordinateType heightUsed = 0;
1011
1012 for (RowReference curRow = potentialTopRow;;) {
1013 PartitionMarker* curPM = curRow.GetPartitionMarker ();
1014 heightUsed += GetRowHeight ();
1015 heightUsed += GetInterLineSpace (curPM);
1016 if (heightUsed >= windowHeight) {
1017 return (potentialTopRow); // Then we used all the space we could have - and that is a good row!
1018 }
1019 if (not GetNextRowReference (&curRow)) {
1020 break;
1021 }
1022 }
1023
1024 // If we got here - we ran out of rows before we ran out of height.
1025 // That means we should scroll back a smidge...
1026 for (RowReference curRow = potentialTopRow;;) {
1027 if (not GetPreviousRowReference (&curRow)) {
1028 return (curRow); // if we've gone back as far as we can - were done!
1029 // Even if we didn't use all the height
1030 }
1031
1032 PartitionMarker* curPM = curRow.GetPartitionMarker ();
1033 heightUsed += GetRowHeight ();
1034 heightUsed += GetInterLineSpace (curPM);
1035 if (heightUsed > windowHeight) {
1036 // We went back one too far - forward one and return that.
1037 [[maybe_unused]] bool result = GetNextRowReference (&curRow);
1038 Assert (result);
1039 return curRow;
1040 }
1041 else if (heightUsed == windowHeight) {
1042 return (curRow); // Then we used all the space we could have - and that is a good row!
1043 }
1044 }
1045 Assert (false);
1046 return (potentialTopRow); // NotReached / silence compiler warnings
1047}
1048
1049bool SimpleTextImager::PositionWouldFitInWindowWithThisTopRow (size_t markerPos, const RowReference& newTopRow)
1050{
1051 if (markerPos < GetStartOfRow (newTopRow)) {
1052 return false;
1053 }
1054
1055 size_t rowCount = ComputeRowsThatWouldFitInWindowWithTopRow (newTopRow);
1056 RowReference lastRow = GetIthRowReferenceFromHere (newTopRow, rowCount - 1);
1057
1058 return (markerPos < GetRealEndOfRow (lastRow));
1059}
1060
1061size_t SimpleTextImager::ComputeRowsThatWouldFitInWindowWithTopRow (const RowReference& newTopRow) const
1062{
1063 /*
1064 * For now, we don't show partial rows at the bottom. We
1065 * might want to reconsider this.
1066 */
1067 CoordinateType windowHeight = GetWindowRect ().GetHeight ();
1068
1069 /*
1070 * Wind out way to the bottom of the window from our current position,
1071 * and count rows.
1072 */
1073 size_t rowCount = 0;
1074 CoordinateType heightUsed = 0;
1075 for (RowReference curRow = newTopRow;;) {
1076 ++rowCount;
1077 PartitionMarker* curPM = curRow.GetPartitionMarker ();
1078 heightUsed += GetRowHeight ();
1079 heightUsed += GetInterLineSpace (curPM);
1080 if (heightUsed > windowHeight) {
1081 // we went one too far
1082 --rowCount;
1083 break;
1084 }
1085 else if (heightUsed == windowHeight) {
1086 break; // thats all that will fit
1087 }
1088
1089 if (not GetNextRowReference (&curRow)) {
1090 break;
1091 }
1092 }
1093 if (rowCount == 0) { // always for the existence of at least one row...
1094 rowCount = 1;
1095 }
1096
1097 return rowCount;
1098}
1099
1100Led_Rect SimpleTextImager::GetCharLocationRowRelative (size_t afterPosition, RowReference topRow, size_t maxRowsToCheck) const
1101{
1102 // MUST FIGURE OUT WHAT TODO HERE BETTER - 10000 not good enough answer always...
1103 const Led_Rect kMagicBeforeRect = Led_Rect (-10000, 0, 0, 0);
1104 const Led_Rect kMagicAfterRect = Led_Rect (10000, 0, 0, 0);
1105
1106 Assert (afterPosition >= 0);
1107 Assert (afterPosition <= GetEnd ()); // does that ever make sense???
1108
1109 if (afterPosition < GetStartOfRow (topRow)) {
1110 return (kMagicBeforeRect);
1111 }
1112
1113 RowReference curRow = topRow;
1114 size_t curTopRowRelativeRowNumber = 0;
1115 CoordinateType topVPos = 0;
1116 do {
1117 PartitionMarker* cur = curRow.GetPartitionMarker ();
1118 AssertNotNull (cur);
1119 size_t start = cur->GetStart ();
1120 size_t end = cur->GetEnd (); // end points JUST PAST LAST VISIBLE/OPERATED ON CHAR
1121
1122 Assert (end <= GetEnd () + 1);
1123
1124 ++curTopRowRelativeRowNumber;
1125
1126 if (afterPosition >= start and afterPosition < end) {
1127 Assert (start <= afterPosition);
1128 DistanceType hStart = 0;
1129 DistanceType hEnd = 0;
1130 GetRowRelativeCharLoc (afterPosition, &hStart, &hEnd);
1131 Assert (hStart <= hEnd);
1132 return (Led_Rect (topVPos, hStart, GetRowHeight (), hEnd - hStart));
1133 }
1134
1135 topVPos += GetRowHeight ();
1136 topVPos += GetInterLineSpace (cur);
1137
1138 if (curTopRowRelativeRowNumber >= maxRowsToCheck) {
1139 break; // return bogus place at the end...
1140 }
1141 } while (GetNextRowReference (&curRow));
1142
1143 return (kMagicAfterRect);
1144}
1145
1146size_t SimpleTextImager::GetCharAtLocationRowRelative (const Led_Point& where, RowReference topRow, size_t maxRowsToCheck) const
1147{
1148 /*
1149 * Not 100% sure how to deal with points outside our range. For now - we just
1150 * return topMost/bottomMost marker positions. That seems to work decently - at least
1151 * for now... But I worry if it is the right thing when we do
1152 * autoscrolling...
1153 */
1154 if (where.v < 0) {
1155 return 0;
1156 }
1157
1158 RowReference curRow = topRow;
1159 size_t curTopRowRelativeRowNumber = 0;
1160 CoordinateType topVPos = 0;
1161 do {
1162 PartitionMarker* cur = curRow.GetPartitionMarker ();
1163
1164 AssertNotNull (cur);
1165 size_t start = cur->GetStart ();
1166
1167 /*
1168 * Count the interline space as part of the last row of the line for the purpose of hit-testing.
1169 */
1170 DistanceType interLineSpace = GetInterLineSpace (cur);
1171
1172 ++curTopRowRelativeRowNumber;
1173 if (where.v >= topVPos and where.v < topVPos + CoordinateType (GetRowHeight () + interLineSpace)) {
1174 return GetRowRelativeCharAtLoc (where.h, start);
1175 }
1176
1177 if (curTopRowRelativeRowNumber >= maxRowsToCheck) {
1178 break; // we've checked enuf...
1179 }
1180
1181 topVPos += GetRowHeight () + interLineSpace;
1182 } while (GetNextRowReference (&curRow));
1183
1184 return GetEnd ();
1185}
1186
1187/*
1188@METHOD: SimpleTextImager::ContainsMappedDisplayCharacters
1189@DESCRIPTION: <p>Override @'TextImager::ContainsMappedDisplayCharacters' to hide '\n' characters.
1190 See @'qStroika_Frameworks_Led_DefaultSoftLineBreakChar'.</p>
1191*/
1192bool SimpleTextImager::ContainsMappedDisplayCharacters (const Led_tChar* text, size_t nTChars) const
1193{
1194 return ContainsMappedDisplayCharacters_HelperForChar (text, nTChars, '\n') or inherited::ContainsMappedDisplayCharacters (text, nTChars);
1195}
1196
1197/*
1198@METHOD: SimpleTextImager::RemoveMappedDisplayCharacters
1199@DESCRIPTION: <p>Override @'TextImager::RemoveMappedDisplayCharacters' to hide '\n' characters.</p>
1200*/
1201size_t SimpleTextImager::RemoveMappedDisplayCharacters (Led_tChar* copyText, size_t nTChars) const
1202{
1203 size_t newLen = inherited::RemoveMappedDisplayCharacters (copyText, nTChars);
1204 Assert (newLen <= nTChars);
1205 size_t newerLen = RemoveMappedDisplayCharacters_HelperForChar (copyText, newLen, '\n');
1206 Assert (newerLen <= newLen);
1207 Assert (newerLen <= nTChars);
1208 return newerLen;
1209}
1210#endif
#define AssertNotNull(p)
Definition Assertions.h:336
#define RequireNotNull(p)
Definition Assertions.h:350
#define AssertMember(p, c)
Definition Assertions.h:315