diff --git a/Src/xWorks/ConfiguredLcmGenerator.cs b/Src/xWorks/ConfiguredLcmGenerator.cs index 704b646ad6..4f9a7f71df 100644 --- a/Src/xWorks/ConfiguredLcmGenerator.cs +++ b/Src/xWorks/ConfiguredLcmGenerator.cs @@ -1410,8 +1410,12 @@ private static IFragment GenerateContentForCollection(object collectionField, Co Debug.Assert(config.DictionaryNodeOptions == null, "double calls to GenerateContentForLexEntryRefsByType don't play nicely with ListOptions. Everything will be generated twice (if it doesn't crash)"); // Display typeless refs + bool first = true; foreach (var entry in lerCollection.Where(item => !item.ComplexEntryTypesRS.Any() && !item.VariantEntryTypesRS.Any())) - bldr.Append(GenerateCollectionItemContent(config, pubDecorator, entry, collectionOwner, settings, lexEntryTypeNode)); + { + bldr.Append(GenerateCollectionItemContent(config, pubDecorator, entry, collectionOwner, settings, first, lexEntryTypeNode)); + first = false; + } // Display refs of each type GenerateContentForLexEntryRefsByType(config, lerCollection, collectionOwner, pubDecorator, settings, bldr, lexEntryTypeNode, true); // complex @@ -1421,8 +1425,12 @@ private static IFragment GenerateContentForCollection(object collectionField, Co else { Debug.WriteLine("Unable to group " + config.FieldDescription + " by LexRefType; generating sequentially"); + bool first = true; foreach (var item in lerCollection) - bldr.Append(GenerateCollectionItemContent(config, pubDecorator, item, collectionOwner, settings)); + { + bldr.Append(GenerateCollectionItemContent(config, pubDecorator, item, collectionOwner, settings, first)); + first = false; + } } } else if (config.FieldDescription.StartsWith("Subentries")) @@ -1435,8 +1443,12 @@ private static IFragment GenerateContentForCollection(object collectionField, Co } else { + bool first = true; foreach (var item in collection) - bldr.Append(GenerateCollectionItemContent(config, pubDecorator, item, collectionOwner, settings)); + { + bldr.Append(GenerateCollectionItemContent(config, pubDecorator, item, collectionOwner, settings, first)); + first = false; + } } } @@ -1528,16 +1540,24 @@ private static IFragment GenerateContentForEntryRefCollection(ConfigurableDictio if (typeNode.IsEnabled && typeNode.ReferencedOrDirectChildren != null && typeNode.ReferencedOrDirectChildren.Any(y => y.IsEnabled)) { // Display typeless refs + bool first = true; foreach (var entry in lerCollection.Where(item => !item.ComplexEntryTypesRS.Any() && !item.VariantEntryTypesRS.Any())) - bldr.Append(GenerateCollectionItemContent(config, pubDecorator, entry, collectionOwner, settings, typeNode)); + { + bldr.Append(GenerateCollectionItemContent(config, pubDecorator, entry, collectionOwner, settings, first, typeNode)); + first = false; + } // Display refs of each type GenerateContentForLexEntryRefsByType(config, lerCollection, collectionOwner, pubDecorator, settings, bldr, typeNode, isComplex); } else { Debug.WriteLine("Unable to group " + config.FieldDescription + " by LexRefType; generating sequentially"); + bool first = true; foreach (var item in lerCollection) - bldr.Append(GenerateCollectionItemContent(config, pubDecorator, item, collectionOwner, settings)); + { + bldr.Append(GenerateCollectionItemContent(config, pubDecorator, item, collectionOwner, settings, first)); + first = false; + } } return bldr; } @@ -1561,11 +1581,13 @@ private static void GenerateContentForLexEntryRefsByType(ConfigurableDictionaryN foreach (var typeGuid in lexEntryTypesFiltered) { var innerBldr = new StringBuilder(); + bool first = true; foreach (var lexEntRef in lerCollection) { if (isComplex ? lexEntRef.ComplexEntryTypesRS.Any(t => t.Guid == typeGuid) : lexEntRef.VariantEntryTypesRS.Any(t => t.Guid == typeGuid)) { - innerBldr.Append(GenerateCollectionItemContent(config, pubDecorator, lexEntRef, collectionOwner, settings, typeNode)); + innerBldr.Append(GenerateCollectionItemContent(config, pubDecorator, lexEntRef, collectionOwner, settings, first, typeNode)); + first = false; } } @@ -1576,7 +1598,7 @@ private static void GenerateContentForLexEntryRefsByType(ConfigurableDictionaryN var generateLexType = typeNode != null; var lexTypeContent = generateLexType ? GenerateCollectionItemContent(typeNode, pubDecorator, lexEntryType, - lexEntryType.Owner, settings) + lexEntryType.Owner, settings, first) : null; var className = generateLexType ? settings.StylesGenerator.AddStyles(typeNode).Trim('.') : null; var refsByType = settings.ContentGenerator.AddLexReferences(generateLexType, @@ -1600,11 +1622,13 @@ private static void GenerateContentForSubentries(ConfigurableDictionaryNode conf .Select(le => new Tuple(EntryRefForSubentry(le, collectionOwner), le)).ToList(); // Generate any Subentries with no ComplexFormType + bool first = true; for (var i = 0; i < subentries.Count; i++) { if (subentries[i].Item1 == null || !subentries[i].Item1.ComplexEntryTypesRS.Any()) { - bldr.Append(GenerateCollectionItemContent(config, pubDecorator, subentries[i].Item2, collectionOwner, settings)); + bldr.Append(GenerateCollectionItemContent(config, pubDecorator, subentries[i].Item2, collectionOwner, settings, first)); + first = false; subentries.RemoveAt(i--); } } @@ -1615,7 +1639,8 @@ private static void GenerateContentForSubentries(ConfigurableDictionaryNode conf { if (subentries[i].Item1.ComplexEntryTypesRS.Any(t => t.Guid == typeGuid)) { - bldr.Append(GenerateCollectionItemContent(config, pubDecorator, subentries[i].Item2, collectionOwner, settings)); + bldr.Append(GenerateCollectionItemContent(config, pubDecorator, subentries[i].Item2, collectionOwner, settings, first)); + first = false; subentries.RemoveAt(i--); } } @@ -1624,8 +1649,12 @@ private static void GenerateContentForSubentries(ConfigurableDictionaryNode conf else { Debug.WriteLine("Unable to group " + config.FieldDescription + " by LexRefType; generating sequentially"); + bool first = true; foreach (var item in collection) - bldr.Append(GenerateCollectionItemContent(config, pubDecorator, item, collectionOwner, settings)); + { + bldr.Append(GenerateCollectionItemContent(config, pubDecorator, item, collectionOwner, settings, first)); + first = false; + } } } @@ -1702,35 +1731,13 @@ private static IFragment GenerateContentForSenses(ConfigurableDictionaryNode con var isThisSenseNumbered = ShouldThisSenseBeNumbered(filteredSenseCollection[0], config, filteredSenseCollection); var bldr = settings.ContentGenerator.CreateFragment(); - // TODO: Can handle separate sense paragraph styling here; likely will make the most sense to be handled wherever we deal with before/after content for senses. - // TODO: If handled here (or elsewhere w/in LcmGenerator), remove sense paragraph handling from the CssGenerator, otherwise sense paragraphs will be separated by two lines in the xhtml export. - /*// Only need to check once whether DisplayEachSenseInAParagraph is true -- value will be the same for each item in the loop - bool newParagraphPerSense; - if (senseNode?.DisplayEachSenseInAParagraph == true) - newParagraphPerSense = true; - else - newParagraphPerSense = false; - - //If the first sense must be inline, we append the first sense without a preceding line break - int startSense = 0; - if (senseNode?.DisplayFirstSenseInline == true) - { - bldr.Append(GenerateSenseContent(config, publicationDecorator, filteredSenseCollection[startSense], isThisSenseNumbered, settings, isSameGrammaticalInfo, info)); - startSense++; - } - - for (int i = startSense; i < filteredSenseCollection.Count; i++)*/ - + bool first = true; foreach (var item in filteredSenseCollection) { info.SenseCounter++; - - // TODO: sense paragraphs - /*// If each sense belongs in a new paragraph, append a line break before the sense content. - if (newParagraphPerSense) - bldr.AppendBreak();*/ - - bldr.Append(GenerateSenseContent(config, publicationDecorator, item, isThisSenseNumbered, settings, isSameGrammaticalInfo, info)); + bldr.Append(GenerateSenseContent(config, publicationDecorator, item, isThisSenseNumbered, settings, + isSameGrammaticalInfo, info, first)); + first = false; } settings.StylesGenerator.AddStyles(config); return bldr; @@ -1876,7 +1883,7 @@ private static bool CheckIfAllGramInfoTheSame(ConfigurableDictionaryNode config, } private static IFragment GenerateSenseContent(ConfigurableDictionaryNode config, DictionaryPublicationDecorator publicationDecorator, - object item, bool isThisSenseNumbered, GeneratorSettings settings, bool isSameGrammaticalInfo, SenseInfo info) + object item, bool isThisSenseNumbered, GeneratorSettings settings, bool isSameGrammaticalInfo, SenseInfo info, bool first) { var senseNumberSpan = GenerateSenseNumberSpanIfNeeded(config, isThisSenseNumbered, ref info, settings); var bldr = settings.ContentGenerator.CreateFragment(); @@ -1893,8 +1900,7 @@ private static IFragment GenerateSenseContent(ConfigurableDictionaryNode config, if (bldr.Length() == 0) return bldr; - return settings.ContentGenerator.AddSenseData(senseNumberSpan, IsBlockProperty(config), ((ICmObject)item).Owner.Guid, - bldr, GetCollectionItemClassAttribute(config)); + return settings.ContentGenerator.AddSenseData(senseNumberSpan, ((ICmObject)item).Owner.Guid, config, bldr, first); } private static IFragment GeneratePictureContent(ConfigurableDictionaryNode config, DictionaryPublicationDecorator publicationDecorator, @@ -1945,7 +1951,7 @@ private static IFragment GeneratePictureContent(ConfigurableDictionaryNode confi } private static IFragment GenerateCollectionItemContent(ConfigurableDictionaryNode config, DictionaryPublicationDecorator publicationDecorator, - object item, object collectionOwner, GeneratorSettings settings, ConfigurableDictionaryNode factoredTypeField = null) + object item, object collectionOwner, GeneratorSettings settings, bool first, ConfigurableDictionaryNode factoredTypeField = null) { if (item is IMultiStringAccessor) return GenerateContentForStrings((IMultiStringAccessor)item, config, settings); @@ -1979,7 +1985,7 @@ private static IFragment GenerateCollectionItemContent(ConfigurableDictionaryNod if (bldr.Length() == 0) return bldr; var collectionContent = bldr; - return settings.ContentGenerator.AddCollectionItem(IsBlockProperty(config), GetCollectionItemClassAttribute(config), config, collectionContent); + return settings.ContentGenerator.AddCollectionItem(IsBlockProperty(config), GetCollectionItemClassAttribute(config), config, collectionContent, first); } private static void GenerateContentForLexRefCollection(ConfigurableDictionaryNode config, @@ -2087,6 +2093,8 @@ private static IFragment GenerateCrossReferenceChildren(ConfigurableDictionaryNo { return settings.ContentGenerator.CreateFragment(); } + + bool first = true; foreach (var child in config.ReferencedOrDirectChildren.Where(c => c.IsEnabled)) { switch (child.FieldDescription) @@ -2097,7 +2105,8 @@ private static IFragment GenerateCrossReferenceChildren(ConfigurableDictionaryNo { var referenceItem = referenceListItem.Item2; var targetItem = referenceListItem.Item1; - contentBldr.Append(GenerateCollectionItemContent(child, publicationDecorator, targetItem, referenceItem, settings)); + contentBldr.Append(GenerateCollectionItemContent(child, publicationDecorator, targetItem, referenceItem, settings, first)); + first = false; } if (contentBldr.Length > 0) { @@ -2252,7 +2261,7 @@ private static IFragment GenerateContentForICmObject(ICmObject propertyValue, Co } /// Write the class element in the span for an individual item in the collection - private static string GetCollectionItemClassAttribute(ConfigurableDictionaryNode config) + internal static string GetCollectionItemClassAttribute(ConfigurableDictionaryNode config) { var classAtt = CssGenerator.GetClassAttributeForCollectionItem(config); if (config.ReferencedNode != null) @@ -2487,8 +2496,7 @@ private static IFragment GenerateContentForValue(object field, object propertyVa if (propertyValue is int) { var cssClassName = settings.StylesGenerator.AddStyles(config).Trim('.'); ; - return settings.ContentGenerator.AddProperty(cssClassName, false, - propertyValue.ToString()); + return settings.ContentGenerator.AddProperty(cssClassName, false, propertyValue.ToString()); } if (propertyValue is DateTime) { diff --git a/Src/xWorks/ILcmContentGenerator.cs b/Src/xWorks/ILcmContentGenerator.cs index 0b74607f3f..0a861ab44b 100644 --- a/Src/xWorks/ILcmContentGenerator.cs +++ b/Src/xWorks/ILcmContentGenerator.cs @@ -22,8 +22,8 @@ public interface ILcmContentGenerator IFragment GenerateGramInfoBeforeSensesContent(IFragment content, ConfigurableDictionaryNode config); IFragment GenerateGroupingNode(object field, string className, ConfigurableDictionaryNode config, DictionaryPublicationDecorator publicationDecorator, ConfiguredLcmGenerator.GeneratorSettings settings, Func childContentGenerator); - IFragment AddSenseData(IFragment senseNumberSpan, bool isBlockProperty, Guid ownerGuid, IFragment senseContent, string className); - IFragment AddCollectionItem(bool isBlock, string collectionItemClass, ConfigurableDictionaryNode config,IFragment content); + IFragment AddSenseData(IFragment senseNumberSpan, Guid ownerGuid, ConfigurableDictionaryNode config, IFragment senseContent, bool first); + IFragment AddCollectionItem(bool isBlock, string collectionItemClass, ConfigurableDictionaryNode config, IFragment content, bool first); IFragment AddProperty(string className, bool isBlockProperty, string content); IFragment CreateFragment(); IFragment CreateFragment(string str); diff --git a/Src/xWorks/LcmJsonGenerator.cs b/Src/xWorks/LcmJsonGenerator.cs index 84a30f1e74..b55c1294f9 100644 --- a/Src/xWorks/LcmJsonGenerator.cs +++ b/Src/xWorks/LcmJsonGenerator.cs @@ -117,7 +117,7 @@ public IFragment GenerateGroupingNode(object field, string className, Configurab return new StringFragment(); } - public IFragment AddCollectionItem(bool isBlock, string className, ConfigurableDictionaryNode config,IFragment content) + public IFragment AddCollectionItem(bool isBlock, string className, ConfigurableDictionaryNode config,IFragment content, bool first) { var fragment = new StringFragment(); fragment.StrBuilder.Append(content.IsNullOrEmpty() ? string.Empty : $"{{{content}}},"); @@ -431,8 +431,7 @@ public IFragment GenerateErrorContent(StringBuilder badStrBuilder) return new StringFragment($"\\u+0FFF\\u+0FFF\\u+0FFF{badStrBuilder}"); } - public IFragment AddSenseData(IFragment senseNumberSpan, bool isBlock, Guid ownerGuid, - IFragment senseContent, string className) + public IFragment AddSenseData(IFragment senseNumberSpan, Guid ownerGuid, ConfigurableDictionaryNode config, IFragment senseContent, bool first) { var bldr = new StringBuilder(); var fragment = new StringFragment(bldr); diff --git a/Src/xWorks/LcmWordGenerator.cs b/Src/xWorks/LcmWordGenerator.cs index cb378779d8..989e6168c0 100644 --- a/Src/xWorks/LcmWordGenerator.cs +++ b/Src/xWorks/LcmWordGenerator.cs @@ -499,6 +499,7 @@ public void Append(string text) { WP.Run lastRun = GetLastRun(); WP.Text newText = new WP.Text(text); + newText.Space = SpaceProcessingModeValues.Preserve; lastRun.Append(newText); } @@ -657,6 +658,31 @@ private IFragment WriteProcessedElementContent(IFragment elementContent, Configu { // Use the style name and type of the config node or its parent to link a style to the elementContent fragment where the processed contents are written. DocFragment.LinkStyleOrInheritParentStyle(elementContent, config); + + bool displayEachInAParagraph = config != null && + config.DictionaryNodeOptions is IParaOption && + ((IParaOption)(config.DictionaryNodeOptions)).DisplayEachInAParagraph; + + // Add Before text, if it is not going to be displayed in it's own paragraph. + if (!displayEachInAParagraph && !string.IsNullOrEmpty(config.Before)) + { + WP.Text txt = new WP.Text(config.Before); + txt.Space = SpaceProcessingModeValues.Preserve; + var beforeRun = new WP.Run(txt); + ((DocFragment)elementContent).DocBody.PrependChild(beforeRun); + } + + // Add After text, if it is not going to be displayed in it's own paragraph. + if (!displayEachInAParagraph && !string.IsNullOrEmpty(config.After)) + { + WP.Text txt = new WP.Text(config.After); + txt.Space = SpaceProcessingModeValues.Preserve; + var afterRun = new WP.Run(txt); + ((DocFragment)elementContent).DocBody.Append(afterRun); + // To be consistent with the xhtml output, only the after text uses the same style. + DocFragment.LinkStyleOrInheritParentStyle(elementContent, config); + } + return elementContent; } public IFragment GenerateGramInfoBeforeSensesContent(IFragment content, ConfigurableDictionaryNode config) @@ -672,18 +698,53 @@ public IFragment GenerateGroupingNode(object field, string className, Configurab //return docfrag; return null; } - public IFragment AddSenseData(IFragment senseNumberSpan, bool isBlockProperty, Guid ownerGuid, IFragment senseContent, string className) + public IFragment AddSenseData(IFragment senseNumberSpan, Guid ownerGuid, ConfigurableDictionaryNode config, IFragment senseContent, bool first) { + var senseData = new DocFragment(); + var senseNode = (DictionaryNodeSenseOptions)config?.DictionaryNodeOptions; + bool eachInAParagraph = false; + bool firstSenseInline = false; + string afterNumber = null; + string beforeNumber = null; + if (senseNode != null) + { + eachInAParagraph = senseNode.DisplayEachSenseInAParagraph; + firstSenseInline = senseNode.DisplayFirstSenseInline; + afterNumber = senseNode.AfterNumber; + beforeNumber = senseNode.BeforeNumber; + } + + // We want a break before the first sense item, between items, and after the last item. + // So, only add a break before the content if it is the first sense and it's not displayed in-line. + if (eachInAParagraph && first && !firstSenseInline) + { + senseData.AppendBreak(); + } + // Add sense numbers if needed if (!senseNumberSpan.IsNullOrEmpty()) { - senseNumberSpan.Append(senseContent); - return senseNumberSpan; + if (!string.IsNullOrEmpty(beforeNumber)) + { + senseData.Append(beforeNumber); + } + senseData.Append(senseNumberSpan); + if (!string.IsNullOrEmpty(afterNumber)) + { + senseData.Append(afterNumber); + } + } + + senseData.Append(senseContent); + + if (eachInAParagraph) + { + senseData.AppendBreak(); } - return senseContent; + return senseData; } - public IFragment AddCollectionItem(bool isBlock, string collectionItemClass, ConfigurableDictionaryNode config, IFragment content) + public IFragment AddCollectionItem(bool isBlock, string collectionItemClass, ConfigurableDictionaryNode config, IFragment content, bool first) { if (!string.IsNullOrEmpty(config.Style)) { @@ -694,7 +755,41 @@ public IFragment AddCollectionItem(bool isBlock, string collectionItemClass, Con ((DocFragment)content).AddStyleLink(config.Style, ConfigurableDictionaryNode.StyleTypes.Character); } - return content; + var collData = CreateFragment(); + bool eachInAParagraph = false; + if (config != null && + config.DictionaryNodeOptions is IParaOption && + ((IParaOption)(config.DictionaryNodeOptions)).DisplayEachInAParagraph) + { + eachInAParagraph = true; + + // We want a break before the first collection item, between items, and after the last item. + // So, only add a break before the content if it is the first. + if (first) + { + collData.AppendBreak(); + } + } + + // Add Between text, if it is not going to be displayed in it's own paragraph + // and it is not the first item in the collection. + if (!first && + config != null && + config.DictionaryNodeOptions is IParaOption && + !eachInAParagraph && + !string.IsNullOrEmpty(config.Between)) + { + ((DocFragment)collData).Append(config.Between); + } + + collData.Append(content); + + if (eachInAParagraph) + { + collData.AppendBreak(); + } + + return collData; } public IFragment AddProperty(string className, bool isBlockProperty, string content) { @@ -987,12 +1082,6 @@ public void AddEntryData(IFragmentWriter writer, List