diff --git a/src/SIL.LCModel/DomainImpl/ScrTxtPara.cs b/src/SIL.LCModel/DomainImpl/ScrTxtPara.cs
index ac109252..2030abaa 100644
--- a/src/SIL.LCModel/DomainImpl/ScrTxtPara.cs
+++ b/src/SIL.LCModel/DomainImpl/ScrTxtPara.cs
@@ -2136,6 +2136,13 @@ public override ITsString Reference(ISegment seg, int ich)
}
return Cache.MakeUserTss("unknown"); // should never happen, I think?
}
+
+ ///
+ public override ITsString ReferenceForSorting(ISegment seg, int ich)
+ {
+ throw new NotImplementedException();
+ }
+
/// ------------------------------------------------------------------------------------
///
/// Gets the footnote sequence.
diff --git a/src/SIL.LCModel/DomainImpl/StTxtPara.cs b/src/SIL.LCModel/DomainImpl/StTxtPara.cs
index 63e944ea..fbca9744 100644
--- a/src/SIL.LCModel/DomainImpl/StTxtPara.cs
+++ b/src/SIL.LCModel/DomainImpl/StTxtPara.cs
@@ -117,6 +117,53 @@ public virtual ITsString Reference(ISegment seg, int ich)
return bldr.GetString();
}
+ ///
+ public virtual ITsString ReferenceForSorting(ISegment seg, int ich)
+ {
+ if (!(Owner is IStText stText))
+ {
+ return TsStringUtils.EmptyString(Cache.DefaultUserWs);
+ }
+
+ ITsString tssName = null;
+ // TODO: Abbr
+ //var fUsingAbbr = false;
+ //if (stText.Owner is IText text)
+ //{
+ // tssName = text.Abbreviation.BestVernacularAnalysisAlternative;
+ // if (!TsStringUtils.IsNullOrEmpty(tssName))
+ // {
+ // fUsingAbbr = true;
+ // }
+ //}
+ tssName = stText.Title.BestVernacularAnalysisAlternative;
+ var bldr = tssName.GetBldr();
+ // TODO: Props?
+ var props = bldr.get_Properties(0);
+ // TODO: *** check?
+
+ bldr.Append(" ", props);
+
+ // Insert paragraph number.
+ int iPara = stText.ParagraphsOS.IndexOf(this) + 1;
+ bldr.Replace(bldr.Length, bldr.Length, iPara.ToString(), props);
+
+ // And a period...
+ bldr.Replace(bldr.Length, bldr.Length, ".", props);
+
+ // And now the segment number
+ int iseg = SegmentsOS.IndexOf(seg) + 1;
+ bldr.Replace(bldr.Length, bldr.Length, iseg.ToString(), props);
+ return bldr.GetString();
+ }
+
+ /// Pads the given int with zeroes to the max length of an int
+ protected internal static string ZeroPadForStringComparison(int i)
+ {
+ // because int.MaxValue.ToString().Length is 10
+ return i.ToString("D10");
+ }
+
/// ------------------------------------------------------------------------------------
///
/// Finds the ORC of the specified picture and deletes it from the paragraph and any
diff --git a/src/SIL.LCModel/InterfaceAdditions.cs b/src/SIL.LCModel/InterfaceAdditions.cs
index cdedf74e..49bed223 100644
--- a/src/SIL.LCModel/InterfaceAdditions.cs
+++ b/src/SIL.LCModel/InterfaceAdditions.cs
@@ -3136,15 +3136,25 @@ IStTxtPara PreviousParagraph
List GetChartCellRefs();
/// ------------------------------------------------------------------------------------
- ///
- /// Return a Reference (e.g., Scripture reference, or text abbreviation/para #/sentence#) for the specified character
- /// position (in the whole paragraph), which is assumed to belong to the specified segment.
- /// (For now, ich is not actually used, but it may become important if we decide not to split segements for
- /// verse numbers.)
- ///
+ ///
+ /// Return a Reference (e.g., Scripture reference, or text abbreviation+para #+sentence #) for the specified character
+ /// position (in the whole paragraph), which is assumed to belong to the specified segment.
+ /// (For now, ich is not actually used, but it may become important if we decide not to split segments for
+ /// verse numbers.)
+ ///
/// ------------------------------------------------------------------------------------
ITsString Reference(ISegment seg, int ich);
+ /// ------------------------------------------------------------------------------------
+ ///
+ /// Return a Reference (e.g., Scripture reference, or text abbreviation+para #+sentence #) for the specified character
+ /// position (in the whole paragraph), which is assumed to belong to the specified segment.
+ /// To allow greater accuracy and precision in sorting, numbers are zero-padded to the length of and ich
+ /// is included at the end.
+ ///
+ /// ------------------------------------------------------------------------------------
+ ITsString ReferenceForSorting(ISegment seg, int ich);
+
/// ------------------------------------------------------------------------------------
///
/// Splits the paragraph at the specified character index.
diff --git a/tests/SIL.LCModel.Tests/DomainImpl/ScrTxtParaTests.cs b/tests/SIL.LCModel.Tests/DomainImpl/ScrTxtParaTests.cs
index d75393fc..d25e5678 100644
--- a/tests/SIL.LCModel.Tests/DomainImpl/ScrTxtParaTests.cs
+++ b/tests/SIL.LCModel.Tests/DomainImpl/ScrTxtParaTests.cs
@@ -567,6 +567,38 @@ public void Reference()
// segments in the previous section.
Assert.That(para3.Reference(v2seg4, v2seg4.BeginOffset + 1).Text, Is.EqualTo("MAT 1:2"));
}
+
+ ///
+ /// Test the ReferenceForSorting method for Scripture paragraphs.
+ ///
+ [Test]
+ public void ReferenceForSorting()
+ {
+ AddDataToMatthew();
+ var para1 = (IStTxtPara) m_book.SectionsOS[1].ContentOA.ParagraphsOS[0]; // Actually ScrTxtPara
+ var seg = para1.SegmentsOS[1]; // first content ref, after the chapter and verse number stuff.
+ Assert.That(para1.ReferenceForSorting(seg, seg.BeginOffset + 1).Text, Is.EqualTo("41_MAT 0000000001:0000000001"));
+ AddRunToMockedPara(para1, "Verse two second sentence.", null);
+ var v2seg1 = para1.SegmentsOS[3]; // first segment of two-sentence verse
+ Assert.That(para1.ReferenceForSorting(v2seg1, v2seg1.BeginOffset + 1).Text, Is.EqualTo("41_MAT 0000000001:0000000002a"));
+ var v2seg2 = para1.SegmentsOS[4]; // first segment of two-sentence verse
+ Assert.That(para1.ReferenceForSorting(v2seg2, v2seg2.BeginOffset + 1).Text, Is.EqualTo("41_MAT 0000000001:0000000002b"));
+ IStTxtPara para2 = AddParaToMockedSectionContent((IScrSection)para1.Owner.Owner, ScrStyleNames.NormalParagraph);
+ AddRunToMockedPara(para2, "Verse 2 seg 3", null);
+ var v2seg3 = para2.SegmentsOS[0]; // third segment of three-sentence verse split over two paragraphs.
+ Assert.That(para2.ReferenceForSorting(v2seg3, v2seg3.BeginOffset + 1).Text, Is.EqualTo("41_MAT 0000000001:0000000002c"));
+ var newSection = AddSectionToMockedBook(m_book);
+ IStTxtPara para3 = AddParaToMockedSectionContent(newSection, ScrStyleNames.NormalParagraph);
+ AddRunToMockedPara(para3, "Verse 2 seg 4", null);
+ var v2seg4 = para3.SegmentsOS[0]; // fourth segment of four-sentence verse split over two sections(!).
+ // JohnT: arguably this should give 41_MAT 0000000001:0000000002d. The current implementation does not detect the
+ // segments in the previous section.
+ Assert.That(para3.ReferenceForSorting(v2seg4, v2seg4.BeginOffset + 1).Text, Is.EqualTo("41_MAT 0000000001:0000000002"));
+
+ var scrBook1Samuel = CreateBookData(9, "1 Samuel");
+ var scrBookSusanna = CreateBookData(76, "Susanna");
+ // TODO (Hasso) 2022.03: Enoch or some other >100 book
+ }
#endregion
#region Moving paragraphs between books tests
diff --git a/tests/SIL.LCModel.Tests/DomainImpl/StTxtParaTests.cs b/tests/SIL.LCModel.Tests/DomainImpl/StTxtParaTests.cs
index ca62f44e..62b5941c 100644
--- a/tests/SIL.LCModel.Tests/DomainImpl/StTxtParaTests.cs
+++ b/tests/SIL.LCModel.Tests/DomainImpl/StTxtParaTests.cs
@@ -35,6 +35,33 @@ protected override void CreateTestData()
}
#endregion
+ #region ReferenceForSorting method tests
+ [Test]
+ public void ReferenceForSorting()
+ {
+ var para1 = AddParaToMockedText(m_stText, null);
+ AddRunToMockedPara(para1, "This text is indexed. It is also segmented.", null);
+
+ var para2 = AddParaToMockedText(m_stText, null);
+ AddRunToMockedPara(para2, "This is the second paragraph. It is runny. It has three sentences.", null);
+
+ // SUT
+ var result = para1.ReferenceForSorting(para1.SegmentsOS[0], 10);
+ Assert.That(result, Is.EqualTo("My Interlinear Text 0000000001.0000000001 0000000010"));
+ result = para1.ReferenceForSorting(para1.SegmentsOS[1], 25);
+ Assert.That(result, Is.EqualTo("My Interlinear Text 0000000001.0000000002 0000000025"));
+ result = para1.ReferenceForSorting(para2.SegmentsOS[0], 5);
+ Assert.That(result, Is.EqualTo("My Interlinear Text 0000000002.0000000001 0000000005"));
+ }
+
+ [TestCase(0, ExpectedResult = "0000000000")]
+ [TestCase(1, ExpectedResult = "0000000001")]
+ [TestCase(12, ExpectedResult = "0000000012")]
+ [TestCase(512, ExpectedResult = "0000000512")]
+ [TestCase(int.MaxValue, ExpectedResult = "2147483647")]
+ public string ZeroPadForStringComparison(int i) => StTxtPara.ZeroPadForStringComparison(i);
+ #endregion ReferenceForSorting method tests
+
#region ReplaceTextRange method tests
///--------------------------------------------------------------------------------------
///