private static int GetWfiAnalysisFromWficInstanceOf(FdoCache cache, int hvoInstanceOf, out int hvoWordform) { hvoWordform = 0; int hvoWfiAnalysis = 0; int classid = cache.GetClassOfObject(hvoInstanceOf); switch (classid) { case WfiWordform.kclsidWfiWordform: hvoWordform = hvoInstanceOf; return 0; // need use or make a guess for analysis case WfiAnalysis.kclsidWfiAnalysis: hvoWfiAnalysis = hvoInstanceOf; break; case WfiGloss.kclsidWfiGloss: hvoWfiAnalysis = cache.GetOwnerOfObject(hvoInstanceOf); break; default: throw new ArgumentException("cba.InstanceOf(" + hvoInstanceOf + ") class(" + classid + ") is not WfiWordform, WfiAnalysis, or WfiGloss."); } return hvoWfiAnalysis; }
/// ------------------------------------------------------------------------------------ /// <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); }
/// <summary> /// Get a wordform from an HVO that may be a WfiWordform, WfiAnalysis, or WfiGloss (or 0). /// Answer 0 if arguent is zero, fail if it is some other class. /// </summary> public static int GetWordformFromWag(FdoCache cache, int cbaInstanceOf) { int hvoWordform = 0; if (cbaInstanceOf != 0) { int classid = cache.GetClassOfObject(cbaInstanceOf); switch (classid) { case WfiWordform.kclsidWfiWordform: hvoWordform = cbaInstanceOf; break; case WfiAnalysis.kclsidWfiAnalysis: hvoWordform = cache.GetOwnerOfObject(cbaInstanceOf); break; case WfiGloss.kclsidWfiGloss: int hvoAnalysis = cache.GetOwnerOfObject(cbaInstanceOf); hvoWordform = cache.GetOwnerOfObject(hvoAnalysis); break; default: Debug.Fail("Actual cba (" + cbaInstanceOf + "): Class of InstanceOf (" + classid + ") is not WfiWordform, WfiAnalysis, or WfiGloss."); break; } } return hvoWordform; }
/// <summary> /// Checks for and reports any disallowed discourse template moves. /// </summary> /// <param name="cache"></param> /// <param name="movingColumn">The proposed possibility item (template column) to move.</param> /// <param name="hvoTemplate">The hvo of the affected Chart Template (only 'default' exists so far).</param> /// <param name="hvoTemplateList">The hvo of the Template List.</param> /// <param name="hvoDest">The hvo of the destination item.</param> /// <returns>true means we found and reported a bad move.</returns> private bool CheckAndReportBadDiscourseTemplateMove(FdoCache cache, CmPossibility movingColumn, int hvoTemplate, int hvoTemplateList, int hvoDest) { // First, check whether we're allowed to manipulate this column at all. This is the same check as // whether we're allowed to delete it. if (movingColumn.CheckAndReportProtectedChartColumn()) return true; // Other things being equal, we now need to make sure we aren't messing up the chart levels // Unless something is badly wrong, the destination is either the root template, // a column group one level down from the root template, a column two levels down, // or the base list. if (hvoDest == hvoTemplateList) { MessageBox.Show(m_tree, xWorksStrings.ksCantPromoteGroupToTemplate, xWorksStrings.ksProhibitedMovement, MessageBoxButtons.OK, MessageBoxIcon.Warning); return true; } // if the destination IS the root, that's fine...anything can move there. if (hvoDest == hvoTemplate) return false; // It's OK to move a leaf to a group (one level down from the root, as long as // the destination 'group' isn't a column that's in use. bool moveColumnIsLeaf = movingColumn.SubPossibilitiesOS.Count == 0; if (cache.GetOwnerOfObject(hvoDest) == hvoTemplate && moveColumnIsLeaf) { CmPossibility dest = (CmPossibility.CreateFromDBObject(cache, hvoDest)) as CmPossibility; // If it isn't already a group, we can only turn it into one if it's empty if (dest.SubPossibilitiesOS.Count == 0) return dest.CheckAndReportProtectedChartColumn(); // If it's already a group it should be fine as a destination. return false; } // Anything else represents an attempt to make the tree too deep, e.g., moving a // column into child column, or a group into another group. MessageBox.Show(m_tree, xWorksStrings.ksTemplateTooDeep, xWorksStrings.ksProhibitedMovement, MessageBoxButtons.OK, MessageBoxIcon.Warning); return true; }
// Is hvoAnn, currently analyzed as hvoAnalysis, fully analyzed? // This means: // -- it isn't a default (property InterlinVc.m_vc.ktagTwficDefault isn't cached) // -- It's a WfiGloss, with non-empty form. // -- Owner is a WfiAnalysis with non-empty Category. // -- Owner has at least one WfiMorphBundle. // -- For each WfiMorphBundle, Form, Msa, and Sense are all filled in. // Alternatively, if hvoAnalysis is zero, the annotation is punctuation, which we don't analyze further; // so return true to indicate that it needs no further attention. internal bool FullyAnalyzed(FdoCache fdoCache, WsListManager listman, int hvoAnn, int hvoAnalysis) { int ktagTwficDefault = StTxtPara.TwficDefaultFlid(fdoCache); if (hvoAnalysis == 0) return true; // punctuation, treat as fully analyzed. ISilDataAccess sda = fdoCache.MainCacheAccessor; // Check for default. If the analysis we're showing is a default it needs at least confirmation. if (sda.get_IsPropInCache(hvoAnn, ktagTwficDefault, (int)CellarModuleDefns.kcptReferenceAtom, 0) && sda.get_ObjectProp(hvoAnn, ktagTwficDefault) != 0) return false; int analysisClass = fdoCache.GetClassOfObject(hvoAnalysis); if (analysisClass != (int)WfiGloss.kclsidWfiGloss && analysisClass != (int)WfiAnalysis.kclsidWfiAnalysis) return false; // Has to BE an analysis...unless pathologically everything is off? Too bad if so... int hvoWfiAnalysis = fdoCache.GetOwnerOfObject(hvoAnalysis); int hvoWordform; if (analysisClass == (int)WfiAnalysis.kclsidWfiAnalysis) { hvoWordform = hvoWfiAnalysis; hvoWfiAnalysis = hvoAnalysis; } else { hvoWordform = fdoCache.GetOwnerOfObject(hvoWfiAnalysis); } foreach (InterlinLineSpec spec in m_vc.LineChoices) { // see if the information required for this linespec is present. switch (spec.Flid) { case InterlinLineChoices.kflidWord: int ws = m_vc.GetRealWs(hvoWordform, spec); if (sda.get_MultiStringAlt(hvoWordform, (int)WfiWordform.WfiWordformTags.kflidForm, ws).Length == 0) return false; break; case InterlinLineChoices.kflidLexEntries: if (!CheckPropSetForAllMorphs(sda, hvoWfiAnalysis, (int)WfiMorphBundle.WfiMorphBundleTags.kflidMorph)) return false; break; case InterlinLineChoices.kflidMorphemes: if (!CheckPropSetForAllMorphs(sda, hvoWfiAnalysis, (int)WfiMorphBundle.WfiMorphBundleTags.kflidMorph)) return false; break; case InterlinLineChoices.kflidLexGloss: if (!CheckPropSetForAllMorphs(sda, hvoWfiAnalysis, (int)WfiMorphBundle.WfiMorphBundleTags.kflidSense)) return false; break; case InterlinLineChoices.kflidLexPos: if (!CheckPropSetForAllMorphs(sda, hvoWfiAnalysis, (int)WfiMorphBundle.WfiMorphBundleTags.kflidMsa)) return false; break; case InterlinLineChoices.kflidWordGloss: // If it isn't a WfiGloss the user needs a chance to supply a word gloss. if (analysisClass != WfiGloss.kclsidWfiGloss) return false; // If it is empty for the (possibly magic) ws specified here, it needs filling in. int ws1 = m_vc.GetRealWs(hvoAnalysis, spec); if (sda.get_MultiStringAlt(hvoAnalysis, (int)WfiGloss.WfiGlossTags.kflidForm, ws1).Length == 0) return false; break; case InterlinLineChoices.kflidWordPos: if (sda.get_ObjectProp(hvoWfiAnalysis, (int)WfiAnalysis.WfiAnalysisTags.kflidCategory) == 0) return false; break; case InterlinLineChoices.kflidFreeTrans: case InterlinLineChoices.kflidLitTrans: case InterlinLineChoices.kflidNote: default: // unrecognized or non-word-level annotation, nothing required. break; } } return true; // If we can't find anything to complain about, it's fully analyzed. }
/// <summary> /// get the index of an item in the list that has a root object that is owned by hvo /// </summary> /// <param name="hvo"></param> /// <returns>-1 if the object is not in the list</returns> public int IndexOfChildOf(int hvoTarget, FdoCache cache) { CheckDisposed(); int i = 0; foreach (ManyOnePathSortItem item in SortedObjects) { ICmObject orange = item.RootObject; for (int hvoOwner = cache.GetOwnerOfObject(orange.Hvo); hvoOwner != 0; hvoOwner = cache.GetOwnerOfObject(hvoOwner)) { if (hvoOwner == hvoTarget) return i; } ++i; } return -1; }
/// <summary> /// Checks for and reports any disallowed tag list moves. /// </summary> /// <param name="cache"></param> /// <param name="movingTagItem">The proposed tag item to move.</param> /// <param name="hvoSubListRoot">The hvo of the top-level Tag Type Possibility containing the moving item.</param> /// <param name="hvoMainTagList">The hvo of the main PossiblityList grouping all TextMarkup Tags.</param> /// <param name="hvoDest">The hvo of the destination tag item.</param> /// <returns>true if we found and reported a bad move.</returns> private bool CheckAndReportBadTagListMove(FdoCache cache, CmPossibility movingTagItem, int hvoSubListRoot, int hvoMainTagList, int hvoDest) { // Check if movingTagItem is a top-level Tag Type. if (movingTagItem.Hvo == hvoSubListRoot) { if (hvoDest == hvoMainTagList) // top-level Tag Type can move to main list (probably already there) return false; // The moving item is a top-level Tag Type, it cannot be demoted. MessageBox.Show(m_tree, xWorksStrings.ksCantDemoteTagList, xWorksStrings.ksProhibitedMovement, MessageBoxButtons.OK, MessageBoxIcon.Warning); return true; } // Unless something is badly wrong, the destination is either the tag type root, // a tag one level down from the root, or the base list. if (cache.GetOwnerOfObject(hvoDest) == hvoMainTagList) { // Destination is Tag Type root, not a problem return false; } if (hvoDest == hvoMainTagList) { // Can't promote tag to Tag Type root MessageBox.Show(m_tree, xWorksStrings.ksCantPromoteTag, xWorksStrings.ksProhibitedMovement, MessageBoxButtons.OK, MessageBoxIcon.Warning); return true; } // This would give us hierarchy too deep (at least for now) MessageBox.Show(m_tree, xWorksStrings.ksTagListTooDeep, xWorksStrings.ksProhibitedMovement, MessageBoxButtons.OK, MessageBoxIcon.Warning); return true; }
public static int GetParentOfClass(FdoCache m_cache, int hvo, int classIdOfParentToSearchFor) { //save the caller the need to see if this property was empty or not if(hvo <1) return -1; int classId = m_cache.GetClassOfObject(hvo) ; while((!m_cache.IsSameOrSubclassOf(classId,classIdOfParentToSearchFor)) && (classId != FDO.LangProj.LangProject.kClassId)) { hvo = m_cache.GetOwnerOfObject(hvo); classId = m_cache.GetClassOfObject(hvo) ; } if((!m_cache.IsSameOrSubclassOf(classId,classIdOfParentToSearchFor))) return -1; else return hvo; }
/// <summary> /// Test whether a particular StTxtPara is part of Scripture. /// </summary> /// <param name="hvoPara"></param> /// <param name="cache"></param> /// <returns></returns> public static bool IsScripturePara(int hvoPara, FdoCache cache) { int flidOfOwnerOfSttext = cache.GetOwningFlidOfObject(cache.GetOwnerOfObject(hvoPara)); return Scripture.Scripture.IsScriptureTextFlid(flidOfOwnerOfSttext); }
/// <summary> /// for variant relationships, return the primary entry /// (of which this morph is a variant). Otherwise, /// return the owning entry of the morph. /// </summary> /// <param name="cache"></param> /// <returns></returns> public int GetPrimaryOrOwningEntry(FdoCache cache) { int hvoMorphEntryReal; if (m_hvoEntry != 0) { // for variant relationships, we want to allow trying to create a // new sense on the entry of which we are a variant. hvoMorphEntryReal = m_hvoEntry; } else { hvoMorphEntryReal = cache.GetOwnerOfObject(m_hvoMorph); } return hvoMorphEntryReal; }
private static bool CheckAndReportBadDiscourseTemplateAdd(FdoCache cache, int hvoItem, int hvoRootItem, int hvoList) { if (cache.GetOwningFlidOfObject(hvoList) != (int)DsDiscourseData.DsDiscourseDataTags.kflidConstChartTempl) return false; // some other list we don't care about. // We can't turn a column into a group if it's in use. CmPossibility col = CmPossibility.CreateFromDBObject(cache, hvoItem) as CmPossibility; // If the item doesn't already have children, we can only add them if it isn't already in use // as a column: we don't want to change a column into a group. Thus, if there are no // children, we generally call the same routine as when deleting. // However, that routine has a special case to prevent deletion of the default template even // if NOT in use...and we must not prevent adding to that when it is empty! Indeed any // empty CHART can always be added to, so only if col's owner is a CmPossibility (it's not a root // item in the templates list) do we need to check for it being in use. if (col.SubPossibilitiesOS.Count == 0 && col.Owner is CmPossibility && col.CheckAndReportProtectedChartColumn()) return true; // Finally, we have to confirm the two-level rule. if (hvoItem != hvoRootItem && cache.GetOwnerOfObject(hvoItem) != hvoRootItem) { MessageBox.Show(FdoUiStrings.ksTemplateTooDeep, FdoUiStrings.ksHierarchyLimit, MessageBoxButtons.OK, MessageBoxIcon.Warning); return true; } return false; }
// Make a string representing a WfiAnalysis, suitable for use in a combo box item. static internal ITsString MakeAnalysisStringRep(int hvoWa, FdoCache fdoCache, bool fUseStyleSheet, int wsVern) { // ITsTextProps boldItalicAnalysis = BoldItalicAnalysis(fdoCache); // ITsTextProps italicAnalysis = ItalicAnalysis(fdoCache, Sandbox.SandboxVc.krgbRed); ITsTextProps posTextProperties = PartOfSpeechTextProperties(fdoCache, true, fUseStyleSheet); ITsTextProps formTextProperties = FormTextProperties(fdoCache, fUseStyleSheet, wsVern); ITsTextProps glossTextProperties = GlossTextProperties(fdoCache, true, fUseStyleSheet); ITsStrBldr tsb = TsStrBldrClass.Create(); ISilDataAccess sda = fdoCache.MainCacheAccessor; int cmorph = fdoCache.GetVectorSize(hvoWa, (int)WfiAnalysis.WfiAnalysisTags.kflidMorphBundles); if (cmorph == 0) return fdoCache.MakeUserTss(ITextStrings.ksNoMorphemes); bool fRtl = fdoCache.LanguageWritingSystemFactoryAccessor.get_EngineOrNull(wsVern).RightToLeft; int start = 0; int lim = cmorph; int increment = 1; if (fRtl) { start = cmorph - 1; lim = -1; increment = -1; } for (int i = start; i != lim; i += increment) { int hvoMb = fdoCache.GetVectorItem(hvoWa, (int)WfiAnalysis.WfiAnalysisTags.kflidMorphBundles, i); int hvoMf = fdoCache.GetObjProperty(hvoMb, (int)WfiMorphBundle.WfiMorphBundleTags.kflidMorph); ITsString tssForm = null; if (hvoMf != 0) { int hvoEntry = fdoCache.GetOwnerOfObject(hvoMf); int hvoLexemeForm = sda.get_ObjectProp(hvoEntry, (int) LexEntry.LexEntryTags.kflidLexemeForm); if (hvoLexemeForm != 0) { tssForm = sda.get_MultiStringAlt(hvoLexemeForm, (int) MoForm.MoFormTags.kflidForm, wsVern); } if (tssForm == null || tssForm.Length == 0) { tssForm = fdoCache.MainCacheAccessor.get_MultiStringAlt(hvoEntry, (int)LexEntry.LexEntryTags.kflidCitationForm, wsVern); } if (tssForm.Length == 0) { // If there isn't a lexeme form OR citation form use the form of the morph. tssForm = fdoCache.MainCacheAccessor.get_MultiStringAlt(hvoMf, (int)MoForm.MoFormTags.kflidForm, wsVern); } } else // no MoForm linked to this bundle, use its own form. { tssForm = fdoCache.MainCacheAccessor.get_MultiStringAlt(hvoMb, (int)WfiMorphBundle.WfiMorphBundleTags.kflidForm, wsVern); } int ichForm = tsb.Length; tsb.ReplaceTsString(ichForm, ichForm, tssForm); tsb.SetProperties(ichForm, tsb.Length,formTextProperties); // add category (part of speech) int hvoMsa = fdoCache.GetObjProperty(hvoMb, (int)WfiMorphBundle.WfiMorphBundleTags.kflidMsa); tsb.Replace(tsb.Length, tsb.Length, " ", null); int ichMinMsa = tsb.Length; string interlinName = ksMissingString; if (hvoMsa != 0) { IMoMorphSynAnalysis msa = MoMorphSynAnalysis.CreateFromDBObject(fdoCache, hvoMsa); interlinName = msa.InterlinearAbbr; } tsb.Replace(ichMinMsa, ichMinMsa, interlinName, posTextProperties); //add sense int hvoSense = fdoCache.GetObjProperty(hvoMb, (int)WfiMorphBundle.WfiMorphBundleTags.kflidSense); tsb.Replace(tsb.Length, tsb.Length, " ", null); int ichMinSense = tsb.Length; if (hvoSense != 0) { ITsString tssGloss = fdoCache.MainCacheAccessor.get_MultiStringAlt(hvoSense, (int)LexSense.LexSenseTags.kflidGloss, fdoCache.DefaultAnalWs); tsb.Replace(ichMinSense, ichMinSense, tssGloss.Text, glossTextProperties); } else tsb.Replace(ichMinSense, ichMinSense, ksMissingString, glossTextProperties); // Enhance JohnT: use proper seps. tsb.Replace(tsb.Length, tsb.Length, ksPartSeparator, null); } // Delete the final separator. (Enhance JohnT: this needs to get smarter when we do // real seps.) int ichFrom = tsb.Length - ksPartSeparator.Length; if (ichFrom < 0) ichFrom = 0; tsb.Replace(ichFrom, tsb.Length, "", null); return tsb.GetString(); }
// Generate a suitable string representation of a WfiGloss. // Todo: finish implementing (add the gloss!) static internal ITsString MakeGlossStringRep(int hvoGloss, FdoCache fdoCache, bool fUseStyleSheet) { ITsStrBldr tsb = TsStrBldrClass.Create(); int hvoWa = fdoCache.GetOwnerOfObject(hvoGloss); int hvoCategory = fdoCache.GetObjProperty(hvoWa, (int)WfiAnalysis.WfiAnalysisTags.kflidCategory); if(hvoCategory > 0) { ITsString tssPos = fdoCache.MainCacheAccessor.get_MultiStringAlt(hvoCategory, (int)CmPossibility.CmPossibilityTags.kflidAbbreviation, fdoCache.DefaultAnalWs); tsb.Replace(0, 0, tssPos.Text, PartOfSpeechTextProperties(fdoCache,false, fUseStyleSheet)); } else { tsb.Replace(0, 0, ksMissingString, PartOfSpeechTextProperties(fdoCache,false, fUseStyleSheet)); } tsb.Replace(tsb.Length, tsb.Length, " ", null); tsb.Replace(tsb.Length, tsb.Length, fdoCache.MainCacheAccessor.get_MultiStringAlt(hvoGloss, (int)WfiGloss.WfiGlossTags.kflidForm, fdoCache.DefaultAnalWs).Text, GlossTextProperties(fdoCache, false, fUseStyleSheet)); //indent tsb.Replace(0,0, " ", null); return tsb.GetString(); }
static internal int GetParagraphIndexForAnnotation(FdoCache cache, int annHvo) { int hvoPara = cache.MainCacheAccessor.get_ObjectProp(annHvo, (int)CmBaseAnnotation.CmBaseAnnotationTags.kflidBeginObject); int hvoStText = cache.GetOwnerOfObject(hvoPara); return GetParagraphIndexForPara(cache, hvoStText, hvoPara); }
/// <summary> /// Test whether a particular StTxtPara is part of Scripture. /// </summary> /// <param name="hvoPara"></param> /// <param name="cache"></param> /// <returns></returns> public static bool IsScripturePara(int hvoPara, FdoCache cache) { int flidOfOwnerOfSttext = cache.GetOwningFlidOfObject(cache.GetOwnerOfObject(hvoPara)); return(Scripture.Scripture.IsScriptureTextFlid(flidOfOwnerOfSttext)); }
/// <summary> /// Get a set of inflectional affix slots which can be prefixal or suffixal /// </summary> /// <param name="cache"></param> /// <param name="allSlots">Original set of all slots</param> /// <param name="fLookForPrefixes">whether to look for prefixal slots</param> /// <returns>subset of slots that are either prefixal or suffixal</returns> public static Set<int> GetSomeSlots(FdoCache cache, Set<int> allSlots, bool fLookForPrefixes) { Set<int> set = new Set<int>(); //Set<int> allSlots = GetAllSlots(); foreach (int hvoSlot in allSlots) { MoInflAffixSlot slot = CmObject.CreateFromDBObject(cache, hvoSlot) as MoInflAffixSlot; if (slot == null) continue; bool fStopLooking = false; List<int> hvosAffixes = slot.Affixes; if (hvosAffixes.Count == 0) { // no affixes in this slot, so include it set.Add(hvoSlot); continue; } foreach (int hvoMsa in hvosAffixes) { int hvoLex = cache.GetOwnerOfObject(hvoMsa); LexEntry lex = CmObject.CreateFromDBObject(cache, hvoLex) as LexEntry; List<IMoMorphType> morphTypes = lex.MorphTypes; foreach (IMoMorphType morphType in morphTypes) { bool fIsCorrectType; if (fLookForPrefixes) fIsCorrectType = MoMorphType.IsPrefixishType(cache, morphType.Hvo); else fIsCorrectType = MoMorphType.IsSuffixishType(cache, morphType.Hvo); if (fIsCorrectType) { set.Add(hvoSlot); fStopLooking = true; break; } } if (fStopLooking) break; } } return set; }
const int ktagMinVp = 0x7f000000; // copied from VwCacheDa.h, mimimum value for assigned virtual props. /// <summary> /// We implement this to record modify times. /// </summary> /// <param name="hvo"></param> /// <param name="tag"></param> /// <param name="ivMin"></param> /// <param name="cvIns"></param> /// <param name="cvDel"></param> public void PropChanged(int hvo, int tag, int ivMin, int cvIns, int cvDel) { CheckDisposed(); if (m_fModifyChangeInProgress) // ignore PropChanged on the DateModified property! { return; } // Typically an Undo will restore the modify time to what it was before the // main change. We don't want to reverse that by recording the time of the Undo! // Note that there is one pathological scenario: // t1: make a change. // t2: undo the change. // t3: export all changed records (since before t1). // t4: redo the change. Record appears to have been modified at time t2. // t5: export all changed records (since t3). // If this was the only change to the record, it is not part of either export, // and the change might be lost. // To prevent this, I think the 'export changed records' function should clear // the Undo stack. if (m_cache.ActionHandlerAccessor != null && m_cache.ActionHandlerAccessor.IsUndoOrRedoInProgress) { return; } if (tag < 0 || tag >= ktagMinVp) { return; // phony or virtual properties, not real changes (e.g., different filter applied, or selection moving in browse). } try { DateTime dtNow = DateTime.Now; int hvoMajor = 0; // hvo of the 'major' object that has the DateModified property. if (SameMinute(dtNow, m_currentMinute)) { // We can use our Dictionary if (m_recentMods.ContainsKey(hvo)) { hvoMajor = m_recentMods[hvo]; } } else { ResetDelay(); } int flidModify = 0; if (hvoMajor == 0) { for (int hvoCandidate = hvo; hvoCandidate != 0; hvoCandidate = m_cache.GetOwnerOfObject(hvoCandidate)) { // We assume that this is a dummy object at this point and GetClassOfObject // only accepts real objects. Since it's a dummy object, changes should never // affect the modify time of a real object. Maybe at some point we can get an // interface method to check for dummy objects. if (hvoCandidate < 0) { return; } int clid = m_cache.GetClassOfObject(hvoCandidate); if (clid == 0) { return; // maybe a deleted object, no good to us, we can't record modify time. } flidModify = (int)m_mdc.GetFieldId2((uint)clid, "DateModified", true); if (flidModify != 0) { hvoMajor = hvoCandidate; break; } } if (hvoMajor == 0) { return; // found no owner with DateCreated property. } m_recentMods[hvo] = hvoMajor; // lets us find it fast if modified again soon. } else { // We need to set flidModify! int clid = m_cache.GetClassOfObject(hvoMajor); if (clid != 0) { flidModify = (int)m_mdc.GetFieldId2((uint)clid, "DateModified", true); } } if (flidModify == 0) { return; // can't set the time prop without a field... } DateTime oldModifyTime = m_cache.GetTimeProperty(hvoMajor, flidModify); // If we already noted a modify time in the current minute, don't do it again. // This is intended to keep down the overhead of recording modify times for frequently // modified objects. if (SameMinute(oldModifyTime, dtNow)) { //Trace.TraceInformation("New Modify Time ({0}) for {1} is same minute as the old ({2}).\n", // dtNow, hvoMajor, oldModifyTime); return; } // if (m_currentMinute != null && // m_currentMinute.Date == dtNow.Date && m_currentMinute.Hour = dtNow.Hour && m_currentMinute.Minute == dtNow.Minute) // return; // Set the modify time. If possible tack this on to the last Undo task (or the current one, if a // transaction is open). if (m_cache.ActionHandlerAccessor != null && m_cache.VwOleDbDaAccessor != null && ((m_cache.DatabaseAccessor != null && m_cache.DatabaseAccessor.IsTransactionOpen()) || m_cache.CanUndo)) { m_cache.ContinueUndoTask(); m_cache.SetTimeProperty(hvoMajor, flidModify, dtNow); m_cache.EndUndoTask(); } else { // We don't have an Undo task to append to, so somehow a Save has occurred // in the midst of the operation that caused the time stamp modification. // If we make an empty undo task, the user will typically have no idea what // could be undone, and will be confused. Best to make it impossible to // undo the timestamp change also. using (new SuppressSubTasks(m_cache)) { m_cache.SetTimeProperty(hvoMajor, flidModify, dtNow); } } //Trace.TraceInformation("Setting new Modify Time ({0}) for {1}\n", // dtNow, hvoMajor); } finally { m_fModifyChangeInProgress = false; } }
/// <summary> /// Check whether the list should be sorted. See LT-5149. /// </summary> /// <param name="labels"></param> /// <param name="cache"></param> /// <returns></returns> private static bool IsListSorted(ObjectLabelCollection labels, FdoCache cache) { if (labels.Count > 0 && cache != null) { int hvoList = cache.GetOwnerOfObject(labels[0].Hvo); ICmObject co = CmObject.CreateFromDBObject(cache, hvoList); if (co is ICmPossibilityList) return (co as ICmPossibilityList).IsSorted; } return true; }