1 /**************************************************************
2 *
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 *
20 *************************************************************/
21
22
23
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_sw.hxx"
26
27
28 #include <ctype.h>
29
30 #ifndef _COM_SUN_STAR_I18N_SCRIPTTYPE_HDL_
31 #include <com/sun/star/i18n/ScriptType.hdl>
32 #endif
33 #include <hintids.hxx> // CH_TXTATR
34 #include <errhdl.hxx> // ASSERT
35 #include <SwPortionHandler.hxx>
36 #include <txtcfg.hxx>
37 #include <porlay.hxx>
38 #include <inftxt.hxx>
39 #include <guess.hxx> // SwTxtGuess, Zeilenumbruch
40 #include <porglue.hxx>
41 #include <portab.hxx> // pLastTab->
42 #include <porfld.hxx> // SwFldPortion
43 #include <wrong.hxx>
44 #include <viewsh.hxx>
45 #include <IDocumentSettingAccess.hxx>
46 #include <viewopt.hxx> // SwViewOptions
47
48 #include <IMark.hxx>
49 #include <pam.hxx>
50 #include <doc.hxx>
51 #include <xmloff/odffields.hxx>
52 #include <vcl/pdfextoutdevdata.hxx>
53
54 #if OSL_DEBUG_LEVEL > 1
55 const sal_Char *GetLangName( const MSHORT nLang );
56 #endif
57
58 using namespace ::sw::mark;
59 using namespace ::com::sun::star;
60 using namespace ::com::sun::star::i18n::ScriptType;
61
62 /*************************************************************************
63 * lcl_AddSpace
64 * Returns for how many characters an extra space has to be added
65 * (for justified alignment).
66 *************************************************************************/
67
lcl_AddSpace(const SwTxtSizeInfo & rInf,const XubString * pStr,const SwLinePortion & rPor)68 sal_uInt16 lcl_AddSpace( const SwTxtSizeInfo &rInf, const XubString* pStr,
69 const SwLinePortion& rPor )
70 {
71 xub_StrLen nPos, nEnd;
72 const SwScriptInfo* pSI = 0;
73
74 if ( pStr )
75 {
76 // passing a string means we are inside a field
77 nPos = 0;
78 nEnd = pStr->Len();
79 }
80 else
81 {
82 nPos = rInf.GetIdx();
83 nEnd = rInf.GetIdx() + rPor.GetLen();
84 pStr = &rInf.GetTxt();
85 pSI = &((SwParaPortion*)rInf.GetParaPortion())->GetScriptInfo();
86 }
87
88 sal_uInt16 nCnt = 0;
89 sal_uInt8 nScript = 0;
90
91 // If portion consists of Asian characters and language is not
92 // Korean, we add extra space to each character.
93 // first we get the script type
94 if ( pSI )
95 nScript = pSI->ScriptType( nPos );
96 else if ( pBreakIt->GetBreakIter().is() )
97 nScript = (sal_uInt8)pBreakIt->GetBreakIter()->getScriptType( *pStr, nPos );
98
99 // Note: rInf.GetIdx() can differ from nPos,
100 // e.g., when rPor is a field portion. nPos referes to the string passed
101 // to the function, rInf.GetIdx() referes to the original string.
102
103 // We try to find out which justification mode is required. This is done by
104 // evaluating the script type and the language attribute set for this portion
105
106 // Asian Justification: Each character get some extra space
107 if ( nEnd > nPos && ASIAN == nScript )
108 {
109 LanguageType aLang =
110 rInf.GetTxtFrm()->GetTxtNode()->GetLang( rInf.GetIdx(), 1, nScript );
111
112 if ( LANGUAGE_KOREAN != aLang && LANGUAGE_KOREAN_JOHAB != aLang )
113 {
114 const SwLinePortion* pPor = rPor.GetPortion();
115 if ( pPor && ( pPor->IsKernPortion() ||
116 pPor->IsControlCharPortion() ||
117 pPor->IsPostItsPortion() ) )
118 pPor = pPor->GetPortion();
119
120 nCnt += nEnd - nPos;
121
122 if ( !pPor || pPor->IsHolePortion() || pPor->InFixMargGrp() ||
123 pPor->IsBreakPortion() )
124 --nCnt;
125
126 return nCnt;
127 }
128 }
129
130 // Kashida Justification: Insert Kashidas
131 if ( nEnd > nPos && pSI && COMPLEX == nScript )
132 {
133 if ( SwScriptInfo::IsArabicText( *pStr, nPos, nEnd - nPos ) && pSI->CountKashida() )
134 {
135 const sal_uInt16 nKashRes = pSI->KashidaJustify( 0, 0, nPos, nEnd - nPos );
136 // i60591: need to check result of KashidaJustify
137 // determine if kashida justification is applicable
138 if( nKashRes != STRING_LEN )
139 return nKashRes;
140 }
141 }
142
143 // Thai Justification: Each character cell gets some extra space
144 if ( nEnd > nPos && COMPLEX == nScript )
145 {
146 LanguageType aLang =
147 rInf.GetTxtFrm()->GetTxtNode()->GetLang( rInf.GetIdx(), 1, nScript );
148
149 if ( LANGUAGE_THAI == aLang )
150 {
151 nCnt = SwScriptInfo::ThaiJustify( *pStr, 0, 0, nPos, nEnd - nPos );
152
153 const SwLinePortion* pPor = rPor.GetPortion();
154 if ( pPor && ( pPor->IsKernPortion() ||
155 pPor->IsControlCharPortion() ||
156 pPor->IsPostItsPortion() ) )
157 pPor = pPor->GetPortion();
158
159 if ( nCnt && ( ! pPor || pPor->IsHolePortion() || pPor->InFixMargGrp() ) )
160 --nCnt;
161
162 return nCnt;
163 }
164 }
165
166 // Here starts the good old "Look for blanks and add space to them" part.
167 // Note: We do not want to add space to an isolated latin blank in front
168 // of some complex characters in RTL environment
169 const sal_Bool bDoNotAddSpace =
170 LATIN == nScript && ( nEnd == nPos + 1 ) && pSI &&
171 ( i18n::ScriptType::COMPLEX ==
172 pSI->ScriptType( nPos + 1 ) ) &&
173 rInf.GetTxtFrm() && rInf.GetTxtFrm()->IsRightToLeft();
174
175 if ( bDoNotAddSpace )
176 return nCnt;
177
178 for ( ; nPos < nEnd; ++nPos )
179 {
180 if( CH_BLANK == pStr->GetChar( nPos ) )
181 ++nCnt;
182 }
183
184 // We still have to examine the next character:
185 // If the next character is ASIAN and not KOREAN we have
186 // to add an extra space
187 // nPos referes to the original string, even if a field string has
188 // been passed to this function
189 nPos = rInf.GetIdx() + rPor.GetLen();
190 if ( nPos < rInf.GetTxt().Len() )
191 {
192 sal_uInt8 nNextScript = 0;
193 const SwLinePortion* pPor = rPor.GetPortion();
194 if ( pPor && pPor->IsKernPortion() )
195 pPor = pPor->GetPortion();
196
197 if ( ! pBreakIt->GetBreakIter().is() || ! pPor || pPor->InFixMargGrp() )
198 return nCnt;
199
200 // next character is inside a field?
201 if ( CH_TXTATR_BREAKWORD == rInf.GetChar( nPos ) && pPor->InExpGrp() )
202 {
203 sal_Bool bOldOnWin = rInf.OnWin();
204 ((SwTxtSizeInfo &)rInf).SetOnWin( sal_False );
205
206 XubString aStr( aEmptyStr );
207 pPor->GetExpTxt( rInf, aStr );
208 ((SwTxtSizeInfo &)rInf).SetOnWin( bOldOnWin );
209
210 nNextScript = (sal_uInt8)pBreakIt->GetBreakIter()->getScriptType( aStr, 0 );
211 }
212 else
213 nNextScript = (sal_uInt8)pBreakIt->GetBreakIter()->getScriptType( rInf.GetTxt(), nPos );
214
215 if( ASIAN == nNextScript )
216 {
217 LanguageType aLang =
218 rInf.GetTxtFrm()->GetTxtNode()->GetLang( nPos, 1, nNextScript );
219
220 if ( LANGUAGE_KOREAN != aLang && LANGUAGE_KOREAN_JOHAB != aLang )
221 ++nCnt;
222 }
223 }
224
225 return nCnt;
226 }
227
228 /*************************************************************************
229 * class SwTxtPortion
230 *************************************************************************/
231
SwTxtPortion(const SwLinePortion & rPortion)232 SwTxtPortion::SwTxtPortion( const SwLinePortion &rPortion )
233 : SwLinePortion( rPortion )
234 {
235 SetWhichPor( POR_TXT );
236 }
237
238 /*************************************************************************
239 * SwTxtPortion::BreakCut()
240 *************************************************************************/
241
BreakCut(SwTxtFormatInfo & rInf,const SwTxtGuess & rGuess)242 void SwTxtPortion::BreakCut( SwTxtFormatInfo &rInf, const SwTxtGuess &rGuess )
243 {
244 // Das Wort/Zeichen ist groesser als die Zeile
245 // Sonderfall Nr.1: Das Wort ist groesser als die Zeile
246 // Wir kappen...
247 const KSHORT nLineWidth = (KSHORT)(rInf.Width() - rInf.X());
248 xub_StrLen nLen = rGuess.CutPos() - rInf.GetIdx();
249 if( nLen )
250 {
251 // special case: guess does not always provide the correct
252 // width, only in common cases.
253 if ( !rGuess.BreakWidth() )
254 {
255 rInf.SetLen( nLen );
256 SetLen( nLen );
257 CalcTxtSize( rInf );
258
259 // changing these values requires also changing them in
260 // guess.cxx
261 KSHORT nItalic = 0;
262 if( ITALIC_NONE != rInf.GetFont()->GetItalic() && !rInf.NotEOL() )
263 {
264 nItalic = Height() / 12;
265 }
266 Width( Width() + nItalic );
267 }
268 else
269 {
270 Width( rGuess.BreakWidth() );
271 SetLen( nLen );
272 }
273 }
274 // special case: first character does not fit to line
275 else if ( rGuess.CutPos() == rInf.GetLineStart() )
276 {
277 SetLen( 1 );
278 Width( nLineWidth );
279 }
280 else
281 {
282 SetLen( 0 );
283 Width( 0 );
284 }
285 }
286
287 /*************************************************************************
288 * SwTxtPortion::BreakUnderflow()
289 *************************************************************************/
290
BreakUnderflow(SwTxtFormatInfo & rInf)291 void SwTxtPortion::BreakUnderflow( SwTxtFormatInfo &rInf )
292 {
293 Truncate();
294 Height( 0 );
295 Width( 0 );
296 SetLen( 0 );
297 SetAscent( 0 );
298 rInf.SetUnderFlow( this );
299 }
300
301 /*************************************************************************
302 * SwTxtPortion::_Format()
303 *************************************************************************/
304
lcl_HasContent(const SwFldPortion & rFld,SwTxtFormatInfo & rInf)305 sal_Bool lcl_HasContent( const SwFldPortion& rFld, SwTxtFormatInfo &rInf )
306 {
307 String aTxt;
308 return rFld.GetExpTxt( rInf, aTxt ) && aTxt.Len();
309 }
310
_Format(SwTxtFormatInfo & rInf)311 sal_Bool SwTxtPortion::_Format( SwTxtFormatInfo &rInf )
312 {
313 // 5744: wenn nur der Trennstrich nicht mehr passt,
314 // muss trotzdem das Wort umgebrochen werden, ansonsten return sal_True!
315 if( rInf.IsUnderFlow() && rInf.GetSoftHyphPos() )
316 {
317 // soft hyphen portion has triggered an underflow event because
318 // of an alternative spelling position
319 sal_Bool bFull = sal_False;
320 const sal_Bool bHyph = rInf.ChgHyph( sal_True );
321 if( rInf.IsHyphenate() )
322 {
323 SwTxtGuess aGuess;
324 // check for alternative spelling left from the soft hyphen
325 // this should usually be true but
326 aGuess.AlternativeSpelling( rInf, rInf.GetSoftHyphPos() - 1 );
327 bFull = CreateHyphen( rInf, aGuess );
328 ASSERT( bFull, "Problem with hyphenation!!!" );
329 }
330 rInf.ChgHyph( bHyph );
331 rInf.SetSoftHyphPos( 0 );
332 return bFull;
333 }
334
335 SwTxtGuess aGuess;
336 const sal_Bool bFull = !aGuess.Guess( *this, rInf, Height() );
337
338 // these are the possible cases:
339 // A Portion fits to current line
340 // B Portion does not fit to current line but a possible line break
341 // within the portion has been found by the break iterator, 2 subcases
342 // B1 break is hyphen
343 // B2 break is word end
344 // C Portion does not fit to current line and no possible line break
345 // has been found by break iterator, 2 subcases:
346 // C1 break iterator found a possible line break in portion before us
347 // ==> this break is used (underflow)
348 // C2 break iterator does not found a possible line break at all:
349 // ==> line break
350
351 // case A: line not yet full
352 if ( !bFull )
353 {
354 Width( aGuess.BreakWidth() );
355 // Vorsicht !
356 if( !InExpGrp() || InFldGrp() )
357 SetLen( rInf.GetLen() );
358
359 short nKern = rInf.GetFont()->CheckKerning();
360 if( nKern > 0 && rInf.Width() < rInf.X() + Width() + nKern )
361 {
362 nKern = (short)(rInf.Width() - rInf.X() - Width() - 1);
363 if( nKern < 0 )
364 nKern = 0;
365 }
366 if( nKern )
367 new SwKernPortion( *this, nKern );
368 }
369 // special case: hanging portion
370 else if( bFull && aGuess.GetHangingPortion() )
371 {
372 Width( aGuess.BreakWidth() );
373 SetLen( aGuess.BreakPos() - rInf.GetIdx() );
374 Insert( aGuess.GetHangingPortion() );
375 aGuess.GetHangingPortion()->SetAscent( GetAscent() );
376 aGuess.ClearHangingPortion();
377 }
378 // breakPos >= index
379 else if ( aGuess.BreakPos() >= rInf.GetIdx() && aGuess.BreakPos() != STRING_LEN )
380 {
381 // case B1
382 if( aGuess.HyphWord().is() && aGuess.BreakPos() > rInf.GetLineStart()
383 && ( aGuess.BreakPos() > rInf.GetIdx() ||
384 ( rInf.GetLast() && ! rInf.GetLast()->IsFlyPortion() ) ) )
385 {
386 CreateHyphen( rInf, aGuess );
387 if ( rInf.GetFly() )
388 rInf.GetRoot()->SetMidHyph( sal_True );
389 else
390 rInf.GetRoot()->SetEndHyph( sal_True );
391 }
392 // case C1
393 // - Footnote portions with fake line start (i.e., not at beginning of line)
394 // should keep together with the text portion. (Note: no keep together
395 // with only footnote portions.
396 // - TabPortions not at beginning of line should keep together with the
397 // text portion, if they are not followed by a blank
398 // (work around different definition of tab stop character - breaking or
399 // non breaking character - in compatibility mode)
400 else if ( ( IsFtnPortion() && rInf.IsFakeLineStart() &&
401 // --> OD 2010-01-29 #b6921213#
402 rInf.IsOtherThanFtnInside() ) ||
403 // <--
404 ( rInf.GetLast() &&
405 rInf.GetTxtFrm()->GetTxtNode()->getIDocumentSettingAccess()->get(IDocumentSettingAccess::TAB_COMPAT) &&
406 rInf.GetLast()->InTabGrp() &&
407 rInf.GetLineStart() + rInf.GetLast()->GetLen() < rInf.GetIdx() &&
408 aGuess.BreakPos() == rInf.GetIdx() &&
409 CH_BLANK != rInf.GetChar( rInf.GetIdx() ) &&
410 0x3000 != rInf.GetChar( rInf.GetIdx() ) ) )
411 BreakUnderflow( rInf );
412 // case B2
413 else if( rInf.GetIdx() > rInf.GetLineStart() ||
414 aGuess.BreakPos() > rInf.GetIdx() ||
415 // this is weird: during formatting the follow of a field
416 // the values rInf.GetIdx and rInf.GetLineStart are replaced
417 // IsFakeLineStart indicates GetIdx > GetLineStart
418 rInf.IsFakeLineStart() ||
419 rInf.GetFly() ||
420 rInf.IsFirstMulti() ||
421 ( rInf.GetLast() &&
422 ( rInf.GetLast()->IsFlyPortion() ||
423 ( rInf.GetLast()->InFldGrp() &&
424 ! rInf.GetLast()->InNumberGrp() &&
425 ! rInf.GetLast()->IsErgoSumPortion() &&
426 lcl_HasContent(*((SwFldPortion*)rInf.GetLast()),rInf ) ) ) ) )
427 {
428 if ( rInf.X() + aGuess.BreakWidth() <= rInf.Width() )
429 Width( aGuess.BreakWidth() );
430 else
431 // this actually should not happen
432 Width( KSHORT(rInf.Width() - rInf.X()) );
433
434 SetLen( aGuess.BreakPos() - rInf.GetIdx() );
435
436 ASSERT( aGuess.BreakStart() >= aGuess.FieldDiff(),
437 "Trouble with expanded field portions during line break" );
438 const xub_StrLen nRealStart = aGuess.BreakStart() - aGuess.FieldDiff();
439 if( aGuess.BreakPos() < nRealStart && !InExpGrp() )
440 {
441 SwHolePortion *pNew = new SwHolePortion( *this );
442 pNew->SetLen( nRealStart - aGuess.BreakPos() );
443 Insert( pNew );
444 }
445 }
446 else // case C2, last exit
447 BreakCut( rInf, aGuess );
448 }
449 // breakPos < index or no breakpos at all
450 else
451 {
452 sal_Bool bFirstPor = rInf.GetLineStart() == rInf.GetIdx();
453 if( aGuess.BreakPos() != STRING_LEN &&
454 aGuess.BreakPos() != rInf.GetLineStart() &&
455 ( !bFirstPor || rInf.GetFly() || rInf.GetLast()->IsFlyPortion() ||
456 rInf.IsFirstMulti() ) &&
457 ( !rInf.GetLast()->IsBlankPortion() || ((SwBlankPortion*)
458 rInf.GetLast())->MayUnderFlow( rInf, rInf.GetIdx()-1, sal_True )))
459 { // case C1 (former BreakUnderflow())
460 BreakUnderflow( rInf );
461 }
462 else
463 // case C2, last exit
464 BreakCut( rInf, aGuess );
465 }
466
467 return bFull;
468 }
469
470 /*************************************************************************
471 * virtual SwTxtPortion::Format()
472 *************************************************************************/
473
474
475
Format(SwTxtFormatInfo & rInf)476 sal_Bool SwTxtPortion::Format( SwTxtFormatInfo &rInf )
477 {
478 #if OSL_DEBUG_LEVEL > 1
479 const XubString aDbgTxt( rInf.GetTxt().Copy( rInf.GetIdx(), rInf.GetLen() ) );
480 #endif
481
482 if( rInf.X() > rInf.Width() || (!GetLen() && !InExpGrp()) )
483 {
484 Height( 0 );
485 Width( 0 );
486 SetLen( 0 );
487 SetAscent( 0 );
488 SetPortion( NULL ); // ????
489 return sal_True;
490 }
491
492 ASSERT( rInf.RealWidth() || (rInf.X() == rInf.Width()),
493 "SwTxtPortion::Format: missing real width" );
494 ASSERT( Height(), "SwTxtPortion::Format: missing height" );
495
496 return _Format( rInf );
497 }
498
499 /*************************************************************************
500 * virtual SwTxtPortion::FormatEOL()
501 *************************************************************************/
502
503 // Format end of line
504 // 5083: Es kann schon manchmal unguenstige Faelle geben...
505 // "vom {Nikolaus}", Nikolaus bricht um "vom " wird im Blocksatz
506 // zu "vom" und " ", wobei der Glue expandiert wird, statt in die
507 // MarginPortion aufzugehen.
508 // rInf.nIdx steht auf dem naechsten Wort, nIdx-1 ist der letzte
509 // Buchstabe der Portion.
510
511
512
FormatEOL(SwTxtFormatInfo & rInf)513 void SwTxtPortion::FormatEOL( SwTxtFormatInfo &rInf )
514 {
515 if( ( !GetPortion() || ( GetPortion()->IsKernPortion() &&
516 !GetPortion()->GetPortion() ) ) && GetLen() &&
517 rInf.GetIdx() < rInf.GetTxt().Len() &&
518 1 < rInf.GetIdx() && ' ' == rInf.GetChar( rInf.GetIdx() - 1 )
519 && !rInf.GetLast()->IsHolePortion() )
520 {
521 // calculate number of blanks
522 xub_StrLen nX = rInf.GetIdx() - 1;
523 sal_uInt16 nHoleLen = 1;
524 while( nX && nHoleLen < GetLen() && CH_BLANK == rInf.GetChar( --nX ) )
525 nHoleLen++;
526
527 // Erst uns einstellen und dann Inserten, weil wir ja auch ein
528 // SwLineLayout sein koennten.
529 KSHORT nBlankSize;
530 if( nHoleLen == GetLen() )
531 nBlankSize = Width();
532 else
533 nBlankSize = nHoleLen * rInf.GetTxtSize( ' ' ).Width();
534 Width( Width() - nBlankSize );
535 rInf.X( rInf.X() - nBlankSize );
536 SetLen( GetLen() - nHoleLen );
537 SwLinePortion *pHole = new SwHolePortion( *this );
538 ( (SwHolePortion *)pHole )->SetBlankWidth( nBlankSize );
539 ( (SwHolePortion *)pHole )->SetLen( nHoleLen );
540 Insert( pHole );
541 }
542 }
543
544 /*************************************************************************
545 * virtual SwTxtPortion::GetCrsrOfst()
546 *************************************************************************/
547
548
549
GetCrsrOfst(const KSHORT nOfst) const550 xub_StrLen SwTxtPortion::GetCrsrOfst( const KSHORT nOfst ) const
551 {
552 ASSERT( !this, "SwTxtPortion::GetCrsrOfst: don't use this method!" );
553 return SwLinePortion::GetCrsrOfst( nOfst );
554 }
555
556 /*************************************************************************
557 * virtual SwTxtPortion::GetTxtSize()
558 *************************************************************************/
559 // Das GetTxtSize() geht davon aus, dass die eigene Laenge korrekt ist
560
GetTxtSize(const SwTxtSizeInfo & rInf) const561 SwPosSize SwTxtPortion::GetTxtSize( const SwTxtSizeInfo &rInf ) const
562 {
563 return rInf.GetTxtSize();
564 }
565
566 /*************************************************************************
567 * virtual SwTxtPortion::Paint()
568 *************************************************************************/
569
570
571
Paint(const SwTxtPaintInfo & rInf) const572 void SwTxtPortion::Paint( const SwTxtPaintInfo &rInf ) const
573 {
574 if (rInf.OnWin() && 1==rInf.GetLen() && CH_TXT_ATR_FIELDEND==rInf.GetTxt().GetChar(rInf.GetIdx()))
575 {
576 rInf.DrawBackBrush( *this );
577 const XubString aTxt = XubString::CreateFromAscii(CH_TXT_ATR_SUBST_FIELDEND);
578 rInf.DrawText( aTxt, *this, 0, aTxt.Len(), false );
579 }
580 else if (rInf.OnWin() && 1==rInf.GetLen() && CH_TXT_ATR_FIELDSTART==rInf.GetTxt().GetChar(rInf.GetIdx()))
581 {
582 rInf.DrawBackBrush( *this );
583 const XubString aTxt = XubString::CreateFromAscii(CH_TXT_ATR_SUBST_FIELDSTART);
584 rInf.DrawText( aTxt, *this, 0, aTxt.Len(), false );
585 }
586 else if( GetLen() )
587 {
588 rInf.DrawBackBrush( *this );
589
590 // do we have to repaint a post it portion?
591 if( rInf.OnWin() && pPortion && !pPortion->Width() )
592 pPortion->PrePaint( rInf, this );
593
594 const SwWrongList *pWrongList = rInf.GetpWrongList();
595 const SwWrongList *pGrammarCheckList = rInf.GetGrammarCheckList();
596 // SMARTTAGS
597 const SwWrongList *pSmarttags = rInf.GetSmartTags();
598
599 const bool bWrong = 0 != pWrongList;
600 const bool bGrammarCheck = 0 != pGrammarCheckList;
601 const bool bSmartTags = 0 != pSmarttags;
602
603 if ( bWrong || bSmartTags || bGrammarCheck )
604 rInf.DrawMarkedText( *this, rInf.GetLen(), sal_False, bWrong, bSmartTags, bGrammarCheck );
605 else
606 rInf.DrawText( *this, rInf.GetLen(), sal_False );
607 }
608 }
609
610 /*************************************************************************
611 * virtual SwTxtPortion::GetExpTxt()
612 *************************************************************************/
613
614
615
GetExpTxt(const SwTxtSizeInfo &,XubString &) const616 sal_Bool SwTxtPortion::GetExpTxt( const SwTxtSizeInfo &, XubString & ) const
617 {
618 return sal_False;
619 }
620
621 /*************************************************************************
622 * xub_StrLen SwTxtPortion::GetSpaceCnt()
623 * long SwTxtPortion::CalcSpacing()
624 * sind fuer den Blocksatz zustaendig und ermitteln die Anzahl der Blanks
625 * und den daraus resultierenden zusaetzlichen Zwischenraum
626 *************************************************************************/
627
GetSpaceCnt(const SwTxtSizeInfo & rInf,xub_StrLen & rCharCnt) const628 xub_StrLen SwTxtPortion::GetSpaceCnt( const SwTxtSizeInfo &rInf,
629 xub_StrLen& rCharCnt ) const
630 {
631 xub_StrLen nCnt = 0;
632 xub_StrLen nPos = 0;
633 if ( InExpGrp() )
634 {
635 if( !IsBlankPortion() && !InNumberGrp() && !IsCombinedPortion() )
636 {
637 // Bei OnWin() wird anstatt eines Leerstrings gern mal ein Blank
638 // zurueckgeliefert, das koennen wir hier aber gar nicht gebrauchen
639 sal_Bool bOldOnWin = rInf.OnWin();
640 ((SwTxtSizeInfo &)rInf).SetOnWin( sal_False );
641
642 XubString aStr( aEmptyStr );
643 GetExpTxt( rInf, aStr );
644 ((SwTxtSizeInfo &)rInf).SetOnWin( bOldOnWin );
645
646 nCnt = nCnt + lcl_AddSpace( rInf, &aStr, *this );
647 nPos = aStr.Len();
648 }
649 }
650 else if( !IsDropPortion() )
651 {
652 nCnt = nCnt + lcl_AddSpace( rInf, 0, *this );
653 nPos = GetLen();
654 }
655 rCharCnt = rCharCnt + nPos;
656 return nCnt;
657 }
658
CalcSpacing(long nSpaceAdd,const SwTxtSizeInfo & rInf) const659 long SwTxtPortion::CalcSpacing( long nSpaceAdd, const SwTxtSizeInfo &rInf ) const
660 {
661 xub_StrLen nCnt = 0;
662
663 if ( InExpGrp() )
664 {
665 if( !IsBlankPortion() && !InNumberGrp() && !IsCombinedPortion() )
666 {
667 // Bei OnWin() wird anstatt eines Leerstrings gern mal ein Blank
668 // zurueckgeliefert, das koennen wir hier aber gar nicht gebrauchen
669 sal_Bool bOldOnWin = rInf.OnWin();
670 ((SwTxtSizeInfo &)rInf).SetOnWin( sal_False );
671
672 XubString aStr( aEmptyStr );
673 GetExpTxt( rInf, aStr );
674 ((SwTxtSizeInfo &)rInf).SetOnWin( bOldOnWin );
675 if( nSpaceAdd > 0 )
676 nCnt = nCnt + lcl_AddSpace( rInf, &aStr, *this );
677 else
678 {
679 nSpaceAdd = -nSpaceAdd;
680 nCnt = aStr.Len();
681 }
682 }
683 }
684 else if( !IsDropPortion() )
685 {
686 if( nSpaceAdd > 0 )
687 nCnt = nCnt + lcl_AddSpace( rInf, 0, *this );
688 else
689 {
690 nSpaceAdd = -nSpaceAdd;
691 nCnt = GetLen();
692 SwLinePortion* pPor = GetPortion();
693
694 // we do not want an extra space in front of margin portions
695 if ( nCnt )
696 {
697 while ( pPor && !pPor->Width() && ! pPor->IsHolePortion() )
698 pPor = pPor->GetPortion();
699
700 if ( !pPor || pPor->InFixMargGrp() || pPor->IsHolePortion() )
701 --nCnt;
702 }
703 }
704 }
705
706 return nCnt * nSpaceAdd / SPACING_PRECISION_FACTOR;
707 }
708
709 /*************************************************************************
710 * virtual SwTxtPortion::HandlePortion()
711 *************************************************************************/
712
HandlePortion(SwPortionHandler & rPH) const713 void SwTxtPortion::HandlePortion( SwPortionHandler& rPH ) const
714 {
715 rPH.Text( GetLen(), GetWhichPor() );
716 }
717
718
SwTxtInputFldPortion()719 SwTxtInputFldPortion::SwTxtInputFldPortion()
720 : SwTxtPortion()
721 , mbContainsInputFieldStart( false )
722 , mbContainsInputFieldEnd( false )
723 {
724 SetWhichPor( POR_INPUTFLD );
725 }
726
727
Format(SwTxtFormatInfo & rInf)728 sal_Bool SwTxtInputFldPortion::Format( SwTxtFormatInfo &rInf )
729 {
730 mbContainsInputFieldStart =
731 rInf.GetChar( rInf.GetIdx() ) == CH_TXT_ATR_INPUTFIELDSTART;
732 mbContainsInputFieldEnd =
733 rInf.GetChar( rInf.GetIdx() + rInf.GetLen() - 1 ) == CH_TXT_ATR_INPUTFIELDEND;
734
735 sal_Bool bRet = sal_False;
736 if ( rInf.GetLen() == 1
737 && ( mbContainsInputFieldStart || mbContainsInputFieldEnd ) )
738 {
739 Width( 0 );
740 }
741 else
742 {
743 SwTxtSlot aFormatTxt( &rInf, this, true, true, 0 );
744 if ( rInf.GetLen() == 0 )
745 {
746 Width( 0 );
747 }
748 else
749 {
750 const xub_StrLen nFormerLineStart = rInf.GetLineStart();
751 if ( !mbContainsInputFieldStart )
752 {
753 rInf.SetLineStart( 0 );
754 }
755
756 bRet = SwTxtPortion::Format( rInf );
757
758 if ( mbContainsInputFieldEnd )
759 {
760 // adjust portion length accordingly, if complete text fits into the portion
761 if ( GetLen() == rInf.GetLen() )
762 {
763 SetLen( GetLen() + 1 );
764 }
765 }
766
767 if ( mbContainsInputFieldStart )
768 {
769 // adjust portion length accordingly
770 SetLen( GetLen() + 1 );
771 }
772 else
773 {
774 rInf.SetLineStart( nFormerLineStart );
775 }
776 }
777 }
778
779 return bRet;
780 }
781
Paint(const SwTxtPaintInfo & rInf) const782 void SwTxtInputFldPortion::Paint( const SwTxtPaintInfo &rInf ) const
783 {
784 if ( Width() )
785 {
786 rInf.DrawViewOpt( *this, POR_INPUTFLD );
787 static sal_Char sSpace = ' ';
788 SwTxtSlot aPaintTxt( &rInf, this, true, true,
789 ContainsOnlyDummyChars() ? &sSpace : 0 );
790 SwTxtPortion::Paint( rInf );
791 }
792 }
793
GetExpTxt(const SwTxtSizeInfo & rInf,XubString & rTxt) const794 sal_Bool SwTxtInputFldPortion::GetExpTxt( const SwTxtSizeInfo &rInf, XubString &rTxt ) const
795 {
796 xub_StrLen nIdx = rInf.GetIdx();
797 xub_StrLen nLen = rInf.GetLen();
798 if ( rInf.GetChar( rInf.GetIdx() ) == CH_TXT_ATR_INPUTFIELDSTART )
799 {
800 ++nIdx;
801 --nLen;
802 }
803 if ( rInf.GetChar( rInf.GetIdx() + rInf.GetLen() - 1 ) == CH_TXT_ATR_INPUTFIELDEND )
804 {
805 --nLen;
806 }
807 rTxt = rInf.GetTxt().Copy( nIdx, nLen );
808
809 return sal_True;
810 }
811
812
GetTxtSize(const SwTxtSizeInfo & rInf) const813 SwPosSize SwTxtInputFldPortion::GetTxtSize( const SwTxtSizeInfo &rInf ) const
814 {
815 SwTxtSlot aFormatTxt( &rInf, this, true, false, 0 );
816 if ( rInf.GetLen() == 0 )
817 {
818 return SwPosSize( 0, 0 );
819 }
820
821 return rInf.GetTxtSize();
822 }
823
824
GetViewWidth(const SwTxtSizeInfo & rInf) const825 KSHORT SwTxtInputFldPortion::GetViewWidth( const SwTxtSizeInfo &rInf ) const
826 {
827 if( !Width()
828 && ContainsOnlyDummyChars()
829 && !rInf.GetOpt().IsPagePreview()
830 && !rInf.GetOpt().IsReadonly()
831 && SwViewOption::IsFieldShadings() )
832 {
833 return rInf.GetTxtSize( ' ' ).Width();
834 }
835
836 return SwTxtPortion::GetViewWidth( rInf );
837 }
838
ContainsOnlyDummyChars() const839 bool SwTxtInputFldPortion::ContainsOnlyDummyChars() const
840 {
841 return GetLen() <= 2
842 && mbContainsInputFieldStart
843 && mbContainsInputFieldEnd;
844 }
845
846 /*************************************************************************
847 * class SwHolePortion
848 *************************************************************************/
849
850
851
SwHolePortion(const SwTxtPortion & rPor)852 SwHolePortion::SwHolePortion( const SwTxtPortion &rPor )
853 : nBlankWidth( 0 )
854 {
855 SetLen( 1 );
856 Height( rPor.Height() );
857 SetAscent( rPor.GetAscent() );
858 SetWhichPor( POR_HOLE );
859 }
860
Compress()861 SwLinePortion *SwHolePortion::Compress() { return this; }
862
863 /*************************************************************************
864 * virtual SwHolePortion::Paint()
865 *************************************************************************/
866
867
868
Paint(const SwTxtPaintInfo & rInf) const869 void SwHolePortion::Paint( const SwTxtPaintInfo &rInf ) const
870 {
871 if( !rInf.GetOut() )
872 return;
873
874 // #i16816# export stuff only needed for tagged pdf support
875 const vcl::PDFExtOutDevData* pPDFExt = dynamic_cast<const vcl::PDFExtOutDevData*>( rInf.GetOut()->GetExtOutDevData() );
876 if( !pPDFExt || !pPDFExt->GetIsExportTaggedPDF())
877 return;
878
879 // #i68503# the hole must have no decoration for a consistent visual appearance
880 const SwFont* pOrigFont = rInf.GetFont();
881 SwFont* pHoleFont = NULL;
882 SwFontSave* pFontSave = NULL;
883 if( pOrigFont->GetUnderline() != UNDERLINE_NONE
884 || pOrigFont->GetOverline() != UNDERLINE_NONE
885 || pOrigFont->GetStrikeout() != STRIKEOUT_NONE )
886 {
887 pHoleFont = new SwFont( *pOrigFont );
888 pHoleFont->SetUnderline( UNDERLINE_NONE );
889 pHoleFont->SetOverline( UNDERLINE_NONE );
890 pHoleFont->SetStrikeout( STRIKEOUT_NONE );
891 pFontSave = new SwFontSave( rInf, pHoleFont );
892 }
893
894 const XubString aTxt( ' ' );
895 rInf.DrawText( aTxt, *this, 0, 1, false );
896
897 delete pFontSave;
898 delete pHoleFont;
899 }
900
901 /*************************************************************************
902 * virtual SwHolePortion::Format()
903 *************************************************************************/
904
905
906
Format(SwTxtFormatInfo & rInf)907 sal_Bool SwHolePortion::Format( SwTxtFormatInfo &rInf )
908 {
909 return rInf.IsFull() || rInf.X() >= rInf.Width();
910 }
911
912 /*************************************************************************
913 * virtual SwHolePortion::HandlePortion()
914 *************************************************************************/
915
HandlePortion(SwPortionHandler & rPH) const916 void SwHolePortion::HandlePortion( SwPortionHandler& rPH ) const
917 {
918 rPH.Text( GetLen(), GetWhichPor() );
919 }
920
Paint(const SwTxtPaintInfo &) const921 void SwFieldMarkPortion::Paint( const SwTxtPaintInfo & /*rInf*/) const
922 {
923 // These shouldn't be painted!
924 //SwTxtPortion::Paint(rInf);
925 }
926
Format(SwTxtFormatInfo &)927 sal_Bool SwFieldMarkPortion::Format( SwTxtFormatInfo & )
928 {
929 sal_Bool ret=0;
930 Width(0);
931 return ret;
932 }
933
934 namespace {
getCurrentListIndex(IFieldmark * pBM,::rtl::OUString * io_pCurrentText=NULL)935 static sal_Int32 getCurrentListIndex( IFieldmark* pBM,
936 ::rtl::OUString* io_pCurrentText = NULL )
937 {
938 const IFieldmark::parameter_map_t* const pParameters = pBM->GetParameters();
939 sal_Int32 nCurrentIdx = 0;
940 const IFieldmark::parameter_map_t::const_iterator pResult = pParameters->find(::rtl::OUString::createFromAscii(ODF_FORMDROPDOWN_RESULT));
941 if(pResult != pParameters->end())
942 pResult->second >>= nCurrentIdx;
943 if(io_pCurrentText)
944 {
945 const IFieldmark::parameter_map_t::const_iterator pListEntries = pParameters->find(::rtl::OUString::createFromAscii(ODF_FORMDROPDOWN_LISTENTRY));
946 if(pListEntries != pParameters->end())
947 {
948 uno::Sequence< ::rtl::OUString > vListEntries;
949 pListEntries->second >>= vListEntries;
950 if(nCurrentIdx < vListEntries.getLength())
951 *io_pCurrentText = vListEntries[nCurrentIdx];
952 }
953 }
954 return nCurrentIdx;
955 }
956 }
957
958 //FIXME Fieldbk
Paint(const SwTxtPaintInfo & rInf) const959 void SwFieldFormPortion::Paint( const SwTxtPaintInfo& rInf ) const
960 {
961 SwTxtNode* pNd = const_cast<SwTxtNode*>(rInf.GetTxtFrm()->GetTxtNode());
962 const SwDoc *doc=pNd->GetDoc();
963 SwIndex aIndex( pNd, rInf.GetIdx() );
964 SwPosition aPosition(*pNd, aIndex);
965
966 IFieldmark* pBM = doc->getIDocumentMarkAccess( )->getFieldmarkFor( aPosition );
967
968 OSL_ENSURE( pBM,
969 "SwFieldFormPortion::Paint(..)"
970 " - Where is my form field bookmark???");
971
972 if ( pBM != NULL )
973 {
974 if ( pBM->GetFieldname( ).equalsAscii( ODF_FORMCHECKBOX ) )
975 { // a checkbox...
976 ICheckboxFieldmark* pCheckboxFm = dynamic_cast< ICheckboxFieldmark* >(pBM);
977 bool checked = pCheckboxFm->IsChecked();
978 rInf.DrawCheckBox(*this, checked);
979 }
980 else if ( pBM->GetFieldname( ).equalsAscii( ODF_FORMDROPDOWN ) )
981 { // a list...
982 rtl::OUString aTxt;
983 rInf.DrawViewOpt( *this, POR_FLD );
984 rInf.DrawText( aTxt, *this, 0, 0/*aTxt.getLength()*/, false );
985 }
986 else
987 {
988 assert(0); // unknown type...
989 }
990 }
991 }
992
Format(SwTxtFormatInfo & rInf)993 sal_Bool SwFieldFormPortion::Format( SwTxtFormatInfo & rInf )
994 {
995 sal_Bool ret = 0;
996 SwTxtNode *pNd = const_cast < SwTxtNode * >( rInf.GetTxtFrm( )->GetTxtNode( ) );
997 const SwDoc *doc = pNd->GetDoc( );
998 SwIndex aIndex( pNd, rInf.GetIdx( ) );
999 SwPosition aPosition( *pNd, aIndex );
1000 IFieldmark *pBM = doc->getIDocumentMarkAccess( )->getFieldmarkFor( aPosition );
1001 ASSERT( pBM != NULL, "Where is my form field bookmark???" );
1002 if ( pBM != NULL )
1003 {
1004 if ( pBM->GetFieldname( ).equalsAscii( ODF_FORMCHECKBOX ) )
1005 {
1006 Width( rInf.GetTxtHeight( ) );
1007 Height( rInf.GetTxtHeight( ) );
1008 SetAscent( rInf.GetAscent( ) );
1009 }
1010 else if ( pBM->GetFieldname( ).equalsAscii( ODF_FORMDROPDOWN ) )
1011 {
1012 ::rtl::OUString aTxt;
1013 getCurrentListIndex( pBM, &aTxt );
1014 SwPosSize aPosSize = rInf.GetTxtSize( aTxt );
1015 Width( aPosSize.Width( ) );
1016 Height( aPosSize.Height( ) );
1017 SetAscent( rInf.GetAscent( ) );
1018 }
1019 else
1020 {
1021 assert( 0 ); // unknown type...
1022 }
1023 }
1024 return ret;
1025 }
1026
1027
1028