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 <linkdlg.hxx>
21 #include <o3tl/safeint.hxx>
22 #include <vcl/svapp.hxx>
24 #include <comphelper/diagnose_ex.hxx>
25 #include <tools/debug.hxx>
26 #include <tools/urlobj.hxx>
27 #include <vcl/idle.hxx>
28 #include <vcl/timer.hxx>
29 #include <vcl/weld.hxx>
30 #include <vcl/weldutils.hxx>
32 #include <strings.hrc>
33 #include <sfx2/filedlghelper.hxx>
34 #include <sfx2/linkmgr.hxx>
35 #include <sfx2/linksrc.hxx>
36 #include <sfx2/lnkbase.hxx>
37 #include <sfx2/objsh.hxx>
39 #include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp>
40 #include <com/sun/star/ui/dialogs/XFolderPicker2.hpp>
41 #include <comphelper/processfactory.hxx>
43 #include <dialmgr.hxx>
47 using namespace ::com::sun::star
;
51 class SvBaseLinkMemberList
{
53 std::vector
<SvBaseLink
*> mLinks
;
56 ~SvBaseLinkMemberList()
58 for (auto const& link
: mLinks
)
65 size_t size() const { return mLinks
.size(); }
67 SvBaseLink
*operator[](size_t i
) const { return mLinks
[i
]; }
69 void push_back(SvBaseLink
* p
)
78 SvBaseLinksDlg::SvBaseLinksDlg(weld::Window
* pParent
, LinkManager
* pMgr
, bool bHtmlMode
)
79 : GenericDialogController(pParent
, u
"cui/ui/baselinksdialog.ui"_ustr
, u
"BaseLinksDialog"_ustr
)
80 , aStrAutolink( CuiResId( STR_AUTOLINK
) )
81 , aStrManuallink( CuiResId( STR_MANUALLINK
) )
82 , aStrBrokenlink( CuiResId( STR_BROKENLINK
) )
83 , aStrCloselinkmsg( CuiResId( STR_CLOSELINKMSG
) )
84 , aStrCloselinkmsgMulti( CuiResId( STR_CLOSELINKMSG_MULTI
) )
85 , aStrWaitinglink( CuiResId( STR_WAITINGLINK
) )
87 , aUpdateIdle("cui SvBaseLinksDlg UpdateIdle")
88 , m_xTbLinks(m_xBuilder
->weld_tree_view(u
"TB_LINKS"_ustr
))
89 , m_xFtFullFileName(m_xBuilder
->weld_link_button(u
"FULL_FILE_NAME"_ustr
))
90 , m_xFtFullSourceName(m_xBuilder
->weld_label(u
"FULL_SOURCE_NAME"_ustr
))
91 , m_xFtFullTypeName(m_xBuilder
->weld_label(u
"FULL_TYPE_NAME"_ustr
))
92 , m_xRbAutomatic(m_xBuilder
->weld_radio_button(u
"AUTOMATIC"_ustr
))
93 , m_xRbManual(m_xBuilder
->weld_radio_button(u
"MANUAL"_ustr
))
94 , m_xPbUpdateNow(m_xBuilder
->weld_button(u
"UPDATE_NOW"_ustr
))
95 , m_xPbChangeSource(m_xBuilder
->weld_button(u
"CHANGE_SOURCE"_ustr
))
96 , m_xPbBreakLink(m_xBuilder
->weld_button(u
"BREAK_LINK"_ustr
))
97 , m_xVirDev(VclPtr
<VirtualDevice
>::Create())
99 // expand the point size of the desired font to the equivalent pixel size
100 weld::SetPointFont(*m_xVirDev
, m_xTbLinks
->get_font());
101 m_xTbLinks
->set_size_request(m_xTbLinks
->get_approximate_digit_width() * 90,
102 m_xTbLinks
->get_height_rows(12));
104 m_xTbLinks
->set_selection_mode(SelectionMode::Multiple
);
106 std::vector
<int> aWidths
108 o3tl::narrowing
<int>(m_xTbLinks
->get_approximate_digit_width() * 30),
109 o3tl::narrowing
<int>(m_xTbLinks
->get_approximate_digit_width() * 20),
110 o3tl::narrowing
<int>(m_xTbLinks
->get_approximate_digit_width() * 20)
112 m_xTbLinks
->set_column_fixed_widths(aWidths
);
114 // UpdateTimer for DDE-/Grf-links, which are waited for
115 aUpdateIdle
.SetInvokeHandler( LINK( this, SvBaseLinksDlg
, UpdateWaitingHdl
) );
116 aUpdateIdle
.SetPriority( TaskPriority::LOWEST
);
118 m_xTbLinks
->connect_selection_changed(LINK(this, SvBaseLinksDlg
, LinksSelectHdl
));
119 m_xTbLinks
->connect_row_activated( LINK( this, SvBaseLinksDlg
, LinksDoubleClickHdl
) );
120 m_xRbAutomatic
->connect_toggled( LINK( this, SvBaseLinksDlg
, ToggleHdl
) );
121 m_xRbManual
->connect_toggled( LINK( this, SvBaseLinksDlg
, ToggleHdl
) );
122 m_xPbUpdateNow
->connect_clicked( LINK( this, SvBaseLinksDlg
, UpdateNowClickHdl
) );
123 m_xPbChangeSource
->connect_clicked( LINK( this, SvBaseLinksDlg
, ChangeSourceClickHdl
) );
125 m_xPbBreakLink
->connect_clicked( LINK( this, SvBaseLinksDlg
, BreakLinkClickHdl
) );
127 m_xPbBreakLink
->hide();
132 SvBaseLinksDlg::~SvBaseLinksDlg()
136 /*************************************************************************
137 |* SvBaseLinksDlg::Handler()
138 *************************************************************************/
139 IMPL_LINK(SvBaseLinksDlg
, LinksSelectHdl
, weld::TreeView
&, rTreeView
, void)
141 LinksSelectHdl(&rTreeView
);
144 void SvBaseLinksDlg::LinksSelectHdl(weld::TreeView
* pSvTabListBox
)
146 const int nSelectionCount
= pSvTabListBox
?
147 pSvTabListBox
->count_selected_rows() : 0;
148 if (nSelectionCount
> 1)
150 // possibly deselect old entries in case of multi-selection
151 int nSelEntry
= pSvTabListBox
->get_selected_index();
152 SvBaseLink
* pLink
= weld::fromId
<SvBaseLink
*>(pSvTabListBox
->get_id(nSelEntry
));
153 SvBaseLinkObjectType nObjectType
= pLink
->GetObjType();
154 if(!isClientFileType(nObjectType
))
156 pSvTabListBox
->unselect_all();
157 pSvTabListBox
->select(nSelEntry
);
161 std::vector
<int> aRows
= pSvTabListBox
->get_selected_rows();
162 for (auto nEntry
: aRows
)
164 pLink
= weld::fromId
<SvBaseLink
*>(pSvTabListBox
->get_id(nEntry
));
165 DBG_ASSERT(pLink
, "Where is the Link?");
168 if( !isClientFileType(pLink
->GetObjType()) )
169 pSvTabListBox
->unselect(nEntry
);
173 m_xPbUpdateNow
->set_sensitive(true);
174 m_xRbAutomatic
->set_sensitive(false);
175 m_xRbManual
->set_active(true);
176 m_xRbManual
->set_sensitive(false);
181 SvBaseLink
* pLink
= GetSelEntry( &nPos
);
185 m_xPbUpdateNow
->set_sensitive(true);
187 OUString sType
, sLink
;
188 OUString
*pLinkNm
= &sLink
, *pFilter
= nullptr;
190 if( isClientFileType(pLink
->GetObjType()) )
192 m_xRbAutomatic
->set_sensitive(false);
193 m_xRbManual
->set_active(true);
194 m_xRbManual
->set_sensitive(false);
195 if( SvBaseLinkObjectType::ClientGraphic
== pLink
->GetObjType() )
203 m_xRbAutomatic
->set_sensitive(true);
204 m_xRbManual
->set_sensitive(true);
206 if( SfxLinkUpdateMode::ALWAYS
== pLink
->GetUpdateMode() )
207 m_xRbAutomatic
->set_active(true);
209 m_xRbManual
->set_active(true);
213 sfx2::LinkManager::GetDisplayNames( pLink
, &sType
, &aFileName
, pLinkNm
, pFilter
);
214 aFileName
= INetURLObject::decode(aFileName
, INetURLObject::DecodeMechanism::Unambiguous
);
215 m_xFtFullFileName
->set_label( aFileName
);
216 m_xFtFullFileName
->set_uri( aFileName
);
217 m_xFtFullSourceName
->set_label( sLink
);
218 m_xFtFullTypeName
->set_label( sType
);
222 IMPL_LINK_NOARG( SvBaseLinksDlg
, LinksDoubleClickHdl
, weld::TreeView
&, bool )
224 ChangeSourceClickHdl(*m_xPbChangeSource
);
228 IMPL_LINK(SvBaseLinksDlg
, ToggleHdl
, weld::Toggleable
&, rButton
, void)
230 if (!rButton
.get_active())
234 SvBaseLink
* pLink
= GetSelEntry( &nPos
);
236 if (m_xRbAutomatic
->get_active())
238 if( pLink
&& !isClientFileType( pLink
->GetObjType() ) &&
239 SfxLinkUpdateMode::ALWAYS
!= pLink
->GetUpdateMode() )
240 SetType( *pLink
, nPos
, SfxLinkUpdateMode::ALWAYS
);
244 if( pLink
&& !isClientFileType( pLink
->GetObjType() ) &&
245 SfxLinkUpdateMode::ONCALL
!= pLink
->GetUpdateMode())
246 SetType( *pLink
, nPos
, SfxLinkUpdateMode::ONCALL
);
250 IMPL_LINK_NOARG(SvBaseLinksDlg
, UpdateNowClickHdl
, weld::Button
&, void)
252 std::vector
< SvBaseLink
* > aLnkArr
;
253 std::vector
< sal_Int16
> aPosArr
;
255 std::vector
<int> aRows
= m_xTbLinks
->get_selected_rows();
256 for (int nFndPos
: aRows
)
258 aLnkArr
.push_back( weld::fromId
<SvBaseLink
*>( m_xTbLinks
->get_id(nFndPos
) ) );
259 aPosArr
.push_back( nFndPos
);
262 if( aLnkArr
.empty() )
265 for( size_t n
= 0; n
< aLnkArr
.size(); ++n
)
267 tools::SvRef
<SvBaseLink
> xLink
= aLnkArr
[ n
];
269 // first look for the entry in the array
270 for(const auto & i
: pLinkMgr
->GetLinks())
273 SetType( *xLink
, aPosArr
[ n
], xLink
->GetUpdateMode() );
278 // if somebody is of the opinion to swap his links (SD)
279 LinkManager
* pNewMgr
= pLinkMgr
;
281 SetManager( pNewMgr
);
284 OUString sId
= weld::toId(aLnkArr
[0]);
285 int nE
= m_xTbLinks
->find_id(sId
);
287 nE
= m_xTbLinks
->get_selected_index();
288 int nSelEntry
= m_xTbLinks
->get_selected_index();
290 m_xTbLinks
->unselect(nSelEntry
);
291 m_xTbLinks
->select(nE
);
292 m_xTbLinks
->scroll_to_row(nE
);
294 pNewMgr
->CloseCachedComps();
297 IMPL_LINK_NOARG(SvBaseLinksDlg
, ChangeSourceClickHdl
, weld::Button
&, void)
299 std::vector
<int> aRows
= m_xTbLinks
->get_selected_rows();
300 if (aRows
.size() > 1)
304 uno::Reference
<ui::dialogs::XFolderPicker2
> xFolderPicker
= sfx2::createFolderPicker(
305 comphelper::getProcessComponentContext(), m_xDialog
.get());
307 OUString sType
, sFile
, sLinkName
;
309 SvBaseLink
* pLink
= weld::fromId
<SvBaseLink
*>(m_xTbLinks
->get_id(aRows
[0]));
310 sfx2::LinkManager::GetDisplayNames( pLink
, &sType
, &sFile
);
311 INetURLObject
aUrl(sFile
);
312 if(aUrl
.GetProtocol() == INetProtocol::File
)
314 OUString
sOldPath(aUrl
.PathToFileName());
315 sal_Int32 nLen
= aUrl
.GetLastName().getLength();
316 sOldPath
= sOldPath
.copy(0, sOldPath
.getLength() - nLen
);
317 xFolderPicker
->setDisplayDirectory(sOldPath
);
319 if (xFolderPicker
->execute() == ui::dialogs::ExecutableDialogResults::OK
)
321 OUString aPath
= xFolderPicker
->getDirectory();
323 for (auto nRow
: aRows
)
325 pLink
= weld::fromId
<SvBaseLink
*>(m_xTbLinks
->get_id(nRow
));
326 DBG_ASSERT(pLink
,"Where is the link?");
329 sfx2::LinkManager::GetDisplayNames( pLink
, &sType
, &sFile
, &sLinkName
, &sFilter
);
330 INetURLObject
aUrl_(sFile
);
331 INetURLObject
aUrl2(aPath
, INetProtocol::File
);
332 aUrl2
.insertName( aUrl_
.getName() );
333 OUString sNewLinkName
;
334 MakeLnkName( sNewLinkName
, nullptr ,
335 aUrl2
.GetMainURL(INetURLObject::DecodeMechanism::ToIUri
), sLinkName
, &sFilter
);
336 pLink
->SetLinkSourceName( sNewLinkName
);
339 if( pLinkMgr
->GetPersist() )
340 pLinkMgr
->GetPersist()->SetModified();
341 LinkManager
* pNewMgr
= pLinkMgr
;
343 SetManager( pNewMgr
);
346 catch (const uno::Exception
&)
348 TOOLS_WARN_EXCEPTION("cui.dialogs", "SvBaseLinksDlg");
354 SvBaseLink
* pLink
= GetSelEntry( &nPos
);
355 if ( pLink
&& !pLink
->GetLinkSourceName().isEmpty() )
356 pLink
->Edit(m_xDialog
.get(), LINK(this, SvBaseLinksDlg
, EndEditHdl
));
360 IMPL_LINK_NOARG( SvBaseLinksDlg
, BreakLinkClickHdl
, weld::Button
&, void )
362 bool bModified
= false;
363 if (m_xTbLinks
->count_selected_rows() <= 1)
366 tools::SvRef
<SvBaseLink
> xLink
= GetSelEntry( &nPos
);
370 std::unique_ptr
<weld::MessageDialog
> xQueryBox(Application::CreateMessageDialog(m_xDialog
.get(),
371 VclMessageType::Question
, VclButtonsType::YesNo
,
373 xQueryBox
->set_default_response(RET_YES
);
375 if (RET_YES
== xQueryBox
->run())
377 m_xTbLinks
->remove(nPos
);
379 // close object, if it's still existing
380 bool bNewLnkMgr
= SvBaseLinkObjectType::ClientFile
== xLink
->GetObjType();
382 // tell the link that it will be resolved!
385 // if somebody has forgotten to deregister himself
387 pLinkMgr
->Remove( xLink
.get() );
391 LinkManager
* pNewMgr
= pLinkMgr
;
393 SetManager( pNewMgr
);
394 m_xTbLinks
->set_cursor(nPos
? --nPos
: 0);
401 std::unique_ptr
<weld::MessageDialog
> xQueryBox(Application::CreateMessageDialog(m_xDialog
.get(),
402 VclMessageType::Question
, VclButtonsType::YesNo
,
403 aStrCloselinkmsgMulti
));
404 xQueryBox
->set_default_response(RET_YES
);
406 if (RET_YES
== xQueryBox
->run())
408 std::vector
<int> aRows
= m_xTbLinks
->get_selected_rows();
409 SvBaseLinkMemberList aLinkList
;
410 for (auto nRow
: aRows
)
412 SvBaseLink
* pLink
= weld::fromId
<SvBaseLink
*>(m_xTbLinks
->get_id(nRow
));
414 aLinkList
.push_back(pLink
);
416 std::sort(aRows
.begin(), aRows
.end());
417 for (auto it
= aRows
.rbegin(); it
!= aRows
.rend(); ++it
)
418 m_xTbLinks
->remove(*it
);
419 for (size_t i
= 0; i
< aLinkList
.size(); ++i
)
421 tools::SvRef
<SvBaseLink
> xLink
= aLinkList
[i
];
422 // tell the link that it will be resolved!
425 // if somebody has forgotten to deregister himself
426 pLinkMgr
->Remove( xLink
.get() );
429 // then remove all selected entries
435 if (!m_xTbLinks
->n_children())
437 m_xRbAutomatic
->set_sensitive(false);
438 m_xRbManual
->set_sensitive(false);
439 m_xPbUpdateNow
->set_sensitive(false);
440 m_xPbChangeSource
->set_sensitive(false);
441 m_xPbBreakLink
->set_sensitive(false);
443 m_xFtFullSourceName
->set_label( u
""_ustr
);
444 m_xFtFullTypeName
->set_label( u
""_ustr
);
446 if( pLinkMgr
&& pLinkMgr
->GetPersist() )
447 pLinkMgr
->GetPersist()->SetModified();
450 IMPL_LINK_NOARG( SvBaseLinksDlg
, UpdateWaitingHdl
, Timer
*, void )
452 m_xTbLinks
->freeze();
453 for (int nPos
= m_xTbLinks
->n_children(); nPos
; --nPos
)
455 tools::SvRef
<SvBaseLink
> xLink( weld::fromId
<SvBaseLink
*>(m_xTbLinks
->get_id(nPos
)) );
458 OUString
sCur( ImplGetStateStr( *xLink
) ),
459 sOld( m_xTbLinks
->get_text(nPos
, 3) );
461 m_xTbLinks
->set_text(nPos
, sCur
, 3);
467 IMPL_LINK( SvBaseLinksDlg
, EndEditHdl
, sfx2::SvBaseLink
&, _rLink
, void )
470 GetSelEntry( &nPos
);
472 if( !_rLink
.WasLastEditOK() )
475 // StarImpress/Draw swap the LinkObjects themselves!
476 // So search for the link in the manager; if it does not exist
477 // anymore, fill the list completely new. Otherwise only the
478 // edited link needs to be refreshed.
479 bool bLinkFnd
= false;
480 for( size_t n
= pLinkMgr
->GetLinks().size(); n
; )
481 if( &_rLink
== &(*pLinkMgr
->GetLinks()[ --n
]) )
489 m_xTbLinks
->remove(nPos
);
490 int nToUnselect
= m_xTbLinks
->get_selected_index();
491 InsertEntry(_rLink
, nPos
, true);
492 if (nToUnselect
!= -1)
493 m_xTbLinks
->unselect(nToUnselect
);
497 LinkManager
* pNewMgr
= pLinkMgr
;
499 SetManager( pNewMgr
);
501 if (pLinkMgr
&& pLinkMgr
->GetPersist())
502 pLinkMgr
->GetPersist()->SetModified();
505 OUString
SvBaseLinksDlg::ImplGetStateStr( const SvBaseLink
& rLnk
)
509 sRet
= aStrBrokenlink
;
510 else if( rLnk
.GetObj()->IsPending() )
512 sRet
= aStrWaitinglink
;
515 else if( SfxLinkUpdateMode::ALWAYS
== rLnk
.GetUpdateMode() )
518 sRet
= aStrManuallink
;
523 void SvBaseLinksDlg::SetManager( LinkManager
* pNewMgr
)
525 if( pLinkMgr
== pNewMgr
)
530 // update has to be stopped before clear
531 m_xTbLinks
->freeze();
540 SvBaseLinks
& rLnks
= const_cast<SvBaseLinks
&>(pLinkMgr
->GetLinks());
541 for( size_t n
= 0; n
< rLnks
.size(); ++n
)
543 tools::SvRef
<SvBaseLink
>& rLinkRef
= rLnks
[ n
];
546 rLnks
.erase( rLnks
.begin() + n
);
550 if( rLinkRef
->IsVisible() )
551 InsertEntry( *rLinkRef
);
558 m_xTbLinks
->set_cursor(0);
559 m_xTbLinks
->select(0);
560 LinksSelectHdl( nullptr );
564 void SvBaseLinksDlg::InsertEntry(const SvBaseLink
& rLink
, int nPos
, bool bSelect
)
566 OUString sFileNm
, sLinkNm
, sTypeNm
, sFilter
;
568 sfx2::LinkManager::GetDisplayNames( &rLink
, &sTypeNm
, &sFileNm
, &sLinkNm
, &sFilter
);
570 auto nWidthPixel
= m_xTbLinks
->get_column_width(0);
571 OUString aTxt
= m_xVirDev
->GetEllipsisString(sFileNm
, nWidthPixel
, DrawTextFlags::PathEllipsis
);
572 INetURLObject
aPath( sFileNm
, INetProtocol::File
);
573 OUString aFileName
= aPath
.getName(
574 INetURLObject::LAST_SEGMENT
, true, INetURLObject::DecodeMechanism::Unambiguous
);
576 if( aFileName
.getLength() > aTxt
.getLength() )
578 else if (!aFileName
.isEmpty() && aTxt
.indexOf(aFileName
, aTxt
.getLength() - aFileName
.getLength()) == -1)
579 // filename not in string
583 nPos
= m_xTbLinks
->n_children();
584 m_xTbLinks
->insert(nPos
);
585 m_xTbLinks
->set_text(nPos
, aTxt
, 0);
586 m_xTbLinks
->set_id(nPos
, weld::toId(&rLink
));
587 if( SvBaseLinkObjectType::ClientGraphic
== rLink
.GetObjType() )
588 m_xTbLinks
->set_text(nPos
, sFilter
, 1);
590 m_xTbLinks
->set_text(nPos
, sLinkNm
, 1);
591 m_xTbLinks
->set_text(nPos
, sTypeNm
, 2);
592 m_xTbLinks
->set_text(nPos
, ImplGetStateStr(rLink
), 3);
594 m_xTbLinks
->select(nPos
);
597 SvBaseLink
* SvBaseLinksDlg::GetSelEntry(int* pPos
)
599 int nPos
= m_xTbLinks
->get_selected_index();
604 return weld::fromId
<SvBaseLink
*>(m_xTbLinks
->get_id(nPos
));
609 void SvBaseLinksDlg::SetType(SvBaseLink
& rLink
,
611 SfxLinkUpdateMode nType
)
613 rLink
.SetUpdateMode( nType
);
615 m_xTbLinks
->set_text(nSelPos
, ImplGetStateStr(rLink
), 3);
616 if (pLinkMgr
->GetPersist())
617 pLinkMgr
->GetPersist()->SetModified();
620 void SvBaseLinksDlg::SetActLink( SvBaseLink
const * pLink
)
625 const SvBaseLinks
& rLnks
= pLinkMgr
->GetLinks();
627 for(const auto & rLinkRef
: rLnks
)
629 // #109573# only visible links have been inserted into the TreeListBox,
630 // invisible ones have to be skipped here
631 if( rLinkRef
->IsVisible() )
633 if( pLink
== rLinkRef
.get() )
635 m_xTbLinks
->select(nSelect
);
636 LinksSelectHdl( nullptr );
644 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */