Files
odfstudio/framework/source/uiconfiguration/CommandImageResolver.cxx
T
Jonathan Clark dcfc32914f 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 <jonathan@libreoffice.org>
Tested-by: Jenkins
2025-03-11 06:40:33 +01:00

159 lines
5.3 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#include "CommandImageResolver.hxx"
#include <vcl/settings.hxx>
#include <vcl/svapp.hxx>
#include <rtl/ustrbuf.hxx>
#include <tools/urlobj.hxx>
using css::uno::Sequence;
namespace vcl
{
namespace
{
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)
{
bool bRemoveSlash(true);
sal_Int32 nLength = rFileName.getLength();
const sal_Unicode* pString = rFileName.getStr();
OUStringBuffer aBuffer(nLength*2);
for (sal_Int32 i = 0; i < nLength; i++)
{
const sal_Unicode cCurrentChar = pString[i];
switch (cCurrentChar)
{
// map forbidden characters to escape
case '/':
if (!bRemoveSlash)
aBuffer.append("%2f");
break;
case '\\': aBuffer.append("%5c"); bRemoveSlash = false; break;
case ':': aBuffer.append("%3a"); bRemoveSlash = false; break;
case '*': aBuffer.append("%2a"); bRemoveSlash = false; break;
case '?': aBuffer.append("%3f"); bRemoveSlash = false; break;
case '<': aBuffer.append("%3c"); bRemoveSlash = false; break;
case '>': aBuffer.append("%3e"); bRemoveSlash = false; break;
case '|': aBuffer.append("%7c"); bRemoveSlash = false; break;
default:
aBuffer.append(cCurrentChar); bRemoveSlash = false; break;
}
}
return aBuffer.makeStringAndClear();
}
} // end anonymous namespace
CommandImageResolver::CommandImageResolver()
{
}
CommandImageResolver::~CommandImageResolver()
{
}
void CommandImageResolver::registerCommands(const Sequence<OUString>& aCommandSequence)
{
sal_Int32 nSequenceSize = aCommandSequence.getLength();
m_aImageCommandNameVector.resize(nSequenceSize);
m_aImageNameVector.resize(nSequenceSize);
for (sal_Int32 i = 0; i < nSequenceSize; ++i)
{
const OUString& aCommandName(aCommandSequence[i]);
OUString aImageName;
m_aImageCommandNameVector[i] = aCommandName;
if (aCommandName.indexOf(".uno:") != 0)
{
INetURLObject aUrlObject(aCommandName, INetURLObject::EncodeMechanism::All);
aImageName = aUrlObject.GetURLPath();
aImageName = lclConvertToCanonicalName(aImageName);
}
else
{
// just remove the schema
if (aCommandName.getLength() > 5)
aImageName = aCommandName.copy(5);
// Search for query part.
if (aImageName.indexOf('?') != -1)
aImageName = lclConvertToCanonicalName(aImageName);
}
// Image names are not case-dependent. Always use lower case characters to
// reflect this.
aImageName = aImageName.toAsciiLowerCase() + ".png";
m_aImageNameVector[i] = aImageName;
m_aCommandToImageNameMap[aCommandName] = aImageName;
}
}
bool CommandImageResolver::hasImage(const OUString& rCommandURL)
{
CommandToImageNameMap::const_iterator pIterator = m_aCommandToImageNameMap.find(rCommandURL);
return pIterator != m_aCommandToImageNameMap.end();
}
ImageList* CommandImageResolver::getImageList(ImageType nImageType, ImageWritingDirection nImageDir)
{
const OUString sIconTheme = Application::GetSettings().GetStyleSettings().DetermineIconTheme();
if (sIconTheme != m_sIconTheme)
{
m_sIconTheme = sIconTheme;
m_pImageList.clear();
}
auto stKey = std::make_tuple(nImageType, nImageDir);
if (auto it = m_pImageList.find(stKey); it != m_pImageList.end())
{
return it->second.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,
ImageWritingDirection nImageDir,
const OUString& rCommandURL)
{
CommandToImageNameMap::const_iterator pIterator = m_aCommandToImageNameMap.find(rCommandURL);
if (pIterator != m_aCommandToImageNameMap.end())
{
ImageList* pImageList = getImageList(nImageType, nImageDir);
return pImageList->GetImage(pIterator->second);
}
return Image();
}
} // end namespace vcl
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */