1
0
Fork 0
libreoffice/cui/source/dialogs/linkdlg.cxx
Daniel Baumann 8e63e14cf6
Adding upstream version 4:25.2.3.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
2025-06-22 16:20:04 +02:00

644 lines
21 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#include <linkdlg.hxx>
#include <o3tl/safeint.hxx>
#include <vcl/svapp.hxx>
#include <comphelper/diagnose_ex.hxx>
#include <tools/debug.hxx>
#include <tools/urlobj.hxx>
#include <vcl/idle.hxx>
#include <vcl/timer.hxx>
#include <vcl/weld.hxx>
#include <vcl/weldutils.hxx>
#include <strings.hrc>
#include <sfx2/filedlghelper.hxx>
#include <sfx2/linkmgr.hxx>
#include <sfx2/linksrc.hxx>
#include <sfx2/lnkbase.hxx>
#include <sfx2/objsh.hxx>
#include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp>
#include <com/sun/star/ui/dialogs/XFolderPicker2.hpp>
#include <comphelper/processfactory.hxx>
#include <dialmgr.hxx>
using namespace sfx2;
using namespace ::com::sun::star;
namespace {
class SvBaseLinkMemberList {
private:
std::vector<SvBaseLink*> mLinks;
public:
~SvBaseLinkMemberList()
{
for (auto const& link : mLinks)
{
if( link )
link->ReleaseRef();
}
}
size_t size() const { return mLinks.size(); }
SvBaseLink *operator[](size_t i) const { return mLinks[i]; }
void push_back(SvBaseLink* p)
{
mLinks.push_back(p);
p->AddFirstRef();
}
};
}
SvBaseLinksDlg::SvBaseLinksDlg(weld::Window * pParent, LinkManager* pMgr, bool bHtmlMode)
: GenericDialogController(pParent, u"cui/ui/baselinksdialog.ui"_ustr, u"BaseLinksDialog"_ustr)
, aStrAutolink( CuiResId( STR_AUTOLINK ) )
, aStrManuallink( CuiResId( STR_MANUALLINK ) )
, aStrBrokenlink( CuiResId( STR_BROKENLINK ) )
, aStrCloselinkmsg( CuiResId( STR_CLOSELINKMSG ) )
, aStrCloselinkmsgMulti( CuiResId( STR_CLOSELINKMSG_MULTI ) )
, aStrWaitinglink( CuiResId( STR_WAITINGLINK ) )
, pLinkMgr( nullptr )
, aUpdateIdle("cui SvBaseLinksDlg UpdateIdle")
, m_xTbLinks(m_xBuilder->weld_tree_view(u"TB_LINKS"_ustr))
, m_xFtFullFileName(m_xBuilder->weld_link_button(u"FULL_FILE_NAME"_ustr))
, m_xFtFullSourceName(m_xBuilder->weld_label(u"FULL_SOURCE_NAME"_ustr))
, m_xFtFullTypeName(m_xBuilder->weld_label(u"FULL_TYPE_NAME"_ustr))
, m_xRbAutomatic(m_xBuilder->weld_radio_button(u"AUTOMATIC"_ustr))
, m_xRbManual(m_xBuilder->weld_radio_button(u"MANUAL"_ustr))
, m_xPbUpdateNow(m_xBuilder->weld_button(u"UPDATE_NOW"_ustr))
, m_xPbChangeSource(m_xBuilder->weld_button(u"CHANGE_SOURCE"_ustr))
, m_xPbBreakLink(m_xBuilder->weld_button(u"BREAK_LINK"_ustr))
, m_xVirDev(VclPtr<VirtualDevice>::Create())
{
// expand the point size of the desired font to the equivalent pixel size
weld::SetPointFont(*m_xVirDev, m_xTbLinks->get_font());
m_xTbLinks->set_size_request(m_xTbLinks->get_approximate_digit_width() * 90,
m_xTbLinks->get_height_rows(12));
m_xTbLinks->set_selection_mode(SelectionMode::Multiple);
std::vector<int> aWidths
{
o3tl::narrowing<int>(m_xTbLinks->get_approximate_digit_width() * 30),
o3tl::narrowing<int>(m_xTbLinks->get_approximate_digit_width() * 20),
o3tl::narrowing<int>(m_xTbLinks->get_approximate_digit_width() * 20)
};
m_xTbLinks->set_column_fixed_widths(aWidths);
// UpdateTimer for DDE-/Grf-links, which are waited for
aUpdateIdle.SetInvokeHandler( LINK( this, SvBaseLinksDlg, UpdateWaitingHdl ) );
aUpdateIdle.SetPriority( TaskPriority::LOWEST );
m_xTbLinks->connect_changed( LINK( this, SvBaseLinksDlg, LinksSelectHdl ) );
m_xTbLinks->connect_row_activated( LINK( this, SvBaseLinksDlg, LinksDoubleClickHdl ) );
m_xRbAutomatic->connect_toggled( LINK( this, SvBaseLinksDlg, ToggleHdl ) );
m_xRbManual->connect_toggled( LINK( this, SvBaseLinksDlg, ToggleHdl ) );
m_xPbUpdateNow->connect_clicked( LINK( this, SvBaseLinksDlg, UpdateNowClickHdl ) );
m_xPbChangeSource->connect_clicked( LINK( this, SvBaseLinksDlg, ChangeSourceClickHdl ) );
if(!bHtmlMode)
m_xPbBreakLink->connect_clicked( LINK( this, SvBaseLinksDlg, BreakLinkClickHdl ) );
else
m_xPbBreakLink->hide();
SetManager( pMgr );
}
SvBaseLinksDlg::~SvBaseLinksDlg()
{
}
/*************************************************************************
|* SvBaseLinksDlg::Handler()
*************************************************************************/
IMPL_LINK(SvBaseLinksDlg, LinksSelectHdl, weld::TreeView&, rTreeView, void)
{
LinksSelectHdl(&rTreeView);
}
void SvBaseLinksDlg::LinksSelectHdl(weld::TreeView* pSvTabListBox)
{
const int nSelectionCount = pSvTabListBox ?
pSvTabListBox->count_selected_rows() : 0;
if (nSelectionCount > 1)
{
// possibly deselect old entries in case of multi-selection
int nSelEntry = pSvTabListBox->get_selected_index();
SvBaseLink* pLink = weld::fromId<SvBaseLink*>(pSvTabListBox->get_id(nSelEntry));
SvBaseLinkObjectType nObjectType = pLink->GetObjType();
if(!isClientFileType(nObjectType))
{
pSvTabListBox->unselect_all();
pSvTabListBox->select(nSelEntry);
}
else
{
std::vector<int> aRows = pSvTabListBox->get_selected_rows();
for (auto nEntry : aRows)
{
pLink = weld::fromId<SvBaseLink*>(pSvTabListBox->get_id(nEntry));
DBG_ASSERT(pLink, "Where is the Link?");
if (!pLink)
continue;
if( !isClientFileType(pLink->GetObjType()) )
pSvTabListBox->unselect(nEntry);
}
}
m_xPbUpdateNow->set_sensitive(true);
m_xRbAutomatic->set_sensitive(false);
m_xRbManual->set_active(true);
m_xRbManual->set_sensitive(false);
}
else
{
int nPos;
SvBaseLink* pLink = GetSelEntry( &nPos );
if( !pLink )
return;
m_xPbUpdateNow->set_sensitive(true);
OUString sType, sLink;
OUString *pLinkNm = &sLink, *pFilter = nullptr;
if( isClientFileType(pLink->GetObjType()) )
{
m_xRbAutomatic->set_sensitive(false);
m_xRbManual->set_active(true);
m_xRbManual->set_sensitive(false);
if( SvBaseLinkObjectType::ClientGraphic == pLink->GetObjType() )
{
pLinkNm = nullptr;
pFilter = &sLink;
}
}
else
{
m_xRbAutomatic->set_sensitive(true);
m_xRbManual->set_sensitive(true);
if( SfxLinkUpdateMode::ALWAYS == pLink->GetUpdateMode() )
m_xRbAutomatic->set_active(true);
else
m_xRbManual->set_active(true);
}
OUString aFileName;
sfx2::LinkManager::GetDisplayNames( pLink, &sType, &aFileName, pLinkNm, pFilter );
aFileName = INetURLObject::decode(aFileName, INetURLObject::DecodeMechanism::Unambiguous);
m_xFtFullFileName->set_label( aFileName );
m_xFtFullFileName->set_uri( aFileName );
m_xFtFullSourceName->set_label( sLink );
m_xFtFullTypeName->set_label( sType );
}
}
IMPL_LINK_NOARG( SvBaseLinksDlg, LinksDoubleClickHdl, weld::TreeView&, bool )
{
ChangeSourceClickHdl(*m_xPbChangeSource);
return true;
}
IMPL_LINK(SvBaseLinksDlg, ToggleHdl, weld::Toggleable&, rButton, void)
{
if (!rButton.get_active())
return;
int nPos;
SvBaseLink* pLink = GetSelEntry( &nPos );
if (m_xRbAutomatic->get_active())
{
if( pLink && !isClientFileType( pLink->GetObjType() ) &&
SfxLinkUpdateMode::ALWAYS != pLink->GetUpdateMode() )
SetType( *pLink, nPos, SfxLinkUpdateMode::ALWAYS );
}
else
{
if( pLink && !isClientFileType( pLink->GetObjType() ) &&
SfxLinkUpdateMode::ONCALL != pLink->GetUpdateMode())
SetType( *pLink, nPos, SfxLinkUpdateMode::ONCALL );
}
}
IMPL_LINK_NOARG(SvBaseLinksDlg, UpdateNowClickHdl, weld::Button&, void)
{
std::vector< SvBaseLink* > aLnkArr;
std::vector< sal_Int16 > aPosArr;
std::vector<int> aRows = m_xTbLinks->get_selected_rows();
for (int nFndPos : aRows)
{
aLnkArr.push_back( weld::fromId<SvBaseLink*>( m_xTbLinks->get_id(nFndPos) ) );
aPosArr.push_back( nFndPos );
}
if( aLnkArr.empty() )
return;
for( size_t n = 0; n < aLnkArr.size(); ++n )
{
tools::SvRef<SvBaseLink> xLink = aLnkArr[ n ];
// first look for the entry in the array
for(const auto & i : pLinkMgr->GetLinks())
if( xLink == i )
{
SetType( *xLink, aPosArr[ n ], xLink->GetUpdateMode() );
break;
}
}
// if somebody is of the opinion to swap his links (SD)
LinkManager* pNewMgr = pLinkMgr;
pLinkMgr = nullptr;
SetManager( pNewMgr );
OUString sId = weld::toId(aLnkArr[0]);
int nE = m_xTbLinks->find_id(sId);
if (nE == -1)
nE = m_xTbLinks->get_selected_index();
int nSelEntry = m_xTbLinks->get_selected_index();
if (nE != nSelEntry)
m_xTbLinks->unselect(nSelEntry);
m_xTbLinks->select(nE);
m_xTbLinks->scroll_to_row(nE);
pNewMgr->CloseCachedComps();
}
IMPL_LINK_NOARG(SvBaseLinksDlg, ChangeSourceClickHdl, weld::Button&, void)
{
std::vector<int> aRows = m_xTbLinks->get_selected_rows();
if (aRows.size() > 1)
{
try
{
uno::Reference<ui::dialogs::XFolderPicker2> xFolderPicker = sfx2::createFolderPicker(
comphelper::getProcessComponentContext(), m_xDialog.get());
OUString sType, sFile, sLinkName;
OUString sFilter;
SvBaseLink* pLink = weld::fromId<SvBaseLink*>(m_xTbLinks->get_id(aRows[0]));
sfx2::LinkManager::GetDisplayNames( pLink, &sType, &sFile );
INetURLObject aUrl(sFile);
if(aUrl.GetProtocol() == INetProtocol::File)
{
OUString sOldPath(aUrl.PathToFileName());
sal_Int32 nLen = aUrl.GetLastName().getLength();
sOldPath = sOldPath.copy(0, sOldPath.getLength() - nLen);
xFolderPicker->setDisplayDirectory(sOldPath);
}
if (xFolderPicker->execute() == ui::dialogs::ExecutableDialogResults::OK)
{
OUString aPath = xFolderPicker->getDirectory();
for (auto nRow : aRows)
{
pLink = weld::fromId<SvBaseLink*>(m_xTbLinks->get_id(nRow));
DBG_ASSERT(pLink,"Where is the link?");
if (!pLink)
continue;
sfx2::LinkManager::GetDisplayNames( pLink, &sType, &sFile, &sLinkName, &sFilter );
INetURLObject aUrl_(sFile);
INetURLObject aUrl2(aPath, INetProtocol::File);
aUrl2.insertName( aUrl_.getName() );
OUString sNewLinkName;
MakeLnkName( sNewLinkName, nullptr ,
aUrl2.GetMainURL(INetURLObject::DecodeMechanism::ToIUri), sLinkName, &sFilter);
pLink->SetLinkSourceName( sNewLinkName );
pLink->Update();
}
if( pLinkMgr->GetPersist() )
pLinkMgr->GetPersist()->SetModified();
LinkManager* pNewMgr = pLinkMgr;
pLinkMgr = nullptr;
SetManager( pNewMgr );
}
}
catch (const uno::Exception &)
{
TOOLS_WARN_EXCEPTION("cui.dialogs", "SvBaseLinksDlg");
}
}
else
{
int nPos;
SvBaseLink* pLink = GetSelEntry( &nPos );
if ( pLink && !pLink->GetLinkSourceName().isEmpty() )
pLink->Edit(m_xDialog.get(), LINK(this, SvBaseLinksDlg, EndEditHdl));
}
}
IMPL_LINK_NOARG( SvBaseLinksDlg, BreakLinkClickHdl, weld::Button&, void )
{
bool bModified = false;
if (m_xTbLinks->count_selected_rows() <= 1)
{
int nPos;
tools::SvRef<SvBaseLink> xLink = GetSelEntry( &nPos );
if( !xLink.is() )
return;
std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(m_xDialog.get(),
VclMessageType::Question, VclButtonsType::YesNo,
aStrCloselinkmsg));
xQueryBox->set_default_response(RET_YES);
if (RET_YES == xQueryBox->run())
{
m_xTbLinks->remove(nPos);
// close object, if it's still existing
bool bNewLnkMgr = SvBaseLinkObjectType::ClientFile == xLink->GetObjType();
// tell the link that it will be resolved!
xLink->Closed();
// if somebody has forgotten to deregister himself
if( xLink.is() )
pLinkMgr->Remove( xLink.get() );
if( bNewLnkMgr )
{
LinkManager* pNewMgr = pLinkMgr;
pLinkMgr = nullptr;
SetManager( pNewMgr );
m_xTbLinks->set_cursor(nPos ? --nPos : 0);
}
bModified = true;
}
}
else
{
std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(m_xDialog.get(),
VclMessageType::Question, VclButtonsType::YesNo,
aStrCloselinkmsgMulti));
xQueryBox->set_default_response(RET_YES);
if (RET_YES == xQueryBox->run())
{
std::vector<int> aRows = m_xTbLinks->get_selected_rows();
SvBaseLinkMemberList aLinkList;
for (auto nRow : aRows)
{
SvBaseLink* pLink = weld::fromId<SvBaseLink*>(m_xTbLinks->get_id(nRow));
if (pLink)
aLinkList.push_back(pLink);
}
std::sort(aRows.begin(), aRows.end());
for (auto it = aRows.rbegin(); it != aRows.rend(); ++it)
m_xTbLinks->remove(*it);
for (size_t i = 0; i < aLinkList.size(); ++i)
{
tools::SvRef<SvBaseLink> xLink = aLinkList[i];
// tell the link that it will be resolved!
xLink->Closed();
// if somebody has forgotten to deregister himself
pLinkMgr->Remove( xLink.get() );
bModified = true;
}
// then remove all selected entries
}
}
if(!bModified)
return;
if (!m_xTbLinks->n_children())
{
m_xRbAutomatic->set_sensitive(false);
m_xRbManual->set_sensitive(false);
m_xPbUpdateNow->set_sensitive(false);
m_xPbChangeSource->set_sensitive(false);
m_xPbBreakLink->set_sensitive(false);
m_xFtFullSourceName->set_label( u""_ustr );
m_xFtFullTypeName->set_label( u""_ustr );
}
if( pLinkMgr && pLinkMgr->GetPersist() )
pLinkMgr->GetPersist()->SetModified();
}
IMPL_LINK_NOARG( SvBaseLinksDlg, UpdateWaitingHdl, Timer*, void )
{
m_xTbLinks->freeze();
for (int nPos = m_xTbLinks->n_children(); nPos; --nPos)
{
tools::SvRef<SvBaseLink> xLink( weld::fromId<SvBaseLink*>(m_xTbLinks->get_id(nPos)) );
if( xLink.is() )
{
OUString sCur( ImplGetStateStr( *xLink ) ),
sOld( m_xTbLinks->get_text(nPos, 3) );
if( sCur != sOld )
m_xTbLinks->set_text(nPos, sCur, 3);
}
}
m_xTbLinks->thaw();
}
IMPL_LINK( SvBaseLinksDlg, EndEditHdl, sfx2::SvBaseLink&, _rLink, void )
{
int nPos;
GetSelEntry( &nPos );
if( !_rLink.WasLastEditOK() )
return;
// StarImpress/Draw swap the LinkObjects themselves!
// So search for the link in the manager; if it does not exist
// anymore, fill the list completely new. Otherwise only the
// edited link needs to be refreshed.
bool bLinkFnd = false;
for( size_t n = pLinkMgr->GetLinks().size(); n; )
if( &_rLink == &(*pLinkMgr->GetLinks()[ --n ]) )
{
bLinkFnd = true;
break;
}
if( bLinkFnd )
{
m_xTbLinks->remove(nPos);
int nToUnselect = m_xTbLinks->get_selected_index();
InsertEntry(_rLink, nPos, true);
if (nToUnselect != -1)
m_xTbLinks->unselect(nToUnselect);
}
else
{
LinkManager* pNewMgr = pLinkMgr;
pLinkMgr = nullptr;
SetManager( pNewMgr );
}
if (pLinkMgr && pLinkMgr->GetPersist())
pLinkMgr->GetPersist()->SetModified();
}
OUString SvBaseLinksDlg::ImplGetStateStr( const SvBaseLink& rLnk )
{
OUString sRet;
if( !rLnk.GetObj() )
sRet = aStrBrokenlink;
else if( rLnk.GetObj()->IsPending() )
{
sRet = aStrWaitinglink;
aUpdateIdle.Start();
}
else if( SfxLinkUpdateMode::ALWAYS == rLnk.GetUpdateMode() )
sRet = aStrAutolink;
else
sRet = aStrManuallink;
return sRet;
}
void SvBaseLinksDlg::SetManager( LinkManager* pNewMgr )
{
if( pLinkMgr == pNewMgr )
return;
if (pNewMgr)
{
// update has to be stopped before clear
m_xTbLinks->freeze();
}
m_xTbLinks->clear();
pLinkMgr = pNewMgr;
if( !pLinkMgr )
return;
SvBaseLinks& rLnks = const_cast<SvBaseLinks&>(pLinkMgr->GetLinks());
for( size_t n = 0; n < rLnks.size(); ++n )
{
tools::SvRef<SvBaseLink>& rLinkRef = rLnks[ n ];
if( !rLinkRef.is() )
{
rLnks.erase( rLnks.begin() + n );
--n;
continue;
}
if( rLinkRef->IsVisible() )
InsertEntry( *rLinkRef );
}
m_xTbLinks->thaw();
if( !rLnks.empty() )
{
m_xTbLinks->set_cursor(0);
m_xTbLinks->select(0);
LinksSelectHdl( nullptr );
}
}
void SvBaseLinksDlg::InsertEntry(const SvBaseLink& rLink, int nPos, bool bSelect)
{
OUString sFileNm, sLinkNm, sTypeNm, sFilter;
sfx2::LinkManager::GetDisplayNames( &rLink, &sTypeNm, &sFileNm, &sLinkNm, &sFilter );
auto nWidthPixel = m_xTbLinks->get_column_width(0);
OUString aTxt = m_xVirDev->GetEllipsisString(sFileNm, nWidthPixel, DrawTextFlags::PathEllipsis);
INetURLObject aPath( sFileNm, INetProtocol::File );
OUString aFileName = aPath.getName(
INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::Unambiguous);
if( aFileName.getLength() > aTxt.getLength() )
aTxt = aFileName;
else if (!aFileName.isEmpty() && aTxt.indexOf(aFileName, aTxt.getLength() - aFileName.getLength()) == -1)
// filename not in string
aTxt = aFileName;
if (nPos == -1)
nPos = m_xTbLinks->n_children();
m_xTbLinks->insert(nPos);
m_xTbLinks->set_text(nPos, aTxt, 0);
m_xTbLinks->set_id(nPos, weld::toId(&rLink));
if( SvBaseLinkObjectType::ClientGraphic == rLink.GetObjType() )
m_xTbLinks->set_text(nPos, sFilter, 1);
else
m_xTbLinks->set_text(nPos, sLinkNm, 1);
m_xTbLinks->set_text(nPos, sTypeNm, 2);
m_xTbLinks->set_text(nPos, ImplGetStateStr(rLink), 3);
if (bSelect)
m_xTbLinks->select(nPos);
}
SvBaseLink* SvBaseLinksDlg::GetSelEntry(int* pPos)
{
int nPos = m_xTbLinks->get_selected_index();
if (nPos != -1)
{
if (pPos)
*pPos = nPos;
return weld::fromId<SvBaseLink*>(m_xTbLinks->get_id(nPos));
}
return nullptr;
}
void SvBaseLinksDlg::SetType(SvBaseLink& rLink,
int nSelPos,
SfxLinkUpdateMode nType)
{
rLink.SetUpdateMode( nType );
rLink.Update();
m_xTbLinks->set_text(nSelPos, ImplGetStateStr(rLink), 3);
if (pLinkMgr->GetPersist())
pLinkMgr->GetPersist()->SetModified();
}
void SvBaseLinksDlg::SetActLink( SvBaseLink const * pLink )
{
if( !pLinkMgr )
return;
const SvBaseLinks& rLnks = pLinkMgr->GetLinks();
int nSelect = 0;
for(const auto & rLinkRef : rLnks)
{
// #109573# only visible links have been inserted into the TreeListBox,
// invisible ones have to be skipped here
if( rLinkRef->IsVisible() )
{
if( pLink == rLinkRef.get() )
{
m_xTbLinks->select(nSelect);
LinksSelectHdl( nullptr );
return ;
}
++nSelect;
}
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */