1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 <com/sun/star/embed/ElementModes.hpp>
21 #include <com/sun/star/embed/XTransactedObject.hpp>
22 #include <com/sun/star/embed/XStorage.hpp>
23 #include <com/sun/star/container/ElementExistException.hpp>
24 #include <com/sun/star/beans/XPropertySet.hpp>
25 #include <osl/file.hxx>
26 #include <rtl/ustring.hxx>
27 #include <sal/log.hxx>
28 #include <sot/exchange.hxx>
29 #include <sot/stg.hxx>
30 #include <sfx2/docfile.hxx>
31 #include <tools/urlobj.hxx>
32 #include <comphelper/diagnose_ex.hxx>
33 #include <unotools/ucbstreamhelper.hxx>
34 #include <o3tl/string_view.hxx>
36 #include <comphelper/storagehelper.hxx>
38 #include <IDocumentUndoRedo.hxx>
39 #include <IDocumentStylePoolAccess.hxx>
42 #include <swblocks.hxx>
44 #include <shellio.hxx>
45 #include <poolfmt.hxx>
46 #include <SwXMLTextBlocks.hxx>
49 using namespace ::com::sun::star
;
51 void SwXMLTextBlocks::InitBlockMode ( const uno::Reference
< embed::XStorage
>& rStorage
)
53 m_xBlkRoot
= rStorage
;
57 void SwXMLTextBlocks::ResetBlockMode ( )
63 SwXMLTextBlocks::SwXMLTextBlocks( const OUString
& rFile
)
65 , m_nFlags(SwXmlFlags::NONE
)
67 SwDocShell
* pDocSh
= new SwDocShell ( SfxObjectCreateMode::INTERNAL
);
68 if( !pDocSh
->DoInitNew() )
71 m_xDoc
= pDocSh
->GetDoc();
72 m_xDocShellRef
= pDocSh
;
73 m_xDoc
->SetOle2Link( Link
<bool,void>() );
74 m_xDoc
->GetIDocumentUndoRedo().DoUndo(false);
75 uno::Reference
< embed::XStorage
> refStg
;
76 if( !m_aDateModified
.GetDate() || !m_aTimeModified
.GetTime() )
77 Touch(); // If it's created anew -> get a new timestamp
81 refStg
= comphelper::OStorageHelper::GetStorageFromURL( rFile
, embed::ElementModes::READWRITE
);
84 catch(const uno::Exception
&)
86 //FIXME: couldn't open the file - maybe it's readonly
92 refStg
= comphelper::OStorageHelper::GetStorageFromURL( rFile
, embed::ElementModes::READ
);
94 catch(const uno::Exception
&)
96 TOOLS_WARN_EXCEPTION( "sw", "exception while creating AutoText storage");
99 InitBlockMode ( refStg
);
102 m_bInfoChanged
= false;
105 SwXMLTextBlocks::SwXMLTextBlocks( const uno::Reference
< embed::XStorage
>& rStg
, const OUString
& rName
)
106 : SwImpBlocks( rName
)
107 , m_nFlags(SwXmlFlags::NONE
)
109 SwDocShell
* pDocSh
= new SwDocShell ( SfxObjectCreateMode::INTERNAL
);
110 if( !pDocSh
->DoInitNew() )
113 m_xDoc
= pDocSh
->GetDoc();
114 m_xDocShellRef
= pDocSh
;
115 m_xDoc
->SetOle2Link( Link
<bool,void>() );
116 m_xDoc
->GetIDocumentUndoRedo().DoUndo(false);
118 InitBlockMode ( rStg
);
120 m_bInfoChanged
= false;
123 SwXMLTextBlocks::~SwXMLTextBlocks()
125 if ( m_bInfoChanged
)
128 if(m_xDocShellRef
.is())
129 m_xDocShellRef
->DoClose();
130 m_xDocShellRef
= nullptr;
133 void SwXMLTextBlocks::ClearDoc()
135 SwDocShell
* pDocShell
= m_xDoc
->GetDocShell();
136 pDocShell
->InvalidateModel();
137 pDocShell
->ReactivateModel();
140 pDocShell
->ClearEmbeddedObjects();
143 void SwXMLTextBlocks::AddName( const OUString
& rShort
, const OUString
& rLong
, bool bOnlyText
)
145 m_aPackageName
= GeneratePackageName( rShort
);
146 AddName(rShort
, rLong
, m_aPackageName
, bOnlyText
);
149 void SwXMLTextBlocks::AddName( const OUString
& rShort
, const OUString
& rLong
,
150 const OUString
& rPackageName
, bool bOnlyText
)
152 sal_uInt16 nIdx
= GetIndex( rShort
);
153 if (nIdx
!= USHRT_MAX
)
155 m_aNames
.erase( m_aNames
.begin() + nIdx
);
157 std::unique_ptr
<SwBlockName
> pNew(new SwBlockName( rShort
, rLong
, rPackageName
));
158 pNew
->m_bIsOnlyTextFlagInit
= true;
159 pNew
->m_bIsOnlyText
= bOnlyText
;
160 m_aNames
.insert( std::move(pNew
) );
161 m_bInfoChanged
= true;
164 ErrCode
SwXMLTextBlocks::Delete( sal_uInt16 n
)
166 const OUString
aPckName (m_aNames
[n
]->m_aPackageName
);
167 if ( m_xBlkRoot
.is() &&
168 m_xBlkRoot
->hasByName( aPckName
) && m_xBlkRoot
->isStreamElement( aPckName
) )
172 m_xBlkRoot
->removeElement ( aPckName
);
173 uno::Reference
< embed::XTransactedObject
> xTrans( m_xBlkRoot
, uno::UNO_QUERY
);
178 catch (const uno::Exception
&)
180 return ERR_SWG_WRITE_ERROR
;
186 ErrCode
SwXMLTextBlocks::Rename( sal_uInt16 nIdx
, const OUString
& rNewShort
)
188 OSL_ENSURE( m_xBlkRoot
.is(), "No storage set" );
191 OUString
aOldName (m_aNames
[nIdx
]->m_aPackageName
);
192 m_aShort
= rNewShort
;
193 m_aPackageName
= GeneratePackageName( m_aShort
);
195 if(aOldName
!= m_aPackageName
)
197 if (IsOnlyTextBlock ( nIdx
) )
199 OUString
sExt(".xml");
200 OUString aOldStreamName
= aOldName
+ sExt
;
201 OUString aNewStreamName
= m_aPackageName
+ sExt
;
203 m_xRoot
= m_xBlkRoot
->openStorageElement( aOldName
, embed::ElementModes::READWRITE
);
206 m_xRoot
->renameElement ( aOldStreamName
, aNewStreamName
);
208 catch(const container::ElementExistException
&)
210 SAL_WARN("sw", "Couldn't rename " << aOldStreamName
<< " to " << aNewStreamName
);
212 uno::Reference
< embed::XTransactedObject
> xTrans( m_xRoot
, uno::UNO_QUERY
);
220 m_xBlkRoot
->renameElement ( aOldName
, m_aPackageName
);
222 catch(const container::ElementExistException
&)
224 SAL_WARN("sw", "Couldn't rename " << aOldName
<< " to " << m_aPackageName
);
227 uno::Reference
< embed::XTransactedObject
> xTrans( m_xBlkRoot
, uno::UNO_QUERY
);
230 // No need to commit xBlkRoot here as SwTextBlocks::Rename calls
231 // WriteInfo which does the commit
235 ErrCode
SwXMLTextBlocks::CopyBlock( SwImpBlocks
& rDestImp
, OUString
& rShort
,
236 const OUString
& rLong
)
238 ErrCode nError
= ERRCODE_NONE
;
240 rDestImp
.OpenFile(false);
241 const OUString
aGroup( rShort
);
242 bool bTextOnly
= IsOnlyTextBlock ( rShort
) ;//pImp->pBlkRoot->IsStream( aGroup );
243 sal_uInt16 nIndex
= GetIndex ( rShort
);
244 OUString
sPackageName( GetPackageName (nIndex
) );
245 OUString
sDestShortName( sPackageName
);
248 OSL_ENSURE( m_xBlkRoot
.is(), "No storage set" );
250 return ERR_SWG_WRITE_ERROR
;
252 uno::Reference
< container::XNameAccess
> xAccess(static_cast<SwXMLTextBlocks
&>(rDestImp
).m_xBlkRoot
);
253 while ( xAccess
->hasByName( sDestShortName
) )
256 // If someone is that crazy ...
257 if(USHRT_MAX
== nIdx
)
260 rDestImp
.CloseFile();
261 return ERR_SWG_WRITE_ERROR
;
263 sDestShortName
= sPackageName
+ OUString::number( nIdx
);
268 uno::Reference
< embed::XStorage
> rSourceRoot
= m_xBlkRoot
->openStorageElement( aGroup
, embed::ElementModes::READ
);
269 uno::Reference
< embed::XStorage
> rDestRoot
= static_cast<SwXMLTextBlocks
&>(rDestImp
).m_xBlkRoot
->openStorageElement( sDestShortName
, embed::ElementModes::READWRITE
);
270 rSourceRoot
->copyToStorage( rDestRoot
);
272 catch (const uno::Exception
&)
274 nError
= ERR_SWG_WRITE_ERROR
;
279 rShort
= sDestShortName
;
280 static_cast<SwXMLTextBlocks
&>(rDestImp
).AddName( rShort
, rLong
, bTextOnly
);
281 static_cast<SwXMLTextBlocks
&>(rDestImp
).MakeBlockList();
284 rDestImp
.CloseFile();
288 ErrCode
SwXMLTextBlocks::StartPutBlock( const OUString
& rShort
, const OUString
& rPackageName
)
290 OSL_ENSURE( m_xBlkRoot
.is(), "No storage set" );
296 m_xRoot
= m_xBlkRoot
->openStorageElement( rPackageName
, embed::ElementModes::READWRITE
);
298 uno::Reference
< beans::XPropertySet
> xRootProps( m_xRoot
, uno::UNO_QUERY_THROW
);
299 OUString
aMime( SotExchange::GetFormatMimeType( SotClipboardFormatId::STARWRITER_8
) );
300 xRootProps
->setPropertyValue( "MediaType", uno::Any( aMime
) );
302 catch (const uno::Exception
&)
308 ErrCode
SwXMLTextBlocks::BeginPutDoc( const OUString
& rShort
, const OUString
& rLong
)
310 // Store in base class
313 m_aPackageName
= GeneratePackageName( rShort
);
314 SetIsTextOnly( rShort
, false);
315 return StartPutBlock (rShort
, m_aPackageName
);
318 ErrCode
SwXMLTextBlocks::PutBlock()
320 ErrCode nRes
= ERRCODE_NONE
; // dead variable, this always returns 0
321 SwXmlFlags nCommitFlags
= m_nFlags
;
324 ::GetXMLWriter ( std::u16string_view(), GetBaseURL(), xWrt
);
325 SwWriter
aWriter (m_xRoot
, *m_xDoc
);
327 xWrt
->m_bBlock
= true;
328 nRes
= aWriter
.Write ( xWrt
);
329 xWrt
->m_bBlock
= false;
330 // Save OLE objects if there are some
331 SwDocShell
*pDocSh
= m_xDoc
->GetDocShell();
333 bool bHasChildren
= pDocSh
&& pDocSh
->GetEmbeddedObjectContainer().HasEmbeddedObjects();
334 if( !nRes
&& bHasChildren
)
336 // we have to write to the temporary storage first, since the used below functions are optimized
337 // TODO/LATER: it is only a temporary solution, that should be changed soon, the used methods should be
338 // called without optimization
343 std::unique_ptr
<SfxMedium
> pTmpMedium
;
346 uno::Reference
< embed::XStorage
> xTempStorage
=
347 ::comphelper::OStorageHelper::GetTemporaryStorage();
349 m_xRoot
->copyToStorage( xTempStorage
);
351 // TODO/LATER: no progress bar?!
352 // TODO/MBA: strange construct
353 pTmpMedium
.reset(new SfxMedium(xTempStorage
, GetBaseURL()));
354 bool bTmpOK
= pDocSh
->SaveAsChildren( *pTmpMedium
);
356 bTmpOK
= pDocSh
->SaveCompletedChildren();
358 xTempStorage
->copyToStorage( m_xRoot
);
361 catch(const uno::Exception
&)
367 nRes
= ERR_SWG_WRITE_ERROR
;
372 uno::Reference
< embed::XTransactedObject
> xTrans( m_xRoot
, uno::UNO_QUERY
);
376 if ( nCommitFlags
== SwXmlFlags::NONE
)
378 uno::Reference
< embed::XTransactedObject
> xTmpTrans( m_xBlkRoot
, uno::UNO_QUERY
);
379 if ( xTmpTrans
.is() )
383 catch (const uno::Exception
&)
387 //TODO/LATER: error handling
391 ErrCode
SwXMLTextBlocks::PutDoc()
393 std::unique_ptr
<SwPaM
> pPaM
= MakePaM();
394 ErrCode nErr
= PutBlock();
398 ErrCode
SwXMLTextBlocks::GetText( std::u16string_view rShort
, OUString
& rText
)
400 return GetBlockText( rShort
, rText
);
403 ErrCode
SwXMLTextBlocks::MakeBlockList()
409 bool SwXMLTextBlocks::PutMuchEntries( bool bOn
)
414 if( m_bInPutMuchBlocks
)
416 OSL_ENSURE( false, "Nested calls are not allowed");
418 else if( !IsFileChanged() )
420 bRet
= ERRCODE_NONE
== OpenFile( false );
423 m_nFlags
|= SwXmlFlags::NoRootCommit
;
424 m_bInPutMuchBlocks
= true;
428 else if( m_bInPutMuchBlocks
)
430 m_nFlags
&= ~SwXmlFlags::NoRootCommit
;
431 if( m_xBlkRoot
.is() )
435 uno::Reference
< embed::XTransactedObject
> xTrans( m_xBlkRoot
, uno::UNO_QUERY
);
441 m_bInPutMuchBlocks
= false;
444 catch (const uno::Exception
&)
452 ErrCode
SwXMLTextBlocks::OpenFile( bool bRdOnly
)
454 ErrCode nRet
= ERRCODE_NONE
;
457 uno::Reference
< embed::XStorage
> refStg
= comphelper::OStorageHelper::GetStorageFromURL( m_aFile
,
458 bRdOnly
? embed::ElementModes::READ
: embed::ElementModes::READWRITE
);
459 InitBlockMode ( refStg
);
461 catch (const uno::Exception
&)
463 //TODO/LATER: error handling
470 void SwXMLTextBlocks::CloseFile()
477 void SwXMLTextBlocks::SetIsTextOnly( const OUString
& rShort
, bool bNewValue
)
479 sal_uInt16 nIdx
= GetIndex ( rShort
);
480 if (nIdx
!= USHRT_MAX
)
481 m_aNames
[nIdx
]->m_bIsOnlyText
= bNewValue
;
484 bool SwXMLTextBlocks::IsOnlyTextBlock( const OUString
& rShort
) const
486 sal_uInt16 nIdx
= GetIndex ( rShort
);
488 if (nIdx
!= USHRT_MAX
)
490 bRet
= m_aNames
[nIdx
]->m_bIsOnlyText
;
494 bool SwXMLTextBlocks::IsOnlyTextBlock( sal_uInt16 nIdx
) const
496 return m_aNames
[nIdx
]->m_bIsOnlyText
;
499 bool SwXMLTextBlocks::IsFileUCBStorage( const OUString
& rFileName
)
501 OUString
aName( rFileName
);
502 INetURLObject
aObj( aName
);
503 if ( aObj
.GetProtocol() == INetProtocol::NotValid
)
506 osl::FileBase::getFileURLFromSystemPath( aName
, aURL
);
508 aName
= aObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
);
511 std::unique_ptr
<SvStream
> pStm
= ::utl::UcbStreamHelper::CreateStream( aName
, StreamMode::STD_READ
);
512 bool bRet
= UCBStorage::IsStorageFile( pStm
.get() );
516 OUString
SwXMLTextBlocks::GeneratePackageName ( std::u16string_view rShort
)
518 OString
sByte(OUStringToOString(rShort
, RTL_TEXTENCODING_UTF7
));
519 OUStringBuffer
aBuf(OStringToOUString(sByte
, RTL_TEXTENCODING_ASCII_US
));
520 const sal_Int32 nLen
= aBuf
.getLength();
521 for (sal_Int32 nPos
=0; nPos
<nLen
; ++nPos
)
536 return aBuf
.makeStringAndClear();
539 ErrCode
SwXMLTextBlocks::PutText( const OUString
& rShort
, const OUString
& rName
,
540 const OUString
& rText
)
542 ErrCode nRes
= ERRCODE_NONE
;
545 m_aCurrentText
= rText
;
546 SetIsTextOnly( m_aShort
, true );
547 m_aPackageName
= GeneratePackageName( rShort
);
549 nRes
= PutBlockText( rShort
, rText
, m_aPackageName
);
553 void SwXMLTextBlocks::MakeBlockText( std::u16string_view rText
)
555 SwTextNode
* pTextNode
= m_xDoc
->GetNodes()[ m_xDoc
->GetNodes().GetEndOfContent().
556 GetIndex() - 1 ]->GetTextNode();
557 if( pTextNode
->GetTextColl() == m_xDoc
->GetDfltTextFormatColl() )
558 pTextNode
->ChgFormatColl( m_xDoc
->getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_STANDARD
));
565 pTextNode
= static_cast<SwTextNode
*>(pTextNode
->AppendNode( SwPosition( *pTextNode
) ));
567 SwContentIndex
aIdx( pTextNode
);
568 pTextNode
->InsertText( OUString(o3tl::getToken(rText
, 0, '\015', nPos
)), aIdx
);
569 } while ( -1 != nPos
);
572 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */