tdf#125969 cui: add in-use area image to bitmap list
This fixes a five year old (non-)easyhack with 3 duplicates. Note the nice-to-have dependencies in the bug report(s). When the document has a unique background (area) image, it is now added to the list of available images IF/WHEN the user looks in the area tab. This allows the user to switch back after changing, or re-use it in other places in the document. Most of this patch ended up being plumbing to ensure that this added image is ONLY available to the current document, because it MUST NOT be saved to the user profile. This change affects all apps and all types of areas: NICE -tested Writer pages, paragraphs, headers, textbox, sidebar(page) -tested Calc page style -tested Draw page, sidebar(page), textbox Caveats: -the bitmap list is NOT updated at the time of document import, only when area property inspected. (The bug requesting inclusion at the time of import is tdf#100832). make -srj1 UITest_writer_tests8 \ UITEST_TEST_NAME=tdf125969.tdf125969.test_tdf125969 \ SAL_USE_VCLPLUGIN=gen Change-Id: Ic9fea9b199602c4df1376e781d5df019526473d5 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/176253 Tested-by: Jenkins Reviewed-by: Justin Luth <jluth@mail.com>
This commit is contained in:
parent
d97085cc6c
commit
a366cd34a8
8 changed files with 187 additions and 15 deletions
|
@ -551,6 +551,8 @@ private:
|
|||
void CalculateBitmapPresetSize();
|
||||
sal_Int32 SearchBitmapList(std::u16string_view rBitmapName);
|
||||
sal_Int32 SearchBitmapList(const GraphicObject& rGraphicObject);
|
||||
tools::Long AddBitmap(const GraphicObject& rGraphicObject, const OUString& rName,
|
||||
bool bOnlyForThisDocument = false);
|
||||
|
||||
public:
|
||||
SvxBitmapTabPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rInAttrs);
|
||||
|
|
|
@ -37,8 +37,10 @@
|
|||
#include <dialmgr.hxx>
|
||||
#include <svx/dlgutil.hxx>
|
||||
#include <svl/intitem.hxx>
|
||||
#include <sfx2/bindings.hxx>
|
||||
#include <sfx2/objsh.hxx>
|
||||
#include <sfx2/opengrf.hxx>
|
||||
#include <sfx2/viewfrm.hxx>
|
||||
#include <vcl/image.hxx>
|
||||
#include <vcl/svapp.hxx>
|
||||
#include <vcl/weld.hxx>
|
||||
|
@ -167,9 +169,13 @@ void SvxBitmapTabPage::ActivatePage( const SfxItemSet& rSet )
|
|||
sal_Int32 nPos( 0 );
|
||||
if ( !aItem.isPattern() )
|
||||
{
|
||||
nPos = SearchBitmapList( aItem.GetGraphicObject() );
|
||||
if (nPos == -1)
|
||||
const GraphicObject& aGraphicObj = aItem.GetGraphicObject();
|
||||
if (aGraphicObj.GetType() != GraphicType::Bitmap)
|
||||
return;
|
||||
|
||||
nPos = SearchBitmapList(aGraphicObj);
|
||||
if (nPos == -1)
|
||||
nPos = AddBitmap(aGraphicObj, aItem.GetName(), /*OnlyForThisDocument=*/true);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -781,18 +787,7 @@ IMPL_LINK_NOARG(SvxBitmapTabPage, ClickImportHdl, weld::Button&, void)
|
|||
pDlg.disposeAndClear();
|
||||
|
||||
if( !nError )
|
||||
{
|
||||
m_pBitmapList->Insert(std::make_unique<XBitmapEntry>(aGraphic, aName), nCount);
|
||||
|
||||
sal_Int32 nId = m_xBitmapLB->GetItemId( nCount - 1 );
|
||||
BitmapEx aBitmap = m_pBitmapList->GetBitmapForPreview( nCount, m_xBitmapLB->GetIconSize() );
|
||||
|
||||
m_xBitmapLB->InsertItem( nId + 1, Image(aBitmap), aName );
|
||||
m_xBitmapLB->SelectItem( nId + 1 );
|
||||
m_nBitmapListState |= ChangeType::MODIFIED;
|
||||
|
||||
ModifyBitmapHdl(m_xBitmapLB.get());
|
||||
}
|
||||
AddBitmap(aGraphic, aName);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -836,4 +831,31 @@ sal_Int32 SvxBitmapTabPage::SearchBitmapList(std::u16string_view rBitmapName)
|
|||
return nPos;
|
||||
}
|
||||
|
||||
tools::Long SvxBitmapTabPage::AddBitmap(const GraphicObject& rGraphicObject, const OUString& rName,
|
||||
bool bOnlyForThisDocument)
|
||||
{
|
||||
const tools::Long nLastPos = m_pBitmapList->Count();
|
||||
|
||||
auto xBitmapEntry = std::make_unique<XBitmapEntry>(rGraphicObject, rName);
|
||||
if (bOnlyForThisDocument)
|
||||
xBitmapEntry->SetSavingAllowed(false);
|
||||
m_pBitmapList->Insert(std::move(xBitmapEntry), nLastPos);
|
||||
|
||||
BitmapEx aBitmap = m_pBitmapList->GetBitmapForPreview(nLastPos, m_xBitmapLB->GetIconSize());
|
||||
|
||||
const sal_uInt16 nHighestId = m_xBitmapLB->GetItemId(nLastPos - 1);
|
||||
m_xBitmapLB->InsertItem(nHighestId + 1, Image(aBitmap), rName);
|
||||
m_xBitmapLB->SelectItem(nHighestId + 1);
|
||||
m_nBitmapListState |= ChangeType::MODIFIED;
|
||||
|
||||
ModifyBitmapHdl(m_xBitmapLB.get());
|
||||
|
||||
// inform sidebar, etc. that the list of images has changed.
|
||||
SfxViewFrame* pViewFrame = SfxViewFrame::Current();
|
||||
if (pViewFrame)
|
||||
pViewFrame->GetBindings().Invalidate(SID_ATTR_PAGE_BITMAP, /*ClearCacheStatus=*/true);
|
||||
|
||||
return nLastPos;
|
||||
}
|
||||
|
||||
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|
||||
|
|
|
@ -28,6 +28,7 @@ class SVXCORE_DLLPUBLIC XPropertyEntry
|
|||
private:
|
||||
OUString maPropEntryName;
|
||||
BitmapEx maUiBitmap;
|
||||
bool mbSavingAllowed;
|
||||
|
||||
protected:
|
||||
XPropertyEntry(OUString aPropEntryName);
|
||||
|
@ -40,11 +41,14 @@ public:
|
|||
|
||||
XPropertyEntry& operator=(XPropertyEntry const&) = default;
|
||||
XPropertyEntry& operator=(XPropertyEntry&&) = default;
|
||||
virtual std::unique_ptr<XPropertyEntry> Clone() const = 0;
|
||||
|
||||
void SetName(const OUString& rPropEntryName) { maPropEntryName = rPropEntryName; }
|
||||
const OUString& GetName() const { return maPropEntryName; }
|
||||
void SetUiBitmap(const BitmapEx& rUiBitmap) { maUiBitmap = rUiBitmap; }
|
||||
const BitmapEx& GetUiBitmap() const { return maUiBitmap; }
|
||||
void SetSavingAllowed(bool bSet) { mbSavingAllowed = bSet; }
|
||||
bool GetSavingAllowed() const { return mbSavingAllowed; }
|
||||
};
|
||||
|
||||
#endif // INCLUDED_SVX_XPROPERTYENTRY_HXX
|
||||
|
|
|
@ -46,6 +46,7 @@ private:
|
|||
|
||||
public:
|
||||
XColorEntry(const Color& rColor, const OUString& rName);
|
||||
std::unique_ptr<XPropertyEntry> Clone() const override;
|
||||
|
||||
const Color& GetColor() const
|
||||
{
|
||||
|
@ -61,6 +62,7 @@ private:
|
|||
public:
|
||||
XLineEndEntry(basegfx::B2DPolyPolygon aB2DPolyPolygon, const OUString& rName);
|
||||
XLineEndEntry(const XLineEndEntry& rOther);
|
||||
std::unique_ptr<XPropertyEntry> Clone() const override;
|
||||
|
||||
const basegfx::B2DPolyPolygon& GetLineEnd() const
|
||||
{
|
||||
|
@ -76,6 +78,7 @@ private:
|
|||
public:
|
||||
XDashEntry(const XDash& rDash, const OUString& rName);
|
||||
XDashEntry(const XDashEntry& rOther);
|
||||
std::unique_ptr<XPropertyEntry> Clone() const override;
|
||||
|
||||
const XDash& GetDash() const
|
||||
{
|
||||
|
@ -91,6 +94,7 @@ private:
|
|||
public:
|
||||
XHatchEntry(const XHatch& rHatch, const OUString& rName);
|
||||
XHatchEntry(const XHatchEntry& rOther);
|
||||
std::unique_ptr<XPropertyEntry> Clone() const override;
|
||||
|
||||
const XHatch& GetHatch() const
|
||||
{
|
||||
|
@ -106,6 +110,7 @@ private:
|
|||
public:
|
||||
XGradientEntry(const basegfx::BGradient& rGradient, const OUString& rName);
|
||||
XGradientEntry(const XGradientEntry& rOther);
|
||||
std::unique_ptr<XPropertyEntry> Clone() const override;
|
||||
|
||||
const basegfx::BGradient& GetGradient() const
|
||||
{
|
||||
|
@ -121,6 +126,7 @@ private:
|
|||
public:
|
||||
XBitmapEntry(const GraphicObject& rGraphicObject, const OUString& rName);
|
||||
XBitmapEntry(const XBitmapEntry& rOther);
|
||||
std::unique_ptr<XPropertyEntry> Clone() const override;
|
||||
|
||||
const GraphicObject& GetGraphicObject() const
|
||||
{
|
||||
|
@ -167,6 +173,9 @@ protected:
|
|||
bool isValidIdx(tools::Long nIndex) const;
|
||||
virtual BitmapEx CreateBitmapForUI(tools::Long nIndex) = 0;
|
||||
|
||||
private:
|
||||
bool mbNeedsExportableList; // impl: avoid seldom-needed, expensive list cloning
|
||||
|
||||
public:
|
||||
XPropertyList(const XPropertyList&) = delete;
|
||||
XPropertyList& operator=(const XPropertyList&) = delete;
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
XPropertyEntry::XPropertyEntry(OUString aPropEntryName)
|
||||
: maPropEntryName(std::move(aPropEntryName))
|
||||
, mbSavingAllowed(true)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -36,6 +36,11 @@ XColorEntry::XColorEntry(const Color& rColor, const OUString& rName)
|
|||
{
|
||||
}
|
||||
|
||||
std::unique_ptr<XPropertyEntry> XColorEntry::Clone() const
|
||||
{
|
||||
return std::make_unique<XColorEntry>(m_aColor, GetName());
|
||||
}
|
||||
|
||||
XLineEndEntry::XLineEndEntry(basegfx::B2DPolyPolygon _aB2DPolyPolygon, const OUString& rName)
|
||||
: XPropertyEntry(rName),
|
||||
m_aB2DPolyPolygon(std::move(_aB2DPolyPolygon))
|
||||
|
@ -48,6 +53,11 @@ XLineEndEntry::XLineEndEntry(const XLineEndEntry& rOther)
|
|||
{
|
||||
}
|
||||
|
||||
std::unique_ptr<XPropertyEntry> XLineEndEntry::Clone() const
|
||||
{
|
||||
return std::make_unique<XLineEndEntry>(*this);
|
||||
}
|
||||
|
||||
XDashEntry::XDashEntry(const XDash& rDash, const OUString& rName)
|
||||
: XPropertyEntry(rName),
|
||||
m_aDash(rDash)
|
||||
|
@ -60,6 +70,11 @@ m_aDash(rOther.m_aDash)
|
|||
{
|
||||
}
|
||||
|
||||
std::unique_ptr<XPropertyEntry> XDashEntry::Clone() const
|
||||
{
|
||||
return std::make_unique<XDashEntry>(*this);
|
||||
}
|
||||
|
||||
XHatchEntry::XHatchEntry(const XHatch& rHatch, const OUString& rName)
|
||||
: XPropertyEntry(rName),
|
||||
m_aHatch(rHatch)
|
||||
|
@ -72,6 +87,11 @@ XHatchEntry::XHatchEntry(const XHatchEntry& rOther)
|
|||
{
|
||||
}
|
||||
|
||||
std::unique_ptr<XPropertyEntry> XHatchEntry::Clone() const
|
||||
{
|
||||
return std::make_unique<XHatchEntry>(*this);
|
||||
}
|
||||
|
||||
XGradientEntry::XGradientEntry(const basegfx::BGradient& rGradient, const OUString& rName)
|
||||
: XPropertyEntry(rName),
|
||||
m_aGradient(rGradient)
|
||||
|
@ -84,6 +104,11 @@ XGradientEntry::XGradientEntry(const XGradientEntry& rOther)
|
|||
{
|
||||
}
|
||||
|
||||
std::unique_ptr<XPropertyEntry> XGradientEntry::Clone() const
|
||||
{
|
||||
return std::make_unique<XGradientEntry>(*this);
|
||||
}
|
||||
|
||||
XBitmapEntry::XBitmapEntry(const GraphicObject& rGraphicObject, const OUString& rName)
|
||||
: XPropertyEntry(rName),
|
||||
maGraphicObject(rGraphicObject)
|
||||
|
@ -96,6 +121,11 @@ XBitmapEntry::XBitmapEntry(const XBitmapEntry& rOther)
|
|||
{
|
||||
}
|
||||
|
||||
std::unique_ptr<XPropertyEntry> XBitmapEntry::Clone() const
|
||||
{
|
||||
return std::make_unique<XBitmapEntry>(*this);
|
||||
}
|
||||
|
||||
XPropertyList::XPropertyList(
|
||||
XPropertyListType type,
|
||||
OUString aPath, OUString aReferer
|
||||
|
@ -105,6 +135,7 @@ XPropertyList::XPropertyList(
|
|||
maReferer (std::move( aReferer )),
|
||||
mbListDirty ( true ),
|
||||
mbEmbedInDocument( false )
|
||||
, mbNeedsExportableList(false)
|
||||
{
|
||||
// fprintf (stderr, "Create type %d count %d\n", (int)meType, count++);
|
||||
}
|
||||
|
@ -183,6 +214,9 @@ void XPropertyList::Insert(std::unique_ptr<XPropertyEntry> pEntry, tools::Long n
|
|||
return;
|
||||
}
|
||||
|
||||
if (!pEntry->GetSavingAllowed())
|
||||
mbNeedsExportableList = true;
|
||||
|
||||
if (isValidIdx(nIndex)) {
|
||||
maList.insert( maList.begin()+nIndex, std::move(pEntry) );
|
||||
} else {
|
||||
|
@ -302,8 +336,27 @@ bool XPropertyList::Save()
|
|||
if( aURL.getExtension().isEmpty() )
|
||||
aURL.setExtension( GetDefaultExt() );
|
||||
|
||||
XPropertyListRef rExportableList = CreatePropertyList(meType, maPath, "");
|
||||
if (mbNeedsExportableList)
|
||||
{
|
||||
rExportableList->SetName(maName);
|
||||
rExportableList->SetDirty(mbListDirty);
|
||||
bool bHasUnsaveableEntry = false;
|
||||
for (const std::unique_ptr<XPropertyEntry>& rEntry : maList)
|
||||
{
|
||||
if (rEntry->GetSavingAllowed())
|
||||
rExportableList->Insert(rEntry->Clone());
|
||||
else
|
||||
bHasUnsaveableEntry = true;
|
||||
}
|
||||
if (!bHasUnsaveableEntry)
|
||||
mbNeedsExportableList = false;
|
||||
}
|
||||
css::uno::Reference<css::container::XNameContainer> xExportableNameContainer
|
||||
= mbNeedsExportableList ? rExportableList->createInstance() : createInstance();
|
||||
|
||||
return SvxXMLXTableExportComponent::save( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ),
|
||||
createInstance(),
|
||||
xExportableNameContainer,
|
||||
uno::Reference< embed::XStorage >(), nullptr );
|
||||
}
|
||||
|
||||
|
|
BIN
sw/qa/uitest/data/paragraphAreaFill.odt
Normal file
BIN
sw/qa/uitest/data/paragraphAreaFill.odt
Normal file
Binary file not shown.
81
sw/qa/uitest/writer_tests8/tdf125969.py
Normal file
81
sw/qa/uitest/writer_tests8/tdf125969.py
Normal file
|
@ -0,0 +1,81 @@
|
|||
# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-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/.
|
||||
#
|
||||
from libreoffice.uno.propertyvalue import mkPropertyValues
|
||||
|
||||
from uitest.framework import UITestCase
|
||||
from uitest.uihelper.common import get_url_for_data_file
|
||||
from uitest.uihelper.common import select_pos, get_state_as_dict
|
||||
import time
|
||||
|
||||
# bug 125969: make in-use bitmap-area-fill available for re-use, but ONLY IN THE SAME DOCUMENT
|
||||
class tdf125969(UITestCase):
|
||||
|
||||
number_of_images = 0
|
||||
|
||||
def click_button(self, dialog, button):
|
||||
xButton = dialog.getChild(button)
|
||||
xButton.executeAction("CLICK", tuple())
|
||||
|
||||
def test_tdf125969(self):
|
||||
with self.ui_test.load_file(get_url_for_data_file("paragraphAreaFill.odt")):
|
||||
xWriterDoc = self.xUITest.getTopFocusWindow()
|
||||
xWriterEdit = xWriterDoc.getChild("writer_edit")
|
||||
|
||||
self.xUITest.executeCommand(".uno:Sidebar") #turn on sidebar
|
||||
xWriterEdit.executeAction("SIDEBAR", mkPropertyValues({"PANEL": "PageStylesPanel"}))
|
||||
|
||||
# Get baseline from sidebar: count number of initially available bitmaps by default
|
||||
backgroundType = xWriterEdit.getChild('bgselect') #type of background: color, gradient, ...
|
||||
self.ui_test.wait_until_property_is_updated(backgroundType, "SelectEntryText", "Bitmap")
|
||||
|
||||
imageCollection = xWriterEdit.getChild("lbbitmap") #listbox containing image names
|
||||
number_of_images = get_state_as_dict(imageCollection)["EntryCount"]
|
||||
# print (get_state_as_dict(imageCollection))
|
||||
# time.sleep (10)
|
||||
|
||||
# The paragraph area has a custom background logo - which we want to become available
|
||||
# for re-use everywhere as a background fill
|
||||
|
||||
# visit the paragraph background property - which now auto-adds it to the collection
|
||||
with self.ui_test.execute_dialog_through_command(".uno:ParagraphDialog", close_button="cancel") as xDialog:
|
||||
tabcontrol = xDialog.getChild("tabcontrol")
|
||||
select_pos(tabcontrol, "8") # area tab
|
||||
#time.sleep(1)
|
||||
|
||||
self.ui_test.wait_until_property_is_updated(imageCollection, "SelectEntryText", "Painted White")
|
||||
# xToolkit = self.xContext.ServiceManager.createInstance('com.sun.star.awt.Toolkit')
|
||||
# xToolkit.waitUntilAllIdlesDispatched()
|
||||
time.sleep (1)
|
||||
# test: the paragraph's wasta-offline logo was added and the list box was refreshed
|
||||
self.assertEqual(int(number_of_images) + 1, int(get_state_as_dict(imageCollection)["EntryCount"]))
|
||||
|
||||
# A new document must not have access to the collected images from another document
|
||||
with self.ui_test.create_doc_in_start_center("writer"):
|
||||
xWriterDoc = self.xUITest.getTopFocusWindow()
|
||||
xWriterEdit = xWriterDoc.getChild("writer_edit")
|
||||
|
||||
# because I don't know how to change the sidebar to bitmap mode, use the page dialog
|
||||
with self.ui_test.execute_dialog_through_command(".uno:PageDialog", close_button="ok") as xDialog:
|
||||
tabcontrol = xDialog.getChild("tabcontrol")
|
||||
select_pos(tabcontrol, "2") # area tab
|
||||
self.click_button(xDialog, 'btnbitmap')
|
||||
#time.sleep (2)
|
||||
|
||||
backgroundType = xWriterEdit.getChild('bgselect')
|
||||
imageCollection = xWriterEdit.getChild("lbbitmap")
|
||||
self.ui_test.wait_until_property_is_updated(backgroundType, "SelectEntryText", "Bitmap")
|
||||
# This number MUST NOT be higher than the initial state.
|
||||
# We must not allow document images to leak into the user profile
|
||||
self.assertEqual(number_of_images, get_state_as_dict(imageCollection)["EntryCount"])
|
||||
#time.sleep (10)
|
||||
|
||||
# xWriterEdit.getChild("bogus for debugging")
|
||||
|
||||
self.xUITest.executeCommand(".uno:Sidebar") # good idea to turn off sidebar again
|
||||
# vim: set shiftwidth=4 softtabstop=4 expandtab:
|
Loading…
Reference in a new issue