Skip to content

Commit c7d8735

Browse files
authored
Fix LT-20848: Word gloss/cat doesn't get inserted automatically (#393)
* Fix LT-20848: Word gloss/cat doesn't get inserted automatically * Copy word gloss and pos after editing morph breaks * Fix unit test * Insert gloss/word automatically when showing interlinear guess
1 parent 624c79c commit c7d8735

4 files changed

Lines changed: 248 additions & 115 deletions

File tree

Src/LexText/Interlinear/ITextDllTests/SandboxBaseTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ public void HandleTab()
100100
VerifySelection(sandbox, false, SandboxBase.ktagSbWordGloss, 0, -1);
101101
// Next the icon on the word cat line.
102102
sandbox.HandleTab(false);
103-
VerifySelection(sandbox, true, SandboxBase.ktagWordPosIcon, 0, -1);
103+
VerifySelection(sandbox, true, SandboxBase.ktagSbNamedObjName, SandboxBase.ktagSbWordPos, -1);
104104
// Then we wrap around to the start icon on the word line.
105105
sandbox.HandleTab(false);
106106
VerifySelection(sandbox, true, SandboxBase.ktagAnalysisIcon, 0, -1);
@@ -117,7 +117,7 @@ public void HandleTab()
117117
sandbox.HandleTab(true);
118118
VerifySelection(sandbox, true, SandboxBase.ktagAnalysisIcon, 0, -1);
119119
sandbox.HandleTab(true);
120-
VerifySelection(sandbox, true, SandboxBase.ktagWordPosIcon, 0, -1);
120+
VerifySelection(sandbox, true, SandboxBase.ktagSbNamedObjName, SandboxBase.ktagSbWordPos, -1);
121121
sandbox.HandleTab(true);
122122
VerifySelection(sandbox, false, SandboxBase.ktagSbWordGloss, 0, -1);
123123
sandbox.HandleTab(true);

Src/LexText/Interlinear/InterlinVc.cs

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1896,6 +1896,14 @@ private void DisplayWordGloss(InterlinLineSpec spec, int choiceIndex)
18961896
var wa = (IWfiAnalysis) m_defaultObj;
18971897
if (wa.MeaningsOC.Count == 0)
18981898
{
1899+
ITsString rootGlossName = GetRootGlossName(wa);
1900+
if (m_hvoDefault != m_hvoWordBundleAnalysis && rootGlossName != null)
1901+
{
1902+
// Display our best guess for the gloss.
1903+
m_this.SetColor(m_vwenv, m_this.LabelRGBFor(choiceIndex));
1904+
m_vwenv.AddString(rootGlossName);
1905+
break;
1906+
}
18991907
// There's no gloss, display something indicating it is missing.
19001908
m_this.SetColor(m_vwenv, m_this.LabelRGBFor(choiceIndex));
19011909
m_vwenv.AddString(m_this.m_tssMissingAnalysis);
@@ -1929,6 +1937,35 @@ private void DisplayWordGloss(InterlinLineSpec spec, int choiceIndex)
19291937
}
19301938
}
19311939

1940+
private ITsString GetRootGlossName(IWfiAnalysis wa)
1941+
{
1942+
// This is modeled after SandboxBase.SyncMonomorphemicGlossAndPos.
1943+
ITsString rootGloss = null;
1944+
foreach (IWfiMorphBundle morphBundle in wa.MorphBundlesOS)
1945+
{
1946+
// Get the sense for morphBundle.
1947+
ILexSense sense = morphBundle.SenseRA;
1948+
if (sense == null && morphBundle.MorphRA != null)
1949+
{
1950+
ILexEntry lexEntry = morphBundle.MorphRA.Owner as ILexEntry;
1951+
if (lexEntry != null && lexEntry.SensesOS.Count > 0)
1952+
{
1953+
sense = lexEntry.SensesOS[0];
1954+
}
1955+
}
1956+
if (sense?.Gloss?.BestAnalysisAlternative == null)
1957+
continue;
1958+
// Consider the sense's gloss.
1959+
IMoMorphSynAnalysis msa = sense.MorphoSyntaxAnalysisRA;
1960+
bool fStem = msa is IMoStemMsa;
1961+
// If we have only one morpheme, treat it as the stem from which we will copy the gloss.
1962+
// otherwise, use the first stem we find, if any.
1963+
if ((fStem && rootGloss == null) || wa.MorphBundlesOS.Count == 1)
1964+
rootGloss = sense.Gloss.BestAnalysisAlternative;
1965+
}
1966+
return rootGloss;
1967+
}
1968+
19321969
private void DisplayWordPOS(int choiceIndex)
19331970
{
19341971
switch(m_defaultObj.ClassID)
@@ -1941,6 +1978,19 @@ private void DisplayWordPOS(int choiceIndex)
19411978
if (m_hvoDefault != m_hvoWordBundleAnalysis)
19421979
{
19431980
m_this.SetGuessing(m_vwenv, m_this.GetGuessColor(m_defaultObj));
1981+
var wa = (IWfiAnalysis) m_defaultObj;
1982+
int hvoPos = wa.CategoryRA != null ? wa.CategoryRA.Hvo : 0;
1983+
if (hvoPos == 0)
1984+
{
1985+
ITsString rootPOSName = GetRootPOSName(wa);
1986+
if (rootPOSName != null)
1987+
{
1988+
// Display our best guess for the gloss.
1989+
m_this.SetColor(m_vwenv, m_this.LabelRGBFor(choiceIndex));
1990+
m_vwenv.AddString(rootPOSName);
1991+
break;
1992+
}
1993+
}
19441994
}
19451995
m_this.AddAnalysisPos(m_vwenv, m_hvoDefault, m_hvoWordBundleAnalysis, choiceIndex);
19461996
break;
@@ -1954,6 +2004,57 @@ private void DisplayWordPOS(int choiceIndex)
19542004
throw new Exception("Invalid type found in Segment analysis");
19552005
}
19562006
}
2007+
2008+
private ITsString GetRootPOSName(IWfiAnalysis wa)
2009+
{
2010+
// This is modeled after SandboxBase.SyncMonomorphemicGlossAndPos.
2011+
IPartOfSpeech stemPOS = null;
2012+
IPartOfSpeech derivedPOS = null;
2013+
foreach (IWfiMorphBundle morphBundle in wa.MorphBundlesOS)
2014+
{
2015+
// Get the sense for morphBundle.
2016+
ILexSense sense = morphBundle.SenseRA;
2017+
if (sense == null && morphBundle.MorphRA != null)
2018+
{
2019+
if (morphBundle.MorphRA.Owner is ILexEntry lexEntry && lexEntry.SensesOS.Count > 0)
2020+
{
2021+
sense = lexEntry.SensesOS[0];
2022+
}
2023+
}
2024+
if (sense == null)
2025+
continue;
2026+
// Consider the sense's part of speech.
2027+
IMoMorphSynAnalysis msa = sense.MorphoSyntaxAnalysisRA;
2028+
bool fStem = msa is IMoStemMsa;
2029+
if (fStem)
2030+
{
2031+
IPartOfSpeech POS = (msa as IMoStemMsa).PartOfSpeechRA;
2032+
if (POS != stemPOS && stemPOS != null)
2033+
{
2034+
// found conflicting stems
2035+
return null;
2036+
}
2037+
else
2038+
stemPOS = POS;
2039+
}
2040+
else if (msa is IMoDerivAffMsa)
2041+
{
2042+
if (derivedPOS != null)
2043+
return null; // more than one DA
2044+
else
2045+
derivedPOS = (msa as IMoDerivAffMsa).ToPartOfSpeechRA;
2046+
}
2047+
}
2048+
if (stemPOS == null)
2049+
return null;
2050+
2051+
IPartOfSpeech lexPOS = derivedPOS ?? stemPOS;
2052+
if (lexPOS == null)
2053+
return null;
2054+
if (lexPOS.Abbreviation.BestAnalysisAlternative.Length > 0)
2055+
return lexPOS.Abbreviation.BestAnalysisAlternative;
2056+
return lexPOS.Name.BestAnalysisAlternative;
2057+
}
19572058
}
19582059

19592060
public override void DisplayVec(IVwEnv vwenv, int hvo, int tag, int frag)

Src/LexText/Interlinear/SandboxBase.ComboHandlers.cs

Lines changed: 6 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -812,129 +812,21 @@ internal ITsString NewAnalysisString(string str)
812812

813813
return TsStringUtils.MakeString(str, m_caches.MainCache.DefaultAnalWs);
814814
}
815-
/// <summary>
816-
/// Synchronize the word gloss and POS with the morpheme gloss and MSA info, to the extent possible.
817-
/// Currently works FROM the morpheme TO the Word, but going the other way may be useful, too.
818-
///
819-
/// for the word gloss:
820-
/// - if only one morpheme, copy sense gloss to word gloss
821-
/// - if multiple morphemes, copy first stem gloss to word gloss, but only if word gloss is empty.
822-
/// for the POS:
823-
/// - if there is more than one stem and they have different parts of speech, do nothing.
824-
/// - if there is more than one derivational affix (DA), do nothing.
825-
/// - otherwise, if there is no DA, use the POS of the stem.
826-
/// - if there is no stem, do nothing.
827-
/// - if there is a DA, use its 'to' POS.
828-
/// (currently we don't insist that the 'from' POS matches the stem)
829-
/// </summary>
815+
830816
internal void SyncMonomorphemicGlossAndPos(bool fCopyToWordGloss, bool fCopyToWordPos)
831817
{
832818
CheckDisposed();
833-
if (!fCopyToWordGloss && !fCopyToWordPos)
834-
return;
835-
836-
ISilDataAccess sda = m_caches.DataAccess;
837-
int cmorphs = sda.get_VecSize(m_hvoSbWord, ktagSbWordMorphs);
838-
int hvoSbRootSense = 0;
839-
int hvoStemPos = 0; // ID in real database of part-of-speech of stem.
840-
bool fGiveUpOnPOS = false;
841-
int hvoDerivedPos = 0; // real ID of POS output of derivational MSA.
842-
for (int imorph = 0; imorph < cmorphs; imorph++)
843-
{
844-
int hvoMorph = sda.get_VecItem(m_hvoSbWord, ktagSbWordMorphs, imorph);
845-
int hvoSbSense = sda.get_ObjectProp(hvoMorph, ktagSbMorphGloss);
846-
if (hvoSbSense == 0)
847-
continue; // Can't sync from morph sense to word if we don't have morph sense.
848-
var sense = m_caches.RealObject(hvoSbSense) as ILexSense;
849-
IMoMorphSynAnalysis msa = sense.MorphoSyntaxAnalysisRA;
850-
851-
// ITsString prefix = sda.get_StringProp(hvoMorph, ktagSbMorphPrefix);
852-
// ITsString suffix = sda.get_StringProp(hvoMorph, ktagSbMorphPostfix);
853-
// bool fStem = prefix.Length == 0 && suffix.Length == 0;
854-
855-
bool fStem = msa is IMoStemMsa;
856-
857-
// If we have only one morpheme, treat it as the stem from which we will copy the gloss.
858-
// otherwise, use the first stem we find, if any.
859-
if ((fStem && hvoSbRootSense == 0) || cmorphs == 1)
860-
hvoSbRootSense = hvoSbSense;
861-
862-
if (fStem)
863-
{
864-
int hvoPOS = (msa as IMoStemMsa).PartOfSpeechRA != null ? (msa as IMoStemMsa).PartOfSpeechRA.Hvo : 0;
865-
if (hvoPOS != hvoStemPos && hvoStemPos != 0)
866-
{
867-
// found conflicting stems
868-
fGiveUpOnPOS = true;
869-
}
870-
else
871-
hvoStemPos = hvoPOS;
872-
}
873-
else if (msa is IMoDerivAffMsa)
874-
{
875-
if (hvoDerivedPos != 0)
876-
fGiveUpOnPOS = true; // more than one DA
877-
else
878-
hvoDerivedPos = (msa as IMoDerivAffMsa).ToPartOfSpeechRA != null ? (msa as IMoDerivAffMsa).ToPartOfSpeechRA.Hvo : 0;
879-
}
880-
}
881-
882-
// If we found a sense to copy from, do it. Replace the word gloss even there already is
883-
// one, since users get confused/frustrated if we don't. (See LT-6141.) It's marked as a
884-
// guess after all!
885-
CopySenseToWordGloss(fCopyToWordGloss, hvoSbRootSense);
886-
887-
// If we didn't find a stem, we don't have enough information to find a POS.
888-
if (hvoStemPos == 0)
889-
fGiveUpOnPOS = true;
890-
891-
int hvoLexPos = 0;
892-
if (!fGiveUpOnPOS)
893-
{
894-
if (hvoDerivedPos != 0)
895-
hvoLexPos = hvoDerivedPos;
896-
else
897-
hvoLexPos = hvoStemPos;
898-
}
899-
CopyLexPosToWordPos(fCopyToWordPos, hvoLexPos);
819+
m_sandbox.SyncMonomorphemicGlossAndPos(fCopyToWordGloss, fCopyToWordPos);
900820
}
901821

902822
protected virtual void CopySenseToWordGloss(bool fCopyWordGloss, int hvoSbRootSense)
903823
{
904-
if (hvoSbRootSense != 0 && fCopyWordGloss)
905-
{
906-
ISilDataAccess sda = m_caches.DataAccess;
907-
m_caches.DataAccess.SetInt(m_hvoSbWord, ktagSbWordGlossGuess, 1);
908-
int hvoRealSense = m_caches.RealHvo(hvoSbRootSense);
909-
foreach (int wsId in m_sandbox.m_choices.EnabledWritingSystemsForFlid(InterlinLineChoices.kflidWordGloss))
910-
{
911-
// Update the guess, by copying the glosses of the SbNamedObj representing the sense
912-
// to the word gloss property.
913-
//ITsString tssGloss = sda.get_MultiStringAlt(hvoSbRootSense, ktagSbNamedObjName, wsId);
914-
// No, it is safer to copy from the real sense. We may be displaying more WSS for the word than the sense.
915-
ITsString tssGloss = m_caches.MainCache.MainCacheAccessor.get_MultiStringAlt(hvoRealSense, LexSenseTags.kflidGloss, wsId);
916-
sda.SetMultiStringAlt(m_hvoSbWord, ktagSbWordGloss, wsId, tssGloss);
917-
sda.PropChanged(null, (int)PropChangeType.kpctNotifyAll, m_hvoSbWord, ktagSbWordGloss,
918-
wsId, 0, 0);
919-
}
920-
}
824+
m_sandbox.CopySenseToWordGloss(fCopyWordGloss, hvoSbRootSense);
921825
}
826+
922827
protected virtual int CopyLexPosToWordPos(bool fCopyToWordCat, int hvoMsaPos)
923828
{
924-
int hvoPos = 0;
925-
if (fCopyToWordCat && hvoMsaPos != 0)
926-
{
927-
// got the one we want, in the real database. Make a corresponding sandbox one
928-
// and install it as a guess
929-
hvoPos = m_sandbox.CreateSecondaryAndCopyStrings(InterlinLineChoices.kflidWordPos, hvoMsaPos,
930-
CmPossibilityTags.kflidAbbreviation);
931-
int hvoSbWordPos = m_caches.DataAccess.get_ObjectProp(m_hvoSbWord, ktagSbWordPos);
932-
m_caches.DataAccess.SetObjProp(m_hvoSbWord, ktagSbWordPos, hvoPos);
933-
m_caches.DataAccess.SetInt(hvoPos, ktagSbNamedObjGuess, 1);
934-
m_caches.DataAccess.PropChanged(m_rootb, (int)PropChangeType.kpctNotifyAll, m_hvoSbWord,
935-
ktagSbWordPos, 0, 1, (hvoSbWordPos == 0 ? 0 : 1));
936-
}
937-
return hvoPos;
829+
return m_sandbox.CopyLexPosToWordPos(fCopyToWordCat, hvoMsaPos);
938830
}
939831
}
940832

@@ -1324,6 +1216,7 @@ internal void UpdateMorphBreaks(string sMorphs)
13241216
MorphemeBreaker mb = new MorphemeBreaker(m_caches, sMorphs, m_hvoSbWord,
13251217
m_wsVern, m_sandbox);
13261218
mb.Run();
1219+
m_sandbox.CopyLexEntryInfoToMonomorphemicWordGlossAndPos();
13271220
m_rootb.Reconstruct(); // Everything changed, more or less.
13281221
// We've changed properties that the morph manager cares about, but we don't want it
13291222
// to fire when we fix the selection.

0 commit comments

Comments
 (0)