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