private void VerifyChangeColumn(NotifyChangeSpy spy, CmIndirectAnnotation row0, ICmAnnotation[] ccasToMove, int hvoColumn, string message) { foreach (ICmAnnotation cca in ccasToMove) Assert.AreEqual(hvoColumn, cca.InstanceOfRAHvo, message); spy.AssertHasNotification(row0.Hvo, (int)CmIndirectAnnotation.CmIndirectAnnotationTags.kflidAppliesTo, 0, 2, 2); }
private void VerifyChangeRow(NotifyChangeSpy spy, CmIndirectAnnotation rowSrc, int[] ccasToMove, CmIndirectAnnotation rowDst, int hvoOriginalColumn, string message, int ihvoDest) { int i = ihvoDest; foreach (int cca in ccasToMove) { Assert.AreEqual(cca, rowDst.AppliesToRS[ihvoDest].Hvo, message); Assert.IsFalse(rowSrc.AppliesToRS.Contains(cca)); ihvoDest++; } spy.AssertHasNotification(rowSrc.Hvo, (int)CmIndirectAnnotation.CmIndirectAnnotationTags.kflidAppliesTo); spy.AssertHasNotification(rowDst.Hvo, (int)CmIndirectAnnotation.CmIndirectAnnotationTags.kflidAppliesTo); }
private void VerifyInsertMarker(NotifyChangeSpy spy, CmIndirectAnnotation row0, ICmPossibility marker) { VerifyRow(0, "1a", 2); VerifyMarkerCca(0, 1, m_allColumns[1], marker.Hvo); }
// Make a column annotation for the specified column that groups the specified words // and append to the specified row. private CmIndirectAnnotation MakeColumnAnnotation(int icol, int[] words, CmIndirectAnnotation row) { return m_helper.MakeColumnAnnotation(icol, words, row); }
/// <summary> /// make one. /// </summary> public UndoSuppressCommentPrompt(TeStVc vc, CmIndirectAnnotation ft) { m_vc = vc; m_ft = ft; }
private void VerifyMoveAnnotationToSameRowLaterColBeforeMtm(int[] allParaWfics, NotifyChangeSpy spy, CmIndirectAnnotation cca0_1, int cExpectCSelect) { // Should have: added allParaWfics[1] to a new CCA in column 3 of row 0 (before moved text marker in col 4). VerifyRow(0, "1a", 3); VerifyCca(0, 0, m_allColumns[1], new int[] { allParaWfics[0] }, ""); VerifyCca(0, 1, m_allColumns[3], new int[] { allParaWfics[1] }, ""); VerifyCca(0, 2, m_allColumns[4], new int[] { cca0_1.Hvo }, ConstituentChartLogic.FTO_MovedTextBefore); Assert.AreEqual(cExpectCSelect, m_mockRibbon.CSelectFirstCalls); AssertUsedAnnotations(allParaWfics, 2); Assert.AreEqual(1, m_chart.RowsRS.Count, "should not add a row"); // Verify various PropChanged calls. VerifyAnnotationListChange(allParaWfics, spy, 2, 1); }
private bool DeleteFreeform(IVwSelection sel) { int hvoAnnotation; if (!CanDeleteFF(sel, out hvoAnnotation)) return false; int hvoSeg, tagFF, ihvoFF, cpropPrevious; IVwPropertyStore vps; // NOTE: Do not use ihvoFF for updating the cache directly, because the display vector indices // does not necessarily correspond to the cache vector indices. sel.PropInfo(false, 1, out hvoSeg, out tagFF, out ihvoFF, out cpropPrevious, out vps); CmBaseAnnotation seg = new CmBaseAnnotation(Cache, hvoSeg); CmIndirectAnnotation ft = new CmIndirectAnnotation(Cache, hvoAnnotation); bool wasFt = ft.AnnotationTypeRAHvo == Cache.GetIdFromGuid(LangProject.kguidAnnFreeTranslation); StTxtPara para = seg.BeginObjectRA as StTxtPara; m_fdoCache.DeleteObject(hvoAnnotation); m_fdoCache.PropChanged(null, PropChangeType.kpctNotifyAll, hvoSeg, tagFF, ihvoFF, 0, 1); if (wasFt && para != null) FreeTransEditMonitor.UpdateMainTransFromSegmented(para, Cache.LangProject.CurAnalysisWssRS.HvoArray); return true; // handled }
/// <summary> /// </summary> /// <param name="cbaSegTargetHvo"></param> /// <param name="cbaSegSrcHvo"></param> /// <param name="segmentFFCommentsBeforeSegBreakEdit_begin"></param> /// <param name="segmentFFCommentsBeforeSegBreakEdit_end"></param> /// <param name="segmentFFsAfterMerge"></param> private void ValidateMergedFreeformAnnotations(int cbaSegTargetHvo, int cbaSegSrcHvo, List<int> segmentFFCommentsBeforeMerge_target, List<int> segmentFFCommentsBeforeMerge_src, FdoObjectSet<ICmIndirectAnnotation> segmentFFsAfterMerge) { int segDefn_note = Cache.GetIdFromGuid(LangProject.kguidAnnNote); Dictionary<int, List<ICmIndirectAnnotation>> typeToAnnBeforeMergeBegin = AnnotationAdjuster.MakeAnnTypeToAnnDictionary(Cache, segmentFFCommentsBeforeMerge_target); Dictionary<int, List<ICmIndirectAnnotation>> typeToAnnAfterMerge = AnnotationAdjuster.MakeAnnTypeToAnnDictionary(segmentFFsAfterMerge); List<ICmIndirectAnnotation> srcNotes = new List<ICmIndirectAnnotation>(); foreach (int srcFreeformAnnHvo in segmentFFCommentsBeforeMerge_src) { ICmIndirectAnnotation srcFreeformAnn = new CmIndirectAnnotation(Cache, srcFreeformAnnHvo) as ICmIndirectAnnotation; ICmIndirectAnnotation realSrcFreeformAnn = new CmIndirectAnnotation(Cache, ffsClonedToReal[srcFreeformAnn.Hvo]); List<ICmIndirectAnnotation> targetAnns; List<ICmIndirectAnnotation> mergedAnns; Assert.IsTrue(typeToAnnAfterMerge.TryGetValue(srcFreeformAnn.AnnotationTypeRAHvo, out mergedAnns), "Expected src ff annotation to be in the merged set."); // see if we can find the annotation on the target segment matching the same type. // if so, we should have merged into its comment. if (srcFreeformAnn.AnnotationTypeRAHvo != segDefn_note && typeToAnnBeforeMergeBegin.TryGetValue(srcFreeformAnn.AnnotationTypeRAHvo, out targetAnns)) { Assert.IsFalse(realSrcFreeformAnn.IsRealObject, "Expected merged ff to have been deleted."); Assert.AreEqual(1, targetAnns.Count, "We only expect to have one annotation of this type."); int hvoTargetFF = targetAnns[0].Hvo; // see if we can find src comment in the same ws as the target. ICmIndirectAnnotation targetFreeformAnn = new CmIndirectAnnotation(Cache, hvoTargetFF) as ICmIndirectAnnotation; foreach (int wsTarget in Cache.LangProject.CurrentAnalysisAndVernWss) { // assume that the first ws-run indicates the ws of this multistring. ITsString tssTargetComment = targetFreeformAnn.Comment.GetAlternativeTss(wsTarget); ITsString tssSrcComment = srcFreeformAnn.Comment.GetAlternativeTss(wsTarget); string sTarget = ""; string sSrc = ""; if (tssTargetComment.Length > 0) { sTarget = tssTargetComment.Text + " "; } if (tssSrcComment.Length > 0) { sSrc = tssSrcComment.Text; } if (tssTargetComment.Text == null && tssSrcComment.Text == null) { Assert.AreEqual(null, typeToAnnAfterMerge[srcFreeformAnn.AnnotationTypeRAHvo][0].Comment.GetAlternativeTss(wsTarget).Text, "Expected annotation comments to be merged."); } else { Assert.AreEqual(sTarget + sSrc, typeToAnnAfterMerge[srcFreeformAnn.AnnotationTypeRAHvo][0].Comment.GetAlternativeTss(wsTarget).Text, "Expected annotation comments to be merged."); } } } else { Assert.IsTrue(mergedAnns.IndexOf(realSrcFreeformAnn) != -1); Assert.IsTrue(realSrcFreeformAnn.IsRealObject, "Expected merged ff to have been deleted."); if (srcFreeformAnn.AnnotationTypeRAHvo == segDefn_note) { srcNotes.Add(srcFreeformAnn); // find this Note in the merged list } foreach (int wsSrc in Cache.LangProject.CurrentAnalysisAndVernWss) { // assume that the first ws-run indicates the ws of this multistring. ITsString tssSrcComment = srcFreeformAnn.Comment.GetAlternativeTss(wsSrc); ITsString tssMovedComment = realSrcFreeformAnn.Comment.GetAlternativeTss(wsSrc); Assert.IsNotNull(tssMovedComment); Assert.AreEqual(tssSrcComment.Text, tssMovedComment.Text, "Expected annotation comments to be identical."); } } } // make sure all the merged ff annotations point to the target and none point to the old src segment // it's a note or not found on the target, just move the AppliesToRC item to the target. // make sure it's not pointing to the old segment. foreach (ICmIndirectAnnotation ciaMergedFF in segmentFFsAfterMerge) { Assert.IsFalse(ciaMergedFF.AppliesToRS.Contains(cbaSegSrcHvo), "Expected freeform annotation to no longer reference a merged annotation."); // make sure it's pointing to the new segment. Assert.IsTrue(ciaMergedFF.AppliesToRS.Contains(cbaSegTargetHvo), "Expected freeform annotation to reference target annotation."); } // make sure we have the expected number of notes. List<ICmIndirectAnnotation> mergedNotes; if (!typeToAnnAfterMerge.TryGetValue(segDefn_note, out mergedNotes)) mergedNotes = new List<ICmIndirectAnnotation>(); List<ICmIndirectAnnotation> targetNotes; if (!typeToAnnBeforeMergeBegin.TryGetValue(segDefn_note, out targetNotes)) targetNotes = new List<ICmIndirectAnnotation>(); Assert.AreEqual(targetNotes.Count + srcNotes.Count, mergedNotes.Count, "Expected the same number of freeform annotations."); }
private void AddSegment(StTxtPara para, int ichMin, int ichLim, string ft) { // Since this is an in-memory test, we can't use CmBaseAnnotation.CreateUnownedCba. CmBaseAnnotation seg = new CmBaseAnnotation(); Cache.LangProject.AnnotationsOC.Add(seg); seg.AnnotationTypeRAHvo = m_hvoSegAnnDefn; seg.BeginOffset = ichMin; seg.EndOffset = ichLim; seg.BeginObjectRA = para; seg.EndObjectRA = para; if (Cache.MainCacheAccessor.get_IsPropInCache(para.Hvo, kflidSegments, (int)CellarModuleDefns.kcptReferenceSequence, 0)) { int cseg = Cache.GetVectorSize(para.Hvo, kflidSegments); m_scrInMemoryCache.CacheAccessor.CacheReplace(para.Hvo, kflidSegments, cseg, cseg, new int[] {seg.Hvo}, 1); } else { // Don't want to just read it, because that actually parses the paragraph. If it's not already in memory // this is the first one, so just make it so. Cache.VwCacheDaAccessor.CacheVecProp(para.Hvo, kflidSegments, new int[] {seg.Hvo}, 1); } if (ft != null) { // Since this is an in-memory test, we can't use CmIndirectAnnotation.CreateUnownedIndirectAnnotation. CmIndirectAnnotation ftAnn = new CmIndirectAnnotation(); Cache.LangProject.AnnotationsOC.Add(ftAnn); ftAnn.AnnotationTypeRAHvo = m_hvoFtAnnDefn; ftAnn.AppliesToRS.Append(seg); ftAnn.Comment.SetAlternative(ft, Cache.DefaultAnalWs); m_scrInMemoryCache.CacheAccessor.CacheObjProp(seg.Hvo, kflidFT, ftAnn.Hvo); } }
/// <summary> /// Verify that the paragraph has the expected number of segments with the expected free translations. /// Lengths should be the lengths of the segments. /// </summary> private void VerifySegments(StTxtPara para, string[] freeTranslations, int[] lengths, string label) { int cseg = Cache.GetVectorSize(para.Hvo, kflidSegments); Assert.AreEqual(freeTranslations.Length, cseg); int i = 0; int cumLength = 0; foreach (string ft in freeTranslations) { int hvoSeg = Cache.GetVectorItem(para.Hvo, kflidSegments, i); CmBaseAnnotation seg = new CmBaseAnnotation(Cache, hvoSeg); Assert.AreEqual(cumLength, seg.BeginOffset, label + " - beginOffset " + i); cumLength += lengths[i]; Assert.AreEqual(cumLength, seg.EndOffset, label + " - endOffset " + i); Assert.AreEqual(m_hvoSegAnnDefn, seg.AnnotationTypeRAHvo, label + " - seg ann type " + i); if (ft != null) { int hvoFt = Cache.GetObjProperty(hvoSeg, kflidFT); Assert.IsTrue(hvoFt != 0, label + "ft present " + i); CmIndirectAnnotation ftAnn = new CmIndirectAnnotation(Cache, hvoFt); Assert.AreEqual(ft, ftAnn.Comment.AnalysisDefaultWritingSystem.Text, label + " - comment " + i); Assert.AreEqual(m_hvoFtAnnDefn, ftAnn.AnnotationTypeRAHvo, label + " - ft type " + i); Assert.AreEqual(1, ftAnn.AppliesToRS.Count, label + " - appliesTo length " + i); Assert.AreEqual(seg.Hvo, ftAnn.AppliesToRS.HvoArray[0], label + " - applies to val " + i); } i++; } }
/// <summary> /// Copy the free translations from the source to the destination. This is designed to be used on the /// output of CopyObject. (It will not remove or merge any existing Segments or FTs, and assumes /// the structure of the two books matches exactly.) /// Enhance JohnT: we could fairly easily copy literal translations, notes, Wfics as well. /// 1. sql1 should allow the additional annotation type(s). /// 2. for Wfic, sql1 should retrieve instanceOf /// 3. for Wfic, Code should copy InstanceOf as well as other stuff to new annotations. /// 4. For literal translations and notes, sql2 and 3 need to include additional annotation types. /// For Wfics, a complication is that the instanceOf target for a saved version Wfic may possibly /// not be guaranteed to be preserved. We should probably not copy any Wfic which has no instanceOf. /// </summary> /// <param name="source"></param> /// <param name="dest"></param> void CopyFreeTranslations(IScrBook source, IScrBook dest) { FdoCache cache = source.Cache; int segmentDefn = cache.GetIdFromGuid(LangProject.kguidAnnTextSegment); int ftSegmentDefn = cache.GetIdFromGuid(LangProject.kguidAnnFreeTranslation); if (segmentDefn == 0 || ftSegmentDefn == 0) return; // Running tests without enough structure to do this. // Build a dictionary mapping corresponding paragraphs in the original and copy. Dictionary<int, int> paraMap = new Dictionary<int, int>(); for (int isec = 0; isec < source.SectionsOS.Count; isec++) { IScrSection sourceSec = source.SectionsOS[isec]; IScrSection destSec = dest.SectionsOS[isec]; MapText(sourceSec.ContentOA, destSec.ContentOA, paraMap); MapText(sourceSec.HeadingOA, destSec.HeadingOA, paraMap); } for (int ifn = 0; ifn < source.FootnotesOS.Count; ++ifn) { IStFootnote sourceFn = source.FootnotesOS[ifn]; IStFootnote destFn = dest.FootnotesOS[ifn]; MapText(sourceFn, destFn, paraMap); } MapText(source.TitleOA, dest.TitleOA, paraMap); // For every segment pointing to a title or footnote paragraph that matches, the query returns // 2 * the number of sections identical results. Thus, DISTINCT is needed to create the proper // set of copies of the segments. string sql1 = @"select DISTINCT seg.id, seg.BeginOffset, seg.EndOffset, seg.BeginObject, seg.WritingSystem from ScrBook book join CmObject sec on sec.Owner$ = book.id join CmObject txt_fn on txt_fn.Owner$ = sec.id or txt_fn.Owner$ = book.id join CmObject para on para.Owner$ = txt_fn.id join CmBaseAnnotation_ seg on seg.BeginObject = para.id and seg.AnnotationType = " + segmentDefn + @" where book.id = " + source.Hvo; List<int[]> segInfo = DbOps.ReadIntArray(cache, sql1, null, 5); // Map the original segment HVOs onto the copies. Dictionary<int, int> segMap = new Dictionary<int, int>(segInfo.Count); Logger.WriteEvent("Creating base annotations"); foreach (int[] info in segInfo) { int hvoPara = paraMap[info[3]]; // REVIEW (SteveMiller): Don't we want the CmBaseAnnotation.Flid, too? string sql4 = @"DECLARE @id INT; INSERT INTO CmObject (Class$) VALUES (37); SET @Id = @@IDENTITY; INSERT INTO CmAnnotation (Id, AnnotationType) VALUES (@Id, " + segmentDefn + @"); INSERT INTO CmBaseAnnotation (Id, BeginOffset, EndOffset, BeginObject, EndObject, WritingSystem) VALUES (@Id, " + info[1] + @", " + info[2] + @", " + hvoPara + @", " + hvoPara + @", " + (info[4] == 0 ? "null" : info[4].ToString()) + @"); SELECT @id;"; int hvo; if (DbOps.ReadOneIntFromCommand(cache, sql4, null, out hvo)) segMap[info[0]] = hvo; } /* * JT: "In revision 12, you moved the actual copy loop into SQL. Unfortunately, in the * process, the new code simply copies the BeginObject from the old segment to the new. * It is supposed to point the new segment to the corresponding paragraph in * the copy, as looked up in paraMap. The result is to duplicate the segments on the * old book's paragraphs instead of copying them to the new. string sql4 = @"SET TRANSACTION ISOLATION LEVEL SERIALIZABLE -- so we're the only ones inserting DECLARE @Id INT, @BeginOffset INT, @Flid INT, @EndOffset INT, @BeginObject INT, @EndObject INT, @WritingSystem INT, @ClassId INT; SELECT @ClassId = Id FROM Class$ WHERE Name = 'CmBaseAnnotation'; DECLARE curBA CURSOR LOCAL STATIC FORWARD_ONLY READ_ONLY FOR select DISTINCT seg.BeginOffset, seg.Flid, seg.EndOffset, seg.BeginObject, seg.EndObject, seg.WritingSystem from ScrBook book join CmObject sec on sec.Owner$ = book.id join CmObject txt_fn on txt_fn.Owner$ = sec.id or txt_fn.Owner$ = book.id join CmObject para on para.Owner$ = txt_fn.id join CmBaseAnnotation_ seg on seg.BeginObject = para.id and seg.AnnotationType = " + segmentDefn + @" where book.id = " + source.Hvo + @" OPEN curBA; FETCH curBA INTO @BeginOffset, @Flid, @EndOffset, @BeginObject, @EndObject, @WritingSystem; WHILE @@FETCH_STATUS = 0 BEGIN INSERT INTO CmObject (Class$) VALUES (@ClassId); SET @Id = @@IDENTITY; INSERT INTO CmAnnotation (Id, AnnotationType) VALUES (@Id, " + segmentDefn + @"); INSERT INTO CmBaseAnnotation (Id, BeginOffset, Flid, EndOffset, BeginObject, EndObject, WritingSystem) VALUES (@Id, @BeginOffset, @Flid, @EndOffset, @BeginObject, @EndObject, @WritingSystem); FETCH curBA INTO @BeginOffset, @Flid, @EndOffset, @BeginObject, @EndObject, @WritingSystem; END CLOSE curBA; DEALLOCATE curBA; SET TRANSACTION ISOLATION LEVEL READ COMMITTED"; DbOps.ExecuteStatementNoResults(cache, sql4, null); */ Logger.WriteEvent("Getting WS in use in BT's"); // Get a list of all the writing systems we care about (all the ones that have any data for the interesting FT annotations). string sql2 = @"select DISTINCT com.ws from ScrBook book join CmObject sec on sec.Owner$ = book.id join CmObject txt_fn on txt_fn.Owner$ = sec.id or txt_fn.Owner$ = book.id join CmObject para on para.Owner$ = txt_fn.id join CmBaseAnnotation_ seg on seg.BeginObject = para.id and seg.AnnotationType = " + segmentDefn + @" join CmIndirectAnnotation_AppliesTo ft_at on ft_at.dst = seg.id join CmIndirectAnnotation_ ft on ft.id = ft_at.src and ft.AnnotationType = " + ftSegmentDefn + @" join CmAnnotation_Comment com on ft.id = com.obj where book.id = " + source.Hvo + @" group by com.ws"; int[] wss = DbOps.ReadIntArrayFromCommand(cache, sql2, null); const int kflidComment = (int)CmAnnotation.CmAnnotationTags.kflidComment; Dictionary<int, int> ftMap = new Dictionary<int, int>(); foreach (int ws in wss) { Logger.WriteEvent("Starting to copy for WS = " + ws); // For performance, and to populate the free translation virtual property, preload all the // free translations of the source text. // DISTINCT would be useful here to minimize db access traffic and duplicated insertions // into the cache, but isn't allowed because com.txt is NTEXT and com.fmt is IMAGE. // An alternative would be to split it into two queries, one for the sections and the // other for the title and footnotes, but then we'd have two round trips to the database, // which might be slower overall. (UNION won't work to combine the two queries because it // implies DISTINCT!) // The duplicate results don't cause errors here, because they're all written to the cache, // which replaces one copy with another without complaining. string sql3 = @"select seg.id, ft.id, com.txt, com.fmt from ScrBook book join CmObject sec on sec.Owner$ = book.id join CmObject txt_fn on txt_fn.Owner$ = sec.id or txt_fn.Owner$ = book.id join CmObject para on para.Owner$ = txt_fn.id join CmBaseAnnotation_ seg on seg.BeginObject = para.id and seg.AnnotationType = " + segmentDefn + @" join CmIndirectAnnotation_AppliesTo ft_at on ft_at.dst = seg.id join CmIndirectAnnotation_ ft on ft.id = ft_at.src and ft.AnnotationType = " + ftSegmentDefn + @" left outer join CmAnnotation_Comment com on ft.id = com.obj and com.ws = " + ws + @" where book.id = " + source.Hvo; int kflidFT = StTxtPara.SegmentFreeTranslationFlid(cache); IDbColSpec dcs = DbColSpecClass.Create(); dcs.Push((int)DbColType.koctBaseId, 0, 0, 0); // segment ID column is base. dcs.Push((int)DbColType.koctObj, 1, kflidFT, 0); // ft annotation is property kflidFT of the segment dcs.Push((int)DbColType.koctMlsAlt, 2, kflidComment, ws); // then comes the text of the comment dcs.Push((int)DbColType.koctFmt, 2, kflidComment, ws); // and the string format information of the comment. Logger.WriteEvent("Loading annotations for WS = " + ws); cache.VwOleDbDaAccessor.Load(sql3, dcs, 0, 0, null, false); // Now we've preloaded the old FTs and comments, copy to new. Logger.WriteEvent("Processing annotations for WS = " + ws); foreach (KeyValuePair<int, int> pair in segMap) { int ftSrc = cache.GetObjProperty(pair.Key, kflidFT); if (ftSrc == 0) continue; // label segment int ftDst = m_cache.GetObjProperty(pair.Value, kflidFT); ICmIndirectAnnotation ft; if (ftDst == 0) { ft = CmIndirectAnnotation.CreateUnownedIndirectAnnotation(cache); ft.AppliesToRS.Append(pair.Value); ft.AnnotationTypeRAHvo = ftSegmentDefn; Cache.VwCacheDaAccessor.CacheObjProp(pair.Value, kflidFT, ft.Hvo); } else { // Already exists: presumably from a different iteration with a different WS. ft = new CmIndirectAnnotation(m_cache, ftDst); } cache.SetMultiStringAlt(ft.Hvo, kflidComment, ws, cache.GetMultiStringAlt(ftSrc, kflidComment, ws)); ftMap[ftSrc] = ft.Hvo; } Logger.WriteEvent("Copying complete for WS = " + ws); } // Update any ScrScriptureNote with old FT hvo in its BeginObject or EndObject, or an // old para hvo in its BeginObject or EndObject. Logger.WriteEvent("Updating Scripture Annotation Refereneces"); foreach (IScrScriptureNote note in this.BookAnnotationsOS[dest.CanonicalNum - 1].NotesOS) { if (ftMap.ContainsKey(note.BeginObjectRAHvo)) note.BeginObjectRAHvo = ftMap[note.BeginObjectRAHvo]; if (ftMap.ContainsKey(note.EndObjectRAHvo)) note.EndObjectRAHvo = ftMap[note.EndObjectRAHvo]; if (paraMap.ContainsKey(note.BeginObjectRAHvo)) note.BeginObjectRAHvo = paraMap[note.BeginObjectRAHvo]; if (paraMap.ContainsKey(note.EndObjectRAHvo)) note.EndObjectRAHvo = paraMap[note.EndObjectRAHvo]; } Logger.WriteEvent("CopyFreeTranslations complete"); }
/// ----------------------------------------------------------------------------------- /// <summary> /// Create a segmented back translation for this paragraph. /// </summary> /// <param name="para"></param> /// <param name="segmentBTsForPara"></param> /// ----------------------------------------------------------------------------------- private void CreateSegmentedBackTranslation(IStTxtPara para, List<BTSegment> segmentBTsForPara) { int[] paraSegs; int wsBt = m_cache.DefaultAnalWs; // this only affects caching... ICmBaseAnnotation[] segments = BtConverter.GetMainParaSegments(para, wsBt, out paraSegs); Debug.Assert(segmentBTsForPara.Count == segments.Length); int kflidFT = StTxtPara.SegmentFreeTranslationFlid(m_cache); for (int i = 0; i < segmentBTsForPara.Count; ++i) { BTSegment bts = segmentBTsForPara[i]; // dummy segments have a comment type "back translation" for ws = 0. if (bts.GetBackTransForWs(0) != null) continue; int hvoFT = m_cache.GetObjProperty(segments[i].Hvo, kflidFT); ICmIndirectAnnotation ft = new CmIndirectAnnotation(m_cache, hvoFT); foreach (int ws in bts.AvailableBackTranslations) ft.Comment.SetAlternative(bts.GetBackTransForWs(ws), ws); foreach (IScrScriptureNote note in bts.Annotations) { note.BeginObjectRAHvo = ft.Hvo; note.EndObjectRAHvo = ft.Hvo; } } }
/// ----------------------------------------------------------------------------------- /// <summary> /// Replace the user prompt with the text the user typed. This method is called from /// the views code when the user prompt is edited. /// </summary> /// <param name="vwsel">Current selection in rootbox where this prop was updated</param> /// <param name="hvo">Hvo of the paragraph</param> /// <param name="tag">Tag</param> /// <param name="frag">Fragment</param> /// <param name="tssVal">Text the user just typed</param> /// <returns>possibly modified ITsString.</returns> /// <remarks>The return value is currently ignored in production code, but we use it /// in our tests.</remarks> /// ----------------------------------------------------------------------------------- public override ITsString UpdateProp(IVwSelection vwsel, int hvo, int tag, int frag, ITsString tssVal) { CheckDisposed(); Debug.Assert(tag == SimpleRootSite.kTagUserPrompt, "Got an unexpected tag"); Debug.Assert(vwsel != null, "Got a null selection!"); Debug.Assert(vwsel.IsValid, "Got an invalid selection!"); IVwRootBox rootbox = vwsel.RootBox; // If a (typically Chinese) character composition is in progress, replacing the prompt will // destroy the selection and end the composition, causing weird typing problems (TE-8267). // Ending the composition does another Commit, which ensures that this will eventually be // called when there is NOT a composition in progress. if (rootbox.IsCompositionInProgress) return tssVal; // Remove the UserPrompt pseudo-property from the text the user typed. // when appropriate also ensure the correct writing system. // The correct WS is m_wsDefault in the view constructor ITsStrBldr bldr = (ITsStrBldr)tssVal.GetBldr(); if (frag != (int)CmAnnotation.CmAnnotationTags.kflidComment) { bldr.SetIntPropValues(0, bldr.Length, (int)FwTextPropType.ktptWs, (int)FwTextPropVar.ktpvDefault, m_wsDefault); } // Delete the user prompt property from the string (TE-3994) bldr.SetIntPropValues(0, bldr.Length, SimpleRootSite.ktptUserPrompt, -1, -1); tssVal = bldr.GetString(); // Get information about current selection int cvsli = vwsel.CLevels(false); cvsli--; // CLevels includes the string property itself, but AllTextSelInfo doesn't need it. int ihvoRoot; int tagTextProp; int cpropPrevious; int ichAnchor; int ichEnd; int ihvoEnd; bool fAssocPrev; int ws; ITsTextProps ttp; SelLevInfo[] rgvsli = SelLevInfo.AllTextSelInfo(vwsel, cvsli, out ihvoRoot, out tagTextProp, out cpropPrevious, out ichAnchor, out ichEnd, out ws, out fAssocPrev, out ihvoEnd, out ttp); // Prior to the Commit in selection changed which causes this UpdateProp to be called, // earlier selection changed code has expanded the selection (because it is in a user prompt) // to the whole prompt. It is therefore a range selection, and the value of fAssocPrev we got // is useless. We want to make a selection associated with the previous character at the END of // the range. if (frag == (int)CmAnnotation.CmAnnotationTags.kflidComment) { // If the length is zero...we need to suppress replacing the comment with a prompt. if (tssVal.Length == 0) m_hvoSuppressCommentPrompt = hvo; CmIndirectAnnotation ann = new CmIndirectAnnotation(Cache, hvo); if (ann.Comment.GetAlternative(BackTranslationWS).Length == 0) { // Undo needs to unset suppressing the comment prompt. Cache.ActionHandlerAccessor.AddAction(new UndoSuppressCommentPrompt(this, ann)); } // Turn the prompt property off for future typing, too. ITsPropsBldr pb = ttp.GetBldr(); pb.SetIntPropValues(SimpleRootSite.ktptUserPrompt, -1, -1); ann.Comment.SetAlternative(tssVal, BackTranslationWS); rootbox.MakeTextSelection(ihvoRoot, cvsli, rgvsli, (int)CmAnnotation.CmAnnotationTags.kflidComment, cpropPrevious, ichEnd, ichEnd, BackTranslationWS, fAssocPrev, ihvoEnd, pb.GetTextProps(), true); return tssVal; } ReplacePromptUndoAction undoAction = new ReplacePromptUndoAction(hvo, m_cache, m_updatedPrompts); if (m_cache.ActionHandlerAccessor != null) m_cache.ActionHandlerAccessor.AddAction(undoAction); // Mark the user prompt as having been updated - will not show prompt again. // Note: ReplacePromptUndoAction:Undo removes items from the Set. m_updatedPrompts.Add(hvo); // Replace the ITsString in the paragraph or translation // - this destroys the selection because we replace the user prompt. StTxtPara para; CmTranslation trans; ITsTextProps props = StyleUtils.CharStyleTextProps(null, m_wsDefault); if (frag == (int)CmTranslation.CmTranslationTags.kflidTranslation) { trans = new CmTranslation(Cache, hvo); trans.Translation.GetAlternative(m_wsDefault).UnderlyingTsString = tssVal; undoAction.ParaHvo = trans.OwnerHVO; // now set the selection to the end of the text that was just put in. ichAnchor = ichEnd; rootbox.MakeTextSelection(ihvoRoot, cvsli, rgvsli, (int)CmTranslation.CmTranslationTags.kflidTranslation, cpropPrevious, ichAnchor, ichEnd, m_wsDefault, true, ihvoEnd, props, true); } else { para = new StTxtPara(Cache, hvo); para.Contents.UnderlyingTsString = tssVal; undoAction.ParaHvo = hvo; // now set the selection to the end of the text that was just put in. ichAnchor = ichEnd; rootbox.MakeTextSelection(ihvoRoot, cvsli, rgvsli, (int)StTxtPara.StTxtParaTags.kflidContents, cpropPrevious, ichAnchor, ichEnd, m_wsDefault, true, ihvoEnd, props, true); } return tssVal; }
private CmIndirectAnnotation MakeMovedTextAnnotation(int icol, CmIndirectAnnotation target, CmIndirectAnnotation row, string marker) { return m_helper.MakeMovedTextAnnotation(icol, target, row, marker); }
internal void CallMakeMovedFrom(int icolActual, int icolMovedFrom, CmIndirectAnnotation rowActual, CmIndirectAnnotation rowMovedFrom, int[] wficsToMove) { MakeMovedFrom(new ChartLocation(icolActual, rowActual), new ChartLocation(icolMovedFrom, rowMovedFrom), wficsToMove); }
private void VerifyDeleteCcas(NotifyChangeSpy spy, CmIndirectAnnotation row0) { VerifyRow(0, "1a", 0); spy.AssertHasNotification(row0.Hvo, (int)CmIndirectAnnotation.CmIndirectAnnotationTags.kflidAppliesTo, 0, 0, 1); }
private void VerifyMoveSecondAnnotationToSameCol(int[] allParaWfics, CmIndirectAnnotation cca0_1, NotifyChangeSpy spy, int cExpectCSelect) { // Should have: added allParaWfics[1] to cca0_1 (and not have added rows or CCAs) VerifyRow(0, "1a", 1); // still 1 CCA, shouldn't make a new one. VerifyCca(0, 0, m_allColumns[1], new int[] { allParaWfics[0], allParaWfics[1] }, ""); Assert.AreEqual(cExpectCSelect, m_mockRibbon.CSelectFirstCalls); AssertUsedAnnotations(allParaWfics, 2); Assert.AreEqual(1, m_chart.RowsRS.Count, "should not add a row"); // Verify various PropChanged calls. VerifyAnnotationListChange(allParaWfics, spy, 2, 1); }
/// <summary> /// Verify that FindFirstCcaWithWfics() finds the given 'testCca' as the first CCA in 'list' /// </summary> /// <param name="testCca"></param> /// <param name="list"></param> /// <param name="message"></param> private void VerifyFirstCca(CmIndirectAnnotation testCca, List<ICmAnnotation> list, string message) { ICmIndirectAnnotation cca = m_logic.CallFindFirstCcaWithWfics(list); Assert.IsNotNull(cca, message); Assert.AreEqual(testCca.Hvo, cca.Hvo, message); }
internal CmIndirectAnnotation MakeMovedTextAnnotation(int icol, CmIndirectAnnotation target, CmIndirectAnnotation rowAnn, string marker) { CmIndirectAnnotation cca = MakeIndirectAnnotation(); if (target != null) cca.AppliesToRS.Append(target); InitializeCca(cca, icol, rowAnn); cca.Comment.AnalysisDefaultWritingSystem.Text = marker; return cca; }