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