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_Foundation_Common_Platform_MacOS
441 RGBColor oldForeColor = GDI_GetForeColor ();
442 RGBColor oldBackColor = GDI_GetBackColor ();
443#elif qStroika_Foundation_Common_Platform_Windows
444 GDI_Obj_Selector pen (tablet, ::GetStockObject (NULL_PEN));
445 GDI_Obj_Selector brush (tablet, ::GetStockObject (NULL_BRUSH));
452 OffscreenTablet thisIsOurNewOST;
453 if (GetImageUsingOffscreenBitmaps () and not printing) {
454 thisIsOurNewOST.Setup (tablet_);
459 size_t totalRowsInWindow = GetTotalRowsInWindow_ ();
460 RowReference topRowInWindow = GetTopRowReferenceInWindow ();
461 size_t rowsLeftInWindow = totalRowsInWindow;
462 for (PartitionMarker* pm = topRowInWindow.GetPartitionMarker (); rowsLeftInWindow != 0; pm = pm->GetNext ()) {
463 Assert (pm !=
nullptr);
464 size_t startSubRow = 0;
465 size_t maxSubRow =
static_cast<size_t> (-1);
466 if (pm == topRowInWindow.GetPartitionMarker ()) {
467 startSubRow = topRowInWindow.GetSubRow ();
469 maxSubRow = rowsLeftInWindow - 1 + startSubRow;
470 size_t rowsDrawn = 0;
471 DrawPartitionElement (pm, startSubRow, maxSubRow, tablet, (GetImageUsingOffscreenBitmaps () and not printing) ? &thisIsOurNewOST : nullptr,
472 printing, subsetToDraw, &rowsLeftToDrawRect, &rowsDrawn);
473 Assert (rowsLeftInWindow >= rowsDrawn);
474 rowsLeftInWindow -= rowsDrawn;
480 Assert (tablet == tablet_);
482 Led_Rect eraser = GetWindowRect ();
483 eraser.top = rowsLeftToDrawRect.top;
484 eraser.bottom = subsetToDraw.bottom;
486 if (eraser.top > eraser.bottom) {
487 eraser.bottom = eraser.top;
494 if (((eraser.top >= subsetToDraw.top and eraser.top <= subsetToDraw.bottom) or
495 (eraser.bottom >= subsetToDraw.top and eraser.bottom <= subsetToDraw.bottom)) and
496 (eraser.GetHeight () > 0 and eraser.GetWidth () > 0)) {
497 if (GetImageUsingOffscreenBitmaps () and not printing) {
498 tablet = thisIsOurNewOST.PrepareRect (eraser);
500 EraseBackground (tablet, eraser, printing);
504 size_t hilightStart = GetSelectionStart ();
505 size_t hilightEnd = GetSelectionEnd ();
506 size_t end = GetMarkerPositionOfEndOfWindow ();
507 bool segmentHilightedAtEnd = GetSelectionShown () and (hilightStart < end) and (end <= hilightEnd);
508 if (not printing and segmentHilightedAtEnd) {
509 HilightARectangle (tablet, eraser);
512 if (GetImageUsingOffscreenBitmaps () and not printing) {
516 thisIsOurNewOST.BlastBitmapToOrigTablet ();
523#if qStroika_Foundation_Common_Platform_MacOS
528 Assert (*tablet == Led_GetCurrentGDIPort ());
529 GDI_RGBForeColor (oldForeColor);
530 GDI_RGBBackColor (oldBackColor);
534#if qStroika_Foundation_Common_Platform_MacOS
535 Assert (*tablet == Led_GetCurrentGDIPort ());
536 GDI_RGBForeColor (oldForeColor);
537 GDI_RGBBackColor (oldBackColor);
545void MultiRowTextImager::DrawPartitionElement (PartitionMarker* pm,
size_t startSubRow,
size_t maxSubRow, Tablet* tablet, OffscreenTablet* offscreenTablet,
546 bool printing,
const Led_Rect& subsetToDraw, Led_Rect* remainingDrawArea,
size_t* rowsDrawn)
552 size_t start = pm->GetStart ();
553 size_t end = pm->GetEnd ();
555 Assert (end <= GetLength () + 1);
556 if (end == GetLength () + 1) {
560 Tablet* savedTablet = tablet;
561 PartitionElementCacheInfo pmCacheInfo = GetPartitionElementCacheInfo (pm);
562 size_t endSubRow = min (pmCacheInfo.GetRowCount () - 1, maxSubRow);
565 size_t partLen = end - start;
567 CopyOut (start, partLen, partitionBuf.data ());
569 for (
size_t subRow = startSubRow; subRow <= endSubRow; ++subRow) {
570 Led_Rect currentRowRect = *remainingDrawArea;
571 currentRowRect.bottom = currentRowRect.top + pmCacheInfo.GetRowHeight (subRow);
572 DistanceType interlineSpace = (subRow == pmCacheInfo.GetLastRow ()) ? pmCacheInfo.GetInterLineSpace () : 0;
573 if ((currentRowRect.bottom + CoordinateType (interlineSpace) > subsetToDraw.top) and (currentRowRect.top < subsetToDraw.bottom)) {
578 size_t rowStart = start + pmCacheInfo.PeekAtRowStart (subRow);
580 if (subRow < pmCacheInfo.GetLastRow ()) {
581 rowEnd = pm->GetStart () + pmCacheInfo.PeekAtRowStart (subRow + 1);
584 if (subRow == pmCacheInfo.GetLastRow ()) {
585 Assert (pm->GetEnd () > 0);
586 size_t markerEnd = pm->GetEnd ();
587 Assert (markerEnd <= GetLength () + 1);
588 if (markerEnd == GetLength () + 1) {
589 rowEnd = GetLength ();
592 size_t prevToEnd = FindPreviousCharacter (markerEnd);
593 if (prevToEnd >= rowStart) {
595 CopyOut (prevToEnd, 1, &lastChar);
596 if (RemoveMappedDisplayCharacters (&lastChar, 1) == 0) {
597 rowEnd = (prevToEnd);
602 Assert (rowEnd == GetEndOfRowContainingPosition (rowStart));
606 TextLayoutBlock_Copy rowText = GetTextLayoutBlock (rowStart, rowEnd);
608 TextLayoutBlock_Basic rowText (partitionBuf + (rowStart - start), partitionBuf + (rowStart - start) + (rowEnd - rowStart));
611 if (offscreenTablet !=
nullptr) {
612 tablet = offscreenTablet->PrepareRect (currentRowRect, interlineSpace);
620 Led_Rect invalidRowRect =
Intersection (currentRowRect, subsetToDraw);
621 DrawRow (tablet, currentRowRect, invalidRowRect, rowText, rowStart, rowEnd, printing);
627 if (interlineSpace != 0) {
628 size_t hilightStart = GetSelectionStart ();
629 size_t hilightEnd = GetSelectionEnd ();
630 bool segmentHilightedAtEnd = GetSelectionShown () and (hilightStart < rowEnd) and (rowEnd <= hilightEnd);
631 if (pm->GetNext () ==
nullptr and subRow == pmCacheInfo.GetLastRow ()) {
632 segmentHilightedAtEnd =
false;
634 DrawInterLineSpace (interlineSpace, tablet, currentRowRect.bottom, segmentHilightedAtEnd, printing);
637 if (offscreenTablet !=
nullptr) {
641 offscreenTablet->BlastBitmapToOrigTablet ();
642 tablet = savedTablet;
646 remainingDrawArea->top = currentRowRect.bottom + interlineSpace;
651Led_Rect MultiRowTextImager::GetCharLocation (
size_t afterPosition)
const
653 return (GetCharLocationRowRelative (afterPosition, RowReference (GetFirstPartitionMarker (), 0)));
656Led_Rect MultiRowTextImager::GetCharWindowLocation (
size_t afterPosition)
const
658 Led_Point windowOrigin = GetWindowRect ().GetOrigin () - Led_Point (0, GetHScrollPos ());
659 return (windowOrigin + GetCharLocationRowRelative (afterPosition, GetTopRowReferenceInWindow (), GetTotalRowsInWindow_ ()));
662size_t MultiRowTextImager::GetCharAtLocation (
const Led_Point& where)
const
664 return (GetCharAtLocationRowRelative (where, RowReference (GetFirstPartitionMarker (), 0)));
667size_t MultiRowTextImager::GetCharAtWindowLocation (
const Led_Point& where)
const
669 Led_Point windowOrigin = GetWindowRect ().GetOrigin () - Led_Point (0, GetHScrollPos ());
670 return (GetCharAtLocationRowRelative (where - windowOrigin, GetTopRowReferenceInWindow (), GetTotalRowsInWindow_ ()));
673size_t MultiRowTextImager::GetStartOfRow (
size_t rowNumber)
const
677 return (GetStartOfRow (GetIthRowReference (rowNumber)));
680size_t MultiRowTextImager::GetStartOfRowContainingPosition (
size_t charPosition)
const
682 return (GetStartOfRow (GetRowReferenceContainingPosition (charPosition)));
685size_t MultiRowTextImager::GetEndOfRow (
size_t rowNumber)
const
689 return (GetEndOfRow (GetIthRowReference (rowNumber)));
692size_t MultiRowTextImager::GetEndOfRowContainingPosition (
size_t charPosition)
const
694 return (GetEndOfRow (GetRowReferenceContainingPosition (charPosition)));
697size_t MultiRowTextImager::GetRealEndOfRow (
size_t rowNumber)
const
701 return (GetRealEndOfRow (GetIthRowReference (rowNumber)));
704size_t MultiRowTextImager::GetRealEndOfRowContainingPosition (
size_t charPosition)
const
706 return (GetRealEndOfRow (GetRowReferenceContainingPosition (charPosition)));
709size_t MultiRowTextImager::GetStartOfRow (RowReference row)
const
711 PartitionMarker* cur = row.GetPartitionMarker ();
712 size_t subRow = row.GetSubRow ();
714 return (cur->GetStart () + (subRow == 0 ? 0 : GetPartitionElementCacheInfo (cur).GetLineRelativeRowStartPosition (subRow)));
717size_t MultiRowTextImager::GetEndOfRow (RowReference row)
const
719 PartitionMarker* cur = row.GetPartitionMarker ();
720 size_t subRow = row.GetSubRow ();
722 PartitionElementCacheInfo pmCacheInfo = GetPartitionElementCacheInfo (cur);
723 if (subRow == pmCacheInfo.GetLastRow ()) {
727 Assert (cur->GetEnd () > 0);
729 size_t markerEnd = cur->GetEnd ();
730 Assert (markerEnd <= GetLength () + 1);
731 if (markerEnd == GetLength () + 1) {
732 return (GetLength ());
735 size_t prevToEnd = FindPreviousCharacter (markerEnd);
736 if (prevToEnd >= GetStartOfRow (row)) {
738 CopyOut (prevToEnd, 1, &lastChar);
739 if (RemoveMappedDisplayCharacters (&lastChar, 1) == 0) {
746 return (cur->GetStart () + pmCacheInfo.GetLineRelativeRowStartPosition (subRow + 1));
750size_t MultiRowTextImager::GetRealEndOfRow (RowReference row)
const
752 PartitionMarker* cur = row.GetPartitionMarker ();
753 size_t subRow = row.GetSubRow ();
755 PartitionElementCacheInfo pmCacheInfo = GetPartitionElementCacheInfo (cur);
756 if (subRow == pmCacheInfo.GetLastRow ()) {
757 Assert (cur->GetEnd () > 0);
758 size_t markerEnd = cur->GetEnd ();
762 return (cur->GetStart () + pmCacheInfo.GetLineRelativeRowStartPosition (subRow + 1));
766MultiRowTextImager::RowReference MultiRowTextImager::GetRowReferenceContainingPosition (
size_t charPosition)
const
768 Require (charPosition <= GetEnd ());
769 PartitionMarker* pm = GetPartitionMarkerContainingPosition (charPosition);
772 size_t pmStart = pm->GetStart ();
773 if (charPosition == pmStart) {
774 return (RowReference (pm, 0));
778 PartitionElementCacheInfo pmCacheInfo = GetPartitionElementCacheInfo (pm);
779 return (RowReference (pm, pmCacheInfo.LineRelativePositionInWhichRow (charPosition - pmStart)));
782size_t MultiRowTextImager::GetRowContainingPosition (
size_t charPosition)
const
786 return (GetRowNumber (GetRowReferenceContainingPosition (charPosition)));
789size_t MultiRowTextImager::GetRowCount ()
const
793 for (PartitionMarker* cur = GetFirstPartitionMarker (); cur !=
nullptr; cur = cur->GetNext ()) {
795 Assert (GetPartitionElementCacheInfo (cur).GetRowCount () >= 1);
796 rowCount += GetPartitionElementCacheInfo (cur).GetRowCount ();
801Led_Rect MultiRowTextImager::GetCharLocationRowRelativeByPosition (
size_t afterPosition,
size_t positionOfTopRow,
size_t maxRowsToCheck)
const
803 return GetCharLocationRowRelative (afterPosition, GetRowReferenceContainingPosition (positionOfTopRow), maxRowsToCheck);
806DistanceType MultiRowTextImager::GetRowHeight (
size_t rowNumber)
const
810 return (GetRowHeight (GetIthRowReference (rowNumber)));
817DistanceType MultiRowTextImager::GetRowRelativeBaselineOfRowContainingPosition (
size_t charPosition)
const
819 RowReference thisRow = GetRowReferenceContainingPosition (charPosition);
820 size_t startOfRow = GetStartOfRow (thisRow);
821 size_t endOfRow = GetEndOfRow (thisRow);
822 return MeasureSegmentBaseLine (startOfRow, endOfRow);
825void MultiRowTextImager::GetStableTypingRegionContaingMarkerRange (
size_t fromMarkerPos,
size_t toMarkerPos,
size_t* expandedFromMarkerPos,
826 size_t* expandedToMarkerPos)
const
830 Assert (fromMarkerPos <= toMarkerPos);
831 Assert (toMarkerPos <= GetEnd ());
833 size_t curTopRowRelativeRowNumber = 0;
835 RowReference curRow = GetTopRowReferenceInWindow ();
837 PartitionMarker* cur = curRow.GetPartitionMarker ();
839 size_t start = cur->GetStart ();
840 size_t end = cur->GetEnd ();
845 if (cur->GetNext () ==
nullptr) {
850 if (curTopRowRelativeRowNumber == 0 and (fromMarkerPos < start)) {
854 ++curTopRowRelativeRowNumber;
856 if (Contains (*cur, fromMarkerPos) and Contains (*cur, toMarkerPos)) {
857 (*expandedFromMarkerPos) = start;
858 (*expandedToMarkerPos) = end;
859 Assert ((*expandedFromMarkerPos) <= (*expandedToMarkerPos));
860 Assert ((*expandedToMarkerPos) <= GetEnd ());
864 if (curTopRowRelativeRowNumber >= GetTotalRowsInWindow_ ()) {
870 }
while (GetNextRowReference (&curRow));
872 (*expandedFromMarkerPos) = 0;
873 (*expandedToMarkerPos) = GetEnd ();
876DistanceType MultiRowTextImager::GetHeightOfRows (
size_t startingRow,
size_t rowCount)
const
878 return (GetHeightOfRows (GetIthRowReference (startingRow), rowCount));
881DistanceType MultiRowTextImager::GetHeightOfRows (RowReference startingRow,
size_t rowCount)
const
883 DistanceType height = 0;
884 for (RowReference curRow = startingRow; rowCount > 0; rowCount--) {
885 PartitionMarker* curPM = curRow.GetPartitionMarker ();
886 PartitionElementCacheInfo pmCacheInfo = GetPartitionElementCacheInfo (curPM);
887 height += pmCacheInfo.GetRowHeight (curRow.GetSubRow ());
888 if (curRow.GetSubRow () == pmCacheInfo.GetLastRow ()) {
889 height += pmCacheInfo.GetInterLineSpace ();
891 (void)GetNextRowReference (&curRow);
896void MultiRowTextImager::DidUpdateText (
const UpdateInfo& updateInfo)
noexcept
910 InvalidateTotalRowsInWindow ();
913 InvalidateTotalRowsInWindow ();
914 inherited::DidUpdateText (updateInfo);
917 AssureWholeWindowUsedIfNeeded ();
918 InvalidateScrollBarParameters ();
921void MultiRowTextImager::SetWindowRect (
const Led_Rect& windowRect)
932 bool heightChanged = GetWindowRect ().GetHeight () != windowRect.GetHeight ();
933 inherited::SetWindowRect (windowRect);
934 if (heightChanged and PeekAtTextStore () !=
nullptr) {
935 InvalidateTotalRowsInWindow ();
936 AssureWholeWindowUsedIfNeeded ();
937 InvalidateScrollBarParameters ();
947void MultiRowTextImager::InvalidateAllCaches ()
949 inherited::InvalidateAllCaches ();
950 if (GetPartition ().get () !=
nullptr) {
951 if (fPMCacheMgr.get () !=
nullptr) {
952 fPMCacheMgr->ClearCache ();
954 InvalidateTotalRowsInWindow ();
956 AssureWholeWindowUsedIfNeeded ();
957 InvalidateScrollBarParameters ();
961MultiRowTextImager::RowReference MultiRowTextImager::AdjustPotentialTopRowReferenceSoWholeWindowUsed (
const RowReference& potentialTopRow)
968 if (potentialTopRow.GetSubRow () == 0 and potentialTopRow.GetPartitionMarker ()->GetPrevious () ==
nullptr) {
969 return potentialTopRow;
972 CoordinateType windowHeight = GetWindowRect ().GetHeight ();
973 CoordinateType heightUsed = 0;
975 for (RowReference curRow = potentialTopRow;;) {
976 PartitionMarker* curPM = curRow.GetPartitionMarker ();
977 PartitionElementCacheInfo pmCacheInfo = GetPartitionElementCacheInfo (curPM);
978 heightUsed += pmCacheInfo.GetRowHeight (curRow.GetSubRow ());
979 if (curRow.GetSubRow () == pmCacheInfo.GetLastRow ()) {
980 heightUsed += pmCacheInfo.GetInterLineSpace ();
982 if (heightUsed >= windowHeight) {
983 return (potentialTopRow);
985 if (not GetNextRowReference (&curRow)) {
992 for (RowReference curRow = potentialTopRow;;) {
993 if (not GetPreviousRowReference (&curRow)) {
998 PartitionMarker* curPM = curRow.GetPartitionMarker ();
999 PartitionElementCacheInfo pmCacheInfo = GetPartitionElementCacheInfo (curPM);
1000 heightUsed += pmCacheInfo.GetRowHeight (curRow.GetSubRow ());
1001 if (curRow.GetSubRow () == pmCacheInfo.GetLastRow ()) {
1002 heightUsed += pmCacheInfo.GetInterLineSpace ();
1004 if (heightUsed > windowHeight) {
1006 [[maybe_unused]]
bool result = GetNextRowReference (&curRow);
1010 else if (heightUsed == windowHeight) {
1015 return (potentialTopRow);
1018bool MultiRowTextImager::PositionWouldFitInWindowWithThisTopRow (
size_t markerPos,
const RowReference& newTopRow)
1020 if (markerPos < GetStartOfRow (newTopRow)) {
1024 size_t rowCount = ComputeRowsThatWouldFitInWindowWithTopRow (newTopRow);
1025 RowReference lastRow = GetIthRowReferenceFromHere (newTopRow, rowCount - 1);
1027 return (markerPos < GetRealEndOfRow (lastRow));
1030void MultiRowTextImager::ReValidateSubRowInTopLineInWindow ()
1035 if (fSubRowInTopLineInWindow != 0) {
1037 size_t lastRow = GetPartitionElementCacheInfo (fTopLinePartitionMarkerInWindow).GetLastRow ();
1038 if (fSubRowInTopLineInWindow > lastRow) {
1039 fSubRowInTopLineInWindow = lastRow;
1042 bool pmNotWrapped = (fTopLinePartitionMarkerInWindow->fPixelHeightCache == DistanceType (-1));
1043 size_t lastRow = GetPartitionElementCacheInfo (fTopLinePartitionMarkerInWindow).GetLastRow ();
1044 if (fSubRowInTopLineInWindow > lastRow) {
1045 fSubRowInTopLineInWindow = lastRow;
1054 fTopLinePartitionMarkerInWindow->InvalidateCache ();
1060size_t MultiRowTextImager::ComputeRowsThatWouldFitInWindowWithTopRow (
const RowReference& newTopRow)
const
1066 CoordinateType windowHeight = GetWindowRect ().GetHeight ();
1072 size_t rowCount = 0;
1073 CoordinateType heightUsed = 0;
1074 for (RowReference curRow = newTopRow;;) {
1076 PartitionMarker* curPM = curRow.GetPartitionMarker ();
1077 PartitionElementCacheInfo pmCacheInfo = GetPartitionElementCacheInfo (curPM);
1078 heightUsed += pmCacheInfo.GetRowHeight (curRow.GetSubRow ());
1079 if (curRow.GetSubRow () == pmCacheInfo.GetLastRow ()) {
1080 heightUsed += pmCacheInfo.GetInterLineSpace ();
1082 if (heightUsed > windowHeight) {
1087 else if (heightUsed == windowHeight) {
1091 if (not GetNextRowReference (&curRow)) {
1095 if (rowCount == 0) {
1108Led_Rect MultiRowTextImager::GetCharLocationRowRelative (
size_t afterPosition, RowReference topRow,
size_t maxRowsToCheck)
const
1111 const Led_Rect kMagicBeforeRect = Led_Rect (-10000, 0, 0, 0);
1112 const Led_Rect kMagicAfterRect = Led_Rect (10000, 0, 0, 0);
1114 Require (afterPosition <= GetEnd ());
1116 if (afterPosition < GetStartOfRow (topRow)) {
1117 return (kMagicBeforeRect);
1120 RowReference curRow = topRow;
1121 size_t curTopRowRelativeRowNumber = 0;
1122 CoordinateType topVPos = 0;
1124 PartitionMarker* cur = curRow.GetPartitionMarker ();
1125 size_t subRow = curRow.GetSubRow ();
1127 size_t start = cur->GetStart ();
1128 size_t end = cur->GetEnd ();
1130 Assert (end <= GetEnd () + 1);
1132 PartitionElementCacheInfo pmCacheInfo = GetPartitionElementCacheInfo (cur);
1137 start += pmCacheInfo.PeekAtRowStart (subRow);
1138 if (subRow < pmCacheInfo.GetLastRow ()) {
1139 end = cur->GetStart () + pmCacheInfo.PeekAtRowStart (subRow + 1);
1140 Assert (start <= end);
1143 ++curTopRowRelativeRowNumber;
1148 if (afterPosition >= start and afterPosition < end) {
1149 Assert (start <= afterPosition);
1150 DistanceType hStart = 0;
1151 DistanceType hEnd = 0;
1152 GetRowRelativeCharLoc (afterPosition, &hStart, &hEnd);
1153 Assert (hStart <= hEnd);
1154 return (Led_Rect (topVPos, hStart, pmCacheInfo.GetRowHeight (subRow), hEnd - hStart));
1157 topVPos += pmCacheInfo.GetRowHeight (subRow);
1159 if (pmCacheInfo.GetLastRow () == subRow) {
1160 topVPos += pmCacheInfo.GetInterLineSpace ();
1163 if (curTopRowRelativeRowNumber >= maxRowsToCheck) {
1166 }
while (GetNextRowReference (&curRow));
1168 return (kMagicAfterRect);
1171size_t MultiRowTextImager::GetCharAtLocationRowRelative (
const Led_Point& where, RowReference topRow,
size_t maxRowsToCheck)
const
1183 RowReference curRow = topRow;
1184 size_t curTopRowRelativeRowNumber = 0;
1185 CoordinateType topVPos = 0;
1187 PartitionMarker* cur = curRow.GetPartitionMarker ();
1188 size_t subRow = curRow.GetSubRow ();
1190 size_t start = cur->GetStart ();
1192 PartitionElementCacheInfo pmCacheInfo = GetPartitionElementCacheInfo (cur);
1197 start += pmCacheInfo.PeekAtRowStart (subRow);
1202 DistanceType interLineSpaceIfAny = (pmCacheInfo.GetLastRow () == subRow) ? pmCacheInfo.GetInterLineSpace () : 0;
1204 curTopRowRelativeRowNumber++;
1205 if (where.v >= topVPos and where.v < topVPos + CoordinateType (pmCacheInfo.GetRowHeight (subRow) + interLineSpaceIfAny)) {
1206 return GetRowRelativeCharAtLoc (where.h, start);
1209 if (curTopRowRelativeRowNumber >= maxRowsToCheck) {
1213 topVPos += pmCacheInfo.GetRowHeight (subRow) + interLineSpaceIfAny;
1214 }
while (GetNextRowReference (&curRow));
1219DistanceType MultiRowTextImager::CalculateInterLineSpace (
const PartitionMarker* )
const
1229bool MultiRowTextImager::ContainsMappedDisplayCharacters (
const Led_tChar* text,
size_t nTChars)
const
1231 return ContainsMappedDisplayCharacters_HelperForChar (text, nTChars,
'\n') or inherited::ContainsMappedDisplayCharacters (text, nTChars);
1238size_t MultiRowTextImager::RemoveMappedDisplayCharacters (Led_tChar* copyText,
size_t nTChars)
const
1240 size_t newLen = inherited::RemoveMappedDisplayCharacters (copyText, nTChars);
1241 Assert (newLen <= nTChars);
1242 size_t newerLen = RemoveMappedDisplayCharacters_HelperForChar (copyText, newLen,
'\n');
1243 Assert (newerLen <= newLen);
1244 Assert (newerLen <= nTChars);
1253void MultiRowTextImager::PartitionElementCacheInfo::Clear ()
1255 fRep = MakeSharedPtr<Rep> ();
1258void MultiRowTextImager::PartitionElementCacheInfo::IncrementRowCountAndFixCacheBuffers (
size_t newStart, DistanceType newRowsHeight)
1260 ++fRep->fRowCountCache;
1263 if (fRep->fRowCountCache > kPackRowStartCount + 1) {
1264 RowStart_* newRowStartArray =
new RowStart_[fRep->fRowCountCache - 1];
1266 if (fRep->fRowCountCache == kPackRowStartCount + 1 + 1) {
1267 ::memcpy (newRowStartArray, &fRep->fRowStartArray, sizeof (fRep->fRowStartArray));
1270 Assert (fRep->fRowCountCache > 2);
1271 if (fRep->fRowCountCache > kPackRowStartCount + 1 + 1) {
1273 (void)memcpy (newRowStartArray, fRep->fRowStartArray, sizeof (newRowStartArray[1]) * (fRep->fRowCountCache - 2));
1274 delete[] fRep->fRowStartArray;
1277 fRep->fRowStartArray = newRowStartArray;
1281 if (fRep->fRowCountCache > kPackRowHeightCount) {
1282 RowHeight_* newRowHeightArray =
new RowHeight_[fRep->fRowCountCache];
1284 if (fRep->fRowCountCache == kPackRowHeightCount + 1) {
1285 ::memcpy (newRowHeightArray, &fRep->fRowHeightArray, sizeof (fRep->fRowHeightArray));
1288 Assert (fRep->fRowCountCache > 1);
1289 if (fRep->fRowCountCache > kPackRowHeightCount + 1) {
1291 memcpy (newRowHeightArray, fRep->fRowHeightArray, sizeof (newRowHeightArray[1]) * (fRep->fRowCountCache - 1));
1292 delete[] fRep->fRowHeightArray;
1295 fRep->fRowHeightArray = newRowHeightArray;
1298 SetRowStart (fRep->fRowCountCache - 1, newStart);
1299 SetRowHeight (fRep->fRowCountCache - 1, newRowsHeight);
1300 fRep->fPixelHeightCache += newRowsHeight;
1308MultiRowTextImager::PMInfoCacheMgr::PMInfoCacheMgr (MultiRowTextImager& imager)
1310 , fCurFillCachePM (nullptr)
1311 , fCurFillCacheInfo ()
1317 PartitionPtr part = imager.GetPartition ();
1318 Assert (part.get () !=
nullptr);
1319 part->AddPartitionWatcher (
this);
1320 fMyMarker = unique_ptr<MyMarker> (
new MyMarker (*
this));
1321 TextStore& ts = part->GetTextStore ();
1322 ts.AddMarker (fMyMarker.get (), 0, ts.GetLength () + 1, part.get ());
1325MultiRowTextImager::PMInfoCacheMgr::~PMInfoCacheMgr ()
1327 PartitionPtr part = fImager.GetPartition ();
1328 part->RemovePartitionWatcher (
this);
1329 TextStore& ts = part->GetTextStore ();
1330 ts.RemoveMarker (fMyMarker.get ());
1333MultiRowTextImager::PartitionElementCacheInfo MultiRowTextImager::PMInfoCacheMgr::GetPartitionElementCacheInfo (Partition::PartitionMarker* pm)
const
1335 if (pm == fCurFillCachePM) {
1336 return fCurFillCacheInfo;
1338 using MAP_CACHE = map<Partition::PartitionMarker*, PartitionElementCacheInfo>;
1339 MAP_CACHE::iterator i = fPMCache.find (pm);
1340 if (i == fPMCache.end ()) {
1342 Assert (fCurFillCachePM ==
nullptr);
1343 fCurFillCachePM = pm;
1344 fImager.FillCache (pm, fCurFillCacheInfo);
1346 for (
size_t t = 0; t < fCurFillCacheInfo.GetRowCount (); ++t) {
1347 Assert (fCurFillCacheInfo.GetLineRelativeRowStartPosition (t) <= pm->GetLength ());
1348 Assert (fCurFillCacheInfo.PeekAtRowStart (t) <= pm->GetLength ());
1351 i = fPMCache.insert (MAP_CACHE::value_type (pm, fCurFillCacheInfo)).first;
1353 Assert (fCurFillCacheInfo.GetRowCount () == i->second.GetRowCount ());
1354 for (
size_t t = 0; t < fCurFillCacheInfo.GetRowCount (); ++t) {
1355 Assert (fCurFillCacheInfo.PeekAtRowHeight (t) == i->second.PeekAtRowHeight (t));
1356 Assert (fCurFillCacheInfo.PeekAtRowStart (t) == i->second.PeekAtRowStart (t));
1359 Assert (fCurFillCachePM == pm);
1360 fCurFillCachePM =
nullptr;
1363 Assert (fCurFillCachePM == pm);
1364 fCurFillCachePM =
nullptr;
1371void MultiRowTextImager::PMInfoCacheMgr::ClearCache ()
1376void MultiRowTextImager::PMInfoCacheMgr::AboutToSplit (PartitionMarker* pm,
size_t ,
void** infoRecord)
const noexcept
1381void MultiRowTextImager::PMInfoCacheMgr::DidSplit (
void* infoRecord)
const noexcept
1383 PartitionMarker* pm =
reinterpret_cast<PartitionMarker*
> (infoRecord);
1384 using MAP_CACHE = map<Partition::PartitionMarker*, PartitionElementCacheInfo>;
1385 MAP_CACHE::iterator i = fPMCache.find (pm);
1386 if (i != fPMCache.end ()) {
1389 fImager.InvalidateTotalRowsInWindow ();
1391 if (pm == fImager.fTopLinePartitionMarkerInWindow) {
1393 fImager.ReValidateSubRowInTopLineInWindow ();
1398void MultiRowTextImager::PMInfoCacheMgr::AboutToCoalece (PartitionMarker* pm,
void** infoRecord)
const noexcept
1402 PartitionMarker* newTopLine =
nullptr;
1403 bool useFirstRow =
false;
1404 if (pm == fImager.fTopLinePartitionMarkerInWindow) {
1405 if (pm->GetNext () ==
nullptr) {
1406 newTopLine = fImager.fTopLinePartitionMarkerInWindow->GetPrevious ();
1407 useFirstRow =
false;
1410 newTopLine = fImager.fTopLinePartitionMarkerInWindow->GetNext ();
1416 if (newTopLine !=
nullptr) {
1421 fImager.SetTopRowInWindow_ (RowReference (newTopLine, 0));
1425 MultiRowPartitionMarker* newTopLine =
nullptr;
1426 bool useFirstRow =
false;
1427 if (pm == fTextImager.fTopLinePartitionMarkerInWindow) {
1428 if (pm->GetNext () ==
nullptr) {
1429 newTopLine = fTextImager.fTopLinePartitionMarkerInWindow->GetPreviousMRPM ();
1430 useFirstRow =
false;
1433 newTopLine = fTextImager.fTopLinePartitionMarkerInWindow->GetNextMRPM ();
1439 MultiRowPartitionMarker* successor = (MultiRowPartitionMarker*)pm->GetNext ();
1440 inherited::Coalece (pm);
1441 if (successor !=
nullptr) {
1442 successor->InvalidateCache ();
1446 if (newTopLine !=
nullptr) {
1447 fTextImager.SetTopRowInWindow_ (RowReference (newTopLine, useFirstRow ? 0 : fTextImager.GetPartitionElementCacheInfo (newTopLine).GetLastRow ()));
1449 AssertNotNull (fTextImager.fTopLinePartitionMarkerInWindow);
1450 AssertMember (fTextImager.fTopLinePartitionMarkerInWindow, MultiRowPartitionMarker);
1451 fTextImager.InvalidateTotalRowsInWindow ();
1455void MultiRowTextImager::PMInfoCacheMgr::DidCoalece (
void* infoRecord)
const noexcept
1457 PartitionMarker* pm =
reinterpret_cast<PartitionMarker*
> (infoRecord);
1458 using MAP_CACHE = map<Partition::PartitionMarker*, PartitionElementCacheInfo>;
1459 MAP_CACHE::iterator i = fPMCache.find (pm);
1460 if (i != fPMCache.end ()) {
1463 fImager.InvalidateTotalRowsInWindow ();
1466void MultiRowTextImager::PMInfoCacheMgr::MyMarkerDidUpdateCallback ()
1480 fImager.InvalidateTotalRowsInWindow ();
1488MultiRowTextImager::PMInfoCacheMgr::MyMarker::MyMarker (PMInfoCacheMgr& pmInfoCacheMgr)
1489 : fPMInfoCacheMgr (pmInfoCacheMgr)
1493void MultiRowTextImager::PMInfoCacheMgr::MyMarker::DidUpdateText (
const UpdateInfo& updateInfo)
noexcept
1495 inherited::DidUpdateText (updateInfo);
1496 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...