xref: /aoo42x/main/sw/source/core/txtnode/atrfld.cxx (revision 36375bf3)
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 #include "fldbas.hxx"          // fuer FieldType
28 #include <fmtfld.hxx>
29 #include <txtfld.hxx>
30 #include <txtannotationfld.hxx>
31 #include <docufld.hxx>
32 #include <doc.hxx>
33 
34 #include "reffld.hxx"
35 #include "ddefld.hxx"
36 #include "usrfld.hxx"
37 #include "expfld.hxx"
38 #include "swfont.hxx"       // fuer GetFldsColor
39 #include "ndtxt.hxx"        // SwTxtNode
40 #include "calc.hxx"         // Update fuer UserFields
41 #include "hints.hxx"
42 #include <IDocumentFieldsAccess.hxx>
43 #include <fieldhint.hxx>
44 #include <svl/smplhint.hxx>
45 
46 TYPEINIT3( SwFmtFld, SfxPoolItem, SwClient,SfxBroadcaster)
47 TYPEINIT1(SwFmtFldHint, SfxHint);
48 
49 /****************************************************************************
50  *
51  *  class SwFmtFld
52  *
53  ****************************************************************************/
54 
55 // constructor for default item in attribute-pool
56 SwFmtFld::SwFmtFld( sal_uInt16 nWhich )
57     : SfxPoolItem( nWhich )
58     , SwClient()
59     , SfxBroadcaster()
60     , mpField( NULL )
61     , mpTxtFld( NULL )
62 {
63 }
64 
65 SwFmtFld::SwFmtFld( const SwField &rFld )
66     : SfxPoolItem( RES_TXTATR_FIELD )
67     , SwClient( rFld.GetTyp() )
68     , SfxBroadcaster()
69     , mpField( rFld.CopyField() )
70     , mpTxtFld( NULL )
71 {
72     if ( GetField()->GetTyp()->Which() == RES_INPUTFLD )
73     {
74         // input field in-place editing
75         SetWhich( RES_TXTATR_INPUTFIELD );
76         dynamic_cast<SwInputField*>(GetField())->SetFmtFld( *this );
77     }
78     else if ( GetField()->GetTyp()->Which() == RES_POSTITFLD )
79     {
80         // text annotation field
81         SetWhich( RES_TXTATR_ANNOTATION );
82     }
83 }
84 
85 // #i24434#
86 // Since Items are used in ItemPool and in default constructed ItemSets with
87 // full pool range, all items need to be clonable. Thus, this one needed to be
88 // corrected
89 SwFmtFld::SwFmtFld( const SwFmtFld& rAttr )
90     : SfxPoolItem( RES_TXTATR_FIELD )
91     , SwClient()
92     , SfxBroadcaster()
93     , mpField( NULL )
94     , mpTxtFld( NULL )
95 {
96     if ( rAttr.GetField() )
97     {
98         rAttr.GetField()->GetTyp()->Add(this);
99         mpField = rAttr.GetField()->CopyField();
100         if ( GetField()->GetTyp()->Which() == RES_INPUTFLD )
101         {
102             // input field in-place editing
103             SetWhich( RES_TXTATR_INPUTFIELD );
104             dynamic_cast<SwInputField*>(GetField())->SetFmtFld( *this );
105         }
106         else if ( GetField()->GetTyp()->Which() == RES_POSTITFLD )
107         {
108             // text annotation field
109             SetWhich( RES_TXTATR_ANNOTATION );
110         }
111     }
112 }
113 
114 SwFmtFld::~SwFmtFld()
115 {
116 	SwFieldType* pType = mpField ? mpField->GetTyp() : 0;
117 
118 	if (pType && pType->Which() == RES_DBFLD)
119 		pType = 0;	// DB-Feldtypen zerstoeren sich selbst
120 
121 	Broadcast( SwFmtFldHint( this, SWFMTFLD_REMOVED ) );
122 	delete mpField;
123 
124 	// bei einige FeldTypen muessen wir den FeldTypen noch loeschen
125 	if( pType && pType->IsLastDepend() )
126 	{
127 		sal_Bool bDel = sal_False;
128 		switch( pType->Which() )
129 		{
130 		case RES_USERFLD:
131 			bDel = ((SwUserFieldType*)pType)->IsDeleted();
132 			break;
133 
134 		case RES_SETEXPFLD:
135 			bDel = ((SwSetExpFieldType*)pType)->IsDeleted();
136 			break;
137 
138 		case RES_DDEFLD:
139 			bDel = ((SwDDEFieldType*)pType)->IsDeleted();
140 			break;
141 		}
142 
143 		if( bDel )
144 		{
145 			// vorm loeschen erstmal austragen
146 			pType->Remove( this );
147 			delete pType;
148 		}
149 	}
150 }
151 
152 void SwFmtFld::RegisterToFieldType( SwFieldType& rType )
153 {
154     rType.Add(this);
155 }
156 
157 
158 // #111840#
159 void SwFmtFld::SetField(SwField * _pField)
160 {
161     if (NULL != mpField)
162         delete mpField;
163 
164     mpField = _pField;
165     if ( GetField()->GetTyp()->Which() == RES_INPUTFLD )
166     {
167         dynamic_cast<SwInputField* >(GetField())->SetFmtFld( *this );
168     }
169     Broadcast( SwFmtFldHint( this, SWFMTFLD_CHANGED ) );
170 }
171 
172 void SwFmtFld::SetTxtFld( SwTxtFld& rTxtFld )
173 {
174     mpTxtFld = &rTxtFld;
175 }
176 
177 void SwFmtFld::ClearTxtFld()
178 {
179     mpTxtFld = NULL;
180 }
181 
182 int SwFmtFld::operator==( const SfxPoolItem& rAttr ) const
183 {
184 	ASSERT( SfxPoolItem::operator==( rAttr ), "keine gleichen Attribute" );
185     return ( ( mpField && ((SwFmtFld&)rAttr).GetField()
186                && mpField->GetTyp() == ((SwFmtFld&)rAttr).GetField()->GetTyp()
187                && mpField->GetFormat() == ((SwFmtFld&)rAttr).GetField()->GetFormat() ) )
188              || ( !mpField && !((SwFmtFld&)rAttr).GetField() );
189 }
190 
191 SfxPoolItem* SwFmtFld::Clone( SfxItemPool* ) const
192 {
193 	return new SwFmtFld( *this );
194 }
195 
196 void SwFmtFld::SwClientNotify( const SwModify&, const SfxHint& rHint )
197 {
198     if( !mpTxtFld )
199         return;
200 
201     const SwFieldHint* pHint = dynamic_cast<const SwFieldHint*>( &rHint );
202     if ( pHint )
203     {
204         // replace field content by text
205         SwPaM* pPaM = pHint->GetPaM();
206         SwDoc* pDoc = pPaM->GetDoc();
207         const SwTxtNode& rTxtNode = mpTxtFld->GetTxtNode();
208         pPaM->GetPoint()->nNode = rTxtNode;
209         pPaM->GetPoint()->nContent.Assign( (SwTxtNode*)&rTxtNode, *mpTxtFld->GetStart() );
210 
211         String const aEntry( GetField()->ExpandField( pDoc->IsClipBoard() ) );
212         pPaM->SetMark();
213         pPaM->Move( fnMoveForward );
214         pDoc->DeleteRange( *pPaM );
215         pDoc->InsertString( *pPaM, aEntry );
216     }
217 }
218 
219 void SwFmtFld::Modify( const SfxPoolItem* pOld, const SfxPoolItem* pNew )
220 {
221     if ( mpTxtFld == NULL )
222         return;
223 
224     if( pNew != NULL
225         && pNew->Which() == RES_OBJECTDYING )
226     {
227         // don't do anything, especially not expand!
228         return;
229     }
230 
231     SwTxtNode* pTxtNd = (SwTxtNode*) &mpTxtFld->GetTxtNode();
232     ASSERT( pTxtNd, "wo ist denn mein Node?" );
233     if ( pNew )
234     {
235         switch (pNew->Which())
236         {
237         case RES_TXTATR_FLDCHG:
238             // "Farbe hat sich geaendert !"
239             // this, this fuer "nur Painten"
240             pTxtNd->ModifyNotification( this, this );
241             return;
242 
243         case RES_REFMARKFLD_UPDATE:
244             // GetReferenz-Felder aktualisieren
245             if ( RES_GETREFFLD == GetField()->GetTyp()->Which() )
246             {
247                 dynamic_cast<SwGetRefField*>(GetField())->UpdateField( mpTxtFld );
248             }
249             break;
250 
251         case RES_DOCPOS_UPDATE:
252             // Je nach DocPos aktualisieren (SwTxtFrm::Modify())
253             pTxtNd->ModifyNotification( pNew, this );
254             return;
255 
256         case RES_ATTRSET_CHG:
257             case RES_FMT_CHG:
258             pTxtNd->ModifyNotification( pOld, pNew );
259             return;
260 
261             default:
262             break;
263         }
264     }
265 
266     switch (GetField()->GetTyp()->Which())
267     {
268     case RES_HIDDENPARAFLD:
269         if ( !pOld || RES_HIDDENPARA_PRINT != pOld->Which() )
270             break;
271     case RES_DBSETNUMBERFLD:
272     case RES_DBNUMSETFLD:
273     case RES_DBNEXTSETFLD:
274     case RES_DBNAMEFLD:
275         pTxtNd->ModifyNotification( 0, pNew );
276         return;
277     }
278 
279     if ( RES_USERFLD == GetField()->GetTyp()->Which() )
280     {
281         SwUserFieldType* pType = (SwUserFieldType*) GetField()->GetTyp();
282         if ( !pType->IsValid() )
283         {
284             SwCalc aCalc( *pTxtNd->GetDoc() );
285             pType->GetValue( aCalc );
286         }
287     }
288 
289     const bool bForceNotify = (pOld == NULL) && (pNew == NULL);
290     mpTxtFld->ExpandTxtFld( bForceNotify );
291 }
292 
293 sal_Bool SwFmtFld::GetInfo( SfxPoolItem& rInfo ) const
294 {
295 	const SwTxtNode* pTxtNd;
296 	if( RES_AUTOFMT_DOCNODE != rInfo.Which() ||
297 		!mpTxtFld || 0 == ( pTxtNd = mpTxtFld->GetpTxtNode() ) ||
298 		&pTxtNd->GetNodes() != ((SwAutoFmtGetDocNode&)rInfo).pNodes )
299 		return sal_True;
300 
301 	((SwAutoFmtGetDocNode&)rInfo).pCntntNode = pTxtNd;
302 	return sal_False;
303 }
304 
305 
306 bool SwFmtFld::IsFldInDoc() const
307 {
308     return mpTxtFld != NULL
309            && mpTxtFld->IsFldInDoc();
310 }
311 
312 sal_Bool SwFmtFld::IsProtect() const
313 {
314     return mpTxtFld != NULL
315            && mpTxtFld->GetpTxtNode() != NULL
316            && mpTxtFld->GetpTxtNode()->IsProtect();
317 }
318 
319 
320 
321 
322 SwTxtFld::SwTxtFld(
323     SwFmtFld & rAttr,
324     xub_StrLen const nStartPos,
325     const bool bIsClipboardDoc )
326     : SwTxtAttr( rAttr, nStartPos )
327     , m_aExpand( rAttr.GetField()->ExpandField( bIsClipboardDoc ) )
328     , m_pTxtNode( NULL )
329 {
330     rAttr.SetTxtFld( *this );
331     SetHasDummyChar(true);
332 }
333 
334 SwTxtFld::~SwTxtFld( )
335 {
336     SwFmtFld & rFmtFld( static_cast<SwFmtFld &>(GetAttr()) );
337     if ( this == rFmtFld.GetTxtFld() )
338     {
339         rFmtFld.ClearTxtFld();
340     }
341 }
342 
343 
344 bool SwTxtFld::IsFldInDoc() const
345 {
346     return GetpTxtNode() != NULL
347            && GetpTxtNode()->GetNodes().IsDocNodes();
348 }
349 
350 void SwTxtFld::ExpandTxtFld( const bool bForceNotify ) const
351 {
352     ASSERT( m_pTxtNode, "SwTxtFld: where is my TxtNode?" );
353 
354     const SwField* pFld = GetFmtFld().GetField();
355     const XubString aNewExpand( pFld->ExpandField(m_pTxtNode->GetDoc()->IsClipBoard()) );
356 
357     if ( !bForceNotify &&
358          aNewExpand == m_aExpand )
359     {
360         // Bei Seitennummernfeldern
361         const sal_uInt16 nWhich = pFld->GetTyp()->Which();
362         if ( RES_CHAPTERFLD != nWhich
363              && RES_PAGENUMBERFLD != nWhich
364              && RES_REFPAGEGETFLD != nWhich
365              // Page count fields to not use aExpand during formatting,
366              // therefore an invalidation of the text frame has to be triggered even if aNewExpand == aExpand:
367              && ( RES_DOCSTATFLD != nWhich || DS_PAGE != static_cast<const SwDocStatField*>(pFld)->GetSubType() )
368              && ( RES_GETEXPFLD != nWhich || ((SwGetExpField*)pFld)->IsInBodyTxt() ) )
369         {
370             if( m_pTxtNode->CalcHiddenParaField() )
371             {
372                 m_pTxtNode->ModifyNotification( 0, 0 );
373             }
374             return;
375         }
376     }
377 
378     m_aExpand = aNewExpand;
379 
380     const_cast<SwTxtFld*>(this)->NotifyContentChange( const_cast<SwFmtFld&>(GetFmtFld()) );
381 }
382 
383 
384 void SwTxtFld::CopyTxtFld( SwTxtFld *pDest ) const
385 {
386     ASSERT( m_pTxtNode, "SwTxtFld: where is my TxtNode?" );
387     ASSERT( pDest->m_pTxtNode, "SwTxtFld: where is pDest's TxtNode?" );
388 
389     IDocumentFieldsAccess* pIDFA = m_pTxtNode->getIDocumentFieldsAccess();
390     IDocumentFieldsAccess* pDestIDFA = pDest->m_pTxtNode->getIDocumentFieldsAccess();
391 
392     SwFmtFld& rDestFmtFld = (SwFmtFld&)pDest->GetFmtFld();
393     const sal_uInt16 nFldWhich = rDestFmtFld.GetField()->GetTyp()->Which();
394 
395     if( pIDFA != pDestIDFA )
396     {
397         // Die Hints stehen in unterschiedlichen Dokumenten,
398         // der Feldtyp muss im neuen Dokument angemeldet werden.
399         // Z.B: Kopieren ins ClipBoard.
400         SwFieldType* pFldType;
401         if( nFldWhich != RES_DBFLD
402             && nFldWhich != RES_USERFLD
403             && nFldWhich != RES_SETEXPFLD
404             && nFldWhich != RES_DDEFLD
405             && RES_AUTHORITY != nFldWhich )
406         {
407             pFldType = pDestIDFA->GetSysFldType( nFldWhich );
408         }
409         else
410         {
411             pFldType = pDestIDFA->InsertFldType( *rDestFmtFld.GetField()->GetTyp() );
412         }
413 
414         // Sonderbehandlung fuer DDE-Felder
415         if( RES_DDEFLD == nFldWhich )
416         {
417             if( rDestFmtFld.GetTxtFld() )
418             {
419                 ((SwDDEFieldType*)rDestFmtFld.GetField()->GetTyp())->DecRefCnt();
420             }
421             ((SwDDEFieldType*)pFldType)->IncRefCnt();
422         }
423 
424         ASSERT( pFldType, "unbekannter FieldType" );
425         pFldType->Add( &rDestFmtFld );          // ummelden
426         rDestFmtFld.GetField()->ChgTyp( pFldType );
427     }
428 
429     // Expressionfelder Updaten
430     if( nFldWhich == RES_SETEXPFLD
431         || nFldWhich == RES_GETEXPFLD
432         || nFldWhich == RES_HIDDENTXTFLD )
433     {
434         SwTxtFld* pFld = (SwTxtFld*)this;
435         pDestIDFA->UpdateExpFlds( pFld, true );
436     }
437     // Tabellenfelder auf externe Darstellung
438     else if( RES_TABLEFLD == nFldWhich
439              && ((SwTblField*)rDestFmtFld.GetField())->IsIntrnlName() )
440     {
441         // erzeuge aus der internen (fuer CORE) die externe (fuer UI) Formel
442         const SwTableNode* pTblNd = m_pTxtNode->FindTableNode();
443         if( pTblNd )		// steht in einer Tabelle
444             ((SwTblField*)rDestFmtFld.GetField())->PtrToBoxNm( &pTblNd->GetTable() );
445     }
446 }
447 
448 
449 void SwTxtFld::NotifyContentChange(SwFmtFld& rFmtFld)
450 {
451     //if not in undo section notify the change
452     if (m_pTxtNode && m_pTxtNode->GetNodes().IsDocNodes())
453     {
454         m_pTxtNode->ModifyNotification(0, &rFmtFld);
455     }
456 }
457 
458 
459 /*static*/
460 void SwTxtFld::GetPamForTxtFld(
461     const SwTxtFld& rTxtFld,
462     boost::shared_ptr< SwPaM >& rPamForTxtFld )
463 {
464     if ( rTxtFld.GetpTxtNode() == NULL )
465     {
466         ASSERT( false, "<SwTxtFld::GetPamForField> - missing <SwTxtNode>" );
467         return;
468     }
469 
470     const SwTxtNode& rTxtNode = rTxtFld.GetTxtNode();
471 
472     rPamForTxtFld.reset( new SwPaM( rTxtNode,
473                                     ( (rTxtFld.End() != NULL) ? *(rTxtFld.End()) : ( *(rTxtFld.GetStart()) + 1 ) ),
474                                     rTxtNode,
475                                     *(rTxtFld.GetStart()) ) );
476 
477 }
478 
479 
480 /*static*/
481 void SwTxtFld::DeleteTxtFld( const SwTxtFld& rTxtFld )
482 {
483     if ( rTxtFld.GetpTxtNode() != NULL )
484     {
485         boost::shared_ptr< SwPaM > pPamForTxtFld;
486         GetPamForTxtFld( rTxtFld, pPamForTxtFld );
487         if ( pPamForTxtFld.get() != NULL )
488         {
489             rTxtFld.GetTxtNode().GetDoc()->DeleteAndJoin( *pPamForTxtFld );
490         }
491     }
492 }
493 
494 
495 
496 // input field in-place editing
497 SwTxtInputFld::SwTxtInputFld(
498     SwFmtFld & rAttr,
499     xub_StrLen const nStart,
500     xub_StrLen const nEnd,
501     const bool bIsClipboardDoc )
502 
503     : SwTxtFld( rAttr, nStart, bIsClipboardDoc )
504     , m_nEnd( nEnd )
505     , m_bLockNotifyContentChange( false )
506 {
507     SetHasDummyChar( false );
508     SetHasContent( true );
509 
510     SetDontExpand( true );
511     SetLockExpandFlag( true );
512     SetDontExpandStartAttr( true );
513 
514     SetNesting( true );
515 }
516 
517 SwTxtInputFld::~SwTxtInputFld()
518 {
519 }
520 
521 xub_StrLen* SwTxtInputFld::GetEnd()
522 {
523     return &m_nEnd;
524 }
525 
526 
527 void SwTxtInputFld::LockNotifyContentChange()
528 {
529     m_bLockNotifyContentChange = true;
530 }
531 
532 
533 void SwTxtInputFld::UnlockNotifyContentChange()
534 {
535     m_bLockNotifyContentChange = false;
536 }
537 
538 
539 void SwTxtInputFld::NotifyContentChange( SwFmtFld& rFmtFld )
540 {
541     if ( !m_bLockNotifyContentChange )
542     {
543         LockNotifyContentChange();
544 
545         SwTxtFld::NotifyContentChange( rFmtFld );
546         UpdateTextNodeContent( GetFieldContent() );
547 
548         UnlockNotifyContentChange();
549     }
550 }
551 
552 const String SwTxtInputFld::GetFieldContent() const
553 {
554     return GetFmtFld().GetField()->ExpandField(false);
555 }
556 
557 void SwTxtInputFld::UpdateFieldContent()
558 {
559     if ( IsFldInDoc()
560          && (*GetStart()) != (*End()) )
561     {
562         ASSERT( (*End()) - (*GetStart()) >= 2,
563                 "<SwTxtInputFld::UpdateFieldContent()> - Are CH_TXT_ATR_INPUTFIELDSTART and/or CH_TXT_ATR_INPUTFIELDEND missing?" );
564         // skip CH_TXT_ATR_INPUTFIELDSTART character
565         const xub_StrLen nIdx = (*GetStart()) + 1;
566         // skip CH_TXT_ATR_INPUTFIELDEND character
567         const xub_StrLen nLen = static_cast<xub_StrLen>(std::max( 0, ( (*End()) - 1 - nIdx ) ));
568         const String aNewFieldContent = GetTxtNode().GetExpandTxt( nIdx, nLen );
569 
570         const SwInputField* pInputFld = dynamic_cast<const SwInputField*>(GetFmtFld().GetField());
571         ASSERT( pInputFld != NULL,
572                 "<SwTxtInputFld::GetContent()> - Missing <SwInputFld> instance!" );
573         if ( pInputFld != NULL )
574         {
575             const_cast<SwInputField*>(pInputFld)->applyFieldContent( aNewFieldContent );
576         }
577     }
578 }
579 
580 void SwTxtInputFld::UpdateTextNodeContent( const String& rNewContent )
581 {
582     if ( !IsFldInDoc() )
583     {
584         ASSERT( false, "<SwTxtInputFld::UpdateTextNodeContent(..)> - misusage as Input Field is not in document content." );
585         return;
586     }
587 
588     ASSERT( (*End()) - (*GetStart()) >= 2,
589             "<SwTxtInputFld::UpdateTextNodeContent(..)> - Are CH_TXT_ATR_INPUTFIELDSTART and/or CH_TXT_ATR_INPUTFIELDEND missing?" );
590     // skip CH_TXT_ATR_INPUTFIELDSTART character
591     const xub_StrLen nIdx = (*GetStart()) + 1;
592     // skip CH_TXT_ATR_INPUTFIELDEND character
593     const xub_StrLen nDelLen = static_cast<xub_StrLen>(std::max( 0, ( (*End()) - 1 - nIdx ) ));
594     SwIndex aIdx( &GetTxtNode(), nIdx );
595     GetTxtNode().ReplaceText( aIdx, nDelLen, rNewContent );
596 }
597 
598 
599 
600 
601 // text annotation field
602 SwTxtAnnotationFld::SwTxtAnnotationFld(
603     SwFmtFld & rAttr,
604     xub_StrLen const nStart,
605     const bool bIsClipboardDoc )
606     : SwTxtFld( rAttr, nStart, bIsClipboardDoc )
607 {
608 }
609 
610 SwTxtAnnotationFld::~SwTxtAnnotationFld()
611 {
612 }
613 
614 
615 ::sw::mark::IMark* SwTxtAnnotationFld::GetAnnotationMark(
616     SwDoc* pDoc ) const
617 {
618     const SwPostItField* pPostItField = dynamic_cast<const SwPostItField*>(GetFmtFld().GetField());
619     ASSERT( pPostItField != NULL, "<SwTxtAnnotationFld::GetAnnotationMark()> - field missing" );
620     if ( pPostItField == NULL )
621     {
622         return NULL;
623     }
624 
625     if ( pDoc == NULL )
626     {
627         pDoc = static_cast<const SwPostItFieldType*>(pPostItField->GetTyp())->GetDoc();
628     }
629     ASSERT( pDoc != NULL, "<SwTxtAnnotationFld::GetAnnotationMark()> - missing document" );
630     if ( pDoc == NULL )
631     {
632         return NULL;
633     }
634 
635     IDocumentMarkAccess* pMarksAccess = pDoc->getIDocumentMarkAccess();
636     IDocumentMarkAccess::const_iterator_t pMark = pMarksAccess->findAnnotationMark( pPostItField->GetName() );
637     return pMark != pMarksAccess->getAnnotationMarksEnd()
638            ? pMark->get()
639            : NULL;
640 }
641 
642