public void GetOrderedValue_extraVoElements_sameSequence() { // Setup AppendTestItemToList("Second"); AppendTestItemToList("Third"); var initialSeq = m_testList.PossibilitiesOS.Cast <ICmObject>(); var myvo = CreateTestVO(initialSeq); // Make sure test setup worked Assert.AreEqual(1, m_voRepo.Count, "There ought to be one VO object."); Assert.AreEqual(3, myvo.ItemsRS.Count, "Wrong number of items in VO."); // Delete an element from the 'testing' sequence. var newList = m_testList.PossibilitiesOS.ToList(); newList.RemoveAt(0); var newSeq = newList.Cast <ICmObject>(); // SUT var resultSeq = VirtualOrderingServices.GetOrderedValue(m_testList, possibilitiesFlid, newSeq); // Verify Assert.AreEqual(1, m_voRepo.Count, "There ought to still be one VO object."); Assert.AreEqual(2, resultSeq.Count(), "Wrong number of items in result."); Assert.AreEqual(newSeq, resultSeq, "Hvo lists differ."); }
public void GetOrderedValue_extraNativeAndExtraVoElements() { // Setup AppendTestItemToList("Second"); AppendTestItemToList("Third"); var initialSeq = m_testList.PossibilitiesOS.Cast <ICmObject>(); CreateTestVO(initialSeq); // Make sure test setup worked Assert.AreEqual(1, m_voRepo.Count, "There ought to be one VO object."); // Make a sequence in a different order AppendTestItemToList("Fourth"); var newList = m_testList.PossibilitiesOS.Reverse().ToList(); // remove 'First' (which is now at the end) newList.RemoveAt(newList.Count - 1); var newSeq = newList.Cast <ICmObject>(); // SUT var resultSeq = VirtualOrderingServices.GetOrderedValue(m_testList, possibilitiesFlid, newSeq); // Verify Assert.AreEqual(1, m_voRepo.Count, "There ought to still be one VO object."); // result should be (Second, Third, Fourth) (or reverse of newSeq) var actualList = newSeq.Reverse().ToList(); Assert.AreEqual(actualList, resultSeq, "Hvo lists differ."); }
public void GetOrderedValue_extraVoElements_diffSequence() { // Setup AppendTestItemToList("Second"); AppendTestItemToList("Third"); var initialSeq = m_testList.PossibilitiesOS.Cast <ICmObject>(); CreateTestVO(initialSeq); // Make sure test setup worked Assert.AreEqual(1, m_voRepo.Count, "There ought to be one VO object."); // Make a sequence in a different order var newList = m_testList.PossibilitiesOS.Reverse().ToList(); newList.RemoveAt(0); var newSeq = newList.Cast <ICmObject>(); // SUT var resultSeq = VirtualOrderingServices.GetOrderedValue(m_testList, possibilitiesFlid, newSeq); // Verify Assert.AreEqual(1, m_voRepo.Count, "There ought to still be one VO object."); var resultList = new List <ICmObject>(resultSeq); // makes it easier to verify results Assert.AreEqual(2, resultList.Count); Assert.AreEqual("First", ((ICmPossibility)resultList[0]).Name.AnalysisDefaultWritingSystem.Text, "Wrong element at beginning of result sequence."); Assert.AreEqual("Second", ((ICmPossibility)resultList[1]).Name.AnalysisDefaultWritingSystem.Text, "Wrong element at end of result sequence."); }
public void ReferringSenses() { // Verify that if we make three lex entries whose senses all refer to niño, // that the reversal index shows them in alphabetical order: boy, child, girl. var girl = (LexEntry)MakeEntry("girl", "niño"); var senseGirl = girl.SensesOS.First(); var boy = (LexEntry)MakeEntry("boy", "niño"); var senseBoy = boy.SensesOS.First(); var child = (LexEntry)MakeEntry("child", "niño"); var senseChild = child.SensesOS.First(); var lexDb = Cache.ServiceLocator.GetInstance <ILexDbRepository>().Singleton; var ri = Cache.ServiceLocator.GetInstance <IReversalIndexFactory>().Create(); lexDb.ReversalIndexesOC.Add(ri); var rie = MakeReversalIndexReference(senseGirl, "niño", ri); senseBoy.ReversalEntriesRC.Add(rie); senseChild.ReversalEntriesRC.Add(rie); Assert.That(rie.ReferringSenses.First(), Is.EqualTo(senseBoy)); Assert.That(rie.ReferringSenses.Skip(1).First(), Is.EqualTo(senseChild)); Assert.That(rie.ReferringSenses.Last(), Is.EqualTo(senseGirl)); // we now manually reorder the senses var flid = Cache.MetaDataCacheAccessor.GetFieldId2(ReversalIndexEntryTags.kClassId, "ReferringSenses", false); VirtualOrderingServices.SetVO(rie, flid, new ICmObject[] { senseGirl, senseChild, senseBoy }); Assert.That(rie.ReferringSenses.First(), Is.EqualTo(senseGirl)); Assert.That(rie.ReferringSenses.Skip(1).First(), Is.EqualTo(senseChild)); Assert.That(rie.ReferringSenses.Last(), Is.EqualTo(senseBoy)); }
public void SetExistingVO_diffSequence() { // Setup AppendTestItemToList("Second"); AppendTestItemToList("Third"); var initialSeq = m_testList.PossibilitiesOS.Cast <ICmObject>(); CreateTestVO(initialSeq); // Make sure test setup worked Assert.AreEqual(1, m_voRepo.Count, "There ought to be one VO object."); // Make a sequence in a different order var newSeq = m_testList.PossibilitiesOS.Reverse().Cast <ICmObject>(); // SUT VirtualOrderingServices.SetVO(m_testList, possibilitiesFlid, newSeq); // Verify Assert.AreEqual(1, m_voRepo.Count, "There ought to still be one VO object."); var myvo = m_voRepo.AllInstances().First(); Assert.AreEqual(m_testList.Hvo, myvo.SourceRA.Hvo, "Got wrong Source object!"); Assert.AreEqual(possName, myvo.Field, "VO is on the wrong field!"); var voList = myvo.ItemsRS; Assert.AreEqual(newSeq, voList, "Hvo lists differ."); }
public void VisibleComplexFormBackRefs() { // Verify that if we make lex entries blackboard and blackbird, blackboard correctly comes first. var black = (LexEntry)MakeEntry("black", "nonreflecting"); var blackbird = (LexEntry)MakeEntry("blackbird", "dark avian"); var blackboard = (LexEntry)MakeEntry("blackboard", "something to write on"); var lerBlackbird = MakeComplexEntryRef(blackbird); lerBlackbird.ComponentLexemesRS.Add(black); lerBlackbird.ShowComplexFormsInRS.Add(black); Assert.That(black.VisibleComplexFormBackRefs.First(), Is.EqualTo(lerBlackbird)); var lerBlackboard = MakeComplexEntryRef(blackboard); lerBlackboard.ComponentLexemesRS.Add(black); lerBlackboard.ShowComplexFormsInRS.Add(black); Assert.That(black.VisibleComplexFormBackRefs.First(), Is.EqualTo(lerBlackbird), "although added later, blackbird is sorted first"); Assert.That(black.VisibleComplexFormBackRefs.Skip(1).First(), Is.EqualTo(lerBlackboard)); var flid = Cache.MetaDataCacheAccessor.GetFieldId2(LexEntryTags.kClassId, "VisibleComplexFormBackRefs", false); VirtualOrderingServices.SetVO(black, flid, new ICmObject[] { lerBlackboard, lerBlackbird }); Assert.That(black.VisibleComplexFormBackRefs.First(), Is.EqualTo(lerBlackboard), "although added later, blackbird is sorted first"); Assert.That(black.VisibleComplexFormBackRefs.Skip(1).First(), Is.EqualTo(lerBlackbird)); }
private IVirtualOrdering CreateTestVO(IEnumerable <ICmObject> desiredSeq) { VirtualOrderingServices.SetVO(m_testList, possibilitiesFlid, desiredSeq); var result = m_voRepo.AllInstances().Where(vo => vo.SourceRA == m_testList && vo.Field == possName); return(result.FirstOrDefault()); }
private void ReorderItems(List <ICmObject> vals) { if (RootPropertyIsRealRefSequence()) { // Since we are re-ordering, presume all objects are replaced by the entire new value. Cache.DomainDataByFlid.Replace(m_rootObj.Hvo, m_rootFlid, 0, vals.Count, vals.Select(obj => obj.Hvo).ToArray(), vals.Count); } else { VirtualOrderingServices.SetVO(m_rootObj, m_rootFlid, vals); } }
public void SetVO_nullSequence() { // Setup AppendTestItemToList("Second"); // For this test, just keep the same order. var desiredSeq = m_testList.PossibilitiesOS.Cast <ICmObject>(); var myvo = CreateTestVO(desiredSeq); // Make sure test setup worked Assert.AreEqual(1, m_voRepo.Count, "There ought to be one VO object."); // SUT VirtualOrderingServices.SetVO(m_testList, possibilitiesFlid, null); // Verify Assert.AreEqual(0, m_voRepo.Count, "Test should have deleted the only VO object."); }
public void VirtualPropTest() { // Setup CreateFakeGenreList(); // creates fake list of 4 genres on LangProj var testText = CreateTestText(); // creates IText on LangProj with its IStText. var testStText = testText.ContentsOA; // Get LP Fake Genre list var entireGenreList = Cache.LangProject.GenreListOA.PossibilitiesOS.ToList(); testText.GenresRC.Add(entireGenreList[1]); // Second testText.GenresRC.Add(entireGenreList[2]); // Third var initialSeq = testText.GenresRC.ToList(); // Verify that setup affects our chosen virtual property Assert.AreEqual(2, testStText.GenreCategories.Count, "Wrong number of items on virtual property."); var mdc = Cache.MetaDataCacheAccessor; int virtFlid = mdc.GetFieldId2(testStText.ClassID, "GenreCategories", true); // SUT1 VirtualOrderingServices.SetVO(testStText, virtFlid, initialSeq.Cast <ICmObject>()); // Make sure SUT1 worked Assert.AreEqual(1, m_voRepo.Count, "There ought to be one VO object."); // Setup for SUT2 // Make a sequence in a different order and with an extra item, but missing an original. var newList = new List <ICmObject>(entireGenreList.Cast <ICmObject>()); newList.Reverse(); // now has (Fourth, Third, Second, First) // remove 'Second' (which is now at index=2) newList.RemoveAt(2); // now has (Fourth, Third, First) // SUT2 var resultSeq = VirtualOrderingServices.GetOrderedValue(testStText, virtFlid, newList); // Verify Assert.AreEqual(1, m_voRepo.Count, "There ought to still be one VO object."); // result should be (Third, Fourth, First) var actualList = new List <ICmPossibility> { entireGenreList[2], entireGenreList[3], entireGenreList[0] }; var actualAsObj = actualList.Cast <ICmObject>(); Assert.AreEqual(actualAsObj, resultSeq, "Hvo lists differ."); }
public void GetOrderedValue_nonexistentVO() { // Setup AppendTestItemToList("Second"); var initialSeq = m_testList.PossibilitiesOS.Cast <ICmObject>(); // Make sure test setup worked Assert.AreEqual(0, m_voRepo.Count, "There shouldn't be an existing VO object."); // Make a sequence in a different order var newSeq = m_testList.PossibilitiesOS.Reverse().Cast <ICmObject>(); // SUT var resultSeq = VirtualOrderingServices.GetOrderedValue(m_testList, possibilitiesFlid, newSeq); // Verify Assert.AreEqual(0, m_voRepo.Count, "There ought to still not be a VO object."); Assert.AreEqual(newSeq, resultSeq, "Hvo lists differ."); }
public void GetOrderedValue_completeOverlap_diffSequence() { // Setup AppendTestItemToList("Second"); AppendTestItemToList("Third"); var initialSeq = m_testList.PossibilitiesOS.Cast <ICmObject>(); CreateTestVO(initialSeq); // Make sure test setup worked Assert.AreEqual(1, m_voRepo.Count, "There ought to be one VO object."); // Make a sequence in a different order var newSeq = m_testList.PossibilitiesOS.Reverse().Cast <ICmObject>(); // SUT var resultSeq = VirtualOrderingServices.GetOrderedValue(m_testList, possibilitiesFlid, newSeq); // Verify Assert.AreEqual(1, m_voRepo.Count, "There ought to still be one VO object."); Assert.AreEqual(3, resultSeq.Count(), "Wrong number of items in result."); Assert.AreEqual(initialSeq, resultSeq, "Hvo lists differ."); }
public void GetOrderedValue_extraNativeElements_diffSequence() { // Setup AppendTestItemToList("Second"); var initialSeq = m_testList.PossibilitiesOS.Cast <ICmObject>(); CreateTestVO(initialSeq); // Make sure test setup worked Assert.AreEqual(1, m_voRepo.Count, "There ought to be one VO object."); // Make a sequence in a different order with an extra element AppendTestItemToList("Third"); var newSeq = m_testList.PossibilitiesOS.Reverse().Cast <ICmObject>().ToList(); // SUT var resultSeq = VirtualOrderingServices.GetOrderedValue(m_testList, possibilitiesFlid, newSeq); // Verify Assert.AreEqual(1, m_voRepo.Count, "There ought to still be one VO object."); newSeq.Reverse(); // the resulting sequence should be the reverse of what was fed in. Assert.AreEqual(newSeq, resultSeq, "Hvo lists differ."); }
public void GetOrderedValue_extraNativeElements_sameSequence() { // Setup AppendTestItemToList("Second"); var initialSeq = m_testList.PossibilitiesOS.Cast <ICmObject>(); CreateTestVO(initialSeq); // Make sure test setup worked Assert.AreEqual(1, m_voRepo.Count, "There ought to be one VO object."); // Add to the 'testing' sequence AppendTestItemToList("Third"); var newSeq = m_testList.PossibilitiesOS.Cast <ICmObject>(); // SUT var resultSeq = VirtualOrderingServices.GetOrderedValue(m_testList, possibilitiesFlid, newSeq); // Verify Assert.AreEqual(1, m_voRepo.Count, "There ought to still be one VO object."); Assert.AreEqual(3, resultSeq.Count(), "Wrong number of items in result."); Assert.AreEqual(newSeq, resultSeq, "Hvo lists differ."); Assert.AreEqual("Third", ((ICmPossibility)resultSeq.Last()).Name.AnalysisDefaultWritingSystem.Text, "Wrong element at end of result sequence."); }
public void GetOrderedValue_completeOverlap_sameSequence() { // Setup AppendTestItemToList("Second"); AppendTestItemToList("Third"); var initialSeq = m_testList.PossibilitiesOS.Cast <ICmObject>(); CreateTestVO(initialSeq); // Make sure test setup worked Assert.AreEqual(1, m_voRepo.Count, "There ought to be one VO object."); // SUT var resultSeq = VirtualOrderingServices.GetOrderedValue(m_testList, possibilitiesFlid, initialSeq); // Verify Assert.AreEqual(1, m_voRepo.Count, "There ought to still be one VO object."); var myvo = m_voRepo.AllInstances().First(); Assert.AreEqual(m_testList.Hvo, myvo.SourceRA.Hvo, "Got wrong Source object!"); Assert.AreEqual(possName, myvo.Field, "VO is on the wrong field!"); var voList = myvo.ItemsRS; Assert.AreEqual(initialSeq, voList, "Hvo lists differ."); }
internal void RemoveOrdering() { UndoableUnitOfWorkHelper.Do(DetailControlsStrings.ksUndoAlphabeticalOrder, DetailControlsStrings.ksRedoAlphabeticalOrder, Cache.ActionHandlerAccessor, () => VirtualOrderingServices.ResetVO(m_rootObj, m_rootFlid)); }
/// <summary> /// The main entry point to do the work of the original method. /// </summary> internal void Display(ref ITsString tssDelayedNumber) { MainCallerDisplayCommand dispInfo; if (!m_viewConstructor.CanGetMainCallerDisplayCommand(m_frag, out dispInfo)) { // Shouldn't be possible, but just in case... Debug.Assert(true, "No MainCallerDisplayCommand!"); return; } XmlNode listDelimitNode; // has the list seps attrs like 'sep' XmlNode specialAttrsNode; // has the more exotic ones like 'excludeHvo' listDelimitNode = specialAttrsNode = dispInfo.MainNode; // 'inheritSeps' attr means to use the 'caller' (the part ref node) // to get the separator information. if (XmlUtils.GetOptionalBooleanAttributeValue(listDelimitNode, "inheritSeps", false)) { listDelimitNode = dispInfo.Caller; } // // 1. get number of items in vector // var chvo = m_sda.get_VecSize(m_hvo, m_flid); if (chvo == 0) { // We may want to do something special for empty vectors. See LT-9687. ProcessEmptyVector(m_vwEnv, m_hvo, m_flid); return; } // // 2. for each item in the vector, // a) print leading number if desired. // b) call AddObj // c) print separator if desired and needed // int[] rghvo = GetVector(m_sda, m_hvo, m_flid); Debug.Assert(chvo == rghvo.Length); // // Define some special boolean flags. // // Note that we deliberately don't use the listDelimitNode here. // These three props are not currently configurable, and they belong on the 'seq' element, // not the part ref. var fCheckForEmptyItems = XmlUtils.GetOptionalBooleanAttributeValue(specialAttrsNode, "checkForEmptyItems", false); string exclude = XmlUtils.GetOptionalAttributeValue(specialAttrsNode, "excludeHvo", null); var fFirstOnly = XmlUtils.GetOptionalBooleanAttributeValue(specialAttrsNode, "firstOnly", false); XmlAttribute xaNum; var fNumber = SetNumberFlagIncludingSingleOption(listDelimitNode, chvo, out xaNum); ApplySortingIfSpecified(rghvo, specialAttrsNode); // Determine if sequence should be filtered by a stored list of Guids. // Note that if we filter, we replace rghvo with the filtered list. if (m_viewConstructor.ShouldFilterByGuid) { // order by vector item type guids // Don't reorder LexEntry VisibleComplexFormBackRefs vector if the user overrode it manually. var obj = m_cache.ServiceLocator.GetObject(m_hvo); if (obj is ILexEntry) { var lexEntry = obj as ILexEntry; if (m_flid == m_cache.MetaDataCacheAccessor.GetFieldId("LexEntry", "VisibleComplexFormBackRefs", false)) { if (!VirtualOrderingServices.HasVirtualOrdering(lexEntry, "VisibleComplexFormBackRefs")) { chvo = ApplyFilterToSequence(ref rghvo); } } else { chvo = ApplyFilterToSequence(ref rghvo); } } else { chvo = ApplyFilterToSequence(ref rghvo); } } // Check whether the user wants the grammatical information to appear only once, preceding any // sense numbers, if there is only one set of grammatical information, and all senses refer to // it. See LT-9663. var childFrag = m_frag; var fSingleGramInfoFirst = false; if (m_flid == LexEntryTags.kflidSenses) { fSingleGramInfoFirst = XmlUtils.GetOptionalBooleanAttributeValue(listDelimitNode, "singlegraminfofirst", false); } // This groups senses by placing graminfo before the number, and omitting it if the same as the // previous sense in the entry. This isn't yet supported by the UI, but may well be requested in // the future. (See LT-9663.) //bool fGramInfoBeforeNumber = XmlUtils.GetOptionalBooleanAttributeValue(listDelimitNode, "graminfobeforenumber", false); // Setup text properties for any numbering int wsEng = m_cache.WritingSystemFactory.GetWsFromStr(strEng); ITsTextProps ttpNum = null; var fDelayNumber = false; if (fNumber) { ttpNum = SetNumberTextProperties(wsEng, listDelimitNode); fDelayNumber = XmlUtils.GetOptionalBooleanAttributeValue(specialAttrsNode, "numdelay", false); } // A vector may be conditionally configured to display its objects as separate paragraphs // in dictionary (document) configuration. See LT-9667. var fShowAsParagraphsInInnerPile = XmlUtils.GetOptionalBooleanAttributeValue(listDelimitNode, "showasindentedpara", false); // We have (this is probably bad) two ways to do this. The better one is setting the flowType to divInPara. // When we do this for a vector, and configure properties that require us to insert numbering and so forth, // we currently force a paragraph for each item. It's too hard otherwise to get the numbers etc. into the paragraph. // This means that the X_AsPara layout which the configure dialog causes to be invoked when setting up sense-as-para // has to be configured NOT to make a paragraph. It also means we can't readily configure a view that has more than one // paragraph, except when doing another layer of inserting paragraphs into what is usually a single one. I don't like // this much but it does what we need for now and making it better looks very messy. var fShowAsParagraphsInDivInPara = XmlUtils.GetOptionalAttributeValue(listDelimitNode, "flowType", null) == "divInPara"; ITsString tssBefore = null; string sParaStyle = null; if (fShowAsParagraphsInInnerPile && chvo > 0) { sParaStyle = XmlUtils.GetOptionalAttributeValue(listDelimitNode, "style"); tssBefore = SetBeforeString(specialAttrsNode, listDelimitNode); // We need a line break here to force the inner pile of paragraphs to begin at // the margin, rather than somewhere in the middle of the line. m_vwEnv.AddString(m_cache.TsStrFactory.MakeString(StringUtils.kChHardLB.ToString(), m_cache.ServiceLocator.WritingSystems.DefaultAnalysisWritingSystem.Handle)); m_vwEnv.OpenInnerPile(); } else if (fShowAsParagraphsInDivInPara) { sParaStyle = XmlUtils.GetOptionalAttributeValue(listDelimitNode, "parastyle", ""); } // Setup and run actual internal vector loop var tsf = m_cache.TsStrFactory; var xattrSeparator = listDelimitNode.Attributes["sep"]; var fFirst = true; // May actually mean first non-empty. for (var ihvo = 0; ihvo < chvo; ++ihvo) { if (IsExcluded(exclude, ihvo, rghvo)) { continue; } if (fCheckForEmptyItems && IsItemEmpty(rghvo[ihvo], childFrag)) { continue; } if (fShowAsParagraphsInInnerPile || fShowAsParagraphsInDivInPara) { SetupParagraph(sParaStyle, (fFirst ? tssBefore : null), listDelimitNode); } // This needs to happen AFTER we wet up the paragraph that we want it to be part of // and any 'before' stuff that goes ahead of the whole sequence, but BEFORE we add any other stuff to the para. if (fFirst && fNumber && fSingleGramInfoFirst) { var fAllMsaSame = SetAllMsaSameFlag(chvo, rghvo); if (fAllMsaSame) { DisplayFirstChildPOS(rghvo[0], childFrag); } // Exactly if we put out the grammatical info at the start, we need to NOT put it out // as part of each item. Note that we must not set this flag before we put out the one-and-only // gram info, or that will be suppressed too! m_viewConstructor.ShouldIgnoreGramInfo = fAllMsaSame; } if (!fShowAsParagraphsInInnerPile && !fShowAsParagraphsInDivInPara) { AddSeparatorIfNeeded(fFirst, xattrSeparator, listDelimitNode, wsEng); } // add the numbering if needed. if (fNumber) { var sTag = CalculateAndFormatSenseLabel(rghvo, ihvo, xaNum); ITsStrBldr tsb = tsf.GetBldr(); tsb.Replace(0, 0, sTag, ttpNum); ITsString tss = tsb.GetString(); m_numberPartRef = listDelimitNode; AddNumberingNowOrDelayed(fDelayNumber, tss, out tssDelayedNumber); } // add the object. Debug.Assert(ihvo < rghvo.Length); m_vwEnv.AddObj(rghvo[ihvo], m_viewConstructor, childFrag); // Close Paragraph if displaying paragraphs if (fShowAsParagraphsInInnerPile || fShowAsParagraphsInDivInPara) { m_vwEnv.CloseParagraph(); } fFirst = false; if (fFirstOnly) { break; } } // end of sequence 'for' loop // Close Inner Pile if displaying paragraphs if (fShowAsParagraphsInInnerPile && chvo > 0) { m_vwEnv.CloseInnerPile(); } // Reset the flag for ignoring grammatical information after the first if it was set // earlier in this method. if (fSingleGramInfoFirst) { m_viewConstructor.ShouldIgnoreGramInfo = false; } // Reset the flag for delaying displaying a number. if (m_viewConstructor.DelayNumFlag && m_hvo == m_hvoDelayedNumber) { m_viewConstructor.DelayNumFlag = false; tssDelayedNumber = null; } // end of Display method }
/// <summary> /// The main entry point to do the work of the original method. /// </summary> internal void Display(ref ITsString tssDelayedNumber) { MainCallerDisplayCommand dispInfo; if (!m_viewConstructor.CanGetMainCallerDisplayCommand(m_frag, out dispInfo)) { // Shouldn't be possible, but just in case... Debug.Assert(true, "No MainCallerDisplayCommand!"); return; } XmlNode listDelimitNode; // has the list seps attrs like 'sep' XmlNode specialAttrsNode; // has the more exotic ones like 'excludeHvo' listDelimitNode = specialAttrsNode = dispInfo.MainNode; // 'inheritSeps' attr means to use the 'caller' (the part ref node) // to get the separator information. if (XmlUtils.GetOptionalBooleanAttributeValue(listDelimitNode, "inheritSeps", false)) { listDelimitNode = dispInfo.Caller; } // // 1. get number of items in vector // var chvo = m_sda.get_VecSize(m_hvo, m_flid); if (chvo == 0) { // We may want to do something special for empty vectors. See LT-9687. ProcessEmptyVector(m_vwEnv, m_hvo, m_flid); return; } // // 2. for each item in the vector, // a) print leading number if desired. // b) call AddObj // c) print separator if desired and needed // int[] rghvo = GetVector(m_sda, m_hvo, m_flid); Debug.Assert(chvo == rghvo.Length); // // Define some special boolean flags. // // Note that we deliberately don't use the listDelimitNode here. // These three props are not currently configurable, and they belong on the 'seq' element, // not the part ref. var fCheckForEmptyItems = XmlUtils.GetOptionalBooleanAttributeValue(specialAttrsNode, "checkForEmptyItems", false); string exclude = XmlUtils.GetOptionalAttributeValue(specialAttrsNode, "excludeHvo", null); var fFirstOnly = XmlUtils.GetOptionalBooleanAttributeValue(specialAttrsNode, "firstOnly", false); XmlAttribute xaNum; var fNumber = SetNumberFlagIncludingSingleOption(listDelimitNode, chvo, out xaNum); ApplySortingIfSpecified(rghvo, specialAttrsNode); // Determine if sequence should be filtered by a stored list of Guids. // Note that if we filter, we replace rghvo with the filtered list. if (m_viewConstructor.ShouldFilterByGuid) { // order by vector item type guids // Don't reorder LexEntry VisibleComplexFormBackRefs vector if the user overrode it manually. var obj = m_cache.ServiceLocator.GetObject(m_hvo); if (obj is ILexEntry) { var lexEntry = obj as ILexEntry; if (m_flid == m_cache.MetaDataCacheAccessor.GetFieldId("LexEntry", "VisibleComplexFormBackRefs", false)) { if (!VirtualOrderingServices.HasVirtualOrdering(lexEntry, "VisibleComplexFormBackRefs")) { chvo = ApplyFilterToSequence(ref rghvo); } } else { chvo = ApplyFilterToSequence(ref rghvo); } } else { chvo = ApplyFilterToSequence(ref rghvo); } } // Check whether the user wants the grammatical information to appear only once, preceding any // sense numbers, if there is only one set of grammatical information, and all senses refer to // it. See LT-9663. var childFrag = m_frag; var fSingleGramInfoFirst = false; if (m_flid == LexEntryTags.kflidSenses) { fSingleGramInfoFirst = XmlUtils.GetOptionalBooleanAttributeValue(listDelimitNode, "singlegraminfofirst", false); } // This groups senses by placing graminfo before the number, and omitting it if the same as the // previous sense in the entry. This isn't yet supported by the UI, but may well be requested in // the future. (See LT-9663.) //bool fGramInfoBeforeNumber = XmlUtils.GetOptionalBooleanAttributeValue(listDelimitNode, "graminfobeforenumber", false); // Setup text properties for any numbering int wsEng = m_cache.WritingSystemFactory.GetWsFromStr(strEng); ITsTextProps ttpNum = null; var fDelayNumber = false; if (fNumber) { ttpNum = SetNumberTextProperties(wsEng, listDelimitNode); fDelayNumber = XmlUtils.GetOptionalBooleanAttributeValue(specialAttrsNode, "numdelay", false); } // A vector may be conditionally configured to display its objects as separate paragraphs // in dictionary (document) configuration. See LT-9667. var fShowAsParagraphsInInnerPile = XmlUtils.GetOptionalBooleanAttributeValue(listDelimitNode, "showasindentedpara", false); // We have (this is probably bad) two ways to do this. The better one is setting the flowType to divInPara. // When we do this for a vector, and configure properties that require us to insert numbering and so forth, // we currently force a paragraph for each item. It's too hard otherwise to get the numbers etc. into the paragraph. // This means that the X_AsPara layout which the configure dialog causes to be invoked when setting up sense-as-para // has to be configured NOT to make a paragraph. It also means we can't readily configure a view that has more than one // paragraph, except when doing another layer of inserting paragraphs into what is usually a single one. I don't like // this much but it does what we need for now and making it better looks very messy. var fShowAsParagraphsInDivInPara = XmlUtils.GetOptionalAttributeValue(listDelimitNode, "flowType", null) == "divInPara"; ITsString tssBefore = null; string sParaStyle = null; if (fShowAsParagraphsInInnerPile && chvo > 0) { sParaStyle = XmlUtils.GetOptionalAttributeValue(listDelimitNode, "style"); tssBefore = SetBeforeString(specialAttrsNode, listDelimitNode); // We need a line break here to force the inner pile of paragraphs to begin at // the margin, rather than somewhere in the middle of the line. m_vwEnv.AddString(TsStringUtils.MakeString(StringUtils.kChHardLB.ToString(), m_cache.ServiceLocator.WritingSystems.DefaultAnalysisWritingSystem.Handle)); m_vwEnv.OpenInnerPile(); } else if (fShowAsParagraphsInDivInPara) { sParaStyle = XmlUtils.GetOptionalAttributeValue(listDelimitNode, "parastyle", ""); } // Setup and run actual internal vector loop var xattrSeparator = listDelimitNode.Attributes["sep"]; var fFirst = true; // May actually mean first non-empty. WrapParagraphDisplayCommand tempCommand = null; int tempId = 0; if (fShowAsParagraphsInInnerPile || fShowAsParagraphsInDivInPara) { // We make a temporary command object, whose purpose is to wrap the paragraphs we want to create // here for each item and any embellishments we add INSIDE the display of each individual item. // This ensures that if we break those paragraphs (e.g., for subentries of a sense), all the // paragraphs for the outer object are still correctly nested inside the item. // This is especially important for XML export, where if the paragraph and object elements are // not correctly nested, we don't even have valid XML. // The command object's lifetime is only as long as this XmlVcDisplayVec, // because it references this XmlVcDisplayVec object and even updates some of its member variables. // I'm not sure it really has to do that, but I was trying to make a minimal change to the code // that had to be wrapped in the command object so it could be invoked by the AddObj call. // It is possible we could make a more permanent and reusable command object, but it would // require extensive analysis of at least how the member variables it modifies are used to make // sure it is safe. At this point I'm going for the safest change I can. This whole area of the // system is likely to be rewritten sometime. tempCommand = new WrapParagraphDisplayCommand(childFrag, this, sParaStyle, tssBefore, listDelimitNode, fNumber, fDelayNumber, xaNum, ttpNum); tempId = m_viewConstructor.GetId(tempCommand); tempCommand.DelayedNumber = tssDelayedNumber; } for (var ihvo = 0; ihvo < chvo; ++ihvo) { if (IsExcluded(exclude, ihvo, rghvo)) { continue; } if (fCheckForEmptyItems && IsItemEmpty(rghvo[ihvo], childFrag)) { continue; } Debug.Assert(ihvo < rghvo.Length); if (fShowAsParagraphsInInnerPile || fShowAsParagraphsInDivInPara) { // Passing tempId causes it to use the tempCommand we made above, which does // much the same as the code in the else branch, except for wrapping the content // of the object in a paragraph (and not inserting separators). In general we want to keep // them the same, which is why the common code is wrapped in the AddItemEmbellishments // method. The critical thing is that the paragraph must be part of the object, // even though it is caused by the listDelimitNode attributes rather than by those // of the XML that directly controls the display of the object. m_vwEnv.AddObj(rghvo[ihvo], m_viewConstructor, tempId); } else { // not part of add embellishments because never used with either showAsParagraphs option AddSeparatorIfNeeded(fFirst, xattrSeparator, listDelimitNode, wsEng); AddItemEmbellishments(listDelimitNode, fNumber, rghvo[ihvo], ihvo, xaNum, ttpNum, fDelayNumber, ref tssDelayedNumber); m_vwEnv.AddObj(rghvo[ihvo], m_viewConstructor, childFrag); fFirst = false; } if (fFirstOnly) { break; } } // end of sequence 'for' loop if (tempCommand != null) { tssDelayedNumber = tempCommand.DelayedNumber; // recover the end result of how it was modified. m_viewConstructor.RemoveCommand(tempCommand, tempId); } // Close Inner Pile if displaying paragraphs if (fShowAsParagraphsInInnerPile && chvo > 0) { m_vwEnv.CloseInnerPile(); } // Reset the flag for ignoring grammatical information after the first if it was set // earlier in this method. if (fSingleGramInfoFirst) { m_viewConstructor.ShouldIgnoreGramInfo = false; } // Reset the flag for delaying displaying a number. if (m_viewConstructor.DelayNumFlag && m_hvo == m_hvoDelayedNumber) { m_viewConstructor.DelayNumFlag = false; tssDelayedNumber = null; } // end of Display method }