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 <DocumentLinksAdministrationManager.hxx>
23 #include <DocumentSettingManager.hxx>
24 #include <IDocumentUndoRedo.hxx>
25 #include <IDocumentState.hxx>
26 #include <IDocumentMarkAccess.hxx>
27 #include <sfx2/objsh.hxx>
28 #include <sfx2/linkmgr.hxx>
29 #include <sfx2/docfile.hxx>
30 #include <dialoghelp.hxx>
31 #include <linkenum.hxx>
32 #include <com/sun/star/document/UpdateDocMode.hpp>
33 #include <swtypes.hxx>
35 #include <bookmark.hxx>
37 #include <swbaslnk.hxx>
38 #include <section.hxx>
41 #include <fmtcntnt.hxx>
42 #include <swtable.hxx>
44 #include <frameformats.hxx>
45 #include <tools/urlobj.hxx>
46 #include <unotools/charclass.hxx>
47 #include <unotools/securityoptions.hxx>
50 using namespace ::com::sun::star
;
52 //Helper functions for this file
55 ::sfx2::SvBaseLink
* lcl_FindNextRemovableLink( const ::sfx2::SvBaseLinks
& rLinks
)
57 for (const auto& rLinkIter
: rLinks
)
59 ::sfx2::SvBaseLink
& rLnk
= *rLinkIter
;
60 if ((sfx2::SvBaseLinkObjectType::ClientGraphic
== rLnk
.GetObjType() || sfx2::SvBaseLinkObjectType::ClientFile
== rLnk
.GetObjType())
61 && dynamic_cast<const SwBaseLink
*>(&rLnk
) != nullptr)
63 tools::SvRef
<sfx2::SvBaseLink
> xLink(&rLnk
);
66 sfx2::LinkManager::GetDisplayNames( xLink
.get(), nullptr, &sFName
);
68 INetURLObject
aURL( sFName
);
69 if( INetProtocol::File
== aURL
.GetProtocol() ||
70 INetProtocol::Cid
== aURL
.GetProtocol() )
78 ::sw::mark::DdeBookmark
* lcl_FindDdeBookmark( const IDocumentMarkAccess
& rMarkAccess
, const OUString
& rName
, const bool bCaseSensitive
)
80 //Iterating over all bookmarks, checking DdeBookmarks
81 const OUString sNameLc
= bCaseSensitive
? rName
: GetAppCharClass().lowercase(rName
);
82 for(IDocumentMarkAccess::const_iterator_t ppMark
= rMarkAccess
.getAllMarksBegin();
83 ppMark
!= rMarkAccess
.getAllMarksEnd();
86 if (::sw::mark::DdeBookmark
* const pBkmk
= dynamic_cast< ::sw::mark::DdeBookmark
*>(*ppMark
))
89 (bCaseSensitive
&& (pBkmk
->GetName() == sNameLc
)) ||
90 (!bCaseSensitive
&& GetAppCharClass().lowercase(pBkmk
->GetName()) == sNameLc
)
101 SwSectionNode
* lcl_FindSection(const SwDoc
& rDoc
, const OUString
& rItem
, bool bCaseSensitive
)
103 const OUString sCompare
= bCaseSensitive
? rItem
: GetAppCharClass().lowercase(rItem
);
104 for (const SwSectionFormat
* pSectFormat
: rDoc
.GetSections())
106 SwSection
* pSect
= pSectFormat
->GetSection();
109 OUString
sNm(bCaseSensitive
? pSect
->GetSectionName()
110 : GetAppCharClass().lowercase(pSect
->GetSectionName()));
113 // found, so get the data
114 const SwNodeIndex
* pIdx
= pSectFormat
->GetContent().GetContentIdx();
115 if (pIdx
&& &pSectFormat
->GetDoc()->GetNodes() == &pIdx
->GetNodes())
117 // a table in the normal NodesArr
118 return pIdx
->GetNode().GetSectionNode();
120 // If the name is already correct, but not the rest then we don't have them.
121 // The names are always unique.
128 SwTableNode
* lcl_FindTable(const SwDoc
& rDoc
, const OUString
& rItem
)
130 const OUString
& aItem
= GetAppCharClass().lowercase(rItem
);
131 for (const SwFrameFormat
* pTableFormat
: *rDoc
.GetTableFrameFormats())
133 OUString
sNm(GetAppCharClass().lowercase(pTableFormat
->GetName()));
136 SwTable
* pTmpTable
= SwTable::FindTable(pTableFormat
);
139 SwTableBox
* pFBox
= pTmpTable
->GetTabSortBoxes()[0];
140 if (pFBox
&& pFBox
->GetSttNd()
141 && &pTableFormat
->GetDoc()->GetNodes() == &pFBox
->GetSttNd()->GetNodes())
143 // a table in the normal NodesArr
144 return const_cast<SwTableNode
*>(pFBox
->GetSttNd()->FindTableNode());
147 // If the name is already correct, but not the rest then we don't have them.
148 // The names are always unique.
160 DocumentLinksAdministrationManager::DocumentLinksAdministrationManager( SwDoc
& i_rSwdoc
)
161 : mbVisibleLinks(true)
162 , mbLinksUpdated( false ) //#i38810#
163 , m_pLinkMgr( new sfx2::LinkManager(nullptr) )
168 bool DocumentLinksAdministrationManager::IsVisibleLinks() const
170 return mbVisibleLinks
;
173 void DocumentLinksAdministrationManager::SetVisibleLinks(bool bFlag
)
175 mbVisibleLinks
= bFlag
;
178 sfx2::LinkManager
& DocumentLinksAdministrationManager::GetLinkManager()
183 const sfx2::LinkManager
& DocumentLinksAdministrationManager::GetLinkManager() const
188 // #i42634# Moved common code of SwReader::Read() and SwDocShell::UpdateLinks()
189 // to new SwDoc::UpdateLinks():
190 void DocumentLinksAdministrationManager::UpdateLinks()
192 if (!m_rDoc
.GetDocShell())
194 SfxObjectCreateMode eMode
= m_rDoc
.GetDocShell()->GetCreateMode();
195 if (eMode
== SfxObjectCreateMode::INTERNAL
)
197 if (eMode
== SfxObjectCreateMode::ORGANIZER
)
199 if (m_rDoc
.GetDocShell()->IsPreview())
201 if (GetLinkManager().GetLinks().empty())
203 sal_uInt16 nLinkMode
= m_rDoc
.GetDocumentSettingManager().getLinkUpdateMode(true);
204 sal_uInt16 nUpdateDocMode
= m_rDoc
.GetDocShell()->GetUpdateDocMode();
205 if (nLinkMode
== NEVER
&& nUpdateDocMode
!= document::UpdateDocMode::FULL_UPDATE
)
208 bool bAskUpdate
= nLinkMode
== MANUAL
;
210 switch(nUpdateDocMode
)
212 case document::UpdateDocMode::NO_UPDATE
: bUpdate
= false;break;
213 case document::UpdateDocMode::QUIET_UPDATE
:bAskUpdate
= false; break;
214 case document::UpdateDocMode::FULL_UPDATE
: bAskUpdate
= true; break;
216 if (nLinkMode
== AUTOMATIC
&& !bAskUpdate
)
218 SfxMedium
* medium
= m_rDoc
.GetDocShell()->GetMedium();
219 if (!SvtSecurityOptions::isTrustedLocationUriForUpdatingLinks(
220 medium
== nullptr ? OUString() : medium
->GetName()))
225 comphelper::EmbeddedObjectContainer
& rEmbeddedObjectContainer
= m_rDoc
.GetDocShell()->getEmbeddedObjectContainer();
228 rEmbeddedObjectContainer
.setUserAllowsLinkUpdate(true);
230 weld::Window
* pDlgParent
= GetFrameWeld(m_rDoc
.GetDocShell());
231 GetLinkManager().UpdateAllLinks(bAskUpdate
, false, pDlgParent
);
235 rEmbeddedObjectContainer
.setUserAllowsLinkUpdate(false);
239 bool DocumentLinksAdministrationManager::GetData( const OUString
& rItem
, const OUString
& rMimeType
,
240 uno::Any
& rValue
) const
242 // search for bookmarks and sections case sensitive at first. If nothing is found then try again case insensitive
243 bool bCaseSensitive
= true;
246 ::sw::mark::DdeBookmark
* const pBkmk
= lcl_FindDdeBookmark(*m_rDoc
.getIDocumentMarkAccess(), rItem
, bCaseSensitive
);
248 return SwServerObject(*pBkmk
).GetData(rValue
, rMimeType
);
250 // Do we already have the Item?
251 if (SwSectionNode
* pSectNd
= lcl_FindSection(m_rDoc
, rItem
, bCaseSensitive
))
253 // found, so get the data
254 return SwServerObject(*pSectNd
).GetData( rValue
, rMimeType
);
256 if( !bCaseSensitive
)
258 bCaseSensitive
= false;
261 if (SwTableNode
* pTableNd
= lcl_FindTable(m_rDoc
, rItem
))
263 return SwServerObject(*pTableNd
).GetData( rValue
, rMimeType
);
269 // TODO/FIXME: do something with the found items? For now, it's just an expensive no-op.
270 void DocumentLinksAdministrationManager::SetData( const OUString
& rItem
)
272 // search for bookmarks and sections case sensitive at first. If nothing is found then try again case insensitive
273 bool bCaseSensitive
= true;
276 ::sw::mark::DdeBookmark
* const pBkmk
= lcl_FindDdeBookmark(*m_rDoc
.getIDocumentMarkAccess(), rItem
, bCaseSensitive
);
282 // Do we already have the Item?
283 if (lcl_FindSection(m_rDoc
, rItem
, bCaseSensitive
))
285 // found, so get the data
288 if( !bCaseSensitive
)
290 bCaseSensitive
= false;
293 (void)lcl_FindTable(m_rDoc
, rItem
);
296 ::sfx2::SvLinkSource
* DocumentLinksAdministrationManager::CreateLinkSource(const OUString
& rItem
)
298 // search for bookmarks and sections case sensitive at first. If nothing is found then try again case insensitive
299 bool bCaseSensitive
= true;
303 ::sw::mark::DdeBookmark
* const pBkmk
= lcl_FindDdeBookmark(*m_rDoc
.getIDocumentMarkAccess(), rItem
, bCaseSensitive
);
304 if(pBkmk
&& pBkmk
->IsExpanded())
306 SwServerObject
* pObj
= pBkmk
->GetRefObject();
309 // mark found, but no link yet -> create hotlink
310 pObj
= new SwServerObject(*pBkmk
);
311 pBkmk
->SetRefObject(pObj
);
312 GetLinkManager().InsertServer(pObj
);
318 if (SwSectionNode
* pSectNd
= lcl_FindSection(m_rDoc
, rItem
, bCaseSensitive
))
320 SwServerObject
* pObj
= pSectNd
->GetSection().GetObject();
323 // section found, but no link yet -> create hotlink
324 pObj
= new SwServerObject(*pSectNd
);
325 pSectNd
->GetSection().SetRefObject( pObj
);
326 GetLinkManager().InsertServer(pObj
);
330 if( !bCaseSensitive
)
332 bCaseSensitive
= false;
336 if (SwTableNode
* pTableNd
= lcl_FindTable(m_rDoc
, rItem
))
338 SwServerObject
* pObj
= pTableNd
->GetTable().GetObject();
341 // table found, but no link yet -> create hotlink
342 pObj
= new SwServerObject(*pTableNd
);
343 pTableNd
->GetTable().SetRefObject(pObj
);
344 GetLinkManager().InsertServer(pObj
);
351 /// embedded all local links (Areas/Graphics)
352 bool DocumentLinksAdministrationManager::EmbedAllLinks()
355 sfx2::LinkManager
& rLnkMgr
= GetLinkManager();
356 const ::sfx2::SvBaseLinks
& rLinks
= rLnkMgr
.GetLinks();
357 if( !rLinks
.empty() )
359 ::sw::UndoGuard
const undoGuard(m_rDoc
.GetIDocumentUndoRedo());
361 ::sfx2::SvBaseLink
* pLnk
= nullptr;
362 while( nullptr != (pLnk
= lcl_FindNextRemovableLink( rLinks
) ) )
364 tools::SvRef
<sfx2::SvBaseLink
> xLink
= pLnk
;
365 // Tell the link that it's being destroyed!
368 // if one forgot to remove itself
370 rLnkMgr
.Remove( xLink
.get() );
375 m_rDoc
.GetIDocumentUndoRedo().DelAllUndoObj();
376 m_rDoc
.getIDocumentState().SetModified();
381 void DocumentLinksAdministrationManager::SetLinksUpdated(const bool bNewLinksUpdated
)
383 mbLinksUpdated
= bNewLinksUpdated
;
386 bool DocumentLinksAdministrationManager::LinksUpdated() const
388 return mbLinksUpdated
;
391 DocumentLinksAdministrationManager::~DocumentLinksAdministrationManager()
395 bool DocumentLinksAdministrationManager::SelectServerObj( std::u16string_view rStr
, SwPaM
*& rpPam
, std::optional
<SwNodeRange
>& roRange
) const
397 // Do we actually have the Item?
401 OUString
sItem( INetURLObject::decode( rStr
,
402 INetURLObject::DecodeMechanism::WithCharset
));
404 sal_Int32 nPos
= sItem
.indexOf( cMarkSeparator
);
406 // Extension for sections: not only link bookmarks/sections
407 // but also frames (text!), tables, outlines:
410 OUString
sName( sItem
.copy( 0, nPos
) );
411 std::u16string_view
sCmp( sItem
.subView( nPos
+ 1 ));
413 if( sCmp
== u
"table" )
415 if (SwTableNode
* pTableNd
= lcl_FindTable(m_rDoc
, sName
))
417 roRange
.emplace( *pTableNd
, SwNodeOffset(0),
418 *pTableNd
->EndOfSectionNode(), SwNodeOffset(1) );
420 return roRange
.has_value();
422 else if( sCmp
== u
"frame" )
424 const SwFlyFrameFormat
* pFlyFormat
= m_rDoc
.FindFlyByName( sName
);
427 SwNodeIndex
* pIdx
= const_cast<SwNodeIndex
*>(pFlyFormat
->GetContent().GetContentIdx());
430 SwNode
* pNd
= &pIdx
->GetNode();
431 if( !pNd
->IsNoTextNode() )
433 roRange
.emplace( *pNd
, SwNodeOffset(1), *pNd
->EndOfSectionNode() );
437 return roRange
.has_value();
439 else if( sCmp
== u
"region" )
441 sItem
= sName
; // Is being dealt with further down!
443 else if( sCmp
== u
"outline" )
445 SwPosition
aPos( m_rDoc
.GetNodes() );
446 if (m_rDoc
.GotoOutline(aPos
, sName
, nullptr))
448 SwNode
* pNd
= &aPos
.GetNode();
449 const int nLvl
= pNd
->GetTextNode()->GetAttrOutlineLevel()-1;
451 const SwOutlineNodes
& rOutlNds
= m_rDoc
.GetNodes().GetOutLineNds();
452 SwOutlineNodes::size_type nTmpPos
;
453 (void)rOutlNds
.Seek_Entry( pNd
, &nTmpPos
);
454 roRange
.emplace( aPos
.GetNode(), SwNodeOffset(0), aPos
.GetNode() );
456 // look for the section's end, now
458 nTmpPos
< rOutlNds
.size() &&
459 nLvl
< rOutlNds
[ nTmpPos
]->GetTextNode()->
460 GetAttrOutlineLevel()-1;
462 ; // there is no block
464 if( nTmpPos
< rOutlNds
.size() )
465 roRange
->aEnd
= *rOutlNds
[ nTmpPos
];
467 roRange
->aEnd
= m_rDoc
.GetNodes().GetEndOfContent();
469 return roRange
.has_value();
473 // search for bookmarks and sections case sensitive at first. If nothing is found then try again case insensitive
474 bool bCaseSensitive
= true;
477 ::sw::mark::DdeBookmark
* const pBkmk
= lcl_FindDdeBookmark(*m_rDoc
.getIDocumentMarkAccess(), sItem
, bCaseSensitive
);
480 if(pBkmk
->IsExpanded())
483 pBkmk
->GetOtherMarkPos());
484 return static_cast<bool>(rpPam
);
487 if( !m_rDoc
.GetSections().empty() )
489 if (SwSectionNode
* pSectNd
= lcl_FindSection(m_rDoc
, sItem
, bCaseSensitive
))
491 roRange
.emplace( *pSectNd
, SwNodeOffset(1),
492 *pSectNd
->EndOfSectionNode() );
497 if( !bCaseSensitive
)
499 bCaseSensitive
= false;
508 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */