Exemplo n.º 1
0
		/// <summary> If this index is the special value "Last", then its explicit value is returned. Otherwise, its this instance is simply returned. </summary>
		/// <param name="box"> The box in which this internal index points. </param>
		/// <param name="elementIndex"> The index of the element in which this internal index points. </param>
		internal InternalIndex MakeLastExplicit(BoxElementsCollection box, ElementIndex elementIndex)
		{
			if (this != Last)
				return this;
			IBoxElement element = (IBoxElement)box[elementIndex];
			return MakeLastExplicit(element);
		}
Exemplo n.º 2
0
		/// <summary> Gets whether this explicit index will not throw an IndexOutOfRangeException in the specified box. </summary>
		/// <param name="elementsCollection"> The elements of a box in which this index may point. </param>
		public bool IsInRange(BoxElementsCollection elementsCollection)
		{
			if (this.ElementIndex == elementsCollection.ElementCount)
				return this.IndexInElement == 0;
			if (this.ElementIndex < elementsCollection.ElementCount)
				return this.IndexInElement <= ((IBoxElement)elementsCollection[this.ElementIndex]).CaretPositionCount;
			return false;
		}
Exemplo n.º 3
0
		internal static bool IsInRange(int position, BoxElementsCollection elements)
		{
			Contract.Requires(elements != null);
			if (position < 0)
				return false;

			//this is a debugging purpose only method, so calculating an explicit index just to check for being in range is acceptable
			int remainder = position;
			for (ElementIndex elementIndex = (ElementIndex)0; elementIndex < elements.ElementCount; elementIndex++)
			{
				int elementCaretPositionCount = elements[elementIndex].CaretPositionCount;
				if (elementCaretPositionCount > remainder)
					return true;
				remainder -= elementCaretPositionCount - 1;
			}
			return false;
		}
		private static NotifyBoxElementsChangedEventArgs CreateImpl(BoxElementsCollection sender, NotifyCollectionChangedAction action, IList newItems, IList oldItems, int newStartingPalpableIndex, int oldStartingPalpableIndex)
		{
			Contract.Requires(newItems?.Cast<object>().All(element => element.HasAnyTypeOf(typeof(Glyph), typeof(BoxCompositionViewModel))) != false);
			Contract.Requires(oldItems?.Cast<object>().All(element => element.HasAnyTypeOf(typeof(Glyph), typeof(BoxCompositionViewModel))) != false);

			if (sender.Box.IsPlaceholder && newItems != null && newItems.Count != 0)//newItems.Count == 0 is only a leaking abstraction (of element-wise to bc/glyph-wise). It will be filtered out shortly
			{
				//checks that if the sender is a placeholder and items were added, that it must be the placeholder glyph and nothing else
				Contract.Assume(newItems.Count == 1 && newItems[0] is Glyph && Placeholder.IsPlaceholder((Glyph)newItems[0]));

				//removes addition of the placeholder glyph
				newItems = EmptyCollection<object>.Array;
				if (action == NotifyCollectionChangedAction.Replace)
					action = NotifyCollectionChangedAction.Remove;//if something was removed and a placeholder was inserted, we only notify the removal.
				else
					Contract.Assert(action == NotifyCollectionChangedAction.Add);//we're never going to invoke an event with this e, so it doesn't matter what NotifyCollectionChangedAction we pick
			}


			switch (action)
			{
				case NotifyCollectionChangedAction.Add:
					return new NotifyBoxElementsChangedEventArgs(sender, action, newItems, newStartingPalpableIndex);
				case NotifyCollectionChangedAction.Remove:
					return new NotifyBoxElementsChangedEventArgs(sender, action, oldItems, oldStartingPalpableIndex);
				case NotifyCollectionChangedAction.Replace:
					throw new NotImplementedException("Not sure if I should specify e.NewStartingIndex or e.OldStartingIndex as fourth argument");
					return new NotifyBoxElementsChangedEventArgs(sender, action, newItems, oldItems, newStartingPalpableIndex);
				case NotifyCollectionChangedAction.Move:
					throw new NotImplementedException("Not sure if I should specify e.NewItems or e.OldItems as second argument");
					return new NotifyBoxElementsChangedEventArgs(sender, action, newItems, newStartingPalpableIndex, oldStartingPalpableIndex);
				case NotifyCollectionChangedAction.Reset:
					//throw new NotImplementedException("Not sure if I should specify e.OldItems at all");
					return new NotifyBoxElementsChangedEventArgs(sender, action, oldItems);
				default:
					throw new ArgumentException();
			}
		}
Exemplo n.º 5
0
		/// <summary> Returns a new explicit index, where ff the internal index of this instance is the special value "Last", then it is converted to its explicit value. </summary>
		/// <param name="box"> The box in which this instance points. </param>
		internal ExplicitIndex MakeLastExplicit(BoxElementsCollection box)
		{
			if (this.IndexInElement == InternalIndex.Last)
				return new ExplicitIndex(this.ElementIndex, this.IndexInElement.MakeLastExplicit(box, this.ElementIndex));// (InternalIndex)((IBoxElement)box[this.ElementIndex]).CaretPositionCount);
			return this;
		}
Exemplo n.º 6
0
		public bool IsRoundedUsing(IndexRoundingBehavior roundingBehavior, BoxElementsCollection box)
		{
			Contract.Requires(box != null);
			Contract.Requires(Enum.IsDefined(typeof(IndexRoundingBehavior), roundingBehavior));

			return this == this.Round(roundingBehavior, box);
		}
Exemplo n.º 7
0
		public ExplicitIndex Round(IndexRoundingBehavior newRoundingBehavior, BoxElementsCollection box)
		{
			//note that this method is implemented inefficiently. A method with an implementation akin to ExplicitIndex.ctor(int, Box, IndexRoundingBehavior) could be much faster
			return new ExplicitIndex(this.ToPalpableIndex(box), box, newRoundingBehavior);
		}
Exemplo n.º 8
0
		/// <summary> Gets the palpable index of this internal index. </summary>
		/// <param name="owner"> The box in which this internal index points. </param>
		public int ToPalpableIndex(BoxElementsCollection owner)
		{
			//A palbable index is the index as perceived by the user: each character and each box composition takes up one position

			//So a character in a unmodifiable box/glyph run cannot be represented by a palpable index
			//but each palpable index IS representable by one or more explicit indices
			//the modifiability aspect is taken care of in this method by CaretPositionCount == 0

			//asserts no two consecutive elements having no caret positions exist
			Contract.Assert((owner.Cast<IBoxElement>().Windowed2().All(tup => tup.Item1.CaretPositionCount != 0 || tup.Item2.CaretPositionCount != 0)));

			int cumulativePreviousElementsCaretCount = 0;
			for (ElementIndex i = (ElementIndex)0; i < this.ElementIndex; i++)
			{
				int currentElementCaretCount = ((IBoxElement)owner[i]).CaretPositionCount;
				if (currentElementCaretCount != 0)
					currentElementCaretCount--;//the end position isn't counted
				/*	if (currentElementCaretCount == 0) 	currentElementCaretCount = 1;//e.g. a box composition. Counts the end caret position of the previous element, that isn't counted by the current element
					else								currentElementCaretCount--;//e.g. a glyph run
				 */

				cumulativePreviousElementsCaretCount += currentElementCaretCount;
			}

			int internalIndex = IndexInElement.MakeLastExplicit(owner, this.ElementIndex).Value;
			int result = cumulativePreviousElementsCaretCount + internalIndex;
			return result;
		}
Exemplo n.º 9
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);
			}
		}
Exemplo n.º 10
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. ");
		}
Exemplo n.º 11
0
		public int ToPalpableIndex(BoxElementsCollection owner)
		{
			return new ExplicitIndex(this, InternalIndex.First).ToPalpableIndex(owner);
		}
Exemplo n.º 12
0
		/// <summary> Initializes a new instances that describes a multi-item change. </summary>
		public NotifyBoxElementsChangedEventArgs(BoxElementsCollection owner, NotifyCollectionChangedAction action, IList changedItems)
			: base(action, changedItems)
		{
			this.owner = owner;
		}
Exemplo n.º 13
0
		/// <summary> Initializes a new instances that describes a multi-item <code>NotifyCollectionChangedAction.Move</code> change. </summary>
		public NotifyBoxElementsChangedEventArgs(BoxElementsCollection owner, NotifyCollectionChangedAction action, IList changedItems, int index, int oldPalpableIndex)
			: base(action, changedItems, index, oldPalpableIndex)
		{
			this.owner = owner;
		}
Exemplo n.º 14
0
		/// <summary> Initializes a new instances that describes a multi-item <code>NotifyCollectionChangedAction.Replace</code> change. </summary>
		public NotifyBoxElementsChangedEventArgs(BoxElementsCollection owner, NotifyCollectionChangedAction action, IList newItems, IList oldItems, int startingPalpableIndex)
			: base(action, oldItems, newItems, startingPalpableIndex)
		{
			this.owner = owner;
		}
Exemplo n.º 15
0
		public static NotifyBoxElementsChangedEventArgs CreateForGlyphRun(BoxElementsCollection sender, NotifyCollectionChangedEventArgs e, int palpableGlyphRunStartIndex)
		{
			return CreateImpl(sender, e.Action, e.NewItems, e.OldItems, palpableGlyphRunStartIndex + e.NewStartingIndex, palpableGlyphRunStartIndex + e.OldStartingIndex);
		}
Exemplo n.º 16
0
		public static NotifyBoxElementsChangedEventArgs Create(BoxElementsCollection sender, NotifyCollectionChangedEventArgs e)
		{
			Func<int, int> toPalpableIndex = i => ((ElementIndex)i).ToPalpableIndex(sender);

			return CreateImpl(sender, e.Action, SelectIndividualGlyphsOrPassBoxComposition(e.NewItems), SelectIndividualGlyphsOrPassBoxComposition(e.OldItems), toPalpableIndex(e.NewStartingIndex), toPalpableIndex(e.OldStartingIndex));
		}