update credits
[LibreOffice.git] / cui / source / dialogs / hyphen.cxx
blob7e4e72474045245939922d8c9c9d29f5ba768182
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"
21 #include "cuires.hrc"
22 #include "dialmgr.hxx"
24 #include <editeng/splwrap.hxx>
25 #include <editeng/svxenum.hxx>
26 #include <editeng/unolingu.hxx>
27 #include <svtools/langtab.hxx>
28 #include <svx/dialmgr.hxx>
29 #include <svx/dlgutil.hxx>
30 #include <tools/shl.hxx>
31 #include <vcl/msgbox.hxx>
33 #define HYPH_POS_CHAR '='
34 #define CONTINUE_HYPH USHRT_MAX
36 #define CUR_HYPH_POS_CHAR '-'
38 using namespace css;
40 HyphenEdit::HyphenEdit(Window* pParent)
41 : Edit(pParent, WB_LEFT|WB_VCENTER|WB_BORDER|WB_3DLOOK|WB_TABSTOP)
45 extern "C" SAL_DLLPUBLIC_EXPORT Window* SAL_CALL makeHyphenEdit(Window *pParent, VclBuilder::stringmap &)
47 return new HyphenEdit(pParent);
50 void HyphenEdit::KeyInput( const KeyEvent& rKEvt )
52 sal_uInt16 nCode = rKEvt.GetKeyCode().GetCode();
54 switch ( nCode )
56 case KEY_LEFT:
57 ( (SvxHyphenWordDialog*)GetParentDialog() )->SelLeft();
58 break;
60 case KEY_RIGHT:
61 ( (SvxHyphenWordDialog*)GetParentDialog() )->SelRight();
62 break;
64 case KEY_TAB:
65 case KEY_ESCAPE:
66 case KEY_RETURN:
67 Edit::KeyInput(rKEvt);
68 break;
69 default:
70 Control::KeyInput( rKEvt ); // pass on to the dialog
71 break;
76 void SvxHyphenWordDialog::EnableLRBtn_Impl()
78 String aTxt( aEditWord );
79 xub_StrLen nLen = aTxt.Len();
80 xub_StrLen i;
82 m_pRightBtn->Disable();
83 for ( i = nOldPos + 2; i < nLen; ++i )
85 if ( aTxt.GetChar( i ) == sal_Unicode( HYPH_POS_CHAR ) )
87 m_pRightBtn->Enable();
88 break;
92 DBG_ASSERT(nOldPos < aTxt.Len(), "nOldPos out of range");
93 if (nOldPos >= aTxt.Len())
94 nOldPos = aTxt.Len() - 1;
95 m_pLeftBtn->Disable();
96 for ( i = nOldPos; i-- > 0; )
98 if ( aTxt.GetChar( i ) == sal_Unicode( HYPH_POS_CHAR ) )
100 m_pLeftBtn->Enable();
101 break;
107 String SvxHyphenWordDialog::EraseUnusableHyphens_Impl(
108 uno::Reference< linguistic2::XPossibleHyphens > &rxPossHyph,
109 sal_uInt16 _nMaxHyphenationPos )
111 // returns a String showing only those hyphen positions which will result
112 // in a line break if hyphenation is done there
113 // 1) we will need to discard all hyphenation positions at th end that
114 // will not result in a line break where the text to the left still fits
115 // on the line.
116 // 2) since as from OOo 3.2 '-' are part of a word an thus text like
117 // 'multi-line-editor' is regarded as single word we also need to discard those
118 // hyphenation positions to the left of the rightmost '-' that is still left of
119 // the rightmost valid hyphenation position according to 1)
121 // Example:
122 // If the possible hyphenation position in 'multi-line-editor' are to eb marked
123 // by '=' then the text will look like this 'mul=ti-line-ed=it=or'.
124 // If now the first line is only large enough for 'multi-line-edi' we need to discard
125 // the last possible hyphnation point because of 1). The right most valid
126 // hyphenation position is "ed=itor". The first '-' left of this position is
127 // "line-ed", thus because of 2) we now need to discard all possible hyphneation
128 // positions to the left of that as well. Thus in the end leaving us with just
129 // 'multi-line-ed=itor' as return value for this function. (Just one valid hyphenation
130 // position for the user too choose from. However ALL the '-' characters in the word
131 // will ALWAYS be valid implicit hyphenation positions for the core to choose from!
132 // And thus even if this word is skipped in the hyphenation dialog it will still be broken
133 // right after 'multi-line-' (actually it might already be broken up that way before
134 // the hyphenation dialog is called!).
135 // Thus rule 2) just eliminates those positions which will not be used by the core at all
136 // even if the user were to select one of them.
138 String aTxt;
139 DBG_ASSERT(rxPossHyph.is(), "missing possible hyphens");
140 if (rxPossHyph.is())
142 DBG_ASSERT( aActWord == String( rxPossHyph->getWord() ), "word mismatch" );
144 aTxt = String( rxPossHyph->getPossibleHyphens() );
146 nHyphenationPositionsOffset = 0;
147 uno::Sequence< sal_Int16 > aHyphenationPositions(
148 rxPossHyph->getHyphenationPositions() );
149 sal_Int32 nLen = aHyphenationPositions.getLength();
150 const sal_Int16 *pHyphenationPos = aHyphenationPositions.getConstArray();
152 // find position nIdx after which all hyphen positions are unusable
153 xub_StrLen nIdx = STRING_NOTFOUND;
154 xub_StrLen nPos = 0, nPos1 = 0, nPos2 = 0;
155 if (nLen)
157 xub_StrLen nStart = 0;
158 for (sal_Int32 i = 0; i < nLen; ++i)
160 if (pHyphenationPos[i] > _nMaxHyphenationPos)
161 break;
162 else
164 // find corresponding hyphen pos in string
165 nPos = aTxt.Search( sal_Unicode( HYPH_POS_CHAR ), nStart );
167 if (nStart == STRING_NOTFOUND)
168 break;
169 else
171 nIdx = nPos;
172 nStart = nPos + 1;
177 DBG_ASSERT(nIdx != STRING_NOTFOUND, "no usable hyphenation position");
179 // 1) remove all not usable hyphenation positions from the end of the string
180 nPos = nIdx == STRING_NOTFOUND ? 0 : nIdx + 1;
181 nPos1 = nPos; //save for later use in 2) below
182 const OUString aTmp( sal_Unicode( HYPH_POS_CHAR ) );
183 const OUString aEmpty;
184 while (nPos != STRING_NOTFOUND)
185 nPos = aTxt.SearchAndReplace( aTmp, aEmpty, nPos + 1 );
187 // 2) remove all hyphenation positions from the start that are not considered by the core
188 const String aSearchRange( aTxt.Copy( 0, nPos1 ) );
189 nPos2 = aSearchRange.SearchBackward( '-' ); // the '-' position the core will use by default
190 if (nPos2 != STRING_NOTFOUND)
192 String aLeft( aSearchRange.Copy( 0, nPos2 ) );
193 nPos = 0;
194 while (nPos != STRING_NOTFOUND)
196 nPos = aLeft.SearchAndReplace( aTmp, aEmpty, nPos + 1 );
197 if (nPos != STRING_NOTFOUND)
198 ++nHyphenationPositionsOffset;
200 aTxt.Replace( 0, nPos2, aLeft );
203 return aTxt;
207 void SvxHyphenWordDialog::InitControls_Impl()
209 xPossHyph = NULL;
210 if (xHyphenator.is())
212 lang::Locale aLocale( LanguageTag(nActLanguage).getLocale() );
213 xPossHyph = xHyphenator->createPossibleHyphens( aActWord, aLocale,
214 uno::Sequence< beans::PropertyValue >() );
215 if (xPossHyph.is())
216 aEditWord = EraseUnusableHyphens_Impl( xPossHyph, nMaxHyphenationPos );
218 m_pWordEdit->SetText( aEditWord );
220 nOldPos = aEditWord.Len();
221 SelLeft();
222 EnableLRBtn_Impl();
226 void SvxHyphenWordDialog::ContinueHyph_Impl( sal_uInt16 nInsPos )
228 if ( nInsPos != CONTINUE_HYPH && xPossHyph.is())
230 if (nInsPos)
232 String aTmp( aEditWord );
233 DBG_ASSERT(nInsPos <= aTmp.Len() - 2, "wrong hyphen position");
235 sal_Int16 nIdxPos = -1;
236 for (sal_uInt16 i = 0; i <= nInsPos; ++i)
238 if (HYPH_POS_CHAR == aTmp.GetChar( i ))
239 nIdxPos++;
241 // take the possible hyphenation positions that got removed from the
242 // start of the wor dinot account:
243 nIdxPos += nHyphenationPositionsOffset;
245 uno::Sequence< sal_Int16 > aSeq = xPossHyph->getHyphenationPositions();
246 sal_Int32 nLen = aSeq.getLength();
247 DBG_ASSERT(nLen, "empty sequence");
248 DBG_ASSERT(0 <= nIdxPos && nIdxPos < nLen, "index out of range");
249 if (nLen && 0 <= nIdxPos && nIdxPos < nLen)
251 nInsPos = aSeq.getConstArray()[ nIdxPos ];
252 pHyphWrapper->InsertHyphen( nInsPos );
255 else
257 //! calling with 0 as argument will remove hyphens!
258 pHyphWrapper->InsertHyphen( nInsPos );
262 if ( pHyphWrapper->FindSpellError() )
264 uno::Reference< linguistic2::XHyphenatedWord > xHyphWord( pHyphWrapper->GetLast(), uno::UNO_QUERY );
266 // adapt actual word and language to new found hyphenation result
267 if(xHyphWord.is())
269 aActWord = String( xHyphWord->getWord() );
270 nActLanguage = LanguageTag( xHyphWord->getLocale() ).getLanguageType();
271 nMaxHyphenationPos = xHyphWord->getHyphenationPos();
272 InitControls_Impl();
273 SetWindowTitle( nActLanguage );
276 else
277 EndDialog( RET_OK );
281 sal_uInt16 SvxHyphenWordDialog::GetHyphIndex_Impl()
283 sal_uInt16 nPos = 0;
284 String aTxt( m_pWordEdit->GetText() );
286 for ( sal_uInt16 i=0 ; i < aTxt.Len(); ++i )
288 sal_Unicode cChar = aTxt.GetChar( i );
289 if ( cChar == CUR_HYPH_POS_CHAR )
290 break;
291 if ( cChar != HYPH_POS_CHAR )
292 nPos++;
294 return nPos;
298 void SvxHyphenWordDialog::SelLeft()
300 DBG_ASSERT( nOldPos > 0, "invalid hyphenation position" );
301 if (nOldPos > 0)
303 String aTxt( aEditWord );
304 for ( xub_StrLen i = nOldPos - 1; i > 0; --i)
306 DBG_ASSERT(i <= aTxt.Len(), "index out of range");
307 if (aTxt.GetChar( i ) == sal_Unicode( HYPH_POS_CHAR ))
309 aTxt.SetChar( i, sal_Unicode( CUR_HYPH_POS_CHAR ) );
311 nOldPos = i;
312 m_pWordEdit->SetText( aTxt );
313 m_pWordEdit->GrabFocus();
314 m_pWordEdit->SetSelection( Selection( i, i + 1 ) );
315 break;
318 nHyphPos = GetHyphIndex_Impl();
319 EnableLRBtn_Impl();
324 void SvxHyphenWordDialog::SelRight()
326 String aTxt( aEditWord );
327 for ( xub_StrLen i = nOldPos + 1; i < aTxt.Len(); ++i )
329 if (aTxt.GetChar( i ) == sal_Unicode( HYPH_POS_CHAR ))
331 aTxt.SetChar( i, sal_Unicode( CUR_HYPH_POS_CHAR ) );
333 nOldPos = i;
334 m_pWordEdit->SetText( aTxt );
335 m_pWordEdit->GrabFocus();
336 m_pWordEdit->SetSelection( Selection( i, i + 1 ) );
337 break;
340 nHyphPos = GetHyphIndex_Impl();
341 EnableLRBtn_Impl();
345 IMPL_LINK_NOARG(SvxHyphenWordDialog, CutHdl_Impl)
347 if( !bBusy )
349 bBusy = sal_True;
350 ContinueHyph_Impl( /*nHyphPos*/nOldPos );
351 bBusy = sal_False;
353 return 0;
357 IMPL_LINK( SvxHyphenWordDialog, HyphenateAllHdl_Impl, Button *, EMPTYARG /*pButton*/ )
359 if( !bBusy )
363 uno::Reference< linguistic2::XLinguProperties > xProp( SvxGetLinguPropertySet() );
365 xProp->setIsHyphAuto( sal_True );
367 bBusy = sal_True;
368 ContinueHyph_Impl( /*nHyphPos*/nOldPos );
369 bBusy = sal_False;
371 xProp->setIsHyphAuto( sal_False );
373 catch (uno::Exception &e)
375 (void) e;
376 DBG_ASSERT( 0, "Hyphenate All failed" );
379 return 0;
383 IMPL_LINK_NOARG(SvxHyphenWordDialog, DeleteHdl_Impl)
385 if( !bBusy )
387 bBusy = sal_True;
388 ContinueHyph_Impl();
389 bBusy = sal_False;
391 return 0;
395 IMPL_LINK_NOARG(SvxHyphenWordDialog, ContinueHdl_Impl)
397 if( !bBusy )
399 bBusy = sal_True;
400 ContinueHyph_Impl( CONTINUE_HYPH );
401 bBusy = sal_False;
403 return 0;
407 IMPL_LINK_NOARG(SvxHyphenWordDialog, CancelHdl_Impl)
409 if( !bBusy )
411 bBusy = sal_True;
412 pHyphWrapper->SpellEnd();
413 EndDialog( RET_CANCEL );
414 bBusy = sal_False;
416 return 0;
420 IMPL_LINK_NOARG(SvxHyphenWordDialog, Left_Impl)
422 if( !bBusy )
424 bBusy = sal_True;
425 SelLeft();
426 bBusy = sal_False;
428 return 0;
432 IMPL_LINK_NOARG(SvxHyphenWordDialog, Right_Impl)
434 if( !bBusy )
436 bBusy = sal_True;
437 SelRight();
438 bBusy = sal_False;
440 return 0;
444 IMPL_LINK_NOARG(SvxHyphenWordDialog, GetFocusHdl_Impl)
446 m_pWordEdit->SetSelection( Selection( nOldPos, nOldPos + 1 ) );
447 return 0;
451 // class SvxHyphenWordDialog ---------------------------------------------
453 SvxHyphenWordDialog::SvxHyphenWordDialog(
454 const String &rWord, LanguageType nLang,
455 Window* pParent,
456 uno::Reference< linguistic2::XHyphenator > &xHyphen,
457 SvxSpellWrapper* pWrapper)
458 : SfxModalDialog(pParent, "HyphenateDialog", "cui/ui/hyphenate.ui")
459 , pHyphWrapper(NULL)
460 , xHyphenator(NULL)
461 , xPossHyph(NULL)
462 , nActLanguage(LANGUAGE_NONE)
463 , nMaxHyphenationPos(0)
464 , nHyphPos(0)
465 , nOldPos(0)
466 , nHyphenationPositionsOffset(0)
467 , bBusy(sal_False)
469 get(m_pWordEdit, "worded");
470 get(m_pLeftBtn, "left");
471 get(m_pRightBtn, "right");
472 get(m_pOkBtn, "ok");
473 get(m_pContBtn, "continue");
474 get(m_pDelBtn, "delete");
475 get(m_pHyphAll, "hyphall");
476 get(m_pCloseBtn, "close");
478 aLabel = GetText();
479 aActWord = rWord;
480 nActLanguage = nLang;
481 xHyphenator = xHyphen;
482 pHyphWrapper = pWrapper;
484 uno::Reference< linguistic2::XHyphenatedWord > xHyphWord( pHyphWrapper ?
485 pHyphWrapper->GetLast() : NULL, uno::UNO_QUERY );
486 DBG_ASSERT( xHyphWord.is(), "hyphenation result missing" );
487 if (xHyphWord.is())
489 DBG_ASSERT( aActWord == String( xHyphWord->getWord() ), "word mismatch" );
490 DBG_ASSERT( nActLanguage == LanguageTag( xHyphWord->getLocale() ).getLanguageType(), "language mismatch" );
491 nMaxHyphenationPos = xHyphWord->getHyphenationPos();
494 InitControls_Impl();
495 m_pWordEdit->GrabFocus();
497 m_pLeftBtn->SetClickHdl( LINK( this, SvxHyphenWordDialog, Left_Impl ) );
498 m_pRightBtn->SetClickHdl( LINK( this, SvxHyphenWordDialog, Right_Impl ) );
499 m_pOkBtn->SetClickHdl( LINK( this, SvxHyphenWordDialog, CutHdl_Impl ) );
500 m_pContBtn->SetClickHdl( LINK( this, SvxHyphenWordDialog, ContinueHdl_Impl ) );
501 m_pDelBtn->SetClickHdl( LINK( this, SvxHyphenWordDialog, DeleteHdl_Impl ) );
502 m_pHyphAll->SetClickHdl( LINK( this, SvxHyphenWordDialog, HyphenateAllHdl_Impl ) );
503 m_pCloseBtn->SetClickHdl( LINK( this, SvxHyphenWordDialog, CancelHdl_Impl ) );
504 m_pWordEdit->SetGetFocusHdl( LINK( this, SvxHyphenWordDialog, GetFocusHdl_Impl ) );
506 SetWindowTitle( nLang );
508 // disable controls if service is not available
509 if (!xHyphenator.is())
510 Enable( sal_False );
514 SvxHyphenWordDialog::~SvxHyphenWordDialog()
519 void SvxHyphenWordDialog::SetWindowTitle( LanguageType nLang )
521 String aLangStr( SvtLanguageTable::GetLanguageString( nLang ) );
522 OUString aTmp( aLabel );
523 aTmp += " (";
524 aTmp += aLangStr;
525 aTmp += ")";
526 SetText( aTmp );
529 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */