1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "thesdlg.hxx"
21 #include "thesdlg_impl.hxx"
23 #include "dialmgr.hxx"
25 #include <svl/lngmisc.hxx>
26 #include <vcl/graphicfilter.hxx>
27 #include <svtools/svlbitm.hxx>
28 #include <svtools/treelistbox.hxx>
29 #include "svtools/treelistentry.hxx"
30 #include "svtools/viewdataentry.hxx"
31 #include <vcl/wrkwin.hxx>
32 #include <vcl/msgbox.hxx>
33 #include <vcl/svapp.hxx>
34 #include <vcl/builderfactory.hxx>
35 #include <svx/dlgutil.hxx>
36 #include <svx/dialmgr.hxx>
37 #include <svx/svxerr.hxx>
38 #include <editeng/unolingu.hxx>
39 #include <svx/langbox.hxx>
40 #include <svtools/langtab.hxx>
41 #include <unotools/lingucfg.hxx>
42 #include <i18nlangtag/mslangid.hxx>
43 #include <comphelper/processfactory.hxx>
44 #include <comphelper/string.hxx>
45 #include <osl/file.hxx>
50 #include <com/sun/star/linguistic2/XThesaurus.hpp>
51 #include <com/sun/star/linguistic2/XMeaning.hpp>
52 #include <com/sun/star/linguistic2/LinguServiceManager.hpp>
54 using namespace ::com::sun::star
;
56 // class LookUpComboBox --------------------------------------------------
58 LookUpComboBox::LookUpComboBox(vcl::Window
*pParent
)
59 : ComboBox(pParent
, WB_LEFT
|WB_DROPDOWN
|WB_VCENTER
|WB_3DLOOK
|WB_TABSTOP
)
64 m_aModifyIdle
.SetIdleHdl( LINK( this, LookUpComboBox
, ModifyTimer_Hdl
) );
65 m_aModifyIdle
.SetPriority( SchedulerPriority::LOWEST
);
67 EnableAutocomplete( false );
70 LookUpComboBox::~LookUpComboBox()
75 void LookUpComboBox::dispose()
81 VCL_BUILDER_FACTORY(LookUpComboBox
)
83 void LookUpComboBox::init(SvxThesaurusDialog
*pDialog
)
88 void LookUpComboBox::Modify()
90 m_aModifyIdle
.Start();
93 IMPL_LINK_NOARG_TYPED( LookUpComboBox
, ModifyTimer_Hdl
, Idle
*, void )
95 m_pDialog
->LookUp( GetText() );
99 // class ReplaceEdit --------------------------------------------------
101 ReplaceEdit::ReplaceEdit(vcl::Window
*pParent
)
102 : Edit(pParent
, WB_BORDER
| WB_TABSTOP
)
107 ReplaceEdit::~ReplaceEdit()
112 void ReplaceEdit::dispose()
118 VCL_BUILDER_FACTORY(ReplaceEdit
)
120 void ReplaceEdit::Modify()
123 m_pBtn
->Enable( !GetText().isEmpty() );
126 void ReplaceEdit::SetText( const OUString
& rStr
)
128 Edit::SetText( rStr
);
132 void ReplaceEdit::SetText( const OUString
& rStr
, const Selection
& rNewSelection
)
134 Edit::SetText( rStr
, rNewSelection
);
138 // class ThesaurusAlternativesCtrl ----------------------------------
140 AlternativesString::AlternativesString(
141 ThesaurusAlternativesCtrl
&rControl
,
142 SvTreeListEntry
* pEntry
, sal_uInt16 nFlags
, const OUString
& rStr
) :
144 SvLBoxString( pEntry
, nFlags
, rStr
),
145 m_rControlImpl( rControl
)
149 void AlternativesString::Paint(const Point
& rPos
, SvTreeListBox
& /*rDev*/, vcl::RenderContext
& rRenderContext
,
150 const SvViewDataEntry
* /*pView*/, const SvTreeListEntry
* pEntry
)
152 AlternativesExtraData
* pData
= m_rControlImpl
.GetExtraData(pEntry
);
154 rRenderContext
.Push(PushFlags::FONT
);
155 if (pData
&& pData
->IsHeader())
157 vcl::Font
aFont(rRenderContext
.GetFont());
158 aFont
.SetWeight(WEIGHT_BOLD
);
159 rRenderContext
.SetFont(aFont
);
164 rRenderContext
.DrawText(aPos
, GetText());
165 rRenderContext
.Pop();
168 ThesaurusAlternativesCtrl::ThesaurusAlternativesCtrl(vcl::Window
* pParent
)
169 : SvxCheckListBox(pParent
)
172 SetStyle( GetStyle() | WB_CLIPCHILDREN
| WB_HSCROLL
| WB_FORCE_MAKEVISIBLE
);
176 VCL_BUILDER_FACTORY(ThesaurusAlternativesCtrl
)
178 void ThesaurusAlternativesCtrl::init(SvxThesaurusDialog
*pDialog
)
183 ThesaurusAlternativesCtrl::~ThesaurusAlternativesCtrl()
188 void ThesaurusAlternativesCtrl::dispose()
192 SvxCheckListBox::dispose();
195 void ThesaurusAlternativesCtrl::ClearExtraData()
197 UserDataMap_t aEmpty
;
198 m_aUserData
.swap( aEmpty
);
201 void ThesaurusAlternativesCtrl::SetExtraData(
202 const SvTreeListEntry
*pEntry
,
203 const AlternativesExtraData
&rData
)
208 UserDataMap_t::iterator
aIt( m_aUserData
.find( pEntry
) );
209 if (aIt
!= m_aUserData
.end())
212 m_aUserData
[ pEntry
] = rData
;
215 AlternativesExtraData
* ThesaurusAlternativesCtrl::GetExtraData(
216 const SvTreeListEntry
*pEntry
)
218 AlternativesExtraData
*pRes
= NULL
;
219 UserDataMap_t::iterator
aIt( m_aUserData
.find( pEntry
) );
220 if (aIt
!= m_aUserData
.end())
225 SvTreeListEntry
* ThesaurusAlternativesCtrl::AddEntry( sal_Int32 nVal
, const OUString
&rText
, bool bIsHeader
)
227 SvTreeListEntry
* pEntry
= new SvTreeListEntry
;
229 if (bIsHeader
&& nVal
>= 0)
231 aText
= OUString::number( nVal
) + ". ";
233 pEntry
->AddItem( new SvLBoxString( pEntry
, 0, OUString() ) ); // add empty column
235 pEntry
->AddItem( new SvLBoxContextBmp( pEntry
, 0, Image(), Image(), false ) ); // otherwise crash
236 pEntry
->AddItem( new AlternativesString( *this, pEntry
, 0, aText
) );
238 SetExtraData( pEntry
, AlternativesExtraData( rText
, bIsHeader
) );
239 GetModel()->Insert( pEntry
);
242 GetViewDataEntry( pEntry
)->SetSelectable( false );
247 void ThesaurusAlternativesCtrl::KeyInput( const KeyEvent
& rKEvt
)
249 const vcl::KeyCode
& rKey
= rKEvt
.GetKeyCode();
251 if (rKey
.GetCode() == KEY_RETURN
|| rKey
.GetCode() == KEY_ESCAPE
)
252 GetParent()->KeyInput( rKEvt
); // parent will close dialog...
253 else if (rKey
.GetCode() == KEY_SPACE
)
254 m_pDialog
->AlternativesDoubleClickHdl_Impl( this ); // look up current selected entry
255 else if (GetEntryCount())
256 SvxCheckListBox::KeyInput( rKEvt
);
259 void ThesaurusAlternativesCtrl::Paint(vcl::RenderContext
& rRenderContext
, const Rectangle
& rRect
)
261 if (!m_pDialog
->WordFound())
263 Size
aTextSize(rRenderContext
.GetTextWidth(m_pDialog
->getErrStr()), rRenderContext
.GetTextHeight());
264 aTextSize
= rRenderContext
.LogicToPixel(aTextSize
);
266 aPos
.X() += GetSizePixel().Width() / 2 - aTextSize
.Width() / 2;
267 aPos
.Y() += GetSizePixel().Height() / 2;
268 aPos
= rRenderContext
.PixelToLogic(aPos
);
269 rRenderContext
.DrawText(aPos
, m_pDialog
->getErrStr());
272 SvxCheckListBox::Paint(rRenderContext
, rRect
);
275 uno::Sequence
< uno::Reference
< linguistic2::XMeaning
> > SvxThesaurusDialog::queryMeanings_Impl(
277 const lang::Locale
& rLocale
,
278 const beans::PropertyValues
& rProperties
)
279 throw(lang::IllegalArgumentException
, uno::RuntimeException
)
281 uno::Sequence
< uno::Reference
< linguistic2::XMeaning
> > aMeanings(
282 xThesaurus
->queryMeanings( rTerm
, rLocale
, rProperties
) );
284 // text with '.' at the end?
285 if ( 0 == aMeanings
.getLength() && rTerm
.endsWith(".") )
287 // try again without trailing '.' chars. It may be a word at the
288 // end of a sentence and not an abbreviation...
289 OUString
aTxt(comphelper::string::stripEnd(rTerm
, '.'));
290 aMeanings
= xThesaurus
->queryMeanings( aTxt
, rLocale
, rProperties
);
291 if (aMeanings
.getLength())
300 bool SvxThesaurusDialog::UpdateAlternativesBox_Impl()
302 lang::Locale
aLocale( LanguageTag::convertToLocale( nLookUpLanguage
) );
303 uno::Sequence
< uno::Reference
< linguistic2::XMeaning
> > aMeanings
= queryMeanings_Impl(
304 aLookUpText
, aLocale
, uno::Sequence
< beans::PropertyValue
>() );
305 const sal_Int32 nMeanings
= aMeanings
.getLength();
306 const uno::Reference
< linguistic2::XMeaning
> *pMeanings
= aMeanings
.getConstArray();
308 m_pAlternativesCT
->SetUpdateMode( false );
310 // clear old user data of control before creating new ones via AddEntry below
311 m_pAlternativesCT
->ClearExtraData();
313 m_pAlternativesCT
->Clear();
314 for (sal_Int32 i
= 0; i
< nMeanings
; ++i
)
316 OUString rMeaningTxt
= pMeanings
[i
]->getMeaning();
317 uno::Sequence
< OUString
> aSynonyms( pMeanings
[i
]->querySynonyms() );
318 const sal_Int32 nSynonyms
= aSynonyms
.getLength();
319 const OUString
*pSynonyms
= aSynonyms
.getConstArray();
320 DBG_ASSERT( !rMeaningTxt
.isEmpty(), "meaning with empty text" );
321 DBG_ASSERT( nSynonyms
> 0, "meaning without synonym" );
323 m_pAlternativesCT
->AddEntry( i
+ 1, rMeaningTxt
, true );
324 for (sal_Int32 k
= 0; k
< nSynonyms
; ++k
)
325 m_pAlternativesCT
->AddEntry( -1, pSynonyms
[k
], false );
328 m_pAlternativesCT
->SetUpdateMode( true );
330 return nMeanings
> 0;
333 void SvxThesaurusDialog::LookUp( const OUString
&rText
)
335 if (OUString(rText
) != m_pWordCB
->GetText()) // avoid moving of the cursor if the text is the same
336 m_pWordCB
->SetText( rText
);
340 IMPL_LINK( SvxThesaurusDialog
, LeftBtnHdl_Impl
, Button
*, pBtn
)
342 if (pBtn
&& aLookUpHistory
.size() >= 2)
344 aLookUpHistory
.pop(); // remove current look up word from stack
345 m_pWordCB
->SetText( aLookUpHistory
.top() ); // retrieve previous look up word
346 aLookUpHistory
.pop();
352 IMPL_LINK( SvxThesaurusDialog
, LanguageHdl_Impl
, ListBox
*, pLB
)
354 OUString
aLangText( pLB
->GetSelectEntry() );
355 LanguageType nLang
= SvtLanguageTable::GetLanguageType( aLangText
);
356 DBG_ASSERT( nLang
!= LANGUAGE_NONE
&& nLang
!= LANGUAGE_DONTKNOW
, "failed to get language" );
357 if (xThesaurus
->hasLocale( LanguageTag::convertToLocale( nLang
) ))
358 nLookUpLanguage
= nLang
;
359 SetWindowTitle( nLang
);
364 void SvxThesaurusDialog::LookUp_Impl()
366 OUString
aText( m_pWordCB
->GetText() );
368 aLookUpText
= OUString( aText
);
369 if (!aLookUpText
.isEmpty() &&
370 (aLookUpHistory
.empty() || aLookUpText
!= aLookUpHistory
.top()))
371 aLookUpHistory
.push( aLookUpText
);
373 m_bWordFound
= UpdateAlternativesBox_Impl();
374 m_pAlternativesCT
->Enable( m_bWordFound
);
376 if ( m_pWordCB
->GetEntryPos( aText
) == LISTBOX_ENTRY_NOTFOUND
)
377 m_pWordCB
->InsertEntry( aText
);
379 m_pReplaceEdit
->SetText( OUString() );
380 m_pLeftBtn
->Enable( aLookUpHistory
.size() > 1 );
383 IMPL_LINK( SvxThesaurusDialog
, WordSelectHdl_Impl
, ComboBox
*, pBox
)
385 if (pBox
&& !m_pWordCB
->IsTravelSelect()) // act only upon return key and not when traveling with cursor keys
387 sal_uInt16 nPos
= pBox
->GetSelectEntryPos();
388 OUString
aStr( pBox
->GetEntry( nPos
) );
389 aStr
= linguistic::GetThesaurusReplaceText( aStr
);
390 m_pWordCB
->SetText( aStr
);
397 IMPL_LINK( SvxThesaurusDialog
, AlternativesSelectHdl_Impl
, SvxCheckListBox
*, pBox
)
399 SvTreeListEntry
*pEntry
= pBox
? pBox
->GetCurEntry() : NULL
;
402 AlternativesExtraData
* pData
= m_pAlternativesCT
->GetExtraData( pEntry
);
404 if (pData
&& !pData
->IsHeader())
406 aStr
= pData
->GetText();
407 aStr
= linguistic::GetThesaurusReplaceText( aStr
);
409 m_pReplaceEdit
->SetText( aStr
);
414 IMPL_LINK( SvxThesaurusDialog
, AlternativesDoubleClickHdl_Impl
, SvxCheckListBox
*, pBox
)
416 SvTreeListEntry
*pEntry
= pBox
? pBox
->GetCurEntry() : NULL
;
419 AlternativesExtraData
* pData
= m_pAlternativesCT
->GetExtraData( pEntry
);
421 if (pData
&& !pData
->IsHeader())
423 aStr
= pData
->GetText();
424 aStr
= linguistic::GetThesaurusReplaceText( aStr
);
427 m_pWordCB
->SetText( aStr
);
432 //! workaround to set the selection since calling SelectEntryPos within
433 //! the double click handler does not work
434 Application::PostUserEvent( LINK( this, SvxThesaurusDialog
, SelectFirstHdl_Impl
), pBox
, true );
438 IMPL_STATIC_LINK( SvxThesaurusDialog
, SelectFirstHdl_Impl
, SvxCheckListBox
*, pBox
)
440 if (pBox
&& pBox
->GetEntryCount() >= 2)
441 pBox
->SelectEntryPos( 1 ); // pos 0 is a 'header' that is not selectable
445 // class SvxThesaurusDialog ----------------------------------------------
447 SvxThesaurusDialog::SvxThesaurusDialog(
448 vcl::Window
* pParent
,
449 uno::Reference
< linguistic2::XThesaurus
> xThes
,
450 const OUString
&rWord
,
451 LanguageType nLanguage
)
452 : SvxStandardDialog(pParent
, "ThesaurusDialog", "cui/ui/thesaurus.ui")
453 , m_aErrStr(CUI_RESSTR(RID_SVXSTR_ERR_TEXTNOTFOUND
))
456 , nLookUpLanguage(LANGUAGE_NONE
)
457 , m_bWordFound(false)
459 get(m_pLeftBtn
, "left");
461 get(m_pWordCB
, "wordcb");
462 m_pWordCB
->init(this);
464 get(m_pAlternativesCT
, "alternatives");
465 m_pAlternativesCT
->init(this);
467 get(m_pReplaceEdit
, "replaceed");
468 PushButton
*pReplaceBtn
= get
<PushButton
>("replace");
469 m_pReplaceEdit
->init(pReplaceBtn
);
471 get(m_pLangLB
, "langcb");
473 pReplaceBtn
->SetClickHdl( LINK( this, SvxThesaurusDialog
, ReplaceBtnHdl_Impl
) );
474 m_pLeftBtn
->SetClickHdl( LINK( this, SvxThesaurusDialog
, LeftBtnHdl_Impl
) );
475 m_pWordCB
->SetSelectHdl( LINK( this, SvxThesaurusDialog
, WordSelectHdl_Impl
) );
476 m_pLangLB
->SetSelectHdl( LINK( this, SvxThesaurusDialog
, LanguageHdl_Impl
) );
477 m_pAlternativesCT
->SetSelectHdl( LINK( this, SvxThesaurusDialog
, AlternativesSelectHdl_Impl
));
478 m_pAlternativesCT
->SetDoubleClickHdl( LINK( this, SvxThesaurusDialog
, AlternativesDoubleClickHdl_Impl
));
481 aLookUpText
= OUString( rWord
);
482 nLookUpLanguage
= nLanguage
;
483 if (!rWord
.isEmpty())
484 aLookUpHistory
.push( rWord
);
486 OUString
aTmp( rWord
);
487 (void)linguistic::RemoveHyphens( aTmp
);
488 (void)linguistic::ReplaceControlChars( aTmp
);
489 m_pReplaceEdit
->SetText( aTmp
);
490 m_pWordCB
->InsertEntry( aTmp
);
493 m_pAlternativesCT
->GrabFocus();
494 m_pLeftBtn
->Enable( false );
496 // fill language menu button list
497 uno::Sequence
< lang::Locale
> aLocales
;
499 aLocales
= xThesaurus
->getLocales();
500 const sal_Int32 nLocales
= aLocales
.getLength();
501 const lang::Locale
*pLocales
= aLocales
.getConstArray();
503 std::vector
< OUString
> aLangVec
;
504 for (sal_Int32 i
= 0; i
< nLocales
; ++i
)
506 const LanguageType nLang
= LanguageTag::convertToLanguageType( pLocales
[i
] );
507 DBG_ASSERT( nLang
!= LANGUAGE_NONE
&& nLang
!= LANGUAGE_DONTKNOW
, "failed to get language" );
508 aLangVec
.push_back( SvtLanguageTable::GetLanguageString( nLang
) );
510 std::sort( aLangVec
.begin(), aLangVec
.end() );
511 for (size_t i
= 0; i
< aLangVec
.size(); ++i
)
512 m_pLangLB
->InsertEntry( aLangVec
[i
] );
514 std::vector
< OUString
>::iterator aI
= std::find(aLangVec
.begin(), aLangVec
.end(),
515 SvtLanguageTable::GetLanguageString(nLanguage
));
516 if (aI
!= aLangVec
.end())
518 m_pLangLB
->SelectEntry(*aI
);
521 SetWindowTitle(nLanguage
);
523 // disable controls if service is missing
524 if (!xThesaurus
.is())
528 SvxThesaurusDialog::~SvxThesaurusDialog()
533 void SvxThesaurusDialog::dispose()
537 m_pAlternativesCT
.clear();
538 m_pReplaceEdit
.clear();
540 SvxStandardDialog::dispose();
543 IMPL_LINK( SvxThesaurusDialog
, ReplaceBtnHdl_Impl
, Button
*, /*pBtn*/ )
549 void SvxThesaurusDialog::SetWindowTitle( LanguageType nLanguage
)
552 OUString
aStr( GetText() );
553 sal_Int32 nIndex
= aStr
.indexOf( '(' );
555 aStr
= aStr
.copy( 0, nIndex
- 1 );
557 aStr
+= SvtLanguageTable::GetLanguageString( nLanguage
);
559 SetText( aStr
); // set window title
562 OUString
SvxThesaurusDialog::GetWord()
564 return m_pReplaceEdit
->GetText();
568 void SvxThesaurusDialog::Apply()
572 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */