Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Src/LexText/Interlinear/ITextDllTests/SandboxBaseTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ public void HandleTab()
VerifySelection(sandbox, false, SandboxBase.ktagSbWordGloss, 0, -1);
// Next the icon on the word cat line.
sandbox.HandleTab(false);
VerifySelection(sandbox, true, SandboxBase.ktagWordPosIcon, 0, -1);
VerifySelection(sandbox, true, SandboxBase.ktagSbNamedObjName, SandboxBase.ktagSbWordPos, -1);
// Then we wrap around to the start icon on the word line.
sandbox.HandleTab(false);
VerifySelection(sandbox, true, SandboxBase.ktagAnalysisIcon, 0, -1);
Expand All @@ -117,7 +117,7 @@ public void HandleTab()
sandbox.HandleTab(true);
VerifySelection(sandbox, true, SandboxBase.ktagAnalysisIcon, 0, -1);
sandbox.HandleTab(true);
VerifySelection(sandbox, true, SandboxBase.ktagWordPosIcon, 0, -1);
VerifySelection(sandbox, true, SandboxBase.ktagSbNamedObjName, SandboxBase.ktagSbWordPos, -1);
sandbox.HandleTab(true);
VerifySelection(sandbox, false, SandboxBase.ktagSbWordGloss, 0, -1);
sandbox.HandleTab(true);
Expand Down
101 changes: 101 additions & 0 deletions Src/LexText/Interlinear/InterlinVc.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1896,6 +1896,14 @@ private void DisplayWordGloss(InterlinLineSpec spec, int choiceIndex)
var wa = (IWfiAnalysis) m_defaultObj;
if (wa.MeaningsOC.Count == 0)
{
ITsString rootGlossName = GetRootGlossName(wa);
if (m_hvoDefault != m_hvoWordBundleAnalysis && rootGlossName != null)
{
// Display our best guess for the gloss.
m_this.SetColor(m_vwenv, m_this.LabelRGBFor(choiceIndex));
m_vwenv.AddString(rootGlossName);
break;
}
// There's no gloss, display something indicating it is missing.
m_this.SetColor(m_vwenv, m_this.LabelRGBFor(choiceIndex));
m_vwenv.AddString(m_this.m_tssMissingAnalysis);
Expand Down Expand Up @@ -1929,6 +1937,35 @@ private void DisplayWordGloss(InterlinLineSpec spec, int choiceIndex)
}
}

private ITsString GetRootGlossName(IWfiAnalysis wa)
{
// This is modeled after SandboxBase.SyncMonomorphemicGlossAndPos.
ITsString rootGloss = null;
foreach (IWfiMorphBundle morphBundle in wa.MorphBundlesOS)
{
// Get the sense for morphBundle.
ILexSense sense = morphBundle.SenseRA;
if (sense == null && morphBundle.MorphRA != null)
{
ILexEntry lexEntry = morphBundle.MorphRA.Owner as ILexEntry;
if (lexEntry != null && lexEntry.SensesOS.Count > 0)
{
sense = lexEntry.SensesOS[0];
}
}
if (sense?.Gloss?.BestAnalysisAlternative == null)
continue;
// Consider the sense's gloss.
IMoMorphSynAnalysis msa = sense.MorphoSyntaxAnalysisRA;
bool fStem = msa is IMoStemMsa;
// If we have only one morpheme, treat it as the stem from which we will copy the gloss.
// otherwise, use the first stem we find, if any.
if ((fStem && rootGloss == null) || wa.MorphBundlesOS.Count == 1)
rootGloss = sense.Gloss.BestAnalysisAlternative;
}
return rootGloss;
}

private void DisplayWordPOS(int choiceIndex)
{
switch(m_defaultObj.ClassID)
Expand All @@ -1941,6 +1978,19 @@ private void DisplayWordPOS(int choiceIndex)
if (m_hvoDefault != m_hvoWordBundleAnalysis)
{
m_this.SetGuessing(m_vwenv, m_this.GetGuessColor(m_defaultObj));
var wa = (IWfiAnalysis) m_defaultObj;
int hvoPos = wa.CategoryRA != null ? wa.CategoryRA.Hvo : 0;
if (hvoPos == 0)
{
ITsString rootPOSName = GetRootPOSName(wa);
if (rootPOSName != null)
{
// Display our best guess for the gloss.
m_this.SetColor(m_vwenv, m_this.LabelRGBFor(choiceIndex));
m_vwenv.AddString(rootPOSName);
break;
}
}
}
m_this.AddAnalysisPos(m_vwenv, m_hvoDefault, m_hvoWordBundleAnalysis, choiceIndex);
break;
Expand All @@ -1954,6 +2004,57 @@ private void DisplayWordPOS(int choiceIndex)
throw new Exception("Invalid type found in Segment analysis");
}
}

private ITsString GetRootPOSName(IWfiAnalysis wa)
{
// This is modeled after SandboxBase.SyncMonomorphemicGlossAndPos.
IPartOfSpeech stemPOS = null;
IPartOfSpeech derivedPOS = null;
foreach (IWfiMorphBundle morphBundle in wa.MorphBundlesOS)
{
// Get the sense for morphBundle.
ILexSense sense = morphBundle.SenseRA;
if (sense == null && morphBundle.MorphRA != null)
{
if (morphBundle.MorphRA.Owner is ILexEntry lexEntry && lexEntry.SensesOS.Count > 0)
{
sense = lexEntry.SensesOS[0];
}
}
if (sense == null)
continue;
// Consider the sense's part of speech.
IMoMorphSynAnalysis msa = sense.MorphoSyntaxAnalysisRA;
bool fStem = msa is IMoStemMsa;
if (fStem)
{
IPartOfSpeech POS = (msa as IMoStemMsa).PartOfSpeechRA;
if (POS != stemPOS && stemPOS != null)
{
// found conflicting stems
return null;
}
else
stemPOS = POS;
}
else if (msa is IMoDerivAffMsa)
{
if (derivedPOS != null)
return null; // more than one DA
else
derivedPOS = (msa as IMoDerivAffMsa).ToPartOfSpeechRA;
}
}
if (stemPOS == null)
return null;

IPartOfSpeech lexPOS = derivedPOS ?? stemPOS;
if (lexPOS == null)
return null;
if (lexPOS.Abbreviation.BestAnalysisAlternative.Length > 0)
return lexPOS.Abbreviation.BestAnalysisAlternative;
return lexPOS.Name.BestAnalysisAlternative;
}
}

public override void DisplayVec(IVwEnv vwenv, int hvo, int tag, int frag)
Expand Down
119 changes: 6 additions & 113 deletions Src/LexText/Interlinear/SandboxBase.ComboHandlers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -812,129 +812,21 @@ internal ITsString NewAnalysisString(string str)

return TsStringUtils.MakeString(str, m_caches.MainCache.DefaultAnalWs);
}
/// <summary>
/// Synchronize the word gloss and POS with the morpheme gloss and MSA info, to the extent possible.
/// Currently works FROM the morpheme TO the Word, but going the other way may be useful, too.
///
/// for the word gloss:
/// - if only one morpheme, copy sense gloss to word gloss
/// - if multiple morphemes, copy first stem gloss to word gloss, but only if word gloss is empty.
/// for the POS:
/// - if there is more than one stem and they have different parts of speech, do nothing.
/// - if there is more than one derivational affix (DA), do nothing.
/// - otherwise, if there is no DA, use the POS of the stem.
/// - if there is no stem, do nothing.
/// - if there is a DA, use its 'to' POS.
/// (currently we don't insist that the 'from' POS matches the stem)
/// </summary>

internal void SyncMonomorphemicGlossAndPos(bool fCopyToWordGloss, bool fCopyToWordPos)
{
CheckDisposed();
if (!fCopyToWordGloss && !fCopyToWordPos)
return;

ISilDataAccess sda = m_caches.DataAccess;
int cmorphs = sda.get_VecSize(m_hvoSbWord, ktagSbWordMorphs);
int hvoSbRootSense = 0;
int hvoStemPos = 0; // ID in real database of part-of-speech of stem.
bool fGiveUpOnPOS = false;
int hvoDerivedPos = 0; // real ID of POS output of derivational MSA.
for (int imorph = 0; imorph < cmorphs; imorph++)
{
int hvoMorph = sda.get_VecItem(m_hvoSbWord, ktagSbWordMorphs, imorph);
int hvoSbSense = sda.get_ObjectProp(hvoMorph, ktagSbMorphGloss);
if (hvoSbSense == 0)
continue; // Can't sync from morph sense to word if we don't have morph sense.
var sense = m_caches.RealObject(hvoSbSense) as ILexSense;
IMoMorphSynAnalysis msa = sense.MorphoSyntaxAnalysisRA;

// ITsString prefix = sda.get_StringProp(hvoMorph, ktagSbMorphPrefix);
// ITsString suffix = sda.get_StringProp(hvoMorph, ktagSbMorphPostfix);
// bool fStem = prefix.Length == 0 && suffix.Length == 0;

bool fStem = msa is IMoStemMsa;

// If we have only one morpheme, treat it as the stem from which we will copy the gloss.
// otherwise, use the first stem we find, if any.
if ((fStem && hvoSbRootSense == 0) || cmorphs == 1)
hvoSbRootSense = hvoSbSense;

if (fStem)
{
int hvoPOS = (msa as IMoStemMsa).PartOfSpeechRA != null ? (msa as IMoStemMsa).PartOfSpeechRA.Hvo : 0;
if (hvoPOS != hvoStemPos && hvoStemPos != 0)
{
// found conflicting stems
fGiveUpOnPOS = true;
}
else
hvoStemPos = hvoPOS;
}
else if (msa is IMoDerivAffMsa)
{
if (hvoDerivedPos != 0)
fGiveUpOnPOS = true; // more than one DA
else
hvoDerivedPos = (msa as IMoDerivAffMsa).ToPartOfSpeechRA != null ? (msa as IMoDerivAffMsa).ToPartOfSpeechRA.Hvo : 0;
}
}

// If we found a sense to copy from, do it. Replace the word gloss even there already is
// one, since users get confused/frustrated if we don't. (See LT-6141.) It's marked as a
// guess after all!
CopySenseToWordGloss(fCopyToWordGloss, hvoSbRootSense);

// If we didn't find a stem, we don't have enough information to find a POS.
if (hvoStemPos == 0)
fGiveUpOnPOS = true;

int hvoLexPos = 0;
if (!fGiveUpOnPOS)
{
if (hvoDerivedPos != 0)
hvoLexPos = hvoDerivedPos;
else
hvoLexPos = hvoStemPos;
}
CopyLexPosToWordPos(fCopyToWordPos, hvoLexPos);
m_sandbox.SyncMonomorphemicGlossAndPos(fCopyToWordGloss, fCopyToWordPos);
}

protected virtual void CopySenseToWordGloss(bool fCopyWordGloss, int hvoSbRootSense)
{
if (hvoSbRootSense != 0 && fCopyWordGloss)
{
ISilDataAccess sda = m_caches.DataAccess;
m_caches.DataAccess.SetInt(m_hvoSbWord, ktagSbWordGlossGuess, 1);
int hvoRealSense = m_caches.RealHvo(hvoSbRootSense);
foreach (int wsId in m_sandbox.m_choices.EnabledWritingSystemsForFlid(InterlinLineChoices.kflidWordGloss))
{
// Update the guess, by copying the glosses of the SbNamedObj representing the sense
// to the word gloss property.
//ITsString tssGloss = sda.get_MultiStringAlt(hvoSbRootSense, ktagSbNamedObjName, wsId);
// No, it is safer to copy from the real sense. We may be displaying more WSS for the word than the sense.
ITsString tssGloss = m_caches.MainCache.MainCacheAccessor.get_MultiStringAlt(hvoRealSense, LexSenseTags.kflidGloss, wsId);
sda.SetMultiStringAlt(m_hvoSbWord, ktagSbWordGloss, wsId, tssGloss);
sda.PropChanged(null, (int)PropChangeType.kpctNotifyAll, m_hvoSbWord, ktagSbWordGloss,
wsId, 0, 0);
}
}
m_sandbox.CopySenseToWordGloss(fCopyWordGloss, hvoSbRootSense);
}

protected virtual int CopyLexPosToWordPos(bool fCopyToWordCat, int hvoMsaPos)
{
int hvoPos = 0;
if (fCopyToWordCat && hvoMsaPos != 0)
{
// got the one we want, in the real database. Make a corresponding sandbox one
// and install it as a guess
hvoPos = m_sandbox.CreateSecondaryAndCopyStrings(InterlinLineChoices.kflidWordPos, hvoMsaPos,
CmPossibilityTags.kflidAbbreviation);
int hvoSbWordPos = m_caches.DataAccess.get_ObjectProp(m_hvoSbWord, ktagSbWordPos);
m_caches.DataAccess.SetObjProp(m_hvoSbWord, ktagSbWordPos, hvoPos);
m_caches.DataAccess.SetInt(hvoPos, ktagSbNamedObjGuess, 1);
m_caches.DataAccess.PropChanged(m_rootb, (int)PropChangeType.kpctNotifyAll, m_hvoSbWord,
ktagSbWordPos, 0, 1, (hvoSbWordPos == 0 ? 0 : 1));
}
return hvoPos;
return m_sandbox.CopyLexPosToWordPos(fCopyToWordCat, hvoMsaPos);
}
}

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