// Handles a change in the view selection. protected override void HandleSelectionChange(IVwSelection vwselNew) { base.HandleSelectionChange(vwselNew); // Figure what property is selected and display combo only if relevant. SIL.FieldWorks.Common.COMInterfaces.ITsString tss; int ich, tag, enc; bool fAssocPrev; vwselNew.TextSelInfo(true, out tss, out ich, out fAssocPrev, out hvoObjSelected, out tag, out enc); string str = tss.get_Text(); if (tag == ktagWord_Type) { // Display combo at selection SIL.FieldWorks.Common.COMInterfaces.Rect loc; vwselNew.GetParaLocation(out loc); typeComboBox.Location = new System.Drawing.Point(loc.left, loc.top); // 60 is an arbitrary minimum size to make the current contents visible. // Enhance JohnT: figure the width needed by the widest string, add width of arrow, use that // as minimum typeComboBox.Size = new System.Drawing.Size(Math.Max(loc.right - loc.left, 60), loc.bottom - loc.top); typeComboBox.Text = str; // This also makes it visible. this.Controls.Add(typeComboBox); } else { // Hide combo if visible. // Enhance JohnT: possibly also remove on loss of focus? if (this.Controls.Contains(typeComboBox)) { this.Controls.Remove(typeComboBox); } } // Todo JohnT: make something interesting happen when a selection is made. }
/// <summary> /// Given an icon selection, find the nearest associated text. /// In left to right, that's the first text to right of the picture. /// In right to left, it's the first text to the left of the picture; /// </summary> /// <param name="selOrig"></param> /// <returns></returns> private IVwSelection SelectFirstAssociatedText(IVwSelection selOrig) { TextSelInfo tsi = new TextSelInfo(selOrig); if (!tsi.IsPicture) { return tsi.Selection; } Debug.Assert(tsi.IsPicture); IVwSelection selStartOfText = null; int dxPixelIncrement = 1; SIL.Utils.Rect rect; selOrig.GetParaLocation(out rect); int widthIconPara = (rect.right - rect.left); int xMaxCountOfPixels = (widthIconPara) * 2; if (m_vc.RightToLeft) { // Right to Left: // start at the left of the icon and move left to find the text. // let the limit be 2x the width of the icon. selStartOfText = FindNearestSelectionType(selOrig, VwSelType.kstText, (uint)(rect.left - dxPixelIncrement), (uint)xMaxCountOfPixels, -dxPixelIncrement); } else { // Left to Right // start at right of icon and move right to find text. selStartOfText = FindNearestSelectionType(selOrig, VwSelType.kstText, (uint)(rect.right + dxPixelIncrement), (uint)xMaxCountOfPixels, dxPixelIncrement); } if (selStartOfText != null) { // install at beginning of text selection selStartOfText.Install(); } return selStartOfText; }
/// <summary> /// find a selection of the given selType in the RootBox starting at xMin coordinate and spanning xMaxCountOfPixels along the mid y-coordinate /// of the given selOrig, by the direction and increment of dxPixelIncrement. /// </summary> /// <param name="selOrig">the selection from which we calculate the y-coordinate along which to scan.</param> /// <param name="selType">the type of selection we're looking for.</param> /// <param name="xMin">the starting x coordinate from which to scan.</param> /// <param name="xMaxCountOfPixels">the number of x units to scan from xMin.</param> /// <param name="dx">number and direction of pixels to probe for selType. a positive value /// will scan right, and a negative value will scan left. must be nonzero.</param> /// <returns></returns> private IVwSelection FindNearestSelectionType(IVwSelection selOrig, VwSelType selType, uint xMin, uint xMaxCountOfPixels, int dxPixelIncrement) { IVwSelection sel = null; if (dxPixelIncrement == 0) throw new ArgumentException(String.Format("dxPixelIncrement({0}) must be nonzero", dxPixelIncrement)); SIL.Utils.Rect rect; selOrig.GetParaLocation(out rect); int y = rect.top + (rect.bottom - rect.top) / 2; Point pt = new Point((int)xMin, y); uint xLim = 0; if (dxPixelIncrement > 0) { // set our bounds for searching forward. xLim = xMin + xMaxCountOfPixels; // truncate if necessary. if (xLim > RootBox.Width) xLim = (uint)RootBox.Width; } else { // set our bounds for searching backward. // truncate if necessary. if (xMin > xMaxCountOfPixels) xLim = xMin - xMaxCountOfPixels; else xLim = 0; } while (dxPixelIncrement < 0 && pt.X > xLim || dxPixelIncrement > 0 && pt.X < xLim) { using (new HoldGraphics(this)) { Rectangle rcSrcRoot; Rectangle rcDstRoot; GetCoordRects(out rcSrcRoot, out rcDstRoot); sel = m_rootb.MakeSelAt(pt.X, pt.Y, rcSrcRoot, rcDstRoot, false); if (sel != null && sel.SelType == selType) break; else sel = null; } pt.X += dxPixelIncrement; } return sel; }
/// <summary> /// given a (text) selection, scan the beginning of its paragraph box and then immediately before its /// paragraph box in search of the icon. /// </summary> /// <param name="selOrig"></param> /// <returns></returns> private IVwSelection ScanForIcon(IVwSelection selOrig) { IVwSelection selArrow; int dxPixelIncrement = 3; uint iconParagraphWidth = 10; SIL.Utils.Rect rect; selOrig.GetParaLocation(out rect); if (m_vc.RightToLeft) { // Right to Left: selArrow = FindNearestSelectionType(selOrig, VwSelType.kstPicture, (uint)rect.right - iconParagraphWidth, iconParagraphWidth * 2, dxPixelIncrement); if (selArrow == null) { // we didn't find it next to our paragraph box. see if we can // find it at the beginning of the RootBox. selArrow = FindNearestSelectionType(selOrig, VwSelType.kstPicture, (uint)RootBox.Width - iconParagraphWidth, iconParagraphWidth, dxPixelIncrement); } } else { // Left to Right selArrow = FindNearestSelectionType(selOrig, VwSelType.kstPicture, (uint)rect.left + iconParagraphWidth, iconParagraphWidth * 2, -dxPixelIncrement); if (selArrow == null) { // we didn't find it next to our paragraph box. see if we can // find it at the beginning of the RootBox. selArrow = FindNearestSelectionType(selOrig, VwSelType.kstPicture, 0, iconParagraphWidth, dxPixelIncrement); } } return selArrow; }
/// <summary> /// Make a combo box appropriate for the specified selection. If fMouseDown is true, /// do so unconditionally...otherwise (mousemove) only if the new selection is on a different thing. /// </summary> /// <param name="vwselNew"></param> /// <param name="fForce"></param> private void ShowComboForSelection(IVwSelection vwselNew, bool fMouseDown) { // It's a good idea to get this first...it's possible for MakeCombo to leave the selection invalid. SIL.Utils.Rect loc; vwselNew.GetParaLocation(out loc); if (!fMouseDown) { // It's a mouse move. // If we've moved to somewhere outside any paragraph get rid of the combos. // But, allow somewhere close, since otherwise it's almost impossible to get // a combo on an empty string. SIL.Utils.Rect locExpanded = loc; locExpanded.right += 50; locExpanded.left -= 5; locExpanded.top -= 2; locExpanded.bottom += 2; if (!locExpanded.Contains(m_LastMouseMovePos)) { HideCombos(); return; } // Don't do anything if the current mouse position is in the same paragraph // as before. Things tend to flicker if we continually create and remove it. // But, if we've hidden all the combos, go ahead even if at the same position as before... // otherwise, when we drag off outside the text and return, we may not get any combo. if (loc.Equals(m_locLastShowCombo) && FirstLineHandler != null) { return; } } FinishUpOk(); // Just like OK, if there are pending edits in the combo, do them. // Changing a different item may result in changes to this one also. This could invalidate // the selection, in which case, we can't use it. // Enhance JohnT: might consider trying the current selection, if any, if called from // MouseDown...that would not be useful if called from hover. But there probably isn't // a current selection in that case. Could try a selection at the saved mouse position. if (!vwselNew.IsValid) return; m_locLastShowCombo = loc; m_fMakingCombo = true; HideCombos(); // No matter what, we are fixin to get rid of the old value. DisposeComboHandler(); if (!m_fInMouseDrag) { m_ComboHandler = InterlinComboHandler.MakeCombo(m_mediator.HelpTopicProvider, vwselNew, this, fMouseDown); } else { m_ComboHandler = null; } m_fMakingCombo = false; m_fLockCombo = false; // nothing typed in it yet. if (m_ComboHandler != null) { // Set the position of the combo and display it. Do this before synchronizing // the LexEntry display, which can take a while. m_ComboHandler.Activate(loc); m_fMouseDownActivatedCombo = true; // If the selection moved to a different morpheme, and we know a corresponding // LexEntry, switch to it. if (m_ComboHandler.SelectedMorphHvo != 0) { int hvoSbEntry = m_caches.DataAccess.get_ObjectProp( m_ComboHandler.SelectedMorphHvo, ktagSbMorphEntry); if (hvoSbEntry != 0) { //SetSelectedEntry(m_caches.RealHvo(hvoSbEntry)); // seems to be buggy. } } } }
/// ------------------------------------------------------------------------------------ /// <summary> /// We override this method to make a selection in all of the views that are in a /// snynced group. This fixes problems where the user changes the selection in one of /// the slaves, but the master is not updated. Thus the view is not scrolled as the /// groups scroll position only scrolls the master's selection into view. (TE-3380) /// </summary> /// <param name="prootb"></param> /// <param name="vwselNew"></param> /// ------------------------------------------------------------------------------------ public override void SelectionChanged(IVwRootBox prootb, IVwSelection vwselNew) { CheckDisposed(); Debug.Assert( vwselNew != null ); if (s_fInSelectionChanged) return; // We need to check if this is our first time in the function because our // call to helper.SetSelection below will end up calling this function.. // this results in a ugly infinite loop. A more elegant solution would be // nice...but not practical with the views architecture. if (m_group != null && !vwselNew.IsRange) { s_fInSelectionChanged = true; // We are after the y position of the top of the paragraph Rectangle rcSrcRoot; Rectangle rcDstRoot; GetCoordRects( out rcSrcRoot, out rcDstRoot); Rect paraRect; vwselNew.GetParaLocation( out paraRect ); foreach(RootSite site in m_group.Slaves ) { if (site != this) { // Recursive call to SelectionChanged happens here =( // Add 2 pixels so we aren't on the end of the rectangle // (improves accuracy) site.RootBox.MakeSelAt(0, paraRect.top + 2, rcSrcRoot, rcDstRoot, true); } } // Reset at this point. s_fInSelectionChanged = false; } base.SelectionChanged(prootb, vwselNew); }