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