4#include "Stroika/Frameworks/StroikaPreComp.h"
11#include "Stroika/Frameworks/Led/GDI.h"
12#include "Stroika/Frameworks/Led/Support.h"
14#include "MultiRowTextImager.h"
18using Memory::MakeSharedPtr;
20using namespace Stroika::Frameworks::Led;
22#if qStroika_Frameworks_Led_SupportGDI
28MultiRowTextImager::~MultiRowTextImager ()
30 Assert (fTopLinePartitionMarkerInWindow ==
nullptr);
33void MultiRowTextImager::HookLosingTextStore ()
35 inherited::HookLosingTextStore ();
36 HookLosingTextStore_ ();
39void MultiRowTextImager::HookLosingTextStore_ ()
41 SetPartition (PartitionPtr ());
44void MultiRowTextImager::HookGainedNewTextStore ()
46 inherited::HookGainedNewTextStore ();
47 HookGainedNewTextStore_ ();
50void MultiRowTextImager::HookGainedNewTextStore_ ()
52 if (GetPartition ().get () ==
nullptr) {
53 SetPartition (MakeDefaultPartition ());
57void MultiRowTextImager::SetPartition (
const PartitionPtr& partitionPtr)
60 inherited::SetPartition (partitionPtr);
61 if (partitionPtr.get () ==
nullptr) {
62 InvalidateTotalRowsInWindow ();
63 fTopLinePartitionMarkerInWindow =
nullptr;
66 fPMCacheMgr = unique_ptr<PMInfoCacheMgr> (
new PMInfoCacheMgr (*
this));
67 fTopLinePartitionMarkerInWindow = GetFirstPartitionMarker ();
69 InvalidateTotalRowsInWindow ();
70 AssureWholeWindowUsedIfNeeded ();
71 InvalidateScrollBarParameters ();
75PartitioningTextImager::PartitionPtr MultiRowTextImager::MakeDefaultPartition ()
const
80MultiRowTextImager::PartitionElementCacheInfo MultiRowTextImager::GetPartitionElementCacheInfo (Partition::PartitionMarker* pm)
const
82 return fPMCacheMgr->GetPartitionElementCacheInfo (pm);
85MultiRowTextImager::PartitionElementCacheInfo MultiRowTextImager::GetPartitionElementCacheInfo (MultiRowTextImager::RowReference row)
const
87 return GetPartitionElementCacheInfo (row.GetPartitionMarker ());
90bool MultiRowTextImager::GetIthRowReferenceFromHere (RowReference* adjustMeInPlace, ptrdiff_t ith)
const
92 for (; ith > 0; --ith) {
93 if (not GetNextRowReference (adjustMeInPlace)) {
97 for (; ith < 0; ++ith) {
98 if (not GetPreviousRowReference (adjustMeInPlace)) {
105size_t MultiRowTextImager::GetRowNumber (RowReference rowRef)
const
110 size_t rowNumber = rowRef.GetSubRow ();
112 for (PartitionMarker* cur = rowRef.GetPartitionMarker ()->GetPrevious (); cur !=
nullptr; cur = cur->GetPrevious ()) {
113 rowNumber += GetPartitionElementCacheInfo (cur).GetRowCount ();
123size_t MultiRowTextImager::CountRowDifference (RowReference lhs, RowReference rhs)
const
132 PartitionMarker* lhsPM = lhs.GetPartitionMarker ();
133 PartitionMarker* rhsPM = rhs.GetPartitionMarker ();
134 size_t lhsMarkerStart = lhsPM->GetStart ();
135 size_t rhsMarkerStart = rhsPM->GetStart ();
136 bool leftSmaller = ((lhsMarkerStart < rhsMarkerStart) or ((lhsMarkerStart == rhsMarkerStart) and lhs.GetSubRow () <= rhs.GetSubRow ()));
137 RowReference firstRowRef = leftSmaller ? lhs : rhs;
138 RowReference lastRowRef = leftSmaller ? rhs : lhs;
140 size_t rowsGoneBy = 0;
141 for (RowReference cur = firstRowRef; cur != lastRowRef; ++rowsGoneBy) {
142 [[maybe_unused]]
bool result = GetIthRowReferenceFromHere (&cur, 1);
157size_t MultiRowTextImager::CountRowDifferenceLimited (RowReference lhs, RowReference rhs,
size_t limit)
const
166 PartitionMarker* lhsPM = lhs.GetPartitionMarker ();
167 PartitionMarker* rhsPM = rhs.GetPartitionMarker ();
168 size_t lhsMarkerStart = lhsPM->GetStart ();
169 size_t rhsMarkerStart = rhsPM->GetStart ();
170 bool leftSmaller = ((lhsMarkerStart < rhsMarkerStart) or ((lhsMarkerStart == rhsMarkerStart) and lhs.GetSubRow () <= rhs.GetSubRow ()));
171 RowReference firstRowRef = leftSmaller ? lhs : rhs;
172 RowReference lastRowRef = leftSmaller ? rhs : lhs;
174 size_t rowsGoneBy = 0;
175 for (RowReference cur = firstRowRef; cur != lastRowRef; ++rowsGoneBy) {
176 [[maybe_unused]]
bool result = GetIthRowReferenceFromHere (&cur, 1);
178 if (rowsGoneBy >= limit) {
185size_t MultiRowTextImager::GetTopRowInWindow ()
const
188 return (GetRowNumber (GetTopRowReferenceInWindow ()));
191size_t MultiRowTextImager::GetTotalRowsInWindow ()
const
193 return GetTotalRowsInWindow_ ();
196size_t MultiRowTextImager::GetLastRowInWindow ()
const
199 return (GetRowNumber (GetLastRowReferenceInWindow ()));
202void MultiRowTextImager::SetTopRowInWindow (
size_t newTopRow)
206 Assert (newTopRow <= GetRowCount ());
209 SetTopRowInWindow (GetIthRowReference (newTopRow));
211 Assert (GetTopRowInWindow () == newTopRow);
216void MultiRowTextImager::AssureWholeWindowUsedIfNeeded ()
218 SetTopRowInWindow (GetTopRowReferenceInWindow ());
225size_t MultiRowTextImager::GetMarkerPositionOfStartOfWindow ()
const
227 return (GetStartOfRow (GetTopRowReferenceInWindow ()));
234size_t MultiRowTextImager::GetMarkerPositionOfEndOfWindow ()
const
236 return GetEndOfRow (GetLastRowReferenceInWindow ());
239size_t MultiRowTextImager::GetMarkerPositionOfStartOfLastRowOfWindow ()
const
241 return GetStartOfRow (GetLastRowReferenceInWindow ());
244ptrdiff_t MultiRowTextImager::CalculateRowDeltaFromCharDeltaFromTopOfWindow (
long deltaChars)
const
246 Assert (
long (GetMarkerPositionOfStartOfWindow ()) >= 0 - deltaChars);
247 size_t pos = long (GetMarkerPositionOfStartOfWindow ()) + deltaChars;
248 RowReference targetRow = GetRowReferenceContainingPosition (pos);
249 size_t rowDiff = CountRowDifference (targetRow, GetTopRowReferenceInWindow ());
250 return (deltaChars >= 0) ? rowDiff : -long (rowDiff);
253ptrdiff_t MultiRowTextImager::CalculateCharDeltaFromRowDeltaFromTopOfWindow (ptrdiff_t deltaRows)
const
255 RowReference row = GetIthRowReferenceFromHere (GetTopRowReferenceInWindow (), deltaRows);
256 return (
long (GetStartOfRow (row)) -
long (GetMarkerPositionOfStartOfWindow ()));
259void MultiRowTextImager::ScrollByIfRoom (ptrdiff_t downByRows)
261 RowReference newTopRow = GetTopRowReferenceInWindow ();
262 (void)GetIthRowReferenceFromHere (&newTopRow, downByRows);
263 SetTopRowInWindow (newTopRow);
270void MultiRowTextImager::ScrollSoShowing (
size_t markerPos,
size_t andTryToShowMarkerPos)
272 Assert (markerPos <= GetLength ());
273 Assert (fTotalRowsInWindow == 0 or fTotalRowsInWindow == ComputeRowsThatWouldFitInWindowWithTopRow (GetTopRowReferenceInWindow ()));
275 if (andTryToShowMarkerPos == 0) {
276 andTryToShowMarkerPos = markerPos;
278 Assert (andTryToShowMarkerPos <= GetLength ());
286 size_t startOfWindow = GetMarkerPositionOfStartOfWindow ();
287 size_t endOfWindow = GetMarkerPositionOfEndOfWindow ();
288 if (markerPos >= startOfWindow and markerPos < endOfWindow and andTryToShowMarkerPos >= startOfWindow and andTryToShowMarkerPos < endOfWindow) {
289 ScrollSoShowingHHelper (markerPos, andTryToShowMarkerPos);
293 RowReference originalTop = GetTopRowReferenceInWindow ();
310 RowReference newTop = originalTop;
311 while (markerPos < newTop.GetPartitionMarker ()->GetStart ()) {
312 newTop = RowReference (newTop.GetPartitionMarker ()->GetPrevious (), 0);
316 if (not PositionWouldFitInWindowWithThisTopRow (markerPos, newTop)) {
317 while (markerPos > newTop.GetPartitionMarker ()->GetEnd ()) {
318 if (newTop.GetPartitionMarker ()->GetNext () ==
nullptr) {
322 newTop = RowReference (newTop.GetPartitionMarker ()->GetNext (), 0);
324 Assert (Contains (markerPos, markerPos, *newTop.GetPartitionMarker ()));
331 while (markerPos < GetStartOfRow (newTop) and GetPreviousRowReference (&newTop))
333 while (not PositionWouldFitInWindowWithThisTopRow (markerPos, newTop) and GetNextRowReference (&newTop))
337 Assert (markerPos >= GetStartOfRow (newTop));
338 Assert (PositionWouldFitInWindowWithThisTopRow (markerPos, newTop));
344 while (not PositionWouldFitInWindowWithThisTopRow (andTryToShowMarkerPos, newTop)) {
345 RowReference trailNewTop = newTop;
346 if (andTryToShowMarkerPos < GetStartOfRow (trailNewTop)) {
347 if (not GetPreviousRowReference (&trailNewTop)) {
352 if (not GetNextRowReference (&trailNewTop)) {
356 if (PositionWouldFitInWindowWithThisTopRow (markerPos, trailNewTop)) {
357 newTop = trailNewTop;
368 const unsigned kRowMoveThreshold = 1;
369 if (CountRowDifferenceLimited (originalTop, newTop, kRowMoveThreshold + 1) > kRowMoveThreshold) {
370 bool mustPreserveSecondPos = PositionWouldFitInWindowWithThisTopRow (andTryToShowMarkerPos, newTop);
374 size_t topMarkerPos = min (markerPos, andTryToShowMarkerPos);
375 size_t botMarkerPos = max (markerPos, andTryToShowMarkerPos);
376 size_t numRowsAbove = CountRowDifference (newTop, GetRowReferenceContainingPosition (topMarkerPos));
377 size_t rowsInWindow = ComputeRowsThatWouldFitInWindowWithTopRow (newTop);
378 RowReference lastRowInWindow = GetIthRowReferenceFromHere (newTop, rowsInWindow - 1);
379 size_t numRowsBelow = CountRowDifference (lastRowInWindow, GetRowReferenceContainingPosition (botMarkerPos));
381 size_t numRowsToSpare = numRowsAbove + numRowsBelow;
384 RowReference trailNewTop = newTop;
385 GetIthRowReferenceFromHere (&trailNewTop,
int (numRowsAbove) -
int (numRowsToSpare / 2));
386 if (PositionWouldFitInWindowWithThisTopRow (markerPos, trailNewTop) and
387 (not mustPreserveSecondPos or PositionWouldFitInWindowWithThisTopRow (andTryToShowMarkerPos, trailNewTop))) {
388 newTop = trailNewTop;
392 SetTopRowInWindow (newTop);
394 Assert (GetMarkerPositionOfStartOfWindow () <= markerPos and markerPos <= GetMarkerPositionOfEndOfWindow ());
399 ScrollSoShowingHHelper (markerPos, andTryToShowMarkerPos);
402void MultiRowTextImager::SetTopRowInWindow (RowReference row)
404 if (GetForceAllRowsShowing ()) {
405 row = AdjustPotentialTopRowReferenceSoWholeWindowUsed (row);
407 if (row != GetTopRowReferenceInWindow ()) {
408 SetTopRowInWindow_ (row);
409 InvalidateScrollBarParameters ();
424void MultiRowTextImager::Draw (
const Led_Rect& subsetToDraw,
bool printing)
428 Led_Rect rowsLeftToDrawRect = GetWindowRect ();
430 Tablet_Acquirer tablet_ (
this);
431 Tablet* tablet = tablet_;
439#if qStroika_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 ();
527void MultiRowTextImager::DrawPartitionElement (PartitionMarker* pm,
size_t startSubRow,
size_t maxSubRow, Tablet* tablet, OffscreenTablet* offscreenTablet,
528 bool printing,
const Led_Rect& subsetToDraw, Led_Rect* remainingDrawArea,
size_t* rowsDrawn)
534 size_t start = pm->GetStart ();
535 size_t end = pm->GetEnd ();
537 Assert (end <= GetLength () + 1);
538 if (end == GetLength () + 1) {
542 Tablet* savedTablet = tablet;
543 PartitionElementCacheInfo pmCacheInfo = GetPartitionElementCacheInfo (pm);
544 size_t endSubRow = min (pmCacheInfo.GetRowCount () - 1, maxSubRow);
547 size_t partLen = end - start;
549 CopyOut (start, partLen, partitionBuf.data ());
551 for (
size_t subRow = startSubRow; subRow <= endSubRow; ++subRow) {
552 Led_Rect currentRowRect = *remainingDrawArea;
553 currentRowRect.bottom = currentRowRect.top + pmCacheInfo.GetRowHeight (subRow);
554 DistanceType interlineSpace = (subRow == pmCacheInfo.GetLastRow ()) ? pmCacheInfo.GetInterLineSpace () : 0;
555 if ((currentRowRect.bottom + CoordinateType (interlineSpace) > subsetToDraw.top) and (currentRowRect.top < subsetToDraw.bottom)) {
560 size_t rowStart = start + pmCacheInfo.PeekAtRowStart (subRow);
562 if (subRow < pmCacheInfo.GetLastRow ()) {
563 rowEnd = pm->GetStart () + pmCacheInfo.PeekAtRowStart (subRow + 1);
566 if (subRow == pmCacheInfo.GetLastRow ()) {
567 Assert (pm->GetEnd () > 0);
568 size_t markerEnd = pm->GetEnd ();
569 Assert (markerEnd <= GetLength () + 1);
570 if (markerEnd == GetLength () + 1) {
571 rowEnd = GetLength ();
574 size_t prevToEnd = FindPreviousCharacter (markerEnd);
575 if (prevToEnd >= rowStart) {
577 CopyOut (prevToEnd, 1, &lastChar);
578 if (RemoveMappedDisplayCharacters (&lastChar, 1) == 0) {
579 rowEnd = (prevToEnd);
584 Assert (rowEnd == GetEndOfRowContainingPosition (rowStart));
588 TextLayoutBlock_Copy rowText = GetTextLayoutBlock (rowStart, rowEnd);
590 TextLayoutBlock_Basic rowText (partitionBuf + (rowStart - start), partitionBuf + (rowStart - start) + (rowEnd - rowStart));
593 if (offscreenTablet !=
nullptr) {
594 tablet = offscreenTablet->PrepareRect (currentRowRect, interlineSpace);
602 Led_Rect invalidRowRect =
Intersection (currentRowRect, subsetToDraw);
603 DrawRow (tablet, currentRowRect, invalidRowRect, rowText, rowStart, rowEnd, printing);
609 if (interlineSpace != 0) {
610 size_t hilightStart = GetSelectionStart ();
611 size_t hilightEnd = GetSelectionEnd ();
612 bool segmentHilightedAtEnd = GetSelectionShown () and (hilightStart < rowEnd) and (rowEnd <= hilightEnd);
613 if (pm->GetNext () ==
nullptr and subRow == pmCacheInfo.GetLastRow ()) {
614 segmentHilightedAtEnd =
false;
616 DrawInterLineSpace (interlineSpace, tablet, currentRowRect.bottom, segmentHilightedAtEnd, printing);
619 if (offscreenTablet !=
nullptr) {
623 offscreenTablet->BlastBitmapToOrigTablet ();
624 tablet = savedTablet;
628 remainingDrawArea->top = currentRowRect.bottom + interlineSpace;
633Led_Rect MultiRowTextImager::GetCharLocation (
size_t afterPosition)
const
635 return (GetCharLocationRowRelative (afterPosition, RowReference (GetFirstPartitionMarker (), 0)));
638Led_Rect MultiRowTextImager::GetCharWindowLocation (
size_t afterPosition)
const
640 Led_Point windowOrigin = GetWindowRect ().GetOrigin () - Led_Point (0, GetHScrollPos ());
641 return (windowOrigin + GetCharLocationRowRelative (afterPosition, GetTopRowReferenceInWindow (), GetTotalRowsInWindow_ ()));
644size_t MultiRowTextImager::GetCharAtLocation (
const Led_Point& where)
const
646 return (GetCharAtLocationRowRelative (where, RowReference (GetFirstPartitionMarker (), 0)));
649size_t MultiRowTextImager::GetCharAtWindowLocation (
const Led_Point& where)
const
651 Led_Point windowOrigin = GetWindowRect ().GetOrigin () - Led_Point (0, GetHScrollPos ());
652 return (GetCharAtLocationRowRelative (where - windowOrigin, GetTopRowReferenceInWindow (), GetTotalRowsInWindow_ ()));
655size_t MultiRowTextImager::GetStartOfRow (
size_t rowNumber)
const
659 return (GetStartOfRow (GetIthRowReference (rowNumber)));
662size_t MultiRowTextImager::GetStartOfRowContainingPosition (
size_t charPosition)
const
664 return (GetStartOfRow (GetRowReferenceContainingPosition (charPosition)));
667size_t MultiRowTextImager::GetEndOfRow (
size_t rowNumber)
const
671 return (GetEndOfRow (GetIthRowReference (rowNumber)));
674size_t MultiRowTextImager::GetEndOfRowContainingPosition (
size_t charPosition)
const
676 return (GetEndOfRow (GetRowReferenceContainingPosition (charPosition)));
679size_t MultiRowTextImager::GetRealEndOfRow (
size_t rowNumber)
const
683 return (GetRealEndOfRow (GetIthRowReference (rowNumber)));
686size_t MultiRowTextImager::GetRealEndOfRowContainingPosition (
size_t charPosition)
const
688 return (GetRealEndOfRow (GetRowReferenceContainingPosition (charPosition)));
691size_t MultiRowTextImager::GetStartOfRow (RowReference row)
const
693 PartitionMarker* cur = row.GetPartitionMarker ();
694 size_t subRow = row.GetSubRow ();
696 return (cur->GetStart () + (subRow == 0 ? 0 : GetPartitionElementCacheInfo (cur).GetLineRelativeRowStartPosition (subRow)));
699size_t MultiRowTextImager::GetEndOfRow (RowReference row)
const
701 PartitionMarker* cur = row.GetPartitionMarker ();
702 size_t subRow = row.GetSubRow ();
704 PartitionElementCacheInfo pmCacheInfo = GetPartitionElementCacheInfo (cur);
705 if (subRow == pmCacheInfo.GetLastRow ()) {
709 Assert (cur->GetEnd () > 0);
711 size_t markerEnd = cur->GetEnd ();
712 Assert (markerEnd <= GetLength () + 1);
713 if (markerEnd == GetLength () + 1) {
714 return (GetLength ());
717 size_t prevToEnd = FindPreviousCharacter (markerEnd);
718 if (prevToEnd >= GetStartOfRow (row)) {
720 CopyOut (prevToEnd, 1, &lastChar);
721 if (RemoveMappedDisplayCharacters (&lastChar, 1) == 0) {
728 return (cur->GetStart () + pmCacheInfo.GetLineRelativeRowStartPosition (subRow + 1));
732size_t MultiRowTextImager::GetRealEndOfRow (RowReference row)
const
734 PartitionMarker* cur = row.GetPartitionMarker ();
735 size_t subRow = row.GetSubRow ();
737 PartitionElementCacheInfo pmCacheInfo = GetPartitionElementCacheInfo (cur);
738 if (subRow == pmCacheInfo.GetLastRow ()) {
739 Assert (cur->GetEnd () > 0);
740 size_t markerEnd = cur->GetEnd ();
744 return (cur->GetStart () + pmCacheInfo.GetLineRelativeRowStartPosition (subRow + 1));
748MultiRowTextImager::RowReference MultiRowTextImager::GetRowReferenceContainingPosition (
size_t charPosition)
const
750 Require (charPosition <= GetEnd ());
751 PartitionMarker* pm = GetPartitionMarkerContainingPosition (charPosition);
754 size_t pmStart = pm->GetStart ();
755 if (charPosition == pmStart) {
756 return (RowReference (pm, 0));
760 PartitionElementCacheInfo pmCacheInfo = GetPartitionElementCacheInfo (pm);
761 return (RowReference (pm, pmCacheInfo.LineRelativePositionInWhichRow (charPosition - pmStart)));
764size_t MultiRowTextImager::GetRowContainingPosition (
size_t charPosition)
const
768 return (GetRowNumber (GetRowReferenceContainingPosition (charPosition)));
771size_t MultiRowTextImager::GetRowCount ()
const
775 for (PartitionMarker* cur = GetFirstPartitionMarker (); cur !=
nullptr; cur = cur->GetNext ()) {
777 Assert (GetPartitionElementCacheInfo (cur).GetRowCount () >= 1);
778 rowCount += GetPartitionElementCacheInfo (cur).GetRowCount ();
783Led_Rect MultiRowTextImager::GetCharLocationRowRelativeByPosition (
size_t afterPosition,
size_t positionOfTopRow,
size_t maxRowsToCheck)
const
785 return GetCharLocationRowRelative (afterPosition, GetRowReferenceContainingPosition (positionOfTopRow), maxRowsToCheck);
788DistanceType MultiRowTextImager::GetRowHeight (
size_t rowNumber)
const
792 return (GetRowHeight (GetIthRowReference (rowNumber)));
799DistanceType MultiRowTextImager::GetRowRelativeBaselineOfRowContainingPosition (
size_t charPosition)
const
801 RowReference thisRow = GetRowReferenceContainingPosition (charPosition);
802 size_t startOfRow = GetStartOfRow (thisRow);
803 size_t endOfRow = GetEndOfRow (thisRow);
804 return MeasureSegmentBaseLine (startOfRow, endOfRow);
807void MultiRowTextImager::GetStableTypingRegionContaingMarkerRange (
size_t fromMarkerPos,
size_t toMarkerPos,
size_t* expandedFromMarkerPos,
808 size_t* expandedToMarkerPos)
const
812 Assert (fromMarkerPos <= toMarkerPos);
813 Assert (toMarkerPos <= GetEnd ());
815 size_t curTopRowRelativeRowNumber = 0;
817 RowReference curRow = GetTopRowReferenceInWindow ();
819 PartitionMarker* cur = curRow.GetPartitionMarker ();
821 size_t start = cur->GetStart ();
822 size_t end = cur->GetEnd ();
827 if (cur->GetNext () ==
nullptr) {
832 if (curTopRowRelativeRowNumber == 0 and (fromMarkerPos < start)) {
836 ++curTopRowRelativeRowNumber;
838 if (Contains (*cur, fromMarkerPos) and Contains (*cur, toMarkerPos)) {
839 (*expandedFromMarkerPos) = start;
840 (*expandedToMarkerPos) = end;
841 Assert ((*expandedFromMarkerPos) <= (*expandedToMarkerPos));
842 Assert ((*expandedToMarkerPos) <= GetEnd ());
846 if (curTopRowRelativeRowNumber >= GetTotalRowsInWindow_ ()) {
852 }
while (GetNextRowReference (&curRow));
854 (*expandedFromMarkerPos) = 0;
855 (*expandedToMarkerPos) = GetEnd ();
858DistanceType MultiRowTextImager::GetHeightOfRows (
size_t startingRow,
size_t rowCount)
const
860 return (GetHeightOfRows (GetIthRowReference (startingRow), rowCount));
863DistanceType MultiRowTextImager::GetHeightOfRows (RowReference startingRow,
size_t rowCount)
const
865 DistanceType height = 0;
866 for (RowReference curRow = startingRow; rowCount > 0; rowCount--) {
867 PartitionMarker* curPM = curRow.GetPartitionMarker ();
868 PartitionElementCacheInfo pmCacheInfo = GetPartitionElementCacheInfo (curPM);
869 height += pmCacheInfo.GetRowHeight (curRow.GetSubRow ());
870 if (curRow.GetSubRow () == pmCacheInfo.GetLastRow ()) {
871 height += pmCacheInfo.GetInterLineSpace ();
873 (void)GetNextRowReference (&curRow);
878void MultiRowTextImager::DidUpdateText (
const UpdateInfo& updateInfo)
noexcept
892 InvalidateTotalRowsInWindow ();
895 InvalidateTotalRowsInWindow ();
896 inherited::DidUpdateText (updateInfo);
899 AssureWholeWindowUsedIfNeeded ();
900 InvalidateScrollBarParameters ();
903void MultiRowTextImager::SetWindowRect (
const Led_Rect& windowRect)
914 bool heightChanged = GetWindowRect ().GetHeight () != windowRect.GetHeight ();
915 inherited::SetWindowRect (windowRect);
916 if (heightChanged and PeekAtTextStore () !=
nullptr) {
917 InvalidateTotalRowsInWindow ();
918 AssureWholeWindowUsedIfNeeded ();
919 InvalidateScrollBarParameters ();
929void MultiRowTextImager::InvalidateAllCaches ()
931 inherited::InvalidateAllCaches ();
932 if (GetPartition ().get () !=
nullptr) {
933 if (fPMCacheMgr.get () !=
nullptr) {
934 fPMCacheMgr->ClearCache ();
936 InvalidateTotalRowsInWindow ();
938 AssureWholeWindowUsedIfNeeded ();
939 InvalidateScrollBarParameters ();
943MultiRowTextImager::RowReference MultiRowTextImager::AdjustPotentialTopRowReferenceSoWholeWindowUsed (
const RowReference& potentialTopRow)
950 if (potentialTopRow.GetSubRow () == 0 and potentialTopRow.GetPartitionMarker ()->GetPrevious () ==
nullptr) {
951 return potentialTopRow;
954 CoordinateType windowHeight = GetWindowRect ().GetHeight ();
955 CoordinateType heightUsed = 0;
957 for (RowReference curRow = potentialTopRow;;) {
958 PartitionMarker* curPM = curRow.GetPartitionMarker ();
959 PartitionElementCacheInfo pmCacheInfo = GetPartitionElementCacheInfo (curPM);
960 heightUsed += pmCacheInfo.GetRowHeight (curRow.GetSubRow ());
961 if (curRow.GetSubRow () == pmCacheInfo.GetLastRow ()) {
962 heightUsed += pmCacheInfo.GetInterLineSpace ();
964 if (heightUsed >= windowHeight) {
965 return (potentialTopRow);
967 if (not GetNextRowReference (&curRow)) {
974 for (RowReference curRow = potentialTopRow;;) {
975 if (not GetPreviousRowReference (&curRow)) {
980 PartitionMarker* curPM = curRow.GetPartitionMarker ();
981 PartitionElementCacheInfo pmCacheInfo = GetPartitionElementCacheInfo (curPM);
982 heightUsed += pmCacheInfo.GetRowHeight (curRow.GetSubRow ());
983 if (curRow.GetSubRow () == pmCacheInfo.GetLastRow ()) {
984 heightUsed += pmCacheInfo.GetInterLineSpace ();
986 if (heightUsed > windowHeight) {
988 [[maybe_unused]]
bool result = GetNextRowReference (&curRow);
992 else if (heightUsed == windowHeight) {
997 return (potentialTopRow);
1000bool MultiRowTextImager::PositionWouldFitInWindowWithThisTopRow (
size_t markerPos,
const RowReference& newTopRow)
1002 if (markerPos < GetStartOfRow (newTopRow)) {
1006 size_t rowCount = ComputeRowsThatWouldFitInWindowWithTopRow (newTopRow);
1007 RowReference lastRow = GetIthRowReferenceFromHere (newTopRow, rowCount - 1);
1009 return (markerPos < GetRealEndOfRow (lastRow));
1012void MultiRowTextImager::ReValidateSubRowInTopLineInWindow ()
1017 if (fSubRowInTopLineInWindow != 0) {
1019 size_t lastRow = GetPartitionElementCacheInfo (fTopLinePartitionMarkerInWindow).GetLastRow ();
1020 if (fSubRowInTopLineInWindow > lastRow) {
1021 fSubRowInTopLineInWindow = lastRow;
1024 bool pmNotWrapped = (fTopLinePartitionMarkerInWindow->fPixelHeightCache == DistanceType (-1));
1025 size_t lastRow = GetPartitionElementCacheInfo (fTopLinePartitionMarkerInWindow).GetLastRow ();
1026 if (fSubRowInTopLineInWindow > lastRow) {
1027 fSubRowInTopLineInWindow = lastRow;
1036 fTopLinePartitionMarkerInWindow->InvalidateCache ();
1042size_t MultiRowTextImager::ComputeRowsThatWouldFitInWindowWithTopRow (
const RowReference& newTopRow)
const
1048 CoordinateType windowHeight = GetWindowRect ().GetHeight ();
1054 size_t rowCount = 0;
1055 CoordinateType heightUsed = 0;
1056 for (RowReference curRow = newTopRow;;) {
1058 PartitionMarker* curPM = curRow.GetPartitionMarker ();
1059 PartitionElementCacheInfo pmCacheInfo = GetPartitionElementCacheInfo (curPM);
1060 heightUsed += pmCacheInfo.GetRowHeight (curRow.GetSubRow ());
1061 if (curRow.GetSubRow () == pmCacheInfo.GetLastRow ()) {
1062 heightUsed += pmCacheInfo.GetInterLineSpace ();
1064 if (heightUsed > windowHeight) {
1069 else if (heightUsed == windowHeight) {
1073 if (not GetNextRowReference (&curRow)) {
1077 if (rowCount == 0) {
1090Led_Rect MultiRowTextImager::GetCharLocationRowRelative (
size_t afterPosition, RowReference topRow,
size_t maxRowsToCheck)
const
1093 const Led_Rect kMagicBeforeRect = Led_Rect (-10000, 0, 0, 0);
1094 const Led_Rect kMagicAfterRect = Led_Rect (10000, 0, 0, 0);
1096 Require (afterPosition <= GetEnd ());
1098 if (afterPosition < GetStartOfRow (topRow)) {
1099 return (kMagicBeforeRect);
1102 RowReference curRow = topRow;
1103 size_t curTopRowRelativeRowNumber = 0;
1104 CoordinateType topVPos = 0;
1106 PartitionMarker* cur = curRow.GetPartitionMarker ();
1107 size_t subRow = curRow.GetSubRow ();
1109 size_t start = cur->GetStart ();
1110 size_t end = cur->GetEnd ();
1112 Assert (end <= GetEnd () + 1);
1114 PartitionElementCacheInfo pmCacheInfo = GetPartitionElementCacheInfo (cur);
1119 start += pmCacheInfo.PeekAtRowStart (subRow);
1120 if (subRow < pmCacheInfo.GetLastRow ()) {
1121 end = cur->GetStart () + pmCacheInfo.PeekAtRowStart (subRow + 1);
1122 Assert (start <= end);
1125 ++curTopRowRelativeRowNumber;
1130 if (afterPosition >= start and afterPosition < end) {
1131 Assert (start <= afterPosition);
1132 DistanceType hStart = 0;
1133 DistanceType hEnd = 0;
1134 GetRowRelativeCharLoc (afterPosition, &hStart, &hEnd);
1135 Assert (hStart <= hEnd);
1136 return (Led_Rect (topVPos, hStart, pmCacheInfo.GetRowHeight (subRow), hEnd - hStart));
1139 topVPos += pmCacheInfo.GetRowHeight (subRow);
1141 if (pmCacheInfo.GetLastRow () == subRow) {
1142 topVPos += pmCacheInfo.GetInterLineSpace ();
1145 if (curTopRowRelativeRowNumber >= maxRowsToCheck) {
1148 }
while (GetNextRowReference (&curRow));
1150 return (kMagicAfterRect);
1153size_t MultiRowTextImager::GetCharAtLocationRowRelative (
const Led_Point& where, RowReference topRow,
size_t maxRowsToCheck)
const
1165 RowReference curRow = topRow;
1166 size_t curTopRowRelativeRowNumber = 0;
1167 CoordinateType topVPos = 0;
1169 PartitionMarker* cur = curRow.GetPartitionMarker ();
1170 size_t subRow = curRow.GetSubRow ();
1172 size_t start = cur->GetStart ();
1174 PartitionElementCacheInfo pmCacheInfo = GetPartitionElementCacheInfo (cur);
1179 start += pmCacheInfo.PeekAtRowStart (subRow);
1184 DistanceType interLineSpaceIfAny = (pmCacheInfo.GetLastRow () == subRow) ? pmCacheInfo.GetInterLineSpace () : 0;
1186 curTopRowRelativeRowNumber++;
1187 if (where.v >= topVPos and where.v < topVPos + CoordinateType (pmCacheInfo.GetRowHeight (subRow) + interLineSpaceIfAny)) {
1188 return GetRowRelativeCharAtLoc (where.h, start);
1191 if (curTopRowRelativeRowNumber >= maxRowsToCheck) {
1195 topVPos += pmCacheInfo.GetRowHeight (subRow) + interLineSpaceIfAny;
1196 }
while (GetNextRowReference (&curRow));
1201DistanceType MultiRowTextImager::CalculateInterLineSpace (
const PartitionMarker* )
const
1211bool MultiRowTextImager::ContainsMappedDisplayCharacters (
const Led_tChar* text,
size_t nTChars)
const
1213 return ContainsMappedDisplayCharacters_HelperForChar (text, nTChars,
'\n') or inherited::ContainsMappedDisplayCharacters (text, nTChars);
1220size_t MultiRowTextImager::RemoveMappedDisplayCharacters (Led_tChar* copyText,
size_t nTChars)
const
1222 size_t newLen = inherited::RemoveMappedDisplayCharacters (copyText, nTChars);
1223 Assert (newLen <= nTChars);
1224 size_t newerLen = RemoveMappedDisplayCharacters_HelperForChar (copyText, newLen,
'\n');
1225 Assert (newerLen <= newLen);
1226 Assert (newerLen <= nTChars);
1235void MultiRowTextImager::PartitionElementCacheInfo::Clear ()
1237 fRep = MakeSharedPtr<Rep> ();
1240void MultiRowTextImager::PartitionElementCacheInfo::IncrementRowCountAndFixCacheBuffers (
size_t newStart, DistanceType newRowsHeight)
1242 ++fRep->fRowCountCache;
1245 if (fRep->fRowCountCache > kPackRowStartCount + 1) {
1246 RowStart_* newRowStartArray =
new RowStart_[fRep->fRowCountCache - 1];
1248 if (fRep->fRowCountCache == kPackRowStartCount + 1 + 1) {
1249 ::memcpy (newRowStartArray, &fRep->fRowStartArray, sizeof (fRep->fRowStartArray));
1252 Assert (fRep->fRowCountCache > 2);
1253 if (fRep->fRowCountCache > kPackRowStartCount + 1 + 1) {
1255 (void)memcpy (newRowStartArray, fRep->fRowStartArray, sizeof (newRowStartArray[1]) * (fRep->fRowCountCache - 2));
1256 delete[] fRep->fRowStartArray;
1259 fRep->fRowStartArray = newRowStartArray;
1263 if (fRep->fRowCountCache > kPackRowHeightCount) {
1264 RowHeight_* newRowHeightArray =
new RowHeight_[fRep->fRowCountCache];
1266 if (fRep->fRowCountCache == kPackRowHeightCount + 1) {
1267 ::memcpy (newRowHeightArray, &fRep->fRowHeightArray, sizeof (fRep->fRowHeightArray));
1270 Assert (fRep->fRowCountCache > 1);
1271 if (fRep->fRowCountCache > kPackRowHeightCount + 1) {
1273 memcpy (newRowHeightArray, fRep->fRowHeightArray, sizeof (newRowHeightArray[1]) * (fRep->fRowCountCache - 1));
1274 delete[] fRep->fRowHeightArray;
1277 fRep->fRowHeightArray = newRowHeightArray;
1280 SetRowStart (fRep->fRowCountCache - 1, newStart);
1281 SetRowHeight (fRep->fRowCountCache - 1, newRowsHeight);
1282 fRep->fPixelHeightCache += newRowsHeight;
1290MultiRowTextImager::PMInfoCacheMgr::PMInfoCacheMgr (MultiRowTextImager& imager)
1292 , fCurFillCachePM (nullptr)
1293 , fCurFillCacheInfo ()
1299 PartitionPtr part = imager.GetPartition ();
1300 Assert (part.get () !=
nullptr);
1301 part->AddPartitionWatcher (
this);
1302 fMyMarker = unique_ptr<MyMarker> (
new MyMarker (*
this));
1303 TextStore& ts = part->GetTextStore ();
1304 ts.AddMarker (fMyMarker.get (), 0, ts.GetLength () + 1, part.get ());
1307MultiRowTextImager::PMInfoCacheMgr::~PMInfoCacheMgr ()
1309 PartitionPtr part = fImager.GetPartition ();
1310 part->RemovePartitionWatcher (
this);
1311 TextStore& ts = part->GetTextStore ();
1312 ts.RemoveMarker (fMyMarker.get ());
1315MultiRowTextImager::PartitionElementCacheInfo MultiRowTextImager::PMInfoCacheMgr::GetPartitionElementCacheInfo (Partition::PartitionMarker* pm)
const
1317 if (pm == fCurFillCachePM) {
1318 return fCurFillCacheInfo;
1320 using MAP_CACHE = map<Partition::PartitionMarker*, PartitionElementCacheInfo>;
1321 MAP_CACHE::iterator i = fPMCache.find (pm);
1322 if (i == fPMCache.end ()) {
1324 Assert (fCurFillCachePM ==
nullptr);
1325 fCurFillCachePM = pm;
1326 fImager.FillCache (pm, fCurFillCacheInfo);
1328 for (
size_t t = 0; t < fCurFillCacheInfo.GetRowCount (); ++t) {
1329 Assert (fCurFillCacheInfo.GetLineRelativeRowStartPosition (t) <= pm->GetLength ());
1330 Assert (fCurFillCacheInfo.PeekAtRowStart (t) <= pm->GetLength ());
1333 i = fPMCache.insert (MAP_CACHE::value_type (pm, fCurFillCacheInfo)).first;
1335 Assert (fCurFillCacheInfo.GetRowCount () == i->second.GetRowCount ());
1336 for (
size_t t = 0; t < fCurFillCacheInfo.GetRowCount (); ++t) {
1337 Assert (fCurFillCacheInfo.PeekAtRowHeight (t) == i->second.PeekAtRowHeight (t));
1338 Assert (fCurFillCacheInfo.PeekAtRowStart (t) == i->second.PeekAtRowStart (t));
1341 Assert (fCurFillCachePM == pm);
1342 fCurFillCachePM =
nullptr;
1345 Assert (fCurFillCachePM == pm);
1346 fCurFillCachePM =
nullptr;
1353void MultiRowTextImager::PMInfoCacheMgr::ClearCache ()
1358void MultiRowTextImager::PMInfoCacheMgr::AboutToSplit (PartitionMarker* pm,
size_t ,
void** infoRecord)
const noexcept
1363void MultiRowTextImager::PMInfoCacheMgr::DidSplit (
void* infoRecord)
const noexcept
1365 PartitionMarker* pm =
reinterpret_cast<PartitionMarker*
> (infoRecord);
1366 using MAP_CACHE = map<Partition::PartitionMarker*, PartitionElementCacheInfo>;
1367 MAP_CACHE::iterator i = fPMCache.find (pm);
1368 if (i != fPMCache.end ()) {
1371 fImager.InvalidateTotalRowsInWindow ();
1373 if (pm == fImager.fTopLinePartitionMarkerInWindow) {
1375 fImager.ReValidateSubRowInTopLineInWindow ();
1380void MultiRowTextImager::PMInfoCacheMgr::AboutToCoalece (PartitionMarker* pm,
void** infoRecord)
const noexcept
1384 PartitionMarker* newTopLine =
nullptr;
1385 bool useFirstRow =
false;
1386 if (pm == fImager.fTopLinePartitionMarkerInWindow) {
1387 if (pm->GetNext () ==
nullptr) {
1388 newTopLine = fImager.fTopLinePartitionMarkerInWindow->GetPrevious ();
1389 useFirstRow =
false;
1392 newTopLine = fImager.fTopLinePartitionMarkerInWindow->GetNext ();
1398 if (newTopLine !=
nullptr) {
1403 fImager.SetTopRowInWindow_ (RowReference (newTopLine, 0));
1407 MultiRowPartitionMarker* newTopLine =
nullptr;
1408 bool useFirstRow =
false;
1409 if (pm == fTextImager.fTopLinePartitionMarkerInWindow) {
1410 if (pm->GetNext () ==
nullptr) {
1411 newTopLine = fTextImager.fTopLinePartitionMarkerInWindow->GetPreviousMRPM ();
1412 useFirstRow =
false;
1415 newTopLine = fTextImager.fTopLinePartitionMarkerInWindow->GetNextMRPM ();
1421 MultiRowPartitionMarker* successor = (MultiRowPartitionMarker*)pm->GetNext ();
1422 inherited::Coalece (pm);
1423 if (successor !=
nullptr) {
1424 successor->InvalidateCache ();
1428 if (newTopLine !=
nullptr) {
1429 fTextImager.SetTopRowInWindow_ (RowReference (newTopLine, useFirstRow ? 0 : fTextImager.GetPartitionElementCacheInfo (newTopLine).GetLastRow ()));
1431 AssertNotNull (fTextImager.fTopLinePartitionMarkerInWindow);
1432 AssertMember (fTextImager.fTopLinePartitionMarkerInWindow, MultiRowPartitionMarker);
1433 fTextImager.InvalidateTotalRowsInWindow ();
1437void MultiRowTextImager::PMInfoCacheMgr::DidCoalece (
void* infoRecord)
const noexcept
1439 PartitionMarker* pm =
reinterpret_cast<PartitionMarker*
> (infoRecord);
1440 using MAP_CACHE = map<Partition::PartitionMarker*, PartitionElementCacheInfo>;
1441 MAP_CACHE::iterator i = fPMCache.find (pm);
1442 if (i != fPMCache.end ()) {
1445 fImager.InvalidateTotalRowsInWindow ();
1448void MultiRowTextImager::PMInfoCacheMgr::MyMarkerDidUpdateCallback ()
1462 fImager.InvalidateTotalRowsInWindow ();
1470MultiRowTextImager::PMInfoCacheMgr::MyMarker::MyMarker (PMInfoCacheMgr& pmInfoCacheMgr)
1471 : fPMInfoCacheMgr (pmInfoCacheMgr)
1475void MultiRowTextImager::PMInfoCacheMgr::MyMarker::DidUpdateText (
const UpdateInfo& updateInfo)
noexcept
1477 inherited::DidUpdateText (updateInfo);
1478 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...