forked from amazingfate/loongoffice
The file cropmarkers.svg is used to create individual crop markers. That methods expects a certain, fixed size in unit pixel. The number of pixels was wrong on Windows, if display scale is set so large that resulting dpi is larger than 120. The patch excludes this special file from scaling. For testing you need to set display scale to larger than 125% in Windows (125%*96dpi=120dpi). And remove any existing cropmarkers.png in cache/themename_svg/DPI/svx/res/. 'DPI' therein is not your actual dpi but the scaling factor from CountDPIScaleFactor. Change-Id: I6b508e28f2f3782441ad1283cfe85c4bd1588383 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/172019 Tested-by: Jenkins Reviewed-by: Patrick Luby <guibomacdev@gmail.com> Reviewed-by: Regina Henschel <rb.henschel@t-online.de>
725 lines
22 KiB
C++
725 lines
22 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/.
|
|
*
|
|
* This file incorporates work covered by the following license notice:
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed
|
|
* with this work for additional information regarding copyright
|
|
* ownership. The ASF licenses this file to you under the Apache
|
|
* License, Version 2.0 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of
|
|
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
|
|
*/
|
|
|
|
#include <config_folders.h>
|
|
|
|
#include <sal/config.h>
|
|
#include <sal/log.hxx>
|
|
|
|
#include <deque>
|
|
#include <string_view>
|
|
|
|
#include <com/sun/star/container/XNameAccess.hpp>
|
|
#include <com/sun/star/io/XInputStream.hpp>
|
|
#include <com/sun/star/packages/zip/ZipFileAccess.hpp>
|
|
#include <com/sun/star/ucb/SimpleFileAccess.hpp>
|
|
#include <com/sun/star/uno/Exception.hpp>
|
|
#include <com/sun/star/uno/RuntimeException.hpp>
|
|
#include <com/sun/star/uno/Sequence.hxx>
|
|
#include <comphelper/processfactory.hxx>
|
|
#include <cppuhelper/implbase.hxx>
|
|
#include <osl/file.hxx>
|
|
#include <osl/diagnose.h>
|
|
#include <osl/process.h>
|
|
#include <rtl/bootstrap.hxx>
|
|
#include <rtl/uri.hxx>
|
|
#include <rtl/strbuf.hxx>
|
|
|
|
#include <comphelper/diagnose_ex.hxx>
|
|
#include <tools/stream.hxx>
|
|
#include <tools/urlobj.hxx>
|
|
#include <implimagetree.hxx>
|
|
|
|
#include <utility>
|
|
#include <vcl/bitmapex.hxx>
|
|
#include <vcl/dibtools.hxx>
|
|
#include <vcl/settings.hxx>
|
|
#include <vcl/svapp.hxx>
|
|
#include <vcl/BitmapTools.hxx>
|
|
#include <IconThemeScanner.hxx>
|
|
#include <vcl/filter/PngImageReader.hxx>
|
|
#include <vcl/outdev.hxx>
|
|
#include <vcl/filter/PngImageWriter.hxx>
|
|
#include <o3tl/string_view.hxx>
|
|
#include <bitmap/BitmapLightenFilter.hxx>
|
|
|
|
using namespace css;
|
|
|
|
bool ImageRequestParameters::convertToDarkTheme()
|
|
{
|
|
static bool bIconsForDarkTheme = !!getenv("VCL_ICONS_FOR_DARK_THEME");
|
|
|
|
bool bConvertToDarkTheme = false;
|
|
if (!(meFlags & ImageLoadFlags::IgnoreDarkTheme))
|
|
bConvertToDarkTheme = bIconsForDarkTheme;
|
|
|
|
return bConvertToDarkTheme;
|
|
}
|
|
|
|
sal_Int32 ImageRequestParameters::scalePercentage()
|
|
{
|
|
sal_Int32 aScalePercentage = 100;
|
|
OutputDevice* pDevice = Application::GetDefaultDevice();
|
|
if (!(meFlags & ImageLoadFlags::IgnoreScalingFactor) && pDevice != nullptr)
|
|
aScalePercentage = pDevice->GetDPIScalePercentage();
|
|
else if (mnScalePercentage > 0)
|
|
aScalePercentage = mnScalePercentage;
|
|
return aScalePercentage;
|
|
}
|
|
|
|
namespace
|
|
{
|
|
|
|
OUString convertLcTo32Path(std::u16string_view rPath)
|
|
{
|
|
OUString aResult;
|
|
size_t nSlashPos = rPath.rfind('/');
|
|
if (nSlashPos != std::u16string_view::npos)
|
|
{
|
|
sal_Int32 nCopyFrom = nSlashPos + 1;
|
|
std::u16string_view sFile = rPath.substr(nCopyFrom);
|
|
std::u16string_view sDir = rPath.substr(0, nSlashPos);
|
|
if (!sFile.empty() && o3tl::starts_with(sFile, u"lc_"))
|
|
{
|
|
aResult = OUString::Concat(sDir) + "/32/" + sFile.substr(3);
|
|
}
|
|
}
|
|
return aResult;
|
|
}
|
|
|
|
OUString createPath(std::u16string_view name, sal_Int32 pos, std::u16string_view locale)
|
|
{
|
|
return OUString::Concat(name.substr(0, pos + 1)) + locale + name.substr(pos);
|
|
}
|
|
|
|
OUString getIconCacheUrlImpl()
|
|
{
|
|
OUString sDir = u"${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("bootstrap") ":UserInstallation}/cache/"_ustr;
|
|
rtl::Bootstrap::expandMacros(sDir);
|
|
return sDir;
|
|
}
|
|
|
|
OUString getIconCacheUrl(std::u16string_view sVariant, ImageRequestParameters const & rParameters)
|
|
{
|
|
// the macro expansion can be expensive in bulk, so cache that
|
|
static OUString CACHE_DIR = getIconCacheUrlImpl();
|
|
return CACHE_DIR + rParameters.msStyle + "/" + sVariant + "/" + rParameters.msName;
|
|
}
|
|
|
|
OUString createIconCacheUrl(
|
|
std::u16string_view sVariant, ImageRequestParameters const & rParameters)
|
|
{
|
|
OUString sUrl(getIconCacheUrl(sVariant, rParameters));
|
|
OUString sDir = sUrl.copy(0, sUrl.lastIndexOf('/'));
|
|
osl::Directory::createPath(sDir);
|
|
return sUrl;
|
|
}
|
|
|
|
bool urlExists(OUString const & sUrl)
|
|
{
|
|
osl::File aFile(sUrl);
|
|
osl::FileBase::RC eRC = aFile.open(osl_File_OpenFlag_Read);
|
|
return osl::FileBase::E_None == eRC;
|
|
}
|
|
|
|
OUString getNameNoExtension(std::u16string_view sName)
|
|
{
|
|
size_t nDotPosition = sName.rfind('.');
|
|
return OUString(sName.substr(0, nDotPosition));
|
|
}
|
|
|
|
std::shared_ptr<SvMemoryStream> wrapStream(uno::Reference<io::XInputStream> const & rInputStream)
|
|
{
|
|
// This could use SvInputStream instead if that did not have a broken
|
|
// SeekPos implementation for an XInputStream that is not also XSeekable
|
|
// (cf. "@@@" at tags/DEV300_m37/svtools/source/misc1/strmadpt.cxx@264807
|
|
// l. 593):
|
|
OSL_ASSERT(rInputStream.is());
|
|
std::shared_ptr<SvMemoryStream> aMemoryStream(std::make_shared<SvMemoryStream>());
|
|
for (;;)
|
|
{
|
|
const sal_Int32 nSize(2048);
|
|
uno::Sequence<sal_Int8> aData(nSize);
|
|
sal_Int32 nRead = rInputStream->readBytes(aData, nSize);
|
|
aMemoryStream->WriteBytes(aData.getConstArray(), nRead);
|
|
if (nRead < nSize)
|
|
break;
|
|
}
|
|
aMemoryStream->Seek(0);
|
|
rInputStream->closeInput();
|
|
return aMemoryStream;
|
|
}
|
|
|
|
void loadImageFromStream(std::shared_ptr<SvStream> const & xStream, OUString const & rPath, ImageRequestParameters& rParameters)
|
|
{
|
|
bool bConvertToDarkTheme = rParameters.convertToDarkTheme();
|
|
sal_Int32 aScalePercentage = rParameters.scalePercentage();
|
|
|
|
if (rPath.endsWith(".png"))
|
|
{
|
|
vcl::PngImageReader aPNGReader(*xStream);
|
|
aPNGReader.read(rParameters.mrBitmap);
|
|
}
|
|
else if (rPath.endsWith(".svg"))
|
|
{
|
|
rParameters.mbWriteImageToCache = true; // We always want to cache a SVG image
|
|
#ifdef _WIN32
|
|
// tdf#153421. Do not scale, individual crop handles are created from it using pixel unit.
|
|
if (rPath.endsWith("cropmarkers.svg"))
|
|
vcl::bitmap::loadFromSvg(*xStream, rPath, rParameters.mrBitmap, 1.0);
|
|
else
|
|
#endif
|
|
vcl::bitmap::loadFromSvg(*xStream, rPath, rParameters.mrBitmap, aScalePercentage / 100.0);
|
|
|
|
if (bConvertToDarkTheme)
|
|
BitmapFilter::Filter(rParameters.mrBitmap, BitmapLightenFilter());
|
|
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
ReadDIBBitmapEx(rParameters.mrBitmap, *xStream);
|
|
}
|
|
|
|
if (bConvertToDarkTheme)
|
|
{
|
|
rParameters.mbWriteImageToCache = true; // Cache the dark variant
|
|
BitmapFilter::Filter(rParameters.mrBitmap, BitmapLightenFilter());
|
|
}
|
|
|
|
if (aScalePercentage > 100)
|
|
{
|
|
rParameters.mbWriteImageToCache = true; // Cache the scaled variant
|
|
double aScaleFactor(aScalePercentage / 100.0);
|
|
// when scaling use the full 24bit RGB values
|
|
rParameters.mrBitmap.Convert(BmpConversion::N24Bit);
|
|
rParameters.mrBitmap.Scale(aScaleFactor, aScaleFactor, BmpScaleFlag::Fast);
|
|
}
|
|
}
|
|
|
|
} // end anonymous namespace
|
|
|
|
ImplImageTree::ImplImageTree()
|
|
{
|
|
}
|
|
|
|
ImplImageTree::~ImplImageTree()
|
|
{
|
|
}
|
|
|
|
std::vector<OUString> ImplImageTree::getPaths(OUString const & name, LanguageTag const & rLanguageTag)
|
|
{
|
|
std::vector<OUString> sPaths;
|
|
|
|
sal_Int32 pos = name.lastIndexOf('/');
|
|
if (pos != -1)
|
|
{
|
|
for (const OUString& rFallback : rLanguageTag.getFallbackStrings(true))
|
|
{
|
|
OUString aFallbackName = getNameNoExtension(getRealImageName(createPath(name, pos, rFallback)));
|
|
sPaths.emplace_back(aFallbackName + ".png");
|
|
sPaths.emplace_back(aFallbackName + ".svg");
|
|
}
|
|
}
|
|
|
|
OUString aRealName = getNameNoExtension(getRealImageName(name));
|
|
sPaths.emplace_back(aRealName + ".png");
|
|
sPaths.emplace_back(aRealName + ".svg");
|
|
|
|
return sPaths;
|
|
}
|
|
|
|
OUString ImplImageTree::getImageUrl(OUString const & rName, OUString const & rStyle, OUString const & rLang)
|
|
{
|
|
OUString aStyle(rStyle);
|
|
|
|
while (!aStyle.isEmpty())
|
|
{
|
|
try
|
|
{
|
|
setStyle(aStyle);
|
|
|
|
if (checkPathAccess())
|
|
{
|
|
IconSet& rIconSet = getCurrentIconSet();
|
|
const uno::Reference<container::XNameAccess> & rNameAccess = rIconSet.maNameAccess;
|
|
|
|
LanguageTag aLanguageTag(rLang);
|
|
|
|
for (const OUString& rPath: getPaths(rName, aLanguageTag))
|
|
{
|
|
if (rNameAccess->hasByName(rPath))
|
|
{
|
|
return "vnd.sun.star.zip://"
|
|
+ rtl::Uri::encode(rIconSet.maURL, rtl_UriCharClassRegName,
|
|
rtl_UriEncodeIgnoreEscapes, RTL_TEXTENCODING_UTF8)
|
|
+ "/" + rPath;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (const uno::Exception &)
|
|
{
|
|
TOOLS_INFO_EXCEPTION("vcl", "");
|
|
}
|
|
|
|
aStyle = fallbackStyle(aStyle);
|
|
}
|
|
return OUString();
|
|
}
|
|
|
|
uno::Reference<io::XInputStream> ImplImageTree::getImageXInputStream(OUString const & rName, OUString const & rStyle, OUString const & rLang)
|
|
{
|
|
OUString aStyle(rStyle);
|
|
|
|
while (!aStyle.isEmpty())
|
|
{
|
|
try
|
|
{
|
|
setStyle(aStyle);
|
|
|
|
if (checkPathAccess())
|
|
{
|
|
IconSet& rIconSet = getCurrentIconSet();
|
|
const uno::Reference<container::XNameAccess>& rNameAccess = rIconSet.maNameAccess;
|
|
|
|
LanguageTag aLanguageTag(rLang);
|
|
|
|
for (const OUString& rPath: getPaths(rName, aLanguageTag))
|
|
{
|
|
if (rNameAccess->hasByName(rPath))
|
|
{
|
|
uno::Reference<io::XInputStream> aStream;
|
|
bool ok = rNameAccess->getByName(rPath) >>= aStream;
|
|
assert(ok);
|
|
(void)ok; // prevent unused warning in release build
|
|
return aStream;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (const uno::Exception &)
|
|
{
|
|
TOOLS_INFO_EXCEPTION("vcl", "");
|
|
}
|
|
|
|
aStyle = fallbackStyle(aStyle);
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
std::shared_ptr<SvMemoryStream> ImplImageTree::getImageStream(OUString const & rName, OUString const & rStyle, OUString const & rLang)
|
|
{
|
|
uno::Reference<io::XInputStream> xStream = getImageXInputStream(rName, rStyle, rLang);
|
|
if (xStream)
|
|
return wrapStream(xStream);
|
|
return std::shared_ptr<SvMemoryStream>();
|
|
}
|
|
|
|
OUString ImplImageTree::fallbackStyle(std::u16string_view rsStyle)
|
|
{
|
|
OUString sResult;
|
|
|
|
if (rsStyle == u"colibre" || rsStyle == u"helpimg")
|
|
sResult = "";
|
|
else if (rsStyle == u"sifr" || rsStyle == u"breeze_dark")
|
|
sResult = "breeze";
|
|
else if (rsStyle == u"sifr_dark" )
|
|
sResult = "breeze_dark";
|
|
else
|
|
sResult = "colibre";
|
|
|
|
return sResult;
|
|
}
|
|
|
|
bool ImplImageTree::loadImage(OUString const & rName, OUString const & rStyle, BitmapEx & rBitmap, bool localized,
|
|
const ImageLoadFlags eFlags, sal_Int32 nScalePercentage)
|
|
{
|
|
OUString aCurrentStyle(rStyle);
|
|
while (!aCurrentStyle.isEmpty())
|
|
{
|
|
try
|
|
{
|
|
ImageRequestParameters aParameters(rName, aCurrentStyle, rBitmap, localized, eFlags, nScalePercentage);
|
|
if (doLoadImage(aParameters))
|
|
return true;
|
|
}
|
|
catch (uno::RuntimeException &)
|
|
{}
|
|
|
|
aCurrentStyle = fallbackStyle(aCurrentStyle);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
namespace
|
|
{
|
|
|
|
OUString createVariant(ImageRequestParameters& rParameters)
|
|
{
|
|
bool bConvertToDarkTheme = rParameters.convertToDarkTheme();
|
|
sal_Int32 aScalePercentage = rParameters.scalePercentage();
|
|
|
|
OUString aVariant = OUString::number(aScalePercentage);
|
|
|
|
if (bConvertToDarkTheme)
|
|
aVariant += "-dark";
|
|
|
|
return aVariant;
|
|
}
|
|
|
|
bool loadDiskCachedVersion(std::u16string_view sVariant, ImageRequestParameters& rParameters)
|
|
{
|
|
OUString sUrl(getIconCacheUrl(sVariant, rParameters));
|
|
if (!urlExists(sUrl))
|
|
return false;
|
|
SvFileStream aFileStream(sUrl, StreamMode::READ);
|
|
vcl::PngImageReader aPNGReader(aFileStream);
|
|
aPNGReader.read(rParameters.mrBitmap);
|
|
return true;
|
|
}
|
|
|
|
void cacheBitmapToDisk(std::u16string_view sVariant, ImageRequestParameters const & rParameters)
|
|
{
|
|
OUString sUrl(createIconCacheUrl(sVariant, rParameters));
|
|
try
|
|
{
|
|
SvFileStream aStream(sUrl, StreamMode::WRITE);
|
|
vcl::PngImageWriter aWriter(aStream);
|
|
aWriter.write(rParameters.mrBitmap);
|
|
aStream.Close();
|
|
}
|
|
catch (...)
|
|
{}
|
|
}
|
|
|
|
} // end anonymous namespace
|
|
|
|
bool ImplImageTree::doLoadImage(ImageRequestParameters& rParameters)
|
|
{
|
|
setStyle(rParameters.msStyle);
|
|
|
|
if (iconCacheLookup(rParameters))
|
|
return true;
|
|
|
|
OUString aVariant = createVariant(rParameters);
|
|
if (loadDiskCachedVersion(aVariant, rParameters))
|
|
return true;
|
|
|
|
if (!rParameters.mrBitmap.IsEmpty())
|
|
rParameters.mrBitmap.SetEmpty();
|
|
|
|
LanguageTag aLanguageTag = Application::GetSettings().GetUILanguageTag();
|
|
|
|
std::vector<OUString> aPaths = getPaths(rParameters.msName, aLanguageTag);
|
|
|
|
bool bFound = false;
|
|
|
|
try
|
|
{
|
|
bFound = findImage(aPaths, rParameters);
|
|
}
|
|
catch (uno::RuntimeException&)
|
|
{
|
|
throw;
|
|
}
|
|
catch (const uno::Exception&)
|
|
{
|
|
TOOLS_INFO_EXCEPTION("vcl", "ImplImageTree::doLoadImage");
|
|
}
|
|
|
|
if (bFound)
|
|
{
|
|
if (rParameters.mbWriteImageToCache)
|
|
{
|
|
cacheBitmapToDisk(aVariant, rParameters);
|
|
}
|
|
getIconCache(rParameters)[rParameters.msName] = std::make_pair(rParameters.mbLocalized, rParameters.mrBitmap);
|
|
}
|
|
|
|
return bFound;
|
|
}
|
|
|
|
void ImplImageTree::shutdown()
|
|
{
|
|
maCurrentStyle.clear();
|
|
maIconSets.clear();
|
|
}
|
|
|
|
void ImplImageTree::setStyle(OUString const & style)
|
|
{
|
|
assert(!style.isEmpty());
|
|
if (style != maCurrentStyle)
|
|
{
|
|
maCurrentStyle = style;
|
|
createStyle();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* The vcldemo app doesn't set up all the config stuff that the main app does, so we need another
|
|
* way of finding the cursor images.
|
|
*/
|
|
static bool isVclDemo()
|
|
{
|
|
static const bool bVclDemoOverride = std::getenv("LIBO_VCL_DEMO") != nullptr;
|
|
return bVclDemoOverride;
|
|
}
|
|
|
|
void ImplImageTree::createStyle()
|
|
{
|
|
if (maIconSets.find(maCurrentStyle) != maIconSets.end())
|
|
return;
|
|
|
|
OUString sThemeUrl;
|
|
|
|
if (isVclDemo())
|
|
{
|
|
if (maCurrentStyle == "default")
|
|
sThemeUrl = "file://" SRC_ROOT "/icon-themes/colibre-svg";
|
|
else
|
|
sThemeUrl = "file://" SRC_ROOT "/icon-themes/" + maCurrentStyle;
|
|
}
|
|
else if (maCurrentStyle != "default")
|
|
{
|
|
OUString paths = vcl::IconThemeScanner::GetStandardIconThemePath();
|
|
std::deque<OUString> aPaths;
|
|
sal_Int32 nIndex = 0;
|
|
do
|
|
{
|
|
aPaths.push_front(paths.getToken(0, ';', nIndex));
|
|
}
|
|
while (nIndex >= 0);
|
|
|
|
for (const auto& path : aPaths)
|
|
{
|
|
INetURLObject aUrl(path);
|
|
OSL_ASSERT(!aUrl.HasError());
|
|
|
|
bool ok = aUrl.Append(Concat2View("images_" + maCurrentStyle), INetURLObject::EncodeMechanism::All);
|
|
OSL_ASSERT(ok);
|
|
sThemeUrl = aUrl.GetMainURL(INetURLObject::DecodeMechanism::NONE) + ".zip";
|
|
if (urlExists(sThemeUrl))
|
|
break;
|
|
sThemeUrl.clear();
|
|
}
|
|
|
|
if (sThemeUrl.isEmpty())
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
sThemeUrl += "images";
|
|
if (!urlExists(sThemeUrl))
|
|
return;
|
|
}
|
|
|
|
maIconSets[maCurrentStyle] = IconSet(sThemeUrl);
|
|
|
|
loadImageLinks();
|
|
}
|
|
|
|
/// Find an icon cache for the right scale factor
|
|
ImplImageTree::IconCache &ImplImageTree::getIconCache(const ImageRequestParameters& rParameters)
|
|
{
|
|
IconSet &rSet = getCurrentIconSet();
|
|
auto it = rSet.maScaledIconCaches.find(rParameters.mnScalePercentage);
|
|
if ( it != rSet.maScaledIconCaches.end() )
|
|
return it->second;
|
|
rSet.maScaledIconCaches.emplace(rParameters.mnScalePercentage, IconCache());
|
|
return rSet.maScaledIconCaches[rParameters.mnScalePercentage];
|
|
}
|
|
|
|
bool ImplImageTree::iconCacheLookup(ImageRequestParameters& rParameters)
|
|
{
|
|
IconCache& rIconCache = getIconCache(rParameters);
|
|
|
|
IconCache::iterator i(rIconCache.find(getRealImageName(rParameters.msName)));
|
|
if (i != rIconCache.end() && i->second.first == rParameters.mbLocalized)
|
|
{
|
|
rParameters.mrBitmap = i->second.second;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool ImplImageTree::findImage(std::vector<OUString> const & rPaths, ImageRequestParameters& rParameters)
|
|
{
|
|
if (!checkPathAccess())
|
|
return false;
|
|
|
|
uno::Reference<container::XNameAccess> const & rNameAccess = getCurrentIconSet().maNameAccess;
|
|
|
|
for (OUString const & rPath : rPaths)
|
|
{
|
|
if (rNameAccess->hasByName(rPath))
|
|
{
|
|
uno::Reference<io::XInputStream> aStream;
|
|
bool ok = rNameAccess->getByName(rPath) >>= aStream;
|
|
assert(ok);
|
|
(void)ok; // prevent unused warning in release build
|
|
|
|
loadImageFromStream(wrapStream(aStream), rPath, rParameters);
|
|
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void ImplImageTree::loadImageLinks()
|
|
{
|
|
static constexpr OUString aLinkFilename(u"links.txt"_ustr);
|
|
|
|
if (!checkPathAccess())
|
|
return;
|
|
|
|
const uno::Reference<container::XNameAccess> &rNameAccess = getCurrentIconSet().maNameAccess;
|
|
|
|
if (rNameAccess->hasByName(aLinkFilename))
|
|
{
|
|
uno::Reference<io::XInputStream> xStream;
|
|
bool ok = rNameAccess->getByName(aLinkFilename) >>= xStream;
|
|
assert(ok);
|
|
(void)ok; // prevent unused warning in release build
|
|
|
|
parseLinkFile(wrapStream(xStream));
|
|
return;
|
|
}
|
|
}
|
|
|
|
void ImplImageTree::parseLinkFile(std::shared_ptr<SvStream> const & xStream)
|
|
{
|
|
OStringBuffer aLine;
|
|
OUString aLink, aOriginal;
|
|
int nLineNo = 0;
|
|
while (xStream->ReadLine(aLine))
|
|
{
|
|
++nLineNo;
|
|
if (aLine.isEmpty())
|
|
continue;
|
|
|
|
sal_Int32 nIndex = 0;
|
|
aLink = OStringToOUString(o3tl::getToken(aLine, 0, ' ', nIndex), RTL_TEXTENCODING_UTF8);
|
|
aOriginal = OStringToOUString(o3tl::getToken(aLine, 0, ' ', nIndex), RTL_TEXTENCODING_UTF8);
|
|
|
|
// skip comments, or incomplete entries
|
|
if (aLink.isEmpty() || aLink[0] == '#' || aOriginal.isEmpty())
|
|
{
|
|
if (aLink.isEmpty() || aOriginal.isEmpty())
|
|
SAL_WARN("vcl", "ImplImageTree::parseLinkFile: icon links.txt parse error, incomplete link at line " << nLineNo);
|
|
continue;
|
|
}
|
|
|
|
getCurrentIconSet().maLinkHash[aLink] = aOriginal;
|
|
|
|
OUString aOriginal32 = convertLcTo32Path(aOriginal);
|
|
OUString aLink32 = convertLcTo32Path(aLink);
|
|
|
|
if (!aOriginal32.isEmpty() && !aLink32.isEmpty())
|
|
getCurrentIconSet().maLinkHash[aLink32] = aOriginal32;
|
|
}
|
|
}
|
|
|
|
OUString const & ImplImageTree::getRealImageName(OUString const & rIconName)
|
|
{
|
|
IconLinkHash & rLinkHash = maIconSets[maCurrentStyle].maLinkHash;
|
|
|
|
OUString sNameWithNoExtension = getNameNoExtension(rIconName);
|
|
|
|
// PNG is priority
|
|
auto it = rLinkHash.find(sNameWithNoExtension + ".png");
|
|
if (it != rLinkHash.end())
|
|
return it->second;
|
|
|
|
// also check SVG name
|
|
it = rLinkHash.find(sNameWithNoExtension + ".svg");
|
|
if (it != rLinkHash.end())
|
|
return it->second;
|
|
|
|
// neither was found so just return the original name
|
|
return rIconName;
|
|
}
|
|
|
|
namespace {
|
|
|
|
class FolderFileAccess : public ::cppu::WeakImplHelper<css::container::XNameAccess>
|
|
{
|
|
public:
|
|
uno::Reference< uno::XComponentContext > mxContext;
|
|
OUString maURL;
|
|
FolderFileAccess(uno::Reference< uno::XComponentContext > context, OUString url)
|
|
: mxContext(std::move(context)), maURL(std::move(url)) {}
|
|
// XElementAccess
|
|
virtual css::uno::Type SAL_CALL getElementType() override { return cppu::UnoType<io::XInputStream>::get(); }
|
|
virtual sal_Bool SAL_CALL hasElements() override { return true; }
|
|
// XNameAccess
|
|
virtual css::uno::Any SAL_CALL getByName( const OUString& aName ) override
|
|
{
|
|
uno::Reference< io::XInputStream > xInputStream = ucb::SimpleFileAccess::create(mxContext)->openFileRead( maURL + "/" + aName );
|
|
return css::uno::Any(xInputStream);
|
|
}
|
|
virtual css::uno::Sequence< OUString > SAL_CALL getElementNames() override
|
|
{
|
|
return {};
|
|
}
|
|
virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override
|
|
{
|
|
osl::File aBaseFile(maURL + "/" + aName);
|
|
return osl::File::E_None == aBaseFile.open(osl_File_OpenFlag_Read);
|
|
}
|
|
};
|
|
|
|
}
|
|
|
|
bool ImplImageTree::checkPathAccess()
|
|
{
|
|
IconSet& rIconSet = getCurrentIconSet();
|
|
uno::Reference<container::XNameAccess> & rNameAccess = rIconSet.maNameAccess;
|
|
if (rNameAccess.is())
|
|
return true;
|
|
|
|
try
|
|
{
|
|
if (isVclDemo())
|
|
rNameAccess = new FolderFileAccess(comphelper::getProcessComponentContext(), rIconSet.maURL);
|
|
else
|
|
rNameAccess = packages::zip::ZipFileAccess::createWithURL(comphelper::getProcessComponentContext(), rIconSet.maURL);
|
|
}
|
|
catch (const uno::RuntimeException &)
|
|
{
|
|
throw;
|
|
}
|
|
catch (const uno::Exception &)
|
|
{
|
|
TOOLS_INFO_EXCEPTION("vcl", "ImplImageTree::zip file location " << rIconSet.maURL);
|
|
return false;
|
|
}
|
|
return rNameAccess.is();
|
|
}
|
|
|
|
uno::Reference<container::XNameAccess> const & ImplImageTree::getNameAccess()
|
|
{
|
|
(void)checkPathAccess();
|
|
return getCurrentIconSet().maNameAccess;
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|