Imported upstream release 0.3.1.

ubuntu/cosmic
Alf Gaida 7 years ago
parent ebc08b2f12
commit 6abff23b83

8
.gitignore vendored

@ -1,8 +0,0 @@
build*
*.qm
*~
*.autosave
*-swp
*.swp
CMakeLists.txt.user*
nbproject/

@ -4,7 +4,7 @@ Upstream Authors:
Copyright:
Copyright (c) 2010-2012 Razor team
Copyright (c) 2012-2016 LXQt team
Copyright (c) 2012-2017 LXQt team
License: LGPL-2.1+ and LGPL-2.1-or-3-with-Digia-1.1-exception
The full text of the LGPL-2.1+ license can be found in the 'COPYING' file.

@ -1,7 +1,82 @@
libqtxdg-2.0.0 / 2016-09-17
libqtxdg-3.1.0 / 2017-10-21
===========================
* Bump version to 3.1.0
* xdgdesktopfile: Add API for getting actions info
* xdgdesktopfile: Add support for activating actions
* xdgdesktopfile: Add getting actions
* Check $XDG_CONFIG_HOME and $XDG_CONFIG_DIRS for mimeapps.list first.
* Fix reading and writing mimeapps.list file.
* Don't export github templates
3.0.0 / 2017-09-22
==================
* Release 3.0.0: Update changelog
* Backport support for Scale directory key according to Icon Theme spec
* Bump Major to 3
* test: Drop Q_FOREACH
* Drop Q_FOREACH
* liblxqt make no sense here
* Copied issue template
* Drops Qt5Core_VERSION_STRING
* Avoid Qt special keywords collision
* XdgDesktopFile: Stops allocating unneeded QMap::keys()
* XdgDesktopFile: Stop allocating unneeded QHash:values()
* XdgDesktopFile: Improve const-ness
* xdgiconloader: Reworks the unthemed/pixmap search
* xdgiconloader: Puts the hicolor at the end of the theme hierarchy
* XdgIcon: Add flag for "FollowsColorScheme" processing
* xdgiconloader: Honor "FolowsColorScheme" theme hint
* xdgiconloader: Support symbolic SVG icons
* More fixes (#131)
* xdgiconloader: Correct hierarchy of fallbacks (#116)
* xdgiconloader: Fix XdgIconLoaderEngine::actualSize() (#130)
* Update CMakeLists.txt
* It adds loadIcon() timing measurements.
* xdgiconloader: Consider all existing files/images
* Check QTXDGX_ICONENGINEPLUGIN_INSTALL_PATH existence
* Mark QTXDGX_ICONENGINEPLUGIN_INSTALL_PATH as advanced
* xdgiconloader: Implement QIconEnginePlugin interface
* Disables uninstall target
* Remove last uses of Java-style (non-mutable) iterators from QtBase
* Adds a development qtxdg-iconfinder utility tool
* Enable strict iterators for debug builds
* Removes extra semi-colons
* Improve build warnings
* Bump year
* QtGui: eradicate Q_FOREACH loops [already const]
* Optimize QIconLoader::findIconHelper()
* Remove unused variable in QIconLoader::findIconHelper()
* Improve use of QHash to minimize double hashing
* QIconLoaderEngine: add missing Q_DECL_OVERRIDEs
* Replace QLatin1Literal with QLatin1String
* QIconCacheGtkReader: use QStringRef more
* Gui: use const (and const APIs) more
* Adds Link Time Optimization
* Replaces CMAKE_SOURCE_DIR by PROJECT_SOURCE_DIR
* Refactors superbuild support
* Remove duplicate use of source header files
* Use AUTOMOC everywhere
* Stop using include_directories()
* Removes test project definition
* Use CMAKE_INCLUDE_CURRENT_DIR
* Adds PROJECT_NAME to the build Qt version message
* Simplify target_compile_definitions() and target_include_directories()
* qiconloader: Reuse Qt implementation
* XdgIconLoader: Fix FTBFS in super-build/in-tree builds
* Allow xdg-user-dirs in the realpath of $HOME. On some systems /home is a symlink and $HOME points to the symlink. This commit allows the xdg-user-dirs to start with the real/canonical path.
* Updates version requirements in pkg-config (.pc) stuff
* Make Qt5Xdg use only the same version Qt5XdgIconLoader
* Adds minimum Qt version requirement (5.4.2)
* test: Fixes false positive in tst_xdgdesktopfile::testReadLocalized()
* Remove cpack (#106)
2.0.0 / 2016-09-17
==================
* Release 2.0.0: Add changelog
* Bump version to 2.0.0
* Extend README.md
* Updates dependencies

@ -25,11 +25,13 @@ else()
set(CMAKE_CXX_STANDARD 11)
endif()
set(QTXDG_MAJOR_VERSION 2)
set(QTXDG_MINOR_VERSION 0)
set(QTXDG_MAJOR_VERSION 3)
set(QTXDG_MINOR_VERSION 1)
set(QTXDG_PATCH_VERSION 0)
set(QTXDG_VERSION_STRING ${QTXDG_MAJOR_VERSION}.${QTXDG_MINOR_VERSION}.${QTXDG_PATCH_VERSION})
set(QT_MINIMUM_VERSION "5.6.1")
include(GNUInstallDirs) # Standard directories for installation
include(CMakePackageConfigHelpers)
include(GenerateExportHeader)
@ -37,13 +39,12 @@ include(create_portable_headers)
include(create_pkgconfig_file)
include(compiler_settings NO_POLICY_SCOPE)
find_package(Qt5Widgets REQUIRED QUIET)
find_package(Qt5Svg REQUIRED QUIET)
find_package(Qt5Xml REQUIRED QUIET)
find_package(Qt5DBus REQUIRED QUIET)
set(CMAKE_AUTOMOC ON)
find_package(Qt5 ${QT_MINIMUM_VERSION} CONFIG REQUIRED Widgets Svg Xml DBus)
if (BUILD_TESTS)
find_package(Qt5Test REQUIRED QUIET)
find_package(Qt5 ${QT_MINIMUM_VERSION} CONFIG REQUIRED Test)
endif()
@ -52,12 +53,13 @@ set(QTXDGX_FILE_NAME "qt5xdg")
set(QTXDGX_ICONLOADER_LIBRARY_NAME "Qt5XdgIconLoader")
set(QTXDGX_ICONLOADER_FILE_NAME "qt5xdgiconloader")
set(QTXDGX_ICONENGINEPLUGIN_LIBRARY_NAME "Qt5XdgIconPlugin")
set(QTXDGX_PKG_CONFIG_DESCRIPTION "Qt5Xdg, a Qt5 implementation of XDG standards")
set(QTXDGX_PKG_CONFIG_REQUIRES "Qt5Core, Qt5Xml, Qt5Widgets, Qt5DBus, Qt5XdgIconLoader")
set(QTXDGX_PKG_CONFIG_REQUIRES "Qt5Core >= ${QT_MINIMUM_VERSION}, Qt5Xml >= ${QT_MINIMUM_VERSION}, Qt5Widgets >= ${QT_MINIMUM_VERSION}, Qt5DBus >= ${QT_MINIMUM_VERSION}, Qt5XdgIconLoader = ${QTXDG_VERSION_STRING}")
set(QTXDGX_ICONLOADER_PKG_CONFIG_DESCRIPTION "Qt5XdgIconLader, a Qt5 XDG Icon Loader")
set(QTXDGX_ICONLOADER_PKG_CONFIG_REQUIRES "Qt5Gui, Qt5Svg")
set(QTXDGX_ICONLOADER_PKG_CONFIG_REQUIRES "Qt5Gui >= ${QT_MINIMUM_VERSION}, Qt5Svg >= ${QT_MINIMUM_VERSION}")
set(QTXDGX_INTREE_INCLUDEDIR "${CMAKE_CURRENT_BINARY_DIR}/InTreeBuild/include")
@ -65,7 +67,7 @@ if (NOT CMAKE_BUILD_TYPE)
set ( CMAKE_BUILD_TYPE Release )
endif (NOT CMAKE_BUILD_TYPE)
message(STATUS "Building with Qt ${Qt5Core_VERSION_STRING}")
message(STATUS "Building ${PROJECT_NAME} with Qt ${Qt5Core_VERSION}")
add_subdirectory(xdgiconloader)
add_subdirectory(qtxdg)
@ -167,16 +169,5 @@ configure_file(
"${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake"
IMMEDIATE @ONLY)
add_custom_target(uninstall
COMMAND ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake")
# building tarball with CPack -------------------------------------------------
include (InstallRequiredSystemLibraries)
set (CPACK_PACKAGE_VERSION_MAJOR ${QTXDG_MAJOR_VERSION})
set (CPACK_PACKAGE_VERSION_MINOR ${QTXDG_MINOR_VERSION})
set (CPACK_PACKAGE_VERSION_PATCH ${QTXDG_PATCH_VERSION})
set (CPACK_GENERATOR TBZ2)
set (CPACK_SOURCE_GENERATOR TBZ2)
set (CPACK_SOURCE_IGNORE_FILES /build/;.gitignore;.*~;.git;.kdev4;temp)
include (CPack)
#add_custom_target(uninstall
# COMMAND ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake")

@ -1,5 +1,6 @@
#=============================================================================
# Copyright 2015 Luís Pereira <luis.artur.pereira@gmail.com>
# Copyright 2013 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
@ -65,6 +66,14 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
endif()
#-----------------------------------------------------------------------------
# Global definitions
#-----------------------------------------------------------------------------
if (CMAKE_BUILD_TYPE MATCHES "Debug")
add_definitions(-DQT_STRICT_ITERATORS)
endif()
#-----------------------------------------------------------------------------
# Set visibility to hidden to hide symbols, unless they're exported manually
# in the code
@ -80,18 +89,25 @@ if (CMAKE_COMPILER_IS_GNUCXX OR QTXDG_COMPILER_IS_CLANGCXX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions")
endif()
#-----------------------------------------------------------------------------
# Common warning flags
#-----------------------------------------------------------------------------
set(QTXDG_COMMON_WARNING_FLAGS "-Wall")
set(QTXDG_COMMON_WARNING_FLAGS "-Wall -Wextra -Wchar-subscripts -Wno-long-long -Wpointer-arith -Wundef -Wformat-security")
#-----------------------------------------------------------------------------
# Warning flags
#-----------------------------------------------------------------------------
if (CMAKE_COMPILER_IS_GNUCXX OR QTXDG_COMPILER_IS_CLANGCXX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${__QTXDG_COMMON_WARNING_FLAGS} -Wnon-virtual-dtor -Woverloaded-virtual -Wpedantic")
endif()
if (QTXDG_COMPILER_IS_CLANGCXX)
# qCDebug(), qCWarning, etc trigger a very verbose warning, about.... nothing. Disable it.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-gnu-zero-variadic-macro-arguments")
endif()
list(APPEND QTXDG_WARNING_FLAGS ${QTXDG_COMMON_WARNING_FLAGS})
add_definitions(${QTXDG_WARNING_FLAGS})
#-----------------------------------------------------------------------------
# String conversion flags
@ -103,3 +119,53 @@ add_definitions(
-DQT_NO_URL_CAST_FROM_STRING
-DQT_NO_CAST_FROM_BYTEARRAY
)
#-----------------------------------------------------------------------------
# Linker flags
# Do not allow undefined symbols
#-----------------------------------------------------------------------------
if (CMAKE_COMPILER_IS_GNUCXX OR QTXDG_COMPILER_IS_CLANGCXX)
# -Bsymbolic-functions: replace dynamic symbols used internally in
# shared libs with direct addresses.
set(SYMBOLIC_FLAGS
"-Wl,-Bsymbolic-functions -Wl,-Bsymbolic"
)
set(CMAKE_SHARED_LINKER_FLAGS
"-Wl,--no-undefined ${SYMBOLIC_FLAGS} ${CMAKE_SHARED_LINKER_FLAGS}"
)
set(CMAKE_MODULE_LINKER_FLAGS
"-Wl,--no-undefined ${SYMBOLIC_FLAGS} ${CMAKE_MODULE_LINKER_FLAGS}"
)
set(CMAKE_EXE_LINKER_FLAGS
"${SYMBOLIC_FLAGS} ${CMAKE_EXE_LINKER_FLAGS}"
)
endif()
#-----------------------------------------------------------------------------
# Turn on more aggrassive optimizations not supported by CMake
# References: https://wiki.qt.io/Performance_Tip_Startup_Time
#-----------------------------------------------------------------------------
if (CMAKE_COMPILER_IS_GNUCXX OR QTXDG_COMPILER_IS_CLANGCXX)
# -flto: use link-time optimizations to generate more efficient code
if (CMAKE_COMPILER_IS_GNUCXX)
set(LTO_FLAGS "-flto -fuse-linker-plugin")
# When building static libraries with LTO in gcc >= 4.9,
# "gcc-ar" and "gcc-ranlib" should be used instead of "ar" and "ranlib".
# references:
# https://gcc.gnu.org/gcc-4.9/changes.html
# http://hubicka.blogspot.tw/2014/04/linktime-optimization-in-gcc-2-firefox.html
# https://github.com/monero-project/monero/pull/1065/commits/1855213c8fb8f8727f4107716aab8e7ba826462b
if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS "4.9.0") # gcc >= 4.9
set(CMAKE_AR "gcc-ar")
set(CMAKE_RANLIB "gcc-ranlib")
endif()
elseif (QTXDG_COMPILER_IS_CLANGCXX)
# The link-time optimization of clang++/llvm seems to be too aggrassive.
# After testing, it breaks the signal/slots of QObject sometimes.
# So disable it for now until there is a solution.
# set(LTO_FLAGS "-flto")
endif()
# apply these options to "Release" build type only
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${LTO_FLAGS}")
endif()

@ -1,13 +1,15 @@
@PACKAGE_INIT@
if (NOT TARGET @QTXDGX_LIBRARY_NAME@)
include(CMakeFindDependencyMacro)
find_dependency(Qt5Widgets)
find_dependency(Qt5Xml)
find_dependency(Qt5DBus)
find_dependency(Qt5XdgIconLoader)
find_dependency(Qt5Widgets @QT_MINIMUM_VERSION@)
find_dependency(Qt5Xml @QT_MINIMUM_VERSION@)
find_dependency(Qt5DBus @QT_MINIMUM_VERSION@)
find_dependency(Qt5XdgIconLoader @QTXDG_VERSION_STRING@ EXACT)
if (CMAKE_VERSION VERSION_GREATER 2.8.12)
cmake_policy(SET CMP0024 OLD)
cmake_policy(SET CMP0024 NEW)
endif()
include("${CMAKE_CURRENT_LIST_DIR}/qt5xdg-targets.cmake")
endif()

@ -1,11 +1,13 @@
@PACKAGE_INIT@
if (NOT TARGET @QTXDGX_ICONLOADER_LIBRARY_NAME@)
include(CMakeFindDependencyMacro)
find_dependency(Qt5Gui)
find_dependency(Qt5Svg)
find_dependency(Qt5Gui @QT_MINIMUM_REQUIRED@)
find_dependency(Qt5Svg @QT_MINIMUM_REQUIRED@)
if (CMAKE_VERSION VERSION_GREATER 2.8.12)
cmake_policy(SET CMP0024 OLD)
cmake_policy(SET CMP0024 NEW)
endif()
include("${CMAKE_CURRENT_LIST_DIR}/qt5xdgiconloader-targets.cmake")
endif()

@ -1,8 +1,5 @@
set(QTX_LIBRARIES Qt5::Widgets Qt5::Xml Qt5::DBus)
include_directories(
"${Qt5Gui_PRIVATE_INCLUDE_DIRS}"
)
set(libqtxdg_PUBLIC_H_FILES
xdgaction.h
xdgdesktopfile.h
@ -63,14 +60,11 @@ set(libqtxdg_MOCS
xdgmenuwidget.h
)
QT5_WRAP_CPP(libqtxdg_CXX_FILES ${libqtxdg_MOCS})
add_library(${QTXDGX_LIBRARY_NAME} SHARED
${libqtxdg_PUBLIC_H_FILES}
${libqtxdg_PRIVATE_H_FILES}
${libqtxdg_PRIVATE_H_FILES}
${libqtxdg_CPP_FILES}
${libqtxdg_CXX_FILES}
${libqtxdg_MOCS}
)
target_link_libraries(${QTXDGX_LIBRARY_NAME}
@ -85,24 +79,22 @@ set_target_properties(${QTXDGX_LIBRARY_NAME} PROPERTIES
)
target_compile_definitions(${QTXDGX_LIBRARY_NAME}
PRIVATE "QTXDG_COMPILATION=\"1\""
PRIVATE "QTXDG_VERSION=\"${QTXDG_VERSION_STRING}\""
PRIVATE
"QTXDG_COMPILATION=\"1\""
"QTXDG_VERSION=\"${QTXDG_VERSION_STRING}\""
"QT_NO_KEYWORDS"
)
target_include_directories(${QTXDGX_LIBRARY_NAME}
INTERFACE "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/${QTXDGX_FILE_NAME}>"
INTERFACE "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
INTERFACE
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/${QTXDGX_FILE_NAME}>"
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
"$<BUILD_INTERFACE:${QTXDGX_INTREE_INCLUDEDIR}/${QTXDGX_FILE_NAME}>"
"$<BUILD_INTERFACE:${QTXDGX_INTREE_INCLUDEDIR}>"
PRIVATE
${Qt5Gui_PRIVATE_INCLUDE_DIRS}
)
# include directories and targets for the in tree build
target_include_directories(${QTXDGX_LIBRARY_NAME}
INTERFACE "$<BUILD_INTERFACE:${QTXDGX_INTREE_INCLUDEDIR}/${QTXDGX_FILE_NAME}>"
INTERFACE "$<BUILD_INTERFACE:${QTXDGX_INTREE_INCLUDEDIR}>"
)
export(TARGETS ${QTXDGX_LIBRARY_NAME} APPEND FILE "${CMAKE_BINARY_DIR}/${QTXDGX_FILE_NAME}-targets.cmake")
# end of in tree build stuff
# create the portble headers
create_portable_headers(libqtxdg_PORTABLE_HEADERS
HEADER_NAMES ${libqtxdg_PUBLIC_CLASSES}

@ -71,7 +71,7 @@ public:
const XdgDesktopFile& desktopFile() const { return mDesktopFile; }
private slots:
private Q_SLOTS:
void runConmmand() const;
void updateIcon();

@ -57,14 +57,14 @@ XdgDesktopFileList XdgAutoStart::desktopFileList(QStringList dirs, bool excludeH
QSet<QString> processed;
XdgDesktopFileList ret;
foreach (const QString &dirName, dirs)
for (const QString &dirName : const_cast<const QStringList&>(dirs))
{
QDir dir(dirName);
if (!dir.exists())
continue;
const QFileInfoList files = dir.entryInfoList(QStringList(QLatin1String("*.desktop")), QDir::Files | QDir::Readable);
foreach (const QFileInfo &fi, files)
for (const QFileInfo &fi : files)
{
if (processed.contains(fi.fileName()))
continue;

@ -66,6 +66,7 @@ static const QStringList nonDetachExecs = QStringList()
static const QLatin1String onlyShowInKey("OnlyShowIn");
static const QLatin1String notShowInKey("NotShowIn");
static const QLatin1String categoriesKey("Categories");
static const QLatin1String actionsKey("Actions");
static const QLatin1String extendPrefixKey("X-");
static const QLatin1String mimeTypeKey("MimeType");
static const QLatin1String applicationsStr("applications");
@ -274,14 +275,37 @@ QString &unEscapeExec(QString& str)
return doUnEscape(str, repl);
}
namespace
{
/*!
* Helper class for getting the keys for "Additional applications actions"
* ([Desktop Action %s] sections)
*/
class XdgDesktopAction : public XdgDesktopFile
{
public:
XdgDesktopAction(const XdgDesktopFile & parent, const QString & action)
: XdgDesktopFile(parent)
, m_prefix(QString{QLatin1String("Desktop Action %1")}.arg(action))
{}
protected:
virtual QString prefix() const { return m_prefix; }
private:
const QString m_prefix;
};
}
class XdgDesktopFileData: public QSharedData {
public:
XdgDesktopFileData();
bool read(const QString &prefix);
XdgDesktopFile::Type detectType(XdgDesktopFile *q) const;
bool startApplicationDetached(const XdgDesktopFile *q, const QStringList& urls) const;
bool startApplicationDetached(const XdgDesktopFile *q, const QString & action, const QStringList& urls) const;
bool startLinkDetached(const XdgDesktopFile *q) const;
bool startByDBus(const QStringList& urls) const;
bool startByDBus(const QString & action, const QStringList& urls) const;
QStringList getListValue(const XdgDesktopFile * q, const QString & key, bool tryExtendPrefix) const;
QString mFileName;
bool mIsValid;
@ -365,7 +389,7 @@ XdgDesktopFile::Type XdgDesktopFileData::detectType(XdgDesktopFile *q) const
return XdgDesktopFile::UnknownType;
}
bool XdgDesktopFileData::startApplicationDetached(const XdgDesktopFile *q, const QStringList& urls) const
bool XdgDesktopFileData::startApplicationDetached(const XdgDesktopFile *q, const QString & action, const QStringList& urls) const
{
//DBusActivatable handling
if (q->value(QLatin1String("DBusActivatable"), false).toBool()) {
@ -390,10 +414,12 @@ bool XdgDesktopFileData::startApplicationDetached(const XdgDesktopFile *q, const
* We consider that this violation is more acceptable than an failure
* in launching an application.
*/
if (startByDBus(urls))
if (startByDBus(action, urls))
return true;
}
QStringList args = q->expandExecString(urls);
QStringList args = action.isEmpty()
? q->expandExecString(urls)
: XdgDesktopAction{*q, action}.expandExecString(urls);
if (args.isEmpty())
return false;
@ -409,9 +435,9 @@ bool XdgDesktopFileData::startApplicationDetached(const XdgDesktopFile *q, const
}
bool nonDetach = false;
foreach(const QString &s, nonDetachExecs)
for (const QString &s : nonDetachExecs)
{
foreach(const QString &a, args)
for (const QString &a : const_cast<const QStringList&>(args))
{
if (a.contains(s))
{
@ -481,8 +507,7 @@ bool XdgDesktopFileData::startLinkDetached(const XdgDesktopFile *q) const
return false;
}
// TODO: Handle ActivateAction
bool XdgDesktopFileData::startByDBus(const QStringList& urls) const
bool XdgDesktopFileData::startByDBus(const QString & action, const QStringList& urls) const
{
QFileInfo f(mFileName);
QString path(f.completeBaseName());
@ -509,7 +534,13 @@ bool XdgDesktopFileData::startByDBus(const QStringList& urls) const
<< ", but trying to continue...";
}
QDBusMessage reply;
if (urls.isEmpty())
if (!action.isEmpty())
{
QList<QVariant> v_urls;
for (const auto & url : urls)
v_urls.append(url);
reply = app.call(QLatin1String("ActivateAction"), action, v_urls, platformData);
} else if (urls.isEmpty())
reply = app.call(QLatin1String("Activate"), platformData);
else
reply = app.call(QLatin1String("Open"), urls, platformData);
@ -517,6 +548,19 @@ bool XdgDesktopFileData::startByDBus(const QStringList& urls) const
return QDBusMessage::ErrorMessage != reply.type();
}
QStringList XdgDesktopFileData::getListValue(const XdgDesktopFile * q, const QString & key, bool tryExtendPrefix) const
{
QString used_key = key;
if (!q->contains(used_key) && tryExtendPrefix)
{
used_key = extendPrefixKey + key;
if (!q->contains(used_key))
return QStringList();
}
return q->value(key).toString().split(QLatin1Char(';'), QString::SkipEmptyParts);
}
XdgDesktopFile::XdgDesktopFile():
d(new XdgDesktopFileData)
@ -750,23 +794,14 @@ QVariant XdgDesktopFile::localizedValue(const QString& key, const QVariant& defa
QStringList XdgDesktopFile::categories() const
{
QString key;
if (contains(categoriesKey))
{
key = categoriesKey;
}
else
{
key = extendPrefixKey + categoriesKey;
if (!contains(key))
return QStringList();
return d->getListValue(this, categoriesKey, true);
}
QStringList cats = value(key).toString().split(QLatin1Char(';'));
return cats;
QStringList XdgDesktopFile::actions() const
{
return d->getListValue(this, actionsKey, false);
}
void XdgDesktopFile::removeEntry(const QString& key)
{
QString path = (!prefix().isEmpty()) ? prefix() + QLatin1Char('/') + key : key;
@ -806,18 +841,41 @@ QIcon const XdgDesktopFile::icon(const QIcon& fallback) const
}
QIcon const XdgDesktopFile::actionIcon(const QString & action, const QIcon& fallback) const
{
return d->mType == ApplicationType
? XdgDesktopAction{*this, action}.icon(icon(fallback))
: fallback;
}
QString const XdgDesktopFile::iconName() const
{
return value(iconKey).toString();
}
QString const XdgDesktopFile::actionIconName(const QString & action) const
{
return d->mType == ApplicationType
? XdgDesktopAction{*this, action}.iconName()
: QString{};
}
QStringList XdgDesktopFile::mimeTypes() const
{
return value(mimeTypeKey).toString().split(QLatin1Char(';'), QString::SkipEmptyParts);
}
QString XdgDesktopFile::actionName(const QString & action) const
{
return d->mType == ApplicationType
? XdgDesktopAction{*this, action}.name()
: QString{};
}
XdgDesktopFile::Type XdgDesktopFile::type() const
{
return d->mType;
@ -839,7 +897,7 @@ bool XdgDesktopFile::startDetached(const QStringList& urls) const
switch(d->mType)
{
case ApplicationType:
return d->startApplicationDetached(this, urls);
return d->startApplicationDetached(this, QString{}, urls);
break;
case LinkType:
@ -851,6 +909,11 @@ bool XdgDesktopFile::startDetached(const QStringList& urls) const
}
}
bool XdgDesktopFile::actionActivate(const QString & action, const QStringList& urls) const
{
return d->mType == ApplicationType ? d->startApplicationDetached(this, action, urls) : false;
}
/************************************************
This is an overloaded function.
@ -953,7 +1016,7 @@ QString expandEnvVariables(const QString str)
QStringList expandEnvVariables(const QStringList strs)
{
QStringList res;
foreach(const QString &s, strs)
for (const QString &s : strs)
res << expandEnvVariables(s);
return res;
@ -969,9 +1032,9 @@ QStringList XdgDesktopFile::expandExecString(const QStringList& urls) const
QString execStr = value(execKey).toString();
unEscapeExec(execStr);
QStringList tokens = parseCombinedArgString(execStr);
const QStringList tokens = parseCombinedArgString(execStr);
foreach (QString token, tokens)
for (QString token : tokens)
{
// The parseCombinedArgString() splits the string by the space symbols,
// we temporarily replaced them on the special characters.
@ -1016,7 +1079,7 @@ QStringList XdgDesktopFile::expandExecString(const QStringList& urls) const
// program. Local files may either be passed as file: URLs or as file path.
if (token == QLatin1String("%U"))
{
foreach (const QString &s, urls)
for (const QString &s : urls)
{
QUrl url(expandEnvVariables(s));
result << ((!url.toLocalFile().isEmpty()) ? url.toLocalFile() : QString::fromUtf8(url.toEncoded()));
@ -1079,9 +1142,9 @@ bool checkTryExec(const QString& progName)
if (progName.startsWith(QDir::separator()))
return QFileInfo(progName).isExecutable();
QStringList dirs = QFile::decodeName(qgetenv("PATH")).split(QLatin1Char(':'));
const QStringList dirs = QFile::decodeName(qgetenv("PATH")).split(QLatin1Char(':'));
foreach (const QString &dir, dirs)
for (const QString &dir : dirs)
{
if (QFileInfo(QDir(dir), progName).isExecutable())
return true;
@ -1103,7 +1166,7 @@ QString XdgDesktopFile::id(const QString &fileName, bool checkFileExists)
QString id = f.absoluteFilePath();
const QStringList dataDirs = XdgDirs::dataDirs();
foreach(const QString &d, dataDirs) {
for (const QString &d : dataDirs) {
if (id.startsWith(d)) {
// remove only the first occurence
id.replace(id.indexOf(d), d.size(), QString());
@ -1211,7 +1274,8 @@ bool XdgDesktopFile::isSuitable(bool excludeHidden, const QString &environment)
QString expandDynamicUrl(QString url)
{
foreach(const QString &line, QProcess::systemEnvironment())
const QStringList env = QProcess::systemEnvironment();
for (const QString &line : env)
{
QString name = line.section(QLatin1Char('='), 0, 0);
QString val = line.section(QLatin1Char('='), 1);
@ -1252,8 +1316,8 @@ QString findDesktopFile(const QString& dirName, const QString& desktopName)
return fi.canonicalFilePath();
// Working recursively ............
QFileInfoList dirs = dir.entryInfoList(QStringList(), QDir::Dirs | QDir::NoDotAndDotDot);
foreach (const QFileInfo &d, dirs)
const QFileInfoList dirs = dir.entryInfoList(QStringList(), QDir::Dirs | QDir::NoDotAndDotDot);
for (const QFileInfo &d : dirs)
{
QString cn = d.canonicalFilePath();
if (dirName != cn)
@ -1273,7 +1337,7 @@ QString findDesktopFile(const QString& desktopName)
QStringList dataDirs = XdgDirs::dataDirs();
dataDirs.prepend(XdgDirs::dataHome(false));
foreach (const QString &dirName, dataDirs)
for (const QString &dirName : const_cast<const QStringList&>(dataDirs))
{
QString f = findDesktopFile(dirName + QLatin1String("/applications"), desktopName);
if (!f.isEmpty())
@ -1395,7 +1459,7 @@ bool readDesktopFile(QIODevice & device, QSettings::SettingsMap & map)
if (value.contains(QLatin1Char(';')))
{
map.insert(key, value.split(QLatin1Char(';')));
map.insert(key, value.split(QLatin1Char(';'), QString::SkipEmptyParts));
}
else
{
@ -1415,14 +1479,17 @@ bool writeDesktopFile(QIODevice & device, const QSettings::SettingsMap & map)
QTextStream stream(&device);
QString section;
foreach (const QString &key, map.keys())
for (auto it = map.constBegin(); it != map.constEnd(); ++it)
{
if (! map.value(key).canConvert<QString>())
bool isString = it.value().canConvert<QString>();
bool isStringList = (it.value().type() == QVariant::StringList);
if ((! isString) && (! isStringList))
{
return false;
}
QString thisSection = key.section(QLatin1Char('/'), 0, 0);
QString thisSection = it.key().section(QLatin1Char('/'), 0, 0);
if (thisSection.isEmpty())
{
qWarning() << "No section defined";
@ -1435,7 +1502,7 @@ bool writeDesktopFile(QIODevice & device, const QSettings::SettingsMap & map)
section = thisSection;
}
QString remainingKey = key.section(QLatin1Char('/'), 1, -1);
QString remainingKey = it.key().section(QLatin1Char('/'), 1, -1);
if (remainingKey.isEmpty())
{
@ -1443,7 +1510,21 @@ bool writeDesktopFile(QIODevice & device, const QSettings::SettingsMap & map)
return false;
}
stream << remainingKey << QLatin1Char('=') << map.value(key).toString() << QLatin1Char('\n');
stream << remainingKey << QLatin1Char('=');
if (isString)
{
stream << it.value().toString() << QLatin1Char(';');
}
else /* if (isStringList) */
{
for (const QString &value: it.value().toStringList())
{
stream << value << QLatin1Char(';');
}
}
stream << QLatin1Char('\n');
}
@ -1461,8 +1542,8 @@ void XdgDesktopFileCache::initialize(const QString& dirName)
// Working recursively ............
QFileInfoList files = dir.entryInfoList(QStringList(), QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
foreach (const QFileInfo &f, files)
const QFileInfoList files = dir.entryInfoList(QStringList(), QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
for (const QFileInfo &f : files)
{
if (f.isDir())
{
@ -1480,9 +1561,9 @@ void XdgDesktopFileCache::initialize(const QString& dirName)
m_fileCache.insert(f.absoluteFilePath(), df);
}
QStringList mimes = df->value(mimeTypeKey).toString().split(QLatin1Char(';'), QString::SkipEmptyParts);
const QStringList mimes = df->value(mimeTypeKey).toString().split(QLatin1Char(';'), QString::SkipEmptyParts);
foreach (const QString &mime, mimes)
for (const QString &mime : mimes)
{
int pref = df->value(initialPreferenceKey, 0).toInt();
// We move the desktopFile forward in the list for this mime, so that
@ -1522,8 +1603,8 @@ void loadMimeCacheDir(const QString& dirName, QHash<QString, QList<XdgDesktopFil
// Working recursively ............
QFileInfoList files = dir.entryInfoList(QStringList(), QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
foreach (const QFileInfo &f, files)
const QFileInfoList files = dir.entryInfoList(QStringList(), QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
for (const QFileInfo &f : files)
{
if (f.isDir())
{
@ -1536,9 +1617,9 @@ void loadMimeCacheDir(const QString& dirName, QHash<QString, QList<XdgDesktopFil
if (!df)
continue;
QStringList mimes = df->value(mimeTypeKey).toString().split(QLatin1Char(';'), QString::SkipEmptyParts);
const QStringList mimes = df->value(mimeTypeKey).toString().split(QLatin1Char(';'), QString::SkipEmptyParts);
foreach (const QString &mime, mimes)
for (const QString &mime : mimes)
{
int pref = df->value(initialPreferenceKey, 0).toInt();
// We move the desktopFile forward in the list for this mime, so that
@ -1582,7 +1663,7 @@ void XdgDesktopFileCache::initialize()
QStringList dataDirs = XdgDirs::dataDirs();
dataDirs.prepend(XdgDirs::dataHome(false));
foreach (const QString &dirname, dataDirs)
for (const QString &dirname : const_cast<const QStringList&>(dataDirs))
{
initialize(dirname + QLatin1String("/applications"));
// loadMimeCacheDir(dirname + "/applications", m_defaultAppsCache);
@ -1593,7 +1674,8 @@ QList<XdgDesktopFile*> XdgDesktopFileCache::getAppsOfCategory(const QString& cat
{
QList<XdgDesktopFile*> list;
const QString _category = category.toUpper();
foreach (XdgDesktopFile *desktopFile, instance().m_fileCache.values())
const QHash<QString, XdgDesktopFile*> fileCache = instance().m_fileCache;
for (XdgDesktopFile *desktopFile : fileCache)
{
QStringList categories = desktopFile->value(categoriesKey).toString().toUpper().split(QLatin1Char(';'));
if (!categories.isEmpty() && (categories.contains(_category) || categories.contains(QLatin1String("X-") + _category)))
@ -1610,14 +1692,23 @@ QList<XdgDesktopFile*> XdgDesktopFileCache::getApps(const QString& mimetype)
XdgDesktopFile* XdgDesktopFileCache::getDefaultApp(const QString& mimetype)
{
// First, we look in ~/.local/share/applications/mimeapps.list, /usr/local/share/applications/mimeapps.list and
// /usr/share/applications/mimeapps.list (in that order) for a default.
QStringList dataDirs = XdgDirs::dataDirs();
dataDirs.prepend(XdgDirs::dataHome(false));
foreach(const QString &dataDir, dataDirs)
// First, we look in following places for a default in specified order:
// ~/.config/mimeapps.list
// /etc/xdg/mimeapps.list
// ~/.local/share/applications/mimeapps.list
// /usr/local/share/applications/mimeapps.list
// /usr/share/applications/mimeapps.list
QStringList mimeDirsList;
mimeDirsList.append(XdgDirs::configHome(false));
mimeDirsList.append(XdgDirs::configDirs());
mimeDirsList.append(XdgDirs::dataHome(false) + QLatin1String("/applications"));
mimeDirsList.append(XdgDirs::dataDirs(QLatin1String("/applications")));
for (const QString &mimeDir : const_cast<const QStringList&>(mimeDirsList))
{
QString defaultsListPath = dataDir + QLatin1String("/applications/mimeapps.list");
if (QFileInfo(defaultsListPath).exists())
QString defaultsListPath = mimeDir + QLatin1String("/mimeapps.list");
if (QFileInfo::exists(defaultsListPath))
{
QSettings defaults(defaultsListPath, desktopFileSettingsFormat());
@ -1628,7 +1719,8 @@ XdgDesktopFile* XdgDesktopFileCache::getDefaultApp(const QString& mimetype)
QVariant value = defaults.value(mimetype);
if (value.canConvert<QStringList>()) // A single string can also convert to a stringlist
{
foreach (const QString &desktopFileName, value.toStringList())
const QStringList values = value.toStringList();
for (const QString &desktopFileName : values)
{
XdgDesktopFile* desktopFile = XdgDesktopFileCache::getFile(desktopFileName);
if (desktopFile)

@ -130,6 +130,9 @@ public:
//! Returns the entry Categories. It supports X-Categories extensions.
QStringList categories() const;
//! Returns list of values in entry Actions.
QStringList actions() const;
//! Returns true if there exists a setting called key; returns false otherwise.
bool contains(const QString& key) const;
@ -142,9 +145,13 @@ public:
//! Returns an icon specified in this file.
QIcon const icon(const QIcon& fallback = QIcon()) const;
//! Returns an icon for application action \param action.
QIcon const actionIcon(const QString & action, const QIcon& fallback = QIcon()) const;
//! Returns an icon name specified in this file.
QString const iconName() const;
//! Returns an icon name for application action \param action.
QString const actionIconName(const QString & action) const;
//! Returns an list of mimetypes specified in this file.
/*! @return Returns a list of the "MimeType=" entries.
@ -155,6 +162,8 @@ public:
//! This function is provided for convenience. It's equivalent to calling localizedValue("Name").toString().
QString name() const { return localizedValue(QLatin1String("Name")).toString(); }
//! Returns an (localized) name for application action \param action.
QString actionName(const QString & action) const;
//! This function is provided for convenience. It's equivalent to calling localizedValue("Comment").toString().
QString comment() const { return localizedValue(QLatin1String("Comment")).toString(); }
@ -175,6 +184,19 @@ public:
//! This function is provided for convenience. It's equivalent to calling startDetached(QStringList(url)).
bool startDetached(const QString& url = QString()) const;
/*! For file with Application type. Activates action defined by the \param action. Action is activated
* either with the [Desktop Action %s]/Exec or by the D-Bus if the [Desktop Entry]/DBusActivatable is set.
* \note Starting is done the same way as \sa startDetached()
*
* \return true on success; otherwise returns false.
* \param urls - A list of files or URLS. Each file is passed as a separate argument to the executable program.
*
* For file with Link type, do nothing.
*
* For file with Directory type, do nothing.
*/
bool actionActivate(const QString & action, const QStringList & urls) const;
/*! A Exec value consists of an executable program optionally followed by one or more arguments.
This function expands this arguments and returns command line string parts.
Note this method make sense only for Application type.

@ -108,7 +108,7 @@ QString userDirFallback(XdgDirs::UserDirectory dir)
if (home.isEmpty())
return QString::fromLatin1("/tmp");
else if (dir == XdgDirs::Desktop)
fallback = QString::fromLatin1("%1/%2").arg(home).arg(QLatin1String("Desktop"));
fallback = QString::fromLatin1("%1/%2").arg(home, QLatin1String("Desktop"));
else
fallback = home;
@ -178,9 +178,11 @@ bool XdgDirs::setUserDir(XdgDirs::UserDirectory dir, const QString& value, bool
if (dir < XdgDirs::Desktop || dir > XdgDirs::Videos)
return false;
const QString home = QFile::decodeName(qgetenv("HOME"));
if (!(value.startsWith(QLatin1String("$HOME"))
|| value.startsWith(QLatin1String("~/"))
|| value.startsWith(QFile::decodeName(qgetenv("HOME")))))
|| value.startsWith(home)
|| value.startsWith(QDir(home).canonicalPath())))
return false;
QString folderName = userDirectoryString[dir];
@ -215,7 +217,7 @@ bool XdgDirs::setUserDir(XdgDirs::UserDirectory dir, const QString& value, bool
stream.reset();
configFile.resize(0);
if (!foundVar)
stream << QString::fromLatin1("XDG_%1_DIR=\"%2\"\n").arg(folderName.toUpper()).arg(value);
stream << QString::fromLatin1("XDG_%1_DIR=\"%2\"\n").arg(folderName.toUpper(),(value));
for (QVector<QString>::iterator i = lines.begin(); i != lines.end(); ++i)
stream << *i << QLatin1Char('\n');
@ -334,7 +336,7 @@ QStringList XdgDirs::autostartDirs(const QString &postfix)
{
QStringList dirs;
const QStringList s = configDirs();
foreach(const QString &dir, s)
for (const QString &dir : s)
dirs << QString::fromLatin1("%1/autostart").arg(dir) + postfix;
return dirs;

@ -50,7 +50,7 @@ struct QtIconCache: public IconCache
}
};
}
Q_GLOBAL_STATIC(IconCache, qtIconCache);
Q_GLOBAL_STATIC(IconCache, qtIconCache)
static void qt_cleanup_icon_cache()
{
@ -68,25 +68,6 @@ XdgIcon::~XdgIcon()
}
/************************************************
Returns the name of the current icon theme.
************************************************/
QString XdgIcon::themeName()
{
return QIcon::themeName();
}
/************************************************
Sets the current icon theme to name.
************************************************/
void XdgIcon::setThemeName(const QString& themeName)
{
QIcon::setThemeName(themeName);
XdgIconLoader::instance()->updateSystemTheme();
}
/************************************************
Returns the QIcon corresponding to name in the current icon theme. If no such icon
is found in the current theme fallback is return instead.
@ -137,7 +118,7 @@ QIcon XdgIcon::fromTheme(const QString& iconName, const QIcon& fallback)
************************************************/
QIcon XdgIcon::fromTheme(const QStringList& iconNames, const QIcon& fallback)
{
foreach (const QString &iconName, iconNames)
for (const QString &iconName : iconNames)
{
QIcon icon = fromTheme(iconName);
if (!icon.isNull())
@ -164,6 +145,17 @@ QIcon XdgIcon::fromTheme(const QString &iconName,
return fromTheme(icons);
}
bool XdgIcon::followColorScheme()
{
return XdgIconLoader::instance()->followColorScheme();
}
void XdgIcon::setFollowColorScheme(bool enable)
{
XdgIconLoader::instance()->setFollowColorScheme(enable);
}
QIcon XdgIcon::defaultApplicationIcon()
{

@ -44,8 +44,19 @@ public:
const QString &fallbackIcon4 = QString());
static QIcon fromTheme(const QStringList& iconNames, const QIcon& fallback = QIcon());
static QString themeName();
static void setThemeName(const QString& themeName);
/*!
* Flag if the "FollowsColorScheme" hint (the KDE extension to XDG
* themes) should be honored. If enabled and the icon theme supports
* this, the icon engine "colorizes" icons based on the application's
* palette.
*
* Default is true (use this extension).
*/
static bool followColorScheme();
static void setFollowColorScheme(bool enable);
/* TODO: deprecate & remove all QIcon wrappers */
static QString themeName() { return QIcon::themeName(); }
static void setThemeName(const QString& themeName) { QIcon::setThemeName(themeName); }
static QIcon defaultApplicationIcon();
static QString defaultApplicationIconName();

@ -205,8 +205,7 @@ void XdgMenu::save(const QString& fileName)
if (!file.open(QFile::WriteOnly | QFile::Text))
{
qWarning() << QString::fromLatin1("Cannot write file %1:\n%2.")
.arg(fileName)
.arg(file.errorString());
.arg(fileName, file.errorString());
return;
}
@ -225,7 +224,7 @@ void XdgMenuPrivate::load(const QString& fileName)
QFile file(fileName);
if (!file.open(QFile::ReadOnly | QFile::Text))
{
qWarning() << QString::fromLatin1("%1 not loading: %2").arg(fileName).arg(file.errorString());
qWarning() << QString::fromLatin1("%1 not loading: %2").arg(fileName, file.errorString());
return;
}
mXml.setContent(&file, true);
@ -413,7 +412,7 @@ QDomElement XdgMenu::findMenu(QDomElement& baseElement, const QString& path, boo
const QStringList names = path.split(QLatin1Char('/'), QString::SkipEmptyParts);
QDomElement el = baseElement;
foreach (const QString &name, names)
for (const QString &name : names)
{
QDomElement p = el;
el = d->mXml.createElement(QLatin1String("Menu"));
@ -538,12 +537,12 @@ void XdgMenuPrivate::processDirectoryEntries(QDomElement& element, const QString
dirs << parentDirs;
bool found = false;
foreach(const QString &file, files){
for (const QString &file : const_cast<const QStringList&>(files)){
if (file.startsWith(QLatin1Char('/')))
found = loadDirectoryFile(file, element);
else
{
foreach (const QString &dir, dirs)
for (const QString &dir : const_cast<const QStringList&>(dirs))
{
found = loadDirectoryFile(dir + QLatin1Char('/') + file, element);
if (found) break;
@ -652,7 +651,7 @@ QString XdgMenu::getMenuFileName(const QString& baseName)
const QStringList configDirs = XdgDirs::configDirs();
QString menuPrefix = QString::fromLocal8Bit(qgetenv("XDG_MENU_PREFIX"));
foreach(const QString &configDir, configDirs)
for (const QString &configDir : configDirs)
{
QFileInfo file(QString::fromLatin1("%1/menus/%2%3").arg(configDir, menuPrefix, baseName));
if (file.exists())
@ -670,9 +669,9 @@ QString XdgMenu::getMenuFileName(const QString& baseName)
wellKnownFiles << QLatin1String("gnome-applications.menu");
wellKnownFiles << QLatin1String("lxde-applications.menu");
foreach(const QString &configDir, configDirs)
for (const QString &configDir : configDirs)
{
foreach (const QString &f, wellKnownFiles)
for (const QString &f : const_cast<const QStringList&>(wellKnownFiles))
{
QFileInfo file(QString::fromLatin1("%1/menus/%2").arg(configDir, f));
if (file.exists())
@ -715,7 +714,7 @@ void XdgMenuPrivate::rebuild()
if (prevHash != mHash)
{
mOutDated = true;
emit changed();
Q_EMIT changed();
}
}

@ -112,7 +112,7 @@ public:
bool isOutDated() const;
signals:
Q_SIGNALS:
void changed();
protected:

@ -73,10 +73,10 @@ public:
QFileSystemWatcher mWatcher;
bool mOutDated;
public slots:
public Q_SLOTS:
void rebuild();
signals:
Q_SIGNALS:
void changed();

@ -90,7 +90,8 @@ void XdgMenuApplinkProcessor::step1()
}
// Process childs menus ...............................
foreach (XdgMenuApplinkProcessor* child, mChilds)
for (XdgMenuApplinkProcessor* child : const_cast<const QLinkedList<XdgMenuApplinkProcessor*>&>(mChilds))
child->step1();
}
@ -100,7 +101,7 @@ void XdgMenuApplinkProcessor::step2()
// Create AppLinks elements ...........................
QDomDocument doc = mElement.ownerDocument();
foreach (XdgMenuAppFileInfo* fileInfo, mSelected)
for (XdgMenuAppFileInfo* fileInfo : const_cast<const QLinkedList<XdgMenuAppFileInfo*>&>(mSelected))
{
if (mOnlyUnallocated && fileInfo->allocated())
continue;
@ -141,7 +142,7 @@ void XdgMenuApplinkProcessor::step2()
// Process childs menus ...............................
foreach (XdgMenuApplinkProcessor* child, mChilds)
for (XdgMenuApplinkProcessor* child : const_cast<const QLinkedList<XdgMenuApplinkProcessor*>&>(mChilds))
child->step2();
}
@ -189,7 +190,7 @@ void XdgMenuApplinkProcessor::findDesktopFiles(const QString& dirName, const QSt
mMenu->addWatchPath(dir.absolutePath());
const QFileInfoList files = dir.entryInfoList(QStringList(QLatin1String("*.desktop")), QDir::Files);
foreach (const QFileInfo &file, files)
for (const QFileInfo &file : files)
{
XdgDesktopFile* f = XdgDesktopFileCache::getFile(file.canonicalFilePath());
if (f)
@ -199,7 +200,7 @@ void XdgMenuApplinkProcessor::findDesktopFiles(const QString& dirName, const QSt
// Working recursively ............
const QFileInfoList dirs = dir.entryInfoList(QStringList(), QDir::Dirs | QDir::NoDotAndDotDot);
foreach (const QFileInfo &d, dirs)
for (const QFileInfo &d : dirs)
{
QString dn = d.canonicalFilePath();
if (dn != dirName)
@ -242,7 +243,7 @@ bool XdgMenuApplinkProcessor::checkTryExec(const QString& progName)
const QStringList dirs = QFile::decodeName(qgetenv("PATH")).split(QLatin1Char(':'));
foreach (const QString &dir, dirs)
for (const QString &dir : dirs)
{
if (QFileInfo(QDir(dir), progName).isExecutable())
return true;

@ -76,7 +76,7 @@ bool XdgMenuReader::load(const QString& fileName, const QString& baseDir)
QFile file(mFileName);
if (!file.open(QFile::ReadOnly | QFile::Text))
{
mErrorStr = QString::fromLatin1("%1 not loading: %2").arg(fileName).arg(file.errorString());
mErrorStr = QString::fromLatin1("%1 not loading: %2").arg(fileName, file.errorString());
return false;
}
//qDebug() << "Load file:" << mFileName;
@ -215,7 +215,7 @@ void XdgMenuReader::processMergeFileTag(QDomElement& element, QStringList* merge
QString relativeName;
QStringList configDirs = XdgDirs::configDirs();
foreach (const QString &configDir, configDirs)
for (const QString &configDir : const_cast<const QStringList&>(configDirs))
{
if (mFileName.startsWith(configDir))
{
@ -236,9 +236,9 @@ void XdgMenuReader::processMergeFileTag(QDomElement& element, QStringList* merge
if (relativeName.isEmpty())
return;
foreach (const QString &configDir, configDirs)
for (const QString &configDir : configDirs)
{
if (QFileInfo(configDir + relativeName).exists())
if (QFileInfo::exists(configDir + relativeName))
{
mergeFile(configDir + relativeName, element, mergedFiles);
return;
@ -295,9 +295,9 @@ void XdgMenuReader::processDefaultMergeDirsTag(QDomElement& element, QStringList
QStringList dirs = XdgDirs::configDirs();
dirs << XdgDirs::configHome();
foreach (const QString &dir, dirs)
for (const QString &dir : const_cast<const QStringList&>(dirs))
{
mergeDir(QString::fromLatin1("%1/menus/%2-merged").arg(dir).arg(menuBaseName), element, mergedFiles);
mergeDir(QString::fromLatin1("%1/menus/%2-merged").arg(dir, menuBaseName), element, mergedFiles);
}
if (menuBaseName == QLatin1String("applications"))
@ -329,7 +329,7 @@ void XdgMenuReader::processDefaultAppDirsTag(QDomElement& element)
QStringList dirs = XdgDirs::dataDirs();
dirs.prepend(XdgDirs::dataHome(false));
foreach (const QString &dir, dirs)
for (const QString &dir : const_cast<const QStringList&> (dirs))
{
//qDebug() << "Add AppDir: " << dir + "/applications/";
addDirTag(element, QLatin1String("AppDir"), dir + QLatin1String("/applications/"));
@ -360,7 +360,7 @@ void XdgMenuReader::processDefaultDirectoryDirsTag(QDomElement& element)
QStringList dirs = XdgDirs::dataDirs();
dirs.prepend(XdgDirs::dataHome(false));
foreach (const QString &dir, dirs)
for (const QString &dir : const_cast<const QStringList&>(dirs))
addDirTag(element, QLatin1String("DirectoryDir"), dir + QLatin1String("/desktop-directories/"));
}
@ -379,8 +379,7 @@ void XdgMenuReader::addDirTag(QDomElement& previousElement, const QString& tagNa
}
}
/************************************************
/*
If fileName is not an absolute path then the file to be merged should be located
relative to the location of this menu file.
************************************************/
@ -431,7 +430,7 @@ void XdgMenuReader::mergeDir(const QString& dirName, QDomElement& element, QStri
QDir dir = QDir(dirInfo.canonicalFilePath());
const QFileInfoList files = dir.entryInfoList(QStringList() << QLatin1String("*.menu"), QDir::Files | QDir::Readable);
foreach (const QFileInfo &file, files)
for (const QFileInfo &file : files)
mergeFile(file.canonicalFilePath(), element, mergedFiles);
}
}

@ -47,9 +47,9 @@ public:
QString errorString() const { return mErrorStr; }
QDomDocument& xml() { return mXml; }
signals:
Q_SIGNALS:
public slots:
public Q_SLOTS:
protected:
void processMergeTags(QDomElement& element);

@ -44,7 +44,7 @@ class XdgMenuWidgetPrivate
{
private:
XdgMenuWidget* const q_ptr;
Q_DECLARE_PUBLIC(XdgMenuWidget);
Q_DECLARE_PUBLIC(XdgMenuWidget)
public:
explicit XdgMenuWidgetPrivate(XdgMenuWidget* parent):
@ -186,7 +186,7 @@ void XdgMenuWidgetPrivate::buildMenu()
QAction* first = 0;
if (!q->actions().isEmpty())
first = q->actions().last();
first = q->actions().constLast();
DomElementIterator it(mXml, QString());

@ -96,7 +96,7 @@ QString XdgMimeType::iconName() const
names.append(QMimeType::iconName());
names.append(QMimeType::genericIconName());
foreach (const QString &s, names) {
for (const QString &s : const_cast<const QStringList&>(names)) {
if (!XdgIcon::fromTheme(s).isNull()) {
dx->iconName = s;
break;

@ -41,6 +41,6 @@ QDebug operator<<(QDebug dbg, const QDomElement &el)
for (int i=0; i<map.count(); ++i)
args += QLatin1Char(' ') + map.item(i).nodeName() + QLatin1Char('=') + map.item(i).nodeValue() + QLatin1Char('\'');
dbg.nospace() << QString::fromLatin1("<%1%2>%3</%1>").arg(el.tagName()).arg(args).arg(el.text());
dbg.nospace() << QString::fromLatin1("<%1%2>%3</%1>").arg(el.tagName(), args, el.text());
return dbg.space();
}

@ -1,28 +0,0 @@
#!/bin/bash
PROJECT="libqtxdg"
version="$1"
prefix=$PROJECT-$version
shift
if [[ -z $version ]]; then
>&2 echo "USAGE: $0 <tag>"
exit 1
fi
mkdir -p "dist/$version"
echo "Creating $prefix.tar.gz"
git archive -9 --format tar.gz $version --prefix="$prefix/" > "dist/$version/$prefix.tar.gz"
gpg --armor --detach-sign "dist/$version/$prefix.tar.gz"
echo "Creating $prefix.tar.xz"
git archive -9 --format tar.xz $version --prefix="$prefix/" > "dist/$version/$prefix.tar.xz"
gpg --armor --detach-sign "dist/$version/$prefix.tar.xz"
cd "dist/$version"
sha1sum --tag *.tar.gz *.tar.xz >> CHECKSUMS
sha256sum --tag *.tar.gz *.tar.xz >> CHECKSUMS
cd ..
echo "Uploading to lxqt.org..."
scp -r "$version" "downloads.lxqt.org:/srv/downloads.lxqt.org/$PROJECT/"

@ -1,25 +1,25 @@
set(PROJECT_NAME "qtxdg_test")
remove_definitions(
-DQT_USE_QSTRINGBUILDER
-DQT_NO_CAST_FROM_ASCII
)
set(CMAKE_AUTOMOC TRUE)
add_definitions(
-DQT_NO_KEYWORDS
)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
macro(qtxdg_add_test)
foreach(_testname ${ARGN})
add_executable(${_testname} ${_testname}.cpp)
target_link_libraries(${_testname} Qt5::Test ${QTXDGX_LIBRARY_NAME})
target_include_directories(${_testname}
PRIVATE "${PROJECT_SOURCE_DIR}/qtxdg"
)
add_test(NAME ${_testname} COMMAND ${_testname})
endforeach()
endmacro()
include_directories (
"${CMAKE_SOURCE_DIR}/qtxdg"
${CMAKE_CURRENT_BINARY_DIR}
)
set_property(DIRECTORY APPEND
PROPERTY COMPILE_DEFINITIONS "QTXDG_BUILDING_TESTS=\"1\""
)

@ -47,15 +47,17 @@ void QtXdgTest::testDefaultApp()
{
QStringList mimedirs = XdgDirs::dataDirs();
mimedirs.prepend(XdgDirs::dataHome(false));
foreach (QString mimedir, mimedirs)
for (const QString &mimedir : const_cast<const QStringList&>(mimedirs))
{
QDir dir(mimedir + "/mime");
qDebug() << dir.path();
QStringList filters = (QStringList() << "*.xml");
foreach(QFileInfo mediaDir, dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot))
const QFileInfoList &mediaDirs = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
for (const QFileInfo &mediaDir : mediaDirs)
{
qDebug() << " " << mediaDir.fileName();
foreach (QString mimeXmlFileName, QDir(mediaDir.absoluteFilePath()).entryList(filters, QDir::Files))
const QStringList mimeXmlFileNames = QDir(mediaDir.absoluteFilePath()).entryList(filters, QDir::Files);
for (const QString &mimeXmlFileName : mimeXmlFileNames)
{
QString mimetype = mediaDir.fileName() + "/" + mimeXmlFileName.left(mimeXmlFileName.length() - 4);
QString xdg_utils_default = xdgUtilDefaultApp(mimetype);

@ -39,7 +39,7 @@ class QtXdgTest : public QObject
{
Q_OBJECT
private slots:
private Q_SLOTS:
void testCustomFormat();
private:

@ -27,20 +27,20 @@ class Language
{
public:
Language (const QString& lang)
: mPreviousLang(QString::fromLocal8Bit(qgetenv("LANG")))
: mPreviousLang(QString::fromLocal8Bit(qgetenv("LC_MESSAGES")))
{
qputenv("LANG", lang.toLocal8Bit());
qputenv("LC_MESSAGES", lang.toLocal8Bit());
}
~Language()
{
qputenv("LANG", mPreviousLang.toLocal8Bit());
qputenv("LC_MESSAGES", mPreviousLang.toLocal8Bit());
}
private:
QString mPreviousLang;
};
QTEST_MAIN(tst_xdgdesktopfile);
QTEST_MAIN(tst_xdgdesktopfile)
void tst_xdgdesktopfile::testRead()
{

@ -25,7 +25,7 @@
class tst_xdgdesktopfile : public QObject {
Q_OBJECT
private slots:
private Q_SLOTS:
void testRead();
void testReadLocalized();

@ -36,7 +36,7 @@ class tst_xdgdirs : public QObject
{
Q_OBJECT
private slots:
private Q_SLOTS:
void initTestCase();
void cleanupTestCase();

@ -1,26 +1,52 @@
include_directories (
"${CMAKE_PROJECT_DIR}/qtxdg"
${CMAKE_CURRENT_BINARY_DIR}
)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(QTXDG_DESKTOP_FILE_START_SRCS
qtxdg-desktop-file-start.cpp
)
set(QTXDG_ICONFINDER_SRCS
qtxdg-iconfinder.cpp
)
add_executable(qtxdg-desktop-file-start
${QTXDG_DESKTOP_FILE_START_SRCS}
)
add_executable(qtxdg-iconfinder
${QTXDG_ICONFINDER_SRCS}
)
target_include_directories(qtxdg-desktop-file-start
PRIVATE "${PROJECT_SOURCE_DIR}/qtxdg"
)
target_include_directories(qtxdg-iconfinder
PRIVATE "${Qt5Gui_PRIVATE_INCLUDE_DIRS}"
)
target_compile_definitions(qtxdg-desktop-file-start
PRIVATE "-DQTXDG_VERSION=\"${QTXDG_VERSION_STRING}\""
PRIVATE
"-DQTXDG_VERSION=\"${QTXDG_VERSION_STRING}\""
"QT_NO_KEYWORDS"
)
target_compile_definitions(qtxdg-iconfinder
PRIVATE
"-DQTXDG_VERSION=\"${QTXDG_VERSION_STRING}\""
"QT_NO_KEYWORDS"
)
target_link_libraries(qtxdg-desktop-file-start
${QTXDGX_LIBRARY_NAME}
)
target_link_libraries(qtxdg-iconfinder
${QTXDGX_ICONLOADER_LIBRARY_NAME}
)
install(TARGETS
qtxdg-desktop-file-start
qtxdg-iconfinder
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
COMPONENT Runtime
)

@ -0,0 +1,74 @@
/*
* libqtxdg - An Qt implementation of freedesktop.org xdg specs
* Copyright (C) 2017 Luís Pereira <luis.artur.pereira@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
#include <QGuiApplication> // XdgIconLoader needs a QGuiApplication
#include <QCommandLineParser>
#include <QElapsedTimer>
#include <private/xdgiconloader/xdgiconloader_p.h>
#include <iostream>
#include <QDebug>
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
app.setApplicationName(QStringLiteral("qtxdg-iconfinder"));
app.setApplicationVersion(QStringLiteral(QTXDG_VERSION));
QCommandLineParser parser;
parser.setApplicationDescription(QStringLiteral("QtXdg icon finder"));
parser.addPositionalArgument(QStringLiteral("iconnames"),
QStringLiteral("The icon names to search for"),
QStringLiteral("[iconnames...]"));
parser.addVersionOption();
parser.addHelpOption();
parser.process(app);
if (parser.positionalArguments().isEmpty())
parser.showHelp(EXIT_FAILURE);
qint64 totalElapsed = 0;
const auto icons = parser.positionalArguments();
for (const QString& iconName : icons) {
QElapsedTimer t;
t.start();
const auto info = XdgIconLoader::instance()->loadIcon(iconName);
qint64 elapsed = t.elapsed();
const auto icon = info.iconName;
const auto entries = info.entries;
std::cout << qPrintable(iconName) <<
qPrintable(QString::fromLatin1(":")) << qPrintable(icon) <<
qPrintable(QString::fromLatin1(":")) <<
qPrintable(QString::number(elapsed)) << "\n";
for (const auto &i : entries) {
std::cout << "\t" << qPrintable(i->filename) << "\n";
}
totalElapsed += elapsed;
}
std::cout << qPrintable(QString::fromLatin1("Total loadIcon() time: ")) <<
qPrintable(QString::number(totalElapsed)) <<
qPrintable(QString::fromLatin1(" ms")) << "\n";
return EXIT_SUCCESS;
}

@ -1,7 +1,3 @@
include_directories(
"${Qt5Gui_PRIVATE_INCLUDE_DIRS}"
)
set(xdgiconloader_PUBLIC_H_FILES
)
@ -38,17 +34,22 @@ configure_file(
COPYONLY
)
target_include_directories(${QTXDGX_ICONLOADER_LIBRARY_NAME}
INTERFACE "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/${QTXDGX_ICONLOADER_FILE_NAME}>"
INTERFACE "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
INTERFACE "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/${QTXDGX_ICONLOADER_FILE_NAME}/${QTXDG_VERSION_STRING}>"
target_compile_definitions(${QTXDGX_ICONLOADER_LIBRARY_NAME}
PRIVATE
"QT_NO_KEYWORDS"
)
# include directories and targets for the in tree build
target_include_directories(${QTXDGX_ICONLOADER_LIBRARY_NAME}
PUBLIC "$<BUILD_INTERFACE:${QTXDGX_INTREE_INCLUDEDIR}/${QTXDGX_ICONLOADER_FILE_NAME}>"
PUBLIC "$<BUILD_INTERFACE:${QTXDGX_INTREE_INCLUDEDIR}>"
PUBLIC "$<BUILD_INTERFACE:${QTXDGX_INTREE_INCLUDEDIR}/${QTXDGX_ICONLOADER_FILE_NAME}/${QTXDG_VERSION_STRING}>"
INTERFACE
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/${QTXDGX_ICONLOADER_FILE_NAME}>"
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/${QTXDGX_ICONLOADER_FILE_NAME}/${QTXDG_VERSION_STRING}>"
PUBLIC
"$<BUILD_INTERFACE:${QTXDGX_INTREE_INCLUDEDIR}/${QTXDGX_ICONLOADER_FILE_NAME}>"
"$<BUILD_INTERFACE:${QTXDGX_INTREE_INCLUDEDIR}>"
"$<BUILD_INTERFACE:${QTXDGX_INTREE_INCLUDEDIR}/${QTXDGX_ICONLOADER_FILE_NAME}/${QTXDG_VERSION_STRING}>"
PRIVATE
${Qt5Gui_PRIVATE_INCLUDE_DIRS}
)
target_link_libraries(${QTXDGX_ICONLOADER_LIBRARY_NAME}
@ -63,7 +64,7 @@ set_target_properties(${QTXDGX_ICONLOADER_LIBRARY_NAME}
SOVERSION ${QTXDG_MAJOR_VERSION}
)
export(TARGETS ${QTXDGX_ICONLOADER_LIBRARY_NAME} FILE "${CMAKE_BINARY_DIR}/${QTXDGX_ICONLOADER_FILE_NAME}-targets.cmake")
add_subdirectory(plugin)
install(TARGETS
${QTXDGX_ICONLOADER_LIBRARY_NAME} DESTINATION "${CMAKE_INSTALL_LIBDIR}"
@ -77,6 +78,11 @@ install(FILES
COMPONENT Devel
)
file(COPY
${xdgiconloader_PRIVATE_INSTALLABLE_H_FILES}
DESTINATION "${QTXDGX_INTREE_INCLUDEDIR}/${QTXDGX_ICONLOADER_FILE_NAME}/${QTXDG_VERSION_STRING}/private/xdgiconloader"
)
install(FILES
"${QTXDGX_INTREE_INCLUDEDIR}/${QTXDGX_ICONLOADER_FILE_NAME}/${XDGICONLOADER_EXPORT_FILE}"
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${QTXDGX_ICONLOADER_FILE_NAME}"

@ -0,0 +1,49 @@
set(xdgiconengineplugin_CPP_FILES
xdgiconengineplugin.cpp
)
add_library(${QTXDGX_ICONENGINEPLUGIN_LIBRARY_NAME} MODULE
${xdgiconengineplugin_CPP_FILES}
)
target_compile_definitions(${QTXDGX_ICONENGINEPLUGIN_LIBRARY_NAME}
PRIVATE
"QT_NO_KEYWORDS"
)
target_link_libraries(${QTXDGX_ICONENGINEPLUGIN_LIBRARY_NAME}
PUBLIC
Qt5::Gui
"${QTXDGX_ICONLOADER_LIBRARY_NAME}"
)
target_include_directories("${QTXDGX_ICONENGINEPLUGIN_LIBRARY_NAME}"
PRIVATE
"${Qt5Gui_PRIVATE_INCLUDE_DIRS}"
)
mark_as_advanced(QTXDGX_ICONENGINEPLUGIN_INSTALL_PATH)
if (NOT DEFINED QTXDGX_ICONENGINEPLUGIN_INSTALL_PATH)
get_target_property(QT_QMAKE_EXECUTABLE ${Qt5Core_QMAKE_EXECUTABLE} IMPORTED_LOCATION)
if(NOT QT_QMAKE_EXECUTABLE)
message(FATAL_ERROR "qmake is not found.")
endif()
# execute the command "qmake -query QT_INSTALL_PLUGINS" to get the path of plugins dir.
execute_process(COMMAND "${QT_QMAKE_EXECUTABLE}" -query QT_INSTALL_PLUGINS
OUTPUT_VARIABLE QT_PLUGINS_DIR
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if (NOT QT_PLUGINS_DIR)
message(FATAL_ERROR "Qt5 plugin directory cannot be detected.")
endif()
set(QTXDGX_ICONENGINEPLUGIN_INSTALL_PATH "${QT_PLUGINS_DIR}/iconengines")
endif()
message(STATUS "XdgIconEnginePlugin will be installed into: ${QTXDGX_ICONENGINEPLUGIN_INSTALL_PATH}")
install(TARGETS
"${QTXDGX_ICONENGINEPLUGIN_LIBRARY_NAME}" DESTINATION "${QTXDGX_ICONENGINEPLUGIN_INSTALL_PATH}"
COMPONENT Runtime
)

@ -0,0 +1,35 @@
/* BEGIN_COMMON_COPYRIGHT_HEADER
* (c)LGPL2+
*
* LXQt - a lightweight Qt based desktop
* http://lxqt.org
*
* Copyright: 2017 LXQt team
* Authors:
* Palo Kisa <palo.kisa@gmail.com>
*
* This program or library is free software; you can redistribute it
* and/or modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA
*
* END_COMMON_COPYRIGHT_HEADER */
#include "xdgiconengineplugin.h"
#include "../xdgiconloader_p.h"
QIconEngine * XdgIconEnginePlugin::create(const QString & filename/* = QString{}*/)
{
return new XdgIconLoaderEngine{filename};
}

@ -0,0 +1,38 @@
/* BEGIN_COMMON_COPYRIGHT_HEADER
* (c)LGPL2+
*
* LXQt - a lightweight Qt based desktop
* http://lxqt.org
*
* Copyright: 2017 LXQt team
* Authors:
* Palo Kisa <palo.kisa@gmail.com>
*
* This program or library is free software; you can redistribute it
* and/or modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA
*
* END_COMMON_COPYRIGHT_HEADER */
#include <QIconEnginePlugin>
class XdgIconEnginePlugin : public QIconEnginePlugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QIconEngineFactoryInterface" FILE "xdgiconengineplugin.json")
public:
using QIconEnginePlugin::QIconEnginePlugin;
virtual QIconEngine * create(const QString & filename = QString{}) override;
};

@ -0,0 +1 @@
{"Keys": ["XdgIconLoaderEngine"]}

@ -41,10 +41,13 @@
#include <qpa/qplatformtheme.h>
#include <QtGui/QIconEngine>
#include <QtGui/QPalette>
#include <QtCore/qmath.h>
#include <QtCore/QList>
#include <QtCore/QDir>
#include <QtCore/QSettings>
#include <QtGui/QPainter>
#include <QImageReader>
#include <QXmlStreamReader>
#ifdef Q_DEAD_CODE_FROM_QT4_MAC
#include <private/qt_cocoa_helpers_mac_p.h>
@ -62,104 +65,36 @@ static QString fallbackTheme()
{
if (const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme()) {
const QVariant themeHint = theme->themeHint(QPlatformTheme::SystemIconFallbackThemeName);
if (themeHint.isValid())
return themeHint.toString();
if (themeHint.isValid()) {
const QString theme = themeHint.toString();
if (theme != QLatin1String("hicolor"))
return theme;
}
return QLatin1String("hicolor");
}
XdgIconLoader::XdgIconLoader() :
m_themeKey(1), m_supportsSvg(false), m_initialized(false)
{
return QString();
}
static inline QString systemThemeName()
{
if (const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme()) {
const QVariant themeHint = theme->themeHint(QPlatformTheme::SystemIconThemeName);
if (themeHint.isValid())
return themeHint.toString();
}
return QIcon::themeName();
}
#ifdef QT_NO_LIBRARY
static bool gSupportsSvg = false;
#else
static bool gSupportsSvg = true;
#endif //QT_NO_LIBRARY
static inline QStringList systemIconSearchPaths()
void XdgIconLoader::setFollowColorScheme(bool enable)
{
if (const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme()) {
const QVariant themeHint = theme->themeHint(QPlatformTheme::IconThemeSearchPaths);
if (themeHint.isValid())
return themeHint.toStringList();
}
return QIcon::themeSearchPaths();
}
#ifndef QT_NO_LIBRARY
//extern QFactoryLoader *qt_iconEngineFactoryLoader(); // qicon.cpp
#endif
void XdgIconLoader::ensureInitialized()
if (m_followColorScheme != enable)
{
if (!m_initialized) {
m_initialized = true;
Q_ASSERT(qApp);
m_systemTheme = systemThemeName();
if (m_systemTheme.isEmpty())
m_systemTheme = fallbackTheme();
#ifndef QT_NO_LIBRARY
// if (qt_iconEngineFactoryLoader()->keyMap().key(QLatin1String("svg"), -1) != -1)
m_supportsSvg = true;
#endif //QT_NO_LIBRARY
QIconLoader::instance()->invalidateKey();
m_followColorScheme = enable;
}
}
XdgIconLoader *XdgIconLoader::instance()
{
iconLoaderInstance()->ensureInitialized();
QIconLoader::instance()->ensureInitialized();
return iconLoaderInstance();
}
// Queries the system theme and invalidates existing
// icons if the theme has changed.
void XdgIconLoader::updateSystemTheme()
{
// Only change if this is not explicitly set by the user
if (m_userTheme.isEmpty()) {
QString theme = systemThemeName();
if (theme.isEmpty())
theme = fallbackTheme();
if (theme != m_systemTheme) {
m_systemTheme = theme;
invalidateKey();
}
}
}
void XdgIconLoader::setThemeName(const QString &themeName)
{
m_userTheme = themeName;
invalidateKey();
}
void XdgIconLoader::setThemeSearchPath(const QStringList &searchPaths)
{
m_iconDirs = searchPaths;
themeList.clear();
invalidateKey();
}
QStringList XdgIconLoader::themeSearchPaths() const
{
if (m_iconDirs.isEmpty()) {
m_iconDirs = systemIconSearchPaths();
// Always add resource directory as search path
m_iconDirs.append(QLatin1String(":/icons"));
}
return m_iconDirs;
}
/*!
\class QIconCacheGtkReader
\internal
@ -172,7 +107,7 @@ class QIconCacheGtkReader
{
public:
explicit QIconCacheGtkReader(const QString &themeDir);
QVector<const char *> lookup(const QString &);
QVector<const char *> lookup(const QStringRef &);
bool isValid() const { return m_isValid; }
private:
QFile m_file;
@ -203,7 +138,7 @@ private:
QIconCacheGtkReader::QIconCacheGtkReader(const QString &dirName)
: m_isValid(false)
{
QFileInfo info(dirName + QLatin1Literal("/icon-theme.cache"));
QFileInfo info(dirName + QLatin1String("/icon-theme.cache"));
if (!info.exists() || info.lastModified() < QFileInfo(dirName).lastModified())
return;
m_file.setFileName(info.absoluteFilePath());
@ -245,7 +180,7 @@ static quint32 icon_name_hash(const char *p)
with this name is present. The char* are pointers to the mapped data.
For example, this would return { "32x32/apps", "24x24/apps" , ... }
*/
QVector<const char *> QIconCacheGtkReader::lookup(const QString &name)
QVector<const char *> QIconCacheGtkReader::lookup(const QStringRef &name)
{
QVector<const char *> ret;
if (!isValid())
@ -295,12 +230,13 @@ QVector<const char *> QIconCacheGtkReader::lookup(const QString &name)
return ret;
}
QIconTheme::QIconTheme(const QString &themeName)
XdgIconTheme::XdgIconTheme(const QString &themeName)
: m_valid(false)
, m_followsColorScheme(false)
{
QFile themeIndex;
QStringList iconDirs = QIcon::themeSearchPaths();
const QStringList iconDirs = QIcon::themeSearchPaths();
for ( int i = 0 ; i < iconDirs.size() ; ++i) {
QDir iconDir(iconDirs[i]);
QString themeDir = iconDir.path() + QLatin1Char('/') + themeName;
@ -320,27 +256,26 @@ QIconTheme::QIconTheme(const QString &themeName)
#ifndef QT_NO_SETTINGS
if (themeIndex.exists()) {
const QSettings indexReader(themeIndex.fileName(), QSettings::IniFormat);
QStringListIterator keyIterator(indexReader.allKeys());
while (keyIterator.hasNext()) {
const QString key = keyIterator.next();
m_followsColorScheme = indexReader.value(QStringLiteral("Icon Theme/FollowsColorScheme"), false).toBool();
const QStringList keys = indexReader.allKeys();
for (auto const &key : keys) {
if (key.endsWith(QLatin1String("/Size"))) {
// Note the QSettings ini-format does not accept
// slashes in key names, hence we have to cheat
if (int size = indexReader.value(key).toInt()) {
QString directoryKey = key.left(key.size() - 5);
XdgIconDirInfo dirInfo(directoryKey);
QIconDirInfo dirInfo(directoryKey);
dirInfo.size = size;
QString type = indexReader.value(directoryKey +
QLatin1String("/Type")
).toString();
if (type == QLatin1String("Fixed"))
dirInfo.type = XdgIconDirInfo::Fixed;
dirInfo.type = QIconDirInfo::Fixed;
else if (type == QLatin1String("Scalable"))
dirInfo.type = XdgIconDirInfo::Scalable;
dirInfo.type = QIconDirInfo::Scalable;
else
dirInfo.type = XdgIconDirInfo::Threshold;
dirInfo.type = QIconDirInfo::Threshold;
dirInfo.threshold = indexReader.value(directoryKey +
QLatin1String("/Threshold"),
@ -353,6 +288,11 @@ QIconTheme::QIconTheme(const QString &themeName)
dirInfo.maxSize = indexReader.value(directoryKey +
QLatin1String("/MaxSize"),
size).toInt();
#if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0)
dirInfo.scale = indexReader.value(directoryKey +
QLatin1String("/Scale"),
1).toInt();
#endif
m_keyList.append(dirInfo);
}
}
@ -362,6 +302,7 @@ QIconTheme::QIconTheme(const QString &themeName)
m_parents = indexReader.value(
QLatin1String("Icon Theme/Inherits")).toStringList();
m_parents.removeAll(QString());
m_parents.removeAll(QLatin1String("hicolor"));
// Ensure a default platform fallback for all themes
if (m_parents.isEmpty()) {
@ -369,33 +310,51 @@ QIconTheme::QIconTheme(const QString &themeName)
if (!fallback.isEmpty())
m_parents.append(fallback);
}
// Ensure that all themes fall back to hicolor
if (!m_parents.contains(QLatin1String("hicolor")))
m_parents.append(QLatin1String("hicolor"));
}
#endif //QT_NO_SETTINGS
}
/* WARNING:
*
* https://standards.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html
*
* <cite>
* The dash - character is used to separate levels of specificity in icon
* names, for all contexts other than MimeTypes. For instance, we use
* input-mouse as the generic item for all mouse devices, and we use
* input-mouse-usb for a USB mouse device. However, if the more specific
* item does not exist in the current theme, and does exist in a parent
* theme, the generic icon from the current theme is preferred, in order
* to keep consistent style.
* </cite>
*
* But we believe, that using the more specific icon (even from parents)
* is better for user experience. So we are violating the standard
* intentionally.
*
* Ref.
* https://github.com/lxde/lxqt/issues/1252
* https://github.com/lxde/libqtxdg/pull/116
*/
QThemeIconInfo XdgIconLoader::findIconHelper(const QString &themeName,
const QString &iconName,
QStringList &visited) const
QStringList &visited,
bool dashFallback) const
{
QThemeIconInfo info;
Q_ASSERT(!themeName.isEmpty());
QPixmap pixmap;
// Used to protect against potential recursions
visited << themeName;
QIconTheme theme = themeList.value(themeName);
XdgIconTheme &theme = themeList[themeName];
if (!theme.isValid()) {
theme = QIconTheme(themeName);
if (!theme.isValid())
theme = QIconTheme(fallbackTheme());
themeList.insert(themeName, theme);
theme = XdgIconTheme(themeName);
if (!theme.isValid()) {
const QString fallback = fallbackTheme();
if (!fallback.isEmpty())
theme = XdgIconTheme(fallback);
}
}
const QStringList contentDirs = theme.contentDirs();
@ -405,7 +364,7 @@ QThemeIconInfo XdgIconLoader::findIconHelper(const QString &themeName,
const QString xpmext(QLatin1String(".xpm"));
QString iconNameFallback = iconName;
QStringRef iconNameFallback(&iconName);
// Iterate through all icon's fallbacks in current theme
while (info.entries.isEmpty()) {
@ -415,21 +374,21 @@ QThemeIconInfo XdgIconLoader::findIconHelper(const QString &themeName,
// Add all relevant files
for (int i = 0; i < contentDirs.size(); ++i) {
QVector<XdgIconDirInfo> subDirs = theme.keyList();
QVector<QIconDirInfo> subDirs = theme.keyList();
// Try to reduce the amount of subDirs by looking in the GTK+ cache in order to save
// a massive amount of file stat (especially if the icon is not there)
auto cache = theme.m_gtkCaches.at(i);
if (cache->isValid()) {
auto result = cache->lookup(iconNameFallback);
const auto result = cache->lookup(iconNameFallback);
if (cache->isValid()) {
const QVector<XdgIconDirInfo> subDirsCopy = subDirs;
const QVector<QIconDirInfo> subDirsCopy = subDirs;
subDirs.clear();
subDirs.reserve(result.count());
foreach (const char *s, result) {
for (const char *s : result) {
QString path = QString::fromUtf8(s);
auto it = std::find_if(subDirsCopy.cbegin(), subDirsCopy.cend(),
[&](const XdgIconDirInfo &info) {
[&](const QIconDirInfo &info) {
return info.path == path; } );
if (it != subDirsCopy.cend()) {
subDirs.append(*it);
@ -440,45 +399,41 @@ QThemeIconInfo XdgIconLoader::findIconHelper(const QString &themeName,
QString contentDir = contentDirs.at(i) + QLatin1Char('/');
for (int j = 0; j < subDirs.size() ; ++j) {
const XdgIconDirInfo &dirInfo = subDirs.at(j);
QString subdir = dirInfo.path;
QDir currentDir(contentDir + subdir);
if (currentDir.exists(pngIconName)) {
const QIconDirInfo &dirInfo = subDirs.at(j);
const QString subDir = contentDir + dirInfo.path + QLatin1Char('/');
const QString pngPath = subDir + pngIconName;
if (QFile::exists(pngPath)) {
PixmapEntry *iconEntry = new PixmapEntry;
iconEntry->dir = dirInfo;
iconEntry->filename = currentDir.filePath(pngIconName);
iconEntry->filename = pngPath;
// Notice we ensure that pixmap entries always come before
// scalable to preserve search order afterwards
info.entries.prepend(iconEntry);
} else if (m_supportsSvg &&
currentDir.exists(svgIconName)) {
ScalableEntry *iconEntry = new ScalableEntry;
} else {
const QString svgPath = subDir + svgIconName;
if (gSupportsSvg && QFile::exists(svgPath)) {
ScalableEntry *iconEntry = (followColorScheme() && theme.followsColorScheme()) ? new ScalableFollowsColorEntry : new ScalableEntry;
iconEntry->dir = dirInfo;
iconEntry->filename = currentDir.filePath(svgIconName);
iconEntry->filename = svgPath;
info.entries.append(iconEntry);
} else if(currentDir.exists(iconName + xpmext)) {
}
}
const QString xpmPath = subDir + xpmIconName;
if (QFile::exists(xpmPath)) {
PixmapEntry *iconEntry = new PixmapEntry;
iconEntry->dir = dirInfo;
iconEntry->filename = currentDir.filePath(iconName + xpmext);
iconEntry->filename = xpmPath;
// Notice we ensure that pixmap entries always come before
// scalable to preserve search order afterwards
info.entries.append(iconEntry);
break;
}
}
}
if (!info.entries.isEmpty()) {
info.iconName = iconNameFallback;
break;
}
if (!info.entries.isEmpty())
info.iconName = iconNameFallback.toString();
// If it's possible - find next fallback for the icon
const int indexOfDash = iconNameFallback.lastIndexOf(QLatin1Char('-'));
if (indexOfDash == -1)
break;
iconNameFallback.truncate(indexOfDash);
}
if (info.entries.isEmpty()) {
@ -496,83 +451,80 @@ QThemeIconInfo XdgIconLoader::findIconHelper(const QString &themeName,
}
}
if (info.entries.isEmpty()) {
// Search for unthemed icons in main dir of search paths
QStringList themeSearchPaths = QIcon::themeSearchPaths();
foreach (QString contentDir, themeSearchPaths) {
QDir currentDir(contentDir);
if (currentDir.exists(iconName + pngext)) {
PixmapEntry *iconEntry = new PixmapEntry;
iconEntry->filename = currentDir.filePath(iconName + pngext);
// Notice we ensure that pixmap entries always come before
// scalable to preserve search order afterwards
info.entries.prepend(iconEntry);
} else if (m_supportsSvg &&
currentDir.exists(iconName + svgext)) {
ScalableEntry *iconEntry = new ScalableEntry;
iconEntry->filename = currentDir.filePath(iconName + svgext);
info.entries.append(iconEntry);
break;
} else if (currentDir.exists(iconName + xpmext)) {
PixmapEntry *iconEntry = new PixmapEntry;
iconEntry->filename = currentDir.filePath(iconName + xpmext);
// Notice we ensure that pixmap entries always come before
// scalable to preserve search order afterwards
info.entries.append(iconEntry);
break;
if (dashFallback && info.entries.isEmpty()) {
// If it's possible - find next fallback for the icon
const int indexOfDash = iconNameFallback.lastIndexOf(QLatin1Char('-'));
if (indexOfDash != -1) {
iconNameFallback.truncate(indexOfDash);
QStringList _visited;
info = findIconHelper(themeName, iconNameFallback.toString(), _visited, true);
}
}
return info;
}
QThemeIconInfo XdgIconLoader::unthemedFallback(const QString &iconName, const QStringList &searchPaths) const
{
QThemeIconInfo info;
/*********************************************************************
Author: Kaitlin Rupert <kaitlin.rupert@intel.com>
Date: Aug 12, 2010
Description: Make it so that the QIcon loader honors /usr/share/pixmaps
directory. This is a valid directory per the Freedesktop.org
icon theme specification.
Bug: https://bugreports.qt.nokia.com/browse/QTBUG-12874
*********************************************************************/
#ifdef Q_OS_LINUX
/* Freedesktop standard says to look in /usr/share/pixmaps last */
if (info.entries.isEmpty()) {
const QString pixmaps(QLatin1String("/usr/share/pixmaps"));
const QString svgext(QLatin1String(".svg"));
const QString pngext(QLatin1String(".png"));
const QString xpmext(QLatin1String(".xpm"));
for (const auto &contentDir : searchPaths) {
QDir currentDir(contentDir);
const QDir currentDir(pixmaps);
const XdgIconDirInfo dirInfo(pixmaps);
if (currentDir.exists(iconName + pngext)) {
PixmapEntry *iconEntry = new PixmapEntry;
iconEntry->dir = dirInfo;
iconEntry->filename = currentDir.filePath(iconName + pngext);
// Notice we ensure that pixmap entries always come before
// scalable to preserve search order afterwards
info.entries.prepend(iconEntry);
} else if (m_supportsSvg &&
} else if (gSupportsSvg &&
currentDir.exists(iconName + svgext)) {
ScalableEntry *iconEntry = new ScalableEntry;
iconEntry->dir = dirInfo;
iconEntry->filename = currentDir.filePath(iconName + svgext);
info.entries.append(iconEntry);
} else if (currentDir.exists(iconName + xpmext)) {
PixmapEntry *iconEntry = new PixmapEntry;
iconEntry->dir = dirInfo;
iconEntry->filename = currentDir.filePath(iconName + xpmext);
// Notice we ensure that pixmap entries always come before
// scalable to preserve search order afterwards
info.entries.append(iconEntry);
}
}
#endif
return info;
}
QThemeIconInfo XdgIconLoader::loadIcon(const QString &name) const
{
if (!themeName().isEmpty()) {
const QString theme_name = QIconLoader::instance()->themeName();
if (!theme_name.isEmpty()) {
QStringList visited;
return findIconHelper(themeName(), name, visited);
auto info = findIconHelper(theme_name, name, visited, true);
if (info.entries.isEmpty()) {
const auto hicolorInfo = findIconHelper(QLatin1String("hicolor"), name, visited, true);
if (hicolorInfo.entries.isEmpty()) {
const auto unthemedInfo = unthemedFallback(name, QIcon::themeSearchPaths());
if (unthemedInfo.entries.isEmpty()) {
/* Freedesktop standard says to look in /usr/share/pixmaps last */
const QStringList pixmapPath = (QStringList() << QString::fromLatin1("/usr/share/pixmaps"));
const auto pixmapInfo = unthemedFallback(name, pixmapPath);
if (pixmapInfo.entries.isEmpty()) {
return QThemeIconInfo();
} else {
return pixmapInfo;
}
} else {
return unthemedInfo;
}
} else {
return hicolorInfo;
}
} else {
return info;
}
}
return QThemeIconInfo();
@ -623,14 +575,14 @@ bool XdgIconLoaderEngine::hasIcon() const
// Lazily load the icon
void XdgIconLoaderEngine::ensureLoaded()
{
if (!(XdgIconLoader::instance()->themeKey() == m_key)) {
if (!(QIconLoader::instance()->themeKey() == m_key)) {
qDeleteAll(m_info.entries);
m_info.entries.clear();
m_info.iconName.clear();
Q_ASSERT(m_info.entries.size() == 0);
m_info = XdgIconLoader::instance()->loadIcon(m_iconName);
m_key = XdgIconLoader::instance()->themeKey();
m_key = QIconLoader::instance()->themeKey();
}
}
@ -648,16 +600,20 @@ void XdgIconLoaderEngine::paint(QPainter *painter, const QRect &rect,
* This algorithm is defined by the freedesktop spec:
* http://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html
*/
static bool directoryMatchesSize(const XdgIconDirInfo &dir, int iconsize)
static bool directoryMatchesSize(const QIconDirInfo &dir, int iconsize, int iconscale)
{
if (dir.type == XdgIconDirInfo::Fixed) {
#if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0)
if (dir.scale != iconscale)
return false;
#endif
if (dir.type == QIconDirInfo::Fixed) {
return dir.size == iconsize;
} else if (dir.type == XdgIconDirInfo::Scalable) {
} else if (dir.type == QIconDirInfo::Scalable) {
return iconsize <= dir.maxSize &&
iconsize >= dir.minSize;
} else if (dir.type == XdgIconDirInfo::Threshold) {
} else if (dir.type == QIconDirInfo::Threshold) {
return iconsize >= dir.size - dir.threshold &&
iconsize <= dir.size + dir.threshold;
}
@ -670,12 +626,33 @@ static bool directoryMatchesSize(const XdgIconDirInfo &dir, int iconsize)
* This algorithm is defined by the freedesktop spec:
* http://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html
*/
static int directorySizeDistance(const XdgIconDirInfo &dir, int iconsize)
{
if (dir.type == XdgIconDirInfo::Fixed) {
static int directorySizeDistance(const QIconDirInfo &dir, int iconsize, int iconscale)
{
#if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0)
const int scaledIconSize = iconsize * iconscale;
if (dir.type == QIconDirInfo::Fixed) {
return qAbs(dir.size * dir.scale - scaledIconSize);
} else if (dir.type == QIconDirInfo::Scalable) {
if (scaledIconSize < dir.minSize * dir.scale)
return dir.minSize * dir.scale - scaledIconSize;
else if (scaledIconSize > dir.maxSize * dir.scale)
return scaledIconSize - dir.maxSize * dir.scale;
else
return 0;
} else if (dir.type == QIconDirInfo::Threshold) {
if (scaledIconSize < (dir.size - dir.threshold) * dir.scale)
return dir.minSize * dir.scale - scaledIconSize;
else if (scaledIconSize > (dir.size + dir.threshold) * dir.scale)
return scaledIconSize - dir.maxSize * dir.scale;
else return 0;
}
#else
if (dir.type == QIconDirInfo::Fixed) {
return qAbs(dir.size - iconsize);
} else if (dir.type == XdgIconDirInfo::Scalable) {
} else if (dir.type == QIconDirInfo::Scalable) {
if (iconsize < dir.minSize)
return dir.minSize - iconsize;
else if (iconsize > dir.maxSize)
@ -683,19 +660,20 @@ static int directorySizeDistance(const XdgIconDirInfo &dir, int iconsize)
else
return 0;
} else if (dir.type == XdgIconDirInfo::Threshold) {
} else if (dir.type == QIconDirInfo::Threshold) {
if (iconsize < dir.size - dir.threshold)
return dir.minSize - iconsize;
else if (iconsize > dir.size + dir.threshold)
return iconsize - dir.maxSize;
else return 0;
}
#endif
Q_ASSERT(1); // Not a valid value
return INT_MAX;
}
XdgIconLoaderEngineEntry *XdgIconLoaderEngine::entryForSize(const QSize &size)
QIconLoaderEngineEntry *XdgIconLoaderEngine::entryForSize(const QSize &size, int scale)
{
int iconsize = qMin(size.width(), size.height());
@ -706,18 +684,18 @@ XdgIconLoaderEngineEntry *XdgIconLoaderEngine::entryForSize(const QSize &size)
// Search for exact matches first
for (int i = 0; i < numEntries; ++i) {
XdgIconLoaderEngineEntry *entry = m_info.entries.at(i);
if (directoryMatchesSize(entry->dir, iconsize)) {
QIconLoaderEngineEntry *entry = m_info.entries.at(i);
if (directoryMatchesSize(entry->dir, iconsize, scale)) {
return entry;
}
}
// Find the minimum distance icon
int minimalSize = INT_MAX;
XdgIconLoaderEngineEntry *closestMatch = 0;
QIconLoaderEngineEntry *closestMatch = 0;
for (int i = 0; i < numEntries; ++i) {
XdgIconLoaderEngineEntry *entry = m_info.entries.at(i);
int distance = directorySizeDistance(entry->dir, iconsize);
QIconLoaderEngineEntry *entry = m_info.entries.at(i);
int distance = directorySizeDistance(entry->dir, iconsize, scale);
if (distance < minimalSize) {
minimalSize = distance;
closestMatch = entry;
@ -737,10 +715,10 @@ QSize XdgIconLoaderEngine::actualSize(const QSize &size, QIcon::Mode mode,
{
ensureLoaded();
XdgIconLoaderEngineEntry *entry = entryForSize(size);
QIconLoaderEngineEntry *entry = entryForSize(size);
if (entry) {
const XdgIconDirInfo &dir = entry->dir;
if (dir.type == XdgIconDirInfo::Scalable || dynamic_cast<ScalableEntry *>(entry))
const QIconDirInfo &dir = entry->dir;
if (dir.type == QIconDirInfo::Scalable || dynamic_cast<ScalableEntry *>(entry))
return size;
else {
int dir_size = dir.size;
@ -756,9 +734,10 @@ QSize XdgIconLoaderEngine::actualSize(const QSize &size, QIcon::Mode mode,
return QSize(result, result);
}
}
return QIconEngine::actualSize(size, mode, state);
return {0, 0};
}
// XXX: duplicated from qiconloader.cpp, because this symbol isn't exported :(
QPixmap PixmapEntry::pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state)
{
Q_UNUSED(state);
@ -769,6 +748,8 @@ QPixmap PixmapEntry::pixmap(const QSize &size, QIcon::Mode mode, QIcon::State st
basePixmap.load(filename);
QSize actualSize = basePixmap.size();
// If the size of the best match we have (basePixmap) is larger than the
// requested size, we downscale it to match.
if (!actualSize.isNull() && (actualSize.width() > size.width() || actualSize.height() > size.height()))
actualSize.scale(size, Qt::KeepAspectRatio);
@ -794,6 +775,7 @@ QPixmap PixmapEntry::pixmap(const QSize &size, QIcon::Mode mode, QIcon::State st
return cachedPixmap;
}
// XXX: duplicated from qiconloader.cpp, because this symbol isn't exported :(
QPixmap ScalableEntry::pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state)
{
if (svgIcon.isNull())
@ -803,12 +785,90 @@ QPixmap ScalableEntry::pixmap(const QSize &size, QIcon::Mode mode, QIcon::State
return svgIcon.pixmap(size, mode, state);
}
// XXX: duplicated from qicon.cpp, because the symbol qt_iconEngineFactoryLoader isn't exported :(
Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, qt_iconEngineFactoryLoader,
(QIconEngineFactoryInterface_iid, QLatin1String("/iconengines"), Qt::CaseInsensitive))
//extern QFactoryLoader *qt_iconEngineFactoryLoader(); // qicon.cpp
QPixmap ScalableFollowsColorEntry::pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state)
{
QIcon & icon = QIcon::Selected == mode ? svgSelectedIcon : svgIcon;
if (icon.isNull())
{
// The following lines are adapted and updated from KDE's "kiconloader.cpp" ->
// KIconLoaderPrivate::processSvg() and KIconLoaderPrivate::createIconImage().
// They read the SVG color scheme of SVG icons and give images based on the icon mode.
QByteArray processedContents;
QFile device{filename};;
if (device.open(QIODevice::ReadOnly))
{
const QPalette pal = qApp->palette();
QString styleSheet = QStringLiteral(".ColorScheme-Text{color:%1;}")
.arg(mode == QIcon::Selected
? pal.highlightedText().color().name()
: pal.windowText().color().name());
QXmlStreamReader xmlReader(&device);
QXmlStreamWriter writer(&processedContents);
while (!xmlReader.atEnd())
{
if (xmlReader.readNext() == QXmlStreamReader::StartElement
&& xmlReader.qualifiedName() == QLatin1String("style")
&& xmlReader.attributes().value(QLatin1String("id")) == QLatin1String("current-color-scheme"))
{
writer.writeStartElement(QLatin1String("style"));
writer.writeAttributes(xmlReader.attributes());
writer.writeCharacters(styleSheet);
writer.writeEndElement();
while (xmlReader.tokenType() != QXmlStreamReader::EndElement)
xmlReader.readNext();
}
else if (xmlReader.tokenType() != QXmlStreamReader::Invalid)
writer.writeCurrentToken(xmlReader);
}
}
// use the QSvgIconEngine
// - assemble the content as it is done by the QSvgIconEngine::write() (operator <<)
// - create the QIcon with QSvgIconEngine initialized from the content
const int index = qt_iconEngineFactoryLoader()->indexOf(QStringLiteral("svg"));
if (index != -1)
{
if (QIconEnginePlugin * factory = qobject_cast<QIconEnginePlugin*>(qt_iconEngineFactoryLoader()->instance(index)))
{
if (QIconEngine * engine = factory->create())
{
QByteArray engine_arr;
QDataStream str{&engine_arr, QIODevice::WriteOnly};
str.setVersion(QDataStream::Qt_4_4);
QHash<int, QString> filenames;
filenames[0] = filename;
QHash<int, QByteArray> svg_buffers;
svg_buffers[0] = processedContents;
str << filenames << static_cast<int>(0)/*isCompressed*/ << svg_buffers << static_cast<int>(0)/*hasAddedPimaps*/;
QDataStream str_read{&engine_arr, QIODevice::ReadOnly};
str_read.setVersion(QDataStream::Qt_4_4);
engine->read(str_read);
icon = QIcon{engine};
}
}
}
// load the icon directly from file, if still null
if (icon.isNull())
icon = QIcon(filename);
}
return icon.pixmap(size, mode, state);
}
QPixmap XdgIconLoaderEngine::pixmap(const QSize &size, QIcon::Mode mode,
QIcon::State state)
{
ensureLoaded();
XdgIconLoaderEngineEntry *entry = entryForSize(size);
QIconLoaderEngineEntry *entry = entryForSize(size);
if (entry)
return entry->pixmap(size, mode, state);
@ -853,6 +913,17 @@ void XdgIconLoaderEngine::virtual_hook(int id, void *data)
*reinterpret_cast<bool*>(data) = m_info.entries.isEmpty();
}
break;
#endif
#if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0)
case QIconEngine::ScaledPixmapHook:
{
QIconEngine::ScaledPixmapArgument &arg = *reinterpret_cast<QIconEngine::ScaledPixmapArgument*>(data);
// QIcon::pixmap() multiplies size by the device pixel ratio.
const int integerScale = qCeil(arg.scale);
QIconLoaderEngineEntry *entry = entryForSize(arg.size / integerScale, integerScale);
arg.pixmap = entry ? entry->pixmap(arg.size, arg.mode, arg.state) : QPixmap();
}
break;
#endif
default:
QIconEngine::virtual_hook(id, data);

@ -31,8 +31,8 @@
**
****************************************************************************/
#ifndef QICONLOADER_P_H
#define QICONLOADER_P_H
#ifndef XDGICONLOADER_P_H
#define XDGICONLOADER_P_H
#include <QtCore/qglobal.h>
@ -52,65 +52,19 @@
#include <QtGui/QIcon>
#include <QtGui/QIconEngine>
#include <QtGui/QPixmapCache>
#include <private/qicon_p.h>
#include <private/qfactoryloader_p.h>
#include <private/qiconloader_p.h>
#include <QtCore/QHash>
#include <QtCore/QVector>
#include <QtCore/QTypeInfo>
//QT_BEGIN_NAMESPACE
class XdgIconLoader;
struct XdgIconDirInfo
{
enum Type { Fixed, Scalable, Threshold };
XdgIconDirInfo(const QString &_path = QString()) :
path(_path),
size(0),
maxSize(0),
minSize(0),
threshold(0),
type(Threshold) {}
QString path;
short size;
short maxSize;
short minSize;
short threshold;
Type type;
};
class XdgIconLoaderEngineEntry
{
public:
virtual ~XdgIconLoaderEngineEntry() {}
virtual QPixmap pixmap(const QSize &size,
QIcon::Mode mode,
QIcon::State state) = 0;
QString filename;
XdgIconDirInfo dir;
static int count;
};
struct ScalableEntry : public XdgIconLoaderEngineEntry
{
QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) Q_DECL_OVERRIDE;
QIcon svgIcon;
};
struct PixmapEntry : public XdgIconLoaderEngineEntry
struct ScalableFollowsColorEntry : public ScalableEntry
{
QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) Q_DECL_OVERRIDE;
QPixmap basePixmap;
};
typedef QList<XdgIconLoaderEngineEntry*> QThemeIconEntries;
struct QThemeIconInfo
{
QThemeIconEntries entries;
QString iconName;
QIcon svgSelectedIcon;
};
//class QIconLoaderEngine : public QIconEngine
@ -120,19 +74,19 @@ public:
XdgIconLoaderEngine(const QString& iconName = QString());
~XdgIconLoaderEngine();
void paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state);
QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state);
QSize actualSize(const QSize &size, QIcon::Mode mode, QIcon::State state);
QIconEngine *clone() const;
bool read(QDataStream &in);
bool write(QDataStream &out) const;
void paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state) Q_DECL_OVERRIDE;
QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) Q_DECL_OVERRIDE;
QSize actualSize(const QSize &size, QIcon::Mode mode, QIcon::State state) Q_DECL_OVERRIDE;
QIconEngine *clone() const Q_DECL_OVERRIDE;
bool read(QDataStream &in) Q_DECL_OVERRIDE;
bool write(QDataStream &out) const Q_DECL_OVERRIDE;
private:
QString key() const;
QString key() const Q_DECL_OVERRIDE;
bool hasIcon() const;
void ensureLoaded();
void virtual_hook(int id, void *data);
XdgIconLoaderEngineEntry *entryForSize(const QSize &size);
void virtual_hook(int id, void *data) Q_DECL_OVERRIDE;
QIconLoaderEngineEntry *entryForSize(const QSize &size, int scale = 1);
XdgIconLoaderEngine(const XdgIconLoaderEngine &other);
QThemeIconInfo m_info;
QString m_iconName;
@ -143,20 +97,24 @@ private:
class QIconCacheGtkReader;
class QIconTheme
// Note: We can't simply reuse the QIconTheme from Qt > 5.7 because
// the QIconTheme constructor symbol isn't exported.
class XdgIconTheme
{
public:
QIconTheme(const QString &name);
QIconTheme() : m_valid(false) {}
XdgIconTheme(const QString &name);
XdgIconTheme() = default;
QStringList parents() { return m_parents; }
QVector <XdgIconDirInfo> keyList() { return m_keyList; }
QVector <QIconDirInfo> keyList() { return m_keyList; }
QStringList contentDirs() { return m_contentDirs; }
bool isValid() { return m_valid; }
bool isValid() const { return m_valid; }
bool followsColorScheme() const { return m_followsColorScheme; }
private:
QStringList m_contentDirs;
QVector <XdgIconDirInfo> m_keyList;
QVector <QIconDirInfo> m_keyList;
QStringList m_parents;
bool m_valid;
bool m_valid = false;
bool m_followsColorScheme = false;
public:
QVector<QSharedPointer<QIconCacheGtkReader>> m_gtkCaches;
};
@ -164,43 +122,39 @@ public:
class XDGICONLOADER_EXPORT XdgIconLoader
{
public:
XdgIconLoader();
QThemeIconInfo loadIcon(const QString &iconName) const;
uint themeKey() const { return m_themeKey; }
QString themeName() const { return m_userTheme.isEmpty() ? m_systemTheme : m_userTheme; }
void setThemeName(const QString &themeName);
QIconTheme theme() { return themeList.value(themeName()); }
void setThemeSearchPath(const QStringList &searchPaths);
QStringList themeSearchPaths() const;
XdgIconDirInfo dirInfo(int dirindex);
/* TODO: deprecate & remove all QIconLoader wrappers */
inline uint themeKey() const { return QIconLoader::instance()->themeKey(); }
inline QString themeName() const { return QIconLoader::instance()->themeName(); }
inline void setThemeName(const QString &themeName) { QIconLoader::instance()->setThemeName(themeName); }
inline void setThemeSearchPath(const QStringList &searchPaths) { QIconLoader::instance()->setThemeSearchPath(searchPaths); }
inline QIconDirInfo dirInfo(int dirindex) { return QIconLoader::instance()->dirInfo(dirindex); }
inline QStringList themeSearchPaths() const { return QIconLoader::instance()->themeSearchPaths(); }
inline void updateSystemTheme() { QIconLoader::instance()->updateSystemTheme(); }
inline void invalidateKey() { QIconLoader::instance()->invalidateKey(); }
inline void ensureInitialized() { QIconLoader::instance()->ensureInitialized(); }
inline bool hasUserTheme() const { return QIconLoader::instance()->hasUserTheme(); }
/*!
* Flag if the "FollowsColorScheme" hint (the KDE extension to XDG
* themes) should be honored.
*/
inline bool followColorScheme() const { return m_followColorScheme; }
void setFollowColorScheme(bool enable);
XdgIconTheme theme() { return themeList.value(QIconLoader::instance()->themeName()); }
static XdgIconLoader *instance();
void updateSystemTheme();
void invalidateKey() { m_themeKey++; }
void ensureInitialized();
bool hasUserTheme() const { return !m_userTheme.isEmpty(); }
private:
QThemeIconInfo findIconHelper(const QString &themeName,
const QString &iconName,
QStringList &visited) const;
uint m_themeKey;
bool m_supportsSvg;
bool m_initialized;
mutable QString m_userTheme;
mutable QString m_systemTheme;
mutable QStringList m_iconDirs;
mutable QHash <QString, QIconTheme> themeList;
QStringList &visited,
bool dashFallback = false) const;
QThemeIconInfo unthemedFallback(const QString &iconName, const QStringList &searchPaths) const;
mutable QHash <QString, XdgIconTheme> themeList;
bool m_followColorScheme = true;
};
// Note: class template specialization of 'QTypeInfo' must occur at
// global scope
Q_DECLARE_TYPEINFO(XdgIconDirInfo, Q_MOVABLE_TYPE);
//QT_END_NAMESPACE
#endif // QT_NO_ICON
#endif // QICONLOADER_P_H
#endif // XDGICONLOADER_P_H

Loading…
Cancel
Save