private static void HandleAnySubstantialPunctuationChange(ICmBaseAnnotation cba, WordMaker wm, int ichEditMin, int cvIns, int cvDel, ref Set<int> punctsToDel) { int ichEditLim = ichEditMin + cvDel; // 1.2) detect deletions that occur across segment annotations // 1.2.1) merge adjacent indirect annotations // - TODO: mark indirect annotations as needing verification. // 1.2.2) delete first annotation if (cvDel > 0 && ichEditMin <= cba.BeginOffset && ichEditLim >= cba.EndOffset) { // user deleted entire punctuation cba // 0123456789 // "sample. sentence" // "sample sentence" punctsToDel.Add(cba.Hvo); return; } else if (cvIns > 0 && ichEditMin >= cba.BeginOffset) { // user inserted something inside the punctuation punctsToDel.Add(cba.Hvo); return; } }
/// <summary> /// Handles the PropChange for relevant paragraph changes in order to adjust annotations effected by the edit. /// </summary> /// <param name="hvo"></param> /// <param name="ivMin"></param> /// <param name="cvIns"></param> /// <param name="cvDel"></param> internal void AdjustTextAnnotationsForEdit(int hvo, int ivMin, int cvIns, int cvDel) { if (m_anchorTextInfo == null) return; StTxtPara para = new StTxtPara(m_cache, hvo); // First collect segment break information before before and after the edit was done. List<int> segBreakBeginOffsetsBeforeEdit; List<int> segBreakBeginOffsetsAfterEdit; // collection of dummy segment annotations for edited paragraph. List<TsStringSegment> segmentsAfterEdit; using (ParagraphParserForEditMonitoring ppfem = new ParagraphParserForEditMonitoring(para as IStTxtPara)) { // collect segment break info for BEFORE the edit List<TsStringSegment> segmentsBeforeEdit = ppfem.CollectTempSegmentAnnotations(m_anchorTextInfo.ParaText, out segBreakBeginOffsetsBeforeEdit); // collect segment break info AFTER the edit. segmentsAfterEdit = ppfem.CollectTempSegmentAnnotations(para.Contents.UnderlyingTsString, out segBreakBeginOffsetsAfterEdit); if (segmentsBeforeEdit.Count != segmentsAfterEdit.Count) m_fSegSeqChanged = true; } WordMaker wm = new WordMaker(para.Contents.UnderlyingTsString, m_cache.LanguageWritingSystemFactoryAccessor); Set<int> cbasToDel = new Set<int>(); int[] cbaHvosAffectedByEdit = GetCbasPotentiallyAffectedByTextEdit(hvo, ivMin); ICmBaseAnnotation segToMerge = null; foreach ( ICmBaseAnnotation cba in new FdoObjectSet<ICmBaseAnnotation>(m_cache, cbaHvosAffectedByEdit, false, typeof (CmBaseAnnotation))) { if (m_annsToMoveFromDeletedParagraph.Contains(cba.Hvo) || m_movedAnnsFromAfterInsertBreak.Contains(cba.Hvo)) { // make sure we don't adjust the offsets a second time. continue; } // 1.0) Determine any substantial changes for CmBaseAnnotations or CmIndirectAnnotations, including // whether or not an annotation needs to be deleted as a result of the edit. // NOTE: The assumption is that the annotations were in the "correct" state before the edit we're monitoring. // We are trying to enforce this state of affairs by ParseText in RawTextPane.SetRoot, but // since the annotations may have gotten out of sync before we started monitoring, it's possible // the parser would guess wrongly on how to adjust things. oh well, that's the best we can do for now. Debug.Assert(cba.EndOffset >= ivMin, "expect to be handling only cbas that come at or after the beginning of an edit"); // All our patching up is based on comparing the current text of the paragraph with the original paragraph. // Don't try to patch up any CBAs that are not currently associated with that paragraph, it just produces // crashs. These cases are relatively rare (typically multi-paragraph deletes), and the process of reparsing // the paragraphs will fix things pretty well. // It is not worth saving the text of the LAST paragraph selected before we start the edit, // because all the special code in this class is only intended to optimize matching things up // for single-line edits. if (cba.BeginObjectRAHvo != m_anchorTextInfo.HvoPara) continue; if (cba.AnnotationTypeRAHvo == m_hvoAnnDefnTwfic) { AnnotationAdjuster.HandleAnySubstantialTwficChange(cba, wm, ivMin, cvIns, cvDel, ref cbasToDel); } else if (cba.AnnotationTypeRAHvo == m_hvoSegDefn) { if (HandleAnySubstantialSegmentChange(cba, segBreakBeginOffsetsBeforeEdit, segBreakBeginOffsetsAfterEdit, segmentsAfterEdit, ivMin, cvIns, cvDel, ref cbasToDel, ref segToMerge)) { continue; // skip default offset adjusting. it's already been handled. } } else if (cba.AnnotationTypeRAHvo == m_hvoPunctDefn) { AnnotationAdjuster.HandleAnySubstantialPunctuationChange(cba, wm, ivMin, cvIns, cvDel, ref cbasToDel); } else if (m_cache.IsValidObject(cba.Hvo, ScrScriptureNote.kClassId) && ScrScriptureNote.GetAnnotationType(cba.AnnotationTypeRA) == NoteType.CheckingError) { // We don't want to adjust for checking error annotations. These will get // cleaned up later if the check is run again (TE-8051) continue; } // 2.0 Handle any remaining nonsubstantial offset changes (simply adjust remaining offsets) // calculate the total change in annotation offsets after ivMin if (!cbasToDel.Contains(cba.Hvo)) { int cbaLengthOrig = cba.EndOffset - cba.BeginOffset; // special case: skip zerolengthed annotations at the beginning of the paragraph (e.g. ProcessTime) if (cba.BeginOffset == 0 && cba.EndOffset == 0) continue; AdjustOffsets(cba, cvIns, cvDel, ivMin, cba.AnnotationTypeRAHvo != m_hvoAnnDefnTwfic); if (cba.AnnotationTypeRAHvo == m_hvoAnnDefnTwfic && (cba.EndOffset - cba.BeginOffset) != cbaLengthOrig) { string msg = "We currently don't support changing the size of twfic."; Debug.Fail(msg); throw new ArgumentException(msg); } } } if (segToMerge != null) { // we didn't find an adjacent real segment to merge with, so // we need to adjust endoffset of segToMerge AdjustSegmentToNewSegmentBounds(segToMerge, segmentsAfterEdit); segToMerge = null; } // delete any cbas that are no longer valid. if (cbasToDel.Count > 0) { // TODO: possibly need to invalidate virtual properties (Segments/Segforms)? DeleteCalculatedAnnotations(cbasToDel); } }
/// <summary> /// find and delete affected real twfics (let the paragraph parser recompute virtual twfics?) /// 1.1) deletions & insertions that occur within or across twfic annotations. /// 1.1.1) delete affected twfic(s) /// 1.1.2) adjust offsets /// </summary> /// <param name="cba"></param> /// <param name="wm"></param> /// <param name="ichEditMin">the beginning offset of the selection used for editing.</param> /// <param name="cvIns"></param> /// <param name="cvDel"></param> /// <param name="twficsToDel"></param> private static void HandleAnySubstantialTwficChange(ICmBaseAnnotation cba, WordMaker wm, int ichEditMin, int cvIns, int cvDel, ref Set<int> twficsToDel) { int ichEditLim = ichEditMin + cvDel; // Determine whether this could possibly be a substantial change in a twfic if (ichEditMin < cba.EndOffset && ichEditLim > cba.BeginOffset) { // the selection used to edit the text overlaps this twfic. // 0123456789 // "sample sentence" // "sample ???tence" MarkCbaAndLinkedObjsForDeletion(cba, ref twficsToDel); return; } else if (cvIns > 0 && ichEditMin > cba.BeginOffset && ichEditMin < cba.EndOffset) { // user changed or broke up this twfic // 0123456789 // "sample sentence" // "sample sen?tence" MarkCbaAndLinkedObjsForDeletion(cba, ref twficsToDel); return; } else if (cvIns > 0 && ichEditMin == cba.BeginOffset) { // user has prepended characters. // delete twfic if insertion ends with a wordforming character. // 0123456789 // "sample sentence" // "sample ?sentence" // make sure the last character before this twfic // is a wordforming (non-whitespace) character before // we consider it to warrant deleting the twfic. if (wm.IsWordforming(ichEditMin + cvIns - 1)) { MarkCbaAndLinkedObjsForDeletion(cba, ref twficsToDel); return; } } else if (cvIns > 0 && ichEditMin == cba.EndOffset) { // user has appended characters. // if it begins with a wordbreaking character, don't delete the twfic // 0123456789 // "sample sentence" // "sample?xx sentence" // make sure the character after this twfic // is a wordforming (non-whitespace) character before // we consider it to warrant deleting the twfic. if (wm.IsWordforming(ichEditMin)) { MarkCbaAndLinkedObjsForDeletion(cba, ref twficsToDel); return; } } }
/// <summary> /// Scan the texts for pictures with captions. /// </summary> /// <param name="texts"></param> private void GetCaptionWfics(int[] texts, IWfiWordform wf) { int flidCaptions = DummyVirtualHandler.InstallDummyHandler(m_cache.VwCacheDaAccessor, "WfiWordform", "OccurrencesInCaptions", (int)CellarModuleDefns.kcptReferenceSequence).Tag; List<int> occurrencesInCaptions = new List<int>(); string wordform = wf.Form.VernacularDefaultWritingSystem; if (string.IsNullOrEmpty(wordform)) return; // paranoia. Set<FwObjDataTypes> desiredType = new Set<FwObjDataTypes>(1); desiredType.Add(FwObjDataTypes.kodtGuidMoveableObjDisp); int hvoAnnType = CmAnnotationDefn.Twfic(m_cache).Hvo; IVwCacheDa cda = m_cache.VwCacheDaAccessor; foreach (int hvoText in texts) { int chvoPara = m_cache.GetVectorSize(hvoText, kflidParagraphs); for (int ipara = 0; ipara < chvoPara; ipara++) { int hvoPara = m_cache.GetVectorItem(hvoText, kflidParagraphs, ipara); ITsString tssContents = m_cache.GetTsStringProperty(hvoPara, kflidContents); int crun = tssContents.RunCount; for (int irun = 0; irun < crun; irun++) { // See if the run is a picture ORC TsRunInfo tri; FwObjDataTypes odt; ITsTextProps props; Guid guid = StringUtils.GetGuidFromRun(tssContents, irun, out odt, out tri, out props, desiredType); if (guid == Guid.Empty) continue; // See if its caption contains our wordform int hvoPicture = m_cache.GetIdFromGuid(guid); int clsid = m_cache.GetClassOfObject(hvoPicture); if (clsid != (int)CmPicture.kclsidCmPicture) continue; // bizarre, just for defensiveness. ITsString tssCaption = m_cache.GetMultiStringAlt(hvoPicture, (int)CmPicture.CmPictureTags.kflidCaption, m_cache.DefaultVernWs); WordMaker wordMaker = new WordMaker(tssCaption, m_cache.LanguageWritingSystemFactoryAccessor); for (; ; ) { int ichMin; int ichLim; ITsString tssTxtWord = wordMaker.NextWord(out ichMin, out ichLim); if (tssTxtWord == null) break; if (tssTxtWord.Text != wordform) continue; int hvoAnn = CmBaseAnnotation.CreateDummyAnnotation(m_cache, hvoPicture, hvoAnnType, ichMin, ichLim, wf.Hvo); cda.CacheIntProp(hvoAnn, (int)CmBaseAnnotation.CmBaseAnnotationTags.kflidFlid, (int)CmPicture.CmPictureTags.kflidCaption); cda.CacheObjProp(hvoAnn, (int)CmBaseAnnotation.CmBaseAnnotationTags.kflidWritingSystem, m_cache.DefaultVernWs); occurrencesInCaptions.Add(hvoAnn); } } } } // Make the list the value of the occurrencesInCaptions property. cda.CacheVecProp(wf.Hvo, flidCaptions, occurrencesInCaptions.ToArray(), occurrencesInCaptions.Count); }
/// <summary> /// Executes in two distinct scenarios. /// /// 1. If disposing is true, the method has been called directly /// or indirectly by a user's code via the Dispose method. /// Both managed and unmanaged resources can be disposed. /// /// 2. If disposing is false, the method has been called by the /// runtime from inside the finalizer and you should not reference (access) /// other managed objects, as they already have been garbage collected. /// Only unmanaged resources can be disposed. /// </summary> /// <param name="disposing"></param> /// <remarks> /// If any exceptions are thrown, that is fine. /// If the method is being done in a finalizer, it will be ignored. /// If it is thrown by client code calling Dispose, /// it needs to be handled by fixing the bug. /// /// If subclasses override this method, they should call the base implementation. /// </remarks> protected virtual void Dispose(bool disposing) { //Debug.WriteLineIf(!disposing, "****************** " + GetType().Name + " 'disposing' is false. ******************"); // Must not be run more than once. if (m_isDisposed) return; if (disposing) { if (RebuildingConcordanceWordforms) RebuildingConcordanceWordforms = false; // don't get rid of m_wfi until we do this. } // Bad idea to do this here, since one can't access C# objects in this context, // which is what the property will try to do. //if (RebuildingConcordanceWordforms) // RebuildingConcordanceWordforms = false; // don't get rid of m_wfi until we do this. m_cadTwfic = null; m_cadPunct = null; m_punctAnnotations = null; m_wordforms = null; m_wordformAnnotationPossibilities = null; m_paragraphTextScanner = null; m_paraRealAnnIds = null; m_paraRealWfIds = null; m_paraRealTwfics = null; m_unusedPunctuationAnnotations = null; m_unusedTwficAnnotations = null; m_unusedSegmentAnnotations = null; m_realParagraphSegmentAnnotations = null; m_realParagraphPunctuationAnnotations = null; m_realParagraphWordforms = null; m_realParagraphTwficAnnotations = null; m_annotations = null; m_paraIdsParsed = null; m_wfi = null; m_lp = null; m_wordMaker = null; m_tssPara = null; m_para = null; m_hvosStText = null; m_hvosStTxtPara = null; m_cache = null; m_isDisposed = true; }
/// <summary> /// Returns the first word in the given tssWordAnn and its lower case form. /// </summary> /// <param name="tssWordAnn"></param> /// <param name="cpe"></param> /// <param name="firstFormLowered"></param> /// <returns>null if we couldn't find a word in the given tssWordAnn</returns> static private ITsString FirstWord(ITsString tssWordAnn, ILgWritingSystemFactory wsf, out string firstFormLowered) { WordMaker wordScanner = new WordMaker(tssWordAnn, wsf); int ichMinFirstWord; int ichLimFirstWord; ITsString firstWord = wordScanner.NextWord(out ichMinFirstWord, out ichLimFirstWord); // Handle null values without crashing. See LT-6309 for how this can happen. if (firstWord != null) firstFormLowered = wordScanner.ToLower(firstWord); else firstFormLowered = null; return firstWord; }
private void Setup(IStTxtPara para, IMatcher matcher, ConcordanceControl.ConcordanceLines line) { m_para = para; m_tssPara = para.Contents.UnderlyingTsString; m_paraWs = StringUtils.GetWsAtOffset(m_tssPara, 0); m_wordMaker = new WordMaker(m_tssPara, para.Cache.LanguageWritingSystemFactoryAccessor); m_paragraphTextScanner.Tss = m_tssPara; m_matcher = matcher; m_line = line; }
private void Init(FdoCache cache) { m_cache = cache; kflidOccurrences = WfiWordform.OccurrencesFlid(m_cache); kflidConcordanceWordforms = WordformInventory.ConcordanceWordformsFlid(m_cache); m_lp = Cache.LangProject; m_paragraphTextScanner = new WordMaker(null, cache.LanguageWritingSystemFactoryAccessor); m_wfi = m_lp.WordformInventoryOA; // Identify the tag used for the virtual property that is the form of a punctuation // annotation. m_tagPunct = CmBaseAnnotation.StringValuePropId(m_cache); m_cadPunct = CmAnnotationDefn.Punctuation(m_cache); m_cadTwfic = CmAnnotationDefn.Twfic(m_cache); m_tagRealForm = InterlinVc.TwficRealFormTag(cache); m_tagLowercaseForm = InterlinVc.MatchingLowercaseWordForm(cache); }