track impress style-sheet fill bitmaps per SfxStyleSheet, drop the pool bodge

Signed-off-by: Caolán McNamara <caolan.mcnamara@collabora.com>
Change-Id: Iafd45f05c32f4d2096be20b354bd6e93b6a9ef89
Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/3550
Tested-by: Jenkins CPCI <releng@collaboraoffice.com>
Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
(cherry picked from commit 6b4932267e3b0a5aec3fc1611d931eaa99eaf05a)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/208580
Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
Tested-by: Xisco Fauli <xiscofauli@libreoffice.org>
This commit is contained in:
Caolán McNamara
2026-07-25 01:48:27 +02:00
committed by Xisco Fauli
parent 09ff91d4fc
commit 8be0c744f5
4 changed files with 100 additions and 95 deletions
+7 -13
View File
@@ -15,7 +15,6 @@
#include <com/sun/star/uno/Reference.hxx>
#include <cppuhelper/weakref.hxx>
#include <tools/ref.hxx>
#include <functional>
#include <map>
#include <string_view>
#include <vector>
@@ -24,6 +23,7 @@ class SdrModel;
class SdrObject;
class SdrPage;
class SfxItemPool;
class SfxStyleSheet;
class XFillBitmapItem;
namespace com::sun::star::beans
@@ -45,17 +45,6 @@ SVXCORE_DLLPUBLIC OUString getDeferredOriginURL(const XFillBitmapItem& rItem);
// unresolved remote URLs (GraphicType::Default with non-empty originURL).
SVXCORE_DLLPUBLIC bool hasDeferredFillBitmapLinks(const SfxItemPool& rPool);
// Scan pool surrogates for XATTR_FILLBITMAP items with unresolved remote
// URLs (GraphicType::Default with non-empty originURL) and register an
// sfx2::SvBaseLink for each. When the link is updated, the fetched graphic
// replaces the pool item and fnInvalidate is called to trigger a repaint.
SVXCORE_DLLPUBLIC void registerFillBitmapLinks(SfxItemPool& rPool, sfx2::LinkManager& rLinkMgr,
std::function<void()> fnInvalidate);
// Convenience overload for the drawing layer: registers fill bitmap links
// and flushes all ViewObjectContacts on all pages when the graphic arrives.
SVXCORE_DLLPUBLIC void registerFillBitmapLinks(SdrModel& rModel, sfx2::LinkManager& rLinkMgr);
namespace sdr
{
// Owns one sfx2::SvBaseLink per host (SdrObject or SdrPage background) that
@@ -74,9 +63,13 @@ public:
~FillBitmapLinkTracker();
// rNewURL is the deferred origin URL of the new item, or empty when the
// item is absent, already resolved, or the host is going away.
// item is absent, already resolved, or the host is going away. The
// SfxStyleSheet host covers a fill shared by every object or page using
// that style (e.g. an Impress slide background on the Background style),
// the drawing-layer counterpart of a fill on a Writer paragraph style.
void onFillBitmapURLChanged(SdrObject& rObj, std::u16string_view rNewURL);
void onFillBitmapURLChanged(SdrPage& rPage, std::u16string_view rNewURL);
void onFillBitmapURLChanged(SfxStyleSheet& rStyle, std::u16string_view rNewURL);
// Suppress the onFillBitmapURLChanged that a link's own write-back of the
// resolved graphic would otherwise raise for its host.
@@ -91,6 +84,7 @@ private:
const void* m_pUpdatingHost = nullptr;
std::map<SdrObject*, tools::SvRef<sfx2::SvBaseLink>> m_aObjLinks;
std::map<SdrPage*, tools::SvRef<sfx2::SvBaseLink>> m_aPageLinks;
std::map<SfxStyleSheet*, tools::SvRef<sfx2::SvBaseLink>> m_aStyleLinks;
};
}
+22
View File
@@ -39,6 +39,9 @@
#include <svl/intitem.hxx>
#include <svl/itemset.hxx>
#include <svx/fillbitmaplink.hxx>
#include <svx/xbtmpit.hxx>
#include <svx/xdef.hxx>
#include <svx/xflbmtit.hxx>
#include <svx/xflbstit.hxx>
#include <svx/xlnclit.hxx>
@@ -1074,6 +1077,25 @@ void SdStyleSheet::setPropertyValue_Impl(const OUString& aPropertyName, const cs
}
rStyleSet.Put( aSet );
// Track a deferred remote fill bitmap landing on this style sheet, so a
// shared background (e.g. an Impress slide Background style) is registered
// per style as the item is set, the drawing-layer counterpart of the
// SwFormat::SetFormatAttr hook for Writer paragraph styles.
if (auto* pPool = dynamic_cast<SdStyleSheetPool*>(GetPool()))
{
if (SdDrawDocument* pDoc = pPool->GetDoc())
{
if (sdr::FillBitmapLinkTracker* pTracker = pDoc->GetFillBitmapLinkTracker())
{
OUString aURL;
if (const XFillBitmapItem* pItem
= rStyleSet.GetItemIfSet(XATTR_FILLBITMAP, false))
aURL = getDeferredOriginURL(*pItem);
pTracker->onFillBitmapURLChanged(*this, aURL);
}
}
}
}
// Must be guarded by solar mutex; must not be disposed
+3 -3
View File
@@ -19,7 +19,6 @@
#include <rtl/ustring.hxx>
#include <sfx2/linkmgr.hxx>
#include <sfx2/objsh.hxx>
#include <svx/fillbitmaplink.hxx>
#include <svx/sdr/contact/displayinfo.hxx>
#include <svx/sdr/contact/viewcontact.hxx>
#include <svx/sdr/contact/viewobjectcontact.hxx>
@@ -187,7 +186,9 @@ CPPUNIT_TEST_FIXTURE(SdrTest, testSlideBackground)
// Allow link updates and resolve the linked background image via the
// LinkManager before rendering, so createNewSdrFillGraphicAttribute
// sees a Bitmap and not a deferred GraphicType::Default item.
// sees a Bitmap and not a deferred GraphicType::Default item. The link
// was registered per style sheet by the model's FillBitmapLinkTracker as
// the background was set on the Background style during load.
SfxObjectShell* pShell = SfxObjectShell::GetShellFromComponent(mxComponent);
CPPUNIT_ASSERT(pShell);
pShell->getEmbeddedObjectContainer().setUserAllowsLinkUpdate(true);
@@ -196,7 +197,6 @@ CPPUNIT_TEST_FIXTURE(SdrTest, testSlideBackground)
SdrModel& rModel = pSvxDrawPage->GetSdrPage()->getSdrModelFromSdrPage();
sfx2::LinkManager* pLinkMgr = rModel.GetLinkManager();
CPPUNIT_ASSERT(pLinkMgr);
registerFillBitmapLinks(rModel.GetItemPool(), *pLinkMgr, []() {});
pLinkMgr->UpdateAllLinks(false, nullptr, u""_ustr);
// When rendering that document:
+68 -79
View File
@@ -17,70 +17,17 @@
#include <svx/svdpage.hxx>
#include <svx/sdrobjectuser.hxx>
#include <svx/sdrpageuser.hxx>
#include <svx/sdr/contact/viewcontact.hxx>
#include <svl/hint.hxx>
#include <svl/itempool.hxx>
#include <svl/itemset.hxx>
#include <svl/lstner.hxx>
#include <svl/style.hxx>
#include <svx/xbtmpit.hxx>
#include <svx/xdef.hxx>
#include <vcl/GraphicObject.hxx>
#include <vcl/graph.hxx>
#include <com/sun/star/beans/XPropertySet.hpp>
namespace
{
// Link class for fill bitmap items with remote URLs.
// DataChanged receives the fetched graphic and updates the actual
// XFillBitmapItem in the attribute pool so the drawing layer picks up
// the resolved image on the next repaint.
class FillBitmapLink final : public sfx2::SvBaseLink
{
SfxItemPool& m_rPool;
sfx2::LinkManager& m_rLinkMgr;
std::function<void()> m_fnInvalidate;
public:
FillBitmapLink(SfxItemPool& rPool, sfx2::LinkManager& rLinkMgr,
std::function<void()> fnInvalidate)
: SvBaseLink(SfxLinkUpdateMode::ONCALL, SotClipboardFormatId::SVXB)
, m_rPool(rPool)
, m_rLinkMgr(rLinkMgr)
, m_fnInvalidate(std::move(fnInvalidate))
{
}
virtual UpdateResult DataChanged(const OUString& rMimeType,
const css::uno::Any& rValue) override
{
Graphic aGrf;
if (!m_rLinkMgr.GetGraphicFromAny(rMimeType, rValue, aGrf, nullptr))
return ERROR_GENERAL;
if (aGrf.GetType() == GraphicType::Default)
return ERROR_GENERAL;
OUString aURL;
sfx2::LinkManager::GetDisplayNames(this, nullptr, &aURL);
GraphicObject aGrfObj(std::move(aGrf));
// Replace the item in all attribute sets via pool surrogates
m_rPool.iterateItemSurrogates(
XATTR_FILLBITMAP, [&aURL, &aGrfObj](SfxItemPool::SurrogateData& rData) -> bool {
auto& rItem = static_cast<const XFillBitmapItem&>(rData.getItem());
const Graphic& rGrf = rItem.GetGraphicObject().GetGraphic();
if (rGrf.GetType() == GraphicType::Default && rGrf.getOriginURL() == aURL)
{
rData.setItem(std::make_unique<XFillBitmapItem>(aGrfObj));
}
return true;
});
if (m_fnInvalidate)
m_fnInvalidate();
return SUCCESS;
}
};
}
OUString getDeferredOriginURL(const XFillBitmapItem& rItem)
{
const Graphic& rGrf = rItem.GetGraphicObject().GetGraphic();
@@ -99,29 +46,6 @@ bool hasDeferredFillBitmapLinks(const SfxItemPool& rPool)
return false;
}
void registerFillBitmapLinks(SfxItemPool& rPool, sfx2::LinkManager& rLinkMgr,
std::function<void()> fnInvalidate)
{
for (const SfxPoolItem* pItem : rPool.GetItemSurrogates(XATTR_FILLBITMAP))
{
OUString aURL = getDeferredOriginURL(static_cast<const XFillBitmapItem&>(*pItem));
if (aURL.isEmpty())
continue;
tools::SvRef<sfx2::SvBaseLink> xLink(new FillBitmapLink(rPool, rLinkMgr, fnInvalidate));
rLinkMgr.InsertFileLink(*xLink, sfx2::SvBaseLinkObjectType::ClientGraphic, aURL);
}
}
void registerFillBitmapLinks(SdrModel& rModel, sfx2::LinkManager& rLinkMgr)
{
registerFillBitmapLinks(rModel.GetItemPool(), rLinkMgr, [&rModel]() {
for (sal_uInt16 nPage = 0; nPage < rModel.GetPageCount(); ++nPage)
rModel.GetPage(nPage)->GetViewContact().flushViewObjectContacts();
for (sal_uInt16 nPage = 0; nPage < rModel.GetMasterPageCount(); ++nPage)
rModel.GetMasterPage(nPage)->GetViewContact().flushViewObjectContacts();
});
}
namespace
{
// Per-host fill bitmap link. Unlike FillBitmapLink, which patches every pool
@@ -247,6 +171,62 @@ public:
m_rTracker.onFillBitmapURLChanged(const_cast<SdrPage&>(rPage), u"");
}
};
// Style-sheet counterpart, for a fill shared by every host using the style
// (e.g. an Impress slide background on the Background pseudo-style). Listens to
// the style sheet's broadcaster for SfxHintId::Dying and writes the resolved
// graphic back into the style sheet's item set so all users repaint.
class SdrStyleSheetFillBitmapLink final : public sfx2::SvBaseLink, public SfxListener
{
sdr::FillBitmapLinkTracker& m_rTracker;
SfxStyleSheet* m_pStyle;
sfx2::LinkManager& m_rLinkMgr;
public:
SdrStyleSheetFillBitmapLink(sdr::FillBitmapLinkTracker& rTracker, SfxStyleSheet& rStyle,
sfx2::LinkManager& rLinkMgr)
: SvBaseLink(SfxLinkUpdateMode::ONCALL, SotClipboardFormatId::SVXB)
, m_rTracker(rTracker)
, m_pStyle(&rStyle)
, m_rLinkMgr(rLinkMgr)
{
StartListening(rStyle);
}
virtual UpdateResult DataChanged(const OUString& rMimeType,
const css::uno::Any& rValue) override
{
tools::SvRef<SvBaseLink> xSelf(this);
if (!m_pStyle)
return ERROR_GENERAL;
Graphic aGrf;
if (!m_rLinkMgr.GetGraphicFromAny(rMimeType, rValue, aGrf, nullptr))
return ERROR_GENERAL;
if (aGrf.GetType() == GraphicType::Default)
return ERROR_GENERAL;
SfxItemSet& rSet = m_pStyle->GetItemSet();
OUString aName;
if (const XFillBitmapItem* pItem = rSet.GetItemIfSet(XATTR_FILLBITMAP, false))
aName = pItem->GetName();
rSet.Put(XFillBitmapItem(aName, aGrf));
m_pStyle->Broadcast(SfxHint(SfxHintId::DataChanged));
return SUCCESS;
}
virtual void Notify(SfxBroadcaster& /*rBC*/, const SfxHint& rHint) override
{
if (rHint.GetId() != SfxHintId::Dying)
return;
SfxStyleSheet* pStyle = m_pStyle;
m_pStyle = nullptr;
tools::SvRef<SvBaseLink> xSelf(this);
if (pStyle)
m_rTracker.onFillBitmapURLChanged(*pStyle, u"");
}
};
}
namespace sdr
@@ -264,9 +244,12 @@ FillBitmapLinkTracker::~FillBitmapLinkTracker()
pLinkMgr->Remove(rEntry.second.get());
for (const auto& rEntry : m_aPageLinks)
pLinkMgr->Remove(rEntry.second.get());
for (const auto& rEntry : m_aStyleLinks)
pLinkMgr->Remove(rEntry.second.get());
}
m_aObjLinks.clear();
m_aPageLinks.clear();
m_aStyleLinks.clear();
}
template <typename Host, typename Link>
@@ -303,6 +286,12 @@ void FillBitmapLinkTracker::onFillBitmapURLChanged(SdrPage& rPage, std::u16strin
{
onURLChangedImpl<SdrPage, SdrPageFillBitmapLink>(m_aPageLinks, rPage, rNewURL);
}
void FillBitmapLinkTracker::onFillBitmapURLChanged(SfxStyleSheet& rStyle,
std::u16string_view rNewURL)
{
onURLChangedImpl<SfxStyleSheet, SdrStyleSheetFillBitmapLink>(m_aStyleLinks, rStyle, rNewURL);
}
}
namespace