| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| // MARKER(update_precomp.py): autogen include statement, do not remove |
| #include "precompiled_cui.hxx" |
| |
| // include --------------------------------------------------------------- |
| |
| #include <tools/ref.hxx> |
| #include <tools/shl.hxx> |
| #include <vcl/wrkwin.hxx> |
| #include <vcl/menu.hxx> |
| #include <vcl/msgbox.hxx> |
| #include <vcl/scrbar.hxx> |
| #include <SpellAttrib.hxx> |
| #include <sfx2/dispatch.hxx> |
| #include <sfx2/bindings.hxx> |
| #include <svl/undo.hxx> |
| #include <unotools/lingucfg.hxx> |
| #include <svtools/textdata.hxx> |
| #include <svtools/filter.hxx> |
| #include <editeng/unolingu.hxx> |
| #include <editeng/splwrap.hxx> |
| #include <linguistic/lngprops.hxx> |
| #include <linguistic/misc.hxx> |
| #include <comphelper/processfactory.hxx> |
| #include <com/sun/star/lang/XMultiServiceFactory.hpp> |
| #include <com/sun/star/lang/XServiceInfo.hpp> |
| #include <com/sun/star/lang/XServiceDisplayName.hpp> |
| #include <com/sun/star/linguistic2/SpellFailure.hpp> |
| #include <com/sun/star/frame/XStorable.hpp> |
| #include <sfx2/app.hxx> |
| #include <vcl/help.hxx> |
| #include <vcl/graph.hxx> |
| #include <osl/file.hxx> |
| #include <cuires.hrc> |
| #include <helpid.hrc> |
| #include "SpellDialog.hrc" |
| #include <editeng/optitems.hxx> |
| #include <editeng/svxenum.hxx> |
| #include <svx/SpellDialogChildWindow.hxx> |
| #include "SpellDialog.hxx" |
| #include <svx/dlgutil.hxx> |
| #include "optlingu.hxx" |
| #include <dialmgr.hxx> |
| #include <svx/svxerr.hxx> |
| #include "treeopt.hxx" |
| #include <svtools/langtab.hxx> |
| |
| using namespace ::com::sun::star; |
| using namespace ::com::sun::star::uno; |
| using namespace ::com::sun::star::beans; |
| using namespace ::com::sun::star::linguistic2; |
| |
| using ::rtl::OUString; |
| |
| #define C2U(cChar) ::rtl::OUString::createFromAscii(cChar) |
| // struct SpellDialog_Impl --------------------------------------------- |
| |
| struct SpellDialog_Impl |
| { |
| Sequence< Reference< XDictionary > > aDics; |
| }; |
| // ----------------------------------------------------------------------- |
| //#define VENDOR_IMAGE_HEIGHT 44 //as specified |
| |
| #define SPELLUNDO_START 200 |
| |
| #define SPELLUNDO_CHANGE_LANGUAGE (SPELLUNDO_START + 1) |
| #define SPELLUNDO_CHANGE_TEXTENGINE (SPELLUNDO_START + 2) |
| #define SPELLUNDO_CHANGE_NEXTERROR (SPELLUNDO_START + 3) |
| #define SPELLUNDO_CHANGE_ADD_TO_DICTIONARY (SPELLUNDO_START + 4) |
| #define SPELLUNDO_CHANGE_GROUP (SPELLUNDO_START + 5) //undo list |
| #define SPELLUNDO_MOVE_ERROREND (SPELLUNDO_START + 6) |
| #define SPELLUNDO_UNDO_EDIT_MODE (SPELLUNDO_START + 7) |
| #define SPELLUNDO_ADD_IGNORE_RULE (SPELLUNDO_START + 8) |
| |
| namespace svx{ |
| class SpellUndoAction_Impl : public SfxUndoAction |
| { |
| sal_uInt16 m_nId; |
| const Link& m_rActionLink; |
| //undo of button enabling |
| bool m_bEnableChangePB; |
| bool m_bEnableChangeAllPB; |
| //undo of MarkNextError - used in change and change all, ignore and ignore all |
| long m_nNewErrorStart; |
| long m_nNewErrorEnd; |
| long m_nOldErrorStart; |
| long m_nOldErrorEnd; |
| bool m_bIsErrorLanguageSelected; |
| ::rtl::OUString m_sRuleId; |
| //undo of AddToDictionary |
| Reference<XDictionary> m_xDictionary; |
| ::rtl::OUString m_sAddedWord; |
| //move end of error - ::ChangeMarkedWord() |
| long m_nOffset; |
| |
| public: |
| SpellUndoAction_Impl(sal_uInt16 nId, const Link& rActionLink) : |
| m_nId(nId), |
| m_rActionLink( rActionLink), |
| m_bEnableChangePB(false), |
| m_bEnableChangeAllPB(false), |
| m_nNewErrorStart(-1), |
| m_nNewErrorEnd(-1), |
| m_nOldErrorStart(-1), |
| m_nOldErrorEnd(-1), |
| m_bIsErrorLanguageSelected(false), |
| m_nOffset(0) |
| {} |
| |
| ~SpellUndoAction_Impl(); |
| |
| virtual void Undo(); |
| virtual sal_uInt16 GetId() const; |
| |
| void SetEnableChangePB(){m_bEnableChangePB = true;} |
| bool IsEnableChangePB(){return m_bEnableChangePB;} |
| |
| void SetEnableChangeAllPB(){m_bEnableChangeAllPB = true;} |
| bool IsEnableChangeAllPB(){return m_bEnableChangeAllPB;} |
| |
| void SetErrorMove(long nNewStart, long nNewEnd, long nOldStart, long nOldEnd) |
| { |
| m_nNewErrorStart = nNewStart; |
| m_nNewErrorEnd = nNewEnd; |
| m_nOldErrorStart = nOldStart; |
| m_nOldErrorEnd = nOldEnd; |
| } |
| long GetNewErrorStart() { return m_nNewErrorStart;} |
| long GetNewErrorEnd() { return m_nNewErrorEnd;} |
| long GetOldErrorStart() { return m_nOldErrorStart;} |
| long GetOldErrorEnd() { return m_nOldErrorEnd;} |
| |
| void SetErrorLanguageSelected(bool bSet){ m_bIsErrorLanguageSelected = bSet;} |
| bool IsErrorLanguageSelected() const {return m_bIsErrorLanguageSelected;} |
| |
| |
| void SetDictionary(Reference<XDictionary> xDict) { m_xDictionary = xDict; } |
| Reference<XDictionary> GetDictionary() const {return m_xDictionary;} |
| void SetAddedWord(const ::rtl::OUString& rWord) {m_sAddedWord = rWord;} |
| const ::rtl::OUString& GetAddedWord() const { return m_sAddedWord;} |
| |
| void SetOffset(long nSet) {m_nOffset = nSet;} |
| long GetOffset() const {return m_nOffset;} |
| |
| void SetErrorType( const ::rtl::OUString& rId ) { m_sRuleId = rId; } |
| const ::rtl::OUString& GetErrorType() const { return m_sRuleId; } |
| |
| }; |
| }//namespace svx |
| using namespace ::svx; |
| /*-- 06.11.2003 12:16:02--------------------------------------------------- |
| |
| -----------------------------------------------------------------------*/ |
| SpellUndoAction_Impl::~SpellUndoAction_Impl() |
| { |
| } |
| /*-- 06.11.2003 12:16:02--------------------------------------------------- |
| |
| -----------------------------------------------------------------------*/ |
| void SpellUndoAction_Impl::Undo() |
| { |
| m_rActionLink.Call(this); |
| } |
| /*-- 06.11.2003 12:16:02--------------------------------------------------- |
| |
| -----------------------------------------------------------------------*/ |
| sal_uInt16 SpellUndoAction_Impl::GetId()const |
| { |
| return m_nId; |
| } |
| |
| // class SvxSpellCheckDialog --------------------------------------------- |
| |
| SpellDialog::SpellDialog( |
| SpellDialogChildWindow* pChildWindow, |
| Window * pParent, |
| SfxBindings* _pBindings) |
| : SfxModelessDialog (_pBindings, |
| pChildWindow, |
| pParent, |
| CUI_RES(RID_SVXDLG_SPELLCHECK)), |
| |
| aVendorImageFI ( this , CUI_RES( IMG_VENDOR ) ), |
| aLanguageFT ( this, CUI_RES( FT_LANGUAGE ) ), |
| aLanguageLB ( this, CUI_RES( LB_LANGUAGE ) ), |
| aNotInDictFT ( this, CUI_RES( FT_NOTINDICT ) ), |
| aSentenceED ( this, CUI_RES( ED_NEWWORD ) ), |
| aSuggestionFT ( this, CUI_RES( FT_SUGGESTION ) ), |
| aSuggestionLB ( this, CUI_RES( LB_SUGGESTION ) ), |
| |
| aIgnorePB ( this, CUI_RES( PB_IGNORE ) ), |
| aIgnoreAllPB ( this, CUI_RES( PB_IGNOREALL ) ), |
| aIgnoreRulePB ( this, CUI_RES( PB_IGNORERULE ) ), |
| aAddToDictMB ( this, CUI_RES( MB_ADDTODICT ) ), |
| |
| aChangePB ( this, CUI_RES( PB_CHANGE ) ), |
| aChangeAllPB ( this, CUI_RES( PB_CHANGEALL ) ), |
| aExplainPB ( this, CUI_RES( PB_EXPLAIN) ), |
| aAutoCorrPB ( this, CUI_RES( PB_AUTOCORR ) ), |
| |
| aCheckGrammarCB ( this, CUI_RES( CB_CHECK_GRAMMAR ) ), |
| |
| aHelpPB ( this, CUI_RES( PB_HELP ) ), |
| aOptionsPB ( this, CUI_RES( PB_OPTIONS ) ), |
| aUndoPB ( this, CUI_RES( PB_UNDO ) ), |
| aClosePB ( this, CUI_RES( PB_CLOSE ) ), |
| aBackgroundGB ( this, CUI_RES( GB_BACKGROUND ) ), |
| |
| aVendorImage ( CUI_RES( IMG_DEFAULT_VENDOR ) ), |
| aVendorImageHC ( CUI_RES( IMG_DEFAULT_VENDOR_HC ) ), |
| |
| aResumeST ( CUI_RES(ST_RESUME )), |
| aIgnoreOnceST ( aIgnorePB.GetText()), |
| aNoSuggestionsST( CUI_RES(ST_NOSUGGESTIONS)), |
| m_sTitleSpelling ( CUI_RES( ST_SPELLING ) ), |
| m_sTitleSpellingGrammar ( CUI_RES( ST_SPELLING_AND_GRAMMAR ) ), |
| m_sTitleSpellingGrammarVendor ( CUI_RES( ST_SPELLING_AND_GRAMMAR_VENDORNAME ) ), |
| aDialogUndoLink( LINK (this, SpellDialog, DialogUndoHdl)), |
| bModified( false ), |
| bFocusLocked( true ), |
| rParent ( *pChildWindow ), |
| nOldLang ( LANGUAGE_NONE ) |
| { |
| FreeResource(); |
| xSpell = LinguMgr::GetSpellChecker(); |
| pImpl = new SpellDialog_Impl; |
| |
| //HelpIds |
| aClosePB. SetHelpId(HID_SPLDLG_BUTTON_CLOSE ); |
| aIgnorePB. SetHelpId(HID_SPLDLG_BUTTON_IGNORE ); |
| aIgnoreAllPB. SetHelpId(HID_SPLDLG_BUTTON_IGNOREALL); |
| aIgnoreRulePB. SetHelpId(HID_SPLDLG_BUTTON_IGNORERULE); |
| aChangePB. SetHelpId(HID_SPLDLG_BUTTON_CHANGE ); |
| aChangeAllPB. SetHelpId(HID_SPLDLG_BUTTON_CHANGEALL); |
| aExplainPB. SetHelpId(HID_SPLDLG_BUTTON_EXPLAIN ); |
| |
| aAddToDictMB.SetPopupMenu( new PopupMenu ); |
| |
| Init_Impl(); |
| |
| // disable controls if service is missing |
| if (!xSpell.is()) |
| Enable( sal_False ); |
| |
| Application::PostUserEvent( STATIC_LINK( |
| this, SpellDialog, InitHdl ) ); |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| SpellDialog::~SpellDialog() |
| { |
| // save possibly modified user-dictionaries |
| Reference< XDictionaryList > xDicList( SvxGetDictionaryList() ); |
| if (xDicList.is()) |
| { |
| linguistic::SaveDictionaries( xDicList ); |
| } |
| |
| delete aAddToDictMB.GetPopupMenu(); |
| delete pImpl; |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| void SpellDialog::Init_Impl() |
| { |
| // Handler initialisieren |
| aClosePB.SetClickHdl(LINK( this, SpellDialog, CancelHdl ) ); |
| aChangePB.SetClickHdl(LINK( this, SpellDialog, ChangeHdl ) ); |
| aChangeAllPB.SetClickHdl(LINK( this, SpellDialog, ChangeAllHdl ) ); |
| aIgnorePB.SetClickHdl(LINK( this, SpellDialog, IgnoreHdl ) ); |
| aIgnoreAllPB.SetClickHdl(LINK( this, SpellDialog, IgnoreAllHdl ) ); |
| aIgnoreRulePB.SetClickHdl(LINK( this, SpellDialog, IgnoreAllHdl ) ); |
| aUndoPB.SetClickHdl(LINK( this, SpellDialog, UndoHdl ) ); |
| |
| aAutoCorrPB.SetClickHdl( LINK( this, SpellDialog, ExtClickHdl ) ); |
| aCheckGrammarCB.SetClickHdl( LINK( this, SpellDialog, CheckGrammarHdl )); |
| aOptionsPB .SetClickHdl( LINK( this, SpellDialog, ExtClickHdl ) ); |
| |
| aSuggestionLB.SetDoubleClickHdl( LINK( this, SpellDialog, ChangeHdl ) ); |
| |
| aSentenceED.SetModifyHdl(LINK ( this, SpellDialog, ModifyHdl) ); |
| aAddToDictMB.SetActivateHdl(LINK ( this, SpellDialog, MenuButtonActivateHdl ) ); |
| aAddToDictMB.SetSelectHdl(LINK ( this, SpellDialog, AddToDictionaryHdl ) ); |
| aLanguageLB.SetSelectHdl(LINK( this, SpellDialog, LanguageSelectHdl ) ); |
| |
| // initialize language ListBox |
| aLanguageLB.SetLanguageList( LANG_LIST_SPELL_USED, sal_False, sal_False, sal_True ); |
| |
| // get current language |
| UpdateBoxes_Impl(); |
| |
| aSentenceED.ClearModifyFlag(); |
| SvxGetChangeAllList()->clear(); |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| void SpellDialog::UpdateBoxes_Impl() |
| { |
| sal_Int32 i; |
| aSuggestionLB.Clear(); |
| |
| const SpellErrorDescription* pSpellErrorDescription = aSentenceED.GetAlternatives(); |
| |
| LanguageType nAltLanguage = LANGUAGE_NONE; |
| //String aAltWord; |
| Sequence< ::rtl::OUString > aNewWords; |
| bool bIsGrammarError = false; |
| if( pSpellErrorDescription ) |
| { |
| nAltLanguage = SvxLocaleToLanguage( pSpellErrorDescription->aLocale ); |
| //aAltWord = String( xAlt->getWord() ); |
| aNewWords = pSpellErrorDescription->aSuggestions; |
| bIsGrammarError = pSpellErrorDescription->bIsGrammarError; |
| aExplainPB.SetExplanation(pSpellErrorDescription->sExplanation ); |
| } |
| if( pSpellErrorDescription && pSpellErrorDescription->sDialogTitle.getLength() ) |
| { |
| // use this function to apply the correct image to be used... |
| SetTitle_Impl( nAltLanguage ); |
| // then change the title to the one to be actually used |
| SetText( pSpellErrorDescription->sDialogTitle ); |
| } |
| else |
| SetTitle_Impl( nAltLanguage ); |
| |
| SetSelectedLang_Impl( nAltLanguage ); |
| |
| // Initialize/update user dictionaries after setting the language in the listbox |
| InitUserDicts(); |
| |
| // Alternativen eintragen |
| const ::rtl::OUString *pNewWords = aNewWords.getConstArray(); |
| const sal_Int32 nSize = aNewWords.getLength(); |
| for ( i = 0; i < nSize; ++i ) |
| { |
| String aTmp( pNewWords[i] ); |
| if ( LISTBOX_ENTRY_NOTFOUND == aSuggestionLB.GetEntryPos( aTmp ) ) |
| { |
| aSuggestionLB.InsertEntry( aTmp ); |
| aSuggestionLB.SetEntryFlags(aSuggestionLB.GetEntryCount() - 1, LISTBOX_ENTRY_FLAG_MULTILINE); |
| } |
| } |
| if(!nSize) |
| aSuggestionLB.InsertEntry( aNoSuggestionsST ); |
| aAutoCorrPB.Enable( nSize > 0 ); |
| //aSentenceED.GrabFocus(); |
| |
| aSuggestionFT.Enable(nSize > 0); |
| aSuggestionLB.Enable(nSize > 0); |
| if( nSize ) |
| { |
| aSuggestionLB.SelectEntryPos(0); |
| } |
| aChangePB.Enable( nSize > 0); |
| aChangeAllPB.Enable(nSize > 0); |
| bool bShowChangeAll = !bIsGrammarError; |
| aChangeAllPB.Show( bShowChangeAll ); |
| aExplainPB.Show( !bShowChangeAll ); |
| aLanguageLB.Enable( bShowChangeAll ); |
| aIgnoreAllPB.Show( bShowChangeAll ); |
| aAddToDictMB.Show( bShowChangeAll ); |
| aIgnoreRulePB.Show( !bShowChangeAll ); |
| aIgnoreRulePB.Enable(pSpellErrorDescription && pSpellErrorDescription->sRuleId.getLength()); |
| aExplainPB.Enable( aExplainPB.HasExplanation() ); |
| aAutoCorrPB.Show( bShowChangeAll && rParent.HasAutoCorrection() ); |
| |
| } |
| // ----------------------------------------------------------------------- |
| |
| void SpellDialog::SpellContinue_Impl(bool bUseSavedSentence, bool bIgnoreCurrentError ) |
| { |
| //initially or after the last error of a sentence MarkNextError will fail |
| //then GetNextSentence() has to be called followed again by MarkNextError() |
| //MarkNextError is not initally called if the UndoEdit mode is active |
| bool bNextSentence = false; |
| if((!aSentenceED.IsUndoEditMode() && aSentenceED.MarkNextError( bIgnoreCurrentError )) || |
| true == ( bNextSentence = GetNextSentence_Impl(bUseSavedSentence, aSentenceED.IsUndoEditMode()) && aSentenceED.MarkNextError( false ))) |
| { |
| const SpellErrorDescription* pSpellErrorDescription = aSentenceED.GetAlternatives(); |
| if( pSpellErrorDescription ) |
| { |
| UpdateBoxes_Impl(); |
| Control* aControls[] = |
| { |
| &aNotInDictFT, |
| &aSentenceED, |
| &aLanguageFT, |
| 0 |
| }; |
| sal_Int32 nIdx = 0; |
| do |
| { |
| aControls[nIdx]->Enable(sal_True); |
| } |
| while(aControls[++nIdx]); |
| |
| |
| } |
| if( bNextSentence ) |
| { |
| //remove undo if a new sentence is active |
| aSentenceED.ResetUndo(); |
| aUndoPB.Enable(sal_False); |
| } |
| } |
| } |
| /* -----------------10.09.2003 14:04----------------- |
| Initialize, asynchronous to prevent virtial calls |
| from a constructor |
| --------------------------------------------------*/ |
| IMPL_STATIC_LINK( SpellDialog, InitHdl, SpellDialog *, EMPTYARG ) |
| { |
| pThis->SetUpdateMode( sal_False ); |
| //show or hide AutoCorrect depending on the modules abilities |
| pThis->aAutoCorrPB.Show(pThis->rParent.HasAutoCorrection()); |
| pThis->SpellContinue_Impl(); |
| pThis->aSentenceED.ResetUndo(); |
| pThis->aUndoPB.Enable(sal_False); |
| |
| pThis->LockFocusChanges(true); |
| if( pThis->aChangePB.IsEnabled() ) |
| pThis->aChangePB.GrabFocus(); |
| else if( pThis->aIgnorePB.IsEnabled() ) |
| pThis->aIgnorePB.GrabFocus(); |
| else if( pThis->aClosePB.IsEnabled() ) |
| pThis->aClosePB.GrabFocus(); |
| pThis->LockFocusChanges(false); |
| //show grammar CheckBox depending on the modules abilities |
| bool bHasGrammarChecking = pThis->rParent.HasGrammarChecking(); |
| pThis->aCheckGrammarCB.Show( bHasGrammarChecking ); |
| if( !bHasGrammarChecking ) |
| { |
| //resize the dialog to hide the hidden area of the CheckBox |
| Size aBackSize = pThis->aBackgroundGB.GetSizePixel(); |
| sal_Int32 nDiff = pThis->aBackgroundGB.GetPosPixel().Y() + aBackSize.Height() |
| - pThis->aCheckGrammarCB.GetPosPixel().Y(); |
| aBackSize.Height() -= nDiff; |
| pThis->aBackgroundGB.SetSizePixel(aBackSize); |
| Button* aButtons[] = { &pThis->aHelpPB, &pThis->aOptionsPB, &pThis->aUndoPB, &pThis->aClosePB, 0 }; |
| sal_Int32 nButton = 0; |
| while( aButtons[nButton]) |
| { |
| Point aPos = aButtons[nButton]->GetPosPixel(); |
| aPos.Y() -= nDiff; |
| aButtons[nButton]->SetPosPixel(aPos); |
| ++nButton; |
| } |
| Size aDlgSize = pThis->GetSizePixel(); |
| aDlgSize.Height() -= nDiff; |
| pThis->SetSizePixel( aDlgSize ); |
| } |
| else |
| { |
| if( SvtLinguConfig().HasVendorImages( "SpellAndGrammarDialogImage" ) ) |
| { |
| pThis->aVendorImageFI.Show(); |
| Size aVendorSize = pThis->aVendorImageFI.GetSizePixel(); |
| Size aImageSize = pThis->aVendorImageFI.GetImage().GetSizePixel(); |
| if( aImageSize.Height() ) |
| { |
| aVendorSize.Height() = aImageSize.Height(); |
| if(aVendorSize.Width() < aImageSize.Width()) |
| aVendorSize.Width() = aImageSize.Width(); |
| pThis->aVendorImageFI.SetSizePixel( aVendorSize ); |
| } |
| //aVendorSize.Height() = nDiff; |
| sal_Int32 nDiff = aVendorSize.Height(); |
| pThis->aVendorImageFI.SetSizePixel(aVendorSize); |
| Control* aControls[] = { |
| &pThis->aLanguageFT, |
| &pThis->aLanguageLB, |
| &pThis->aNotInDictFT, |
| &pThis->aSentenceED, |
| &pThis->aSuggestionFT, |
| &pThis->aSuggestionLB, |
| &pThis->aIgnorePB, |
| &pThis->aIgnoreAllPB, |
| &pThis->aIgnoreRulePB, |
| &pThis->aAddToDictMB, |
| &pThis->aChangePB, |
| &pThis->aChangeAllPB, |
| &pThis->aExplainPB, |
| &pThis->aAutoCorrPB, |
| &pThis->aCheckGrammarCB, |
| &pThis->aHelpPB, |
| &pThis->aOptionsPB, |
| &pThis->aUndoPB, |
| &pThis->aClosePB, |
| &pThis->aBackgroundGB, |
| 0 |
| }; |
| sal_Int32 nControl = 0; |
| while( aControls[nControl]) |
| { |
| Point aPos = aControls[nControl]->GetPosPixel(); |
| aPos.Y() += nDiff; |
| aControls[nControl]->SetPosPixel(aPos); |
| ++nControl; |
| } |
| Size aDlgSize = pThis->GetSizePixel(); |
| aDlgSize.Height() += nDiff; |
| pThis->SetSizePixel( aDlgSize ); |
| pThis->Invalidate(); |
| } |
| } |
| pThis->aCheckGrammarCB.Check( pThis->rParent.IsGrammarChecking() ); |
| pThis->SetUpdateMode( sal_True ); |
| pThis->Show(); |
| return 0; |
| }; |
| |
| // ----------------------------------------------------------------------- |
| |
| IMPL_LINK( SpellDialog, ExtClickHdl, Button *, pBtn ) |
| { |
| if (&aOptionsPB == pBtn) |
| StartSpellOptDlg_Impl(); |
| else if(&aAutoCorrPB == pBtn) |
| { |
| //get the currently selected wrong word |
| String sCurrentErrorText = aSentenceED.GetErrorText(); |
| //get the wrong word from the XSpellAlternative |
| const SpellErrorDescription* pSpellErrorDescription = aSentenceED.GetAlternatives(); |
| if( pSpellErrorDescription ) |
| { |
| String sWrong(pSpellErrorDescription->sErrorText); |
| //if the word has not been edited in the MultiLineEdit then |
| //the current suggestion should be used |
| //if it's not the 'no suggestions' entry |
| if(sWrong == sCurrentErrorText && |
| aSuggestionLB.IsEnabled() && aSuggestionLB.GetSelectEntryCount() > 0 && |
| aNoSuggestionsST != aSuggestionLB.GetSelectEntry()) |
| { |
| sCurrentErrorText = aSuggestionLB.GetSelectEntry(); |
| } |
| if(sWrong != sCurrentErrorText) |
| { |
| SvxPrepareAutoCorrect( sWrong, sCurrentErrorText ); |
| LanguageType eLang = GetSelectedLang_Impl(); |
| rParent.AddAutoCorrection( sWrong, sCurrentErrorText, eLang ); |
| } |
| } |
| } |
| return 0; |
| } |
| // ----------------------------------------------------------------------- |
| IMPL_LINK( SpellDialog, CheckGrammarHdl, CheckBox*, pBox ) |
| { |
| rParent.SetGrammarChecking( pBox->IsChecked() ); |
| Impl_Restore(); |
| return 0; |
| } |
| |
| void SpellDialog::StartSpellOptDlg_Impl() |
| { |
| sal_uInt16 aSpellInfos[] = |
| { |
| SID_ATTR_SPELL,SID_ATTR_SPELL, |
| SID_SPELL_MODIFIED, SID_SPELL_MODIFIED, |
| SID_AUTOSPELL_CHECK, SID_AUTOSPELL_CHECK, |
| 0 |
| }; |
| SfxItemSet aSet( SFX_APP()->GetPool(), aSpellInfos); |
| aSet.Put(SfxSpellCheckItem( xSpell, SID_ATTR_SPELL )); |
| SfxSingleTabDialog* pDlg = |
| new SfxSingleTabDialog( this, aSet, RID_SFXPAGE_LINGU ); |
| SfxTabPage* pPage = SvxLinguTabPage::Create( pDlg, aSet ); |
| ( (SvxLinguTabPage*)pPage )->HideGroups( GROUP_MODULES ); |
| pDlg->SetTabPage( pPage ); |
| if(RET_OK == pDlg->Execute()) |
| { |
| |
| // Benutzerb"ucher anzeigen |
| InitUserDicts(); |
| const SfxItemSet* pOutSet = pDlg->GetOutputItemSet(); |
| if(pOutSet) |
| OfaTreeOptionsDialog::ApplyLanguageOptions(*pOutSet); |
| } |
| delete pDlg; |
| |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| IMPL_LINK( SpellDialog, ChangeHdl, Button *, EMPTYARG ) |
| { |
| if(aSentenceED.IsUndoEditMode()) |
| { |
| SpellContinue_Impl(); |
| } |
| else |
| { |
| aSentenceED.UndoActionStart( SPELLUNDO_CHANGE_GROUP ); |
| String aString = aSentenceED.GetErrorText(); |
| //dots are sometimes part of the spelled word but they are not necessarily part of the replacement |
| bool bDot = aString.Len() && aString.GetChar(aString.Len() - 1 ) == '.'; |
| if(aSuggestionLB.IsEnabled() && |
| aSuggestionLB.GetSelectEntryCount()>0 && |
| aNoSuggestionsST != aSuggestionLB.GetSelectEntry()) |
| aString = aSuggestionLB.GetSelectEntry(); |
| if(bDot && (!aString.Len() || aString.GetChar(aString.Len() - 1 ) != '.')) |
| aString += '.'; |
| |
| aSentenceED.ChangeMarkedWord(aString, GetSelectedLang_Impl()); |
| SpellContinue_Impl(); |
| bModified = false; |
| aSentenceED.UndoActionEnd(); |
| } |
| if(!aChangePB.IsEnabled()) |
| aIgnorePB.GrabFocus(); |
| return 1; |
| } |
| |
| |
| // ----------------------------------------------------------------------- |
| |
| IMPL_LINK( SpellDialog, ChangeAllHdl, Button *, EMPTYARG ) |
| { |
| aSentenceED.UndoActionStart( SPELLUNDO_CHANGE_GROUP ); |
| // change the current word first |
| String aString = aSentenceED.GetErrorText(); |
| if(aSuggestionLB.IsEnabled() && |
| aSuggestionLB.GetSelectEntryCount()>0 && |
| aNoSuggestionsST != aSuggestionLB.GetSelectEntry()) |
| aString = aSuggestionLB.GetSelectEntry(); |
| |
| LanguageType eLang = GetSelectedLang_Impl(); |
| |
| // add new word to ChangeAll list |
| String aOldWord( aSentenceED.GetErrorText() ); |
| SvxPrepareAutoCorrect( aOldWord, aString ); |
| Reference<XDictionary> aXDictionary( SvxGetChangeAllList(), UNO_QUERY ); |
| sal_uInt8 nAdded = linguistic::AddEntryToDic( aXDictionary, |
| aOldWord , sal_True, |
| aString, eLang ); |
| |
| if(nAdded == DIC_ERR_NONE) |
| { |
| SpellUndoAction_Impl* pAction = new SpellUndoAction_Impl( |
| SPELLUNDO_CHANGE_ADD_TO_DICTIONARY, aDialogUndoLink); |
| pAction->SetDictionary(aXDictionary); |
| pAction->SetAddedWord(aOldWord); |
| aSentenceED.AddUndoAction(pAction); |
| } |
| |
| aSentenceED.ChangeMarkedWord(aString, eLang); |
| SpellContinue_Impl(); |
| bModified = false; |
| aSentenceED.UndoActionEnd(); |
| return 1; |
| } |
| // ----------------------------------------------------------------------- |
| |
| IMPL_LINK( SpellDialog, IgnoreAllHdl, Button *, pButton ) |
| { |
| aSentenceED.UndoActionStart( SPELLUNDO_CHANGE_GROUP ); |
| // add word to IgnoreAll list |
| Reference< XDictionary > aXDictionary( SvxGetIgnoreAllList(), UNO_QUERY ); |
| //in case the error has been changed manually it has to be restored |
| aSentenceED.RestoreCurrentError(); |
| if( pButton == &aIgnoreRulePB ) |
| { |
| const SpellErrorDescription* pSpellErrorDescription = aSentenceED.GetAlternatives(); |
| try |
| { |
| if( pSpellErrorDescription && pSpellErrorDescription->xGrammarChecker.is() ) |
| { |
| pSpellErrorDescription->xGrammarChecker->ignoreRule( pSpellErrorDescription->sRuleId, |
| pSpellErrorDescription->aLocale ); |
| } |
| } |
| catch( const uno::Exception& ) |
| { |
| } |
| } |
| else |
| { |
| String sErrorText(aSentenceED.GetErrorText()); |
| sal_uInt8 nAdded = linguistic::AddEntryToDic( aXDictionary, |
| sErrorText, sal_False, |
| ::rtl::OUString(), LANGUAGE_NONE ); |
| if(nAdded == DIC_ERR_NONE) |
| { |
| SpellUndoAction_Impl* pAction = new SpellUndoAction_Impl( |
| SPELLUNDO_CHANGE_ADD_TO_DICTIONARY, aDialogUndoLink); |
| pAction->SetDictionary(aXDictionary); |
| pAction->SetAddedWord(sErrorText); |
| aSentenceED.AddUndoAction(pAction); |
| } |
| } |
| |
| SpellContinue_Impl(); |
| bModified = false; |
| aSentenceED.UndoActionEnd(); |
| return 1; |
| } |
| /*-- 06.11.2003 11:24:08--------------------------------------------------- |
| |
| -----------------------------------------------------------------------*/ |
| IMPL_LINK( SpellDialog, UndoHdl, Button*, EMPTYARG ) |
| { |
| aSentenceED.Undo(); |
| if(!aSentenceED.GetUndoActionCount()) |
| aUndoPB.Enable(sal_False); |
| return 0; |
| } |
| /*-- 06.11.2003 12:19:15--------------------------------------------------- |
| |
| -----------------------------------------------------------------------*/ |
| IMPL_LINK( SpellDialog, DialogUndoHdl, SpellUndoAction_Impl*, pAction ) |
| { |
| switch(pAction->GetId()) |
| { |
| case SPELLUNDO_CHANGE_TEXTENGINE: |
| { |
| if(pAction->IsEnableChangePB()) |
| aChangePB.Enable(sal_False); |
| if(pAction->IsEnableChangeAllPB()) |
| aChangeAllPB.Enable(sal_False); |
| } |
| break; |
| case SPELLUNDO_CHANGE_NEXTERROR: |
| { |
| aSentenceED.MoveErrorMarkTo((sal_uInt16)pAction->GetOldErrorStart(), (sal_uInt16)pAction->GetOldErrorEnd(), false); |
| if(pAction->IsErrorLanguageSelected()) |
| { |
| UpdateBoxes_Impl(); |
| } |
| } |
| break; |
| case SPELLUNDO_CHANGE_ADD_TO_DICTIONARY: |
| { |
| if(pAction->GetDictionary().is()) |
| pAction->GetDictionary()->remove(pAction->GetAddedWord()); |
| } |
| break; |
| case SPELLUNDO_MOVE_ERROREND : |
| { |
| if(pAction->GetOffset() != 0) |
| aSentenceED.MoveErrorEnd(pAction->GetOffset()); |
| } |
| break; |
| case SPELLUNDO_UNDO_EDIT_MODE : |
| { |
| //refill the dialog with the currently spelled sentence - throw away all changes |
| SpellContinue_Impl(true); |
| } |
| break; |
| case SPELLUNDO_ADD_IGNORE_RULE: |
| //undo of ignored rules is not supported |
| break; |
| } |
| |
| return 0; |
| } |
| // ----------------------------------------------------------------------- |
| void SpellDialog::Impl_Restore() |
| { |
| //clear the "ChangeAllList" |
| SvxGetChangeAllList()->clear(); |
| //get a new sentence |
| aSentenceED.SetText(rtl::OUString()); |
| aSentenceED.ResetModified(); |
| SpellContinue_Impl(); |
| aIgnorePB.SetText(aIgnoreOnceST); |
| } |
| |
| IMPL_LINK( SpellDialog, IgnoreHdl, Button *, EMPTYARG ) |
| { |
| if(aIgnorePB.GetText() == aResumeST) |
| { |
| Impl_Restore(); |
| } |
| else |
| { |
| //in case the error has been changed manually it has to be restored, |
| // since the users choice now was to ignore the error |
| aSentenceED.RestoreCurrentError(); |
| |
| // the word is being ignored |
| SpellContinue_Impl( false, true ); |
| } |
| return 1; |
| } |
| |
| |
| // ----------------------------------------------------------------------- |
| |
| sal_Bool SpellDialog::Close() |
| { |
| GetBindings().GetDispatcher()-> |
| Execute(rParent.GetType(), |
| SFX_CALLMODE_ASYNCHRON|SFX_CALLMODE_RECORD); |
| return sal_True; |
| } |
| // ----------------------------------------------------------------------- |
| |
| void SpellDialog::SetSelectedLang_Impl( LanguageType nLang ) |
| { |
| aLanguageLB.SelectLanguage( nLang ); |
| } |
| |
| // ----------------------------------------------------------------------- |
| |
| LanguageType SpellDialog::GetSelectedLang_Impl() const |
| { |
| sal_Int16 nLang = aLanguageLB.GetSelectLanguage(); |
| return nLang; |
| } |
| /* -----------------28.10.2003 14:27----------------- |
| |
| --------------------------------------------------*/ |
| IMPL_LINK(SpellDialog, LanguageSelectHdl, SvxLanguageBox*, pBox) |
| { |
| //if currently an error is selected then search for alternatives for |
| //this word and fill the alternatives ListBox accordingly |
| String sError = aSentenceED.GetErrorText(); |
| aSuggestionLB.Clear(); |
| if(sError.Len()) |
| { |
| LanguageType eLanguage = pBox->GetSelectLanguage(); |
| Reference <XSpellAlternatives> xAlt = xSpell->spell( sError, eLanguage, |
| Sequence< PropertyValue >() ); |
| if( xAlt.is() ) |
| aSentenceED.SetAlternatives( xAlt ); |
| else |
| { |
| aSentenceED.ChangeMarkedWord( sError, eLanguage ); |
| SpellContinue_Impl(); |
| } |
| |
| aSentenceED.AddUndoAction(new SpellUndoAction_Impl(SPELLUNDO_CHANGE_LANGUAGE, aDialogUndoLink)); |
| } |
| |
| // Update listboxes and user dictionaries when selected language changes |
| SpellDialog::UpdateBoxes_Impl(); |
| return 0; |
| } |
| // ----------------------------------------------------------------------- |
| |
| void SpellDialog::SetLanguage( sal_uInt16 nLang ) |
| |
| /* [Beschreibung] |
| |
| wenn die Sprache im Thesaurus umgestellt wurde, |
| muss auch hier die Sprache umgestellt werden. |
| */ |
| |
| { |
| SetTitle_Impl( nLang ); |
| |
| // den richtigen Eintrag finden, da sortiert |
| aLanguageLB.SelectLanguage( nLang ); |
| } |
| /*-- 16.06.2008 11:27:02--------------------------------------------------- |
| |
| -----------------------------------------------------------------------*/ |
| static Image lcl_GetImageFromPngUrl( const ::rtl::OUString &rFileUrl ) |
| { |
| Image aRes; |
| ::rtl::OUString aTmp; |
| osl::FileBase::getSystemPathFromFileURL( rFileUrl, aTmp ); |
| Graphic aGraphic; |
| const String aFilterName( RTL_CONSTASCII_USTRINGPARAM( IMP_PNG ) ); |
| if( GRFILTER_OK == GraphicFilter::LoadGraphic( aTmp, aFilterName, aGraphic ) ) |
| { |
| aRes = Image( aGraphic.GetBitmapEx() ); |
| } |
| return aRes; |
| } |
| void SpellDialog::SetTitle_Impl(LanguageType nLang) |
| { |
| String sTitle( m_sTitleSpelling ); |
| if( rParent.HasGrammarChecking() ) |
| { |
| String sVendor; |
| const SpellErrorDescription* pSpellErrorDescription = aSentenceED.GetAlternatives(); |
| if( pSpellErrorDescription && pSpellErrorDescription->sServiceName.getLength() ) |
| { |
| bool bHighContrast = GetSettings().GetStyleSettings().GetHighContrastMode(); |
| ::rtl::OUString sSuggestionImageUrl = |
| SvtLinguConfig().GetSpellAndGrammarDialogImage( pSpellErrorDescription->sServiceName, bHighContrast ); |
| aVendorImageFI.SetImage( lcl_GetImageFromPngUrl( sSuggestionImageUrl ) ); |
| uno::Reference< lang::XServiceDisplayName > xDisplayName( pSpellErrorDescription->xGrammarChecker, uno::UNO_QUERY ); |
| if( xDisplayName.is() ) |
| sVendor = xDisplayName->getServiceDisplayName( pSpellErrorDescription->aLocale ); |
| } |
| else |
| { |
| bool bHighContrast = GetSettings().GetStyleSettings().GetHighContrastMode(); |
| aVendorImageFI.SetImage( bHighContrast ? aVendorImageHC : aVendorImage ); |
| } |
| |
| if( sVendor.Len() ) |
| { |
| sTitle = m_sTitleSpellingGrammarVendor; |
| sTitle.SearchAndReplaceAscii( "$VendorName", sVendor ); |
| } |
| else |
| { |
| //bool bHighContrast = GetSettings().GetStyleSettings().GetHighContrastMode(); |
| sTitle = m_sTitleSpellingGrammar; |
| } |
| } |
| sTitle.SearchAndReplaceAscii( "$LANGUAGE ($LOCATION)", SvtLanguageTable::GetLanguageString(nLang) ); |
| SetText( sTitle ); |
| } |
| /*------------------------------------------------------------------------- |
| |
| -----------------------------------------------------------------------*/ |
| void SpellDialog::InitUserDicts() |
| { |
| bool bEnable = false; |
| |
| // get list of dictionaries |
| Reference< XDictionaryList > xDicList( SvxGetDictionaryList() ); |
| if (xDicList.is()) |
| { |
| // add active, positive dictionary to dic-list (if not already done). |
| // This is to ensure that there is at least one dictionary to which |
| // words could be added. |
| Reference< XDictionary > xDic( SvxGetOrCreatePosDic( xDicList ) ); |
| if (xDic.is()) |
| xDic->setActive( sal_True ); |
| |
| pImpl->aDics = xDicList->getDictionaries(); |
| |
| // this is redundant, there will always be *at least* one dictionary |
| bEnable = pImpl->aDics.getLength(); |
| } |
| |
| aAddToDictMB.Enable( bEnable ); |
| } |
| |
| IMPL_LINK(SpellDialog, MenuButtonActivateHdl, MenuButton*, ) |
| { |
| bool bEnable = false; |
| const LanguageType nLang = aLanguageLB.GetSelectLanguage(); |
| const Reference< XDictionary > *pDic = 0; |
| |
| SvtLinguConfig aCfg; |
| const bool bHC = Application::GetSettings().GetStyleSettings().GetHighContrastMode(); |
| |
| // list suitable dictionaries |
| const sal_Int32 nSize = pImpl->aDics.getLength(); |
| pDic = pImpl->aDics.getConstArray(); |
| |
| PopupMenu* pMenu = aAddToDictMB.GetPopupMenu(); |
| OSL_ENSURE( pMenu, "svx::SpellDialog::MenuButtonActivateHdl - no PopupMenu!" ); |
| pMenu->Clear(); |
| pMenu->SetMenuFlags(MENU_FLAG_NOAUTOMNEMONICS); |
| |
| sal_uInt16 nItemId = 1; // menu items should be enumerated from 1 and not 0 |
| for (sal_Int32 i = 0; i < nSize; ++i) |
| { |
| uno::Reference< linguistic2::XDictionary > xDicTmp( pDic[i], uno::UNO_QUERY ); |
| if (!xDicTmp.is() || SvxGetIgnoreAllList() == xDicTmp) |
| continue; |
| |
| uno::Reference< frame::XStorable > xStor( xDicTmp, uno::UNO_QUERY ); |
| LanguageType nActLanguage = SvxLocaleToLanguage( xDicTmp->getLocale() ); |
| if( xDicTmp->isActive() |
| && xDicTmp->getDictionaryType() != linguistic2::DictionaryType_NEGATIVE |
| && (nLang == nActLanguage || LANGUAGE_NONE == nActLanguage ) |
| && (!xStor.is() || !xStor->isReadonly()) ) |
| { |
| pMenu->InsertItem( nItemId, xDicTmp->getName() ); |
| bEnable = sal_True; |
| |
| uno::Reference< lang::XServiceInfo > xSvcInfo( xDicTmp, uno::UNO_QUERY ); |
| if (xSvcInfo.is()) |
| { |
| OUString aDictionaryImageUrl( aCfg.GetSpellAndGrammarContextDictionaryImage( |
| xSvcInfo->getImplementationName(), bHC) ); |
| if (aDictionaryImageUrl.getLength() > 0) |
| { |
| Image aImage( lcl_GetImageFromPngUrl( aDictionaryImageUrl ) ); |
| pMenu->SetItemImage( nItemId, aImage ); |
| } |
| } |
| |
| ++nItemId; |
| } |
| } |
| |
| aAddToDictMB.Enable( bEnable ); |
| |
| return 0; |
| } |
| |
| /*-- 20.10.2003 15:31:06--------------------------------------------------- |
| |
| -----------------------------------------------------------------------*/ |
| IMPL_LINK(SpellDialog, AddToDictionaryHdl, MenuButton*, pButton ) |
| { |
| aSentenceED.UndoActionStart( SPELLUNDO_CHANGE_GROUP ); |
| |
| //GetErrorText() returns the current error even if the text is already |
| //manually changed |
| const String aNewWord= aSentenceED.GetErrorText(); |
| |
| sal_uInt16 nItemId = pButton->GetCurItemId(); |
| PopupMenu *pMenu = pButton->GetPopupMenu(); |
| String aDicName ( pMenu->GetItemText( nItemId ) ); |
| |
| uno::Reference< linguistic2::XDictionary > xDic; |
| uno::Reference< linguistic2::XDictionaryList > xDicList( SvxGetDictionaryList() ); |
| if (xDicList.is()) |
| xDic = xDicList->getDictionaryByName( aDicName ); |
| |
| sal_Int16 nAddRes = DIC_ERR_UNKNOWN; |
| if (xDic.is()) |
| { |
| nAddRes = linguistic::AddEntryToDic( xDic, aNewWord, sal_False, OUString(), LANGUAGE_NONE ); |
| // save modified user-dictionary if it is persistent |
| uno::Reference< frame::XStorable > xSavDic( xDic, uno::UNO_QUERY ); |
| if (xSavDic.is()) |
| xSavDic->store(); |
| |
| if (nAddRes == DIC_ERR_NONE) |
| { |
| SpellUndoAction_Impl* pAction = new SpellUndoAction_Impl( |
| SPELLUNDO_CHANGE_ADD_TO_DICTIONARY, aDialogUndoLink); |
| pAction->SetDictionary( xDic ); |
| pAction->SetAddedWord( aNewWord ); |
| aSentenceED.AddUndoAction( pAction ); |
| } |
| // failed because there is already an entry? |
| if (DIC_ERR_NONE != nAddRes && xDic->getEntry( aNewWord ).is()) |
| nAddRes = DIC_ERR_NONE; |
| } |
| if (DIC_ERR_NONE != nAddRes) |
| { |
| SvxDicError( this, nAddRes ); |
| return 0; // Nicht weitermachen |
| } |
| |
| // go on |
| SpellContinue_Impl(); |
| aSentenceED.UndoActionEnd(); |
| return 0; |
| } |
| /*------------------------------------------------------------------------- |
| |
| -----------------------------------------------------------------------*/ |
| IMPL_LINK(SpellDialog, ModifyHdl, SentenceEditWindow_Impl*, pEd) |
| { |
| if (&aSentenceED == pEd) |
| { |
| bModified = true; |
| aSuggestionLB.SetNoSelection(); |
| aSuggestionLB.Disable(); |
| String sNewText( aSentenceED.GetText() ); |
| aAutoCorrPB.Enable( sNewText != aSentenceED.GetText() ); |
| SpellUndoAction_Impl* pSpellAction = new SpellUndoAction_Impl(SPELLUNDO_CHANGE_TEXTENGINE, aDialogUndoLink); |
| if(!aChangeAllPB.IsEnabled()) |
| { |
| aChangeAllPB.Enable(); |
| pSpellAction->SetEnableChangeAllPB(); |
| } |
| if(!aChangePB.IsEnabled()) |
| { |
| aChangePB.Enable(); |
| pSpellAction->SetEnableChangePB(); |
| } |
| aSentenceED.AddUndoAction(pSpellAction); |
| } |
| return 0; |
| }; |
| /*------------------------------------------------------------------------- |
| |
| -----------------------------------------------------------------------*/ |
| IMPL_LINK(SpellDialog, CancelHdl, Button *, EMPTYARG ) |
| { |
| //apply changes and ignored text parts first - if there are any |
| rParent.ApplyChangedSentence(aSentenceED.CreateSpellPortions(true), false); |
| Close(); |
| return 0; |
| } |
| /*------------------------------------------------------------------------- |
| |
| -----------------------------------------------------------------------*/ |
| void SpellDialog::Paint( const Rectangle& rRect ) |
| { |
| ModelessDialog::Paint(rRect ); |
| Rectangle aRect(aBackgroundGB.GetPosPixel(), aBackgroundGB.GetSizePixel()); |
| DecorationView aDecoView( this ); |
| aDecoView.DrawButton( aRect, BUTTON_DRAW_NOFILL); |
| } |
| /*-- 28.10.2003 13:26:39--------------------------------------------------- |
| |
| -----------------------------------------------------------------------*/ |
| long SpellDialog::Notify( NotifyEvent& rNEvt ) |
| { |
| /* #i38338# |
| * FIXME: LoseFocus and GetFocus are signals from vcl that |
| * a window actually got/lost the focus, it never should be |
| * forwarded from another window, that is simply wrong. |
| * FIXME: overloading the virtual methods GetFocus and LoseFocus |
| * in SpellDialogChildWindow by making them pure is at least questionable. |
| * The only sensible thing would be to call the new Method differently, |
| * e.g. DialogGot/LostFocus or so. |
| */ |
| if( IsVisible() && !bFocusLocked ) |
| { |
| if( rNEvt.GetType() == EVENT_GETFOCUS ) |
| { |
| //notify the child window of the focus change |
| rParent.GetFocus(); |
| } |
| else if( rNEvt.GetType() == EVENT_LOSEFOCUS ) |
| { |
| //notify the child window of the focus change |
| rParent.LoseFocus(); |
| } |
| } |
| return SfxModelessDialog::Notify(rNEvt); |
| } |
| /* -----------------10.09.2003 08:26----------------- |
| |
| --------------------------------------------------*/ |
| void SpellDialog::InvalidateDialog() |
| { |
| if( bFocusLocked ) |
| return; |
| aIgnorePB.SetText(aResumeST); |
| Window* aDisableArr[] = |
| { |
| &aNotInDictFT, |
| &aSentenceED, |
| &aSuggestionFT, |
| &aSuggestionLB, |
| &aLanguageFT, |
| &aLanguageLB, |
| &aIgnoreAllPB, |
| &aIgnoreRulePB, |
| &aAddToDictMB, |
| &aChangePB, |
| &aChangeAllPB, |
| &aAutoCorrPB, |
| &aUndoPB, |
| 0 |
| }; |
| sal_Int16 i = 0; |
| while(aDisableArr[i]) |
| { |
| aDisableArr[i]->Enable(sal_False); |
| i++; |
| } |
| SfxModelessDialog::Deactivate(); |
| } |
| |
| /*-- 10.09.2003 08:35:56--------------------------------------------------- |
| |
| -----------------------------------------------------------------------*/ |
| bool SpellDialog::GetNextSentence_Impl(bool bUseSavedSentence, bool bRecheck) |
| { |
| bool bRet = false; |
| if(!bUseSavedSentence /*&& aSentenceED.IsModified()*/) |
| { |
| //apply changes and ignored text parts |
| rParent.ApplyChangedSentence(aSentenceED.CreateSpellPortions(true), bRecheck); |
| } |
| aSentenceED.ResetIgnoreErrorsAt(); |
| aSentenceED.ResetModified(); |
| SpellPortions aSentence = bUseSavedSentence ? m_aSavedSentence : rParent.GetNextWrongSentence( bRecheck ); |
| if(!bUseSavedSentence) |
| m_aSavedSentence = aSentence; |
| bool bHasReplaced = false; |
| while(aSentence.size()) |
| { |
| //apply all changes that are already part of the "ChangeAllList" |
| //returns true if the list still contains errors after the changes have been applied |
| |
| if(!ApplyChangeAllList_Impl(aSentence, bHasReplaced)) |
| { |
| rParent.ApplyChangedSentence(aSentence, bRecheck); |
| aSentence = rParent.GetNextWrongSentence( bRecheck ); |
| } |
| else |
| break; |
| } |
| |
| if(aSentence.size()) |
| { |
| SpellPortions::iterator aStart = aSentence.begin(); |
| rtl::OUString sText; |
| while(aStart != aSentence.end()) |
| { |
| // hidden text has to be ignored |
| if(!aStart->bIsHidden) |
| sText += aStart->sText; |
| aStart++; |
| } |
| aSentenceED.SetText(sText); |
| aStart = aSentence.begin(); |
| sal_Int32 nStartPosition = 0; |
| sal_Int32 nEndPosition = 0; |
| |
| while(aStart != aSentence.end()) |
| { |
| // hidden text has to be ignored |
| if(!aStart->bIsHidden) |
| { |
| nEndPosition += aStart->sText.getLength(); |
| if(aStart->xAlternatives.is()) |
| { |
| uno::Reference< container::XNamed > xNamed( aStart->xAlternatives, uno::UNO_QUERY ); |
| ::rtl::OUString sServiceName; |
| if( xNamed.is() ) |
| sServiceName = xNamed->getName(); |
| SpellErrorDescription aDesc( false, aStart->xAlternatives->getWord(), |
| aStart->xAlternatives->getLocale(), aStart->xAlternatives->getAlternatives(), 0, sServiceName); |
| aSentenceED.SetAttrib( SpellErrorAttrib(aDesc), 0, (sal_uInt16) nStartPosition, (sal_uInt16) nEndPosition ); |
| } |
| else if(aStart->bIsGrammarError ) |
| { |
| uno::Reference< lang::XServiceInfo > xInfo( aStart->xGrammarChecker, uno::UNO_QUERY ); |
| SpellErrorDescription aDesc( true, |
| aStart->sText, |
| SvxCreateLocale( aStart->eLanguage ), |
| aStart->aGrammarError.aSuggestions, |
| aStart->xGrammarChecker, |
| xInfo->getImplementationName(), |
| &aStart->sDialogTitle, |
| &aStart->aGrammarError.aFullComment, |
| &aStart->aGrammarError.aRuleIdentifier ); |
| aSentenceED.SetAttrib( SpellErrorAttrib(aDesc), 0, (sal_uInt16) nStartPosition, (sal_uInt16) nEndPosition ); |
| } |
| if(aStart->bIsField) |
| aSentenceED.SetAttrib( SpellBackgroundAttrib(COL_LIGHTGRAY), 0, (sal_uInt16) nStartPosition, (sal_uInt16) nEndPosition ); |
| aSentenceED.SetAttrib( SpellLanguageAttrib(aStart->eLanguage), 0, (sal_uInt16) nStartPosition, (sal_uInt16) nEndPosition ); |
| nStartPosition = nEndPosition; |
| } |
| aStart++; |
| } |
| //the edit field needs to be modified to apply the change from the ApplyChangeAllList |
| if(!bHasReplaced) |
| aSentenceED.ClearModifyFlag(); |
| aSentenceED.ResetUndo(); |
| aUndoPB.Enable(sal_False); |
| bRet = nStartPosition > 0; |
| } |
| return bRet; |
| } |
| /*-- 12.11.2003 15:21:25--------------------------------------------------- |
| replace errrors that have a replacement in the ChangeAllList |
| returns false if the result doesn't contain errors after the replacement |
| -----------------------------------------------------------------------*/ |
| bool SpellDialog::ApplyChangeAllList_Impl(SpellPortions& rSentence, bool &bHasReplaced) |
| { |
| bHasReplaced = false; |
| bool bRet = true; |
| SpellPortions::iterator aStart = rSentence.begin(); |
| Reference<XDictionary> xChangeAll( SvxGetChangeAllList(), UNO_QUERY ); |
| if(!xChangeAll->getCount()) |
| return bRet; |
| bRet = false; |
| while(aStart != rSentence.end()) |
| { |
| if(aStart->xAlternatives.is()) |
| { |
| Reference<XDictionaryEntry> xEntry = xChangeAll->getEntry( aStart->sText ); |
| if(xEntry.is()) |
| { |
| aStart->sText = xEntry->getReplacementText(); |
| aStart->xAlternatives = 0; |
| bHasReplaced = true; |
| } |
| else |
| bRet = true; |
| } |
| else if( aStart->bIsGrammarError ) |
| bRet = true; |
| aStart++; |
| } |
| return bRet; |
| } |
| /*-- 10.09.2003 10:40:21--------------------------------------------------- |
| |
| -----------------------------------------------------------------------*/ |
| SentenceEditWindow_Impl::SentenceEditWindow_Impl( SpellDialog* pParent, const ResId& rResId ) : |
| MultiLineEdit( pParent, rResId ), |
| m_nErrorStart(0), |
| m_nErrorEnd(0), |
| m_bIsUndoEditMode(false) |
| { |
| DisableSelectionOnFocus(); |
| } |
| /*-- 10.09.2003 10:40:11--------------------------------------------------- |
| |
| -----------------------------------------------------------------------*/ |
| SentenceEditWindow_Impl::~SentenceEditWindow_Impl() |
| { |
| } |
| /*-- 20.10.2003 13:42:34--------------------------------------------------- |
| The selection before inputting a key may have a range or not |
| and it may be inside or outside of field or error attributes. |
| A range may include the attribute partially, completely or together |
| with surrounding text. It may also contain more than one attribute |
| or no attribute at all. |
| Depending on this starting conditions some actions are necessary: |
| Attempts to delete a field are only allowed if the selection is the same |
| as the field's selection. Otherwise the field has to be selected and the key |
| input action has to be skipped. |
| Input of text at the start of the field requires the field attribute to be |
| corrected - it is not allowed to grow. |
| |
| In case of errors the appending of text should grow the error attribute because |
| that is what the user usually wants to do. |
| |
| Backspace at the start of the attribute requires to find out if a field ends |
| directly in front of the cursor position. In case of a field this attribute has to be |
| selected otherwise the key input method is allowed. |
| |
| All changes outside of the error attributes switch the dialog mode to a "Undo edit" state that |
| removes all visible attributes and switches off further attribute checks. |
| Undo in this restarts the dialog with a current sentence newly presented. |
| All changes to the sentence are undone including the ones before the "Undo edit state" has been reached |
| |
| We end up with 9 types of selection |
| 1 (LEFT_NO) - no range, start of attribute - can also be 3 at the same time |
| 2 (INSIDE_NO) - no range, inside of attribute |
| 3 (RIGHT_NO) - no range, end of attribute - can also be 1 at the same time |
| 4 (FULL) - range, same as attribute |
| 5 (INSIDE_YES) - range, inside of the attribute |
| 6 (BRACE)- range, from outside of the attribute to the inside or |
| including the complete attribute and something outside, |
| maybe more than one attribute |
| 7 (OUTSIDE_NO) - no range, not at an attribute |
| 8 (OUTSIDE_YES) - range, completely outside of all attributes |
| |
| What has to be done depending on the attribute type involved |
| possible actions: UE - Undo edit mode |
| CO - Continue, no additional action is required |
| FS - Field has to be completely selected |
| EX - The attribute has to be expanded to include the added text |
| |
| 1 - backspace delete any other |
| UE on field FS on error CO on field FS on error CO |
| |
| 2 - on field FS on error C |
| 3 - backspace delete any other |
| on field FS on error CO UE on field UE on error EX |
| |
| if 1 and 3 happen to apply both then backspace and other handling is 1 delete is 3 |
| |
| 4 - on field UE and on error CO |
| 5 - on field FS and on error CO |
| 6 - on field FS and on error UE |
| 7 - UE |
| 8 - UE |
| -----------------------------------------------------------------------*/ |
| #define INVALID 0 |
| #define LEFT_NO 1 |
| #define INSIDE_NO 2 |
| #define RIGHT_NO 3 |
| #define FULL 4 |
| #define INSIDE_YES 5 |
| #define BRACE 6 |
| #define OUTSIDE_NO 7 |
| #define OUTSIDE_YES 8 |
| |
| #define ACTION_UNDOEDIT 0 |
| #define ACTION_CONTINUE 1 |
| #define ACTION_SELECTFIELD 2 |
| #define ACTION_EXPAND 3 |
| |
| long SentenceEditWindow_Impl::PreNotify( NotifyEvent& rNEvt ) |
| { |
| bool bChange = false; |
| const TextCharAttrib* pErrorAttrib = 0; |
| if(rNEvt.GetType() == EVENT_KEYINPUT) |
| { |
| const KeyEvent& rKeyEvt = *rNEvt.GetKeyEvent(); |
| bChange = TextEngine::DoesKeyChangeText( rKeyEvt ); |
| if(bChange && !IsUndoEditMode() && |
| rKeyEvt.GetKeyCode().GetCode() != KEY_TAB) |
| { |
| TextEngine* pTextEngine = GetTextEngine(); |
| TextView* pTextView = pTextEngine->GetActiveView(); |
| const TextSelection& rCurrentSelection = pTextView->GetSelection(); |
| //determine if the selection contains a field |
| bool bHasField = false; |
| bool bHasError = false; |
| bool bHasFieldLeft = false; |
| bool bHasErrorLeft = false; |
| // bool bInsideAttr = false; |
| |
| bool bHasRange = rCurrentSelection.HasRange(); |
| sal_uInt8 nSelectionType = 0; // invalid type! |
| |
| TextPaM aCursor(rCurrentSelection.GetStart()); |
| const TextCharAttrib* pBackAttr = pTextEngine->FindCharAttrib( aCursor, TEXTATTR_SPELL_BACKGROUND ); |
| const TextCharAttrib* pErrorAttr = pTextEngine->FindCharAttrib( aCursor, TEXTATTR_SPELL_ERROR ); |
| const TextCharAttrib* pBackAttrLeft = 0; |
| const TextCharAttrib* pErrorAttrLeft = 0; |
| |
| bHasField = pBackAttr != 0 && (bHasRange || pBackAttr->GetEnd() > aCursor.GetIndex()); |
| bHasError = pErrorAttr != 0 && (bHasRange || pErrorAttr->GetEnd() > aCursor.GetIndex()); |
| if(bHasRange) |
| { |
| if(pBackAttr && |
| pBackAttr->GetStart() == rCurrentSelection.GetStart().GetIndex() && |
| pBackAttr->GetEnd() == rCurrentSelection.GetEnd().GetIndex()) |
| { |
| nSelectionType = FULL; |
| } |
| else if(pErrorAttr && |
| pErrorAttr->GetStart() <= rCurrentSelection.GetStart().GetIndex() && |
| pErrorAttr->GetEnd() >= rCurrentSelection.GetEnd().GetIndex()) |
| { |
| nSelectionType = INSIDE_YES; |
| } |
| else |
| { |
| nSelectionType = bHasField||bHasError ? BRACE : OUTSIDE_NO; |
| while(aCursor.GetIndex() < rCurrentSelection.GetEnd().GetIndex()) |
| { |
| ++aCursor.GetIndex(); |
| const TextCharAttrib* pIntBackAttr = pTextEngine->FindCharAttrib( aCursor, TEXTATTR_SPELL_BACKGROUND ); |
| const TextCharAttrib* pIntErrorAttr = pTextEngine->FindCharAttrib( aCursor, TEXTATTR_SPELL_ERROR ); |
| //if any attr has been found then BRACE |
| if(pIntBackAttr || pIntErrorAttr) |
| nSelectionType = BRACE; |
| //the field has to be selected |
| if(pIntBackAttr && !pBackAttr) |
| pBackAttr = pIntBackAttr; |
| bHasField |= pIntBackAttr != 0; |
| } |
| } |
| } |
| else |
| { |
| //no range selection: then 1 2 3 and 8 are possible |
| const TextCharAttrib* pCurAttr = pBackAttr ? pBackAttr : pErrorAttr; |
| if(pCurAttr) |
| { |
| nSelectionType = pCurAttr->GetStart() == rCurrentSelection.GetStart().GetIndex() ? |
| LEFT_NO : pCurAttr->GetEnd() == rCurrentSelection.GetEnd().GetIndex() ? RIGHT_NO : INSIDE_NO; |
| } |
| else |
| nSelectionType = OUTSIDE_NO; |
| |
| bHasFieldLeft = pBackAttr && pBackAttr->GetEnd() == aCursor.GetIndex(); |
| if(bHasFieldLeft) |
| { |
| pBackAttrLeft = pBackAttr; |
| pBackAttr = 0; |
| } |
| bHasErrorLeft = pErrorAttr && pErrorAttr->GetEnd() == aCursor.GetIndex(); |
| if(bHasErrorLeft) |
| { |
| pErrorAttrLeft = pErrorAttr; |
| pErrorAttr = 0; |
| } |
| |
| //check previous position if this exists |
| //that is a redundant in the case the the attribute found above already is on the left cursor side |
| //but it's o.k. for two errors/fields side by side |
| if(aCursor.GetIndex()) |
| { |
| --aCursor.GetIndex(); |
| pBackAttrLeft = pTextEngine->FindCharAttrib( aCursor, TEXTATTR_SPELL_BACKGROUND ); |
| pErrorAttrLeft = pTextEngine->FindCharAttrib( aCursor, TEXTATTR_SPELL_ERROR ); |
| bHasFieldLeft = pBackAttrLeft !=0; |
| bHasErrorLeft = pErrorAttrLeft != 0; |
| // bInsideAttr = (bHasField || bHasError) && (bHasFieldLeft || bHasErrorLeft); |
| ++aCursor.GetIndex(); |
| } |
| } |
| //Here we have to determine if the error found is the one currently active |
| bool bIsErrorActive = (pErrorAttr && pErrorAttr->GetStart() == m_nErrorStart) || |
| (pErrorAttrLeft && pErrorAttrLeft->GetStart() == m_nErrorStart); |
| |
| DBG_ASSERT(nSelectionType != INVALID, "selection type not set!"); |
| |
| const KeyCode& rKeyCode = rKeyEvt.GetKeyCode(); |
| bool bDelete = rKeyCode.GetCode() == KEY_DELETE; |
| bool bBackspace = rKeyCode.GetCode() == KEY_BACKSPACE; |
| |
| sal_Int8 nAction = ACTION_CONTINUE; |
| // nAction = ACTION_UNDOEDIT |
| // nAction = ACTION_SELECTFIELD |
| // nAction = ACTION_EXPAND |
| switch(nSelectionType) |
| { |
| // 1 - backspace delete any other |
| // UE on field FS on error CO on field FS on error CO |
| case LEFT_NO : |
| if(bBackspace) |
| { |
| nAction = bHasFieldLeft ? ACTION_SELECTFIELD : ACTION_UNDOEDIT; |
| //to force the use of pBackAttrLeft |
| pBackAttr = 0; |
| } |
| else if(bDelete) |
| nAction = bHasField ? ACTION_SELECTFIELD : ACTION_CONTINUE; |
| else |
| nAction = bHasError && !aCursor.GetIndex() ? ACTION_CONTINUE : |
| bHasError ? ACTION_EXPAND : bHasErrorLeft ? ACTION_CONTINUE : ACTION_UNDOEDIT; |
| break; |
| // 2 - on field FS on error C |
| case INSIDE_NO : |
| nAction = bHasField ? ACTION_SELECTFIELD : |
| bIsErrorActive ? ACTION_CONTINUE : ACTION_UNDOEDIT; |
| break; |
| // 3 - backspace delete any other |
| // on field FS on error CO UE on field UE on error EX |
| case RIGHT_NO : |
| if(bBackspace) |
| nAction = bHasFieldLeft ? ACTION_SELECTFIELD : ACTION_CONTINUE; |
| else if(bDelete) |
| nAction = bHasFieldLeft && bHasError ? ACTION_CONTINUE : ACTION_UNDOEDIT; |
| else |
| nAction = bHasFieldLeft && bHasError ? ACTION_EXPAND : |
| bHasError ? ACTION_CONTINUE : bHasErrorLeft ? ACTION_EXPAND :ACTION_UNDOEDIT; |
| break; |
| // 4 - on field UE and on error CO |
| case FULL : |
| nAction = bHasField ? ACTION_UNDOEDIT : ACTION_CONTINUE; |
| break; |
| // 5 - on field FS and on error CO |
| case INSIDE_YES : |
| nAction = bHasField ? ACTION_SELECTFIELD : ACTION_CONTINUE; |
| break; |
| // 6 - on field FS and on error UE |
| case BRACE : |
| nAction = bHasField ? ACTION_SELECTFIELD : ACTION_UNDOEDIT;; |
| break; |
| // 7 - UE |
| // 8 - UE |
| case OUTSIDE_NO : |
| case OUTSIDE_YES: |
| nAction = ACTION_UNDOEDIT; |
| break; |
| } |
| //save the current paragraph |
| sal_uInt16 nCurrentLen = GetText().Len(); |
| if(nAction != ACTION_SELECTFIELD) |
| pTextView->GetWindow()->KeyInput(rKeyEvt); |
| else |
| { |
| const TextCharAttrib* pCharAttr = pBackAttr ? pBackAttr : pBackAttrLeft; |
| if(pCharAttr) |
| { |
| TextPaM aStart(0, pCharAttr->GetStart()); |
| TextPaM aEnd(0, pCharAttr->GetEnd()); |
| TextSelection aNewSel(aStart, aEnd); |
| pTextView->SetSelection( aNewSel); |
| } |
| } |
| if(nAction == ACTION_EXPAND) |
| { |
| DBG_ASSERT(pErrorAttrLeft || pErrorAttr, "where is the error"); |
| //text has been added on the right and only the 'error attribute has to be corrected |
| if(pErrorAttrLeft) |
| { |
| TextAttrib* pNewError = pErrorAttrLeft->GetAttr().Clone(); |
| sal_uInt16 nStart = pErrorAttrLeft->GetStart(); |
| sal_uInt16 nEnd = pErrorAttrLeft->GetEnd(); |
| pTextEngine->RemoveAttrib( 0, *pErrorAttrLeft ); |
| SetAttrib( *pNewError, 0, nStart, ++nEnd ); |
| //only active errors move the mark |
| if(bIsErrorActive) |
| { |
| bool bGrammar = static_cast<const SpellErrorAttrib&>(*pNewError).GetErrorDescription().bIsGrammarError; |
| MoveErrorMarkTo(nStart, nEnd, bGrammar); |
| } |
| delete pNewError; |
| } |
| //text has been added on the left then the error attribute has to be expanded and the |
| //field attribute on the right - if any - has to be contracted |
| else if(pErrorAttr) |
| { |
| //determine the change |
| sal_uInt16 nAddedChars = GetText().Len() - nCurrentLen; |
| |
| TextAttrib* pNewError = pErrorAttr->GetAttr().Clone(); |
| sal_uInt16 nStart = pErrorAttr->GetStart(); |
| sal_uInt16 nEnd = pErrorAttr->GetEnd(); |
| pTextEngine->RemoveAttrib( 0, *pErrorAttr ); |
| nStart = nStart - (sal_uInt16)nAddedChars; |
| SetAttrib( *pNewError, 0, nStart - nAddedChars, nEnd ); |
| //only if the error is active the mark is moved here |
| if(bIsErrorActive) |
| { |
| bool bGrammar = static_cast<const SpellErrorAttrib&>(*pNewError).GetErrorDescription().bIsGrammarError; |
| MoveErrorMarkTo(nStart, nEnd, bGrammar); |
| } |
| delete pNewError; |
| |
| if(pBackAttrLeft) |
| { |
| TextAttrib* pNewBack = pBackAttrLeft->GetAttr().Clone(); |
| sal_uInt16 _nStart = pBackAttrLeft->GetStart(); |
| sal_uInt16 _nEnd = pBackAttrLeft->GetEnd(); |
| pTextEngine->RemoveAttrib( 0, *pBackAttrLeft ); |
| SetAttrib( *pNewBack, 0, _nStart, _nEnd - nAddedChars); |
| delete pNewBack; |
| } |
| } |
| } |
| else if(nAction == ACTION_UNDOEDIT) |
| { |
| SetUndoEditMode(true); |
| } |
| //make sure the error positions are correct after text changes |
| //the old attribute may have been deleted |
| //all changes inside of the current error leave the error attribute at the current |
| //start position |
| if(!IsUndoEditMode() && bIsErrorActive) |
| { |
| const TextCharAttrib* pFontColor = pTextEngine->FindCharAttrib( aCursor, TEXTATTR_FONTCOLOR ); |
| pErrorAttrib = pTextEngine->FindCharAttrib( TextPaM(0, m_nErrorStart), TEXTATTR_SPELL_ERROR ); |
| if(pFontColor && pErrorAttrib ) |
| { |
| m_nErrorStart = pFontColor->GetStart(); |
| m_nErrorEnd = pFontColor->GetEnd(); |
| if(pErrorAttrib->GetStart() != m_nErrorStart || pErrorAttrib->GetEnd() != m_nErrorEnd) |
| { |
| TextAttrib* pNewError = pErrorAttrib->GetAttr().Clone(); |
| pTextEngine->RemoveAttrib( 0, *pErrorAttr ); |
| SetAttrib( *pNewError, 0, m_nErrorStart, m_nErrorEnd ); |
| delete pNewError; |
| } |
| } |
| } |
| //this is not a modification anymore |
| if(nAction != ACTION_SELECTFIELD && !m_bIsUndoEditMode) |
| CallModifyLink(); |
| } |
| else |
| bChange = false; |
| } |
| long nRet = bChange ? 1 : MultiLineEdit::PreNotify(rNEvt); |
| return nRet; |
| } |
| /*-- 10.09.2003 13:38:14--------------------------------------------------- |
| |
| -----------------------------------------------------------------------*/ |
| bool SentenceEditWindow_Impl::MarkNextError( bool bIgnoreCurrentError ) |
| { |
| if (bIgnoreCurrentError) |
| m_aIgnoreErrorsAt.insert( m_nErrorStart ); |
| ExtTextEngine* pTextEngine = GetTextEngine(); |
| sal_uInt16 nTextLen = pTextEngine->GetTextLen(0); |
| if(m_nErrorEnd >= nTextLen - 1) |
| return false; |
| //if it's not already modified the modified flag has to be reset at the and of the marking |
| bool bModified = IsModified(); |
| bool bRet = false; |
| const sal_uInt16 nOldErrorStart = m_nErrorStart; |
| const sal_uInt16 nOldErrorEnd = m_nErrorEnd; |
| |
| //create a cursor behind the end of the last error |
| //- or at 0 at the start of the sentence |
| TextPaM aCursor(0, m_nErrorEnd ? m_nErrorEnd + 1 : 0); |
| //search for SpellErrorAttrib |
| |
| const TextCharAttrib* pNextError = 0; |
| //iterate over the text and search for the next error that maybe has |
| //to be replace by a ChangeAllList replacement |
| bool bGrammarError = false; |
| while(aCursor.GetIndex() < nTextLen) |
| { |
| while(aCursor.GetIndex() < nTextLen && |
| 0 == (pNextError = pTextEngine->FindCharAttrib( aCursor, TEXTATTR_SPELL_ERROR))) |
| { |
| ++aCursor.GetIndex(); |
| } |
| // maybe the error found here is already in the ChangeAllList and has to be replaced |
| |
| Reference<XDictionary> xChangeAll( SvxGetChangeAllList(), UNO_QUERY ); |
| Reference<XDictionaryEntry> xEntry; |
| |
| // Reference <XSpellAlternatives> xAlternatives; |
| const SpellErrorDescription* pSpellErrorDescription = 0; |
| if(pNextError) |
| { |
| pSpellErrorDescription = &static_cast<const SpellErrorAttrib&>(pNextError->GetAttr()).GetErrorDescription(); |
| bGrammarError = pSpellErrorDescription->bIsGrammarError; |
| } |
| if(xChangeAll->getCount() && pSpellErrorDescription && |
| (xEntry = xChangeAll->getEntry( pSpellErrorDescription->sErrorText )).is()) |
| { |
| m_nErrorStart = pNextError->GetStart(); |
| m_nErrorEnd = pNextError->GetEnd(); |
| ChangeMarkedWord(xEntry->getReplacementText(), |
| SvxLocaleToLanguage( pSpellErrorDescription->aLocale )); |
| aCursor.GetIndex() = aCursor.GetIndex() + (sal_uInt16)(xEntry->getReplacementText().getLength()); |
| } |
| else |
| break; |
| } |
| |
| //if an attrib has been found search for the end of the error string |
| if(aCursor.GetIndex() < nTextLen) |
| { |
| m_nErrorStart = aCursor.GetIndex(); |
| m_nErrorEnd = pNextError->GetEnd(); |
| MoveErrorMarkTo(m_nErrorStart, m_nErrorEnd, bGrammarError); |
| bRet = true; |
| //add an undo action |
| SpellUndoAction_Impl* pAction = new SpellUndoAction_Impl( |
| SPELLUNDO_CHANGE_NEXTERROR, GetSpellDialog()->aDialogUndoLink); |
| pAction->SetErrorMove(m_nErrorStart, m_nErrorEnd, nOldErrorStart, nOldErrorEnd); |
| const SpellErrorAttrib* pOldAttrib = static_cast<const SpellErrorAttrib*>( |
| pTextEngine->FindAttrib( TextPaM(0, nOldErrorStart), TEXTATTR_SPELL_ERROR )); |
| pAction->SetErrorLanguageSelected(pOldAttrib && pOldAttrib->GetErrorDescription().aSuggestions.getLength() && |
| SvxLocaleToLanguage( pOldAttrib->GetErrorDescription().aLocale) == |
| GetSpellDialog()->aLanguageLB.GetSelectLanguage()); |
| AddUndoAction(pAction); |
| } |
| else |
| m_nErrorStart = m_nErrorEnd = nTextLen; |
| if( !bModified ) |
| ClearModifyFlag(); |
| SpellDialog* pSpellDialog = GetSpellDialog(); |
| pSpellDialog->aIgnorePB.Enable(bRet); |
| pSpellDialog->aIgnoreAllPB.Enable(bRet); |
| pSpellDialog->aAutoCorrPB.Enable(bRet); |
| pSpellDialog->aAddToDictMB.Enable(bRet); |
| return bRet; |
| } |
| |
| /*-- 06.11.2003 13:30:26--------------------------------------------------- |
| |
| -----------------------------------------------------------------------*/ |
| void SentenceEditWindow_Impl::MoveErrorMarkTo(sal_uInt16 nStart, sal_uInt16 nEnd, bool bGrammarError) |
| { |
| TextEngine* pTextEngine = GetTextEngine(); |
| pTextEngine->RemoveAttribs( 0, (sal_uInt16)TEXTATTR_FONTCOLOR, sal_True ); |
| pTextEngine->RemoveAttribs( 0, (sal_uInt16)TEXTATTR_FONTWEIGHT, sal_True ); |
| pTextEngine->SetAttrib( TextAttribFontWeight(WEIGHT_BOLD), 0, nStart, nEnd ); |
| pTextEngine->SetAttrib( TextAttribFontColor(bGrammarError ? COL_LIGHTBLUE : COL_LIGHTRED), 0, nStart, nEnd ); |
| m_nErrorStart = nStart; |
| m_nErrorEnd = nEnd; |
| } |
| |
| /*-- 17.09.2003 10:13:08--------------------------------------------------- |
| |
| -----------------------------------------------------------------------*/ |
| void SentenceEditWindow_Impl::ChangeMarkedWord(const String& rNewWord, LanguageType eLanguage) |
| { |
| //calculate length changes |
| long nDiffLen = rNewWord.Len() - m_nErrorEnd + m_nErrorStart; |
| TextSelection aSel(TextPaM(0, m_nErrorStart), TextPaM(0, m_nErrorEnd)); |
| //Remove spell errror attribute |
| ExtTextEngine* pTextEngine = GetTextEngine(); |
| pTextEngine->UndoActionStart(); |
| const TextCharAttrib* pErrorAttrib = pTextEngine->FindCharAttrib( TextPaM(0, m_nErrorStart), TEXTATTR_SPELL_ERROR ); |
| DBG_ASSERT(pErrorAttrib, "no error attribute found"); |
| // Reference <XSpellAlternatives> xAlternatives; |
| const SpellErrorDescription* pSpellErrorDescription = 0; |
| if(pErrorAttrib) |
| { |
| pTextEngine->RemoveAttrib(0, *pErrorAttrib); |
| pSpellErrorDescription = &static_cast<const SpellErrorAttrib&>(pErrorAttrib->GetAttr()).GetErrorDescription(); |
| } |
| const TextCharAttrib* pBackAttrib = pTextEngine->FindCharAttrib( TextPaM(0, m_nErrorStart), TEXTATTR_SPELL_BACKGROUND ); |
| pTextEngine->ReplaceText( aSel, rNewWord ); |
| // |
| if(!m_nErrorStart) |
| { |
| //attributes following an error at the start of the text are not moved but expanded from the |
| //text engine - this is done to keep full-paragraph-attributes |
| //in the current case that handling is not desired |
| const TextCharAttrib* pLangAttrib = |
| pTextEngine->FindCharAttrib( |
| TextPaM(0, m_nErrorEnd), TEXTATTR_SPELL_LANGUAGE ); |
| sal_uInt16 nTextLen = pTextEngine->GetTextLen( 0 ); |
| if(pLangAttrib && !pLangAttrib->GetStart() && pLangAttrib->GetEnd() == |
| nTextLen) |
| { |
| SpellLanguageAttrib aNewLangAttrib( static_cast<const SpellLanguageAttrib&>(pLangAttrib->GetAttr()).GetLanguage()); |
| pTextEngine->RemoveAttrib(0, *pLangAttrib); |
| pTextEngine->SetAttrib( aNewLangAttrib, 0, (sal_uInt16)(m_nErrorEnd + nDiffLen) , nTextLen ); |
| } |
| } |
| // undo expanded attributes! |
| if( pBackAttrib && pBackAttrib->GetStart() < m_nErrorStart && pBackAttrib->GetEnd() == m_nErrorEnd + nDiffLen) |
| { |
| TextAttrib* pNewBackground = pBackAttrib->GetAttr().Clone(); |
| sal_uInt16 nStart = pBackAttrib->GetStart(); |
| pTextEngine->RemoveAttrib(0, *pBackAttrib); |
| pTextEngine->SetAttrib(*pNewBackground, 0, nStart, m_nErrorStart); |
| delete pNewBackground; |
| } |
| pTextEngine->SetModified(sal_True); |
| |
| //adjust end position |
| long nEndTemp = m_nErrorEnd; |
| nEndTemp += nDiffLen; |
| m_nErrorEnd = (sal_uInt16)nEndTemp; |
| |
| SpellUndoAction_Impl* pAction = new SpellUndoAction_Impl( |
| SPELLUNDO_MOVE_ERROREND, GetSpellDialog()->aDialogUndoLink); |
| pAction->SetOffset(nDiffLen); |
| AddUndoAction(pAction); |
| if(pSpellErrorDescription) |
| SetAttrib( SpellErrorAttrib(*pSpellErrorDescription), 0, m_nErrorStart, m_nErrorEnd ); |
| SetAttrib( SpellLanguageAttrib(eLanguage), 0, m_nErrorStart, m_nErrorEnd ); |
| pTextEngine->UndoActionEnd(); |
| } |
| /* -----------------08.10.2003 13:18----------------- |
| |
| --------------------------------------------------*/ |
| String SentenceEditWindow_Impl::GetErrorText() const |
| { |
| return GetTextEngine()->GetText(TextSelection(TextPaM(0, m_nErrorStart), TextPaM(0, m_nErrorEnd) )); |
| } |
| /*-- 26.06.2008 10:54:13--------------------------------------------------- |
| |
| -----------------------------------------------------------------------*/ |
| const SpellErrorDescription* SentenceEditWindow_Impl::GetAlternatives() |
| { |
| TextPaM aCursor(0, m_nErrorStart); |
| const SpellErrorAttrib* pAttrib = static_cast<const SpellErrorAttrib*>( |
| GetTextEngine()->FindAttrib( aCursor, TEXTATTR_SPELL_ERROR)); |
| return pAttrib ? &pAttrib->GetErrorDescription() : 0; |
| } |
| /*-- 06.09.2004 10:50:32--------------------------------------------------- |
| |
| -----------------------------------------------------------------------*/ |
| void SentenceEditWindow_Impl::RestoreCurrentError() |
| { |
| TextPaM aCursor(0, m_nErrorStart); |
| const SpellErrorAttrib* pAttrib = static_cast<const SpellErrorAttrib*>( |
| GetTextEngine()->FindAttrib( aCursor, TEXTATTR_SPELL_ERROR)); |
| if( pAttrib ) |
| { |
| const SpellErrorDescription& rDesc = pAttrib->GetErrorDescription(); |
| if( !rDesc.sErrorText.equals( GetErrorText() ) ) |
| ChangeMarkedWord(rDesc.sErrorText, SvxLocaleToLanguage( rDesc.aLocale )); |
| } |
| } |
| /*-- 28.10.2003 14:44:10--------------------------------------------------- |
| |
| -----------------------------------------------------------------------*/ |
| void SentenceEditWindow_Impl::SetAlternatives( Reference< XSpellAlternatives> xAlt ) |
| { |
| TextPaM aCursor(0, m_nErrorStart); |
| DBG_ASSERT(static_cast<const SpellErrorAttrib*>( |
| GetTextEngine()->FindAttrib( aCursor, TEXTATTR_SPELL_ERROR)), "no error set?"); |
| |
| ::rtl::OUString aWord; |
| lang::Locale aLocale; |
| uno::Sequence< ::rtl::OUString > aAlts; |
| ::rtl::OUString sServiceName; |
| if (xAlt.is()) |
| { |
| aWord = xAlt->getWord(); |
| aLocale = xAlt->getLocale(); |
| aAlts = xAlt->getAlternatives(); |
| uno::Reference< container::XNamed > xNamed( xAlt, uno::UNO_QUERY ); |
| if (xNamed.is()) |
| sServiceName = xNamed->getName(); |
| } |
| SpellErrorDescription aDesc( false, aWord, aLocale, aAlts, 0, sServiceName); |
| GetTextEngine()->SetAttrib( SpellErrorAttrib(aDesc), 0, m_nErrorStart, m_nErrorEnd ); |
| } |
| |
| /*-- 10.09.2003 14:43:02--------------------------------------------------- |
| |
| -----------------------------------------------------------------------*/ |
| void SentenceEditWindow_Impl::SetAttrib( const TextAttrib& rAttr, sal_uLong nPara, sal_uInt16 nStart, sal_uInt16 nEnd ) |
| { |
| GetTextEngine()->SetAttrib(rAttr, nPara, nStart, nEnd); |
| } |
| /*-- 10.09.2003 14:43:02--------------------------------------------------- |
| |
| -----------------------------------------------------------------------*/ |
| void SentenceEditWindow_Impl::SetText( const String& rStr ) |
| { |
| m_nErrorStart = m_nErrorEnd = 0; |
| GetTextEngine()->SetText(rStr); |
| // InitScrollBars(); |
| } |
| /*-- 08.10.2003 14:35:52--------------------------------------------------- |
| |
| -----------------------------------------------------------------------*/ |
| struct LanguagePosition_Impl |
| { |
| sal_uInt16 nPosition; |
| LanguageType eLanguage; |
| |
| LanguagePosition_Impl(sal_uInt16 nPos, LanguageType eLang) : |
| nPosition(nPos), |
| eLanguage(eLang) |
| {} |
| }; |
| typedef std::vector<LanguagePosition_Impl> LanguagePositions_Impl; |
| |
| void lcl_InsertBreakPosition_Impl( |
| LanguagePositions_Impl& rBreakPositions, sal_uInt16 nInsert, LanguageType eLanguage) |
| { |
| LanguagePositions_Impl::iterator aStart = rBreakPositions.begin(); |
| while(aStart != rBreakPositions.end()) |
| { |
| if(aStart->nPosition == nInsert) |
| { |
| //the language of following starts has to overwrite |
| //the one of previous ends |
| aStart->eLanguage = eLanguage; |
| return; |
| } |
| else if(aStart->nPosition > nInsert) |
| { |
| |
| rBreakPositions.insert(aStart, LanguagePosition_Impl(nInsert, eLanguage)); |
| return; |
| } |
| else |
| ++aStart; |
| } |
| rBreakPositions.push_back(LanguagePosition_Impl(nInsert, eLanguage)); |
| } |
| /*-- 17.09.2003 14:26:59--------------------------------------------------- |
| Returns the text in spell portions. Each portion contains text with an |
| equal language and attribute. The spell alternatives are empty. |
| -----------------------------------------------------------------------*/ |
| svx::SpellPortions SentenceEditWindow_Impl::CreateSpellPortions( bool bSetIgnoreFlag ) const |
| { |
| svx::SpellPortions aRet; |
| ExtTextEngine* pTextEngine = GetTextEngine(); |
| const sal_uInt16 nTextLen = pTextEngine->GetTextLen(0); |
| if(nTextLen) |
| { |
| TextPaM aCursor(0, 0); |
| LanguagePositions_Impl aBreakPositions; |
| const TextCharAttrib* pLastLang = 0; |
| const TextCharAttrib* pLastError = 0; |
| LanguageType eLang = LANGUAGE_DONTKNOW; |
| const TextCharAttrib* pError = 0; |
| while(aCursor.GetIndex() < nTextLen) |
| { |
| const TextCharAttrib* pLang = pTextEngine->FindCharAttrib( aCursor, TEXTATTR_SPELL_LANGUAGE); |
| if(pLang && pLang != pLastLang) |
| { |
| eLang = static_cast<const SpellLanguageAttrib&>(pLang->GetAttr()).GetLanguage(); |
| lcl_InsertBreakPosition_Impl(aBreakPositions, pLang->GetStart(), eLang); |
| lcl_InsertBreakPosition_Impl(aBreakPositions, pLang->GetEnd(), eLang); |
| pLastLang = pLang; |
| } |
| pError = pTextEngine->FindCharAttrib( aCursor, TEXTATTR_SPELL_ERROR); |
| if(pError && pLastError != pError) |
| { |
| lcl_InsertBreakPosition_Impl(aBreakPositions, pError->GetStart(), eLang); |
| lcl_InsertBreakPosition_Impl(aBreakPositions, pError->GetEnd(), eLang); |
| pLastError = pError; |
| |
| } |
| aCursor.GetIndex()++; |
| } |
| // |
| if(nTextLen && aBreakPositions.empty()) |
| { |
| //if all content has been overwritten the attributes may have been removed, too |
| svx::SpellPortion aPortion1; |
| aPortion1.eLanguage = GetSpellDialog()->GetSelectedLang_Impl(); |
| aPortion1.sText = pTextEngine->GetText( |
| TextSelection(TextPaM(0, 0), TextPaM(0, nTextLen))); |
| |
| aRet.push_back(aPortion1); |
| |
| } |
| else if(!aBreakPositions.empty()) |
| { |
| LanguagePositions_Impl::iterator aStart = aBreakPositions.begin(); |
| //start should always be Null |
| eLang = aStart->eLanguage; |
| sal_uInt16 nStart = aStart->nPosition; |
| DBG_ASSERT(!nStart, "invalid start position - language attribute missing?"); |
| ++aStart; |
| |
| while(aStart != aBreakPositions.end()) |
| { |
| svx::SpellPortion aPortion1; |
| aPortion1.eLanguage = eLang; |
| aPortion1.sText = pTextEngine->GetText( |
| TextSelection(TextPaM(0, nStart), TextPaM(0, aStart->nPosition))); |
| bool bIsIgnoreError = m_aIgnoreErrorsAt.find( nStart ) != m_aIgnoreErrorsAt.end(); |
| if( bSetIgnoreFlag && bIsIgnoreError /*m_nErrorStart == nStart*/ ) |
| { |
| aPortion1.bIgnoreThisError = true; |
| } |
| aRet.push_back(aPortion1); |
| nStart = aStart->nPosition; |
| eLang = aStart->eLanguage; |
| ++aStart; |
| } |
| } |
| |
| // quick partly fix of #i71318. Correct fix needs to patch the TextEngine itself... |
| // this one will only prevent text from disappearing. It may to not have the |
| // correct language and will probably not spell checked... |
| sal_uLong nPara = pTextEngine->GetParagraphCount(); |
| if (nPara > 1) |
| { |
| String aLeftOverText; |
| for (sal_uLong i = 1; i < nPara; ++i) |
| { |
| aLeftOverText.AppendAscii( "\x0a" ); // the manual line break... |
| aLeftOverText += pTextEngine->GetText(i); |
| } |
| if (pError) |
| { // we need to add a new portion containing the left-over text |
| svx::SpellPortion aPortion2; |
| aPortion2.eLanguage = eLang; |
| aPortion2.sText = aLeftOverText; |
| aRet.push_back( aPortion2 ); |
| } |
| else |
| { // we just need to append the left-over text to the last portion (which had no errors) |
| aRet[ aRet.size() - 1 ].sText += aLeftOverText; |
| } |
| } |
| } |
| return aRet; |
| } |
| |
| /*-- 06.11.2003 11:30:10--------------------------------------------------- |
| |
| -----------------------------------------------------------------------*/ |
| void SentenceEditWindow_Impl::Undo() |
| { |
| ::svl::IUndoManager& rUndoMgr = GetTextEngine()->GetUndoManager(); |
| DBG_ASSERT(GetUndoActionCount(), "no undo actions available" ); |
| if(!GetUndoActionCount()) |
| return; |
| bool bSaveUndoEdit = IsUndoEditMode(); |
| sal_uInt16 nId; |
| //if the undo edit mode is active then undo all changes until the UNDO_EDIT_MODE action has been found |
| do |
| { |
| nId = rUndoMgr.GetUndoActionId(); |
| rUndoMgr.Undo(); |
| }while(bSaveUndoEdit && SPELLUNDO_UNDO_EDIT_MODE != nId && GetUndoActionCount()); |
| |
| if(bSaveUndoEdit || SPELLUNDO_CHANGE_GROUP == nId) |
| GetSpellDialog()->UpdateBoxes_Impl(); |
| } |
| /*-- 06.11.2003 11:30:10--------------------------------------------------- |
| |
| -----------------------------------------------------------------------*/ |
| void SentenceEditWindow_Impl::ResetUndo() |
| { |
| GetTextEngine()->ResetUndo(); |
| } |
| /*-- 06.11.2003 12:30:41--------------------------------------------------- |
| |
| -----------------------------------------------------------------------*/ |
| void SentenceEditWindow_Impl::AddUndoAction( SfxUndoAction *pAction, sal_Bool bTryMerg ) |
| { |
| ::svl::IUndoManager& rUndoMgr = GetTextEngine()->GetUndoManager(); |
| rUndoMgr.AddUndoAction(pAction, bTryMerg); |
| GetSpellDialog()->aUndoPB.Enable(); |
| } |
| /*-- 06.11.2003 12:38:44--------------------------------------------------- |
| |
| -----------------------------------------------------------------------*/ |
| sal_uInt16 SentenceEditWindow_Impl::GetUndoActionCount() |
| { |
| return GetTextEngine()->GetUndoManager().GetUndoActionCount(); |
| } |
| |
| /*-- 12.11.2003 12:12:38--------------------------------------------------- |
| |
| -----------------------------------------------------------------------*/ |
| void SentenceEditWindow_Impl::UndoActionStart( sal_uInt16 nId ) |
| { |
| GetTextEngine()->UndoActionStart(nId); |
| } |
| /*-- 12.11.2003 12:12:38--------------------------------------------------- |
| |
| -----------------------------------------------------------------------*/ |
| void SentenceEditWindow_Impl::UndoActionEnd() |
| { |
| GetTextEngine()->UndoActionEnd(); |
| } |
| /*-- 12.11.2003 12:12:38--------------------------------------------------- |
| |
| -----------------------------------------------------------------------*/ |
| void SentenceEditWindow_Impl::MoveErrorEnd(long nOffset) |
| { |
| if(nOffset > 0) |
| m_nErrorEnd = m_nErrorEnd - (sal_uInt16)nOffset; |
| else |
| m_nErrorEnd = m_nErrorEnd -(sal_uInt16)- nOffset; |
| } |
| /*-- 13.11.2003 15:15:19--------------------------------------------------- |
| |
| -----------------------------------------------------------------------*/ |
| void SentenceEditWindow_Impl::SetUndoEditMode(bool bSet) |
| { |
| DBG_ASSERT(!bSet || m_bIsUndoEditMode != bSet, "SetUndoEditMode with equal values?"); |
| m_bIsUndoEditMode = bSet; |
| //disable all buttons except the Change |
| SpellDialog* pSpellDialog = GetSpellDialog(); |
| Control* aControls[] = |
| { |
| &pSpellDialog->aChangeAllPB, |
| &pSpellDialog->aExplainPB, |
| &pSpellDialog->aIgnoreAllPB, |
| &pSpellDialog->aIgnoreRulePB, |
| &pSpellDialog->aIgnorePB, |
| &pSpellDialog->aSuggestionLB, |
| &pSpellDialog->aSuggestionFT, |
| &pSpellDialog->aLanguageFT, |
| &pSpellDialog->aLanguageLB, |
| &pSpellDialog->aAddToDictMB, |
| &pSpellDialog->aAutoCorrPB, |
| 0 |
| }; |
| sal_Int32 nIdx = 0; |
| do |
| { |
| aControls[nIdx]->Enable(sal_False); |
| } |
| while(aControls[++nIdx]); |
| |
| //remove error marks |
| TextEngine* pTextEngine = GetTextEngine(); |
| pTextEngine->RemoveAttribs( 0, (sal_uInt16)TEXTATTR_FONTCOLOR, sal_True ); |
| pTextEngine->RemoveAttribs( 0, (sal_uInt16)TEXTATTR_FONTWEIGHT, sal_True ); |
| |
| //put the appropriate action on the Undo-stack |
| SpellUndoAction_Impl* pAction = new SpellUndoAction_Impl( |
| SPELLUNDO_UNDO_EDIT_MODE, GetSpellDialog()->aDialogUndoLink); |
| AddUndoAction(pAction); |
| pSpellDialog->aChangePB.Enable(); |
| } |
| |
| /*-- 30.06.2008 14:15:19--------------------------------------------------- |
| |
| -----------------------------------------------------------------------*/ |
| ExplainButton::~ExplainButton() |
| { |
| } |
| /*-- 30.06.2008 14:15:19--------------------------------------------------- |
| |
| -----------------------------------------------------------------------*/ |
| void ExplainButton::RequestHelp( const HelpEvent& ) |
| { |
| Help::ShowBalloon( this, GetPosPixel(), m_sExplanation ); |
| } |
| |
| void ExplainButton::Click() |
| { |
| RequestHelp( HelpEvent() ); |
| } |