/// ------------------------------------------------------------------------------------ /// <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; }
/// <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); }