forked from amazingfate/loongoffice
The xmloff/draw test series was crashing soon after "testTextBoxLoss" finished. Even though it finished successfully, CppUnitTest was crashing when popping the protector. From the backtrace, the crash resulted from attempts to access null pointers. I do not know what caused those pointers to be null, but I added the checks so that it could at least proceed to the other tests. Change-Id: Ia0014a273142442a791a43fb9e3f75c2452bbd45 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/143332 Tested-by: Jenkins Reviewed-by: Noel Grandin <noel.grandin@collabora.co.uk>
717 lines
21 KiB
C++
717 lines
21 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 getIconCacheUrl(std::u16string_view sVariant, ImageRequestParameters const & rParameters)
|
|
{
|
|
// the macro expansion can be expensive in bulk, so cache that
|
|
static OUString CACHE_DIR = []()
|
|
{
|
|
OUString sDir = "${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("bootstrap") ":UserInstallation}/cache/";
|
|
rtl::Bootstrap::expandMacros(sDir);
|
|
return sDir;
|
|
}();
|
|
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
|
|
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 const OUStringLiteral aLinkFilename(u"links.txt");
|
|
|
|
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: */
|