4#include "Stroika/Frameworks/StroikaPreComp.h"
9#include "Stroika/Frameworks/Led/GDI.h"
10#include "Stroika/Frameworks/Led/Support.h"
12#include "MultiRowTextImager.h"
16using namespace Stroika::Frameworks::Led;
18#if qStroika_Frameworks_Led_SupportGDI
24MultiRowTextImager::~MultiRowTextImager ()
26 Assert (fTopLinePartitionMarkerInWindow ==
nullptr);
29void MultiRowTextImager::HookLosingTextStore ()
31 inherited::HookLosingTextStore ();
32 HookLosingTextStore_ ();
35void MultiRowTextImager::HookLosingTextStore_ ()
37 SetPartition (PartitionPtr ());
40void MultiRowTextImager::HookGainedNewTextStore ()
42 inherited::HookGainedNewTextStore ();
43 HookGainedNewTextStore_ ();
46void MultiRowTextImager::HookGainedNewTextStore_ ()
48 if (GetPartition ().get () ==
nullptr) {
49 SetPartition (MakeDefaultPartition ());
53void MultiRowTextImager::SetPartition (
const PartitionPtr& partitionPtr)
56 inherited::SetPartition (partitionPtr);
57 if (partitionPtr.get () ==
nullptr) {
58 InvalidateTotalRowsInWindow ();
59 fTopLinePartitionMarkerInWindow =
nullptr;
62 fPMCacheMgr = unique_ptr<PMInfoCacheMgr> (
new PMInfoCacheMgr (*
this));
63 fTopLinePartitionMarkerInWindow = GetFirstPartitionMarker ();
65 InvalidateTotalRowsInWindow ();
66 AssureWholeWindowUsedIfNeeded ();
67 InvalidateScrollBarParameters ();
71PartitioningTextImager::PartitionPtr MultiRowTextImager::MakeDefaultPartition ()
const
76MultiRowTextImager::PartitionElementCacheInfo MultiRowTextImager::GetPartitionElementCacheInfo (Partition::PartitionMarker* pm)
const
78 return fPMCacheMgr->GetPartitionElementCacheInfo (pm);
81MultiRowTextImager::PartitionElementCacheInfo MultiRowTextImager::GetPartitionElementCacheInfo (MultiRowTextImager::RowReference row)
const
83 return GetPartitionElementCacheInfo (row.GetPartitionMarker ());
86bool MultiRowTextImager::GetIthRowReferenceFromHere (RowReference* adjustMeInPlace, ptrdiff_t ith)
const
88 for (; ith > 0; --ith) {
89 if (not GetNextRowReference (adjustMeInPlace)) {
93 for (; ith < 0; ++ith) {
94 if (not GetPreviousRowReference (adjustMeInPlace)) {
101size_t MultiRowTextImager::GetRowNumber (RowReference rowRef)
const
106 size_t rowNumber = rowRef.GetSubRow ();
108 for (PartitionMarker* cur = rowRef.GetPartitionMarker ()->GetPrevious (); cur !=
nullptr; cur = cur->GetPrevious ()) {
109 rowNumber += GetPartitionElementCacheInfo (cur).GetRowCount ();
119size_t MultiRowTextImager::CountRowDifference (RowReference lhs, RowReference rhs)
const
128 PartitionMarker* lhsPM = lhs.GetPartitionMarker ();
129 PartitionMarker* rhsPM = rhs.GetPartitionMarker ();
130 size_t lhsMarkerStart = lhsPM->GetStart ();
131 size_t rhsMarkerStart = rhsPM->GetStart ();
132 bool leftSmaller = ((lhsMarkerStart < rhsMarkerStart) or ((lhsMarkerStart == rhsMarkerStart) and lhs.GetSubRow () <= rhs.GetSubRow ()));
133 RowReference firstRowRef = leftSmaller ? lhs : rhs;
134 RowReference lastRowRef = leftSmaller ? rhs : lhs;
136 size_t rowsGoneBy = 0;
137 for (RowReference cur = firstRowRef; cur != lastRowRef; ++rowsGoneBy) {
138 [[maybe_unused]]
bool result = GetIthRowReferenceFromHere (&cur, 1);
153size_t MultiRowTextImager::CountRowDifferenceLimited (RowReference lhs, RowReference rhs,
size_t limit)
const
162 PartitionMarker* lhsPM = lhs.GetPartitionMarker ();
163 PartitionMarker* rhsPM = rhs.GetPartitionMarker ();
164 size_t lhsMarkerStart = lhsPM->GetStart ();
165 size_t rhsMarkerStart = rhsPM->GetStart ();
166 bool leftSmaller = ((lhsMarkerStart < rhsMarkerStart) or ((lhsMarkerStart == rhsMarkerStart) and lhs.GetSubRow () <= rhs.GetSubRow ()));
167 RowReference firstRowRef = leftSmaller ? lhs : rhs;
168 RowReference lastRowRef = leftSmaller ? rhs : lhs;
170 size_t rowsGoneBy = 0;
171 for (RowReference cur = firstRowRef; cur != lastRowRef; ++rowsGoneBy) {
172 [[maybe_unused]]
bool result = GetIthRowReferenceFromHere (&cur, 1);
174 if (rowsGoneBy >= limit) {
181size_t MultiRowTextImager::GetTopRowInWindow ()
const
184 return (GetRowNumber (GetTopRowReferenceInWindow ()));
187size_t MultiRowTextImager::GetTotalRowsInWindow ()
const
189 return GetTotalRowsInWindow_ ();
192size_t MultiRowTextImager::GetLastRowInWindow ()
const
195 return (GetRowNumber (GetLastRowReferenceInWindow ()));
198void MultiRowTextImager::SetTopRowInWindow (
size_t newTopRow)
202 Assert (newTopRow <= GetRowCount ());
205 SetTopRowInWindow (GetIthRowReference (newTopRow));
207 Assert (GetTopRowInWindow () == newTopRow);
212void MultiRowTextImager::AssureWholeWindowUsedIfNeeded ()
214 SetTopRowInWindow (GetTopRowReferenceInWindow ());
221size_t MultiRowTextImager::GetMarkerPositionOfStartOfWindow ()
const
223 return (GetStartOfRow (GetTopRowReferenceInWindow ()));
230size_t MultiRowTextImager::GetMarkerPositionOfEndOfWindow ()
const
232 return GetEndOfRow (GetLastRowReferenceInWindow ());
235size_t MultiRowTextImager::GetMarkerPositionOfStartOfLastRowOfWindow ()
const
237 return GetStartOfRow (GetLastRowReferenceInWindow ());
240ptrdiff_t MultiRowTextImager::CalculateRowDeltaFromCharDeltaFromTopOfWindow (
long deltaChars)
const
242 Assert (
long (GetMarkerPositionOfStartOfWindow ()) >= 0 - deltaChars);
243 size_t pos = long (GetMarkerPositionOfStartOfWindow ()) + deltaChars;
244 RowReference targetRow = GetRowReferenceContainingPosition (pos);
245 size_t rowDiff = CountRowDifference (targetRow, GetTopRowReferenceInWindow ());
246 return (deltaChars >= 0) ? rowDiff : -long (rowDiff);
249ptrdiff_t MultiRowTextImager::CalculateCharDeltaFromRowDeltaFromTopOfWindow (ptrdiff_t deltaRows)
const
251 RowReference row = GetIthRowReferenceFromHere (GetTopRowReferenceInWindow (), deltaRows);
252 return (
long (GetStartOfRow (row)) -
long (GetMarkerPositionOfStartOfWindow ()));
255void MultiRowTextImager::ScrollByIfRoom (ptrdiff_t downByRows)
257 RowReference newTopRow = GetTopRowReferenceInWindow ();
258 (void)GetIthRowReferenceFromHere (&newTopRow, downByRows);
259 SetTopRowInWindow (newTopRow);
266void MultiRowTextImager::ScrollSoShowing (
size_t markerPos,
size_t andTryToShowMarkerPos)
268 Assert (markerPos <= GetLength ());
269 Assert (fTotalRowsInWindow == 0 or fTotalRowsInWindow == ComputeRowsThatWouldFitInWindowWithTopRow (GetTopRowReferenceInWindow ()));
271 if (andTryToShowMarkerPos == 0) {
272 andTryToShowMarkerPos = markerPos;
274 Assert (andTryToShowMarkerPos <= GetLength ());
282 size_t startOfWindow = GetMarkerPositionOfStartOfWindow ();
283 size_t endOfWindow = GetMarkerPositionOfEndOfWindow ();
284 if (markerPos >= startOfWindow and markerPos < endOfWindow and andTryToShowMarkerPos >= startOfWindow and andTryToShowMarkerPos < endOfWindow) {
285 ScrollSoShowingHHelper (markerPos, andTryToShowMarkerPos);
289 RowReference originalTop = GetTopRowReferenceInWindow ();
306 RowReference newTop = originalTop;
307 while (markerPos < newTop.GetPartitionMarker ()->GetStart ()) {
308 newTop = RowReference (newTop.GetPartitionMarker ()->GetPrevious (), 0);
312 if (not PositionWouldFitInWindowWithThisTopRow (markerPos, newTop)) {
313 while (markerPos > newTop.GetPartitionMarker ()->GetEnd ()) {
314 if (newTop.GetPartitionMarker ()->GetNext () ==
nullptr) {
318 newTop = RowReference (newTop.GetPartitionMarker ()->GetNext (), 0);
320 Assert (Contains (markerPos, markerPos, *newTop.GetPartitionMarker ()));
327 while (markerPos < GetStartOfRow (newTop) and GetPreviousRowReference (&newTop))
329 while (not PositionWouldFitInWindowWithThisTopRow (markerPos, newTop) and GetNextRowReference (&newTop))
333 Assert (markerPos >= GetStartOfRow (newTop));
334 Assert (PositionWouldFitInWindowWithThisTopRow (markerPos, newTop));
340 while (not PositionWouldFitInWindowWithThisTopRow (andTryToShowMarkerPos, newTop)) {
341 RowReference trailNewTop = newTop;
342 if (andTryToShowMarkerPos < GetStartOfRow (trailNewTop)) {
343 if (not GetPreviousRowReference (&trailNewTop)) {
348 if (not GetNextRowReference (&trailNewTop)) {
352 if (PositionWouldFitInWindowWithThisTopRow (markerPos, trailNewTop)) {
353 newTop = trailNewTop;
364 const unsigned kRowMoveThreshold = 1;
365 if (CountRowDifferenceLimited (originalTop, newTop, kRowMoveThreshold + 1) > kRowMoveThreshold) {
366 bool mustPreserveSecondPos = PositionWouldFitInWindowWithThisTopRow (andTryToShowMarkerPos, newTop);
370 size_t topMarkerPos = min (markerPos, andTryToShowMarkerPos);
371 size_t botMarkerPos = max (markerPos, andTryToShowMarkerPos);
372 size_t numRowsAbove = CountRowDifference (newTop, GetRowReferenceContainingPosition (topMarkerPos));
373 size_t rowsInWindow = ComputeRowsThatWouldFitInWindowWithTopRow (newTop);
374 RowReference lastRowInWindow = GetIthRowReferenceFromHere (newTop, rowsInWindow - 1);
375 size_t numRowsBelow = CountRowDifference (lastRowInWindow, GetRowReferenceContainingPosition (botMarkerPos));
377 size_t numRowsToSpare = numRowsAbove + numRowsBelow;
380 RowReference trailNewTop = newTop;
381 GetIthRowReferenceFromHere (&trailNewTop,
int (numRowsAbove) -
int (numRowsToSpare / 2));
382 if (PositionWouldFitInWindowWithThisTopRow (markerPos, trailNewTop) and
383 (not mustPreserveSecondPos or PositionWouldFitInWindowWithThisTopRow (andTryToShowMarkerPos, trailNewTop))) {
384 newTop = trailNewTop;
388 SetTopRowInWindow (newTop);
390 Assert (GetMarkerPositionOfStartOfWindow () <= markerPos and markerPos <= GetMarkerPositionOfEndOfWindow ());
395 ScrollSoShowingHHelper (markerPos, andTryToShowMarkerPos);
398void MultiRowTextImager::SetTopRowInWindow (RowReference row)
400 if (GetForceAllRowsShowing ()) {
401 row = AdjustPotentialTopRowReferenceSoWholeWindowUsed (row);
403 if (row != GetTopRowReferenceInWindow ()) {
404 SetTopRowInWindow_ (row);
405 InvalidateScrollBarParameters ();
420void MultiRowTextImager::Draw (
const Led_Rect& subsetToDraw,
bool printing)
424 Led_Rect rowsLeftToDrawRect = GetWindowRect ();
426 Tablet_Acquirer tablet_ (
this);
427 Tablet* tablet = tablet_;
435#if qStroika_Foundation_Common_Platform_MacOS
437 RGBColor oldForeColor = GDI_GetForeColor ();
438 RGBColor oldBackColor = GDI_GetBackColor ();
439#elif qStroika_Foundation_Common_Platform_Windows
440 GDI_Obj_Selector pen (tablet, ::GetStockObject (NULL_PEN));
441 GDI_Obj_Selector brush (tablet, ::GetStockObject (NULL_BRUSH));
448 OffscreenTablet thisIsOurNewOST;
449 if (GetImageUsingOffscreenBitmaps () and not printing) {
450 thisIsOurNewOST.Setup (tablet_);
455 size_t totalRowsInWindow = GetTotalRowsInWindow_ ();
456 RowReference topRowInWindow = GetTopRowReferenceInWindow ();
457 size_t rowsLeftInWindow = totalRowsInWindow;
458 for (PartitionMarker* pm = topRowInWindow.GetPartitionMarker (); rowsLeftInWindow != 0; pm = pm->GetNext ()) {
459 Assert (pm !=
nullptr);
460 size_t startSubRow = 0;
461 size_t maxSubRow =
static_cast<size_t> (-1);
462 if (pm == topRowInWindow.GetPartitionMarker ()) {
463 startSubRow = topRowInWindow.GetSubRow ();
465 maxSubRow = rowsLeftInWindow - 1 + startSubRow;
466 size_t rowsDrawn = 0;
467 DrawPartitionElement (pm, startSubRow, maxSubRow, tablet, (GetImageUsingOffscreenBitmaps () and not printing) ? &thisIsOurNewOST : nullptr,
468 printing, subsetToDraw, &rowsLeftToDrawRect, &rowsDrawn);
469 Assert (rowsLeftInWindow >= rowsDrawn);
470 rowsLeftInWindow -= rowsDrawn;
476 Assert (tablet == tablet_);
478 Led_Rect eraser = GetWindowRect ();
479 eraser.top = rowsLeftToDrawRect.top;
480 eraser.bottom = subsetToDraw.bottom;
482 if (eraser.top > eraser.bottom) {
483 eraser.bottom = eraser.top;
490 if (((eraser.top >= subsetToDraw.top and eraser.top <= subsetToDraw.bottom) or
491 (eraser.bottom >= subsetToDraw.top and eraser.bottom <= subsetToDraw.bottom)) and
492 (eraser.GetHeight () > 0 and eraser.GetWidth () > 0)) {
493 if (GetImageUsingOffscreenBitmaps () and not printing) {
494 tablet = thisIsOurNewOST.PrepareRect (eraser);
496 EraseBackground (tablet, eraser, printing);
500 size_t hilightStart = GetSelectionStart ();
501 size_t hilightEnd = GetSelectionEnd ();
502 size_t end = GetMarkerPositionOfEndOfWindow ();
503 bool segmentHilightedAtEnd = GetSelectionShown () and (hilightStart < end) and (end <= hilightEnd);
504 if (not printing and segmentHilightedAtEnd) {
505 HilightARectangle (tablet, eraser);
508 if (GetImageUsingOffscreenBitmaps () and not printing) {
512 thisIsOurNewOST.BlastBitmapToOrigTablet ();
519#if qStroika_Foundation_Common_Platform_MacOS
524 Assert (*tablet == Led_GetCurrentGDIPort ());
525 GDI_RGBForeColor (oldForeColor);
526 GDI_RGBBackColor (oldBackColor);
530#if qStroika_Foundation_Common_Platform_MacOS
531 Assert (*tablet == Led_GetCurrentGDIPort ());
532 GDI_RGBForeColor (oldForeColor);
533 GDI_RGBBackColor (oldBackColor);
541void MultiRowTextImager::DrawPartitionElement (PartitionMarker* pm,
size_t startSubRow,
size_t maxSubRow, Tablet* tablet, OffscreenTablet* offscreenTablet,
542 bool printing,
const Led_Rect& subsetToDraw, Led_Rect* remainingDrawArea,
size_t* rowsDrawn)
548 size_t start = pm->GetStart ();
549 size_t end = pm->GetEnd ();
551 Assert (end <= GetLength () + 1);
552 if (end == GetLength () + 1) {
556 Tablet* savedTablet = tablet;
557 PartitionElementCacheInfo pmCacheInfo = GetPartitionElementCacheInfo (pm);
558 size_t endSubRow = min (pmCacheInfo.GetRowCount () - 1, maxSubRow);
561 size_t partLen = end - start;
563 CopyOut (start, partLen, partitionBuf.data ());
565 for (
size_t subRow = startSubRow; subRow <= endSubRow; ++subRow) {
566 Led_Rect currentRowRect = *remainingDrawArea;
567 currentRowRect.bottom = currentRowRect.top + pmCacheInfo.GetRowHeight (subRow);
568 DistanceType interlineSpace = (subRow == pmCacheInfo.GetLastRow ()) ? pmCacheInfo.GetInterLineSpace () : 0;
569 if ((currentRowRect.bottom + CoordinateType (interlineSpace) > subsetToDraw.top) and (currentRowRect.top < subsetToDraw.bottom)) {
574 size_t rowStart = start + pmCacheInfo.PeekAtRowStart (subRow);
576 if (subRow < pmCacheInfo.GetLastRow ()) {
577 rowEnd = pm->GetStart () + pmCacheInfo.PeekAtRowStart (subRow + 1);
580 if (subRow == pmCacheInfo.GetLastRow ()) {
581 Assert (pm->GetEnd () > 0);
582 size_t markerEnd = pm->GetEnd ();
583 Assert (markerEnd <= GetLength () + 1);
584 if (markerEnd == GetLength () + 1) {
585 rowEnd = GetLength ();
588 size_t prevToEnd = FindPreviousCharacter (markerEnd);
589 if (prevToEnd >= rowStart) {
591 CopyOut (prevToEnd, 1, &lastChar);
592 if (RemoveMappedDisplayCharacters (&lastChar, 1) == 0) {
593 rowEnd = (prevToEnd);
598 Assert (rowEnd == GetEndOfRowContainingPosition (rowStart));
602 TextLayoutBlock_Copy rowText = GetTextLayoutBlock (rowStart, rowEnd);
604 TextLayoutBlock_Basic rowText (partitionBuf + (rowStart - start), partitionBuf + (rowStart - start) + (rowEnd - rowStart));
607 if (offscreenTablet !=
nullptr) {
608 tablet = offscreenTablet->PrepareRect (currentRowRect, interlineSpace);
616 Led_Rect invalidRowRect =
Intersection (currentRowRect, subsetToDraw);
617 DrawRow (tablet, currentRowRect, invalidRowRect, rowText, rowStart, rowEnd, printing);
623 if (interlineSpace != 0) {
624 size_t hilightStart = GetSelectionStart ();
625 size_t hilightEnd = GetSelectionEnd ();
626 bool segmentHilightedAtEnd = GetSelectionShown () and (hilightStart < rowEnd) and (rowEnd <= hilightEnd);
627 if (pm->GetNext () ==
nullptr and subRow == pmCacheInfo.GetLastRow ()) {
628 segmentHilightedAtEnd =
false;
630 DrawInterLineSpace (interlineSpace, tablet, currentRowRect.bottom, segmentHilightedAtEnd, printing);
633 if (offscreenTablet !=
nullptr) {
637 offscreenTablet->BlastBitmapToOrigTablet ();
638 tablet = savedTablet;
642 remainingDrawArea->top = currentRowRect.bottom + interlineSpace;
647Led_Rect MultiRowTextImager::GetCharLocation (
size_t afterPosition)
const
649 return (GetCharLocationRowRelative (afterPosition, RowReference (GetFirstPartitionMarker (), 0)));
652Led_Rect MultiRowTextImager::GetCharWindowLocation (
size_t afterPosition)
const
654 Led_Point windowOrigin = GetWindowRect ().GetOrigin () - Led_Point (0, GetHScrollPos ());
655 return (windowOrigin + GetCharLocationRowRelative (afterPosition, GetTopRowReferenceInWindow (), GetTotalRowsInWindow_ ()));
658size_t MultiRowTextImager::GetCharAtLocation (
const Led_Point& where)
const
660 return (GetCharAtLocationRowRelative (where, RowReference (GetFirstPartitionMarker (), 0)));
663size_t MultiRowTextImager::GetCharAtWindowLocation (
const Led_Point& where)
const
665 Led_Point windowOrigin = GetWindowRect ().GetOrigin () - Led_Point (0, GetHScrollPos ());
666 return (GetCharAtLocationRowRelative (where - windowOrigin, GetTopRowReferenceInWindow (), GetTotalRowsInWindow_ ()));
669size_t MultiRowTextImager::GetStartOfRow (
size_t rowNumber)
const
673 return (GetStartOfRow (GetIthRowReference (rowNumber)));
676size_t MultiRowTextImager::GetStartOfRowContainingPosition (
size_t charPosition)
const
678 return (GetStartOfRow (GetRowReferenceContainingPosition (charPosition)));
681size_t MultiRowTextImager::GetEndOfRow (
size_t rowNumber)
const
685 return (GetEndOfRow (GetIthRowReference (rowNumber)));
688size_t MultiRowTextImager::GetEndOfRowContainingPosition (
size_t charPosition)
const
690 return (GetEndOfRow (GetRowReferenceContainingPosition (charPosition)));
693size_t MultiRowTextImager::GetRealEndOfRow (
size_t rowNumber)
const
697 return (GetRealEndOfRow (GetIthRowReference (rowNumber)));
700size_t MultiRowTextImager::GetRealEndOfRowContainingPosition (
size_t charPosition)
const
702 return (GetRealEndOfRow (GetRowReferenceContainingPosition (charPosition)));
705size_t MultiRowTextImager::GetStartOfRow (RowReference row)
const
707 PartitionMarker* cur = row.GetPartitionMarker ();
708 size_t subRow = row.GetSubRow ();
710 return (cur->GetStart () + (subRow == 0 ? 0 : GetPartitionElementCacheInfo (cur).GetLineRelativeRowStartPosition (subRow)));
713size_t MultiRowTextImager::GetEndOfRow (RowReference row)
const
715 PartitionMarker* cur = row.GetPartitionMarker ();
716 size_t subRow = row.GetSubRow ();
718 PartitionElementCacheInfo pmCacheInfo = GetPartitionElementCacheInfo (cur);
719 if (subRow == pmCacheInfo.GetLastRow ()) {
723 Assert (cur->GetEnd () > 0);
725 size_t markerEnd = cur->GetEnd ();
726 Assert (markerEnd <= GetLength () + 1);
727 if (markerEnd == GetLength () + 1) {
728 return (GetLength ());
731 size_t prevToEnd = FindPreviousCharacter (markerEnd);
732 if (prevToEnd >= GetStartOfRow (row)) {
734 CopyOut (prevToEnd, 1, &lastChar);
735 if (RemoveMappedDisplayCharacters (&lastChar, 1) == 0) {
742 return (cur->GetStart () + pmCacheInfo.GetLineRelativeRowStartPosition (subRow + 1));
746size_t MultiRowTextImager::GetRealEndOfRow (RowReference row)
const
748 PartitionMarker* cur = row.GetPartitionMarker ();
749 size_t subRow = row.GetSubRow ();
751 PartitionElementCacheInfo pmCacheInfo = GetPartitionElementCacheInfo (cur);
752 if (subRow == pmCacheInfo.GetLastRow ()) {
753 Assert (cur->GetEnd () > 0);
754 size_t markerEnd = cur->GetEnd ();
758 return (cur->GetStart () + pmCacheInfo.GetLineRelativeRowStartPosition (subRow + 1));
762MultiRowTextImager::RowReference MultiRowTextImager::GetRowReferenceContainingPosition (
size_t charPosition)
const
764 Require (charPosition <= GetEnd ());
765 PartitionMarker* pm = GetPartitionMarkerContainingPosition (charPosition);
768 size_t pmStart = pm->GetStart ();
769 if (charPosition == pmStart) {
770 return (RowReference (pm, 0));
774 PartitionElementCacheInfo pmCacheInfo = GetPartitionElementCacheInfo (pm);
775 return (RowReference (pm, pmCacheInfo.LineRelativePositionInWhichRow (charPosition - pmStart)));
778size_t MultiRowTextImager::GetRowContainingPosition (
size_t charPosition)
const
782 return (GetRowNumber (GetRowReferenceContainingPosition (charPosition)));
785size_t MultiRowTextImager::GetRowCount ()
const
789 for (PartitionMarker* cur = GetFirstPartitionMarker (); cur !=
nullptr; cur = cur->GetNext ()) {
791 Assert (GetPartitionElementCacheInfo (cur).GetRowCount () >= 1);
792 rowCount += GetPartitionElementCacheInfo (cur).GetRowCount ();
797Led_Rect MultiRowTextImager::GetCharLocationRowRelativeByPosition (
size_t afterPosition,
size_t positionOfTopRow,
size_t maxRowsToCheck)
const
799 return GetCharLocationRowRelative (afterPosition, GetRowReferenceContainingPosition (positionOfTopRow), maxRowsToCheck);
802DistanceType MultiRowTextImager::GetRowHeight (
size_t rowNumber)
const
806 return (GetRowHeight (GetIthRowReference (rowNumber)));
813DistanceType MultiRowTextImager::GetRowRelativeBaselineOfRowContainingPosition (
size_t charPosition)
const
815 RowReference thisRow = GetRowReferenceContainingPosition (charPosition);
816 size_t startOfRow = GetStartOfRow (thisRow);
817 size_t endOfRow = GetEndOfRow (thisRow);
818 return MeasureSegmentBaseLine (startOfRow, endOfRow);
821void MultiRowTextImager::GetStableTypingRegionContaingMarkerRange (
size_t fromMarkerPos,
size_t toMarkerPos,
size_t* expandedFromMarkerPos,
822 size_t* expandedToMarkerPos)
const
826 Assert (fromMarkerPos <= toMarkerPos);
827 Assert (toMarkerPos <= GetEnd ());
829 size_t curTopRowRelativeRowNumber = 0;
831 RowReference curRow = GetTopRowReferenceInWindow ();
833 PartitionMarker* cur = curRow.GetPartitionMarker ();
835 size_t start = cur->GetStart ();
836 size_t end = cur->GetEnd ();
841 if (cur->GetNext () ==
nullptr) {
846 if (curTopRowRelativeRowNumber == 0 and (fromMarkerPos < start)) {
850 ++curTopRowRelativeRowNumber;
852 if (Contains (*cur, fromMarkerPos) and Contains (*cur, toMarkerPos)) {
853 (*expandedFromMarkerPos) = start;
854 (*expandedToMarkerPos) = end;
855 Assert ((*expandedFromMarkerPos) <= (*expandedToMarkerPos));
856 Assert ((*expandedToMarkerPos) <= GetEnd ());
860 if (curTopRowRelativeRowNumber >= GetTotalRowsInWindow_ ()) {
866 }
while (GetNextRowReference (&curRow));
868 (*expandedFromMarkerPos) = 0;
869 (*expandedToMarkerPos) = GetEnd ();
872DistanceType MultiRowTextImager::GetHeightOfRows (
size_t startingRow,
size_t rowCount)
const
874 return (GetHeightOfRows (GetIthRowReference (startingRow), rowCount));
877DistanceType MultiRowTextImager::GetHeightOfRows (RowReference startingRow,
size_t rowCount)
const
879 DistanceType height = 0;
880 for (RowReference curRow = startingRow; rowCount > 0; rowCount--) {
881 PartitionMarker* curPM = curRow.GetPartitionMarker ();
882 PartitionElementCacheInfo pmCacheInfo = GetPartitionElementCacheInfo (curPM);
883 height += pmCacheInfo.GetRowHeight (curRow.GetSubRow ());
884 if (curRow.GetSubRow () == pmCacheInfo.GetLastRow ()) {
885 height += pmCacheInfo.GetInterLineSpace ();
887 (void)GetNextRowReference (&curRow);
892void MultiRowTextImager::DidUpdateText (
const UpdateInfo& updateInfo)
noexcept
906 InvalidateTotalRowsInWindow ();
909 InvalidateTotalRowsInWindow ();
910 inherited::DidUpdateText (updateInfo);
913 AssureWholeWindowUsedIfNeeded ();
914 InvalidateScrollBarParameters ();
917void MultiRowTextImager::SetWindowRect (
const Led_Rect& windowRect)
928 bool heightChanged = GetWindowRect ().GetHeight () != windowRect.GetHeight ();
929 inherited::SetWindowRect (windowRect);
930 if (heightChanged and PeekAtTextStore () !=
nullptr) {
931 InvalidateTotalRowsInWindow ();
932 AssureWholeWindowUsedIfNeeded ();
933 InvalidateScrollBarParameters ();
943void MultiRowTextImager::InvalidateAllCaches ()
945 inherited::InvalidateAllCaches ();
946 if (GetPartition ().get () !=
nullptr) {
947 if (fPMCacheMgr.get () !=
nullptr) {
948 fPMCacheMgr->ClearCache ();
950 InvalidateTotalRowsInWindow ();
952 AssureWholeWindowUsedIfNeeded ();
953 InvalidateScrollBarParameters ();
957MultiRowTextImager::RowReference MultiRowTextImager::AdjustPotentialTopRowReferenceSoWholeWindowUsed (
const RowReference& potentialTopRow)
964 if (potentialTopRow.GetSubRow () == 0 and potentialTopRow.GetPartitionMarker ()->GetPrevious () ==
nullptr) {
965 return potentialTopRow;
968 CoordinateType windowHeight = GetWindowRect ().GetHeight ();
969 CoordinateType heightUsed = 0;
971 for (RowReference curRow = potentialTopRow;;) {
972 PartitionMarker* curPM = curRow.GetPartitionMarker ();
973 PartitionElementCacheInfo pmCacheInfo = GetPartitionElementCacheInfo (curPM);
974 heightUsed += pmCacheInfo.GetRowHeight (curRow.GetSubRow ());
975 if (curRow.GetSubRow () == pmCacheInfo.GetLastRow ()) {
976 heightUsed += pmCacheInfo.GetInterLineSpace ();
978 if (heightUsed >= windowHeight) {
979 return (potentialTopRow);
981 if (not GetNextRowReference (&curRow)) {
988 for (RowReference curRow = potentialTopRow;;) {
989 if (not GetPreviousRowReference (&curRow)) {
994 PartitionMarker* curPM = curRow.GetPartitionMarker ();
995 PartitionElementCacheInfo pmCacheInfo = GetPartitionElementCacheInfo (curPM);
996 heightUsed += pmCacheInfo.GetRowHeight (curRow.GetSubRow ());
997 if (curRow.GetSubRow () == pmCacheInfo.GetLastRow ()) {
998 heightUsed += pmCacheInfo.GetInterLineSpace ();
1000 if (heightUsed > windowHeight) {
1002 [[maybe_unused]]
bool result = GetNextRowReference (&curRow);
1006 else if (heightUsed == windowHeight) {
1011 return (potentialTopRow);
1014bool MultiRowTextImager::PositionWouldFitInWindowWithThisTopRow (
size_t markerPos,
const RowReference& newTopRow)
1016 if (markerPos < GetStartOfRow (newTopRow)) {
1020 size_t rowCount = ComputeRowsThatWouldFitInWindowWithTopRow (newTopRow);
1021 RowReference lastRow = GetIthRowReferenceFromHere (newTopRow, rowCount - 1);
1023 return (markerPos < GetRealEndOfRow (lastRow));
1026void MultiRowTextImager::ReValidateSubRowInTopLineInWindow ()
1031 if (fSubRowInTopLineInWindow != 0) {
1033 size_t lastRow = GetPartitionElementCacheInfo (fTopLinePartitionMarkerInWindow).GetLastRow ();
1034 if (fSubRowInTopLineInWindow > lastRow) {
1035 fSubRowInTopLineInWindow = lastRow;
1038 bool pmNotWrapped = (fTopLinePartitionMarkerInWindow->fPixelHeightCache == DistanceType (-1));
1039 size_t lastRow = GetPartitionElementCacheInfo (fTopLinePartitionMarkerInWindow).GetLastRow ();
1040 if (fSubRowInTopLineInWindow > lastRow) {
1041 fSubRowInTopLineInWindow = lastRow;
1050 fTopLinePartitionMarkerInWindow->InvalidateCache ();
1056size_t MultiRowTextImager::ComputeRowsThatWouldFitInWindowWithTopRow (
const RowReference& newTopRow)
const
1062 CoordinateType windowHeight = GetWindowRect ().GetHeight ();
1068 size_t rowCount = 0;
1069 CoordinateType heightUsed = 0;
1070 for (RowReference curRow = newTopRow;;) {
1072 PartitionMarker* curPM = curRow.GetPartitionMarker ();
1073 PartitionElementCacheInfo pmCacheInfo = GetPartitionElementCacheInfo (curPM);
1074 heightUsed += pmCacheInfo.GetRowHeight (curRow.GetSubRow ());
1075 if (curRow.GetSubRow () == pmCacheInfo.GetLastRow ()) {
1076 heightUsed += pmCacheInfo.GetInterLineSpace ();
1078 if (heightUsed > windowHeight) {
1083 else if (heightUsed == windowHeight) {
1087 if (not GetNextRowReference (&curRow)) {
1091 if (rowCount == 0) {
1104Led_Rect MultiRowTextImager::GetCharLocationRowRelative (
size_t afterPosition, RowReference topRow,
size_t maxRowsToCheck)
const
1107 const Led_Rect kMagicBeforeRect = Led_Rect (-10000, 0, 0, 0);
1108 const Led_Rect kMagicAfterRect = Led_Rect (10000, 0, 0, 0);
1110 Require (afterPosition <= GetEnd ());
1112 if (afterPosition < GetStartOfRow (topRow)) {
1113 return (kMagicBeforeRect);
1116 RowReference curRow = topRow;
1117 size_t curTopRowRelativeRowNumber = 0;
1118 CoordinateType topVPos = 0;
1120 PartitionMarker* cur = curRow.GetPartitionMarker ();
1121 size_t subRow = curRow.GetSubRow ();
1123 size_t start = cur->GetStart ();
1124 size_t end = cur->GetEnd ();
1126 Assert (end <= GetEnd () + 1);
1128 PartitionElementCacheInfo pmCacheInfo = GetPartitionElementCacheInfo (cur);
1133 start += pmCacheInfo.PeekAtRowStart (subRow);
1134 if (subRow < pmCacheInfo.GetLastRow ()) {
1135 end = cur->GetStart () + pmCacheInfo.PeekAtRowStart (subRow + 1);
1136 Assert (start <= end);
1139 ++curTopRowRelativeRowNumber;
1144 if (afterPosition >= start and afterPosition < end) {
1145 Assert (start <= afterPosition);
1146 DistanceType hStart = 0;
1147 DistanceType hEnd = 0;
1148 GetRowRelativeCharLoc (afterPosition, &hStart, &hEnd);
1149 Assert (hStart <= hEnd);
1150 return (Led_Rect (topVPos, hStart, pmCacheInfo.GetRowHeight (subRow), hEnd - hStart));
1153 topVPos += pmCacheInfo.GetRowHeight (subRow);
1155 if (pmCacheInfo.GetLastRow () == subRow) {
1156 topVPos += pmCacheInfo.GetInterLineSpace ();
1159 if (curTopRowRelativeRowNumber >= maxRowsToCheck) {
1162 }
while (GetNextRowReference (&curRow));
1164 return (kMagicAfterRect);
1167size_t MultiRowTextImager::GetCharAtLocationRowRelative (
const Led_Point& where, RowReference topRow,
size_t maxRowsToCheck)
const
1179 RowReference curRow = topRow;
1180 size_t curTopRowRelativeRowNumber = 0;
1181 CoordinateType topVPos = 0;
1183 PartitionMarker* cur = curRow.GetPartitionMarker ();
1184 size_t subRow = curRow.GetSubRow ();
1186 size_t start = cur->GetStart ();
1188 PartitionElementCacheInfo pmCacheInfo = GetPartitionElementCacheInfo (cur);
1193 start += pmCacheInfo.PeekAtRowStart (subRow);
1198 DistanceType interLineSpaceIfAny = (pmCacheInfo.GetLastRow () == subRow) ? pmCacheInfo.GetInterLineSpace () : 0;
1200 curTopRowRelativeRowNumber++;
1201 if (where.v >= topVPos and where.v < topVPos + CoordinateType (pmCacheInfo.GetRowHeight (subRow) + interLineSpaceIfAny)) {
1202 return GetRowRelativeCharAtLoc (where.h, start);
1205 if (curTopRowRelativeRowNumber >= maxRowsToCheck) {
1209 topVPos += pmCacheInfo.GetRowHeight (subRow) + interLineSpaceIfAny;
1210 }
while (GetNextRowReference (&curRow));
1215DistanceType MultiRowTextImager::CalculateInterLineSpace (
const PartitionMarker* )
const
1225bool MultiRowTextImager::ContainsMappedDisplayCharacters (
const Led_tChar* text,
size_t nTChars)
const
1227 return ContainsMappedDisplayCharacters_HelperForChar (text, nTChars,
'\n') or inherited::ContainsMappedDisplayCharacters (text, nTChars);
1234size_t MultiRowTextImager::RemoveMappedDisplayCharacters (Led_tChar* copyText,
size_t nTChars)
const
1236 size_t newLen = inherited::RemoveMappedDisplayCharacters (copyText, nTChars);
1237 Assert (newLen <= nTChars);
1238 size_t newerLen = RemoveMappedDisplayCharacters_HelperForChar (copyText, newLen,
'\n');
1239 Assert (newerLen <= newLen);
1240 Assert (newerLen <= nTChars);
1249void MultiRowTextImager::PartitionElementCacheInfo::Clear ()
1251 fRep = make_shared<Rep> ();
1254void MultiRowTextImager::PartitionElementCacheInfo::IncrementRowCountAndFixCacheBuffers (
size_t newStart, DistanceType newRowsHeight)
1256 ++fRep->fRowCountCache;
1259 if (fRep->fRowCountCache > kPackRowStartCount + 1) {
1260 RowStart_* newRowStartArray =
new RowStart_[fRep->fRowCountCache - 1];
1262 if (fRep->fRowCountCache == kPackRowStartCount + 1 + 1) {
1263 ::memcpy (newRowStartArray, &fRep->fRowStartArray, sizeof (fRep->fRowStartArray));
1266 Assert (fRep->fRowCountCache > 2);
1267 if (fRep->fRowCountCache > kPackRowStartCount + 1 + 1) {
1269 (void)memcpy (newRowStartArray, fRep->fRowStartArray, sizeof (newRowStartArray[1]) * (fRep->fRowCountCache - 2));
1270 delete[] fRep->fRowStartArray;
1273 fRep->fRowStartArray = newRowStartArray;
1277 if (fRep->fRowCountCache > kPackRowHeightCount) {
1278 RowHeight_* newRowHeightArray =
new RowHeight_[fRep->fRowCountCache];
1280 if (fRep->fRowCountCache == kPackRowHeightCount + 1) {
1281 ::memcpy (newRowHeightArray, &fRep->fRowHeightArray, sizeof (fRep->fRowHeightArray));
1284 Assert (fRep->fRowCountCache > 1);
1285 if (fRep->fRowCountCache > kPackRowHeightCount + 1) {
1287 memcpy (newRowHeightArray, fRep->fRowHeightArray, sizeof (newRowHeightArray[1]) * (fRep->fRowCountCache - 1));
1288 delete[] fRep->fRowHeightArray;
1291 fRep->fRowHeightArray = newRowHeightArray;
1294 SetRowStart (fRep->fRowCountCache - 1, newStart);
1295 SetRowHeight (fRep->fRowCountCache - 1, newRowsHeight);
1296 fRep->fPixelHeightCache += newRowsHeight;
1304MultiRowTextImager::PMInfoCacheMgr::PMInfoCacheMgr (MultiRowTextImager& imager)
1306 , fCurFillCachePM (nullptr)
1307 , fCurFillCacheInfo ()
1313 PartitionPtr part = imager.GetPartition ();
1314 Assert (part.get () !=
nullptr);
1315 part->AddPartitionWatcher (
this);
1316 fMyMarker = unique_ptr<MyMarker> (
new MyMarker (*
this));
1317 TextStore& ts = part->GetTextStore ();
1318 ts.AddMarker (fMyMarker.get (), 0, ts.GetLength () + 1, part.get ());
1321MultiRowTextImager::PMInfoCacheMgr::~PMInfoCacheMgr ()
1323 PartitionPtr part = fImager.GetPartition ();
1324 part->RemovePartitionWatcher (
this);
1325 TextStore& ts = part->GetTextStore ();
1326 ts.RemoveMarker (fMyMarker.get ());
1329MultiRowTextImager::PartitionElementCacheInfo MultiRowTextImager::PMInfoCacheMgr::GetPartitionElementCacheInfo (Partition::PartitionMarker* pm)
const
1331 if (pm == fCurFillCachePM) {
1332 return fCurFillCacheInfo;
1334 using MAP_CACHE = map<Partition::PartitionMarker*, PartitionElementCacheInfo>;
1335 MAP_CACHE::iterator i = fPMCache.find (pm);
1336 if (i == fPMCache.end ()) {
1338 Assert (fCurFillCachePM ==
nullptr);
1339 fCurFillCachePM = pm;
1340 fImager.FillCache (pm, fCurFillCacheInfo);
1342 for (
size_t t = 0; t < fCurFillCacheInfo.GetRowCount (); ++t) {
1343 Assert (fCurFillCacheInfo.GetLineRelativeRowStartPosition (t) <= pm->GetLength ());
1344 Assert (fCurFillCacheInfo.PeekAtRowStart (t) <= pm->GetLength ());
1347 i = fPMCache.insert (MAP_CACHE::value_type (pm, fCurFillCacheInfo)).first;
1349 Assert (fCurFillCacheInfo.GetRowCount () == i->second.GetRowCount ());
1350 for (
size_t t = 0; t < fCurFillCacheInfo.GetRowCount (); ++t) {
1351 Assert (fCurFillCacheInfo.PeekAtRowHeight (t) == i->second.PeekAtRowHeight (t));
1352 Assert (fCurFillCacheInfo.PeekAtRowStart (t) == i->second.PeekAtRowStart (t));
1355 Assert (fCurFillCachePM == pm);
1356 fCurFillCachePM =
nullptr;
1359 Assert (fCurFillCachePM == pm);
1360 fCurFillCachePM =
nullptr;
1367void MultiRowTextImager::PMInfoCacheMgr::ClearCache ()
1372void MultiRowTextImager::PMInfoCacheMgr::AboutToSplit (PartitionMarker* pm,
size_t ,
void** infoRecord)
const noexcept
1377void MultiRowTextImager::PMInfoCacheMgr::DidSplit (
void* infoRecord)
const noexcept
1379 PartitionMarker* pm =
reinterpret_cast<PartitionMarker*
> (infoRecord);
1380 using MAP_CACHE = map<Partition::PartitionMarker*, PartitionElementCacheInfo>;
1381 MAP_CACHE::iterator i = fPMCache.find (pm);
1382 if (i != fPMCache.end ()) {
1385 fImager.InvalidateTotalRowsInWindow ();
1387 if (pm == fImager.fTopLinePartitionMarkerInWindow) {
1389 fImager.ReValidateSubRowInTopLineInWindow ();
1394void MultiRowTextImager::PMInfoCacheMgr::AboutToCoalece (PartitionMarker* pm,
void** infoRecord)
const noexcept
1398 PartitionMarker* newTopLine =
nullptr;
1399 bool useFirstRow =
false;
1400 if (pm == fImager.fTopLinePartitionMarkerInWindow) {
1401 if (pm->GetNext () ==
nullptr) {
1402 newTopLine = fImager.fTopLinePartitionMarkerInWindow->GetPrevious ();
1403 useFirstRow =
false;
1406 newTopLine = fImager.fTopLinePartitionMarkerInWindow->GetNext ();
1412 if (newTopLine !=
nullptr) {
1417 fImager.SetTopRowInWindow_ (RowReference (newTopLine, 0));
1421 MultiRowPartitionMarker* newTopLine =
nullptr;
1422 bool useFirstRow =
false;
1423 if (pm == fTextImager.fTopLinePartitionMarkerInWindow) {
1424 if (pm->GetNext () ==
nullptr) {
1425 newTopLine = fTextImager.fTopLinePartitionMarkerInWindow->GetPreviousMRPM ();
1426 useFirstRow =
false;
1429 newTopLine = fTextImager.fTopLinePartitionMarkerInWindow->GetNextMRPM ();
1435 MultiRowPartitionMarker* successor = (MultiRowPartitionMarker*)pm->GetNext ();
1436 inherited::Coalece (pm);
1437 if (successor !=
nullptr) {
1438 successor->InvalidateCache ();
1442 if (newTopLine !=
nullptr) {
1443 fTextImager.SetTopRowInWindow_ (RowReference (newTopLine, useFirstRow ? 0 : fTextImager.GetPartitionElementCacheInfo (newTopLine).GetLastRow ()));
1445 AssertNotNull (fTextImager.fTopLinePartitionMarkerInWindow);
1446 AssertMember (fTextImager.fTopLinePartitionMarkerInWindow, MultiRowPartitionMarker);
1447 fTextImager.InvalidateTotalRowsInWindow ();
1451void MultiRowTextImager::PMInfoCacheMgr::DidCoalece (
void* infoRecord)
const noexcept
1453 PartitionMarker* pm =
reinterpret_cast<PartitionMarker*
> (infoRecord);
1454 using MAP_CACHE = map<Partition::PartitionMarker*, PartitionElementCacheInfo>;
1455 MAP_CACHE::iterator i = fPMCache.find (pm);
1456 if (i != fPMCache.end ()) {
1459 fImager.InvalidateTotalRowsInWindow ();
1462void MultiRowTextImager::PMInfoCacheMgr::MyMarkerDidUpdateCallback ()
1476 fImager.InvalidateTotalRowsInWindow ();
1484MultiRowTextImager::PMInfoCacheMgr::MyMarker::MyMarker (PMInfoCacheMgr& pmInfoCacheMgr)
1485 : fPMInfoCacheMgr (pmInfoCacheMgr)
1489void MultiRowTextImager::PMInfoCacheMgr::MyMarker::DidUpdateText (
const UpdateInfo& updateInfo)
noexcept
1491 inherited::DidUpdateText (updateInfo);
1492 fPMInfoCacheMgr.MyMarkerDidUpdateCallback ();
#define qStroika_Foundation_Debug_AssertionsChecked
The qStroika_Foundation_Debug_AssertionsChecked flag determines if assertions are checked and validat...
#define RequireNotNull(p)
#define AssertMember(p, c)
set< T > Intersection(const set< T > &s1, const set< T > &s2)
Logically halfway between std::array and std::vector; Smart 'direct memory array' - which when needed...