protected override void HandleSelectionChange(IVwRootBox rootb, IVwSelection vwselNew) { CheckDisposed(); base.HandleSelectionChange(rootb, vwselNew); // JohnT: it's remotely possible that the base, in calling commit, made this // selection no longer useable. if (!vwselNew.IsValid) { return; } IWfiWordform wordform; if (!GetSelectedWordform(vwselNew, out wordform)) { wordform = null; } m_mediator.PropertyTable.SetProperty("TextSelectedWord", wordform); m_mediator.PropertyTable.SetPropertyPersistence("TextSelectedWord", false); SelectionHelper helper = SelectionHelper.Create(vwselNew, this); if (helper != null && helper.GetTextPropId(SelectionHelper.SelLimitType.Anchor) == RawTextVc.kTagUserPrompt) { vwselNew.ExtendToStringBoundaries(); EditingHelper.SetKeyboardForSelection(vwselNew); } }
public void GetSetClipboard() { var wsManager = new WritingSystemManager(); CoreWritingSystemDefinition enWs; wsManager.GetOrSet("en", out enWs); int wsEng = enWs.Handle; CoreWritingSystemDefinition swgWs; wsManager.GetOrSet("swg", out swgWs); int wsSwg = swgWs.Handle; var incStrBldr = TsStringUtils.MakeIncStrBldr(); incStrBldr.AppendTsString(TsStringUtils.MakeString("Gogomer ", wsSwg)); incStrBldr.AppendTsString(TsStringUtils.MakeString("cucumber", wsEng)); EditingHelper.SetTsStringOnClipboard(incStrBldr.GetString(), false, wsManager); var tss = m_basicView.EditingHelper.GetTsStringFromClipboard(wsManager); Assert.IsNotNull(tss, "Couldn't get TsString from clipboard"); Assert.AreEqual(2, tss.RunCount); Assert.AreEqual("Gogomer ", tss.get_RunText(0)); Assert.AreEqual("cucumber", tss.get_RunText(1)); var newDataObj = ClipboardUtils.GetDataObject(); Assert.IsNotNull(newDataObj, "Couldn't get DataObject from clipboard"); Assert.AreEqual("Gogomer cucumber", newDataObj.GetData("Text")); }
/// ------------------------------------------------------------------------------------ /// <summary> /// Initializes a new instance of the <see cref="T:PubEditingHelper"/> class. /// </summary> /// <param name="decoratedEditingHelper">The decorated editing helper.</param> /// <param name="callbacks">The callbacks.</param> /// ------------------------------------------------------------------------------------ public PubEditingHelper(EditingHelper decoratedEditingHelper, IEditingCallbacks callbacks) : base(callbacks) { m_decoratedEditingHelper = decoratedEditingHelper; Debug.Assert(callbacks is PublicationControl); }
/// <summary> /// Handle fixing the writing system values inside pasted text. Eventually, we may /// want some form of UI to guide the process, but for now we just bash in the /// default vernacular ws wherever any other ws occurs. /// </summary> /// <param name="sender"></param> /// <param name="args"></param> private void OnPasteFixWs(EditingHelper sender, FwPasteFixTssEventArgs args) { if (args.EventHandled) { return; // only one delegate gets to play! } bool fFixed = false; int wsVern = Cache.LangProject.ActualWs(LangProject.kwsVernInParagraph, m_hvoRoot, (int)StText.StTextTags.kflidParagraphs); ITsStrBldr tsb = args.TsString.GetBldr(); int crun = tsb.RunCount; for (int irun = 0; irun < crun; ++irun) { ITsTextProps ttp = tsb.get_Properties(irun); int var; int ws = ttp.GetIntPropValues((int)FwTextPropType.ktptWs, out var); if (ws != wsVern) { fFixed = true; int ichMin; int ichLim; tsb.GetBoundsOfRun(irun, out ichMin, out ichLim); tsb.SetIntPropValues(ichMin, ichLim, (int)FwTextPropType.ktptWs, var, wsVern); } } if (fFixed) { args.TsString = tsb.GetString(); } args.EventHandled = true; }
/// ------------------------------------------------------------------------------------- /// <summary> /// This method gets called whenever the focused reference in the error pane changes. /// We respond by telling the draft view to scroll to and select the text of the new /// verse. /// </summary> /// <param name="sender"></param> /// <param name="e"></param> /// ------------------------------------------------------------------------------------- private void OnRefInGridChanged(object sender, CheckingError e) { // TE-6691 -- Not fully initialized yet so return if (EditingHelper == null) { return; } if (e != CheckingError.Empty) { EditingHelper.EditedRootBox.DestroySelection(); StTxtPara para = e.BeginObjectRA as StTxtPara; if (para != null && para.Owner is StFootnote) { // Checking error is for text in a footnote. Make sure the footnote pane is open. StFootnote footnote = para.Owner as StFootnote; ((IViewFootnotes)DraftView).ShowFootnoteView(footnote); } else { ((IViewFootnotes)DraftView).FootnoteViewFocused = false; } // Note: EditingHelper is actually the active editing helper, so the above // code can change its value. EditingHelper.GoToScrScriptureNoteRef(e); } }
/// ------------------------------------------------------------------------------------ /// <summary> /// Get the view selection and paragraph properties. /// </summary> /// <param name="vwsel">[out] The selection</param> /// <param name="hvoText">[out] The HVO</param> /// <param name="tagText">[out] The tag</param> /// <param name="vqvps">[out] The paragraph properties</param> /// <param name="ihvoFirst">[out] Start index of selection</param> /// <param name="ihvoLast">[out] End index of selection</param> /// <param name="vqttp">[out] The style rules</param> /// <returns>Return false if there is neither a selection nor a paragraph property. /// Otherwise return true.</returns> /// ------------------------------------------------------------------------------------ public bool GetParagraphProps(out IVwSelection vwsel, out int hvoText, out int tagText, out IVwPropertyStore[] vqvps, out int ihvoFirst, out int ihvoLast, out ITsTextProps[] vqttp) { CheckDisposed(); return(EditingHelper.GetParagraphProps(out vwsel, out hvoText, out tagText, out vqvps, out ihvoFirst, out ihvoLast, out vqttp)); }
/// ------------------------------------------------------------------------------------ /// <summary> /// Check for presence of proper paragraph properties. /// </summary> /// <param name="vwsel">[out] The selection</param> /// <param name="hvoText">[out] The HVO</param> /// <param name="tagText">[out] The tag</param> /// <param name="vqvps">[out] The paragraph properties</param> /// <param name="ihvoAnchor">[out] Start index of selection</param> /// <param name="ihvoEnd">[out] End index of selection</param> /// <returns>Return <c>false</c> if neither selection nor paragraph property. Otherwise /// return <c>true</c>.</returns> /// ------------------------------------------------------------------------------------ public bool IsParagraphProps(out IVwSelection vwsel, out int hvoText, out int tagText, out IVwPropertyStore[] vqvps, out int ihvoAnchor, out int ihvoEnd) { CheckDisposed(); return(EditingHelper.IsParagraphProps(out vwsel, out hvoText, out tagText, out vqvps, out ihvoAnchor, out ihvoEnd)); }
/// ----------------------------------------------------------------------------------- /// <summary> /// Handle a key press. /// </summary> /// <param name="keyChar">The pressed character key</param> /// ----------------------------------------------------------------------------------- public void HandleKeyPress(char keyChar) { CheckDisposed(); using (new HoldGraphics(this)) { EditingHelper.HandleKeyPress(keyChar, ModifierKeys); } }
public void TestTextRepOfObj_InvalidObject() { CheckDisposed(); EditingHelper editHelper = new EditingHelper(null); string sTextRepOfObject = editHelper.TextRepOfObj(m_fdoCache, Guid.Empty); Assert.IsNull(sTextRepOfObject); }
/// ------------------------------------------------------------------------------------ /// <summary> /// Watch for keys to do the cut/copy/paste operations /// </summary> /// <param name="e"></param> /// ------------------------------------------------------------------------------------ protected override void OnKeyDown(KeyEventArgs e) { m_sda.GetActionHandler().BeginUndoTask("dummy undo Key Down", "dummy redo Key Down"); if (!EditingHelper.HandleOnKeyDown(e)) { base.OnKeyDown(e); } m_sda.GetActionHandler().EndUndoTask(); }
/// ----------------------------------------------------------------------------------- /// <summary> /// Handle a key press. /// </summary> /// <param name="keyChar">The pressed character key</param> /// <param name="fCalledFromKeyDown">true if this method gets called from OnKeyDown /// (to handle Delete)</param> /// ----------------------------------------------------------------------------------- public void HandleKeyPress(char keyChar, bool fCalledFromKeyDown) { CheckDisposed(); using (new HoldGraphics(this)) { EditingHelper.HandleKeyPress(keyChar, fCalledFromKeyDown, ModifierKeys, m_graphicsManager.VwGraphics); } }
/// <summary> /// Try to keep the selection from including any of the characters in a writing system label. /// See LT-8396. /// </summary> protected override void HandleSelectionChange(IVwRootBox prootb, IVwSelection vwselNew) { base.HandleSelectionChange(prootb, vwselNew); // 1) We don't want to recurse into here. // 2) If the selection is invalid we can't use it. // 3) If the selection is entirely formattable ("IsSelectionInOneFormattableProp"), we don't need to do // anything. if (s_fProcessingSelectionChanged || !vwselNew.IsValid || EditingHelper.IsSelectionInOneFormattableProp()) { return; } try { s_fProcessingSelectionChanged = true; SelectionHelper hlpr = SelectionHelper.Create(vwselNew, this); bool fRange = hlpr.IsRange; bool fChangeRange = false; if (fRange) { bool fAnchorEditable = vwselNew.IsEditable; hlpr.GetIch(SelectionHelper.SelLimitType.Anchor); int tagAnchor = hlpr.GetTextPropId(SelectionHelper.SelLimitType.Anchor); hlpr.GetIch(SelectionHelper.SelLimitType.End); int tagEnd = hlpr.GetTextPropId(SelectionHelper.SelLimitType.End); bool fEndBeforeAnchor = vwselNew.EndBeforeAnchor; if (fEndBeforeAnchor) { if (fAnchorEditable && tagAnchor > 0 && tagEnd < 0) { hlpr.SetTextPropId(SelectionHelper.SelLimitType.End, tagAnchor); hlpr.SetIch(SelectionHelper.SelLimitType.End, 0); fChangeRange = true; } } else { if (!fAnchorEditable && tagAnchor < 0 && tagEnd > 0) { hlpr.SetTextPropId(SelectionHelper.SelLimitType.Anchor, tagEnd); hlpr.SetIch(SelectionHelper.SelLimitType.Anchor, 0); fChangeRange = true; } } } if (fChangeRange) { hlpr.SetSelection(true); } } finally { s_fProcessingSelectionChanged = false; } }
/// <summary> /// Create one. /// </summary> /// <param name="owner"></param> /// <param name="sda"></param> /// <param name="site"></param> /// <param name="updateDescription"></param> /// <param name="fTurnOnMonitor">if true, start monitoring. if false, suspend/disable monitoring.</param> /// <param name="fSuppressRecordingPriorSelection">True for operations (currently only ReplaceAll) /// where we do not want to record the prior selection. This prevents calling editinghelper.OnAboutToEdit, /// which therefore doesn't save info about the current selection. The current actual effect /// is to suppress the work of the AnnotationAdjuster for the selected paragraphs.</param> public DataUpdateMonitor(Control owner, ISilDataAccess sda, IVwRootSite site, string updateDescription, bool fTurnOnMonitor, bool fSuppressRecordingPriorSelection) { // DataUpdateMonitor may be nested, so make sure we're not already monitoring the site. m_fTurnOnMonitor = fTurnOnMonitor && (site == null || !s_sitesMonitoring.Contains(site)); if (!m_fTurnOnMonitor) { return; } // register this site as being monitored. if (site != null) { s_sitesMonitoring.Add(site); } Debug.Assert(sda != null); if (s_UpdateSemaphores.ContainsKey(sda)) { UpdateSemaphore semaphore = s_UpdateSemaphores[sda]; if (semaphore.fDataUpdateInProgress) { throw new Exception("Concurrent access on Database detected"); } // Set ((static semaphore) members) for this data update semaphore.fDataUpdateInProgress = true; semaphore.sDescr = updateDescription; } else { s_UpdateSemaphores[sda] = new UpdateSemaphore(true, updateDescription); } m_Owner = owner; m_sda = sda; m_rootSite = site; if (m_rootSite != null) { m_editingHelper = ((IRootSite)m_rootSite).EditingHelper; } // store original selection info. // Note, some of its internals are computed from the actual selection // which can get changed during the life of DataUpdateMonitor. // But its properties and Hvo(fEndPoint) should remain the same. if (m_editingHelper != null && !fSuppressRecordingPriorSelection) { m_tsi = new TextSelInfo(m_editingHelper.EditedRootBox); m_editingHelper.OnAboutToEdit(); } // Set wait cursor if (owner != null) { m_oldCursor = owner.Cursor; owner.Cursor = Cursors.WaitCursor; } }
/// ------------------------------------------------------------------------------------ /// <summary> /// Shows the Format Apply Style dialog. Apply the results to the selection of the active /// view if the user clicks OK. /// </summary> /// <param name="paraStyleName">Name of the para style.</param> /// <param name="charStyleName">Name of the char style.</param> /// <param name="maxStyleLevel">The maximum style level that will be shown in this /// dialog. (apps that do not use style levels in their stylesheets can pass 0)</param> /// ------------------------------------------------------------------------------------ public void ShowApplyStyleDialog(string paraStyleName, string charStyleName, int maxStyleLevel) { SimpleRootSite rootsite = (ActiveView as SimpleRootSite); try { if (rootsite != null) { rootsite.ShowRangeSelAfterLostFocus = true; } int hvoRoot, frag; IVwViewConstructor vc; IVwStylesheet ss; ActiveView.CastAsIVwRootSite().RootBox.GetRootObject(out hvoRoot, out vc, out frag, out ss); using (FwApplyStyleDlg applyStyleDlg = new FwApplyStyleDlg(ActiveView.CastAsIVwRootSite(), Cache, StyleSheet.RootObjectHvo, StyleSheetOwningFlid, StyleSheet.GetDefaultBasedOnStyleName(), maxStyleLevel, paraStyleName, charStyleName, hvoRoot, FwApp.App, FwApp.App)) { if (FwEditingHelper != null) { if (FwEditingHelper.ApplicableStyleContexts != null) { applyStyleDlg.ApplicableStyleContexts = FwEditingHelper.ApplicableStyleContexts; } } else if (FwApp.App != null) { // Window doesn't have an editing helper, go with whole-app default if (FwApp.App.DefaultStyleContexts != null) { applyStyleDlg.ApplicableStyleContexts = FwApp.App.DefaultStyleContexts; } } applyStyleDlg.AllowSelectStyleTypes = m_callbacks.ShowSelectStylesComboInStylesDialog; IVwSelection sel = EditingHelper.CurrentSelection.Selection; applyStyleDlg.CanApplyCharacterStyle = sel.CanFormatChar; applyStyleDlg.CanApplyParagraphStyle = sel.CanFormatPara; if (applyStyleDlg.ShowDialog(m_callbacks) == DialogResult.OK) { EditingHelper.ApplyStyle(applyStyleDlg.StyleChosen); } } } finally { if (rootsite != null) { rootsite.ShowRangeSelAfterLostFocus = false; } } }
/// ------------------------------------------------------------------------------------ /// <summary> /// Adds to para BLDR. /// </summary> /// ------------------------------------------------------------------------------------ internal override void AddToParaBldr(StTxtParaBldr bldr, int ws, FwStyleSheet styleSheet) { if (string.IsNullOrEmpty(Href)) { throw new Exception("OXESA validation found an invalid URL."); } bool fSucceeded = EditingHelper.AddHyperlink(bldr.StringBuilder, ws, Text, Href, styleSheet); Debug.Assert(fSucceeded); }
/// ------------------------------------------------------------------------------------ /// <summary> /// Executes in two distinct scenarios. /// 1. If disposing is true, the method has been called directly /// or indirectly by a user's code via the Dispose method. /// Both managed and unmanaged resources can be disposed. /// 2. If disposing is false, the method has been called by the /// runtime from inside the finalizer and you should not reference (access) /// other managed objects, as they already have been garbage collected. /// Only unmanaged resources can be disposed. /// </summary> /// <param name="disposing"></param> /// <remarks> /// If any exceptions are thrown, that is fine. /// If the method is being done in a finalizer, it will be ignored. /// If it is thrown by client code calling Dispose, /// it needs to be handled by fixing the bug. /// If subclasses override this method, they should call the base implementation. /// </remarks> /// ------------------------------------------------------------------------------------ protected override void Dispose(bool disposing) { if (disposing) { if (m_decoratedEditingHelper != null) { m_decoratedEditingHelper.Dispose(); } } m_decoratedEditingHelper = null; base.Dispose(disposing); }
public void TestTextRepOfObj_CmPicture() { CheckDisposed(); string internalPathOrig = null; string internalPathNew = null; try { using (DummyFileMaker filemaker = new DummyFileMaker("junk.jpg", true)) { ITsStrFactory factory = TsStrFactoryClass.Create(); EditingHelper editHelper = new EditingHelper(null); CmPicture pict = new CmPicture(m_fdoCache, filemaker.Filename, factory.MakeString("Test picture", m_fdoCache.DefaultVernWs), StringUtils.LocalPictures); Assert.IsNotNull(pict); Assert.IsTrue(pict.PictureFileRA.AbsoluteInternalPath == pict.PictureFileRA.InternalPath); Guid guid = Cache.GetGuidFromId(pict.Hvo); string sTextRepOfObject = editHelper.TextRepOfObj(m_fdoCache, guid); int objectDataType; guid = editHelper.MakeObjFromText(m_fdoCache, sTextRepOfObject, null, out objectDataType); CmPicture pictNew = new CmPicture(Cache, Cache.GetIdFromGuid(guid)); Assert.IsTrue(pict != pictNew); internalPathOrig = pict.PictureFileRA.AbsoluteInternalPath; internalPathNew = pictNew.PictureFileRA.AbsoluteInternalPath; Assert.AreEqual(internalPathOrig, internalPathNew); Assert.AreEqual(internalPathOrig.IndexOf("junk"), internalPathNew.IndexOf("junk")); Assert.IsTrue(internalPathNew.EndsWith(".jpg")); AssertEx.AreTsStringsEqual(pict.Caption.VernacularDefaultWritingSystem.UnderlyingTsString, pictNew.Caption.VernacularDefaultWritingSystem.UnderlyingTsString); Assert.AreEqual(pict.PictureFileRA.OwnerHVO, pictNew.PictureFileRA.OwnerHVO); } } finally { // TODO: When Undo works right, these should get cleaned up automatically if (internalPathOrig != null) { File.Delete(internalPathOrig); } if (internalPathNew != null) { File.Delete(internalPathNew); } } }
public void HandleKeyDownAndKeyPress(Keys key) { KeyEventArgs kea = new KeyEventArgs(key); if (EditingHelper.HandleOnKeyDown(kea)) { return; } OnKeyDown(kea); // for some reason OnKeyPress does not handle Delete key // In FLEX, OnKeyPress does not even get called for Delete key. if (key != Keys.Delete) { OnKeyPress(new KeyPressEventArgs((char)kea.KeyValue)); } }
/// ------------------------------------------------------------------------------------ /// <summary> /// /// </summary> /// <param name="e"></param> /// ------------------------------------------------------------------------------------ protected override void OnMouseUp(MouseEventArgs e) { if (e.Button != MouseButtons.Right) { base.OnMouseUp(e); return; } FwMainWnd mainWnd = TheMainWnd as FwMainWnd; if (mainWnd != null && mainWnd.TMAdapter != null) { EditingHelper.ShowContextMenu(e.Location, mainWnd.TMAdapter, this, "cmnuFootnoteView", "cmnuAddToDictFV", "cmnuChangeMultiOccurencesFV", "cmnuAddToDictFV", ((FootnoteEditingHelper)EditingHelper).ShowSpellingErrors); } }
/// <summary> /// Executes in two distinct scenarios. /// /// 1. If disposing is true, the method has been called directly /// or indirectly by a user's code via the Dispose method. /// Both managed and unmanaged resources can be disposed. /// /// 2. If disposing is false, the method has been called by the /// runtime from inside the finalizer and you should not reference (access) /// other managed objects, as they already have been garbage collected. /// Only unmanaged resources can be disposed. /// </summary> /// <param name="disposing"></param> /// <remarks> /// If any exceptions are thrown, that is fine. /// If the method is being done in a finalizer, it will be ignored. /// If it is thrown by client code calling Dispose, /// it needs to be handled by fixing the bug. /// /// If subclasses override this method, they should call the base implementation. /// </remarks> protected override void Dispose(bool disposing) { //Debug.WriteLineIf(!disposing, "****************** " + GetType().Name + " 'disposing' is false. ******************"); // Must not be run more than once. if (IsDisposed) return; if (disposing) { // Dispose managed resources here. } // Dispose unmanaged resources here, whether disposing is true or false. m_mockedEditingHelper = null; base.Dispose(disposing); }
/// <summary> /// Executes in two distinct scenarios. /// /// 1. If disposing is true, the method has been called directly /// or indirectly by a user's code via the Dispose method. /// Both managed and unmanaged resources can be disposed. /// /// 2. If disposing is false, the method has been called by the /// runtime from inside the finalizer and you should not reference (access) /// other managed objects, as they already have been garbage collected. /// Only unmanaged resources can be disposed. /// </summary> /// <param name="disposing"></param> /// <remarks> /// If any exceptions are thrown, that is fine. /// If the method is being done in a finalizer, it will be ignored. /// If it is thrown by client code calling Dispose, /// it needs to be handled by fixing the bug. /// /// If subclasses override this method, they should call the base implementation. /// </remarks> protected override void Dispose(bool disposing) { //Debug.WriteLineIf(!disposing, "****************** " + GetType().Name + " 'disposing' is false. ******************"); // Must not be run more than once. if (IsDisposed) { return; } if (disposing) { // Dispose managed resources here. } // Dispose unmanaged resources here, whether disposing is true or false. m_mockedEditingHelper = null; base.Dispose(disposing); }
public void SetTsStringOnClipboard_UsesNFC() { var wsManager = new WritingSystemManager(); CoreWritingSystemDefinition enWs; wsManager.GetOrSet("en", out enWs); int wsEng = enWs.Handle; var originalInput = "\x7a7a\x60f3\x79d1\x5b78\x0020\xd558"; var input = originalInput.Normalize(NormalizationForm.FormD); Assert.That(originalInput, Is.Not.EqualTo(input)); // make sure input is NOT NFC var tss = TsStringUtils.MakeString(input, wsEng); EditingHelper.SetTsStringOnClipboard(tss, false, wsManager); var newDataObj = ClipboardUtils.GetDataObject(); Assert.IsNotNull(newDataObj, "Couldn't get DataObject from clipboard"); Assert.AreEqual(originalInput, newDataObj.GetData("Text")); }
public AnnotationAdjuster(FdoCache cache, EditingHelper parent) { m_cache = cache; m_parentEditingHelper = parent; m_segDefn_note = cache.GetIdFromGuid(LangProject.kguidAnnNote); m_hvoSegDefn = cache.GetIdFromGuid(LangProject.kguidAnnTextSegment); if (m_segDefn_note == 0 || m_hvoSegDefn == 0) { m_segDefn_note = 0; // used to subsequently test for in-memory test disabling things. return; // In-memory testing, can't do anything useful. } m_cache.MainCacheAccessor.AddNotification(this); m_hvoAnnDefnTwfic = CmAnnotationDefn.Twfic(cache).Hvo; m_hvoSegDefn = CmAnnotationDefn.TextSegment(cache).Hvo; m_hvoPunctDefn = CmAnnotationDefn.Punctuation(cache).Hvo; m_hvoAnnErrorDefn = CmAnnotationDefn.Errors(cache).Hvo; m_segDefn_note = cache.GetIdFromGuid(LangProject.kguidAnnNote); }
/// ------------------------------------------------------------------------------------ /// <summary> /// Handles OnKeyPress to remove any formatting before passing the keystroke along. This /// prevents the gray background used for ORC strings from bleeding over into literal /// text. /// </summary> /// ------------------------------------------------------------------------------------ protected override void OnKeyPress(KeyPressEventArgs e) { if (EditingHelper == null) { return; } IVwSelection vwsel; ITsTextProps[] vttp; IVwPropertyStore[] vvps; if (!EditingHelper.GetCharacterProps(out vwsel, out vttp, out vvps)) { return; } EditingHelper.RemoveCharFormatting(vwsel, ref vttp, null, true); base.OnKeyPress(e); }
public override void SelectionChanged(IVwRootBox rootb, IVwSelection vwselNew) { CheckDisposed(); base.SelectionChanged(rootb, vwselNew); // JohnT: it's remotely possible that the base, in calling commit, made this // selection no longer useable. if (!vwselNew.IsValid) { return; } SelectionHelper helper = SelectionHelper.Create(vwselNew, this); if (helper != null && helper.GetTextPropId(SelectionHelper.SelLimitType.Anchor) == RawTextVc.kTagUserPrompt) { vwselNew.ExtendToStringBoundaries(); EditingHelper.SetKeyboardForSelection(vwselNew); } }
public void AddHyperlink() { ITsStrBldr strBldr = TsStrBldrClass.Create(); DynamicMock mockStylesheet = new DynamicMock(typeof(FwStyleSheet)); mockStylesheet.Strict = true; DynamicMock mockHyperlinkStyle = new DynamicMock(typeof(StStyle)); mockHyperlinkStyle.Strict = true; mockHyperlinkStyle.Expect("InUse", true); mockStylesheet.ExpectAndReturn("FindStyle", (IStStyle)mockHyperlinkStyle.MockInstance, StStyle.Hyperlink); FwStyleSheet styleSheet = (FwStyleSheet)mockStylesheet.MockInstance; Assert.IsTrue(EditingHelper.AddHyperlink(strBldr, Cache.DefaultAnalWs, "Click Here", "www.google.com", styleSheet)); Assert.AreEqual(1, strBldr.RunCount); Assert.AreEqual("Click Here", strBldr.get_RunText(0)); ITsTextProps props = strBldr.get_Properties(0); StStyleTests.AssertHyperlinkPropsAreCorrect(props, Cache.DefaultAnalWs, "www.google.com"); mockHyperlinkStyle.Verify(); mockStylesheet.Verify(); }
public void PasteMultiParasIntoFootnote() { IScrBook book = AddBookToMockedScripture(1, "Genesis"); IScrSection section = AddSectionToMockedBook(book); IStTxtPara para = AddParaToMockedSectionContent(section, ScrStyleNames.NormalParagraph); AddRunToMockedPara(para, "Scripture text", Cache.DefaultVernWs); IStFootnote footnote1 = AddFootnote(book, para, 9, "footnote 1"); int footnote1Length = ((IStTxtPara)footnote1.ParagraphsOS[0]).Contents.Length; m_hvoRoot = Cache.LangProject.TranslatedScriptureOA.Hvo; ShowForm(DummyBasicViewVc.DisplayType.kNormal); IVwRootBox rootBox = m_basicView.RootBox; ITsStrBldr strBldr = TsStrBldrClass.Create(); strBldr.Replace(0, 0, "New heading line1", StyleUtils.CharStyleTextProps(null, Cache.DefaultVernWs)); strBldr.Replace(strBldr.Length, strBldr.Length, Environment.NewLine, StyleUtils.ParaStyleTextProps(ScrStyleNames.NormalParagraph)); strBldr.Replace(strBldr.Length, strBldr.Length, "heading line2" + Environment.NewLine + "New heading line3", StyleUtils.CharStyleTextProps(null, Cache.DefaultVernWs)); EditingHelper.SetTsStringOnClipboard(strBldr.GetString(), false, Cache.WritingSystemFactory); // Make selection at the end of the footnote Assert.IsNotNull(rootBox.MakeSimpleSel(false, true, false, true)); // Paste contents of clipboard at current selection. m_basicView.EditingHelper.PasteClipboard(); // We expect that the footnote will only have one paragraph with the extra paragraphs // concatenated, separated with spaces. Assert.AreEqual(1, footnote1.ParagraphsOS.Count); Assert.AreEqual("footnote 1New heading line1 heading line2 New heading line3", ((IStTxtPara)footnote1.ParagraphsOS[0]).Contents.Text); }
/// ----------------------------------------------------------------------------------- /// <summary> /// Clean up any resources being used. /// </summary> /// ----------------------------------------------------------------------------------- protected override void Dispose(bool disposing) { //Debug.WriteLineIf(!disposing, "****************** " + GetType().Name + " 'disposing' is false. ******************"); // Must not be run more than once. if (IsDisposed) return; if (disposing) { // Do as early as possible, we don't want to do more spell checking while being disposed. Application.Idle -= new EventHandler(Application_Idle); // Do this here, before disposing m_messageSequencer, // as we still get messages during dispose. // Once the the base class has shut down the window handle, // we are good to go on. // If we find we getting messages during this call, // we will be forced to call DestroyHandle() first. // That is done part way through the base method code, // but it may not be soon enough, for all the re-entrant events // that keep showing up. DestroyHandle(); } base.Dispose(disposing); if (disposing) { if (m_rootb != null) CloseRootBox(); if (g_focusRootSite == this) g_focusRootSite = null; if (m_Timer != null) { m_Timer.Stop(); m_Timer.Tick -= new EventHandler(OnTimer); m_Timer.Dispose(); } // Remove the filter when we are disposed now. if (m_messageFilterInstalled) { Application.RemoveMessageFilter(this); m_messageFilterInstalled = false; } if (m_editingHelper != null) m_editingHelper.Dispose(); if (m_messageSequencer != null) m_messageSequencer.Dispose(); if (m_graphicsManager != null) { // Uninit() first in case we're in the middle of displaying something. See LT-7365. m_graphicsManager.Uninit(); m_graphicsManager.Dispose(); } if (components != null) components.Dispose(); } if (m_vdrb != null && Marshal.IsComObject(m_vdrb)) Marshal.ReleaseComObject(m_vdrb); m_vdrb = null; if (m_styleSheet != null && Marshal.IsComObject(m_styleSheet)) Marshal.ReleaseComObject(m_styleSheet); m_rootb = null; m_styleSheet = null; m_graphicsManager = null; m_editingHelper = null; m_Timer = null; m_mediator = null; m_wsf = null; m_styleSheet = null; m_savedSelection = null; m_messageSequencer = null; // Don't do it here. //base.Dispose( disposing ); m_fDisposed = true; }
/// ------------------------------------------------------------------------------------ /// <summary> /// Provides access to EditingHelper.ApplyStyle /// </summary> /// <param name="sStyleToApply"></param> /// ------------------------------------------------------------------------------------ public void ApplyStyle(string sStyleToApply) { CheckDisposed(); EditingHelper.ApplyStyle(sStyleToApply); }
/// <summary> /// If the text for pasting is too long, truncate it and warn the user. /// </summary> void OnPasteFixTssEvent(EditingHelper sender, FwPasteFixTssEventArgs e) { if (!sender.Editable) return; TextSelInfo tsi = sender.TextSelInfoBeforeEdit; if (tsi == null) return; int flid = tsi.TagAnchor; if (flid == 0) return; TruncatePasteIfNecessary(e, flid); }
/// <summary> /// If the text for pasting is too long, truncate it and warn the user. /// </summary> void OnPasteFixTssEvent(EditingHelper sender, FwPasteFixTssEventArgs e) { EliminateExtraStyleAndWsInfo(RootBox.DataAccess.MetaDataCache, e, m_flid); }
/// <summary> /// If the text for pasting is too long, truncate it and warn the user. /// </summary> void OnPasteFixTssEvent(EditingHelper sender, FwPasteFixTssEventArgs e) { TruncatePasteIfNecessary(e, m_flid); }
/// <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; }
public void TestTextRepOfObj_CmPicture() { CheckDisposed(); string internalPathOrig = null; string internalPathNew = null; try { using (DummyFileMaker filemaker = new DummyFileMaker("junk.jpg", true)) { ITsStrFactory factory = TsStrFactoryClass.Create(); EditingHelper editHelper = new EditingHelper(null); CmPicture pict = new CmPicture(m_fdoCache, filemaker.Filename, factory.MakeString("Test picture", m_fdoCache.DefaultVernWs), StringUtils.LocalPictures); Assert.IsNotNull(pict); Assert.IsTrue(pict.PictureFileRA.AbsoluteInternalPath == pict.PictureFileRA.InternalPath); Guid guid = Cache.GetGuidFromId(pict.Hvo); string sTextRepOfObject = editHelper.TextRepOfObj(m_fdoCache, guid); int objectDataType; guid = editHelper.MakeObjFromText(m_fdoCache, sTextRepOfObject, null, out objectDataType); CmPicture pictNew = new CmPicture(Cache, Cache.GetIdFromGuid(guid)); Assert.IsTrue(pict != pictNew); internalPathOrig = pict.PictureFileRA.AbsoluteInternalPath; internalPathNew = pictNew.PictureFileRA.AbsoluteInternalPath; Assert.AreEqual(internalPathOrig, internalPathNew); Assert.AreEqual(internalPathOrig.IndexOf("junk"), internalPathNew.IndexOf("junk")); Assert.IsTrue(internalPathNew.EndsWith(".jpg")); AssertEx.AreTsStringsEqual(pict.Caption.VernacularDefaultWritingSystem.UnderlyingTsString, pictNew.Caption.VernacularDefaultWritingSystem.UnderlyingTsString); Assert.AreEqual(pict.PictureFileRA.OwnerHVO, pictNew.PictureFileRA.OwnerHVO); } } finally { // TODO: When Undo works right, these should get cleaned up automatically if (internalPathOrig != null) File.Delete(internalPathOrig); if (internalPathNew != null) File.Delete(internalPathNew); } }
/// <summary> /// Create one. /// </summary> /// <param name="owner"></param> /// <param name="sda"></param> /// <param name="site"></param> /// <param name="updateDescription"></param> /// <param name="fTurnOnMonitor">if true, start monitoring. if false, suspend/disable monitoring.</param> /// <param name="fSuppressRecordingPriorSelection">True for operations (currently only ReplaceAll) /// where we do not want to record the prior selection. This prevents calling editinghelper.OnAboutToEdit, /// which therefore doesn't save info about the current selection. The current actual effect /// is to suppress the work of the AnnotationAdjuster for the selected paragraphs.</param> public DataUpdateMonitor(Control owner, ISilDataAccess sda, IVwRootSite site, string updateDescription, bool fTurnOnMonitor, bool fSuppressRecordingPriorSelection) { // DataUpdateMonitor may be nested, so make sure we're not already monitoring the site. m_fTurnOnMonitor = fTurnOnMonitor && (site == null || !s_sitesMonitoring.Contains(site)); if (!m_fTurnOnMonitor) return; // register this site as being monitored. if (site != null) s_sitesMonitoring.Add(site); Debug.Assert(sda != null); if (s_UpdateSemaphores.ContainsKey(sda)) { UpdateSemaphore semaphore = s_UpdateSemaphores[sda]; if (semaphore.fDataUpdateInProgress) throw new Exception("Concurrent access on Database detected"); // Set ((static semaphore) members) for this data update semaphore.fDataUpdateInProgress = true; semaphore.sDescr = updateDescription; } else { s_UpdateSemaphores[sda] = new UpdateSemaphore(true, updateDescription); } m_Owner = owner; m_sda = sda; m_rootSite = site; if (m_rootSite != null) m_editingHelper = ((IRootSite)m_rootSite).EditingHelper; // store original selection info. // Note, some of its internals are computed from the actual selection // which can get changed during the life of DataUpdateMonitor. // But its properties and Hvo(fEndPoint) should remain the same. if (m_editingHelper != null && !fSuppressRecordingPriorSelection) { m_tsi = new TextSelInfo(m_editingHelper.EditedRootBox); m_editingHelper.OnAboutToEdit(); } // Set wait cursor if (owner != null) { m_oldCursor = owner.Cursor; owner.Cursor = Cursors.WaitCursor; } }
/// ----------------------------------------------------------------------------------- /// <summary> /// If the mousePos is part of a word that is not properly spelled, add to the menu /// options for correcting it. /// </summary> /// <param name="pt">The location on the screen of the word for which we want spelling /// suggestions (usually the mouse position)</param> /// <param name="rootsite">The focused rootsite</param> /// <param name="menu">to add items to.</param> /// <returns>the number of menu items added (not counting a possible separator line)</returns> /// ----------------------------------------------------------------------------------- public virtual int MakeSpellCheckMenuOptions(Point pt, SimpleRootSite rootsite, ContextMenuStrip menu) { int hvoObj, tag, wsAlt, wsText; string word; ISpellEngine dict; bool nonSpellingError; ICollection <SpellCorrectMenuItem> suggestions = GetSuggestions(pt, rootsite, out hvoObj, out tag, out wsAlt, out wsText, out word, out dict, out nonSpellingError); if (suggestions == null) { return(0); } // no detectable spelling problem. // Note that items are inserted in order starting at the beginning, rather than // added to the end. This is to support TE-6901. // If the menu isn't empty, add a separator. if (menu.Items.Count > 0) { menu.Items.Insert(0, new ToolStripSeparator()); } // Make the menu option. ToolStripMenuItem itemExtras = null; int cSuggestions = 0; int iMenuItem = 0; IVwRootBox rootb = rootsite.RootBox; foreach (SpellCorrectMenuItem subItem in suggestions) { subItem.Click += spellingMenuItemClick; if (cSuggestions++ < RootSiteEditingHelper.kMaxSpellingSuggestionsInRootMenu) { Font createdFont = null; try { Font font = subItem.Font; if (wsText != 0) { font = createdFont = EditingHelper.GetFontForNormalStyle(wsText, rootb.Stylesheet, rootb.DataAccess.WritingSystemFactory); //string familyName = rootb.DataAccess.WritingSystemFactory.get_EngineOrNull(wsText).DefaultBodyFont; //font = new Font(familyName, font.Size, FontStyle.Bold); } subItem.Font = new Font(font, FontStyle.Bold); menu.Items.Insert(iMenuItem++, subItem); } finally { if (createdFont != null) { createdFont.Dispose(); } } } else { if (itemExtras == null) { itemExtras = new ToolStripMenuItem(RootSiteStrings.ksAdditionalSuggestions); menu.Items.Insert(iMenuItem++, itemExtras); } itemExtras.DropDownItems.Add(subItem); } } if (suggestions.Count == 0) { ToolStripMenuItem noSuggestItems = new ToolStripMenuItem(RootSiteStrings.ksNoSuggestions); menu.Items.Insert(iMenuItem++, noSuggestItems); noSuggestItems.Enabled = false; } ToolStripMenuItem itemAdd = new AddToDictMenuItem(dict, word, rootb, hvoObj, tag, wsAlt, wsText, RootSiteStrings.ksAddToDictionary, m_cache); if (nonSpellingError) { itemAdd.Enabled = false; } menu.Items.Insert(iMenuItem++, itemAdd); itemAdd.Image = Resources.ResourceHelper.SpellingIcon; itemAdd.Click += spellingMenuItemClick; return(iMenuItem); }
/// <summary> /// If the text for pasting is too long, truncate it and warn the user. /// </summary> void OnPasteFixTssEvent(EditingHelper sender, FwPasteFixTssEventArgs e) { if (!sender.Editable) return; TextSelInfo tsi = e.TextSelInfo; if (tsi == null) return; int flid = tsi.TagAnchor; if (flid == 0) return; }
/// ----------------------------------------------------------------------------------- /// <summary> /// Clean up any resources being used. /// </summary> /// ----------------------------------------------------------------------------------- protected override void Dispose(bool disposing) { Debug.WriteLineIf(!disposing, "****************** Missing Dispose() call for " + GetType().Name + "******************"); // Must not be run more than once. if (IsDisposed) return; if (disposing) { // Do this here, before disposing m_messageSequencer, // as we still get messages during dispose. // Once the the base class has shut down the window handle, // we are good to go on. // If we find we getting messages during this call, // we will be forced to call DestroyHandle() first. // That is done part way through the base method code, // but it may not be soon enough, for all the re-entrant events // that keep showing up. m_fAllowLayout = false; DestroyHandle(); } base.Dispose(disposing); if (disposing) { if (m_rootb != null) CloseRootBox(); if (m_Timer != null) { m_Timer.Stop(); m_Timer.Tick -= OnTimer; m_Timer.Dispose(); } // Remove the filter when we are disposed now. if (m_messageFilterInstalled) { Application.RemoveMessageFilter(this); m_messageFilterInstalled = false; } if (m_editingHelper != null) m_editingHelper.Dispose(); if (m_messageSequencer != null) m_messageSequencer.Dispose(); if (m_graphicsManager != null) { // Uninit() first in case we're in the middle of displaying something. See LT-7365. m_graphicsManager.Uninit(); m_graphicsManager.Dispose(); } if (components != null) components.Dispose(); #if __MonoCS__ KeyboardController.Unregister(this); #endif } if (m_vdrb != null && Marshal.IsComObject(m_vdrb)) Marshal.ReleaseComObject(m_vdrb); m_vdrb = null; if (m_styleSheet != null && Marshal.IsComObject(m_styleSheet)) Marshal.ReleaseComObject(m_styleSheet); if (m_rootb != null && Marshal.IsComObject(m_rootb)) Marshal.ReleaseComObject(m_rootb); m_rootb = null; m_styleSheet = null; m_graphicsManager = null; m_editingHelper = null; m_Timer = null; m_mediator = null; m_wsf = null; m_messageSequencer = null; // Don't do it here. //base.Dispose( disposing ); m_fDisposed = true; }
/// ------------------------------------------------------------------------------------ /// <summary> /// Initializes a new instance of the <see cref="T:PubEditingHelper"/> class. /// </summary> /// <param name="decoratedEditingHelper">The editing helper we decorate.</param> /// ------------------------------------------------------------------------------------ public PubEditingHelper(EditingHelper decoratedEditingHelper) : this(decoratedEditingHelper, null) { }
/// ------------------------------------------------------------------------------------ /// <summary> /// Removes the hard formatting from text that is pasted. /// </summary> /// <param name="sender">The sender.</param> /// <param name="e">The <see cref="T:SIL.FieldWorks.Common.RootSites.FwPasteFixTssEventArgs"/> /// instance containing the event data.</param> /// ------------------------------------------------------------------------------------ public void RemoveHardFormatting(EditingHelper sender, FwPasteFixTssEventArgs e) { ITsString tss = e.TsString; ITsStrBldr tssBldr = tss.GetBldr(); for (int i = 0; i < tss.RunCount; i++) { TsRunInfo runInfo; ITsTextProps props = tss.FetchRunInfo(i, out runInfo); ITsPropsBldr propsBuilder = TsPropsBldrClass.Create(); // Copy the style name from the run properties string style = props.GetStrPropValue((int)FwTextPropType.ktptNamedStyle); if (style != null) propsBuilder.SetStrPropValue((int)FwTextPropType.ktptNamedStyle, style); // Copy the WS information from the run properties int var; int ws = props.GetIntPropValues((int)FwTextPropType.ktptWs, out var); if (ws > 0) propsBuilder.SetIntPropValues((int)FwTextPropType.ktptWs, var, ws); // Copy the ORC information from the run properties string orcData = props.GetStrPropValue((int)FwTextPropType.ktptObjData); if (orcData != null) propsBuilder.SetStrPropValue((int)FwTextPropType.ktptObjData, orcData); tssBldr.SetProperties(runInfo.ichMin, runInfo.ichLim, propsBuilder.GetTextProps()); } e.TsString = tssBldr.GetString(); }
/// <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> /// Handle fixing the writing system values inside pasted text. Eventually, we may /// want some form of UI to guide the process, but for now we just bash in the /// default vernacular ws wherever any other ws occurs. /// </summary> /// <param name="sender"></param> /// <param name="args"></param> private void OnPasteFixWs(EditingHelper sender, FwPasteFixTssEventArgs args) { if (args.EventHandled) return; // only one delegate gets to play! bool fFixed = false; int wsVern = Cache.LangProject.ActualWs(LangProject.kwsVernInParagraph, m_hvoRoot, (int)StText.StTextTags.kflidParagraphs); ITsStrBldr tsb = args.TsString.GetBldr(); int crun = tsb.RunCount; for (int irun = 0; irun < crun; ++irun) { ITsTextProps ttp = tsb.get_Properties(irun); int var; int ws = ttp.GetIntPropValues((int)FwTextPropType.ktptWs, out var); if (ws != wsVern) { fFixed = true; int ichMin; int ichLim; tsb.GetBoundsOfRun(irun, out ichMin, out ichLim); tsb.SetIntPropValues(ichMin, ichLim, (int)FwTextPropType.ktptWs, var, wsVern); } } if (fFixed) args.TsString = tsb.GetString(); args.EventHandled = true; }