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 <comphelper/string.hxx>
21 #include <utility>
22 
23 #include "eertfpar.hxx"
24 #include "impedit.hxx"
25 #include <svl/intitem.hxx>
26 #include <editeng/escapementitem.hxx>
27 #include <editeng/fhgtitem.hxx>
28 #include <editeng/fontitem.hxx>
29 #include <editeng/flditem.hxx>
30 #include <editeng/editeng.hxx>
31 
32 #include <svtools/rtftoken.h>
33 #include <svtools/htmltokn.h>
34 
35 using namespace com::sun::star;
36 
HtmlImportInfo(HtmlImportState eSt,SvParser<HtmlTokenId> * pPrsrs,const ESelection & rSel)37 HtmlImportInfo::HtmlImportInfo( HtmlImportState eSt, SvParser<HtmlTokenId>* pPrsrs, const ESelection& rSel )
38     : aSelection( rSel )
39 {
40     pParser     = pPrsrs;
41     eState      = eSt;
42     nToken      = HtmlTokenId::NONE;
43 }
44 
~HtmlImportInfo()45 HtmlImportInfo::~HtmlImportInfo()
46 {
47 }
48 
RtfImportInfo(RtfImportState eSt,SvParser<int> * pPrsrs,const ESelection & rSel)49 RtfImportInfo::RtfImportInfo( RtfImportState eSt, SvParser<int>* pPrsrs, const ESelection& rSel )
50     : aSelection( rSel )
51 {
52     pParser     = pPrsrs;
53     eState      = eSt;
54     nToken      = 0;
55     nTokenValue = 0;
56 }
57 
~RtfImportInfo()58 RtfImportInfo::~RtfImportInfo()
59 {
60 }
61 
62 static constexpr MapUnit gRTFMapUnit = MapUnit::MapTwip;
63 
EditRTFParser(SvStream & rIn,EditSelection aSel,SfxItemPool & rAttrPool,EditEngine * pEditEngine)64 EditRTFParser::EditRTFParser(
65     SvStream& rIn, EditSelection aSel, SfxItemPool& rAttrPool, EditEngine* pEditEngine) :
66     SvxRTFParser(rAttrPool, rIn),
67     aCurSel(std::move(aSel)),
68     mpEditEngine(pEditEngine),
69     nDefFont(0),
70     bLastActionInsertParaBreak(false)
71 {
72     SetInsPos(EditPosition(mpEditEngine, &aCurSel));
73 
74     // Convert the twips values ...
75     SetCalcValue(true);
76     SetChkStyleAttr(mpEditEngine->IsImportRTFStyleSheetsSet());
77     SetNewDoc(false);     // So that the Pool-Defaults are not overwritten...
78     aEditMapMode = MapMode(mpEditEngine->GetRefDevice()->GetMapMode().GetMapUnit());
79 }
80 
~EditRTFParser()81 EditRTFParser::~EditRTFParser()
82 {
83 }
84 
CallParser()85 SvParserState EditRTFParser::CallParser()
86 {
87     DBG_ASSERT( !aCurSel.HasRange(), "Selection for CallParser!" );
88     // Separate the part that is imported from the rest.
89     // This expression should be used for all imports.
90     // aStart1PaM: Last position before the imported content
91     // aEnd1PaM: First position after the imported content
92     // aStart2PaM: First position of the imported content
93     // aEnd2PaM: Last position of the imported content
94     EditPaM aStart1PaM( aCurSel.Min().GetNode(), aCurSel.Min().GetIndex() );
95     aCurSel = mpEditEngine->InsertParaBreak(aCurSel);
96     EditPaM aStart2PaM = aCurSel.Min();
97     // Useful or not?
98     aStart2PaM.GetNode()->GetContentAttribs().GetItems().ClearItem();
99     AddRTFDefaultValues( aStart2PaM, aStart2PaM );
100     EditPaM aEnd1PaM = mpEditEngine->InsertParaBreak(aCurSel.Max());
101     // aCurCel now points to the gap
102 
103     if (mpEditEngine->IsRtfImportHandlerSet())
104     {
105         RtfImportInfo aImportInfo(RtfImportState::Start, this, mpEditEngine->CreateESelection(aCurSel));
106         mpEditEngine->CallRtfImportHandler(aImportInfo);
107     }
108 
109     SvParserState _eState = SvxRTFParser::CallParser();
110 
111     if (mpEditEngine->IsRtfImportHandlerSet())
112     {
113         RtfImportInfo aImportInfo(RtfImportState::End, this, mpEditEngine->CreateESelection(aCurSel));
114         mpEditEngine->CallRtfImportHandler(aImportInfo);
115     }
116 
117     if (bLastActionInsertParaBreak)
118     {
119         ContentNode* pCurNode = aCurSel.Max().GetNode();
120         sal_Int32 nPara = mpEditEngine->GetEditDoc().GetPos(pCurNode);
121         ContentNode* pPrevNode = mpEditEngine->GetEditDoc().GetObject(nPara-1);
122         DBG_ASSERT( pPrevNode, "Invalid RTF-Document?!" );
123         EditSelection aSel;
124         aSel.Min() = EditPaM( pPrevNode, pPrevNode->Len() );
125         aSel.Max() = EditPaM( pCurNode, 0 );
126         aCurSel.Max() = mpEditEngine->DeleteSelection(aSel);
127     }
128     EditPaM aEnd2PaM( aCurSel.Max() );
129     //AddRTFDefaultValues( aStart2PaM, aEnd2PaM );
130     bool bOnlyOnePara = ( aEnd2PaM.GetNode() == aStart2PaM.GetNode() );
131     // Paste the chunk again ...
132     // Problem: Paragraph attributes may not possibly be taken over
133     // => Do Character attributes.
134 
135     bool bSpecialBackward = aStart1PaM.GetNode()->Len() == 0;
136     if ( bOnlyOnePara || aStart1PaM.GetNode()->Len() )
137         mpEditEngine->ParaAttribsToCharAttribs( aStart2PaM.GetNode() );
138     aCurSel.Min() = mpEditEngine->ConnectParagraphs(
139         aStart1PaM.GetNode(), aStart2PaM.GetNode(), bSpecialBackward );
140     bSpecialBackward = aEnd1PaM.GetNode()->Len() != 0;
141     // when bOnlyOnePara, then the node is gone on Connect.
142     if ( !bOnlyOnePara && aEnd1PaM.GetNode()->Len() )
143         mpEditEngine->ParaAttribsToCharAttribs( aEnd2PaM.GetNode() );
144     aCurSel.Max() = mpEditEngine->ConnectParagraphs(
145         ( bOnlyOnePara ? aStart1PaM.GetNode() : aEnd2PaM.GetNode() ),
146             aEnd1PaM.GetNode(), bSpecialBackward );
147 
148     return _eState;
149 }
150 
AddRTFDefaultValues(const EditPaM & rStart,const EditPaM & rEnd)151 void EditRTFParser::AddRTFDefaultValues( const EditPaM& rStart, const EditPaM& rEnd )
152 {
153     // Problem: DefFont and DefFontHeight
154     Size aSz( 12, 0 );
155     MapMode aPntMode( MapUnit::MapPoint );
156     MapMode _aEditMapMode(mpEditEngine->GetRefDevice()->GetMapMode().GetMapUnit());
157     aSz = mpEditEngine->GetRefDevice()->LogicToLogic(aSz, &aPntMode, &_aEditMapMode);
158     SvxFontHeightItem aFontHeightItem( aSz.Width(), 100, EE_CHAR_FONTHEIGHT );
159     vcl::Font aDefFont( GetFont( nDefFont ) );
160     SvxFontItem aFontItem( aDefFont.GetFamilyType(), aDefFont.GetFamilyName(),
161                     aDefFont.GetStyleName(), aDefFont.GetPitch(), aDefFont.GetCharSet(), EE_CHAR_FONTINFO );
162 
163     sal_Int32 nStartPara = mpEditEngine->GetEditDoc().GetPos( rStart.GetNode() );
164     sal_Int32 nEndPara = mpEditEngine->GetEditDoc().GetPos( rEnd.GetNode() );
165     for ( sal_Int32 nPara = nStartPara; nPara <= nEndPara; nPara++ )
166     {
167         ContentNode* pNode = mpEditEngine->GetEditDoc().GetObject( nPara );
168         assert(pNode && "AddRTFDefaultValues - No paragraph?!");
169         if ( !pNode->GetContentAttribs().HasItem( EE_CHAR_FONTINFO ) )
170             pNode->GetContentAttribs().GetItems().Put( aFontItem );
171         if ( !pNode->GetContentAttribs().HasItem( EE_CHAR_FONTHEIGHT ) )
172             pNode->GetContentAttribs().GetItems().Put( aFontHeightItem );
173     }
174 }
175 
NextToken(int nToken)176 void EditRTFParser::NextToken( int nToken )
177 {
178     switch( nToken )
179     {
180         case RTF_DEFF:
181         {
182             nDefFont = sal_uInt16(nTokenValue);
183         }
184         break;
185         case RTF_DEFTAB:
186         break;
187         case RTF_CELL:
188         {
189             aCurSel = mpEditEngine->InsertParaBreak(aCurSel);
190         }
191         break;
192         case RTF_LINE:
193         {
194             aCurSel = mpEditEngine->InsertLineBreak(aCurSel);
195         }
196         break;
197         case RTF_FIELD:
198         {
199             ReadField();
200         }
201         break;
202         case RTF_SHPINST:  // fdo#76776 process contents of shpinst
203         break;
204         case RTF_SP:       // fdo#76776 but skip SP groups
205         {
206             SkipGroup();
207         }
208         break;
209         case RTF_PGDSCTBL: // #i29453# ignore \*\pgdsctbl destination
210         case RTF_LISTTEXT:
211         {
212             SkipGroup();
213         }
214         break;
215         default:
216         {
217             SvxRTFParser::NextToken( nToken );
218             if ( nToken == RTF_STYLESHEET )
219                 CreateStyleSheets();
220         }
221         break;
222     }
223     if (mpEditEngine->IsRtfImportHandlerSet())
224     {
225         RtfImportInfo aImportInfo(RtfImportState::NextToken, this, mpEditEngine->CreateESelection(aCurSel));
226         aImportInfo.nToken = nToken;
227         aImportInfo.nTokenValue = short(nTokenValue);
228         mpEditEngine->CallRtfImportHandler(aImportInfo);
229     }
230 }
231 
UnknownAttrToken(int nToken)232 void EditRTFParser::UnknownAttrToken( int nToken )
233 {
234     // for Tokens which are not evaluated in ReadAttr
235     // Actually, only for Calc (RTFTokenHdl), so that RTF_INTBL
236     if (mpEditEngine->IsRtfImportHandlerSet())
237     {
238         RtfImportInfo aImportInfo(RtfImportState::UnknownAttr, this, mpEditEngine->CreateESelection(aCurSel));
239         aImportInfo.nToken = nToken;
240         aImportInfo.nTokenValue = short(nTokenValue);
241         mpEditEngine->CallRtfImportHandler(aImportInfo);
242     }
243 }
244 
InsertText()245 void EditRTFParser::InsertText()
246 {
247     OUString aText( aToken );
248     if (mpEditEngine->IsRtfImportHandlerSet())
249     {
250         RtfImportInfo aImportInfo(RtfImportState::InsertText, this, mpEditEngine->CreateESelection(aCurSel));
251         mpEditEngine->CallRtfImportHandler(aImportInfo);
252     }
253     aCurSel = mpEditEngine->InsertText(aCurSel, aText);
254     bLastActionInsertParaBreak = false;
255 }
256 
InsertPara()257 void EditRTFParser::InsertPara()
258 {
259     if (mpEditEngine->IsRtfImportHandlerSet())
260     {
261         RtfImportInfo aImportInfo(RtfImportState::InsertPara, this, mpEditEngine->CreateESelection(aCurSel));
262         mpEditEngine->CallRtfImportHandler(aImportInfo);
263     }
264     aCurSel = mpEditEngine->InsertParaBreak(aCurSel);
265     bLastActionInsertParaBreak = true;
266 }
267 
MovePos(bool const bForward)268 void EditRTFParser::MovePos( bool const bForward )
269 {
270     if( bForward )
271         aCurSel = mpEditEngine->CursorRight(
272             aCurSel.Max(), i18n::CharacterIteratorMode::SKIPCHARACTER);
273     else
274         aCurSel = mpEditEngine->CursorLeft(
275             aCurSel.Max(), i18n::CharacterIteratorMode::SKIPCHARACTER);
276 }
277 
SetEndPrevPara(EditNodeIdx * & rpNodePos,sal_Int32 & rCntPos)278 void EditRTFParser::SetEndPrevPara( EditNodeIdx*& rpNodePos,
279                                     sal_Int32& rCntPos )
280 {
281     // The Intention is to: determine the current insert position of the
282     //                      previous paragraph and set the end from this.
283     //                      This "\pard" always apply on the right paragraph.
284 
285     ContentNode* pN = aCurSel.Max().GetNode();
286     sal_Int32 nCurPara = mpEditEngine->GetEditDoc().GetPos( pN );
287     DBG_ASSERT( nCurPara != 0, "Paragraph equal to 0: SetEnfPrevPara" );
288     if ( nCurPara )
289         nCurPara--;
290     ContentNode* pPrevNode = mpEditEngine->GetEditDoc().GetObject( nCurPara );
291     assert(pPrevNode && "pPrevNode = 0!");
292     rpNodePos = new EditNodeIdx(mpEditEngine, pPrevNode);
293     rCntPos = pPrevNode->Len();
294 }
295 
IsEndPara(EditNodeIdx * pNd,sal_Int32 nCnt) const296 bool EditRTFParser::IsEndPara( EditNodeIdx* pNd, sal_Int32 nCnt ) const
297 {
298     return nCnt == pNd->GetNode()->Len();
299 }
300 
SetAttrInDoc(SvxRTFItemStackType & rSet)301 void EditRTFParser::SetAttrInDoc( SvxRTFItemStackType &rSet )
302 {
303     ContentNode* pSttNode = const_cast<EditNodeIdx&>(rSet.GetSttNode()).GetNode();
304     ContentNode* pEndNode = const_cast<EditNodeIdx&>(rSet.GetEndNode()).GetNode();
305 
306     EditPaM aStartPaM( pSttNode, rSet.GetSttCnt() );
307     EditPaM aEndPaM( pEndNode, rSet.GetEndCnt() );
308 
309     // If possible adjust the Escapement-Item:
310     const SfxPoolItem* pItem;
311 
312     // #i66167# adapt font heights to destination MapUnit if necessary
313     const MapUnit eDestUnit = mpEditEngine->GetEditDoc().GetItemPool().GetMetric(0);
314     if (eDestUnit != gRTFMapUnit)
315     {
316         sal_uInt16 const aFntHeightIems[3] = { EE_CHAR_FONTHEIGHT, EE_CHAR_FONTHEIGHT_CJK, EE_CHAR_FONTHEIGHT_CTL };
317         for (unsigned short aFntHeightIem : aFntHeightIems)
318         {
319             if (SfxItemState::SET == rSet.GetAttrSet().GetItemState( aFntHeightIem, false, &pItem ))
320             {
321                 sal_uInt32 nHeight  = static_cast<const SvxFontHeightItem*>(pItem)->GetHeight();
322                 long nNewHeight;
323                 nNewHeight = OutputDevice::LogicToLogic( static_cast<long>(nHeight), gRTFMapUnit, eDestUnit );
324 
325                 SvxFontHeightItem aFntHeightItem( nNewHeight, 100, aFntHeightIem );
326                 aFntHeightItem.SetProp(
327                     static_cast<const SvxFontHeightItem*>(pItem)->GetProp(),
328                     static_cast<const SvxFontHeightItem*>(pItem)->GetPropUnit());
329                 rSet.GetAttrSet().Put( aFntHeightItem );
330             }
331         }
332     }
333 
334     if( SfxItemState::SET == rSet.GetAttrSet().GetItemState( EE_CHAR_ESCAPEMENT, false, &pItem ))
335     {
336         // the correct one
337         long nEsc = static_cast<const SvxEscapementItem*>(pItem)->GetEsc();
338         long nEscFontHeight = 0;
339         if( ( DFLT_ESC_AUTO_SUPER != nEsc ) && ( DFLT_ESC_AUTO_SUB != nEsc ) )
340         {
341             nEsc *= 10; //HalfPoints => Twips was embezzled in RTFITEM.CXX!
342             SvxFont aFont;
343             mpEditEngine->SeekCursor(aStartPaM.GetNode(), aStartPaM.GetIndex()+1, aFont);
344             nEscFontHeight = aFont.GetFontSize().Height();
345         }
346         if (nEscFontHeight)
347         {
348             nEsc = nEsc * 100 / nEscFontHeight;
349 
350             SvxEscapementItem aEscItem( static_cast<short>(nEsc), static_cast<const SvxEscapementItem*>(pItem)->GetProportionalHeight(), EE_CHAR_ESCAPEMENT );
351             rSet.GetAttrSet().Put( aEscItem );
352         }
353     }
354 
355     if (mpEditEngine->IsRtfImportHandlerSet())
356     {
357         EditSelection aSel( aStartPaM, aEndPaM );
358         RtfImportInfo aImportInfo(RtfImportState::SetAttr, this, mpEditEngine->CreateESelection(aSel));
359         mpEditEngine->CallRtfImportHandler(aImportInfo);
360     }
361 
362     ContentNode* pSN = aStartPaM.GetNode();
363     ContentNode* pEN = aEndPaM.GetNode();
364     sal_Int32 nStartNode = mpEditEngine->GetEditDoc().GetPos( pSN );
365     sal_Int32 nEndNode = mpEditEngine->GetEditDoc().GetPos( pEN );
366     sal_Int16 nOutlLevel = 0xff;
367 
368     if (rSet.StyleNo() && mpEditEngine->GetStyleSheetPool() && mpEditEngine->IsImportRTFStyleSheetsSet())
369     {
370         SvxRTFStyleTbl::iterator it = GetStyleTbl().find( rSet.StyleNo() );
371         DBG_ASSERT( it != GetStyleTbl().end(), "Template not defined in RTF!" );
372         if ( it != GetStyleTbl().end() )
373         {
374             auto const& pS = it->second;
375             mpEditEngine->SetStyleSheet(
376                 EditSelection(aStartPaM, aEndPaM),
377                 static_cast<SfxStyleSheet*>(mpEditEngine->GetStyleSheetPool()->Find(pS->sName, SfxStyleFamily::All)));
378             nOutlLevel = pS->nOutlineNo;
379         }
380     }
381 
382     // When an Attribute goes from 0 to the current paragraph length,
383     // it should be a paragraph attribute!
384 
385     // Note: Selection can reach over several paragraphs.
386     // All Complete paragraphs are paragraph attributes ...
387     for ( sal_Int32 z = nStartNode+1; z < nEndNode; z++ )
388     {
389         DBG_ASSERT(mpEditEngine->GetEditDoc().GetObject(z), "Node does not exist yet(RTF)");
390         mpEditEngine->SetParaAttribsOnly(z, rSet.GetAttrSet());
391     }
392 
393     if ( aStartPaM.GetNode() != aEndPaM.GetNode() )
394     {
395         // The rest of the StartNodes...
396         if ( aStartPaM.GetIndex() == 0 )
397             mpEditEngine->SetParaAttribsOnly(nStartNode, rSet.GetAttrSet());
398         else
399             mpEditEngine->SetAttribs(
400                 EditSelection(aStartPaM, EditPaM(aStartPaM.GetNode(), aStartPaM.GetNode()->Len())), rSet.GetAttrSet());
401 
402         // the beginning of the EndNodes...
403         if ( aEndPaM.GetIndex() == aEndPaM.GetNode()->Len() )
404             mpEditEngine->SetParaAttribsOnly(nEndNode, rSet.GetAttrSet());
405         else
406             mpEditEngine->SetAttribs(
407                 EditSelection(EditPaM(aEndPaM.GetNode(), 0), aEndPaM), rSet.GetAttrSet());
408     }
409     else
410     {
411         if ( ( aStartPaM.GetIndex() == 0 ) && ( aEndPaM.GetIndex() == aEndPaM.GetNode()->Len() ) )
412         {
413             // When settings char attribs as para attribs, we must merge with existing attribs, not overwrite the ItemSet!
414             SfxItemSet aAttrs = mpEditEngine->GetBaseParaAttribs(nStartNode);
415             aAttrs.Put( rSet.GetAttrSet() );
416             mpEditEngine->SetParaAttribsOnly(nStartNode, aAttrs);
417         }
418         else
419         {
420             mpEditEngine->SetAttribs(
421                 EditSelection(aStartPaM, aEndPaM), rSet.GetAttrSet());
422         }
423     }
424 
425     // OutlLevel...
426     if ( nOutlLevel != 0xff )
427     {
428         for ( sal_Int32 n = nStartNode; n <= nEndNode; n++ )
429         {
430             ContentNode* pNode = mpEditEngine->GetEditDoc().GetObject( n );
431             pNode->GetContentAttribs().GetItems().Put( SfxInt16Item( EE_PARA_OUTLLEVEL, nOutlLevel ) );
432         }
433     }
434 }
435 
FindStyleSheet(const OUString & rName)436 SvxRTFStyleType* EditRTFParser::FindStyleSheet( const OUString& rName )
437 {
438     SvxRTFStyleTbl& rTable = GetStyleTbl();
439     for (auto const& iter : rTable)
440     {
441         if (iter.second->sName == rName)
442             return iter.second.get();
443     }
444     return nullptr;
445 }
446 
CreateStyleSheet(SvxRTFStyleType const * pRTFStyle)447 SfxStyleSheet* EditRTFParser::CreateStyleSheet( SvxRTFStyleType const * pRTFStyle )
448 {
449     // Check if a template exists, then it will not be changed!
450     SfxStyleSheet* pStyle = static_cast<SfxStyleSheet*>(mpEditEngine->GetStyleSheetPool()->Find( pRTFStyle->sName, SfxStyleFamily::All ));
451     if ( pStyle )
452         return pStyle;
453 
454     OUString aName( pRTFStyle->sName );
455     OUString aParent;
456     if ( pRTFStyle->nBasedOn )
457     {
458         SvxRTFStyleTbl::iterator it = GetStyleTbl().find( pRTFStyle->nBasedOn );
459         if ( it != GetStyleTbl().end())
460         {
461             SvxRTFStyleType *const pS = it->second.get();
462             if ( pS && ( pS !=pRTFStyle ) )
463                 aParent = pS->sName;
464         }
465     }
466 
467     pStyle = static_cast<SfxStyleSheet*>( &mpEditEngine->GetStyleSheetPool()->Make( aName, SfxStyleFamily::Para ) );
468 
469     // 1) convert and take over Items ...
470     ConvertAndPutItems( pStyle->GetItemSet(), pRTFStyle->aAttrSet );
471 
472     // 2) As long as Parent is not in the pool, also create this ...
473     if ( !aParent.isEmpty() && ( aParent != aName ) )
474     {
475         SfxStyleSheet* pS = static_cast<SfxStyleSheet*>(mpEditEngine->GetStyleSheetPool()->Find( aParent, SfxStyleFamily::All ));
476         if ( !pS )
477         {
478             // If not found anywhere, create from RTF ...
479             SvxRTFStyleType* _pRTFStyle = FindStyleSheet( aParent );
480             if ( _pRTFStyle )
481                 pS = CreateStyleSheet( _pRTFStyle );
482         }
483         // 2b) Link Itemset with Parent ...
484         if ( pS )
485             pStyle->GetItemSet().SetParent( &pS->GetItemSet() );
486     }
487     return pStyle;
488 }
489 
CreateStyleSheets()490 void EditRTFParser::CreateStyleSheets()
491 {
492     // the SvxRTFParser has now created the template...
493     if (mpEditEngine->GetStyleSheetPool() && mpEditEngine->IsImportRTFStyleSheetsSet())
494     {
495         for (auto const& elem : GetStyleTbl())
496         {
497             SvxRTFStyleType* pRTFStyle = elem.second.get();
498             CreateStyleSheet( pRTFStyle );
499         }
500     }
501 }
502 
CalcValue()503 void EditRTFParser::CalcValue()
504 {
505     const MapUnit eDestUnit = aEditMapMode.GetMapUnit();
506     if (eDestUnit != gRTFMapUnit)
507         nTokenValue = OutputDevice::LogicToLogic( nTokenValue, gRTFMapUnit, eDestUnit );
508 }
509 
ReadField()510 void EditRTFParser::ReadField()
511 {
512     // From SwRTFParser::ReadField()
513     int _nOpenBrakets = 1;      // the first was already detected earlier
514     bool bFldInst = false;
515     bool bFldRslt = false;
516     OUString aFldInst;
517     OUString aFldRslt;
518 
519     while( _nOpenBrakets && IsParserWorking() )
520     {
521         switch( GetNextToken() )
522         {
523             case '}':
524             {
525                 _nOpenBrakets--;
526                 if ( _nOpenBrakets == 1 )
527                 {
528                     bFldInst = false;
529                     bFldRslt = false;
530                 }
531             }
532             break;
533 
534             case '{':           _nOpenBrakets++;
535                                 break;
536 
537             case RTF_FIELD:     SkipGroup();
538                                 break;
539 
540             case RTF_FLDINST:   bFldInst = true;
541                                 break;
542 
543             case RTF_FLDRSLT:   bFldRslt = true;
544                                 break;
545 
546             case RTF_TEXTTOKEN:
547             {
548                 if ( bFldInst )
549                     aFldInst += aToken;
550                 else if ( bFldRslt )
551                     aFldRslt += aToken;
552             }
553             break;
554         }
555     }
556     if ( !aFldInst.isEmpty() )
557     {
558         OUString aHyperLinkMarker( "HYPERLINK " );
559         if ( aFldInst.startsWithIgnoreAsciiCase( aHyperLinkMarker ) )
560         {
561             aFldInst = aFldInst.copy( aHyperLinkMarker.getLength() );
562             aFldInst = comphelper::string::strip(aFldInst, ' ');
563             // strip start and end quotes
564             aFldInst = aFldInst.copy( 1, aFldInst.getLength()-2 );
565 
566             if ( aFldRslt.isEmpty() )
567                 aFldRslt = aFldInst;
568 
569             SvxFieldItem aField( SvxURLField( aFldInst, aFldRslt, SvxURLFormat::Repr ), EE_FEATURE_FIELD  );
570             aCurSel = mpEditEngine->InsertField(aCurSel, aField);
571             mpEditEngine->UpdateFieldsOnly();
572             bLastActionInsertParaBreak = false;
573         }
574     }
575 
576     SkipToken();        // the closing brace is evaluated "above"
577 }
578 
SkipGroup()579 void EditRTFParser::SkipGroup()
580 {
581     int _nOpenBrakets = 1;      // the first was already detected earlier
582 
583     while( _nOpenBrakets && IsParserWorking() )
584     {
585         switch( GetNextToken() )
586         {
587             case '}':
588             {
589                 _nOpenBrakets--;
590             }
591             break;
592 
593             case '{':
594             {
595                 _nOpenBrakets++;
596             }
597             break;
598         }
599     }
600 
601     SkipToken();        // the closing brace is evaluated "above"
602 }
603 
EditNodeIdx(EditEngine * pEE,ContentNode * pNd)604 EditNodeIdx::EditNodeIdx(EditEngine* pEE, ContentNode* pNd) :
605     mpEditEngine(pEE), mpNode(pNd) {}
606 
GetIdx() const607 sal_Int32 EditNodeIdx::GetIdx() const
608 {
609     return mpEditEngine->GetEditDoc().GetPos(mpNode);
610 }
611 
EditPosition(EditEngine * pEE,EditSelection * pSel)612 EditPosition::EditPosition(EditEngine* pEE, EditSelection* pSel) :
613     mpEditEngine(pEE), mpCurSel(pSel) {}
614 
Clone() const615 std::unique_ptr<EditPosition> EditPosition::Clone() const
616 {
617     return std::unique_ptr<EditPosition>(new EditPosition(mpEditEngine, mpCurSel));
618 }
619 
MakeNodeIdx() const620 std::unique_ptr<EditNodeIdx> EditPosition::MakeNodeIdx() const
621 {
622     return std::make_unique<EditNodeIdx>(mpEditEngine, mpCurSel->Max().GetNode());
623 }
624 
GetNodeIdx() const625 sal_Int32 EditPosition::GetNodeIdx() const
626 {
627     ContentNode* pN = mpCurSel->Max().GetNode();
628     return mpEditEngine->GetEditDoc().GetPos(pN);
629 }
630 
GetCntIdx() const631 sal_Int32 EditPosition::GetCntIdx() const
632 {
633     return mpCurSel->Max().GetIndex();
634 }
635 
636 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
637