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/unolingu.hxx>
26 #include <svtools/langtab.hxx>
27 #include <sal/log.hxx>
28 #include <i18nlangtag/languagetag.hxx>
29 #include <tools/debug.hxx>
32 #define HYPH_POS_CHAR '='
34 #define CUR_HYPH_POS_CHAR '-'
38 IMPL_LINK_NOARG(SvxHyphenWordDialog
, CursorChangeHdl_Impl
, weld::Entry
&, void)
41 m_xWordEdit
->get_selection_bounds(nStart
, nEnd
);
42 if (nStart
== m_nOldPos
&& nEnd
== m_nOldPos
+ 1)
45 if (nStart
<= m_nOldPos
)
46 bReSelect
= !SelLeft();
48 bReSelect
= !SelRight();
50 select_region(m_nOldPos
, m_nOldPos
+ 1);
53 void SvxHyphenWordDialog::EnableLRBtn_Impl()
55 const sal_Int32 nLen
= m_aEditWord
.getLength();
57 m_xRightBtn
->set_sensitive(false);
58 for ( sal_Int32 i
= m_nOldPos
+ 2; i
< nLen
; ++i
)
60 if ( m_aEditWord
[ i
] == sal_Unicode( HYPH_POS_CHAR
) )
62 m_xRightBtn
->set_sensitive(true);
67 DBG_ASSERT(m_nOldPos
< nLen
, "nOldPos out of range");
68 if (m_nOldPos
>= nLen
)
70 m_xLeftBtn
->set_sensitive(false);
71 for ( sal_Int32 i
= m_nOldPos
; i
-- > 0; )
73 if ( m_aEditWord
[ i
] == sal_Unicode( HYPH_POS_CHAR
) )
75 m_xLeftBtn
->set_sensitive(true);
81 OUString
SvxHyphenWordDialog::EraseUnusableHyphens_Impl()
83 // returns a String showing only those hyphen positions which will result
84 // in a line break if hyphenation is done there
85 // 1) we will need to discard all hyphenation positions at the end that
86 // will not result in a line break where the text to the left still fits
88 // 2) since as from OOo 3.2 '-' are part of a word and thus text like
89 // 'multi-line-editor' is regarded as single word we also need to discard those
90 // hyphenation positions to the left of the rightmost '-' that is still left of
91 // the rightmost valid hyphenation position according to 1)
94 // If the possible hyphenation position in 'multi-line-editor' are to be marked
95 // by '=' then the text will look like this: 'mul=ti-line-ed=it=or'.
96 // If now the first line is only large enough for 'multi-line-edi' we need to discard
97 // the last possible hyphenation point because of 1). The right most valid
98 // hyphenation position is "ed=itor". The first '-' left of this position is
99 // "line-ed", thus because of 2) we now need to discard all possible hyphenation
100 // positions to the left of that as well. Thus in the end leaving us with just
101 // 'multi-line-ed=itor' as return value for this function. (Just one valid hyphenation
102 // position for the user to choose from. However ALL the '-' characters in the word
103 // will ALWAYS be valid implicit hyphenation positions for the core to choose from!
104 // And thus even if this word is skipped in the hyphenation dialog it will still be broken
105 // right after 'multi-line-' (actually it might already be broken up that way before
106 // the hyphenation dialog is called!).
107 // Thus rule 2) just eliminates those positions which will not be used by the core at all
108 // even if the user were to select one of them.
111 DBG_ASSERT(m_xPossHyph
.is(), "missing possible hyphens");
112 if (m_xPossHyph
.is())
114 DBG_ASSERT( m_aActWord
== m_xPossHyph
->getWord(), "word mismatch" );
116 aTxt
= m_xPossHyph
->getPossibleHyphens();
118 m_nHyphenationPositionsOffset
= 0;
119 uno::Sequence
< sal_Int16
> aHyphenationPositions(
120 m_xPossHyph
->getHyphenationPositions() );
121 sal_Int32 nLen
= aHyphenationPositions
.getLength();
122 const sal_Int16
*pHyphenationPos
= aHyphenationPositions
.getConstArray();
124 // find position nIdx after which all hyphen positions are unusable
126 sal_Int32 nPos
= 0, nPos1
= 0;
129 sal_Int32 nStart
= 0;
130 for (sal_Int32 i
= 0; i
< nLen
; ++i
)
132 if (pHyphenationPos
[i
] > m_nMaxHyphenationPos
)
136 // find corresponding hyphen positions in string
137 nPos
= aTxt
.indexOf( sal_Unicode( HYPH_POS_CHAR
), nStart
);
149 DBG_ASSERT(nIdx
!= -1, "no usable hyphenation position");
151 // 1) remove all not usable hyphenation positions from the end of the string
152 nPos
= nIdx
== -1 ? 0 : nIdx
+ 1;
153 nPos1
= nPos
; //save for later use in 2) below
154 const OUString
aTmp( sal_Unicode( HYPH_POS_CHAR
) );
158 aTxt
= aTxt
.replaceFirst( aTmp
, "", &nPos
);
161 // 2) remove all hyphenation positions from the start that are not considered by the core
162 const std::u16string_view
aSearchRange( aTxt
.subView( 0, nPos1
) );
163 size_t nPos2
= aSearchRange
.rfind( '-' ); // the '-' position the core will use by default
164 if (nPos2
!= std::u16string_view::npos
&& nPos2
!= 0)
166 OUString
aLeft( aSearchRange
.substr( 0, nPos2
) );
171 aLeft
= aLeft
.replaceFirst( aTmp
, "", &nPos
);
173 ++m_nHyphenationPositionsOffset
;
175 aTxt
= aTxt
.replaceAt( 0, nPos2
, aLeft
);
181 void SvxHyphenWordDialog::InitControls_Impl()
183 m_xPossHyph
= nullptr;
184 if (m_xHyphenator
.is())
186 lang::Locale
aLocale( LanguageTag::convertToLocale(m_nActLanguage
) );
187 m_xPossHyph
= m_xHyphenator
->createPossibleHyphens( m_aActWord
, aLocale
,
188 uno::Sequence
< beans::PropertyValue
>() );
189 if (m_xPossHyph
.is())
190 m_aEditWord
= EraseUnusableHyphens_Impl();
192 m_xWordEdit
->set_text(m_aEditWord
);
194 m_nOldPos
= m_aEditWord
.getLength();
199 void SvxHyphenWordDialog::ContinueHyph_Impl( sal_Int32 nInsPos
)
201 if ( nInsPos
>= 0 && m_xPossHyph
.is() )
205 DBG_ASSERT(nInsPos
<= m_aEditWord
.getLength() - 2, "wrong hyphen position");
207 sal_Int32 nIdxPos
= -1;
208 for (sal_Int32 i
= 0; i
<= nInsPos
; ++i
)
210 if (HYPH_POS_CHAR
== m_aEditWord
[ i
])
213 // take the possible hyphenation positions that got removed from the
214 // start of the word into account:
215 nIdxPos
+= m_nHyphenationPositionsOffset
;
217 uno::Sequence
< sal_Int16
> aSeq
= m_xPossHyph
->getHyphenationPositions();
218 sal_Int32 nLen
= aSeq
.getLength();
219 DBG_ASSERT(nLen
, "empty sequence");
220 DBG_ASSERT(0 <= nIdxPos
&& nIdxPos
< nLen
, "index out of range");
221 if (nLen
&& 0 <= nIdxPos
&& nIdxPos
< nLen
)
223 nInsPos
= aSeq
.getConstArray()[ nIdxPos
];
224 m_pHyphWrapper
->InsertHyphen( nInsPos
);
229 //! calling with 0 as argument will remove hyphens!
230 m_pHyphWrapper
->InsertHyphen( nInsPos
);
234 if ( m_pHyphWrapper
->FindSpellError() )
236 uno::Reference
< linguistic2::XHyphenatedWord
> xHyphWord( m_pHyphWrapper
->GetLast(), uno::UNO_QUERY
);
238 // adapt actual word and language to new found hyphenation result
241 m_aActWord
= xHyphWord
->getWord();
242 m_nActLanguage
= LanguageTag( xHyphWord
->getLocale() ).getLanguageType();
243 m_nMaxHyphenationPos
= xHyphWord
->getHyphenationPos();
245 SetWindowTitle( m_nActLanguage
);
250 m_xCloseBtn
->set_sensitive(false);
251 m_xDialog
->response(RET_OK
);
255 bool SvxHyphenWordDialog::SelLeft()
258 DBG_ASSERT( m_nOldPos
> 0, "invalid hyphenation position" );
261 OUString
aTxt( m_aEditWord
);
262 for( sal_Int32 i
= m_nOldPos
- 1; i
> 0; --i
)
264 DBG_ASSERT(i
<= aTxt
.getLength(), "index out of range");
265 if (aTxt
[ i
] == sal_Unicode( HYPH_POS_CHAR
))
267 aTxt
= aTxt
.replaceAt( i
, 1, rtl::OUStringChar( CUR_HYPH_POS_CHAR
) );
270 m_xWordEdit
->set_text(aTxt
);
271 select_region(i
, i
+ 1);
272 m_xWordEdit
->grab_focus();
282 bool SvxHyphenWordDialog::SelRight()
285 OUString
aTxt( m_aEditWord
);
286 for ( sal_Int32 i
= m_nOldPos
+ 1; i
< aTxt
.getLength(); ++i
)
288 if (aTxt
[ i
] == sal_Unicode( HYPH_POS_CHAR
))
290 aTxt
= aTxt
.replaceAt( i
, 1, rtl::OUStringChar( CUR_HYPH_POS_CHAR
) );
293 m_xWordEdit
->set_text(aTxt
);
294 select_region(i
, i
+ 1);
295 m_xWordEdit
->grab_focus();
304 IMPL_LINK_NOARG(SvxHyphenWordDialog
, CutHdl_Impl
, weld::Button
&, void)
309 ContinueHyph_Impl( /*m_nHyphPos*/m_nOldPos
);
314 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" );
337 IMPL_LINK_NOARG(SvxHyphenWordDialog
, DeleteHdl_Impl
, weld::Button
&, void)
342 ContinueHyph_Impl( 0 );
347 IMPL_LINK_NOARG(SvxHyphenWordDialog
, ContinueHdl_Impl
, weld::Button
&, void)
357 IMPL_LINK_NOARG(SvxHyphenWordDialog
, CancelHdl_Impl
, weld::Button
&, void)
362 m_xDialog
->response(RET_CANCEL
);
367 IMPL_LINK_NOARG(SvxHyphenWordDialog
, Left_Impl
, weld::Button
&, void)
377 IMPL_LINK_NOARG(SvxHyphenWordDialog
, Right_Impl
, weld::Button
&, void)
387 void SvxHyphenWordDialog::select_region(int nStart
, int nEnd
)
389 int nScrollPos
= nStart
+ m_nWordEditWidth
/2;
390 if (nScrollPos
> m_aEditWord
.getLength())
391 nScrollPos
= m_aEditWord
.getLength() - m_nWordEditWidth
/2;
394 m_xWordEdit
->set_position(nScrollPos
);
395 m_xWordEdit
->select_region(nStart
, nEnd
);
398 IMPL_LINK_NOARG(SvxHyphenWordDialog
, GetFocusHdl_Impl
, weld::Widget
&, void)
400 select_region(m_nOldPos
, m_nOldPos
+ 1);
403 // class SvxHyphenWordDialog ---------------------------------------------
405 SvxHyphenWordDialog::SvxHyphenWordDialog(
406 OUString aWord
, LanguageType nLang
,
407 weld::Widget
* pParent
,
408 uno::Reference
< linguistic2::XHyphenator
> const &xHyphen
,
409 SvxSpellWrapper
* pWrapper
)
410 : SfxDialogController(pParent
, "cui/ui/hyphenate.ui", "HyphenateDialog")
411 , m_pHyphWrapper(pWrapper
)
412 , m_aActWord(std::move(aWord
))
413 , m_nActLanguage(nLang
)
414 , m_nMaxHyphenationPos(0)
416 , m_nHyphenationPositionsOffset(0)
418 , m_xWordEdit(m_xBuilder
->weld_entry("worded"))
419 , m_xLeftBtn(m_xBuilder
->weld_button("left"))
420 , m_xRightBtn(m_xBuilder
->weld_button("right"))
421 , m_xOkBtn(m_xBuilder
->weld_button("ok"))
422 , m_xContBtn(m_xBuilder
->weld_button("continue"))
423 , m_xDelBtn(m_xBuilder
->weld_button("delete"))
424 , m_xHyphAll(m_xBuilder
->weld_button("hyphall"))
425 , m_xCloseBtn(m_xBuilder
->weld_button("close"))
427 m_nWordEditWidth
= m_xWordEdit
->get_width_chars();
428 m_aLabel
= m_xDialog
->get_title();
429 m_xHyphenator
= xHyphen
;
431 uno::Reference
< linguistic2::XHyphenatedWord
> xHyphWord( m_pHyphWrapper
?
432 m_pHyphWrapper
->GetLast() : nullptr, uno::UNO_QUERY
);
433 DBG_ASSERT( xHyphWord
.is(), "hyphenation result missing" );
436 DBG_ASSERT( m_aActWord
== xHyphWord
->getWord(), "word mismatch" );
437 DBG_ASSERT( m_nActLanguage
== LanguageTag( xHyphWord
->getLocale() ).getLanguageType(), "language mismatch" );
438 m_nMaxHyphenationPos
= xHyphWord
->getHyphenationPos();
442 m_xWordEdit
->grab_focus();
444 m_xLeftBtn
->connect_clicked( LINK( this, SvxHyphenWordDialog
, Left_Impl
) );
445 m_xRightBtn
->connect_clicked( LINK( this, SvxHyphenWordDialog
, Right_Impl
) );
446 m_xOkBtn
->connect_clicked( LINK( this, SvxHyphenWordDialog
, CutHdl_Impl
) );
447 m_xContBtn
->connect_clicked( LINK( this, SvxHyphenWordDialog
, ContinueHdl_Impl
) );
448 m_xDelBtn
->connect_clicked( LINK( this, SvxHyphenWordDialog
, DeleteHdl_Impl
) );
449 m_xHyphAll
->connect_clicked( LINK( this, SvxHyphenWordDialog
, HyphenateAllHdl_Impl
) );
450 m_xCloseBtn
->connect_clicked( LINK( this, SvxHyphenWordDialog
, CancelHdl_Impl
) );
451 m_xWordEdit
->connect_focus_in( LINK( this, SvxHyphenWordDialog
, GetFocusHdl_Impl
) );
452 m_xWordEdit
->connect_cursor_position( LINK( this, SvxHyphenWordDialog
, CursorChangeHdl_Impl
) );
454 SetWindowTitle( nLang
);
456 // disable controls if service is not available
457 if (!m_xHyphenator
.is())
458 m_xDialog
->set_sensitive(false);
461 SvxHyphenWordDialog::~SvxHyphenWordDialog()
463 if (m_xCloseBtn
->get_sensitive())
464 m_pHyphWrapper
->SpellEnd();
467 void SvxHyphenWordDialog::SetWindowTitle(LanguageType nLang
)
469 m_xDialog
->set_title(m_aLabel
+ " (" + SvtLanguageTable::GetLanguageString(nLang
) + ")");
472 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */