public override void Initialize() { CheckDisposed(); base.Initialize(); //m_inMemoryCache.InitializeAnnotationDefs(); InstallVirtuals(@"Language Explorer\Configuration\Words\AreaConfiguration.xml", new string[] { "SIL.FieldWorks.IText.ParagraphSegmentsVirtualHandler", "SIL.FieldWorks.IText.OccurrencesInTextsVirtualHandler" }); m_wsVern = Cache.DefaultVernWs; m_wsTrans = Cache.DefaultAnalWs; m_text = new Text(); Cache.LangProject.TextsOC.Add(m_text); m_para = new StTxtPara(); StText text = new StText(); m_text.ContentsOA = text; text.ParagraphsOS.Append(m_para); m_trans = new CmTranslation(); m_para.TranslationsOC.Add(m_trans); kflidFT = StTxtPara.SegmentFreeTranslationFlid(Cache); kflidSegments = StTxtPara.SegmentsFlid(Cache); m_btPoss = Cache.LangProject.TranslationTagsOA.LookupPossibilityByGuid( LangProject.kguidTranBackTranslation); m_trans.TypeRA = m_btPoss; m_fWasUseScriptDigits = Cache.LangProject.TranslatedScriptureOA.UseScriptDigits; Cache.LangProject.TranslatedScriptureOA.UseScriptDigits = false; // do we need to set status? }
/// ------------------------------------------------------------------------------------ /// <summary> /// Use this method to remove the contents (all paragraphs) of an StText. /// </summary> /// <param name="text">StText from which the contents is removed</param> /// ------------------------------------------------------------------------------------ public static void ClearContents(StText text) { for (int lastPara = text.ParagraphsOS.Count - 1; lastPara >= 0; lastPara--) { text.ParagraphsOS.RemoveAt(lastPara); } }
public void Dispose() { m_stText = null; m_tsf = null; m_text = null; m_firstPara = null; m_cache = null; }
public DiscourseTestHelper(FdoCache cache) { m_cache = cache; m_tsf = TsStrFactoryClass.Create(); m_text = (Text)Cache.LangProject.TextsOC.Add(new Text()); m_stText = new StText(); m_text.ContentsOA = m_stText; m_firstPara = MakeParagraph(); }
/// ------------------------------------------------------------------------------------ /// <summary> /// Append a new <see cref="StTxtPara"/> to the given <see cref="StText"/>. /// Set it with data accumulated in this builder. /// </summary> /// <param name="hvoOwner">HVO of the <see cref="StText"/> that is to own the new /// paragraph</param> /// <returns>A new StTextPara whose contents are built up from the prior calls /// to <see cref="AppendRun"/> and whose style is set based on the current value of /// <see cref="ParaStylePropsProxy"/>.</returns> /// ------------------------------------------------------------------------------------ public StTxtPara CreateParagraph(int hvoOwner) { CheckDisposed(); // insert a new para in the owner's collection StTxtPara para = new StTxtPara(); StText text = new StText(m_cache, hvoOwner); text.ParagraphsOS.Append(para); SetStTxtParaPropertiesAndClearBuilder(para, 0); return(para); }
public override void Initialize() { CheckDisposed(); base.Initialize(); m_inMemoryCache.InitializeAnnotationDefs(); InstallVirtuals(@"Language Explorer\Configuration\Words\AreaConfiguration.xml", new string[] { "SIL.FieldWorks.IText.ParagraphSegmentsVirtualHandler", "SIL.FieldWorks.IText.OccurrencesInTextsVirtualHandler" }); m_text = new Text(); Cache.LangProject.TextsOC.Add(m_text); m_para = new StTxtPara(); StText text = new StText(); m_text.ContentsOA = text; text.ParagraphsOS.Append(m_para); }
/// ------------------------------------------------------------------------------------ /// <summary> /// Create a new <see cref="StTxtPara"/>, owned by the given <see cref="StText"/>. /// Set it with data accumulated in this builder. /// </summary> /// <param name="hvoOwner">HVO of the <see cref="StText"/> that is to own the new /// paragraph</param> /// <param name="iPos">0-based index of the position in the sequence of paragraphs where the /// new paragraph is to be inserted. If a paragraph is already in this position, the new /// paragraph will be inserted before the existing paragraph.</param> /// <returns>A new StTextPara whose contents are built up from the prior calls /// to <see cref="AppendRun"/> and whose style is set based on the current value of /// <see cref="ParaStylePropsProxy"/>.</returns> /// ------------------------------------------------------------------------------------ public StTxtPara CreateParagraph(int hvoOwner, int iPos) { CheckDisposed(); // ENHANCE: Could maybe squeeze a little more performance out of this by calling the // stored procedure that used to be called by TeImporter to create paragraphs. // insert a new para in the owner's collection StTxtPara para = new StTxtPara(); StText text = new StText(m_cache, hvoOwner); text.ParagraphsOS.InsertAt(para, iPos); SetStTxtParaPropertiesAndClearBuilder(para, iPos); return(para); }
protected void CreateTestData() { using (new UndoRedoTaskHelper(Cache, "Undo LogicTest - CreateTestData", "Redo LogicTest - CreateTestData")) { m_helper = new DiscourseTestHelper(Cache); m_firstPara = m_helper.FirstPara; m_stText = m_firstPara.Owner as StText; m_firstParaWfics = m_helper.MakeAnnotations(m_firstPara); m_logic = new TestCCLogic(Cache, m_chart, m_stText.Hvo); m_helper.Logic = m_logic; m_logic.Ribbon = m_mockRibbon = new MockRibbon(Cache, m_stText.Hvo); m_template = m_helper.MakeTemplate(out m_allColumns); // Note: do this AFTER creating the template, which may also create the DiscourseData object. m_chart = new DsConstChart(); Cache.LangProject.DiscourseDataOA.ChartsOC.Add(m_chart); m_chart.TemplateRA = m_template; m_logic.Chart = m_chart; m_helper.Chart = m_chart; } }
public override void Initialize() { CheckDisposed(); base.Initialize(); InstallVirtuals(@"Language Explorer\Configuration\Words\AreaConfiguration.xml", new string[] { "SIL.FieldWorks.IText.ParagraphSegmentsVirtualHandler", "SIL.FieldWorks.IText.OccurrencesInTextsVirtualHandler" }); m_wsVern = Cache.DefaultVernWs; m_wsTrans = Cache.DefaultAnalWs; m_book = new ScrBook(); Cache.LangProject.TranslatedScriptureOA.ScriptureBooksOS.Append(m_book); m_section = new ScrSection(); m_book.SectionsOS.Append(m_section); m_para = new StTxtPara(); m_text = new StText(); m_section.ContentOA = m_text; m_text.ParagraphsOS.Append(m_para); m_hvoSegDefn = CmAnnotationDefn.TextSegment(Cache).Hvo; m_hvoFtDefn = Cache.GetIdFromGuid(new Guid(LangProject.kguidAnnFreeTranslation)); kflidFT = StTxtPara.SegmentFreeTranslationFlid(Cache); kflidSegments = StTxtPara.SegmentsFlid(Cache); }
/// ------------------------------------------------------------------------------------ /// <summary> /// Create minimal test data required for every test. /// </summary> /// ------------------------------------------------------------------------------------ protected override void CreateTestData() { base.CreateTestData(); m_inMemoryCache.InitializeWritingSystemEncodings(); m_inMemoryCache.InitializeLexDb(); m_helper = new DiscourseTestHelper(Cache); m_firstPara = m_helper.FirstPara; m_stText = m_firstPara.Owner as StText; //// We want a real action handler so we can test Undo/Redo. //m_originalHandler = m_inMemoryCache.Cache.ActionHandlerAccessor; //((NewFdoCache)(m_inMemoryCache.Cache)).ActionHandler = ActionHandlerClass.Create(); //// Make the key annotationdefns. ICmPossibilityList defns = Cache.LangProject.AnnotationDefsOA; if (defns == null) { defns = new CmPossibilityList(); Cache.LangProject.AnnotationDefsOA = defns; } MakeAnnDefn(defns, LangProject.kguidAnnWordformInContext); MakeAnnDefn(defns, LangProject.kguidConstituentChartRow); MakeAnnDefn(defns, LangProject.kguidConstituentChartAnnotation); }
/// ------------------------------------------------------------------------------------ /// <summary> /// Use this method to determine if the selection within StText has an edge adjacent to /// a different context. I.e.: the selection top is within the first paragraph /// of a context OR the selection bottom is within the last paragraph of a context. /// </summary> /// <param name="top">Top of the selection.</param> /// <param name="bottom">Bottom of the selection.</param> /// <returns>True if the selection is within an StText /// AND EITHER the selection top is within the first paragraph of the context /// OR the selection bottom is within the last paragraph of the context. /// </returns> /// ------------------------------------------------------------------------------------ private bool SelectionNearContextBoundary(SelLevInfo[] top, SelLevInfo[] bottom) { int headingFlid = (int)ScrSection.ScrSectionTags.kflidHeading; int contentFlid = (int)ScrSection.ScrSectionTags.kflidContent; if (top[1].ihvo != bottom[1].ihvo) return false; // Applies only within an StText StText curText = new StText(m_cache, top[1].hvo); return // Top within first paragraph of heading of first section ((top[1].tag == headingFlid & true) || // Bottom within last paragraph of context (bottom[1].tag == contentFlid & true)); }
/// ------------------------------------------------------------------------------------ /// <summary> /// Use this method to determine if the selection within StText has an edge adjacent to /// the section's other StText. I.e.: the selection bottom is within the last paragraph /// in the heading of the section OR the selection top is within the first paragraph /// in the content of the section. /// </summary> /// <param name="top">Top of the selection.</param> /// <param name="bottom">Bottom of the selection.</param> /// <returns>True if the selection is within an StText /// AND EITHER the selection bottom is within the last paragraph of the heading /// OR the selection top is within the first paragraph of the content. /// </returns> /// ------------------------------------------------------------------------------------ private bool SelectionNearInternalBoundary(SelLevInfo[] top, SelLevInfo[] bottom) { StText text = new StText(m_cache, top[1].hvo); int headingFlid = (int)ScrSection.ScrSectionTags.kflidHeading; int contentFlid = (int)ScrSection.ScrSectionTags.kflidContent; return // Applies only within an StText; (top[1].ihvo == bottom[1].ihvo) && // Top within first paragraph of content ((top[1].tag == contentFlid && top[0].ihvo == 0) || // Bottom within last paragraph of heading (bottom[1].tag == headingFlid && text.ParagraphsOS.Count == bottom[0].ihvo + 1)); }
/// ------------------------------------------------------------------------------------ /// <summary> /// Determines whether the selection represented by the given top and bottom selection /// level information covers the entire range of paragraphs in the selcted text /// </summary> /// <param name="top">The selection levels corresponding to the top of the selection</param> /// <param name="bottom">The selection levels corresponding to the bottom of the /// selection</param> /// <param name="text">The text.</param> /// <returns> /// <c>true</c> if all paragraphs of the text are included in the selection; /// <c>false</c> otherwise /// </returns> /// <exception cref="Exception">If the selection is invalid, spans multiple StTexts, or /// covers more paragraphs than actually exist in the StText</exception> /// ------------------------------------------------------------------------------------ private bool AreAllParagraphsSelected(SelLevInfo[] top, SelLevInfo[] bottom, IStText text) { if (text.Hvo != top[1].hvo || top[1].hvo != bottom[1].hvo || text.ParagraphsOS.Count <= bottom[0].ihvo) { throw new Exception("Selection is invalid! Top and bottom of selection do not " + "correspond to the paragraphs of the same text. textHvo = " + text.Hvo + "; top[1].hvo = " + top[1].hvo + "; bottom[1].hvo = " + bottom[1].hvo + "; text.ParagraphsOS.Count = " + text.ParagraphsOS.Count + "; bottom[0].ihvo = " + bottom[0].ihvo + ". Please save a backup copy of your database so that developers can attempt to find the cause of this problem (TE-5717)."); } if (top[0].ihvo != 0) return false; // verify that number of selected paragraphs equals paragraphs in StText StText topText = new StText(m_cache, top[1].hvo); return topText.ParagraphsOS.Count == bottom[0].ihvo + 1; }
public void ArchiveDraft_TestFootnoteOrder() { CheckDisposed(); int draftCount = m_scr.ArchivedDraftsOC.Count; ScrBook james = (ScrBook)m_scr.ScriptureBooksOS[1]; int hvoBookRef = m_fdoCache.ScriptureReferenceSystem.BooksOS[58].Hvo; // Add a footnote to the first content para2 of the second section. FdoOwningSequence<IStPara> contentParas = james.SectionsOS[1].ContentOA.ParagraphsOS; StTxtPara para2 = (StTxtPara)contentParas[0]; StFootnote footnoteOrig3 = InsertTestFootnote(james, para2, 0, 0); StTxtPara para1 = new StTxtPara(); contentParas.InsertAt(para1, 0); StFootnote footnoteOrig2 = InsertTestFootnote(james, para1, 0, 0); StText titleText = new StText(); StTxtPara title = new StTxtPara(); james.TitleOA = titleText; titleText.ParagraphsOS.Append(title); StFootnote footnoteOrig1 = InsertTestFootnote(james, title, 0, 0); // archive draft IScrDraft draft = m_scr.CreateSavedVersion("FootnoteOrder james", new int[] { james.Hvo }); Assert.AreEqual(draftCount + 1, m_scr.ArchivedDraftsOC.Count); Assert.AreEqual("FootnoteOrder james", draft.Description); Assert.AreEqual(1, draft.BooksOS.Count); IScrBook revision = draft.BooksOS.FirstItem; Assert.IsFalse(james.Hvo == revision.Hvo); Assert.AreEqual(james.SectionsOS.Count, revision.SectionsOS.Count); IScrSection revSection1 = revision.SectionsOS[0]; IScrSection revSection2 = revision.SectionsOS[1]; Assert.IsTrue(james.SectionsOS[0].Hvo != revSection1.Hvo); Assert.IsTrue(james.SectionsOS[0].Hvo != revSection2.Hvo); Assert.IsTrue(james.SectionsOS[1].Hvo != revSection1.Hvo); Assert.IsTrue(james.SectionsOS[1].Hvo != revSection2.Hvo); FdoOwningSequence<IStPara> s2Paras = revSection2.ContentOA.ParagraphsOS; Assert.AreEqual(james.SectionsOS[1].ContentOA.ParagraphsOS.Count, s2Paras.Count); StTxtPara titleRev = (StTxtPara)revision.TitleOA.ParagraphsOS[0]; StTxtPara paraRev1 = (StTxtPara)s2Paras[0]; StTxtPara paraRev2 = (StTxtPara)s2Paras[1]; Assert.IsFalse(title.Hvo == titleRev.Hvo); Assert.IsFalse(para1.Hvo == paraRev1.Hvo); Assert.IsFalse(para2.Hvo == paraRev2.Hvo); Assert.AreEqual(hvoBookRef, revision.BookIdRAHvo); // Check the footnote Assert.AreEqual(james.FootnotesOS.Count, revision.FootnotesOS.Count); Assert.AreEqual(footnoteOrig1.Hvo, james.FootnotesOS[0].Hvo); Assert.AreEqual(footnoteOrig2.Hvo, james.FootnotesOS[1].Hvo); Assert.AreEqual(footnoteOrig3.Hvo, james.FootnotesOS[2].Hvo); IStFootnote footnoteRev1 = revision.FootnotesOS[0]; IStFootnote footnoteRev2 = revision.FootnotesOS[1]; IStFootnote footnoteRev3 = revision.FootnotesOS[2]; Assert.IsTrue(footnoteRev1.Hvo != footnoteOrig1.Hvo); Assert.IsTrue(footnoteRev2.Hvo != footnoteOrig2.Hvo); Assert.IsTrue(footnoteRev3.Hvo != footnoteOrig3.Hvo); VerifyFootnote(footnoteRev1, titleRev, 0); VerifyFootnote(footnoteRev2, paraRev1, 0); VerifyFootnote(footnoteRev3, paraRev2, 0); }
/// ------------------------------------------------------------------------------------ /// <summary> /// Finalizes the title. If is empty, a single blank paragraph is written for /// whatever is missing. /// </summary> /// ------------------------------------------------------------------------------------ protected void FinalizePrevTitle() { if (m_cache == null || m_hvoTitle == 0) return; StText title = new StText(m_cache, m_hvoTitle); // First, check if there is content. If not, add a blank paragraph. if (title.ParagraphsOS.Count == 0) { StTxtParaBldr titleParaBldr = new StTxtParaBldr(m_cache); titleParaBldr.ParaStylePropsProxy = m_BookTitleParaProxy; titleParaBldr.CreateParagraph(m_hvoTitle); } m_fInBookTitle = false; }
/// ------------------------------------------------------------------------------------ /// <summary> /// Append a new <see cref="StTxtPara"/> to the given <see cref="StText"/>. /// Set it with data accumulated in this builder. /// </summary> /// <param name="hvoOwner">HVO of the <see cref="StText"/> that is to own the new /// paragraph</param> /// <returns>A new StTextPara whose contents are built up from the prior calls /// to <see cref="AppendRun"/> and whose style is set based on the current value of /// <see cref="ParaStylePropsProxy"/>.</returns> /// ------------------------------------------------------------------------------------ public StTxtPara CreateParagraph(int hvoOwner) { CheckDisposed(); // insert a new para in the owner's collection StTxtPara para = new StTxtPara(); StText text = new StText(m_cache, hvoOwner); text.ParagraphsOS.Append(para); SetStTxtParaPropertiesAndClearBuilder(para, 0); return para; }
/// ------------------------------------------------------------------------------------ /// <summary> /// Bring the paragraph difference into view in the diff view. /// </summary> /// <param name="hvoPara">hvo of paragraph containing the difference</param> /// <param name="ichMin">starting character position of the difference</param> /// ------------------------------------------------------------------------------------ public void ScrollToParaDiff(int hvoPara, int ichMin) { CheckDisposed(); StTxtPara para = new StTxtPara(m_fdoCache, hvoPara); int iPara = para.IndexInOwner; StText text = new StText(m_fdoCache, para.OwnerHVO); if (m_fdoCache.GetClassOfObject(text.OwnerHVO) == ScrSection.kClassId) { int tag = text.OwningFlid == (int)ScrSection.ScrSectionTags.kflidHeading ? (int)ScrSection.ScrSectionTags.kflidHeading : (int)ScrSection.ScrSectionTags.kflidContent; ScrSection section = new ScrSection(m_fdoCache, text.OwnerHVO); SetInsertionPoint(tag, 0, section.IndexInBook, iPara, ichMin, false); } else SetInsertionPoint((int)ScrBook.ScrBookTags.kflidTitle, 0, 0, iPara, ichMin, false); }
public override void Display(IVwEnv vwenv, int hvo, int frag) { CheckDisposed(); ITsStrFactory tsf = null; switch (frag) { case kfragStText: // The whole text, root object for the InterlinDocChild. if (hvo == 0) return; // What if the user deleted all the texts? See LT-6727. vwenv.set_IntProperty((int)FwTextPropType.ktptEditable, (int)FwTextPropVar.ktpvDefault, (int)TptEditable.ktptNotEditable); vwenv.OpenDiv(); StText stText = new StText(m_cache, hvo); vwenv.set_IntProperty((int)FwTextPropType.ktptMarginBottom, (int)FwTextPropVar.ktpvMilliPoint, 6000); vwenv.set_IntProperty((int)FwTextPropType.ktptFontSize, (int)FwTextPropVar.ktpvMilliPoint, 24000); // Add both vernacular and analysis if we have them (LT-5561). bool fAddedVernacular = false; int wsVernTitle = 0; // if (stText.Title.TryWs(LangProject.kwsFirstVern, out wsVernTitle)) { vwenv.OpenParagraph(); vwenv.AddStringAltMember(vtagStTextTitle, wsVernTitle, this); vwenv.CloseParagraph(); fAddedVernacular = true; } int wsAnalysisTitle = 0; vwenv.set_IntProperty((int)FwTextPropType.ktptMarginBottom, (int)FwTextPropVar.ktpvMilliPoint, 10000); vwenv.OpenParagraph(); ITsString tssAnal; if (stText.Title.TryWs(LangProject.kwsFirstAnal, out wsAnalysisTitle, out tssAnal) && !tssAnal.Equals(stText.Title.BestVernacularAlternative)) { if (fAddedVernacular) { // display analysis title at smaller font size. vwenv.set_IntProperty((int)FwTextPropType.ktptFontSize, (int)FwTextPropVar.ktpvMilliPoint, 12000); } vwenv.AddStringAltMember(vtagStTextTitle, wsAnalysisTitle, this); } else { // just add a blank title. tsf = TsStrFactoryClass.Create(); ITsString blankTitle = tsf.MakeString("", m_wsAnalysis); vwenv.AddString(blankTitle); } vwenv.CloseParagraph(); int wsSource = 0; ITsString tssSource = stText.SourceOfTextForWs(m_wsVernForDisplay); if (tssSource == null || tssSource.Length == 0) { tssSource = stText.SourceOfTextForWs(m_wsAnalysis); if (tssSource != null && tssSource.Length > 0) wsSource = m_wsAnalysis; } else { wsSource = m_wsVernForDisplay; } vwenv.set_IntProperty((int)FwTextPropType.ktptMarginBottom, (int)FwTextPropVar.ktpvMilliPoint, 10000); if (tssSource != null && tssSource.Length > 0) { vwenv.OpenParagraph(); vwenv.set_IntProperty((int)FwTextPropType.ktptFontSize, (int)FwTextPropVar.ktpvMilliPoint, 12000); vwenv.AddStringAltMember(vtagStTextSource, wsSource, this); vwenv.CloseParagraph(); } else { // just add a blank source. tsf = TsStrFactoryClass.Create(); ITsString tssBlank = tsf.MakeString("", m_wsAnalysis); vwenv.AddString(tssBlank); } vwenv.set_IntProperty((int)FwTextPropType.ktptMarginBottom, (int)FwTextPropVar.ktpvMilliPoint, 10000); vwenv.OpenParagraph(); if (stText.OwningFlid == (int)Text.TextTags.kflidContents) { vwenv.AddObjProp((int)CmObjectFields.kflidCmObject_Owner, this, kfragTextDescription); } vwenv.CloseParagraph(); base.Display(vwenv, hvo, frag); vwenv.CloseDiv(); break; case kfragTextDescription: vwenv.AddStringAltMember((int)CmMajorObject.CmMajorObjectTags.kflidDescription, m_wsAnalysis, this); break; case kfragSegFf: // One freeform annotation. int[] wssAnalysis = m_WsList.AnalysisWsIds; if (wssAnalysis.Length == 0) break; // This is bizarre, but for the sake of paranoia... tsf = TsStrFactoryClass.Create(); int hvoType = m_cache.MainCacheAccessor.get_ObjectProp(hvo, (int)CmAnnotation.CmAnnotationTags.kflidAnnotationType); string label = ""; if (hvoType == NoteSegmentDefn) label = ITextStrings.ksNt; else if (hvoType == FtSegmentDefn) label = ITextStrings.ksFT; else if (hvoType == LtSegmentDefn) label = ITextStrings.ksLT; else throw new Exception("Unexpected FF annotation type"); ITsString tssLabel = tsf.MakeString(label, m_cache.DefaultUserWs); ISilDataAccess sda = vwenv.DataAccess; if (wssAnalysis.Length == 1) { ITsString tss = sda.get_MultiStringAlt(hvo, (int)CmAnnotation.CmAnnotationTags.kflidComment, m_cache.DefaultAnalWs); if (tss.Length == 0) break; vwenv.OpenParagraph(); vwenv.AddString(tssLabel); vwenv.AddStringAltMember((int)CmAnnotation.CmAnnotationTags.kflidComment, m_cache.DefaultAnalWs, this); vwenv.CloseParagraph(); } else { int labelWidth, labelHeight; vwenv.get_StringWidth(tssLabel, null, out labelWidth, out labelHeight); // This roughly corresponds to the width of the space at the end of FT. // The nice way to do it (here and in the base class) would be a table or 'interlinear' paragraph. labelWidth += 3000; int cNonBlank = 0; for (int i = 0; i < wssAnalysis.Length; i++) { ITsString tss = sda.get_MultiStringAlt(hvo, (int)CmAnnotation.CmAnnotationTags.kflidComment, wssAnalysis[i]); if (tss.Length == 0) continue; if (cNonBlank != 0) { // Indent subsequent paragraphs by the width of the main label. vwenv.set_IntProperty((int)FwTextPropType.ktptLeadingIndent, (int) FwTextPropVar.ktpvMilliPoint, labelWidth); } vwenv.OpenParagraph(); if (cNonBlank == 0) vwenv.AddString(tssLabel); cNonBlank++; // after tests above! m_WsList.AddWsLabel(vwenv, i); vwenv.AddStringAltMember((int)CmAnnotation.CmAnnotationTags.kflidComment, wssAnalysis[i], this); vwenv.CloseParagraph(); } } break; default: base.Display(vwenv, hvo, frag); break; } }
/// <summary> /// Put all (unique) wordforms of the text in the medium priority queue of the Parser /// </summary> /// <param name="text"></param> public void UpdateWordformsInText(StText text) { CheckDisposed(); int[] aiWordformHvos = text.UniqueWordforms(); ParserConnection con = Connection; if (con != null) { ParserScheduler parser = con.Parser; if (parser != null) { parser.LoadGrammarAndLexiconIfNeeded(); parser.ScheduleWordformsForUpdate(aiWordformHvos, WordWorks.Parser.ParserScheduler.Priority.soon); } } }
public void DeleteObjects() { CheckDisposed(); m_fdoCache.BeginUndoTask("make texts", "undo make texts"); // One text with 4 paras SIL.FieldWorks.FDO.Ling.Text text1 = new Text(); m_fdoCache.LangProject.TextsOC.Add(text1); StText body1 = new StText(); text1.ContentsOA = body1; StTxtPara para1A = new StTxtPara(); body1.ParagraphsOS.Append(para1A); StTxtPara para1B = new StTxtPara(); body1.ParagraphsOS.Append(para1B); StTxtPara para1C = new StTxtPara(); body1.ParagraphsOS.Append(para1C); StTxtPara para1D = new StTxtPara(); body1.ParagraphsOS.Append(para1D); para1A.Contents.Text = "Contents of para1A"; para1B.Contents.Text = "Para 1B stuff"; para1C.Contents.Text = "Rubbish in 1C"; para1D.Contents.Text = "Things in 1D"; // Second text, 2 paras SIL.FieldWorks.FDO.Ling.Text text2 = new Text(); m_fdoCache.LangProject.TextsOC.Add(text2); StText body2 = new StText(); text2.ContentsOA = body2; StTxtPara para2A = new StTxtPara(); body2.ParagraphsOS.Append(para2A); StTxtPara para2B = new StTxtPara(); body2.ParagraphsOS.Append(para2B); para2A.Contents.Text = "2A contents"; para2B.Contents.Text = "2B stuff"; m_fdoCache.EndUndoTask(); Set<int> idsToDelete = new Set<int>(); idsToDelete.Add(para1A.Hvo); idsToDelete.Add(para1B.Hvo); idsToDelete.Add(para1D.Hvo); idsToDelete.Add(para2A.Hvo); m_fdoCache.BeginUndoTask("delete paras", "undo delete paras"); CmObject.DeleteObjects(idsToDelete, m_fdoCache); m_fdoCache.EndUndoTask(); Assert.AreEqual(1, body1.ParagraphsOS.Count, "paras were deleted1"); Assert.AreEqual(1, body2.ParagraphsOS.Count, "paras were deleted2"); m_fdoCache.Undo(); m_fdoCache.VwCacheDaAccessor.ClearAllData(); // as in Refresh. Assert.AreEqual(4, body1.ParagraphsOS.Count, "paras were restored1"); Assert.AreEqual(2, body2.ParagraphsOS.Count, "paras were restored1"); Assert.AreEqual("Contents of para1A", para1A.Contents.Text, "para 1A contents restored"); Assert.AreEqual("Things in 1D", para1D.Contents.Text, "para 1D contents restored"); Assert.AreEqual("2A contents", para2A.Contents.Text, "para 2A contents restored"); m_fdoCache.Redo(); m_fdoCache.VwCacheDaAccessor.ClearAllData(); // as in Refresh. Assert.AreEqual(1, body1.ParagraphsOS.Count, "paras were deleted1"); Assert.AreEqual(1, body2.ParagraphsOS.Count, "paras were deleted2"); m_fdoCache.Undo(); m_fdoCache.VwCacheDaAccessor.ClearAllData(); // as in Refresh. Assert.AreEqual(4, body1.ParagraphsOS.Count, "paras were restored1"); Assert.AreEqual(2, body2.ParagraphsOS.Count, "paras were restored1"); Assert.AreEqual("Contents of para1A", para1A.Contents.Text, "para 1A contents restored"); Assert.AreEqual("Things in 1D", para1D.Contents.Text, "para 1D contents restored"); Assert.AreEqual("2A contents", para2A.Contents.Text, "para 2A contents restored"); m_fdoCache.Undo(); // cleans up the last of what we added. }
public override void ShowObject(int hvoRoot, string layoutName) { int hvoShowObj = hvoRoot; int clsid = Cache.GetClassOfObject(hvoRoot); int hvoStText = 0; if (clsid == CmBaseAnnotation.kClassId) // RecordClerk is tracking the annotation { // This pane, as well as knowing how to work with a record list of Texts, knows // how to work with one of CmBaseAnnotations, that is, a list of occurrences of // a word. int annHvo = hvoRoot; int hvoPara = Cache.MainCacheAccessor.get_ObjectProp(annHvo, (int)CmBaseAnnotation.CmBaseAnnotationTags.kflidBeginObject); hvoStText = Cache.GetOwnerOfObject(hvoPara); hvoShowObj = hvoStText; } else { hvoStText = hvoRoot; } StText stText = new StText(Cache, hvoStText); if (stText.OwningFlid == (int)FDO.Ling.Text.TextTags.kflidContents) hvoShowObj = stText.OwnerHVO; base.ShowObject(hvoShowObj, layoutName); }
/// ------------------------------------------------------------------------------------ /// <summary> /// Use this method to remove the contents (all paragraphs) of an StText. /// </summary> /// <param name="text">StText from which the contents is removed</param> /// ------------------------------------------------------------------------------------ public static void ClearContents(StText text) { for (int lastPara = text.ParagraphsOS.Count - 1; lastPara >= 0; lastPara--) text.ParagraphsOS.RemoveAt(lastPara); }
/// ------------------------------------------------------------------------------------ /// <summary> /// Remove any duplicate verse numbers following the new verse number in the following /// text in the current as well as the following section, if any. /// </summary> /// <param name="hvoObj">The id of the para or translation being modified</param> /// <param name="propTag">The flid (Contents or Translation)</param> /// <param name="tss">The structured string or the para or trans with the new verse /// number</param> /// <param name="wsAlt">The writing system, if a back trans multiString alt</param> /// <param name="chapterToRemove">A string representation of the duplicate chapter number /// to remove.</param> /// <param name="verseRangeToRemove">A string representation of the duplicate verse number to /// remove. This may also be a verse bridge, in which case we will remove all verse /// numbers up to the end value of the bridge</param> /// <param name="ich">The character offset after which we start looking for dups</param> /// ------------------------------------------------------------------------------------ private void RemoveDuplicateVerseNumbers(int hvoObj, int propTag, ITsString tss, int wsAlt, string chapterToRemove, string verseRangeToRemove, int ich) { // Determine the verse number we will remove up to int removeUpToVerse = ScrReference.VerseToIntEnd(verseRangeToRemove); bool inBackTrans = (m_cache.GetClassOfObject(hvoObj) == CmTranslation.kClassId); // Get my current StText, ScrSection and ScrBook int hvoCurrPara = inBackTrans ? (new CmTranslation(m_cache, hvoObj)).OwnerHVO : hvoObj; ScrTxtPara currentPara = new ScrTxtPara(m_cache, hvoCurrPara); // Determine the last chapter reference to remove. int removeChapter = (chapterToRemove != null && chapterToRemove.Length > 0) ? ScrReference.ChapterToInt(chapterToRemove) : 0; // First look in the paragraph where the verse was inserted. if (RemoveDuplicateVerseNumbersInPara(hvoObj, propTag, tss, wsAlt, removeChapter, removeUpToVerse, ich)) { return; } // Search through current and subsequent section (if any) for duplicate verse numbers. StText text = new StText(m_cache, currentPara.OwnerHVO); ScrSection currentSection = new ScrSection(m_cache, text.OwnerHVO); IScrBook currentBook = currentSection.OwningBook; // First look through successive paragraphs for duplicate verse numbers, and then // try the next section if necessary. if (!RemoveDuplicateVerseNumbersInText(text, currentPara.IndexInOwner + 1, inBackTrans, propTag, wsAlt, removeChapter, removeUpToVerse)) { ScrSection nextSection = currentSection.NextSection; if (nextSection != null) { RemoveDuplicateVerseNumbersInText(nextSection.ContentOA, 0, inBackTrans, propTag, wsAlt, removeChapter, removeUpToVerse); } } }
/// ------------------------------------------------------------------------------------ /// <summary> /// Create a new <see cref="StTxtPara"/>, owned by the given <see cref="StText"/>. /// Set it with data accumulated in this builder. /// </summary> /// <param name="hvoOwner">HVO of the <see cref="StText"/> that is to own the new /// paragraph</param> /// <param name="iPos">0-based index of the position in the sequence of paragraphs where the /// new paragraph is to be inserted. If a paragraph is already in this position, the new /// paragraph will be inserted before the existing paragraph.</param> /// <returns>A new StTextPara whose contents are built up from the prior calls /// to <see cref="AppendRun"/> and whose style is set based on the current value of /// <see cref="ParaStylePropsProxy"/>.</returns> /// ------------------------------------------------------------------------------------ public StTxtPara CreateParagraph(int hvoOwner, int iPos) { CheckDisposed(); // ENHANCE: Could maybe squeeze a little more performance out of this by calling the // stored procedure that used to be called by TeImporter to create paragraphs. // insert a new para in the owner's collection StTxtPara para = new StTxtPara(); StText text = new StText(m_cache, hvoOwner); text.ParagraphsOS.InsertAt(para, iPos); SetStTxtParaPropertiesAndClearBuilder(para, iPos); return para; }
/// ------------------------------------------------------------------------------------ /// <summary> /// Called to make the test data for the tests /// </summary> /// ------------------------------------------------------------------------------------ protected override void CreateTestData() { m_text = m_scrInMemoryCache.AddTitleToMockedBook(Int32.MaxValue, "Who cares?"); }
/// ------------------------------------------------------------------------------------ /// <summary> /// if we are in an intro paragraph (i.e. not in scripture content) then /// make sure the section head has an intro section head style /// </summary> /// ------------------------------------------------------------------------------------ private void CheckForSectionHeadInIntroMaterial() { if (!m_fInScriptureText && !m_fCurrentSectionIsIntro && m_hvoSectionHeading != 0) { m_fCurrentSectionIsIntro = true; StText text = new StText(m_cache, m_hvoSectionHeading); foreach (StTxtPara para in text.ParagraphsOS) { if (para.StyleRules == StyleUtils.ParaStyleTextProps(ScrStyleNames.SectionHead)) { para.StyleRules = StyleUtils.ParaStyleTextProps(ScrStyleNames.IntroSectionHead); } } } }
/// ------------------------------------------------------------------------------------ /// <summary> /// Finds the nearest footnote before the given selection. /// </summary> /// <param name="helper">The selection helper.</param> /// <param name="book">The book that owns the footnote collection.</param> /// <param name="fSearchForward">True to also search forward within the current paragraph</param> /// <returns></returns> /// ------------------------------------------------------------------------------------ private StFootnote FindFootnoteNearSelection(SelectionHelper helper, IScrBook book, bool fSearchForward) { CheckDisposed(); if (helper == null) helper = CurrentSelection; if (helper == null || book == null) return null; SelLevInfo[] levels = helper.GetLevelInfo(SelectionHelper.SelLimitType.Anchor); int iParagraph = -1; int tag = 0; int ich = helper.IchAnchor; int paraLev = helper.GetLevelForTag((int)StText.StTextTags.kflidParagraphs); int contentLev = helper.GetLevelForTag((int)StTxtPara.StTxtParaTags.kflidContents); Debug.Assert(paraLev != -1, "Need a paragraph for this method"); iParagraph = levels[paraLev].ihvo; int iSection = ((ITeView)Control).LocationTracker.GetSectionIndexInBook( helper, SelectionHelper.SelLimitType.Anchor); if (iSection < 0) tag = (int)ScrBook.ScrBookTags.kflidTitle; else { tag = (helper.GetLevelForTag((int)ScrSection.ScrSectionTags.kflidContent) >= 0 ? (int)ScrSection.ScrSectionTags.kflidContent : (int)ScrSection.ScrSectionTags.kflidHeading); } // Special case: if we're in the caption of a picture, get the ich from // the first level instead of the anchor. (TE-4696) if (contentLev >= 0 && levels[contentLev].ihvo == -1) ich = levels[contentLev].ich; ScrFootnote prevFootnote = null; if (fSearchForward) // look first at our current position, if we are searching foward { prevFootnote = ScrFootnote.FindCurrentFootnote(m_cache, book, iSection, iParagraph, ich, tag); } if (prevFootnote == null) { StTxtPara para = new StTxtPara(m_cache, levels[paraLev].hvo); ITsString tss = para.Contents.UnderlyingTsString; if (ich != 0) { // look backwards in our current paragraph prevFootnote = ScrFootnote.FindLastFootnoteInString(m_cache, tss, ref ich, true); } else if (iParagraph > 0) { // look at the previous paragraph for a footnote at the end StText text = new StText(m_cache, levels[paraLev + 1].hvo); StTxtPara prevPara = (StTxtPara)text.ParagraphsOS[iParagraph - 1]; ITsString prevTss = prevPara.Contents.UnderlyingTsString; int ichTmp = -1; prevFootnote = ScrFootnote.FindLastFootnoteInString(m_cache, prevTss, ref ichTmp, false); if (prevFootnote != null) { if (ichTmp == prevTss.Length - 1) ich = ichTmp; else prevFootnote = null; } // ENHANCE: Look across contexts. } } if (prevFootnote == null && fSearchForward) { // look ahead in the same paragraph StTxtPara para = new StTxtPara(m_cache, levels[paraLev].hvo); ITsString tss = para.Contents.UnderlyingTsString; prevFootnote = ScrFootnote.FindFirstFootnoteInString(m_cache, tss, ref ich, true); } if (prevFootnote == null) { // just go back until we find one prevFootnote = ScrFootnote.FindPreviousFootnote(m_cache, book, ref iSection, ref iParagraph, ref ich, ref tag); } return prevFootnote; }
/// ------------------------------------------------------------------------------------ /// <summary> /// Set everything up for processing a book title. /// </summary> /// ------------------------------------------------------------------------------------ protected void StartBookTitle() { m_fInBookTitle = true; m_fInVerseTextParagraph = false; m_fInSectionHeading = false; // Blow away any existing title so that we don't append this one to the previous // one. This can happen if there is an intro book title, followed later by a real // book title. In this case, we don't need to keep both. The intro book title // can be generated for the view (or for publishing) from the book title. They // should always be the same. StText title = new StText(m_cache, m_hvoTitle); int cTitleParas = title.ParagraphsOS.Count; for (int i = 0; i < cTitleParas; i++) title.ParagraphsOS.RemoveAt(0); }
/// ------------------------------------------------------------------------------------ /// <summary> /// This is called by a view when the views code is about to delete a paragraph. We /// need to save the back translations by moving them to whatever paragraph the deleted /// one is merging with. /// </summary> /// <param name="selHelper">The selection helper</param> /// <param name="hvoObject">HVO of the StTxtPara to be deleted</param> /// <param name="hvoOwner">HVO of the StText that owns the para</param> /// <param name="tag">flid in which para is owned</param> /// <param name="ihvo">index of paragraph in text</param> /// <param name="fMergeNext"><c>true</c> if this paragraph is merging with the /// following paragraph.</param> /// ------------------------------------------------------------------------------------ public override void AboutToDelete(SelectionHelper selHelper, int hvoObject, int hvoOwner, int tag, int ihvo, bool fMergeNext) { CheckDisposed(); // do the normal processing to combine the paragraphs base.AboutToDelete(selHelper, hvoObject, hvoOwner, tag, ihvo, fMergeNext); // only do the rest of the processing if we are about to delete a paragraph. if (tag == (int)StText.StTextTags.kflidParagraphs) { // Now need to refresh footnote cache for surviving paragraph - footnotes in the // deleted paragraph will still refer to it as the containing paragraph. IStText text = new StText(Cache, hvoOwner); int hvoSurvivor = SurvivorParagraphHvo(selHelper, text, ihvo, fMergeNext); if (hvoSurvivor > 0) ScrFootnote.ReplaceReferencesToParagraph(Cache, hvoObject, hvoSurvivor); } // And finally give the annotation adjust a chance to do its thing. (Nulls only in mock testing.) if (m_annotationAdjuster != null && selHelper != null && selHelper.RootSite != null) m_annotationAdjuster.AboutToDelete(selHelper.Selection, selHelper.RootSite.RootBox, hvoObject, hvoOwner, tag, ihvo, fMergeNext); }
/// ------------------------------------------------------------------------------------ /// <summary> /// Refreshes cache for all footnotes in the StText containing the paragraph. We only /// use this method when verses of a paragraph are changed - this will only happen on /// content paragraphs of a ScrSection. /// </summary> /// <param name="cache"></param> /// <param name="hvoObj"></param> /// ------------------------------------------------------------------------------------ internal static void RefreshCacheForParagraph(FdoCache cache, int hvoObj) { int hvoPara = hvoObj; if (cache.GetClassOfObject(hvoObj) == CmTranslation.kClassId) hvoPara = cache.GetOwnerOfObject(hvoObj); int hvoText = cache.GetOwnerOfObject(hvoPara); IStText text = new StText(cache, hvoText); ScrSection section = new ScrSection(cache, text.OwnerHVO); BCVRef verseRef = new BCVRef(section.VerseRefStart); Dictionary<int, FootnoteHashEntry> dict; // Don't bother on empty cache - it will be created when needed. Need to use HVO to get GUID so we // don't create the book and potentially cause the refresh routine to be called. Guid bookGuid = cache.GetGuidFromId(section.OwnerHVO); if (!cache.TryGetHashtable<int, FootnoteHashEntry>(bookGuid, out dict) || dict.Count == 0) { return; } AddFootnoteRefsInText(text, dict, ref verseRef); }
public void NonScriptureText() { IText text = new Text(); Cache.LangProject.TextsOC.Add(text); StText sttext = new StText(); text.ContentsOA = sttext; m_para = new StTxtPara(); sttext.ParagraphsOS.Append(m_para); string paraContents = "Das buch ist rot"; string trans = "The book is red"; m_para.Contents.UnderlyingTsString = m_tsf.MakeString(paraContents, m_wsVern); ICmIndirectAnnotation ft = MakeFt(m_para, trans, 0, paraContents.Length); FreeTransEditMonitor monitor = new FreeTransEditMonitor(Cache, m_wsTrans); // BEFORE propChanged! Cache.PropChanged(ft.Hvo, (int)CmAnnotation.CmAnnotationTags.kflidComment, 0, 0, 0); monitor.LoseFocus(); Assert.AreEqual(0, m_para.TranslationsOC.Count, "monitor should not make CmTranslation for non-Scripture"); }
public void Export() { using (MemoryStream stream = new MemoryStream()) { //Set up some cells. int[] allParaWfics = m_helper.MakeAnnotationsUsedN(5); // This block makes the first row, puts CCAs in cells 1 and 2, and list refs in cells 1 and 2 CmIndirectAnnotation row0 = m_helper.MakeFirstRow(); int[] movedItems = new int[] { allParaWfics[1] }; CmIndirectAnnotation cca0_1 = m_helper.MakeColumnAnnotation(1, new int[] { allParaWfics[0] }, row0); ICmPossibility marker = m_helper.GetAMarker(); ICmBaseAnnotation cca0_1b = m_helper.MakeMarkerAnnotation(1, row0, marker); CmIndirectAnnotation cca0_2 = m_helper.MakeColumnAnnotation(2, movedItems, row0); ICmPossibility marker2 = m_helper.GetAnotherMarker(); ICmBaseAnnotation cca0_2b = m_helper.MakeMarkerAnnotation(2, row0, marker2); ICmBaseAnnotation cca0_2c = m_helper.MakeMarkerAnnotation(2, row0, marker); // Now another row, and cell 4 on the first has a ref to it. The new row has a CCA with two wfics in cell 1. The cell is // two columns wide, being merged with the previous cell. CmIndirectAnnotation row1 = m_helper.MakeSecondRow(); ICmIndirectAnnotation cca0_4 = m_helper.MakeDependentClauseMarker(row0, 4, new int[] { row1.Hvo }, "song", "2"); CmIndirectAnnotation cca1_1 = m_helper.MakeColumnAnnotation(1, new int[] { allParaWfics[2], allParaWfics[3] }, row1); ConstituentChartLogic.SetFeature(Cache.MainCacheAccessor, cca1_1.Hvo, ConstituentChartLogic.mergeBeforeTag, true); // Let's have some notes on row 0. StText notesText = new StText(); row0.TextOA = notesText; StTxtPara notesPara = new StTxtPara(); notesText.ParagraphsOS.Append(notesPara); notesPara.Contents.UnderlyingTsString = Cache.MakeAnalysisTss("This is a test note"); // And some moved text in row 1 CmIndirectAnnotation cca1_2 = m_helper.MakeColumnAnnotation(2, new int[] { allParaWfics[4] }, row1); ConstituentChartLogic.SetFeature(Cache.MainCacheAccessor, cca1_2.Hvo, ConstituentChartLogic.MovedTextFeatureName, true); CmIndirectAnnotation cca2_3 = m_helper.MakeMovedTextAnnotation(3, cca1_2, row1, "Preposed"); // We need four rows to properly test the variations on endPara/endSent CmIndirectAnnotation row2 = m_helper.MakeRow(m_chart, "2"); ConstituentChartLogic.SetFeature(Cache.MainCacheAccessor, row2.Hvo, ConstituentChartLogic.EndSentFeatureName, true); CmIndirectAnnotation row3 = m_helper.MakeRow(m_chart, "3"); ConstituentChartLogic.SetFeature(Cache.MainCacheAccessor, row3.Hvo, ConstituentChartLogic.EndParaFeatureName, true); ConstituentChartLogic.SetFeature(Cache.MainCacheAccessor, row3.Hvo, ConstituentChartLogic.EndSentFeatureName, true); CmIndirectAnnotation row4 = m_helper.MakeRow(m_chart, "4"); XmlWriter writer = new XmlTextWriter(stream, Encoding.UTF8); ConstChartVc vc = new ConstChartVc(m_chartBody); vc.LineChoices = m_chartBody.LineChoices; DiscourseExporter exporter = new DiscourseExporter(m_inMemoryCache.Cache, writer, m_chart.Hvo, vc, m_inMemoryCache.Cache.DefaultAnalWs); writer.WriteStartDocument(); writer.WriteStartElement("document"); exporter.ExportDisplay(); writer.WriteEndElement(); writer.WriteEndDocument(); writer.Flush(); // Close makes it unuseable stream.Position = 0; StreamReader reader = new StreamReader(stream, Encoding.UTF8); string result = reader.ReadToEnd(); XmlDocument doc = new XmlDocument(); doc.LoadXml(result); XmlNode docNode = doc.DocumentElement; Assert.AreEqual("document", docNode.Name); XmlNode chartNode = VerifyNode("chart", docNode, 0, "chart", 7, 0); VerifyTitleRow(chartNode); VerifyTitle2Row(chartNode); VerifyFirstDataRow(chartNode); VerifySecondDataRow(chartNode); XmlNode thirdRow = VerifyNode("row", chartNode, 4, "row", 8, 3); AssertAttr(thirdRow, "endSent", "true"); XmlNode fourthRow = VerifyNode("row", chartNode, 5, "row", 8, 3); AssertAttr(fourthRow, "endPara", "true"); XmlNode langNode = VerifyNode("languages", docNode, 1, "languages", 2, 0); XmlNode enNode = VerifyNode("english lang node", langNode, 0, "language", 0, 2); AssertAttr(enNode, "lang", "en"); AssertAttr(enNode, "font", null); // don't verify exact font, may depend on installation. } }
/// ------------------------------------------------------------------------------------ /// <summary> /// Finds the previous footnote and returns it. /// </summary> /// <param name="para">Paragraph to start search</param> /// <param name="ich">Character index to start search, or -1 to start at the end of /// the paragraph.</param> /// <returns>Previous footnote, or <c>null</c> if there isn't a previous footnote in the /// current book.</returns> /// ------------------------------------------------------------------------------------ public static ScrFootnote FindPreviousFootnote(IStTxtPara para, int ich) { FdoCache cache = para.Cache; IScrBook book; IStText text = new StText(cache, para.OwnerHVO); int iPara = Array.IndexOf(text.ParagraphsOS.HvoArray, para.Hvo); int flid = text.OwningFlid; if (flid == (int)ScrSection.ScrSectionTags.kflidHeading || flid == (int)ScrSection.ScrSectionTags.kflidContent) { ScrSection section = new ScrSection(cache, text.OwnerHVO); int iSection = section.IndexInBook; return ScrFootnote.FindPreviousFootnote(cache, section.OwningBook, ref iSection, ref iPara, ref ich, ref flid); } else if (flid == (int)ScrBook.ScrBookTags.kflidTitle) { book = new ScrBook(cache, text.OwnerHVO); return ScrFootnote.FindPreviousFootnoteInText(text, ref iPara, ref ich, false); } else throw new Exception("Can only create footnotes in Scripture Book titles, section heads, and contents"); }