diff --git a/Src/LexText/Interlinear/InterlinDocForAnalysis.cs b/Src/LexText/Interlinear/InterlinDocForAnalysis.cs index 8cd44701cf..541c0d37a7 100644 --- a/Src/LexText/Interlinear/InterlinDocForAnalysis.cs +++ b/Src/LexText/Interlinear/InterlinDocForAnalysis.cs @@ -78,7 +78,7 @@ void InterlinDocForAnalysis_RightMouseClickedEvent(SimpleRootSite sender, FwRigh internal void SuppressResettingGuesses(Action task) { - Vc.Decorator.SuppressResettingGuesses(task); + Vc.GuessCache.SuppressResettingGuesses(task); } public override void PropChanged(int hvo, int tag, int ivMin, int cvIns, int cvDel) @@ -1473,9 +1473,10 @@ public void OnAddWordGlossesToFreeTrans(object arg) ITsStrBldr bldr = TsStringUtils.MakeStrBldr(); bool fOpenPunc = false; ITsString space = TsStringUtils.MakeString(" ", ws); - foreach (var analysis in seg.AnalysesRS) + for (var i = 0; i < seg.AnalysesRS.Count; i++) { ITsString insert = null; + var analysis = seg.AnalysesRS[i]; if (analysis.Wordform == null) { // PunctForm...insert its text. @@ -1511,7 +1512,7 @@ public void OnAddWordGlossesToFreeTrans(object arg) else if (analysis is IWfiAnalysis || analysis is IWfiWordform) { // check if we have a guess cached with a gloss. (LT-9973) - int guessHvo = Vc.GetGuess(analysis); + int guessHvo = Vc.GetGuess(analysis, new AnalysisOccurrence(seg, i)); if (guessHvo != 0) { var guess = Cache.ServiceLocator.ObjectRepository.GetObject(guessHvo) as IWfiGloss; @@ -2244,6 +2245,13 @@ public void AddNote(Command command) Focus(); // So we can actually see the selection we just made. } + internal InterlinViewDataCache GetGuessCache() + { + if (Vc != null) + return Vc.GuessCache; + return null; + } + internal void RecordGuessIfNotKnown(AnalysisOccurrence selected) { if (Vc != null) // I think this only happens in tests. @@ -2308,7 +2316,7 @@ public void ApproveAllSuggestedAnalyses(Command cmd) 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 = Vc.GetGuess(occAn); + int hvo = Vc.GetGuess(occAn, occ); if (occAn.Hvo != hvo) { // Move the sandbox to the next AnalysisOccurrence, then do the approval (using the sandbox data). diff --git a/Src/LexText/Interlinear/InterlinDocRootSiteBase.cs b/Src/LexText/Interlinear/InterlinDocRootSiteBase.cs index defc4fba27..4c6580956c 100644 --- a/Src/LexText/Interlinear/InterlinDocRootSiteBase.cs +++ b/Src/LexText/Interlinear/InterlinDocRootSiteBase.cs @@ -919,7 +919,7 @@ internal virtual void UpdateGuesses(HashSet wordforms) private void UpdateGuesses(HashSet wordforms, bool fUpdateDisplayWhereNeeded) { // now update the guesses for the paragraphs. - var pdut = new ParaDataUpdateTracker(Vc.GuessServices, Vc.Decorator); + var pdut = new ParaDataUpdateTracker(Vc.GuessServices, Vc.GuessCache); if (wordforms != null) // The user may have changed the analyses for wordforms. (LT-21814) foreach (var wordform in wordforms) @@ -991,12 +991,6 @@ public IVwRootBox GetRootBox() /// protected virtual void AddDecorator() { - // by default, just use the InterinVc decorator. - if (m_rootb != null) - { - m_rootb.DataAccess = Vc.Decorator; - } - } protected virtual void SetRootInternal(int hvo) diff --git a/Src/LexText/Interlinear/InterlinTaggingChild.cs b/Src/LexText/Interlinear/InterlinTaggingChild.cs index 0283dbc8ae..148816b62e 100644 --- a/Src/LexText/Interlinear/InterlinTaggingChild.cs +++ b/Src/LexText/Interlinear/InterlinTaggingChild.cs @@ -69,14 +69,6 @@ protected override void MakeVc() m_segRepo = m_cache.ServiceLocator.GetInstance(); } - /// - /// This causes all rootbox access to go through our Tagging Decorator. - /// - protected override void AddDecorator() - { - m_rootb.DataAccess = (Vc as InterlinTaggingVc).Decorator; - } - #region SelectionMethods bool m_fInSelChanged; diff --git a/Src/LexText/Interlinear/InterlinVc.cs b/Src/LexText/Interlinear/InterlinVc.cs index 5cc069cb46..0b916ee3ee 100644 --- a/Src/LexText/Interlinear/InterlinVc.cs +++ b/Src/LexText/Interlinear/InterlinVc.cs @@ -171,7 +171,7 @@ public InterlinVc(LcmCache cache) : base(cache.DefaultAnalWs) StTxtParaRepository = m_cache.ServiceLocator.GetInstance(); m_wsAnalysis = cache.DefaultAnalWs; m_wsUi = cache.LanguageWritingSystemFactoryAccessor.UserWs; - Decorator = new InterlinViewDataCache(m_cache); + GuessCache = new InterlinViewDataCache(m_cache); PreferredVernWs = cache.DefaultVernWs; m_selfFlid = m_cache.MetaDataCacheAccessor.GetFieldId2(CmObjectTags.kClassId, "Self", false); m_tssMissingAnalysis = TsStringUtils.MakeString(ITextStrings.ksStars, m_wsAnalysis); @@ -195,7 +195,7 @@ public InterlinVc(LcmCache cache) : base(cache.DefaultAnalWs) LangProjectHvo = m_cache.LangProject.Hvo; } - internal InterlinViewDataCache Decorator { get; set; } + internal InterlinViewDataCache GuessCache { get; set; } private IStTxtParaRepository StTxtParaRepository { get; set; } @@ -554,12 +554,12 @@ private void SetGuessing(IVwEnv vwenv) /// /// /// - internal int GetGuess(IAnalysis analysis) + internal int GetGuess(IAnalysis analysis, AnalysisOccurrence occurrence) { - if (Decorator.get_IsPropInCache(analysis.Hvo, InterlinViewDataCache.AnalysisMostApprovedFlid, + if (GuessCache.get_IsPropInCache(occurrence, InterlinViewDataCache.AnalysisMostApprovedFlid, (int)CellarPropertyType.ReferenceAtomic, 0)) { - var hvoResult = Decorator.get_ObjectProp(analysis.Hvo, InterlinViewDataCache.AnalysisMostApprovedFlid); + var hvoResult = GuessCache.get_ObjectProp(occurrence, InterlinViewDataCache.AnalysisMostApprovedFlid); if(hvoResult != 0 && Cache.ServiceLocator.IsValidObjectId(hvoResult)) return hvoResult; // may have been cleared by setting to zero, or the Decorator could have stale data } @@ -1718,12 +1718,12 @@ public void Run(bool showMultipleAnalyses) { case WfiWordformTags.kClassId: m_hvoWordform = wag.Wordform.Hvo; - m_hvoDefault = m_this.GetGuess(wag.Wordform); + m_hvoDefault = m_this.GetGuess(wag.Wordform, m_analysisOccurrence); break; case WfiAnalysisTags.kClassId: m_hvoWordform = wag.Wordform.Hvo; m_hvoWfiAnalysis = wag.Analysis.Hvo; - m_hvoDefault = m_this.GetGuess(wag.Analysis); + m_hvoDefault = m_this.GetGuess(wag.Analysis, m_analysisOccurrence); break; case WfiGlossTags.kClassId: m_hvoWfiAnalysis = wag.Analysis.Hvo; @@ -1823,7 +1823,7 @@ private void DisplayMorphemes() { // Real analysis isn't what we're displaying, so morph breakdown // is a guess. Is it a human-approved guess? - bool isHumanGuess = m_this.Decorator.get_IntProp(m_hvoDefault, InterlinViewDataCache.OpinionAgentFlid) != + bool isHumanGuess = m_this.GuessCache.get_IntProp(m_hvoDefault, InterlinViewDataCache.OpinionAgentFlid) != (int) AnalysisGuessServices.OpinionAgent.Parser; m_this.SetGuessing(m_vwenv, isHumanGuess ? ApprovedGuessColor : MachineGuessColor); // Let the exporter know that this is a guessed analysis. @@ -1867,7 +1867,7 @@ private void DisplayWordGloss(InterlinLineSpec spec, int choiceIndex) { // Real analysis isn't what we're displaying, so morph breakdown // is a guess. Is it a human-approved guess? - bool isHumanGuess = m_this.Decorator.get_IntProp(m_hvoDefault, InterlinViewDataCache.OpinionAgentFlid) != + bool isHumanGuess = m_this.GuessCache.get_IntProp(m_hvoDefault, InterlinViewDataCache.OpinionAgentFlid) != (int)AnalysisGuessServices.OpinionAgent.Parser; m_this.SetGuessing(m_vwenv, isHumanGuess ? ApprovedGuessColor : MachineGuessColor); } @@ -1919,7 +1919,7 @@ private void DisplayWordPOS(int choiceIndex) if (m_hvoDefault != m_hvoWordBundleAnalysis) { // Real analysis isn't what we're displaying, so POS is a guess. - bool isHumanApproved = m_this.Decorator.get_IntProp(m_hvoDefault, InterlinViewDataCache.OpinionAgentFlid) + bool isHumanApproved = m_this.GuessCache.get_IntProp(m_hvoDefault, InterlinViewDataCache.OpinionAgentFlid) != (int)AnalysisGuessServices.OpinionAgent.Parser; m_this.SetGuessing(m_vwenv, isHumanApproved ? ApprovedGuessColor : MachineGuessColor); @@ -2287,7 +2287,7 @@ private void EnsureLoader() internal virtual IParaDataLoader CreateParaLoader() { - return new InterlinViewCacheLoader(new AnalysisGuessServices(m_cache), Decorator); + return new InterlinViewCacheLoader(new AnalysisGuessServices(m_cache), GuessCache); } internal void RecordGuessIfNotKnown(AnalysisOccurrence selected) @@ -2413,23 +2413,24 @@ public interface IParaDataLoader void RecordGuessIfNotKnown(AnalysisOccurrence occurrence); IAnalysis GetGuessForWordform(IWfiWordform wf, int ws); AnalysisGuessServices GuessServices { get; } + InterlinViewDataCache GuessCache { get; } } public class InterlinViewCacheLoader : IParaDataLoader { - private InterlinViewDataCache m_sdaDecorator; + private InterlinViewDataCache m_guessCache; public InterlinViewCacheLoader(AnalysisGuessServices guessServices, - InterlinViewDataCache sdaDecorator) + InterlinViewDataCache guessCache) { GuessServices = guessServices; - m_sdaDecorator = sdaDecorator; + m_guessCache = guessCache; } /// /// /// public AnalysisGuessServices GuessServices { get; private set; } - protected InterlinViewDataCache Decorator { get { return m_sdaDecorator; } } + public InterlinViewDataCache GuessCache { get { return m_guessCache; } } #region IParaDataLoader Members @@ -2470,7 +2471,7 @@ internal void LoadAnalysisData(IStTxtPara para, HashSet wordforms) public void RecordGuessIfNotKnown(AnalysisOccurrence occurrence) { - if (m_sdaDecorator.get_ObjectProp(occurrence.Analysis.Hvo, InterlinViewDataCache.AnalysisMostApprovedFlid) == 0) + if (m_guessCache.get_ObjectProp(occurrence, InterlinViewDataCache.AnalysisMostApprovedFlid) == 0) RecordGuessIfAvailable(occurrence); } @@ -2494,16 +2495,16 @@ private void RecordGuessIfAvailable(AnalysisOccurrence occurrence) // next get the best guess for wordform or analysis IAnalysis wag = occurrence.Analysis; - IAnalysis wagGuess = GuessServices.GetBestGuess(occurrence, false, false); + IAnalysis wagGuess = GuessServices.GetBestGuess(occurrence, false); // now record the guess in the decorator. if (!(wagGuess is NullWAG)) { - SetObjProp(wag.Hvo, InterlinViewDataCache.AnalysisMostApprovedFlid, wagGuess.Hvo); + SetObjProp(occurrence, InterlinViewDataCache.AnalysisMostApprovedFlid, wagGuess.Hvo); SetInt(wagGuess.Analysis.Hvo, InterlinViewDataCache.OpinionAgentFlid, (int)GuessServices.GetOpinionAgent(wagGuess.Analysis)); } else { - SetObjProp(wag.Hvo, InterlinViewDataCache.AnalysisMostApprovedFlid, 0); + SetObjProp(occurrence, InterlinViewDataCache.AnalysisMostApprovedFlid, 0); } } @@ -2512,15 +2513,9 @@ public IAnalysis GetGuessForWordform(IWfiWordform wf, int ws) return GuessServices.GetBestGuess(wf, ws); } - /// - /// this is so we can subclass the loader to test whether values have actually changed. - /// - /// - /// - /// - protected virtual void SetObjProp(int hvo, int flid, int objValue) + protected virtual void SetObjProp(AnalysisOccurrence occurrence, int flid, int objValue) { - m_sdaDecorator.SetObjProp(hvo, flid, objValue); + m_guessCache.SetObjProp(occurrence, flid, objValue); } /// @@ -2531,7 +2526,7 @@ protected virtual void SetObjProp(int hvo, int flid, int objValue) /// protected virtual void SetInt(int hvo, int flid, int n) { - m_sdaDecorator.SetInt(hvo, flid, n); + m_guessCache.SetInt(hvo, flid, n); } #region IParaDataLoader Members @@ -2541,8 +2536,8 @@ public void ResetGuessCache() { // recreate the guess services, so they will use the latest FDO data. GuessServices.ClearGuessData(); - // clear the Decorator cache for the guesses, so it won't have any stale data. - m_sdaDecorator.ClearPropFromCache(InterlinViewDataCache.AnalysisMostApprovedFlid); + // clear the cache for the guesses, so it won't have any stale data. + m_guessCache.ClearPropFromCache(InterlinViewDataCache.AnalysisMostApprovedFlid); } /// @@ -2552,7 +2547,7 @@ public bool UpdatingOccurrence(IAnalysis oldAnalysis, IAnalysis newAnalysis) { var result = GuessServices.UpdatingOccurrence(oldAnalysis, newAnalysis); if (result) - m_sdaDecorator.ClearPropFromCache(InterlinViewDataCache.AnalysisMostApprovedFlid); + m_guessCache.ClearPropFromCache(InterlinViewDataCache.AnalysisMostApprovedFlid); return result; } @@ -2566,11 +2561,12 @@ public bool UpdatingOccurrence(IAnalysis oldAnalysis, IAnalysis newAnalysis) internal class ParaDataUpdateTracker : InterlinViewCacheLoader { private HashSet m_annotationsChanged = new HashSet(); + private HashSet m_annotationsUnchanged = new HashSet(); private AnalysisOccurrence m_currentAnnotation; HashSet m_analysesWithNewGuesses = new HashSet(); - public ParaDataUpdateTracker(AnalysisGuessServices guessServices, InterlinViewDataCache sdaDecorator) : - base(guessServices, sdaDecorator) + public ParaDataUpdateTracker(AnalysisGuessServices guessServices, InterlinViewDataCache guessCache) : + base(guessServices, guessCache) { } @@ -2598,32 +2594,41 @@ private void MarkCurrentAnnotationAsChanged() /// internal IList ChangedAnnotations { - get { return m_annotationsChanged.ToArray(); } + get + { + // Include occurrences that are unchanged but might add a yellow background. + foreach (var unchangedAnnotation in m_annotationsUnchanged) + { + if (m_analysesWithNewGuesses.Contains(unchangedAnnotation.Analysis.Hvo)) + { + m_annotationsChanged.Add(unchangedAnnotation); + } + } + return m_annotationsChanged.ToArray(); + } } - protected override void SetObjProp(int hvo, int flid, int newObjValue) + protected override void SetObjProp(AnalysisOccurrence occurrence, int flid, int newObjValue) { - int oldObjValue = Decorator.get_ObjectProp(hvo, flid); + int oldObjValue = GuessCache.get_ObjectProp(occurrence, flid); if (oldObjValue != newObjValue) { - base.SetObjProp(hvo, flid, newObjValue); - m_analysesWithNewGuesses.Add(hvo); + base.SetObjProp(occurrence, flid, newObjValue); + m_annotationsChanged.Add(occurrence); + m_analysesWithNewGuesses.Add(occurrence.Analysis.Hvo); MarkCurrentAnnotationAsChanged(); - return; } - // If we find more than one occurrence of the same analysis, only the first time - // will its guess change. But all of them need to be updated! So any occurrence whose - // guess has changed needs to be marked as changed. - if (m_currentAnnotation != null && m_currentAnnotation.Analysis !=null - && m_analysesWithNewGuesses.Contains(m_currentAnnotation.Analysis.Hvo)) + else { - MarkCurrentAnnotationAsChanged(); + // We will want to redisplay these with a yellow background + // if the number of possibilities change. + m_annotationsUnchanged.Add(occurrence); } } protected override void SetInt(int hvo, int flid, int newValue) { - int oldValue = Decorator.get_IntProp(hvo, flid); + int oldValue = GuessCache.get_IntProp(hvo, flid); if (oldValue != newValue) { base.SetInt(hvo, flid, newValue); diff --git a/Src/LexText/Interlinear/InterlinViewDataCache.cs b/Src/LexText/Interlinear/InterlinViewDataCache.cs index e08fe1bff0..74dd3b47aa 100644 --- a/Src/LexText/Interlinear/InterlinViewDataCache.cs +++ b/Src/LexText/Interlinear/InterlinViewDataCache.cs @@ -1,73 +1,65 @@ // Copyright (c) 2009-2013 SIL International // This software is licensed under the LGPL, version 2.1 or later // (http://www.gnu.org/licenses/lgpl-2.1.html) -// -// File: InterlinViewDataCache.cs -// Responsibility: pyle -// -// -// using System; using System.Collections.Generic; using SIL.LCModel; -using SIL.LCModel.Application; +using SIL.LCModel.DomainServices; using HvoFlidKey=SIL.LCModel.HvoFlidKey; namespace SIL.FieldWorks.IText { /// ---------------------------------------------------------------------------------------- /// - /// + /// A data cache for guesses /// /// ---------------------------------------------------------------------------------------- - public class InterlinViewDataCache : DomainDataByFlidDecoratorBase + public class InterlinViewDataCache { private const int ktagMostApprovedAnalysis = -64; // arbitrary non-valid flid to use for storing Guesses private const int ktagOpinionAgent = -66; // arbitrary non-valid flid to use for storing opinion agents - private readonly IDictionary m_guessCache = new Dictionary(); + private readonly IDictionary m_guessCache = new Dictionary(); private readonly IDictionary m_humanApproved = new Dictionary(); - public InterlinViewDataCache(LcmCache cache) : base(cache.DomainDataByFlid as ISilDataAccessManaged) + public InterlinViewDataCache(LcmCache cache) { } - public override bool get_IsPropInCache(int hvo, int tag, int cpt, int ws) + public bool get_IsPropInCache(AnalysisOccurrence occurrence, int tag, int cpt, int ws) { switch (tag) { default: - return base.get_IsPropInCache(hvo, tag, cpt, ws); + throw new ArgumentException(string.Format("Unhandled property id: {0}", tag.ToString()), nameof(tag)); case ktagMostApprovedAnalysis: - return m_guessCache.ContainsKey(new HvoFlidKey(hvo, tag)); - case ktagOpinionAgent: - return m_humanApproved.ContainsKey(new HvoFlidKey(hvo, tag)); + return m_guessCache.ContainsKey(occurrence); } } - public override int get_ObjectProp(int hvo, int tag) + public int get_ObjectProp(AnalysisOccurrence occurrence, int tag) { switch (tag) { default: - return base.get_ObjectProp(hvo, tag); + throw new ArgumentException(string.Format("Unhandled property id: {0}", tag.ToString()), nameof(tag)); case ktagMostApprovedAnalysis: { int result; - if (m_guessCache.TryGetValue(new HvoFlidKey(hvo, tag), out result)) + if (m_guessCache.TryGetValue(occurrence, out result)) return result; return 0; // no guess cached. } } } - public override int get_IntProp(int hvo, int tag) + public int get_IntProp(int hvo, int tag) { switch (tag) { default: - return base.get_IntProp(hvo, tag); + throw new ArgumentException(string.Format("Unhandled property id: {0}", tag.ToString()), nameof(tag)); case ktagOpinionAgent: { int result; @@ -78,30 +70,27 @@ public override int get_IntProp(int hvo, int tag) } } - public override void SetObjProp(int hvo, int tag, int hvoObj) + public void SetObjProp(AnalysisOccurrence occurrence, int tag, int hvoObj) { switch (tag) { default: - base.SetObjProp(hvo, tag, hvoObj); - break; + throw new ArgumentException(string.Format("Unhandled property id: {0}", tag.ToString()), nameof(tag)); case ktagMostApprovedAnalysis: - var key = new HvoFlidKey(hvo, tag); if (hvoObj == 0) - m_guessCache.Remove(key); + m_guessCache.Remove(occurrence); else - m_guessCache[key] = hvoObj; + m_guessCache[occurrence] = hvoObj; break; } } - public override void SetInt(int hvo, int tag, int n) + public void SetInt(int hvo, int tag, int n) { switch (tag) { default: - base.SetInt(hvo, tag, n); - break; + throw new ArgumentException(string.Format("Unhandled property id: {0}", tag.ToString()), nameof(tag)); case ktagOpinionAgent: m_humanApproved[new HvoFlidKey(hvo, tag)] = n; break; diff --git a/Src/LexText/Interlinear/SandboxBase.cs b/Src/LexText/Interlinear/SandboxBase.cs index 5501856e6b..325735b164 100644 --- a/Src/LexText/Interlinear/SandboxBase.cs +++ b/Src/LexText/Interlinear/SandboxBase.cs @@ -1641,7 +1641,7 @@ private void GetDefaults(IWfiWordform wordform, ref IWfiAnalysis analysis, out I if (InterlinDoc == null) // In Wordform Analyses tool and some unit tests, InterlinDoc is null return; - ISilDataAccess sda = InterlinDoc.RootBox.DataAccess; + var guessCache = InterlinDoc.GetGuessCache(); // If we're calling from the context of SetWordform(), we may be trying to establish // an alternative wordform/form/analysis. In that case, or if we don't have a default cached, @@ -1656,12 +1656,12 @@ private void GetDefaults(IWfiWordform wordform, ref IWfiAnalysis analysis, out I { // Try to establish a default based on the current occurrence. if (m_fSetWordformInProgress || - !sda.get_IsPropInCache(HvoAnnotation, InterlinViewDataCache.AnalysisMostApprovedFlid, + !guessCache.get_IsPropInCache(m_occurrenceSelected, InterlinViewDataCache.AnalysisMostApprovedFlid, (int) CellarPropertyType.ReferenceAtomic, 0)) { InterlinDoc.RecordGuessIfNotKnown(m_occurrenceSelected); } - hvoDefault = sda.get_ObjectProp(HvoAnnotation, InterlinViewDataCache.AnalysisMostApprovedFlid); + hvoDefault = guessCache.get_ObjectProp(m_occurrenceSelected, InterlinViewDataCache.AnalysisMostApprovedFlid); // In certain cases like during an undo the Decorator data might be stale, so validate the result before we continue // to prevent using data that does not exist anymore if(!Cache.ServiceLocator.IsValidObjectId(hvoDefault))