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 <vcl/msgbox.hxx>
31 :
32 : #define HYPH_POS_CHAR '='
33 : #define CONTINUE_HYPH USHRT_MAX
34 :
35 : #define CUR_HYPH_POS_CHAR '-'
36 :
37 : using namespace css;
38 :
39 0 : HyphenEdit::HyphenEdit(vcl::Window* pParent)
40 0 : : Edit(pParent, WB_LEFT|WB_VCENTER|WB_BORDER|WB_3DLOOK|WB_TABSTOP)
41 : {
42 0 : }
43 :
44 0 : extern "C" SAL_DLLPUBLIC_EXPORT vcl::Window* SAL_CALL makeHyphenEdit(vcl::Window *pParent, VclBuilder::stringmap &)
45 : {
46 0 : return new HyphenEdit(pParent);
47 : }
48 :
49 0 : void HyphenEdit::KeyInput( const KeyEvent& rKEvt )
50 : {
51 0 : sal_uInt16 nCode = rKEvt.GetKeyCode().GetCode();
52 :
53 0 : switch ( nCode )
54 : {
55 : case KEY_LEFT:
56 0 : static_cast<SvxHyphenWordDialog*>( GetParentDialog() )->SelLeft();
57 0 : break;
58 :
59 : case KEY_RIGHT:
60 0 : static_cast<SvxHyphenWordDialog*>( GetParentDialog() )->SelRight();
61 0 : break;
62 :
63 : case KEY_TAB:
64 : case KEY_ESCAPE:
65 : case KEY_RETURN:
66 0 : Edit::KeyInput(rKEvt);
67 0 : break;
68 : default:
69 0 : Control::KeyInput( rKEvt ); // pass on to the dialog
70 0 : break;
71 : }
72 0 : }
73 :
74 :
75 0 : void SvxHyphenWordDialog::EnableLRBtn_Impl()
76 : {
77 0 : OUString aTxt( aEditWord );
78 0 : sal_Int32 nLen = aTxt.getLength();
79 :
80 0 : m_pRightBtn->Disable();
81 0 : for ( sal_Int32 i = nOldPos + 2; i < nLen; ++i )
82 : {
83 0 : if ( aTxt[ i ] == sal_Unicode( HYPH_POS_CHAR ) )
84 : {
85 0 : m_pRightBtn->Enable();
86 0 : break;
87 : }
88 : }
89 :
90 : DBG_ASSERT(nOldPos < aTxt.getLength(), "nOldPos out of range");
91 0 : if (nOldPos >= aTxt.getLength())
92 0 : nOldPos = aTxt.getLength() - 1;
93 0 : m_pLeftBtn->Disable();
94 0 : for ( sal_Int32 i = nOldPos; i-- > 0; )
95 : {
96 0 : if ( aTxt[ i ] == sal_Unicode( HYPH_POS_CHAR ) )
97 : {
98 0 : m_pLeftBtn->Enable();
99 0 : break;
100 : }
101 0 : }
102 0 : }
103 :
104 :
105 0 : OUString 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 be 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 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 : OUString aTxt;
137 : DBG_ASSERT(rxPossHyph.is(), "missing possible hyphens");
138 0 : if (rxPossHyph.is())
139 : {
140 : DBG_ASSERT( aActWord == rxPossHyph->getWord(), "word mismatch" );
141 :
142 0 : aTxt = 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 : sal_Int32 nIdx = -1;
152 0 : sal_Int32 nPos = 0, nPos1 = 0;
153 0 : if (nLen)
154 : {
155 0 : sal_Int32 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.indexOf( sal_Unicode( HYPH_POS_CHAR ), nStart );
164 :
165 0 : if (nPos == -1)
166 0 : break;
167 : else
168 : {
169 0 : nIdx = nPos;
170 0 : nStart = nPos + 1;
171 : }
172 : }
173 : }
174 : }
175 : DBG_ASSERT(nIdx != -1, "no usable hyphenation position");
176 :
177 : // 1) remove all not usable hyphenation positions from the end of the string
178 0 : nPos = nIdx == -1 ? 0 : nIdx + 1;
179 0 : nPos1 = nPos; //save for later use in 2) below
180 0 : const OUString aTmp( sal_Unicode( HYPH_POS_CHAR ) );
181 0 : const OUString aEmpty;
182 0 : while (nPos != -1)
183 : {
184 0 : nPos++;
185 0 : aTxt = aTxt.replaceFirst( aTmp, aEmpty, &nPos);
186 : }
187 :
188 : // 2) remove all hyphenation positions from the start that are not considered by the core
189 0 : const OUString aSearchRange( aTxt.copy( 0, nPos1 ) );
190 0 : sal_Int32 nPos2 = aSearchRange.lastIndexOf( '-' ); // the '-' position the core will use by default
191 0 : if (nPos2 != -1 )
192 : {
193 0 : OUString aLeft( aSearchRange.copy( 0, nPos2 ) );
194 0 : nPos = 0;
195 0 : while (nPos != -1)
196 : {
197 0 : nPos++;
198 0 : aLeft = aLeft.replaceFirst( aTmp, aEmpty, &nPos );
199 0 : if (nPos != -1)
200 0 : ++nHyphenationPositionsOffset;
201 : }
202 0 : aTxt = aTxt.replaceAt( 0, nPos2, aLeft );
203 0 : }
204 : }
205 0 : return aTxt;
206 : }
207 :
208 :
209 0 : void SvxHyphenWordDialog::InitControls_Impl()
210 : {
211 0 : xPossHyph = NULL;
212 0 : if (xHyphenator.is())
213 : {
214 0 : lang::Locale aLocale( LanguageTag::convertToLocale(nActLanguage) );
215 0 : xPossHyph = xHyphenator->createPossibleHyphens( aActWord, aLocale,
216 0 : uno::Sequence< beans::PropertyValue >() );
217 0 : if (xPossHyph.is())
218 0 : aEditWord = EraseUnusableHyphens_Impl( xPossHyph, nMaxHyphenationPos );
219 : }
220 0 : m_pWordEdit->SetText( aEditWord );
221 :
222 0 : nOldPos = aEditWord.getLength();
223 0 : SelLeft();
224 0 : EnableLRBtn_Impl();
225 0 : }
226 :
227 :
228 0 : void SvxHyphenWordDialog::ContinueHyph_Impl( sal_uInt16 nInsPos )
229 : {
230 0 : if ( nInsPos != CONTINUE_HYPH && xPossHyph.is())
231 : {
232 0 : if (nInsPos)
233 : {
234 0 : OUString aTmp( aEditWord );
235 : DBG_ASSERT(nInsPos <= aTmp.getLength() - 2, "wrong hyphen position");
236 :
237 0 : sal_Int16 nIdxPos = -1;
238 0 : for (sal_Int32 i = 0; i <= nInsPos; ++i)
239 : {
240 0 : if (HYPH_POS_CHAR == aTmp[ i ])
241 0 : nIdxPos++;
242 : }
243 : // take the possible hyphenation positions that got removed from the
244 : // start of the wor dinot account:
245 0 : nIdxPos += nHyphenationPositionsOffset;
246 :
247 0 : uno::Sequence< sal_Int16 > aSeq = xPossHyph->getHyphenationPositions();
248 0 : sal_Int32 nLen = aSeq.getLength();
249 : DBG_ASSERT(nLen, "empty sequence");
250 : DBG_ASSERT(0 <= nIdxPos && nIdxPos < nLen, "index out of range");
251 0 : if (nLen && 0 <= nIdxPos && nIdxPos < nLen)
252 : {
253 0 : nInsPos = aSeq.getConstArray()[ nIdxPos ];
254 0 : pHyphWrapper->InsertHyphen( nInsPos );
255 0 : }
256 : }
257 : else
258 : {
259 : //! calling with 0 as argument will remove hyphens!
260 0 : pHyphWrapper->InsertHyphen( nInsPos );
261 : }
262 : }
263 :
264 0 : if ( pHyphWrapper->FindSpellError() )
265 : {
266 0 : uno::Reference< linguistic2::XHyphenatedWord > xHyphWord( pHyphWrapper->GetLast(), uno::UNO_QUERY );
267 :
268 : // adapt actual word and language to new found hyphenation result
269 0 : if(xHyphWord.is())
270 : {
271 0 : aActWord = xHyphWord->getWord();
272 0 : nActLanguage = LanguageTag( xHyphWord->getLocale() ).getLanguageType();
273 0 : nMaxHyphenationPos = xHyphWord->getHyphenationPos();
274 0 : InitControls_Impl();
275 0 : SetWindowTitle( nActLanguage );
276 0 : }
277 : }
278 : else
279 0 : EndDialog( RET_OK );
280 0 : }
281 :
282 :
283 0 : sal_uInt16 SvxHyphenWordDialog::GetHyphIndex_Impl()
284 : {
285 0 : sal_uInt16 nPos = 0;
286 0 : OUString aTxt( m_pWordEdit->GetText() );
287 :
288 0 : for ( sal_Int32 i=0; i < aTxt.getLength(); ++i )
289 : {
290 0 : sal_Unicode cChar = aTxt[ i ];
291 0 : if ( cChar == CUR_HYPH_POS_CHAR )
292 0 : break;
293 0 : if ( cChar != HYPH_POS_CHAR )
294 0 : nPos++;
295 : }
296 0 : return nPos;
297 : }
298 :
299 :
300 0 : void SvxHyphenWordDialog::SelLeft()
301 : {
302 : DBG_ASSERT( nOldPos > 0, "invalid hyphenation position" );
303 0 : if (nOldPos > 0)
304 : {
305 0 : OUString aTxt( aEditWord );
306 0 : for( sal_Int32 i = nOldPos - 1; i > 0; --i )
307 : {
308 : DBG_ASSERT(i <= aTxt.getLength(), "index out of range");
309 0 : if (aTxt[ i ] == sal_Unicode( HYPH_POS_CHAR ))
310 : {
311 0 : aTxt = aTxt.replaceAt( i, 1, OUString( CUR_HYPH_POS_CHAR ) );
312 :
313 0 : nOldPos = i;
314 0 : m_pWordEdit->SetText( aTxt );
315 0 : m_pWordEdit->GrabFocus();
316 0 : m_pWordEdit->SetSelection( Selection( i, i + 1 ) );
317 0 : break;
318 : }
319 : }
320 0 : nHyphPos = GetHyphIndex_Impl();
321 0 : EnableLRBtn_Impl();
322 : }
323 0 : }
324 :
325 :
326 0 : void SvxHyphenWordDialog::SelRight()
327 : {
328 0 : OUString aTxt( aEditWord );
329 0 : for ( sal_Int32 i = nOldPos + 1; i < aTxt.getLength(); ++i )
330 : {
331 0 : if (aTxt[ i ] == sal_Unicode( HYPH_POS_CHAR ))
332 : {
333 0 : aTxt = aTxt.replaceAt( i, 1, OUString( CUR_HYPH_POS_CHAR ) );
334 :
335 0 : nOldPos = i;
336 0 : m_pWordEdit->SetText( aTxt );
337 0 : m_pWordEdit->GrabFocus();
338 0 : m_pWordEdit->SetSelection( Selection( i, i + 1 ) );
339 0 : break;
340 : }
341 : }
342 0 : nHyphPos = GetHyphIndex_Impl();
343 0 : EnableLRBtn_Impl();
344 0 : }
345 :
346 :
347 0 : IMPL_LINK_NOARG(SvxHyphenWordDialog, CutHdl_Impl)
348 : {
349 0 : if( !bBusy )
350 : {
351 0 : bBusy = true;
352 0 : ContinueHyph_Impl( /*nHyphPos*/nOldPos );
353 0 : bBusy = false;
354 : }
355 0 : return 0;
356 : }
357 :
358 :
359 0 : IMPL_LINK( SvxHyphenWordDialog, HyphenateAllHdl_Impl, Button *, EMPTYARG /*pButton*/ )
360 : {
361 0 : if( !bBusy )
362 : {
363 : try
364 : {
365 0 : uno::Reference< linguistic2::XLinguProperties > xProp( SvxGetLinguPropertySet() );
366 :
367 0 : xProp->setIsHyphAuto( sal_True );
368 :
369 0 : bBusy = true;
370 0 : ContinueHyph_Impl( /*nHyphPos*/nOldPos );
371 0 : bBusy = false;
372 :
373 0 : xProp->setIsHyphAuto( sal_False );
374 : }
375 0 : catch (uno::Exception &e)
376 : {
377 : (void) e;
378 : DBG_ASSERT( false, "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 = true;
390 0 : ContinueHyph_Impl();
391 0 : bBusy = 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 = true;
402 0 : ContinueHyph_Impl( CONTINUE_HYPH );
403 0 : bBusy = 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 = true;
414 0 : pHyphWrapper->SpellEnd();
415 0 : EndDialog( RET_CANCEL );
416 0 : bBusy = 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 = true;
427 0 : SelLeft();
428 0 : bBusy = 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 = true;
439 0 : SelRight();
440 0 : bBusy = 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 OUString &rWord, LanguageType nLang,
457 : vcl::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(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 == 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( false );
513 0 : }
514 :
515 :
516 0 : SvxHyphenWordDialog::~SvxHyphenWordDialog()
517 : {
518 0 : }
519 :
520 :
521 0 : void SvxHyphenWordDialog::SetWindowTitle( LanguageType nLang )
522 : {
523 0 : OUString aLangStr( SvtLanguageTable::GetLanguageString( nLang ) );
524 0 : OUString aTmp( aLabel );
525 0 : aTmp += " (";
526 0 : aTmp += aLangStr;
527 0 : aTmp += ")";
528 0 : SetText( aTmp );
529 0 : }
530 :
531 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|