android: Update app-specific/MIME type icons
[LibreOffice.git] / sw / source / core / doc / docglbl.cxx
blob737506ca275e5e554455c8cf91fdec2b1fc4d57b
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <osl/diagnose.h>
21 #include <unotools/tempfile.hxx>
22 #include <svl/stritem.hxx>
23 #include <svl/eitem.hxx>
24 #include <sfx2/docfile.hxx>
25 #include <sfx2/docfilt.hxx>
26 #include <sfx2/fcontnr.hxx>
27 #include <sfx2/bindings.hxx>
28 #include <sfx2/request.hxx>
29 #include <sfx2/sfxsids.hrc>
30 #include <sfx2/viewfrm.hxx>
31 #include <tools/datetime.hxx>
32 #include <fmtinfmt.hxx>
33 #include <fmtanchr.hxx>
34 #include <doc.hxx>
35 #include <IDocumentUndoRedo.hxx>
36 #include <IDocumentRedlineAccess.hxx>
37 #include <DocumentSettingManager.hxx>
38 #include <DocumentContentOperationsManager.hxx>
39 #include <IDocumentLayoutAccess.hxx>
40 #include <docary.hxx>
41 #include <pam.hxx>
42 #include <ndtxt.hxx>
43 #include <docsh.hxx>
44 #include <section.hxx>
45 #include <calbck.hxx>
46 #include <iodetect.hxx>
47 #include <frameformats.hxx>
48 #include <memory>
49 #include <com/sun/star/uno/Reference.h>
50 #include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
51 #include <com/sun/star/document/XDocumentProperties.hpp>
52 #include <com/sun/star/frame/XModel.hpp>
54 using namespace ::com::sun::star;
56 namespace {
58 enum SwSplitDocType
60 SPLITDOC_TO_GLOBALDOC,
61 SPLITDOC_TO_HTML
66 bool SwDoc::GenerateGlobalDoc( const OUString& rPath,
67 const SwTextFormatColl* pSplitColl )
69 return SplitDoc( SPLITDOC_TO_GLOBALDOC, rPath, false, pSplitColl );
72 bool SwDoc::GenerateGlobalDoc( const OUString& rPath, int nOutlineLevel )
74 return SplitDoc( SPLITDOC_TO_GLOBALDOC, rPath, true, nullptr, nOutlineLevel );
77 bool SwDoc::GenerateHTMLDoc( const OUString& rPath, int nOutlineLevel )
79 return SplitDoc( SPLITDOC_TO_HTML, rPath, true, nullptr, nOutlineLevel );
82 bool SwDoc::GenerateHTMLDoc( const OUString& rPath,
83 const SwTextFormatColl* pSplitColl )
85 return SplitDoc( SPLITDOC_TO_HTML, rPath, false, pSplitColl );
88 // two helpers for outline mode
89 static SwNode* GetStartNode( SwOutlineNodes const * pOutlNds, int nOutlineLevel, SwOutlineNodes::size_type* nOutl )
91 for( ; *nOutl < pOutlNds->size(); ++(*nOutl) )
93 SwNode* pNd = (*pOutlNds)[ *nOutl ];
94 if( pNd->GetTextNode()->GetAttrOutlineLevel() == nOutlineLevel && !pNd->FindTableNode() )
96 return pNd;
100 return nullptr;
103 static SwNode* GetEndNode( SwOutlineNodes const * pOutlNds, int nOutlineLevel, SwOutlineNodes::size_type* nOutl )
105 SwNode* pNd;
107 for( ++(*nOutl); (*nOutl) < pOutlNds->size(); ++(*nOutl) )
109 pNd = (*pOutlNds)[ *nOutl ];
111 const int nLevel = pNd->GetTextNode()->GetAttrOutlineLevel();
113 if( ( 0 < nLevel && nLevel <= nOutlineLevel ) &&
114 !pNd->FindTableNode() )
116 return pNd;
119 return nullptr;
122 // two helpers for collection mode
123 static SwNode* GetStartNode( const SwOutlineNodes* pOutlNds, const SwTextFormatColl* pSplitColl, SwOutlineNodes::size_type* nOutl )
125 for( ; *nOutl < pOutlNds->size(); ++(*nOutl) )
127 SwNode* pNd = (*pOutlNds)[ *nOutl ];
128 if( pNd->GetTextNode()->GetTextColl() == pSplitColl &&
129 !pNd->FindTableNode() )
131 return pNd;
134 return nullptr;
137 static SwNode* GetEndNode( const SwOutlineNodes* pOutlNds, const SwTextFormatColl* pSplitColl, SwOutlineNodes::size_type* nOutl )
139 SwNode* pNd;
141 for( ++(*nOutl); *nOutl < pOutlNds->size(); ++(*nOutl) )
143 pNd = (*pOutlNds)[ *nOutl ];
144 SwTextFormatColl* pTColl = pNd->GetTextNode()->GetTextColl();
146 if( ( pTColl == pSplitColl ||
147 ( pSplitColl->GetAttrOutlineLevel() > 0 &&
148 pTColl->GetAttrOutlineLevel() > 0 &&
149 pTColl->GetAttrOutlineLevel() <
150 pSplitColl->GetAttrOutlineLevel() )) &&
151 !pNd->FindTableNode() )
153 return pNd;
156 return nullptr;
159 bool SwDoc::SplitDoc( sal_uInt16 eDocType, const OUString& rPath, bool bOutline, const SwTextFormatColl* pSplitColl, int nOutlineLevel )
161 // Iterate over all the template's Nodes, creating an own
162 // document for every single one and replace linked sections (GlobalDoc) for links (HTML).
163 // Finally, we save this document as a GlobalDoc/HTMLDoc.
164 if( !mpDocShell || !mpDocShell->GetMedium() ||
165 ( SPLITDOC_TO_GLOBALDOC == eDocType && GetDocumentSettingManager().get(DocumentSettingId::GLOBAL_DOCUMENT) ) )
166 return false;
168 SwOutlineNodes::size_type nOutl = 0;
169 SwOutlineNodes* pOutlNds = const_cast<SwOutlineNodes*>(&GetNodes().GetOutLineNds());
170 std::unique_ptr<SwOutlineNodes> xTmpOutlNds;
171 SwNode* pStartNd;
173 if ( !bOutline) {
174 if( pSplitColl )
176 // If it isn't an OutlineNumbering, then use an own array and collect the Nodes.
177 if( pSplitColl->GetAttrOutlineLevel() == 0 )
179 xTmpOutlNds.reset(new SwOutlineNodes);
180 pOutlNds = xTmpOutlNds.get();
181 SwIterator<SwTextNode,SwFormatColl> aIter( *pSplitColl );
182 for( SwTextNode* pTNd = aIter.First(); pTNd; pTNd = aIter.Next() )
183 if( pTNd->GetNodes().IsDocNodes() )
184 pOutlNds->insert( pTNd );
186 if( pOutlNds->empty() )
187 return false;
190 else
192 // Look for the 1st level OutlineTemplate
193 const SwTextFormatColls& rFormatColls =*GetTextFormatColls();
194 for( SwTextFormatColls::size_type n = rFormatColls.size(); n; )
195 if ( rFormatColls[ --n ]->GetAttrOutlineLevel() == 1 )
197 pSplitColl = rFormatColls[ n ];
198 break;
201 if( !pSplitColl )
202 return false;
206 std::shared_ptr<const SfxFilter> pFilter;
207 switch( eDocType )
209 case SPLITDOC_TO_HTML:
210 pFilter = SwIoSystem::GetFilterOfFormat(u"HTML");
211 break;
213 default:
214 pFilter = SwIoSystem::GetFilterOfFormat(FILTER_XML);
215 eDocType = SPLITDOC_TO_GLOBALDOC;
216 break;
219 if( !pFilter )
220 return false;
222 // Deactivate Undo/Redline in any case
223 GetIDocumentUndoRedo().DoUndo(false);
224 getIDocumentRedlineAccess().SetRedlineFlags_intern( getIDocumentRedlineAccess().GetRedlineFlags() & ~RedlineFlags::On );
226 OUString sExt = pFilter->GetSuffixes().getToken(0, ',');
227 if( sExt.isEmpty() )
229 sExt = ".sxw";
231 else
233 if( '.' != sExt[ 0 ] )
235 sExt = "." + sExt;
239 INetURLObject aEntry(rPath);
240 OUString sLeading(aEntry.GetBase());
241 aEntry.removeSegment();
242 OUString sPath = aEntry.GetMainURL( INetURLObject::DecodeMechanism::NONE );
243 utl::TempFileNamed aTemp(sLeading, true, sExt, &sPath);
244 aTemp.EnableKillingFile();
246 DateTime aTmplDate( DateTime::SYSTEM );
248 tools::Time a2Min( 0 ); a2Min.SetMin( 2 );
249 aTmplDate += a2Min;
252 // Skip all invalid ones
253 while( nOutl < pOutlNds->size() &&
254 (*pOutlNds)[ nOutl ]->GetIndex() < GetNodes().GetEndOfExtras().GetIndex() )
255 ++nOutl;
257 do {
258 if( bOutline )
259 pStartNd = GetStartNode( pOutlNds, nOutlineLevel, &nOutl );
260 else
261 pStartNd = GetStartNode( pOutlNds, pSplitColl, &nOutl );
263 if( pStartNd )
265 SwNode* pEndNd;
266 if( bOutline )
267 pEndNd = GetEndNode( pOutlNds, nOutlineLevel, &nOutl );
268 else
269 pEndNd = GetEndNode( pOutlNds, pSplitColl, &nOutl );
270 SwNodeIndex aEndIdx( pEndNd ? *pEndNd
271 : GetNodes().GetEndOfContent() );
273 // Write out the Nodes completely
274 OUString sFileName;
275 if( pStartNd->GetIndex() + 1 < aEndIdx.GetIndex() )
277 SfxObjectShellLock xDocSh( new SwDocShell( SfxObjectCreateMode::INTERNAL ));
278 if( xDocSh->DoInitNew() )
280 SwDoc* pDoc = static_cast<SwDocShell*>(&xDocSh)->GetDoc();
282 uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
283 static_cast<SwDocShell*>(&xDocSh)->GetModel(),
284 uno::UNO_QUERY_THROW);
285 uno::Reference<document::XDocumentProperties> xDocProps(
286 xDPS->getDocumentProperties());
287 OSL_ENSURE(xDocProps.is(), "Doc has no DocumentProperties");
288 // the GlobalDoc is the template
289 xDocProps->setTemplateName(OUString());
290 ::util::DateTime uDT = aTmplDate.GetUNODateTime();
291 xDocProps->setTemplateDate(uDT);
292 xDocProps->setTemplateURL(rPath);
293 // Set the new doc's title to the text of the "split para".
294 // If the current doc has a title, insert it at the begin.
295 OUString sTitle( xDocProps->getTitle() );
296 if (!sTitle.isEmpty())
297 sTitle += ": ";
298 sTitle += pStartNd->GetTextNode()->GetExpandText(nullptr);
299 xDocProps->setTitle( sTitle );
301 // Replace template
302 pDoc->ReplaceStyles( *this );
304 // Take over chapter numbering
305 if( mpOutlineRule )
306 pDoc->SetOutlineNumRule( *mpOutlineRule );
308 SwNodeRange aRg( *pStartNd, SwNodeOffset(0), aEndIdx.GetNode() );
309 GetDocumentContentOperationsManager().CopyWithFlyInFly(
310 aRg, pDoc->GetNodes().GetEndOfContent(), nullptr, false, false);
312 // Delete the initial TextNode
313 SwNodeIndex aIdx( pDoc->GetNodes().GetEndOfExtras(), 2 );
314 if( aIdx.GetIndex() + 1 !=
315 pDoc->GetNodes().GetEndOfContent().GetIndex() )
316 pDoc->GetNodes().Delete( aIdx );
318 sFileName = utl::CreateTempURL(sLeading, true, sExt, &sPath);
319 SfxMedium* pTmpMed = new SfxMedium( sFileName,
320 StreamMode::STD_READWRITE );
321 pTmpMed->SetFilter( pFilter );
323 // We need to have a Layout for the HTMLFilter, so that
324 // TextFrames/Controls/OLE objects can be exported correctly as graphics.
325 if( SPLITDOC_TO_HTML == eDocType &&
326 !pDoc->GetSpzFrameFormats()->empty() )
328 SfxViewFrame::LoadHiddenDocument( *xDocSh, SFX_INTERFACE_NONE );
330 xDocSh->DoSaveAs( *pTmpMed );
331 xDocSh->DoSaveCompleted( pTmpMed );
333 // do not insert a FileLinkSection in case of error
334 if( xDocSh->GetError() )
335 sFileName.clear();
337 xDocSh->DoClose();
340 // We can now insert the section
341 if( !sFileName.isEmpty() )
343 switch( eDocType )
345 case SPLITDOC_TO_HTML:
347 // Delete all nodes in the section and, in the "start node",
348 // set the Link to the saved document.
349 SwNodeOffset nNodeDiff = aEndIdx.GetIndex() -
350 pStartNd->GetIndex() - 1;
351 if( nNodeDiff )
353 SwPaM aTmp( *pStartNd, aEndIdx.GetNode(), SwNodeOffset(1), SwNodeOffset(-1) );
354 SwNodeIndex aSIdx( aTmp.GetMark()->GetNode() );
355 SwNodeIndex aEIdx( aTmp.GetPoint()->GetNode() );
357 // Try to move past the end
358 if( !aTmp.Move( fnMoveForward, GoInNode ) )
360 // well then, back to the beginning
361 aTmp.Exchange();
362 if( !aTmp.Move( fnMoveBackward, GoInNode ))
364 OSL_FAIL( "no more Nodes!" );
367 // Move Bookmarks and so forth
368 CorrAbs( aSIdx, aEIdx, *aTmp.GetPoint(), true);
370 // If FlyFrames are still around, delete these too
371 auto& rSpzs = *GetSpzFrameFormats();
372 for(sw::FrameFormats<sw::SpzFrameFormat*>::size_type n = 0; n < GetSpzFrameFormats()->size(); ++n)
374 auto pFly = rSpzs[n];
375 const SwFormatAnchor* pAnchor = &pFly->GetAnchor();
376 SwNode const*const pAnchorNode =
377 pAnchor->GetAnchorNode();
378 if (pAnchorNode &&
379 ((RndStdIds::FLY_AT_PARA == pAnchor->GetAnchorId()) ||
380 (RndStdIds::FLY_AT_CHAR == pAnchor->GetAnchorId())) &&
381 aSIdx <= *pAnchorNode &&
382 *pAnchorNode < aEIdx.GetNode() )
384 getIDocumentLayoutAccess().DelLayoutFormat( pFly );
385 --n;
389 GetNodes().Delete( aSIdx, nNodeDiff );
392 // set the link in the StartNode
393 SwFormatINetFormat aINet( sFileName , OUString() );
394 SwTextNode* pTNd = pStartNd->GetTextNode();
395 pTNd->InsertItem(aINet, 0, pTNd->GetText().getLength());
397 // If the link cannot be found anymore,
398 // it has to be a bug!
399 if( !pOutlNds->Seek_Entry( pStartNd, &nOutl ))
400 pStartNd = nullptr;
401 ++nOutl ;
403 break;
405 default:
407 const OUString sNm(INetURLObject(sFileName).GetLastName());
408 SwSectionData aSectData( SectionType::FileLink,
409 GetUniqueSectionName( &sNm ));
410 SwSectionFormat* pFormat = MakeSectionFormat();
411 aSectData.SetLinkFileName(sFileName);
412 aSectData.SetProtectFlag(true);
414 --aEndIdx; // in the InsertSection the end is inclusive
415 while( aEndIdx.GetNode().IsStartNode() )
416 --aEndIdx;
418 // If any Section ends or starts in the new sectionrange,
419 // they must end or start before or after the range!
420 SwSectionNode* pSectNd = pStartNd->FindSectionNode();
421 while( pSectNd && pSectNd->EndOfSectionIndex()
422 <= aEndIdx.GetIndex() )
424 const SwNode* pSectEnd = pSectNd->EndOfSectionNode();
425 if( pSectNd->GetIndex() + 1 ==
426 pStartNd->GetIndex() )
428 bool bMvIdx = aEndIdx == *pSectEnd;
429 DelSectionFormat( pSectNd->GetSection().GetFormat() );
430 if( bMvIdx )
431 --aEndIdx;
433 else
435 SwNodeRange aRg( *pStartNd, *pSectEnd );
436 SwNodeIndex aIdx( *pSectEnd, 1 );
437 GetNodes().MoveNodes( aRg, GetNodes(), aIdx.GetNode() );
439 pSectNd = pStartNd->FindSectionNode();
442 pSectNd = aEndIdx.GetNode().FindSectionNode();
443 while( pSectNd && pSectNd->GetIndex() >
444 pStartNd->GetIndex() )
446 // #i15712# don't attempt to split sections if
447 // they are fully enclosed in [pSectNd,aEndIdx].
448 if( aEndIdx < pSectNd->EndOfSectionIndex() )
450 SwNodeRange aRg( *pSectNd, SwNodeOffset(1), aEndIdx.GetNode(), SwNodeOffset(1) );
451 GetNodes().MoveNodes( aRg, GetNodes(), *pSectNd );
454 pSectNd = pStartNd->FindSectionNode();
457 // -> #i26762#
458 // Ensure order of start and end of section is sane.
459 SwNodeIndex aStartIdx(*pStartNd);
461 if (aEndIdx >= aStartIdx)
463 pSectNd = GetNodes().InsertTextSection(aStartIdx.GetNode(),
464 *pFormat, aSectData, nullptr, &aEndIdx.GetNode(), false);
466 else
468 pSectNd = GetNodes().InsertTextSection(aEndIdx.GetNode(),
469 *pFormat, aSectData, nullptr, &aStartIdx.GetNode(), false);
471 // <- #i26762#
473 pSectNd->GetSection().CreateLink( LinkCreateType::Connect );
475 break;
479 } while( pStartNd );
481 xTmpOutlNds.reset();
483 switch( eDocType )
485 case SPLITDOC_TO_HTML:
486 if( GetDocumentSettingManager().get(DocumentSettingId::GLOBAL_DOCUMENT) )
488 // save all remaining sections
489 while( !GetSections().empty() )
490 DelSectionFormat( GetSections().front() );
492 SfxFilterContainer* pFCntnr = mpDocShell->GetFactory().GetFilterContainer();
493 pFilter = pFCntnr->GetFilter4EA( pFilter->GetTypeName(), SfxFilterFlags::EXPORT );
495 break;
497 default:
498 // save the Globaldoc
499 GetDocumentSettingManager().set(DocumentSettingId::GLOBAL_DOCUMENT, true);
500 GetDocumentSettingManager().set(DocumentSettingId::GLOBAL_DOCUMENT_SAVE_LINKS, false);
503 // The medium isn't locked after reopening the document.
504 SfxRequest aReq( SID_SAVEASDOC, SfxCallMode::SYNCHRON, GetAttrPool() );
505 aReq.AppendItem( SfxStringItem( SID_FILE_NAME, rPath ) );
506 aReq.AppendItem( SfxBoolItem( SID_SAVETO, true ) );
507 if(pFilter)
508 aReq.AppendItem( SfxStringItem( SID_FILTER_NAME, pFilter->GetName() ) );
509 const SfxBoolItem *pRet = static_cast<const SfxBoolItem*>(mpDocShell->ExecuteSlot( aReq ));
511 return pRet && pRet->GetValue();
514 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */