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