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 #include <doc.hxx>
20 #include <IDocumentFieldsAccess.hxx>
21 #include <IDocumentUndoRedo.hxx>
22 #include <node.hxx>
23 #include <frmfmt.hxx>
24 #include <swtable.hxx>
25 #include <ndtxt.hxx>
26 #include <swtblfmt.hxx>
27 #include <cellatr.hxx>
28 #include <ddefld.hxx>
29 #include <swddetbl.hxx>
30 #include <ndindex.hxx>
31 #include <frameformats.hxx>
32 #include <vector>
33 #include <osl/diagnose.h>
34 
35 
36 #ifdef DBG_UTIL
37 #define CHECK_TABLE(t) (t).CheckConsistency();
38 #else
39 #define CHECK_TABLE(t)
40 #endif
41 
42 namespace {
43 
44 // Structure for the mapping from old and new frame formats to the
45 // boxes and lines of a table
46 struct MapTableFrameFormat
47 {
48     const SwFrameFormat *pOld;
49     SwFrameFormat *pNew;
MapTableFrameFormat__anonc6cd2ef70111::MapTableFrameFormat50     MapTableFrameFormat( const SwFrameFormat *pOldFormat, SwFrameFormat*pNewFormat )
51         : pOld( pOldFormat ), pNew( pNewFormat )
52     {}
53 };
54 
55 }
56 
57 typedef std::vector<MapTableFrameFormat> MapTableFrameFormats;
58 
MakeCopy(SwDoc & rDoc,const SwNodeIndex & rIdx,bool const bNewFrames) const59 SwContentNode* SwTextNode::MakeCopy(SwDoc& rDoc, const SwNodeIndex& rIdx, bool const bNewFrames) const
60 {
61     // the Copy-Textnode is the Node with the Text, the Copy-Attrnode is the
62     // node with the collection and hard attributes. Normally is the same
63     // node, but if insert a glossary without formatting, then the Attrnode
64     // is the prev node of the destination position in dest. document.
65     SwTextNode* pCpyTextNd = const_cast<SwTextNode*>(this);
66     SwTextNode* pCpyAttrNd = pCpyTextNd;
67 
68     // Copy the formats to the other document
69     SwTextFormatColl* pColl = nullptr;
70     if( rDoc.IsInsOnlyTextGlossary() )
71     {
72         SwNodeIndex aIdx( rIdx, -1 );
73         if( aIdx.GetNode().IsTextNode() )
74         {
75             pCpyAttrNd = aIdx.GetNode().GetTextNode();
76             pColl = &pCpyAttrNd->GetTextColl()->GetNextTextFormatColl();
77         }
78     }
79     if( !pColl )
80         pColl = rDoc.CopyTextColl( *GetTextColl() );
81 
82     SwTextNode* pTextNd = rDoc.GetNodes().MakeTextNode(rIdx, pColl, bNewFrames);
83 
84     // METADATA: register copy
85     pTextNd->RegisterAsCopyOf(*pCpyTextNd);
86 
87     // Copy Attribute/Text
88     if( !pCpyAttrNd->HasSwAttrSet() )
89         // An AttrSet was added for numbering, so delete it
90         pTextNd->ResetAllAttr();
91 
92     // if Copy-Textnode unequal to Copy-Attrnode, then copy first
93     // the attributes into the new Node.
94     if( pCpyAttrNd != pCpyTextNd )
95     {
96         pCpyAttrNd->CopyAttr( pTextNd, 0, 0 );
97         if( pCpyAttrNd->HasSwAttrSet() )
98         {
99             SwAttrSet aSet( *pCpyAttrNd->GetpSwAttrSet() );
100             aSet.ClearItem( RES_PAGEDESC );
101             aSet.ClearItem( RES_BREAK );
102             aSet.CopyToModify( *pTextNd );
103         }
104     }
105 
106     // Is that enough? What about PostIts/Fields/FieldTypes?
107     // #i96213# - force copy of all attributes
108     pCpyTextNd->CopyText( pTextNd, SwIndex( pCpyTextNd ),
109         pCpyTextNd->GetText().getLength(), true );
110 
111     if( RES_CONDTXTFMTCOLL == pColl->Which() )
112         pTextNd->ChkCondColl();
113 
114     return pTextNd;
115 }
116 
lcl_SrchNew(const MapTableFrameFormat & rMap,SwFrameFormat ** pPara)117 static bool lcl_SrchNew( const MapTableFrameFormat& rMap, SwFrameFormat** pPara )
118 {
119     if( rMap.pOld != *pPara )
120         return true;
121     *pPara = rMap.pNew;
122     return false;
123 }
124 
125 namespace {
126 
127 struct CopyTable
128 {
129     SwDoc& m_rDoc;
130     sal_uLong m_nOldTableSttIdx;
131     MapTableFrameFormats& m_rMapArr;
132     SwTableLine* m_pInsLine;
133     SwTableBox* m_pInsBox;
134     SwTableNode *m_pTableNd;
135     const SwTable *m_pOldTable;
136 
CopyTable__anonc6cd2ef70211::CopyTable137     CopyTable(SwDoc& rDc, MapTableFrameFormats& rArr, sal_uLong nOldStt,
138                SwTableNode& rTableNd, const SwTable* pOldTable)
139         : m_rDoc(rDc), m_nOldTableSttIdx(nOldStt), m_rMapArr(rArr),
140           m_pInsLine(nullptr), m_pInsBox(nullptr), m_pTableNd(&rTableNd), m_pOldTable(pOldTable)
141     {}
142 };
143 
144 }
145 
146 static void lcl_CopyTableLine( const SwTableLine* pLine, CopyTable* pCT );
147 
lcl_CopyTableBox(SwTableBox * pBox,CopyTable * pCT)148 static void lcl_CopyTableBox( SwTableBox* pBox, CopyTable* pCT )
149 {
150     SwTableBoxFormat * pBoxFormat = static_cast<SwTableBoxFormat*>(pBox->GetFrameFormat());
151     for (const auto& rMap : pCT->m_rMapArr)
152         if ( !lcl_SrchNew( rMap, reinterpret_cast<SwFrameFormat**>(&pBoxFormat) ) )
153             break;
154 
155     if (pBoxFormat == pBox->GetFrameFormat()) // Create a new one?
156     {
157         const SfxPoolItem* pItem;
158         if( SfxItemState::SET == pBoxFormat->GetItemState( RES_BOXATR_FORMULA, false,
159             &pItem ) && static_cast<const SwTableBoxFormula*>(pItem)->IsIntrnlName() )
160         {
161             const_cast<SwTableBoxFormula*>(static_cast<const SwTableBoxFormula*>(pItem))->PtrToBoxNm(pCT->m_pOldTable);
162         }
163 
164         pBoxFormat = pCT->m_rDoc.MakeTableBoxFormat();
165         pBoxFormat->CopyAttrs( *pBox->GetFrameFormat() );
166 
167         if( pBox->GetSttIdx() )
168         {
169             SvNumberFormatter* pN = pCT->m_rDoc.GetNumberFormatter(false);
170             if( pN && pN->HasMergeFormatTable() && SfxItemState::SET == pBoxFormat->
171                 GetItemState( RES_BOXATR_FORMAT, false, &pItem ) )
172             {
173                 sal_uLong nOldIdx = static_cast<const SwTableBoxNumFormat*>(pItem)->GetValue();
174                 sal_uLong nNewIdx = pN->GetMergeFormatIndex( nOldIdx );
175                 if( nNewIdx != nOldIdx )
176                     pBoxFormat->SetFormatAttr( SwTableBoxNumFormat( nNewIdx ));
177 
178             }
179         }
180 
181         pCT->m_rMapArr.emplace_back(pBox->GetFrameFormat(), pBoxFormat);
182     }
183 
184     sal_uInt16 nLines = pBox->GetTabLines().size();
185     SwTableBox* pNewBox;
186     if( nLines )
187         pNewBox = new SwTableBox(pBoxFormat, nLines, pCT->m_pInsLine);
188     else
189     {
190         SwNodeIndex aNewIdx(*pCT->m_pTableNd, pBox->GetSttIdx() - pCT->m_nOldTableSttIdx);
191         assert(aNewIdx.GetNode().IsStartNode() && "Index is not on the start node");
192 
193         pNewBox = new SwTableBox(pBoxFormat, aNewIdx, pCT->m_pInsLine);
194         pNewBox->setRowSpan( pBox->getRowSpan() );
195     }
196 
197     pCT->m_pInsLine->GetTabBoxes().push_back( pNewBox );
198 
199     if (nLines)
200     {
201         CopyTable aPara(*pCT);
202         aPara.m_pInsBox = pNewBox;
203         for( const SwTableLine* pLine : pBox->GetTabLines() )
204             lcl_CopyTableLine( pLine, &aPara );
205     }
206     else if (pNewBox->IsInHeadline(&pCT->m_pTableNd->GetTable()))
207     {
208         // In the headline, the paragraphs must match conditional styles
209         pNewBox->GetSttNd()->CheckSectionCondColl();
210     }
211 }
212 
lcl_CopyTableLine(const SwTableLine * pLine,CopyTable * pCT)213 static void lcl_CopyTableLine( const SwTableLine* pLine, CopyTable* pCT )
214 {
215     SwTableLineFormat * pLineFormat = static_cast<SwTableLineFormat*>(pLine->GetFrameFormat());
216     for (const auto& rMap : pCT->m_rMapArr)
217         if ( !lcl_SrchNew( rMap, reinterpret_cast<SwFrameFormat**>(&pLineFormat) ) )
218             break;
219 
220     if( pLineFormat == pLine->GetFrameFormat() ) // Create a new one?
221     {
222         pLineFormat = pCT->m_rDoc.MakeTableLineFormat();
223         pLineFormat->CopyAttrs( *pLine->GetFrameFormat() );
224         pCT->m_rMapArr.emplace_back(pLine->GetFrameFormat(), pLineFormat);
225     }
226 
227     SwTableLine* pNewLine = new SwTableLine(pLineFormat, pLine->GetTabBoxes().size(), pCT->m_pInsBox);
228     // Insert the new row into the table
229     if (pCT->m_pInsBox)
230     {
231         pCT->m_pInsBox->GetTabLines().push_back(pNewLine);
232     }
233     else
234     {
235         pCT->m_pTableNd->GetTable().GetTabLines().push_back(pNewLine);
236     }
237 
238     pCT->m_pInsLine = pNewLine;
239     for( auto& rpBox : const_cast<SwTableLine*>(pLine)->GetTabBoxes() )
240         lcl_CopyTableBox(rpBox, pCT);
241 }
242 
MakeCopy(SwDoc & rDoc,const SwNodeIndex & rIdx) const243 SwTableNode* SwTableNode::MakeCopy( SwDoc& rDoc, const SwNodeIndex& rIdx ) const
244 {
245     // In which array are we? Nodes? UndoNodes?
246     SwNodes& rNds = const_cast<SwNodes&>(GetNodes());
247 
248     {
249         if( rIdx < rDoc.GetNodes().GetEndOfInserts().GetIndex() &&
250             rIdx >= rDoc.GetNodes().GetEndOfInserts().StartOfSectionIndex() )
251             return nullptr;
252     }
253 
254     // Copy the TableFrameFormat
255     OUString sTableName( GetTable().GetFrameFormat()->GetName() );
256     if( !rDoc.IsCopyIsMove() )
257     {
258         const SwFrameFormats& rTableFormats = *rDoc.GetTableFrameFormats();
259         for( size_t n = rTableFormats.size(); n; )
260             if( rTableFormats[ --n ]->GetName() == sTableName )
261             {
262                 sTableName = rDoc.GetUniqueTableName();
263                 break;
264             }
265     }
266 
267     SwFrameFormat* pTableFormat = rDoc.MakeTableFrameFormat( sTableName, rDoc.GetDfltFrameFormat() );
268     pTableFormat->CopyAttrs( *GetTable().GetFrameFormat() );
269     SwTableNode* pTableNd = new SwTableNode( rIdx );
270     SwEndNode* pEndNd = new SwEndNode( rIdx, *pTableNd );
271     SwNodeIndex aInsPos( *pEndNd );
272 
273     SwTable& rTable = pTableNd->GetTable();
274     rTable.SetTableStyleName(GetTable().GetTableStyleName());
275     rTable.RegisterToFormat( *pTableFormat );
276 
277     rTable.SetRowsToRepeat( GetTable().GetRowsToRepeat() );
278     rTable.SetTableChgMode( GetTable().GetTableChgMode() );
279     rTable.SetTableModel( GetTable().IsNewModel() );
280 
281     SwDDEFieldType* pDDEType = nullptr;
282     if( auto pSwDDETable = dynamic_cast<const SwDDETable*>( &GetTable() ) )
283     {
284         // We're copying a DDE table
285         // Is the field type available in the new document?
286         pDDEType = const_cast<SwDDETable*>(pSwDDETable)->GetDDEFieldType();
287         if( pDDEType->IsDeleted() )
288             rDoc.getIDocumentFieldsAccess().InsDeletedFieldType( *pDDEType );
289         else
290             pDDEType = static_cast<SwDDEFieldType*>(rDoc.getIDocumentFieldsAccess().InsertFieldType( *pDDEType ));
291         OSL_ENSURE( pDDEType, "unknown FieldType" );
292 
293         // Swap the table pointers in the node
294         std::unique_ptr<SwDDETable> pNewTable(new SwDDETable( pTableNd->GetTable(), pDDEType ));
295         pTableNd->SetNewTable( std::move(pNewTable), false );
296     }
297     // First copy the content of the tables, we will later assign the
298     // boxes/lines and create the frames
299     SwNodeRange aRg( *this, +1, *EndOfSectionNode() );
300 
301     // If there is a table in this table, the table format for the outer table
302     // does not seem to be used, because the table does not have any contents yet
303     // (see IsUsed). Therefore the inner table gets the same name as the outer table.
304     // We have to make sure that the table node of the SwTable is accessible, even
305     // without any content in m_TabSortContentBoxes. #i26629#
306     pTableNd->GetTable().SetTableNode( pTableNd );
307     rNds.Copy_( aRg, aInsPos, false );
308     pTableNd->GetTable().SetTableNode( nullptr );
309 
310     // Special case for a single box
311     if( 1 == GetTable().GetTabSortBoxes().size() )
312     {
313         aRg.aStart.Assign( *pTableNd, 1 );
314         aRg.aEnd.Assign( *pTableNd->EndOfSectionNode() );
315         rDoc.GetNodes().SectionDown( &aRg, SwTableBoxStartNode );
316     }
317 
318     // Delete all frames from the copied area, they will be created
319     // during the generation of the table frame
320     pTableNd->DelFrames();
321 
322     MapTableFrameFormats aMapArr;
323     CopyTable aPara( rDoc, aMapArr, GetIndex(), *pTableNd, &GetTable() );
324 
325     for( const SwTableLine* pLine : GetTable().GetTabLines() )
326         lcl_CopyTableLine( pLine, &aPara );
327 
328     if( pDDEType )
329         pDDEType->IncRefCnt();
330 
331     CHECK_TABLE( GetTable() );
332     return pTableNd;
333 }
334 
CopyCollFormat(SwTextNode & rDestNd,bool const bUndoForChgFormatColl)335 void SwTextNode::CopyCollFormat(SwTextNode& rDestNd, bool const bUndoForChgFormatColl)
336 {
337     // Copy the formats into the other document:
338     // Special case for PageBreak/PageDesc/ColBrk
339     SwDoc& rDestDoc = rDestNd.GetDoc();
340     SwAttrSet aPgBrkSet( rDestDoc.GetAttrPool(), aBreakSetRange );
341     const SwAttrSet* pSet;
342 
343     pSet = rDestNd.GetpSwAttrSet();
344     if( nullptr != pSet )
345     {
346         // Special cases for Break-Attributes
347         const SfxPoolItem* pAttr;
348         if( SfxItemState::SET == pSet->GetItemState( RES_BREAK, false, &pAttr ) )
349             aPgBrkSet.Put( *pAttr );
350 
351         if( SfxItemState::SET == pSet->GetItemState( RES_PAGEDESC, false, &pAttr ) )
352             aPgBrkSet.Put( *pAttr );
353     }
354 
355     // this may create undo action SwUndoFormatCreate
356     auto const pCopy( rDestDoc.CopyTextColl( *GetTextColl() ) );
357     if (bUndoForChgFormatColl)
358     {
359         rDestNd.ChgFormatColl(pCopy);
360     }
361     else // tdf#138897
362     {
363         ::sw::UndoGuard const ug(rDestDoc.GetIDocumentUndoRedo());
364         rDestNd.ChgFormatColl(pCopy);
365     }
366     pSet = GetpSwAttrSet();
367     if( nullptr != pSet )
368     {
369         // note: this may create undo actions but not for setting the items
370         pSet->CopyToModify( rDestNd );
371     }
372 
373     if( aPgBrkSet.Count() )
374         rDestNd.SetAttr( aPgBrkSet );
375 }
376 
377 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
378