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 <vcl/svapp.hxx>
23 #include <tools/diagnose_ex.h>
24 #include <tools/debug.hxx>
25 #include <tools/urlobj.hxx>
26 #include <vcl/idle.hxx>
27 #include <vcl/timer.hxx>
28 #include <vcl/weld.hxx>
29 #include <vcl/window.hxx>
31 #include <strings.hrc>
32 #include <sfx2/linkmgr.hxx>
33 #include <sfx2/linksrc.hxx>
34 #include <sfx2/lnkbase.hxx>
35 #include <sfx2/objsh.hxx>
37 #include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp>
38 #include <com/sun/star/ui/dialogs/FolderPicker.hpp>
39 #include <comphelper/processfactory.hxx>
41 #include <dialmgr.hxx>
44 #define FILEOBJECT ( OBJECT_CLIENT_FILE & ~OBJECT_CLIENT_SO )
47 using namespace ::com::sun::star
;
49 class SvBaseLinkMemberList
{
51 std::vector
<SvBaseLink
*> mLinks
;
54 ~SvBaseLinkMemberList()
56 for (auto const& link
: mLinks
)
63 size_t size() const { return mLinks
.size(); }
65 SvBaseLink
*operator[](size_t i
) const { return mLinks
[i
]; }
67 void push_back(SvBaseLink
* p
)
74 SvBaseLinksDlg::SvBaseLinksDlg(weld::Window
* pParent
, LinkManager
* pMgr
, bool bHtmlMode
)
75 : GenericDialogController(pParent
, "cui/ui/baselinksdialog.ui", "BaseLinksDialog")
76 , aStrAutolink( CuiResId( STR_AUTOLINK
) )
77 , aStrManuallink( CuiResId( STR_MANUALLINK
) )
78 , aStrBrokenlink( CuiResId( STR_BROKENLINK
) )
79 , aStrCloselinkmsg( CuiResId( STR_CLOSELINKMSG
) )
80 , aStrCloselinkmsgMulti( CuiResId( STR_CLOSELINKMSG_MULTI
) )
81 , aStrWaitinglink( CuiResId( STR_WAITINGLINK
) )
83 , aUpdateIdle("cui SvBaseLinksDlg UpdateIdle")
84 , m_xTbLinks(m_xBuilder
->weld_tree_view("TB_LINKS"))
85 , m_xFtFullFileName(m_xBuilder
->weld_link_button("FULL_FILE_NAME"))
86 , m_xFtFullSourceName(m_xBuilder
->weld_label("FULL_SOURCE_NAME"))
87 , m_xFtFullTypeName(m_xBuilder
->weld_label("FULL_TYPE_NAME"))
88 , m_xRbAutomatic(m_xBuilder
->weld_radio_button("AUTOMATIC"))
89 , m_xRbManual(m_xBuilder
->weld_radio_button("MANUAL"))
90 , m_xPbUpdateNow(m_xBuilder
->weld_button("UPDATE_NOW"))
91 , m_xPbChangeSource(m_xBuilder
->weld_button("CHANGE_SOURCE"))
92 , m_xPbBreakLink(m_xBuilder
->weld_button("BREAK_LINK"))
93 , m_xVirDev(VclPtr
<VirtualDevice
>::Create())
95 // expand the point size of the desired font to the equivalent pixel size
96 if (vcl::Window
* pDefaultDevice
= dynamic_cast<vcl::Window
*>(Application::GetDefaultDevice()))
97 pDefaultDevice
->SetPointFont(*m_xVirDev
, m_xTbLinks
->get_font());
98 m_xTbLinks
->set_size_request(m_xTbLinks
->get_approximate_digit_width() * 90,
99 m_xTbLinks
->get_height_rows(12));
101 m_xTbLinks
->set_selection_mode(SelectionMode::Multiple
);
103 std::vector
<int> aWidths
;
104 aWidths
.push_back(m_xTbLinks
->get_approximate_digit_width() * 30);
105 aWidths
.push_back(m_xTbLinks
->get_approximate_digit_width() * 20);
106 aWidths
.push_back(m_xTbLinks
->get_approximate_digit_width() * 20);
107 m_xTbLinks
->set_column_fixed_widths(aWidths
);
109 // UpdateTimer for DDE-/Grf-links, which are waited for
110 aUpdateIdle
.SetInvokeHandler( LINK( this, SvBaseLinksDlg
, UpdateWaitingHdl
) );
111 aUpdateIdle
.SetPriority( TaskPriority::LOWEST
);
113 m_xTbLinks
->connect_changed( LINK( this, SvBaseLinksDlg
, LinksSelectHdl
) );
114 m_xTbLinks
->connect_row_activated( LINK( this, SvBaseLinksDlg
, LinksDoubleClickHdl
) );
115 m_xRbAutomatic
->connect_clicked( LINK( this, SvBaseLinksDlg
, AutomaticClickHdl
) );
116 m_xRbManual
->connect_clicked( LINK( this, SvBaseLinksDlg
, ManualClickHdl
) );
117 m_xPbUpdateNow
->connect_clicked( LINK( this, SvBaseLinksDlg
, UpdateNowClickHdl
) );
118 m_xPbChangeSource
->connect_clicked( LINK( this, SvBaseLinksDlg
, ChangeSourceClickHdl
) );
120 m_xPbBreakLink
->connect_clicked( LINK( this, SvBaseLinksDlg
, BreakLinkClickHdl
) );
122 m_xPbBreakLink
->hide();
127 SvBaseLinksDlg::~SvBaseLinksDlg()
131 /*************************************************************************
132 |* SvBaseLinksDlg::Handler()
133 *************************************************************************/
134 IMPL_LINK(SvBaseLinksDlg
, LinksSelectHdl
, weld::TreeView
&, rTreeView
, void)
136 LinksSelectHdl(&rTreeView
);
139 void SvBaseLinksDlg::LinksSelectHdl(weld::TreeView
* pSvTabListBox
)
141 const int nSelectionCount
= pSvTabListBox
?
142 pSvTabListBox
->count_selected_rows() : 0;
143 if (nSelectionCount
> 1)
145 // possibly deselect old entries in case of multi-selection
146 int nSelEntry
= pSvTabListBox
->get_selected_index();
147 SvBaseLink
* pLink
= reinterpret_cast<SvBaseLink
*>(pSvTabListBox
->get_id(nSelEntry
).toInt64());
148 sal_uInt16 nObjectType
= pLink
->GetObjType();
149 if((OBJECT_CLIENT_FILE
& nObjectType
) != OBJECT_CLIENT_FILE
)
151 pSvTabListBox
->unselect_all();
152 pSvTabListBox
->select(nSelEntry
);
156 std::vector
<int> aRows
= pSvTabListBox
->get_selected_rows();
157 for (auto nEntry
: aRows
)
159 pLink
= reinterpret_cast<SvBaseLink
*>(pSvTabListBox
->get_id(nEntry
).toInt64());
160 DBG_ASSERT(pLink
, "Where is the Link?");
163 if( (OBJECT_CLIENT_FILE
& pLink
->GetObjType()) != OBJECT_CLIENT_FILE
)
164 pSvTabListBox
->unselect(nEntry
);
168 m_xPbUpdateNow
->set_sensitive(true);
169 m_xRbAutomatic
->set_sensitive(false);
170 m_xRbManual
->set_active(true);
171 m_xRbManual
->set_sensitive(false);
176 SvBaseLink
* pLink
= GetSelEntry( &nPos
);
180 m_xPbUpdateNow
->set_sensitive(true);
182 OUString sType
, sLink
;
183 OUString
*pLinkNm
= &sLink
, *pFilter
= nullptr;
185 if( FILEOBJECT
& pLink
->GetObjType() )
187 m_xRbAutomatic
->set_sensitive(false);
188 m_xRbManual
->set_active(true);
189 m_xRbManual
->set_sensitive(false);
190 if( OBJECT_CLIENT_GRF
== pLink
->GetObjType() )
198 m_xRbAutomatic
->set_sensitive(true);
199 m_xRbManual
->set_sensitive(true);
201 if( SfxLinkUpdateMode::ALWAYS
== pLink
->GetUpdateMode() )
202 m_xRbAutomatic
->set_active(true);
204 m_xRbManual
->set_active(true);
208 sfx2::LinkManager::GetDisplayNames( pLink
, &sType
, &aFileName
, pLinkNm
, pFilter
);
209 aFileName
= INetURLObject::decode(aFileName
, INetURLObject::DecodeMechanism::Unambiguous
);
210 m_xFtFullFileName
->set_label( aFileName
);
211 m_xFtFullFileName
->set_uri( aFileName
);
212 m_xFtFullSourceName
->set_label( sLink
);
213 m_xFtFullTypeName
->set_label( sType
);
217 IMPL_LINK_NOARG( SvBaseLinksDlg
, LinksDoubleClickHdl
, weld::TreeView
&, bool )
219 ChangeSourceClickHdl(*m_xPbChangeSource
);
223 IMPL_LINK_NOARG( SvBaseLinksDlg
, AutomaticClickHdl
, weld::Button
&, void )
226 SvBaseLink
* pLink
= GetSelEntry( &nPos
);
227 if( pLink
&& !( FILEOBJECT
& pLink
->GetObjType() ) &&
228 SfxLinkUpdateMode::ALWAYS
!= pLink
->GetUpdateMode() )
229 SetType( *pLink
, nPos
, SfxLinkUpdateMode::ALWAYS
);
232 IMPL_LINK_NOARG( SvBaseLinksDlg
, ManualClickHdl
, weld::Button
&, void )
235 SvBaseLink
* pLink
= GetSelEntry( &nPos
);
236 if( pLink
&& !( FILEOBJECT
& pLink
->GetObjType() ) &&
237 SfxLinkUpdateMode::ONCALL
!= pLink
->GetUpdateMode())
238 SetType( *pLink
, nPos
, SfxLinkUpdateMode::ONCALL
);
241 IMPL_LINK_NOARG(SvBaseLinksDlg
, UpdateNowClickHdl
, weld::Button
&, void)
243 std::vector
< SvBaseLink
* > aLnkArr
;
244 std::vector
< sal_Int16
> aPosArr
;
246 std::vector
<int> aRows
= m_xTbLinks
->get_selected_rows();
247 for (int nFndPos
: aRows
)
249 aLnkArr
.push_back( reinterpret_cast<SvBaseLink
*>( m_xTbLinks
->get_id(nFndPos
).toInt64() ) );
250 aPosArr
.push_back( nFndPos
);
253 if( !aLnkArr
.empty() )
255 for( size_t n
= 0; n
< aLnkArr
.size(); ++n
)
257 tools::SvRef
<SvBaseLink
> xLink
= aLnkArr
[ n
];
259 // first look for the entry in the array
260 for(const auto & i
: pLinkMgr
->GetLinks())
263 SetType( *xLink
, aPosArr
[ n
], xLink
->GetUpdateMode() );
268 // if somebody is of the opinion to swap his links (SD)
269 LinkManager
* pNewMgr
= pLinkMgr
;
271 SetManager( pNewMgr
);
274 OUString sId
= OUString::number(reinterpret_cast<sal_Int64
>(aLnkArr
[0]));
275 int nE
= m_xTbLinks
->find_id(sId
);
277 nE
= m_xTbLinks
->get_selected_index();
278 int nSelEntry
= m_xTbLinks
->get_selected_index();
280 m_xTbLinks
->unselect(nSelEntry
);
281 m_xTbLinks
->select(nE
);
282 m_xTbLinks
->scroll_to_row(nE
);
284 pNewMgr
->CloseCachedComps();
288 IMPL_LINK_NOARG(SvBaseLinksDlg
, ChangeSourceClickHdl
, weld::Button
&, void)
290 std::vector
<int> aRows
= m_xTbLinks
->get_selected_rows();
291 if (aRows
.size() > 1)
295 uno::Reference
<ui::dialogs::XFolderPicker2
> xFolderPicker
= ui::dialogs::FolderPicker::create(comphelper::getProcessComponentContext());
297 OUString sType
, sFile
, sLinkName
;
299 SvBaseLink
* pLink
= reinterpret_cast<SvBaseLink
*>(m_xTbLinks
->get_id(aRows
[0]).toInt64());
300 sfx2::LinkManager::GetDisplayNames( pLink
, &sType
, &sFile
);
301 INetURLObject
aUrl(sFile
);
302 if(aUrl
.GetProtocol() == INetProtocol::File
)
304 OUString
sOldPath(aUrl
.PathToFileName());
305 sal_Int32 nLen
= aUrl
.GetLastName().getLength();
306 sOldPath
= sOldPath
.copy(0, sOldPath
.getLength() - nLen
);
307 xFolderPicker
->setDisplayDirectory(sOldPath
);
309 if (xFolderPicker
->execute() == ui::dialogs::ExecutableDialogResults::OK
)
311 OUString aPath
= xFolderPicker
->getDirectory();
313 for (auto nRow
: aRows
)
315 pLink
= reinterpret_cast<SvBaseLink
*>(m_xTbLinks
->get_id(nRow
).toInt64());
316 DBG_ASSERT(pLink
,"Where is the link?");
319 sfx2::LinkManager::GetDisplayNames( pLink
, &sType
, &sFile
, &sLinkName
, &sFilter
);
320 INetURLObject
aUrl_(sFile
);
321 INetURLObject
aUrl2(aPath
, INetProtocol::File
);
322 aUrl2
.insertName( aUrl_
.getName() );
323 OUString sNewLinkName
;
324 MakeLnkName( sNewLinkName
, nullptr ,
325 aUrl2
.GetMainURL(INetURLObject::DecodeMechanism::ToIUri
), sLinkName
, &sFilter
);
326 pLink
->SetLinkSourceName( sNewLinkName
);
329 if( pLinkMgr
->GetPersist() )
330 pLinkMgr
->GetPersist()->SetModified();
331 LinkManager
* pNewMgr
= pLinkMgr
;
333 SetManager( pNewMgr
);
336 catch (const uno::Exception
&)
338 TOOLS_WARN_EXCEPTION("cui.dialogs", "SvBaseLinksDlg");
344 SvBaseLink
* pLink
= GetSelEntry( &nPos
);
345 if ( pLink
&& !pLink
->GetLinkSourceName().isEmpty() )
346 pLink
->Edit(m_xDialog
.get(), LINK(this, SvBaseLinksDlg
, EndEditHdl
));
350 IMPL_LINK_NOARG( SvBaseLinksDlg
, BreakLinkClickHdl
, weld::Button
&, void )
352 bool bModified
= false;
353 if (m_xTbLinks
->count_selected_rows() <= 1)
356 tools::SvRef
<SvBaseLink
> xLink
= GetSelEntry( &nPos
);
360 std::unique_ptr
<weld::MessageDialog
> xQueryBox(Application::CreateMessageDialog(m_xDialog
.get(),
361 VclMessageType::Question
, VclButtonsType::YesNo
,
363 xQueryBox
->set_default_response(RET_YES
);
365 if (RET_YES
== xQueryBox
->run())
367 m_xTbLinks
->remove(nPos
);
369 // close object, if it's still existing
370 bool bNewLnkMgr
= OBJECT_CLIENT_FILE
== xLink
->GetObjType();
372 // tell the link that it will be resolved!
375 // if somebody has forgotten to deregister himself
377 pLinkMgr
->Remove( xLink
.get() );
381 LinkManager
* pNewMgr
= pLinkMgr
;
383 SetManager( pNewMgr
);
384 m_xTbLinks
->set_cursor(nPos
? --nPos
: 0);
391 std::unique_ptr
<weld::MessageDialog
> xQueryBox(Application::CreateMessageDialog(m_xDialog
.get(),
392 VclMessageType::Question
, VclButtonsType::YesNo
,
393 aStrCloselinkmsgMulti
));
394 xQueryBox
->set_default_response(RET_YES
);
396 if (RET_YES
== xQueryBox
->run())
398 std::vector
<int> aRows
= m_xTbLinks
->get_selected_rows();
399 SvBaseLinkMemberList aLinkList
;
400 for (auto nRow
: aRows
)
402 SvBaseLink
* pLink
= reinterpret_cast<SvBaseLink
*>(m_xTbLinks
->get_id(nRow
).toInt64());
404 aLinkList
.push_back(pLink
);
406 std::sort(aRows
.begin(), aRows
.end());
407 for (auto it
= aRows
.rbegin(); it
!= aRows
.rend(); ++it
)
408 m_xTbLinks
->remove(*it
);
409 for (size_t i
= 0; i
< aLinkList
.size(); ++i
)
411 tools::SvRef
<SvBaseLink
> xLink
= aLinkList
[i
];
412 // tell the link that it will be resolved!
415 // if somebody has forgotten to deregister himself
416 pLinkMgr
->Remove( xLink
.get() );
419 // then remove all selected entries
424 if (!m_xTbLinks
->n_children())
426 m_xRbAutomatic
->set_sensitive(false);
427 m_xRbManual
->set_sensitive(false);
428 m_xPbUpdateNow
->set_sensitive(false);
429 m_xPbChangeSource
->set_sensitive(false);
430 m_xPbBreakLink
->set_sensitive(false);
432 m_xFtFullSourceName
->set_label( "" );
433 m_xFtFullTypeName
->set_label( "" );
435 if( pLinkMgr
&& pLinkMgr
->GetPersist() )
436 pLinkMgr
->GetPersist()->SetModified();
440 IMPL_LINK_NOARG( SvBaseLinksDlg
, UpdateWaitingHdl
, Timer
*, void )
442 m_xTbLinks
->freeze();
443 for (int nPos
= m_xTbLinks
->n_children(); nPos
; --nPos
)
445 tools::SvRef
<SvBaseLink
> xLink( reinterpret_cast<SvBaseLink
*>(m_xTbLinks
->get_id(nPos
).toInt64()) );
448 OUString
sCur( ImplGetStateStr( *xLink
) ),
449 sOld( m_xTbLinks
->get_text(nPos
, 3) );
451 m_xTbLinks
->set_text(nPos
, sCur
, 3);
457 IMPL_LINK( SvBaseLinksDlg
, EndEditHdl
, sfx2::SvBaseLink
&, _rLink
, void )
460 GetSelEntry( &nPos
);
462 if( _rLink
.WasLastEditOK() )
464 // StarImpress/Draw swap the LinkObjects themselves!
465 // So search for the link in the manager; if it does not exist
466 // anymore, fill the list completely new. Otherwise only the
467 // edited link needs to be refreshed.
468 bool bLinkFnd
= false;
469 for( size_t n
= pLinkMgr
->GetLinks().size(); n
; )
470 if( &_rLink
== &(*pLinkMgr
->GetLinks()[ --n
]) )
478 m_xTbLinks
->remove(nPos
);
479 int nToUnselect
= m_xTbLinks
->get_selected_index();
480 InsertEntry(_rLink
, nPos
, true);
481 if (nToUnselect
!= -1)
482 m_xTbLinks
->unselect(nToUnselect
);
486 LinkManager
* pNewMgr
= pLinkMgr
;
488 SetManager( pNewMgr
);
490 if (pLinkMgr
&& pLinkMgr
->GetPersist())
491 pLinkMgr
->GetPersist()->SetModified();
495 OUString
SvBaseLinksDlg::ImplGetStateStr( const SvBaseLink
& rLnk
)
499 sRet
= aStrBrokenlink
;
500 else if( rLnk
.GetObj()->IsPending() )
502 sRet
= aStrWaitinglink
;
505 else if( SfxLinkUpdateMode::ALWAYS
== rLnk
.GetUpdateMode() )
508 sRet
= aStrManuallink
;
513 void SvBaseLinksDlg::SetManager( LinkManager
* pNewMgr
)
515 if( pLinkMgr
== pNewMgr
)
520 // update has to be stopped before clear
521 m_xTbLinks
->freeze();
529 SvBaseLinks
& rLnks
= const_cast<SvBaseLinks
&>(pLinkMgr
->GetLinks());
530 for( size_t n
= 0; n
< rLnks
.size(); ++n
)
532 tools::SvRef
<SvBaseLink
>& rLinkRef
= rLnks
[ n
];
535 rLnks
.erase( rLnks
.begin() + n
);
539 if( rLinkRef
->IsVisible() )
540 InsertEntry( *rLinkRef
);
547 m_xTbLinks
->set_cursor(0);
548 m_xTbLinks
->select(0);
549 LinksSelectHdl( nullptr );
554 void SvBaseLinksDlg::InsertEntry(const SvBaseLink
& rLink
, int nPos
, bool bSelect
)
556 OUString sFileNm
, sLinkNm
, sTypeNm
, sFilter
;
558 sfx2::LinkManager::GetDisplayNames( &rLink
, &sTypeNm
, &sFileNm
, &sLinkNm
, &sFilter
);
560 auto nWidthPixel
= m_xTbLinks
->get_column_width(0);
561 OUString aTxt
= m_xVirDev
->GetEllipsisString(sFileNm
, nWidthPixel
, DrawTextFlags::PathEllipsis
);
562 INetURLObject
aPath( sFileNm
, INetProtocol::File
);
563 OUString aFileName
= aPath
.getName(
564 INetURLObject::LAST_SEGMENT
, true, INetURLObject::DecodeMechanism::Unambiguous
);
566 if( aFileName
.getLength() > aTxt
.getLength() )
568 else if (!aFileName
.isEmpty() && aTxt
.indexOf(aFileName
, aTxt
.getLength() - aFileName
.getLength()) == -1)
569 // filename not in string
573 nPos
= m_xTbLinks
->n_children();
574 m_xTbLinks
->insert(nPos
);
575 m_xTbLinks
->set_text(nPos
, aTxt
, 0);
576 m_xTbLinks
->set_id(nPos
, OUString::number(reinterpret_cast<sal_Int64
>(&rLink
)));
577 if( OBJECT_CLIENT_GRF
== rLink
.GetObjType() )
578 m_xTbLinks
->set_text(nPos
, sFilter
, 1);
580 m_xTbLinks
->set_text(nPos
, sLinkNm
, 1);
581 m_xTbLinks
->set_text(nPos
, sTypeNm
, 2);
582 m_xTbLinks
->set_text(nPos
, ImplGetStateStr(rLink
), 3);
584 m_xTbLinks
->select(nPos
);
587 SvBaseLink
* SvBaseLinksDlg::GetSelEntry(int* pPos
)
589 int nPos
= m_xTbLinks
->get_selected_index();
594 return reinterpret_cast<SvBaseLink
*>(m_xTbLinks
->get_id(nPos
).toInt64());
599 void SvBaseLinksDlg::SetType(SvBaseLink
& rLink
,
601 SfxLinkUpdateMode nType
)
603 rLink
.SetUpdateMode( nType
);
605 m_xTbLinks
->set_text(nSelPos
, ImplGetStateStr(rLink
), 3);
606 if (pLinkMgr
->GetPersist())
607 pLinkMgr
->GetPersist()->SetModified();
610 void SvBaseLinksDlg::SetActLink( SvBaseLink
const * pLink
)
614 const SvBaseLinks
& rLnks
= pLinkMgr
->GetLinks();
616 for(const auto & rLinkRef
: rLnks
)
618 // #109573# only visible links have been inserted into the TreeListBox,
619 // invisible ones have to be skipped here
620 if( rLinkRef
->IsVisible() )
622 if( pLink
== rLinkRef
.get() )
624 m_xTbLinks
->select(nSelect
);
625 LinksSelectHdl( nullptr );
634 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */