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 .
22 #include <com/sun/star/linguistic2/XLinguProperties.hpp>
24 #include <editeng/splwrap.hxx>
25 #include <editeng/svxenum.hxx>
26 #include <editeng/unolingu.hxx>
27 #include <svtools/langtab.hxx>
28 #include <svx/dlgutil.hxx>
29 #include <sal/log.hxx>
30 #include <i18nlangtag/languagetag.hxx>
31 #include <tools/debug.hxx>
33 #define HYPH_POS_CHAR '='
35 #define CUR_HYPH_POS_CHAR '-'
39 IMPL_LINK_NOARG(SvxHyphenWordDialog
, CursorChangeHdl_Impl
, weld::Entry
&, void)
42 m_xWordEdit
->get_selection_bounds(nStart
, nEnd
);
43 if (nStart
== m_nOldPos
&& nEnd
== m_nOldPos
+ 1)
46 if (nStart
<= m_nOldPos
)
47 bReSelect
= !SelLeft();
49 bReSelect
= !SelRight();
51 select_region(m_nOldPos
, m_nOldPos
+ 1);
54 void SvxHyphenWordDialog::EnableLRBtn_Impl()
56 const sal_Int32 nLen
= m_aEditWord
.getLength();
58 m_xRightBtn
->set_sensitive(false);
59 for ( sal_Int32 i
= m_nOldPos
+ 2; i
< nLen
; ++i
)
61 if ( m_aEditWord
[ i
] == sal_Unicode( HYPH_POS_CHAR
) )
63 m_xRightBtn
->set_sensitive(true);
68 DBG_ASSERT(m_nOldPos
< nLen
, "nOldPos out of range");
69 if (m_nOldPos
>= nLen
)
71 m_xLeftBtn
->set_sensitive(false);
72 for ( sal_Int32 i
= m_nOldPos
; i
-- > 0; )
74 if ( m_aEditWord
[ i
] == sal_Unicode( HYPH_POS_CHAR
) )
76 m_xLeftBtn
->set_sensitive(true);
82 OUString
SvxHyphenWordDialog::EraseUnusableHyphens_Impl()
84 // returns a String showing only those hyphen positions which will result
85 // in a line break if hyphenation is done there
86 // 1) we will need to discard all hyphenation positions at the end that
87 // will not result in a line break where the text to the left still fits
89 // 2) since as from OOo 3.2 '-' are part of a word and thus text like
90 // 'multi-line-editor' is regarded as single word we also need to discard those
91 // hyphenation positions to the left of the rightmost '-' that is still left of
92 // the rightmost valid hyphenation position according to 1)
95 // If the possible hyphenation position in 'multi-line-editor' are to be marked
96 // by '=' then the text will look like this: 'mul=ti-line-ed=it=or'.
97 // If now the first line is only large enough for 'multi-line-edi' we need to discard
98 // the last possible hyphenation point because of 1). The right most valid
99 // hyphenation position is "ed=itor". The first '-' left of this position is
100 // "line-ed", thus because of 2) we now need to discard all possible hyphenation
101 // positions to the left of that as well. Thus in the end leaving us with just
102 // 'multi-line-ed=itor' as return value for this function. (Just one valid hyphenation
103 // position for the user to choose from. However ALL the '-' characters in the word
104 // will ALWAYS be valid implicit hyphenation positions for the core to choose from!
105 // And thus even if this word is skipped in the hyphenation dialog it will still be broken
106 // right after 'multi-line-' (actually it might already be broken up that way before
107 // the hyphenation dialog is called!).
108 // Thus rule 2) just eliminates those positions which will not be used by the core at all
109 // even if the user were to select one of them.
112 DBG_ASSERT(m_xPossHyph
.is(), "missing possible hyphens");
113 if (m_xPossHyph
.is())
115 DBG_ASSERT( m_aActWord
== m_xPossHyph
->getWord(), "word mismatch" );
117 aTxt
= m_xPossHyph
->getPossibleHyphens();
119 m_nHyphenationPositionsOffset
= 0;
120 uno::Sequence
< sal_Int16
> aHyphenationPositions(
121 m_xPossHyph
->getHyphenationPositions() );
122 sal_Int32 nLen
= aHyphenationPositions
.getLength();
123 const sal_Int16
*pHyphenationPos
= aHyphenationPositions
.getConstArray();
125 // find position nIdx after which all hyphen positions are unusable
127 sal_Int32 nPos
= 0, nPos1
= 0;
130 sal_Int32 nStart
= 0;
131 for (sal_Int32 i
= 0; i
< nLen
; ++i
)
133 if (pHyphenationPos
[i
] > m_nMaxHyphenationPos
)
137 // find corresponding hyphen positions in string
138 nPos
= aTxt
.indexOf( sal_Unicode( HYPH_POS_CHAR
), nStart
);
150 DBG_ASSERT(nIdx
!= -1, "no usable hyphenation position");
152 // 1) remove all not usable hyphenation positions from the end of the string
153 nPos
= nIdx
== -1 ? 0 : nIdx
+ 1;
154 nPos1
= nPos
; //save for later use in 2) below
155 const OUString
aTmp( sal_Unicode( HYPH_POS_CHAR
) );
159 aTxt
= aTxt
.replaceFirst( aTmp
, "", &nPos
);
162 // 2) remove all hyphenation positions from the start that are not considered by the core
163 const OUString
aSearchRange( aTxt
.copy( 0, nPos1
) );
164 sal_Int32 nPos2
= aSearchRange
.lastIndexOf( '-' ); // the '-' position the core will use by default
167 OUString
aLeft( aSearchRange
.copy( 0, nPos2
) );
172 aLeft
= aLeft
.replaceFirst( aTmp
, "", &nPos
);
174 ++m_nHyphenationPositionsOffset
;
176 aTxt
= aTxt
.replaceAt( 0, nPos2
, aLeft
);
182 void SvxHyphenWordDialog::InitControls_Impl()
184 m_xPossHyph
= nullptr;
185 if (m_xHyphenator
.is())
187 lang::Locale
aLocale( LanguageTag::convertToLocale(m_nActLanguage
) );
188 m_xPossHyph
= m_xHyphenator
->createPossibleHyphens( m_aActWord
, aLocale
,
189 uno::Sequence
< beans::PropertyValue
>() );
190 if (m_xPossHyph
.is())
191 m_aEditWord
= EraseUnusableHyphens_Impl();
193 m_xWordEdit
->set_text(m_aEditWord
);
195 m_nOldPos
= m_aEditWord
.getLength();
200 void SvxHyphenWordDialog::ContinueHyph_Impl( sal_Int32 nInsPos
)
202 if ( nInsPos
>= 0 && m_xPossHyph
.is() )
206 DBG_ASSERT(nInsPos
<= m_aEditWord
.getLength() - 2, "wrong hyphen position");
208 sal_Int32 nIdxPos
= -1;
209 for (sal_Int32 i
= 0; i
<= nInsPos
; ++i
)
211 if (HYPH_POS_CHAR
== m_aEditWord
[ i
])
214 // take the possible hyphenation positions that got removed from the
215 // start of the word into account:
216 nIdxPos
+= m_nHyphenationPositionsOffset
;
218 uno::Sequence
< sal_Int16
> aSeq
= m_xPossHyph
->getHyphenationPositions();
219 sal_Int32 nLen
= aSeq
.getLength();
220 DBG_ASSERT(nLen
, "empty sequence");
221 DBG_ASSERT(0 <= nIdxPos
&& nIdxPos
< nLen
, "index out of range");
222 if (nLen
&& 0 <= nIdxPos
&& nIdxPos
< nLen
)
224 nInsPos
= aSeq
.getConstArray()[ nIdxPos
];
225 m_pHyphWrapper
->InsertHyphen( nInsPos
);
230 //! calling with 0 as argument will remove hyphens!
231 m_pHyphWrapper
->InsertHyphen( nInsPos
);
235 if ( m_pHyphWrapper
->FindSpellError() )
237 uno::Reference
< linguistic2::XHyphenatedWord
> xHyphWord( m_pHyphWrapper
->GetLast(), uno::UNO_QUERY
);
239 // adapt actual word and language to new found hyphenation result
242 m_aActWord
= xHyphWord
->getWord();
243 m_nActLanguage
= LanguageTag( xHyphWord
->getLocale() ).getLanguageType();
244 m_nMaxHyphenationPos
= xHyphWord
->getHyphenationPos();
246 SetWindowTitle( m_nActLanguage
);
251 m_xCloseBtn
->set_sensitive(false);
252 m_xDialog
->response(RET_OK
);
256 bool SvxHyphenWordDialog::SelLeft()
259 DBG_ASSERT( m_nOldPos
> 0, "invalid hyphenation position" );
262 OUString
aTxt( m_aEditWord
);
263 for( sal_Int32 i
= m_nOldPos
- 1; i
> 0; --i
)
265 DBG_ASSERT(i
<= aTxt
.getLength(), "index out of range");
266 if (aTxt
[ i
] == sal_Unicode( HYPH_POS_CHAR
))
268 aTxt
= aTxt
.replaceAt( i
, 1, OUString( CUR_HYPH_POS_CHAR
) );
271 m_xWordEdit
->set_text(aTxt
);
272 select_region(i
, i
+ 1);
273 m_xWordEdit
->grab_focus();
283 bool SvxHyphenWordDialog::SelRight()
286 OUString
aTxt( m_aEditWord
);
287 for ( sal_Int32 i
= m_nOldPos
+ 1; i
< aTxt
.getLength(); ++i
)
289 if (aTxt
[ i
] == sal_Unicode( HYPH_POS_CHAR
))
291 aTxt
= aTxt
.replaceAt( i
, 1, OUString( CUR_HYPH_POS_CHAR
) );
294 m_xWordEdit
->set_text(aTxt
);
295 select_region(i
, i
+ 1);
296 m_xWordEdit
->grab_focus();
305 IMPL_LINK_NOARG(SvxHyphenWordDialog
, CutHdl_Impl
, weld::Button
&, void)
310 ContinueHyph_Impl( /*m_nHyphPos*/m_nOldPos
);
315 IMPL_LINK_NOARG(SvxHyphenWordDialog
, HyphenateAllHdl_Impl
, weld::Button
&, void)
321 uno::Reference
< linguistic2::XLinguProperties
> xProp( LinguMgr::GetLinguPropertySet() );
323 xProp
->setIsHyphAuto( true );
326 ContinueHyph_Impl( /*m_nHyphPos*/m_nOldPos
);
329 xProp
->setIsHyphAuto( false );
331 catch (uno::Exception
&)
333 SAL_WARN( "cui.dialogs", "Hyphenate All failed" );
338 IMPL_LINK_NOARG(SvxHyphenWordDialog
, DeleteHdl_Impl
, weld::Button
&, void)
343 ContinueHyph_Impl( 0 );
348 IMPL_LINK_NOARG(SvxHyphenWordDialog
, ContinueHdl_Impl
, weld::Button
&, void)
358 IMPL_LINK_NOARG(SvxHyphenWordDialog
, CancelHdl_Impl
, weld::Button
&, void)
363 m_xDialog
->response(RET_CANCEL
);
368 IMPL_LINK_NOARG(SvxHyphenWordDialog
, Left_Impl
, weld::Button
&, void)
378 IMPL_LINK_NOARG(SvxHyphenWordDialog
, Right_Impl
, weld::Button
&, void)
388 void SvxHyphenWordDialog::select_region(int nStart
, int nEnd
)
390 int nScrollPos
= nStart
+ m_nWordEditWidth
/2;
391 if (nScrollPos
> m_aEditWord
.getLength())
392 nScrollPos
= m_aEditWord
.getLength() - m_nWordEditWidth
/2;
395 m_xWordEdit
->set_position(nScrollPos
);
396 m_xWordEdit
->select_region(nStart
, nEnd
);
399 IMPL_LINK_NOARG(SvxHyphenWordDialog
, GetFocusHdl_Impl
, weld::Widget
&, void)
401 select_region(m_nOldPos
, m_nOldPos
+ 1);
404 // class SvxHyphenWordDialog ---------------------------------------------
406 SvxHyphenWordDialog::SvxHyphenWordDialog(
407 const OUString
&rWord
, LanguageType nLang
,
408 weld::Window
* pParent
,
409 uno::Reference
< linguistic2::XHyphenator
> const &xHyphen
,
410 SvxSpellWrapper
* pWrapper
)
411 : SfxDialogController(pParent
, "cui/ui/hyphenate.ui", "HyphenateDialog")
412 , m_pHyphWrapper(pWrapper
)
414 , m_nActLanguage(nLang
)
415 , m_nMaxHyphenationPos(0)
417 , m_nHyphenationPositionsOffset(0)
419 , m_xWordEdit(m_xBuilder
->weld_entry("worded"))
420 , m_xLeftBtn(m_xBuilder
->weld_button("left"))
421 , m_xRightBtn(m_xBuilder
->weld_button("right"))
422 , m_xOkBtn(m_xBuilder
->weld_button("ok"))
423 , m_xContBtn(m_xBuilder
->weld_button("continue"))
424 , m_xDelBtn(m_xBuilder
->weld_button("delete"))
425 , m_xHyphAll(m_xBuilder
->weld_button("hyphall"))
426 , m_xCloseBtn(m_xBuilder
->weld_button("close"))
428 m_nWordEditWidth
= m_xWordEdit
->get_width_chars();
429 m_aLabel
= m_xDialog
->get_title();
430 m_xHyphenator
= xHyphen
;
432 uno::Reference
< linguistic2::XHyphenatedWord
> xHyphWord( m_pHyphWrapper
?
433 m_pHyphWrapper
->GetLast() : nullptr, uno::UNO_QUERY
);
434 DBG_ASSERT( xHyphWord
.is(), "hyphenation result missing" );
437 DBG_ASSERT( m_aActWord
== xHyphWord
->getWord(), "word mismatch" );
438 DBG_ASSERT( m_nActLanguage
== LanguageTag( xHyphWord
->getLocale() ).getLanguageType(), "language mismatch" );
439 m_nMaxHyphenationPos
= xHyphWord
->getHyphenationPos();
443 m_xWordEdit
->grab_focus();
445 m_xLeftBtn
->connect_clicked( LINK( this, SvxHyphenWordDialog
, Left_Impl
) );
446 m_xRightBtn
->connect_clicked( LINK( this, SvxHyphenWordDialog
, Right_Impl
) );
447 m_xOkBtn
->connect_clicked( LINK( this, SvxHyphenWordDialog
, CutHdl_Impl
) );
448 m_xContBtn
->connect_clicked( LINK( this, SvxHyphenWordDialog
, ContinueHdl_Impl
) );
449 m_xDelBtn
->connect_clicked( LINK( this, SvxHyphenWordDialog
, DeleteHdl_Impl
) );
450 m_xHyphAll
->connect_clicked( LINK( this, SvxHyphenWordDialog
, HyphenateAllHdl_Impl
) );
451 m_xCloseBtn
->connect_clicked( LINK( this, SvxHyphenWordDialog
, CancelHdl_Impl
) );
452 m_xWordEdit
->connect_focus_in( LINK( this, SvxHyphenWordDialog
, GetFocusHdl_Impl
) );
453 m_xWordEdit
->connect_cursor_position( LINK( this, SvxHyphenWordDialog
, CursorChangeHdl_Impl
) );
455 SetWindowTitle( nLang
);
457 // disable controls if service is not available
458 if (!m_xHyphenator
.is())
459 m_xDialog
->set_sensitive(false);
462 SvxHyphenWordDialog::~SvxHyphenWordDialog()
464 if (m_xCloseBtn
->get_sensitive())
465 m_pHyphWrapper
->SpellEnd();
468 void SvxHyphenWordDialog::SetWindowTitle(LanguageType nLang
)
470 m_xDialog
->set_title(m_aLabel
+ " (" + SvtLanguageTable::GetLanguageString(nLang
) + ")");
473 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */