/// <summary>
 /// This guess factors in the placement of an occurrence in its segment for making other
 /// decisions like matching lowercase alternatives for sentence initial occurrences.
 /// </summary>
 public IAnalysis GetBestGuess(AnalysisOccurrence occurrence)
 {
     // first see if we can make a guess based on the lowercase form of a sentence initial (non-lowercase) wordform
     // TODO: make it look for the first word in the sentence...may not be at Index 0!
     if (occurrence.Analysis is IWfiWordform && occurrence.Index == 0)
     {
         ITsString tssWfBaseline        = occurrence.BaselineText;
         var       tracker              = new CpeTracker(Cache.WritingSystemFactory, tssWfBaseline);
         ILgCharacterPropertyEngine cpe = tracker.CharPropEngine(0);
         string sLower = cpe.ToLower(tssWfBaseline.Text);
         // don't bother looking up the lowercased wordform if the instanceOf is already in lowercase form.
         if (sLower != tssWfBaseline.Text)
         {
             ITsString    tssLower = TsStringUtils.MakeTss(sLower, TsStringUtils.GetWsAtOffset(tssWfBaseline, 0));
             IWfiWordform lowercaseWf;
             if (Cache.ServiceLocator.GetInstance <IWfiWordformRepository>().TryGetObject(tssLower, out lowercaseWf))
             {
                 IAnalysis bestGuess;
                 if (TryGetBestGuess(lowercaseWf, occurrence.BaselineWs, out bestGuess))
                 {
                     return(bestGuess);
                 }
             }
         }
     }
     if (occurrence.BaselineWs == -1)
     {
         return(null);                // happens with empty translation lines
     }
     return(GetBestGuess(occurrence.Analysis, occurrence.BaselineWs));
 }
        /// <summary>
        /// Returns a sequence of IAnalysis objects inclusive of the current occurrence
        /// and the end occurrence (otherAC). Parameter occurrence must occur AFTER 'this'
        /// occurrence.
        /// </summary>
        /// <param name="point2">An AnalysisOccurrence</param>
        /// <returns></returns>
        public IEnumerable <IAnalysis> GetAdvancingOccurrencesInclusiveOf(AnalysisOccurrence point2)
        {
            if (!point2.IsValid)
            {
                throw new ArgumentException("Invalid Analysis Occurrence");
            }
            if (Segment == point2.Segment)
            {
                // Case 1: Two points in same ISegment object
                if (Index > point2.Index)
                {
                    throw new ArgumentException("Second AnalysisOccurrence is before the first!");
                }
                // Copy out sub-array of Analyses between the two ACs indices
                return(Segment.AnalysesRS.ToList().GetRange(Index, point2.Index - Index + 1));
            }
            if (Segment.Owner == point2.Segment.Owner)
            {
                // Case 2: Two points in different ISegment objects, but in same StTxtPara
                if (Segment.IndexInOwner > point2.Segment.IndexInOwner)
                {
                    throw new ArgumentException("Second AnalysisOccurrence is before the first!");
                }
                // Need to copy out end of first segment, any segments in between and beginning of second segment
                return(CopyOutAnalysesFromMultipleSegments(point2));
            }
            var para1 = Segment.Owner as IStTxtPara;
            var para2 = point2.Segment.Owner as IStTxtPara;

            if (!(para1.Owner as IStText).ParagraphsOS.Contains(para2))
            {
                throw new ArgumentOutOfRangeException("point2", "AnalysisOccurrences are not within the same Text!");
            }
            throw new NotImplementedException("So far we only handle this in same Segment or Paragraph.");
        }
        /// <summary>
        /// Copy out IAnalysis objects from the end of this ISegment through the beginning of
        /// parameter's ISegment (inclusive). Also copies objects from intervening ISegments.
        /// Assumes parameter is in the same StTxtPara!
        /// </summary>
        /// <param name="point2"></param>
        /// <returns></returns>
        private IEnumerable <IAnalysis> CopyOutAnalysesFromMultipleSegments(AnalysisOccurrence point2)
        {
            // Need to copy out end of first segment, any segments in between and beginning of second segment
            var result   = new List <IAnalysis>();
            var paraSegs = (Segment.Owner as IStTxtPara).SegmentsOS;

            if (paraSegs == null)
            {
                throw new NullReferenceException("Unexpected error!");
            }
            for (var i = Segment.IndexInOwner; i <= point2.Segment.IndexInOwner; i++)
            {
                if (i == Segment.IndexInOwner)
                {
                    // Copy out end of this segment
                    result.AddRange(paraSegs[i].AnalysesRS.ToList().GetRange(Index, Segment.AnalysesRS.Count - Index));
                    continue;
                }
                if (i == point2.Segment.IndexInOwner)
                {
                    // Copy out beginning of this segment
                    result.AddRange(paraSegs[i].AnalysesRS.ToList().GetRange(0, point2.Index + 1));
                    continue;
                }
                // Copy out all of this segment
                result.AddRange(paraSegs[i].AnalysesRS);
            }
            return(result);
        }
        /// <summary>
        /// Tests to see if this AnalysisOccurrence is later in the text than the
        /// parameter.
        /// </summary>
        /// <param name="otherPoint"></param>
        /// <returns></returns>
        public bool IsAfter(AnalysisOccurrence otherPoint)
        {
            if (Paragraph.Owner.Hvo != otherPoint.Paragraph.Owner.Hvo)
            {
                throw new ArgumentException("The two points are not from the same text!");
            }
            var imyPara    = Paragraph.IndexInOwner;
            var iotherPara = otherPoint.Paragraph.IndexInOwner;

            if (imyPara > iotherPara)
            {
                return(true);
            }
            if (imyPara < iotherPara)
            {
                return(false);
            }
            var imySeg    = Segment.IndexInOwner;
            var iotherSeg = otherPoint.Segment.IndexInOwner;

            if (imySeg > iotherSeg)
            {
                return(true);
            }
            if (imySeg < iotherSeg)
            {
                return(false);
            }
            var iother = otherPoint.Index;

            return(Index > iother);
        }
		/// <summary>
		/// Get all of the Analyses associated with a single ConstChartWordGroup.
		/// Returns null if there is a problem finding them. Includes PunctuationForms.
		/// </summary>
		/// <returns></returns>
		public IEnumerable<IAnalysis> GetAllAnalyses()
		{
			if (!IsValidRef)
				return null;

			var point1 = new AnalysisOccurrence(BeginSegmentRA, BeginAnalysisIndex);
			var point2 = new AnalysisOccurrence(EndSegmentRA, EndAnalysisIndex);
			return point1.GetAdvancingOccurrencesInclusiveOf(point2);
		}
		internal void CallGetWordGroupCellsBorderingChOrph(AnalysisOccurrence occurrence,
			out ChartLocation precCell, out ChartLocation follCell)
		{
			var iPara = m_ccl.CallGetParaIndexForOccurrence(occurrence);
			Assert.Greater(iPara, -1, "Can't get ChOrph paragraph index.");
			var offset = occurrence.GetMyBeginOffsetInPara();
			Assert.Greater(offset, -1, "Can't get ChOrph offset.");
			m_ccl.GetWordGroupCellsBorderingChOrph(iPara, offset, out precCell, out follCell);
		}
Exemple #7
0
		/// <summary>
		/// Make one.
		/// </summary>
		/// <param name="cache"></param>
		/// <param name="hvoRoot"></param>
		public InterlinRibbon(FdoCache cache, int hvoRoot)
		{
			Cache = cache;
			m_iEndSelLim = -1;
			m_endSelLimPoint = null;
			m_wsf = cache.WritingSystemFactory;
			HvoRoot = hvoRoot;
			ReadOnlyView = true;
			m_fShowRangeSelAfterLostFocus = true;
		}
Exemple #8
0
		/// <summary>
		/// Create a new one.
		/// </summary>
		/// <param name="cache"></param>
		/// <param name="ss"></param>
		/// <param name="choices"></param>
		/// <param name="mediator"></param>
		public Sandbox(FdoCache cache, Mediator mediator, IVwStylesheet ss,
			InterlinLineChoices choices, AnalysisOccurrence selected, FocusBoxController focusBox)
			: this(cache, mediator, ss, choices)
		{
			FocusBox = focusBox;
			m_interlinDoc = focusBox.InterlinDoc;
			m_occurrenceSelected = selected;
			// Finish initialization with occurrence context.
			LoadForWordBundleAnalysis(m_occurrenceSelected.Analysis.Hvo);
		}
		/// <summary>
		/// Saves the given AnalysisOccurrence in the InterlinMaster.
		/// </summary>
		/// <param name="point"></param>
		/// <param name="fPersistNow">if true, this annotation will persist.</param>
		/// <param name="index">The index of the selected text in the list</param>
		public void Save(AnalysisOccurrence point, bool fPersistNow, int index, Mediator mediator)
		{
			if (point == null || !point.IsValid)
			{
				Reset(index, mediator); // let's just reset for an empty location.
				return;
			}
			var iParaInText = point.Segment.Paragraph.IndexInOwner;
			var begOffset = point.Segment.GetAnalysisBeginOffset(point.Index);
			var endOffset = point.HasWordform ? begOffset + point.BaselineText.Length : begOffset;

			Save(index, iParaInText, begOffset, endOffset, fPersistNow, mediator);
		}
		private void ParseText()
		{
			using (var pp = new ParagraphParser(Cache))
			{
				pp.Parse(m_txtPara);
			}
			var seg = m_txtPara.SegmentsOS[0];
			var wordArray = seg.AnalysesRS.ToArray();
			var cwords = wordArray.Length;
			m_occurrences = new AnalysisOccurrence[cwords];
			for (var i = 0; i < cwords; i++)
				m_occurrences[i] = new AnalysisOccurrence(seg, i);
		}
		private static AnalysisOccurrence[] GetParaAnalyses(IStTxtPara para)
		{
			var result = new List<AnalysisOccurrence>();
			var point1 = new AnalysisOccurrence(para.SegmentsOS[0], 0);
			if (!point1.IsValid)
				return result.ToArray();
			do
			{
				if (point1.HasWordform)
					result.Add(point1);
				point1 = point1.NextWordform();
			} while (point1 != null && point1.IsValid);
			return result.ToArray();
		}
		public void RibbonLayout()
		{
			EndSetupTask();
			// SUT#1 (but not one that changes data)
			m_ribbon.MakeRoot();
			m_ribbon.CallLayout();
			Assert.IsNotNull(m_ribbon.RootBox, "layout should produce some root box");
			var widthEmpty = m_ribbon.RootBox.Width;
			var glosses = new AnalysisOccurrence[0];

			// SUT#2 This changes data! Use a UOW.
			UndoableUnitOfWorkHelper.Do("RibbonLayoutUndo", "RibbonLayoutRedo",
										Cache.ActionHandlerAccessor, () => glosses = GetParaAnalyses(m_firstPara));

			Assert.Greater(glosses.Length, 0);
			var firstGloss = new List<AnalysisOccurrence> { glosses[0] };

			// SUT#3 This changes some internal data! Use a UOW.
			UndoableUnitOfWorkHelper.Do("CacheAnnsUndo", "CacheAnnsRedo", Cache.ActionHandlerAccessor,
										() => m_ribbon.CacheRibbonItems(firstGloss));
			m_ribbon.CallLayout();

			int widthOne = m_ribbon.RootBox.Width;
			int heightOne = m_ribbon.RootBox.Height;
			Assert.IsTrue(widthOne > widthEmpty, "adding a wordform should make the root box wider");

			var glossList = new List<AnalysisOccurrence>();
			glossList.AddRange(glosses);

			// SUT#4 This changes some internal data! Use a UOW.
			UndoableUnitOfWorkHelper.Do("CacheAnnsUndo", "CacheAnnsRedo", Cache.ActionHandlerAccessor,
										() => m_ribbon.CacheRibbonItems(glossList));
			m_ribbon.CallLayout();
			int widthMany = m_ribbon.RootBox.Width;
			int heightMany = m_ribbon.RootBox.Height;
			Assert.IsTrue(widthMany > widthOne, "adding more wordforms should make the root box wider");
			// In a real view they might not be exactly equal due to subscripts and the like, but our
			// text and anaysis are very simple.
			Assert.AreEqual(heightOne, heightMany, "ribbon should not wrap!");
		}
		/// <summary>
		/// Copy out IAnalysis objects from the end of this ISegment through the beginning of
		/// parameter's ISegment (inclusive). Also copies objects from intervening ISegments.
		/// Assumes parameter is in the same StTxtPara!
		/// </summary>
		/// <param name="point2"></param>
		/// <returns></returns>
		private IEnumerable<IAnalysis> CopyOutAnalysesFromMultipleSegments(AnalysisOccurrence point2)
		{
			// Need to copy out end of first segment, any segments in between and beginning of second segment
			var result = new List<IAnalysis>();
			var paraSegs = (Segment.Owner as IStTxtPara).SegmentsOS;
			if (paraSegs == null)
				throw new NullReferenceException("Unexpected error!");
			for (var i = Segment.IndexInOwner; i <= point2.Segment.IndexInOwner; i++)
			{
				if (i == Segment.IndexInOwner)
				{
					// Copy out end of this segment
					result.AddRange(paraSegs[i].AnalysesRS.ToList().GetRange(Index, Segment.AnalysesRS.Count - Index));
					continue;
				}
				if (i == point2.Segment.IndexInOwner)
				{
					// Copy out beginning of this segment
					result.AddRange(paraSegs[i].AnalysesRS.ToList().GetRange(0, point2.Index + 1));
					continue;
				}
				// Copy out all of this segment
				result.AddRange(paraSegs[i].AnalysesRS);
			}
			return result;
		}
		/// <summary>
		/// Returns a sequence of IAnalysis objects inclusive of the current occurrence
		/// and the end occurrence (otherAC). Parameter occurrence must occur AFTER 'this'
		/// occurrence.
		/// </summary>
		/// <param name="point2">An AnalysisOccurrence</param>
		/// <returns></returns>
		public IEnumerable<IAnalysis> GetAdvancingOccurrencesInclusiveOf(AnalysisOccurrence point2)
		{
			if (!point2.IsValid)
				throw new ArgumentException("Invalid Analysis Occurrence");
			if (Segment == point2.Segment)
			{
				// Case 1: Two points in same ISegment object
				if (Index > point2.Index)
					throw new ArgumentException("Second AnalysisOccurrence is before the first!");
				// Copy out sub-array of Analyses between the two ACs indices
				return Segment.AnalysesRS.ToList().GetRange(Index, point2.Index - Index + 1);
			}
			if (Segment.Owner == point2.Segment.Owner)
			{
				// Case 2: Two points in different ISegment objects, but in same StTxtPara
				if (Segment.IndexInOwner > point2.Segment.IndexInOwner)
					throw new ArgumentException("Second AnalysisOccurrence is before the first!");
				// Need to copy out end of first segment, any segments in between and beginning of second segment
				return CopyOutAnalysesFromMultipleSegments(point2);
			}
			var para1 = Segment.Owner as IStTxtPara;
			var para2 = point2.Segment.Owner as IStTxtPara;
			if (!(para1.Owner as IStText).ParagraphsOS.Contains(para2))
			{
				throw new ArgumentOutOfRangeException("point2", "AnalysisOccurrences are not within the same Text!");
			}
			throw new NotImplementedException("So far we only handle this in same Segment or Paragraph.");
		}
		/// <summary>
		/// More efficient equality if the other argument is known to be an AnalysisOccurrence.
		/// </summary>
		/// <param name="other"></param>
		/// <returns></returns>
		public bool Equals(AnalysisOccurrence other)
		{
			return other.Segment == Segment && other.Index == Index;
		}
		/// <summary>
		/// Verifies that the specified number of Analyses have been removed from
		/// the start of the original list.
		/// </summary>
		/// <param name="allParaOccurrences"></param>
		/// <param name="removedWords"></param>
		/// <returns></returns>
		private void AssertUsedAnalyses(AnalysisOccurrence[] allParaOccurrences, int removedWords)
		{
			m_helper.AssertUsedAnalyses(m_mockRibbon, allParaOccurrences, removedWords);
		}
 /// <summary>
 ///
 /// </summary>
 public bool TryGetBestGuess(AnalysisOccurrence occurrence, out IAnalysis bestGuess)
 {
     bestGuess = GetBestGuess(occurrence);
     return(!(bestGuess is NullWAG));
 }
		private void ParseTestText()
		{
			// Seg:  0								1								2
			// Index:0	  1		 2			 3 0			  1	  2		3 0		 1			 2
			//		xxxpus xxxyalola xxxnihimbilira. xxxnihimbilira xxxpus xxxyalola. xxxhesyla xxxnihimbilira.
			using (var pp = new ParagraphParser(Cache))
			{
				pp.Parse(m_para1);
			}
			var coords = new int[8, 2] { { 0, 0 }, { 0, 1 }, { 0, 2 }, { 1, 0 }, { 1, 1 }, { 1, 2 }, { 2, 0 }, { 2, 1 } };
			m_occurrences = new AnalysisOccurrence[8];
			for (int i = 0; i < 8; i++)
				m_occurrences[i] = new AnalysisOccurrence(m_para1.SegmentsOS[coords[i, 0]], coords[i, 1]);
		}
		/// <summary>
		/// Finds a real analysis in the segment from the indicated direction.
		/// When used just to check if there is an analysis, the direction doesn't matter.
		/// </summary>
		/// <param name="seg">The seg.</param>
		/// <param name="forward">if set to <c>true</c> find a real analysis looking forward.</param>
		/// <returns>The analysis found or null</returns>
		private AnalysisOccurrence FindRealAnalysisInSegment(ISegment seg, bool forward)
		{
			if (seg == null) return null;
			int index = -1;
			AnalysisOccurrence realAnalysis = null;
			bool found = false;
			for (int i = 0; i < seg.AnalysesRS.Count; i++)
			{	// need to count to create occurances
				index++;
				int ind = forward ? index : seg.AnalysesRS.Count - index;
				realAnalysis = new AnalysisOccurrence(seg, ind);
				if (m_vc.CanBeAnalyzed(realAnalysis))
				{
					found = true;
					break; // found the first or last real analysis
				}
			}
			return found ? realAnalysis : null;
		}
		/// <summary>
		/// Get the next segment with either a non-null annotation that is configured or
		/// a non-punctuation analysis. Also skip segments that are Scripture labels (like
		/// Chapter/Verse/Footnote numbers.
		/// It tries the next one after the SelectedOccurrence.Segment
		/// then tries the next paragraph, etc..
		/// Use this version if the calling code already has the actual para/seg objects.
		/// </summary>
		/// <param name="currentPara"></param>
		/// <param name="seg"></param>
		/// <param name="upward">true if moving up and left, false otherwise</param>
		/// <param name="realAnalysis">the first or last real analysis found in the next segment</param>
		/// <returns>A segment meeting the criteria or null if not found.</returns>
		private ISegment GetNextSegment(IStTxtPara currentPara, ISegment seg, bool upward,
				out AnalysisOccurrence realAnalysis)
		{
			ISegment nextSeg = null;
			realAnalysis = null;
			var currentText = currentPara.Owner as IStText;
			Debug.Assert(currentText != null, "Paragraph not owned by a text.");
			var lines = LineChoices.m_specs as IEnumerable<InterlinLineSpec>;
			var delta = upward ? -1 : 1;
			var nextSegIndex = delta + seg.IndexInOwner;
			do
			{
				if (0 <= nextSegIndex && nextSegIndex < currentPara.SegmentsOS.Count)
				{
					nextSeg = currentPara.SegmentsOS[nextSegIndex];
					nextSegIndex += delta; // increment for next loop in case it doesn't check out
				}
				else
				{   // try the first (last) segment in the next (previous) paragraph
					int nextParaIndex = delta + currentPara.IndexInOwner;
					nextSeg = null;
					IStTxtPara nextPara = null;
					if (0 <= nextParaIndex && nextParaIndex < currentText.ParagraphsOS.Count)
					{   // try to find this paragraph's first (last) segment
						currentPara = (IStTxtPara)currentText.ParagraphsOS[nextParaIndex];
						nextSegIndex = upward ? currentPara.SegmentsOS.Count - 1 : 0;
					}
					else
					{	// no more paragraphs in this text
						break;
					}
				}
				realAnalysis = FindRealAnalysisInSegment(nextSeg, !upward);
			} while (nextSeg == null || (realAnalysis == null && !HasVisibleTranslationOrNote(nextSeg, lines)));
			return nextSeg;
		}
		/// <summary>
		/// Move the sandbox to the AnalysisOccurrence, (which may be a WfiWordform, WfiAnalysis, or WfiGloss).
		/// </summary>
		/// <param name="target"></param>
		/// <param name="fSaveGuess">if true, saves guesses; if false, skips guesses but still saves edits.</param>
		/// <param name="fMakeDefaultSelection">true to make the default selection within the new sandbox.</param>
		/// <param name="fShow">true makes the focusbox visible.</param>
		public virtual void TriggerAnalysisSelected(AnalysisOccurrence target, bool fSaveGuess, bool fMakeDefaultSelection, bool fShow)
		{
			// This can happen, though it is rare...see LT-8193.
			if (!target.IsValid)
			{
				return;
			}
			if (IsFocusBoxInstalled)
				FocusBox.UpdateRealFromSandbox(null, fSaveGuess, target);
			TryHideFocusBoxAndUninstall();
			RecordGuessIfNotKnown(target);
			InstallFocusBox();
			RootBox.DestroySelection();
			FocusBox.SelectOccurrence(target);
			SetFocusBoxSizeForVc();
			SelectedOccurrence = target;

			if (fShow)
			{
				SimulateReplaceAnalysis(target);
				MoveFocusBoxIntoPlace();
				// Now it is the right size and place we can show it.
				TryShowFocusBox();
				// All this CAN hapen because we're editing in another window...for example,
				// if we edit something that deletes the current wordform in a concordance view.
				// In that case we don't want to steal the focus.
				if (ParentForm == Form.ActiveForm)
					FocusBox.FocusSandbox();
			}

			if (fMakeDefaultSelection)
				m_mediator.IdleQueue.Add(IdleQueuePriority.Medium, FocusBox.MakeDefaultSelection);
		}
 /// <summary>
 /// Returns a sequence of IAnalysis objects inclusive of the current occurrence
 /// and the end occurrence (otherAC).
 /// </summary>
 /// <param name="point2">An AnalysisOccurrence</param>
 public IEnumerable <IAnalysis> GetAdvancingWordformsInclusiveOf(AnalysisOccurrence point2)
 {
     return(from occurrence in GetAdvancingOccurrencesInclusiveOf(point2)
            where !(occurrence is IPunctuationForm)
            select occurrence);
 }
 /// <summary>
 /// More efficient equality if the other argument is known to be an AnalysisOccurrence.
 /// </summary>
 /// <param name="other"></param>
 /// <returns></returns>
 public bool Equals(AnalysisOccurrence other)
 {
     return(other.Segment == Segment && other.Index == Index);
 }
		/// <summary>
		/// Verify that the parameter tag points to the right possibility marker
		/// and that it refers to the correct begin and end points in the text.
		/// </summary>
		/// <param name="ttag"></param>
		/// <param name="poss"></param>
		/// <param name="point1"></param>
		/// <param name="point2"></param>
		private static void VerifyTextTag(ITextTag ttag, ICmPossibility poss,
										  AnalysisOccurrence point1, AnalysisOccurrence point2)
		{
			Assert.IsNotNull(ttag, "There should be a TextTag object.");
			Assert.AreEqual(poss.Hvo, ttag.TagRA.Hvo, "Text Tag has wrong possibility Hvo.");
			Assert.AreEqual(point1.Segment.Hvo, ttag.BeginSegmentRA.Hvo, "Tag has wrong BeginSegment");
			Assert.AreEqual(point1.Index, ttag.BeginAnalysisIndex, "Tag has wrong BeginAnalysisIndex");
			Assert.AreEqual(point2.Segment.Hvo, ttag.EndSegmentRA.Hvo, "Tag has wrong EndSegment");
			Assert.AreEqual(point2.Index, ttag.EndAnalysisIndex, "Tag has wrong EndAnalysisIndex");
		}
		/// <summary>
		/// Returns a sequence of IAnalysis objects inclusive of the current occurrence
		/// and the end occurrence (otherAC).
		/// </summary>
		/// <param name="point2">An AnalysisOccurrence</param>
		public IEnumerable<IAnalysis> GetAdvancingWordformsInclusiveOf(AnalysisOccurrence point2)
		{
			return from occurrence in GetAdvancingOccurrencesInclusiveOf(point2)
				   where !(occurrence is IPunctuationForm)
				   select occurrence;
		}
		/// <summary>
		/// Tests to see if this AnalysisOccurrence is later in the text than the
		/// parameter.
		/// </summary>
		/// <param name="otherPoint"></param>
		/// <returns></returns>
		public bool IsAfter(AnalysisOccurrence otherPoint)
		{
			if(Paragraph.Owner.Hvo != otherPoint.Paragraph.Owner.Hvo)
				throw new ArgumentException("The two points are not from the same text!");
			var imyPara = Paragraph.IndexInOwner;
			var iotherPara = otherPoint.Paragraph.IndexInOwner;
			if (imyPara > iotherPara)
				return true;
			if (imyPara < iotherPara)
				return false;
			var imySeg = Segment.IndexInOwner;
			var iotherSeg = otherPoint.Segment.IndexInOwner;
			if (imySeg > iotherSeg)
				return true;
			if (imySeg < iotherSeg)
				return false;
			var iother = otherPoint.Index;
			return Index > iother;
		}
		internal void RecordGuessIfNotKnown(AnalysisOccurrence selected)
		{
			if (m_vc != null) // I think this only happens in tests.
				m_vc.RecordGuessIfNotKnown(selected);
		}
		/// <summary>
		/// Select the word indicated by the occurrence.
		/// Note that this does not save any changes made in the Sandbox. It is mainly used
		/// when the view is read-only.
		/// </summary>
		public override void SelectOccurrence(AnalysisOccurrence target)
		{
			if (target == null)
			{
				TryHideFocusBoxAndUninstall();
				return;
			}
			if (SelectedOccurrence == target && IsFocusBoxInstalled)
			{
				// Don't steal the focus from another window.  See FWR-1795.
				if (ParentForm == Form.ActiveForm)
				{
					if (ExistingFocusBox.CanFocus)
					{
						ExistingFocusBox.Focus(); // important when switching tabs with ctrl-tab.
					}
					else
					{
						VisibleChanged += FocusWhenVisible;
					}
				}
				return;
			}
			if (!m_vc.CanBeAnalyzed(target))
				return;
#if DEBUG
			// test preconditions.
			Debug.Assert(target.IsValid && !(target.Analysis is IPunctuationForm), "Given annotation type should not be punctuation"
				+ " but was " + target.Analysis.ShortName + ".");
#endif
			TriggerAnnotationSelected(target, true);
		}
		/// <summary>
		/// Move the sandbox (see main method), making the default selection.
		/// </summary>
		/// <param name="target"></param>
		/// <param name="fSaveGuess">if true, saves guesses; if false, skips guesses but still saves edits.</param>
		public void TriggerAnnotationSelected(AnalysisOccurrence target, bool fSaveGuess)
		{
			TriggerAnalysisSelected(target, fSaveGuess, true);
		}
		/// <summary>
		/// Approve all the suggested analyses in this text. See LT-4312.
		/// </summary>
		/// <param name="cmd">The command object from the selection event.</param>
		public void ApproveAllSuggestedAnalyses(Command cmd)
		{   // Go through the entire text looking for suggested analyses that can be approved.
			// remember where the focus box or ip is
			// might be on an analysis, labels or translation text
			var helper = SelectionHelper.Create(RootBox.Site); // only helps restore translation and note line selections
			AnalysisOccurrence focusedWf = SelectedOccurrence; // need to restore focus box if selected

			// find the very first analysis
			ISegment firstRealSeg = null;
			IAnalysis firstRealOcc = null;
			int occInd = 0;
			foreach (IStPara p in RootStText.ParagraphsOS)
			{
				var para = (IStTxtPara) p;
				foreach (ISegment seg in para.SegmentsOS)
				{
					firstRealSeg = seg;
					occInd = 0;
					foreach(IAnalysis an in seg.AnalysesRS)
					{
						if (an.HasWordform && an.IsValidObject)
						{
							firstRealOcc = an;
							break;
						}
						occInd++;
					}
					if (firstRealOcc != null) break;
				}
				if (firstRealOcc != null) break;
			}
			// Set it as the current segment and recurse
			if (firstRealOcc == null)
				return; // punctuation only or nothing to analyze
			AnalysisOccurrence ao = null;
			if (focusedWf != null && focusedWf.Analysis == firstRealOcc)
				ao = new AnalysisOccurrence(focusedWf.Segment, focusedWf.Index);
			else
				ao = new AnalysisOccurrence(firstRealSeg, occInd);
			TriggerAnalysisSelected(ao, true, true, false);
			var navigator = new SegmentServices.StTextAnnotationNavigator(ao);

			// This needs to be outside the block for the UOW, since what we are suppressing
			// happens at the completion of the UOW.
			SuppressResettingGuesses(
				() =>
				{
					// Needs to include GetRealAnalysis, since it might create a new one.
					UndoableUnitOfWorkHelper.Do(cmd.UndoText, cmd.RedoText, Cache.ActionHandlerAccessor,
					() =>
					{
						var nav = new SegmentServices.StTextAnnotationNavigator(SelectedOccurrence);
						AnalysisOccurrence lastOccurrence;
						var analyses = navigator.GetAnalysisOccurrencesAdvancingInStText().ToList();
						foreach (var occ in analyses)
						{   // This could be punctuation or any kind of analysis.
							IAnalysis occAn = occ.Analysis; // averts “Access to the modified closure” warning in resharper
							if (occAn is IWfiAnalysis || occAn is IWfiWordform)
							{   // this is an analysis or a wordform
								int hvo = m_vc.GetGuess(occAn);
								if (occAn.Hvo != hvo)
								{   // this is a guess, so approve it
									// 1) A second occurence of a word that has had a lexicon entry or sense created for it.
									// 2) A parser result - not sure which gets picked if multiple.
									// #2 May take a while to "percolate" through to become a "guess".
									var guess = Cache.ServiceLocator.ObjectRepository.GetObject(hvo);
									if (guess != null && guess is IAnalysis)
										occ.Segment.AnalysesRS[occ.Index] = (IAnalysis) guess;
									else
									{
										occ.Segment.AnalysesRS[occ.Index] = occAn.Wordform.AnalysesOC.FirstOrDefault();
									}
								}
							/*	else if (occAn.HasWordform && occAn.Wordform.ParserCount > 0)
								{   // this doesn't seem to be needed (and may not be correct) - always caught above
									bool isHumanNoOpinion = occAn.Wordform.HumanNoOpinionParses.Cast<IWfiWordform>().Any(wf => wf.Hvo == occAn.Hvo);
									if (isHumanNoOpinion)
									{
										occ.Segment.AnalysesRS[occ.Index] = occAn.Wordform.AnalysesOC.FirstOrDefault();
									}
								} */
							}
						}
					});
				}
			);
			// MoveFocusBoxIntoPlace();
			if (focusedWf != null)
				SelectOccurrence(focusedWf);
			else if (helper != null)
				helper.SetSelection(true, true);
			Update();
		}
		public override void SelectOccurrence(AnalysisOccurrence selected)
		{
			// when we change wordforms we should create a new Analysis Tree, so we don't
			// overwrite the last state of one we may have saved during the tests.
			if (m_sandbox != null && selected != SelectedOccurrence)
				(m_sandbox as MockSandbox).NewAnalysisTree = new AnalysisTree();
			base.SelectOccurrence(selected);
		}
		/// <summary>
		/// Move the sandbox to the AnalysisOccurrence, (which may be a WfiWordform, WfiAnalysis, or WfiGloss).
		/// </summary>
		/// <param name="target"></param>
		/// <param name="fSaveGuess">if true, saves guesses; if false, skips guesses but still saves edits.</param>
		/// <param name="fMakeDefaultSelection">true to make the default selection within the new sandbox.</param>
		public virtual void TriggerAnalysisSelected(AnalysisOccurrence target, bool fSaveGuess, bool fMakeDefaultSelection)
		{
			TriggerAnalysisSelected(target, fSaveGuess, fMakeDefaultSelection, true);
		}
		void IAnalysisControlInternal.SwitchWord(AnalysisOccurrence selected)
		{
			CurrentAnalysisTree.Analysis = selected.Analysis;
			NewAnalysisTree.Analysis = selected.Analysis;
		}
		/// <summary>
		/// Something about the display of the AnalysisOccurrence has changed...perhaps it has become or ceased to
		/// be the current annotation displayed using the Sandbox, or the Sandbox changed size. Produce
		/// a PropChanged that makes the system think it has been replaced (with itself) to refresh the
		/// relevant part of the display.
		/// </summary>
		void SimulateReplaceAnalysis(AnalysisOccurrence occurrence)
		{
			UpdateDisplayForOccurrence(occurrence);
		}
		public override void SelectOccurrence(AnalysisOccurrence target)
		{
			InstallFocusBox();
			FocusBox.SelectOccurrence(target);
		}
		/// <summary>
		/// Get the next segment with either a non-null annotation that is configured or
		/// a non-punctuation analysis.
		/// It tries the next one after the SelectedOccurrence.Segment
		/// then tries the next paragraph, etc..
		/// </summary>
		/// <param name="paraIndex"></param>
		/// <param name="segIndex"></param>
		/// <param name="upward">true if moving up and left, false otherwise</param>
		/// <param name="realAnalysis">the first or last real analysis found in the next segment</param>
		/// <returns>A segment meeting the criteria or null if not found.</returns>
		internal ISegment GetNextSegment(int paraIndex, int segIndex, bool upward, out AnalysisOccurrence realAnalysis)
		{
			var currentPara = (IStTxtPara)RootStText.ParagraphsOS[paraIndex];
			Debug.Assert(currentPara != null, "Tried to use a null paragraph ind=" + paraIndex);
			ISegment currentSeg = currentPara.SegmentsOS[segIndex];
			Debug.Assert(currentSeg != null, "Tried to use a null segment ind=" + segIndex + " in para " + paraIndex);
			return GetNextSegment(currentPara, currentSeg, upward, out realAnalysis);
		}
		internal override IAnalysisControlInternal CreateNewSandbox(AnalysisOccurrence selected)
		{
			var sandbox = new MockSandbox();
			sandbox.CurrentAnalysisTree.Analysis = selected.Analysis;
			sandbox.NewAnalysisTree.Analysis = selected.Analysis;
			return sandbox;
		}