Пример #1
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);
        }