// Token: 0x06006720 RID: 26400 RVA: 0x001CD9EC File Offset: 0x001CBBEC internal void UpdateBackgroundFormatInfo() { this._cpInterrupted = -1; this._lastCPUninterruptible = 0; this._doesFinalDTRCoverRestOfText = false; this._cchAllText = this._structuralCache.TextContainer.SymbolCount; if (this._structuralCache.DtrList != null) { int num = 0; for (int i = 0; i < this._structuralCache.DtrList.Length - 1; i++) { num += this._structuralCache.DtrList[i].PositionsAdded - this._structuralCache.DtrList[i].PositionsRemoved; } DirtyTextRange dirtyTextRange = this._structuralCache.DtrList[this._structuralCache.DtrList.Length - 1]; if (dirtyTextRange.StartIndex + num + dirtyTextRange.PositionsAdded >= this._cchAllText) { this._doesFinalDTRCoverRestOfText = true; this._lastCPUninterruptible = dirtyTextRange.StartIndex + num; } } else { this._doesFinalDTRCoverRestOfText = true; } this._backgroundFormatStopTime = DateTime.UtcNow.AddMilliseconds(200.0); }
// ------------------------------------------------------------------ // Merge DRT at position 'index' with the next one, if possible. // // index - Index of the DTR to be merged with next one. // ------------------------------------------------------------------ private void MergeWithNext(int index) { while (index + 1 < _count) { DirtyTextRange dtrNext = _dtrs[index + 1]; // DTR starts in the range of the previous DTR. In this case // merge these 2 DTRs. if (dtrNext.StartIndex <= _dtrs[index].StartIndex + _dtrs[index].PositionsRemoved) { //_dtrs[index].dcp: no need to change it _dtrs[index].PositionsAdded += dtrNext.PositionsAdded; _dtrs[index].PositionsRemoved += dtrNext.PositionsRemoved; // Remove merged entry for (int i = index + 2; i < _count; i++) { _dtrs[i - 1] = _dtrs[i]; } --_count; } else { break; } } }
/// <summary> /// Merges the DtrList into a single DirtyTextRange containing all /// ranges in the list. /// </summary> /// <returns>A DirtyTextRange containing all ranges in this list</returns> internal DirtyTextRange GetMergedRange() { if (_count > 0) { DirtyTextRange range = _dtrs[0]; int previousOffset = range.StartIndex; int positionsAdded = range.PositionsAdded; int positionsRemoved = range.PositionsRemoved; bool fromHighlightLayer = range.FromHighlightLayer; for (int i = 1; i < _count; i++) { range = _dtrs[i]; int rangeDistance = range.StartIndex - previousOffset; positionsAdded = rangeDistance + range.PositionsAdded; positionsRemoved = rangeDistance + range.PositionsRemoved; // Prefer not from highlight layer when squashing fromHighlightLayer &= range.FromHighlightLayer; previousOffset = range.StartIndex; } return(new DirtyTextRange(_dtrs[0].StartIndex, positionsAdded, positionsRemoved, fromHighlightLayer)); } return(new DirtyTextRange(0, 0, 0, false)); }
//------------------------------------------------------------------- // // Internal Methods // //------------------------------------------------------------------- #region Internal Methods /// <summary> /// Updates background layout information from a structuralCache /// </summary> internal void UpdateBackgroundFormatInfo() { _cpInterrupted = -1; _lastCPUninterruptible = 0; _doesFinalDTRCoverRestOfText = false; _cchAllText = _structuralCache.TextContainer.SymbolCount; if (_structuralCache.DtrList != null) { int positionsAdded = 0; // Sum for all dtrs but the last for (int dtrIndex = 0; dtrIndex < _structuralCache.DtrList.Length - 1; dtrIndex++) { positionsAdded += _structuralCache.DtrList[dtrIndex].PositionsAdded - _structuralCache.DtrList[dtrIndex].PositionsRemoved; } DirtyTextRange dtrLast = _structuralCache.DtrList[_structuralCache.DtrList.Length - 1]; if ((dtrLast.StartIndex + positionsAdded + dtrLast.PositionsAdded) >= _cchAllText) { _doesFinalDTRCoverRestOfText = true; _lastCPUninterruptible = dtrLast.StartIndex + positionsAdded; } } else { _doesFinalDTRCoverRestOfText = true; } // And set a good stop time for formatting _backgroundFormatStopTime = DateTime.UtcNow.AddMilliseconds(_stopTimeDelta); }
// ------------------------------------------------------------------ // Table has been structurally altered // ------------------------------------------------------------------ private void TableStructureChanged(object sender, EventArgs e) { // Disconnect obsolete paragraphs. BaseParagraph paraInvalid = _firstChild; while (paraInvalid != null) { paraInvalid.Dispose(); paraInvalid = paraInvalid.Next; } _firstChild = null; // // Since whole table content is disposed, we need // - to create dirty text range corresponding to the Table content // - notify formatter that Table's content is changed. // int charCount = Table.SymbolCount - 2;// This is equivalent to (ContentEndOffset – ContentStartOffset) but is more performant. if (charCount > 0) { DirtyTextRange dtr = new DirtyTextRange(Table.ContentStartOffset, charCount, charCount); StructuralCache.AddDirtyTextRange(dtr); } if (StructuralCache.FormattingOwner.Formatter != null) { StructuralCache.FormattingOwner.Formatter.OnContentInvalidated(true, Table.ContentStart, Table.ContentEnd); } }
// ------------------------------------------------------------------ // Constructor // ------------------------------------------------------------------ internal UpdateRecord() { Dtr = new DirtyTextRange(0,0,0); FirstPara = SyncPara = null; ChangeType = PTS.FSKCHANGE.fskchNone; Next = null; InProcessing = false; }
/// <summary> /// Add new DirtyTextRange. /// </summary> /// <param name="dtr">New DTR being added.</param> internal void AddDirtyTextRange(DirtyTextRange dtr) { if (_dtrs == null) { _dtrs = new DtrList(); } _dtrs.Merge(dtr); }
// ------------------------------------------------------------------ // Constructor // ------------------------------------------------------------------ internal UpdateRecord() { Dtr = new DirtyTextRange(0, 0, 0); FirstPara = SyncPara = null; ChangeType = PTS.FSKCHANGE.fskchNone; Next = null; InProcessing = false; }
// ------------------------------------------------------------------ // Retrieve list of dtrs from range. // // dcpNew - Distance from the beginning of TextContainer after all // tree changes. // cchOld - Number of characters in the range, but before any // tree changes. // // Returns: List of DRTs for specified range. // ------------------------------------------------------------------ internal DtrList DtrsFromRange(int dcpNew, int cchOld) { DtrList dtrs = null; int i = 0; int first, last; int positionsAdded = 0; // Find the first dtr intersecting with the specified range. // Since DTRs store dcp before any changes, during iteration // accumulate positionsAdded (number of characters added to the tree // up to the current point). while (i < _count) { if (dcpNew <= _dtrs[i].StartIndex + positionsAdded + _dtrs[i].PositionsAdded) { break; } positionsAdded += _dtrs[i].PositionsAdded - _dtrs[i].PositionsRemoved; ++i; } first = i; // Find the last dtr intersecting with the specified range. // dcpNew-positionsAdded points to position before any tree changes, from // where we start counting cchOld. // Do not add characters (positionsAdded), since start position has been already found. while (i < _count) { if (dcpNew - positionsAdded + cchOld <= _dtrs[i].StartIndex + _dtrs[i].PositionsRemoved) { // If there is no intersection with the current DTR, go to the previous one. if (dcpNew - positionsAdded + cchOld < _dtrs[i].StartIndex) { --i; } break; } ++i; } last = (i < _count) ? i : _count - 1; // If there are DTRs in the specified range, create new DtrList object if (last >= first) { dtrs = new DtrList(); while (last >= first) { // Since dcpNew is after tree changes, add positionsAdded to all dtrs // to build dtr list relative to dcpNew position. DirtyTextRange dtr = _dtrs[first]; dtr.StartIndex += positionsAdded; dtrs.Append(dtr); ++first; } } return(dtrs); }
// ------------------------------------------------------------------ // Append new DTR to the list. // // dtr - DTR to be appended to the list. // ------------------------------------------------------------------ private void Append(DirtyTextRange dtr) { if (_count == _dtrs.Length) { Resize(); } _dtrs[_count] = dtr; ++_count; }
// Token: 0x060067B6 RID: 26550 RVA: 0x001D1476 File Offset: 0x001CF676 private void Append(DirtyTextRange dtr) { if (this._count == this._dtrs.Length) { this.Resize(); } this._dtrs[this._count] = dtr; this._count++; }
// ------------------------------------------------------------------ // Increases the capacity of the DTR array. // <new size> = <current size>*2. // ------------------------------------------------------------------ private void Resize() { Debug.Assert(_dtrs.Length > 0); // Allocate new array and copy all existing entries into it DirtyTextRange [] newdtrs = new DirtyTextRange[_dtrs.Length * 2]; Array.Copy(_dtrs, newdtrs, _dtrs.Length); _dtrs = newdtrs; }
// Token: 0x060069F2 RID: 27122 RVA: 0x001E20F4 File Offset: 0x001E02F4 private void DeferFormattingToBackground() { int cpinterrupted = this._section.StructuralCache.BackgroundFormatInfo.CPInterrupted; int cchAllText = this._section.StructuralCache.BackgroundFormatInfo.CchAllText; DirtyTextRange dtr = new DirtyTextRange(cpinterrupted, cchAllText - cpinterrupted, cchAllText - cpinterrupted, false); this._section.StructuralCache.AddDirtyTextRange(dtr); this._backgroundFormatOperation = Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, PtsPage.BackgroundUpdateCallback, this); }
// Token: 0x060067B4 RID: 26548 RVA: 0x001D1260 File Offset: 0x001CF460 internal DtrList DtrsFromRange(int dcpNew, int cchOld) { DtrList dtrList = null; int i = 0; int num = 0; while (i < this._count && dcpNew > this._dtrs[i].StartIndex + num + this._dtrs[i].PositionsAdded) { num += this._dtrs[i].PositionsAdded - this._dtrs[i].PositionsRemoved; i++; } int num2 = i; while (i < this._count) { if (dcpNew - num + cchOld <= this._dtrs[i].StartIndex + this._dtrs[i].PositionsRemoved) { if (dcpNew - num + cchOld < this._dtrs[i].StartIndex) { i--; break; } break; } else { i++; } } int j = (i < this._count) ? i : (this._count - 1); if (j >= num2) { dtrList = new DtrList(); while (j >= num2) { DirtyTextRange dtr = this._dtrs[num2]; dtr.StartIndex += num; dtrList.Append(dtr); num2++; } } return(dtrList); }
// Token: 0x06006ADC RID: 27356 RVA: 0x001E94F0 File Offset: 0x001E76F0 private void TableStructureChanged(object sender, EventArgs e) { for (BaseParagraph baseParagraph = this._firstChild; baseParagraph != null; baseParagraph = baseParagraph.Next) { baseParagraph.Dispose(); } this._firstChild = null; int num = this.Table.SymbolCount - 2; if (num > 0) { DirtyTextRange dtr = new DirtyTextRange(this.Table.ContentStartOffset, num, num, false); base.StructuralCache.AddDirtyTextRange(dtr); } if (base.StructuralCache.FormattingOwner.Formatter != null) { base.StructuralCache.FormattingOwner.Formatter.OnContentInvalidated(true, this.Table.ContentStart, this.Table.ContentEnd); } }
// Token: 0x060067A6 RID: 26534 RVA: 0x001D0CB8 File Offset: 0x001CEEB8 private UpdateRecord UpdateRecordFromDtr(DtrList dtrs, DirtyTextRange dtr, int dcpContent) { UpdateRecord updateRecord = new UpdateRecord(); updateRecord.Dtr = dtr; BaseParagraph baseParagraph = this._firstChild; int num = dcpContent; if (num < updateRecord.Dtr.StartIndex) { while (baseParagraph != null && num + baseParagraph.LastFormatCch <= updateRecord.Dtr.StartIndex && (num + baseParagraph.LastFormatCch != updateRecord.Dtr.StartIndex || !(baseParagraph is TextParagraph))) { num += baseParagraph.LastFormatCch; baseParagraph = baseParagraph.Next; } } updateRecord.FirstPara = baseParagraph; if (baseParagraph == null) { updateRecord.ChangeType = PTS.FSKCHANGE.fskchNew; } else if (num < updateRecord.Dtr.StartIndex) { updateRecord.ChangeType = PTS.FSKCHANGE.fskchInside; } else { updateRecord.ChangeType = PTS.FSKCHANGE.fskchNew; } updateRecord.SyncPara = null; while (baseParagraph != null) { if (num + baseParagraph.LastFormatCch > updateRecord.Dtr.StartIndex + updateRecord.Dtr.PositionsRemoved || (num + baseParagraph.LastFormatCch == updateRecord.Dtr.StartIndex + updateRecord.Dtr.PositionsRemoved && updateRecord.ChangeType != PTS.FSKCHANGE.fskchNew)) { updateRecord.SyncPara = baseParagraph.Next; break; } num += baseParagraph.LastFormatCch; baseParagraph = baseParagraph.Next; } return(updateRecord); }
// Token: 0x060067B3 RID: 26547 RVA: 0x001D119C File Offset: 0x001CF39C internal DirtyTextRange GetMergedRange() { if (this._count > 0) { DirtyTextRange dirtyTextRange = this._dtrs[0]; int startIndex = dirtyTextRange.StartIndex; int positionsAdded = dirtyTextRange.PositionsAdded; int positionsRemoved = dirtyTextRange.PositionsRemoved; bool flag = dirtyTextRange.FromHighlightLayer; for (int i = 1; i < this._count; i++) { dirtyTextRange = this._dtrs[i]; int num = dirtyTextRange.StartIndex - startIndex; positionsAdded = num + dirtyTextRange.PositionsAdded; positionsRemoved = num + dirtyTextRange.PositionsRemoved; flag &= dirtyTextRange.FromHighlightLayer; startIndex = dirtyTextRange.StartIndex; } return(new DirtyTextRange(this._dtrs[0].StartIndex, positionsAdded, positionsRemoved, flag)); } return(new DirtyTextRange(0, 0, 0, false)); }
// Token: 0x060067B5 RID: 26549 RVA: 0x001D1380 File Offset: 0x001CF580 private void MergeWithNext(int index) { while (index + 1 < this._count) { DirtyTextRange dirtyTextRange = this._dtrs[index + 1]; if (dirtyTextRange.StartIndex > this._dtrs[index].StartIndex + this._dtrs[index].PositionsRemoved) { break; } DirtyTextRange[] dtrs = this._dtrs; dtrs[index].PositionsAdded = dtrs[index].PositionsAdded + dirtyTextRange.PositionsAdded; DirtyTextRange[] dtrs2 = this._dtrs; dtrs2[index].PositionsRemoved = dtrs2[index].PositionsRemoved + dirtyTextRange.PositionsRemoved; DirtyTextRange[] dtrs3 = this._dtrs; dtrs3[index].FromHighlightLayer = (dtrs3[index].FromHighlightLayer & dirtyTextRange.FromHighlightLayer); for (int i = index + 2; i < this._count; i++) { this._dtrs[i - 1] = this._dtrs[i]; } this._count--; } }
// Token: 0x0600679A RID: 26522 RVA: 0x001CFD2C File Offset: 0x001CDF2C internal void UpdGetSegmentChange(out PTS.FSKCHANGE fskch) { if (base.StructuralCache.CurrentFormatContext.FinitePage) { DtrList dtrList = base.StructuralCache.DtrsFromRange(TextContainerHelper.GetCPFromElement(base.StructuralCache.TextContainer, base.Element, ElementEdge.BeforeStart), base.LastFormatCch); if (dtrList != null) { int cpfromElement = TextContainerHelper.GetCPFromElement(base.StructuralCache.TextContainer, base.Element, ElementEdge.AfterStart); DirtyTextRange dirtyTextRange = dtrList[0]; int num = cpfromElement; BaseParagraph baseParagraph = this._firstChild; if (num < dirtyTextRange.StartIndex) { while (baseParagraph != null && num + baseParagraph.LastFormatCch <= dirtyTextRange.StartIndex && (num + baseParagraph.LastFormatCch != dirtyTextRange.StartIndex || !(baseParagraph is TextParagraph))) { num += baseParagraph.Cch; baseParagraph = baseParagraph.Next; } if (baseParagraph != null) { baseParagraph.SetUpdateInfo(PTS.FSKCHANGE.fskchInside, false); } } else { baseParagraph.SetUpdateInfo(PTS.FSKCHANGE.fskchNew, false); } if (baseParagraph != null) { for (baseParagraph = baseParagraph.Next; baseParagraph != null; baseParagraph = baseParagraph.Next) { baseParagraph.SetUpdateInfo(PTS.FSKCHANGE.fskchNew, false); } } this._changeType = PTS.FSKCHANGE.fskchInside; } } fskch = this._changeType; }
// Token: 0x060067B7 RID: 26551 RVA: 0x001D14B0 File Offset: 0x001CF6B0 private void Resize() { DirtyTextRange[] array = new DirtyTextRange[this._dtrs.Length * 2]; Array.Copy(this._dtrs, array, this._dtrs.Length); this._dtrs = array; }
/// <summary> /// OnChildDesiredSizeChanged /// Called from FlowDocumentPage for IContentHost implementation /// internal void OnChildDesiredSizeChanged(UIElement child) { if (_structuralCache != null && _structuralCache.IsFormattedOnce && !_structuralCache.ForceReformat) { // If executed during formatting process, delay invalidation. // This may happen during formatting when text host notifies its about // baseline changes. if (_structuralCache.IsFormattingInProgress) { Dispatcher.BeginInvoke(DispatcherPriority.Normal, new DispatcherOperationCallback(OnChildDesiredSizeChangedAsync), child); return; } // Get start and end positions int childStartIndex = TextContainerHelper.GetCPFromEmbeddedObject(child, ElementEdge.BeforeStart); if (childStartIndex < 0) { return; } TextPointer childStart = new TextPointer(_structuralCache.TextContainer.Start); childStart.MoveByOffset(childStartIndex); TextPointer childEnd = new TextPointer(childStart); childEnd.MoveByOffset(TextContainerHelper.EmbeddedObjectLength); // Create new DTR for changing UIElement and add it to DRTList. DirtyTextRange dtr = new DirtyTextRange(childStartIndex, TextContainerHelper.EmbeddedObjectLength, TextContainerHelper.EmbeddedObjectLength); _structuralCache.AddDirtyTextRange(dtr); // Notify formatter about content invalidation. if (_formatter != null) { _formatter.OnContentInvalidated(true, childStart, childEnd); } } }
// Callback from the TextContainer when a highlight changes. private void OnHighlightChanged(object sender, HighlightChangedEventArgs args) { // The only supported highlight type for TextBoxView is SpellerHighlight. if (args.OwnerType != typeof(SpellerHighlightLayer)) { return; } if (_dirtyList == null) { _dirtyList = new DtrList(); } // // Add the change to our dirty list. // foreach (TextSegment segment in args.Ranges) { int positionsCovered = segment.End.Offset - segment.Start.Offset; DirtyTextRange dirtyTextRange = new DirtyTextRange(segment.Start.Offset, positionsCovered, positionsCovered); _dirtyList.Merge(dirtyTextRange); } // // Force a re-measure. // // NB: it's not currently possible to InvalidateArrange here. // "Render only" changes from the highlight layer change the way we // ultimately feed text to the formatter. Introducing breaks for // highlights may actually change the layout of the text as // characters are interpreted in different contexts. Dev10 Bugs // 511849 has an example. // InvalidateMeasure(); }
/// <summary> /// Handler for TextContainer change notifications. /// </summary> /// <param name="sender"></param> /// <param name="args"></param> private void OnTextContainerChange(object sender, TextContainerChangeEventArgs args) { DirtyTextRange dtr; ITextPointer segmentEnd; Invariant.Assert(args != null); Invariant.Assert(sender == _structuralCache.TextContainer); Invariant.Assert(_structuralCache != null && _structuralCache.IsFormattedOnce, "Unexpected TextContainer.Change callback before first format!"); if (args.Count == 0) { // A no-op for this control. Happens when IMECharCount updates happen // without corresponding SymbolCount changes. return; } try { // Detect invalid content change operations. if (_structuralCache.IsFormattingInProgress) { _structuralCache.OnInvalidOperationDetected(); throw new InvalidOperationException(SR.Get(SRID.FlowDocumentInvalidContnetChange)); } // Since content is changeing, do partial invalidation of BreakRecordTable. if (args.TextChange != TextChangeType.ContentRemoved) { segmentEnd = args.ITextPosition.CreatePointer(args.Count, LogicalDirection.Forward); } else { segmentEnd = args.ITextPosition; } // Invalidate affected pages and break records. // We DTR invalidate if we're using a formatter as well for incremental update. if (!args.AffectsRenderOnly || (_formatter != null && _formatter is FlowDocumentFormatter)) { // Create new DTR for changing range and add it to DRTList. dtr = new DirtyTextRange(args); _structuralCache.AddDirtyTextRange(dtr); } else { // Clear format caches. _structuralCache.InvalidateFormatCache(/*Clear structure*/ false); } // Notify formatter about content invalidation. if (_formatter != null) { _formatter.OnContentInvalidated(!args.AffectsRenderOnly, args.ITextPosition, segmentEnd); } } finally { // Content has been changed, so reset appropriate flag. _structuralCache.IsContentChangeInProgress = false; } }
// ------------------------------------------------------------------ // Merge new DTR with list of exising DTRs: // 1) Convert startIndex to index reflecting position before any changes. // 2) Merge it with existing list of DTRs. // // dtr - New DTR to be merged with exising list of DTRs. // ------------------------------------------------------------------ internal void Merge(DirtyTextRange dtr) { bool merge = false; int i = 0; int startIndexOld = dtr.StartIndex; // 1) Convert StartIndex to index reflecting position before any changes. // And find out if there is a need to merge DTRs if (_count > 0) { while (i < _count) { // a) New DTR starts before the next one. In this case there are // two possibilities: // * new DTR does not intersect with the beginning of the next DTR, // in this case insert new DTR before the next one. // * new DTR does intersect with the beginning of the next DTR, // in this case merge these 2 DTRs if (startIndexOld < _dtrs[i].StartIndex) { if (startIndexOld + dtr.PositionsRemoved > _dtrs[i].StartIndex) { merge = true; } // else new dtr has to be inserted at position 'i' break; } // b) New DTR starts in the range of the previous DTR. In this case // merge these 2 DTRs else if (startIndexOld <= _dtrs[i].StartIndex + _dtrs[i].PositionsAdded) { // merge with existing dtr at position 'i' merge = true; break; } // c) No intersection has been found, go to the next DTR in the list. startIndexOld -= _dtrs[i].PositionsAdded - _dtrs[i].PositionsRemoved; ++i; } // Update dcp of the new DTR, to reflect position before any tree changes. dtr.StartIndex = startIndexOld; } // 2) Insert new DTR into the list, merge if necessary if (i < _count) { if (merge) { // The simplest way to merge these two DTRs is to add together // cchAdded/cchDeleted form both DTRs, but it will invalidate more // than required. Formula used below is more accurate. // a) New DTR does intersect with the beginning of the next DTR, // in this case merge these 2 DTRs. // * dcp = dcpN (since it starts before dcpO) // * add = addN + addO - min(addO, delN - (dcpO - dcpN)) // * del = delN + delO - min(addO, delN - (dcpO - dcpN)) // NOTE: dcpO - dcpN is always <= delN if (dtr.StartIndex < _dtrs[i].StartIndex) { int delta = _dtrs[i].StartIndex - dtr.StartIndex; int adjust = Math.Min(_dtrs[i].PositionsAdded, dtr.PositionsRemoved - delta); _dtrs[i].StartIndex = dtr.StartIndex; _dtrs[i].PositionsAdded += dtr.PositionsAdded - adjust; _dtrs[i].PositionsRemoved += dtr.PositionsRemoved - adjust; } // b) New DTR starts in the range of the previous DTR. In this case // merge these 2 DTRs. // * dcp = dcpO (since it starts before dcpN) // * add = addN + addO - min(delN, addO - (dcpN - dcpO)) // * del = delN + delO - min(delN, addO - (dcpN - dcpO)) // NOTE: dcpN - dcpO is always <= addO else { int delta = dtr.StartIndex - _dtrs[i].StartIndex; int adjust = Math.Min(dtr.PositionsRemoved, _dtrs[i].PositionsAdded - delta); //_dtrs[i].dcp: no need to change it _dtrs[i].PositionsAdded += dtr.PositionsAdded - adjust; _dtrs[i].PositionsRemoved += dtr.PositionsRemoved - adjust; } } else { // The new DTR has to be inserted before DTR at position 'i'. if (_count == _dtrs.Length) { Resize(); } Array.Copy(_dtrs, i, _dtrs, i + 1, _count - i); _dtrs[i] = dtr; ++_count; } MergeWithNext(i); } else { // The new DTR has to be appended to the end of the list. if (_count == _dtrs.Length) { Resize(); } _dtrs[_count] = dtr; ++_count; } #if TEXTPANELLAYOUTDEBUG System.Text.StringBuilder msg = new System.Text.StringBuilder(); msg.Append("Merge DTR (" + dtr.StartIndex + "," + dtr.PositionsAdded + "," + dtr.PositionsRemoved + ") ->"); for (i = 0; i < _count; i++) { msg.Append(" (" + _dtrs[i].StartIndex + "," + _dtrs[i].PositionsAdded + "," + _dtrs[i].PositionsRemoved + ")"); } TextPanelDebug.Log(msg.ToString(), TextPanelDebug.Category.ContentChange); #endif }
// ------------------------------------------------------------------ // Merge new DTR with list of exising DTRs: // 1) Convert startIndex to index reflecting position before any changes. // 2) Merge it with existing list of DTRs. // // dtr - New DTR to be merged with exising list of DTRs. // ------------------------------------------------------------------ internal void Merge(DirtyTextRange dtr) { bool merge = false; int i = 0; int startIndexOld = dtr.StartIndex; // 1) Convert StartIndex to index reflecting position before any changes. // And find out if there is a need to merge DTRs if (_count > 0) { while (i < _count) { // a) New DTR starts before the next one. In this case there are // two possibilities: // * new DTR does not intersect with the beginning of the next DTR, // in this case insert new DTR before the next one. // * new DTR does intersect with the beginning of the next DTR, // in this case merge these 2 DTRs if (startIndexOld < _dtrs[i].StartIndex) { if (startIndexOld + dtr.PositionsRemoved > _dtrs[i].StartIndex) { merge = true; } // else new dtr has to be inserted at position 'i' break; } // b) New DTR starts in the range of the previous DTR. In this case // merge these 2 DTRs else if (startIndexOld <= _dtrs[i].StartIndex + _dtrs[i].PositionsAdded) { // merge with existing dtr at position 'i' merge = true; break; } // c) No intersection has been found, go to the next DTR in the list. startIndexOld -= _dtrs[i].PositionsAdded - _dtrs[i].PositionsRemoved; ++i; } // Update dcp of the new DTR, to reflect position before any tree changes. dtr.StartIndex = startIndexOld; } // 2) Insert new DTR into the list, merge if necessary if (i < _count) { if (merge) { // The simplest way to merge these two DTRs is to add together // cchAdded/cchDeleted form both DTRs, but it will invalidate more // than required. Formula used below is more accurate. // a) New DTR does intersect with the beginning of the next DTR, // in this case merge these 2 DTRs. // * dcp = dcpN (since it starts before dcpO) // * add = addN + addO - min(addO, delN - (dcpO - dcpN)) // * del = delN + delO - min(addO, delN - (dcpO - dcpN)) // NOTE: dcpO - dcpN is always <= delN if (dtr.StartIndex < _dtrs[i].StartIndex) { int delta = _dtrs[i].StartIndex - dtr.StartIndex; int adjust = Math.Min(_dtrs[i].PositionsAdded, dtr.PositionsRemoved - delta); _dtrs[i].StartIndex = dtr.StartIndex; _dtrs[i].PositionsAdded += dtr.PositionsAdded - adjust; _dtrs[i].PositionsRemoved += dtr.PositionsRemoved - adjust; } // b) New DTR starts in the range of the previous DTR. In this case // merge these 2 DTRs. // * dcp = dcpO (since it starts before dcpN) // * add = addN + addO - min(delN, addO - (dcpN - dcpO)) // * del = delN + delO - min(delN, addO - (dcpN - dcpO)) // NOTE: dcpN - dcpO is always <= addO else { int delta = dtr.StartIndex - _dtrs[i].StartIndex; int adjust = Math.Min(dtr.PositionsRemoved, _dtrs[i].PositionsAdded - delta); //_dtrs[i].dcp: no need to change it _dtrs[i].PositionsAdded += dtr.PositionsAdded - adjust; _dtrs[i].PositionsRemoved += dtr.PositionsRemoved - adjust; } } else { // The new DTR has to be inserted before DTR at position 'i'. if (_count == _dtrs.Length) { Resize(); } Array.Copy(_dtrs, i, _dtrs, i+1, _count-i); _dtrs[i] = dtr; ++_count; } MergeWithNext(i); } else { // The new DTR has to be appended to the end of the list. if (_count == _dtrs.Length) { Resize(); } _dtrs[_count] = dtr; ++_count; } #if TEXTPANELLAYOUTDEBUG System.Text.StringBuilder msg = new System.Text.StringBuilder(); msg.Append("Merge DTR (" + dtr.StartIndex + "," + dtr.PositionsAdded + "," + dtr.PositionsRemoved + ") ->"); for (i = 0; i < _count; i++) { msg.Append(" (" + _dtrs[i].StartIndex + "," + _dtrs[i].PositionsAdded + "," + _dtrs[i].PositionsRemoved + ")"); } TextPanelDebug.Log(msg.ToString(), TextPanelDebug.Category.ContentChange); #endif }
// ------------------------------------------------------------------ // Build UpdateRecord from DTR. // ------------------------------------------------------------------ private UpdateRecord UpdateRecordFromDtr( DtrList dtrs, DirtyTextRange dtr, int dcpContent) { UpdateRecord ur = new UpdateRecord(); // (1) Initialize DTR ur.Dtr = dtr; // (2) Find first paragraph affected by DTR BaseParagraph para = _firstChild; BaseParagraph paraPrev = null; // There might be gaps between paragraphs (example: content of List element, only // nested Lists or ListItems are valid paragraphs, all other content is skipped). // For this reason always use para.ParagraphStartCharacterPosition to get the first // character position of the current paragraph. int dcpPara = dcpContent; if (dcpPara < ur.Dtr.StartIndex) { while (para != null) { // We're looking for first affected para - We start with dco content. For // all paras but TextParagraph, StartPosition/EndPosition is // |<Section></Section>|, so insertion at edge points is adding new paragraphs, // not affecting current. For textpara, <Paragraph>|abcde|</Paragraph>, // insertion at edge points is a change inside for that text paragraph. if ( dcpPara + para.LastFormatCch > ur.Dtr.StartIndex || (dcpPara + para.LastFormatCch == ur.Dtr.StartIndex && para is TextParagraph)) { break; // the first paragraph is found } dcpPara += para.LastFormatCch; paraPrev = para; para = para.Next; } } // else the change is before the first paragraph ur.FirstPara = para; // (3) Determine change type for the fist affected paragraph if (para == null) { ur.ChangeType = PTS.FSKCHANGE.fskchNew; } else if (dcpPara < ur.Dtr.StartIndex) { ur.ChangeType = PTS.FSKCHANGE.fskchInside; } else { ur.ChangeType = PTS.FSKCHANGE.fskchNew; } // (4) Find synchronization point, the first paragraph after DTR ur.SyncPara = null; while (para != null) { if ( (dcpPara + para.LastFormatCch > ur.Dtr.StartIndex + ur.Dtr.PositionsRemoved) || (dcpPara + para.LastFormatCch == ur.Dtr.StartIndex + ur.Dtr.PositionsRemoved && ur.ChangeType != PTS.FSKCHANGE.fskchNew)) { ur.SyncPara = para.Next; break; } dcpPara += para.LastFormatCch; para = para.Next; } return ur; }
// Callback from the TextContainer on a document edit. private void OnTextContainerChange(object sender, TextContainerChangeEventArgs args) { if (args.Count == 0) { // A no-op for this control. Happens when IMECharCount updates happen // without corresponding SymbolCount changes. return; } // // Add the change to our dirty list. // if (_dirtyList == null) { _dirtyList = new DtrList(); } DirtyTextRange dirtyTextRange = new DirtyTextRange(args); _dirtyList.Merge(dirtyTextRange); // // Force a re-measure. // InvalidateMeasure(); }
// Helper for IncrementalMeasureLinesAfterInsert, IncrementalMeasureLinesAfterDelete. // Formats line until we hit a synchronization point, a position where we know // following lines could not be affected by the change. private void SyncLineMetrics(DirtyTextRange range, double constraintWidth, LineProperties lineProperties, TextBoxLine line, bool endOfParagraph, int lineIndex, int lineOffset) { bool offsetSyncOk = (range.PositionsAdded == 0 || range.PositionsRemoved == 0); int lastCoveredCharOffset = range.StartIndex + Math.Max(range.PositionsAdded, range.PositionsRemoved); // Keep updating lines until we find a synchronized position. while (!endOfParagraph && (lineIndex == _lineMetrics.Count || !offsetSyncOk || lineOffset != _lineMetrics[lineIndex].Offset)) { if (lineIndex < _lineMetrics.Count && lineOffset >= _lineMetrics[lineIndex].EndOffset) { // If the current line offset starts past the current line metric offset, // remove the metric. This happens when the previous line // frees up enough space to completely consume the following line. // We can't simply replace the record without potentially missing our // [....] position. _lineMetrics.RemoveAt(lineIndex); // RemoveLineVisualRange(lineIndex, 1); } else { using (line) { line.Format(lineOffset, constraintWidth, constraintWidth, lineProperties, _cache.TextRunCache, _cache.TextFormatter); LineRecord record = new LineRecord(lineOffset, line); if (lineIndex == _lineMetrics.Count || lineOffset + line.Length <= _lineMetrics[lineIndex].Offset) { // The new line preceeds the old line, insert a new record. // _lineMetrics.Insert(lineIndex, record); AddLineVisualPlaceholder(lineIndex); } else { // We expect to be colliding with the old line directly. // If we extend past it, we're in danger of needlessly // re-formatting the entire doc (ie, we miss the real // [....] position and don't stop until EndOfParagraph). Invariant.Assert(lineOffset < _lineMetrics[lineIndex].EndOffset); _lineMetrics[lineIndex] = record; ClearLineVisual(lineIndex); // If this line ends past the invalidated region, and it // has a hard line break, it's safe to synchronize on the next // line metric with a matching start offset. offsetSyncOk |= lastCoveredCharOffset <= record.EndOffset && line.HasLineBreak; } lineIndex++; lineOffset += line.Length; endOfParagraph = line.EndOfParagraph; } } } // Remove any trailing lines that got absorbed into the new last line. if (endOfParagraph && lineIndex < _lineMetrics.Count) { int count = _lineMetrics.Count - lineIndex; _lineMetrics.RemoveRange(lineIndex, count); RemoveLineVisualRange(lineIndex, count); } }
// Token: 0x060067B2 RID: 26546 RVA: 0x001D0EDC File Offset: 0x001CF0DC internal void Merge(DirtyTextRange dtr) { bool flag = false; int i = 0; int num = dtr.StartIndex; if (this._count > 0) { while (i < this._count) { if (num < this._dtrs[i].StartIndex) { if (num + dtr.PositionsRemoved > this._dtrs[i].StartIndex) { flag = true; break; } break; } else { if (num <= this._dtrs[i].StartIndex + this._dtrs[i].PositionsAdded) { flag = true; break; } num -= this._dtrs[i].PositionsAdded - this._dtrs[i].PositionsRemoved; i++; } } dtr.StartIndex = num; } if (i < this._count) { if (flag) { if (dtr.StartIndex < this._dtrs[i].StartIndex) { int num2 = this._dtrs[i].StartIndex - dtr.StartIndex; int num3 = Math.Min(this._dtrs[i].PositionsAdded, dtr.PositionsRemoved - num2); this._dtrs[i].StartIndex = dtr.StartIndex; DirtyTextRange[] dtrs = this._dtrs; int num4 = i; dtrs[num4].PositionsAdded = dtrs[num4].PositionsAdded + (dtr.PositionsAdded - num3); DirtyTextRange[] dtrs2 = this._dtrs; int num5 = i; dtrs2[num5].PositionsRemoved = dtrs2[num5].PositionsRemoved + (dtr.PositionsRemoved - num3); } else { int num6 = dtr.StartIndex - this._dtrs[i].StartIndex; int num7 = Math.Min(dtr.PositionsRemoved, this._dtrs[i].PositionsAdded - num6); DirtyTextRange[] dtrs3 = this._dtrs; int num8 = i; dtrs3[num8].PositionsAdded = dtrs3[num8].PositionsAdded + (dtr.PositionsAdded - num7); DirtyTextRange[] dtrs4 = this._dtrs; int num9 = i; dtrs4[num9].PositionsRemoved = dtrs4[num9].PositionsRemoved + (dtr.PositionsRemoved - num7); } DirtyTextRange[] dtrs5 = this._dtrs; int num10 = i; dtrs5[num10].FromHighlightLayer = (dtrs5[num10].FromHighlightLayer & dtr.FromHighlightLayer); } else { if (this._count == this._dtrs.Length) { this.Resize(); } Array.Copy(this._dtrs, i, this._dtrs, i + 1, this._count - i); this._dtrs[i] = dtr; this._count++; } this.MergeWithNext(i); return; } if (this._count == this._dtrs.Length) { this.Resize(); } this._dtrs[this._count] = dtr; this._count++; }
// ------------------------------------------------------------------ // Construct a DtrList. The array of DTRs initially can store up to // 4 entries. Upon adding elements the capacity increased in multiples // of two as required. // ------------------------------------------------------------------ internal DtrList() { _dtrs = new DirtyTextRange[_defaultCapacity]; _count = 0; }
/// <summary> /// Invalidates a portion of text affected by a highlight change. /// </summary> /// <param name="sender"></param> /// <param name="args"></param> private void OnHighlightChanged(object sender, HighlightChangedEventArgs args) { TextSegment textSegment; int i; Invariant.Assert(args != null); Invariant.Assert(args.Ranges != null); Invariant.Assert(_structuralCache != null && _structuralCache.IsFormattedOnce, "Unexpected Highlights.Changed callback before first format!"); // Detect invalid content change operations. if (_structuralCache.IsFormattingInProgress) { _structuralCache.OnInvalidOperationDetected(); throw new InvalidOperationException(SR.Get(SRID.FlowDocumentInvalidContnetChange)); } // The only supported highlight type for FlowDocument is SpellerHightlight. // TextSelection and HighlightComponent are ignored, because they are handled by // separate layer. if (args.OwnerType != typeof(SpellerHighlightLayer)) { return; } if (args.Ranges.Count > 0) { // Invalidate affected pages and break records. // We DTR invalidate if we're using a formatter as well for incremental update. if (_formatter == null || !(_formatter is FlowDocumentFormatter)) { _structuralCache.InvalidateFormatCache(/*Clear structure*/ false); } // Notify formatter about content invalidation. if (_formatter != null) { for (i = 0; i < args.Ranges.Count; i++) { textSegment = (TextSegment)args.Ranges[i]; _formatter.OnContentInvalidated(false, textSegment.Start, textSegment.End); if (_formatter is FlowDocumentFormatter) { DirtyTextRange dtr = new DirtyTextRange(textSegment.Start.Offset, textSegment.Start.GetOffsetToPosition(textSegment.End), textSegment.Start.GetOffsetToPosition(textSegment.End) ); _structuralCache.AddDirtyTextRange(dtr); } } } } }
// Measures content invalidated due to a TextContainer change. private void IncrementalMeasureLinesAfterInsert(double constraintWidth, LineProperties lineProperties, DirtyTextRange range, ref Size desiredSize) { int delta = range.PositionsAdded - range.PositionsRemoved; Invariant.Assert(delta >= 0); int lineIndex = GetLineIndexFromOffset(range.StartIndex, LogicalDirection.Forward); if (delta > 0) { // Increment of the offsets of all following lines. // for (int i = lineIndex + 1; i < _lineMetrics.Count; i++) { _lineMetrics[i].Offset += delta; } } TextBoxLine line = new TextBoxLine(this); int lineOffset; bool endOfParagraph = false; // We need to re-format the previous line, because if someone inserted // a hard break, the first directly affected line might now be shorter // and mergeable with its predecessor. if (lineIndex > 0) // { FormatFirstIncrementalLine(lineIndex - 1, constraintWidth, lineProperties, line, out lineOffset, out endOfParagraph); } else { lineOffset = _lineMetrics[lineIndex].Offset; } // Format the line directly affected by the change. // If endOfParagraph == true, then the line was absorbed into its // predessor (because its new content is thinner, or because the // TextWrapping property changed). if (!endOfParagraph) { using (line) { line.Format(lineOffset, constraintWidth, constraintWidth, lineProperties, _cache.TextRunCache, _cache.TextFormatter); _lineMetrics[lineIndex] = new LineRecord(lineOffset, line); lineOffset += line.Length; endOfParagraph = line.EndOfParagraph; } ClearLineVisual(lineIndex); lineIndex++; } // Recalc the following lines not directly affected as needed. SyncLineMetrics(range, constraintWidth, lineProperties, line, endOfParagraph, lineIndex, lineOffset); desiredSize = BruteForceCalculateDesiredSize(); }
// Measures content invalidated due to a TextContainer change (rather than // a constraint change). // // Returns the full content size, omitting any unanalyzed content // at the document end (due to pending background layout). private Size IncrementalMeasure(double constraintWidth, LineProperties lineProperties) { Invariant.Assert(_dirtyList != null); Invariant.Assert(_dirtyList.Length > 0); // We only allocate _dirtyList when it has content. Size desiredSize = _contentSize; DirtyTextRange range = _dirtyList[0]; // Background layout may be running, in which case we need to // "clip" the scope of this incremental edit. We want to ignore // changes that extend past the area of the document we're already // tracking. if (range.StartIndex > _lineMetrics[_lineMetrics.Count - 1].EndOffset) { Invariant.Assert(this.IsBackgroundLayoutPending); return desiredSize; } // Merge the dirty list into a single superset DirtyTextRange. // int previousOffset = range.StartIndex; int positionsAdded = range.PositionsAdded; int positionsRemoved = range.PositionsRemoved; for (int i = 1; i < _dirtyList.Length; i++) { range = _dirtyList[i]; if (range.StartIndex > _lineMetrics[_lineMetrics.Count - 1].EndOffset) { Invariant.Assert(this.IsBackgroundLayoutPending); break; } int rangeDistance = range.StartIndex - previousOffset; positionsAdded += rangeDistance + range.PositionsAdded; positionsRemoved += rangeDistance + range.PositionsRemoved; previousOffset = range.StartIndex; } range = new DirtyTextRange(_dirtyList[0].StartIndex, positionsAdded, positionsRemoved); if (range.PositionsAdded >= range.PositionsRemoved) { IncrementalMeasureLinesAfterInsert(constraintWidth, lineProperties, range, ref desiredSize); } else if (range.PositionsAdded < range.PositionsRemoved) { IncrementalMeasureLinesAfterDelete(constraintWidth, lineProperties, range, ref desiredSize); } return desiredSize; }
//------------------------------------------------------------------- // Defers remaining text formatting to background - treated as if new text //------------------------------------------------------------------- private void DeferFormattingToBackground() { int cpLast = _section.StructuralCache.BackgroundFormatInfo.CPInterrupted; int cpTextContainer = _section.StructuralCache.BackgroundFormatInfo.CchAllText; DirtyTextRange dtr = new DirtyTextRange(cpLast, cpTextContainer - cpLast, cpTextContainer - cpLast); _section.StructuralCache.AddDirtyTextRange(dtr); _backgroundFormatOperation = Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, BackgroundUpdateCallback, this); }
// Measures content invalidated due to a TextContainer change. private void IncrementalMeasureLinesAfterDelete(double constraintWidth, LineProperties lineProperties, DirtyTextRange range, ref Size desiredSize) { int delta = range.PositionsAdded - range.PositionsRemoved; Invariant.Assert(delta < 0); int firstLineIndex = GetLineIndexFromOffset(range.StartIndex); // Clip the scope of the affected lines to the region of the document // we've already inspected. Clipping happens when background layout // has not yet completed but an incremental update happens. int endOffset = range.StartIndex + -delta - 1; if (endOffset > _lineMetrics[_lineMetrics.Count - 1].EndOffset) { Invariant.Assert(this.IsBackgroundLayoutPending); endOffset = _lineMetrics[_lineMetrics.Count - 1].EndOffset; if (range.StartIndex == endOffset) { // Nothing left to do until background layout runs. return; } } int lastLineIndex = GetLineIndexFromOffset(endOffset); // Increment the offsets of all following lines. // for (int i = lastLineIndex + 1; i < _lineMetrics.Count; i++) { _lineMetrics[i].Offset += delta; } TextBoxLine line = new TextBoxLine(this); int lineIndex = firstLineIndex; int lineOffset; bool endOfParagraph; // We need to re-format the previous line, because if someone inserted // a hard break, the first directly affected line might now be shorter // and mergeable with its predecessor. if (lineIndex > 0) // { FormatFirstIncrementalLine(lineIndex - 1, constraintWidth, lineProperties, line, out lineOffset, out endOfParagraph); } else { lineOffset = _lineMetrics[lineIndex].Offset; endOfParagraph = false; } // // Update the first affected line. If it's completely covered, remove it entirely below. if (!endOfParagraph && (range.StartIndex > lineOffset || range.StartIndex + -delta < _lineMetrics[lineIndex].EndOffset)) { // Only part of the line is covered, reformat it. using (line) { line.Format(lineOffset, constraintWidth, constraintWidth, lineProperties, _cache.TextRunCache, _cache.TextFormatter); _lineMetrics[lineIndex] = new LineRecord(lineOffset, line); lineOffset += line.Length; endOfParagraph = line.EndOfParagraph; } ClearLineVisual(lineIndex); lineIndex++; } // Remove all the following lines that are completely covered. // _lineMetrics.RemoveRange(lineIndex, lastLineIndex - lineIndex + 1); RemoveLineVisualRange(lineIndex, lastLineIndex - lineIndex + 1); // Recalc the following lines not directly affected as needed. SyncLineMetrics(range, constraintWidth, lineProperties, line, endOfParagraph, lineIndex, lineOffset); desiredSize = BruteForceCalculateDesiredSize(); }