Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
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/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
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 .
18 : */
19 :
20 : #include "hyphen.hxx"
21 : #include "cuires.hrc"
22 : #include "dialmgr.hxx"
23 :
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>
32 :
33 : #define HYPH_POS_CHAR '='
34 : #define CONTINUE_HYPH USHRT_MAX
35 :
36 : #define CUR_HYPH_POS_CHAR '-'
37 :
38 0 : HyphenEdit::HyphenEdit(Window* pParent)
39 0 : : Edit(pParent, WB_LEFT|WB_VCENTER|WB_BORDER|WB_3DLOOK|WB_TABSTOP)
40 : {
41 0 : }
42 :
43 0 : extern "C" SAL_DLLPUBLIC_EXPORT Window* SAL_CALL makeHyphenEdit(Window *pParent, VclBuilder::stringmap &)
44 : {
45 0 : return new HyphenEdit(pParent);
46 : }
47 :
48 0 : void HyphenEdit::KeyInput( const KeyEvent& rKEvt )
49 : {
50 0 : sal_uInt16 nCode = rKEvt.GetKeyCode().GetCode();
51 :
52 0 : switch ( nCode )
53 : {
54 : case KEY_LEFT:
55 0 : ( (SvxHyphenWordDialog*)GetParent() )->SelLeft();
56 0 : break;
57 :
58 : case KEY_RIGHT:
59 0 : ( (SvxHyphenWordDialog*)GetParent() )->SelRight();
60 0 : break;
61 :
62 : case KEY_TAB:
63 : case KEY_ESCAPE:
64 : case KEY_RETURN:
65 0 : Edit::KeyInput(rKEvt);
66 0 : break;
67 : default:
68 0 : Control::KeyInput( rKEvt ); // pass on to the dialog
69 0 : break;
70 : }
71 0 : }
72 :
73 :
74 0 : void SvxHyphenWordDialog::EnableLRBtn_Impl()
75 : {
76 0 : String aTxt( aEditWord );
77 0 : xub_StrLen nLen = aTxt.Len();
78 : xub_StrLen i;
79 :
80 0 : m_pRightBtn->Disable();
81 0 : for ( i = nOldPos + 2; i < nLen; ++i )
82 : {
83 0 : if ( aTxt.GetChar( i ) == sal_Unicode( HYPH_POS_CHAR ) )
84 : {
85 0 : m_pRightBtn->Enable();
86 0 : break;
87 : }
88 : }
89 :
90 : DBG_ASSERT(nOldPos < aTxt.Len(), "nOldPos out of range");
91 0 : if (nOldPos >= aTxt.Len())
92 0 : nOldPos = aTxt.Len() - 1;
93 0 : m_pLeftBtn->Disable();
94 0 : for ( i = nOldPos; i-- > 0; )
95 : {
96 0 : if ( aTxt.GetChar( i ) == sal_Unicode( HYPH_POS_CHAR ) )
97 : {
98 0 : m_pLeftBtn->Enable();
99 0 : break;
100 : }
101 0 : }
102 0 : }
103 :
104 :
105 0 : String SvxHyphenWordDialog::EraseUnusableHyphens_Impl(
106 : uno::Reference< linguistic2::XPossibleHyphens > &rxPossHyph,
107 : sal_uInt16 _nMaxHyphenationPos )
108 : {
109 : // returns a String showing only those hyphen positions which will result
110 : // in a line break if hyphenation is done there
111 : // 1) we will need to discard all hyphenation positions at th end that
112 : // will not result in a line break where the text to the left still fits
113 : // on the line.
114 : // 2) since as from OOo 3.2 '-' are part of a word an thus text like
115 : // 'multi-line-editor' is regarded as single word we also need to discard those
116 : // hyphenation positions to the left of the rightmost '-' that is still left of
117 : // the rightmost valid hyphenation position according to 1)
118 : //
119 : // Example:
120 : // If the possible hyphenation position in 'multi-line-editor' are to eb marked
121 : // by '=' then the text will look like this 'mul=ti-line-ed=it=or'.
122 : // If now the first line is only large enough for 'multi-line-edi' we need to discard
123 : // the last possible hyphnation point because of 1). The the right most valid
124 : // hyphenation position is "ed=itor". The first '-' left of this position is
125 : // "line-ed", thus because of 2) we now need to discard all possible hyphneation
126 : // positions to the left of that as well. Thus in the end leaving us with just
127 : // 'multi-line-ed=itor' as return value for this function. (Just one valid hyphenation
128 : // position for the user too choose from. However ALL the '-' characters in the word
129 : // will ALWAYS be valid implicit hyphenation positions for the core to choose from!
130 : // And thus even if this word is skipped in the hyphenation dialog it will still be broken
131 : // right after 'multi-line-' (actually it might already be broken up that way before
132 : // the hyphenation dialog is called!).
133 : // Thus rule 2) just eliminates those positions which will not be used by the core at all
134 : // even if the user were to select one of them.
135 :
136 0 : String aTxt;
137 : DBG_ASSERT(rxPossHyph.is(), "missing possible hyphens");
138 0 : if (rxPossHyph.is())
139 : {
140 : DBG_ASSERT( aActWord == String( rxPossHyph->getWord() ), "word mismatch" );
141 :
142 0 : aTxt = String( rxPossHyph->getPossibleHyphens() );
143 :
144 0 : nHyphenationPositionsOffset = 0;
145 : uno::Sequence< sal_Int16 > aHyphenationPositions(
146 0 : rxPossHyph->getHyphenationPositions() );
147 0 : sal_Int32 nLen = aHyphenationPositions.getLength();
148 0 : const sal_Int16 *pHyphenationPos = aHyphenationPositions.getConstArray();
149 :
150 : // find position nIdx after which all hyphen positions are unusable
151 0 : xub_StrLen nIdx = STRING_NOTFOUND;
152 0 : xub_StrLen nPos = 0, nPos1 = 0, nPos2 = 0;
153 0 : if (nLen)
154 : {
155 0 : xub_StrLen nStart = 0;
156 0 : for (sal_Int32 i = 0; i < nLen; ++i)
157 : {
158 0 : if (pHyphenationPos[i] > _nMaxHyphenationPos)
159 0 : break;
160 : else
161 : {
162 : // find corresponding hyphen pos in string
163 0 : nPos = aTxt.Search( sal_Unicode( HYPH_POS_CHAR ), nStart );
164 :
165 0 : if (nStart == STRING_NOTFOUND)
166 0 : break;
167 : else
168 : {
169 0 : nIdx = nPos;
170 0 : nStart = nPos + 1;
171 : }
172 : }
173 : }
174 : }
175 : DBG_ASSERT(nIdx != STRING_NOTFOUND, "no usable hyphenation position");
176 :
177 : // 1) remove all not usable hyphenation positions from the end of the string
178 0 : nPos = nIdx == STRING_NOTFOUND ? 0 : nIdx + 1;
179 0 : nPos1 = nPos; //save for later use in 2) below
180 0 : const rtl::OUString aTmp( sal_Unicode( HYPH_POS_CHAR ) );
181 0 : const rtl::OUString aEmpty;
182 0 : while (nPos != STRING_NOTFOUND)
183 0 : nPos = aTxt.SearchAndReplace( aTmp, aEmpty, nPos + 1 );
184 :
185 : // 2) remove all hyphenation positions from the start that are not considered by the core
186 0 : const String aSearchRange( aTxt.Copy( 0, nPos1 ) );
187 0 : nPos2 = aSearchRange.SearchBackward( '-' ); // the '-' position the core will use by default
188 0 : if (nPos2 != STRING_NOTFOUND)
189 : {
190 0 : String aLeft( aSearchRange.Copy( 0, nPos2 ) );
191 0 : nPos = 0;
192 0 : while (nPos != STRING_NOTFOUND)
193 : {
194 0 : nPos = aLeft.SearchAndReplace( aTmp, aEmpty, nPos + 1 );
195 0 : if (nPos != STRING_NOTFOUND)
196 0 : ++nHyphenationPositionsOffset;
197 : }
198 0 : aTxt.Replace( 0, nPos2, aLeft );
199 0 : }
200 : }
201 0 : return aTxt;
202 : }
203 :
204 :
205 0 : void SvxHyphenWordDialog::InitControls_Impl()
206 : {
207 0 : xPossHyph = NULL;
208 0 : if (xHyphenator.is())
209 : {
210 0 : lang::Locale aLocale( LanguageTag(nActLanguage).getLocale() );
211 0 : xPossHyph = xHyphenator->createPossibleHyphens( aActWord, aLocale,
212 0 : uno::Sequence< beans::PropertyValue >() );
213 0 : if (xPossHyph.is())
214 0 : aEditWord = EraseUnusableHyphens_Impl( xPossHyph, nMaxHyphenationPos );
215 : }
216 0 : m_pWordEdit->SetText( aEditWord );
217 :
218 0 : nOldPos = aEditWord.Len();
219 0 : SelLeft();
220 0 : EnableLRBtn_Impl();
221 0 : }
222 :
223 :
224 0 : void SvxHyphenWordDialog::ContinueHyph_Impl( sal_uInt16 nInsPos )
225 : {
226 0 : if ( nInsPos != CONTINUE_HYPH && xPossHyph.is())
227 : {
228 0 : if (nInsPos)
229 : {
230 0 : String aTmp( aEditWord );
231 : DBG_ASSERT(nInsPos <= aTmp.Len() - 2, "wrong hyphen position");
232 :
233 0 : sal_Int16 nIdxPos = -1;
234 0 : for (sal_uInt16 i = 0; i <= nInsPos; ++i)
235 : {
236 0 : if (HYPH_POS_CHAR == aTmp.GetChar( i ))
237 0 : nIdxPos++;
238 : }
239 : // take the possible hyphenation positions that got removed from the
240 : // start of the wor dinot account:
241 0 : nIdxPos += nHyphenationPositionsOffset;
242 :
243 0 : uno::Sequence< sal_Int16 > aSeq = xPossHyph->getHyphenationPositions();
244 0 : sal_Int32 nLen = aSeq.getLength();
245 : DBG_ASSERT(nLen, "empty sequence");
246 : DBG_ASSERT(0 <= nIdxPos && nIdxPos < nLen, "index out of range");
247 0 : if (nLen && 0 <= nIdxPos && nIdxPos < nLen)
248 : {
249 0 : nInsPos = aSeq.getConstArray()[ nIdxPos ];
250 0 : pHyphWrapper->InsertHyphen( nInsPos );
251 0 : }
252 : }
253 : else
254 : {
255 : //! calling with 0 as argument will remove hyphens!
256 0 : pHyphWrapper->InsertHyphen( nInsPos );
257 : }
258 : }
259 :
260 0 : if ( pHyphWrapper->FindSpellError() )
261 : {
262 0 : uno::Reference< linguistic2::XHyphenatedWord > xHyphWord( pHyphWrapper->GetLast(), uno::UNO_QUERY );
263 :
264 : // adapt actual word and language to new found hyphenation result
265 0 : if(xHyphWord.is())
266 : {
267 0 : aActWord = String( xHyphWord->getWord() );
268 0 : nActLanguage = LanguageTag( xHyphWord->getLocale() ).getLanguageType();
269 0 : nMaxHyphenationPos = xHyphWord->getHyphenationPos();
270 0 : InitControls_Impl();
271 0 : SetWindowTitle( nActLanguage );
272 0 : }
273 : }
274 : else
275 0 : EndDialog( RET_OK );
276 0 : }
277 :
278 :
279 0 : sal_uInt16 SvxHyphenWordDialog::GetHyphIndex_Impl()
280 : {
281 0 : sal_uInt16 nPos = 0;
282 0 : String aTxt( m_pWordEdit->GetText() );
283 :
284 0 : for ( sal_uInt16 i=0 ; i < aTxt.Len(); ++i )
285 : {
286 0 : sal_Unicode cChar = aTxt.GetChar( i );
287 0 : if ( cChar == CUR_HYPH_POS_CHAR )
288 0 : break;
289 0 : if ( cChar != HYPH_POS_CHAR )
290 0 : nPos++;
291 : }
292 0 : return nPos;
293 : }
294 :
295 :
296 0 : void SvxHyphenWordDialog::SelLeft()
297 : {
298 : DBG_ASSERT( nOldPos > 0, "invalid hyphenation position" );
299 0 : if (nOldPos > 0)
300 : {
301 0 : String aTxt( aEditWord );
302 0 : for ( xub_StrLen i = nOldPos - 1; i > 0; --i)
303 : {
304 : DBG_ASSERT(i <= aTxt.Len(), "index out of range");
305 0 : if (aTxt.GetChar( i ) == sal_Unicode( HYPH_POS_CHAR ))
306 : {
307 0 : aTxt.SetChar( i, sal_Unicode( CUR_HYPH_POS_CHAR ) );
308 :
309 0 : nOldPos = i;
310 0 : m_pWordEdit->SetText( aTxt );
311 0 : m_pWordEdit->GrabFocus();
312 0 : m_pWordEdit->SetSelection( Selection( i, i + 1 ) );
313 0 : break;
314 : }
315 : }
316 0 : nHyphPos = GetHyphIndex_Impl();
317 0 : EnableLRBtn_Impl();
318 : }
319 0 : }
320 :
321 :
322 0 : void SvxHyphenWordDialog::SelRight()
323 : {
324 0 : String aTxt( aEditWord );
325 0 : for ( xub_StrLen i = nOldPos + 1; i < aTxt.Len(); ++i )
326 : {
327 0 : if (aTxt.GetChar( i ) == sal_Unicode( HYPH_POS_CHAR ))
328 : {
329 0 : aTxt.SetChar( i, sal_Unicode( CUR_HYPH_POS_CHAR ) );
330 :
331 0 : nOldPos = i;
332 0 : m_pWordEdit->SetText( aTxt );
333 0 : m_pWordEdit->GrabFocus();
334 0 : m_pWordEdit->SetSelection( Selection( i, i + 1 ) );
335 0 : break;
336 : }
337 : }
338 0 : nHyphPos = GetHyphIndex_Impl();
339 0 : EnableLRBtn_Impl();
340 0 : }
341 :
342 :
343 0 : IMPL_LINK_NOARG(SvxHyphenWordDialog, CutHdl_Impl)
344 : {
345 0 : if( !bBusy )
346 : {
347 0 : bBusy = sal_True;
348 0 : ContinueHyph_Impl( /*nHyphPos*/nOldPos );
349 0 : bBusy = sal_False;
350 : }
351 0 : return 0;
352 : }
353 :
354 :
355 0 : IMPL_LINK( SvxHyphenWordDialog, HyphenateAllHdl_Impl, Button *, EMPTYARG /*pButton*/ )
356 : {
357 0 : if( !bBusy )
358 : {
359 : try
360 : {
361 0 : uno::Reference< beans::XPropertySet > xProp( SvxGetLinguPropertySet() );
362 0 : const rtl::OUString aName( RTL_CONSTASCII_USTRINGPARAM( "IsHyphAuto" ) );
363 0 : uno::Any aAny;
364 :
365 0 : aAny <<= sal_True;
366 0 : xProp->setPropertyValue( aName, aAny );
367 :
368 0 : bBusy = sal_True;
369 0 : ContinueHyph_Impl( /*nHyphPos*/nOldPos );
370 0 : bBusy = sal_False;
371 :
372 0 : aAny <<= sal_False;
373 0 : xProp->setPropertyValue( aName, aAny );
374 : }
375 0 : catch (uno::Exception &e)
376 : {
377 : (void) e;
378 : DBG_ASSERT( 0, "Hyphenate All failed" );
379 : }
380 : }
381 0 : return 0;
382 : }
383 :
384 :
385 0 : IMPL_LINK_NOARG(SvxHyphenWordDialog, DeleteHdl_Impl)
386 : {
387 0 : if( !bBusy )
388 : {
389 0 : bBusy = sal_True;
390 0 : ContinueHyph_Impl();
391 0 : bBusy = sal_False;
392 : }
393 0 : return 0;
394 : }
395 :
396 :
397 0 : IMPL_LINK_NOARG(SvxHyphenWordDialog, ContinueHdl_Impl)
398 : {
399 0 : if( !bBusy )
400 : {
401 0 : bBusy = sal_True;
402 0 : ContinueHyph_Impl( CONTINUE_HYPH );
403 0 : bBusy = sal_False;
404 : }
405 0 : return 0;
406 : }
407 :
408 :
409 0 : IMPL_LINK_NOARG(SvxHyphenWordDialog, CancelHdl_Impl)
410 : {
411 0 : if( !bBusy )
412 : {
413 0 : bBusy = sal_True;
414 0 : pHyphWrapper->SpellEnd();
415 0 : EndDialog( RET_CANCEL );
416 0 : bBusy = sal_False;
417 : }
418 0 : return 0;
419 : }
420 :
421 :
422 0 : IMPL_LINK_NOARG(SvxHyphenWordDialog, Left_Impl)
423 : {
424 0 : if( !bBusy )
425 : {
426 0 : bBusy = sal_True;
427 0 : SelLeft();
428 0 : bBusy = sal_False;
429 : }
430 0 : return 0;
431 : }
432 :
433 :
434 0 : IMPL_LINK_NOARG(SvxHyphenWordDialog, Right_Impl)
435 : {
436 0 : if( !bBusy )
437 : {
438 0 : bBusy = sal_True;
439 0 : SelRight();
440 0 : bBusy = sal_False;
441 : }
442 0 : return 0;
443 : }
444 :
445 :
446 0 : IMPL_LINK_NOARG(SvxHyphenWordDialog, GetFocusHdl_Impl)
447 : {
448 0 : m_pWordEdit->SetSelection( Selection( nOldPos, nOldPos + 1 ) );
449 0 : return 0;
450 : }
451 :
452 :
453 : // class SvxHyphenWordDialog ---------------------------------------------
454 :
455 0 : SvxHyphenWordDialog::SvxHyphenWordDialog(
456 : const String &rWord, LanguageType nLang,
457 : Window* pParent,
458 : uno::Reference< linguistic2::XHyphenator > &xHyphen,
459 : SvxSpellWrapper* pWrapper)
460 : : SfxModalDialog(pParent, "HyphenateDialog", "cui/ui/hyphenate.ui")
461 : , pHyphWrapper(NULL)
462 : , xHyphenator(NULL)
463 : , xPossHyph(NULL)
464 : , nActLanguage(LANGUAGE_NONE)
465 : , nMaxHyphenationPos(0)
466 : , nHyphPos(0)
467 : , nOldPos(0)
468 : , nHyphenationPositionsOffset(0)
469 0 : , bBusy(sal_False)
470 : {
471 0 : get(m_pWordEdit, "worded");
472 0 : get(m_pLeftBtn, "left");
473 0 : get(m_pRightBtn, "right");
474 0 : get(m_pOkBtn, "ok");
475 0 : get(m_pContBtn, "continue");
476 0 : get(m_pDelBtn, "delete");
477 0 : get(m_pHyphAll, "hyphall");
478 0 : get(m_pCloseBtn, "close");
479 :
480 0 : aLabel = GetText();
481 0 : aActWord = rWord;
482 0 : nActLanguage = nLang;
483 0 : xHyphenator = xHyphen;
484 0 : pHyphWrapper = pWrapper;
485 :
486 : uno::Reference< linguistic2::XHyphenatedWord > xHyphWord( pHyphWrapper ?
487 0 : pHyphWrapper->GetLast() : NULL, uno::UNO_QUERY );
488 : DBG_ASSERT( xHyphWord.is(), "hyphenation result missing" );
489 0 : if (xHyphWord.is())
490 : {
491 : DBG_ASSERT( aActWord == String( xHyphWord->getWord() ), "word mismatch" );
492 : DBG_ASSERT( nActLanguage == LanguageTag( xHyphWord->getLocale() ).getLanguageType(), "language mismatch" );
493 0 : nMaxHyphenationPos = xHyphWord->getHyphenationPos();
494 : }
495 :
496 0 : InitControls_Impl();
497 0 : m_pWordEdit->GrabFocus();
498 :
499 0 : m_pLeftBtn->SetClickHdl( LINK( this, SvxHyphenWordDialog, Left_Impl ) );
500 0 : m_pRightBtn->SetClickHdl( LINK( this, SvxHyphenWordDialog, Right_Impl ) );
501 0 : m_pOkBtn->SetClickHdl( LINK( this, SvxHyphenWordDialog, CutHdl_Impl ) );
502 0 : m_pContBtn->SetClickHdl( LINK( this, SvxHyphenWordDialog, ContinueHdl_Impl ) );
503 0 : m_pDelBtn->SetClickHdl( LINK( this, SvxHyphenWordDialog, DeleteHdl_Impl ) );
504 0 : m_pHyphAll->SetClickHdl( LINK( this, SvxHyphenWordDialog, HyphenateAllHdl_Impl ) );
505 0 : m_pCloseBtn->SetClickHdl( LINK( this, SvxHyphenWordDialog, CancelHdl_Impl ) );
506 0 : m_pWordEdit->SetGetFocusHdl( LINK( this, SvxHyphenWordDialog, GetFocusHdl_Impl ) );
507 :
508 0 : SetWindowTitle( nLang );
509 :
510 : // disable controls if service is not available
511 0 : if (!xHyphenator.is())
512 0 : Enable( sal_False );
513 0 : }
514 :
515 :
516 0 : SvxHyphenWordDialog::~SvxHyphenWordDialog()
517 : {
518 0 : }
519 :
520 :
521 0 : void SvxHyphenWordDialog::SetWindowTitle( LanguageType nLang )
522 : {
523 0 : String aLangStr( SvtLanguageTable::GetLanguageString( nLang ) );
524 0 : String aTmp( aLabel );
525 0 : aTmp.AppendAscii( RTL_CONSTASCII_STRINGPARAM( " (" ) );
526 0 : aTmp.Append( aLangStr );
527 0 : aTmp.Append( sal_Unicode( ')' ) );
528 0 : SetText( aTmp );
529 0 : }
530 :
531 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|