1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2000, 2010 Oracle and/or its affiliates.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * This file is part of OpenOffice.org.
11 * OpenOffice.org is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU Lesser General Public License version 3
13 * only, as published by the Free Software Foundation.
15 * OpenOffice.org is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License version 3 for more details
19 * (a copy is included in the LICENSE file that accompanied this code).
21 * You should have received a copy of the GNU Lesser General Public License
22 * version 3 along with OpenOffice.org. If not, see
23 * <http://www.openoffice.org/license.html>
24 * for a copy of the LGPLv3 License.
26 ************************************************************************/
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_cui.hxx"
34 #include "dialmgr.hxx"
36 #include <editeng/splwrap.hxx>
37 #include <editeng/svxenum.hxx>
38 #include <editeng/unolingu.hxx>
39 #include <svtools/langtab.hxx>
40 #include <svx/dialmgr.hxx>
41 #include <svx/dlgutil.hxx>
42 #include <tools/list.hxx>
43 #include <tools/shl.hxx>
44 #include <vcl/msgbox.hxx>
46 #include <com/sun/star/linguistic2/XPossibleHyphens.hpp>
48 using namespace ::com::sun::star
;
51 #define HYPH_POS_CHAR '='
52 #define CONTINUE_HYPH USHRT_MAX
54 #define CUR_HYPH_POS_CHAR '-'
57 // class HyphenEdit_Impl -------------------------------------------------------
59 class HyphenEdit_Impl
: public Edit
62 HyphenEdit_Impl( Window
* pParent
, const ResId
& rResId
);
65 virtual void KeyInput( const KeyEvent
&rKEvt
);
69 HyphenEdit_Impl::HyphenEdit_Impl( Window
* pParent
, const ResId
& rResId
) :
70 Edit( pParent
, rResId
)
75 void HyphenEdit_Impl::KeyInput( const KeyEvent
& rKEvt
)
77 // sal_uInt16 nMod = rKEvt.GetKeyCode().GetModifier();
78 sal_uInt16 nCode
= rKEvt
.GetKeyCode().GetCode();
83 ( (SvxHyphenWordDialog
*)GetParent() )->SelLeft();
87 ( (SvxHyphenWordDialog
*)GetParent() )->SelRight();
93 Edit::KeyInput(rKEvt
);
96 Control::KeyInput( rKEvt
); // An den Dialog weiterleiten
102 // struct SvxHyphenWordDialog_Impl ---------------------------------------------
104 struct SvxHyphenWordDialog_Impl
106 SvxHyphenWordDialog
* m_pDialog
;
107 // Window * m_pParent;
110 HyphenEdit_Impl aWordEdit
;
111 ImageButton aLeftBtn
;
112 ImageButton aRightBtn
;
119 CancelButton aCancelBtn
;
121 SvxSpellWrapper
* pHyphWrapper
;
122 uno::Reference
< linguistic2::XHyphenator
> xHyphenator
;
123 uno::Reference
< linguistic2::XPossibleHyphens
> xPossHyph
;
124 String aEditWord
; // aEditWord and aWordEdit.GetText() differ only by the character for the current selected hyphenation position
125 String aActWord
; // actual word to be hyphenated
126 LanguageType nActLanguage
; // and its language
127 sal_uInt16 nMaxHyphenationPos
; // right most valid hyphenation pos
130 sal_Int32 nHyphenationPositionsOffset
;
134 void EnableLRBtn_Impl();
135 String
EraseUnusableHyphens_Impl( ::com::sun::star::uno::Reference
< ::com::sun::star::linguistic2::XPossibleHyphens
> &rxPossHyph
, sal_uInt16 nMaxHyphenationPos
);
137 void InitControls_Impl();
138 void ContinueHyph_Impl( sal_uInt16 nInsPos
= 0 );
139 sal_uInt16
GetHyphIndex_Impl();
141 void SelRight_Impl();
143 DECL_LINK( Left_Impl
, Button
* );
144 DECL_LINK( Right_Impl
, Button
* );
145 DECL_LINK( CutHdl_Impl
, Button
* );
146 DECL_LINK( ContinueHdl_Impl
, Button
* );
147 DECL_LINK( DeleteHdl_Impl
, Button
* );
148 DECL_LINK( HyphenateAllHdl_Impl
, Button
* );
149 DECL_LINK( CancelHdl_Impl
, Button
* );
150 DECL_LINK( GetFocusHdl_Impl
, Edit
* );
153 SvxHyphenWordDialog_Impl(
154 SvxHyphenWordDialog
* pDialog
,
157 uno::Reference
< linguistic2::XHyphenator
> &xHyphen
,
158 SvxSpellWrapper
* pWrapper
);
159 ~SvxHyphenWordDialog_Impl();
163 SvxHyphenWordDialog_Impl::SvxHyphenWordDialog_Impl(
164 SvxHyphenWordDialog
* pDialog
,
167 uno::Reference
< linguistic2::XHyphenator
> &xHyphen
,
168 SvxSpellWrapper
* pWrapper
) :
170 m_pDialog ( pDialog
),
171 aWordFT ( pDialog
, CUI_RES( FT_WORD
) ),
172 aWordEdit ( pDialog
, CUI_RES( ED_WORD
) ),
173 aLeftBtn ( pDialog
, CUI_RES( BTN_LEFT
) ),
174 aRightBtn ( pDialog
, CUI_RES( BTN_RIGHT
) ),
175 aOkBtn ( pDialog
, CUI_RES( BTN_HYPH_CUT
) ),
176 aContBtn ( pDialog
, CUI_RES( BTN_HYPH_CONTINUE
) ),
177 aDelBtn ( pDialog
, CUI_RES( BTN_HYPH_DELETE
) ),
178 aFLBottom ( pDialog
, CUI_RES( FL_BOTTOM
) ),
179 aHelpBtn ( pDialog
, CUI_RES( BTN_HYPH_HELP
) ),
180 aHyphAll ( pDialog
, CUI_RES( BTN_HYPH_ALL
) ),
181 aCancelBtn ( pDialog
, CUI_RES( BTN_HYPH_CANCEL
) ),
182 aLabel ( pDialog
->GetText() ),
183 pHyphWrapper ( NULL
),
184 xHyphenator ( NULL
),
187 nActLanguage ( LANGUAGE_NONE
),
188 nMaxHyphenationPos ( 0 ),
191 nHyphenationPositionsOffset( 0 ),
195 nActLanguage
= nLang
;
196 xHyphenator
= xHyphen
;
197 pHyphWrapper
= pWrapper
;
199 uno::Reference
< linguistic2::XHyphenatedWord
> xHyphWord( pHyphWrapper
?
200 pHyphWrapper
->GetLast() : NULL
, uno::UNO_QUERY
);
201 DBG_ASSERT( xHyphWord
.is(), "hyphenation result missing" );
204 DBG_ASSERT( aActWord
== String( xHyphWord
->getWord() ), "word mismatch" );
205 DBG_ASSERT( nActLanguage
== SvxLocaleToLanguage( xHyphWord
->getLocale() ), "language mismatch" );
206 nMaxHyphenationPos
= xHyphWord
->getHyphenationPos();
210 aWordEdit
.GrabFocus();
212 aLeftBtn
.SetClickHdl( LINK( this, SvxHyphenWordDialog_Impl
, Left_Impl
) );
213 aRightBtn
.SetClickHdl( LINK( this, SvxHyphenWordDialog_Impl
, Right_Impl
) );
214 aOkBtn
.SetClickHdl( LINK( this, SvxHyphenWordDialog_Impl
, CutHdl_Impl
) );
215 aContBtn
.SetClickHdl( LINK( this, SvxHyphenWordDialog_Impl
, ContinueHdl_Impl
) );
216 aDelBtn
.SetClickHdl( LINK( this, SvxHyphenWordDialog_Impl
, DeleteHdl_Impl
) );
217 aHyphAll
.SetClickHdl( LINK( this, SvxHyphenWordDialog_Impl
, HyphenateAllHdl_Impl
) );
218 aCancelBtn
.SetClickHdl( LINK( this, SvxHyphenWordDialog_Impl
, CancelHdl_Impl
) );
219 aWordEdit
.SetGetFocusHdl( LINK( this, SvxHyphenWordDialog_Impl
, GetFocusHdl_Impl
) );
223 SvxHyphenWordDialog_Impl::~SvxHyphenWordDialog_Impl()
228 void SvxHyphenWordDialog_Impl::EnableLRBtn_Impl()
230 String
aTxt( aEditWord
);
231 xub_StrLen nLen
= aTxt
.Len();
235 for ( i
= nOldPos
+ 2; i
< nLen
; ++i
)
237 if ( aTxt
.GetChar( i
) == sal_Unicode( HYPH_POS_CHAR
) )
244 DBG_ASSERT(nOldPos
< aTxt
.Len(), "nOldPos out of range");
245 if (nOldPos
>= aTxt
.Len())
246 nOldPos
= aTxt
.Len() - 1;
248 for ( i
= nOldPos
; i
-- > 0; )
250 if ( aTxt
.GetChar( i
) == sal_Unicode( HYPH_POS_CHAR
) )
259 String
SvxHyphenWordDialog_Impl::EraseUnusableHyphens_Impl(
260 uno::Reference
< linguistic2::XPossibleHyphens
> &rxPossHyph
,
261 sal_uInt16 _nMaxHyphenationPos
)
263 // returns a String showing only those hyphen positions which will result
264 // in a line break if hyphenation is done there
265 // 1) we will need to discard all hyphenation positions at th end that
266 // will not result in a line break where the text to the left still fits
268 // 2) since as from OOo 3.2 '-' are part of a word an thus text like
269 // 'multi-line-editor' is regarded as single word we also need to discard those
270 // hyphenation positions to the left of the rightmost '-' that is still left of
271 // the rightmost valid hyphenation position according to 1)
274 // If the possible hyphenation position in 'multi-line-editor' are to eb marked
275 // by '=' then the text will look like this 'mul=ti-line-ed=it=or'.
276 // If now the first line is only large enough for 'multi-line-edi' we need to discard
277 // the last possible hyphnation point because of 1). The the right most valid
278 // hyphenation position is "ed=itor". The first '-' left of this position is
279 // "line-ed", thus because of 2) we now need to discard all possible hyphneation
280 // positions to the left of that as well. Thus in the end leaving us with just
281 // 'multi-line-ed=itor' as return value for this function. (Just one valid hyphenation
282 // position for the user too choose from. However ALL the '-' characters in the word
283 // will ALWAYS be valid implicit hyphenation positions for the core to choose from!
284 // And thus even if this word is skipped in the hyphenation dialog it will still be broken
285 // right after 'multi-line-' (actually it might already be broken up that way before
286 // the hyphenation dialog is called!).
287 // Thus rule 2) just eliminates those positions which will not be used by the core at all
288 // even if the user were to select one of them.
291 DBG_ASSERT(rxPossHyph
.is(), "missing possible hyphens");
294 DBG_ASSERT( aActWord
== String( rxPossHyph
->getWord() ), "word mismatch" );
296 aTxt
= String( rxPossHyph
->getPossibleHyphens() );
298 nHyphenationPositionsOffset
= 0;
299 uno::Sequence
< sal_Int16
> aHyphenationPositions(
300 rxPossHyph
->getHyphenationPositions() );
301 sal_Int32 nLen
= aHyphenationPositions
.getLength();
302 const sal_Int16
*pHyphenationPos
= aHyphenationPositions
.getConstArray();
304 // find position nIdx after which all hyphen positions are unusable
305 xub_StrLen nIdx
= STRING_NOTFOUND
;
306 xub_StrLen nPos
= 0, nPos1
= 0, nPos2
= 0;
309 xub_StrLen nStart
= 0;
310 for (sal_Int32 i
= 0; i
< nLen
; ++i
)
312 if (pHyphenationPos
[i
] > _nMaxHyphenationPos
)
316 // find corresponding hyphen pos in string
317 nPos
= aTxt
.Search( sal_Unicode( HYPH_POS_CHAR
), nStart
);
319 if (nStart
== STRING_NOTFOUND
)
329 DBG_ASSERT(nIdx
!= STRING_NOTFOUND
, "no usable hyphenation position");
331 // 1) remove all not usable hyphenation positions from the end of the string
332 nPos
= nIdx
== STRING_NOTFOUND
? 0 : nIdx
+ 1;
333 nPos1
= nPos
; //save for later use in 2) below
334 const String
aTmp( sal_Unicode( HYPH_POS_CHAR
) );
336 while (nPos
!= STRING_NOTFOUND
)
337 nPos
= aTxt
.SearchAndReplace( aTmp
, aEmpty
, nPos
+ 1 );
339 // 2) remove all hyphenation positions from the start that are not considered by the core
340 const String
aSearchRange( aTxt
.Copy( 0, nPos1
) );
341 nPos2
= aSearchRange
.SearchBackward( '-' ); // the '-' position the core will use by default
342 if (nPos2
!= STRING_NOTFOUND
)
344 String
aLeft( aSearchRange
.Copy( 0, nPos2
) );
346 while (nPos
!= STRING_NOTFOUND
)
348 nPos
= aLeft
.SearchAndReplace( aTmp
, aEmpty
, nPos
+ 1 );
349 if (nPos
!= STRING_NOTFOUND
)
350 ++nHyphenationPositionsOffset
;
352 aTxt
.Replace( 0, nPos2
, aLeft
);
359 void SvxHyphenWordDialog_Impl::InitControls_Impl()
362 if (xHyphenator
.is())
364 lang::Locale
aLocale( SvxCreateLocale(nActLanguage
) );
365 xPossHyph
= xHyphenator
->createPossibleHyphens( aActWord
, aLocale
,
366 uno::Sequence
< beans::PropertyValue
>() );
368 aEditWord
= EraseUnusableHyphens_Impl( xPossHyph
, nMaxHyphenationPos
);
370 aWordEdit
.SetText( aEditWord
);
372 nOldPos
= aEditWord
.Len();
378 void SvxHyphenWordDialog_Impl::ContinueHyph_Impl( sal_uInt16 nInsPos
)
380 if ( nInsPos
!= CONTINUE_HYPH
&& xPossHyph
.is())
384 String
aTmp( aEditWord
);
385 DBG_ASSERT(nInsPos
<= aTmp
.Len() - 2, "wrong hyphen position");
387 sal_Int16 nIdxPos
= -1;
388 for (sal_uInt16 i
= 0; i
<= nInsPos
; ++i
)
390 if (HYPH_POS_CHAR
== aTmp
.GetChar( i
))
393 // take the possible hyphenation positions that got removed from the
394 // start of the wor dinot account:
395 nIdxPos
+= nHyphenationPositionsOffset
;
397 uno::Sequence
< sal_Int16
> aSeq
= xPossHyph
->getHyphenationPositions();
398 sal_Int32 nLen
= aSeq
.getLength();
399 DBG_ASSERT(nLen
, "empty sequence");
400 DBG_ASSERT(0 <= nIdxPos
&& nIdxPos
< nLen
, "index out of range");
401 if (nLen
&& 0 <= nIdxPos
&& nIdxPos
< nLen
)
403 nInsPos
= aSeq
.getConstArray()[ nIdxPos
];
404 pHyphWrapper
->InsertHyphen( nInsPos
);
409 //! calling with 0 as argument will remove hyphens!
410 pHyphWrapper
->InsertHyphen( nInsPos
);
414 if ( pHyphWrapper
->FindSpellError() )
416 uno::Reference
< linguistic2::XHyphenatedWord
> xHyphWord( pHyphWrapper
->GetLast(), uno::UNO_QUERY
);
418 // adapt actual word and language to new found hyphenation result
421 aActWord
= String( xHyphWord
->getWord() );
422 nActLanguage
= SvxLocaleToLanguage( xHyphWord
->getLocale() );
423 nMaxHyphenationPos
= xHyphWord
->getHyphenationPos();
425 m_pDialog
->SetWindowTitle( nActLanguage
);
429 m_pDialog
->EndDialog( RET_OK
);
433 sal_uInt16
SvxHyphenWordDialog_Impl::GetHyphIndex_Impl()
436 String
aTxt( aWordEdit
.GetText() );
438 for ( sal_uInt16 i
=0 ; i
< aTxt
.Len(); ++i
)
440 sal_Unicode cChar
= aTxt
.GetChar( i
);
441 if ( cChar
== CUR_HYPH_POS_CHAR
)
443 if ( cChar
!= HYPH_POS_CHAR
)
450 void SvxHyphenWordDialog_Impl::SelLeft_Impl()
452 DBG_ASSERT( nOldPos
> 0, "invalid hyphenation position" );
455 String
aTxt( aEditWord
);
456 for ( xub_StrLen i
= nOldPos
- 1; i
> 0; --i
)
458 DBG_ASSERT(i
<= aTxt
.Len(), "index out of range");
459 if (aTxt
.GetChar( i
) == sal_Unicode( HYPH_POS_CHAR
))
461 aTxt
.SetChar( i
, sal_Unicode( CUR_HYPH_POS_CHAR
) );
464 aWordEdit
.SetText( aTxt
);
465 aWordEdit
.GrabFocus();
466 aWordEdit
.SetSelection( Selection( i
, i
+ 1 ) );
470 nHyphPos
= GetHyphIndex_Impl();
476 void SvxHyphenWordDialog_Impl::SelRight_Impl()
478 String
aTxt( aEditWord
);
479 for ( xub_StrLen i
= nOldPos
+ 1; i
< aTxt
.Len(); ++i
)
481 if (aTxt
.GetChar( i
) == sal_Unicode( HYPH_POS_CHAR
))
483 aTxt
.SetChar( i
, sal_Unicode( CUR_HYPH_POS_CHAR
) );
486 aWordEdit
.SetText( aTxt
);
487 aWordEdit
.GrabFocus();
488 aWordEdit
.SetSelection( Selection( i
, i
+ 1 ) );
492 nHyphPos
= GetHyphIndex_Impl();
497 IMPL_LINK( SvxHyphenWordDialog_Impl
, CutHdl_Impl
, Button
*, EMPTYARG
)
502 ContinueHyph_Impl( /*nHyphPos*/nOldPos
);
509 IMPL_LINK( SvxHyphenWordDialog_Impl
, HyphenateAllHdl_Impl
, Button
*, EMPTYARG
/*pButton*/ )
515 uno::Reference
< beans::XPropertySet
> xProp( SvxGetLinguPropertySet() );
516 const rtl::OUString
aName( rtl::OUString::createFromAscii( "IsHyphAuto" ) );
520 xProp
->setPropertyValue( aName
, aAny
);
523 ContinueHyph_Impl( /*nHyphPos*/nOldPos
);
527 xProp
->setPropertyValue( aName
, aAny
);
529 catch (uno::Exception
&e
)
532 DBG_ASSERT( 0, "Hyphenate All failed" );
539 IMPL_LINK( SvxHyphenWordDialog_Impl
, DeleteHdl_Impl
, Button
*, EMPTYARG
)
551 IMPL_LINK( SvxHyphenWordDialog_Impl
, ContinueHdl_Impl
, Button
*, EMPTYARG
)
556 ContinueHyph_Impl( CONTINUE_HYPH
);
563 IMPL_LINK( SvxHyphenWordDialog_Impl
, CancelHdl_Impl
, Button
*, EMPTYARG
)
568 pHyphWrapper
->SpellEnd();
569 m_pDialog
->EndDialog( RET_CANCEL
);
576 IMPL_LINK( SvxHyphenWordDialog_Impl
, Left_Impl
, Button
*, EMPTYARG
)
588 IMPL_LINK( SvxHyphenWordDialog_Impl
, Right_Impl
, Button
*, EMPTYARG
)
600 IMPL_LINK( SvxHyphenWordDialog_Impl
, GetFocusHdl_Impl
, Edit
*, EMPTYARG
)
602 aWordEdit
.SetSelection( Selection( nOldPos
, nOldPos
+ 1 ) );
607 // class SvxHyphenWordDialog ---------------------------------------------
609 SvxHyphenWordDialog::SvxHyphenWordDialog(
610 const String
&rWord
, LanguageType nLang
,
612 uno::Reference
< linguistic2::XHyphenator
> &xHyphen
,
613 SvxSpellWrapper
* pWrapper
) :
615 SfxModalDialog( pParent
, CUI_RES( RID_SVXDLG_HYPHENATE
) )
617 m_pImpl
= std::auto_ptr
< SvxHyphenWordDialog_Impl
>(
618 new SvxHyphenWordDialog_Impl( this, rWord
, nLang
, xHyphen
, pWrapper
) );
622 SetWindowTitle( nLang
);
624 // disable controls if service is not available
625 if (!m_pImpl
->xHyphenator
.is())
630 SvxHyphenWordDialog::~SvxHyphenWordDialog()
635 void SvxHyphenWordDialog::SetWindowTitle( LanguageType nLang
)
637 String
aLangStr( SvtLanguageTable::GetLanguageString( nLang
) );
638 String
aTmp( m_pImpl
->aLabel
);
639 aTmp
.AppendAscii( RTL_CONSTASCII_STRINGPARAM( " (" ) );
640 aTmp
.Append( aLangStr
);
641 aTmp
.Append( sal_Unicode( ')' ) );
646 void SvxHyphenWordDialog::SelLeft()
648 m_pImpl
->SelRight_Impl();
652 void SvxHyphenWordDialog::SelRight()
654 m_pImpl
->SelLeft_Impl();