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;
            }
        }
Esempio n. 2
0
		/// <summary>
		/// Returns an array of string values (keys) for the objects under this layout node.
		/// </summary>
		/// <param name="fdoCache"></param>
		/// <param name="layout"></param>
		/// <param name="hvo"></param>
		/// <param name="layoutCache"></param>
		/// <param name="caller">where layout is a component of a 'part' element, caller
		/// is the 'part ref' that invoked it.</param>
		/// <param name="stringTbl"></param>
		/// <param name="wsForce">if non-zero, "string" elements are forced to use that writing system for multistrings.</param>
		/// <returns></returns>
		static public string[] StringsFor(FdoCache fdoCache, XmlNode layout, int hvo,
			LayoutCache layoutCache, XmlNode caller, StringTable stringTbl, int wsForce)
		{
			// Some nodes are known to be uninteresting.
			if (XmlVc.CanSkipNode(layout))
				return new string[0]; // don't know how to sort, treat as empty key.

			switch (layout.Name)
			{
				case "string":
					{
						int hvoTarget = hvo;
						XmlVc.GetActualTarget(layout, ref hvoTarget, fdoCache); // modify the hvo if needed
						if (hvo != hvoTarget)
						{
							return AddStringFromOtherObj(layout, hvoTarget, fdoCache);
						}
						int flid = GetFlid(fdoCache, layout, hvo);
						if (wsForce != 0)
						{
							// If we are forcing a writing system, and it's a multistring, get the forced alternative.
							int itype = fdoCache.MetaDataCacheAccessor.GetFieldType((uint)flid);
							itype = itype & 0x1f; // strip virtual bit
							switch (itype)
							{
								case (int) FieldType.kcptMultiUnicode:
								case (int) FieldType.kcptMultiString:
								case (int) FieldType.kcptMultiBigUnicode:
								case (int) FieldType.kcptMultiBigString:
									return new string[]
											   {fdoCache.MainCacheAccessor.get_MultiStringAlt(hvo, flid, wsForce).Text};
							}
						}
						bool fFoundType;
						string strValue = fdoCache.GetText(hvo, flid, layout, out fFoundType);
						if (fFoundType)
						{
							return new string[] {strValue};
						}
						else
						{
							throw new Exception("Bad property type (" + strValue + " for hvo " + hvo +
												" found for string property "
												+ flid + " in " + layout.OuterXml);
						}
					}
				case "configureMlString":
				{
					int flid = GetFlid(fdoCache, layout, hvo);
					// The Ws info specified in the part ref node
					Set<int> wsIds = LangProject.GetAllWritingSystems(caller, fdoCache, null, hvo, flid);
					if (wsIds.Count == 1)
					{
						string strValue = fdoCache.MainCacheAccessor.get_MultiStringAlt(hvo, flid, wsIds.ToArray()[0]).Text;
						return new string[] {strValue};
					}
					else
						return new string[] {AddMultipleAlternatives(fdoCache, wsIds, hvo, flid, caller)};
				}
				case "multiling":
					return ProcessMultiLingualChildren(fdoCache, layout, hvo, layoutCache, caller, stringTbl, wsForce);
				case "layout":
					// "layout" can occur when GetNodeToUseForColumn returns a phony 'layout'
					// formed by unifying a layout with child nodes. Assemble its children.
					// (arguably, we should treat that like div if current flow is a pile.
					// but we can't tell that and it rarely makes a difference.)
				case "para":
				case "span":
				{
					return AssembleChildKeys(fdoCache, layout, hvo, layoutCache, caller, stringTbl, wsForce);
				}
				case "column":
					// top-level node for whole column; concatenate children as for "para"
					// if multipara is false, otherwise as for "div"
					if (XmlUtils.GetOptionalBooleanAttributeValue(layout, "multipara", false))
						return ChildKeys(fdoCache, layout, hvo, layoutCache, caller, stringTbl, wsForce);
					else
						return AssembleChildKeys(fdoCache, layout, hvo, layoutCache, caller, stringTbl, wsForce);

				case "part":
				{
					string partref = XmlUtils.GetOptionalAttributeValue(layout, "ref");
					if (partref == null)
						return ChildKeys(fdoCache, layout, hvo, layoutCache, caller, stringTbl, wsForce); // an actual part, made up of its pieces
					XmlNode part = XmlVc.GetNodeForPart(hvo, partref, false, fdoCache.MainCacheAccessor, layoutCache);
					// This is the critical place where we introduce a caller. The 'layout' is really a 'part ref' which is the
					// 'caller' for all embedded nodes in the called part.
					return StringsFor(fdoCache, part, hvo, layoutCache, layout, stringTbl, wsForce);
				}
				case "div":
				case "innerpile":
				{
					// Concatenate keys for child nodes (as distinct strings)
					return ChildKeys(fdoCache, layout, hvo, layoutCache, caller, stringTbl, wsForce);
				}
				case "obj":
				{
					// Follow the property, get the object, look up the layout to use,
					// invoke recursively.
					int flid = GetFlid(fdoCache, layout, hvo);
					int hvoTarget = fdoCache.MainCacheAccessor.get_ObjectProp(hvo, flid);
					if (hvoTarget == 0)
						break; // return empty key
					string targetLayoutName = XmlUtils.GetOptionalAttributeValue(layout, "layout"); // uses 'default' if missing.
					XmlNode layoutTarget = GetLayoutNodeForChild(fdoCache, hvoTarget, flid, targetLayoutName, layout, layoutCache);
					if (layoutTarget == null)
						break;
					return ChildKeys(fdoCache, layoutTarget, hvoTarget, layoutCache, caller, stringTbl, wsForce);
				}
				case "seq":
				{
					// Follow the property. For each object, look up the layout to use,
					// invoke recursively, concatenate
					int flid = GetFlid(fdoCache, layout, hvo);
					int ctarget = fdoCache.MainCacheAccessor.get_VecSize(hvo, flid);
					string[] result = null;
					string targetLayoutName = XmlVc.GetLayoutName(layout, caller); // also allows for finding "param" attr in caller, if not null
					//string targetLayoutName = XmlUtils.GetOptionalAttributeValue(layout, "layout");
					for(int i = 0; i < ctarget; i++)
					{
						int hvoTarget = fdoCache.MainCacheAccessor.get_VecItem(hvo, flid, i);
						int prevResultLength = GetArrayLength(result);
						XmlNode layoutTarget = GetLayoutNodeForChild(fdoCache, hvoTarget, flid, targetLayoutName, layout, layoutCache);
						if (layoutTarget == null)
							continue; // should not happen, but best recovery we can make
						result = Concatenate(result, ChildKeys(fdoCache, layoutTarget, hvoTarget, layoutCache, caller, stringTbl, wsForce));
						// add a separator between the new childkey group and the previous childkey group
						if (i > 0 && prevResultLength != GetArrayLength(result) && prevResultLength > 0)
						{
							int ichIns = 0;
							if (result[prevResultLength - 1] != null)
								ichIns = result[prevResultLength - 1].Length;
							AddSeparator(ref result[prevResultLength - 1],  ichIns, layout);
						}
					}
					return result;
				}
				case "choice":
				{
					foreach(XmlNode whereNode in layout.ChildNodes)
					{
						if (whereNode.Name != "where")
						{
							if (whereNode.Name == "otherwise")
								return StringsFor(fdoCache, XmlUtils.GetFirstNonCommentChild(whereNode), hvo, layoutCache, caller, stringTbl, wsForce);
							continue; // ignore any other nodes,typically comments
						}
						// OK, it's a where node.
						if (XmlVc.ConditionPasses(whereNode, hvo, fdoCache, caller))
							return StringsFor(fdoCache, XmlUtils.GetFirstNonCommentChild(whereNode), hvo, layoutCache, caller, stringTbl, wsForce);
					}
					break; // if no condition passes and no otherwise, return null.
				}
				case "if":
				{
					if (XmlVc.ConditionPasses(layout, hvo, fdoCache, caller))
						return StringsFor(fdoCache, XmlUtils.GetFirstNonCommentChild(layout), hvo, layoutCache, caller, stringTbl, wsForce);
					break;
				}
				case "ifnot":
				{
					if (!XmlVc.ConditionPasses(layout, hvo, fdoCache, caller))
						return StringsFor(fdoCache, XmlUtils.GetFirstNonCommentChild(layout), hvo, layoutCache, caller, stringTbl, wsForce);
					break;
				}
				case "lit":
				{
					string literal = layout.InnerText;
					if (stringTbl != null)
					{
						string sTranslate = XmlUtils.GetOptionalAttributeValue(layout, "translate", "");
						if (sTranslate.Trim().ToLower() != "do not translate")
							literal = stringTbl.LocalizeLiteralValue(literal);
					}
					return new string[] { literal };
				}
				case "int":
				{
					int flid = GetFlid(fdoCache, layout, hvo);
					int val = fdoCache.MainCacheAccessor.get_IntProp(hvo, flid);
					return new string[] {XmlViewsUtils.AlphaCompNumberString(val)};
				}
				case "datetime":
				{
					int flid = GetFlid(fdoCache, layout, hvo);
					FieldType itype = fdoCache.GetFieldType(flid);
					if (itype == FieldType.kcptTime)
					{
						DateTime dt = fdoCache.GetTimeProperty(hvo, flid);
						return new string[] {XmlViewsUtils.DateTimeCompString(dt)};
					}
					else
					{
						string stFieldName = XmlUtils.GetManditoryAttributeValue(layout, "field");
						throw new Exception("Bad field type (" + stFieldName + " for hvo " + hvo + " found for " +
							layout.Name + "  property "	+ flid + " in " + layout.OuterXml);
					}
				}
				case "picture":
					// Treat a picture as a non-empty string for purposes of deciding whether something is empty.
					// This string seems as good as anything for other purposes.
					return new string[] {"a picture"};
				default: // unknown or comment node, adds nothing
					Debug.Assert(false, "unrecognized XML node.");
					break;
			}
			return new string[0]; // don't know how to sort, treat as empty key.
		}