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"
22 #include <strings.hrc>
23 #include <dialmgr.hxx>
25 #include <tools/debug.hxx>
26 #include <svl/lngmisc.hxx>
27 #include <vcl/event.hxx>
28 #include <vcl/graphicfilter.hxx>
29 #include <vcl/svlbitm.hxx>
30 #include <vcl/treelistbox.hxx>
31 #include <vcl/treelistentry.hxx>
32 #include <vcl/viewdataentry.hxx>
33 #include <vcl/wrkwin.hxx>
34 #include <vcl/svapp.hxx>
35 #include <vcl/builderfactory.hxx>
36 #include <svx/dlgutil.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/languagetag.hxx>
43 #include <i18nlangtag/mslangid.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>
53 using namespace ::com::sun::star
;
55 // class LookUpComboBox --------------------------------------------------
57 LookUpComboBox::LookUpComboBox(vcl::Window
*pParent
)
58 : ComboBox(pParent
, WB_LEFT
|WB_DROPDOWN
|WB_VCENTER
|WB_3DLOOK
|WB_TABSTOP
)
59 , m_aModifyIdle("cui LookUpComboBox Modify")
64 m_aModifyIdle
.SetInvokeHandler( LINK( this, LookUpComboBox
, ModifyTimer_Hdl
) );
65 m_aModifyIdle
.SetPriority( TaskPriority::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( LookUpComboBox
, ModifyTimer_Hdl
, Timer
*, 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( ThesaurusAlternativesCtrl
&rControl
, const OUString
& rStr
) :
141 SvLBoxString( rStr
),
142 m_rControlImpl( rControl
)
146 void AlternativesString::Paint(const Point
& rPos
, SvTreeListBox
& /*rDev*/, vcl::RenderContext
& rRenderContext
,
147 const SvViewDataEntry
* /*pView*/, const SvTreeListEntry
& rEntry
)
149 AlternativesExtraData
* pData
= m_rControlImpl
.GetExtraData(&rEntry
);
151 rRenderContext
.Push(PushFlags::FONT
);
152 if (pData
&& pData
->IsHeader())
154 vcl::Font
aFont(rRenderContext
.GetFont());
155 aFont
.SetWeight(WEIGHT_BOLD
);
156 rRenderContext
.SetFont(aFont
);
161 rRenderContext
.DrawText(aPos
, GetText());
162 rRenderContext
.Pop();
165 ThesaurusAlternativesCtrl::ThesaurusAlternativesCtrl(vcl::Window
* pParent
)
166 : SvxCheckListBox(pParent
)
169 SetStyle( GetStyle() | WB_CLIPCHILDREN
| WB_HSCROLL
);
170 SetForceMakeVisible(true);
174 VCL_BUILDER_FACTORY(ThesaurusAlternativesCtrl
)
176 void ThesaurusAlternativesCtrl::init(SvxThesaurusDialog
*pDialog
)
181 ThesaurusAlternativesCtrl::~ThesaurusAlternativesCtrl()
186 void ThesaurusAlternativesCtrl::dispose()
190 SvxCheckListBox::dispose();
193 void ThesaurusAlternativesCtrl::ClearExtraData()
195 UserDataMap_t aEmpty
;
196 m_aUserData
.swap( aEmpty
);
199 void ThesaurusAlternativesCtrl::SetExtraData(
200 const SvTreeListEntry
*pEntry
,
201 const AlternativesExtraData
&rData
)
206 UserDataMap_t::iterator
aIt( m_aUserData
.find( pEntry
) );
207 if (aIt
!= m_aUserData
.end())
210 m_aUserData
[ pEntry
] = rData
;
213 AlternativesExtraData
* ThesaurusAlternativesCtrl::GetExtraData(
214 const SvTreeListEntry
*pEntry
)
216 AlternativesExtraData
*pRes
= nullptr;
217 UserDataMap_t::iterator
aIt( m_aUserData
.find( pEntry
) );
218 if (aIt
!= m_aUserData
.end())
223 SvTreeListEntry
* ThesaurusAlternativesCtrl::AddEntry( sal_Int32 nVal
, const OUString
&rText
, bool bIsHeader
)
225 SvTreeListEntry
* pEntry
= new SvTreeListEntry
;
227 if (bIsHeader
&& nVal
>= 0)
229 aText
= OUString::number( nVal
) + ". ";
231 pEntry
->AddItem(std::make_unique
<SvLBoxString
>(OUString())); // add empty column
233 pEntry
->AddItem(std::make_unique
<SvLBoxContextBmp
>(Image(), Image(), false)); // otherwise crash
234 pEntry
->AddItem(std::make_unique
<AlternativesString
>(*this, aText
));
236 SetExtraData( pEntry
, AlternativesExtraData( rText
, bIsHeader
) );
237 GetModel()->Insert( pEntry
);
240 GetViewDataEntry( pEntry
)->SetSelectable( false );
245 void ThesaurusAlternativesCtrl::KeyInput( const KeyEvent
& rKEvt
)
247 const vcl::KeyCode
& rKey
= rKEvt
.GetKeyCode();
249 if (rKey
.GetCode() == KEY_RETURN
|| rKey
.GetCode() == KEY_ESCAPE
)
250 GetParent()->KeyInput( rKEvt
); // parent will close dialog...
251 else if (rKey
.GetCode() == KEY_SPACE
)
252 m_pDialog
->AlternativesDoubleClickHdl_Impl( this ); // look up current selected entry
253 else if (GetEntryCount())
254 SvxCheckListBox::KeyInput( rKEvt
);
257 void ThesaurusAlternativesCtrl::Paint(vcl::RenderContext
& rRenderContext
, const ::tools::Rectangle
& rRect
)
259 if (!m_pDialog
->WordFound())
261 Size
aTextSize(rRenderContext
.GetTextWidth(m_pDialog
->getErrStr()), rRenderContext
.GetTextHeight());
262 aTextSize
= rRenderContext
.LogicToPixel(aTextSize
);
264 aPos
.AdjustX(GetSizePixel().Width() / 2 - aTextSize
.Width() / 2 );
265 aPos
.AdjustY(GetSizePixel().Height() / 2 );
266 aPos
= rRenderContext
.PixelToLogic(aPos
);
267 rRenderContext
.DrawText(aPos
, m_pDialog
->getErrStr());
270 SvxCheckListBox::Paint(rRenderContext
, rRect
);
273 uno::Sequence
< uno::Reference
< linguistic2::XMeaning
> > SvxThesaurusDialog::queryMeanings_Impl(
275 const lang::Locale
& rLocale
,
276 const beans::PropertyValues
& rProperties
)
278 uno::Sequence
< uno::Reference
< linguistic2::XMeaning
> > aMeanings(
279 xThesaurus
->queryMeanings( rTerm
, rLocale
, rProperties
) );
281 // text with '.' at the end?
282 if ( 0 == aMeanings
.getLength() && rTerm
.endsWith(".") )
284 // try again without trailing '.' chars. It may be a word at the
285 // end of a sentence and not an abbreviation...
286 OUString
aTxt(comphelper::string::stripEnd(rTerm
, '.'));
287 aMeanings
= xThesaurus
->queryMeanings( aTxt
, rLocale
, rProperties
);
288 if (aMeanings
.getLength())
297 bool SvxThesaurusDialog::UpdateAlternativesBox_Impl()
299 lang::Locale
aLocale( LanguageTag::convertToLocale( nLookUpLanguage
) );
300 uno::Sequence
< uno::Reference
< linguistic2::XMeaning
> > aMeanings
= queryMeanings_Impl(
301 aLookUpText
, aLocale
, uno::Sequence
< beans::PropertyValue
>() );
302 const sal_Int32 nMeanings
= aMeanings
.getLength();
303 const uno::Reference
< linguistic2::XMeaning
> *pMeanings
= aMeanings
.getConstArray();
305 m_pAlternativesCT
->SetUpdateMode( false );
307 // clear old user data of control before creating new ones via AddEntry below
308 m_pAlternativesCT
->ClearExtraData();
310 m_pAlternativesCT
->Clear();
311 for (sal_Int32 i
= 0; i
< nMeanings
; ++i
)
313 OUString rMeaningTxt
= pMeanings
[i
]->getMeaning();
314 uno::Sequence
< OUString
> aSynonyms( pMeanings
[i
]->querySynonyms() );
315 const sal_Int32 nSynonyms
= aSynonyms
.getLength();
316 const OUString
*pSynonyms
= aSynonyms
.getConstArray();
317 DBG_ASSERT( !rMeaningTxt
.isEmpty(), "meaning with empty text" );
318 DBG_ASSERT( nSynonyms
> 0, "meaning without synonym" );
320 m_pAlternativesCT
->AddEntry( i
+ 1, rMeaningTxt
, true );
321 for (sal_Int32 k
= 0; k
< nSynonyms
; ++k
)
322 m_pAlternativesCT
->AddEntry( -1, pSynonyms
[k
], false );
325 m_pAlternativesCT
->SetUpdateMode( true );
327 return nMeanings
> 0;
330 void SvxThesaurusDialog::LookUp( const OUString
&rText
)
332 if (rText
!= m_pWordCB
->GetText()) // avoid moving of the cursor if the text is the same
333 m_pWordCB
->SetText( rText
);
337 IMPL_LINK( SvxThesaurusDialog
, LeftBtnHdl_Impl
, Button
*, pBtn
, void )
339 if (pBtn
&& aLookUpHistory
.size() >= 2)
341 aLookUpHistory
.pop(); // remove current look up word from stack
342 m_pWordCB
->SetText( aLookUpHistory
.top() ); // retrieve previous look up word
343 aLookUpHistory
.pop();
348 IMPL_LINK( SvxThesaurusDialog
, LanguageHdl_Impl
, ListBox
&, rLB
, void )
350 OUString
aLangText( rLB
.GetSelectedEntry() );
351 LanguageType nLang
= SvtLanguageTable::GetLanguageType( aLangText
);
352 DBG_ASSERT( nLang
!= LANGUAGE_NONE
&& nLang
!= LANGUAGE_DONTKNOW
, "failed to get language" );
353 if (xThesaurus
->hasLocale( LanguageTag::convertToLocale( nLang
) ))
354 nLookUpLanguage
= nLang
;
355 SetWindowTitle( nLang
);
359 void SvxThesaurusDialog::LookUp_Impl()
361 OUString
aText( m_pWordCB
->GetText() );
364 if (!aLookUpText
.isEmpty() &&
365 (aLookUpHistory
.empty() || aLookUpText
!= aLookUpHistory
.top()))
366 aLookUpHistory
.push( aLookUpText
);
368 m_bWordFound
= UpdateAlternativesBox_Impl();
369 m_pAlternativesCT
->Enable( m_bWordFound
);
371 if ( m_pWordCB
->GetEntryPos( aText
) == LISTBOX_ENTRY_NOTFOUND
)
372 m_pWordCB
->InsertEntry( aText
);
374 m_pReplaceEdit
->SetText( OUString() );
375 m_pLeftBtn
->Enable( aLookUpHistory
.size() > 1 );
378 IMPL_LINK( SvxThesaurusDialog
, WordSelectHdl_Impl
, ComboBox
&, rBox
, void )
380 if (!m_pWordCB
->IsTravelSelect()) // act only upon return key and not when traveling with cursor keys
382 const sal_Int32 nPos
= rBox
.GetSelectedEntryPos();
383 OUString
aStr( rBox
.GetEntry( nPos
) );
384 aStr
= linguistic::GetThesaurusReplaceText( aStr
);
385 m_pWordCB
->SetText( aStr
);
390 IMPL_LINK( SvxThesaurusDialog
, AlternativesSelectHdl_Impl
, SvTreeListBox
*, pBox
, void )
392 SvTreeListEntry
*pEntry
= pBox
? pBox
->GetCurEntry() : nullptr;
395 AlternativesExtraData
* pData
= m_pAlternativesCT
->GetExtraData( pEntry
);
397 if (pData
&& !pData
->IsHeader())
399 aStr
= pData
->GetText();
400 aStr
= linguistic::GetThesaurusReplaceText( aStr
);
402 m_pReplaceEdit
->SetText( aStr
);
406 IMPL_LINK( SvxThesaurusDialog
, AlternativesDoubleClickHdl_Impl
, SvTreeListBox
*, pBox
, bool )
408 SvTreeListEntry
*pEntry
= pBox
? pBox
->GetCurEntry() : nullptr;
411 AlternativesExtraData
* pData
= m_pAlternativesCT
->GetExtraData( pEntry
);
413 if (pData
&& !pData
->IsHeader())
415 aStr
= pData
->GetText();
416 aStr
= linguistic::GetThesaurusReplaceText( aStr
);
419 m_pWordCB
->SetText( aStr
);
424 //! workaround to set the selection since calling SelectEntryPos within
425 //! the double click handler does not work
426 Application::PostUserEvent( LINK( this, SvxThesaurusDialog
, SelectFirstHdl_Impl
), pBox
, true );
430 IMPL_STATIC_LINK( SvxThesaurusDialog
, SelectFirstHdl_Impl
, void *, p
, void )
432 SvxCheckListBox
* pBox
= static_cast<SvxCheckListBox
*>(p
);
433 if (pBox
&& pBox
->GetEntryCount() >= 2)
434 pBox
->SelectEntryPos( 1 ); // pos 0 is a 'header' that is not selectable
437 // class SvxThesaurusDialog ----------------------------------------------
439 SvxThesaurusDialog::SvxThesaurusDialog(
440 vcl::Window
* pParent
,
441 uno::Reference
< linguistic2::XThesaurus
> const & xThes
,
442 const OUString
&rWord
,
443 LanguageType nLanguage
)
444 : SvxStandardDialog(pParent
, "ThesaurusDialog", "cui/ui/thesaurus.ui")
445 , m_aErrStr(CuiResId(RID_SVXSTR_ERR_TEXTNOTFOUND
))
447 , nLookUpLanguage(LANGUAGE_NONE
)
448 , m_bWordFound(false)
450 get(m_pLeftBtn
, "left");
452 get(m_pWordCB
, "wordcb");
453 m_pWordCB
->init(this);
455 get(m_pAlternativesCT
, "alternatives");
456 m_pAlternativesCT
->init(this);
458 get(m_pReplaceEdit
, "replaceed");
459 PushButton
*pReplaceBtn
= get
<PushButton
>("replace");
460 m_pReplaceEdit
->init(pReplaceBtn
);
462 get(m_pLangLB
, "langcb");
464 pReplaceBtn
->SetClickHdl( LINK( this, SvxThesaurusDialog
, ReplaceBtnHdl_Impl
) );
465 m_pLeftBtn
->SetClickHdl( LINK( this, SvxThesaurusDialog
, LeftBtnHdl_Impl
) );
466 m_pWordCB
->SetSelectHdl( LINK( this, SvxThesaurusDialog
, WordSelectHdl_Impl
) );
467 m_pLangLB
->SetSelectHdl( LINK( this, SvxThesaurusDialog
, LanguageHdl_Impl
) );
468 m_pAlternativesCT
->SetSelectHdl( LINK( this, SvxThesaurusDialog
, AlternativesSelectHdl_Impl
));
469 m_pAlternativesCT
->SetDoubleClickHdl( LINK( this, SvxThesaurusDialog
, AlternativesDoubleClickHdl_Impl
));
473 nLookUpLanguage
= nLanguage
;
474 if (!rWord
.isEmpty())
475 aLookUpHistory
.push( rWord
);
477 OUString
aTmp( rWord
);
478 (void)linguistic::RemoveHyphens( aTmp
);
479 (void)linguistic::ReplaceControlChars( aTmp
);
480 m_pReplaceEdit
->SetText( aTmp
);
481 m_pWordCB
->InsertEntry( aTmp
);
484 m_pAlternativesCT
->GrabFocus();
485 m_pLeftBtn
->Enable( false );
487 // fill language menu button list
488 uno::Sequence
< lang::Locale
> aLocales
;
490 aLocales
= xThesaurus
->getLocales();
491 const sal_Int32 nLocales
= aLocales
.getLength();
492 const lang::Locale
*pLocales
= aLocales
.getConstArray();
494 std::vector
< OUString
> aLangVec
;
495 for (sal_Int32 i
= 0; i
< nLocales
; ++i
)
497 const LanguageType nLang
= LanguageTag::convertToLanguageType( pLocales
[i
] );
498 DBG_ASSERT( nLang
!= LANGUAGE_NONE
&& nLang
!= LANGUAGE_DONTKNOW
, "failed to get language" );
499 aLangVec
.push_back( SvtLanguageTable::GetLanguageString( nLang
) );
501 std::sort( aLangVec
.begin(), aLangVec
.end() );
502 for (OUString
& i
: aLangVec
)
503 m_pLangLB
->InsertEntry( i
);
505 std::vector
< OUString
>::iterator aI
= std::find(aLangVec
.begin(), aLangVec
.end(),
506 SvtLanguageTable::GetLanguageString(nLanguage
));
507 if (aI
!= aLangVec
.end())
509 m_pLangLB
->SelectEntry(*aI
);
512 SetWindowTitle(nLanguage
);
514 // disable controls if service is missing
515 if (!xThesaurus
.is())
519 SvxThesaurusDialog::~SvxThesaurusDialog()
524 void SvxThesaurusDialog::dispose()
528 m_pAlternativesCT
.clear();
529 m_pReplaceEdit
.clear();
531 SvxStandardDialog::dispose();
534 IMPL_LINK_NOARG( SvxThesaurusDialog
, ReplaceBtnHdl_Impl
, Button
*, void )
539 void SvxThesaurusDialog::SetWindowTitle( LanguageType nLanguage
)
542 OUString
aStr( GetText() );
543 sal_Int32 nIndex
= aStr
.indexOf( '(' );
545 aStr
= aStr
.copy( 0, nIndex
- 1 );
546 aStr
+= " (" + SvtLanguageTable::GetLanguageString( nLanguage
) + ")";
547 SetText( aStr
); // set window title
550 OUString
SvxThesaurusDialog::GetWord()
552 return m_pReplaceEdit
->GetText();
556 void SvxThesaurusDialog::Apply()
560 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */