tdf#89826 Add interactive property chips to style Organizer tab

The Organizer tab of the style dialog now shows interactive "chips"
for each property that differs from the parent style. Each chip
displays the property name and value, and includes an X button to
reset that property back to parent inheritance. All properties
are shown full text, no ellipsis.

Properties are grouped by tab page (Font, Font effects, etc.)
and displayed in a scrollable area. A View/Edit toggle lets the
user switch between the traditional description text and the
interactive chip view.

Implementation summary:

- New PropertyChip and PropertyCategoryRow widgets (sfx2) with
  corresponding .ui files for the chip layout.

- SfxStyleSheetBase::GetItemPresentation() virtual method returns
  per-property presentation strings, overridden in SwDocStyleSheet
  and ScStyleSheet for module-specific attributes.

- SfxItemSet access tracker mechanism to automatically map WhichIds
  to their owning tab pages via BuildWhichToTabMap().

- SfxTabDialogController::InvalidateItem() propagates chip resets
  to the output set and affected tab pages.

- SwDocStyleSheet::ResetItems() uses SwDoc::ResetAttrAtFormat() to
  ensure proper undo support in Writer.

- SfxStyleDialogController::Ok() clears invalidated items from the
  style's item set on dialog confirmation.

- SvxAsianTabPage now calls SetExchangeSupport() and implements
  DeactivatePage() so its items are properly tracked.

- Access tracker ignore guards in SvxCharBasePage::SetPrevFontWidthScale
  and SvxTransparenceTabPage::InitPreview to avoid false positives
  from preview-only item access.

Includes a UITest for Writer (sw/qa/uitest/ui/fmtui/tdf89826.py)
that creates a child style, modifies a property, removes it via
chip, and verifies inheritance is restored including undo.

A Calc UITest is not included because the style edit dialog in Calc
is opened asynchronously (ScTabViewShell::ExecuteStyleEdit) and the
UITest framework's execute_dialog_through_command cannot capture it.
The Calc functionality has been verified manually. A Calc UITest can
be added once the framework supports async style dialogs.

Change-Id: Ia764405798c212fe5340bba254f2ff6ac62365f6
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/199318
Reviewed-by: Heiko Tietze <heiko.tietze@documentfoundation.org>
Tested-by: Jenkins
This commit is contained in:
Paolo Benvenuto
2026-02-28 15:52:40 +01:00
committed by Heiko Tietze
parent 6bb9a35e5b
commit 52010fde09
31 changed files with 1627 additions and 22 deletions
+1
View File
@@ -357,6 +357,7 @@ public:
virtual bool FillItemSet( SfxItemSet* rSet ) override;
virtual void Reset( const SfxItemSet* rSet ) override;
virtual DeactivateRC DeactivatePage( SfxItemSet* pSet ) override;
virtual void ChangesApplied() override;
};
+2
View File
@@ -154,11 +154,13 @@ void SvxCharBasePage::ActivatePage(const SfxItemSet& rSet)
void SvxCharBasePage::SetPrevFontWidthScale( const SfxItemSet& rSet )
{
sal_uInt16 nWhich = GetWhich( SID_ATTR_CHAR_SCALEWIDTH );
rSet.SetAccessTrackerIgnore(true);
if (rSet.GetItemState(nWhich)>=SfxItemState::DEFAULT)
{
const SvxCharScaleWidthItem &rItem = static_cast<const SvxCharScaleWidthItem&>( rSet.Get( nWhich ) );
m_aPreviewWin.SetFontWidthScale(rItem.GetValue());
}
rSet.SetAccessTrackerIgnore(false);
}
void SvxCharBasePage::SetPrevFontEscapement( sal_uInt8 nProp, sal_uInt8 nEscProp, short nEsc )
+8
View File
@@ -3056,12 +3056,20 @@ SvxAsianTabPage::SvxAsianTabPage(weld::Container* pPage, weld::DialogController*
, m_xHangingPunctCB(m_xBuilder->weld_check_button(u"checkHangPunct"_ustr))
, m_xScriptSpaceCB(m_xBuilder->weld_check_button(u"checkApplySpacing"_ustr))
{
SetExchangeSupport();
}
SvxAsianTabPage::~SvxAsianTabPage()
{
}
DeactivateRC SvxAsianTabPage::DeactivatePage( SfxItemSet* pSet )
{
if ( pSet )
FillItemSet( pSet );
return DeactivateRC::LeavePage;
}
std::unique_ptr<SfxTabPage> SvxAsianTabPage::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rSet)
{
return std::make_unique<SvxAsianTabPage>(pPage, pController, *rSet);
+2
View File
@@ -458,6 +458,7 @@ bool SvxTransparenceTabPage::InitPreview(const SfxItemSet& rSet)
}
// Get fillstyle for preview
rSet.SetAccessTrackerIgnore(true);
rXFSet.Put ( rSet.Get(XATTR_FILLSTYLE) );
rXFSet.Put ( rSet.Get(XATTR_FILLCOLOR) );
rXFSet.Put ( rSet.Get(XATTR_FILLGRADIENT) );
@@ -469,6 +470,7 @@ bool SvxTransparenceTabPage::InitPreview(const SfxItemSet& rSet)
m_aCtlBitmapPreview.SetAttributes( aXFillAttr.GetItemSet() );
bBitmap = rSet.Get(XATTR_FILLSTYLE).GetValue() == drawing::FillStyle_BITMAP;
rSet.SetAccessTrackerIgnore(false);
// show the right preview window
if ( bBitmap )
+5
View File
@@ -64,6 +64,11 @@ public:
virtual void SetCurPageId( const OUString &rName ) = 0;
virtual WhichRangesContainer GetInputRanges( const SfxItemPool& ) = 0;
virtual void SetInputSet( const SfxItemSet* pInSet ) = 0;
virtual const std::set<sal_uInt16>& GetInvalidatedWhichIds() const
{
static const std::set<sal_uInt16> empty;
return empty;
}
};
class SfxAbstractApplyTabDialog : virtual public SfxAbstractTabDialog
+2
View File
@@ -231,6 +231,8 @@
#define STR_TABPAGE_INVALIDNAME NC_("STR_TABPAGE_INVALIDNAME", "This name is already in use.")
#define STR_TABPAGE_INVALIDSTYLE NC_("STR_TABPAGE_INVALIDSTYLE", "This Style does not exist.")
#define STR_TABPAGE_INVALIDPARENT NC_("STR_TABPAGE_INVALIDPARENT", "This Style cannot be used as a base Style,\nbecause it would result in a recursive reference.")
#define STR_RESET_PROPERTY_TOOLTIP NC_("STR_RESET_PROPERTY_TOOLTIP", "Reset this property to inherit from parent style")
#define STR_NO_MODIFIED_PROPERTIES NC_("STR_NO_MODIFIED_PROPERTIES", "All properties are inherited from parent style.")
#define STR_POOL_STYLE_NAME NC_("STR_POOL_STYLE_NAME", "Name already exists as a default Style.\nPlease choose another name.")
#define STR_DELETE_STYLE_USED NC_("STR_DELETE_STYLE_USED", "One or more of the selected styles is in use in this document.\nIf you will delete it, text or objects using these styles will revert to the parent style.\nDo you still wish to delete these styles?\n")
#define STR_DELETE_STYLE NC_("STR_DELETE_STYLE", "Styles in use: ")
+22 -2
View File
@@ -22,6 +22,7 @@
#include <memory>
#include <unordered_map>
#include <string_view>
#include <set>
#include <sal/config.h>
#include <sfx2/dllapi.h>
@@ -32,6 +33,7 @@
#include <svl/itemset.hxx>
#include <svl/setitem.hxx>
#include <o3tl/typed_flags_set.hxx>
#include <map>
class Bitmap;
class SfxTabPage;
@@ -80,9 +82,11 @@ private:
std::unique_ptr<SfxItemSet> m_pOutSet;
std::unique_ptr<TabDlg_Impl> m_pImpl;
WhichRangesContainer m_pRanges;
std::map<sal_uInt16, OUString> m_aWhichToTabMap;
OUString m_sAppPageId;
bool m_bStandardPushed;
std::unique_ptr<SfxAllItemSet> m_xItemSet;
std::map<sal_uInt16, int> m_aWhichOrderMap;
DECL_DLLPRIVATE_LINK(ActivatePageHdl, const OUString&, void);
DECL_DLLPRIVATE_LINK(DeactivatePageHdl, const OUString&, bool);
@@ -92,6 +96,7 @@ private:
protected:
virtual short Ok();
std::set<sal_uInt16> m_aInvalidatedWhichIds;
virtual void RefreshInputSet();
virtual SfxItemSet* CreateInputItemSet(const OUString& rName);
virtual void PageCreated(const OUString &rName, SfxTabPage &rPage);
@@ -161,6 +166,23 @@ public:
void ShowPage(const OUString& rName); // SetCurPageId + call Activate on it
OUString GetCurPageId() const;
SfxTabPage* GetCurTabPage() const { return GetTabPage(GetCurPageId()); }
void ResetTabPage(std::u16string_view rPageId);
void ResetAllTabPages();
void InvalidateItem(sal_uInt16 nWhich);
OUString GetTabPageNameForWhich(sal_uInt16 nWhich) const;
void BuildWhichToTabMap();
OUString GetTabPageLabel(const OUString& rPageId) const;
std::vector<OUString> GetTabPageIds() const;
int GetWhichOrder(sal_uInt16 nWhich) const {
auto it = m_aWhichOrderMap.find(nWhich);
return it != m_aWhichOrderMap.end() ? it->second : INT_MAX;
}
const SfxItemSet* GetExampleSet() const override { return m_xExampleSet.get(); }
/// Subclasses can override to provide additional WhichId→tab mappings
/// that the access tracker cannot determine correctly.
virtual std::map<sal_uInt16, OUString> GetWhichToTabOverrides() const { return {}; }
const std::set<sal_uInt16>& GetInvalidatedWhichIds() const { return m_aInvalidatedWhichIds; }
// may provide local slots converted by Map
const WhichRangesContainer& GetInputRanges( const SfxItemPool& );
@@ -181,8 +203,6 @@ public:
static bool runAsync(const std::shared_ptr<SfxTabDialogController>& rController,
const std::function<void(sal_Int32)>&);
virtual const SfxItemSet* GetExampleSet() const override { return m_xExampleSet.get(); }
void SetApplyHandler(const Link<weld::Button&,void>& _rHdl);
//calls Ok without closing dialog
+5
View File
@@ -28,6 +28,7 @@
#include <svl/poolitem.hxx>
#include <svl/typedwhich.hxx>
#include <svl/whichranges.hxx>
#include <set>
class SfxItemPool;
@@ -90,6 +91,8 @@ class SAL_WARN_UNUSED SVL_DLLPUBLIC SfxItemSet
SfxItemPool* m_pPool; ///< pool that stores the items
const SfxItemSet* m_pParent; ///< derivation
sal_uInt16 m_nRegister; ///< number of items with NeedsSurrogateSupport
mutable std::vector<sal_uInt16>* m_pAccessTracker = nullptr;
mutable bool m_bAccessTrackerIgnore = false;
#ifdef DBG_UTIL
sal_uInt16 m_nRegisteredSfxItemIter;
@@ -244,6 +247,8 @@ public:
void SetRanges( WhichRangesContainer&& );
void MergeRange( sal_uInt16 nFrom, sal_uInt16 nTo );
const SfxItemSet* GetParent() const { return m_pParent; }
void SetAccessTracker(std::vector<sal_uInt16>* pTracker) const { m_pAccessTracker = pTracker; }
void SetAccessTrackerIgnore(bool b) const { m_bAccessTrackerIgnore = b; }
bool operator==(const SfxItemSet &) const;
size_t GetHashCode() const;
+8
View File
@@ -34,6 +34,9 @@
#include <memory>
#include <optional>
#include <vector>
#include <utility>
// This is used as a flags enum in sw/, but only there,
// so I don't pull in o3tl::typed_flags here
enum class SfxStyleFamily {
@@ -164,6 +167,11 @@ public:
virtual bool IsUsed() const; // Default true
virtual OUString GetDescription( MapUnit eMetric );
/// Returns individual property presentations for building UI chips
/// Each pair contains: WhichId and presentation string
virtual std::vector<std::pair<sal_uInt16, OUString>> GetItemPresentation(
MapUnit eMetric, const SfxItemSet* pWorkingSet = nullptr);
virtual OUString GetUsedBy() { return OUString(); }
SfxStyleSheetBasePool* GetPool() { return m_pPool; }
+3
View File
@@ -54,6 +54,9 @@ public:
/// Fix for expensive dynamic_cast
virtual bool isScStyleSheet() const override { return true; }
virtual std::vector<std::pair<sal_uInt16, OUString>> GetItemPresentation(
MapUnit eMetric, const SfxItemSet* pWorkingSet = nullptr) override;
private:
virtual ~ScStyleSheet() override;
+13
View File
@@ -445,6 +445,19 @@
#define STR_DUPLICATERECORDS_DATACONATINSROWHEADERS NC_("STR_DUPLICATERECORDS_DATACONATINSROWHEADERS", "Data contains row headers")
#define STR_DUPLICATERECORDS_DATACONATINSCOLUMNHEADERS NC_("STR_DUPLICATERECORDS_DATACONATINSCOLUMNHEADERS", "Data contains column headers")
#define STR_ATTR_PROTECTION NC_("STR_ATTR_PROTECTION", "Protection")
#define STR_ATTR_ROTATION NC_("STR_ATTR_ROTATION", "Rotation")
#define STR_ATTR_ROTATION_MODE NC_("STR_ATTR_ROTATION_MODE", "Rotation Mode")
#define STR_ATTR_TEXT_DIR NC_("STR_ATTR_TEXT_DIR", "Text Direction")
#define STR_ATTR_WRAP_TEXT NC_("STR_ATTR_WRAP_TEXT", "Wrap Text")
#define STR_ATTR_SHRINK_FIT NC_("STR_ATTR_SHRINK_FIT", "Shrink to Fit")
#define STR_ATTR_STACKED NC_("STR_ATTR_STACKED", "Stacked")
#define STR_ATTR_H_ALIGN NC_("STR_ATTR_H_ALIGN", "Horizontal Alignment")
#define STR_ATTR_V_ALIGN NC_("STR_ATTR_V_ALIGN", "Vertical Alignment")
#define STR_ATTR_INDENT NC_("STR_ATTR_INDENT", "Indent")
#define STR_ATTR_SPACING NC_("STR_ATTR_SPACING", "Spacing")
#define STR_ATTR_NUM_FORMAT NC_("STR_ATTR_NUM_FORMAT", "Number Format")
#define STR_ATTR_ASIAN_LAYOUT NC_("STR_ATTR_ASIAN_LAYOUT", "Asian Layout Mode")
#define STR_CONTENT_WITH_UNKNOWN_ENCRYPTION NC_("STR_CONTENT_WITH_UNKNOWN_ENCRYPTION", "Document contains DRM content that is encrypted with an unknown encryption method. Only the un-encrypted content will be shown.")
+189
View File
@@ -39,13 +39,33 @@
#include <svl/itemset.hxx>
#include <svl/numformat.hxx>
#include <svl/hint.hxx>
#include <unotools/intlwrapper.hxx>
#include <unotools/syslocale.hxx>
#include <svl/zformat.hxx>
#include <o3tl/unit_conversion.hxx>
#include <attrib.hxx>
#include <globstr.hrc>
#include <strings.hrc>
#include <scresid.hxx>
#include <sc.hrc>
#include <svx/strarray.hxx>
#include <svx/svxids.hrc>
#include <svl/numformat.hxx>
#include <svl/zforlist.hxx>
#include <svl/intitem.hxx>
#include <svl/ilstitem.hxx>
#include <svl/itemiter.hxx>
#include <svl/eitem.hxx>
#include <editeng/boxitem.hxx>
#include <scitems.hxx>
#include <globstr.hrc>
#include <scresid.hxx>
#include <document.hxx>
#include <docsh.hxx>
#include <sfx2/objsh.hxx>
constexpr auto TWO_CM = o3tl::convert(2, o3tl::Length::cm, o3tl::Length::twip); // 1134
constexpr auto HFDIST_CM = o3tl::convert(250, o3tl::Length::mm100, o3tl::Length::twip); // 142
@@ -274,6 +294,175 @@ SfxItemSet& ScStyleSheet::GetItemSet()
return *pSet;
}
std::vector<std::pair<sal_uInt16, OUString>> ScStyleSheet::GetItemPresentation(
MapUnit eMetric, const SfxItemSet* pWorkingSet)
{
std::vector<std::pair<sal_uInt16, OUString>> aResult;
IntlWrapper aIntlWrapper(SvtSysLocale().GetUILanguageTag());
const SfxItemSet& rSet = GetItemSet();
const SfxItemSet* pCheckSet = pWorkingSet ? pWorkingSet : &rSet;
// Get parent item set for comparison
const SfxItemSet* pParentSet = nullptr;
SfxStyleSheetBase* pParentStyle = nullptr;
if (!GetParent().isEmpty())
{
pParentStyle = m_pPool->Find(GetParent(), GetFamily());
if (pParentStyle)
pParentSet = &pParentStyle->GetItemSet();
}
// Get the number formatter for ATTR_VALUE_FORMAT
SvNumberFormatter* pFormatter = nullptr;
if (ScDocShell* pDocSh = dynamic_cast<ScDocShell*>(SfxObjectShell::Current()))
{
pFormatter = pDocSh->GetDocument().GetFormatTable();
}
SfxItemIter aIter(rSet);
for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem())
{
if (IsInvalidItem(pItem))
continue;
sal_uInt16 nWhich = pItem->Which();
// Only show items explicitly SET in this style (not inherited)
if (pCheckSet->GetItemState(nWhich, false) != SfxItemState::SET)
continue;
// Skip items identical to parent
if (pParentSet)
{
const SfxPoolItem* pParentItem = nullptr;
if (pParentSet->GetItemState(nWhich, true, &pParentItem) == SfxItemState::SET
&& pParentItem && *pParentItem == *pItem)
continue;
}
// Skip items equal to pool default
if (*pItem == rSet.GetPool()->GetUserOrPoolDefaultItem(nWhich))
continue;
// Skip internal/structural items that don't make sense as chips
switch (nWhich)
{
case SID_ATTR_BORDER_INNER:
case SID_ATTR_PARA_MODEL:
case SID_ATTR_PAGE_SIZE:
case SID_ATTR_PAGE_MAXSIZE:
case SID_ATTR_PAGE_PAPERBIN:
continue;
default:
break;
}
OUString aItemPresentation;
// Special handling for number format: the default GetPresentation
// just returns the raw format key (e.g. "10122")
if (nWhich == ATTR_VALUE_FORMAT && pFormatter)
{
sal_uInt32 nFormat = static_cast<const SfxUInt32Item*>(pItem)->GetValue();
const SvNumberformat* pEntry = pFormatter->GetEntry(nFormat);
if (pEntry)
{
OUString sFormatStr = pEntry->GetFormatstring();
// Truncate very long format strings
if (sFormatStr.getLength() > 40)
sFormatStr = OUString::Concat(sFormatStr.subView(0, 37)) + "...";
aItemPresentation = SvxAttrNameTable::GetString(
SvxAttrNameTable::FindIndex(
rSet.GetPool()->GetSlotId(nWhich)));
if (aItemPresentation.isEmpty())
aItemPresentation = ScResId(STR_ATTR_NUM_FORMAT);
aItemPresentation += ": " + sFormatStr;
}
else
{
continue; // Skip unresolvable format keys
}
}
else
{
// Standard presentation via pool
if (!m_pPool->GetPool().GetPresentation(
*pItem, eMetric, aItemPresentation, aIntlWrapper))
continue;
if (aItemPresentation.isEmpty())
continue;
// Add attribute name prefix if not already present
if (aItemPresentation.indexOf(": ") == -1)
{
sal_uInt16 nSlotId = rSet.GetPool()->GetSlotId(nWhich);
sal_uInt32 nIdx = SvxAttrNameTable::FindIndex(nSlotId);
OUString aAttrName = SvxAttrNameTable::GetString(nIdx);
// Fallback for Calc-specific WhichIds not in SvxAttrNameTable
if (aAttrName.isEmpty())
{
// You may want to add string resources in sc/inc/strings.hrc
// for now using English fallbacks that you can later replace
// with proper NC_ macros
switch (nWhich)
{
case ATTR_PROTECTION:
aAttrName = ScResId(STR_ATTR_PROTECTION);
break;
case ATTR_ROTATE_VALUE:
aAttrName = ScResId(STR_ATTR_ROTATION);
break;
case ATTR_ROTATE_MODE:
aAttrName = ScResId(STR_ATTR_ROTATION_MODE);
break;
case ATTR_WRITINGDIR:
aAttrName = ScResId(STR_ATTR_TEXT_DIR);
break;
case ATTR_LINEBREAK:
aAttrName = ScResId(STR_ATTR_WRAP_TEXT);
break;
case ATTR_SHRINKTOFIT:
aAttrName = ScResId(STR_ATTR_SHRINK_FIT);
break;
case ATTR_STACKED:
aAttrName = ScResId(STR_ATTR_STACKED);
break;
case ATTR_HOR_JUSTIFY:
aAttrName = ScResId(STR_ATTR_H_ALIGN);
break;
case ATTR_VER_JUSTIFY:
aAttrName = ScResId(STR_ATTR_V_ALIGN);
break;
case ATTR_INDENT:
aAttrName = ScResId(STR_ATTR_INDENT);
break;
case ATTR_MARGIN:
aAttrName = ScResId(STR_ATTR_SPACING);
break;
case ATTR_VERTICAL_ASIAN:
aAttrName = ScResId(STR_ATTR_ASIAN_LAYOUT);
break;
default:
break;
}
}
if (!aAttrName.isEmpty())
aItemPresentation = aAttrName + ": " + aItemPresentation;
}
}
if (!aItemPresentation.isEmpty())
aResult.emplace_back(nWhich, aItemPresentation);
}
return aResult;
}
bool ScStyleSheet::IsUsed() const
{
switch (GetFamily())
+3
View File
@@ -56,6 +56,9 @@ $(eval $(call gb_UIConfig_add_uifiles,sfx,\
sfx2/uiconfig/ui/password \
sfx2/uiconfig/ui/notebookbarpopup \
sfx2/uiconfig/ui/printeroptionsdialog \
sfx2/uiconfig/ui/propertychip \
sfx2/uiconfig/ui/propertychiprow \
sfx2/uiconfig/ui/propertycategoryrow \
sfx2/uiconfig/ui/querysavedialog \
sfx2/uiconfig/ui/quickfind \
sfx2/uiconfig/ui/saveastemplatedlg \
+369 -2
View File
@@ -45,9 +45,141 @@
#include <svl/stritem.hxx>
#include <sfx2/dispatch.hxx>
#include <sal/log.hxx>
#include <algorithm>
#include "mgetempl.hxx"
// PropertyChip implementation
PropertyChip::PropertyChip(weld::Box* pParent, SfxManageStyleSheetPage* pPage,
sal_uInt16 nWhich, const OUString& rText)
: m_xBuilder(Application::CreateBuilder(pParent, u"sfx/ui/propertychip.ui"_ustr))
, m_xContainer(m_xBuilder->weld_container(u"PropertyChip"_ustr))
, m_xLabel(m_xBuilder->weld_label(u"label"_ustr))
, m_xRemoveBtn(m_xBuilder->weld_toolbar(u"removebar"_ustr))
, m_pPage(pPage)
, m_sText(rText)
, m_nWhich(nWhich)
{
// For texts longer than one line, insert manual newlines.
static constexpr int MAX_LINE_CHARS = 100;
if (rText.getLength() > MAX_LINE_CHARS)
{
OUStringBuffer aBuf;
int nLineLen = 0;
for (sal_Int32 i = 0; i < rText.getLength(); ++i)
{
sal_Unicode c = rText[i];
aBuf.append(c);
nLineLen++;
if (nLineLen >= MAX_LINE_CHARS && (c == ' ' || c == ',' || c == ';' || c == '&'))
{
aBuf.append('\n');
nLineLen = 0;
}
}
m_xLabel->set_label(aBuf.makeStringAndClear());
}
else
{
m_xLabel->set_label(rText);
}
m_xLabel->set_tooltip_text(rText);
m_xRemoveBtn->connect_clicked(LINK(this, PropertyChip, RemoveHdl));
}
PropertyChip::~PropertyChip()
{
if (m_xContainer)
m_xContainer->set_visible(false);
}
IMPL_LINK_NOARG(PropertyChip, RemoveHdl, const OUString&, void)
{
m_pPage->ResetPropertyToParent(m_nWhich);
}
// PropertyCategoryRow implementation
PropertyCategoryRow::PropertyCategoryRow(weld::Box* pParentBox, std::u16string_view rLabel)
: m_xBuilder(Application::CreateBuilder(pParentBox, u"sfx/ui/propertycategoryrow.ui"_ustr))
, m_xContainer(m_xBuilder->weld_container(u"PropertyCategoryRow"_ustr))
, m_xLabel(m_xBuilder->weld_label(u"label"_ustr))
, m_xChipsBox(m_xBuilder->weld_container(u"chipsbox"_ustr))
{
m_xLabel->set_label(OUString::Concat(rLabel) + ":");
}
PropertyCategoryRow::~PropertyCategoryRow()
{
m_aChips.clear();
m_aChipRows.clear();
if (m_xContainer)
m_xContainer->set_visible(false);
}
weld::Box* PropertyCategoryRow::EnsureCurrentRow(int nChipChars)
{
if (m_aChipRows.empty()
|| (m_aChipRows.back()->nTotalChars + nChipChars > MAX_CHARS_PER_ROW
&& m_aChipRows.back()->nTotalChars > 0))
{
auto pRow = std::make_unique<ChipRow>();
pRow->xBuilder = Application::CreateBuilder(
m_xChipsBox.get(), u"sfx/ui/propertychiprow.ui"_ustr);
pRow->xBox = pRow->xBuilder->weld_box(u"PropertyChipRow"_ustr);
pRow->xBox->show();
pRow->nTotalChars = 0;
m_aChipRows.push_back(std::move(pRow));
}
return m_aChipRows.back()->xBox.get();
}
void PropertyCategoryRow::AddChip(SfxManageStyleSheetPage* pPage, sal_uInt16 nWhich, const OUString& rText)
{
int nTextLen = rText.getLength();
int nChipChars = nTextLen + 3;
weld::Box* pCurrentRow = EnsureCurrentRow(nChipChars);
m_aChips.emplace_back(std::make_unique<PropertyChip>(pCurrentRow, pPage, nWhich, rText));
m_aChipRows.back()->nTotalChars += nChipChars;
}
void PropertyCategoryRow::RemoveChip(sal_uInt16 nWhich)
{
struct ChipData
{
sal_uInt16 nWhich;
OUString sText;
};
std::vector<ChipData> aData;
SfxManageStyleSheetPage* pPage = nullptr;
for (const auto& pChip : m_aChips)
{
if (pChip->GetWhich() == nWhich)
continue;
if (!pPage)
pPage = pChip->GetPage();
aData.push_back({pChip->GetWhich(), pChip->GetText()});
}
// Hide rows before destroying
for (auto& pRow : m_aChipRows)
{
if (pRow->xBox)
pRow->xBox->set_visible(false);
}
m_aChips.clear();
m_aChipRows.clear();
if (pPage)
{
for (const auto& d : aData)
AddChip(pPage, d.nWhich, d.sText);
}
}
/* SfxManageStyleSheetPage Constructor
*
* initializes the list box with the templates
@@ -72,6 +204,10 @@ SfxManageStyleSheetPage::SfxManageStyleSheetPage(weld::Container* pPage, weld::D
, m_xFilterFt(m_xBuilder->weld_label(u"categoryft"_ustr))
, m_xFilterLb(m_xBuilder->weld_combo_box(u"category"_ustr))
, m_xDescFt(m_xBuilder->weld_label(u"desc"_ustr))
, m_xEditViewBox(m_xBuilder->weld_box(u"editviewbox"_ustr))
, m_xEditPropsBtn(m_xBuilder->weld_toggle_button(u"editprops"_ustr))
, m_xViewPropsBtn(m_xBuilder->weld_toggle_button(u"viewprops"_ustr))
, m_xPropBox(m_xBuilder->weld_box(u"propbox"_ustr))
{
m_xFollowLb->make_sorted();
// tdf#120188 like SwCharURLPage limit the width of the style combos
@@ -258,10 +394,13 @@ SfxManageStyleSheetPage::SfxManageStyleSheetPage(weld::Container* pPage, weld::D
m_xBaseLb->connect_changed(LINK(this, SfxManageStyleSheetPage, EditLinkStyleSelectHdl_Impl));
m_xEditStyleBtn->connect_clicked(LINK(this, SfxManageStyleSheetPage, EditStyleHdl_Impl));
m_xEditLinkStyleBtn->connect_clicked(LINK(this, SfxManageStyleSheetPage, EditLinkStyleHdl_Impl));
m_xEditPropsBtn->connect_toggled(LINK(this, SfxManageStyleSheetPage, EditPropsHdl_Impl));
m_xViewPropsBtn->connect_toggled(LINK(this, SfxManageStyleSheetPage, ViewPropsHdl_Impl));
}
SfxManageStyleSheetPage::~SfxManageStyleSheetPage()
{
m_aPropertyRows.clear();
pItem = nullptr;
pStyle = nullptr;
}
@@ -299,6 +438,7 @@ void SfxManageStyleSheetPage::SetDescriptionText_Impl()
/* [Description]
Set attribute description. Get the set metric for this.
Also builds property chips if the style has a parent.
*/
{
@@ -325,7 +465,195 @@ void SfxManageStyleSheetPage::SetDescriptionText_Impl()
default:
OSL_FAIL( "non supported field unit" );
}
m_xDescFt->set_label(pStyle->GetDescription(eUnit));
// If style has a parent, show description text and Edit button
if (pStyle->HasParentSupport() && !pStyle->GetParent().isEmpty())
{
if (m_bEditMode)
{
m_xDescFt->hide();
m_bInToggleHandler = true;
m_xEditPropsBtn->set_active(true);
m_xViewPropsBtn->set_active(false);
m_bInToggleHandler = false;
BuildPropertyChips_Impl();
}
else
{
m_xDescFt->set_label(pStyle->GetDescription(eUnit));
m_xDescFt->show();
m_bInToggleHandler = true;
m_xEditPropsBtn->set_active(false);
m_xViewPropsBtn->set_active(true);
m_bInToggleHandler = false;
}
m_xEditViewBox->show();
}
else
{
// No parent - show the full description, no Edit button
m_aPropertyRows.clear();
m_xDescFt->set_label(pStyle->GetDescription(eUnit));
m_xDescFt->show();
m_xEditPropsBtn->hide();
m_xEditViewBox->hide();
}
}
void SfxManageStyleSheetPage::BuildPropertyChips_Impl()
/* [Description]
Build property chips for all properties in this style that differ from the parent.
Each chip shows the property name/value and has an X button to reset it to parent.
Chips are grouped by tab page.
*/
{
m_aPropertyRows.clear();
if (!pStyle->HasParentSupport() || pStyle->GetParent().isEmpty())
return;
MapUnit eUnit = MapUnit::MapCM;
FieldUnit eFieldUnit(FieldUnit::CM);
SfxModule* pModule = SfxModule::GetActiveModule();
if (pModule)
{
eFieldUnit = pModule->GetFieldUnit();
}
switch (eFieldUnit)
{
case FieldUnit::MM: eUnit = MapUnit::MapMM; break;
case FieldUnit::CM:
case FieldUnit::M:
case FieldUnit::KM: eUnit = MapUnit::MapCM; break;
case FieldUnit::POINT:
case FieldUnit::PICA: eUnit = MapUnit::MapPoint; break;
case FieldUnit::INCH:
case FieldUnit::FOOT:
case FieldUnit::MILE: eUnit = MapUnit::MapInch; break;
default:
break;
}
// Get the dialog controller to find tab page names
SfxTabDialogController* pDlgController = static_cast<SfxTabDialogController*>(GetDialogController());
if (pDlgController)
pDlgController->BuildWhichToTabMap();
// Use the virtual method to get individual property presentations
const SfxItemSet* pWorkingSet = pDlgController ? pDlgController->GetExampleSet() : nullptr;
std::vector<std::pair<sal_uInt16, OUString>> aItems = pStyle->GetItemPresentation(eUnit, pWorkingSet);
// Group items by tab page
std::map<OUString, std::vector<std::pair<sal_uInt16, OUString>>> aGroupedItems;
for (const auto& rItem : aItems)
{
// Skip items that user has explicitly reset
if (m_aResetWhichIds.find(rItem.first) != m_aResetWhichIds.end())
continue;
OUString sTabId;
if (pDlgController)
{
sTabId = pDlgController->GetTabPageNameForWhich(rItem.first);
}
if (sTabId.isEmpty())
{
continue; // skip unclassifiable items
}
aGroupedItems[sTabId].push_back(rItem);
}
// Create category rows in tab order (not alphabetical)
std::vector<OUString> aTabOrder;
if (pDlgController)
{
aTabOrder = pDlgController->GetTabPageIds();
}
// Add "other" at the end for items not matching any tab
aTabOrder.push_back("other");
for (const auto& sTabId : aTabOrder)
{
auto itGroup = aGroupedItems.find(sTabId);
if (itGroup == aGroupedItems.end())
continue; // No items for this tab
OUString sLabel;
if (pDlgController && sTabId != "other")
{
sLabel = pDlgController->GetTabPageLabel(sTabId);
}
if (sLabel.isEmpty())
sLabel = sTabId;
auto pRow = std::make_unique<PropertyCategoryRow>(m_xPropBox.get(), sLabel);
auto& aItemVec = itGroup->second;
std::sort(aItemVec.begin(), aItemVec.end(),
[&](const auto& a, const auto& b) {
auto itA = pDlgController->GetWhichOrder(a.first);
auto itB = pDlgController->GetWhichOrder(b.first);
return itA < itB;
});
for (const auto& rItem : itGroup->second)
{
pRow->AddChip(this, rItem.first, rItem.second);
}
m_aPropertyRows[sTabId] = std::move(pRow);
}
for (const auto& sTabId : aTabOrder)
{
auto it = m_aPropertyRows.find(sTabId);
if (it != m_aPropertyRows.end())
it->second->Show();
}
if (m_aPropertyRows.empty())
{
m_xDescFt->set_label(SfxResId(STR_NO_MODIFIED_PROPERTIES));
m_xDescFt->show();
}
}
void SfxManageStyleSheetPage::ResetPropertyToParent(sal_uInt16 nWhich)
{
SfxItemSet& rSet = pStyle->GetItemSet();
// Clear this item to restore inheritance
rSet.ClearItem(nWhich);
// Track that user has reset this property
m_aResetWhichIds.insert(nWhich);
bModified = true;
// Invalidate this item in the dialog's output set
SfxTabDialogController* pDlgController = static_cast<SfxTabDialogController*>(GetDialogController());
if (pDlgController)
{
pDlgController->InvalidateItem(nWhich);
}
// Rebuild all chips from scratch — this avoids FlowBox residue issues
m_aPropertyRows.clear();
BuildPropertyChips_Impl();
// If no rows left, show the "all inherited" message
if (m_aPropertyRows.empty())
{
m_xDescFt->set_label(SfxResId(STR_NO_MODIFIED_PROPERTIES));
m_xDescFt->show();
}
}
IMPL_LINK_NOARG(SfxManageStyleSheetPage, EditStyleSelectHdl_Impl, weld::ComboBox&, void)
@@ -357,6 +685,38 @@ IMPL_LINK_NOARG(SfxManageStyleSheetPage, EditLinkStyleHdl_Impl, weld::Button&, v
Execute_Impl( SID_STYLE_EDIT, aTemplName, static_cast<sal_uInt16>(pStyle->GetFamily()) );
}
IMPL_LINK_NOARG(SfxManageStyleSheetPage, EditPropsHdl_Impl, weld::Toggleable&, void)
{
if (m_bInToggleHandler)
return;
m_bInToggleHandler = true;
m_xEditPropsBtn->set_active(true);
m_xViewPropsBtn->set_active(false);
m_bEditMode = true;
m_xDescFt->hide();
BuildPropertyChips_Impl();
m_bInToggleHandler = false;
}
IMPL_LINK_NOARG(SfxManageStyleSheetPage, ViewPropsHdl_Impl, weld::Toggleable&, void)
{
if (m_bInToggleHandler)
return;
m_bInToggleHandler = true;
m_xViewPropsBtn->set_active(true);
m_xEditPropsBtn->set_active(false);
m_bEditMode = false;
m_aPropertyRows.clear();
SetDescriptionText_Impl();
m_bInToggleHandler = false;
}
// Internal: Perform functions through the Dispatcher
bool SfxManageStyleSheetPage::Execute_Impl(
sal_uInt16 nId, const OUString &rStr, sal_uInt16 nFamily)
@@ -454,7 +814,6 @@ bool SfxManageStyleSheetPage::FillItemSet( SfxItemSet* rSet )
return bModified;
}
void SfxManageStyleSheetPage::Reset( const SfxItemSet* /*rAttrSet*/ )
/* [Description]
@@ -555,6 +914,14 @@ void SfxManageStyleSheetPage::ActivatePage( const SfxItemSet& rSet)
*/
{
// Rebuild property chips when returning to this page in edit mode,
// so that changes made in other tab pages are reflected immediately.
if (m_bEditMode)
{
m_aPropertyRows.clear();
BuildPropertyChips_Impl();
}
SetDescriptionText_Impl();
// It is a style with auto update? (SW only)
+81 -3
View File
@@ -21,7 +21,12 @@
#include <sfx2/styfitem.hxx>
#include <sfx2/tabdlg.hxx>
#include <vcl/weld/Toolbar.hxx>
#include <memory>
#include <vector>
#include <algorithm>
#include <map>
#include <set>
namespace weld { class Button; }
namespace weld { class CheckButton; }
@@ -30,11 +35,73 @@ namespace weld { class Entry; }
namespace weld { class Label; }
namespace weld { class Widget; }
namespace weld { class Container; }
namespace weld { class Box; }
class SfxManageStyleSheetPage;
/// A "chip" widget representing a single style property that can be reset to parent
class PropertyChip final
{
private:
std::unique_ptr<weld::Builder> m_xBuilder;
std::unique_ptr<weld::Container> m_xContainer;
std::unique_ptr<weld::Label> m_xLabel;
std::unique_ptr<weld::Toolbar> m_xRemoveBtn;;
SfxManageStyleSheetPage* m_pPage;
OUString m_sText;
sal_uInt16 m_nWhich;
DECL_LINK(RemoveHdl, const OUString&, void);
public:
PropertyChip(weld::Box* pParent, SfxManageStyleSheetPage* pPage,
sal_uInt16 nWhich, const OUString& rText);
~PropertyChip();
sal_uInt16 GetWhich() const { return m_nWhich; }
const OUString& GetText() const { return m_sText; }
SfxManageStyleSheetPage* GetPage() const { return m_pPage; }
};
/* expected:
SID_TEMPLATE_NAME : In: StringItem, Name of Template
SID_TEMPLATE_FAMILY : In: Family of Template
*/
/// A row containing chips for a single tab/category
class PropertyCategoryRow final
{
private:
std::unique_ptr<weld::Builder> m_xBuilder;
std::unique_ptr<weld::Container> m_xContainer;
std::unique_ptr<weld::Label> m_xLabel;
std::unique_ptr<weld::Container> m_xChipsBox;
std::vector<std::unique_ptr<PropertyChip>> m_aChips;
struct ChipRow
{
std::unique_ptr<weld::Builder> xBuilder;
std::unique_ptr<weld::Box> xBox;
int nTotalChars = 0; // estimated total character width
};
std::vector<std::unique_ptr<ChipRow>> m_aChipRows;
static constexpr int MAX_CHARS_PER_ROW = 100;
weld::Box* EnsureCurrentRow(int nChipChars);
public:
PropertyCategoryRow(weld::Box* pParentBox, std::u16string_view rLabel);
~PropertyCategoryRow();
void AddChip(SfxManageStyleSheetPage* pPage, sal_uInt16 nWhich, const OUString& rText);
void RemoveChip(sal_uInt16 nWhich);
bool IsEmpty() const { return m_aChips.empty(); }
void Show() { if (m_xContainer) m_xContainer->set_visible(true); }
void Hide() { if (m_xContainer) m_xContainer->set_visible(false); }
OUString GetLabel() const { return m_xLabel->get_label(); }
int GetChipRowCount() const { return static_cast<int>(m_aChipRows.size()); }
};
class SfxManageStyleSheetPage final : public SfxTabPage
{
SfxStyleSheetBase *pStyle;
@@ -60,8 +127,17 @@ class SfxManageStyleSheetPage final : public SfxTabPage
std::unique_ptr<weld::Label> m_xFilterFt;
std::unique_ptr<weld::ComboBox> m_xFilterLb;
std::unique_ptr<weld::Label> m_xDescFt;
std::unique_ptr<weld::Box> m_xEditViewBox;
std::unique_ptr<weld::Toggleable> m_xEditPropsBtn;
std::unique_ptr<weld::Toggleable> m_xViewPropsBtn;
std::unique_ptr<weld::Box> m_xPropBox;
std::map<OUString, std::unique_ptr<PropertyCategoryRow>> m_aPropertyRows;
std::set<sal_uInt16> m_aResetWhichIds; // Track Which IDs that user has reset
bool m_bEditMode = false; // True after user clicks Edit
bool m_bInToggleHandler = false;
friend class SfxStyleDialogController;
friend class PropertyChip;
DECL_LINK(GetFocusHdl, weld::Widget&, void);
DECL_LINK(LoseFocusHdl, weld::Widget&, void);
@@ -69,18 +145,20 @@ class SfxManageStyleSheetPage final : public SfxTabPage
DECL_LINK(EditStyleHdl_Impl, weld::Button&, void);
DECL_LINK(EditLinkStyleSelectHdl_Impl, weld::ComboBox&, void);
DECL_LINK(EditLinkStyleHdl_Impl, weld::Button&, void);
DECL_LINK(EditPropsHdl_Impl, weld::Toggleable&, void);
DECL_LINK(ViewPropsHdl_Impl, weld::Toggleable&, void);
void UpdateName_Impl(weld::ComboBox*, const OUString &rNew);
void SetDescriptionText_Impl();
void BuildPropertyChips_Impl();
void ResetPropertyToParent(sal_uInt16 nWhich);
virtual void ActivatePage(const SfxItemSet& rSet) override;
static std::unique_ptr<SfxTabPage> Create( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* );
virtual bool FillItemSet(SfxItemSet *) override;
virtual void Reset(const SfxItemSet *) override;
static bool Execute_Impl( sal_uInt16 nId, const OUString& rStr, sal_uInt16 nFamily );
virtual void ActivatePage(const SfxItemSet &) override;
virtual DeactivateRC DeactivatePage(SfxItemSet *) override;
public:
+7
View File
@@ -81,6 +81,13 @@ SfxStyleDialogController::~SfxStyleDialogController()
short SfxStyleDialogController::Ok()
{
SfxTabDialogController::Ok();
SfxItemSet& rStyleSet = m_rStyle.GetItemSet();
for (sal_uInt16 nWhich : m_aInvalidatedWhichIds)
{
rStyleSet.ClearItem(nWhich);
}
return RET_OK;
}
+233 -4
View File
@@ -37,9 +37,15 @@
#include <sal/log.hxx>
#include <tools/debug.hxx>
#include <comphelper/lok.hxx>
#include <editeng/editids.hrc>
#include <svx/xdef.hxx>
#include <sfx2/strings.hrc>
#include <helpids.h>
#include <map>
#include <set>
using namespace ::com::sun::star::uno;
@@ -60,6 +66,7 @@ namespace {
struct Data_Impl
{
OUString sId; // The ID
OUString sLabel; // The tab label
CreateTabPage fnCreatePage; // Pointer to Factory
GetTabPageRanges fnGetRanges; // Pointer to Ranges-Function
std::unique_ptr<SfxTabPage> xTabPage; // The TabPage itself
@@ -595,7 +602,6 @@ bool SfxTabDialogController::DeactivatePage(std::u16string_view aPage)
if (m_pSet)
{
SfxItemSet aTmp( *m_pSet->GetPool(), m_pSet->GetRanges() );
if (pPage->HasExchangeSupport())
nRet = pPage->DeactivatePage(&aTmp);
else
@@ -605,6 +611,12 @@ bool SfxTabDialogController::DeactivatePage(std::u16string_view aPage)
{
m_xExampleSet->Put( aTmp );
m_pOutSet->Put( aTmp );
// Re-clear any items that were explicitly invalidated by user
for (sal_uInt16 nWhich : m_aInvalidatedWhichIds)
{
m_xExampleSet->ClearItem(nWhich);
}
}
}
else
@@ -817,6 +829,16 @@ short SfxTabDialogController::Ok()
if (m_bStandardPushed)
bModified = true;
if (!m_aInvalidatedWhichIds.empty())
bModified = true;
// Re-apply user-requested property resets after FillItemSet
for (sal_uInt16 nWhich : m_aInvalidatedWhichIds)
{
if (m_pOutSet)
m_pOutSet->ClearItem(nWhich);
}
return bModified ? RET_OK : RET_CANCEL;
}
@@ -877,11 +899,9 @@ void SfxTabDialogController::AddTabPage(const OUString &rName /* Page ID */,
}
/* [Description]
Add a page to the dialog. The Rider text is passed on, the page has no
counterpart in the TabControl in the resource of the dialogue.
*/
void SfxTabDialogController::AddTabPage(const OUString &rName, /* Page ID */
const OUString& rRiderText,
CreateTabPage pCreateFunc, /* Pointer to the Factory Method */
@@ -890,6 +910,10 @@ void SfxTabDialogController::AddTabPage(const OUString &rName, /* Page ID */
assert(!m_xTabCtrl->get_page(rName) && "Double Page-Ids in the Tabpage");
AddTabPage(rName, pCreateFunc, nullptr);
m_xTabCtrl->append_page(rName, rRiderText, pIconName);
// Save the label in Data_Impl
auto it = Find(m_pImpl->aData, rName);
if (it != m_pImpl->aData.end())
(*it)->sLabel = rRiderText;
}
void SfxTabDialogController::AddTabPage(const OUString &rName, /* Page ID */
@@ -901,6 +925,10 @@ void SfxTabDialogController::AddTabPage(const OUString &rName, /* Page ID */
assert(!m_xTabCtrl->get_page(rName) && "Double Page-Ids in the Tabpage");
AddTabPage(rName, pCreateFunc, pRangesFunc);
m_xTabCtrl->append_page(rName, rRiderText, &rIconName);
// Save the label in Data_Impl
auto it = Find(m_pImpl->aData, rName);
if (it != m_pImpl->aData.end())
(*it)->sLabel = rRiderText;
}
void SfxTabDialogController::AddTabPage(const OUString &rName,
@@ -918,6 +946,10 @@ void SfxTabDialogController::AddTabPage(const OUString &rName, const OUString& r
assert(!m_xTabCtrl->get_page(rName) && "Double Page-Ids in the Tabpage");
AddTabPage(rName, nPageCreateId);
m_xTabCtrl->append_page(rName, rRiderText, pIconName);
// Save the label in Data_Impl
auto it = Find(m_pImpl->aData, rName);
if (it != m_pImpl->aData.end())
(*it)->sLabel = rRiderText;
}
void SfxTabDialogController::AddTabPage(const OUString &rName, const OUString& rRiderText,
@@ -1036,7 +1068,6 @@ void SfxTabDialogController::RemoveTabPage(const OUString& rId)
void SfxTabDialogController::Start_Impl()
{
CreatePages();
setPreviewsToSamePlace();
assert(m_pImpl->aData.size() == static_cast<size_t>(m_xTabCtrl->get_n_pages())
@@ -1083,6 +1114,204 @@ OUString SfxTabDialogController::GetCurPageId() const
return m_xTabCtrl->get_current_page_ident();
}
void SfxTabDialogController::ResetTabPage(std::u16string_view rPageId)
{
SfxTabPage* pPage = GetTabPage(rPageId);
if (pPage)
{
const SfxItemSet* pSet = m_xExampleSet ? m_xExampleSet.get() : m_pSet.get();
if (pSet)
{
pPage->Reset(pSet);
}
}
}
void SfxTabDialogController::ResetAllTabPages()
{
const SfxItemSet* pSet = m_xExampleSet ? m_xExampleSet.get() : m_pSet.get();
if (!pSet)
return;
for (auto const& elem : m_pImpl->aData)
{
if (elem->xTabPage)
{
elem->xTabPage->Reset(pSet);
}
}
}
void SfxTabDialogController::InvalidateItem(sal_uInt16 nWhich)
{
m_aInvalidatedWhichIds.insert(nWhich);
if (m_xExampleSet)
{
m_xExampleSet->ClearItem(nWhich);
}
if (m_pOutSet)
m_pOutSet->InvalidateItem(nWhich);
if (!m_pSet)
return;
const SfxItemPool* pPool = m_pSet->GetPool();
if (!pPool)
return;
sal_uInt16 nSlotId = pPool->GetSlotId(nWhich);
for (auto const& elem : m_pImpl->aData)
{
if (!elem->xTabPage)
continue;
bool bFound = false;
if (elem->fnGetRanges)
{
const WhichRangesContainer aRanges = elem->fnGetRanges();
for (const auto& rPair : aRanges)
{
// Check 1: via SlotId
if (nSlotId >= rPair.first && nSlotId <= rPair.second)
{
bFound = true;
break;
}
// Check 2: iterate each slot, convert to WhichId
for (sal_uInt16 nSlot = rPair.first; nSlot <= rPair.second; ++nSlot)
{
if (pPool->GetWhichIDFromSlotID(nSlot) == nWhich)
{
bFound = true;
break;
}
}
if (bFound)
break;
}
}
if (bFound)
{
const SfxItemSet* pResetSet = m_xExampleSet ? m_xExampleSet.get() : m_pSet.get();
if (pResetSet)
{
elem->xTabPage->Reset(pResetSet);
}
}
else
{
elem->bRefresh = true;
}
}
}
void SfxTabDialogController::BuildWhichToTabMap()
{
if (!m_aWhichToTabMap.empty())
return;
const SfxItemSet* pSet = m_xExampleSet ? m_xExampleSet.get() : m_pSet.get();
if (!pSet)
return;
const SfxItemPool* pPool = m_pSet ? m_pSet->GetPool() : nullptr;
// Passata 1: tracker (copre le tab che accedono via Get/GetItemState)
for (auto const& elem : m_pImpl->aData)
{
if (!elem->xTabPage)
continue;
std::vector<sal_uInt16> aAccessed;
pSet->SetAccessTracker(&aAccessed);
elem->xTabPage->Reset(pSet);
pSet->SetAccessTracker(nullptr);
for (int i = 0; i < static_cast<int>(aAccessed.size()); ++i)
{
sal_uInt16 nWhich = aAccessed[i];
if (m_aWhichToTabMap.find(nWhich) == m_aWhichToTabMap.end())
{
m_aWhichToTabMap[nWhich] = elem->sId;
m_aWhichOrderMap[nWhich] = i;
}
}
}
// Passata 2: GetRanges (copre le tab che accedono ItemSet interni)
if (pPool)
{
for (auto const& elem : m_pImpl->aData)
{
if (!elem->fnGetRanges)
continue;
const WhichRangesContainer aRanges = elem->fnGetRanges();
for (const auto& rPair : aRanges)
{
for (sal_uInt16 nSlot = rPair.first; nSlot <= rPair.second; ++nSlot)
{
sal_uInt16 nWhich = pPool->GetWhichIDFromSlotID(nSlot);
// Solo se non già classificato dal tracker
if (m_aWhichToTabMap.find(nWhich) == m_aWhichToTabMap.end())
m_aWhichToTabMap[nWhich] = elem->sId;
}
}
}
}
// Restore delle tab page
for (auto const& elem : m_pImpl->aData)
{
if (!elem->xTabPage)
continue;
if (elem->xTabPage->DeferResetToFirstActivation())
elem->bRefresh = true;
else
elem->xTabPage->Reset(pSet);
}
}
OUString SfxTabDialogController::GetTabPageNameForWhich(sal_uInt16 nWhich) const
{
auto it = m_aWhichToTabMap.find(nWhich);
if (it != m_aWhichToTabMap.end())
return it->second;
return OUString();
}
OUString SfxTabDialogController::GetTabPageLabel(const OUString& rPageId) const
{
// First try to get the label from Data_Impl
auto it = Find(m_pImpl->aData, rPageId);
if (it != m_pImpl->aData.end() && !(*it)->sLabel.isEmpty())
return (*it)->sLabel;
// Then try from the notebook
OUString sLabel = m_xTabCtrl->get_tab_label_text(rPageId);
if (!sLabel.isEmpty())
return sLabel;
// Last resort: return the page ID itself
return rPageId;
}
std::vector<OUString> SfxTabDialogController::GetTabPageIds() const
{
std::vector<OUString> aResult;
for (auto const& elem : m_pImpl->aData)
{
aResult.push_back(elem->sId);
}
return aResult;
}
short SfxTabDialogController::run()
{
Start_Impl();
+107 -10
View File
@@ -245,18 +245,115 @@
<property name="label-xalign">0</property>
<property name="shadow-type">none</property>
<child>
<object class="GtkLabel" id="desc">
<object class="GtkBox" id="containsbox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="margin-start">12</property>
<property name="margin-start">24</property>
<property name="margin-top">6</property>
<property name="wrap">True</property>
<property name="max-width-chars">52</property>
<property name="xalign">0</property>
<property name="yalign">0</property>
<attributes>
<attribute name="scale" value="0.90000000000000002"/>
</attributes>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkLabel" id="desc">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="wrap">True</property>
<property name="max-width-chars">52</property>
<property name="xalign">0</property>
<property name="yalign">0</property>
<attributes>
<attribute name="scale" value="0.90000000000000002"/>
</attributes>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkScrolledWindow" id="propscroll">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<property name="hscrollbar-policy">never</property>
<property name="vscrollbar-policy">automatic</property>
<property name="shadow-type">none</property>
<property name="min-content-height">60</property>
<property name="max-content-height">200</property>
<child>
<object class="GtkViewport" id="propviewport">
<property name="visible">True</property>
<property name="can-focus">False</property>
<child>
<object class="GtkBox" id="propbox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="hexpand">True</property>
<property name="orientation">vertical</property>
<property name="spacing">4</property>
</object>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkBox" id="editviewbox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="orientation">horizontal</property>
<property name="spacing">0</property>
<property name="halign">start</property>
<property name="margin-top">6</property>
<child>
<object class="GtkToggleButton" id="viewprops">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="label" translatable="yes" context="managestylepage|viewprops">View</property>
<child internal-child="accessible">
<object class="AtkObject" id="viewprops-atkobject">
<property name="AtkObject::accessible-name" translatable="yes" context="managestylepage|viewprops">View style properties as description text</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkToggleButton" id="editprops">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="label" translatable="yes" context="managestylepage|editprops">Edit</property>
<child internal-child="accessible">
<object class="AtkObject" id="editprops-atkobject">
<property name="AtkObject::accessible-name" translatable="yes" context="managestylepage|editprops">Edit style properties as interactive chips</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">2</property>
</packing>
</child>
</object>
</child>
<child type="label">
@@ -271,7 +368,7 @@
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
+42
View File
@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface domain="sfx">
<requires lib="gtk+" version="3.24"/>
<object class="GtkBox" id="PropertyCategoryRow">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">0</property>
<property name="margin-bottom">2</property>
<child>
<object class="GtkLabel" id="label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="xalign">0</property>
<property name="label">Category:</property>
<child internal-child="accessible">
<object class="AtkObject" id="label-atkobject">
<property name="AtkObject::accessible-name" translatable="yes" context="propertycategoryrow|label">Property category name</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<object class="GtkBox" id="chipsbox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">0</property>
<property name="margin-start">12</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
</packing>
</child>
</object>
</interface>
+55
View File
@@ -0,0 +1,55 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface domain="sfx">
<requires lib="gtk+" version="3.24"/>
<object class="GtkBox" id="PropertyChip">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="orientation">horizontal</property>
<property name="spacing">2</property>
<child>
<object class="GtkToolbar" id="removebar">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="toolbar-style">icons</property>
<property name="show-arrow">False</property>
<property name="icon_size">2</property>
<property name="valign">start</property>
<child>
<object class="GtkToolButton" id="remove">
<property name="visible">True</property>
<property name="icon-name">window-close-symbolic</property>
<property name="tooltip-text" translatable="yes" context="propertychip|remove">Reset this property to inherit from parent style</property>
</object>
<packing>
<property name="expand">False</property>
<property name="homogeneous">False</property>
</packing>
</child>
<style>
<class name="small-button"/>
</style>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="ellipsize">none</property>
<property name="xalign">0</property>
<attributes>
<attribute name="scale" value="0.90000000000000002"/>
</attributes>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</object>
</interface>
+10
View File
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface domain="sfx">
<requires lib="gtk+" version="3.24"/>
<object class="GtkBox" id="PropertyChipRow">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="orientation">horizontal</property>
<property name="spacing">8</property>
</object>
</interface>
+2
View File
@@ -55,3 +55,5 @@ sfx2/uiconfig/ui/password.ui://GtkLevelBar[@id='pass1bar'] no-labelled-by
sfx2/uiconfig/ui/password.ui://GtkLabel[@id='pass1policylabel'] orphan-label
sfx2/uiconfig/ui/password.ui://GtkLevelBar[@id='pass2bar'] no-labelled-by
sfx2/uiconfig/ui/password.ui://GtkLabel[@id='pass2policylabel'] orphan-label
sfx2/uiconfig/ui/propertychip.ui://GtkLabel[@id='label'] orphan-label
sfx2/uiconfig/ui/propertycategoryrow.ui://GtkLabel[@id='label'] orphan-label
+18
View File
@@ -496,6 +496,12 @@ SfxItemState SfxItemSet::GetItemState_ForIter(PoolItemMap::const_iterator aHit,
SfxItemState SfxItemSet::GetItemState_ForWhichID( SfxItemState eState, sal_uInt16 nWhich, bool bSrchInParent, const SfxPoolItem **ppItem) const
{
if (m_pAccessTracker && !m_bAccessTrackerIgnore) {
if (std::find(m_pAccessTracker->begin(), m_pAccessTracker->end(), nWhich)
== m_pAccessTracker->end())
m_pAccessTracker->push_back(nWhich);
}
PoolItemMap::const_iterator aHit(m_aPoolItemMap.find(nWhich));
if (aHit != m_aPoolItemMap.end())
@@ -519,6 +525,12 @@ SfxItemState SfxItemSet::GetItemState_ForWhichID( SfxItemState eState, sal_uInt1
bool SfxItemSet::HasItem(sal_uInt16 nWhich, const SfxPoolItem** ppItem) const
{
if (m_pAccessTracker && !m_bAccessTrackerIgnore) {
if (std::find(m_pAccessTracker->begin(), m_pAccessTracker->end(), nWhich)
== m_pAccessTracker->end())
m_pAccessTracker->push_back(nWhich);
}
const bool bRet(SfxItemState::SET == GetItemState_ForWhichID(SfxItemState::UNKNOWN, nWhich, true, ppItem));
// we need to reset ppItem when it was *not* set by GetItemState_ForWhichID
@@ -921,6 +933,12 @@ const SfxPoolItem* SfxItemSet::GetItem(sal_uInt16 nId, bool bSearchInParent) con
const SfxPoolItem& SfxItemSet::Get( sal_uInt16 nWhich, bool bSrchInParent) const
{
if (m_pAccessTracker && !m_bAccessTrackerIgnore) {
if (std::find(m_pAccessTracker->begin(), m_pAccessTracker->end(), nWhich)
== m_pAccessTracker->end())
m_pAccessTracker->push_back(nWhich);
}
PoolItemMap::const_iterator aHit(m_aPoolItemMap.find(nWhich));
if (aHit != m_aPoolItemMap.end())
+49
View File
@@ -364,6 +364,55 @@ OUString SfxStyleSheetBase::GetDescription( MapUnit eMetric )
return aDesc.makeStringAndClear();
}
std::vector<std::pair<sal_uInt16, OUString>> SfxStyleSheetBase::GetItemPresentation(MapUnit eMetric, const SfxItemSet* /*pWorkingSet*/)
{
std::vector<std::pair<sal_uInt16, OUString>> aResult;
SfxItemIter aIter(GetItemSet());
IntlWrapper aIntlWrapper(SvtSysLocale().GetUILanguageTag());
// Get parent item set for comparison
const SfxItemSet* pParentSet = nullptr;
SfxStyleSheetBase* pParentStyle = nullptr;
if (!GetParent().isEmpty())
{
pParentStyle = m_pPool->Find(GetParent(), GetFamily());
if (pParentStyle)
pParentSet = &pParentStyle->GetItemSet();
}
for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem())
{
if (IsInvalidItem(pItem))
continue;
sal_uInt16 nWhich = pItem->Which();
// Only include items that are set in this style (not inherited)
if (GetItemSet().GetItemState(nWhich, false) != SfxItemState::SET)
continue;
// Skip items identical to parent
if (pParentSet)
{
const SfxPoolItem* pParentItem = nullptr;
if (pParentSet->GetItemState(nWhich, true, &pParentItem) == SfxItemState::SET
&& pParentItem && *pParentItem == *pItem)
continue;
}
OUString aItemPresentation;
if (m_pPool->GetPool().GetPresentation(*pItem, eMetric, aItemPresentation, aIntlWrapper))
{
if (!aItemPresentation.isEmpty())
{
aResult.emplace_back(nWhich, aItemPresentation);
}
}
}
return aResult;
}
inline bool SfxStyleSheetIterator::IsTrivialSearch() const
{
return (( nMask & SfxStyleSearchBits::AllVisible ) == SfxStyleSearchBits::AllVisible) &&
+3
View File
@@ -132,6 +132,9 @@ public:
virtual bool HasParentSupport() const override;
virtual bool HasClearParentSupport() const override;
virtual OUString GetDescription(MapUnit eUnit) override;
virtual std::vector<std::pair<sal_uInt16, OUString>> GetItemPresentation(
MapUnit eMetric, const SfxItemSet* pWorkingSet = nullptr) override;
void ResetItems(const std::set<sal_uInt16>& rWhichIds);
virtual OUString GetUsedBy() override;
+14
View File
@@ -1555,6 +1555,20 @@
#define STR_UNDO_MAKE_ENDNOTES_FOOTNOTES NC_("STR_UNDO_MAKE_ENDNOTES_FOOTNOTES", "Make all endnotes footnotes")
#define STR_UNDO_CONVERT_FIELD_TO_TEXT NC_("STR_UNDO_CONVERT_FIELD_TO_TEXT", "Convert field to text")
// Attribute names for style property chips in the Organizer tab.
// These labels are shown alongside property values when a style overrides
// a property from its parent (e.g. "Outline Level: 6", "Fill Style: Gradient").
// They are needed for Writer-specific attributes not covered by SvxAttrNameTable.
#define STR_ATTR_OUTLINE_LEVEL NC_("STR_ATTR_OUTLINE_LEVEL", "Outline Level")
#define STR_ATTR_LIST_LEVEL NC_("STR_ATTR_LIST_LEVEL", "List Level")
#define STR_ATTR_LIST_RESTART NC_("STR_ATTR_LIST_RESTART", "List Restart")
#define STR_ATTR_LIST_RESTART_VALUE NC_("STR_ATTR_LIST_RESTART_VALUE", "List Restart Value")
#define STR_ATTR_TEXT_DIRECTION NC_("STR_ATTR_TEXT_DIRECTION", "Text Direction")
#define STR_ATTR_HIDDEN NC_("STR_ATTR_HIDDEN", "Hidden")
#define STR_ATTR_FILL_STYLE NC_("STR_ATTR_FILL_STYLE", "Fill Style")
#define STR_ATTR_GRADIENT NC_("STR_ATTR_GRADIENT", "Gradient")
#define STR_ATTR_GRADIENT_STEPS NC_("STR_ATTR_GRADIENT_STEPS", "Gradient Steps")
// To translators: title, text, question for confirmation whether to switch overwrite mode on
#define STR_QUERY_INSMODE_TITLE NC_("STR_QUERY_INSMODE_TITLE", "You are switching to the overwrite mode")
#define STR_QUERY_INSMODE_TEXT NC_("STR_QUERY_INSMODE_TEXT", "The overwrite mode allows to type over text. It is indicated by a block cursor and at the statusbar. Press Insert again to switch back.")
+95
View File
@@ -0,0 +1,95 @@
# -*- 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/.
#
"""Tests for tdf#89826 - interactive property chips on Organizer tab."""
from uitest.framework import UITestCase
from uitest.uihelper.common import select_pos
from uitest.uihelper.common import type_text
from libreoffice.uno.propertyvalue import mkPropertyValues
class tdf89826(UITestCase):
def _find_and_click_first_chip(self, xDialog):
"""Navigate into propbox and click the remove button on the first chip.
Returns True if a chip was found and clicked."""
xPropBox = xDialog.getChild("propbox")
for sCatName in xPropBox.getChildren():
xCatRow = xPropBox.getChild(sCatName)
if "chipsbox" not in xCatRow.getChildren():
continue
xChipsBox = xCatRow.getChild("chipsbox")
for sRowName in xChipsBox.getChildren():
xChipRow = xChipsBox.getChild(sRowName)
for sChipName in xChipRow.getChildren():
xChip = xChipRow.getChild(sChipName)
if "removebar" in xChip.getChildren():
xRemoveBar = xChip.getChild("removebar")
xRemoveBar.executeAction("CLICK",
mkPropertyValues({"POS": "0"}))
return True
return False
def test_property_chip_reset(self):
"""Test that removing a property via chip resets it to parent value,
and that undo restores it."""
with self.ui_test.create_doc_in_start_center("writer"):
document = self.ui_test.get_component()
# Create a new style
with self.ui_test.execute_dialog_through_command(
".uno:StyleNewByExample") as xDialog:
xStyleName = xDialog.getChild("stylename")
type_text(xStyleName, "Test Chip Style")
# Set parent and change font size in one dialog opening
with self.ui_test.execute_dialog_through_command(
".uno:EditStyle") as xDialog:
xTabs = xDialog.getChild("tabcontrol")
# Tab 0 = Organizer: set parent
select_pos(xTabs, "0")
xLinkedWith = xTabs.getChild("linkedwith")
xLinkedWith.executeAction("SELECT",
mkPropertyValues({"TEXT": "Default Paragraph Style"}))
# Tab 1 = Character: change font size
select_pos(xTabs, "1")
xSizeWest = xTabs.getChild("cbWestSize")
xSizeWest.executeAction("CLEAR", tuple())
type_text(xSizeWest, "20pt")
# Verify font size was applied
xParaStyles = document.StyleFamilies.ParagraphStyles
self.assertEqual(
xParaStyles.getByName("Test Chip Style").getPropertyValue(
"CharHeight"), 20)
# Open style dialog, switch to Edit mode, remove first chip
with self.ui_test.execute_dialog_through_command(
".uno:EditStyle") as xDialog:
xTabs = xDialog.getChild("tabcontrol")
select_pos(xTabs, "0")
xEditBtn = xDialog.getChild("editprops")
xEditBtn.executeAction("CLICK", tuple())
self.assertTrue(self._find_and_click_first_chip(xDialog),
"No property chip found to remove")
# Property should now be inherited from parent
xParent = xParaStyles.getByName("Standard")
self.assertEqual(
xParaStyles.getByName("Test Chip Style").getPropertyValue(
"CharHeight"),
xParent.getPropertyValue("CharHeight"))
# Undo should restore the removed property
self.xUITest.executeCommand(".uno:Undo")
self.assertEqual(
xParaStyles.getByName("Test Chip Style").getPropertyValue(
"CharHeight"), 20)
# vim: set shiftwidth=4 softtabstop=4 expandtab:
+2
View File
@@ -94,6 +94,7 @@
#include <translatelangselect.hxx>
#include <copyfielddlg.hxx>
#include <SwGridTabPage.hxx>
#include <set>
using namespace css::frame;
using namespace css::uno;
@@ -375,6 +376,7 @@ public:
return this->m_pDlg->GetInputRanges(pItem);
}
void SetInputSet(const SfxItemSet* pInSet) override { this->m_pDlg->SetInputSet(pInSet); }
const std::set<sal_uInt16>& GetInvalidatedWhichIds() const override { return this->m_pDlg->GetInvalidatedWhichIds(); }
// From class Window.
void SetText(const OUString& rStr) override { this->m_pDlg->set_title(rStr); }
};
+1 -1
View File
@@ -267,7 +267,7 @@ SwTemplateDlgController::SwTemplateDlgController(weld::Window* pParent,
short SwTemplateDlgController::Ok()
{
short nRet = SfxTabDialogController::Ok();
short nRet = SfxStyleDialogController::Ok();
if( RET_OK == nRet )
{
const SfxPoolItem *pOutItem, *pExItem;
+6
View File
@@ -20,6 +20,7 @@
#include <config_wasm_strip.h>
#include <memory>
#include <set>
#include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
@@ -653,6 +654,11 @@ IMPL_LINK_NOARG(ApplyStyle, ApplyHdl, LinkParamNone*, void)
// reset indent attributes at paragraph style, if a list style
// will be applied and no indent attributes will be applied.
m_xTmp->SetItemSet( aSet, false, true );
// Reset properties that user explicitly removed via property chips
const std::set<sal_uInt16>& rInvalidated = m_pDlg->GetInvalidatedWhichIds();
if (!rInvalidated.empty())
m_xTmp->ResetItems(rInvalidated);
}
else
{
+270
View File
@@ -69,6 +69,7 @@
#include <svx/xfillit0.hxx>
#include <svx/xflftrit.hxx>
#include <svx/drawitem.hxx>
#include <svx/strarray.hxx>
#include <names.hxx>
using namespace com::sun::star;
@@ -1203,6 +1204,275 @@ OUString SwDocStyleSheet::GetDescription(MapUnit eUnit)
return SfxStyleSheetBase::GetDescription(eUnit);
}
std::vector<std::pair<sal_uInt16, OUString>> SwDocStyleSheet::GetItemPresentation(
MapUnit eMetric, const SfxItemSet* pWorkingSet)
{
std::vector<std::pair<sal_uInt16, OUString>> aResult;
IntlWrapper aIntlWrapper(SvtSysLocale().GetUILanguageTag());
// Get parent item set for comparison
const SfxItemSet* pParentSet = nullptr;
SfxStyleSheetBase* pParentStyle = nullptr;
if (!GetParent().isEmpty())
{
pParentStyle = m_pPool->Find(GetParent(), nFamily);
if (pParentStyle)
pParentSet = &pParentStyle->GetItemSet();
}
if (SfxStyleFamily::Page == nFamily)
{
if (!pSet)
GetItemSet();
SfxItemIter aIter(*pSet);
for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem())
{
if (!IsInvalidItem(pItem))
{
// Only show items that are explicitly SET in this style, not inherited from parent
if (pSet->GetItemState(pItem->Which(), false) != SfxItemState::SET)
continue;
// Skip items identical to parent
if (pParentSet)
{
const SfxPoolItem* pParentItem = nullptr;
if (pParentSet->GetItemState(pItem->Which(), true, &pParentItem) == SfxItemState::SET
&& pParentItem && *pParentItem == *pItem)
continue;
}
// Skip items whose value equals the pool default
if (*pItem == pSet->GetPool()->GetUserOrPoolDefaultItem(pItem->Which()))
continue;
switch (pItem->Which())
{
case RES_LR_SPACE:
case SID_ATTR_PAGE_SIZE:
case SID_ATTR_PAGE_MAXSIZE:
case SID_ATTR_PAGE_PAPERBIN:
case SID_ATTR_BORDER_INNER:
break;
default:
{
OUString aItemPresentation;
if (!IsInvalidItem(pItem) &&
m_pPool->GetPool().GetPresentation(
*pItem, eMetric, aItemPresentation, aIntlWrapper))
{
if (!aItemPresentation.isEmpty())
{
// If there isn't "Name: value", try adding the name
if (aItemPresentation.indexOf(": ") == -1)
{
sal_uInt16 nSlotId = m_pPool->GetPool().GetSlotId(pItem->Which());
sal_uInt32 nIdx = SvxAttrNameTable::FindIndex(nSlotId);
OUString aAttrName = SvxAttrNameTable::GetString(nIdx);
if (aAttrName.isEmpty())
{
// Fallback for Writer-specific WhichId
static const std::map<sal_uInt16, TranslateId> aSwAttrNames = {
{ sal_uInt16(RES_PARATR_OUTLINELEVEL), STR_ATTR_OUTLINE_LEVEL },
{ sal_uInt16(RES_PARATR_LIST_LEVEL), STR_ATTR_LIST_LEVEL },
{ sal_uInt16(RES_PARATR_LIST_ISRESTART), STR_ATTR_LIST_RESTART },
{ sal_uInt16(RES_PARATR_LIST_RESTARTVALUE), STR_ATTR_LIST_RESTART_VALUE },
{ sal_uInt16(RES_PARATR_AUTOFRAMEDIR), STR_ATTR_TEXT_DIRECTION },
{ sal_uInt16(RES_CHRATR_HIDDEN), STR_ATTR_HIDDEN },
{ sal_uInt16(XATTR_FILLSTYLE), STR_ATTR_FILL_STYLE },
{ sal_uInt16(XATTR_FILLGRADIENT), STR_ATTR_GRADIENT },
{ sal_uInt16(XATTR_GRADIENTSTEPCOUNT), STR_ATTR_GRADIENT_STEPS },
};
auto itW = aSwAttrNames.find(pItem->Which());
if (itW != aSwAttrNames.end())
aAttrName = SwResId(itW->second);
}
if (!aAttrName.isEmpty())
aItemPresentation = aAttrName + ": " + aItemPresentation;
}
aResult.emplace_back(pItem->Which(), aItemPresentation);
}
}
}
}
}
}
return aResult;
}
if (SfxStyleFamily::Frame == nFamily || SfxStyleFamily::Para == nFamily || SfxStyleFamily::Char == nFamily)
{
if (!pSet)
GetItemSet();
const SfxItemSet* pCheckSet = pWorkingSet ? pWorkingSet : pSet;
const drawing::FillStyle eFillStyle(pCheckSet->Get(XATTR_FILLSTYLE).GetValue());
const bool bUseFloatTransparence(pCheckSet->Get(XATTR_FILLFLOATTRANSPARENCE).IsEnabled());
SfxItemIter aIter(*pSet);
for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem())
{
if (!IsInvalidItem(pItem))
{
// Only show items that are explicitly SET in this style, not inherited from parent
if (pCheckSet->GetItemState(pItem->Which(), false) != SfxItemState::SET)
continue;
// Skip items identical to parent
if (pParentSet)
{
const SfxPoolItem* pParentItem = nullptr;
if (pParentSet->GetItemState(pItem->Which(), true, &pParentItem) == SfxItemState::SET
&& pParentItem && *pParentItem == *pItem)
continue;
}
switch (pItem->Which())
{
case SID_ATTR_AUTO_STYLE_UPDATE:
case RES_PAGEDESC:
case SID_ATTR_PARA_PAGENUM:
case SID_ATTR_PARA_MODEL:
case RES_BREAK:
// Skip these - they have special handling in GetDescription
break;
default:
{
OUString aItemPresentation;
if (!IsInvalidItem(pItem) &&
m_pPool->GetPool().GetPresentation(
*pItem, eMetric, aItemPresentation, aIntlWrapper))
{
bool bIsDefault = false;
switch (pItem->Which())
{
case XATTR_FILLCOLOR:
bIsDefault = (drawing::FillStyle_SOLID == eFillStyle);
break;
case XATTR_FILLGRADIENT:
bIsDefault = (drawing::FillStyle_GRADIENT == eFillStyle);
break;
case XATTR_FILLHATCH:
bIsDefault = (drawing::FillStyle_HATCH == eFillStyle);
break;
case XATTR_FILLBITMAP:
bIsDefault = (drawing::FillStyle_BITMAP == eFillStyle);
break;
case XATTR_FILLTRANSPARENCE:
bIsDefault = !bUseFloatTransparence;
break;
case XATTR_FILLFLOATTRANSPARENCE:
bIsDefault = bUseFloatTransparence;
break;
case XATTR_GRADIENTSTEPCOUNT:
bIsDefault = bUseFloatTransparence;
break;
case RES_CHRATR_CJK_FONT:
case RES_CHRATR_CJK_FONTSIZE:
case RES_CHRATR_CJK_LANGUAGE:
case RES_CHRATR_CJK_POSTURE:
case RES_CHRATR_CJK_WEIGHT:
if (SvtCJKOptions::IsCJKFontEnabled())
bIsDefault = true;
aItemPresentation = SwResId(STR_CJK_FONT) + aItemPresentation;
break;
case RES_CHRATR_CTL_FONT:
case RES_CHRATR_CTL_FONTSIZE:
case RES_CHRATR_CTL_LANGUAGE:
case RES_CHRATR_CTL_POSTURE:
case RES_CHRATR_CTL_WEIGHT:
if (SvtCTLOptions::IsCTLFontEnabled())
bIsDefault = true;
aItemPresentation = SwResId(STR_CTL_FONT) + aItemPresentation;
break;
case RES_CHRATR_FONT:
case RES_CHRATR_FONTSIZE:
case RES_CHRATR_LANGUAGE:
case RES_CHRATR_POSTURE:
case RES_CHRATR_WEIGHT:
aItemPresentation = SwResId(STR_WESTERN_FONT) + aItemPresentation;
[[fallthrough]];
default:
bIsDefault = true;
}
if (bIsDefault && !aItemPresentation.isEmpty())
{
// If there isn't "Name: value", try adding the name
if (aItemPresentation.indexOf(": ") == -1)
{
sal_uInt16 nSlotId = m_pPool->GetPool().GetSlotId(pItem->Which());
sal_uInt32 nIdx = SvxAttrNameTable::FindIndex(nSlotId);
OUString aAttrName = SvxAttrNameTable::GetString(nIdx);
if (aAttrName.isEmpty())
{
// Fallback for Writer-specific WhichId
static const std::map<sal_uInt16, TranslateId> aSwAttrNames = {
{ sal_uInt16(RES_PARATR_OUTLINELEVEL), STR_ATTR_OUTLINE_LEVEL },
{ sal_uInt16(RES_PARATR_LIST_LEVEL), STR_ATTR_LIST_LEVEL },
{ sal_uInt16(RES_PARATR_LIST_ISRESTART), STR_ATTR_LIST_RESTART },
{ sal_uInt16(RES_PARATR_LIST_RESTARTVALUE), STR_ATTR_LIST_RESTART_VALUE },
{ sal_uInt16(RES_PARATR_AUTOFRAMEDIR), STR_ATTR_TEXT_DIRECTION },
{ sal_uInt16(RES_CHRATR_HIDDEN), STR_ATTR_HIDDEN },
{ sal_uInt16(XATTR_FILLSTYLE), STR_ATTR_FILL_STYLE },
{ sal_uInt16(XATTR_FILLGRADIENT), STR_ATTR_GRADIENT },
{ sal_uInt16(XATTR_GRADIENTSTEPCOUNT), STR_ATTR_GRADIENT_STEPS },
};
auto itW = aSwAttrNames.find(pItem->Which());
if (itW != aSwAttrNames.end())
aAttrName = SwResId(itW->second);
}
if (!aAttrName.isEmpty())
aItemPresentation = aAttrName + ": " + aItemPresentation;
}
aResult.emplace_back(pItem->Which(), aItemPresentation);
}
}
}
}
}
}
return aResult;
}
if (SfxStyleFamily::Pseudo == nFamily)
{
return aResult;
}
return SfxStyleSheetBase::GetItemPresentation(eMetric);
}
void SwDocStyleSheet::ResetItems(const std::set<sal_uInt16>& rWhichIds)
{
if (rWhichIds.empty())
return;
std::vector<sal_uInt16> aIds(rWhichIds.begin(), rWhichIds.end());
if (nFamily == SfxStyleFamily::Para)
{
SwTextFormatColl* pColl = m_rDoc.FindTextFormatCollByName(UIName(aName));
if (pColl)
m_rDoc.ResetAttrAtFormat(aIds, *pColl);
}
else if (nFamily == SfxStyleFamily::Char)
{
SwCharFormat* pCharFormat = m_rDoc.FindCharFormatByName(UIName(aName));
if (pCharFormat)
m_rDoc.ResetAttrAtFormat(aIds, *pCharFormat);
}
else if (nFamily == SfxStyleFamily::Frame)
{
SwFrameFormat* pFrameFormat = m_rDoc.FindFrameFormatByName(UIName(aName));
if (pFrameFormat)
m_rDoc.ResetAttrAtFormat(aIds, *pFrameFormat);
}
}
// Set names
bool SwDocStyleSheet::SetName(const OUString& rStr, bool bReindexNow)
{