Example #1
0
 /// ------------------------------------------------------------------------------------
 /// <summary>
 /// Handle a WM_CHAR message.
 /// </summary>
 /// <param name="e"></param>
 /// <param name="modifiers"></param>
 /// <param name="graphics"></param>
 /// ------------------------------------------------------------------------------------
 public override void OnKeyPress(KeyPressEventArgs e, Keys modifiers, IVwGraphics graphics)
 {
     if (m_decoratedEditingHelper != null)
     {
         m_decoratedEditingHelper.OnKeyPress(e, modifiers, graphics);
     }
     else
     {
         base.OnKeyPress(e, modifiers, graphics);
     }
 }
		/// <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;
		}
Example #3
0
        /// <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);
        }