framework/source/uiconfiguration/CommandImageResolver.cxx |   34 ++-
 framework/source/uiconfiguration/CommandImageResolver.hxx |   10 -
 framework/source/uiconfiguration/imagemanagerimpl.cxx     |  137 +++++++++-----
 framework/source/uiconfiguration/imagemanagerimpl.hxx     |   15 +
 framework/source/uielement/generictoolbarcontroller.cxx   |   33 +++
 icon-themes/sifr_dark/cmd/rltb/32/defaultnumbering.png    |binary
 icon-themes/sifr_dark/cmd/rltb/lc_defaultnumbering.png    |binary
 icon-themes/sifr_dark/cmd/rltb/sc_defaultnumbering.png    |binary
 include/vcl/commandinfoprovider.hxx                       |    6 
 include/vcl/image.hxx                                     |    1 
 include/vcl/vclenum.hxx                                   |    9 
 offapi/com/sun/star/ui/ImageType.idl                      |    7 
 vcl/inc/image.h                                           |    4 
 vcl/source/helper/commandinfoprovider.cxx                 |   18 +
 vcl/source/image/Image.cxx                                |    8 
 vcl/source/image/ImplImage.cxx                            |   13 -
 16 files changed, 213 insertions(+), 82 deletions(-)

New commits:
commit dcfc32914fe1d5239f9cbcb46a286f1a2e2d437e
Author:     Jonathan Clark <jonat...@libreoffice.org>
AuthorDate: Fri Mar 7 17:15:06 2025 -0700
Commit:     Jonathan Clark <jonat...@libreoffice.org>
CommitDate: Tue Mar 11 06:40:33 2025 +0100

    tdf#70102 Code support for RTL variants in icon themes
    
    UNO commands may optionally rotate or mirror their icons depending on
    context (usually based on writing direction). This approach is an easy
    way to provide large numbers of such context-sensitive icons, but
    suffers from serious drawbacks in certain cases. For example, icons
    which contain asymmetrical symbols or text will be obviously mirrored,
    and icon themes which use shading will appear inconsistent next to
    non-transformed icons from the same theme.
    
    This code change lets icon themes provide specialized graphics for the
    left-right-top-bottom text direction. Such specializations are available
    for any command which specifies COMMAND_PROPERTY_MIRROR.
    
    Direction-specialized versions should be placed in the `cmd/rltb`
    directory within the icon theme.
    
    Localized specializations may also be nested inside that directory. For
    example, icons in `cmd/rltb/en` will be used for English in RTL context.
    
    Change-Id: Ic7a8cf3a6657a171e92123413d1c47e621f1bddd
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/182761
    Reviewed-by: Jonathan Clark <jonat...@libreoffice.org>
    Tested-by: Jenkins

diff --git a/framework/source/uiconfiguration/CommandImageResolver.cxx 
b/framework/source/uiconfiguration/CommandImageResolver.cxx
index 10e3ae4c85b9..2daf737dbc90 100644
--- a/framework/source/uiconfiguration/CommandImageResolver.cxx
+++ b/framework/source/uiconfiguration/CommandImageResolver.cxx
@@ -21,11 +21,13 @@ namespace vcl
 namespace
 {
 
-constexpr o3tl::enumarray<ImageType, OUString> ImageType_Prefixes
-{
-    u"cmd/sc_"_ustr,
-    u"cmd/lc_"_ustr,
-    u"cmd/32/"_ustr
+std::map<std::tuple<ImageType, ImageWritingDirection>, OUString> const 
ImageType_Prefixes{
+    { { ImageType::Size16, ImageWritingDirection::DontCare }, u"cmd/sc_"_ustr 
},
+    { { ImageType::Size26, ImageWritingDirection::DontCare }, u"cmd/lc_"_ustr 
},
+    { { ImageType::Size32, ImageWritingDirection::DontCare }, u"cmd/32/"_ustr 
},
+    { { ImageType::Size16, ImageWritingDirection::RightLeftTopBottom }, 
u"cmd/rltb/sc_"_ustr },
+    { { ImageType::Size26, ImageWritingDirection::RightLeftTopBottom }, 
u"cmd/rltb/lc_"_ustr },
+    { { ImageType::Size32, ImageWritingDirection::RightLeftTopBottom }, 
u"cmd/rltb/32/"_ustr },
 };
 
 OUString lclConvertToCanonicalName(const OUString& rFileName)
@@ -116,32 +118,36 @@ bool CommandImageResolver::hasImage(const OUString& 
rCommandURL)
     return pIterator != m_aCommandToImageNameMap.end();
 }
 
-ImageList* CommandImageResolver::getImageList(ImageType nImageType)
+ImageList* CommandImageResolver::getImageList(ImageType nImageType, 
ImageWritingDirection nImageDir)
 {
     const OUString sIconTheme = 
Application::GetSettings().GetStyleSettings().DetermineIconTheme();
 
     if (sIconTheme != m_sIconTheme)
     {
         m_sIconTheme = sIconTheme;
-        for (auto& rp : m_pImageList)
-            rp.reset();
+        m_pImageList.clear();
     }
 
-    if (!m_pImageList[nImageType])
+    auto stKey = std::make_tuple(nImageType, nImageDir);
+    if (auto it = m_pImageList.find(stKey); it != m_pImageList.end())
     {
-        const OUString& sIconPath = ImageType_Prefixes[nImageType];
-        m_pImageList[nImageType].reset( new ImageList(m_aImageNameVector, 
sIconPath) );
+        return it->second.get();
     }
 
-    return m_pImageList[nImageType].get();
+    const OUString& sIconPath = ImageType_Prefixes.at(stKey);
+    auto pImageList = std::make_unique<ImageList>(m_aImageNameVector, 
sIconPath);
+
+    return m_pImageList.emplace(stKey, 
std::move(pImageList)).first->second.get();
 }
 
-Image CommandImageResolver::getImageFromCommandURL(ImageType nImageType, const 
OUString& rCommandURL)
+Image CommandImageResolver::getImageFromCommandURL(ImageType nImageType,
+                                                   ImageWritingDirection 
nImageDir,
+                                                   const OUString& rCommandURL)
 {
     CommandToImageNameMap::const_iterator pIterator = 
m_aCommandToImageNameMap.find(rCommandURL);
     if (pIterator != m_aCommandToImageNameMap.end())
     {
-        ImageList* pImageList = getImageList(nImageType);
+        ImageList* pImageList = getImageList(nImageType, nImageDir);
         return pImageList->GetImage(pIterator->second);
     }
     return Image();
diff --git a/framework/source/uiconfiguration/CommandImageResolver.hxx 
b/framework/source/uiconfiguration/CommandImageResolver.hxx
index 0622caf332bb..a826e6005a51 100644
--- a/framework/source/uiconfiguration/CommandImageResolver.hxx
+++ b/framework/source/uiconfiguration/CommandImageResolver.hxx
@@ -10,7 +10,6 @@
 #pragma once
 
 #include <vcl/image.hxx>
-#include <o3tl/enumarray.hxx>
 
 #include <com/sun/star/uno/Sequence.hxx>
 
@@ -19,6 +18,8 @@
 #include <memory>
 #include <unordered_map>
 #include <vector>
+#include <map>
+#include <tuple>
 
 namespace vcl
 {
@@ -31,17 +32,18 @@ private:
     std::vector<OUString> m_aImageCommandNameVector;
     std::vector<OUString> m_aImageNameVector;
 
-    o3tl::enumarray<ImageType, std::unique_ptr<ImageList>> m_pImageList;
+    std::map<std::tuple<ImageType, ImageWritingDirection>, 
std::unique_ptr<ImageList>> m_pImageList;
     OUString m_sIconTheme;
 
-    ImageList* getImageList(ImageType nImageType);
+    ImageList* getImageList(ImageType nImageType, ImageWritingDirection 
nImageDir);
 
 public:
     CommandImageResolver();
     ~CommandImageResolver();
 
     void registerCommands(const css::uno::Sequence<OUString>& 
aCommandSequence);
-    Image getImageFromCommandURL(ImageType nImageType, const OUString& 
rCommandURL);
+    Image getImageFromCommandURL(ImageType nImageType, ImageWritingDirection 
nImageDir,
+                                 const OUString& rCommandURL);
 
     std::vector<OUString>& getCommandNames() { return 
m_aImageCommandNameVector; }
 
diff --git a/framework/source/uiconfiguration/imagemanagerimpl.cxx 
b/framework/source/uiconfiguration/imagemanagerimpl.cxx
index 3dcfe40d764f..c351971aa1bb 100644
--- a/framework/source/uiconfiguration/imagemanagerimpl.cxx
+++ b/framework/source/uiconfiguration/imagemanagerimpl.cxx
@@ -62,8 +62,6 @@ using namespace ::com::sun::star::beans;
 using namespace ::com::sun::star::ui;
 using namespace ::cppu;
 
-const sal_Int16 MAX_IMAGETYPE_VALUE       = css::ui::ImageType::SIZE_32;
-
 constexpr OUString IMAGE_FOLDER = u"images"_ustr;
 constexpr OUString BITMAPS_FOLDER = u"Bitmaps"_ustr;
 
@@ -83,6 +81,10 @@ constexpr o3tl::enumarray<vcl::ImageType, OUString> 
BITMAP_FILE_NAMES
 
 namespace framework
 {
+namespace
+{
+using ImageIndex = std::tuple<vcl::ImageType, vcl::ImageWritingDirection>;
+}
 
 static GlobalImageList*     pGlobalImageList = nullptr;
 
@@ -159,14 +161,16 @@ void CmdImageList::initialize()
     m_bInitialized = true;
 }
 
-
-Image CmdImageList::getImageFromCommandURL(vcl::ImageType nImageType, const 
OUString& rCommandURL)
+Image CmdImageList::getImageFromCommandURL(vcl::ImageType nImageType,
+                                           vcl::ImageWritingDirection 
nImageDir,
+                                           const OUString& rCommandURL)
 {
     initialize();
-    return m_aResolver.getImageFromCommandURL(nImageType, rCommandURL);
+    return m_aResolver.getImageFromCommandURL(nImageType, nImageDir, 
rCommandURL);
 }
 
-bool CmdImageList::hasImage(vcl::ImageType /*nImageType*/, const OUString& 
rCommandURL)
+bool CmdImageList::hasImage(vcl::ImageType /*nImageType*/, 
vcl::ImageWritingDirection /*nImageDir*/,
+                            const OUString& rCommandURL)
 {
     initialize();
     return m_aResolver.hasImage(rCommandURL);
@@ -189,16 +193,19 @@ GlobalImageList::~GlobalImageList()
     pGlobalImageList = nullptr;
 }
 
-Image GlobalImageList::getImageFromCommandURL( vcl::ImageType nImageType, 
const OUString& rCommandURL )
+Image GlobalImageList::getImageFromCommandURL(vcl::ImageType nImageType,
+                                              vcl::ImageWritingDirection 
nImageDir,
+                                              const OUString& rCommandURL)
 {
-    std::unique_lock guard( getGlobalImageListMutex() );
-    return CmdImageList::getImageFromCommandURL( nImageType, rCommandURL );
+    std::unique_lock guard(getGlobalImageListMutex());
+    return CmdImageList::getImageFromCommandURL(nImageType, nImageDir, 
rCommandURL);
 }
 
-bool GlobalImageList::hasImage( vcl::ImageType nImageType, const OUString& 
rCommandURL )
+bool GlobalImageList::hasImage(vcl::ImageType nImageType, 
vcl::ImageWritingDirection nImageDir,
+                               const OUString& rCommandURL)
 {
-    std::unique_lock guard( getGlobalImageListMutex() );
-    return CmdImageList::hasImage( nImageType, rCommandURL );
+    std::unique_lock guard(getGlobalImageListMutex());
+    return CmdImageList::hasImage(nImageType, nImageDir, rCommandURL);
 }
 
 ::std::vector< OUString >& GlobalImageList::getImageCommandNames()
@@ -235,14 +242,36 @@ static bool implts_checkAndScaleGraphic( uno::Reference< 
XGraphic >& rOutGraphic
     return true;
 }
 
-static vcl::ImageType implts_convertImageTypeToIndex( sal_Int16 nImageType )
+static std::optional<ImageIndex> implts_convertImageTypeToIndex(sal_Int16 
nImageType)
 {
+    sal_Int16 nSanitize = 0;
+
+    vcl::ImageType nType = vcl::ImageType::Size16;
+    vcl::ImageWritingDirection nDir = vcl::ImageWritingDirection::DontCare;
+
     if (nImageType & css::ui::ImageType::SIZE_LARGE)
-        return vcl::ImageType::Size26;
+    {
+        nType = vcl::ImageType::Size26;
+        nSanitize |= css::ui::ImageType::SIZE_LARGE;
+    }
     else if (nImageType & css::ui::ImageType::SIZE_32)
-        return vcl::ImageType::Size32;
-    else
-        return vcl::ImageType::Size16;
+    {
+        nType = vcl::ImageType::Size32;
+        nSanitize |= css::ui::ImageType::SIZE_32;
+    }
+
+    if (nImageType & css::ui::ImageType::DIR_RL_TB)
+    {
+        nDir = vcl::ImageWritingDirection::RightLeftTopBottom;
+        nSanitize |= css::ui::ImageType::DIR_RL_TB;
+    }
+
+    if (nSanitize != nImageType)
+    {
+        return std::nullopt;
+    }
+
+    return ImageIndex{ nType, nDir };
 }
 
 ImageList* ImageManagerImpl::implts_getUserImageList( vcl::ImageType 
nImageType )
@@ -632,7 +661,9 @@ Sequence< OUString > ImageManagerImpl::getAllImageNames( 
::sal_Int16 nImageType
 
     std::unordered_set< OUString > aImageCmdNames;
 
-    vcl::ImageType nIndex = implts_convertImageTypeToIndex( nImageType );
+    auto nIndex = implts_convertImageTypeToIndex(nImageType);
+    if (!nIndex.has_value())
+        throw IllegalArgumentException();
 
     sal_uInt32 i( 0 );
     if ( m_bUseGlobal )
@@ -650,7 +681,7 @@ Sequence< OUString > ImageManagerImpl::getAllImageNames( 
::sal_Int16 nImageType
             aImageCmdNames.insert( rModuleImageNameVector[i] );
     }
 
-    ImageList* pImageList = implts_getUserImageList(nIndex);
+    ImageList* pImageList = implts_getUserImageList(std::get<0>(*nIndex));
     std::vector< OUString > rUserImageNames;
     pImageList->GetImageNames( rUserImageNames );
     const sal_uInt32 nUserCount = rUserImageNames.size();
@@ -668,20 +699,24 @@ bool ImageManagerImpl::hasImage( ::sal_Int16 nImageType, 
const OUString& aComman
     if ( m_bDisposed )
         throw DisposedException();
 
-    if (( nImageType < 0 ) || ( nImageType > MAX_IMAGETYPE_VALUE ))
+    auto nIndex = implts_convertImageTypeToIndex(nImageType);
+    if (!nIndex.has_value())
         throw IllegalArgumentException();
 
-    vcl::ImageType nIndex = implts_convertImageTypeToIndex( nImageType );
-    if ( m_bUseGlobal && implts_getGlobalImageList()->hasImage( nIndex, 
aCommandURL ))
+    if (m_bUseGlobal
+        && implts_getGlobalImageList()->hasImage(std::get<0>(*nIndex), 
std::get<1>(*nIndex),
+                                                 aCommandURL))
         return true;
     else
     {
-        if ( m_bUseGlobal && implts_getDefaultImageList()->hasImage( nIndex, 
aCommandURL ))
+        if (m_bUseGlobal
+            && implts_getDefaultImageList()->hasImage(std::get<0>(*nIndex), 
std::get<1>(*nIndex),
+                                                      aCommandURL))
             return true;
         else
         {
             // User layer
-            ImageList* pImageList = implts_getUserImageList(nIndex);
+            ImageList* pImageList = 
implts_getUserImageList(std::get<0>(*nIndex));
             if ( pImageList )
                 return ( pImageList->GetImagePos( aCommandURL ) != 
IMAGELIST_IMAGE_NOTFOUND );
         }
@@ -708,12 +743,12 @@ Sequence< uno::Reference< XGraphic > > 
ImageManagerImpl::getImages(
     if ( m_bDisposed )
         throw DisposedException();
 
-    if (( nImageType < 0 ) || ( nImageType > MAX_IMAGETYPE_VALUE ))
+    auto nIndex = implts_convertImageTypeToIndex(nImageType);
+    if (!nIndex.has_value())
         throw IllegalArgumentException();
 
     Sequence< uno::Reference< XGraphic > > aGraphSeq( 
aCommandURLSequence.getLength() );
 
-    vcl::ImageType                    nIndex            = 
implts_convertImageTypeToIndex( nImageType );
     rtl::Reference< GlobalImageList > rGlobalImageList;
     CmdImageList*                     pDefaultImageList = nullptr;
     if ( m_bUseGlobal )
@@ -721,7 +756,7 @@ Sequence< uno::Reference< XGraphic > > 
ImageManagerImpl::getImages(
         rGlobalImageList  = implts_getGlobalImageList();
         pDefaultImageList = implts_getDefaultImageList();
     }
-    ImageList*                        pUserImageList    = 
implts_getUserImageList(nIndex);
+    ImageList* pUserImageList = implts_getUserImageList(std::get<0>(*nIndex));
 
     // We have to search our image list in the following order:
     // 1. user image list (read/write)
@@ -734,11 +769,17 @@ Sequence< uno::Reference< XGraphic > > 
ImageManagerImpl::getImages(
         Image aImage = pUserImageList->GetImage( rURL );
         if ( !aImage && m_bUseGlobal )
         {
-            aImage = pDefaultImageList->getImageFromCommandURL( nIndex, rURL );
-            if ( !aImage )
-                aImage = rGlobalImageList->getImageFromCommandURL( nIndex, 
rURL );
+            aImage = 
pDefaultImageList->getImageFromCommandURL(std::get<0>(*nIndex),
+                                                               
std::get<1>(*nIndex), rURL);
+            if (!aImage)
+                aImage = 
rGlobalImageList->getImageFromCommandURL(std::get<0>(*nIndex),
+                                                                  
std::get<1>(*nIndex), rURL);
         }
 
+        // tdf#70102: Writing direction specializations are always optional. 
Suppress
+        // missing image file warnings on load.
+        aImage.SetOptional(std::get<1>(*nIndex) != 
vcl::ImageWritingDirection::DontCare);
+
         aGraphSeqRange[n++] = GetXGraphic(aImage);
     }
 
@@ -760,21 +801,21 @@ void ImageManagerImpl::replaceImages(
         if ( m_bDisposed )
             throw DisposedException();
 
-        if (( aCommandURLSequence.getLength() != aGraphicsSequence.getLength() 
) ||
-            (( nImageType < 0 ) || ( nImageType > MAX_IMAGETYPE_VALUE )))
+        auto nIndex = implts_convertImageTypeToIndex(nImageType);
+        if ((aCommandURLSequence.getLength() != aGraphicsSequence.getLength())
+            || (!nIndex.has_value()))
             throw IllegalArgumentException();
 
         if ( m_bReadOnly )
             throw IllegalAccessException();
 
-        vcl::ImageType nIndex = implts_convertImageTypeToIndex( nImageType );
-        ImageList* pImageList = implts_getUserImageList(nIndex);
+        ImageList* pImageList = implts_getUserImageList(std::get<0>(*nIndex));
 
         uno::Reference< XGraphic > xGraphic;
         for ( sal_Int32 i = 0; i < aCommandURLSequence.getLength(); i++ )
         {
             // Check size and scale. If we don't have any graphics ignore it
-            if ( !implts_checkAndScaleGraphic( xGraphic, aGraphicsSequence[i], 
nIndex ))
+            if (!implts_checkAndScaleGraphic(xGraphic, aGraphicsSequence[i], 
std::get<0>(*nIndex)))
                 continue;
 
             sal_uInt16 nPos = pImageList->GetImagePos( aCommandURLSequence[i] 
);
@@ -797,7 +838,7 @@ void ImageManagerImpl::replaceImages(
         if (( pInsertedImages != nullptr ) || (  pReplacedImages != nullptr ))
         {
             m_bModified = true;
-            m_bUserImageListModified[nIndex] = true;
+            m_bUserImageListModified[std::get<0>(*nIndex)] = true;
         }
     }
 
@@ -838,13 +879,13 @@ void ImageManagerImpl::removeImages( ::sal_Int16 
nImageType, const Sequence< OUS
         if ( m_bDisposed )
             throw DisposedException();
 
-        if (( nImageType < 0 ) || ( nImageType > MAX_IMAGETYPE_VALUE ))
+        auto nIndex = implts_convertImageTypeToIndex(nImageType);
+        if (!nIndex.has_value())
             throw IllegalArgumentException();
 
         if ( m_bReadOnly )
             throw IllegalAccessException();
 
-        vcl::ImageType nIndex = implts_convertImageTypeToIndex( nImageType );
         rtl::Reference< GlobalImageList > rGlobalImageList;
         CmdImageList*                     pDefaultImageList = nullptr;
         if ( m_bUseGlobal )
@@ -852,7 +893,7 @@ void ImageManagerImpl::removeImages( ::sal_Int16 
nImageType, const Sequence< OUS
             rGlobalImageList  = implts_getGlobalImageList();
             pDefaultImageList = implts_getDefaultImageList();
         }
-        ImageList*                        pImageList        = 
implts_getUserImageList(nIndex);
+        ImageList* pImageList = implts_getUserImageList(std::get<0>(*nIndex));
         uno::Reference<XGraphic> xEmptyGraphic;
 
         for ( const OUString& rURL : aCommandURLSequence )
@@ -867,9 +908,11 @@ void ImageManagerImpl::removeImages( ::sal_Int16 
nImageType, const Sequence< OUS
                 {
                     // Check, if we have an image in our module/global image 
list. If we find one =>
                     // this is a replace instead of a remove operation!
-                    Image aNewImage = 
pDefaultImageList->getImageFromCommandURL( nIndex, rURL );
-                    if ( !aNewImage )
-                        aNewImage = rGlobalImageList->getImageFromCommandURL( 
nIndex, rURL );
+                    Image aNewImage = 
pDefaultImageList->getImageFromCommandURL(
+                        std::get<0>(*nIndex), std::get<1>(*nIndex), rURL);
+                    if (!aNewImage)
+                        aNewImage = rGlobalImageList->getImageFromCommandURL(
+                            std::get<0>(*nIndex), std::get<1>(*nIndex), rURL);
                     if ( !aNewImage )
                     {
                         if ( !pRemovedImages )
@@ -895,7 +938,7 @@ void ImageManagerImpl::removeImages( ::sal_Int16 
nImageType, const Sequence< OUS
         if (( pReplacedImages != nullptr ) || ( pRemovedImages != nullptr ))
         {
             m_bModified = true;
-            m_bUserImageListModified[nIndex] = true;
+            m_bUserImageListModified[std::get<0>(*nIndex)] = true;
         }
     }
 
@@ -1003,9 +1046,11 @@ void ImageManagerImpl::reload()
                 {
                     if ( m_bUseGlobal )
                     {
-                        Image aImage = 
pDefaultImageList->getImageFromCommandURL( i, oldUserCmdImage.first );
-                        if ( !aImage )
-                            aImage = rGlobalImageList->getImageFromCommandURL( 
i, oldUserCmdImage.first );
+                        Image aImage = 
pDefaultImageList->getImageFromCommandURL(
+                            i, vcl::ImageWritingDirection::DontCare, 
oldUserCmdImage.first);
+                        if (!aImage)
+                            aImage = rGlobalImageList->getImageFromCommandURL(
+                                i, vcl::ImageWritingDirection::DontCare, 
oldUserCmdImage.first);
 
                         if ( !aImage )
                         {
diff --git a/framework/source/uiconfiguration/imagemanagerimpl.hxx 
b/framework/source/uiconfiguration/imagemanagerimpl.hxx
index 4d48da1c2312..643436d48b36 100644
--- a/framework/source/uiconfiguration/imagemanagerimpl.hxx
+++ b/framework/source/uiconfiguration/imagemanagerimpl.hxx
@@ -30,6 +30,7 @@
 #include <comphelper/interfacecontainer4.hxx>
 #include <rtl/ustring.hxx>
 
+#include <o3tl/enumarray.hxx>
 #include <rtl/ref.hxx>
 #include <salhelper/simplereferenceobject.hxx>
 
@@ -47,8 +48,11 @@ namespace framework
             CmdImageList(css::uno::Reference< css::uno::XComponentContext > 
xContext, OUString aModuleIdentifier);
             virtual ~CmdImageList();
 
-            virtual Image getImageFromCommandURL(vcl::ImageType nImageType, 
const OUString& rCommandURL);
-            virtual bool hasImage(vcl::ImageType nImageType, const OUString& 
rCommandURL);
+            virtual Image getImageFromCommandURL(vcl::ImageType nImageType,
+                                                 vcl::ImageWritingDirection 
nImageDir,
+                                                 const OUString& rCommandURL);
+            virtual bool hasImage(vcl::ImageType nImageType, 
vcl::ImageWritingDirection nImageDir,
+                                  const OUString& rCommandURL);
             virtual std::vector<OUString>& getImageCommandNames();
 
         protected:
@@ -68,8 +72,11 @@ namespace framework
             explicit GlobalImageList(const css::uno::Reference< 
css::uno::XComponentContext >& rxContext);
             virtual ~GlobalImageList() override;
 
-            virtual Image                           getImageFromCommandURL( 
vcl::ImageType nImageType, const OUString& rCommandURL ) override;
-            virtual bool                            hasImage( vcl::ImageType 
nImageType, const OUString& rCommandURL ) override;
+            virtual Image getImageFromCommandURL(vcl::ImageType nImageType,
+                                                 vcl::ImageWritingDirection 
nImageDir,
+                                                 const OUString& rCommandURL) 
override;
+            virtual bool hasImage(vcl::ImageType nImageType, 
vcl::ImageWritingDirection nImageDir,
+                                  const OUString& rCommandURL) override;
             virtual ::std::vector< OUString >&      getImageCommandNames() 
override;
     };
 
diff --git a/framework/source/uielement/generictoolbarcontroller.cxx 
b/framework/source/uielement/generictoolbarcontroller.cxx
index 8e72962018d8..bc47dfa26c34 100644
--- a/framework/source/uielement/generictoolbarcontroller.cxx
+++ b/framework/source/uielement/generictoolbarcontroller.cxx
@@ -394,6 +394,20 @@ void ImageOrientationController::statusChanged(const 
css::frame::FeatureStateEve
             OUString aCommand = m_pToolbar->get_item_ident(i);
             if (vcl::CommandInfoProvider::IsMirrored(aCommand, 
getModuleName()))
             {
+                if (m_bMirrored)
+                {
+                    // Search for a specialized mirrored graphic
+                    auto xRltbGraphic = 
vcl::CommandInfoProvider::GetXGraphicForCommand(
+                        aCommand, m_xFrame, m_pToolbar->get_icon_size(),
+                        vcl::ImageWritingDirection::RightLeftTopBottom);
+                    if (xRltbGraphic)
+                    {
+                        m_pToolbar->set_item_image_mirrored(aCommand, false);
+                        m_pToolbar->set_item_image(aCommand, xRltbGraphic);
+                        continue;
+                    }
+                }
+
                 m_pToolbar->set_item_image_mirrored(aCommand, m_bMirrored);
                 auto xGraphic(vcl::CommandInfoProvider::GetXGraphicForCommand(
                     aCommand, m_xFrame, m_pToolbar->get_icon_size()));
@@ -408,8 +422,25 @@ void ImageOrientationController::statusChanged(const 
css::frame::FeatureStateEve
         {
             ToolBoxItemId nItemId = pToolBox->GetItemId(i);
             OUString aCommand = pToolBox->GetItemCommand(nItemId);
+
+            bool bCmdMirrored = vcl::CommandInfoProvider::IsMirrored(aCommand, 
getModuleName());
+            if (bCmdMirrored && m_bMirrored)
+            {
+                // Search for a specialized mirrored graphic
+                auto xRltbGraphic = 
vcl::CommandInfoProvider::GetXGraphicForCommand(
+                    aCommand, m_xFrame, pToolBox->GetImageSize(),
+                    vcl::ImageWritingDirection::RightLeftTopBottom);
+                if (xRltbGraphic)
+                {
+                    pToolBox->SetItemImageMirrorMode(nItemId, false);
+                    pToolBox->SetItemImageAngle(nItemId, m_nRotationAngle);
+                    pToolBox->SetItemImage(nItemId, Image{ xRltbGraphic });
+                    continue;
+                }
+            }
+
             bool bModified = false;
-            if (vcl::CommandInfoProvider::IsMirrored(aCommand, 
getModuleName()))
+            if (bCmdMirrored)
             {
                 pToolBox->SetItemImageMirrorMode(nItemId, m_bMirrored);
                 bModified = true;
diff --git a/icon-themes/sifr_dark/cmd/rltb/32/defaultnumbering.png 
b/icon-themes/sifr_dark/cmd/rltb/32/defaultnumbering.png
new file mode 100644
index 000000000000..53789e67fed1
Binary files /dev/null and 
b/icon-themes/sifr_dark/cmd/rltb/32/defaultnumbering.png differ
diff --git a/icon-themes/sifr_dark/cmd/rltb/lc_defaultnumbering.png 
b/icon-themes/sifr_dark/cmd/rltb/lc_defaultnumbering.png
new file mode 100644
index 000000000000..1545b5c02e68
Binary files /dev/null and 
b/icon-themes/sifr_dark/cmd/rltb/lc_defaultnumbering.png differ
diff --git a/icon-themes/sifr_dark/cmd/rltb/sc_defaultnumbering.png 
b/icon-themes/sifr_dark/cmd/rltb/sc_defaultnumbering.png
new file mode 100644
index 000000000000..b99c75428e61
Binary files /dev/null and 
b/icon-themes/sifr_dark/cmd/rltb/sc_defaultnumbering.png differ
diff --git a/include/vcl/commandinfoprovider.hxx 
b/include/vcl/commandinfoprovider.hxx
index 016e53f79b3d..30aeb9af4a43 100644
--- a/include/vcl/commandinfoprovider.hxx
+++ b/include/vcl/commandinfoprovider.hxx
@@ -80,12 +80,14 @@ namespace vcl::CommandInfoProvider {
     VCL_DLLPUBLIC css::uno::Reference<css::graphic::XGraphic> 
GetXGraphicForCommand(
         const OUString& rsCommandName,
         const css::uno::Reference<css::frame::XFrame>& rxFrame,
-        vcl::ImageType eImageType = vcl::ImageType::Small);
+        vcl::ImageType eImageType = vcl::ImageType::Small,
+        vcl::ImageWritingDirection eImageDir = 
vcl::ImageWritingDirection::DontCare);
 
     VCL_DLLPUBLIC Image GetImageForCommand(
         const OUString& rsCommandName,
         const css::uno::Reference<css::frame::XFrame>& rxFrame,
-        vcl::ImageType eImageType = vcl::ImageType::Small);
+        vcl::ImageType eImageType = vcl::ImageType::Small,
+        vcl::ImageWritingDirection eImageDir = 
vcl::ImageWritingDirection::DontCare);
 
     VCL_DLLPUBLIC sal_Int32 GetPropertiesForCommand(
         const OUString& rsCommandName,
diff --git a/include/vcl/image.hxx b/include/vcl/image.hxx
index f7184820fc8e..10348d4a9cb7 100644
--- a/include/vcl/image.hxx
+++ b/include/vcl/image.hxx
@@ -48,6 +48,7 @@ public:
 
     Size GetSizePixel() const;
     BitmapEx GetBitmapEx() const;
+    void SetOptional(bool bValue);
 
     bool operator!() const
     {
diff --git a/include/vcl/vclenum.hxx b/include/vcl/vclenum.hxx
index e19648515d7d..f32ce771590d 100644
--- a/include/vcl/vclenum.hxx
+++ b/include/vcl/vclenum.hxx
@@ -308,6 +308,15 @@ namespace vcl
         Small = Size16,
         LAST = Size32,
     };
+
+    // Specifies a writing direction-based specialization for an image.
+    // Sets do not necessarily provide all possible specializations.
+    enum class ImageWritingDirection
+    {
+        LeftRightTopBottom = 0,
+        RightLeftTopBottom,
+        DontCare = LeftRightTopBottom,
+    };
 }
 
 enum class DrawFrameStyle
diff --git a/offapi/com/sun/star/ui/ImageType.idl 
b/offapi/com/sun/star/ui/ImageType.idl
index 98b741065d62..37ac8712a1c4 100644
--- a/offapi/com/sun/star/ui/ImageType.idl
+++ b/offapi/com/sun/star/ui/ImageType.idl
@@ -51,6 +51,13 @@ constants ImageType
      */
     const short COLOR_HIGHCONTRAST = 4;
 
+    /** an image unspecialized, or for lr-tb writing.
+     */
+    const short DIR_LR_TB = 0;
+
+    /** an image specialized for rl-tb writing.
+     */
+    const short DIR_RL_TB = 8;
 };
 
 }; }; }; };
diff --git a/vcl/inc/image.h b/vcl/inc/image.h
index cb75b45b837d..3f6feb3f96e7 100644
--- a/vcl/inc/image.h
+++ b/vcl/inc/image.h
@@ -40,6 +40,8 @@ private:
     BitmapEx maBitmapEx;
     BitmapEx maDisabledBitmapEx;
 
+    bool bOptional = false;
+
     bool loadStockAtScale(SalGraphics* pGraphics, BitmapEx &rBitmapEx);
 
 public:
@@ -64,6 +66,8 @@ public:
     /// Taking account of HiDPI scaling
     BitmapEx const & getBitmapExForHiDPI(bool bDisabled, SalGraphics* 
pGraphics);
 
+    void SetOptional(bool bValue);
+
     bool isEqual(const ImplImage &ref) const;
     bool isSizeEmpty() const
     {
diff --git a/vcl/source/helper/commandinfoprovider.cxx 
b/vcl/source/helper/commandinfoprovider.cxx
index 86ea1f0397cb..bc6a3eaa3638 100644
--- a/vcl/source/helper/commandinfoprovider.cxx
+++ b/vcl/source/helper/commandinfoprovider.cxx
@@ -339,18 +339,25 @@ OUString GetRealCommandForCommand(const 
css::uno::Sequence<css::beans::PropertyV
 
 Reference<graphic::XGraphic> GetXGraphicForCommand(const OUString& 
rsCommandName,
                                                    const 
Reference<frame::XFrame>& rxFrame,
-                                                   vcl::ImageType eImageType)
+                                                   vcl::ImageType eImageType,
+                                                   vcl::ImageWritingDirection 
eImageDir)
 {
     if (rsCommandName.isEmpty())
         return nullptr;
 
-    sal_Int16 nImageType(ui::ImageType::COLOR_NORMAL | 
ui::ImageType::SIZE_DEFAULT);
+    sal_Int16 nImageType(ui::ImageType::COLOR_NORMAL | 
ui::ImageType::SIZE_DEFAULT
+                         | ui::ImageType::DIR_LR_TB);
 
     if (eImageType == vcl::ImageType::Size26)
         nImageType |= ui::ImageType::SIZE_LARGE;
     else if (eImageType == vcl::ImageType::Size32)
         nImageType |= ui::ImageType::SIZE_32;
 
+    if (eImageDir == vcl::ImageWritingDirection::RightLeftTopBottom)
+    {
+        nImageType |= ui::ImageType::DIR_RL_TB;
+    }
+
     try
     {
         Reference<frame::XController> xController(rxFrame->getController(), 
UNO_SET_THROW);
@@ -392,11 +399,10 @@ Reference<graphic::XGraphic> GetXGraphicForCommand(const 
OUString& rsCommandName
     return nullptr;
 }
 
-Image GetImageForCommand(const OUString& rsCommandName,
-                         const Reference<frame::XFrame>& rxFrame,
-                         vcl::ImageType eImageType)
+Image GetImageForCommand(const OUString& rsCommandName, const 
Reference<frame::XFrame>& rxFrame,
+                         vcl::ImageType eImageType, vcl::ImageWritingDirection 
eImageDir)
 {
-    return Image(GetXGraphicForCommand(rsCommandName, rxFrame, eImageType));
+    return Image(GetXGraphicForCommand(rsCommandName, rxFrame, eImageType, 
eImageDir));
 }
 
 sal_Int32 GetPropertiesForCommand (
diff --git a/vcl/source/image/Image.cxx b/vcl/source/image/Image.cxx
index d368b9ece343..d0e3657499b0 100644
--- a/vcl/source/image/Image.cxx
+++ b/vcl/source/image/Image.cxx
@@ -101,6 +101,14 @@ BitmapEx Image::GetBitmapEx() const
         return BitmapEx();
 }
 
+void Image::SetOptional(bool bValue)
+{
+    if (mpImplData)
+    {
+        mpImplData->SetOptional(bValue);
+    }
+}
+
 bool Image::operator==(const Image& rImage) const
 {
     bool bRet = false;
diff --git a/vcl/source/image/ImplImage.cxx b/vcl/source/image/ImplImage.cxx
index 08bc9c64a836..94b35f9e75cd 100644
--- a/vcl/source/image/ImplImage.cxx
+++ b/vcl/source/image/ImplImage.cxx
@@ -92,15 +92,16 @@ bool ImplImage::loadStockAtScale(SalGraphics* pGraphics, 
BitmapEx &rBitmapEx)
             if (!ImageTree::get().loadImage(aFileName, aIconTheme, aBitmapEx, 
true,
                                             nScalePercentage, eScalingFlags))
             {
-                SAL_WARN("vcl", "Failed to load scaled image from " << 
maStockName <<
-                         " and " << aFileName << " at " << fScale);
+                SAL_WARN_IF(!bOptional, "vcl",
+                            "Failed to load scaled image from " << maStockName 
<< " and "
+                                                                << aFileName 
<< " at " << fScale);
                 return false;
             }
         }
         else
         {
-            SAL_WARN("vcl", "Failed to load scaled image from " << maStockName 
<<
-                     " at " << fScale);
+            SAL_WARN_IF(!bOptional, "vcl",
+                        "Failed to load scaled image from " << maStockName << 
" at " << fScale);
             return false;
         }
     }
@@ -123,7 +124,7 @@ Size ImplImage::getSizePixel()
             aRet = maSizePixel;
         }
         else
-            SAL_WARN("vcl", "Failed to load stock icon " << maStockName);
+            SAL_WARN_IF(!bOptional, "vcl", "Failed to load stock icon " << 
maStockName);
     }
     return aRet;
 }
@@ -149,6 +150,8 @@ BitmapEx const & ImplImage::getBitmapEx(bool bDisabled)
     return maBitmapEx;
 }
 
+void ImplImage::SetOptional(bool bValue) { bOptional = bValue; }
+
 bool ImplImage::isEqual(const ImplImage &ref) const
 {
     if (isStock() != ref.isStock())

Reply via email to