bump product version to 6.3.0.0.beta1
[LibreOffice.git] / cui / source / dialogs / hyphen.cxx
blob8cd7d38f24ecf3db6416a885536522db8b4d9d62
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 <hyphen.hxx>
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 '-'
37 using namespace css;
39 IMPL_LINK_NOARG(SvxHyphenWordDialog, CursorChangeHdl_Impl, weld::Entry&, void)
41 int nStart, nEnd;
42 m_xWordEdit->get_selection_bounds(nStart, nEnd);
43 if (nStart == m_nOldPos && nEnd == m_nOldPos + 1)
44 return;
45 bool bReSelect;
46 if (nStart <= m_nOldPos)
47 bReSelect = !SelLeft();
48 else
49 bReSelect = !SelRight();
50 if (bReSelect)
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);
64 break;
68 DBG_ASSERT(m_nOldPos < nLen, "nOldPos out of range");
69 if (m_nOldPos >= nLen)
70 m_nOldPos = nLen - 1;
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);
77 break;
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
88 // on the line.
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)
94 // Example:
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.
111 OUString aTxt;
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
126 sal_Int32 nIdx = -1;
127 sal_Int32 nPos = 0, nPos1 = 0;
128 if (nLen)
130 sal_Int32 nStart = 0;
131 for (sal_Int32 i = 0; i < nLen; ++i)
133 if (pHyphenationPos[i] > m_nMaxHyphenationPos)
134 break;
135 else
137 // find corresponding hyphen positions in string
138 nPos = aTxt.indexOf( sal_Unicode( HYPH_POS_CHAR ), nStart );
140 if (nPos == -1)
141 break;
142 else
144 nIdx = nPos;
145 nStart = nPos + 1;
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 ) );
156 while (nPos != -1)
158 nPos++;
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
165 if (nPos2 != -1 )
167 OUString aLeft( aSearchRange.copy( 0, nPos2 ) );
168 nPos = 0;
169 while (nPos != -1)
171 nPos++;
172 aLeft = aLeft.replaceFirst( aTmp, "", &nPos );
173 if (nPos != -1)
174 ++m_nHyphenationPositionsOffset;
176 aTxt = aTxt.replaceAt( 0, nPos2, aLeft );
179 return aTxt;
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();
196 SelLeft();
197 EnableLRBtn_Impl();
200 void SvxHyphenWordDialog::ContinueHyph_Impl( sal_Int32 nInsPos )
202 if ( nInsPos >= 0 && m_xPossHyph.is() )
204 if (nInsPos)
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 ])
212 nIdxPos++;
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 );
228 else
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
240 if(xHyphWord.is())
242 m_aActWord = xHyphWord->getWord();
243 m_nActLanguage = LanguageTag( xHyphWord->getLocale() ).getLanguageType();
244 m_nMaxHyphenationPos = xHyphWord->getHyphenationPos();
245 InitControls_Impl();
246 SetWindowTitle( m_nActLanguage );
249 else
251 m_xCloseBtn->set_sensitive(false);
252 m_xDialog->response(RET_OK);
256 bool SvxHyphenWordDialog::SelLeft()
258 bool bRet = false;
259 DBG_ASSERT( m_nOldPos > 0, "invalid hyphenation position" );
260 if (m_nOldPos > 0)
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 ) );
270 m_nOldPos = i;
271 m_xWordEdit->set_text(aTxt);
272 select_region(i, i + 1);
273 m_xWordEdit->grab_focus();
274 bRet = true;
275 break;
278 EnableLRBtn_Impl();
280 return bRet;
283 bool SvxHyphenWordDialog::SelRight()
285 bool bRet = false;
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 ) );
293 m_nOldPos = i;
294 m_xWordEdit->set_text(aTxt);
295 select_region(i, i + 1);
296 m_xWordEdit->grab_focus();
297 bRet = true;
298 break;
301 EnableLRBtn_Impl();
302 return bRet;
305 IMPL_LINK_NOARG(SvxHyphenWordDialog, CutHdl_Impl, weld::Button&, void)
307 if( !m_bBusy )
309 m_bBusy = true;
310 ContinueHyph_Impl( /*m_nHyphPos*/m_nOldPos );
311 m_bBusy = false;
315 IMPL_LINK_NOARG(SvxHyphenWordDialog, HyphenateAllHdl_Impl, weld::Button&, void)
317 if( !m_bBusy )
321 uno::Reference< linguistic2::XLinguProperties > xProp( LinguMgr::GetLinguPropertySet() );
323 xProp->setIsHyphAuto( true );
325 m_bBusy = true;
326 ContinueHyph_Impl( /*m_nHyphPos*/m_nOldPos );
327 m_bBusy = false;
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)
340 if( !m_bBusy )
342 m_bBusy = true;
343 ContinueHyph_Impl( 0 );
344 m_bBusy = false;
348 IMPL_LINK_NOARG(SvxHyphenWordDialog, ContinueHdl_Impl, weld::Button&, void)
350 if( !m_bBusy )
352 m_bBusy = true;
353 ContinueHyph_Impl();
354 m_bBusy = false;
358 IMPL_LINK_NOARG(SvxHyphenWordDialog, CancelHdl_Impl, weld::Button&, void)
360 if( !m_bBusy )
362 m_bBusy = true;
363 m_xDialog->response(RET_CANCEL);
364 m_bBusy = false;
368 IMPL_LINK_NOARG(SvxHyphenWordDialog, Left_Impl, weld::Button&, void)
370 if( !m_bBusy )
372 m_bBusy = true;
373 SelLeft();
374 m_bBusy = false;
378 IMPL_LINK_NOARG(SvxHyphenWordDialog, Right_Impl, weld::Button&, void)
380 if( !m_bBusy )
382 m_bBusy = true;
383 SelRight();
384 m_bBusy = false;
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;
393 if (nScrollPos < 0)
394 nScrollPos = 0;
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)
413 , m_aActWord(rWord)
414 , m_nActLanguage(nLang)
415 , m_nMaxHyphenationPos(0)
416 , m_nOldPos(0)
417 , m_nHyphenationPositionsOffset(0)
418 , m_bBusy(false)
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" );
435 if (xHyphWord.is())
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();
442 InitControls_Impl();
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: */