Beispiel #1
0
		//* this asterisk is referring to throughout the code calling the ctor ExplicitIndex(int, ElementsCollection, IndexRoundingBehavior)
		//This method is usually called when converting a publicly specified index (a palpable index) to an explicit index
		//The statement of specifying * is that the specified rounding behavior is correct. The considerations to make are the following:
		//- when the palpableIndex is meant to point to the end of a range, that is, excluding the glyph or box composition at that index,
		//  then 'Last' is correct, as it selects the least amount of elements. 
		//	'Last' can never point to empty elements, so the invariant that a palpable index cannot refer to empty elements is satisfied
		//- when the palpableIndex is meant to point to the glyph or box composition at that index, 
		//	then 'Extra' is correct.
		//	'Extra' skips empty elements, so the invariant that a palpable index cannot refer to empty elements is satisfied (hence Extra and not ExtraUnlessEmpty). 

		/// <summary> Returns the correct nested index when converting a palpable index to a nested index and the end of an element is exactly reached. </summary>
		/// <param name="roundingBehavior"> The rounding behavior to apply. </param>
		/// <param name="elementCollection"> The box in which this palpable index is converted into a nested one. </param>
		private void ApplyRoundingBehavior(IndexRoundingBehavior roundingBehavior, BoxElementsCollection elementCollection)
		{
			Contract.Requires(elementCollection != null);
			Contract.Requires(ElementIndex < elementCollection.ElementCount);

			bool roundToNextElement;
			switch (roundingBehavior)
			{
				case IndexRoundingBehavior.Last:
					//Specifies that when a position points to the end of an element and at the end another, the first is used.
					roundToNextElement = false;
					break;
				case IndexRoundingBehavior.Extra:
					//Specifies that when a position points to the end of an element and at the end another, the latter is used.
					roundToNextElement = true;
					//skip empty elements:
					while (elementCollection.IsEmptyAt(ElementIndex + 1))
						ElementIndex++;
					break;
				case IndexRoundingBehavior.ExtraUnlessEmpty:
					if(elementCollection.IsEmptyAt(ElementIndex))
						goto case IndexRoundingBehavior.Last;
					else
						goto case IndexRoundingBehavior.Extra;
				case IndexRoundingBehavior.ToText:
					//Specifies that a MathGlyphRun is preferred over other box element types. In ambiguous cases between two MathGlyphRuns, the latter is chosen. At the end of a box, there is no ambiguity.
					if (elementCollection[ElementIndex] is GlyphRunViewModel)
					{
						roundToNextElement = false;
					}
					else
					{
						Contract.Assume(ElementIndex + 1 < elementCollection.ElementCount && elementCollection[ElementIndex + 1] is GlyphRunViewModel, "The Box.Elements invariant is violated");
						roundToNextElement = true;
					}
					break;
				default:
					throw new ContractException("Forgot to handle case");
			}
			if (roundToNextElement)
			{
				this.ElementIndex++;
				this.IndexInElement = InternalIndex.First;
			}
			else
			{
				//ElementIndex already set correctly
				this.IndexInElement = InternalIndex.Last.MakeLastExplicit(elementCollection, ElementIndex);
			}
		}
Beispiel #2
0
		public ExplicitIndex(IBoxElement element, InternalIndex indexInElement)
			: this()
		{
			Contract.Requires(element != null);
			this.ElementIndex = element.Box.Elements.IndexOf(element);
			Contract.Requires(this.ElementIndex != -1, "The specified element is not in the specified box. ");
			this.IndexInElement = indexInElement;
		}
Beispiel #3
0
		/// <summary> Creates a new explicit index pointing to the physical position at the specified index in the specified element collection. </summary>
		/// <param name="palpableIndex"> The physical index to which this new index is to point. </param>
		/// <param name="elements"> The elements in which this new index is to point. </param>
		/// <param name="roundingBehavior"> The rounding applied to this new index. </param>
		public ExplicitIndex(int palpableIndex, BoxElementsCollection elements, IndexRoundingBehavior roundingBehavior)
			: this()
		{
			Contract.Requires(palpableIndex >= 0);
			Contract.Requires(elements != null);
			Contract.Requires(Enum.IsDefined(typeof(IndexRoundingBehavior), roundingBehavior));

			//the default explicit index is allowed, even when there are no elements 
			if (elements.ElementCount == 0)
			{
				Contract.Assert(palpableIndex == 0, "Palpable index out of range");
				return;
			}

			int palpableRemainder = palpableIndex;
			for (ElementIndex = (ElementIndex)0; ElementIndex < elements.ElementCount; ElementIndex++)
			{
				//get the number of positions in the current element
				int elementCaretPositionCount = ((IBoxElement)elements[ElementIndex]).CaretPositionCount;

				//check whether the remainder fits in this element, in which case the result is found, since this palpable index then points to the current element
				if (elementCaretPositionCount - 1 > palpableRemainder)
				{
					this.IndexInElement = (InternalIndex)palpableRemainder;
					Contract.Assert(this.ToPalpableIndex(elements) == palpableIndex, "Consistency check failed");
					return;
				}

				//if this palpable index points exactly to the end of the current element, then rounding behavior applies
				if (elementCaretPositionCount - 1 == palpableRemainder)
				{
					ApplyRoundingBehavior(roundingBehavior, elements);
					Contract.Assert(this.ToPalpableIndex(elements) == palpableIndex, "Consistency check failed");
					return;
				}

				//get the remainder of the palpable index
				//this has the complication that some palpable indices are counted twice, 
				//namely when the end of an element and the beginning of the next element both have an associated index, but which physically share the same location
				//it is assumed that whenever the element caret position count (ECPC) is non-zero, that then the element represents both the beginning and the end.
				//hence, when two consecutive elements have nonzero ECPC , exactly one position is counted doubly (the position they share)
				//the ideal way to fix this would be to remove one from the palpable remainder if the current element and the next have nonzero ECPC, however, the next element cannot be obtained yet.
				//Therefore, I assume the next has nonzero ECPC and subtract one every time. This is corrected whenever an element with ECPC == 0 is encountered 
				//(since this default subtraction of one is only wrong there). It can be fixed by adding one in that case. 
				palpableRemainder -= elementCaretPositionCount - 1;
			}

			//if it is still positive after all elements have been enumerated over, this index is out of range
			throw new ArgumentOutOfRangeException("elements", "The specified box does not contain this index. ");
		}
Beispiel #4
0
		public ExplicitIndex(ElementIndex elementIndex, InternalIndex indexInElement)
			: this()
		{
			this.ElementIndex = elementIndex;
			this.IndexInElement = indexInElement;
		}