/// ------------------------------------------------------------------------------------ /// <summary> /// Merges content of given section into the content of the previous section and then /// deletes the given section. /// </summary> /// <param name="helper"> </param> /// <param name="book"></param> /// <param name="section"></param> /// <param name="fPositionAtEnd">If true position of Selection is placed at end of /// paragraph, else at the beginning.</param> /// ------------------------------------------------------------------------------------ private void MergeContentWithPreviousSection(SelectionHelper helper, IScrBook book, IScrSection section, bool fPositionAtEnd) { //REVIEW: Can the methods that call this be refactored //to use (a refactored?) ScrSection.MergeWithPreviousSection? // // Get the previous section and move the paragraphs. IScrSection sectionPrev = section.PreviousSection; IStText textPrev = sectionPrev.ContentOA; ILocationTracker tracker = ((ITeView)Control).LocationTracker; int iBook = tracker.GetBookIndex(helper, SelectionHelper.SelLimitType.Top); int cparaPrev = 0; if (textPrev == null) { // Prevent crash when dealing with corrupt database (TE-4869) // Since the previous section doesn't have a text, we simply move the entire text // object from the current section to the previous section. sectionPrev.ContentOA = section.ContentOA; } else { cparaPrev = textPrev.ParagraphsOS.Count; IStText textOldContents = section.ContentOA; textOldContents.ParagraphsOS.MoveTo(0, textOldContents.ParagraphsOS.Count - 1, textPrev.ParagraphsOS, cparaPrev); } // protected for some reason...textPrev.ParagraphsOS.Append(text.ParagraphsOS.HvoArray); book.SectionsOS.Remove(section); // Now we have to re-establish a selection. Whatever happens, it will be in the // same book as before, and the previous section, and in the body. if (InSectionHead || !fPositionAtEnd) { tracker.SetBookAndSection(helper, SelectionHelper.SelLimitType.Top, iBook, sectionPrev.IndexInOwner); helper.GetLevelInfo(SelectionHelper.SelLimitType.Top)[1].tag = ScrSectionTags.kflidContent; } Debug.Assert(helper.GetLevelInfo(SelectionHelper.SelLimitType.Top)[1].tag == ScrSectionTags.kflidContent); if (fPositionAtEnd) { // we want selection at end of last paragraph of old previous section. // (That is, at the end of paragraph cparaPrev - 1.) Debug.Assert(cparaPrev > 0); helper.GetLevelInfo(SelectionHelper.SelLimitType.Top)[0].ihvo = cparaPrev - 1; IStTxtPara paraPrev = textPrev[cparaPrev - 1]; int cchParaPrev = paraPrev.Contents.Length; helper.IchAnchor = cchParaPrev; helper.IchEnd = cchParaPrev; helper.AssocPrev = true; } else { // want selection at start of old first paragraph of deleted section. // (That is, at the start of paragraph cparaPrev.) helper.GetLevelInfo(SelectionHelper.SelLimitType.Top)[0].ihvo = cparaPrev; helper.IchAnchor = 0; helper.IchEnd = 0; helper.AssocPrev = false; } helper.SetLevelInfo(SelectionHelper.SelLimitType.Bottom, helper.GetLevelInfo(SelectionHelper.SelLimitType.Top)); helper.SetIPAfterUOW(); }
/// ------------------------------------------------------------------------------------ /// <summary> /// Low-level implementation of insert verse number. /// </summary> /// <param name="selHelper">the given SelectionHelper</param> /// ------------------------------------------------------------------------------------ public void InsertVerseNumber(SelectionHelper selHelper) { CheckDisposed(); Debug.Assert(selHelper != null); Debug.Assert(!selHelper.IsRange || IsSelectionInPrompt(selHelper)); // Get the details about the current selection int ichSelOrig; //the character offset of the selection in the ITsString int hvoObj; //the id of the object the selection is in (StTxtPara or CmTranslation) int propTag; //property tag of object ITsString tssSel; //ITsString containing the selection int wsAlt; //the WS of the multiString alt, if selection is in a back translation ichSelOrig = GetSelectionInfo(selHelper, out hvoObj, out propTag, out tssSel, out wsAlt); // The current run is a chapter number and IP is either at the beginning of the line or // in the middle of the chapter number, we need to jump past it to insert the verse number // otherwise it will insert it before the chapter number or in the chapter number. int iRun = tssSel.get_RunAt(ichSelOrig); if (tssSel.Style(iRun) == ScrStyleNames.ChapterNumber && (ichSelOrig == 0 || ichSelOrig > tssSel.get_MinOfRun(iRun))) ichSelOrig = tssSel.get_LimOfRun(iRun); // Adjust the insertion position to the beginning of a word - not in the middle // (may move to an existing verse number too) int ichWord = tssSel.FindWordBoundary(ichSelOrig, UnicodeCharProps, ScrStyleNames.ChapterAndVerse); // TomB and MarkB have decided we won't do this, at least for now // // If the start of the Bt does not match the vernacular, adjust it if required. // if (ichWord > 0) // cInsDel = SetVerseAtStartOfBtIfNeeded(); // ichWord += cInsDel; //adjust // some key variables set by Update or Insert methods, etc string sVerseNumIns = null; // will hold the verse number string we inserted; could be // a simple number, a verse bridge, or the end number added to a bridge string sChapterNumIns = null; // will hold chapter number string inserted or null if none int ichLimIns = -1; //will hold the end of the new chapter/verse numbers we update or insert // Is ichWord in or next to a verse number? (if so, get its ich range) bool fCheckForChapter = (wsAlt != 0); //check for chapter in BT only int ichMin; // min of the verse number run, if we are on one int ichLim; // lim of the verse number run, if we are on one bool fFoundExistingRef = InReference(tssSel, ichWord, fCheckForChapter, out ichMin, out ichLim); SelLevInfo paraInfo = selHelper.GetLevelInfoForTag(StTextTags.kflidParagraphs); IScrTxtPara para = m_cache.ServiceLocator.GetInstance<IScrTxtParaRepository>().GetObject(paraInfo.hvo); // If we moved the selection forward (over spaces or punctuation) to an // existing verse number ... if (fFoundExistingRef && (ichSelOrig < ichWord)) { //Attempt to insert a verse number at the IP, if one is missing there. // if selection is in vernacular... if (propTag == StTxtParaTags.kflidContents) { // Insert missing verse number in vernacular para.InsertMissingVerseNumberInVern(ichSelOrig, ichWord, out sVerseNumIns, out ichLimIns); } } // if a verse number was not inserted, sVerseNumIns is null // If no verse number inserted yet... if (sVerseNumIns == null) { if (fFoundExistingRef) { //We must update the existing verse number at ichWord // is selection in vern or BT? if (propTag == StTxtParaTags.kflidContents) { // Update verse number in vernacular para.UpdateExistingVerseNumberInVern(ichMin, ichLim, out sVerseNumIns, out ichLimIns); } else { //Update verse number in back translation para.UpdateExistingVerseNumberInBt(wsAlt, ichMin, ichLim, out sVerseNumIns, out sChapterNumIns, out ichLimIns); } } else { // We're NOT on an existing verse number, so insert the next appropriate one. // is selection in vern or BT? if (propTag == StTxtParaTags.kflidContents) { para.InsertNextVerseNumberInVern(ichWord, out sVerseNumIns, out ichLimIns); } else { para.InsertNextVerseNumberInBt(wsAlt, ichWord, out sVerseNumIns, out sChapterNumIns, out ichLimIns); selHelper.SetTextPropId(SelectionHelper.SelLimitType.Anchor, CmTranslationTags.kflidTranslation); selHelper.SetTextPropId(SelectionHelper.SelLimitType.End, CmTranslationTags.kflidTranslation); } } } if (sVerseNumIns == null) { MiscUtils.ErrorBeep(); // No verse number inserted or updated return; } // set new IP behind the verse number selHelper.IchAnchor = ichLimIns; selHelper.IchEnd = ichLimIns; selHelper.AssocPrev = true; selHelper.SetIPAfterUOW(); // Remove any duplicate chapter/verse numbers following the new verse number. para.RemoveDuplicateVerseNumbers(wsAlt, sChapterNumIns, sVerseNumIns, ichLimIns); }
/// ------------------------------------------------------------------------------------ /// <summary> /// Handles deletion of empty section content paragraph on delete key being pressed at /// text boundary. /// </summary> /// <param name="helper">The selection helper.</param> /// <returns><c>true</c> if we merged the sections, otherwise <c>false</c>.</returns> /// ------------------------------------------------------------------------------------ private bool HandleDeleteBeforeEmptySectionContentParagraph(SelectionHelper helper) { // delete problem deletion will occur at end of section heading ILocationTracker tracker = ((ITeView)Control).LocationTracker; IScrBook book = tracker.GetBook(helper, SelectionHelper.SelLimitType.Top); int iSection = tracker.GetSectionIndexInBook(helper, SelectionHelper.SelLimitType.Top); IScrSection section = book[iSection]; int cParas = section.ContentOA != null ? section.ContentOA.ParagraphsOS.Count : 0; if (cParas == 0) { return MergeWithFollowingSectionIfInSameContext( helper, book, iSection, section, true); } // If we are the end of heading before a multi-paragraph content // and the first paragraph is empty, we delete the first paragraph of // the content. if (cParas > 1) { if (section.ContentOA[0].Contents.Length == 0) { section.ContentOA.ParagraphsOS.RemoveAt(0); helper.SetIPAfterUOW(); return true; } } // If we are at end of section heading or beginning of section content // and not in the last section, we should merge sections. else { if (iSection < book.SectionsOS.Count - 1 && section.ContentOA[0].Contents.Length == 0) { return MergeWithFollowingSectionIfInSameContext( helper, book, iSection, section, true); } } return false; }
/// ------------------------------------------------------------------------------------ /// <summary> /// Handles deletion of empty section heading paragraph on delete key being pressed at /// text boundary. /// </summary> /// <param name="helper">The helper.</param> /// <param name="fRestoreSelectionIfParaDeleted">True if the selection should be reset /// if the first section head paragraph is deleted, false if the selection should remain /// unchanged.</param> /// ------------------------------------------------------------------------------------ private bool HandleDeleteBeforeEmptySectionHeadParagraph(SelectionHelper helper, bool fRestoreSelectionIfParaDeleted) { IScrBook book = ((ITeView)Control).LocationTracker.GetBook( helper, SelectionHelper.SelLimitType.Top); // Delete problem deletion will be at end of last paragraph of content, // need to check following section head (if available) // Get next section of book. int iSection = ((ITeView)Control).LocationTracker.GetSectionIndexInBook( helper, SelectionHelper.SelLimitType.Top); bool positionAtEnd = false; if (!InSectionHead) { iSection++; if (iSection > book.SectionsOS.Count - 1) return false; positionAtEnd = true; } else if (iSection == 0) return false; IScrSection section = book[iSection]; if (section.HeadingOA == null || section.HeadingOA.ParagraphsOS.Count == 0) { // don't crash if database is corrupt - allow user to merge the two // sections (TE-4869) return MergeWithPreviousSectionIfInSameContext(helper, book, section, positionAtEnd); } else { // if there are more than one paragraph in heading, check to see if first // paragraph can be deleted. if (section.HeadingOA.ParagraphsOS.Count > 1) { if (section.HeadingOA[0].Contents.Length == 0) { section.HeadingOA.ParagraphsOS.RemoveAt(0); if (fRestoreSelectionIfParaDeleted) { // Paragraph where the selection was located was removed so we need // to reset the selection to its same location. helper.SetIPAfterUOW(); } return true; } } else if (section.HeadingOA[0].Contents.Length == 0) { // If we are at end of content before an empty section head, // we should merge sections. return MergeWithPreviousSectionIfInSameContext(helper, book, section, positionAtEnd); } } return false; }
/// ------------------------------------------------------------------------------------ /// <summary> /// Merges the paras in table. /// </summary> /// <param name="helper">The helper.</param> /// <param name="dpt">The problem deletion type.</param> /// <returns><c>true</c> if we merged the paras, otherwise <c>false</c>.</returns> /// ------------------------------------------------------------------------------------ protected internal bool MergeParasInTable(SelectionHelper helper, VwDelProbType dpt) { SelLevInfo[] levInfo = helper.GetLevelInfo(SelectionHelper.SelLimitType.Top); if (levInfo[0].tag != StTextTags.kflidParagraphs) return false; IStText text; int iPara; int tag; IStTxtPara currPara = GetPara(helper, out text, out iPara, out tag); // Backspace at beginning of paragraph ITsStrBldr bldr; if (dpt == VwDelProbType.kdptBsAtStartPara) { if (iPara <= 0) { MiscUtils.ErrorBeep(); return false; } IStTxtPara prevPara = text[iPara - 1]; int prevParaLen = prevPara.Contents.Length; prevPara.MergeParaWithNext(); helper.SetIch(SelectionHelper.SelLimitType.Top, prevParaLen); helper.SetIch(SelectionHelper.SelLimitType.Bottom, prevParaLen); levInfo[0].ihvo = iPara - 1; helper.SetLevelInfo(SelectionHelper.SelLimitType.Top, levInfo); helper.SetLevelInfo(SelectionHelper.SelLimitType.Bottom, levInfo); if (DeferSelectionUntilEndOfUOW) { // We are within a unit of work, so setting the selection will not work now. // we request that a selection be made after the unit of work. Debug.Assert(!helper.IsRange, "Currently, a selection made during a unit of work can only be an insertion point."); helper.SetIPAfterUOW(EditedRootBox.Site); } else { helper.SetSelection(true); } return true; } // delete at end of a paragraph int cParas = text.ParagraphsOS.Count; if (iPara + 1 >= cParas) return false; // We don't handle merging across StTexts currPara.MergeParaWithNext(); if (DeferSelectionUntilEndOfUOW) { // We are within a unit of work, so setting the selection will not work now. // we request that a selection be made after the unit of work. Debug.Assert(!helper.IsRange, "Currently, a selection made during a unit of work can only be an insertion point."); helper.SetIPAfterUOW(EditedRootBox.Site); } else { helper.SetSelection(true); } return true; }
/// ------------------------------------------------------------------------------------ /// <summary> /// Set an insertion point or character-range selection at the specified offsets /// (including segment index) with specified options. /// This is the actual workhorse for all the above methods. /// </summary> /// <param name="iBook">The 0-based index of the Scripture book in which to put the /// selection</param> /// <param name="iSection">The 0-based index of the Scripture section in which to put the /// selection</param> /// <param name="tag">Indicates whether selection should be made in the section /// Heading or Content or in the book title</param> /// <param name="isegment">If ContentType == segmentBT, the index of the segment /// in which to place the selection; otherwise ignored.</param> /// <param name="iPara">The 0-based index of the paragraph in which to put the insertion /// point</param> /// <param name="startCharacter">The 0-based index of the character at which the /// selection begins (or before which the insertion point is to be placed if /// startCharacter == endCharacter)</param> /// <param name="endCharacter">The character location to end the selection</param> /// <param name="fInstall"></param> /// <param name="fMakeVisible"></param> /// <param name="fAssocPrev">If an insertion point, does it have the properties of the /// previous character?</param> /// <param name="scrollOption">Where to scroll the selection</param> /// <returns>The selection helper</returns> /// ------------------------------------------------------------------------------------ public SelectionHelper SelectRangeOfChars(int iBook, int iSection, int tag, int iPara, int isegment, int startCharacter, int endCharacter, bool fInstall, bool fMakeVisible, bool fAssocPrev, VwScrollSelOpts scrollOption) { CheckDisposed(); if (Callbacks == null || Callbacks.EditedRootBox == null) return null; // can't make a selection Debug.Assert(tag == ScrSectionTags.kflidHeading || tag == ScrSectionTags.kflidContent || tag == ScrBookTags.kflidTitle); SelectionHelper selHelper = new SelectionHelper(); selHelper.NumberOfLevels = ((ITeView)Control).LocationTracker.GetLevelCount(tag); int levelForPara = LocationTrackerImpl.GetLevelIndexForTag(StTextTags.kflidParagraphs, m_contentType); selHelper.LevelInfo[levelForPara].tag = StTextTags.kflidParagraphs; selHelper.LevelInfo[levelForPara].ihvo = iPara; selHelper.LevelInfo[levelForPara + 1].tag = tag; ((ITeView)Control).LocationTracker.SetBookAndSection(selHelper, SelectionHelper.SelLimitType.Anchor, iBook, tag == ScrBookTags.kflidTitle ? -1 : iSection); if (ContentType == StVc.ContentTypes.kctSimpleBT) { int levelForBT = LocationTrackerImpl.GetLevelIndexForTag(StTxtParaTags.kflidTranslations, m_contentType); selHelper.LevelInfo[levelForBT].tag = -1; selHelper.LevelInfo[levelForBT].ihvo = 0; selHelper.SetTextPropId(SelectionHelper.SelLimitType.Anchor, CmTranslationTags.kflidTranslation); selHelper.SetTextPropId(SelectionHelper.SelLimitType.End, CmTranslationTags.kflidTranslation); } else if (ContentType == StVc.ContentTypes.kctSegmentBT) { // In all segment BT views, under the paragraph there is a segment, and under that // an object which is the free translation itself. selHelper.LevelInfo[1].tag = StTextTags.kflidParagraphs; // JohnT: why don't we need this for non-BT?? selHelper.LevelInfo[0].ihvo = isegment; selHelper.LevelInfo[0].tag = StTxtParaTags.kflidSegments; selHelper.SetTextPropId(SelectionHelper.SelLimitType.Anchor, SegmentTags.kflidFreeTranslation); selHelper.SetTextPropId(SelectionHelper.SelLimitType.End, SegmentTags.kflidFreeTranslation); } else { // selHelper.LevelInfo[0].tag is set automatically by SelectionHelper class selHelper.SetTextPropId(SelectionHelper.SelLimitType.Anchor, StTxtParaTags.kflidContents); selHelper.SetTextPropId(SelectionHelper.SelLimitType.End, StTxtParaTags.kflidContents); } selHelper.AssocPrev = fAssocPrev; selHelper.SetLevelInfo(SelectionHelper.SelLimitType.End, selHelper.LevelInfo); // Prepare to move the IP to the specified character in the paragraph. selHelper.IchAnchor = startCharacter; selHelper.IchEnd = endCharacter; if (DeferSelectionUntilEndOfUOW) { // We are within a unit of work, so setting the selection will not work now. // we request that a selection be made after the unit of work. Debug.Assert(!selHelper.IsRange, "Currently, a selection made during a unit of work can only be an insertion point."); selHelper.SetIPAfterUOW(EditedRootBox.Site); return selHelper; } // Now that all the preparation to set the IP is done, set it. IVwSelection vwsel = selHelper.SetSelection(Callbacks.EditedRootBox.Site, fInstall, fMakeVisible, scrollOption); // If the selection fails, then try selecting the user prompt. if (vwsel == null) { selHelper.SetTextPropId(SelectionHelper.SelLimitType.Anchor, SimpleRootSite.kTagUserPrompt); selHelper.SetTextPropId(SelectionHelper.SelLimitType.End, SimpleRootSite.kTagUserPrompt); vwsel = selHelper.SetSelection(Callbacks.EditedRootBox.Site, fInstall, fMakeVisible, scrollOption); } if (vwsel == null) { Debug.WriteLine("SetSelection failed in TeEditingHelper.SelectRangeOfChars()"); } return selHelper; }
/// ------------------------------------------------------------------------------------ /// <summary> /// Scroll to a footnote /// </summary> /// <param name="iBook">Index of the book's hvo</param> /// <param name="iFootnote">Index of the footnote's hvo</param> /// <param name="ich">The offset of the character position in the footnote's (first) /// paragraph to use as the insertion point.</param> /// ------------------------------------------------------------------------------------ public void ScrollToFootnote(int iBook, int iFootnote, int ich) { CheckDisposed(); if (Control == null || !(Control is SimpleRootSite)) return; int paraLevel = 0; int footnoteLevel = 1; int bookLevel = 2; // create selection pointing to this footnote SelectionHelper selHelper = new SelectionHelper(); selHelper.AssocPrev = false; // Set up selection for a back translation if (ContentType == StVc.ContentTypes.kctSimpleBT) { paraLevel++; footnoteLevel++; bookLevel++; selHelper.NumberOfLevels = 4; selHelper.LevelInfo[0].tag = -1; selHelper.LevelInfo[0].ihvo = 0; selHelper.LevelInfo[0].cpropPrevious = 2; selHelper.SetTextPropId(SelectionHelper.SelLimitType.Anchor, CmTranslationTags.kflidTranslation); } else if (ContentType == StVc.ContentTypes.kctSegmentBT) { // In all segment BT views, under the paragraph there is a segment, and under that // an object which is the free translation itself. paraLevel++; footnoteLevel++; bookLevel++; selHelper.NumberOfLevels = 4; selHelper.LevelInfo[0].ihvo = 0; // Segment index selHelper.LevelInfo[0].tag = StTxtParaTags.kflidSegments; selHelper.SetTextPropId(SelectionHelper.SelLimitType.Anchor, SegmentTags.kflidFreeTranslation); } else // Selection is in vernacular { selHelper.NumberOfLevels = 3; selHelper.TextPropId = StTxtParaTags.kflidContents; } selHelper.LevelInfo[paraLevel].tag = StTextTags.kflidParagraphs; selHelper.LevelInfo[paraLevel].ihvo = 0; selHelper.LevelInfo[footnoteLevel].tag = ScrBookTags.kflidFootnotes; selHelper.LevelInfo[footnoteLevel].ihvo = iFootnote; selHelper.LevelInfo[bookLevel].tag = BookFilter.Tag; selHelper.LevelInfo[bookLevel].ihvo = iBook; selHelper.IchAnchor = ich; selHelper.IchEnd = ich; if (DeferSelectionUntilEndOfUOW) { // We are within a unit of work, so setting the selection will not work now. // we request that a selection be made after the unit of work. Debug.Assert(!selHelper.IsRange, "Currently, a selection made during a unit of work can only be an insertion point."); selHelper.SetIPAfterUOW(EditedRootBox.Site); return; } IVwSelection vwsel = selHelper.SetSelection((SimpleRootSite)Control, true, true, VwScrollSelOpts.kssoTop); if (vwsel == null) { selHelper.SetTextPropId(SelectionHelper.SelLimitType.Anchor, SimpleRootSite.kTagUserPrompt); selHelper.SetTextPropId(SelectionHelper.SelLimitType.End, SimpleRootSite.kTagUserPrompt); selHelper.SetSelection((SimpleRootSite)Control, true, true); } }