private void SetReferencesForReferenceFlid(int thisFlid, int flidType, int hvoSrc, int hvoCopy) { switch (flidType) { case (int)CellarPropertyType.ReferenceAtomic: int hvoAtomic = m_sda.get_ObjectProp(hvoSrc, thisFlid); if (hvoAtomic > 0) { // If we find the object referred to by the RA property in our copy map, // put a reference to its copy in our copied object. Otherwise, use the same // reference as our source object. ICmObject copiedAtomic; if (m_sourceToCopyMap.TryGetValue(hvoAtomic, out copiedAtomic)) { m_sda.SetObjProp(hvoCopy, thisFlid, copiedAtomic.Hvo); } else { m_sda.SetObjProp(hvoCopy, thisFlid, hvoAtomic); } } break; case (int)CellarPropertyType.ReferenceCollection: case (int)CellarPropertyType.ReferenceSequence: // Handle Reference Vectors int cVec = m_sda.get_VecSize(hvoSrc, thisFlid); for (int i = 0; i < cVec; i++) { int hvoVecItem = m_sda.get_VecItem(hvoSrc, thisFlid, i); ICmObject copiedVecItem; if (m_sourceToCopyMap.TryGetValue(hvoVecItem, out copiedVecItem)) { m_sda.Replace(hvoCopy, thisFlid, i, i, new[] { copiedVecItem.Hvo }, 1); } else { m_sda.Replace(hvoCopy, thisFlid, i, i, new[] { hvoVecItem }, 1); } } break; default: throw new ArgumentException("Non-reference Field in wrong method!", "flidType"); } }
public void ObjectProp() { CheckDisposed(); int hvo = m_ISilDataAccess.get_ObjectProp(1000, 2000); Assert.AreEqual(0, hvo); Assert.IsFalse(m_ISilDataAccess.IsDirty()); m_ISilDataAccess.SetObjProp(1000, 2000, 7777); hvo = m_ISilDataAccess.get_ObjectProp(1000, 2000); Assert.AreEqual(7777, hvo); Assert.IsTrue(m_ISilDataAccess.IsDirty()); m_ISilDataAccess.SetObjProp(1000, 2000, 8888); hvo = m_ISilDataAccess.get_ObjectProp(1000, 2000); Assert.AreEqual(8888, hvo); Assert.IsTrue(m_ISilDataAccess.IsDirty()); CheckProp(1000, 2000, 8888, CellarModuleDefns.kcptOwningAtom); }
/// <summary> /// /// </summary> /// <param name="sda"></param> /// <param name="hvoSel"></param> /// <param name="hvoItem"></param> /// <param name="hvoOld"></param> protected virtual void UpdateListItemToNewValue(ISilDataAccess sda, int hvoItem, int hvoSel, int hvoOld) { int hvoOwningAtomic = hvoItem; if (hvoOld == 0 && m_ghostParentHelper != null) { // it's possible that hvoItem is actually the owner of a object that needs to be created // for hvoSel to be set. hvoOwningAtomic = m_ghostParentHelper.FindOrCreateOwnerOfTargetProp(hvoItem, m_flidAtomicProp); } sda.SetObjProp(hvoOwningAtomic, m_flidAtomicProp, hvoSel); }
/// <summary> /// /// </summary> /// <param name="hvo"></param> /// <param name="tag"></param> /// <param name="hvoObj"></param> public virtual void SetObjProp(int hvo, int tag, int hvoObj) { VerifyUpdate(hvo, tag); m_sda.SetObjProp(hvo, tag, hvoObj); }
int MakeRealObject(ITsString tssTyped) { // Figure whether owning atomic or owning collection or owning sequence. Throw if none. // Unless we're making an unowned IText for a Notebook Record. ISilDataAccess sdaReal = m_fdoCache.DomainDataByFlid; IFwMetaDataCache mdc = sdaReal.MetaDataCache; CellarPropertyType typeOwning = (CellarPropertyType)(mdc.GetFieldType(m_flidEmptyProp) & (int)CellarPropertyTypeFilter.VirtualMask); // Make a new object of the specified class in the specified property. int ord = 0; switch (typeOwning) { default: if (m_flidEmptyProp != RnGenericRecTags.kflidText) { throw new Exception("ghost string property must be owning object property"); } break; case CellarPropertyType.OwningAtomic: ord = -2; break; case CellarPropertyType.OwningCollection: ord = -1; break; case CellarPropertyType.OwningSequence: // ord = 0 set above (inserting the first and only object at position 0). break; } string sClassRaw = mdc.GetClassName(m_clidDst); string sClass = m_mediator.StringTbl.GetString(sClassRaw, "ClassNames"); string sUndo = String.Format(DetailControlsStrings.ksUndoCreate0, sClass); string sRedo = String.Format(DetailControlsStrings.ksRedoCreate0, sClass); int hvoNewObj = 0; int hvoStringObj = 0; UndoableUnitOfWorkHelper.DoUsingNewOrCurrentUOW(sUndo, sRedo, m_fdoCache.ServiceLocator.GetInstance <IActionHandler>(), () => { // Special case: if we just created a Text in RnGenericRecord, and we want to show the contents // of an StTxtPara, make the intermediate objects if (m_flidEmptyProp == RnGenericRecTags.kflidText) { var servLoc = Cache.ServiceLocator; var text = servLoc.GetInstance <ITextFactory>().Create(); var stText = servLoc.GetInstance <IStTextFactory>().Create(); text.ContentsOA = stText; var para = servLoc.GetInstance <IStTxtParaFactory>().Create(); stText.ParagraphsOS.Add(para); hvoNewObj = text.Hvo; hvoStringObj = para.Hvo; // Set the RnGenericRec's Text property to reference the new text sdaReal.SetObjProp(m_hvoObj, m_flidEmptyProp, hvoNewObj); } else { hvoNewObj = hvoStringObj = sdaReal.MakeNewObject(m_clidDst, m_hvoObj, m_flidEmptyProp, ord); } // Set its property m_flidStringProp to tssTyped. If it is multilingual, choose based on ghostWs. var typeString = (CellarPropertyType)(mdc.GetFieldType(m_flidStringProp) & (int)CellarPropertyTypeFilter.VirtualMask); switch (typeString) { default: throw new Exception("ghost property must store strings!"); case CellarPropertyType.MultiString: case CellarPropertyType.MultiUnicode: sdaReal.SetMultiStringAlt(hvoStringObj, m_flidStringProp, m_wsToCreate, tssTyped); break; case CellarPropertyType.String: sdaReal.SetString(hvoStringObj, m_flidStringProp, tssTyped); break; } string ghostInitMethod = XmlUtils.GetOptionalAttributeValue(m_nodeObjProp, "ghostInitMethod"); if (ghostInitMethod != null) { var obj = m_fdoCache.ServiceLocator.GetInstance <ICmObjectRepository>().GetObject(hvoNewObj); Type objType = obj.GetType(); System.Reflection.MethodInfo mi = objType.GetMethod(ghostInitMethod); mi.Invoke(obj, null); } }); return(hvoNewObj); }
/// <summary> /// This is the real guts of type-ahead. It is called by the client whenever a key is pressed. /// It returns true if it handled the key press, which it does if the current selection /// is in a type-ahead name property. /// </summary> /// <param name="ehelp"></param> /// <param name="e"></param> /// <param name="modifiers"></param> /// <param name="vwGraphics"></param> /// <returns></returns> public virtual bool OnKeyPress(EditingHelper ehelp, KeyPressEventArgs e, Keys modifiers, IVwGraphics vwGraphics) { IVwRootBox rootb = ehelp.Callbacks.EditedRootBox; if (rootb == null) // If we don't have a root box, can't do anything interesting. { return(false); } IVwSelection sel = rootb.Selection; if (sel == null) // nothing interesting to do without a selection, either. { return(false); } ITsString tssA, tssE; int ichA, ichE, hvoObjA, hvoObjE, tagA, tagE, ws; bool fAssocPrev; // Enhance JohnT: what we're really trying to do here is confirm that the selection is // all in one string property. We could readily have a method in the selection interface to tell us that. sel.TextSelInfo(false, out tssA, out ichA, out fAssocPrev, out hvoObjA, out tagA, out ws); if (tagA != m_taTagName) { return(false); // selection not anchored in a type-ahead name property. } sel.TextSelInfo(true, out tssE, out ichE, out fAssocPrev, out hvoObjE, out tagE, out ws); int cch = tssA.Length; // To do our type-ahead trick, both ends of the seleciton must be in the same string property. // Also, we want the selection to extend to the end of the name. // Enhance JohnT: poupu list may not depend on selection extending to end. if (tagE != m_taTagName || hvoObjE != hvoObjA || cch != tssE.Length || Math.Max(ichA, ichE) != cch) { return(false); // not going to attempt type-ahead behavior } // if the key pressed is a backspace or del, prevent smart completion, // otherwise we are likely to put back what the user deleted. // Review JohnT: do arrow keys come through here? What do we do if so? int charT = Convert.ToInt32(e.KeyChar); if (charT == (int)Keys.Back || charT == (int)Keys.Delete) { return(false); // normal key handling will just delete selection. // Review: should backspace delete one more? } // OK, we're in a type-ahead situation. First step is to let normal editing take place. ehelp.OnKeyPress(e, modifiers); e.Handled = true; // Now see what we have. Note that our old selection is no longer valid. sel = rootb.Selection; if (sel == null) { return(true); // can't be smart, but we already did the keypress. } int cvsli = sel.CLevels(false); // CLevels includes the string prop itself, but AllTextSelInfo does not need it. cvsli--; // Get selection information to determine where the user is typing. int ihvoObj; int tagTextProp; int cpropPrevious, ichAnchor, ichEnd, ihvoEnd; ITsTextProps ttp; SelLevInfo[] rgvsli = SelLevInfo.AllTextSelInfo(sel, cvsli, out ihvoObj, out tagTextProp, out cpropPrevious, out ichAnchor, out ichEnd, out ws, out fAssocPrev, out ihvoEnd, out ttp); if (tagTextProp != m_taTagName || ichAnchor != ichEnd || ihvoEnd != -1 || cvsli < 1) { return(true); // something bizarre happened, but keypress is done. } int hvoLeaf = rgvsli[0].hvo; // Get the parent object we will modify. // (This would usually work, but not if the parent object is the root of the whole display, // as in a simple atomic ref type ahead slice. //int hvoParent = rgvsli[1].hvo; // object whose reference property we are setting.) int tagParent, cpropPreviousDummy, ihvo; IVwPropertyStore vps; int hvoParent; sel.PropInfo(false, 1, out hvoParent, out tagParent, out ihvo, out cpropPreviousDummy, out vps); if (hvoParent != m_hvoParent) { return(true); // another bizarre unexpected event. } // This is what the name looks like after the keypress. ITsString tssTyped = m_sda.get_StringProp(hvoLeaf, m_taTagName); // Get the substitute. This is where the actual type-ahead behavior happens. Sets hvoNewRef to 0 if no match. ICmObject objNewRef; ITsString tssLookup = Lookup(tssTyped, out objNewRef); int hvoNewRef = (objNewRef != null) ? objNewRef.Hvo : 0; IVwCacheDa cda = m_sda as IVwCacheDa; if (hvoNewRef == 0 && tssTyped.Length > 0) { // No match...underline string in red squiggle. ITsStrBldr bldr = tssLookup.GetBldr(); bldr.SetIntPropValues(0, tssLookup.Length, (int)FwTextPropType.ktptUnderline, (int)FwTextPropVar.ktpvEnum, (int)FwUnderlineType.kuntSquiggle); bldr.SetIntPropValues(0, tssLookup.Length, (int)FwTextPropType.ktptUnderColor, (int)FwTextPropVar.ktpvDefault, (int)ColorUtil.ConvertColorToBGR(Color.Red)); tssLookup = bldr.GetString(); } // Don't rely on sel from here on. if (hvoNewRef != hvoLeaf) { m_hvoTa = hvoNewRef; // Before we replace in the prop, so it gets displayed using special ta prop. switch (m_type) { case CellarPropertyType.ReferenceAtomic: if (m_hvoParent != 0) // I think it always is, except when loss of focus during debugging causes problems. { // If nothing matched, set the real property to null and the fake one to kbaseFakeObj. // Otherwise set both to the indicated object. m_sda.SetObjProp(m_hvoParent, m_tag, hvoNewRef); // Review: do we want to set the real thing yet? m_sda.PropChanged(null, (int)PropChangeType.kpctNotifyAll, m_hvoParent, m_tag, 0, 1, 1); if (hvoNewRef == 0) { hvoNewRef = m_hvoTa = kBaseFakeObj; // use in fake so we can display something. } cda.CacheObjProp(m_hvoParent, m_virtualTagObj, hvoNewRef); // Change the fake property m_sda.PropChanged(null, (int)PropChangeType.kpctNotifyAll, m_hvoParent, m_virtualTagObj, 0, 1, 1); } break; case CellarPropertyType.ReferenceSequence: case CellarPropertyType.ReferenceCollection: // Several cases, depending on whether we got a match and whether hvoLeaf is the dummy object // 1. match on dummy: insert appropriate real object, change dummy name to empty. // 2. match on non-dummy: replace old object with new // 3: non-match: do nothing. (Even if not looking at the fake object, we'll go on using the // actual object as a base for the fake name, since it's displayed only for the active position.) if (hvoNewRef == 0) { break; // case 3 } if (hvoLeaf == kBaseFakeObj) { // case 1 // The fake object goes back to being an empty name at the end of the list. ITsStrBldr bldr = tssLookup.GetBldr(); bldr.ReplaceTsString(0, bldr.Length, null); // makes an empty string in correct ws. cda.CacheStringProp(kBaseFakeObj, m_taTagName, bldr.GetString()); // Insert the new object before the fake one in fake prop and at end of real seq. // Include the fake object in the replace to get it redisplayed also. cda.CacheReplace(m_hvoParent, m_virtualTagObj, m_ihvoTa, m_ihvoTa + 1, new int[] { hvoNewRef, kBaseFakeObj }, 2); m_sda.PropChanged(null, (int)PropChangeType.kpctNotifyAll, m_hvoParent, m_virtualTagObj, m_ihvoTa, 2, 1); m_sda.Replace(m_hvoParent, m_tag, m_ihvoTa, m_ihvoTa, new int[] { hvoNewRef }, 1); m_sda.PropChanged(null, (int)PropChangeType.kpctNotifyAll, m_hvoParent, m_tag, m_ihvoTa, 1, 0); } else { // case 2 // Replace the object being edited with the indicated one in both props. cda.CacheReplace(m_hvoParent, m_virtualTagObj, m_ihvoTa, m_ihvoTa + 1, new int[] { hvoNewRef }, 1); m_sda.PropChanged(null, (int)PropChangeType.kpctNotifyAll, m_hvoParent, m_virtualTagObj, m_ihvoTa, 1, 1); m_sda.Replace(m_hvoParent, m_tag, m_ihvoTa, m_ihvoTa + 1, new int[] { hvoNewRef }, 1); m_sda.PropChanged(null, (int)PropChangeType.kpctNotifyAll, m_hvoParent, m_tag, m_ihvoTa, 1, 1); } break; default: throw new Exception("unsupported property type for type-ahead chooser"); } } cda.CacheStringProp(hvoNewRef, m_taTagName, tssLookup); m_sda.PropChanged(null, (int)PropChangeType.kpctNotifyAll, hvoNewRef, m_taTagName, 0, tssLookup.Length, tssTyped.Length); // Make a new selection, typically the range that is the bit added to the typed string. // no change is needed to rgvsli because it's the same object index in the same property of the same parent. sel = rootb.MakeTextSelection(ihvoObj, cvsli, rgvsli, m_taTagName, cpropPrevious, ichAnchor, tssLookup.Length, ws, true, -1, null, true); return(true); }
public override void DoIt(Set <int> itemsToChange, ProgressState state) { CheckDisposed(); ISilDataAccess sda = m_cache.MainCacheAccessor; // Make a hashtable from HVO of entry to list of modified senses. Dictionary <int, List <int> > sensesByEntry = new Dictionary <int, List <int> >(); int tagOwningEntry = m_cache.VwCacheDaAccessor.GetVirtualHandlerName("LexSense", "OwningEntry").Tag; int i = 0; // Report progress 50 times or every 100 items, whichever is more (but no more than once per item!) int interval = Math.Min(100, Math.Max(itemsToChange.Count / 50, 1)); foreach (int hvoSense in itemsToChange) { i++; if (i % interval == 0) { state.PercentDone = i * 20 / itemsToChange.Count; state.Breath(); } int hvoMsa = sda.get_ObjectProp(hvoSense, (int)LexSense.LexSenseTags.kflidMorphoSyntaxAnalysis); if (hvoMsa != 0 && m_cache.GetClassOfObject(hvoMsa) != MoStemMsa.kclsidMoStemMsa) { continue; // can't fix this one, not a stem. } int hvoEntry = sda.get_ObjectProp(hvoSense, tagOwningEntry); List <int> senses = null; if (!sensesByEntry.TryGetValue(hvoEntry, out senses)) { senses = new List <int>(); sensesByEntry[hvoEntry] = senses; } senses.Add(hvoSense); } m_cache.BeginUndoTask(FdoUiStrings.ksUndoBulkEditPOS, FdoUiStrings.ksRedoBulkEditPOS); BulkEditBar.ForceRefreshOnUndoRedo(sda); i = 0; interval = Math.Min(100, Math.Max(sensesByEntry.Count / 50, 1)); foreach (KeyValuePair <int, List <int> > kvp in sensesByEntry) { i++; if (i % interval == 0) { state.PercentDone = i * 80 / sensesByEntry.Count + 20; state.Breath(); } int hvoEntry = kvp.Key; List <int> sensesToChange = kvp.Value; int hvoMsmTarget = 0; int cmsa = sda.get_VecSize(hvoEntry, (int)LexEntry.LexEntryTags.kflidMorphoSyntaxAnalyses); bool fAssumeSurvives = true; // true if we know all old MSAs will survive. for (int imsa = 0; imsa < cmsa; imsa++) { int hvoMsa = sda.get_VecItem(hvoEntry, (int)LexEntry.LexEntryTags.kflidMorphoSyntaxAnalyses, imsa); if (m_cache.GetClassOfObject(hvoMsa) == MoStemMsa.kclsidMoStemMsa && sda.get_ObjectProp(hvoMsa, (int)MoStemMsa.MoStemMsaTags.kflidPartOfSpeech) == m_selectedHvo) { // Can reuse this one! hvoMsmTarget = hvoMsa; fAssumeSurvives = false; // old MSA may be redundant. break; } } if (hvoMsmTarget == 0) { // See if we can reuse an existing MoStemMsa by changing it. // This is possible if it is used only by senses in the list, or not used at all. List <int> otherSenses = new List <int>(); AddExcludedSenses(sda, hvoEntry, (int)LexEntry.LexEntryTags.kflidSenses, otherSenses, sensesToChange); for (int imsa = 0; imsa < cmsa; imsa++) { int hvoMsa = sda.get_VecItem(hvoEntry, (int)LexEntry.LexEntryTags.kflidMorphoSyntaxAnalyses, imsa); if (m_cache.GetClassOfObject(hvoMsa) != MoStemMsa.kclsidMoStemMsa) { continue; } bool fOk = true; foreach (int hvoOtherSense in otherSenses) { if (sda.get_ObjectProp(hvoOtherSense, (int)LexSense.LexSenseTags.kflidMorphoSyntaxAnalysis) == hvoMsa) { fOk = false; // we can't change it, one of the unchanged senses uses it break; } } if (fOk) { // Can reuse this one! Nothing we don't want to change uses it. Go ahead and set it to the // required POS. hvoMsmTarget = hvoMsa; int hvoOld = sda.get_ObjectProp(hvoMsmTarget, (int)MoStemMsa.MoStemMsaTags.kflidPartOfSpeech); sda.SetObjProp(hvoMsmTarget, (int)MoStemMsa.MoStemMsaTags.kflidPartOfSpeech, m_selectedHvo); sda.PropChanged(null, (int)PropChangeType.kpctNotifyAll, hvoMsmTarget, (int)MoStemMsa.MoStemMsaTags.kflidPartOfSpeech, 0, 1, hvoOld == 0 ? 1 : 0); // compare MoStemMsa.ResetInflectionClass: changing POS requires us to clear inflection class, // if it is set. if (hvoOld != 0 && sda.get_ObjectProp(hvoMsmTarget, (int)MoStemMsa.MoStemMsaTags.kflidInflectionClass) != 0) { sda.SetObjProp(hvoMsmTarget, (int)MoStemMsa.MoStemMsaTags.kflidInflectionClass, 0); sda.PropChanged(null, (int)PropChangeType.kpctNotifyAll, hvoMsmTarget, (int)MoStemMsa.MoStemMsaTags.kflidInflectionClass, 0, 0, 1); } break; } } } if (hvoMsmTarget == 0) { // Nothing we can reuse...make a new one. hvoMsmTarget = sda.MakeNewObject((int)MoStemMsa.kclsidMoStemMsa, hvoEntry, (int)LexEntry.LexEntryTags.kflidMorphoSyntaxAnalyses, -1); sda.SetObjProp(hvoMsmTarget, (int)MoStemMsa.MoStemMsaTags.kflidPartOfSpeech, m_selectedHvo); sda.PropChanged(null, (int)PropChangeType.kpctNotifyAll, hvoMsmTarget, (int)MoStemMsa.MoStemMsaTags.kflidInflectionClass, m_cache.GetObjIndex(hvoEntry, (int)LexEntry.LexEntryTags.kflidMorphoSyntaxAnalyses, hvoMsmTarget), 1, 0); } // Finally! Make the senses we want to change use it. foreach (int hvoSense in sensesToChange) { int hvoOld = sda.get_ObjectProp(hvoSense, (int)LexSense.LexSenseTags.kflidMorphoSyntaxAnalysis); if (hvoOld == hvoMsmTarget) { continue; // reusing a modified msa. } LexSense.HandleOldMSA(m_cache, hvoSense, hvoMsmTarget, fAssumeSurvives); sda.SetObjProp(hvoSense, (int)LexSense.LexSenseTags.kflidMorphoSyntaxAnalysis, hvoMsmTarget); sda.PropChanged(null, (int)PropChangeType.kpctNotifyAll, hvoSense, (int)LexSense.LexSenseTags.kflidMorphoSyntaxAnalysis, 0, 1, hvoOld == 0 ? 1 : 0); } } m_cache.EndUndoTask(); }
/// <summary> /// Change the value of an atomic REFERENCE property. (Use ${MakeNewObject} or /// ${DeleteObjOwner} to make similar changes to owning atomic properties. /// The caller should also call PropChanged to notify interested parties, /// except where the change is being made to a newly created object. ///</summary> /// <param name='hvo'> </param> /// <param name='tag'> </param> /// <param name='hvoObj'> </param> public virtual void SetObjProp(int hvo, int tag, int hvoObj) { m_baseSda.SetObjProp(hvo, tag, hvoObj); }