Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
SimpleTextImager.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#include "Stroika/Frameworks/StroikaPreComp.h"
5
6#include <limits.h>
7
8#include "LineBasedPartition.h"
9#include "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_Foundation_Common_Platform_MacOS
501 tablet->SetPort ();
502 RGBColor oldForeColor = GDI_GetForeColor ();
503 RGBColor oldBackColor = GDI_GetBackColor ();
504#elif qStroika_Foundation_Common_Platform_Windows
505 GDI_Obj_Selector pen (tablet, ::GetStockObject (NULL_PEN));
506 GDI_Obj_Selector brush (tablet, ::GetStockObject (NULL_BRUSH));
507#endif
508
509 /*
510 * Do this AFTER the save of colors above cuz no need in preserving that crap for
511 * offscreen bitmap we cons up here on the fly.
512 */
513 OffscreenTablet thisIsOurNewOST;
514 if (GetImageUsingOffscreenBitmaps () and not printing) {
515 thisIsOurNewOST.Setup (tablet_);
516 }
517
518 try {
519 size_t totalRowsInWindow = GetTotalRowsInWindow_ ();
520 RowReference topRowInWindow = GetTopRowReferenceInWindow ();
521 size_t rowsLeftInWindow = totalRowsInWindow;
522 for (PartitionMarker* pm = topRowInWindow.GetPartitionMarker (); rowsLeftInWindow != 0; pm = pm->GetNext ()) {
523 Assert (pm != NULL);
524 size_t startSubRow = 0;
525 size_t maxSubRow = 0;
526 maxSubRow = rowsLeftInWindow - 1 + startSubRow;
527 size_t rowsDrawn = 0;
528 DrawPartitionElement (pm, startSubRow, maxSubRow, tablet, (GetImageUsingOffscreenBitmaps () and not printing) ? &thisIsOurNewOST : NULL,
529 printing, subsetToDraw, &rowsLeftToDrawRect, &rowsDrawn);
530 Assert (rowsLeftInWindow >= rowsDrawn);
531 rowsLeftInWindow -= rowsDrawn;
532 }
533
534 /*
535 * Now erase to the end of the page.
536 */
537 Assert (tablet == tablet_); // Draw to screen directly past here...
538 {
539 Led_Rect eraser = GetWindowRect ();
540 eraser.top = rowsLeftToDrawRect.top; // only from here down...
541 eraser.bottom = subsetToDraw.bottom; // cuz image rect may not cover what it used to, and never any need to
542
543 if (eraser.top > eraser.bottom) {
544 eraser.bottom = eraser.top;
545 }
546
547 // SEE IF WE CAN TIGHTEN THIS TEST A BIT MORE, SO WHEN NO PIXELS WILL BE DRAWN, WE DONT BOTHER
548 // IN OTHER WORDS, CHANGE A COUPLE <= to < - LGP 970315
549
550 // QUICKIE INTERSECT TEST
551 if (((eraser.top >= subsetToDraw.top and eraser.top <= subsetToDraw.bottom) or
552 (eraser.bottom >= subsetToDraw.top and eraser.bottom <= subsetToDraw.bottom)) and
553 (eraser.GetHeight () > 0 and eraser.GetWidth () > 0)) {
554
555 if (GetImageUsingOffscreenBitmaps () and not printing) {
556 tablet = thisIsOurNewOST.PrepareRect (eraser);
557 }
558 EraseBackground (tablet, eraser, printing);
559#if 0
560 // Do we want to hilight the section after the end of the last row displayed if the selection continues onto
561 // the next window? Somehow, I think it ends up looking schlocky. Leave off for now...
562 size_t hilightStart = GetSelectionStart ();
563 size_t hilightEnd = GetSelectionEnd ();
564 size_t end = GetMarkerPositionOfEndOfWindow ();
565 bool segmentHilightedAtEnd = GetSelectionShown () and (hilightStart < end) and (end <= hilightEnd);
566 if (not printing and segmentHilightedAtEnd) {
567 HilightARectangle (tablet, eraser);
568 }
569#endif
570 if (GetImageUsingOffscreenBitmaps () and not printing) {
571 /*
572 * Blast offscreen bitmap onto the screen.
573 */
574 thisIsOurNewOST.BlastBitmapToOrigTablet ();
575 tablet = tablet_; // don't use offscreen tablet past here... Draw to screen directly!!!
576 }
577 }
578 }
579 }
580 catch (...) {
581#if qStroika_Foundation_Common_Platform_MacOS
582 Assert (*tablet == Led_GetCurrentGDIPort ());
583 GDI_RGBForeColor (oldForeColor);
584 GDI_RGBBackColor (oldBackColor);
585#endif
586 throw;
587 }
588#if qStroika_Foundation_Common_Platform_MacOS
589 Assert (*tablet == Led_GetCurrentGDIPort ());
590 GDI_RGBForeColor (oldForeColor);
591 GDI_RGBBackColor (oldBackColor);
592#endif
593}
594
595/*
596@METHOD: SimpleTextImager::DrawPartitionElement
597@DESCRIPTION: <p></p>
598*/
599void SimpleTextImager::DrawPartitionElement (PartitionMarker* pm, size_t startSubRow, size_t /*maxSubRow*/, Tablet* tablet, OffscreenTablet* offscreenTablet,
600 bool printing, const Led_Rect& subsetToDraw, Led_Rect* remainingDrawArea, size_t* rowsDrawn)
601{
602 RequireNotNull (pm);
603 RequireNotNull (remainingDrawArea);
604 RequireNotNull (rowsDrawn);
605
606 size_t start = pm->GetStart ();
607 size_t end = pm->GetEnd ();
608
609 Assert (end <= GetLength () + 1);
610 if (end == GetLength () + 1) {
611 --end; // don't include bogus char at end of buffer
612 }
613
614 Tablet* savedTablet = tablet;
615 size_t endSubRow = 0;
616 *rowsDrawn = 0;
617
618 for (size_t subRow = startSubRow; subRow <= endSubRow; ++subRow) {
619 Led_Rect currentRowRect = *remainingDrawArea;
620 currentRowRect.bottom = currentRowRect.top + GetRowHeight ();
621 DistanceType interlineSpace = GetInterLineSpace ();
622 if ((currentRowRect.bottom + CoordinateType (interlineSpace) > subsetToDraw.top) and (currentRowRect.top < subsetToDraw.bottom)) {
623
624 /*
625 * patch start/end/len to take into account rows...
626 */
627 size_t rowStart = start;
628 size_t rowEnd = end;
629
630 // rowEnd is just BEFORE the terminating NL - if any...
631 {
632 Assert (rowEnd < GetEnd () + 1);
633 size_t prevToEnd = FindPreviousCharacter (rowEnd);
634 if (prevToEnd >= rowStart and prevToEnd < GetEnd ()) {
635 Led_tChar lastChar;
636 CopyOut (prevToEnd, 1, &lastChar);
637 if (RemoveMappedDisplayCharacters (&lastChar, 1) == 0) {
638 rowEnd = prevToEnd;
639 }
640 }
641 }
642
643 TextLayoutBlock_Copy rowText = GetTextLayoutBlock (rowStart, rowEnd);
644
645 if (offscreenTablet != NULL) {
646 tablet = offscreenTablet->PrepareRect (currentRowRect, interlineSpace);
647 }
648
649 {
650 Led_Rect invalidRowRect = currentRowRect;
651 invalidRowRect.left = subsetToDraw.left;
652 invalidRowRect.right = subsetToDraw.right;
653 // PERHAPS should also intersect the 'subsetToDraw' top/bottom with this invalidRowRect to get better logical clipping?
654 // Maybe important for tables!!!
655 // LGP 2002-09-22
656 // DrawRow (RowReference (pm), tablet, currentRowRect, invalidRowRect, rowText, rowStart, rowEnd, printing);
657 DrawRow (tablet, currentRowRect, invalidRowRect, rowText, rowStart, rowEnd, printing);
658 }
659
660 /*
661 * Now erase/draw any interline space.
662 */
663 if (interlineSpace != 0) {
664 size_t hilightStart = GetSelectionStart ();
665 size_t hilightEnd = GetSelectionEnd ();
666 bool segmentHilightedAtEnd = GetSelectionShown () and (hilightStart < rowEnd) and (rowEnd <= hilightEnd);
667 if (pm->GetNext () == NULL) {
668 segmentHilightedAtEnd = false; // last row always contains no NL - so no invert off to the right...
669 }
670 DrawInterLineSpace (interlineSpace, tablet, currentRowRect.bottom, segmentHilightedAtEnd, printing);
671 }
672
673 if (offscreenTablet != NULL) {
674 /*
675 * Blast offscreen bitmap onto the screen.
676 */
677 offscreenTablet->BlastBitmapToOrigTablet ();
678 tablet = savedTablet; // don't use offscreen tablet past here... Draw to screen directly!!!
679 }
680 }
681
682 remainingDrawArea->top = currentRowRect.bottom + interlineSpace;
683 ++(*rowsDrawn);
684 }
685}
686
687DistanceType SimpleTextImager::ComputeMaxHScrollPos () const
688{
689 DistanceType maxHWidth = 0;
690 {
691 /*
692 * Figure the largest amount we might need to scroll given the current windows contents.
693 * But take into account where we've scrolled so far, and never invalidate that
694 * scroll amount. Always leave at least as much layout-width as needed to
695 * preserve the current scroll-to position.
696 */
697 DistanceType width = CalculateLongestRowInWindowPixelWidth ();
698 if (GetHScrollPos () != 0) {
699 width = max (width, GetHScrollPos () + GetWindowRect ().GetWidth ());
700 }
701 maxHWidth = max<DistanceType> (width, 1);
702 }
703 DistanceType wWidth = GetWindowRect ().GetWidth ();
704 if (maxHWidth > wWidth) {
705 return maxHWidth - wWidth;
706 }
707 else {
708 return 0;
709 }
710}
711
712Led_Rect SimpleTextImager::GetCharLocation (size_t afterPosition) const
713{
714 return (GetCharLocationRowRelative (afterPosition, RowReference (GetFirstPartitionMarker ())));
715}
716
717Led_Rect SimpleTextImager::GetCharWindowLocation (size_t afterPosition) const
718{
719 Led_Point windowOrigin = GetWindowRect ().GetOrigin () - Led_Point (0, GetHScrollPos ());
720 return windowOrigin + GetCharLocationRowRelative (afterPosition, GetTopRowReferenceInWindow (), GetTotalRowsInWindow_ ());
721}
722
723size_t SimpleTextImager::GetCharAtLocation (const Led_Point& where) const
724{
725 return GetCharAtLocationRowRelative (where, RowReference (GetFirstPartitionMarker ()));
726}
727
728size_t SimpleTextImager::GetCharAtWindowLocation (const Led_Point& where) const
729{
730 Led_Point windowOrigin = GetWindowRect ().GetOrigin () - Led_Point (0, GetHScrollPos ());
731 return GetCharAtLocationRowRelative (where - windowOrigin, GetTopRowReferenceInWindow (), GetTotalRowsInWindow_ ());
732}
733
734size_t SimpleTextImager::GetStartOfRow (size_t rowNumber) const
735{
736 // NB: Use of routines using rowNumbers force word-wrap, and so can be quite slow.
737 // Routines using RowReferences often perform MUCH better
738 return (GetStartOfRow (GetIthRowReference (rowNumber)));
739}
740
741size_t SimpleTextImager::GetStartOfRowContainingPosition (size_t charPosition) const
742{
743 return GetStartOfRow (GetRowReferenceContainingPosition (charPosition));
744}
745
746size_t SimpleTextImager::GetEndOfRow (size_t rowNumber) const
747{
748 // NB: Use of routines using rowNumbers force word-wrap, and so can be quite slow.
749 // Routines using RowReferences often perform MUCH better
750 return GetEndOfRow (GetIthRowReference (rowNumber));
751}
752
753size_t SimpleTextImager::GetEndOfRowContainingPosition (size_t charPosition) const
754{
755 return GetEndOfRow (GetRowReferenceContainingPosition (charPosition));
756}
757
758size_t SimpleTextImager::GetRealEndOfRow (size_t rowNumber) const
759{
760 // NB: Use of routines using rowNumbers force word-wrap, and so can be quite slow.
761 // Routines using RowReferences often perform MUCH better
762 return GetRealEndOfRow (GetIthRowReference (rowNumber));
763}
764
765size_t SimpleTextImager::GetRealEndOfRowContainingPosition (size_t charPosition) const
766{
767 return GetRealEndOfRow (GetRowReferenceContainingPosition (charPosition));
768}
769
770size_t SimpleTextImager::GetStartOfRow (RowReference row) const
771{
772 PartitionMarker* cur = row.GetPartitionMarker ();
773 AssertNotNull (cur);
774 return cur->GetStart ();
775}
776
777size_t SimpleTextImager::GetEndOfRow (RowReference row) const
778{
779 size_t markerEnd = GetRealEndOfRow (row);
780 // Be careful about NL at end. If we end with an NL, then don't count that.
781 // And for the last PM - it contains a bogus empty character. Don't count
782 // that either.
783 Assert (markerEnd <= GetLength () + 1);
784 if (markerEnd == GetLength () + 1) {
785 return (GetLength ());
786 }
787 size_t prevToEnd = FindPreviousCharacter (markerEnd);
788 if (prevToEnd >= GetStartOfRow (row)) {
789 Led_tChar lastChar;
790 CopyOut (prevToEnd, 1, &lastChar);
791 if (RemoveMappedDisplayCharacters (&lastChar, 1) == 0) {
792 return prevToEnd;
793 }
794 }
795 return markerEnd;
796}
797
798size_t SimpleTextImager::GetRealEndOfRow (RowReference row) const
799{
800 PartitionMarker* cur = row.GetPartitionMarker ();
801 AssertNotNull (cur);
802 Assert (cur->GetEnd () > 0);
803 size_t markerEnd = cur->GetEnd ();
804 Assert (markerEnd <= GetLength () + 1);
805 return markerEnd;
806}
807
808SimpleTextImager::RowReference SimpleTextImager::GetRowReferenceContainingPosition (size_t charPosition) const
809{
810 Require (charPosition >= 0);
811 Require (charPosition <= GetEnd ());
812 PartitionMarker* pm = GetPartitionMarkerContainingPosition (charPosition);
813 AssertNotNull (pm);
814 return (RowReference{pm});
815}
816
817size_t SimpleTextImager::GetRowContainingPosition (size_t charPosition) const
818{
819 return GetRowNumber (GetRowReferenceContainingPosition (charPosition));
820}
821
822size_t SimpleTextImager::GetRowCount () const
823{
824 // NB: This is an expensive routine because it forces a word-wrap on all the text!
825 size_t rowCount = 0;
826 for (PartitionMarker* cur = GetFirstPartitionMarker (); cur != NULL; cur = cur->GetNext ()) {
827 [[maybe_unused]] PartitionMarker* pm = cur;
828 AssertNotNull (pm);
829 AssertMember (pm, PartitionMarker);
830 rowCount += 1;
831 }
832 return (rowCount);
833}
834
835Led_Rect SimpleTextImager::GetCharLocationRowRelativeByPosition (size_t afterPosition, size_t positionOfTopRow, size_t maxRowsToCheck) const
836{
837 return GetCharLocationRowRelative (afterPosition, GetRowReferenceContainingPosition (positionOfTopRow), maxRowsToCheck);
838}
839
840DistanceType SimpleTextImager::GetRowHeight (size_t /*rowNumber*/) const
841{
842 return (GetRowHeight ());
843}
844
845/*
846@METHOD: SimpleTextImager::GetRowRelativeBaselineOfRowContainingPosition
847@DESCRIPTION: <p>Override/implement @'TextImager::GetRowRelativeBaselineOfRowContainingPosition'.</p>
848*/
849DistanceType SimpleTextImager::GetRowRelativeBaselineOfRowContainingPosition (size_t charPosition) const
850{
851 RowReference thisRow = GetRowReferenceContainingPosition (charPosition);
852 size_t startOfRow = GetStartOfRow (thisRow);
853 size_t endOfRow = GetEndOfRow (thisRow);
854 return MeasureSegmentBaseLine (startOfRow, endOfRow);
855}
856
857void SimpleTextImager::GetStableTypingRegionContaingMarkerRange (size_t fromMarkerPos, size_t toMarkerPos, size_t* expandedFromMarkerPos,
858 size_t* expandedToMarkerPos) const
859{
860 AssertNotNull (expandedFromMarkerPos);
861 AssertNotNull (expandedToMarkerPos);
862 Assert (fromMarkerPos >= 0);
863 Assert (fromMarkerPos <= toMarkerPos);
864 Assert (toMarkerPos <= GetEnd ());
865
866 size_t curTopRowRelativeRowNumber = 0;
867
868 RowReference curRow = GetTopRowReferenceInWindow ();
869 do {
870 PartitionMarker* cur = curRow.GetPartitionMarker ();
871 AssertNotNull (cur);
872 size_t start = cur->GetStart ();
873 size_t end = cur->GetEnd ();
874
875 // For the last partition marker - we are including a BOGUS character past the end of the buffer.
876 // We don't want to return that. But otherwise - it is OK to return the NL at the end of the
877 // other lines (though perhaps that is unnecceary).... LGP 950210
878 if (cur->GetNext () == NULL) {
879 --end;
880 }
881
882 //size_t len = end - start;
883
884 // If we are strictly before the first row, we won't appear later...
885 if (curTopRowRelativeRowNumber == 0 and (fromMarkerPos < start)) {
886 break;
887 }
888
889 ++curTopRowRelativeRowNumber;
890
891 if (Contains (*cur, fromMarkerPos) and Contains (*cur, toMarkerPos)) {
892 (*expandedFromMarkerPos) = start;
893 (*expandedToMarkerPos) = end;
894 Assert ((*expandedFromMarkerPos) >= 0);
895 Assert ((*expandedFromMarkerPos) <= (*expandedToMarkerPos));
896 Assert ((*expandedToMarkerPos) <= GetEnd ());
897 return;
898 }
899
900 if (curTopRowRelativeRowNumber >= GetTotalRowsInWindow_ ()) {
901 break; // though this might allow is to go too far - no matter. We'd return
902 // the same result anyhow. And the extra overhead in counter rows
903 // as opposed to lines doesn't offset the overhead in counting a few
904 // extra lines - besides - this is simpler...
905 }
906 } while (GetNextRowReference (&curRow));
907
908 (*expandedFromMarkerPos) = 0;
909 (*expandedToMarkerPos) = GetEnd ();
910}
911
912DistanceType SimpleTextImager::GetHeightOfRows (size_t startingRow, size_t rowCount) const
913{
914 return (GetHeightOfRows (GetIthRowReference (startingRow), rowCount));
915}
916
917DistanceType SimpleTextImager::GetHeightOfRows (RowReference startingRow, size_t rowCount) const
918{
919 DistanceType height = 0;
920 for (RowReference curRow = startingRow; rowCount > 0; --rowCount) {
921 PartitionMarker* curPM = curRow.GetPartitionMarker ();
922 height += GetRowHeight ();
923 height += GetInterLineSpace (curPM);
924 (void)GetNextRowReference (&curRow);
925 }
926 return height;
927}
928
929/*
930@METHOD: SimpleTextImager::SetInterLineSpace
931@DESCRIPTION: <p>Set the interline space associated for the entire text buffer. Call the
932 no-arg version of @'SimpleTextImager::GetInterLineSpace' to get the currently set value.</p>
933*/
934void SimpleTextImager::SetInterLineSpace (DistanceType interlineSpace)
935{
936 fInterlineSpace = interlineSpace;
937}
938
939/*
940@METHOD: SimpleTextImager::GetInterLineSpace
941@DESCRIPTION: <p>Get the interline space associated with a particular partition element. This method
942 is virtual so that subclassers can provide a different interline space for each partition element.
943 But the default behavior is to simply use the global - buffer value - specified by
944 @'SimpleTextImager::SetInterLineSpace' and returned by the no-arg version of
945 @'SimpleTextImager::GetInterLineSpace'.</p>
946*/
947DistanceType SimpleTextImager::GetInterLineSpace (PartitionMarker* /*pm*/) const
948{
949 return GetInterLineSpace ();
950}
951
952/*
953@METHOD: SimpleTextImager::ChangedInterLineSpace
954@DESCRIPTION: <p>If you override the virtual, one-arg version of @'SimpleTextImager::GetInterLineSpace', you must
955 call this method whenever the interline space value changes, for a given PM. This method will clear any caches
956 associated with that PM. And refresh the screen, as needed (in some subclass).</p>
957 <p>See the one-arg @'SimpleTextImager::GetInterLineSpace' for more details.</p>
958*/
959void SimpleTextImager::ChangedInterLineSpace (PartitionMarker* /*pm*/)
960{
961 // for now - we do nothing. Not sure there are any caches - at this point.
962}
963
964void SimpleTextImager::SetDefaultFont (const IncrementalFontSpecification& defaultFont)
965{
966 // TextImager::SetDefaultFont_ (defaultFont);
967 inherited::SetDefaultFont (defaultFont);
968 try {
969 TabletChangedMetrics ();
970 }
971 catch (NotFullyInitialized&) {
972 // ignore this - no harm...
973 }
974 catch (...) {
975 throw;
976 }
977}
978
979void SimpleTextImager::DidUpdateText (const UpdateInfo& updateInfo) noexcept
980{
981 InvalidateTotalRowsInWindow ();
982 inherited::DidUpdateText (updateInfo);
983 AssertNotNull (fTopLinePartitionMarkerInWindow);
984 AssureWholeWindowUsedIfNeeded ();
985 InvalidateScrollBarParameters (); // even if we don't change the top row, we might change enuf about the text to change sbar
986}
987
988void SimpleTextImager::SetWindowRect (const Led_Rect& windowRect)
989{
990 bool heightChanged = GetWindowRect ().GetHeight () != windowRect.GetHeight ();
991 inherited::SetWindowRect (windowRect);
992 if (heightChanged and PeekAtTextStore () != NULL) {
993 InvalidateTotalRowsInWindow ();
994 AssureWholeWindowUsedIfNeeded ();
995 InvalidateScrollBarParameters ();
996 }
997}
998
999void SimpleTextImager::InvalidateAllCaches ()
1000{
1001 inherited::InvalidateAllCaches ();
1002 InvalidateRowHeight ();
1003 if (GetPartition ().get () != nullptr) {
1004 // Invalidate totalRows at END of this procedure - after invalidating scrollbar params and assurewholewindowusedifneeded () - cuz
1005 // InvalidateScrollBarParameters/AssureWholeWindowUsedIfNeeded can revalidate some cache values...
1006 InvalidateScrollBarParameters ();
1007 AssureWholeWindowUsedIfNeeded ();
1008 InvalidateTotalRowsInWindow ();
1009 }
1010}
1011
1012SimpleTextImager::RowReference SimpleTextImager::AdjustPotentialTopRowReferenceSoWholeWindowUsed (const RowReference& potentialTopRow)
1013{
1014 /*
1015 * This check is always safe, but probably not a worthwhile optimization, except that it avoids
1016 * some problems about initializing the top-row-reference before we've got a valid
1017 * tablet setup to use.
1018 */
1019 if (potentialTopRow.GetPartitionMarker ()->GetPrevious () == NULL) {
1020 return potentialTopRow;
1021 }
1022
1023 CoordinateType windowHeight = GetWindowRect ().GetHeight ();
1024 CoordinateType heightUsed = 0;
1025
1026 for (RowReference curRow = potentialTopRow;;) {
1027 PartitionMarker* curPM = curRow.GetPartitionMarker ();
1028 heightUsed += GetRowHeight ();
1029 heightUsed += GetInterLineSpace (curPM);
1030 if (heightUsed >= windowHeight) {
1031 return (potentialTopRow); // Then we used all the space we could have - and that is a good row!
1032 }
1033 if (not GetNextRowReference (&curRow)) {
1034 break;
1035 }
1036 }
1037
1038 // If we got here - we ran out of rows before we ran out of height.
1039 // That means we should scroll back a smidge...
1040 for (RowReference curRow = potentialTopRow;;) {
1041 if (not GetPreviousRowReference (&curRow)) {
1042 return (curRow); // if we've gone back as far as we can - were done!
1043 // Even if we didn't use all the height
1044 }
1045
1046 PartitionMarker* curPM = curRow.GetPartitionMarker ();
1047 heightUsed += GetRowHeight ();
1048 heightUsed += GetInterLineSpace (curPM);
1049 if (heightUsed > windowHeight) {
1050 // We went back one too far - forward one and return that.
1051 [[maybe_unused]] bool result = GetNextRowReference (&curRow);
1052 Assert (result);
1053 return curRow;
1054 }
1055 else if (heightUsed == windowHeight) {
1056 return (curRow); // Then we used all the space we could have - and that is a good row!
1057 }
1058 }
1059 Assert (false);
1060 return (potentialTopRow); // NotReached / silence compiler warnings
1061}
1062
1063bool SimpleTextImager::PositionWouldFitInWindowWithThisTopRow (size_t markerPos, const RowReference& newTopRow)
1064{
1065 if (markerPos < GetStartOfRow (newTopRow)) {
1066 return false;
1067 }
1068
1069 size_t rowCount = ComputeRowsThatWouldFitInWindowWithTopRow (newTopRow);
1070 RowReference lastRow = GetIthRowReferenceFromHere (newTopRow, rowCount - 1);
1071
1072 return (markerPos < GetRealEndOfRow (lastRow));
1073}
1074
1075size_t SimpleTextImager::ComputeRowsThatWouldFitInWindowWithTopRow (const RowReference& newTopRow) const
1076{
1077 /*
1078 * For now, we don't show partial rows at the bottom. We
1079 * might want to reconsider this.
1080 */
1081 CoordinateType windowHeight = GetWindowRect ().GetHeight ();
1082
1083 /*
1084 * Wind out way to the bottom of the window from our current position,
1085 * and count rows.
1086 */
1087 size_t rowCount = 0;
1088 CoordinateType heightUsed = 0;
1089 for (RowReference curRow = newTopRow;;) {
1090 ++rowCount;
1091 PartitionMarker* curPM = curRow.GetPartitionMarker ();
1092 heightUsed += GetRowHeight ();
1093 heightUsed += GetInterLineSpace (curPM);
1094 if (heightUsed > windowHeight) {
1095 // we went one too far
1096 --rowCount;
1097 break;
1098 }
1099 else if (heightUsed == windowHeight) {
1100 break; // thats all that will fit
1101 }
1102
1103 if (not GetNextRowReference (&curRow)) {
1104 break;
1105 }
1106 }
1107 if (rowCount == 0) { // always for the existence of at least one row...
1108 rowCount = 1;
1109 }
1110
1111 return rowCount;
1112}
1113
1114Led_Rect SimpleTextImager::GetCharLocationRowRelative (size_t afterPosition, RowReference topRow, size_t maxRowsToCheck) const
1115{
1116 // MUST FIGURE OUT WHAT TODO HERE BETTER - 10000 not good enough answer always...
1117 const Led_Rect kMagicBeforeRect = Led_Rect (-10000, 0, 0, 0);
1118 const Led_Rect kMagicAfterRect = Led_Rect (10000, 0, 0, 0);
1119
1120 Assert (afterPosition >= 0);
1121 Assert (afterPosition <= GetEnd ()); // does that ever make sense???
1122
1123 if (afterPosition < GetStartOfRow (topRow)) {
1124 return (kMagicBeforeRect);
1125 }
1126
1127 RowReference curRow = topRow;
1128 size_t curTopRowRelativeRowNumber = 0;
1129 CoordinateType topVPos = 0;
1130 do {
1131 PartitionMarker* cur = curRow.GetPartitionMarker ();
1132 AssertNotNull (cur);
1133 size_t start = cur->GetStart ();
1134 size_t end = cur->GetEnd (); // end points JUST PAST LAST VISIBLE/OPERATED ON CHAR
1135
1136 Assert (end <= GetEnd () + 1);
1137
1138 ++curTopRowRelativeRowNumber;
1139
1140 if (afterPosition >= start and afterPosition < end) {
1141 Assert (start <= afterPosition);
1142 DistanceType hStart = 0;
1143 DistanceType hEnd = 0;
1144 GetRowRelativeCharLoc (afterPosition, &hStart, &hEnd);
1145 Assert (hStart <= hEnd);
1146 return (Led_Rect (topVPos, hStart, GetRowHeight (), hEnd - hStart));
1147 }
1148
1149 topVPos += GetRowHeight ();
1150 topVPos += GetInterLineSpace (cur);
1151
1152 if (curTopRowRelativeRowNumber >= maxRowsToCheck) {
1153 break; // return bogus place at the end...
1154 }
1155 } while (GetNextRowReference (&curRow));
1156
1157 return (kMagicAfterRect);
1158}
1159
1160size_t SimpleTextImager::GetCharAtLocationRowRelative (const Led_Point& where, RowReference topRow, size_t maxRowsToCheck) const
1161{
1162 /*
1163 * Not 100% sure how to deal with points outside our range. For now - we just
1164 * return topMost/bottomMost marker positions. That seems to work decently - at least
1165 * for now... But I worry if it is the right thing when we do
1166 * autoscrolling...
1167 */
1168 if (where.v < 0) {
1169 return 0;
1170 }
1171
1172 RowReference curRow = topRow;
1173 size_t curTopRowRelativeRowNumber = 0;
1174 CoordinateType topVPos = 0;
1175 do {
1176 PartitionMarker* cur = curRow.GetPartitionMarker ();
1177
1178 AssertNotNull (cur);
1179 size_t start = cur->GetStart ();
1180
1181 /*
1182 * Count the interline space as part of the last row of the line for the purpose of hit-testing.
1183 */
1184 DistanceType interLineSpace = GetInterLineSpace (cur);
1185
1186 ++curTopRowRelativeRowNumber;
1187 if (where.v >= topVPos and where.v < topVPos + CoordinateType (GetRowHeight () + interLineSpace)) {
1188 return GetRowRelativeCharAtLoc (where.h, start);
1189 }
1190
1191 if (curTopRowRelativeRowNumber >= maxRowsToCheck) {
1192 break; // we've checked enuf...
1193 }
1194
1195 topVPos += GetRowHeight () + interLineSpace;
1196 } while (GetNextRowReference (&curRow));
1197
1198 return GetEnd ();
1199}
1200
1201/*
1202@METHOD: SimpleTextImager::ContainsMappedDisplayCharacters
1203@DESCRIPTION: <p>Override @'TextImager::ContainsMappedDisplayCharacters' to hide '\n' characters.
1204 See @'qDefaultLedSoftLineBreakChar'.</p>
1205*/
1206bool SimpleTextImager::ContainsMappedDisplayCharacters (const Led_tChar* text, size_t nTChars) const
1207{
1208 return ContainsMappedDisplayCharacters_HelperForChar (text, nTChars, '\n') or inherited::ContainsMappedDisplayCharacters (text, nTChars);
1209}
1210
1211/*
1212@METHOD: SimpleTextImager::RemoveMappedDisplayCharacters
1213@DESCRIPTION: <p>Override @'TextImager::RemoveMappedDisplayCharacters' to hide '\n' characters.</p>
1214*/
1215size_t SimpleTextImager::RemoveMappedDisplayCharacters (Led_tChar* copyText, size_t nTChars) const
1216{
1217 size_t newLen = inherited::RemoveMappedDisplayCharacters (copyText, nTChars);
1218 Assert (newLen <= nTChars);
1219 size_t newerLen = RemoveMappedDisplayCharacters_HelperForChar (copyText, newLen, '\n');
1220 Assert (newerLen <= newLen);
1221 Assert (newerLen <= nTChars);
1222 return newerLen;
1223}
1224#endif
#define AssertNotNull(p)
Definition Assertions.h:333
#define RequireNotNull(p)
Definition Assertions.h:347
#define AssertMember(p, c)
Definition Assertions.h:312