Signed-off-by: Andrew Lee (李健秋) <ajqlee@debian.org>ubuntu/cosmic upstream/1.2.0
commit
8762635176
@ -0,0 +1,8 @@
|
|||||||
|
build*
|
||||||
|
*.qm
|
||||||
|
*~
|
||||||
|
*.autosave
|
||||||
|
*-swp
|
||||||
|
*.swp
|
||||||
|
CMakeLists.txt.user*
|
||||||
|
nbproject/
|
@ -0,0 +1,10 @@
|
|||||||
|
Upstream Authors:
|
||||||
|
LXQt team: http://lxqt.org
|
||||||
|
Razor team: http://razor-qt.org
|
||||||
|
|
||||||
|
Copyright:
|
||||||
|
Copyright (c) 2010-2012 Razor team
|
||||||
|
Copyright (c) 2012-2014 LXQt team
|
||||||
|
|
||||||
|
License: GPL-2 and LGPL-2.1+
|
||||||
|
The full text of the licenses can be found in the 'COPYING' file.
|
@ -0,0 +1,354 @@
|
|||||||
|
cmake_minimum_required( VERSION 2.8.5 )
|
||||||
|
|
||||||
|
project(libqtxdg)
|
||||||
|
|
||||||
|
# Support different versions of Qt
|
||||||
|
option(USE_QT4 "Build with Qt4." $ENV{USE_QT4})
|
||||||
|
option(BUILD_TESTS "Builds tests" OFF)
|
||||||
|
|
||||||
|
if (USE_QT4)
|
||||||
|
set(USE_QT5 FALSE)
|
||||||
|
else()
|
||||||
|
set(USE_QT5 TRUE)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# The Qt4 version can be compiled with libmagic or with QtMimeTypes
|
||||||
|
# QtMimeTypes is the preferred way and also the default. libmagic will be
|
||||||
|
# dropped in future releases.
|
||||||
|
if (NOT USE_QT5)
|
||||||
|
option(USE_QTMIMETYPES "Use QtMimeTypes library" ON)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Standard directories for installation
|
||||||
|
include(GNUInstallDirs)
|
||||||
|
|
||||||
|
# additional cmake files
|
||||||
|
set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}" "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||||
|
|
||||||
|
set (MAJOR_VERSION 1)
|
||||||
|
set (MINOR_VERSION 2)
|
||||||
|
set (PATCH_VERSION 0)
|
||||||
|
set(QTXDG_VERSION_STRING ${MAJOR_VERSION}.${MINOR_VERSION}.${PATCH_VERSION})
|
||||||
|
|
||||||
|
add_definitions(-Wall -DQTXDG_COMPILATION=1)
|
||||||
|
|
||||||
|
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
|
||||||
|
set(QTXDG_COMPILER_IS_CLANGCXX 1)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (CMAKE_COMPILER_IS_GNUCXX OR QTXDG_COMPILER_IS_CLANGCXX)
|
||||||
|
# set visibility to hidden to hide symbols, unless they're exported manually in the code
|
||||||
|
set(CMAKE_CXX_FLAGS "-fvisibility=hidden -fvisibility-inlines-hidden -fno-exceptions ${CMAKE_CXX_FLAGS}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
find_package(PkgConfig)
|
||||||
|
|
||||||
|
if (USE_QT5)
|
||||||
|
cmake_minimum_required(VERSION 2.8.11)
|
||||||
|
find_package(Qt5Widgets REQUIRED QUIET)
|
||||||
|
find_package(Qt5Xml REQUIRED QUIET)
|
||||||
|
find_package(Qt5DBus REQUIRED QUIET)
|
||||||
|
if (BUILD_TESTS)
|
||||||
|
find_package(Qt5Test REQUIRED QUIET)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# if both Qt4 and Qt5 are installed we must check what version was found
|
||||||
|
if (NOT ${Qt5Core_VERSION_MAJOR} EQUAL 5)
|
||||||
|
message(FATAL_ERROR "Qt was found, but NOT Qt5.")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(QTXDGX_LIBRARY_NAME "Qt5Xdg")
|
||||||
|
set(QTXDGX_FILE_NAME "qt5xdg")
|
||||||
|
|
||||||
|
set(QTXDGX_PKG_CONFIG_DESCRIPTION "Qt5Xdg, a Qt5 implementation of XDG standards")
|
||||||
|
set(QTXDGX_PKG_CONFIG_REQUIRES "Qt5Core, Qt5Xml, Qt5Widgets, Qt5DBus")
|
||||||
|
|
||||||
|
include_directories(
|
||||||
|
"${Qt5Widgets_INCLUDE_DIRS}"
|
||||||
|
"${Qt5Gui_PRIVATE_INCLUDE_DIRS}"
|
||||||
|
"${Qt5Xml_INCLUDE_DIRS}"
|
||||||
|
)
|
||||||
|
add_definitions(${Qt5Core_DEFINITIONS})
|
||||||
|
# set(CMAKE_CXX_FLAGS
|
||||||
|
# "${CMAKE_CXX_FLAGS} ${Qt5Widgets_EXECUTABLE_COMPILE_FLAGS}"
|
||||||
|
# )
|
||||||
|
|
||||||
|
# QMimeDatabase and QMimeType are part of Qt5Core
|
||||||
|
# We just use that as an mimetype provider.
|
||||||
|
# An empty MIMETYPES_PROVIDER_LIBRARY means we are using Qt internal
|
||||||
|
# mimetypes support
|
||||||
|
set(MIMETYPES_PROVIDER_LIBRARY "")
|
||||||
|
add_definitions("-DHAVE_QTMIMETYPES")
|
||||||
|
|
||||||
|
set(QTX_LIBRARIES ${Qt5Widgets_LIBRARIES} ${Qt5Xml_LIBRARIES} ${Qt5DBus_LIBRARIES})
|
||||||
|
message(STATUS "Building with Qt ${Qt5Core_VERSION_STRING}")
|
||||||
|
else()
|
||||||
|
find_package(Qt4 REQUIRED QtCore QtGui QtXml QtDBus QUIET)
|
||||||
|
if (BUILD_TESTS)
|
||||||
|
find_package(Qt4 REQUIRED QtTest QUIET)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# if both Qt4 and Qt5 are installed we must check what version was found
|
||||||
|
if (NOT ${QT_VERSION_MAJOR} EQUAL 4)
|
||||||
|
message(FATAL_ERROR "Qt was found, but NOT Qt4")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(QTXDGX_LIBRARY_NAME "qtxdg")
|
||||||
|
set(QTXDGX_FILE_NAME "qtxdg")
|
||||||
|
|
||||||
|
set(QTXDGX_PKG_CONFIG_DESCRIPTION "QtXdg, a Qt4 implementation of XDG standards")
|
||||||
|
|
||||||
|
include(${QT_USE_FILE})
|
||||||
|
set(QTX_LIBRARIES
|
||||||
|
${QT_QTCORE_LIBRARY}
|
||||||
|
${QT_QTGUI_LIBRARY}
|
||||||
|
${QT_QTXML_LIBRARY}
|
||||||
|
${QT_QTDBUS_LIBRARY}
|
||||||
|
)
|
||||||
|
message(STATUS "Building with Qt ${QTVERSION}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
set(libqtxdg_PUBLIC_H_FILES
|
||||||
|
xdgaction.h
|
||||||
|
xdgdesktopfile.h
|
||||||
|
xdgdirs.h
|
||||||
|
xdgicon.h
|
||||||
|
xdgmenu.h
|
||||||
|
xdgmenuwidget.h
|
||||||
|
xmlhelper.h
|
||||||
|
xdgautostart.h
|
||||||
|
xdgmacros.h
|
||||||
|
)
|
||||||
|
|
||||||
|
set(libqtxdg_PUBLIC_CLASSES
|
||||||
|
XdgAction
|
||||||
|
XdgDesktopFile
|
||||||
|
XdgDirs
|
||||||
|
XdgIcon
|
||||||
|
XdgMenu
|
||||||
|
XdgMenuWidget
|
||||||
|
XmlHelper
|
||||||
|
XdgAutoStart
|
||||||
|
)
|
||||||
|
|
||||||
|
set(libqtxdg_PRIVATE_H_FILES
|
||||||
|
xdgmenuapplinkprocessor.h
|
||||||
|
xdgmenulayoutprocessor.h
|
||||||
|
xdgmenu_p.h
|
||||||
|
xdgmenureader.h
|
||||||
|
xdgmenurules.h
|
||||||
|
xdgdesktopfile_p.h
|
||||||
|
)
|
||||||
|
|
||||||
|
set(libqtxdg_CPP_FILES
|
||||||
|
xdgaction.cpp
|
||||||
|
xdgdesktopfile.cpp
|
||||||
|
xdgdirs.cpp
|
||||||
|
xdgicon.cpp
|
||||||
|
xdgmenuapplinkprocessor.cpp
|
||||||
|
xdgmenu.cpp
|
||||||
|
xdgmenulayoutprocessor.cpp
|
||||||
|
xdgmenureader.cpp
|
||||||
|
xdgmenurules.cpp
|
||||||
|
xdgmenuwidget.cpp
|
||||||
|
xmlhelper.cpp
|
||||||
|
xdgautostart.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
set(libqtxdg_MOCS
|
||||||
|
xdgaction.h
|
||||||
|
xdgmenuapplinkprocessor.h
|
||||||
|
xdgmenu.h
|
||||||
|
xdgmenu_p.h
|
||||||
|
xdgmenureader.h
|
||||||
|
xdgmenurules.h
|
||||||
|
xdgmenuwidget.h
|
||||||
|
)
|
||||||
|
|
||||||
|
if (USE_QT5)
|
||||||
|
list(APPEND libqtxdg_PRIVATE_INSTALLABLE_H_FILES qiconfix/qiconloader_p.h)
|
||||||
|
list(APPEND libqtxdg_CPP_FILES qiconfix/qiconloader.cpp)
|
||||||
|
else()
|
||||||
|
list(APPEND libqtxdg_PRIVATE_H_FILES qiconfix/qiconloader_p_qt4.h)
|
||||||
|
list(APPEND libqtxdg_CPP_FILES qiconfix/qiconloader_qt4.cpp)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (NOT USE_QT5)
|
||||||
|
if (USE_QTMIMETYPES)
|
||||||
|
# Using QtMimeTypes to provide a better mimetype support on Qt4
|
||||||
|
# Project repo: https://qt.gitorious.org/qtplayground/mimetypes
|
||||||
|
pkg_check_modules(QTMIMETYPES REQUIRED
|
||||||
|
QtMimeTypes
|
||||||
|
)
|
||||||
|
include_directories("${QTMIMETYPES_INCLUDE_DIRS}")
|
||||||
|
set(QTXDGX_PKG_CONFIG_REQUIRES "QtCore, QtXml, QtDbus, QtMimeTypes")
|
||||||
|
set(MIMETYPES_PROVIDER_LIBRARY ${QTMIMETYPES_LIBRARIES})
|
||||||
|
link_directories("${QTMIMETYPES_LIBRARY_DIRS}")
|
||||||
|
add_definitions("-DHAVE_QTMIMETYPES")
|
||||||
|
else()
|
||||||
|
# Use libmagic
|
||||||
|
find_package(LibMagic REQUIRED QUIET)
|
||||||
|
set(MIMETYPES_PROVIDER_LIBRARY ${LIBMAGIC_LIBRARY})
|
||||||
|
set(QTXDGX_PKG_CONFIG_REQUIRES "QtCore, QtXml, QtDBus")
|
||||||
|
|
||||||
|
list(APPEND libqtxdg_PUBLIC_H_FILES xdgmime.h)
|
||||||
|
list(APPEND libqtxdg_PUBLIC_CLASSES XdgMime)
|
||||||
|
list(APPEND libqtxdg_CPP_FILES xdgmime.cpp)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (USE_QTMIMETYPES OR USE_QT5)
|
||||||
|
list(APPEND libqtxdg_PUBLIC_H_FILES xdgmimetype.h)
|
||||||
|
list(APPEND libqtxdg_PUBLIC_CLASSES XdgMimeType)
|
||||||
|
list(APPEND libqtxdg_CPP_FILES xdgmimetype.cpp)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
set(APP_SHARE_DIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/lib${QTXDGX_FILE_NAME}")
|
||||||
|
add_definitions(-DTRANSLATIONS_DIR=\"${APP_SHARE_DIR}\")
|
||||||
|
|
||||||
|
|
||||||
|
#************************************************
|
||||||
|
# Build 2 config.cmake files
|
||||||
|
# One for in-tree build and second for normal one.
|
||||||
|
#************************************************
|
||||||
|
set(QTXDG_MAJOR_VERSION ${MAJOR_VERSION})
|
||||||
|
set(QTXDG_MINOR_VERSION ${MINOR_VERSION})
|
||||||
|
set(QTXDG_PATCH_VERSION ${PATCH_VERSION})
|
||||||
|
|
||||||
|
# In tree compilation ......................
|
||||||
|
set(QTXDG_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR};${CMAKE_CURRENT_BINARY_DIR}")
|
||||||
|
|
||||||
|
if (USE_QT5)
|
||||||
|
set(QTXDG_PRIVATE_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/qiconfix")
|
||||||
|
configure_file(
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/qt5xdg-config.cmake.in"
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/cmake/${QTXDGX_FILE_NAME}-config.cmake"
|
||||||
|
@ONLY
|
||||||
|
)
|
||||||
|
else()
|
||||||
|
configure_file(
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/qtxdg-config.cmake.in"
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/cmake/${QTXDGX_FILE_NAME}-config.cmake"
|
||||||
|
@ONLY
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
configure_file(
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/${QTXDGX_FILE_NAME}_use.cmake"
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/cmake/${QTXDGX_FILE_NAME}_use.cmake"
|
||||||
|
@ONLY
|
||||||
|
)
|
||||||
|
|
||||||
|
# Instalable ...............................
|
||||||
|
set(QTXDG_INCLUDE_DIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}/${QTXDGX_FILE_NAME}")
|
||||||
|
|
||||||
|
if (USE_QT5)
|
||||||
|
set(QTXDG_PRIVATE_INCLUDE_DIR "${QTXDG_INCLUDE_DIR}/${QTXDG_VERSION_STRING}")
|
||||||
|
configure_file(
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/qt5xdg-config.cmake.in"
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/${QTXDGX_FILE_NAME}-config.cmake"
|
||||||
|
@ONLY
|
||||||
|
)
|
||||||
|
else()
|
||||||
|
configure_file(
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/qtxdg-config.cmake.in"
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/${QTXDGX_FILE_NAME}-config.cmake"
|
||||||
|
@ONLY
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
#**********************************************************
|
||||||
|
|
||||||
|
include(FindLibSuffix)
|
||||||
|
|
||||||
|
if(USE_QT5)
|
||||||
|
QT5_WRAP_CPP(libqtxdg_CXX_FILES ${libqtxdg_MOCS})
|
||||||
|
else()
|
||||||
|
QT4_WRAP_CPP(libqtxdg_CXX_FILES ${libqtxdg_MOCS})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
if (NOT CMAKE_BUILD_TYPE)
|
||||||
|
set ( CMAKE_BUILD_TYPE Release )
|
||||||
|
endif (NOT CMAKE_BUILD_TYPE)
|
||||||
|
|
||||||
|
|
||||||
|
add_library(${QTXDGX_LIBRARY_NAME} SHARED
|
||||||
|
${libqtxdg_PUBLIC_H_FILES}
|
||||||
|
${libqtxdg_PRIVATE_H_FILES}
|
||||||
|
${libqtxdg_PRIVATE_INSTALLABLE_H_FILES}
|
||||||
|
${libqtxdg_PRIVATE_H_FILES}
|
||||||
|
${libqtxdg_CPP_FILES}
|
||||||
|
${libqtxdg_CXX_FILES}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
target_link_libraries(${QTXDGX_LIBRARY_NAME}
|
||||||
|
${QTX_LIBRARIES}
|
||||||
|
${MIMETYPES_PROVIDER_LIBRARY}
|
||||||
|
)
|
||||||
|
|
||||||
|
set_target_properties(${QTXDGX_LIBRARY_NAME} PROPERTIES
|
||||||
|
VERSION ${MAJOR_VERSION}.${MINOR_VERSION}.${PATCH_VERSION}
|
||||||
|
SOVERSION ${MAJOR_VERSION}
|
||||||
|
)
|
||||||
|
|
||||||
|
# create the portable headers
|
||||||
|
include(create_portable_headers)
|
||||||
|
create_portable_headers(libqtxdg_PORTABLE_HEADERS ${libqtxdg_PUBLIC_CLASSES})
|
||||||
|
|
||||||
|
install(TARGETS ${QTXDGX_LIBRARY_NAME} DESTINATION "${CMAKE_INSTALL_LIBDIR}")
|
||||||
|
install(FILES ${libqtxdg_PUBLIC_H_FILES} DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${QTXDGX_FILE_NAME}")
|
||||||
|
|
||||||
|
if (USE_QT5)
|
||||||
|
install(FILES
|
||||||
|
${libqtxdg_PRIVATE_INSTALLABLE_H_FILES}
|
||||||
|
DESTINATION
|
||||||
|
"${QTXDG_PRIVATE_INCLUDE_DIR}/private/qtxdg"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${QTXDGX_FILE_NAME}-config.cmake" DESTINATION "${CMAKE_INSTALL_DATADIR}/cmake/${QTXDGX_FILE_NAME}")
|
||||||
|
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/cmake/${QTXDGX_FILE_NAME}_use.cmake" DESTINATION "${CMAKE_INSTALL_DATADIR}/cmake/${QTXDGX_FILE_NAME}")
|
||||||
|
install(FILES ${libqtxdg_PORTABLE_HEADERS} DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${QTXDGX_FILE_NAME}")
|
||||||
|
|
||||||
|
include(create_pkgconfig_file)
|
||||||
|
create_pkgconfig_file(${QTXDGX_LIBRARY_NAME}
|
||||||
|
${QTXDGX_PKG_CONFIG_DESCRIPTION}
|
||||||
|
${QTXDGX_PKG_CONFIG_REQUIRES}
|
||||||
|
${QTXDGX_FILE_NAME}
|
||||||
|
${QTXDG_VERSION_STRING}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if(BUILD_TESTS)
|
||||||
|
enable_testing()
|
||||||
|
add_definitions(-DQTXDG_TESTS=1)
|
||||||
|
add_subdirectory(test)
|
||||||
|
else()
|
||||||
|
message(STATUS "")
|
||||||
|
message(STATUS "For building tests use -DBUILD_TESTS=Yes option.")
|
||||||
|
message(STATUS "")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# uninstall target
|
||||||
|
configure_file(
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in"
|
||||||
|
"${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 ${MAJOR_VERSION})
|
||||||
|
set (CPACK_PACKAGE_VERSION_MINOR ${MINOR_VERSION})
|
||||||
|
set (CPACK_PACKAGE_VERSION_PATCH ${PATCH_VERSION})
|
||||||
|
set (CPACK_GENERATOR TBZ2)
|
||||||
|
set (CPACK_SOURCE_GENERATOR TBZ2)
|
||||||
|
set (CPACK_SOURCE_IGNORE_FILES /build/;.gitignore;.*~;.git;.kdev4;temp)
|
||||||
|
include (CPack)
|
@ -0,0 +1,461 @@
|
|||||||
|
|
||||||
|
GNU LESSER GENERAL PUBLIC LICENSE
|
||||||
|
Version 2.1, February 1999
|
||||||
|
|
||||||
|
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||||
|
51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
[This is the first released version of the Lesser GPL. It also counts
|
||||||
|
as the successor of the GNU Library Public License, version 2, hence
|
||||||
|
the version number 2.1.]
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The licenses for most software are designed to take away your
|
||||||
|
freedom to share and change it. By contrast, the GNU General Public
|
||||||
|
Licenses are intended to guarantee your freedom to share and change
|
||||||
|
free software--to make sure the software is free for all its users.
|
||||||
|
|
||||||
|
This license, the Lesser General Public License, applies to some
|
||||||
|
specially designated software packages--typically libraries--of the
|
||||||
|
Free Software Foundation and other authors who decide to use it. You
|
||||||
|
can use it too, but we suggest you first think carefully about whether
|
||||||
|
this license or the ordinary General Public License is the better
|
||||||
|
strategy to use in any particular case, based on the explanations
|
||||||
|
below.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom of use,
|
||||||
|
not price. Our General Public Licenses are designed to make sure that
|
||||||
|
you have the freedom to distribute copies of free software (and charge
|
||||||
|
for this service if you wish); that you receive source code or can get
|
||||||
|
it if you want it; that you can change the software and use pieces of
|
||||||
|
it in new free programs; and that you are informed that you can do
|
||||||
|
these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to make restrictions that forbid
|
||||||
|
distributors to deny you these rights or to ask you to surrender these
|
||||||
|
rights. These restrictions translate to certain responsibilities for
|
||||||
|
you if you distribute copies of the library or if you modify it.
|
||||||
|
|
||||||
|
For example, if you distribute copies of the library, whether gratis
|
||||||
|
or for a fee, you must give the recipients all the rights that we gave
|
||||||
|
you. You must make sure that they, too, receive or can get the source
|
||||||
|
code. If you link other code with the library, you must provide
|
||||||
|
complete object files to the recipients, so that they can relink them
|
||||||
|
with the library after making changes to the library and recompiling
|
||||||
|
it. And you must show them these terms so they know their rights.
|
||||||
|
|
||||||
|
We protect your rights with a two-step method: (1) we copyright the
|
||||||
|
library, and (2) we offer you this license, which gives you legal
|
||||||
|
permission to copy, distribute and/or modify the library.
|
||||||
|
|
||||||
|
To protect each distributor, we want to make it very clear that
|
||||||
|
there is no warranty for the free library. Also, if the library is
|
||||||
|
modified by someone else and passed on, the recipients should know
|
||||||
|
that what they have is not the original version, so that the original
|
||||||
|
author's reputation will not be affected by problems that might be
|
||||||
|
introduced by others.
|
||||||
|
|
||||||
|
Finally, software patents pose a constant threat to the existence of
|
||||||
|
any free program. We wish to make sure that a company cannot
|
||||||
|
effectively restrict the users of a free program by obtaining a
|
||||||
|
restrictive license from a patent holder. Therefore, we insist that
|
||||||
|
any patent license obtained for a version of the library must be
|
||||||
|
consistent with the full freedom of use specified in this license.
|
||||||
|
|
||||||
|
Most GNU software, including some libraries, is covered by the
|
||||||
|
ordinary GNU General Public License. This license, the GNU Lesser
|
||||||
|
General Public License, applies to certain designated libraries, and
|
||||||
|
is quite different from the ordinary General Public License. We use
|
||||||
|
this license for certain libraries in order to permit linking those
|
||||||
|
libraries into non-free programs.
|
||||||
|
|
||||||
|
When a program is linked with a library, whether statically or using
|
||||||
|
a shared library, the combination of the two is legally speaking a
|
||||||
|
combined work, a derivative of the original library. The ordinary
|
||||||
|
General Public License therefore permits such linking only if the
|
||||||
|
entire combination fits its criteria of freedom. The Lesser General
|
||||||
|
Public License permits more lax criteria for linking other code with
|
||||||
|
the library.
|
||||||
|
|
||||||
|
We call this license the "Lesser" General Public License because it
|
||||||
|
does Less to protect the user's freedom than the ordinary General
|
||||||
|
Public License. It also provides other free software developers Less
|
||||||
|
of an advantage over competing non-free programs. These disadvantages
|
||||||
|
are the reason we use the ordinary General Public License for many
|
||||||
|
libraries. However, the Lesser license provides advantages in certain
|
||||||
|
special circumstances.
|
||||||
|
|
||||||
|
For example, on rare occasions, there may be a special need to
|
||||||
|
encourage the widest possible use of a certain library, so that it
|
||||||
|
becomes a de-facto standard. To achieve this, non-free programs must
|
||||||
|
be allowed to use the library. A more frequent case is that a free
|
||||||
|
library does the same job as widely used non-free libraries. In this
|
||||||
|
case, there is little to gain by limiting the free library to free
|
||||||
|
software only, so we use the Lesser General Public License.
|
||||||
|
|
||||||
|
In other cases, permission to use a particular library in non-free
|
||||||
|
programs enables a greater number of people to use a large body of
|
||||||
|
free software. For example, permission to use the GNU C Library in
|
||||||
|
non-free programs enables many more people to use the whole GNU
|
||||||
|
operating system, as well as its variant, the GNU/Linux operating
|
||||||
|
system.
|
||||||
|
|
||||||
|
Although the Lesser General Public License is Less protective of the
|
||||||
|
users' freedom, it does ensure that the user of a program that is
|
||||||
|
linked with the Library has the freedom and the wherewithal to run
|
||||||
|
that program using a modified version of the Library.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow. Pay close attention to the difference between a
|
||||||
|
"work based on the library" and a "work that uses the library". The
|
||||||
|
former contains code derived from the library, whereas the latter must
|
||||||
|
be combined with the library in order to run.
|
||||||
|
|
||||||
|
GNU LESSER GENERAL PUBLIC LICENSE
|
||||||
|
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
|
||||||
|
0. This License Agreement applies to any software library or other
|
||||||
|
program which contains a notice placed by the copyright holder or
|
||||||
|
other authorized party saying it may be distributed under the terms of
|
||||||
|
this Lesser General Public License (also called "this License").
|
||||||
|
Each licensee is addressed as "you".
|
||||||
|
|
||||||
|
A "library" means a collection of software functions and/or data
|
||||||
|
prepared so as to be conveniently linked with application programs
|
||||||
|
(which use some of those functions and data) to form executables.
|
||||||
|
|
||||||
|
The "Library", below, refers to any such software library or work
|
||||||
|
which has been distributed under these terms. A "work based on the
|
||||||
|
Library" means either the Library or any derivative work under
|
||||||
|
copyright law: that is to say, a work containing the Library or a
|
||||||
|
portion of it, either verbatim or with modifications and/or translated
|
||||||
|
straightforwardly into another language. (Hereinafter, translation is
|
||||||
|
included without limitation in the term "modification".)
|
||||||
|
|
||||||
|
"Source code" for a work means the preferred form of the work for
|
||||||
|
making modifications to it. For a library, complete source code means
|
||||||
|
all the source code for all modules it contains, plus any associated
|
||||||
|
interface definition files, plus the scripts used to control
|
||||||
|
compilation and installation of the library.
|
||||||
|
|
||||||
|
Activities other than copying, distribution and modification are not
|
||||||
|
covered by this License; they are outside its scope. The act of
|
||||||
|
running a program using the Library is not restricted, and output from
|
||||||
|
such a program is covered only if its contents constitute a work based
|
||||||
|
on the Library (independent of the use of the Library in a tool for
|
||||||
|
writing it). Whether that is true depends on what the Library does
|
||||||
|
and what the program that uses the Library does.
|
||||||
|
|
||||||
|
1. You may copy and distribute verbatim copies of the Library's
|
||||||
|
complete source code as you receive it, in any medium, provided that
|
||||||
|
you conspicuously and appropriately publish on each copy an
|
||||||
|
appropriate copyright notice and disclaimer of warranty; keep intact
|
||||||
|
all the notices that refer to this License and to the absence of any
|
||||||
|
warranty; and distribute a copy of this License along with the
|
||||||
|
Library.
|
||||||
|
|
||||||
|
You may charge a fee for the physical act of transferring a copy,
|
||||||
|
and you may at your option offer warranty protection in exchange for a
|
||||||
|
fee.
|
||||||
|
|
||||||
|
2. You may modify your copy or copies of the Library or any portion
|
||||||
|
of it, thus forming a work based on the Library, and copy and
|
||||||
|
distribute such modifications or work under the terms of Section 1
|
||||||
|
above, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) The modified work must itself be a software library.
|
||||||
|
|
||||||
|
b) You must cause the files modified to carry prominent notices
|
||||||
|
stating that you changed the files and the date of any change.
|
||||||
|
|
||||||
|
c) You must cause the whole of the work to be licensed at no
|
||||||
|
charge to all third parties under the terms of this License.
|
||||||
|
|
||||||
|
d) If a facility in the modified Library refers to a function or a
|
||||||
|
table of data to be supplied by an application program that uses
|
||||||
|
the facility, other than as an argument passed when the facility
|
||||||
|
is invoked, then you must make a good faith effort to ensure that,
|
||||||
|
in the event an application does not supply such function or
|
||||||
|
table, the facility still operates, and performs whatever part of
|
||||||
|
its purpose remains meaningful.
|
||||||
|
|
||||||
|
(For example, a function in a library to compute square roots has
|
||||||
|
a purpose that is entirely well-defined independent of the
|
||||||
|
application. Therefore, Subsection 2d requires that any
|
||||||
|
application-supplied function or table used by this function must
|
||||||
|
be optional: if the application does not supply it, the square
|
||||||
|
root function must still compute square roots.)
|
||||||
|
|
||||||
|
These requirements apply to the modified work as a whole. If
|
||||||
|
identifiable sections of that work are not derived from the Library,
|
||||||
|
and can be reasonably considered independent and separate works in
|
||||||
|
themselves, then this License, and its terms, do not apply to those
|
||||||
|
sections when you distribute them as separate works. But when you
|
||||||
|
distribute the same sections as part of a whole which is a work based
|
||||||
|
on the Library, the distribution of the whole must be on the terms of
|
||||||
|
this License, whose permissions for other licensees extend to the
|
||||||
|
entire whole, and thus to each and every part regardless of who wrote
|
||||||
|
it.
|
||||||
|
|
||||||
|
Thus, it is not the intent of this section to claim rights or contest
|
||||||
|
your rights to work written entirely by you; rather, the intent is to
|
||||||
|
exercise the right to control the distribution of derivative or
|
||||||
|
collective works based on the Library.
|
||||||
|
|
||||||
|
In addition, mere aggregation of another work not based on the Library
|
||||||
|
with the Library (or with a work based on the Library) on a volume of
|
||||||
|
a storage or distribution medium does not bring the other work under
|
||||||
|
the scope of this License.
|
||||||
|
|
||||||
|
3. You may opt to apply the terms of the ordinary GNU General Public
|
||||||
|
License instead of this License to a given copy of the Library. To do
|
||||||
|
this, you must alter all the notices that refer to this License, so
|
||||||
|
that they refer to the ordinary GNU General Public License, version 2,
|
||||||
|
instead of to this License. (If a newer version than version 2 of the
|
||||||
|
ordinary GNU General Public License has appeared, then you can specify
|
||||||
|
that version instead if you wish.) Do not make any other change in
|
||||||
|
these notices.
|
||||||
|
|
||||||
|
Once this change is made in a given copy, it is irreversible for
|
||||||
|
that copy, so the ordinary GNU General Public License applies to all
|
||||||
|
subsequent copies and derivative works made from that copy.
|
||||||
|
|
||||||
|
This option is useful when you wish to copy part of the code of
|
||||||
|
the Library into a program that is not a library.
|
||||||
|
|
||||||
|
4. You may copy and distribute the Library (or a portion or
|
||||||
|
derivative of it, under Section 2) in object code or executable form
|
||||||
|
under the terms of Sections 1 and 2 above provided that you accompany
|
||||||
|
it with the complete corresponding machine-readable source code, which
|
||||||
|
must be distributed under the terms of Sections 1 and 2 above on a
|
||||||
|
medium customarily used for software interchange.
|
||||||
|
|
||||||
|
If distribution of object code is made by offering access to copy
|
||||||
|
from a designated place, then offering equivalent access to copy the
|
||||||
|
source code from the same place satisfies the requirement to
|
||||||
|
distribute the source code, even though third parties are not
|
||||||
|
compelled to copy the source along with the object code.
|
||||||
|
|
||||||
|
5. A program that contains no derivative of any portion of the
|
||||||
|
Library, but is designed to work with the Library by being compiled or
|
||||||
|
linked with it, is called a "work that uses the Library". Such a
|
||||||
|
work, in isolation, is not a derivative work of the Library, and
|
||||||
|
therefore falls outside the scope of this License.
|
||||||
|
|
||||||
|
However, linking a "work that uses the Library" with the Library
|
||||||
|
creates an executable that is a derivative of the Library (because it
|
||||||
|
contains portions of the Library), rather than a "work that uses the
|
||||||
|
library". The executable is therefore covered by this License.
|
||||||
|
Section 6 states terms for distribution of such executables.
|
||||||
|
|
||||||
|
When a "work that uses the Library" uses material from a header file
|
||||||
|
that is part of the Library, the object code for the work may be a
|
||||||
|
derivative work of the Library even though the source code is not.
|
||||||
|
Whether this is true is especially significant if the work can be
|
||||||
|
linked without the Library, or if the work is itself a library. The
|
||||||
|
threshold for this to be true is not precisely defined by law.
|
||||||
|
|
||||||
|
If such an object file uses only numerical parameters, data
|
||||||
|
structure layouts and accessors, and small macros and small inline
|
||||||
|
functions (ten lines or less in length), then the use of the object
|
||||||
|
file is unrestricted, regardless of whether it is legally a derivative
|
||||||
|
work. (Executables containing this object code plus portions of the
|
||||||
|
Library will still fall under Section 6.)
|
||||||
|
|
||||||
|
Otherwise, if the work is a derivative of the Library, you may
|
||||||
|
distribute the object code for the work under the terms of Section 6.
|
||||||
|
Any executables containing that work also fall under Section 6,
|
||||||
|
whether or not they are linked directly with the Library itself.
|
||||||
|
|
||||||
|
6. As an exception to the Sections above, you may also combine or
|
||||||
|
link a "work that uses the Library" with the Library to produce a
|
||||||
|
work containing portions of the Library, and distribute that work
|
||||||
|
under terms of your choice, provided that the terms permit
|
||||||
|
modification of the work for the customer's own use and reverse
|
||||||
|
engineering for debugging such modifications.
|
||||||
|
|
||||||
|
You must give prominent notice with each copy of the work that the
|
||||||
|
Library is used in it and that the Library and its use are covered by
|
||||||
|
this License. You must supply a copy of this License. If the work
|
||||||
|
during execution displays copyright notices, you must include the
|
||||||
|
copyright notice for the Library among them, as well as a reference
|
||||||
|
directing the user to the copy of this License. Also, you must do one
|
||||||
|
of these things:
|
||||||
|
|
||||||
|
a) Accompany the work with the complete corresponding
|
||||||
|
machine-readable source code for the Library including whatever
|
||||||
|
changes were used in the work (which must be distributed under
|
||||||
|
Sections 1 and 2 above); and, if the work is an executable linked
|
||||||
|
with the Library, with the complete machine-readable "work that
|
||||||
|
uses the Library", as object code and/or source code, so that the
|
||||||
|
user can modify the Library and then relink to produce a modified
|
||||||
|
executable containing the modified Library. (It is understood
|
||||||
|
that the user who changes the contents of definitions files in the
|
||||||
|
Library will not necessarily be able to recompile the application
|
||||||
|
to use the modified definitions.)
|
||||||
|
|
||||||
|
b) Use a suitable shared library mechanism for linking with the
|
||||||
|
Library. A suitable mechanism is one that (1) uses at run time a
|
||||||
|
copy of the library already present on the user's computer system,
|
||||||
|
rather than copying library functions into the executable, and (2)
|
||||||
|
will operate properly with a modified version of the library, if
|
||||||
|
the user installs one, as long as the modified version is
|
||||||
|
interface-compatible with the version that the work was made with.
|
||||||
|
|
||||||
|
c) Accompany the work with a written offer, valid for at least
|
||||||
|
three years, to give the same user the materials specified in
|
||||||
|
Subsection 6a, above, for a charge no more than the cost of
|
||||||
|
performing this distribution.
|
||||||
|
|
||||||
|
d) If distribution of the work is made by offering access to copy
|
||||||
|
from a designated place, offer equivalent access to copy the above
|
||||||
|
specified materials from the same place.
|
||||||
|
|
||||||
|
e) Verify that the user has already received a copy of these
|
||||||
|
materials or that you have already sent this user a copy.
|
||||||
|
|
||||||
|
For an executable, the required form of the "work that uses the
|
||||||
|
Library" must include any data and utility programs needed for
|
||||||
|
reproducing the executable from it. However, as a special exception,
|
||||||
|
the materials to be distributed need not include anything that is
|
||||||
|
normally distributed (in either source or binary form) with the major
|
||||||
|
components (compiler, kernel, and so on) of the operating system on
|
||||||
|
which the executable runs, unless that component itself accompanies
|
||||||
|
the executable.
|
||||||
|
|
||||||
|
It may happen that this requirement contradicts the license
|
||||||
|
restrictions of other proprietary libraries that do not normally
|
||||||
|
accompany the operating system. Such a contradiction means you cannot
|
||||||
|
use both them and the Library together in an executable that you
|
||||||
|
distribute.
|
||||||
|
|
||||||
|
7. You may place library facilities that are a work based on the
|
||||||
|
Library side-by-side in a single library together with other library
|
||||||
|
facilities not covered by this License, and distribute such a combined
|
||||||
|
library, provided that the separate distribution of the work based on
|
||||||
|
the Library and of the other library facilities is otherwise
|
||||||
|
permitted, and provided that you do these two things:
|
||||||
|
|
||||||
|
a) Accompany the combined library with a copy of the same work
|
||||||
|
based on the Library, uncombined with any other library
|
||||||
|
facilities. This must be distributed under the terms of the
|
||||||
|
Sections above.
|
||||||
|
|
||||||
|
b) Give prominent notice with the combined library of the fact
|
||||||
|
that part of it is a work based on the Library, and explaining
|
||||||
|
where to find the accompanying uncombined form of the same work.
|
||||||
|
|
||||||
|
8. You may not copy, modify, sublicense, link with, or distribute
|
||||||
|
the Library except as expressly provided under this License. Any
|
||||||
|
attempt otherwise to copy, modify, sublicense, link with, or
|
||||||
|
distribute the Library is void, and will automatically terminate your
|
||||||
|
rights under this License. However, parties who have received copies,
|
||||||
|
or rights, from you under this License will not have their licenses
|
||||||
|
terminated so long as such parties remain in full compliance.
|
||||||
|
|
||||||
|
9. You are not required to accept this License, since you have not
|
||||||
|
signed it. However, nothing else grants you permission to modify or
|
||||||
|
distribute the Library or its derivative works. These actions are
|
||||||
|
prohibited by law if you do not accept this License. Therefore, by
|
||||||
|
modifying or distributing the Library (or any work based on the
|
||||||
|
Library), you indicate your acceptance of this License to do so, and
|
||||||
|
all its terms and conditions for copying, distributing or modifying
|
||||||
|
the Library or works based on it.
|
||||||
|
|
||||||
|
10. Each time you redistribute the Library (or any work based on the
|
||||||
|
Library), the recipient automatically receives a license from the
|
||||||
|
original licensor to copy, distribute, link with or modify the Library
|
||||||
|
subject to these terms and conditions. You may not impose any further
|
||||||
|
restrictions on the recipients' exercise of the rights granted herein.
|
||||||
|
You are not responsible for enforcing compliance by third parties with
|
||||||
|
this License.
|
||||||
|
|
||||||
|
11. If, as a consequence of a court judgment or allegation of patent
|
||||||
|
infringement or for any other reason (not limited to patent issues),
|
||||||
|
conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot
|
||||||
|
distribute so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you
|
||||||
|
may not distribute the Library at all. For example, if a patent
|
||||||
|
license would not permit royalty-free redistribution of the Library by
|
||||||
|
all those who receive copies directly or indirectly through you, then
|
||||||
|
the only way you could satisfy both it and this License would be to
|
||||||
|
refrain entirely from distribution of the Library.
|
||||||
|
|
||||||
|
If any portion of this section is held invalid or unenforceable under
|
||||||
|
any particular circumstance, the balance of the section is intended to
|
||||||
|
apply, and the section as a whole is intended to apply in other
|
||||||
|
circumstances.
|
||||||
|
|
||||||
|
It is not the purpose of this section to induce you to infringe any
|
||||||
|
patents or other property right claims or to contest validity of any
|
||||||
|
such claims; this section has the sole purpose of protecting the
|
||||||
|
integrity of the free software distribution system which is
|
||||||
|
implemented by public license practices. Many people have made
|
||||||
|
generous contributions to the wide range of software distributed
|
||||||
|
through that system in reliance on consistent application of that
|
||||||
|
system; it is up to the author/donor to decide if he or she is willing
|
||||||
|
to distribute software through any other system and a licensee cannot
|
||||||
|
impose that choice.
|
||||||
|
|
||||||
|
This section is intended to make thoroughly clear what is believed to
|
||||||
|
be a consequence of the rest of this License.
|
||||||
|
|
||||||
|
12. If the distribution and/or use of the Library is restricted in
|
||||||
|
certain countries either by patents or by copyrighted interfaces, the
|
||||||
|
original copyright holder who places the Library under this License
|
||||||
|
may add an explicit geographical distribution limitation excluding those
|
||||||
|
countries, so that distribution is permitted only in or among
|
||||||
|
countries not thus excluded. In such case, this License incorporates
|
||||||
|
the limitation as if written in the body of this License.
|
||||||
|
|
||||||
|
13. The Free Software Foundation may publish revised and/or new
|
||||||
|
versions of the Lesser General Public License from time to time.
|
||||||
|
Such new versions will be similar in spirit to the present version,
|
||||||
|
but may differ in detail to address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the Library
|
||||||
|
specifies a version number of this License which applies to it and
|
||||||
|
"any later version", you have the option of following the terms and
|
||||||
|
conditions either of that version or of any later version published by
|
||||||
|
the Free Software Foundation. If the Library does not specify a
|
||||||
|
license version number, you may choose any version ever published by
|
||||||
|
the Free Software Foundation.
|
||||||
|
|
||||||
|
14. If you wish to incorporate parts of the Library into other free
|
||||||
|
programs whose distribution conditions are incompatible with these,
|
||||||
|
write to the author to ask for permission. For software which is
|
||||||
|
copyrighted by the Free Software Foundation, write to the Free
|
||||||
|
Software Foundation; we sometimes make exceptions for this. Our
|
||||||
|
decision will be guided by the two goals of preserving the free status
|
||||||
|
of all derivatives of our free software and of promoting the sharing
|
||||||
|
and reuse of software generally.
|
||||||
|
|
||||||
|
NO WARRANTY
|
||||||
|
|
||||||
|
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||||
|
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||||
|
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||||
|
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
|
||||||
|
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
|
||||||
|
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
||||||
|
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
|
||||||
|
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
|
||||||
|
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
|
||||||
|
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
|
||||||
|
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
|
||||||
|
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
|
||||||
|
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
|
||||||
|
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
|
||||||
|
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||||
|
DAMAGES.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
@ -0,0 +1,43 @@
|
|||||||
|
Overview
|
||||||
|
========
|
||||||
|
libqtxdg is An Qt implementation of freedesktop.org xdg specifications.
|
||||||
|
It can be built with Qt4 and Qt5
|
||||||
|
|
||||||
|
|
||||||
|
Dependencies
|
||||||
|
============
|
||||||
|
Qt4 build:
|
||||||
|
Qt4
|
||||||
|
libmagic OR QtMimeTypes
|
||||||
|
|
||||||
|
QtMimeTypes the preffered. libmagic is deprecated and may be dropped in future
|
||||||
|
releases.
|
||||||
|
QtMimeTypes can be found at: https://qt.gitorious.org/qtplayground/mimetypes
|
||||||
|
|
||||||
|
Qt5 build:
|
||||||
|
Qt5
|
||||||
|
|
||||||
|
|
||||||
|
Configuration
|
||||||
|
============
|
||||||
|
libqtxdg uses the CMake build system. Everything that applies to CMake also
|
||||||
|
applies here.
|
||||||
|
|
||||||
|
Configuration options:
|
||||||
|
USE_QT5 Builds with Qt5, defaults to then environment variable
|
||||||
|
with the same name.
|
||||||
|
|
||||||
|
USE_QTMIMETYPES It only affects the Qt4 build. Builds using QtMimeTypes.
|
||||||
|
Defaults to On. If set to OFF libmagic will be used.
|
||||||
|
|
||||||
|
BUILD_TESTS Builds tests, defaults to OFF
|
||||||
|
|
||||||
|
Configuration Examples:
|
||||||
|
Build with Qt5 and build self tests:
|
||||||
|
cmake -DUSE_QT5=ON -DBUILD_TESTS=ON ..
|
||||||
|
|
||||||
|
Build with Qt4 and no tests using QtMimeTypes
|
||||||
|
cmake -DUSE_QT5=OFF ..
|
||||||
|
|
||||||
|
Build with Qt4, no tests and using libmagic
|
||||||
|
cmake -DUSE_QT5=OFF -DUSE_QTMIMETYPES=OFF ..
|
@ -0,0 +1,23 @@
|
|||||||
|
FIND_PATH(LIBMAGIC_INCLUDE_DIR magic.h)
|
||||||
|
|
||||||
|
FIND_LIBRARY(LIBMAGIC_LIBRARY NAMES magic)
|
||||||
|
|
||||||
|
IF (LIBMAGIC_INCLUDE_DIR AND LIBMAGIC_LIBRARY)
|
||||||
|
SET(LIBMAGIC_FOUND TRUE)
|
||||||
|
ENDIF (LIBMAGIC_INCLUDE_DIR AND LIBMAGIC_LIBRARY)
|
||||||
|
|
||||||
|
IF (LIBMAGIC_FOUND)
|
||||||
|
IF (NOT LibMagic_FIND_QUIETLY)
|
||||||
|
MESSAGE(STATUS "Found libmagic: ${LIBMAGIC_LIBRARY}")
|
||||||
|
MESSAGE(STATUS " includes: ${LIBMAGIC_INCLUDE_DIR}")
|
||||||
|
ENDIF (NOT LibMagic_FIND_QUIETLY)
|
||||||
|
ELSE (LIBMAGIC_FOUND)
|
||||||
|
IF (LibMagic_FIND_REQUIRED)
|
||||||
|
MESSAGE(STATUS "")
|
||||||
|
MESSAGE(STATUS "libmagic development package cannot be found. Install it, please")
|
||||||
|
MESSAGE(STATUS "For example in (open)SUSE it's file-devel package")
|
||||||
|
MESSAGE(STATUS "")
|
||||||
|
MESSAGE(FATAL_ERROR "Could not find libmagic")
|
||||||
|
ENDIF (LibMagic_FIND_REQUIRED)
|
||||||
|
ENDIF (LIBMAGIC_FOUND)
|
||||||
|
|
@ -0,0 +1,26 @@
|
|||||||
|
# some system (rpm builds) setup LIB_SUFFIX for cmake. If there is no set, try to get it from system
|
||||||
|
IF(NOT DEFINED LIB_SUFFIX AND LIB_SUFFIX_ALREADY_SET)
|
||||||
|
MESSAGE(STATUS "*********************************************************************")
|
||||||
|
MESSAGE(STATUS "LIB_SUFFIX variable is not defined. It will be autodetected now")
|
||||||
|
MESSAGE(STATUS "You can set it manually with -DLIB_SUFFIX=<value> (64 for example)")
|
||||||
|
|
||||||
|
# All 32bit system have empty lib suffix
|
||||||
|
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||||
|
# If there is lib64 dir, set suffix to 64
|
||||||
|
if(IS_DIRECTORY "${CMAKE_INSTALL_PREFIX}/lib64")
|
||||||
|
set(LIB_SUFFIX 64)
|
||||||
|
elseif(IS_DIRECTORY "${CMAKE_INSTALL_PREFIX}/lib")
|
||||||
|
set(LIB_SUFFIX "")
|
||||||
|
else()
|
||||||
|
message(WARNING "LIB_SUFFIX cannot be autodetected. No \"${CMAKE_INSTALL_PREFIX}/lib\" neither \"${CMAKE_INSTALL_PREFIX}/lib64\" found.")
|
||||||
|
set(LIB_SUFFIX "")
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
set(LIB_SUFFIX "")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(LIB_SUFFIX_ALREADY_SET 1)
|
||||||
|
|
||||||
|
message(STATUS "LIB_SUFFIX autodetected as '${LIB_SUFFIX}', libraries will be installed into \"${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}\"")
|
||||||
|
MESSAGE(STATUS "*********************************************************************")
|
||||||
|
ENDIF(NOT DEFINED LIB_SUFFIX AND LIB_SUFFIX_ALREADY_SET)
|
@ -0,0 +1,21 @@
|
|||||||
|
if(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
|
||||||
|
message(FATAL_ERROR "Cannot find install manifest: @CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
|
||||||
|
endif(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
|
||||||
|
|
||||||
|
file(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files)
|
||||||
|
string(REGEX REPLACE "\n" ";" files "${files}")
|
||||||
|
foreach(file ${files})
|
||||||
|
message(STATUS "Uninstalling $ENV{DESTDIR}${file}")
|
||||||
|
if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
|
||||||
|
exec_program(
|
||||||
|
"@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\""
|
||||||
|
OUTPUT_VARIABLE rm_out
|
||||||
|
RETURN_VALUE rm_retval
|
||||||
|
)
|
||||||
|
if(NOT "${rm_retval}" STREQUAL 0)
|
||||||
|
message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}")
|
||||||
|
endif(NOT "${rm_retval}" STREQUAL 0)
|
||||||
|
else(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
|
||||||
|
message(STATUS "File $ENV{DESTDIR}${file} does not exist.")
|
||||||
|
endif(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
|
||||||
|
endforeach(file)
|
@ -0,0 +1,36 @@
|
|||||||
|
#
|
||||||
|
# Write a pkg-config pc file for given "name" with "decription"
|
||||||
|
# Arguments:
|
||||||
|
# name: a library name (withoud "lib" prefix and "so" suffixes
|
||||||
|
# desc: a desription string
|
||||||
|
# requires: required libraries
|
||||||
|
# include_rel_dir: include directory, relative to includedir
|
||||||
|
# version: package version
|
||||||
|
#
|
||||||
|
macro (create_pkgconfig_file name desc requires include_rel_dir version)
|
||||||
|
set(_pkgfname "${CMAKE_CURRENT_BINARY_DIR}/${name}.pc")
|
||||||
|
message(STATUS "${name}: writing pkgconfig file ${_pkgfname}")
|
||||||
|
|
||||||
|
file(WRITE "${_pkgfname}"
|
||||||
|
"# file generated by razor-qt cmake build\n"
|
||||||
|
"prefix=${CMAKE_INSTALL_PREFIX}\n"
|
||||||
|
"libdir=\${prefix}/${CMAKE_INSTALL_LIBDIR}\n"
|
||||||
|
"includedir=\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}\n"
|
||||||
|
"\n"
|
||||||
|
"Name: ${name}\n"
|
||||||
|
"Description: ${desc}\n"
|
||||||
|
"Version: ${version}\n"
|
||||||
|
"Requires: ${requires}\n"
|
||||||
|
"Libs: -L\${libdir} -l${name}\n"
|
||||||
|
"Cflags: -I\${includedir} -I\${includedir}/${include_rel_dir}\n"
|
||||||
|
"\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
# FreeBSD loves to install files to different locations
|
||||||
|
# http://www.freebsd.org/doc/handbook/dirstructure.html
|
||||||
|
if(${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD")
|
||||||
|
install(FILES ${_pkgfname} DESTINATION libdata/pkgconfig)
|
||||||
|
else()
|
||||||
|
install(FILES ${_pkgfname} DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
|
||||||
|
endif()
|
||||||
|
endmacro()
|
@ -0,0 +1,30 @@
|
|||||||
|
# Creates portable headers; e.g.:
|
||||||
|
# Creates XdgAction from xdgaction.h
|
||||||
|
# XdgAction contents:
|
||||||
|
# #include "xdgaction.h"
|
||||||
|
#
|
||||||
|
# Use:
|
||||||
|
# set(PUBLIC_CLASSES MyClass YourClass)
|
||||||
|
# create_portable_headers(PORTABLE_HEADERS ${PUBLIC_CLASSES})
|
||||||
|
# PORTABLE_HEADER is an return value that contains the full name of the
|
||||||
|
# generated headers.
|
||||||
|
|
||||||
|
function(create_portable_headers outfiles)
|
||||||
|
set(options)
|
||||||
|
set(oneValueArgs)
|
||||||
|
set(multiValueArgs)
|
||||||
|
|
||||||
|
cmake_parse_arguments(_CREATE_PORTABLE_HEADERS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||||
|
set(class_list ${_CREATE_PORTABLE_HEADERS_UNPARSED_ARGUMENTS})
|
||||||
|
foreach(f ${class_list})
|
||||||
|
string(TOLOWER "${f}.h" _filename)
|
||||||
|
|
||||||
|
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/${f}"
|
||||||
|
"#include \"${_filename}\"")
|
||||||
|
|
||||||
|
list(APPEND ${outfiles} "${CMAKE_CURRENT_BINARY_DIR}/${f}")
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
set(${outfiles} ${${outfiles}} PARENT_SCOPE)
|
||||||
|
endfunction()
|
||||||
|
|
@ -0,0 +1,54 @@
|
|||||||
|
# - Find the QtXdg include and library dirs and define a some macros
|
||||||
|
#
|
||||||
|
# The module defines the following variables
|
||||||
|
# QTXDG_FOUND - Set to TRUE if all of the above has been found
|
||||||
|
#
|
||||||
|
# QTXDG_INCLUDE_DIR - The QtXdg include directory
|
||||||
|
#
|
||||||
|
# QTXDG_INCLUDE_DIRS - The QtXdg lib and it's dependencies include directories
|
||||||
|
#
|
||||||
|
# QTXDG_LIBRARY_DIRS - The QtXdg lib and it's dependencies linker search paths
|
||||||
|
#
|
||||||
|
# QTXDG_LIBRARY - The QtXdg library itself
|
||||||
|
# QTXDG_LIBRARIES - The QtXdg library and all it's dependencies
|
||||||
|
#
|
||||||
|
# QTXDG_USE_FILE - The variable QTXDG_USE_FILE is set which is the path
|
||||||
|
# to a CMake file that can be included to compile qtxdg
|
||||||
|
# applications and libraries. It sets up the compilation
|
||||||
|
# environment for include directories and populates a
|
||||||
|
# QTXDG_LIBRARIES variable.
|
||||||
|
#
|
||||||
|
# QTXDG_QT_LIBRARIES - The QtXdg Qt dependencies libraries
|
||||||
|
#
|
||||||
|
# Typical usage:
|
||||||
|
# option(USE_QT5 "Build using Qt5. Default off" OFF)
|
||||||
|
# if (USE_QT5)
|
||||||
|
# find_package(QT5XDG)
|
||||||
|
# else()
|
||||||
|
# find_package(QTXDG)
|
||||||
|
# endif()
|
||||||
|
#
|
||||||
|
# include(${QTXDG_USE_FILE})
|
||||||
|
# add_executable(use-qtxdg main.cpp)
|
||||||
|
# target_link_libraries(use-qtxdg ${QTXDG_LIBRARIES})
|
||||||
|
|
||||||
|
set(QTXDG_INCLUDE_DIR "@QTXDG_INCLUDE_DIR@")
|
||||||
|
set(QTXDG_LIBRARY @QTXDGX_LIBRARY_NAME@)
|
||||||
|
set(QTXDG_PRIVATE_INCLUDE_DIR "@QTXDG_PRIVATE_INCLUDE_DIR@" CACHE PATH "Qt5Xdg private include dir")
|
||||||
|
|
||||||
|
set(QTXDG_LIBRARIES ${QTXDG_LIBRARY})
|
||||||
|
set(QTXDG_INCLUDE_DIRS "${QTXDG_INCLUDE_DIR}")
|
||||||
|
set(QTXDG_PRIVATE_INCLUDE_DIRS "${QTXDG_PRIVATE_INCLUDE_DIR}")
|
||||||
|
|
||||||
|
set(QTXDG_LIBRARY_DIRS "@CMAKE_INSTALL_FULL_LIBDIR@")
|
||||||
|
|
||||||
|
set(QTXDG_USE_FILE "${CMAKE_CURRENT_LIST_DIR}/@QTXDGX_FILE_NAME@_use.cmake")
|
||||||
|
set(QTXDG_FOUND 1)
|
||||||
|
set(QTXDG_QTMIMETYPES @USE_QTMIMETYPES@)
|
||||||
|
|
||||||
|
set(QTXDG_MAJOR_VERSION @QTXDG_MAJOR_VERSION@)
|
||||||
|
set(QTXDG_MINOR_VERSION @QTXDG_MINOR_VERSION@)
|
||||||
|
set(QTXDG_PATCH_VERSION @QTXDG_PATCH_VERSION@)
|
||||||
|
set(QTXDG_VERSION @QTXDG_MAJOR_VERSION@.@QTXDG_MINOR_VERSION@.@QTXDG_PATCH_VERSION@)
|
||||||
|
|
||||||
|
mark_as_advanced(QTXDG_LIBRARY QTXDG_INCLUDE_DIR QTXDG_PRIVATE_INCLUDE_DIR)
|
@ -0,0 +1,23 @@
|
|||||||
|
find_package(Qt5Widgets REQUIRED)
|
||||||
|
find_package(Qt5Xml REQUIRED)
|
||||||
|
find_package(Qt5DBus REQUIRED)
|
||||||
|
|
||||||
|
if (QTXDG_QTMIMETYPES)
|
||||||
|
add_definitions("-DQT_MIMETYPES")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_definitions(${Qt5Core_DEFINITIONS})
|
||||||
|
set(CMAKE_CXX_FLAGS
|
||||||
|
"${CMAKE_CXX_FLAGS} ${Qt5Widgets_EXECUTABLE_COMPILE_FLAGS}"
|
||||||
|
)
|
||||||
|
set(QTXDG_QT_LIBRARIES ${Qt5Widgets_LIBRARIES} ${Qt5Xml_LIBRARIES} ${Qt5DBus_LIBRARIES})
|
||||||
|
set(QTXDG_LIBRARIES ${QTXDG_LIBRARIES} ${QTXDG_QT_LIBRARIES})
|
||||||
|
set(QTXDG_INCLUDE_DIRS
|
||||||
|
"${QTXDG_INCLUDE_DIRS}"
|
||||||
|
"${Qt5Widgets_INCLUDE_DIRS}"
|
||||||
|
"${Qt5Xml_INCLUDE_DIRS}"
|
||||||
|
"${Qt5DBus_INCLUDE_DIRS}"
|
||||||
|
)
|
||||||
|
|
||||||
|
link_directories("${QTXDG_LIBRARY_DIRS}")
|
||||||
|
include_directories("${QTXDG_INCLUDE_DIRS}")
|
@ -0,0 +1,53 @@
|
|||||||
|
# - Find the QtXdg include and library dirs and define a some macros
|
||||||
|
#
|
||||||
|
# The module defines the following variables
|
||||||
|
# QTXDG_FOUND - Set to TRUE if all of the above has been found
|
||||||
|
#
|
||||||
|
# QTXDG_INCLUDE_DIR - The QtXdg include directory
|
||||||
|
#
|
||||||
|
# QTXDG_INCLUDE_DIRS - The QtXdg lib and it's dependencies include directories
|
||||||
|
#
|
||||||
|
# QTXDG_LIBRARY_DIRS - The QtXdg lib and it's dependencies linker search paths
|
||||||
|
#
|
||||||
|
# QTXDG_LIBRARY - The QtXdg library itself
|
||||||
|
# QTXDG_LIBRARIES - The QtXdg library and all it's dependencies
|
||||||
|
#
|
||||||
|
# QTXDG_USE_FILE - The variable QTXDG_USE_FILE is set which is the path
|
||||||
|
# to a CMake file that can be included to compile qtxdg
|
||||||
|
# applications and libraries. It sets up the compilation
|
||||||
|
# environment for include directories and populates a
|
||||||
|
# QTXDG_LIBRARIES variable.
|
||||||
|
#
|
||||||
|
# QTXDG_QT_LIBRARIES - The QtXdg Qt dependencies libraries
|
||||||
|
#
|
||||||
|
# Typical usage:
|
||||||
|
# option(USE_QT5 "Build using Qt5. Default off" OFF)
|
||||||
|
# if (USE_QT5)
|
||||||
|
# find_package(QT5XDG)
|
||||||
|
# else()
|
||||||
|
# find_package(QTXDG)
|
||||||
|
# endif()
|
||||||
|
#
|
||||||
|
# include(${QTXDG_USE_FILE})
|
||||||
|
# add_executable(use-qtxdg main.cpp)
|
||||||
|
# target_link_libraries(use-qtxdg ${QTXDG_LIBRARIES})
|
||||||
|
|
||||||
|
set(QTXDG_INCLUDE_DIR "@QTXDG_INCLUDE_DIR@")
|
||||||
|
set(QTXDG_PRIVATE_INCLUDE_DIR "@QTXDG_PRIVATE_INCLUDE_DIR@")
|
||||||
|
set(QTXDG_LIBRARY @QTXDGX_LIBRARY_NAME@)
|
||||||
|
|
||||||
|
set(QTXDG_LIBRARIES ${QTXDG_LIBRARY})
|
||||||
|
set(QTXDG_INCLUDE_DIRS "${QTXDG_INCLUDE_DIR}")
|
||||||
|
|
||||||
|
set(QTXDG_LIBRARY_DIRS "@CMAKE_INSTALL_FULL_LIBDIR@")
|
||||||
|
|
||||||
|
set(QTXDG_USE_FILE "${CMAKE_CURRENT_LIST_DIR}/@QTXDGX_FILE_NAME@_use.cmake")
|
||||||
|
set(QTXDG_FOUND 1)
|
||||||
|
set(QTXDG_QTMIMETYPES @USE_QTMIMETYPES@)
|
||||||
|
|
||||||
|
set(QTXDG_MAJOR_VERSION @QTXDG_MAJOR_VERSION@)
|
||||||
|
set(QTXDG_MINOR_VERSION @QTXDG_MINOR_VERSION@)
|
||||||
|
set(QTXDG_PATCH_VERSION @QTXDG_PATCH_VERSION@)
|
||||||
|
set(QTXDG_VERSION @QTXDG_MAJOR_VERSION@.@QTXDG_MINOR_VERSION@.@QTXDG_PATCH_VERSION@)
|
||||||
|
|
||||||
|
mark_as_advanced(QTXDG_LIBRARY QTXDG_INCLUDE_DIR)
|
@ -0,0 +1,42 @@
|
|||||||
|
# - Find the Razor-qt include and library dirs and define a some macros
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
find_package(Qt4 REQUIRED QtCore QtGui QtXml QtDBus QUIET)
|
||||||
|
include(${QT_USE_FILE})
|
||||||
|
|
||||||
|
set(QTXDG_QT_LIBRARIES
|
||||||
|
${QT_QTCORE_LIBRARY}
|
||||||
|
${QT_QTGUI_LIBRARY}
|
||||||
|
${QT_QTXML_LIBRARY}
|
||||||
|
${QT_DBUS_LIBRARY}
|
||||||
|
)
|
||||||
|
|
||||||
|
set(QTXDG_LIBRARIES ${QTXDG_LIBRARY} ${QTXDG_QT_LIBRARIES})
|
||||||
|
|
||||||
|
set(QTXDG_INCLUDE_DIRS
|
||||||
|
${QTXDG_INCLUDE_DIRS}
|
||||||
|
${QT_QTCORE_INCLUDE_DIR}
|
||||||
|
${QT_QTGUI_INCLUDE_DIR}
|
||||||
|
${QT_QTXML_INCLUDE_DIR}
|
||||||
|
${QT_QTDBUS_INCLUDE_DIR}
|
||||||
|
)
|
||||||
|
|
||||||
|
set(QTXDG_DEFINITIONS ${QT_DEFINITIONS})
|
||||||
|
|
||||||
|
if (QTXDG_QTMIMETYPES)
|
||||||
|
find_package(PkgConfig)
|
||||||
|
pkg_check_modules(QTMIMETYPES REQUIRED
|
||||||
|
QtMimeTypes
|
||||||
|
)
|
||||||
|
|
||||||
|
set(QTXDG_LIBRARIES ${QTXDG_LIBRARY} ${QTMIMETYPES_LIBRARIES})
|
||||||
|
set(QTXDG_LIBRARY_DIRS ${QTXDG_LIBRARY_DIRS} ${QTMIMETYPES_LIBRARY_DIRS})
|
||||||
|
set(QTXDG_DEFINITIONS ${QTXDG_DEFINITIONS} "-DQT_MIMETYPES")
|
||||||
|
include_directories(${QTXDG_INCLUDE_DIR} ${QTMIMETYPES_INCLUDE_DIRS})
|
||||||
|
link_directories(${QTXDG_LIBRARY_DIRS})
|
||||||
|
add_definitions("-DQT_MIMETYPES")
|
||||||
|
else()
|
||||||
|
include_directories(${QTXDG_INCLUDE_DIR} ${QTMIMETYPES_INCLUDE_DIRS})
|
||||||
|
link_directories(${QTXDG_LIBRARY_DIRS})
|
||||||
|
endif()
|
@ -0,0 +1,11 @@
|
|||||||
|
|
||||||
|
#include <QtCore/QByteArray>
|
||||||
|
|
||||||
|
static inline QByteArray detectDesktopEnvironment()
|
||||||
|
{
|
||||||
|
const QByteArray _desktop = qgetenv("XDG_CURRENT_DESKTOP");
|
||||||
|
if (!_desktop.isEmpty()) {
|
||||||
|
return _desktop.toUpper();
|
||||||
|
}
|
||||||
|
return QByteArray("UNKNOWN");
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
project(use-qtxdg)
|
||||||
|
cmake_minimum_required(VERSION 2.6)
|
||||||
|
|
||||||
|
option(USE_QT5 "Build using Qt5. Default off" OFF)
|
||||||
|
|
||||||
|
if (USE_QT5)
|
||||||
|
find_package(QT5XDG)
|
||||||
|
else()
|
||||||
|
find_package(QTXDG)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
include(${QTXDG_USE_FILE})
|
||||||
|
|
||||||
|
add_executable(use-qtxdg main.cpp)
|
||||||
|
|
||||||
|
# The QTXDG_QT_LIBRARIES variable contains the needed Qt libraries. They are
|
||||||
|
# set taking in account the choosed Qt version.
|
||||||
|
target_link_libraries(use-qtxdg ${QTXDG_QT_LIBRARIES} ${QTXDG_LIBRARIES})
|
@ -0,0 +1,9 @@
|
|||||||
|
An example of how to write an CMakeLists.txt to use libqtxdg in a portable
|
||||||
|
way.
|
||||||
|
|
||||||
|
Building with Qt4 is the default. Ex:
|
||||||
|
cmake..
|
||||||
|
|
||||||
|
To build with Qt5 just pass to set the USE_QT5 to Yes. Ex:
|
||||||
|
|
||||||
|
cmake -DUSE_QT5=On ..
|
@ -0,0 +1,8 @@
|
|||||||
|
#include <XdgDirs>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
qDebug() << XdgDirs::dataDirs();
|
||||||
|
return 0;
|
||||||
|
}
|
@ -0,0 +1,677 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
||||||
|
** Contact: http://www.qt-project.org/legal
|
||||||
|
**
|
||||||
|
** This file is part of the QtGui module of the Qt Toolkit.
|
||||||
|
**
|
||||||
|
** $QT_BEGIN_LICENSE:LGPL21$
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and Digia. For licensing terms and
|
||||||
|
** conditions see http://qt.digia.com/licensing. For further information
|
||||||
|
** use the contact form at http://qt.digia.com/contact-us.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 or version 3 as published by the Free
|
||||||
|
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
|
||||||
|
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
|
||||||
|
** following information to ensure the GNU Lesser General Public License
|
||||||
|
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
|
||||||
|
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** In addition, as a special exception, Digia gives you certain additional
|
||||||
|
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||||
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||||
|
**
|
||||||
|
** $QT_END_LICENSE$
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
#ifndef QT_NO_ICON
|
||||||
|
#include "qiconloader_p.h"
|
||||||
|
|
||||||
|
#include <private/qguiapplication_p.h>
|
||||||
|
#include <private/qicon_p.h>
|
||||||
|
|
||||||
|
#include <QtGui/QIconEnginePlugin>
|
||||||
|
#include <QtGui/QPixmapCache>
|
||||||
|
#include <qpa/qplatformtheme.h>
|
||||||
|
#include <QtGui/QIconEngine>
|
||||||
|
#include <QtGui/QPalette>
|
||||||
|
#include <QtCore/QList>
|
||||||
|
#include <QtCore/QHash>
|
||||||
|
#include <QtCore/QDir>
|
||||||
|
#include <QtCore/QSettings>
|
||||||
|
#include <QtGui/QPainter>
|
||||||
|
|
||||||
|
#ifdef Q_WS_MAC
|
||||||
|
#include <private/qt_cocoa_helpers_mac_p.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <private/qhexstring_p.h>
|
||||||
|
|
||||||
|
//QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
namespace QtXdg {
|
||||||
|
|
||||||
|
Q_GLOBAL_STATIC(QIconLoader, iconLoaderInstance)
|
||||||
|
|
||||||
|
/* Theme to use in last resort, if the theme does not have the icon, neither the parents */
|
||||||
|
static QString fallbackTheme()
|
||||||
|
{
|
||||||
|
if (const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme()) {
|
||||||
|
const QVariant themeHint = theme->themeHint(QPlatformTheme::SystemIconFallbackThemeName);
|
||||||
|
if (themeHint.isValid())
|
||||||
|
return themeHint.toString();
|
||||||
|
}
|
||||||
|
return QString("hicolor");
|
||||||
|
}
|
||||||
|
|
||||||
|
QIconLoader::QIconLoader() :
|
||||||
|
m_themeKey(1), m_supportsSvg(false), m_initialized(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// We lazily initialize the loader to make static icons
|
||||||
|
// work. Though we do not officially support this.
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline QStringList systemIconSearchPaths()
|
||||||
|
{
|
||||||
|
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 QIconLoader::ensureInitialized()
|
||||||
|
{
|
||||||
|
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 *QIconLoader::instance()
|
||||||
|
{
|
||||||
|
iconLoaderInstance()->ensureInitialized();
|
||||||
|
return iconLoaderInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Queries the system theme and invalidates existing
|
||||||
|
// icons if the theme has changed.
|
||||||
|
void QIconLoader::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 QIconLoader::setThemeName(const QString &themeName)
|
||||||
|
{
|
||||||
|
m_userTheme = themeName;
|
||||||
|
invalidateKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QIconLoader::setThemeSearchPath(const QStringList &searchPaths)
|
||||||
|
{
|
||||||
|
m_iconDirs = searchPaths;
|
||||||
|
themeList.clear();
|
||||||
|
invalidateKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList QIconLoader::themeSearchPaths() const
|
||||||
|
{
|
||||||
|
if (m_iconDirs.isEmpty()) {
|
||||||
|
m_iconDirs = systemIconSearchPaths();
|
||||||
|
// Always add resource directory as search path
|
||||||
|
m_iconDirs.append(QLatin1String(":/icons"));
|
||||||
|
}
|
||||||
|
return m_iconDirs;
|
||||||
|
}
|
||||||
|
|
||||||
|
QIconTheme::QIconTheme(const QString &themeName)
|
||||||
|
: m_valid(false)
|
||||||
|
{
|
||||||
|
QFile themeIndex;
|
||||||
|
|
||||||
|
QStringList iconDirs = QIcon::themeSearchPaths();
|
||||||
|
for ( int i = 0 ; i < iconDirs.size() ; ++i) {
|
||||||
|
QDir iconDir(iconDirs[i]);
|
||||||
|
QString themeDir = iconDir.path() + QLatin1Char('/') + themeName;
|
||||||
|
themeIndex.setFileName(themeDir + QLatin1String("/index.theme"));
|
||||||
|
if (themeIndex.exists()) {
|
||||||
|
m_contentDir = themeDir;
|
||||||
|
m_valid = true;
|
||||||
|
|
||||||
|
QStringList themeSearchPaths = QIcon::themeSearchPaths();
|
||||||
|
foreach (QString path, themeSearchPaths)
|
||||||
|
{
|
||||||
|
if (!path.startsWith(':') && QFileInfo(path).isDir())
|
||||||
|
m_contentDirs.append(path + QLatin1Char('/') + themeName);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#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();
|
||||||
|
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);
|
||||||
|
QIconDirInfo dirInfo(directoryKey);
|
||||||
|
dirInfo.size = size;
|
||||||
|
QString type = indexReader.value(directoryKey +
|
||||||
|
QLatin1String("/Type")
|
||||||
|
).toString();
|
||||||
|
|
||||||
|
if (type == QLatin1String("Fixed"))
|
||||||
|
dirInfo.type = QIconDirInfo::Fixed;
|
||||||
|
else if (type == QLatin1String("Scalable"))
|
||||||
|
dirInfo.type = QIconDirInfo::Scalable;
|
||||||
|
else
|
||||||
|
dirInfo.type = QIconDirInfo::Threshold;
|
||||||
|
|
||||||
|
dirInfo.threshold = indexReader.value(directoryKey +
|
||||||
|
QLatin1String("/Threshold"),
|
||||||
|
2).toInt();
|
||||||
|
|
||||||
|
dirInfo.minSize = indexReader.value(directoryKey +
|
||||||
|
QLatin1String("/MinSize"),
|
||||||
|
size).toInt();
|
||||||
|
|
||||||
|
dirInfo.maxSize = indexReader.value(directoryKey +
|
||||||
|
QLatin1String("/MaxSize"),
|
||||||
|
size).toInt();
|
||||||
|
m_keyList.append(dirInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parent themes provide fallbacks for missing icons
|
||||||
|
m_parents = indexReader.value(
|
||||||
|
QLatin1String("Icon Theme/Inherits")).toStringList();
|
||||||
|
m_parents.removeAll(QString());
|
||||||
|
|
||||||
|
// Ensure a default platform fallback for all themes
|
||||||
|
if (m_parents.isEmpty()) {
|
||||||
|
const QString fallback = fallbackTheme();
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
QThemeIconEntries QIconLoader::findIconHelper(const QString &themeName,
|
||||||
|
const QString &iconName,
|
||||||
|
QStringList &visited) const
|
||||||
|
{
|
||||||
|
QThemeIconEntries entries;
|
||||||
|
Q_ASSERT(!themeName.isEmpty());
|
||||||
|
|
||||||
|
QPixmap pixmap;
|
||||||
|
|
||||||
|
// Used to protect against potential recursions
|
||||||
|
visited << themeName;
|
||||||
|
|
||||||
|
QIconTheme theme = themeList.value(themeName);
|
||||||
|
if (!theme.isValid()) {
|
||||||
|
theme = QIconTheme(themeName);
|
||||||
|
if (!theme.isValid())
|
||||||
|
theme = QIconTheme(fallbackTheme());
|
||||||
|
|
||||||
|
themeList.insert(themeName, theme);
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList contentDirs = theme.contentDirs();
|
||||||
|
const QVector<QIconDirInfo> subDirs = theme.keyList();
|
||||||
|
|
||||||
|
const QString svgext(QLatin1String(".svg"));
|
||||||
|
const QString pngext(QLatin1String(".png"));
|
||||||
|
const QString xpmext(QLatin1String(".xpm"));
|
||||||
|
|
||||||
|
// Add all relevant files
|
||||||
|
for (int i = 0; i < subDirs.size() ; ++i) {
|
||||||
|
const QIconDirInfo &dirInfo = subDirs.at(i);
|
||||||
|
QString subdir = dirInfo.path;
|
||||||
|
|
||||||
|
foreach (QString contentDir, contentDirs) {
|
||||||
|
QDir currentDir(contentDir + '/' + subdir);
|
||||||
|
|
||||||
|
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
|
||||||
|
entries.prepend(iconEntry);
|
||||||
|
} else if (m_supportsSvg &&
|
||||||
|
currentDir.exists(iconName + svgext)) {
|
||||||
|
ScalableEntry *iconEntry = new ScalableEntry;
|
||||||
|
iconEntry->dir = dirInfo;
|
||||||
|
iconEntry->filename = currentDir.filePath(iconName + svgext);
|
||||||
|
entries.append(iconEntry);
|
||||||
|
break;
|
||||||
|
} 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
|
||||||
|
entries.append(iconEntry);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entries.isEmpty()) {
|
||||||
|
const QStringList parents = theme.parents();
|
||||||
|
// Search recursively through inherited themes
|
||||||
|
for (int i = 0 ; i < parents.size() ; ++i) {
|
||||||
|
|
||||||
|
const QString parentTheme = parents.at(i).trimmed();
|
||||||
|
|
||||||
|
if (!visited.contains(parentTheme)) // guard against recursion
|
||||||
|
entries = findIconHelper(parentTheme, iconName, visited);
|
||||||
|
|
||||||
|
if (!entries.isEmpty()) // success
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*********************************************************************
|
||||||
|
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 (entries.isEmpty()) {
|
||||||
|
const QString pixmaps(QLatin1String("/usr/share/pixmaps"));
|
||||||
|
|
||||||
|
QDir currentDir(pixmaps);
|
||||||
|
QIconDirInfo 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
|
||||||
|
entries.prepend(iconEntry);
|
||||||
|
} else if (m_supportsSvg &&
|
||||||
|
currentDir.exists(iconName + svgext)) {
|
||||||
|
ScalableEntry *iconEntry = new ScalableEntry;
|
||||||
|
iconEntry->dir = dirInfo;
|
||||||
|
iconEntry->filename = currentDir.filePath(iconName + svgext);
|
||||||
|
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
|
||||||
|
entries.append(iconEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (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
|
||||||
|
entries.prepend(iconEntry);
|
||||||
|
} else if (m_supportsSvg &&
|
||||||
|
currentDir.exists(iconName + svgext)) {
|
||||||
|
ScalableEntry *iconEntry = new ScalableEntry;
|
||||||
|
iconEntry->filename = currentDir.filePath(iconName + svgext);
|
||||||
|
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
|
||||||
|
entries.append(iconEntry);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
QThemeIconEntries QIconLoader::loadIcon(const QString &name) const
|
||||||
|
{
|
||||||
|
if (!themeName().isEmpty()) {
|
||||||
|
QStringList visited;
|
||||||
|
return findIconHelper(themeName(), name, visited);
|
||||||
|
}
|
||||||
|
|
||||||
|
return QThemeIconEntries();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -------- Icon Loader Engine -------- //
|
||||||
|
|
||||||
|
|
||||||
|
QIconLoaderEngineFixed::QIconLoaderEngineFixed(const QString& iconName)
|
||||||
|
: m_iconName(iconName), m_key(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QIconLoaderEngineFixed::~QIconLoaderEngineFixed()
|
||||||
|
{
|
||||||
|
qDeleteAll(m_entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
QIconLoaderEngineFixed::QIconLoaderEngineFixed(const QIconLoaderEngineFixed &other)
|
||||||
|
: QIconEngine(other),
|
||||||
|
m_iconName(other.m_iconName),
|
||||||
|
m_key(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QIconEngine *QIconLoaderEngineFixed::clone() const
|
||||||
|
{
|
||||||
|
return new QIconLoaderEngineFixed(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QIconLoaderEngineFixed::read(QDataStream &in) {
|
||||||
|
in >> m_iconName;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QIconLoaderEngineFixed::write(QDataStream &out) const
|
||||||
|
{
|
||||||
|
out << m_iconName;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QIconLoaderEngineFixed::hasIcon() const
|
||||||
|
{
|
||||||
|
return !(m_entries.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lazily load the icon
|
||||||
|
void QIconLoaderEngineFixed::ensureLoaded()
|
||||||
|
{
|
||||||
|
if (!(QIconLoader::instance()->themeKey() == m_key)) {
|
||||||
|
|
||||||
|
qDeleteAll(m_entries);
|
||||||
|
|
||||||
|
m_entries = QIconLoader::instance()->loadIcon(m_iconName);
|
||||||
|
m_key = QIconLoader::instance()->themeKey();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QIconLoaderEngineFixed::paint(QPainter *painter, const QRect &rect,
|
||||||
|
QIcon::Mode mode, QIcon::State state)
|
||||||
|
{
|
||||||
|
QSize pixmapSize = rect.size();
|
||||||
|
#if defined(Q_WS_MAC)
|
||||||
|
pixmapSize *= qt_mac_get_scalefactor();
|
||||||
|
#endif
|
||||||
|
painter->drawPixmap(rect, pixmap(pixmapSize, mode, state));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This algorithm is defined by the freedesktop spec:
|
||||||
|
* http://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html
|
||||||
|
*/
|
||||||
|
static bool directoryMatchesSize(const QIconDirInfo &dir, int iconsize)
|
||||||
|
{
|
||||||
|
if (dir.type == QIconDirInfo::Fixed) {
|
||||||
|
return dir.size == iconsize;
|
||||||
|
|
||||||
|
} else if (dir.type == QIconDirInfo::Scalable) {
|
||||||
|
return dir.size <= dir.maxSize &&
|
||||||
|
iconsize >= dir.minSize;
|
||||||
|
|
||||||
|
} else if (dir.type == QIconDirInfo::Threshold) {
|
||||||
|
return iconsize >= dir.size - dir.threshold &&
|
||||||
|
iconsize <= dir.size + dir.threshold;
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_ASSERT(1); // Not a valid value
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This algorithm is defined by the freedesktop spec:
|
||||||
|
* http://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html
|
||||||
|
*/
|
||||||
|
static int directorySizeDistance(const QIconDirInfo &dir, int iconsize)
|
||||||
|
{
|
||||||
|
if (dir.type == QIconDirInfo::Fixed) {
|
||||||
|
return qAbs(dir.size - iconsize);
|
||||||
|
|
||||||
|
} else if (dir.type == QIconDirInfo::Scalable) {
|
||||||
|
if (iconsize < dir.minSize)
|
||||||
|
return dir.minSize - iconsize;
|
||||||
|
else if (iconsize > dir.maxSize)
|
||||||
|
return iconsize - dir.maxSize;
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
} 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_ASSERT(1); // Not a valid value
|
||||||
|
return INT_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
|
QIconLoaderEngineEntry *QIconLoaderEngineFixed::entryForSize(const QSize &size)
|
||||||
|
{
|
||||||
|
int iconsize = qMin(size.width(), size.height());
|
||||||
|
|
||||||
|
// Note that m_entries are sorted so that png-files
|
||||||
|
// come first
|
||||||
|
|
||||||
|
const int numEntries = m_entries.size();
|
||||||
|
|
||||||
|
// Search for exact matches first
|
||||||
|
for (int i = 0; i < numEntries; ++i) {
|
||||||
|
QIconLoaderEngineEntry *entry = m_entries.at(i);
|
||||||
|
if (directoryMatchesSize(entry->dir, iconsize)) {
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the minimum distance icon
|
||||||
|
int minimalSize = INT_MAX;
|
||||||
|
QIconLoaderEngineEntry *closestMatch = 0;
|
||||||
|
for (int i = 0; i < numEntries; ++i) {
|
||||||
|
QIconLoaderEngineEntry *entry = m_entries.at(i);
|
||||||
|
int distance = directorySizeDistance(entry->dir, iconsize);
|
||||||
|
if (distance < minimalSize) {
|
||||||
|
minimalSize = distance;
|
||||||
|
closestMatch = entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return closestMatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the actual icon size. For scalable svg's this is equivalent
|
||||||
|
* to the requested size. Otherwise the closest match is returned but
|
||||||
|
* we can never return a bigger size than the requested size.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
QSize QIconLoaderEngineFixed::actualSize(const QSize &size, QIcon::Mode mode,
|
||||||
|
QIcon::State state)
|
||||||
|
{
|
||||||
|
ensureLoaded();
|
||||||
|
|
||||||
|
QIconLoaderEngineEntry *entry = entryForSize(size);
|
||||||
|
if (entry) {
|
||||||
|
const QIconDirInfo &dir = entry->dir;
|
||||||
|
if (dir.type == QIconDirInfo::Scalable)
|
||||||
|
return size;
|
||||||
|
else {
|
||||||
|
int result = qMin<int>(dir.size, qMin(size.width(), size.height()));
|
||||||
|
return QSize(result, result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return QIconEngine::actualSize(size, mode, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
QPixmap PixmapEntry::pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state)
|
||||||
|
{
|
||||||
|
Q_UNUSED(state);
|
||||||
|
|
||||||
|
// Ensure that basePixmap is lazily initialized before generating the
|
||||||
|
// key, otherwise the cache key is not unique
|
||||||
|
if (basePixmap.isNull())
|
||||||
|
basePixmap.load(filename);
|
||||||
|
|
||||||
|
QSize actualSize = basePixmap.size();
|
||||||
|
if (!actualSize.isNull() && (actualSize.width() > size.width() || actualSize.height() > size.height()))
|
||||||
|
actualSize.scale(size, Qt::KeepAspectRatio);
|
||||||
|
|
||||||
|
QString key = QLatin1String("$qt_theme_")
|
||||||
|
% HexString<qint64>(basePixmap.cacheKey())
|
||||||
|
% HexString<int>(mode)
|
||||||
|
% HexString<qint64>(QGuiApplication::palette().cacheKey())
|
||||||
|
% HexString<int>(actualSize.width())
|
||||||
|
% HexString<int>(actualSize.height());
|
||||||
|
|
||||||
|
QPixmap cachedPixmap;
|
||||||
|
if (QPixmapCache::find(key, &cachedPixmap)) {
|
||||||
|
return cachedPixmap;
|
||||||
|
} else {
|
||||||
|
if (basePixmap.size() != actualSize)
|
||||||
|
cachedPixmap = basePixmap.scaled(actualSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
|
||||||
|
else
|
||||||
|
cachedPixmap = basePixmap;
|
||||||
|
if (QGuiApplication *guiApp = qobject_cast<QGuiApplication *>(qApp))
|
||||||
|
cachedPixmap = static_cast<QGuiApplicationPrivate*>(QObjectPrivate::get(guiApp))->applyQIconStyleHelper(mode, cachedPixmap);
|
||||||
|
QPixmapCache::insert(key, cachedPixmap);
|
||||||
|
}
|
||||||
|
return cachedPixmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPixmap ScalableEntry::pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state)
|
||||||
|
{
|
||||||
|
if (svgIcon.isNull())
|
||||||
|
svgIcon = QIcon(filename);
|
||||||
|
|
||||||
|
// Simply reuse svg icon engine
|
||||||
|
return svgIcon.pixmap(size, mode, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
QPixmap QIconLoaderEngineFixed::pixmap(const QSize &size, QIcon::Mode mode,
|
||||||
|
QIcon::State state)
|
||||||
|
{
|
||||||
|
ensureLoaded();
|
||||||
|
|
||||||
|
QIconLoaderEngineEntry *entry = entryForSize(size);
|
||||||
|
if (entry)
|
||||||
|
return entry->pixmap(size, mode, state);
|
||||||
|
|
||||||
|
return QPixmap();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString QIconLoaderEngineFixed::key() const
|
||||||
|
{
|
||||||
|
return QLatin1String("QIconLoaderEngineFixed");
|
||||||
|
}
|
||||||
|
|
||||||
|
void QIconLoaderEngineFixed::virtual_hook(int id, void *data)
|
||||||
|
{
|
||||||
|
ensureLoaded();
|
||||||
|
|
||||||
|
switch (id) {
|
||||||
|
case QIconEngine::AvailableSizesHook:
|
||||||
|
{
|
||||||
|
QIconEngine::AvailableSizesArgument &arg
|
||||||
|
= *reinterpret_cast<QIconEngine::AvailableSizesArgument*>(data);
|
||||||
|
const int N = m_entries.size();
|
||||||
|
QList<QSize> sizes;
|
||||||
|
sizes.reserve(N);
|
||||||
|
|
||||||
|
// Gets all sizes from the DirectoryInfo entries
|
||||||
|
for (int i = 0; i < N; ++i) {
|
||||||
|
int size = m_entries.at(i)->dir.size;
|
||||||
|
sizes.append(QSize(size, size));
|
||||||
|
}
|
||||||
|
arg.sizes.swap(sizes); // commit
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case QIconEngine::IconNameHook:
|
||||||
|
{
|
||||||
|
QString &name = *reinterpret_cast<QString*>(data);
|
||||||
|
name = m_iconName;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
QIconEngine::virtual_hook(id, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // QtXdg
|
||||||
|
|
||||||
|
//QT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif //QT_NO_ICON
|
@ -0,0 +1,199 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
||||||
|
** Contact: http://www.qt-project.org/legal
|
||||||
|
**
|
||||||
|
** This file is part of the QtGui module of the Qt Toolkit.
|
||||||
|
**
|
||||||
|
** $QT_BEGIN_LICENSE:LGPL21$
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and Digia. For licensing terms and
|
||||||
|
** conditions see http://qt.digia.com/licensing. For further information
|
||||||
|
** use the contact form at http://qt.digia.com/contact-us.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 or version 3 as published by the Free
|
||||||
|
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
|
||||||
|
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
|
||||||
|
** following information to ensure the GNU Lesser General Public License
|
||||||
|
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
|
||||||
|
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** In addition, as a special exception, Digia gives you certain additional
|
||||||
|
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||||
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||||
|
**
|
||||||
|
** $QT_END_LICENSE$
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#ifndef QICONLOADER_P_H
|
||||||
|
#define QICONLOADER_P_H
|
||||||
|
|
||||||
|
#include <QtCore/qglobal.h>
|
||||||
|
|
||||||
|
#ifndef QT_NO_ICON
|
||||||
|
//
|
||||||
|
// W A R N I N G
|
||||||
|
// -------------
|
||||||
|
//
|
||||||
|
// This file is not part of the Qt API. It exists purely as an
|
||||||
|
// implementation detail. This header file may change from version to
|
||||||
|
// version without notice, or even be removed.
|
||||||
|
//
|
||||||
|
// We mean it.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <QtGui/QIcon>
|
||||||
|
#include <QtGui/QIconEngine>
|
||||||
|
#include <QtGui/QPixmapCache>
|
||||||
|
#include <private/qicon_p.h>
|
||||||
|
#include <private/qfactoryloader_p.h>
|
||||||
|
#include <QtCore/QHash>
|
||||||
|
#include <QtCore/QVector>
|
||||||
|
#include <QtCore/QTypeInfo>
|
||||||
|
|
||||||
|
//QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
namespace QtXdg {
|
||||||
|
|
||||||
|
class QIconLoader;
|
||||||
|
|
||||||
|
struct QIconDirInfo
|
||||||
|
{
|
||||||
|
enum Type { Fixed, Scalable, Threshold };
|
||||||
|
QIconDirInfo(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 : 4;
|
||||||
|
};
|
||||||
|
|
||||||
|
class QIconLoaderEngineEntry
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~QIconLoaderEngineEntry() {}
|
||||||
|
virtual QPixmap pixmap(const QSize &size,
|
||||||
|
QIcon::Mode mode,
|
||||||
|
QIcon::State state) = 0;
|
||||||
|
QString filename;
|
||||||
|
QIconDirInfo dir;
|
||||||
|
static int count;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ScalableEntry : public QIconLoaderEngineEntry
|
||||||
|
{
|
||||||
|
QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) Q_DECL_OVERRIDE;
|
||||||
|
QIcon svgIcon;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PixmapEntry : public QIconLoaderEngineEntry
|
||||||
|
{
|
||||||
|
QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) Q_DECL_OVERRIDE;
|
||||||
|
QPixmap basePixmap;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef QList<QIconLoaderEngineEntry*> QThemeIconEntries;
|
||||||
|
|
||||||
|
//class QIconLoaderEngine : public QIconEngine
|
||||||
|
class QIconLoaderEngineFixed : public QIconEngine
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QIconLoaderEngineFixed(const QString& iconName = QString());
|
||||||
|
~QIconLoaderEngineFixed();
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString key() const;
|
||||||
|
bool hasIcon() const;
|
||||||
|
void ensureLoaded();
|
||||||
|
void virtual_hook(int id, void *data);
|
||||||
|
QIconLoaderEngineEntry *entryForSize(const QSize &size);
|
||||||
|
QIconLoaderEngineFixed(const QIconLoaderEngineFixed &other);
|
||||||
|
QThemeIconEntries m_entries;
|
||||||
|
QString m_iconName;
|
||||||
|
uint m_key;
|
||||||
|
|
||||||
|
friend class QIconLoader;
|
||||||
|
};
|
||||||
|
|
||||||
|
class QIconTheme
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QIconTheme(const QString &name);
|
||||||
|
QIconTheme() : m_valid(false) {}
|
||||||
|
QStringList parents() { return m_parents; }
|
||||||
|
QVector <QIconDirInfo> keyList() { return m_keyList; }
|
||||||
|
QString contentDir() { return m_contentDir; }
|
||||||
|
QStringList contentDirs() { return m_contentDirs; }
|
||||||
|
bool isValid() { return m_valid; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString m_contentDir;
|
||||||
|
QStringList m_contentDirs;
|
||||||
|
QVector <QIconDirInfo> m_keyList;
|
||||||
|
QStringList m_parents;
|
||||||
|
bool m_valid;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Q_GUI_EXPORT QIconLoader
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QIconLoader();
|
||||||
|
QThemeIconEntries 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;
|
||||||
|
QIconDirInfo dirInfo(int dirindex);
|
||||||
|
static QIconLoader *instance();
|
||||||
|
void updateSystemTheme();
|
||||||
|
void invalidateKey() { m_themeKey++; }
|
||||||
|
void ensureInitialized();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QThemeIconEntries 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;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // QtXdg
|
||||||
|
|
||||||
|
// Note: class template specialization of 'QTypeInfo' must occur at
|
||||||
|
// global scope
|
||||||
|
Q_DECLARE_TYPEINFO(QtXdg::QIconDirInfo, Q_MOVABLE_TYPE);
|
||||||
|
|
||||||
|
//QT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // QT_NO_ICON
|
||||||
|
|
||||||
|
#endif // QICONLOADER_P_H
|
@ -0,0 +1,216 @@
|
|||||||
|
/* BEGIN_COMMON_COPYRIGHT_HEADER
|
||||||
|
* (c)LGPL2
|
||||||
|
*/
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||||
|
** All rights reserved.
|
||||||
|
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||||
|
**
|
||||||
|
** This file is part of the QtGui module of the Qt Toolkit.
|
||||||
|
**
|
||||||
|
** $QT_BEGIN_LICENSE:LGPL$
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
** This file may be used under the terms of the GNU Lesser General Public
|
||||||
|
** License version 2.1 as published by the Free Software Foundation and
|
||||||
|
** appearing in the file LICENSE.LGPL included in the packaging of this
|
||||||
|
** file. Please review the following information to ensure the GNU Lesser
|
||||||
|
** General Public License version 2.1 requirements will be met:
|
||||||
|
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** In addition, as a special exception, Nokia gives you certain additional
|
||||||
|
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||||
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU General
|
||||||
|
** Public License version 3.0 as published by the Free Software Foundation
|
||||||
|
** and appearing in the file LICENSE.GPL included in the packaging of this
|
||||||
|
** file. Please review the following information to ensure the GNU General
|
||||||
|
** Public License version 3.0 requirements will be met:
|
||||||
|
** http://www.gnu.org/copyleft/gpl.html.
|
||||||
|
**
|
||||||
|
** Other Usage
|
||||||
|
** Alternatively, this file may be used in accordance with the terms and
|
||||||
|
** conditions contained in a signed written agreement between you and Nokia.
|
||||||
|
**
|
||||||
|
**
|
||||||
|
**
|
||||||
|
**
|
||||||
|
**
|
||||||
|
** $QT_END_LICENSE$
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
//END_COMMON_COPYRIGHT_HEADER
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
It's fixes the following bugs:
|
||||||
|
* QIcon::fromTheme returns pixmaps that are bigger than requested
|
||||||
|
https://bugreports.qt.nokia.com/browse/QTBUG-17953
|
||||||
|
|
||||||
|
* Qt should honor /usr/share/pixmaps as a valid icon directory on Linux
|
||||||
|
https://bugreports.qt.nokia.com/browse/QTBUG-12874
|
||||||
|
|
||||||
|
*************************************************************************/
|
||||||
|
|
||||||
|
#ifndef QDESKTOPICON_P_H
|
||||||
|
#define QDESKTOPICON_P_H
|
||||||
|
|
||||||
|
#ifndef QT_NO_ICON
|
||||||
|
//
|
||||||
|
// W A R N I N G
|
||||||
|
// -------------
|
||||||
|
//
|
||||||
|
// This file is not part of the Qt API. It exists purely as an
|
||||||
|
// implementation detail. This header file may change from version to
|
||||||
|
// version without notice, or even be removed.
|
||||||
|
//
|
||||||
|
// We mean it.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <QtGui/QIcon>
|
||||||
|
#include <QtGui/QIconEngine>
|
||||||
|
#include <QtGui/QPixmapCache>
|
||||||
|
//#include "qt/qicon_p.h"
|
||||||
|
//#include "qt/qfactoryloader_p.h"
|
||||||
|
#include <QHash>
|
||||||
|
|
||||||
|
namespace QtXdg {
|
||||||
|
|
||||||
|
class QIconLoader;
|
||||||
|
|
||||||
|
struct QIconDirInfo
|
||||||
|
{
|
||||||
|
enum Type { Fixed, Scalable, Threshold };
|
||||||
|
QIconDirInfo(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 : 4;
|
||||||
|
};
|
||||||
|
|
||||||
|
class QIconLoaderEngineEntry
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~QIconLoaderEngineEntry() {}
|
||||||
|
virtual QPixmap pixmap(const QSize &size,
|
||||||
|
QIcon::Mode mode,
|
||||||
|
QIcon::State state) = 0;
|
||||||
|
QString filename;
|
||||||
|
QIconDirInfo dir;
|
||||||
|
static int count;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ScalableEntry : public QIconLoaderEngineEntry
|
||||||
|
{
|
||||||
|
QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state);
|
||||||
|
QIcon svgIcon;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PixmapEntry : public QIconLoaderEngineEntry
|
||||||
|
{
|
||||||
|
QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state);
|
||||||
|
QPixmap basePixmap;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef QList<QIconLoaderEngineEntry*> QThemeIconEntries;
|
||||||
|
|
||||||
|
#if QT_VERSION < QT_VERSION_CHECK(5,0,0)
|
||||||
|
class QIconLoaderEngineFixed : public QIconEngineV2
|
||||||
|
#else
|
||||||
|
class QIconLoaderEngineFixed : public QIconEngine
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QIconLoaderEngineFixed(const QString& iconName = QString());
|
||||||
|
~QIconLoaderEngineFixed();
|
||||||
|
|
||||||
|
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);
|
||||||
|
#if QT_VERSION < QT_VERSION_CHECK(5,0,0)
|
||||||
|
QIconEngineV2 *clone() const;
|
||||||
|
#else
|
||||||
|
QIconEngine *clone() const;
|
||||||
|
#endif
|
||||||
|
bool read(QDataStream &in);
|
||||||
|
bool write(QDataStream &out) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString key() const;
|
||||||
|
bool hasIcon() const;
|
||||||
|
void ensureLoaded();
|
||||||
|
void virtual_hook(int id, void *data);
|
||||||
|
QIconLoaderEngineEntry *entryForSize(const QSize &size);
|
||||||
|
QIconLoaderEngineFixed(const QIconLoaderEngineFixed &other);
|
||||||
|
QThemeIconEntries m_entries;
|
||||||
|
QString m_iconName;
|
||||||
|
uint m_key;
|
||||||
|
|
||||||
|
friend class QIconLoader;
|
||||||
|
};
|
||||||
|
|
||||||
|
class QIconTheme
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QIconTheme(const QString &name);
|
||||||
|
QIconTheme() : m_valid(false) {}
|
||||||
|
QStringList parents() { return m_parents; }
|
||||||
|
QList <QIconDirInfo> keyList() { return m_keyList; }
|
||||||
|
QString contentDir() { return m_contentDir; }
|
||||||
|
QStringList contentDirs() { return m_contentDirs; }
|
||||||
|
bool isValid() { return m_valid; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString m_contentDir;
|
||||||
|
QStringList m_contentDirs;
|
||||||
|
QList <QIconDirInfo> m_keyList;
|
||||||
|
QStringList m_parents;
|
||||||
|
bool m_valid;
|
||||||
|
};
|
||||||
|
|
||||||
|
class QIconLoader : public QObject
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QIconLoader();
|
||||||
|
QThemeIconEntries 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;
|
||||||
|
QIconDirInfo dirInfo(int dirindex);
|
||||||
|
static QIconLoader *instance();
|
||||||
|
void updateSystemTheme();
|
||||||
|
void invalidateKey() { m_themeKey++; }
|
||||||
|
void ensureInitialized();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QThemeIconEntries 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;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // QtXdg
|
||||||
|
|
||||||
|
#endif // QDESKTOPICON_P_H
|
||||||
|
|
||||||
|
#endif //QT_NO_ICON
|
@ -0,0 +1,737 @@
|
|||||||
|
/* BEGIN_COMMON_COPYRIGHT_HEADER
|
||||||
|
* (c)LGPL2
|
||||||
|
*/
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||||
|
** All rights reserved.
|
||||||
|
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||||
|
**
|
||||||
|
** This file is part of the QtGui module of the Qt Toolkit.
|
||||||
|
**
|
||||||
|
** $QT_BEGIN_LICENSE:LGPL$
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
** This file may be used under the terms of the GNU Lesser General Public
|
||||||
|
** License version 2.1 as published by the Free Software Foundation and
|
||||||
|
** appearing in the file LICENSE.LGPL included in the packaging of this
|
||||||
|
** file. Please review the following information to ensure the GNU Lesser
|
||||||
|
** General Public License version 2.1 requirements will be met:
|
||||||
|
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** In addition, as a special exception, Nokia gives you certain additional
|
||||||
|
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||||
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU General
|
||||||
|
** Public License version 3.0 as published by the Free Software Foundation
|
||||||
|
** and appearing in the file LICENSE.GPL included in the packaging of this
|
||||||
|
** file. Please review the following information to ensure the GNU General
|
||||||
|
** Public License version 3.0 requirements will be met:
|
||||||
|
** http://www.gnu.org/copyleft/gpl.html.
|
||||||
|
**
|
||||||
|
** Other Usage
|
||||||
|
** Alternatively, this file may be used in accordance with the terms and
|
||||||
|
** conditions contained in a signed written agreement between you and Nokia.
|
||||||
|
**
|
||||||
|
**
|
||||||
|
**
|
||||||
|
**
|
||||||
|
**
|
||||||
|
** $QT_END_LICENSE$
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
//END_COMMON_COPYRIGHT_HEADER
|
||||||
|
|
||||||
|
#ifndef QT_NO_ICON
|
||||||
|
#include "qiconloader_p_qt4.h"
|
||||||
|
|
||||||
|
//#include "qt/qapplication_p.h"
|
||||||
|
//#include <qt/qicon_p.h>
|
||||||
|
//#include <qt/qguiplatformplugin_p.h>
|
||||||
|
|
||||||
|
#include <QtGui/QIconEnginePlugin>
|
||||||
|
#include <QtGui/QPixmapCache>
|
||||||
|
#include <QtGui/QIconEngine>
|
||||||
|
#include <QStyleOption>
|
||||||
|
#include <QList>
|
||||||
|
#include <QHash>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QSettings>
|
||||||
|
#include <QtGui/QPainter>
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QLatin1Literal>
|
||||||
|
//#ifdef Q_WS_MAC
|
||||||
|
//#include <private/qt_cocoa_helpers_mac_p.h>
|
||||||
|
//#endif
|
||||||
|
|
||||||
|
//#ifdef Q_WS_X11
|
||||||
|
//#include "qt/qt_x11_p.h"
|
||||||
|
//#endif
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
#if QT_VERSION < 0x040700
|
||||||
|
#include <limits.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace QtXdg {
|
||||||
|
|
||||||
|
Q_GLOBAL_STATIC(QIconLoader, iconLoaderInstance)
|
||||||
|
|
||||||
|
/* Theme to use in last resort, if the theme does not have the icon, neither the parents */
|
||||||
|
/*static QString fallbackTheme()
|
||||||
|
{
|
||||||
|
#ifdef Q_WS_X11
|
||||||
|
if (X11->desktopEnvironment == DE_GNOME) {
|
||||||
|
return QLatin1String("gnome");
|
||||||
|
} else if (X11->desktopEnvironment == DE_KDE) {
|
||||||
|
return X11->desktopVersion >= 4
|
||||||
|
? QString::fromLatin1("oxygen")
|
||||||
|
: QString::fromLatin1("crystalsvg");
|
||||||
|
} else {
|
||||||
|
return QLatin1String("hicolor");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
QIconLoader::QIconLoader() :
|
||||||
|
m_themeKey(1), m_supportsSvg(false), m_initialized(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// We lazily initialize the loader to make static icons
|
||||||
|
// work. Though we do not officially support this.
|
||||||
|
void QIconLoader::ensureInitialized()
|
||||||
|
{
|
||||||
|
if (!m_initialized) {
|
||||||
|
m_initialized = true;
|
||||||
|
|
||||||
|
Q_ASSERT(qApp);
|
||||||
|
|
||||||
|
m_systemTheme = QIcon::themeName();
|
||||||
|
|
||||||
|
#ifndef QT_NO_LIBRARY
|
||||||
|
// QFactoryLoader iconFactoryLoader(QIconEngineFactoryInterfaceV2_iid,
|
||||||
|
// QLatin1String("/iconengines"),
|
||||||
|
// Qt::CaseInsensitive);
|
||||||
|
// if (iconFactoryLoader.keys().contains(QLatin1String("svg")))
|
||||||
|
m_supportsSvg = true;
|
||||||
|
#endif //QT_NO_LIBRARY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QIconLoader *QIconLoader::instance()
|
||||||
|
{
|
||||||
|
return iconLoaderInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Queries the system theme and invalidates existing
|
||||||
|
// icons if the theme has changed.
|
||||||
|
void QIconLoader::updateSystemTheme()
|
||||||
|
{
|
||||||
|
// Only change if this is not explicitly set by the user
|
||||||
|
if (m_userTheme.isEmpty()) {
|
||||||
|
QString theme = QIcon::themeName();//qt_guiPlatformPlugin()->systemIconThemeName();
|
||||||
|
//if (theme.isEmpty())
|
||||||
|
// theme = fallbackTheme();
|
||||||
|
if (theme != m_systemTheme) {
|
||||||
|
m_systemTheme = theme;
|
||||||
|
invalidateKey();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QIconLoader::setThemeName(const QString &themeName)
|
||||||
|
{
|
||||||
|
m_userTheme = themeName;
|
||||||
|
invalidateKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QIconLoader::setThemeSearchPath(const QStringList &searchPaths)
|
||||||
|
{
|
||||||
|
m_iconDirs = searchPaths;
|
||||||
|
themeList.clear();
|
||||||
|
invalidateKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList QIconLoader::themeSearchPaths() const
|
||||||
|
{
|
||||||
|
if (m_iconDirs.isEmpty())
|
||||||
|
{
|
||||||
|
m_iconDirs = QIcon::themeSearchPaths();//qt_guiPlatformPlugin()->iconThemeSearchPaths();
|
||||||
|
// Always add resource directory as search path
|
||||||
|
m_iconDirs.append(QLatin1String(":/icons"));
|
||||||
|
}
|
||||||
|
return m_iconDirs;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QIconTheme::QIconTheme(const QString &themeName)
|
||||||
|
: m_valid(false)
|
||||||
|
{
|
||||||
|
|
||||||
|
QFile themeIndex;
|
||||||
|
|
||||||
|
QList <QIconDirInfo> keyList;
|
||||||
|
QStringList iconDirs = QIcon::themeSearchPaths();
|
||||||
|
for ( int i = 0 ; i < iconDirs.size() ; ++i) {
|
||||||
|
QDir iconDir(iconDirs[i]);
|
||||||
|
QString themeDir = iconDir.path() + QLatin1Char('/') + themeName;
|
||||||
|
themeIndex.setFileName(themeDir + QLatin1String("/index.theme"));
|
||||||
|
if (themeIndex.exists()) {
|
||||||
|
m_contentDir = themeDir;
|
||||||
|
m_valid = true;
|
||||||
|
|
||||||
|
QStringList themeSearchPaths = QIcon::themeSearchPaths();
|
||||||
|
foreach (QString path, themeSearchPaths)
|
||||||
|
{
|
||||||
|
if (!path.startsWith(':') && QFileInfo(path).isDir())
|
||||||
|
m_contentDirs.append(path + QLatin1Char('/') + themeName);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#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();
|
||||||
|
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);
|
||||||
|
QIconDirInfo dirInfo(directoryKey);
|
||||||
|
dirInfo.size = size;
|
||||||
|
QString type = indexReader.value(directoryKey +
|
||||||
|
QLatin1String("/Type")
|
||||||
|
).toString();
|
||||||
|
|
||||||
|
if (type == QLatin1String("Fixed"))
|
||||||
|
dirInfo.type = QIconDirInfo::Fixed;
|
||||||
|
else if (type == QLatin1String("Scalable"))
|
||||||
|
dirInfo.type = QIconDirInfo::Scalable;
|
||||||
|
else
|
||||||
|
dirInfo.type = QIconDirInfo::Threshold;
|
||||||
|
|
||||||
|
dirInfo.threshold = indexReader.value(directoryKey +
|
||||||
|
QLatin1String("/Threshold"),
|
||||||
|
2).toInt();
|
||||||
|
|
||||||
|
dirInfo.minSize = indexReader.value(directoryKey +
|
||||||
|
QLatin1String("/MinSize"),
|
||||||
|
size).toInt();
|
||||||
|
|
||||||
|
dirInfo.maxSize = indexReader.value(directoryKey +
|
||||||
|
QLatin1String("/MaxSize"),
|
||||||
|
size).toInt();
|
||||||
|
m_keyList.append(dirInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parent themes provide fallbacks for missing icons
|
||||||
|
m_parents = indexReader.value(
|
||||||
|
QLatin1String("Icon Theme/Inherits")).toStringList();
|
||||||
|
|
||||||
|
// Ensure a default platform fallback for all themes
|
||||||
|
if (m_parents.isEmpty())
|
||||||
|
m_parents.append(QIcon::themeName());//fallbackTheme());
|
||||||
|
|
||||||
|
// Ensure that all themes fall back to hicolor
|
||||||
|
if (!m_parents.contains(QLatin1String("hicolor")))
|
||||||
|
m_parents.append(QLatin1String("hicolor"));
|
||||||
|
}
|
||||||
|
#endif //QT_NO_SETTINGS
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QThemeIconEntries QIconLoader::findIconHelper(const QString &themeName,
|
||||||
|
const QString &iconName,
|
||||||
|
QStringList &visited) const
|
||||||
|
{
|
||||||
|
QThemeIconEntries entries;
|
||||||
|
Q_ASSERT(!themeName.isEmpty());
|
||||||
|
|
||||||
|
QPixmap pixmap;
|
||||||
|
|
||||||
|
// Used to protect against potential recursions
|
||||||
|
visited << themeName;
|
||||||
|
|
||||||
|
QIconTheme theme = themeList.value(themeName);
|
||||||
|
if (!theme.isValid()) {
|
||||||
|
theme = QIconTheme(themeName);
|
||||||
|
if (!theme.isValid())
|
||||||
|
theme = QIconTheme(QIcon::themeName());//fallbackTheme());
|
||||||
|
|
||||||
|
themeList.insert(themeName, theme);
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList contentDirs = theme.contentDirs();
|
||||||
|
QList<QIconDirInfo> subDirs = theme.keyList();
|
||||||
|
|
||||||
|
const QString svgext(QLatin1String(".svg"));
|
||||||
|
const QString pngext(QLatin1String(".png"));
|
||||||
|
const QString xpmext(QLatin1String(".xpm"));
|
||||||
|
|
||||||
|
// Add all relevant files
|
||||||
|
for (int i = 0; i < subDirs.size() ; ++i)
|
||||||
|
{
|
||||||
|
const QIconDirInfo &dirInfo = subDirs.at(i);
|
||||||
|
QString subdir = dirInfo.path;
|
||||||
|
|
||||||
|
foreach (QString contentDir, contentDirs)
|
||||||
|
{
|
||||||
|
QDir currentDir(contentDir + '/' + subdir);
|
||||||
|
|
||||||
|
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
|
||||||
|
entries.prepend(iconEntry);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (m_supportsSvg &&
|
||||||
|
currentDir.exists(iconName + svgext))
|
||||||
|
{
|
||||||
|
ScalableEntry *iconEntry = new ScalableEntry;
|
||||||
|
iconEntry->dir = dirInfo;
|
||||||
|
iconEntry->filename = currentDir.filePath(iconName + svgext);
|
||||||
|
entries.append(iconEntry);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
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
|
||||||
|
entries.append(iconEntry);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entries.isEmpty()) {
|
||||||
|
const QStringList parents = theme.parents();
|
||||||
|
// Search recursively through inherited themes
|
||||||
|
for (int i = 0 ; i < parents.size() ; ++i) {
|
||||||
|
|
||||||
|
const QString parentTheme = parents.at(i).trimmed();
|
||||||
|
|
||||||
|
if (!visited.contains(parentTheme)) // guard against recursion
|
||||||
|
entries = findIconHelper(parentTheme, iconName, visited);
|
||||||
|
|
||||||
|
if (!entries.isEmpty()) // success
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*********************************************************************
|
||||||
|
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 (entries.isEmpty()) {
|
||||||
|
const QString pixmaps(QLatin1String("/usr/share/pixmaps"));
|
||||||
|
|
||||||
|
QDir currentDir(pixmaps);
|
||||||
|
QIconDirInfo 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
|
||||||
|
entries.prepend(iconEntry);
|
||||||
|
} else if (m_supportsSvg &&
|
||||||
|
currentDir.exists(iconName + svgext)) {
|
||||||
|
ScalableEntry *iconEntry = new ScalableEntry;
|
||||||
|
iconEntry->dir = dirInfo;
|
||||||
|
iconEntry->filename = currentDir.filePath(iconName + svgext);
|
||||||
|
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
|
||||||
|
entries.append(iconEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (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
|
||||||
|
entries.prepend(iconEntry);
|
||||||
|
} else if (m_supportsSvg &&
|
||||||
|
currentDir.exists(iconName + svgext)) {
|
||||||
|
ScalableEntry *iconEntry = new ScalableEntry;
|
||||||
|
iconEntry->filename = currentDir.filePath(iconName + svgext);
|
||||||
|
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
|
||||||
|
entries.append(iconEntry);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
QThemeIconEntries QIconLoader::loadIcon(const QString &name) const
|
||||||
|
{
|
||||||
|
if (!themeName().isEmpty()) {
|
||||||
|
QStringList visited;
|
||||||
|
return findIconHelper(themeName(), name, visited);
|
||||||
|
}
|
||||||
|
|
||||||
|
return QThemeIconEntries();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -------- Icon Loader Engine -------- //
|
||||||
|
|
||||||
|
|
||||||
|
QIconLoaderEngineFixed::QIconLoaderEngineFixed(const QString& iconName)
|
||||||
|
: m_iconName(iconName), m_key(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QIconLoaderEngineFixed::~QIconLoaderEngineFixed()
|
||||||
|
{
|
||||||
|
while (!m_entries.isEmpty())
|
||||||
|
delete m_entries.takeLast();
|
||||||
|
Q_ASSERT(m_entries.size() == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
QIconLoaderEngineFixed::QIconLoaderEngineFixed(const QIconLoaderEngineFixed &other)
|
||||||
|
#if QT_VERSION < QT_VERSION_CHECK(5,0,0)
|
||||||
|
: QIconEngineV2(other),
|
||||||
|
#else
|
||||||
|
: QIconEngine(other),
|
||||||
|
#endif
|
||||||
|
m_iconName(other.m_iconName),
|
||||||
|
m_key(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#if QT_VERSION < QT_VERSION_CHECK(5,0,0)
|
||||||
|
QIconEngineV2 *QIconLoaderEngineFixed::clone() const
|
||||||
|
#else
|
||||||
|
QIconEngine *QIconLoaderEngineFixed::clone() const
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
return new QIconLoaderEngineFixed(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QIconLoaderEngineFixed::read(QDataStream &in) {
|
||||||
|
in >> m_iconName;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QIconLoaderEngineFixed::write(QDataStream &out) const
|
||||||
|
{
|
||||||
|
out << m_iconName;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QIconLoaderEngineFixed::hasIcon() const
|
||||||
|
{
|
||||||
|
return !(m_entries.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lazily load the icon
|
||||||
|
void QIconLoaderEngineFixed::ensureLoaded()
|
||||||
|
{
|
||||||
|
|
||||||
|
iconLoaderInstance()->ensureInitialized();
|
||||||
|
|
||||||
|
if (!(iconLoaderInstance()->themeKey() == m_key)) {
|
||||||
|
|
||||||
|
while (!m_entries.isEmpty())
|
||||||
|
delete m_entries.takeLast();
|
||||||
|
|
||||||
|
Q_ASSERT(m_entries.size() == 0);
|
||||||
|
m_entries = iconLoaderInstance()->loadIcon(m_iconName);
|
||||||
|
m_key = iconLoaderInstance()->themeKey();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QIconLoaderEngineFixed::paint(QPainter *painter, const QRect &rect,
|
||||||
|
QIcon::Mode mode, QIcon::State state)
|
||||||
|
{
|
||||||
|
QSize pixmapSize = rect.size();
|
||||||
|
#if defined(Q_WS_MAC)
|
||||||
|
pixmapSize *= qt_mac_get_scalefactor();
|
||||||
|
#endif
|
||||||
|
painter->drawPixmap(rect, pixmap(pixmapSize, mode, state));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This algorithm is defined by the freedesktop spec:
|
||||||
|
* http://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html
|
||||||
|
*/
|
||||||
|
static bool directoryMatchesSize(const QIconDirInfo &dir, int iconsize)
|
||||||
|
{
|
||||||
|
if (dir.type == QIconDirInfo::Fixed) {
|
||||||
|
return dir.size == iconsize;
|
||||||
|
|
||||||
|
} else if (dir.type == QIconDirInfo::Scalable) {
|
||||||
|
return dir.size <= dir.maxSize &&
|
||||||
|
iconsize >= dir.minSize;
|
||||||
|
|
||||||
|
} else if (dir.type == QIconDirInfo::Threshold) {
|
||||||
|
return iconsize >= dir.size - dir.threshold &&
|
||||||
|
iconsize <= dir.size + dir.threshold;
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_ASSERT(1); // Not a valid value
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This algorithm is defined by the freedesktop spec:
|
||||||
|
* http://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html
|
||||||
|
*/
|
||||||
|
static int directorySizeDistance(const QIconDirInfo &dir, int iconsize)
|
||||||
|
{
|
||||||
|
if (dir.type == QIconDirInfo::Fixed) {
|
||||||
|
return qAbs(dir.size - iconsize);
|
||||||
|
|
||||||
|
} else if (dir.type == QIconDirInfo::Scalable) {
|
||||||
|
if (iconsize < dir.minSize)
|
||||||
|
return dir.minSize - iconsize;
|
||||||
|
else if (iconsize > dir.maxSize)
|
||||||
|
return iconsize - dir.maxSize;
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
} 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_ASSERT(1); // Not a valid value
|
||||||
|
return INT_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
|
QIconLoaderEngineEntry *QIconLoaderEngineFixed::entryForSize(const QSize &size)
|
||||||
|
{
|
||||||
|
int iconsize = qMin(size.width(), size.height());
|
||||||
|
|
||||||
|
// Note that m_entries are sorted so that png-files
|
||||||
|
// come first
|
||||||
|
|
||||||
|
// Search for exact matches first
|
||||||
|
for (int i = 0; i < m_entries.count(); ++i) {
|
||||||
|
QIconLoaderEngineEntry *entry = m_entries.at(i);
|
||||||
|
if (directoryMatchesSize(entry->dir, iconsize)) {
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the minimum distance icon
|
||||||
|
int minimalSize = INT_MAX;
|
||||||
|
QIconLoaderEngineEntry *closestMatch = 0;
|
||||||
|
for (int i = 0; i < m_entries.count(); ++i) {
|
||||||
|
QIconLoaderEngineEntry *entry = m_entries.at(i);
|
||||||
|
int distance = directorySizeDistance(entry->dir, iconsize);
|
||||||
|
if (distance < minimalSize) {
|
||||||
|
minimalSize = distance;
|
||||||
|
closestMatch = entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return closestMatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the actual icon size. For scalable svg's this is equivalent
|
||||||
|
* to the requested size. Otherwise the closest match is returned but
|
||||||
|
* we can never return a bigger size than the requested size.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
QSize QIconLoaderEngineFixed::actualSize(const QSize &size, QIcon::Mode mode,
|
||||||
|
QIcon::State state)
|
||||||
|
{
|
||||||
|
ensureLoaded();
|
||||||
|
QIconLoaderEngineEntry *entry = entryForSize(size);
|
||||||
|
if (entry) {
|
||||||
|
const QIconDirInfo &dir = entry->dir;
|
||||||
|
if (dir.type == QIconDirInfo::Scalable)
|
||||||
|
{
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (dir.size == 0)
|
||||||
|
{
|
||||||
|
entry->dir.size = QPixmap(entry->filename).size().width();
|
||||||
|
entry->dir.minSize = dir.size;
|
||||||
|
entry->dir.maxSize = dir.size;
|
||||||
|
}
|
||||||
|
int result = qMin<int>(dir.size, qMin(size.width(), size.height()));
|
||||||
|
|
||||||
|
return QSize(result, result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#if QT_VERSION < QT_VERSION_CHECK(5,0,0)
|
||||||
|
return QIconEngineV2::actualSize(size, mode, state);
|
||||||
|
#else
|
||||||
|
return QIconEngine::actualSize(size, mode, state);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
QPixmap PixmapEntry::pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state)
|
||||||
|
{
|
||||||
|
Q_UNUSED(state);
|
||||||
|
|
||||||
|
// Ensure that basePixmap is lazily initialized before generating the
|
||||||
|
// key, otherwise the cache key is not unique
|
||||||
|
if (basePixmap.isNull())
|
||||||
|
basePixmap.load(filename);
|
||||||
|
|
||||||
|
QSize actualSize = basePixmap.size();
|
||||||
|
if (!actualSize.isNull() && (actualSize.width() > size.width() || actualSize.height() > size.height()))
|
||||||
|
actualSize.scale(size, Qt::KeepAspectRatio);
|
||||||
|
|
||||||
|
|
||||||
|
QString key = QString("$qt_theme_%1%2%3%4%5")
|
||||||
|
.arg(basePixmap.cacheKey(), 16, 16, QChar('0'))
|
||||||
|
.arg(mode, 8, 16, QChar('0'))
|
||||||
|
.arg(qApp->palette().cacheKey(),16, 16, QChar('0'))
|
||||||
|
.arg(actualSize.width(), 8, 16, QChar('0'))
|
||||||
|
.arg(actualSize.height(), 8, 16, QChar('0'));
|
||||||
|
|
||||||
|
QPixmap cachedPixmap;
|
||||||
|
if (QPixmapCache::find(key, &cachedPixmap)) {
|
||||||
|
return cachedPixmap;
|
||||||
|
} else {
|
||||||
|
if (basePixmap.size() != actualSize)
|
||||||
|
basePixmap = basePixmap.scaled(actualSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
|
||||||
|
|
||||||
|
QStyleOption opt(0);
|
||||||
|
opt.palette = qApp->palette();
|
||||||
|
cachedPixmap = qApp->style()->generatedIconPixmap(mode, basePixmap, &opt);
|
||||||
|
QPixmapCache::insert(key, cachedPixmap);
|
||||||
|
}
|
||||||
|
return cachedPixmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPixmap ScalableEntry::pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state)
|
||||||
|
{
|
||||||
|
if (svgIcon.isNull())
|
||||||
|
svgIcon = QIcon(filename);
|
||||||
|
|
||||||
|
// Simply reuse svg icon engine
|
||||||
|
return svgIcon.pixmap(size, mode, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
QPixmap QIconLoaderEngineFixed::pixmap(const QSize &size, QIcon::Mode mode,
|
||||||
|
QIcon::State state)
|
||||||
|
{
|
||||||
|
|
||||||
|
ensureLoaded();
|
||||||
|
|
||||||
|
QIconLoaderEngineEntry *entry = entryForSize(size);
|
||||||
|
|
||||||
|
if (entry)
|
||||||
|
return entry->pixmap(size, mode, state);
|
||||||
|
|
||||||
|
return QPixmap();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString QIconLoaderEngineFixed::key() const
|
||||||
|
{
|
||||||
|
return QLatin1String("QIconLoaderEngineFixed");
|
||||||
|
}
|
||||||
|
|
||||||
|
void QIconLoaderEngineFixed::virtual_hook(int id, void *data)
|
||||||
|
{
|
||||||
|
ensureLoaded();
|
||||||
|
|
||||||
|
switch (id) {
|
||||||
|
#if QT_VERSION < QT_VERSION_CHECK(5,0,0)
|
||||||
|
case QIconEngineV2::AvailableSizesHook:
|
||||||
|
#else
|
||||||
|
case QIconEngine::AvailableSizesHook:
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
#if QT_VERSION < QT_VERSION_CHECK(5,0,0)
|
||||||
|
QIconEngineV2::AvailableSizesArgument &arg
|
||||||
|
= *reinterpret_cast<QIconEngineV2::AvailableSizesArgument*>(data);
|
||||||
|
#else
|
||||||
|
QIconEngine::AvailableSizesArgument &arg
|
||||||
|
= *reinterpret_cast<QIconEngine::AvailableSizesArgument*>(data);
|
||||||
|
#endif
|
||||||
|
const QList<QIconDirInfo> directoryKey = iconLoaderInstance()->theme().keyList();
|
||||||
|
arg.sizes.clear();
|
||||||
|
|
||||||
|
// Gets all sizes from the DirectoryInfo entries
|
||||||
|
for (int i = 0 ; i < m_entries.size() ; ++i) {
|
||||||
|
int size = m_entries.at(i)->dir.size;
|
||||||
|
arg.sizes.append(QSize(size, size));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
#if (QT_VERSION >= 0x040700) && (QT_VERSION < 0x050000)
|
||||||
|
case QIconEngineV2::IconNameHook:
|
||||||
|
{
|
||||||
|
QString &name = *reinterpret_cast<QString*>(data);
|
||||||
|
name = m_iconName;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
#elif QT_VERSION > QT_VERSION_CHECK(5,0,0)
|
||||||
|
case QIconEngine::IconNameHook:
|
||||||
|
{
|
||||||
|
QString &name = *reinterpret_cast<QString*>(data);
|
||||||
|
name = m_iconName;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
#else// QT_VERSION > QT_VERSION_CHECK(5,0,0)
|
||||||
|
#warning QIconEngineV2::IconNameHook is ignored due Qt version. Upgrade to 4.7.x
|
||||||
|
#endif
|
||||||
|
default:
|
||||||
|
#if QT_VERSION < QT_VERSION_CHECK(5,0,0)
|
||||||
|
QIconEngineV2::virtual_hook(id, data);
|
||||||
|
#else
|
||||||
|
QIconEngine::virtual_hook(id, data);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // QtXdg
|
||||||
|
|
||||||
|
#endif //QT_NO_ICON
|
@ -0,0 +1,26 @@
|
|||||||
|
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" "lxde.org:/var/www/lxqt/downloads/$PROJECT/"
|
@ -0,0 +1,52 @@
|
|||||||
|
set(PROJECT_NAME "qtxdg_test")
|
||||||
|
|
||||||
|
set(${PROJECT_NAME}_SRCS
|
||||||
|
qtxdg_test.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
set(${PROJECT_NAME}_MOCS
|
||||||
|
qtxdg_test.h
|
||||||
|
)
|
||||||
|
|
||||||
|
set(LIBRARIES
|
||||||
|
${QTXDGX_LIBRARY_NAME}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if (BUILD_TESTS)
|
||||||
|
add_definitions(-DQTXDG_BUILDING_TESTS=1)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (USE_QT5)
|
||||||
|
qt5_wrap_cpp(MOCS ${${PROJECT_NAME}_MOCS})
|
||||||
|
else()
|
||||||
|
qt4_wrap_cpp(MOCS ${${PROJECT_NAME}_MOCS})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
include_directories (
|
||||||
|
${CMAKE_SOURCE_DIR}
|
||||||
|
)
|
||||||
|
|
||||||
|
if (USE_QT5)
|
||||||
|
add_definitions(${Qt5Test_DEFINITINS})
|
||||||
|
include_directories (
|
||||||
|
${Qt5Test_INCLUDE_DIRS}
|
||||||
|
)
|
||||||
|
set(CMAKE_CXX_FLAGS
|
||||||
|
"${CMAKE_CXX_FLAGS} ${Qt5Test_EXECUTABLE_COMPILE_FLAGS}"
|
||||||
|
)
|
||||||
|
else()
|
||||||
|
include_directories (
|
||||||
|
${QT_QTCORE_INCLUDE_DIR}
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_executable(${PROJECT_NAME} ${${PROJECT_NAME}_SRCS} ${UIS} ${RSCS} ${TRS} ${MOCS} )
|
||||||
|
|
||||||
|
if (USE_QT5)
|
||||||
|
target_link_libraries ( ${PROJECT_NAME} ${Qt5Test_LIBRARIES} ${LIBRARIES} )
|
||||||
|
else()
|
||||||
|
target_link_libraries ( ${PROJECT_NAME} ${QT_LIBRARIES} ${LIBRARIES} )
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_test(NAME ${PROJECT_NAME} COMMAND ${PROJECT_NAME})
|
@ -0,0 +1,141 @@
|
|||||||
|
#include "qtxdg_test.h"
|
||||||
|
|
||||||
|
#include "xdgdesktopfile.h"
|
||||||
|
#include "xdgdesktopfile_p.h"
|
||||||
|
#include "xdgdirs.h"
|
||||||
|
|
||||||
|
#include <QtTest>
|
||||||
|
|
||||||
|
#include <QDir>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QProcess>
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QSettings>
|
||||||
|
|
||||||
|
void QtXdgTest::testDefaultApp()
|
||||||
|
{
|
||||||
|
QStringList mimedirs = XdgDirs::dataDirs();
|
||||||
|
mimedirs.prepend(XdgDirs::dataHome(false));
|
||||||
|
foreach (QString mimedir, mimedirs)
|
||||||
|
{
|
||||||
|
QDir dir(mimedir + "/mime");
|
||||||
|
qDebug() << dir.path();
|
||||||
|
QStringList filters = (QStringList() << "*.xml");
|
||||||
|
foreach(QFileInfo mediaDir, dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot))
|
||||||
|
{
|
||||||
|
qDebug() << " " << mediaDir.fileName();
|
||||||
|
foreach (QString mimeXmlFileName, QDir(mediaDir.absoluteFilePath()).entryList(filters, QDir::Files))
|
||||||
|
{
|
||||||
|
QString mimetype = mediaDir.fileName() + "/" + mimeXmlFileName.left(mimeXmlFileName.length() - 4);
|
||||||
|
QString xdg_utils_default = xdgUtilDefaultApp(mimetype);
|
||||||
|
QString desktop_file_default = xdgDesktopFileDefaultApp(mimetype);
|
||||||
|
|
||||||
|
if (xdg_utils_default != desktop_file_default)
|
||||||
|
{
|
||||||
|
qDebug() << mimetype;
|
||||||
|
qDebug() << "xdg-mime query default:" << xdg_utils_default;
|
||||||
|
qDebug() << "xdgdesktopfile default:" << desktop_file_default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtXdgTest::compare(QString mimetype)
|
||||||
|
{
|
||||||
|
QString xdgUtilDefault = xdgUtilDefaultApp(mimetype);
|
||||||
|
QString xdgDesktopDefault = xdgDesktopFileDefaultApp(mimetype);
|
||||||
|
if (xdgUtilDefault != xdgDesktopDefault)
|
||||||
|
{
|
||||||
|
qDebug() << mimetype;
|
||||||
|
qDebug() << "xdg-mime default:" << xdgUtilDefault;
|
||||||
|
qDebug() << "xdgDesktopfile default:" << xdgDesktopDefault;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void QtXdgTest::testTextHtml()
|
||||||
|
{
|
||||||
|
compare("text/html");
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtXdgTest::testMeldComparison()
|
||||||
|
{
|
||||||
|
compare("application/x-meld-comparison");
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtXdgTest::testCustomFormat()
|
||||||
|
{
|
||||||
|
QSettings::Format desktopFormat = QSettings::registerFormat("list", readDesktopFile, writeDesktopFile);
|
||||||
|
QFile::remove("/tmp/test.list");
|
||||||
|
QFile::remove("/tmp/test2.list");
|
||||||
|
QSettings test("/tmp/test.list", desktopFormat);
|
||||||
|
test.beginGroup("Default Applications");
|
||||||
|
test.setValue("text/plain", QString("gvim.desktop"));
|
||||||
|
test.setValue("text/html", QString("firefox.desktop"));
|
||||||
|
test.endGroup();
|
||||||
|
test.beginGroup("Other Applications");
|
||||||
|
test.setValue("application/pdf", QString("qpdfview.desktop"));
|
||||||
|
test.setValue("image/svg+xml", QString("inkscape.desktop"));
|
||||||
|
test.sync();
|
||||||
|
|
||||||
|
QFile::copy("/tmp/test.list", "/tmp/test2.list");
|
||||||
|
|
||||||
|
QSettings test2("/tmp/test2.list", desktopFormat);
|
||||||
|
QVERIFY(test2.allKeys().size() == 4);
|
||||||
|
|
||||||
|
test2.beginGroup("Default Applications");
|
||||||
|
// qDebug() << test2.value("text/plain");
|
||||||
|
QVERIFY(test2.value("text/plain") == QString("gvim.desktop"));
|
||||||
|
|
||||||
|
// qDebug() << test2.value("text/html");
|
||||||
|
QVERIFY(test2.value("text/html") == QString("firefox.desktop"));
|
||||||
|
test2.endGroup();
|
||||||
|
|
||||||
|
test2.beginGroup("Other Applications");
|
||||||
|
// qDebug() << test2.value("application/pdf");
|
||||||
|
QVERIFY(test2.value("application/pdf") == QString("qpdfview.desktop"));
|
||||||
|
|
||||||
|
// qDebug() << test2.value("image/svg+xml");
|
||||||
|
QVERIFY(test2.value("image/svg+xml") == QString("inkscape.desktop"));
|
||||||
|
test2.endGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QString QtXdgTest::xdgDesktopFileDefaultApp(QString mimetype)
|
||||||
|
{
|
||||||
|
XdgDesktopFile *defaultApp = XdgDesktopFileCache::getDefaultApp(mimetype);
|
||||||
|
QString defaultAppS;
|
||||||
|
if (defaultApp)
|
||||||
|
{
|
||||||
|
defaultAppS = QFileInfo(defaultApp->fileName()).fileName();
|
||||||
|
}
|
||||||
|
return defaultAppS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
QString QtXdgTest::xdgUtilDefaultApp(QString mimetype)
|
||||||
|
{
|
||||||
|
QProcess xdg_mime;
|
||||||
|
QString program = "xdg-mime";
|
||||||
|
QStringList args = (QStringList() << "query" << "default" << mimetype);
|
||||||
|
qDebug() << "running" << program << args.join(" ");
|
||||||
|
xdg_mime.start(program, args);
|
||||||
|
xdg_mime.waitForFinished(1000);
|
||||||
|
return QString(xdg_mime.readAll()).trimmed();
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
int main(int argc, char** args)
|
||||||
|
{
|
||||||
|
// QtXdgTest().testDefaultApp();
|
||||||
|
// qDebug() << "Default for text/html:" << QtXdgTest().xdgDesktopFileDefaultApp("text/html");
|
||||||
|
// QtXdgTest().testMeldComparison();
|
||||||
|
qDebug() << QtXdgTest().testCustomFormat();
|
||||||
|
};
|
||||||
|
#endif // 0
|
||||||
|
|
||||||
|
QTEST_MAIN(QtXdgTest)
|
@ -0,0 +1,28 @@
|
|||||||
|
#ifndef QTXDG_TEST_H
|
||||||
|
#define QTXDG_TEST_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QString>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
class QtXdgTest : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void testCustomFormat();
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Test that XdgDesktopFile and xdg-mime script agree on
|
||||||
|
// default application for each mime-type.
|
||||||
|
void testDefaultApp();
|
||||||
|
|
||||||
|
void testTextHtml();
|
||||||
|
void testMeldComparison();
|
||||||
|
void compare(QString mimetype);
|
||||||
|
QString xdgDesktopFileDefaultApp(QString mimetype);
|
||||||
|
QString xdgUtilDefaultApp(QString mimetype);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* QTXDG_TEST_H */
|
@ -0,0 +1,156 @@
|
|||||||
|
/* BEGIN_COMMON_COPYRIGHT_HEADER
|
||||||
|
* (c)LGPL2+
|
||||||
|
*
|
||||||
|
* Razor - a lightweight, Qt based, desktop toolset
|
||||||
|
* http://razor-qt.org
|
||||||
|
*
|
||||||
|
* Copyright: 2010-2011 Razor team
|
||||||
|
* Authors:
|
||||||
|
* Alexander Sokoloff <sokoloff.a@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 "xdgaction.h"
|
||||||
|
#include "xdgicon.h"
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QCoreApplication>
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
XdgAction::XdgAction(QObject *parent):
|
||||||
|
QAction(parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
XdgAction::XdgAction(const XdgDesktopFile& desktopFile, QObject *parent):
|
||||||
|
QAction(parent)
|
||||||
|
{
|
||||||
|
load(desktopFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
XdgAction::XdgAction(const XdgDesktopFile* desktopFile, QObject *parent):
|
||||||
|
QAction(parent)
|
||||||
|
{
|
||||||
|
load(*desktopFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
XdgAction::XdgAction(const QString& desktopFileName, QObject *parent):
|
||||||
|
QAction(parent)
|
||||||
|
{
|
||||||
|
XdgDesktopFile df;
|
||||||
|
df.load(desktopFileName);
|
||||||
|
load(df);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
XdgAction::XdgAction(const XdgAction& other, QObject *parent):
|
||||||
|
QAction(parent)
|
||||||
|
{
|
||||||
|
load(other.mDesktopFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
XdgAction::~XdgAction()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
XdgAction& XdgAction::operator=(const XdgAction& other)
|
||||||
|
{
|
||||||
|
load(other.mDesktopFile);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
bool XdgAction::isValid() const
|
||||||
|
{
|
||||||
|
return mDesktopFile.isValid();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
void XdgAction::load(const XdgDesktopFile& desktopFile)
|
||||||
|
{
|
||||||
|
mDesktopFile = desktopFile;
|
||||||
|
if (mDesktopFile.isValid())
|
||||||
|
{
|
||||||
|
// & is reserved for mnemonics
|
||||||
|
setText(mDesktopFile.name().replace('&', QLatin1String("&&")));
|
||||||
|
setToolTip(mDesktopFile.comment());
|
||||||
|
|
||||||
|
connect(this, SIGNAL(triggered()), this, SLOT(runConmmand()));
|
||||||
|
QMetaObject::invokeMethod(this, "updateIcon", Qt::QueuedConnection);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
setText(QString());
|
||||||
|
setToolTip(QString());
|
||||||
|
setIcon(QIcon());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
void XdgAction::runConmmand() const
|
||||||
|
{
|
||||||
|
if (mDesktopFile.isValid())
|
||||||
|
mDesktopFile.startDetached();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
void XdgAction::updateIcon()
|
||||||
|
{
|
||||||
|
setIcon(mDesktopFile.icon());
|
||||||
|
if (icon().isNull())
|
||||||
|
setIcon(XdgIcon::fromTheme("application-x-executable"));
|
||||||
|
QCoreApplication::processEvents();
|
||||||
|
}
|
@ -0,0 +1,86 @@
|
|||||||
|
/* BEGIN_COMMON_COPYRIGHT_HEADER
|
||||||
|
* (c)LGPL2+
|
||||||
|
*
|
||||||
|
* Razor - a lightweight, Qt based, desktop toolset
|
||||||
|
* http://razor-qt.org
|
||||||
|
*
|
||||||
|
* Copyright: 2010-2011 Razor team
|
||||||
|
* Authors:
|
||||||
|
* Alexander Sokoloff <sokoloff.a@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 */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef QTXDG_XDGACTION_H
|
||||||
|
#define QTXDG_XDGACTION_H
|
||||||
|
|
||||||
|
#include "xdgmacros.h"
|
||||||
|
#include "xdgdesktopfile.h"
|
||||||
|
|
||||||
|
#include <QAction>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************/ /*!
|
||||||
|
@brief The XdgAction class provides an QAction object based on XdgDesktopFile.
|
||||||
|
|
||||||
|
The following properties of the action are set based on the desktopFile.
|
||||||
|
Text - XdgDesktopFile localizeValue("Name")
|
||||||
|
Icon - XdgDesktopFile icon()
|
||||||
|
ToolTip - XdgDesktopFile localizeValue("Comment")
|
||||||
|
|
||||||
|
Internally this function will create a copy of the desktopFile, so you
|
||||||
|
can delete original XdgDesktopFile object.
|
||||||
|
|
||||||
|
When an action is activated by the user; for example, when the user clicks
|
||||||
|
a menu option, toolbar button or when trigger() was called, XdgAction start
|
||||||
|
the application defined in XdgDesktopFile. @sa XdgDesktopFile::startDetached.
|
||||||
|
****************************************/
|
||||||
|
//*******************************************************************
|
||||||
|
class QTXDG_API XdgAction : public QAction
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit XdgAction(QObject *parent=0);
|
||||||
|
explicit XdgAction(const XdgDesktopFile& desktopFile, QObject *parent=0);
|
||||||
|
explicit XdgAction(const XdgDesktopFile* desktopFile, QObject *parent=0);
|
||||||
|
explicit XdgAction(const QString& desktopFileName, QObject *parent=0);
|
||||||
|
// Constructs a XdgAction that is a copy of the given XdgAction.
|
||||||
|
explicit XdgAction(const XdgAction& other, QObject *parent=0);
|
||||||
|
|
||||||
|
/// Destroys the object and frees allocated resources.
|
||||||
|
virtual ~XdgAction();
|
||||||
|
XdgAction& operator=(const XdgAction& other);
|
||||||
|
|
||||||
|
//! Returns true if the XdgAction is valid; otherwise returns false.
|
||||||
|
bool isValid() const;
|
||||||
|
|
||||||
|
const XdgDesktopFile& desktopFile() const { return mDesktopFile; }
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void runConmmand() const;
|
||||||
|
void updateIcon();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void load(const XdgDesktopFile& desktopFile);
|
||||||
|
|
||||||
|
XdgDesktopFile mDesktopFile;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // QTXDG_XDGACTION_H
|
@ -0,0 +1,91 @@
|
|||||||
|
/* BEGIN_COMMON_COPYRIGHT_HEADER
|
||||||
|
* (c)LGPL2+
|
||||||
|
*
|
||||||
|
* Razor - a lightweight, Qt based, desktop toolset
|
||||||
|
* http://razor-qt.org
|
||||||
|
*
|
||||||
|
* Copyright: 2012 Razor team
|
||||||
|
* Authors:
|
||||||
|
* Alexander Sokoloff <sokoloff.a@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 "xdgautostart.h"
|
||||||
|
#include "xdgdirs.h"
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QSet>
|
||||||
|
#include <QDir>
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
The Autostart Directories are $XDG_CONFIG_DIRS/autostart. If the same filename is
|
||||||
|
located under multiple Autostart Directories only the file under the most
|
||||||
|
important directory should be used.
|
||||||
|
|
||||||
|
When multiple .desktop files with the same name exists in multiple directories
|
||||||
|
then only the Hidden key in the most important .desktop file must be considered:
|
||||||
|
If it is set to true all .desktop files with the same name in the other
|
||||||
|
directories MUST be ignored as well.
|
||||||
|
************************************************/
|
||||||
|
XdgDesktopFileList XdgAutoStart::desktopFileList(bool excludeHidden)
|
||||||
|
{
|
||||||
|
QStringList dirs;
|
||||||
|
dirs << XdgDirs::autostartHome(false) << XdgDirs::autostartDirs();
|
||||||
|
|
||||||
|
return desktopFileList(dirs, excludeHidden);
|
||||||
|
}
|
||||||
|
|
||||||
|
XdgDesktopFileList XdgAutoStart::desktopFileList(QStringList dirs, bool excludeHidden)
|
||||||
|
{
|
||||||
|
dirs.removeDuplicates();
|
||||||
|
|
||||||
|
QSet<QString> processed;
|
||||||
|
XdgDesktopFileList ret;
|
||||||
|
foreach (QString dirName, dirs)
|
||||||
|
{
|
||||||
|
QDir dir(dirName);
|
||||||
|
if (!dir.exists())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
QFileInfoList files = dir.entryInfoList(QStringList("*.desktop"), QDir::Files | QDir::Readable);
|
||||||
|
foreach (QFileInfo fi, files)
|
||||||
|
{
|
||||||
|
if (processed.contains(fi.fileName()))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
processed << fi.fileName();
|
||||||
|
|
||||||
|
XdgDesktopFile desktop;
|
||||||
|
if (!desktop.load(fi.absoluteFilePath()))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!desktop.isSuitable(excludeHidden))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ret << desktop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QString XdgAutoStart::localPath(const XdgDesktopFile& file)
|
||||||
|
{
|
||||||
|
QFileInfo fi(file.fileName());
|
||||||
|
return QString("%1/%2").arg(XdgDirs::autostartHome(), fi.fileName());
|
||||||
|
}
|
@ -0,0 +1,60 @@
|
|||||||
|
/* BEGIN_COMMON_COPYRIGHT_HEADER
|
||||||
|
* (c)LGPL2+
|
||||||
|
*
|
||||||
|
* Razor - a lightweight, Qt based, desktop toolset
|
||||||
|
* http://razor-qt.org
|
||||||
|
*
|
||||||
|
* Copyright: 2012 Razor team
|
||||||
|
* Authors:
|
||||||
|
* Alexander Sokoloff <sokoloff.a@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 */
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef QTXDG_XDGAUTOSTART_H
|
||||||
|
#define QTXDG_XDGAUTOSTART_H
|
||||||
|
|
||||||
|
#include "xdgmacros.h"
|
||||||
|
#include "xdgdesktopfile.h"
|
||||||
|
|
||||||
|
/*! @brief The XdgAutoStart class implements the "Desktop Application Autostart Specification"
|
||||||
|
* from freedesktop.org.
|
||||||
|
* This specification defines a method for automatically starting applications during the startup
|
||||||
|
* of a desktop environment and after mounting a removable medium.
|
||||||
|
* Now we impliment only startup.
|
||||||
|
*
|
||||||
|
* @sa http://standards.freedesktop.org/autostart-spec/autostart-spec-latest.html
|
||||||
|
*/
|
||||||
|
class QTXDG_API XdgAutoStart
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/*! Returns a list of XdgDesktopFile objects for all the .desktop files in the Autostart directories
|
||||||
|
When the .desktop file has the Hidden key set to true, the .desktop file must be ignored. But you
|
||||||
|
can change this behavior by setting excludeHidden to false. */
|
||||||
|
static XdgDesktopFileList desktopFileList(bool excludeHidden=true);
|
||||||
|
|
||||||
|
/*! Returns a list of XdgDesktopFile objects for .desktop files in the specified Autostart directories
|
||||||
|
When the .desktop file has the Hidden key set to true, the .desktop file must be ignored. But you
|
||||||
|
can change this behavior by setting excludeHidden to false. */
|
||||||
|
static XdgDesktopFileList desktopFileList(QStringList dirs, bool excludeHidden=true);
|
||||||
|
|
||||||
|
/// For XdgDesktopFile returns the file path of the same name in users personal autostart directory.
|
||||||
|
static QString localPath(const XdgDesktopFile& file);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // XDGAUTOSTART_H
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,262 @@
|
|||||||
|
/* BEGIN_COMMON_COPYRIGHT_HEADER
|
||||||
|
* (c)LGPL2+
|
||||||
|
*
|
||||||
|
* Razor - a lightweight, Qt based, desktop toolset
|
||||||
|
* http://razor-qt.org
|
||||||
|
*
|
||||||
|
* Copyright: 2010-2011 Razor team
|
||||||
|
* Authors:
|
||||||
|
* Alexander Sokoloff <sokoloff.a@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 */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef QTXDG_XDGDESKTOPFILE_H
|
||||||
|
#define QTXDG_XDGDESKTOPFILE_H
|
||||||
|
|
||||||
|
#include "xdgmacros.h"
|
||||||
|
|
||||||
|
#include <QSharedDataPointer>
|
||||||
|
//#include <QObject>
|
||||||
|
#include <QString>
|
||||||
|
#include <QVariant>
|
||||||
|
#include <QStringList>
|
||||||
|
#include <QtGui/QIcon>
|
||||||
|
#include <QSettings>
|
||||||
|
|
||||||
|
class XdgDesktopFileData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
\brief Desktop files handling.
|
||||||
|
XdgDesktopFile class gives the interface for reading the values from the XDG .desktop file.
|
||||||
|
The interface of this class is similar on QSettings. XdgDesktopFile objects can be passed
|
||||||
|
around by value since the XdgDesktopFile class uses implicit data sharing.
|
||||||
|
|
||||||
|
The Desktop Entry Specification defines 3 types of desktop entries: Application, Link and
|
||||||
|
Directory. The format of .desktop file is described on
|
||||||
|
http://standards.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html
|
||||||
|
|
||||||
|
Note that not all methods in this class make sense for all types of desktop files.
|
||||||
|
\author Alexander Sokoloff <sokoloff.a@gmail.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
class QTXDG_API XdgDesktopFile
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/*! The Desktop Entry Specification defines 3 types of desktop entries: Application, Link and
|
||||||
|
Directory. File type is determined by the "Type" tag. */
|
||||||
|
enum Type
|
||||||
|
{
|
||||||
|
UnknownType, //! Unknown desktop file type. Maybe is invalid.
|
||||||
|
ApplicationType, //! The file describes application.
|
||||||
|
LinkType, //! The file describes URL.
|
||||||
|
DirectoryType //! The file describes directory settings.
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Constructs an empty XdgDesktopFile
|
||||||
|
XdgDesktopFile();
|
||||||
|
|
||||||
|
/*! Constructs a copy of other.
|
||||||
|
This operation takes constant time, because XdgDesktopFile is implicitly shared. This makes
|
||||||
|
returning a XdgDesktopFile from a function very fast. If a shared instance is modified,
|
||||||
|
it will be copied (copy-on-write), and that takes linear time. */
|
||||||
|
XdgDesktopFile(const XdgDesktopFile& other);
|
||||||
|
|
||||||
|
/*! Constructs a new basic DesktopFile. If type is:
|
||||||
|
- ApplicationType, "value" should be the Exec value;
|
||||||
|
- LinkType, "value" should be the URL;
|
||||||
|
- DirectoryType, "value" should be omitted */
|
||||||
|
XdgDesktopFile(XdgDesktopFile::Type type, const QString& name, const QString& value = 0);
|
||||||
|
|
||||||
|
//! Destroys the object.
|
||||||
|
virtual ~XdgDesktopFile();
|
||||||
|
|
||||||
|
//! Assigns other to this DesktopFile and returns a reference to this DesktopFile.
|
||||||
|
XdgDesktopFile& operator=(const XdgDesktopFile& other);
|
||||||
|
|
||||||
|
//! Returns true if both files contain the identical key-value pairs
|
||||||
|
bool operator==(const XdgDesktopFile &other) const;
|
||||||
|
|
||||||
|
//! Loads an DesktopFile from the file with the given fileName.
|
||||||
|
virtual bool load(const QString& fileName);
|
||||||
|
|
||||||
|
//! Saves the DesktopFile to the file with the given fileName. Returns true if successful; otherwise returns false.
|
||||||
|
virtual bool save(const QString &fileName) const;
|
||||||
|
|
||||||
|
/*! This is an overloaded function.
|
||||||
|
This function writes a DesktopFile to the given device. */
|
||||||
|
virtual bool save(QIODevice *device) const;
|
||||||
|
|
||||||
|
/*! Returns the value for key. If the key doesn't exist, returns defaultValue.
|
||||||
|
If no default value is specified, a default QVariant is returned. */
|
||||||
|
QVariant value(const QString& key, const QVariant& defaultValue = QVariant()) const;
|
||||||
|
|
||||||
|
/*! Returns the localized value for key. In the desktop file keys may be postfixed by [LOCALE]. If the
|
||||||
|
localized value doesn't exist, returns non lokalized value. If non localized value doesn't exist, returns defaultValue.
|
||||||
|
LOCALE must be of the form lang_COUNTRY.ENCODING@MODIFIER, where _COUNTRY, .ENCODING, and @MODIFIER may be omitted.
|
||||||
|
|
||||||
|
If no default value is specified, a default QVariant is returned. */
|
||||||
|
QVariant localizedValue(const QString& key, const QVariant& defaultValue = QVariant()) const;
|
||||||
|
|
||||||
|
//! Sets the value of setting key to value. If the key already exists, the previous value is overwritten.
|
||||||
|
void setValue(const QString &key, const QVariant &value);
|
||||||
|
|
||||||
|
/*! Sets the value of setting key to value. If a localized version of the key already exists, the previous value is
|
||||||
|
overwritten. Otherwise, it overwrites the the un-localized version. */
|
||||||
|
void setLocalizedValue(const QString &key, const QVariant &value);
|
||||||
|
|
||||||
|
//! Removes the entry with the specified key, if it exists.
|
||||||
|
void removeEntry(const QString& key);
|
||||||
|
|
||||||
|
//! Returns the entry Categories. It supports X-Categories extensions.
|
||||||
|
QStringList categories() const;
|
||||||
|
|
||||||
|
//! Returns true if there exists a setting called key; returns false otherwise.
|
||||||
|
bool contains(const QString& key) const;
|
||||||
|
|
||||||
|
//! Returns true if the XdgDesktopFile is valid; otherwise returns false.
|
||||||
|
bool isValid() const;
|
||||||
|
|
||||||
|
/*! Returns the file name of the desktop file.
|
||||||
|
* Returns QString() if the file wasn't found when load was called. */
|
||||||
|
QString fileName() const;
|
||||||
|
|
||||||
|
//! Returns an icon specified in this file.
|
||||||
|
QIcon const icon(const QIcon& fallback = QIcon()) const;
|
||||||
|
|
||||||
|
//! Returns an icon name specified in this file.
|
||||||
|
QString const iconName() const;
|
||||||
|
|
||||||
|
//! This function is provided for convenience. It's equivalent to calling localizedValue("Name").toString().
|
||||||
|
QString name() const { return localizedValue("Name").toString(); }
|
||||||
|
|
||||||
|
//! This function is provided for convenience. It's equivalent to calling localizedValue("Comment").toString().
|
||||||
|
QString comment() const { return localizedValue("Comment").toString(); }
|
||||||
|
|
||||||
|
/*! Returns the desktop file type.
|
||||||
|
@see XdgDesktopFile::Type */
|
||||||
|
Type type() const;
|
||||||
|
|
||||||
|
/*! For file with Application type. Starts the program with the optional urls in a new process, and detaches from it.
|
||||||
|
Returns true on success; otherwise returns false.
|
||||||
|
@par urls - A list of files or URLS. Each file is passed as a separate argument to the executable program.
|
||||||
|
|
||||||
|
For file with Link type. Opens URL in the associated application. Parametr urls is not used.
|
||||||
|
|
||||||
|
For file with Directory type, do nothing. */
|
||||||
|
bool startDetached(const QStringList& urls) const;
|
||||||
|
|
||||||
|
//! This function is provided for convenience. It's equivalent to calling startDetached(QStringList(url)).
|
||||||
|
bool startDetached(const QString& url = QString()) 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.
|
||||||
|
@par urls - A list of files or URLS. Each file is passed as a separate argument to the result string program.*/
|
||||||
|
QStringList expandExecString(const QStringList& urls = QStringList()) const;
|
||||||
|
|
||||||
|
/*! Returns the URL for the Link desktop file; otherwise an empty string is returned. */
|
||||||
|
QString url() const;
|
||||||
|
|
||||||
|
/*! The desktop entry specification defines a number of fields to control the visibility of the application menu. This function
|
||||||
|
checks whether to display a this application or not. */
|
||||||
|
QTXDG_DEPRECATED bool isShow(const QString& environment = "Razor") const;
|
||||||
|
|
||||||
|
/*! The desktop entry specification defines a number of fields to control
|
||||||
|
the visibility of the application menu. Thisfunction checks whether
|
||||||
|
to display a this application or not.
|
||||||
|
@par environment - User supplied desktop environment name. If not
|
||||||
|
supplied the desktop will be detected reading the
|
||||||
|
XDG_CURRENT_DESKTOP environment variable. If not set, "UNKNOWN"
|
||||||
|
will be used as the desktop name. All operations envolving the
|
||||||
|
desktop environment name are case insensitive.
|
||||||
|
*/
|
||||||
|
bool isShown(const QString &environment = QString()) const;
|
||||||
|
|
||||||
|
/*! This fuction returns true if the desktop file is applicable to the current environment.
|
||||||
|
@par excludeHidden - if set to true (default), files with "Hidden=true" will be considered "not applicable".
|
||||||
|
Setting this to false is be useful when the user wants to enable/disable items and wants to see those
|
||||||
|
that are Hidden */
|
||||||
|
QTXDG_DEPRECATED bool isApplicable(bool excludeHidden = true, const QString& environment = "Razor") const;
|
||||||
|
|
||||||
|
/*! This fuction returns true if the desktop file is applicable to the
|
||||||
|
current environment.
|
||||||
|
@par excludeHidden - if set to true (default), files with
|
||||||
|
"Hidden=true" will be considered "not applicable". Setting this
|
||||||
|
to false is be useful when the user wants to enable/disable items
|
||||||
|
and wants to see those that are Hidden
|
||||||
|
@par environment - User supplied desktop environment name. If not
|
||||||
|
supplied the desktop will be detected reading the
|
||||||
|
XDG_CURRENT_DESKTOP environment variable. If not set, "UNKNOWN"
|
||||||
|
will be used as the desktop name. All operations envolving the
|
||||||
|
desktop environment name are case insensitive.
|
||||||
|
*/
|
||||||
|
bool isSuitable(bool excludeHidden = true, const QString &environment = QString()) const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual QString prefix() const { return "Desktop Entry"; }
|
||||||
|
virtual bool check() const { return true; }
|
||||||
|
private:
|
||||||
|
/*! Returns the localized version of the key if the Desktop File already contains a localized version of it.
|
||||||
|
If not, returns the same key back */
|
||||||
|
QString localizedKey(const QString& key) const;
|
||||||
|
|
||||||
|
QSharedDataPointer<XdgDesktopFileData> d;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/// Synonym for QList<XdgDesktopFile>
|
||||||
|
typedef QList<XdgDesktopFile> XdgDesktopFileList;
|
||||||
|
|
||||||
|
|
||||||
|
class QTXDG_API XdgDesktopFileCache
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static XdgDesktopFile* getFile(const QString& fileName);
|
||||||
|
static QList<XdgDesktopFile*> getAllFiles();
|
||||||
|
static QList<XdgDesktopFile*> getApps(const QString & mimeType);
|
||||||
|
static XdgDesktopFile* getDefaultApp(const QString& mimeType);
|
||||||
|
static QSettings::Format desktopFileSettingsFormat();
|
||||||
|
|
||||||
|
/*! Return all desktop apps that have category for their Categories key
|
||||||
|
* Note that, according to xdg's spec, for non-standard categories "X-"
|
||||||
|
* is added to the beginning of the category's name. This method takes care
|
||||||
|
* of both cases.
|
||||||
|
* See http://standards.freedesktop.org/menu-spec/menu-spec-latest.html#desktop-entry-extensions
|
||||||
|
*/
|
||||||
|
static QList<XdgDesktopFile*> getAppsOfCategory(const QString &category);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static XdgDesktopFileCache & instance();
|
||||||
|
static XdgDesktopFile* load(const QString & fileName);
|
||||||
|
|
||||||
|
XdgDesktopFileCache();
|
||||||
|
~XdgDesktopFileCache();
|
||||||
|
|
||||||
|
void initialize();
|
||||||
|
void initialize(const QString & dirName);
|
||||||
|
bool m_IsInitialized;
|
||||||
|
QHash<QString, QList<XdgDesktopFile*> > m_defaultAppsCache;
|
||||||
|
QHash<QString, XdgDesktopFile*> m_fileCache;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // QTXDG_XDGDESKTOPFILE_H
|
||||||
|
|
||||||
|
|
@ -0,0 +1,7 @@
|
|||||||
|
#ifndef XDGDESKTOPFILE_P_H
|
||||||
|
#define XDGDESKTOPFILE_P_H
|
||||||
|
|
||||||
|
QTXDG_AUTOTEST bool readDesktopFile(QIODevice & device, QSettings::SettingsMap & map);
|
||||||
|
QTXDG_AUTOTEST bool writeDesktopFile(QIODevice & device, const QSettings::SettingsMap & map);
|
||||||
|
|
||||||
|
#endif // XDGDESKTOPFILE_P_H
|
@ -0,0 +1,430 @@
|
|||||||
|
/* BEGIN_COMMON_COPYRIGHT_HEADER
|
||||||
|
* (c)LGPL2+
|
||||||
|
*
|
||||||
|
* Razor - a lightweight, Qt based, desktop toolset
|
||||||
|
* http://razor-qt.org
|
||||||
|
*
|
||||||
|
* Copyright: 2010-2011 Razor team
|
||||||
|
* Authors:
|
||||||
|
* Alexander Sokoloff <sokoloff.a@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 "xdgdirs.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QStringBuilder> // for the % operator
|
||||||
|
#include <QDebug>
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
|
||||||
|
#include <QStandardPaths>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const QString userDirectoryString[8] =
|
||||||
|
{
|
||||||
|
"Desktop",
|
||||||
|
"Download",
|
||||||
|
"Templates",
|
||||||
|
"Publicshare",
|
||||||
|
"Documents",
|
||||||
|
"Music",
|
||||||
|
"Pictures",
|
||||||
|
"Videos"
|
||||||
|
};
|
||||||
|
|
||||||
|
// Helper functions prototypes
|
||||||
|
void fixBashShortcuts(QString &s);
|
||||||
|
void removeEndingSlash(QString &s);
|
||||||
|
QString createDirectory(const QString &dir);
|
||||||
|
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
|
||||||
|
void cleanAndAddPostfix(QStringList &dirs, const QString& postfix);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if QT_VERSION < QT_VERSION_CHECK(5,0,0)
|
||||||
|
QString xdgSingleDir(const QString &envVar, const QString &def, bool createDir);
|
||||||
|
QStringList xdgDirList(const QString &envVar, const QString &postfix);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
Helper func.
|
||||||
|
************************************************/
|
||||||
|
void fixBashShortcuts(QString &s)
|
||||||
|
{
|
||||||
|
if (s.startsWith(QLatin1Char('~')))
|
||||||
|
s = QString(getenv("HOME")) + (s).mid(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void removeEndingSlash(QString &s)
|
||||||
|
{
|
||||||
|
// We don't check for empty strings. Caller must check it.
|
||||||
|
|
||||||
|
// Remove the ending slash, except for root dirs.
|
||||||
|
if (s.length() > 1 && s.endsWith(QLatin1Char('/')))
|
||||||
|
s.chop(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString createDirectory(const QString &dir)
|
||||||
|
{
|
||||||
|
QDir d(dir);
|
||||||
|
if (!d.exists())
|
||||||
|
{
|
||||||
|
if (!d.mkpath("."))
|
||||||
|
{
|
||||||
|
qWarning() << QString("Can't create %1 directory.").arg(d.absolutePath());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QString r = d.absolutePath();
|
||||||
|
removeEndingSlash(r);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cleanAndAddPostfix(QStringList &dirs, const QString& postfix)
|
||||||
|
{
|
||||||
|
const int N = dirs.count();
|
||||||
|
for(int i = 0; i < N; ++i)
|
||||||
|
{
|
||||||
|
fixBashShortcuts(dirs[i]);
|
||||||
|
removeEndingSlash(dirs[i]);
|
||||||
|
dirs[i].append(postfix);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
Helper func.
|
||||||
|
************************************************/
|
||||||
|
#if QT_VERSION < QT_VERSION_CHECK(5,0,0)
|
||||||
|
QString xdgSingleDir(const QString &envVar, const QString &def, bool createDir)
|
||||||
|
{
|
||||||
|
QString s(getenv(envVar.toAscii()));
|
||||||
|
|
||||||
|
if (!s.isEmpty())
|
||||||
|
fixBashShortcuts(s);
|
||||||
|
else
|
||||||
|
s = QString("%1/%2").arg(getenv("HOME"), def);
|
||||||
|
|
||||||
|
if (createDir)
|
||||||
|
return createDirectory(s);
|
||||||
|
|
||||||
|
removeEndingSlash(s);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
Helper func.
|
||||||
|
************************************************/
|
||||||
|
#if QT_VERSION < QT_VERSION_CHECK(5,0,0)
|
||||||
|
QStringList xdgDirList(const QString &envVar, const QString &postfix)
|
||||||
|
{
|
||||||
|
QStringList dirs = QString(getenv(envVar.toAscii())).split(':', QString::SkipEmptyParts);
|
||||||
|
|
||||||
|
QMutableStringListIterator i(dirs);
|
||||||
|
while(i.hasNext()) {
|
||||||
|
i.next();
|
||||||
|
QString s = i.value();
|
||||||
|
if (s.isEmpty()) {
|
||||||
|
i.remove();
|
||||||
|
} else {
|
||||||
|
fixBashShortcuts(s);
|
||||||
|
removeEndingSlash(s);
|
||||||
|
i.setValue(s % postfix);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dirs;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
QString XdgDirs::userDir(XdgDirs::UserDirectory dir)
|
||||||
|
{
|
||||||
|
// possible values for UserDirectory
|
||||||
|
if (dir < 0 || dir > 7)
|
||||||
|
return QString();
|
||||||
|
|
||||||
|
QString folderName = userDirectoryString[dir];
|
||||||
|
|
||||||
|
QString fallback;
|
||||||
|
if (getenv("HOME") == NULL)
|
||||||
|
return QString("/tmp");
|
||||||
|
else if (dir == XdgDirs::Desktop)
|
||||||
|
fallback = QString("%1/%2").arg(getenv("HOME")).arg("Desktop");
|
||||||
|
else
|
||||||
|
fallback = QString(getenv("HOME"));
|
||||||
|
|
||||||
|
QString configDir(configHome());
|
||||||
|
QFile configFile(configDir + "/user-dirs.dirs");
|
||||||
|
if (!configFile.exists())
|
||||||
|
return fallback;
|
||||||
|
|
||||||
|
if (!configFile.open(QIODevice::ReadOnly | QIODevice::Text))
|
||||||
|
return fallback;
|
||||||
|
|
||||||
|
QString userDirVar("XDG_" + folderName.toUpper() + "_DIR");
|
||||||
|
QTextStream in(&configFile);
|
||||||
|
QString line;
|
||||||
|
while (!in.atEnd())
|
||||||
|
{
|
||||||
|
line = in.readLine();
|
||||||
|
if (line.contains(userDirVar))
|
||||||
|
{
|
||||||
|
configFile.close();
|
||||||
|
|
||||||
|
// get path between quotes
|
||||||
|
line = line.section(QLatin1Char('"'), 1, 1);
|
||||||
|
line.replace(QLatin1String("$HOME"), QLatin1String("~"));
|
||||||
|
fixBashShortcuts(line);
|
||||||
|
return line;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
configFile.close();
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
bool XdgDirs::setUserDir(XdgDirs::UserDirectory dir, const QString& value, bool createDir)
|
||||||
|
{
|
||||||
|
// possible values for UserDirectory
|
||||||
|
if (dir < 0 || dir > 7)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!(value.startsWith(QLatin1String("$HOME"))
|
||||||
|
|| value.startsWith(QLatin1String("~/"))
|
||||||
|
|| value.startsWith(QString(getenv("HOME")))))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
QString folderName = userDirectoryString[dir];
|
||||||
|
|
||||||
|
QString configDir(configHome());
|
||||||
|
QFile configFile(configDir % QLatin1String("/user-dirs.dirs"));
|
||||||
|
|
||||||
|
// create the file if doesn't exist and opens it
|
||||||
|
if (!configFile.open(QIODevice::ReadWrite | QIODevice::Text))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
QTextStream stream(&configFile);
|
||||||
|
QVector<QString> lines;
|
||||||
|
QString line;
|
||||||
|
bool foundVar = false;
|
||||||
|
while (!stream.atEnd())
|
||||||
|
{
|
||||||
|
line = stream.readLine();
|
||||||
|
if (line.indexOf(QLatin1String("XDG_") + folderName.toUpper() + QLatin1String("_DIR")) == 0)
|
||||||
|
{
|
||||||
|
foundVar = true;
|
||||||
|
QString path = line.section(QLatin1Char('"'), 1, 1);
|
||||||
|
line.replace(path, value);
|
||||||
|
lines.append(line);
|
||||||
|
}
|
||||||
|
else if (line.indexOf(QLatin1String("XDG_")) == 0)
|
||||||
|
{
|
||||||
|
lines.append(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.reset();
|
||||||
|
configFile.resize(0);
|
||||||
|
if (!foundVar)
|
||||||
|
stream << QString("XDG_%1_DIR=\"%2\"\n").arg(folderName.toUpper()).arg(value);
|
||||||
|
|
||||||
|
for (QVector<QString>::iterator i = lines.begin(); i != lines.end(); ++i)
|
||||||
|
stream << *i << "\n";
|
||||||
|
|
||||||
|
configFile.close();
|
||||||
|
|
||||||
|
if (createDir) {
|
||||||
|
QString path = QString(value).replace(QLatin1String("$HOME"), QLatin1String("~"));
|
||||||
|
fixBashShortcuts(path);
|
||||||
|
QDir().mkpath(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
QString XdgDirs::dataHome(bool createDir)
|
||||||
|
{
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
|
||||||
|
QString s = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
|
||||||
|
fixBashShortcuts(s);
|
||||||
|
if (createDir)
|
||||||
|
return createDirectory(s);
|
||||||
|
|
||||||
|
removeEndingSlash(s);
|
||||||
|
return s;
|
||||||
|
#else
|
||||||
|
return xdgSingleDir("XDG_DATA_HOME", QLatin1String(".local/share"), createDir);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
QString XdgDirs::configHome(bool createDir)
|
||||||
|
{
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
|
||||||
|
QString s = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation);
|
||||||
|
fixBashShortcuts(s);
|
||||||
|
if (createDir)
|
||||||
|
return createDirectory(s);
|
||||||
|
|
||||||
|
removeEndingSlash(s);
|
||||||
|
return s;
|
||||||
|
#else
|
||||||
|
return xdgSingleDir("XDG_CONFIG_HOME", QLatin1String(".config"), createDir);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
QStringList XdgDirs::dataDirs(const QString &postfix)
|
||||||
|
{
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
|
||||||
|
QString d = QFile::decodeName(qgetenv("XDG_DATA_DIRS"));
|
||||||
|
QStringList dirs = d.split(QLatin1Char(':'), QString::SkipEmptyParts);
|
||||||
|
|
||||||
|
QMutableListIterator<QString> it(dirs);
|
||||||
|
while (it.hasNext()) {
|
||||||
|
const QString dir = it.next();
|
||||||
|
if (!dir.startsWith(QLatin1Char('/')))
|
||||||
|
it.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
dirs.removeDuplicates();
|
||||||
|
cleanAndAddPostfix(dirs, postfix);
|
||||||
|
return dirs;
|
||||||
|
#else
|
||||||
|
QStringList dirs = xdgDirList("XDG_DATA_DIRS", postfix);
|
||||||
|
if (dirs.isEmpty())
|
||||||
|
{
|
||||||
|
dirs << QLatin1String("/usr/local/share") % postfix;
|
||||||
|
dirs << QLatin1String("/usr/share") % postfix;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dirs;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
QStringList XdgDirs::configDirs(const QString &postfix)
|
||||||
|
{
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
|
||||||
|
QStringList dirs;
|
||||||
|
const QString env = QFile::decodeName(qgetenv("XDG_CONFIG_DIRS"));
|
||||||
|
if (env.isEmpty())
|
||||||
|
dirs.append(QString::fromLatin1("/etc/xdg"));
|
||||||
|
else
|
||||||
|
dirs = env.split(QLatin1Char(':'), QString::SkipEmptyParts);
|
||||||
|
|
||||||
|
cleanAndAddPostfix(dirs, postfix);
|
||||||
|
return dirs;
|
||||||
|
#else
|
||||||
|
QStringList dirs = xdgDirList("XDG_CONFIG_DIRS", postfix);
|
||||||
|
if (dirs.isEmpty())
|
||||||
|
{
|
||||||
|
dirs << QLatin1String("/etc/xdg") % postfix;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dirs;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
QString XdgDirs::cacheHome(bool createDir)
|
||||||
|
{
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
|
||||||
|
QString s = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation);
|
||||||
|
fixBashShortcuts(s);
|
||||||
|
if (createDir)
|
||||||
|
return createDirectory(s);
|
||||||
|
|
||||||
|
removeEndingSlash(s);
|
||||||
|
return s;
|
||||||
|
#else
|
||||||
|
return xdgSingleDir("XDG_CACHE_HOME", QLatin1String(".cache"), createDir);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
QString XdgDirs::runtimeDir()
|
||||||
|
{
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
|
||||||
|
QString result = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation);
|
||||||
|
fixBashShortcuts(result);
|
||||||
|
removeEndingSlash(result);
|
||||||
|
return result;
|
||||||
|
#else
|
||||||
|
QString result(getenv("XDG_RUNTIME_DIR"));
|
||||||
|
fixBashShortcuts(result);
|
||||||
|
return result;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
QString XdgDirs::autostartHome(bool createDir)
|
||||||
|
{
|
||||||
|
QString s = QString("%1/autostart").arg(configHome(createDir));
|
||||||
|
fixBashShortcuts(s);
|
||||||
|
|
||||||
|
if (createDir)
|
||||||
|
return createDirectory(s);
|
||||||
|
|
||||||
|
QDir d(s);
|
||||||
|
QString r = d.absolutePath();
|
||||||
|
removeEndingSlash(r);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
QStringList XdgDirs::autostartDirs(const QString &postfix)
|
||||||
|
{
|
||||||
|
QStringList dirs;
|
||||||
|
QStringList s = configDirs();
|
||||||
|
foreach(QString dir, s)
|
||||||
|
dirs << QString("%1/autostart").arg(dir) + postfix;
|
||||||
|
|
||||||
|
return dirs;
|
||||||
|
}
|
@ -0,0 +1,149 @@
|
|||||||
|
/* BEGIN_COMMON_COPYRIGHT_HEADER
|
||||||
|
* (c)LGPL2+
|
||||||
|
*
|
||||||
|
* Razor - a lightweight, Qt based, desktop toolset
|
||||||
|
* http://razor-qt.org
|
||||||
|
*
|
||||||
|
* Copyright: 2010-2011 Razor team
|
||||||
|
* Authors:
|
||||||
|
* Alexander Sokoloff <sokoloff.a@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 */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef QTXDG_XDGDIRS_H
|
||||||
|
#define QTXDG_XDGDIRS_H
|
||||||
|
|
||||||
|
#include "xdgmacros.h"
|
||||||
|
#include <QString>
|
||||||
|
#include <QStringList>
|
||||||
|
|
||||||
|
/*! @brief The XdgMenu class implements the "XDG Base Directory Specification" from freedesktop.org.
|
||||||
|
* This specification defines where these files should be looked for by defining one or more base
|
||||||
|
* directories relative to which files should be located.
|
||||||
|
*
|
||||||
|
* All postfix parameters should start with an '/' slash.
|
||||||
|
*
|
||||||
|
* @sa http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
|
||||||
|
*/
|
||||||
|
|
||||||
|
class QTXDG_API XdgDirs
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum UserDirectory
|
||||||
|
{
|
||||||
|
Desktop,
|
||||||
|
Download,
|
||||||
|
Templates,
|
||||||
|
PublicShare,
|
||||||
|
Documents,
|
||||||
|
Music,
|
||||||
|
Pictures,
|
||||||
|
Videos
|
||||||
|
};
|
||||||
|
|
||||||
|
/*! @brief Returns the path to the user folder passed as parameter dir defined in
|
||||||
|
* $XDG_CONFIG_HOME/user-dirs.dirs. Returns /tmp if no $HOME defined, $HOME/Desktop if
|
||||||
|
* dir equals XdgDirs::Desktop or $HOME othewise.
|
||||||
|
*/
|
||||||
|
static QString userDir(UserDirectory dir);
|
||||||
|
|
||||||
|
/*! @brief Returns true if writting into configuration file $XDG_CONFIG_HOME/user-dirs.dirs
|
||||||
|
* the path in value for the directory in dir is succesfull. Returns false otherwise. If
|
||||||
|
* createDir is true, dir will be created if it doesn't exist.
|
||||||
|
*/
|
||||||
|
static bool setUserDir(UserDirectory dir, const QString &value, bool createDir);
|
||||||
|
|
||||||
|
/*! @brief Returns the path to the directory that corresponds to the $XDG_DATA_HOME.
|
||||||
|
* If @i createDir is true, the function will create the directory.
|
||||||
|
*
|
||||||
|
* $XDG_DATA_HOME defines the base directory relative to which user specific data files
|
||||||
|
* should be stored. If $XDG_DATA_HOME is either not set or empty, a default equal to
|
||||||
|
* $HOME/.local/share should be used.
|
||||||
|
*/
|
||||||
|
static QString dataHome(bool createDir=true);
|
||||||
|
|
||||||
|
|
||||||
|
/*! @brief Returns the path to the directory that corresponds to the $XDG_CONFIG_HOME.
|
||||||
|
* If @i createDir is true, the function will create the directory.
|
||||||
|
*
|
||||||
|
* $XDG_CONFIG_HOME defines the base directory relative to which user specific configuration
|
||||||
|
* files should be stored. If $XDG_CONFIG_HOME is either not set or empty, a default equal
|
||||||
|
* to $HOME/.config should be used.
|
||||||
|
*/
|
||||||
|
static QString configHome(bool createDir=true);
|
||||||
|
|
||||||
|
|
||||||
|
/*! @brief Returns a list of all directories that corresponds to the $XDG_DATA_DIRS.
|
||||||
|
* $XDG_DATA_DIRS defines the preference-ordered set of base directories to search for data
|
||||||
|
* files in addition to the $XDG_DATA_HOME base directory. If $XDG_DATA_DIRS is either not set
|
||||||
|
* or empty, a value equal to /usr/local/share:/usr/share is used.
|
||||||
|
*
|
||||||
|
* If the postfix is not empty it will append to end of each returned directory.
|
||||||
|
*/
|
||||||
|
static QStringList dataDirs(const QString &postfix = QString());
|
||||||
|
|
||||||
|
|
||||||
|
/*! @brief Returns a list of all directories that corresponds to the $XDG_CONFIG_DIRS.
|
||||||
|
* $XDG_CONFIG_DIRS defines the preference-ordered set of base directories to search for
|
||||||
|
* configuration files in addition to the $XDG_CONFIG_HOME base directory. If $XDG_CONFIG_DIRS
|
||||||
|
* is either not set or empty, a value equal to /etc/xdg should be used.
|
||||||
|
*
|
||||||
|
* If the postfix is not empty it will append to end of each returned directory.
|
||||||
|
*/
|
||||||
|
static QStringList configDirs(const QString &postfix = QString());
|
||||||
|
|
||||||
|
|
||||||
|
/*! @brief Returns the path to the directory that corresponds to the $XDG_CACHE_HOME.
|
||||||
|
* If @i createDir is true, the function will create the directory.
|
||||||
|
*
|
||||||
|
* $XDG_CACHE_HOME defines the base directory relative to which user specific non-essential
|
||||||
|
* data files should be stored. If $XDG_CACHE_HOME is either not set or empty,
|
||||||
|
* a default equal to $HOME/.cache should be used.
|
||||||
|
*/
|
||||||
|
static QString cacheHome(bool createDir=true);
|
||||||
|
|
||||||
|
|
||||||
|
/*! @brief Returns the path to the directory that corresponds to the $XDG_RUNTIME_DIR.
|
||||||
|
* $XDG_RUNTIME_DIR defines the base directory relative to which user-specific non-essential
|
||||||
|
* runtime files and other file objects (such as sockets, named pipes, ...) should be stored.
|
||||||
|
* The directory MUST be owned by the user, and he MUST be the only one having read and write
|
||||||
|
* access to it. Its Unix access mode MUST be 0700.
|
||||||
|
*/
|
||||||
|
static QString runtimeDir();
|
||||||
|
|
||||||
|
/*! @brief Returns the path to the directory that corresponds to the $XDG_CONFIG_HOME/autostart
|
||||||
|
*
|
||||||
|
* If $XDG_CONFIG_HOME is not set, the Autostart Directory in the user's home directory is
|
||||||
|
* ~/.config/autostart/
|
||||||
|
*/
|
||||||
|
static QString autostartHome(bool createDir=true);
|
||||||
|
|
||||||
|
/*! @brief Returns a list of all directories that correspond to $XDG_CONFIG_DIRS/autostart
|
||||||
|
* If $XDG_CONFIG_DIRS is not set, the system wide Autostart Directory is /etc/xdg/autostart
|
||||||
|
*
|
||||||
|
* If the postfix is not empty it will append to end of each returned directory.
|
||||||
|
*
|
||||||
|
* Note: this does not include the user's autostart directory
|
||||||
|
* @sa autostartHome()
|
||||||
|
*/
|
||||||
|
static QStringList autostartDirs(const QString &postfix = QString());
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // QTXDG_XDGDIRS_H
|
@ -0,0 +1,200 @@
|
|||||||
|
/* BEGIN_COMMON_COPYRIGHT_HEADER
|
||||||
|
* (c)LGPL2+
|
||||||
|
*
|
||||||
|
* Razor - a lightweight, Qt based, desktop toolset
|
||||||
|
* http://razor-qt.org
|
||||||
|
*
|
||||||
|
* Copyright: 2010-2011 Razor team
|
||||||
|
* Authors:
|
||||||
|
* Alexander Sokoloff <sokoloff.a@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 "xdgicon.h"
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QStringList>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QCache>
|
||||||
|
#if QT_VERSION < QT_VERSION_CHECK(5,0,0)
|
||||||
|
#include "qiconfix/qiconloader_p_qt4.h"
|
||||||
|
#else
|
||||||
|
#include "qiconfix/qiconloader_p.h"
|
||||||
|
#endif
|
||||||
|
#include <QCoreApplication>
|
||||||
|
|
||||||
|
#define DEFAULT_APP_ICON "application-x-executable"
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
static void qt_cleanup_icon_cache();
|
||||||
|
typedef QCache<QString, QIcon> IconCache;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
struct QtIconCache: public IconCache
|
||||||
|
{
|
||||||
|
QtIconCache()
|
||||||
|
{
|
||||||
|
qAddPostRoutine(qt_cleanup_icon_cache);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Q_GLOBAL_STATIC(IconCache, qtIconCache);
|
||||||
|
|
||||||
|
static void qt_cleanup_icon_cache()
|
||||||
|
{
|
||||||
|
qtIconCache()->clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
XdgIcon::XdgIcon()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
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);
|
||||||
|
QtXdg::QIconLoader::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.
|
||||||
|
************************************************/
|
||||||
|
QIcon XdgIcon::fromTheme(const QString& iconName, const QIcon& fallback)
|
||||||
|
{
|
||||||
|
if (iconName.isEmpty())
|
||||||
|
return fallback;
|
||||||
|
|
||||||
|
bool isAbsolute = (iconName[0] == '/');
|
||||||
|
|
||||||
|
QString name = QFileInfo(iconName).fileName();
|
||||||
|
if (name.endsWith(".png", Qt::CaseInsensitive) ||
|
||||||
|
name.endsWith(".svg", Qt::CaseInsensitive) ||
|
||||||
|
name.endsWith(".xpm", Qt::CaseInsensitive))
|
||||||
|
{
|
||||||
|
name.truncate(name.length() - 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
QIcon icon;
|
||||||
|
|
||||||
|
if (qtIconCache()->contains(name)) {
|
||||||
|
icon = *qtIconCache()->object(name);
|
||||||
|
} else {
|
||||||
|
QIcon *cachedIcon;
|
||||||
|
if (!isAbsolute)
|
||||||
|
cachedIcon = new QIcon(new QtXdg::QIconLoaderEngineFixed(name));
|
||||||
|
else
|
||||||
|
cachedIcon = new QIcon(iconName);
|
||||||
|
qtIconCache()->insert(name, cachedIcon);
|
||||||
|
icon = *cachedIcon;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note the qapp check is to allow lazy loading of static icons
|
||||||
|
// Supporting fallbacks will not work for this case.
|
||||||
|
if (qApp && !isAbsolute && icon.availableSizes().isEmpty())
|
||||||
|
{
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
return icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
Returns the QIcon corresponding to names in the current icon theme. If no such icon
|
||||||
|
is found in the current theme fallback is return instead.
|
||||||
|
************************************************/
|
||||||
|
QIcon XdgIcon::fromTheme(const QStringList& iconNames, const QIcon& fallback)
|
||||||
|
{
|
||||||
|
foreach (QString iconName, iconNames)
|
||||||
|
{
|
||||||
|
QIcon icon = fromTheme(iconName);
|
||||||
|
if (!icon.isNull())
|
||||||
|
return icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
QIcon XdgIcon::fromTheme(const QString &iconName,
|
||||||
|
const QString &fallbackIcon1,
|
||||||
|
const QString &fallbackIcon2,
|
||||||
|
const QString &fallbackIcon3,
|
||||||
|
const QString &fallbackIcon4)
|
||||||
|
{
|
||||||
|
QStringList icons;
|
||||||
|
icons << iconName;
|
||||||
|
if (!fallbackIcon1.isEmpty()) icons << fallbackIcon1;
|
||||||
|
if (!fallbackIcon2.isEmpty()) icons << fallbackIcon2;
|
||||||
|
if (!fallbackIcon3.isEmpty()) icons << fallbackIcon3;
|
||||||
|
if (!fallbackIcon4.isEmpty()) icons << fallbackIcon4;
|
||||||
|
|
||||||
|
return fromTheme(icons);
|
||||||
|
}
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
QIcon XdgIcon::defaultApplicationIcon()
|
||||||
|
{
|
||||||
|
return fromTheme(DEFAULT_APP_ICON);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
QString XdgIcon::defaultApplicationIconName()
|
||||||
|
{
|
||||||
|
return DEFAULT_APP_ICON;
|
||||||
|
}
|
@ -0,0 +1,62 @@
|
|||||||
|
/* BEGIN_COMMON_COPYRIGHT_HEADER
|
||||||
|
* (c)LGPL2+
|
||||||
|
*
|
||||||
|
* Razor - a lightweight, Qt based, desktop toolset
|
||||||
|
* http://razor-qt.org
|
||||||
|
*
|
||||||
|
* Copyright: 2010-2011 Razor team
|
||||||
|
* Authors:
|
||||||
|
* Alexander Sokoloff <sokoloff.a@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 */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef QTXDG_XDGICON_H
|
||||||
|
#define QTXDG_XDGICON_H
|
||||||
|
|
||||||
|
#include "xdgmacros.h"
|
||||||
|
#include <QtGui/QIcon>
|
||||||
|
#include <QString>
|
||||||
|
#include <QStringList>
|
||||||
|
|
||||||
|
class QTXDG_API XdgIcon
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static QIcon fromTheme(const QString& iconName, const QIcon& fallback = QIcon());
|
||||||
|
static QIcon fromTheme(const QString& iconName,
|
||||||
|
const QString &fallbackIcon1,
|
||||||
|
const QString &fallbackIcon2 = QString(),
|
||||||
|
const QString &fallbackIcon3 = QString(),
|
||||||
|
const QString &fallbackIcon4 = QString());
|
||||||
|
static QIcon fromTheme(const QStringList& iconNames, const QIcon& fallback = QIcon());
|
||||||
|
|
||||||
|
static QString themeName();
|
||||||
|
static void setThemeName(const QString& themeName);
|
||||||
|
|
||||||
|
static QIcon defaultApplicationIcon();
|
||||||
|
static QString defaultApplicationIconName();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
explicit XdgIcon();
|
||||||
|
virtual ~XdgIcon();
|
||||||
|
private:
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // QTXDG_XDGICON_H
|
@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* libqtxdg - An Qt implementation of freedesktop.org xdg specs
|
||||||
|
* Copyright (C) 2014 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef QTXDG_MACROS_H
|
||||||
|
#define QTXDG_MACROS_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
# include <QtCore/qglobal.h>
|
||||||
|
# ifndef QTXDG_DEPRECATED
|
||||||
|
# define QTXDG_DEPRECATED Q_DECL_DEPRECATED
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef QTXDG_COMPILATION
|
||||||
|
#define QTXDG_API Q_DECL_EXPORT
|
||||||
|
#else
|
||||||
|
#define QTXDG_API Q_DECL_IMPORT
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(QTXDG_COMPILATION) && defined(QTXDG_BUILDING_TESTS)
|
||||||
|
# define QTXDG_AUTOTEST Q_DECL_IMPORT
|
||||||
|
#elif defined(QTXDG_COMPILATION) && defined(QTXDG_TESTS)
|
||||||
|
# define QTXDG_AUTOTEST Q_DECL_EXPORT
|
||||||
|
#else
|
||||||
|
# define QTXDG_AUTOTEST
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // QTXDG_MACROS_H
|
@ -0,0 +1,825 @@
|
|||||||
|
/* BEGIN_COMMON_COPYRIGHT_HEADER
|
||||||
|
* (c)LGPL2+
|
||||||
|
*
|
||||||
|
* Razor - a lightweight, Qt based, desktop toolset
|
||||||
|
* http://razor-qt.org
|
||||||
|
*
|
||||||
|
* Copyright: 2010-2011 Razor team
|
||||||
|
* Authors:
|
||||||
|
* Alexander Sokoloff <sokoloff.a@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 "xdgmenu.h"
|
||||||
|
#include "xdgmenu_p.h"
|
||||||
|
#include "xdgmenureader.h"
|
||||||
|
#include "xmlhelper.h"
|
||||||
|
#include "xdgmenurules.h"
|
||||||
|
#include "xdgmenuapplinkprocessor.h"
|
||||||
|
#include "xdgdirs.h"
|
||||||
|
#include "xdgmenulayoutprocessor.h"
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QtXml/QDomElement>
|
||||||
|
#include <QtXml/QDomNamedNodeMap>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QSettings>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QHash>
|
||||||
|
#include <QLocale>
|
||||||
|
#include <QFileSystemWatcher>
|
||||||
|
#include <QTranslator>
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QCryptographicHash>
|
||||||
|
|
||||||
|
|
||||||
|
// Helper functions prototypes
|
||||||
|
void installTranslation(const QString &name);
|
||||||
|
bool isParent(const QDomElement& parent, const QDomElement& child);
|
||||||
|
|
||||||
|
void installTranslation(const QString &name)
|
||||||
|
{
|
||||||
|
static bool alreadyLoaded = false;
|
||||||
|
|
||||||
|
if (alreadyLoaded)
|
||||||
|
return;
|
||||||
|
|
||||||
|
QString locale = QLocale::system().name();
|
||||||
|
QTranslator *translator = new QTranslator(qApp);
|
||||||
|
translator->load(QString("%1/%2_%3.qm").arg(TRANSLATIONS_DIR, name, locale));
|
||||||
|
|
||||||
|
QCoreApplication::installTranslator(translator);
|
||||||
|
alreadyLoaded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
XdgMenu::XdgMenu(QObject *parent) :
|
||||||
|
QObject(parent),
|
||||||
|
d_ptr(new XdgMenuPrivate(this))
|
||||||
|
{
|
||||||
|
installTranslation("libqtxdg");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
XdgMenu::~XdgMenu()
|
||||||
|
{
|
||||||
|
Q_D(XdgMenu);
|
||||||
|
delete d;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
XdgMenuPrivate::XdgMenuPrivate(XdgMenu *parent):
|
||||||
|
mOutDated(true),
|
||||||
|
q_ptr(parent)
|
||||||
|
{
|
||||||
|
mRebuildDelayTimer.setSingleShot(true);
|
||||||
|
mRebuildDelayTimer.setInterval(REBUILD_DELAY);
|
||||||
|
|
||||||
|
connect(&mRebuildDelayTimer, SIGNAL(timeout()), this, SLOT(rebuild()));
|
||||||
|
connect(&mWatcher, SIGNAL(fileChanged(QString)), &mRebuildDelayTimer, SLOT(start()));
|
||||||
|
connect(&mWatcher, SIGNAL(directoryChanged(QString)), &mRebuildDelayTimer, SLOT(start()));
|
||||||
|
|
||||||
|
|
||||||
|
connect(this, SIGNAL(changed()), q_ptr, SIGNAL(changed()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
const QString XdgMenu::logDir() const
|
||||||
|
{
|
||||||
|
Q_D(const XdgMenu);
|
||||||
|
return d->mLogDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
void XdgMenu::setLogDir(const QString& directory)
|
||||||
|
{
|
||||||
|
Q_D(XdgMenu);
|
||||||
|
d->mLogDir = directory;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
const QDomDocument XdgMenu::xml() const
|
||||||
|
{
|
||||||
|
Q_D(const XdgMenu);
|
||||||
|
return d->mXml;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
QString XdgMenu::menuFileName() const
|
||||||
|
{
|
||||||
|
Q_D(const XdgMenu);
|
||||||
|
return d->mMenuFileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
QStringList XdgMenu::environments()
|
||||||
|
{
|
||||||
|
Q_D(XdgMenu);
|
||||||
|
return d->mEnvironments;
|
||||||
|
}
|
||||||
|
|
||||||
|
void XdgMenu::setEnvironments(const QStringList &envs)
|
||||||
|
{
|
||||||
|
Q_D(XdgMenu);
|
||||||
|
d->mEnvironments = envs;
|
||||||
|
}
|
||||||
|
void XdgMenu::setEnvironments(const QString &env)
|
||||||
|
{
|
||||||
|
setEnvironments(QStringList() << env);
|
||||||
|
}
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
const QString XdgMenu::errorString() const
|
||||||
|
{
|
||||||
|
Q_D(const XdgMenu);
|
||||||
|
return d->mErrorString;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
bool XdgMenu::read(const QString& menuFileName)
|
||||||
|
{
|
||||||
|
Q_D(XdgMenu);
|
||||||
|
|
||||||
|
d->mMenuFileName = menuFileName;
|
||||||
|
|
||||||
|
d->clearWatcher();
|
||||||
|
|
||||||
|
XdgMenuReader reader(this);
|
||||||
|
if (!reader.load(d->mMenuFileName))
|
||||||
|
{
|
||||||
|
qWarning() << reader.errorString();
|
||||||
|
d->mErrorString = reader.errorString();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
d->mXml = reader.xml();
|
||||||
|
QDomElement root = d->mXml.documentElement();
|
||||||
|
d->saveLog("00-reader.xml");
|
||||||
|
|
||||||
|
d->simplify(root);
|
||||||
|
d->saveLog("01-simplify.xml");
|
||||||
|
|
||||||
|
d->mergeMenus(root);
|
||||||
|
d->saveLog("02-mergeMenus.xml");
|
||||||
|
|
||||||
|
d->moveMenus(root);
|
||||||
|
d->saveLog("03-moveMenus.xml");
|
||||||
|
|
||||||
|
d->mergeMenus(root);
|
||||||
|
d->saveLog("04-mergeMenus.xml");
|
||||||
|
|
||||||
|
d->deleteDeletedMenus(root);
|
||||||
|
d->saveLog("05-deleteDeletedMenus.xml");
|
||||||
|
|
||||||
|
d->processDirectoryEntries(root, QStringList());
|
||||||
|
d->saveLog("06-processDirectoryEntries.xml");
|
||||||
|
|
||||||
|
d->processApps(root);
|
||||||
|
d->saveLog("07-processApps.xml");
|
||||||
|
|
||||||
|
d->processLayouts(root);
|
||||||
|
d->saveLog("08-processLayouts.xml");
|
||||||
|
|
||||||
|
d->deleteEmpty(root);
|
||||||
|
d->saveLog("09-deleteEmpty.xml");
|
||||||
|
|
||||||
|
d->fixSeparators(root);
|
||||||
|
d->saveLog("10-fixSeparators.xml");
|
||||||
|
|
||||||
|
|
||||||
|
d->mOutDated = false;
|
||||||
|
d->mHash = QCryptographicHash::hash(d->mXml.toByteArray(), QCryptographicHash::Md5);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
void XdgMenu::save(const QString& fileName)
|
||||||
|
{
|
||||||
|
Q_D(const XdgMenu);
|
||||||
|
|
||||||
|
QFile file(fileName);
|
||||||
|
if (!file.open(QFile::WriteOnly | QFile::Text))
|
||||||
|
{
|
||||||
|
qWarning() << QString("Cannot write file %1:\n%2.")
|
||||||
|
.arg(fileName)
|
||||||
|
.arg(file.errorString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QTextStream ts(&file);
|
||||||
|
d->mXml.save(ts, 2);
|
||||||
|
|
||||||
|
file.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
For debug only
|
||||||
|
************************************************/
|
||||||
|
void XdgMenuPrivate::load(const QString& fileName)
|
||||||
|
{
|
||||||
|
QFile file(fileName);
|
||||||
|
if (!file.open(QFile::ReadOnly | QFile::Text))
|
||||||
|
{
|
||||||
|
qWarning() << QString("%1 not loading: %2").arg(fileName).arg(file.errorString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mXml.setContent(&file, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
void XdgMenuPrivate::saveLog(const QString& logFileName)
|
||||||
|
{
|
||||||
|
Q_Q(XdgMenu);
|
||||||
|
if (!mLogDir.isEmpty())
|
||||||
|
q->save(mLogDir + "/" + logFileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
void XdgMenuPrivate::mergeMenus(QDomElement& element)
|
||||||
|
{
|
||||||
|
QHash<QString, QDomElement> menus;
|
||||||
|
|
||||||
|
MutableDomElementIterator it(element, "Menu");
|
||||||
|
|
||||||
|
it.toFront();
|
||||||
|
while(it.hasNext())
|
||||||
|
{
|
||||||
|
it.next();
|
||||||
|
menus[it.current().attribute("name")] = it.current();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
it.toBack();
|
||||||
|
while (it.hasPrevious())
|
||||||
|
{
|
||||||
|
QDomElement src = it.previous();
|
||||||
|
QDomElement dest = menus[src.attribute("name")];
|
||||||
|
if (dest != src)
|
||||||
|
{
|
||||||
|
prependChilds(src, dest);
|
||||||
|
element.removeChild(src);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QDomElement n = element.firstChildElement("Menu");
|
||||||
|
while (!n.isNull())
|
||||||
|
{
|
||||||
|
mergeMenus(n);
|
||||||
|
n = n.nextSiblingElement("Menu");
|
||||||
|
}
|
||||||
|
|
||||||
|
it.toFront();
|
||||||
|
while(it.hasNext())
|
||||||
|
mergeMenus(it.next());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
void XdgMenuPrivate::simplify(QDomElement& element)
|
||||||
|
{
|
||||||
|
MutableDomElementIterator it(element);
|
||||||
|
//it.toFront();
|
||||||
|
while(it.hasNext())
|
||||||
|
{
|
||||||
|
QDomElement n = it.next();
|
||||||
|
|
||||||
|
if (n.tagName() == "Name")
|
||||||
|
{
|
||||||
|
// The <Name> field must not contain the slash character ("/");
|
||||||
|
// implementations should discard any name containing a slash.
|
||||||
|
element.setAttribute("name", n.text().remove('/'));
|
||||||
|
n.parentNode().removeChild(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ......................................
|
||||||
|
else if(n.tagName() == "Deleted")
|
||||||
|
{
|
||||||
|
element.setAttribute("deleted", true);
|
||||||
|
n.parentNode().removeChild(n);
|
||||||
|
}
|
||||||
|
else if(n.tagName() == "NotDeleted")
|
||||||
|
{
|
||||||
|
element.setAttribute("deleted", false);
|
||||||
|
n.parentNode().removeChild(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ......................................
|
||||||
|
else if(n.tagName() == "OnlyUnallocated")
|
||||||
|
{
|
||||||
|
element.setAttribute("onlyUnallocated", true);
|
||||||
|
n.parentNode().removeChild(n);
|
||||||
|
}
|
||||||
|
else if(n.tagName() == "NotOnlyUnallocated")
|
||||||
|
{
|
||||||
|
element.setAttribute("onlyUnallocated", false);
|
||||||
|
n.parentNode().removeChild(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ......................................
|
||||||
|
else if(n.tagName() == "FileInfo")
|
||||||
|
{
|
||||||
|
n.parentNode().removeChild(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ......................................
|
||||||
|
else if(n.tagName() == "Menu")
|
||||||
|
{
|
||||||
|
simplify(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
void XdgMenuPrivate::prependChilds(QDomElement& srcElement, QDomElement& destElement)
|
||||||
|
{
|
||||||
|
MutableDomElementIterator it(srcElement);
|
||||||
|
|
||||||
|
it.toBack();
|
||||||
|
while(it.hasPrevious())
|
||||||
|
{
|
||||||
|
QDomElement n = it.previous();
|
||||||
|
destElement.insertBefore(n, destElement.firstChild());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (srcElement.attributes().contains("deleted") &&
|
||||||
|
!destElement.attributes().contains("deleted")
|
||||||
|
)
|
||||||
|
destElement.setAttribute("deleted", srcElement.attribute("deleted"));
|
||||||
|
|
||||||
|
if (srcElement.attributes().contains("onlyUnallocated") &&
|
||||||
|
!destElement.attributes().contains("onlyUnallocated")
|
||||||
|
)
|
||||||
|
destElement.setAttribute("onlyUnallocated", srcElement.attribute("onlyUnallocated"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
void XdgMenuPrivate::appendChilds(QDomElement& srcElement, QDomElement& destElement)
|
||||||
|
{
|
||||||
|
MutableDomElementIterator it(srcElement);
|
||||||
|
|
||||||
|
while(it.hasNext())
|
||||||
|
destElement.appendChild(it.next());
|
||||||
|
|
||||||
|
if (srcElement.attributes().contains("deleted"))
|
||||||
|
destElement.setAttribute("deleted", srcElement.attribute("deleted"));
|
||||||
|
|
||||||
|
if (srcElement.attributes().contains("onlyUnallocated"))
|
||||||
|
destElement.setAttribute("onlyUnallocated", srcElement.attribute("onlyUnallocated"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
Search item by path. The path can be absolute or relative. If the element not
|
||||||
|
found, the behavior depends on a parameter "createNonExisting." If it's true, then
|
||||||
|
the missing items will be created, otherwise the function returns 0.
|
||||||
|
************************************************/
|
||||||
|
QDomElement XdgMenu::findMenu(QDomElement& baseElement, const QString& path, bool createNonExisting)
|
||||||
|
{
|
||||||
|
Q_D(XdgMenu);
|
||||||
|
// Absolute path ..................
|
||||||
|
if (path.startsWith('/'))
|
||||||
|
{
|
||||||
|
QDomElement root = d->mXml.documentElement();
|
||||||
|
return findMenu(root, path.section('/', 2), createNonExisting);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Relative path ..................
|
||||||
|
if (path.isEmpty())
|
||||||
|
return baseElement;
|
||||||
|
|
||||||
|
|
||||||
|
QString name = path.section('/', 0, 0);
|
||||||
|
MutableDomElementIterator it(baseElement);
|
||||||
|
while(it.hasNext())
|
||||||
|
{
|
||||||
|
QDomElement n = it.next();
|
||||||
|
if (n.attribute("name") == name)
|
||||||
|
return findMenu(n, path.section('/', 1), createNonExisting);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Not found ......................
|
||||||
|
if (!createNonExisting)
|
||||||
|
return QDomElement();
|
||||||
|
|
||||||
|
|
||||||
|
QStringList names = path.split('/', QString::SkipEmptyParts);
|
||||||
|
QDomElement el = baseElement;
|
||||||
|
foreach (QString name, names)
|
||||||
|
{
|
||||||
|
QDomElement p = el;
|
||||||
|
el = d->mXml.createElement("Menu");
|
||||||
|
p.appendChild(el);
|
||||||
|
el.setAttribute("name", name);
|
||||||
|
}
|
||||||
|
return el;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
bool isParent(const QDomElement& parent, const QDomElement& child)
|
||||||
|
{
|
||||||
|
QDomNode n = child;
|
||||||
|
while (!n.isNull())
|
||||||
|
{
|
||||||
|
if (n == parent)
|
||||||
|
return true;
|
||||||
|
n = n.parentNode();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
Recurse to resolve <Move> elements for each menu starting with any child menu before
|
||||||
|
handling the more top level menus. So the deepest menus have their <Move> operations
|
||||||
|
performed first. Within each <Menu>, execute <Move> operations in the order that
|
||||||
|
they appear.
|
||||||
|
If the destination path does not exist, simply relocate the origin <Menu> element,
|
||||||
|
and change its <Name> field to match the destination path.
|
||||||
|
If the origin path does not exist, do nothing.
|
||||||
|
If both paths exist, take the origin <Menu> element, delete its <Name> element, and
|
||||||
|
prepend its remaining child elements to the destination <Menu> element.
|
||||||
|
************************************************/
|
||||||
|
void XdgMenuPrivate::moveMenus(QDomElement& element)
|
||||||
|
{
|
||||||
|
Q_Q(XdgMenu);
|
||||||
|
|
||||||
|
{
|
||||||
|
MutableDomElementIterator i(element, "Menu");
|
||||||
|
while(i.hasNext())
|
||||||
|
moveMenus(i.next());
|
||||||
|
}
|
||||||
|
|
||||||
|
MutableDomElementIterator i(element, "Move");
|
||||||
|
while(i.hasNext())
|
||||||
|
{
|
||||||
|
i.next();
|
||||||
|
QString oldPath = i.current().lastChildElement("Old").text();
|
||||||
|
QString newPath = i.current().lastChildElement("New").text();
|
||||||
|
|
||||||
|
element.removeChild(i.current());
|
||||||
|
|
||||||
|
if (oldPath.isEmpty() || newPath.isEmpty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
QDomElement oldMenu = q->findMenu(element, oldPath, false);
|
||||||
|
if (oldMenu.isNull())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
QDomElement newMenu = q->findMenu(element, newPath, true);
|
||||||
|
|
||||||
|
if (isParent(oldMenu, newMenu))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
appendChilds(oldMenu, newMenu);
|
||||||
|
oldMenu.parentNode().removeChild(oldMenu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
For each <Menu> containing a <Deleted> element which is not followed by a
|
||||||
|
<NotDeleted> element, remove that menu and all its child menus.
|
||||||
|
|
||||||
|
Kmenuedit create .hidden menu entry, delete it too.
|
||||||
|
************************************************/
|
||||||
|
void XdgMenuPrivate::deleteDeletedMenus(QDomElement& element)
|
||||||
|
{
|
||||||
|
MutableDomElementIterator i(element, "Menu");
|
||||||
|
while(i.hasNext())
|
||||||
|
{
|
||||||
|
QDomElement e = i.next();
|
||||||
|
if (e.attribute("deleted") == "1" ||
|
||||||
|
e.attribute("name") == ".hidden"
|
||||||
|
)
|
||||||
|
element.removeChild(e);
|
||||||
|
else
|
||||||
|
deleteDeletedMenus(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
void XdgMenuPrivate::processDirectoryEntries(QDomElement& element, const QStringList& parentDirs)
|
||||||
|
{
|
||||||
|
QStringList dirs;
|
||||||
|
QStringList files;
|
||||||
|
|
||||||
|
element.setAttribute("title", element.attribute("name"));
|
||||||
|
|
||||||
|
MutableDomElementIterator i(element, QString());
|
||||||
|
i.toBack();
|
||||||
|
while(i.hasPrevious())
|
||||||
|
{
|
||||||
|
QDomElement e = i.previous();
|
||||||
|
|
||||||
|
if (e.tagName() == "Directory")
|
||||||
|
{
|
||||||
|
files << e.text();
|
||||||
|
element.removeChild(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (e.tagName() == "DirectoryDir")
|
||||||
|
{
|
||||||
|
dirs << e.text();
|
||||||
|
element.removeChild(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dirs << parentDirs;
|
||||||
|
|
||||||
|
bool found = false;
|
||||||
|
foreach(QString file, files){
|
||||||
|
if (file.startsWith('/'))
|
||||||
|
found = loadDirectoryFile(file, element);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach (QString dir, dirs)
|
||||||
|
{
|
||||||
|
found = loadDirectoryFile(dir + "/" + file, element);
|
||||||
|
if (found) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (found) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
MutableDomElementIterator it(element, "Menu");
|
||||||
|
while(it.hasNext())
|
||||||
|
{
|
||||||
|
QDomElement e = it.next();
|
||||||
|
processDirectoryEntries(e, dirs);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
bool XdgMenuPrivate::loadDirectoryFile(const QString& fileName, QDomElement& element)
|
||||||
|
{
|
||||||
|
XdgDesktopFile file;
|
||||||
|
file.load(fileName);
|
||||||
|
|
||||||
|
if (!file.isValid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
|
||||||
|
element.setAttribute("title", file.localizedValue("Name").toString());
|
||||||
|
element.setAttribute("comment", file.localizedValue("Comment").toString());
|
||||||
|
element.setAttribute("icon", file.value("Icon").toString());
|
||||||
|
|
||||||
|
Q_Q(XdgMenu);
|
||||||
|
q->addWatchPath(QFileInfo(file.fileName()).absolutePath());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
void XdgMenuPrivate::processApps(QDomElement& element)
|
||||||
|
{
|
||||||
|
Q_Q(XdgMenu);
|
||||||
|
XdgMenuApplinkProcessor processor(element, q);
|
||||||
|
processor.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
void XdgMenuPrivate::deleteEmpty(QDomElement& element)
|
||||||
|
{
|
||||||
|
MutableDomElementIterator it(element, "Menu");
|
||||||
|
while(it.hasNext())
|
||||||
|
deleteEmpty(it.next());
|
||||||
|
|
||||||
|
if (element.attribute("keep") == "true")
|
||||||
|
return;
|
||||||
|
|
||||||
|
QDomElement childMenu = element.firstChildElement("Menu");
|
||||||
|
QDomElement childApps = element.firstChildElement("AppLink");
|
||||||
|
|
||||||
|
if (childMenu.isNull() && childApps.isNull())
|
||||||
|
{
|
||||||
|
element.parentNode().removeChild(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
void XdgMenuPrivate::processLayouts(QDomElement& element)
|
||||||
|
{
|
||||||
|
XdgMenuLayoutProcessor proc(element);
|
||||||
|
proc.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
void XdgMenuPrivate::fixSeparators(QDomElement& element)
|
||||||
|
{
|
||||||
|
|
||||||
|
MutableDomElementIterator it(element, "Separator");
|
||||||
|
while(it.hasNext())
|
||||||
|
{
|
||||||
|
QDomElement s = it.next();
|
||||||
|
if (s.previousSiblingElement().tagName() == "Separator")
|
||||||
|
element.removeChild(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QDomElement first = element.firstChild().toElement();
|
||||||
|
if (first.tagName() == "Separator")
|
||||||
|
element.removeChild(first);
|
||||||
|
|
||||||
|
QDomElement last = element.lastChild().toElement();
|
||||||
|
if (last.tagName() == "Separator")
|
||||||
|
element.removeChild(last);
|
||||||
|
|
||||||
|
|
||||||
|
MutableDomElementIterator mi(element, "Menu");
|
||||||
|
while(mi.hasNext())
|
||||||
|
fixSeparators(mi.next());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
$XDG_CONFIG_DIRS/menus/${XDG_MENU_PREFIX}applications.menu
|
||||||
|
The first file found in the search path should be used; other files are ignored.
|
||||||
|
************************************************/
|
||||||
|
QString XdgMenu::getMenuFileName(const QString& baseName)
|
||||||
|
{
|
||||||
|
QStringList configDirs = XdgDirs::configDirs();
|
||||||
|
QString menuPrefix = getenv("XDG_MENU_PREFIX");
|
||||||
|
|
||||||
|
foreach(QString configDir, configDirs)
|
||||||
|
{
|
||||||
|
QFileInfo file(QString("%1/menus/%2%3").arg(configDir, menuPrefix, baseName));
|
||||||
|
if (file.exists())
|
||||||
|
return file.filePath();
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList wellKnownFiles;
|
||||||
|
// razor- is a priority for us
|
||||||
|
wellKnownFiles << "razor-applications.menu";
|
||||||
|
// the "global" menu file name on suse and fedora
|
||||||
|
wellKnownFiles << "applications.menu";
|
||||||
|
// rest files ordered by priority (descending)
|
||||||
|
wellKnownFiles << "kde4-applications.menu";
|
||||||
|
wellKnownFiles << "kde-applications.menu";
|
||||||
|
wellKnownFiles << "gnome-applications.menu";
|
||||||
|
wellKnownFiles << "lxde-applications.menu";
|
||||||
|
|
||||||
|
foreach(QString configDir, configDirs)
|
||||||
|
{
|
||||||
|
foreach (QString f, wellKnownFiles)
|
||||||
|
{
|
||||||
|
QFileInfo file(QString("%1/menus/%2").arg(configDir, f));
|
||||||
|
if (file.exists())
|
||||||
|
return file.filePath();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
void XdgMenu::addWatchPath(const QString &path)
|
||||||
|
{
|
||||||
|
Q_D(XdgMenu);
|
||||||
|
|
||||||
|
if (d->mWatcher.files().contains(path))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (d->mWatcher.directories().contains(path))
|
||||||
|
return;
|
||||||
|
|
||||||
|
d->mWatcher.addPath(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
bool XdgMenu::isOutDated() const
|
||||||
|
{
|
||||||
|
Q_D(const XdgMenu);
|
||||||
|
return d->mOutDated;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
void XdgMenuPrivate::rebuild()
|
||||||
|
{
|
||||||
|
Q_Q(XdgMenu);
|
||||||
|
QByteArray prevHash = mHash;
|
||||||
|
q->read(mMenuFileName);
|
||||||
|
|
||||||
|
if (prevHash != mHash)
|
||||||
|
{
|
||||||
|
mOutDated = true;
|
||||||
|
emit changed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
void XdgMenuPrivate::clearWatcher()
|
||||||
|
{
|
||||||
|
QStringList sl;
|
||||||
|
sl << mWatcher.files();
|
||||||
|
sl << mWatcher.directories();
|
||||||
|
if (sl.length())
|
||||||
|
mWatcher.removePaths(sl);
|
||||||
|
}
|
@ -0,0 +1,130 @@
|
|||||||
|
/* BEGIN_COMMON_COPYRIGHT_HEADER
|
||||||
|
* (c)LGPL2+
|
||||||
|
*
|
||||||
|
* Razor - a lightweight, Qt based, desktop toolset
|
||||||
|
* http://razor-qt.org
|
||||||
|
*
|
||||||
|
* Copyright: 2010-2011 Razor team
|
||||||
|
* Authors:
|
||||||
|
* Alexander Sokoloff <sokoloff.a@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 */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef QTXDG_XDGMENU_H
|
||||||
|
#define QTXDG_XDGMENU_H
|
||||||
|
|
||||||
|
#include "xdgmacros.h"
|
||||||
|
#include <QObject>
|
||||||
|
#include <QString>
|
||||||
|
#include <QStringList>
|
||||||
|
#include <QtXml/QDomDocument>
|
||||||
|
|
||||||
|
|
||||||
|
class QDomDocument;
|
||||||
|
class QDomElement;
|
||||||
|
class XdgMenuPrivate;
|
||||||
|
|
||||||
|
|
||||||
|
/*! @brief The XdgMenu class implements the "Desktop Menu Specification" from freedesktop.org.
|
||||||
|
|
||||||
|
Freedesktop menu is a user-visible hierarchy of applications, typically displayed as a menu.
|
||||||
|
|
||||||
|
Example usage:
|
||||||
|
@code
|
||||||
|
QString menuFile = XdgMenu::getMenuFileName();
|
||||||
|
XdgMenu xdgMenu();
|
||||||
|
|
||||||
|
bool res = xdgMenu.read(menuFile);
|
||||||
|
if (!res)
|
||||||
|
{
|
||||||
|
QMessageBox::warning(this, "Parse error", xdgMenu.errorString());
|
||||||
|
}
|
||||||
|
|
||||||
|
QDomElement rootElement = xdgMenu.xml().documentElement()
|
||||||
|
@endcode
|
||||||
|
|
||||||
|
@sa http://specifications.freedesktop.org/menu-spec/menu-spec-latest.html
|
||||||
|
*/
|
||||||
|
|
||||||
|
class QTXDG_API XdgMenu : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
friend class XdgMenuReader;
|
||||||
|
friend class XdgMenuApplinkProcessor;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit XdgMenu(QObject *parent = 0);
|
||||||
|
virtual ~XdgMenu();
|
||||||
|
|
||||||
|
bool read(const QString& menuFileName);
|
||||||
|
void save(const QString& fileName);
|
||||||
|
|
||||||
|
const QDomDocument xml() const;
|
||||||
|
QString menuFileName() const;
|
||||||
|
|
||||||
|
QDomElement findMenu(QDomElement& baseElement, const QString& path, bool createNonExisting);
|
||||||
|
|
||||||
|
/*! Returns a list of strings identifying the environments that should
|
||||||
|
* display a desktop entry. Internally all comparisions involving the
|
||||||
|
* desktop enviroment names are made case insensitive.
|
||||||
|
*/
|
||||||
|
QStringList environments();
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Set currently running environments. Example: RAZOR, KDE, or GNOME...
|
||||||
|
* Internally all comparisions involving the desktop enviroment names
|
||||||
|
* are made case insensitive.
|
||||||
|
*/
|
||||||
|
void setEnvironments(const QStringList &envs);
|
||||||
|
void setEnvironments(const QString &env);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Returns a string description of the last error that occurred if read() returns false.
|
||||||
|
*/
|
||||||
|
const QString errorString() const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief The name of the directory for the debug XML-files.
|
||||||
|
*/
|
||||||
|
const QString logDir() const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief The name of the directory for the debug XML-files. If a directory is specified,
|
||||||
|
* then after you run the XdgMenu::read, you can see and check the results of the each step.
|
||||||
|
*/
|
||||||
|
void setLogDir(const QString& directory);
|
||||||
|
|
||||||
|
static QString getMenuFileName(const QString& baseName = "applications.menu");
|
||||||
|
|
||||||
|
bool isOutDated() const;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void changed();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void addWatchPath(const QString& path);
|
||||||
|
|
||||||
|
private:
|
||||||
|
XdgMenuPrivate* const d_ptr;
|
||||||
|
Q_DECLARE_PRIVATE(XdgMenu)
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // QTXDG_XDGMENU_H
|
@ -0,0 +1,88 @@
|
|||||||
|
/* BEGIN_COMMON_COPYRIGHT_HEADER
|
||||||
|
* (c)LGPL2+
|
||||||
|
*
|
||||||
|
* Razor - a lightweight, Qt based, desktop toolset
|
||||||
|
* http://razor-qt.org
|
||||||
|
*
|
||||||
|
* Copyright: 2010-2011 Razor team
|
||||||
|
* Authors:
|
||||||
|
* Alexander Sokoloff <sokoloff.a@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 "xdgmenu.h"
|
||||||
|
#include <QObject>
|
||||||
|
#include <QFileSystemWatcher>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
#define REBUILD_DELAY 3000
|
||||||
|
|
||||||
|
class QDomElement;
|
||||||
|
class QStringList;
|
||||||
|
class QString;
|
||||||
|
class QDomDocument;
|
||||||
|
|
||||||
|
class XdgMenuPrivate: QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
XdgMenuPrivate(XdgMenu* parent);
|
||||||
|
|
||||||
|
void simplify(QDomElement& element);
|
||||||
|
void mergeMenus(QDomElement& element);
|
||||||
|
void moveMenus(QDomElement& element);
|
||||||
|
void deleteDeletedMenus(QDomElement& element);
|
||||||
|
void processDirectoryEntries(QDomElement& element, const QStringList& parentDirs);
|
||||||
|
void processApps(QDomElement& element);
|
||||||
|
void deleteEmpty(QDomElement& element);
|
||||||
|
void processLayouts(QDomElement& element);
|
||||||
|
void fixSeparators(QDomElement& element);
|
||||||
|
|
||||||
|
bool loadDirectoryFile(const QString& fileName, QDomElement& element);
|
||||||
|
void prependChilds(QDomElement& srcElement, QDomElement& destElement);
|
||||||
|
void appendChilds(QDomElement& srcElement, QDomElement& destElement);
|
||||||
|
|
||||||
|
void saveLog(const QString& logFileName);
|
||||||
|
void load(const QString& fileName);
|
||||||
|
|
||||||
|
void clearWatcher();
|
||||||
|
|
||||||
|
QString mErrorString;
|
||||||
|
QStringList mEnvironments;
|
||||||
|
QString mMenuFileName;
|
||||||
|
QString mLogDir;
|
||||||
|
QDomDocument mXml;
|
||||||
|
QByteArray mHash;
|
||||||
|
QTimer mRebuildDelayTimer;
|
||||||
|
|
||||||
|
QFileSystemWatcher mWatcher;
|
||||||
|
bool mOutDated;
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void rebuild();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void changed();
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
XdgMenu* const q_ptr;
|
||||||
|
Q_DECLARE_PUBLIC(XdgMenu)
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,275 @@
|
|||||||
|
/* BEGIN_COMMON_COPYRIGHT_HEADER
|
||||||
|
* (c)LGPL2+
|
||||||
|
*
|
||||||
|
* Razor - a lightweight, Qt based, desktop toolset
|
||||||
|
* http://razor-qt.org
|
||||||
|
*
|
||||||
|
* Copyright: 2010-2011 Razor team
|
||||||
|
* Authors:
|
||||||
|
* Alexander Sokoloff <sokoloff.a@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 "xdgmenu.h"
|
||||||
|
#include "xdgmenuapplinkprocessor.h"
|
||||||
|
#include "xmlhelper.h"
|
||||||
|
#include "xdgdesktopfile.h"
|
||||||
|
|
||||||
|
#include <QDir>
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
XdgMenuApplinkProcessor::XdgMenuApplinkProcessor(QDomElement& element, XdgMenu* menu, XdgMenuApplinkProcessor *parent) :
|
||||||
|
QObject(parent)
|
||||||
|
{
|
||||||
|
mElement = element;
|
||||||
|
mParent = parent;
|
||||||
|
mMenu = menu;
|
||||||
|
|
||||||
|
mOnlyUnallocated = element.attribute("onlyUnallocated") == "1";
|
||||||
|
|
||||||
|
MutableDomElementIterator i(element, "Menu");
|
||||||
|
while(i.hasNext())
|
||||||
|
{
|
||||||
|
QDomElement e = i.next();
|
||||||
|
mChilds.append(new XdgMenuApplinkProcessor(e, mMenu, this));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
XdgMenuApplinkProcessor::~XdgMenuApplinkProcessor()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
void XdgMenuApplinkProcessor::run()
|
||||||
|
{
|
||||||
|
step1();
|
||||||
|
step2();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
void XdgMenuApplinkProcessor::step1()
|
||||||
|
{
|
||||||
|
fillAppFileInfoList();
|
||||||
|
createRules();
|
||||||
|
|
||||||
|
// Check Include rules & mark as allocated ............
|
||||||
|
XdgMenuAppFileInfoHashIterator i(mAppFileInfoHash);
|
||||||
|
while(i.hasNext())
|
||||||
|
{
|
||||||
|
i.next();
|
||||||
|
XdgDesktopFile* file = i.value()->desktopFile();
|
||||||
|
|
||||||
|
if (mRules.checkInclude(i.key(), *file))
|
||||||
|
{
|
||||||
|
if (!mOnlyUnallocated)
|
||||||
|
i.value()->setAllocated(true);
|
||||||
|
|
||||||
|
if (!mRules.checkExclude(i.key(), *file))
|
||||||
|
{
|
||||||
|
mSelected.append(i.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process childs menus ...............................
|
||||||
|
foreach (XdgMenuApplinkProcessor* child, mChilds)
|
||||||
|
child->step1();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
void XdgMenuApplinkProcessor::step2()
|
||||||
|
{
|
||||||
|
// Create AppLinks elements ...........................
|
||||||
|
QDomDocument doc = mElement.ownerDocument();
|
||||||
|
|
||||||
|
foreach (XdgMenuAppFileInfo* fileInfo, mSelected)
|
||||||
|
{
|
||||||
|
if (mOnlyUnallocated && fileInfo->allocated())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
XdgDesktopFile* file = fileInfo->desktopFile();
|
||||||
|
|
||||||
|
bool show = false;
|
||||||
|
QStringList envs = mMenu->environments();
|
||||||
|
const int N = envs.size();
|
||||||
|
for (int i = 0; i < N; ++i)
|
||||||
|
{
|
||||||
|
if (file->isShown(envs.at(i)))
|
||||||
|
{
|
||||||
|
show = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!show)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
QDomElement appLink = doc.createElement("AppLink");
|
||||||
|
|
||||||
|
appLink.setAttribute("id", fileInfo->id());
|
||||||
|
appLink.setAttribute("title", file->localizedValue("Name").toString());
|
||||||
|
appLink.setAttribute("comment", file->localizedValue("Comment").toString());
|
||||||
|
appLink.setAttribute("genericName", file->localizedValue("GenericName").toString());
|
||||||
|
appLink.setAttribute("exec", file->value("Exec").toString());
|
||||||
|
appLink.setAttribute("terminal", file->value("Terminal").toBool());
|
||||||
|
appLink.setAttribute("startupNotify", file->value("StartupNotify").toBool());
|
||||||
|
appLink.setAttribute("path", file->value("Path").toString());
|
||||||
|
appLink.setAttribute("icon", file->value("Icon").toString());
|
||||||
|
appLink.setAttribute("desktopFile", file->fileName());
|
||||||
|
|
||||||
|
mElement.appendChild(appLink);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Process childs menus ...............................
|
||||||
|
foreach (XdgMenuApplinkProcessor* child, mChilds)
|
||||||
|
child->step2();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
For each <Menu> element, build a pool of desktop entries by collecting entries found
|
||||||
|
in each <AppDir> for the menu element. If two entries have the same desktop-file id,
|
||||||
|
the entry for the earlier (closer to the top of the file) <AppDir> must be discarded.
|
||||||
|
|
||||||
|
Next, add to the pool the entries for any <AppDir>s specified by ancestor <Menu>
|
||||||
|
elements. If a parent menu has a duplicate entry (same desktop-file id), the entry
|
||||||
|
for the child menu has priority.
|
||||||
|
************************************************/
|
||||||
|
void XdgMenuApplinkProcessor::fillAppFileInfoList()
|
||||||
|
{
|
||||||
|
// Build a pool by collecting entries found in <AppDir>
|
||||||
|
{
|
||||||
|
MutableDomElementIterator i(mElement, "AppDir");
|
||||||
|
i.toBack();
|
||||||
|
while(i.hasPrevious())
|
||||||
|
{
|
||||||
|
QDomElement e = i.previous();
|
||||||
|
findDesktopFiles(e.text(), QString());
|
||||||
|
mElement.removeChild(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the entries for ancestor <Menu> ................
|
||||||
|
if (mParent)
|
||||||
|
{
|
||||||
|
XdgMenuAppFileInfoHashIterator i(mParent->mAppFileInfoHash);
|
||||||
|
while(i.hasNext())
|
||||||
|
{
|
||||||
|
i.next();
|
||||||
|
//if (!mAppFileInfoHash.contains(i.key()))
|
||||||
|
mAppFileInfoHash.insert(i.key(), i.value());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
void XdgMenuApplinkProcessor::findDesktopFiles(const QString& dirName, const QString& prefix)
|
||||||
|
{
|
||||||
|
QDir dir(dirName);
|
||||||
|
mMenu->addWatchPath(dir.absolutePath());
|
||||||
|
QFileInfoList files = dir.entryInfoList(QStringList("*.desktop"), QDir::Files);
|
||||||
|
|
||||||
|
foreach (QFileInfo file, files)
|
||||||
|
{
|
||||||
|
XdgDesktopFile* f = XdgDesktopFileCache::getFile(file.canonicalFilePath());
|
||||||
|
if (f)
|
||||||
|
mAppFileInfoHash.insert(prefix + file.fileName(), new XdgMenuAppFileInfo(f, prefix + file.fileName(), this));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Working recursively ............
|
||||||
|
QFileInfoList dirs = dir.entryInfoList(QStringList(), QDir::Dirs | QDir::NoDotAndDotDot);
|
||||||
|
foreach (QFileInfo d, dirs)
|
||||||
|
{
|
||||||
|
QString dn = d.canonicalFilePath();
|
||||||
|
if (dn != dirName)
|
||||||
|
{
|
||||||
|
findDesktopFiles(dn, QString("%1%2-").arg(prefix, d.fileName()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
Create rules
|
||||||
|
************************************************/
|
||||||
|
void XdgMenuApplinkProcessor::createRules()
|
||||||
|
{
|
||||||
|
MutableDomElementIterator i(mElement, QString());
|
||||||
|
while(i.hasNext())
|
||||||
|
{
|
||||||
|
QDomElement e = i.next();
|
||||||
|
if (e.tagName()=="Include")
|
||||||
|
{
|
||||||
|
mRules.addInclude(e);
|
||||||
|
mElement.removeChild(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (e.tagName()=="Exclude")
|
||||||
|
{
|
||||||
|
mRules.addExclude(e);
|
||||||
|
mElement.removeChild(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
Check if the program is actually installed.
|
||||||
|
************************************************/
|
||||||
|
bool XdgMenuApplinkProcessor::checkTryExec(const QString& progName)
|
||||||
|
{
|
||||||
|
if (progName.startsWith(QDir::separator()))
|
||||||
|
return QFileInfo(progName).isExecutable();
|
||||||
|
|
||||||
|
QStringList dirs = QString(getenv("PATH")).split(":");
|
||||||
|
|
||||||
|
foreach (QString dir, dirs)
|
||||||
|
{
|
||||||
|
if (QFileInfo(QDir(dir), progName).isExecutable())
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,102 @@
|
|||||||
|
/* BEGIN_COMMON_COPYRIGHT_HEADER
|
||||||
|
* (c)LGPL2+
|
||||||
|
*
|
||||||
|
* Razor - a lightweight, Qt based, desktop toolset
|
||||||
|
* http://razor-qt.org
|
||||||
|
*
|
||||||
|
* Copyright: 2010-2011 Razor team
|
||||||
|
* Authors:
|
||||||
|
* Alexander Sokoloff <sokoloff.a@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 */
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef QTXDG_XDGMENUAPPLINKPROCESSOR_H
|
||||||
|
#define QTXDG_XDGMENUAPPLINKPROCESSOR_H
|
||||||
|
|
||||||
|
#include "xdgmenurules.h"
|
||||||
|
#include <QObject>
|
||||||
|
#include <QtXml/QDomElement>
|
||||||
|
#include <QLinkedList>
|
||||||
|
#include <QString>
|
||||||
|
#include <QHash>
|
||||||
|
|
||||||
|
class XdgMenu;
|
||||||
|
class XdgMenuAppFileInfo;
|
||||||
|
class XdgDesktopFile;
|
||||||
|
|
||||||
|
typedef QLinkedList<XdgMenuAppFileInfo*> XdgMenuAppFileInfoList;
|
||||||
|
|
||||||
|
typedef QHash<QString, XdgMenuAppFileInfo*> XdgMenuAppFileInfoHash;
|
||||||
|
typedef QHashIterator<QString, XdgMenuAppFileInfo*> XdgMenuAppFileInfoHashIterator;
|
||||||
|
|
||||||
|
class XdgMenuApplinkProcessor : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit XdgMenuApplinkProcessor(QDomElement& element, XdgMenu* menu, XdgMenuApplinkProcessor *parent = 0);
|
||||||
|
virtual ~XdgMenuApplinkProcessor();
|
||||||
|
void run();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void step1();
|
||||||
|
void step2();
|
||||||
|
void fillAppFileInfoList();
|
||||||
|
void findDesktopFiles(const QString& dirName, const QString& prefix);
|
||||||
|
|
||||||
|
//bool loadDirectoryFile(const QString& fileName, QDomElement& element);
|
||||||
|
void createRules();
|
||||||
|
bool checkTryExec(const QString& progName);
|
||||||
|
|
||||||
|
private:
|
||||||
|
XdgMenuApplinkProcessor* mParent;
|
||||||
|
QLinkedList<XdgMenuApplinkProcessor*> mChilds;
|
||||||
|
XdgMenuAppFileInfoHash mAppFileInfoHash;
|
||||||
|
XdgMenuAppFileInfoList mSelected;
|
||||||
|
QDomElement mElement;
|
||||||
|
bool mOnlyUnallocated;
|
||||||
|
|
||||||
|
XdgMenu* mMenu;
|
||||||
|
XdgMenuRules mRules;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class XdgMenuAppFileInfo: public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit XdgMenuAppFileInfo(XdgDesktopFile* desktopFile, const QString& id, QObject *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
{
|
||||||
|
mDesktopFile = desktopFile;
|
||||||
|
mAllocated = false;
|
||||||
|
mId = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
XdgDesktopFile* desktopFile() const { return mDesktopFile; }
|
||||||
|
bool allocated() const { return mAllocated; }
|
||||||
|
void setAllocated(bool value) { mAllocated = value; }
|
||||||
|
QString id() const { return mId; }
|
||||||
|
private:
|
||||||
|
XdgDesktopFile* mDesktopFile;
|
||||||
|
bool mAllocated;
|
||||||
|
QString mId;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // QTXDG_XDGMENUAPPLINKPROCESSOR_H
|
@ -0,0 +1,430 @@
|
|||||||
|
/* BEGIN_COMMON_COPYRIGHT_HEADER
|
||||||
|
* (c)LGPL2+
|
||||||
|
*
|
||||||
|
* Razor - a lightweight, Qt based, desktop toolset
|
||||||
|
* http://razor-qt.org
|
||||||
|
*
|
||||||
|
* Copyright: 2010-2011 Razor team
|
||||||
|
* Authors:
|
||||||
|
* Alexander Sokoloff <sokoloff.a@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 "xdgmenulayoutprocessor.h"
|
||||||
|
#include "xmlhelper.h"
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QMap>
|
||||||
|
|
||||||
|
|
||||||
|
// Helper functions prototypes
|
||||||
|
QDomElement findLastElementByTag(const QDomElement element, const QString tagName);
|
||||||
|
int childsCount(const QDomElement& element);
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
QDomElement findLastElementByTag(const QDomElement element, const QString tagName)
|
||||||
|
{
|
||||||
|
QDomNodeList l = element.elementsByTagName(tagName);
|
||||||
|
if (l.isEmpty())
|
||||||
|
return QDomElement();
|
||||||
|
|
||||||
|
return l.at(l.length()-1).toElement();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
If no default-layout has been specified then the layout as specified by
|
||||||
|
the following elements should be assumed:
|
||||||
|
<DefaultLayout
|
||||||
|
show_empty="false"
|
||||||
|
inline="false"
|
||||||
|
inline_limit="4"
|
||||||
|
inline_header="true"
|
||||||
|
inline_alias="false">
|
||||||
|
<Merge type="menus"/>
|
||||||
|
<Merge type="files"/>
|
||||||
|
</DefaultLayout>
|
||||||
|
************************************************/
|
||||||
|
XdgMenuLayoutProcessor::XdgMenuLayoutProcessor(QDomElement& element):
|
||||||
|
mElement(element)
|
||||||
|
{
|
||||||
|
mDefaultParams.mShowEmpty = false;
|
||||||
|
mDefaultParams.mInline = false;
|
||||||
|
mDefaultParams.mInlineLimit = 4;
|
||||||
|
mDefaultParams.mInlineHeader = true;
|
||||||
|
mDefaultParams.mInlineAlias = false;
|
||||||
|
|
||||||
|
mDefaultLayout = findLastElementByTag(element, "DefaultLayout");
|
||||||
|
|
||||||
|
if (mDefaultLayout.isNull())
|
||||||
|
{
|
||||||
|
// Create DefaultLayout node
|
||||||
|
QDomDocument doc = element.ownerDocument();
|
||||||
|
mDefaultLayout = doc.createElement("DefaultLayout");
|
||||||
|
|
||||||
|
QDomElement menus = doc.createElement("Merge");
|
||||||
|
menus.setAttribute("type", "menus");
|
||||||
|
mDefaultLayout.appendChild(menus);
|
||||||
|
|
||||||
|
QDomElement files = doc.createElement("Merge");
|
||||||
|
files.setAttribute("type", "files");
|
||||||
|
mDefaultLayout.appendChild(files);
|
||||||
|
|
||||||
|
mElement.appendChild(mDefaultLayout);
|
||||||
|
}
|
||||||
|
|
||||||
|
setParams(mDefaultLayout, &mDefaultParams);
|
||||||
|
|
||||||
|
// If a menu does not contain a <Layout> element or if it contains an empty <Layout> element
|
||||||
|
// then the default layout should be used.
|
||||||
|
mLayout = findLastElementByTag(element, "Layout");
|
||||||
|
if (mLayout.isNull() || !mLayout.hasChildNodes())
|
||||||
|
mLayout = mDefaultLayout;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
XdgMenuLayoutProcessor::XdgMenuLayoutProcessor(QDomElement& element, XdgMenuLayoutProcessor *parent):
|
||||||
|
mElement(element)
|
||||||
|
{
|
||||||
|
mDefaultParams = parent->mDefaultParams;
|
||||||
|
|
||||||
|
// DefaultLayout ............................
|
||||||
|
QDomElement defaultLayout = findLastElementByTag(element, "DefaultLayout");
|
||||||
|
|
||||||
|
if (defaultLayout.isNull())
|
||||||
|
mDefaultLayout = parent->mDefaultLayout;
|
||||||
|
else
|
||||||
|
mDefaultLayout = defaultLayout;
|
||||||
|
|
||||||
|
setParams(mDefaultLayout, &mDefaultParams);
|
||||||
|
|
||||||
|
// If a menu does not contain a <Layout> element or if it contains an empty <Layout> element
|
||||||
|
// then the default layout should be used.
|
||||||
|
mLayout = findLastElementByTag(element, "Layout");
|
||||||
|
if (mLayout.isNull() || !mLayout.hasChildNodes())
|
||||||
|
mLayout = mDefaultLayout;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
void XdgMenuLayoutProcessor::setParams(QDomElement defaultLayout, LayoutParams *result)
|
||||||
|
{
|
||||||
|
if (defaultLayout.hasAttribute("show_empty"))
|
||||||
|
result->mShowEmpty = defaultLayout.attribute("show_empty") == "true";
|
||||||
|
|
||||||
|
if (defaultLayout.hasAttribute("inline"))
|
||||||
|
result->mInline = defaultLayout.attribute("inline") == "true";
|
||||||
|
|
||||||
|
if (defaultLayout.hasAttribute("inline_limit"))
|
||||||
|
result->mInlineLimit = defaultLayout.attribute("inline_limit").toInt();
|
||||||
|
|
||||||
|
if (defaultLayout.hasAttribute("inline_header"))
|
||||||
|
result->mInlineHeader = defaultLayout.attribute("inline_header") == "true";
|
||||||
|
|
||||||
|
if (defaultLayout.hasAttribute("inline_alias"))
|
||||||
|
result->mInlineAlias = defaultLayout.attribute("inline_alias") == "true";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
QDomElement XdgMenuLayoutProcessor::searchElement(const QString &tagName, const QString &attributeName, const QString &attributeValue) const
|
||||||
|
{
|
||||||
|
DomElementIterator it(mElement, tagName);
|
||||||
|
while (it.hasNext())
|
||||||
|
{
|
||||||
|
QDomElement e = it.next();
|
||||||
|
if (e.attribute(attributeName) == attributeValue)
|
||||||
|
{
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return QDomElement();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
int childsCount(const QDomElement& element)
|
||||||
|
{
|
||||||
|
int count = 0;
|
||||||
|
DomElementIterator it(element);
|
||||||
|
while (it.hasNext())
|
||||||
|
{
|
||||||
|
QString tag = it.next().tagName();
|
||||||
|
if (tag == "AppLink" || tag == "Menu" || tag == "Separator")
|
||||||
|
count ++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
void XdgMenuLayoutProcessor::run()
|
||||||
|
{
|
||||||
|
QDomDocument doc = mLayout.ownerDocument();
|
||||||
|
mResult = doc.createElement("Result");
|
||||||
|
mElement.appendChild(mResult);
|
||||||
|
|
||||||
|
// Process childs menus ...............................
|
||||||
|
{
|
||||||
|
DomElementIterator it(mElement, "Menu");
|
||||||
|
while (it.hasNext())
|
||||||
|
{
|
||||||
|
QDomElement e = it.next();
|
||||||
|
XdgMenuLayoutProcessor p(e, this);
|
||||||
|
p.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Step 1 ...................................
|
||||||
|
DomElementIterator it(mLayout);
|
||||||
|
it.toFront();
|
||||||
|
while (it.hasNext())
|
||||||
|
{
|
||||||
|
QDomElement e = it.next();
|
||||||
|
|
||||||
|
if (e.tagName() == "Filename")
|
||||||
|
processFilenameTag(e);
|
||||||
|
|
||||||
|
else if (e.tagName() == "Menuname")
|
||||||
|
processMenunameTag(e);
|
||||||
|
|
||||||
|
else if (e.tagName() == "Separator")
|
||||||
|
processSeparatorTag(e);
|
||||||
|
|
||||||
|
else if (e.tagName() == "Merge")
|
||||||
|
{
|
||||||
|
QDomElement merge = mResult.ownerDocument().createElement("Merge");
|
||||||
|
merge.setAttribute("type", e.attribute("type"));
|
||||||
|
mResult.appendChild(merge);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 2 ...................................
|
||||||
|
{
|
||||||
|
MutableDomElementIterator ri(mResult, "Merge");
|
||||||
|
while (ri.hasNext())
|
||||||
|
{
|
||||||
|
processMergeTag(ri.next());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move result cilds to element .............
|
||||||
|
MutableDomElementIterator ri(mResult);
|
||||||
|
while (ri.hasNext())
|
||||||
|
{
|
||||||
|
mElement.appendChild(ri.next());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Final ....................................
|
||||||
|
mElement.removeChild(mResult);
|
||||||
|
|
||||||
|
if (mLayout.parentNode() == mElement)
|
||||||
|
mElement.removeChild(mLayout);
|
||||||
|
|
||||||
|
if (mDefaultLayout.parentNode() == mElement)
|
||||||
|
mElement.removeChild(mDefaultLayout);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
The <Filename> element is the most basic matching rule.
|
||||||
|
It matches a desktop entry if the desktop entry has the given desktop-file id
|
||||||
|
************************************************/
|
||||||
|
void XdgMenuLayoutProcessor::processFilenameTag(const QDomElement &element)
|
||||||
|
{
|
||||||
|
QString id = element.text();
|
||||||
|
|
||||||
|
QDomElement appLink = searchElement("AppLink", "id", id);
|
||||||
|
if (!appLink.isNull())
|
||||||
|
mResult.appendChild(appLink);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
Its contents references an immediate sub-menu of the current menu, as such it should never contain
|
||||||
|
a slash. If no such sub-menu exists the element should be ignored.
|
||||||
|
This element may have various attributes, the default values are taken from the DefaultLayout key.
|
||||||
|
|
||||||
|
show_empty [ bool ]
|
||||||
|
defines whether a menu that contains no desktop entries and no sub-menus
|
||||||
|
should be shown at all.
|
||||||
|
|
||||||
|
inline [ bool ]
|
||||||
|
If the inline attribute is "true" the menu that is referenced may be copied into the current
|
||||||
|
menu at the current point instead of being inserted as sub-menu of the current menu.
|
||||||
|
|
||||||
|
inline_limit [ int ]
|
||||||
|
defines the maximum number of entries that can be inlined. If the sub-menu has more entries
|
||||||
|
than inline_limit, the sub-menu will not be inlined. If the inline_limit is 0 (zero) there
|
||||||
|
is no limit.
|
||||||
|
|
||||||
|
inline_header [ bool ]
|
||||||
|
defines whether an inlined menu should be preceded with a header entry listing the caption of
|
||||||
|
the sub-menu.
|
||||||
|
|
||||||
|
inline_alias [ bool ]
|
||||||
|
defines whether a single inlined entry should adopt the caption of the inlined menu. In such
|
||||||
|
case no additional header entry will be added regardless of the value of the inline_header
|
||||||
|
attribute.
|
||||||
|
|
||||||
|
Example: if a menu has a sub-menu titled "WordProcessor" with a single entry "OpenOffice 4.2", and
|
||||||
|
both inline="true" and inline_alias="true" are specified then this would result in the
|
||||||
|
"OpenOffice 4.2" entry being inlined in the current menu but the "OpenOffice 4.2" caption of the
|
||||||
|
entry would be replaced with "WordProcessor".
|
||||||
|
************************************************/
|
||||||
|
void XdgMenuLayoutProcessor::processMenunameTag(const QDomElement &element)
|
||||||
|
{
|
||||||
|
QString id = element.text();
|
||||||
|
QDomElement menu = searchElement("Menu", "name", id);
|
||||||
|
if (menu.isNull())
|
||||||
|
return;
|
||||||
|
|
||||||
|
LayoutParams params = mDefaultParams;
|
||||||
|
setParams(element, ¶ms);
|
||||||
|
|
||||||
|
int count = childsCount(menu);
|
||||||
|
|
||||||
|
if (count == 0)
|
||||||
|
{
|
||||||
|
if (params.mShowEmpty)
|
||||||
|
{
|
||||||
|
menu.setAttribute("keep", "true");
|
||||||
|
mResult.appendChild(menu);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool doInline = params.mInline &&
|
||||||
|
(!params.mInlineLimit || params.mInlineLimit > count);
|
||||||
|
|
||||||
|
bool doAlias = params.mInlineAlias &&
|
||||||
|
doInline && (count == 1);
|
||||||
|
|
||||||
|
bool doHeader = params.mInlineHeader &&
|
||||||
|
doInline && !doAlias;
|
||||||
|
|
||||||
|
|
||||||
|
if (!doInline)
|
||||||
|
{
|
||||||
|
mResult.appendChild(menu);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Header ....................................
|
||||||
|
if (doHeader)
|
||||||
|
{
|
||||||
|
QDomElement header = mLayout.ownerDocument().createElement("Header");
|
||||||
|
|
||||||
|
QDomNamedNodeMap attrs = menu.attributes();
|
||||||
|
for (int i=0; i < attrs.count(); ++i)
|
||||||
|
{
|
||||||
|
header.setAttributeNode(attrs.item(i).toAttr());
|
||||||
|
}
|
||||||
|
|
||||||
|
mResult.appendChild(header);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alias .....................................
|
||||||
|
if (doAlias)
|
||||||
|
{
|
||||||
|
menu.firstChild().toElement().setAttribute("title", menu.attribute("title"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inline ....................................
|
||||||
|
MutableDomElementIterator it(menu);
|
||||||
|
while (it.hasNext())
|
||||||
|
{
|
||||||
|
mResult.appendChild(it.next());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
It indicates a suggestion to draw a visual separator at this point in the menu.
|
||||||
|
<Separator> elements at the start of a menu, at the end of a menu or that directly
|
||||||
|
follow other <Separator> elements may be ignored.
|
||||||
|
************************************************/
|
||||||
|
void XdgMenuLayoutProcessor::processSeparatorTag(const QDomElement &element)
|
||||||
|
{
|
||||||
|
QDomElement separator = element.ownerDocument().createElement("Separator");
|
||||||
|
mResult.appendChild(separator);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
It indicates the point where desktop entries and sub-menus that are not explicitly mentioned
|
||||||
|
within the <Layout> or <DefaultLayout> element are to be inserted.
|
||||||
|
It has a type attribute that indicates which elements should be inserted:
|
||||||
|
|
||||||
|
type="menus"
|
||||||
|
means that all sub-menus that are not explicitly mentioned should be inserted in alphabetical
|
||||||
|
order of their visual caption at this point.
|
||||||
|
|
||||||
|
type="files" means that all desktop entries contained in this menu that are not explicitly
|
||||||
|
mentioned should be inserted in alphabetical order of their visual caption at this point.
|
||||||
|
|
||||||
|
type="all" means that a mix of all sub-menus and all desktop entries that are not explicitly
|
||||||
|
mentioned should be inserted in alphabetical order of their visual caption at this point.
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
void XdgMenuLayoutProcessor::processMergeTag(const QDomElement &element)
|
||||||
|
{
|
||||||
|
QString type = element.attribute("type");
|
||||||
|
QMap<QString, QDomElement> map;
|
||||||
|
MutableDomElementIterator it(mElement);
|
||||||
|
|
||||||
|
while (it.hasNext())
|
||||||
|
{
|
||||||
|
QDomElement e = it.next();
|
||||||
|
if (
|
||||||
|
((type == "menus" || type == "all") && e.tagName() == "Menu" ) ||
|
||||||
|
((type == "files" || type == "all") && e.tagName() == "AppLink")
|
||||||
|
)
|
||||||
|
map.insert(e.attribute("title"), e);
|
||||||
|
}
|
||||||
|
|
||||||
|
QMapIterator<QString, QDomElement> mi(map);
|
||||||
|
while (mi.hasNext()) {
|
||||||
|
mi.next();
|
||||||
|
mResult.insertBefore(mi.value(), element);
|
||||||
|
}
|
||||||
|
|
||||||
|
mResult.removeChild(element);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,115 @@
|
|||||||
|
/* BEGIN_COMMON_COPYRIGHT_HEADER
|
||||||
|
* (c)LGPL2+
|
||||||
|
*
|
||||||
|
* Razor - a lightweight, Qt based, desktop toolset
|
||||||
|
* http://razor-qt.org
|
||||||
|
*
|
||||||
|
* Copyright: 2010-2011 Razor team
|
||||||
|
* Authors:
|
||||||
|
* Alexander Sokoloff <sokoloff.a@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 */
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef QTXDG_XDGMENULAYOUTPROCESSOR_H
|
||||||
|
#define QTXDG_XDGMENULAYOUTPROCESSOR_H
|
||||||
|
|
||||||
|
#include <QtXml/QDomElement>
|
||||||
|
#include <QList>
|
||||||
|
|
||||||
|
struct LayoutItem
|
||||||
|
{
|
||||||
|
enum Type{
|
||||||
|
Filename,
|
||||||
|
Menuname,
|
||||||
|
Separator,
|
||||||
|
MergeMenus,
|
||||||
|
MergeFiles,
|
||||||
|
MergeAll,
|
||||||
|
};
|
||||||
|
|
||||||
|
Type type;
|
||||||
|
bool showEmpty;
|
||||||
|
bool isInline;
|
||||||
|
bool inlineLimit;
|
||||||
|
bool inlineHeader;
|
||||||
|
bool inlineAlias;
|
||||||
|
QString fileId;
|
||||||
|
};
|
||||||
|
|
||||||
|
//class Layout: public QList<LayoutItem>
|
||||||
|
//{
|
||||||
|
//public:
|
||||||
|
/* Layout() {}
|
||||||
|
|
||||||
|
|
||||||
|
bool showEmpty() { return mShowEmpty; }
|
||||||
|
void setShowEmpty(bool value) { mShowEmpty = value; }
|
||||||
|
|
||||||
|
bool isInline() { return mInline; }
|
||||||
|
void setInline(bool value) { mInline = value; }
|
||||||
|
|
||||||
|
int inlineLimit() { return mInlineLimit; }
|
||||||
|
void setInlineLimit(int value) { mInlineLimit = value; }
|
||||||
|
|
||||||
|
bool inlineHeader() {return mInlineHeader; }
|
||||||
|
void setInlineHeader(bool value) { mInlineHeader = value; }
|
||||||
|
|
||||||
|
bool inlineAlias() { return mInlineAlias; }
|
||||||
|
void setInlineAlias(bool value) { mInlineAlias = value; }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct LayoutParams
|
||||||
|
{
|
||||||
|
bool mShowEmpty;
|
||||||
|
bool mInline;
|
||||||
|
int mInlineLimit;
|
||||||
|
bool mInlineHeader;
|
||||||
|
bool mInlineAlias;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class XdgMenuLayoutProcessor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
XdgMenuLayoutProcessor(QDomElement& element);
|
||||||
|
void run();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
XdgMenuLayoutProcessor(QDomElement& element, XdgMenuLayoutProcessor *parent);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void setParams(QDomElement defaultLayout, LayoutParams *result);
|
||||||
|
QDomElement searchElement(const QString &tagName, const QString &attributeName, const QString &attributeValue) const;
|
||||||
|
void processFilenameTag(const QDomElement &element);
|
||||||
|
void processMenunameTag(const QDomElement &element);
|
||||||
|
void processSeparatorTag(const QDomElement &element);
|
||||||
|
void processMergeTag(const QDomElement &element);
|
||||||
|
|
||||||
|
LayoutParams mDefaultParams;
|
||||||
|
QDomElement& mElement;
|
||||||
|
QDomElement mDefaultLayout;
|
||||||
|
QDomElement mLayout;
|
||||||
|
QDomElement mResult;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // QTXDG_XDGMENULAYOUTPROCESSOR_H
|
@ -0,0 +1,456 @@
|
|||||||
|
/* BEGIN_COMMON_COPYRIGHT_HEADER
|
||||||
|
* (c)LGPL2+
|
||||||
|
*
|
||||||
|
* Razor - a lightweight, Qt based, desktop toolset
|
||||||
|
* http://razor-qt.org
|
||||||
|
*
|
||||||
|
* Copyright: 2010-2011 Razor team
|
||||||
|
* Authors:
|
||||||
|
* Alexander Sokoloff <sokoloff.a@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 "xdgmenureader.h"
|
||||||
|
#include "xdgmenu.h"
|
||||||
|
#include "xdgdirs.h"
|
||||||
|
#include "xmlhelper.h"
|
||||||
|
|
||||||
|
#include <QFile>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QString>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QtXml/QDomNamedNodeMap>
|
||||||
|
#include <QtXml/QDomNode>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
XdgMenuReader::XdgMenuReader(XdgMenu* menu, XdgMenuReader* parentReader, QObject *parent) :
|
||||||
|
QObject(parent),
|
||||||
|
mMenu(menu)
|
||||||
|
{
|
||||||
|
mParentReader = parentReader;
|
||||||
|
if (mParentReader)
|
||||||
|
mBranchFiles << mParentReader->mBranchFiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
XdgMenuReader::~XdgMenuReader()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
bool XdgMenuReader::load(const QString& fileName, const QString& baseDir)
|
||||||
|
{
|
||||||
|
if (fileName.isEmpty())
|
||||||
|
{
|
||||||
|
mErrorStr = QLatin1String("Menu file not defined.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QFileInfo fileInfo(QDir(baseDir), fileName);
|
||||||
|
|
||||||
|
mFileName = fileInfo.canonicalFilePath();
|
||||||
|
mDirName = fileInfo.canonicalPath();
|
||||||
|
|
||||||
|
if (mBranchFiles.contains(mFileName))
|
||||||
|
return false; // Recursive loop detected
|
||||||
|
|
||||||
|
mBranchFiles << mFileName;
|
||||||
|
|
||||||
|
QFile file(mFileName);
|
||||||
|
if (!file.open(QFile::ReadOnly | QFile::Text))
|
||||||
|
{
|
||||||
|
mErrorStr = QString("%1 not loading: %2").arg(fileName).arg(file.errorString());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
//qDebug() << "Load file:" << mFileName;
|
||||||
|
mMenu->addWatchPath(mFileName);
|
||||||
|
|
||||||
|
QString errorStr;
|
||||||
|
int errorLine;
|
||||||
|
int errorColumn;
|
||||||
|
|
||||||
|
if (!mXml.setContent(&file, true, &errorStr, &errorLine, &errorColumn))
|
||||||
|
{
|
||||||
|
mErrorStr = QString("Parse error at line %1, column %2:\n%3")
|
||||||
|
.arg(errorLine)
|
||||||
|
.arg(errorColumn)
|
||||||
|
.arg(errorStr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDomElement root = mXml.documentElement();
|
||||||
|
|
||||||
|
QDomElement debugElement = mXml.createElement("FileInfo");
|
||||||
|
debugElement.setAttribute("file", mFileName);
|
||||||
|
if (mParentReader)
|
||||||
|
debugElement.setAttribute("parent", mParentReader->fileName());
|
||||||
|
|
||||||
|
QDomNode null;
|
||||||
|
root.insertBefore(debugElement, null);
|
||||||
|
|
||||||
|
processMergeTags(root);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
Duplicate <MergeXXX> elements (that specify the same file) are handled as with
|
||||||
|
duplicate <AppDir> elements (the last duplicate is used).
|
||||||
|
************************************************/
|
||||||
|
void XdgMenuReader::processMergeTags(QDomElement& element)
|
||||||
|
{
|
||||||
|
QDomElement n = element.lastChildElement();
|
||||||
|
QStringList mergedFiles;
|
||||||
|
|
||||||
|
|
||||||
|
while (!n.isNull())
|
||||||
|
{
|
||||||
|
QDomElement next = n.previousSiblingElement();
|
||||||
|
// MergeFile ..................
|
||||||
|
if (n.tagName() == "MergeFile")
|
||||||
|
{
|
||||||
|
processMergeFileTag(n, &mergedFiles);
|
||||||
|
n.parentNode().removeChild(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
// MergeDir ...................
|
||||||
|
else if(n.tagName() == "MergeDir")
|
||||||
|
{
|
||||||
|
processMergeDirTag(n, &mergedFiles);
|
||||||
|
n.parentNode().removeChild(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultMergeDirs ...........
|
||||||
|
else if (n.tagName() == "DefaultMergeDirs")
|
||||||
|
{
|
||||||
|
processDefaultMergeDirsTag(n, &mergedFiles);
|
||||||
|
n.parentNode().removeChild(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
// AppDir ...................
|
||||||
|
else if(n.tagName() == "AppDir")
|
||||||
|
{
|
||||||
|
processAppDirTag(n);
|
||||||
|
n.parentNode().removeChild(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultAppDirs .............
|
||||||
|
else if(n.tagName() == "DefaultAppDirs")
|
||||||
|
{
|
||||||
|
processDefaultAppDirsTag(n);
|
||||||
|
n.parentNode().removeChild(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
// DirectoryDir ...................
|
||||||
|
else if(n.tagName() == "DirectoryDir")
|
||||||
|
{
|
||||||
|
processDirectoryDirTag(n);
|
||||||
|
n.parentNode().removeChild(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultDirectoryDirs ...........
|
||||||
|
else if(n.tagName() == "DefaultDirectoryDirs")
|
||||||
|
{
|
||||||
|
processDefaultDirectoryDirsTag(n);
|
||||||
|
n.parentNode().removeChild(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Menu .......................
|
||||||
|
else if(n.tagName() == "Menu")
|
||||||
|
{
|
||||||
|
processMergeTags(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
n = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
Any number of <MergeFile> elements may be listed below a <Menu> element, giving
|
||||||
|
the name of another menu file to be merged into this one.
|
||||||
|
If fileName is not an absolute path then the file to be merged should be located
|
||||||
|
relative to the location of this menu.
|
||||||
|
|
||||||
|
If the type attribute is missing or set to "path" then the contents of the
|
||||||
|
<MergeFile> element indicates the file to be merged.
|
||||||
|
|
||||||
|
If the type attribute is set to "parent" and the file that contains this
|
||||||
|
<MergeFile> element is located under one of the paths specified by
|
||||||
|
$XDG_CONFIG_DIRS, the contents of the element should be ignored and the remaining
|
||||||
|
paths specified by $XDG_CONFIG_DIRS are searched for a file with the same relative
|
||||||
|
filename. The first file encountered should be merged. There should be no merging
|
||||||
|
at all if no matching file is found. ( Libmenu additional scans ~/.config/menus.)
|
||||||
|
************************************************/
|
||||||
|
void XdgMenuReader::processMergeFileTag(QDomElement& element, QStringList* mergedFiles)
|
||||||
|
{
|
||||||
|
//qDebug() << "Process " << element;// << "in" << mFileName;
|
||||||
|
|
||||||
|
if (element.attribute("type") != "parent")
|
||||||
|
{
|
||||||
|
mergeFile(element.text(), element, mergedFiles);
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
QString relativeName;
|
||||||
|
QStringList configDirs = XdgDirs::configDirs();
|
||||||
|
|
||||||
|
foreach (QString configDir, configDirs)
|
||||||
|
{
|
||||||
|
if (mFileName.startsWith(configDir))
|
||||||
|
{
|
||||||
|
relativeName = mFileName.mid(configDir.length());
|
||||||
|
configDirs.removeAll(configDir);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (relativeName.isEmpty())
|
||||||
|
{
|
||||||
|
QString configHome = XdgDirs::configHome();
|
||||||
|
if (mFileName.startsWith(configHome))
|
||||||
|
relativeName = mFileName.mid(configHome.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (relativeName.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach (QString configDir, configDirs)
|
||||||
|
{
|
||||||
|
if (QFileInfo(configDir + relativeName).exists())
|
||||||
|
{
|
||||||
|
mergeFile(configDir + relativeName, element, mergedFiles);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
A <MergeDir> contains the name of a directory. Each file in the given directory
|
||||||
|
which ends in the ".menu" extension should be merged in the same way that a
|
||||||
|
<MergeFile> would be. If the filename given as a <MergeDir> is not an absolute
|
||||||
|
path, it should be located relative to the location of the menu file being parsed.
|
||||||
|
The files inside the merged directory are not merged in any specified order.
|
||||||
|
|
||||||
|
Duplicate <MergeDir> elements (that specify the same directory) are handled as with
|
||||||
|
duplicate <AppDir> elements (the last duplicate is used).
|
||||||
|
|
||||||
|
KDE additional scans ~/.config/menus.
|
||||||
|
************************************************/
|
||||||
|
void XdgMenuReader::processMergeDirTag(QDomElement& element, QStringList* mergedFiles)
|
||||||
|
{
|
||||||
|
//qDebug() << "Process " << element;// << "in" << mFileName;
|
||||||
|
|
||||||
|
mergeDir(element.text(), element, mergedFiles);
|
||||||
|
element.parentNode().removeChild(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
The element has no content. The element should be treated as if it were a list of
|
||||||
|
<MergeDir> elements containing the default merge directory locations. When expanding
|
||||||
|
<DefaultMergeDirs> to a list of <MergeDir>, the default locations that are earlier
|
||||||
|
in the search path go later in the <Menu> so that they have priority.
|
||||||
|
|
||||||
|
Note that a system that uses either gnome-applications.menu or kde-applications.menu
|
||||||
|
depending on the desktop environment in use must still use applications-merged as the
|
||||||
|
default merge directory in both cases.
|
||||||
|
|
||||||
|
Implementations may chose to use .menu files with names other than application.menu
|
||||||
|
for tasks or menus other than the main application menu. In that case the first part
|
||||||
|
of the name of the default merge directory is derived from the name of the .menu file.
|
||||||
|
************************************************/
|
||||||
|
void XdgMenuReader::processDefaultMergeDirsTag(QDomElement& element, QStringList* mergedFiles)
|
||||||
|
{
|
||||||
|
//qDebug() << "Process " << element;// << "in" << mFileName;
|
||||||
|
|
||||||
|
QString menuBaseName = QFileInfo(mMenu->menuFileName()).baseName();
|
||||||
|
int n = menuBaseName.lastIndexOf('-');
|
||||||
|
if (n>-1)
|
||||||
|
menuBaseName = menuBaseName.mid(n+1);
|
||||||
|
|
||||||
|
QStringList dirs = XdgDirs::configDirs();
|
||||||
|
dirs << XdgDirs::configHome();
|
||||||
|
|
||||||
|
foreach (QString dir, dirs)
|
||||||
|
{
|
||||||
|
mergeDir(QString("%1/menus/%2-merged").arg(dir).arg(menuBaseName), element, mergedFiles);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (menuBaseName == "applications")
|
||||||
|
mergeFile(QString("%1/menus/applications-kmenuedit.menu").arg(XdgDirs::configHome()), element, mergedFiles);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
If the filename given as an <AppDir> is not an absolute path, it should be located
|
||||||
|
relative to the location of the menu file being parsed.
|
||||||
|
************************************************/
|
||||||
|
void XdgMenuReader::processAppDirTag(QDomElement& element)
|
||||||
|
{
|
||||||
|
//qDebug() << "Process " << element;
|
||||||
|
addDirTag(element, "AppDir", element.text());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
The element has no content. The element should be treated as if it were a list of
|
||||||
|
<AppDir> elements containing the default app dir locations
|
||||||
|
($XDG_DATA_DIRS/applications/).
|
||||||
|
|
||||||
|
menu-cache additional prepends $XDG_DATA_HOME/applications.
|
||||||
|
************************************************/
|
||||||
|
void XdgMenuReader::processDefaultAppDirsTag(QDomElement& element)
|
||||||
|
{
|
||||||
|
//qDebug() << "Process " << element;
|
||||||
|
QStringList dirs = XdgDirs::dataDirs();
|
||||||
|
dirs.prepend(XdgDirs::dataHome(false));
|
||||||
|
|
||||||
|
foreach (QString dir, dirs)
|
||||||
|
{
|
||||||
|
//qDebug() << "Add AppDir: " << dir + "/applications/";
|
||||||
|
addDirTag(element, "AppDir", dir + "/applications/");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
If the filename given as a <DirectoryDir> is not an absolute path, it should be
|
||||||
|
located relative to the location of the menu file being parsed.
|
||||||
|
************************************************/
|
||||||
|
void XdgMenuReader::processDirectoryDirTag(QDomElement& element)
|
||||||
|
{
|
||||||
|
//qDebug() << "Process " << element;
|
||||||
|
addDirTag(element, "DirectoryDir", element.text());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
The element has no content. The element should be treated as if it were a list of
|
||||||
|
<DirectoryDir> elements containing the default desktop dir locations
|
||||||
|
($XDG_DATA_DIRS/desktop-directories/).
|
||||||
|
|
||||||
|
menu-cache additional prepends $XDG_DATA_HOME/applications.
|
||||||
|
************************************************/
|
||||||
|
void XdgMenuReader::processDefaultDirectoryDirsTag(QDomElement& element)
|
||||||
|
{
|
||||||
|
//qDebug() << "Process " << element;
|
||||||
|
QStringList dirs = XdgDirs::dataDirs();
|
||||||
|
dirs.prepend(XdgDirs::dataHome(false));
|
||||||
|
|
||||||
|
foreach (QString dir, dirs)
|
||||||
|
addDirTag(element, "DirectoryDir", dir + "/desktop-directories/");
|
||||||
|
}
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
void XdgMenuReader::addDirTag(QDomElement& previousElement, const QString& tagName, const QString& dir)
|
||||||
|
{
|
||||||
|
QFileInfo dirInfo(mDirName, dir);
|
||||||
|
if (dirInfo.isDir())
|
||||||
|
{
|
||||||
|
// qDebug() << "\tAdding " + dirInfo.canonicalFilePath();
|
||||||
|
QDomElement element = mXml.createElement(tagName);
|
||||||
|
element.appendChild(mXml.createTextNode(dirInfo.canonicalFilePath()));
|
||||||
|
previousElement.parentNode().insertBefore(element, previousElement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
If fileName is not an absolute path then the file to be merged should be located
|
||||||
|
relative to the location of this menu file.
|
||||||
|
************************************************/
|
||||||
|
void XdgMenuReader::mergeFile(const QString& fileName, QDomElement& element, QStringList* mergedFiles)
|
||||||
|
{
|
||||||
|
XdgMenuReader reader(mMenu, this);
|
||||||
|
QFileInfo fileInfo(QDir(mDirName), fileName);
|
||||||
|
|
||||||
|
if (!fileInfo.exists())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (mergedFiles->contains(fileInfo.canonicalFilePath()))
|
||||||
|
{
|
||||||
|
//qDebug() << "\tSkip: allredy merged";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//qDebug() << "Merge file: " << fileName;
|
||||||
|
mergedFiles->append(fileInfo.canonicalFilePath());
|
||||||
|
|
||||||
|
if (reader.load(fileName, mDirName))
|
||||||
|
{
|
||||||
|
//qDebug() << "\tOK";
|
||||||
|
QDomElement n = reader.xml().firstChildElement().firstChildElement();
|
||||||
|
while (!n.isNull())
|
||||||
|
{
|
||||||
|
// As a special exception, remove the <Name> element from the root
|
||||||
|
// element of each file being merged.
|
||||||
|
if (n.tagName() != "Name")
|
||||||
|
{
|
||||||
|
QDomNode imp = mXml.importNode(n, true);
|
||||||
|
element.parentNode().insertBefore(imp, element);
|
||||||
|
}
|
||||||
|
|
||||||
|
n = n.nextSiblingElement();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
void XdgMenuReader::mergeDir(const QString& dirName, QDomElement& element, QStringList* mergedFiles)
|
||||||
|
{
|
||||||
|
QFileInfo dirInfo(mDirName, dirName);
|
||||||
|
|
||||||
|
if (dirInfo.isDir())
|
||||||
|
{
|
||||||
|
//qDebug() << "Merge dir: " << dirInfo.canonicalFilePath();
|
||||||
|
QDir dir = QDir(dirInfo.canonicalFilePath());
|
||||||
|
const QFileInfoList files = dir.entryInfoList(QStringList() << "*.menu", QDir::Files | QDir::Readable);
|
||||||
|
|
||||||
|
foreach (QFileInfo file, files)
|
||||||
|
mergeFile(file.canonicalFilePath(), element, mergedFiles);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,80 @@
|
|||||||
|
/* BEGIN_COMMON_COPYRIGHT_HEADER
|
||||||
|
* (c)LGPL2+
|
||||||
|
*
|
||||||
|
* Razor - a lightweight, Qt based, desktop toolset
|
||||||
|
* http://razor-qt.org
|
||||||
|
*
|
||||||
|
* Copyright: 2010-2011 Razor team
|
||||||
|
* Authors:
|
||||||
|
* Alexander Sokoloff <sokoloff.a@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 */
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef QTXDG_XDGMENUREADER_H
|
||||||
|
#define QTXDG_XDGMENUREADER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QString>
|
||||||
|
#include <QStringList>
|
||||||
|
#include <QtXml/QDomDocument>
|
||||||
|
#include <QtXml/QDomElement>
|
||||||
|
class XdgMenu;
|
||||||
|
class XdgMenuReader : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit XdgMenuReader(XdgMenu* menu, XdgMenuReader* parentReader = 0, QObject *parent = 0);
|
||||||
|
virtual ~XdgMenuReader();
|
||||||
|
|
||||||
|
bool load(const QString& fileName, const QString& baseDir = QString());
|
||||||
|
QString fileName() const { return mFileName; }
|
||||||
|
QString errorString() const { return mErrorStr; }
|
||||||
|
QDomDocument& xml() { return mXml; }
|
||||||
|
|
||||||
|
signals:
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void processMergeTags(QDomElement& element);
|
||||||
|
void processMergeFileTag(QDomElement& element, QStringList* mergedFiles);
|
||||||
|
void processMergeDirTag(QDomElement& element, QStringList* mergedFiles);
|
||||||
|
void processDefaultMergeDirsTag(QDomElement& element, QStringList* mergedFiles);
|
||||||
|
|
||||||
|
void processAppDirTag(QDomElement& element);
|
||||||
|
void processDefaultAppDirsTag(QDomElement& element);
|
||||||
|
|
||||||
|
void processDirectoryDirTag(QDomElement& element);
|
||||||
|
void processDefaultDirectoryDirsTag(QDomElement& element);
|
||||||
|
void addDirTag(QDomElement& previousElement, const QString& tagName, const QString& dir);
|
||||||
|
|
||||||
|
void mergeFile(const QString& fileName, QDomElement& element, QStringList* mergedFiles);
|
||||||
|
void mergeDir(const QString& dirName, QDomElement& element, QStringList* mergedFiles);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString mFileName;
|
||||||
|
QString mDirName;
|
||||||
|
QString mErrorStr;
|
||||||
|
QDomDocument mXml;
|
||||||
|
XdgMenuReader* mParentReader;
|
||||||
|
QStringList mBranchFiles;
|
||||||
|
XdgMenu* mMenu;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // QTXDG_XDGMENUREADER_H
|
@ -0,0 +1,289 @@
|
|||||||
|
/* BEGIN_COMMON_COPYRIGHT_HEADER
|
||||||
|
* (c)LGPL2+
|
||||||
|
*
|
||||||
|
* Razor - a lightweight, Qt based, desktop toolset
|
||||||
|
* http://razor-qt.org
|
||||||
|
*
|
||||||
|
* Copyright: 2010-2011 Razor team
|
||||||
|
* Authors:
|
||||||
|
* Alexander Sokoloff <sokoloff.a@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 */
|
||||||
|
|
||||||
|
|
||||||
|
/*********************************************************************
|
||||||
|
See: http://standards.freedesktop.org/desktop-entry-spec
|
||||||
|
|
||||||
|
*********************************************************************/
|
||||||
|
|
||||||
|
#include "xdgmenurules.h"
|
||||||
|
#include "xmlhelper.h"
|
||||||
|
|
||||||
|
#include <QStringList>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
XdgMenuRule::XdgMenuRule(const QDomElement& element, QObject* parent) :
|
||||||
|
QObject(parent)
|
||||||
|
{
|
||||||
|
Q_UNUSED(element)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
XdgMenuRule::~XdgMenuRule()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
The <Or> element contains a list of matching rules. If any of the matching rules
|
||||||
|
inside the <Or> element match a desktop entry, then the entire <Or> rule matches
|
||||||
|
the desktop entry.
|
||||||
|
************************************************/
|
||||||
|
XdgMenuRuleOr::XdgMenuRuleOr(const QDomElement& element, QObject* parent) :
|
||||||
|
XdgMenuRule(element, parent)
|
||||||
|
{
|
||||||
|
//qDebug() << "Create OR rule";
|
||||||
|
DomElementIterator iter(element, QString());
|
||||||
|
|
||||||
|
while(iter.hasNext())
|
||||||
|
{
|
||||||
|
QDomElement e = iter.next();
|
||||||
|
|
||||||
|
if (e.tagName() == "Or")
|
||||||
|
mChilds.append(new XdgMenuRuleOr(e, this));
|
||||||
|
|
||||||
|
else if (e.tagName() == "And")
|
||||||
|
mChilds.append(new XdgMenuRuleAnd(e, this));
|
||||||
|
|
||||||
|
else if (e.tagName() == "Not")
|
||||||
|
mChilds.append(new XdgMenuRuleNot(e, this));
|
||||||
|
|
||||||
|
else if (e.tagName() == "Filename")
|
||||||
|
mChilds.append(new XdgMenuRuleFileName(e, this));
|
||||||
|
|
||||||
|
else if (e.tagName() == "Category")
|
||||||
|
mChilds.append(new XdgMenuRuleCategory(e, this));
|
||||||
|
|
||||||
|
else if (e.tagName() == "All")
|
||||||
|
mChilds.append(new XdgMenuRuleAll(e, this));
|
||||||
|
|
||||||
|
else
|
||||||
|
qWarning() << "Unknown rule" << e.tagName();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
bool XdgMenuRuleOr::check(const QString& desktopFileId, const XdgDesktopFile& desktopFile)
|
||||||
|
{
|
||||||
|
for (QLinkedList<XdgMenuRule*>::Iterator i=mChilds.begin(); i!=mChilds.end(); ++i)
|
||||||
|
if ((*i)->check(desktopFileId, desktopFile)) return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
The <And> element contains a list of matching rules. If each of the matching rules
|
||||||
|
inside the <And> element match a desktop entry, then the entire <And> rule matches
|
||||||
|
the desktop entry.
|
||||||
|
************************************************/
|
||||||
|
XdgMenuRuleAnd::XdgMenuRuleAnd(const QDomElement& element, QObject *parent) :
|
||||||
|
XdgMenuRuleOr(element, parent)
|
||||||
|
{
|
||||||
|
// qDebug() << "Create AND rule";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
bool XdgMenuRuleAnd::check(const QString& desktopFileId, const XdgDesktopFile& desktopFile)
|
||||||
|
{
|
||||||
|
for (QLinkedList<XdgMenuRule*>::Iterator i=mChilds.begin(); i!=mChilds.end(); ++i)
|
||||||
|
if (!(*i)->check(desktopFileId, desktopFile)) return false;
|
||||||
|
|
||||||
|
return mChilds.count();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
The <Not> element contains a list of matching rules. If any of the matching rules
|
||||||
|
inside the <Not> element matches a desktop entry, then the entire <Not> rule does
|
||||||
|
not match the desktop entry. That is, matching rules below <Not> have a logical OR
|
||||||
|
relationship.
|
||||||
|
************************************************/
|
||||||
|
XdgMenuRuleNot::XdgMenuRuleNot(const QDomElement& element, QObject *parent) :
|
||||||
|
XdgMenuRuleOr(element, parent)
|
||||||
|
{
|
||||||
|
// qDebug() << "Create NOT rule";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
bool XdgMenuRuleNot::check(const QString& desktopFileId, const XdgDesktopFile& desktopFile)
|
||||||
|
{
|
||||||
|
return ! XdgMenuRuleOr::check(desktopFileId, desktopFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
The <Filename> element is the most basic matching rule. It matches a desktop entry
|
||||||
|
if the desktop entry has the given desktop-file id. See Desktop-File Id.
|
||||||
|
************************************************/
|
||||||
|
XdgMenuRuleFileName::XdgMenuRuleFileName(const QDomElement& element, QObject *parent) :
|
||||||
|
XdgMenuRule(element, parent)
|
||||||
|
{
|
||||||
|
//qDebug() << "Create FILENAME rule";
|
||||||
|
mId = element.text();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
bool XdgMenuRuleFileName::check(const QString& desktopFileId, const XdgDesktopFile& desktopFile)
|
||||||
|
{
|
||||||
|
Q_UNUSED(desktopFile)
|
||||||
|
//qDebug() << "XdgMenuRuleFileName:" << desktopFileId << mId;
|
||||||
|
return desktopFileId == mId;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
The <Category> element is another basic matching predicate. It matches a desktop entry
|
||||||
|
if the desktop entry has the given category in its Categories field.
|
||||||
|
************************************************/
|
||||||
|
XdgMenuRuleCategory::XdgMenuRuleCategory(const QDomElement& element, QObject *parent) :
|
||||||
|
XdgMenuRule(element, parent)
|
||||||
|
{
|
||||||
|
mCategory = element.text();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
bool XdgMenuRuleCategory::check(const QString& desktopFileId, const XdgDesktopFile& desktopFile)
|
||||||
|
{
|
||||||
|
Q_UNUSED(desktopFileId)
|
||||||
|
QStringList cats = desktopFile.categories();
|
||||||
|
return cats.contains(mCategory);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
The <All> element is a matching rule that matches all desktop entries.
|
||||||
|
************************************************/
|
||||||
|
XdgMenuRuleAll::XdgMenuRuleAll(const QDomElement& element, QObject *parent) :
|
||||||
|
XdgMenuRule(element, parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
bool XdgMenuRuleAll::check(const QString& desktopFileId, const XdgDesktopFile& desktopFile)
|
||||||
|
{
|
||||||
|
Q_UNUSED(desktopFileId)
|
||||||
|
Q_UNUSED(desktopFile)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
XdgMenuRules::XdgMenuRules(QObject* parent) :
|
||||||
|
QObject(parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
XdgMenuRules::~XdgMenuRules()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
void XdgMenuRules::addInclude(const QDomElement& element)
|
||||||
|
{
|
||||||
|
mIncludeRules.append(new XdgMenuRuleOr(element, this));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
void XdgMenuRules::addExclude(const QDomElement& element)
|
||||||
|
{
|
||||||
|
mExcludeRules.append(new XdgMenuRuleOr(element, this));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
bool XdgMenuRules::checkInclude(const QString& desktopFileId, const XdgDesktopFile& desktopFile)
|
||||||
|
{
|
||||||
|
for (QLinkedList<XdgMenuRule*>::Iterator i=mIncludeRules.begin(); i!=mIncludeRules.end(); ++i)
|
||||||
|
if ((*i)->check(desktopFileId, desktopFile)) return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
bool XdgMenuRules::checkExclude(const QString& desktopFileId, const XdgDesktopFile& desktopFile)
|
||||||
|
{
|
||||||
|
for (QLinkedList<XdgMenuRule*>::Iterator i=mExcludeRules.begin(); i!=mExcludeRules.end(); ++i)
|
||||||
|
if ((*i)->check(desktopFileId, desktopFile)) return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,135 @@
|
|||||||
|
/* BEGIN_COMMON_COPYRIGHT_HEADER
|
||||||
|
* (c)LGPL2+
|
||||||
|
*
|
||||||
|
* Razor - a lightweight, Qt based, desktop toolset
|
||||||
|
* http://razor-qt.org
|
||||||
|
*
|
||||||
|
* Copyright: 2010-2011 Razor team
|
||||||
|
* Authors:
|
||||||
|
* Alexander Sokoloff <sokoloff.a@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 */
|
||||||
|
|
||||||
|
|
||||||
|
/*********************************************************************
|
||||||
|
See: http://standards.freedesktop.org/desktop-entry-spec
|
||||||
|
|
||||||
|
*********************************************************************/
|
||||||
|
|
||||||
|
#ifndef QTXDG_XDGMENURULES_H
|
||||||
|
#define QTXDG_XDGMENURULES_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QtXml/QDomElement>
|
||||||
|
#include <QLinkedList>
|
||||||
|
|
||||||
|
#include "xdgdesktopfile.h"
|
||||||
|
|
||||||
|
class XdgMenuRule : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit XdgMenuRule(const QDomElement& element, QObject* parent = 0);
|
||||||
|
virtual ~XdgMenuRule();
|
||||||
|
|
||||||
|
virtual bool check(const QString& desktopFileId, const XdgDesktopFile& desktopFile) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class XdgMenuRuleOr : public XdgMenuRule
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit XdgMenuRuleOr(const QDomElement& element, QObject* parent = 0);
|
||||||
|
|
||||||
|
bool check(const QString& desktopFileId, const XdgDesktopFile& desktopFile);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
QLinkedList<XdgMenuRule*> mChilds;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class XdgMenuRuleAnd : public XdgMenuRuleOr
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit XdgMenuRuleAnd(const QDomElement& element, QObject* parent = 0);
|
||||||
|
bool check(const QString& desktopFileId, const XdgDesktopFile& desktopFile);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class XdgMenuRuleNot : public XdgMenuRuleOr
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit XdgMenuRuleNot(const QDomElement& element, QObject* parent = 0);
|
||||||
|
bool check(const QString& desktopFileId, const XdgDesktopFile& desktopFile);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class XdgMenuRuleFileName : public XdgMenuRule
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit XdgMenuRuleFileName(const QDomElement& element, QObject* parent = 0);
|
||||||
|
bool check(const QString& desktopFileId, const XdgDesktopFile& desktopFile);
|
||||||
|
private:
|
||||||
|
QString mId;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class XdgMenuRuleCategory : public XdgMenuRule
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit XdgMenuRuleCategory(const QDomElement& element, QObject* parent = 0);
|
||||||
|
bool check(const QString& desktopFileId, const XdgDesktopFile& desktopFile);
|
||||||
|
private:
|
||||||
|
QString mCategory;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class XdgMenuRuleAll : public XdgMenuRule
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit XdgMenuRuleAll(const QDomElement& element, QObject* parent = 0);
|
||||||
|
bool check(const QString& desktopFileId, const XdgDesktopFile& desktopFile);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class XdgMenuRules : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit XdgMenuRules(QObject* parent = 0);
|
||||||
|
virtual ~XdgMenuRules();
|
||||||
|
|
||||||
|
void addInclude(const QDomElement& element);
|
||||||
|
void addExclude(const QDomElement& element);
|
||||||
|
|
||||||
|
bool checkInclude(const QString& desktopFileId, const XdgDesktopFile& desktopFile);
|
||||||
|
bool checkExclude(const QString& desktopFileId, const XdgDesktopFile& desktopFile);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
QLinkedList<XdgMenuRule*> mIncludeRules;
|
||||||
|
QLinkedList<XdgMenuRule*> mExcludeRules;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // QTXDG_XDGMENURULES_H
|
@ -0,0 +1,275 @@
|
|||||||
|
/* BEGIN_COMMON_COPYRIGHT_HEADER
|
||||||
|
* (c)LGPL2+
|
||||||
|
*
|
||||||
|
* Razor - a lightweight, Qt based, desktop toolset
|
||||||
|
* http://razor-qt.org
|
||||||
|
*
|
||||||
|
* Copyright: 2010-2011 Razor team
|
||||||
|
* Authors:
|
||||||
|
* Alexander Sokoloff <sokoloff.a@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 "xdgmenuwidget.h"
|
||||||
|
#include "xdgicon.h"
|
||||||
|
#include "xmlhelper.h"
|
||||||
|
#include "xdgaction.h"
|
||||||
|
#include "xdgmenu.h"
|
||||||
|
|
||||||
|
#include <QEvent>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QUrl>
|
||||||
|
#if QT_VERSION < QT_VERSION_CHECK(5,0,0)
|
||||||
|
#else
|
||||||
|
#include <QMimeData>
|
||||||
|
#endif
|
||||||
|
#include <QtGui/QDrag>
|
||||||
|
#include <QtGui/QMouseEvent>
|
||||||
|
#include <QApplication>
|
||||||
|
|
||||||
|
class XdgMenuWidgetPrivate
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
XdgMenuWidget* const q_ptr;
|
||||||
|
Q_DECLARE_PUBLIC(XdgMenuWidget);
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit XdgMenuWidgetPrivate(XdgMenuWidget* parent):
|
||||||
|
q_ptr(parent)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void init(const QDomElement& xml);
|
||||||
|
void buildMenu();
|
||||||
|
|
||||||
|
QDomElement mXml;
|
||||||
|
|
||||||
|
void mouseMoveEvent(QMouseEvent *event);
|
||||||
|
|
||||||
|
QPoint mDragStartPosition;
|
||||||
|
|
||||||
|
private:
|
||||||
|
XdgAction* createAction(const QDomElement& xml);
|
||||||
|
static QString escape(QString string);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
XdgMenuWidget::XdgMenuWidget(const XdgMenu& xdgMenu, const QString& title, QWidget* parent):
|
||||||
|
QMenu(parent),
|
||||||
|
d_ptr(new XdgMenuWidgetPrivate(this))
|
||||||
|
{
|
||||||
|
d_ptr->init(xdgMenu.xml().documentElement());
|
||||||
|
setTitle(XdgMenuWidgetPrivate::escape(title));
|
||||||
|
}
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
XdgMenuWidget::XdgMenuWidget(const QDomElement& menuElement, QWidget* parent):
|
||||||
|
QMenu(parent),
|
||||||
|
d_ptr(new XdgMenuWidgetPrivate(this))
|
||||||
|
{
|
||||||
|
d_ptr->init(menuElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
XdgMenuWidget::XdgMenuWidget(const XdgMenuWidget& other, QWidget* parent):
|
||||||
|
QMenu(parent),
|
||||||
|
d_ptr(new XdgMenuWidgetPrivate(this))
|
||||||
|
{
|
||||||
|
d_ptr->init(other.d_ptr->mXml);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
void XdgMenuWidgetPrivate::init(const QDomElement& xml)
|
||||||
|
{
|
||||||
|
Q_Q(XdgMenuWidget);
|
||||||
|
mXml = xml;
|
||||||
|
|
||||||
|
q->clear();
|
||||||
|
|
||||||
|
QString title;
|
||||||
|
if (! xml.attribute("title").isEmpty())
|
||||||
|
title = xml.attribute("title");
|
||||||
|
else
|
||||||
|
title = xml.attribute("name");
|
||||||
|
q->setTitle(escape(title));
|
||||||
|
|
||||||
|
q->setToolTip(xml.attribute("comment"));
|
||||||
|
|
||||||
|
|
||||||
|
QIcon parentIcon;
|
||||||
|
QMenu* parentMenu = qobject_cast<QMenu*>(q->parent());
|
||||||
|
if (parentMenu)
|
||||||
|
parentIcon = parentMenu->icon();
|
||||||
|
|
||||||
|
q->setIcon(XdgIcon::fromTheme(mXml.attribute("icon"), parentIcon));
|
||||||
|
|
||||||
|
buildMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
XdgMenuWidget::~XdgMenuWidget()
|
||||||
|
{
|
||||||
|
delete d_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
XdgMenuWidget& XdgMenuWidget::operator=(const XdgMenuWidget& other)
|
||||||
|
{
|
||||||
|
Q_D(XdgMenuWidget);
|
||||||
|
d->init(other.d_ptr->mXml);
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
bool XdgMenuWidget::event(QEvent* event)
|
||||||
|
{
|
||||||
|
Q_D(XdgMenuWidget);
|
||||||
|
|
||||||
|
if (event->type() == QEvent::MouseButtonPress)
|
||||||
|
{
|
||||||
|
QMouseEvent *e = static_cast<QMouseEvent*>(event);
|
||||||
|
if (e->button() == Qt::LeftButton)
|
||||||
|
d->mDragStartPosition = e->pos();
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (event->type() == QEvent::MouseMove)
|
||||||
|
{
|
||||||
|
QMouseEvent *e = static_cast<QMouseEvent*>(event);
|
||||||
|
d->mouseMoveEvent(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return QMenu::event(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
void XdgMenuWidgetPrivate::mouseMoveEvent(QMouseEvent *event)
|
||||||
|
{
|
||||||
|
if (!(event->buttons() & Qt::LeftButton))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if ((event->pos() - mDragStartPosition).manhattanLength() < QApplication::startDragDistance())
|
||||||
|
return;
|
||||||
|
|
||||||
|
Q_Q(XdgMenuWidget);
|
||||||
|
XdgAction *a = qobject_cast<XdgAction*>(q->actionAt(event->pos()));
|
||||||
|
if (!a)
|
||||||
|
return;
|
||||||
|
|
||||||
|
QList<QUrl> urls;
|
||||||
|
urls << QUrl(a->desktopFile().fileName());
|
||||||
|
|
||||||
|
QMimeData *mimeData = new QMimeData();
|
||||||
|
mimeData->setUrls(urls);
|
||||||
|
|
||||||
|
QDrag *drag = new QDrag(q);
|
||||||
|
drag->setMimeData(mimeData);
|
||||||
|
drag->exec(Qt::CopyAction | Qt::LinkAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
void XdgMenuWidgetPrivate::buildMenu()
|
||||||
|
{
|
||||||
|
Q_Q(XdgMenuWidget);
|
||||||
|
|
||||||
|
QAction* first = 0;
|
||||||
|
if (!q->actions().isEmpty())
|
||||||
|
first = q->actions().last();
|
||||||
|
|
||||||
|
|
||||||
|
DomElementIterator it(mXml, QString());
|
||||||
|
while(it.hasNext())
|
||||||
|
{
|
||||||
|
QDomElement xml = it.next();
|
||||||
|
|
||||||
|
// Build submenu ........................
|
||||||
|
if (xml.tagName() == "Menu")
|
||||||
|
q->insertMenu(first, new XdgMenuWidget(xml, q));
|
||||||
|
|
||||||
|
//Build application link ................
|
||||||
|
else if (xml.tagName() == "AppLink")
|
||||||
|
q->insertAction(first, createAction(xml));
|
||||||
|
|
||||||
|
//Build separator .......................
|
||||||
|
else if (xml.tagName() == "Separator")
|
||||||
|
q->insertSeparator(first);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
XdgAction* XdgMenuWidgetPrivate::createAction(const QDomElement& xml)
|
||||||
|
{
|
||||||
|
Q_Q(XdgMenuWidget);
|
||||||
|
XdgAction* action = new XdgAction(xml.attribute("desktopFile"), q);
|
||||||
|
|
||||||
|
QString title;
|
||||||
|
if (!xml.attribute("title").isEmpty())
|
||||||
|
title = xml.attribute("title");
|
||||||
|
else
|
||||||
|
title = xml.attribute("name");
|
||||||
|
|
||||||
|
|
||||||
|
if (!xml.attribute("genericName").isEmpty() &&
|
||||||
|
xml.attribute("genericName") != title)
|
||||||
|
title += QString(" (%1)").arg(xml.attribute("genericName"));
|
||||||
|
|
||||||
|
action->setText(escape(title));
|
||||||
|
return action;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
This should be used when a menu item text is set
|
||||||
|
otherwise Qt uses the &'s for creating mnemonics
|
||||||
|
************************************************/
|
||||||
|
QString XdgMenuWidgetPrivate::escape(QString string)
|
||||||
|
{
|
||||||
|
return string.replace("&", "&&");
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,89 @@
|
|||||||
|
/* BEGIN_COMMON_COPYRIGHT_HEADER
|
||||||
|
* (c)LGPL2+
|
||||||
|
*
|
||||||
|
* Razor - a lightweight, Qt based, desktop toolset
|
||||||
|
* http://razor-qt.org
|
||||||
|
*
|
||||||
|
* Copyright: 2010-2011 Razor team
|
||||||
|
* Authors:
|
||||||
|
* Alexander Sokoloff <sokoloff.a@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 */
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef QTXDG_XDGMENUWIDGET_H
|
||||||
|
#define QTXDG_XDGMENUWIDGET_H
|
||||||
|
|
||||||
|
#include "xdgmacros.h"
|
||||||
|
#include <QMenu>
|
||||||
|
#include <QtXml/QDomElement>
|
||||||
|
|
||||||
|
class XdgMenu;
|
||||||
|
class QEvent;
|
||||||
|
class XdgMenuWidgetPrivate;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief The XdgMenuWidget class provides an QMenu widget for application menu or its part.
|
||||||
|
|
||||||
|
Example usage:
|
||||||
|
@code
|
||||||
|
QString menuFile = XdgMenu::getMenuFileName();
|
||||||
|
XdgMenu xdgMenu(menuFile);
|
||||||
|
|
||||||
|
bool res = xdgMenu.read();
|
||||||
|
if (res)
|
||||||
|
{
|
||||||
|
XdgMenuWidget menu(xdgMenu, QString(), this);
|
||||||
|
menu.exec(QCursor::pos());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
QMessageBox::warning(this, "Parse error", xdgMenu.errorString());
|
||||||
|
}
|
||||||
|
@endcode
|
||||||
|
*/
|
||||||
|
|
||||||
|
class QTXDG_API XdgMenuWidget : public QMenu
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
/// Constructs a menu for root documentElement in xdgMenu with some text and parent.
|
||||||
|
XdgMenuWidget(const XdgMenu& xdgMenu, const QString& title = QString(), QWidget* parent=0);
|
||||||
|
|
||||||
|
/// Constructs a menu for menuElement with parent.
|
||||||
|
explicit XdgMenuWidget(const QDomElement& menuElement, QWidget* parent=0);
|
||||||
|
|
||||||
|
/// Constructs a copy of other.
|
||||||
|
XdgMenuWidget(const XdgMenuWidget& other, QWidget* parent=0);
|
||||||
|
|
||||||
|
/// Assigns other to this menu.
|
||||||
|
XdgMenuWidget& operator=(const XdgMenuWidget& other);
|
||||||
|
|
||||||
|
/// Destroys the menu.
|
||||||
|
virtual ~XdgMenuWidget();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool event(QEvent* event);
|
||||||
|
|
||||||
|
private:
|
||||||
|
XdgMenuWidgetPrivate* const d_ptr;
|
||||||
|
Q_DECLARE_PRIVATE(XdgMenuWidget)
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // QTXDG_XDGMENUWIDGET_H
|
@ -0,0 +1,366 @@
|
|||||||
|
/* BEGIN_COMMON_COPYRIGHT_HEADER
|
||||||
|
* (c)LGPL2+
|
||||||
|
*
|
||||||
|
* Razor - a lightweight, Qt based, desktop toolset
|
||||||
|
* http://razor-qt.org
|
||||||
|
*
|
||||||
|
* Copyright: 2010-2011 Razor team
|
||||||
|
* Authors:
|
||||||
|
* Alexander Sokoloff <sokoloff.a@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 "xdgmime.h"
|
||||||
|
#include "xdgicon.h"
|
||||||
|
#include "xdgdirs.h"
|
||||||
|
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <magic.h>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QStringList>
|
||||||
|
#include <QMap>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QDomDocument>
|
||||||
|
#include <QDomElement>
|
||||||
|
#include <QSharedData>
|
||||||
|
|
||||||
|
|
||||||
|
struct XdgMimeData
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
XdgMimeData(QString media, QString subtype);
|
||||||
|
bool readXml(QIODevice* xml);
|
||||||
|
|
||||||
|
QString mMedia;
|
||||||
|
QString mSubtype;
|
||||||
|
|
||||||
|
bool mDbLoaded;
|
||||||
|
QString mComment;
|
||||||
|
QMap<QString, QString> mLocalizedComments;
|
||||||
|
QStringList mPatterns;
|
||||||
|
QString mSubClassOf;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
XdgMimeInfo::XdgMimeInfo(const QString& mimeType)
|
||||||
|
{
|
||||||
|
QString media = mimeType.section('/', 0, 0);
|
||||||
|
QString subtype = mimeType.section('/', 1);
|
||||||
|
mData = new XdgMimeData(media, subtype);
|
||||||
|
}
|
||||||
|
|
||||||
|
XdgMimeInfo::~XdgMimeInfo()
|
||||||
|
{
|
||||||
|
delete mData;
|
||||||
|
mData = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
QString getFileMimeType(const QFileInfo& fileInfo, bool followSymLinks)
|
||||||
|
{
|
||||||
|
|
||||||
|
QString result("application/octet-stream");
|
||||||
|
|
||||||
|
magic_t magicMimePredictor;
|
||||||
|
magicMimePredictor = magic_open(MAGIC_MIME_TYPE); // Open predictor
|
||||||
|
if (!magicMimePredictor) {
|
||||||
|
qWarning() << "libmagic: Unable to initialize magic library";
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (magic_load(magicMimePredictor, 0)) { // if not 0 - error
|
||||||
|
qWarning() << QString("libmagic: Can't load magic database - %1").arg(magic_error(magicMimePredictor));
|
||||||
|
magic_close(magicMimePredictor); // Close predictor
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray ar = fileInfo.absoluteFilePath().toLocal8Bit();
|
||||||
|
if (followSymLinks && fileInfo.isSymLink())
|
||||||
|
{
|
||||||
|
ar = fileInfo.symLinkTarget().toLocal8Bit();
|
||||||
|
}
|
||||||
|
char *file = ar.data();
|
||||||
|
|
||||||
|
// getting mime-type ........................
|
||||||
|
const char *mime;
|
||||||
|
mime = magic_file(magicMimePredictor, file);
|
||||||
|
result = QString(mime);
|
||||||
|
|
||||||
|
// Close predictor ..........................
|
||||||
|
magic_close(magicMimePredictor);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
XdgMimeInfo::XdgMimeInfo(const QFileInfo& file, bool followSymLinks)
|
||||||
|
{
|
||||||
|
QString mimeType = getFileMimeType(file, followSymLinks);
|
||||||
|
QString media = mimeType.section('/', 0, 0);
|
||||||
|
QString subtype = mimeType.section('/', 1);
|
||||||
|
mData = new XdgMimeData(media, subtype);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
QString XdgMimeInfo::mimeType() const
|
||||||
|
{
|
||||||
|
return mData->mMedia + "/" + mData->mSubtype;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QString XdgMimeInfo::mediaType() const
|
||||||
|
{
|
||||||
|
return mData->mMedia;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QString XdgMimeInfo::subType() const
|
||||||
|
{
|
||||||
|
return mData->mSubtype;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString XdgMimeInfo::comment() const
|
||||||
|
{
|
||||||
|
return mData->mComment;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString XdgMimeInfo::localizedComment() const
|
||||||
|
{
|
||||||
|
// FIXME
|
||||||
|
return mData->mComment;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList XdgMimeInfo::patterns() const
|
||||||
|
{
|
||||||
|
return mData->mPatterns;
|
||||||
|
}
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
QString XdgMimeInfo::iconName() const
|
||||||
|
{
|
||||||
|
QStringList names;
|
||||||
|
names << QString("%1-x-%2").arg(mData->mMedia, mData->mSubtype);
|
||||||
|
names << QString("%1-%2").arg(mData->mMedia, mData->mSubtype);
|
||||||
|
names << QString("%1-x-generic").arg(mData->mMedia);
|
||||||
|
names << QString("%1-generic").arg(mData->mMedia);
|
||||||
|
|
||||||
|
foreach (QString s, names)
|
||||||
|
{
|
||||||
|
if (!XdgIcon::fromTheme(s).isNull())
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
QIcon XdgMimeInfo::icon() const
|
||||||
|
{
|
||||||
|
return XdgIcon::fromTheme(iconName());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QString XdgMimeInfo::subClassOf() const
|
||||||
|
{
|
||||||
|
return mData->mSubClassOf;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool XdgMimeInfo::loadFromDb(QIODevice* xml)
|
||||||
|
{
|
||||||
|
return mData->readXml(xml);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
XdgMimeData::XdgMimeData(QString media, QString subtype):
|
||||||
|
mMedia(media),
|
||||||
|
mSubtype(subtype),
|
||||||
|
mDbLoaded(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool XdgMimeData::readXml(QIODevice* xml)
|
||||||
|
{
|
||||||
|
QDomDocument domDocument;
|
||||||
|
if (! domDocument.setContent(xml, false))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDomElement rootElement = domDocument.documentElement();
|
||||||
|
if (rootElement.nodeName() != "mime-type")
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rootElement.attribute("type") != mMedia + "/" + mSubtype)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDomNodeList commentNodes = rootElement.elementsByTagName("comment");
|
||||||
|
for(int i = 0; i < commentNodes.size(); i++)
|
||||||
|
{
|
||||||
|
if (! commentNodes.item(i).isElement())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDomElement commentElement = commentNodes.item(i).toElement();
|
||||||
|
|
||||||
|
if (commentElement.hasAttribute("xml:lang"))
|
||||||
|
{
|
||||||
|
mLocalizedComments[commentElement.attribute("xml:lang")] = commentElement.text();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mComment = commentElement.text();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QSet<QString> collectedPatterns;
|
||||||
|
QDomNodeList globNodes = rootElement.elementsByTagName("glob");
|
||||||
|
for(int i = 0; i < globNodes.size(); i++)
|
||||||
|
{
|
||||||
|
if (globNodes.item(i).isElement() && globNodes.item(i).toElement().hasAttribute("pattern"))
|
||||||
|
{
|
||||||
|
collectedPatterns << globNodes.item(i).toElement().attribute("pattern");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mPatterns = collectedPatterns.toList();
|
||||||
|
mPatterns.sort();
|
||||||
|
|
||||||
|
QDomNodeList subClassOfElements = rootElement.elementsByTagName("sub-class-of");
|
||||||
|
if (subClassOfElements.size() > 0)
|
||||||
|
{
|
||||||
|
mSubClassOf = subClassOfElements.at(0).toElement().attribute("type");
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QStringList XdgMimeInfoCache::mediatypes()
|
||||||
|
{
|
||||||
|
return cache().keys();
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList XdgMimeInfoCache::subtypes(const QString& media)
|
||||||
|
{
|
||||||
|
return cache().value(media).keys();
|
||||||
|
}
|
||||||
|
|
||||||
|
XdgMimeInfo* XdgMimeInfoCache::xdgMimeInfo(const QString & media, const QString & subtype)
|
||||||
|
{
|
||||||
|
return cache().value(media).value(subtype);
|
||||||
|
}
|
||||||
|
|
||||||
|
XdgMimeInfo* XdgMimeInfoCache::xdgMimeInfo(const QString& mimetype)
|
||||||
|
{
|
||||||
|
QString media = mimetype.section("/", 0, 0);
|
||||||
|
QString subtype = mimetype.section("/", 1, 1);
|
||||||
|
return xdgMimeInfo(media, subtype);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void loadMimeInfoCache(QMap<QString, QMap<QString, XdgMimeInfo*> > & cache)
|
||||||
|
{
|
||||||
|
qDebug() << "loadMimeInfoCache";
|
||||||
|
QStringList datadirs = XdgDirs::dataDirs();
|
||||||
|
datadirs.prepend(XdgDirs::dataHome(false));
|
||||||
|
const QStringList filters = (QStringList() << "*.xml");
|
||||||
|
|
||||||
|
foreach (const QString datadir, datadirs)
|
||||||
|
{
|
||||||
|
QDir mimedir(datadir + "/mime");
|
||||||
|
|
||||||
|
if (! mimedir.exists())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
foreach (QFileInfo mediadirInfo, mimedir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot))
|
||||||
|
{
|
||||||
|
QString media = mediadirInfo.fileName();
|
||||||
|
|
||||||
|
QDir mediadir(mediadirInfo.absoluteFilePath());
|
||||||
|
foreach (QFileInfo subtypefileInfo, mediadir.entryInfoList(filters, QDir::Files))
|
||||||
|
{
|
||||||
|
QString subtype = subtypefileInfo.fileName().left(subtypefileInfo.fileName().length() - 4);
|
||||||
|
//qDebug() << "subtype:" << subtype;
|
||||||
|
QFile subtypefile(subtypefileInfo.absoluteFilePath());
|
||||||
|
XdgMimeInfo* mimeInfo = new XdgMimeInfo(media + "/" + subtype);
|
||||||
|
if (subtypefile.open(QIODevice::ReadOnly) && mimeInfo->loadFromDb(&subtypefile))
|
||||||
|
{
|
||||||
|
cache[media][subtype] = mimeInfo;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
delete mimeInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TESTING
|
||||||
|
XdgMimeData data("application", "msword");
|
||||||
|
QFile mswordxml("/usr/share/mime/application/msword.xml");
|
||||||
|
mswordxml.open(QIODevice::ReadOnly);
|
||||||
|
data.readXml(&mswordxml);
|
||||||
|
qDebug() << "=================================================================================";
|
||||||
|
qDebug() << "data:" << data.mMedia << data.mSubtype << data.mComment << data.mLocalizedComments << data.mPatterns;
|
||||||
|
qDebug() << "=================================================================================";
|
||||||
|
}
|
||||||
|
|
||||||
|
QMap<QString, QMap<QString, XdgMimeInfo*> > & XdgMimeInfoCache::cache()
|
||||||
|
{
|
||||||
|
static QMap<QString, QMap<QString, XdgMimeInfo*> > _cache;
|
||||||
|
static bool cache_loaded = false;
|
||||||
|
|
||||||
|
if (! cache_loaded)
|
||||||
|
{
|
||||||
|
loadMimeInfoCache(_cache);
|
||||||
|
cache_loaded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _cache;
|
||||||
|
}
|
@ -0,0 +1,99 @@
|
|||||||
|
/* BEGIN_COMMON_COPYRIGHT_HEADER
|
||||||
|
* (c)LGPL2+
|
||||||
|
*
|
||||||
|
* Razor - a lightweight, Qt based, desktop toolset
|
||||||
|
* http://razor-qt.org
|
||||||
|
*
|
||||||
|
* Copyright: 2010-2011 Razor team
|
||||||
|
* Authors:
|
||||||
|
* Alexander Sokoloff <sokoloff.a@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 */
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef QTXDG_XDGMIME_H
|
||||||
|
#define QTXDG_XDGMIME_H
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QtGui/QIcon>
|
||||||
|
|
||||||
|
#include "xdgmacros.h"
|
||||||
|
|
||||||
|
struct XdgMimeData;
|
||||||
|
|
||||||
|
/*! @brief The XdgMimeInfo class provides mime information about file.
|
||||||
|
*/
|
||||||
|
class QTXDG_DEPRECATED QTXDG_API XdgMimeInfo
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// Constructs a XdgMimeInfo with the mimeType type.
|
||||||
|
explicit XdgMimeInfo(const QString& mimeType);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Constructs a new XdgMimeInfo that gives mime information about the given file.
|
||||||
|
If file is symlink and followSymLinks is true function gives information for the
|
||||||
|
file the link references rather than for the link itself.
|
||||||
|
**/
|
||||||
|
explicit XdgMimeInfo(const QFileInfo& file, bool followSymLinks=true);
|
||||||
|
|
||||||
|
~XdgMimeInfo();
|
||||||
|
|
||||||
|
/// Returns the name of the mime type.
|
||||||
|
QString mimeType() const;
|
||||||
|
|
||||||
|
/// Returns the media type, eg. 'application' for mimetype 'application/pdf'
|
||||||
|
QString mediaType() const;
|
||||||
|
|
||||||
|
/// Returns the subtype, e.g. 'pdf' for 'application/pdf'
|
||||||
|
QString subType() const;
|
||||||
|
|
||||||
|
QString comment() const;
|
||||||
|
|
||||||
|
QString localizedComment() const;
|
||||||
|
|
||||||
|
QStringList patterns() const;
|
||||||
|
|
||||||
|
/// Returns an icon associated with the mime type.
|
||||||
|
QIcon icon() const;
|
||||||
|
|
||||||
|
/// Returns an icon associated with the mime type.
|
||||||
|
QString iconName() const;
|
||||||
|
|
||||||
|
QString subClassOf() const;
|
||||||
|
|
||||||
|
bool loadFromDb(QIODevice* xml);
|
||||||
|
|
||||||
|
private:
|
||||||
|
XdgMimeData *mData;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class QTXDG_DEPRECATED QTXDG_API XdgMimeInfoCache
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static QStringList mediatypes();
|
||||||
|
static QStringList subtypes(const QString & media);
|
||||||
|
static XdgMimeInfo* xdgMimeInfo(const QString & media, const QString & subtype);
|
||||||
|
static XdgMimeInfo* xdgMimeInfo(const QString & mimetype);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static QMap<QString, QMap<QString, XdgMimeInfo*> > & cache();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // QTXDG_XDGMIME_H
|
@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
* libqtxdg - An Qt implementation of freedesktop.org xdg specs
|
||||||
|
* Copyright (C) 2014 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 "xdgmimetype.h"
|
||||||
|
|
||||||
|
#include "xdgicon.h"
|
||||||
|
|
||||||
|
class XdgMimeTypePrivate : public QSharedData {
|
||||||
|
public:
|
||||||
|
XdgMimeTypePrivate();
|
||||||
|
XdgMimeTypePrivate(const XdgMimeType& other);
|
||||||
|
|
||||||
|
void computeIconName();
|
||||||
|
|
||||||
|
QString iconName;
|
||||||
|
bool computed;
|
||||||
|
};
|
||||||
|
|
||||||
|
XdgMimeTypePrivate::XdgMimeTypePrivate()
|
||||||
|
: computed(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
XdgMimeTypePrivate::XdgMimeTypePrivate(const XdgMimeType& other)
|
||||||
|
: iconName(other.dx->iconName),
|
||||||
|
computed(other.dx->computed)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
XdgMimeType::XdgMimeType()
|
||||||
|
: QMimeType(),
|
||||||
|
dx(new XdgMimeTypePrivate())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
XdgMimeType::XdgMimeType(const QMimeType& mime)
|
||||||
|
: QMimeType(mime),
|
||||||
|
dx(new XdgMimeTypePrivate())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
XdgMimeType::XdgMimeType(const XdgMimeType& mime)
|
||||||
|
: QMimeType(mime),
|
||||||
|
dx(mime.dx)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
XdgMimeType &XdgMimeType::operator=(const XdgMimeType &other)
|
||||||
|
{
|
||||||
|
QMimeType::operator =(other);
|
||||||
|
|
||||||
|
if (dx != other.dx)
|
||||||
|
dx = other.dx;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
XdgMimeType::~XdgMimeType()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QString XdgMimeType::iconName() const
|
||||||
|
{
|
||||||
|
if (dx->computed) {
|
||||||
|
return dx->iconName;
|
||||||
|
} else {
|
||||||
|
dx->iconName.clear();
|
||||||
|
QStringList names;
|
||||||
|
|
||||||
|
names.append(QMimeType::iconName());
|
||||||
|
names.append(QMimeType::genericIconName());
|
||||||
|
|
||||||
|
foreach (QString s, names) {
|
||||||
|
if (!XdgIcon::fromTheme(s).isNull()) {
|
||||||
|
dx->iconName = s;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dx->computed = true;
|
||||||
|
return dx->iconName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QIcon XdgMimeType::icon() const
|
||||||
|
{
|
||||||
|
return XdgIcon::fromTheme((iconName()));
|
||||||
|
}
|
@ -0,0 +1,129 @@
|
|||||||
|
/*
|
||||||
|
* libqtxdg - An Qt implementation of freedesktop.org xdg specs
|
||||||
|
* Copyright (C) 2014 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef QTXDG_MIMETYPE_H
|
||||||
|
#define QTXDG_MIMETYPE_H
|
||||||
|
|
||||||
|
#include "xdgmacros.h"
|
||||||
|
#include <QMimeType>
|
||||||
|
#include <QIcon>
|
||||||
|
#include <QString>
|
||||||
|
#include <QSharedData>
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
class XdgMimeTypePrivate;
|
||||||
|
|
||||||
|
//! Describes types of file or data, represented by a MIME type string.
|
||||||
|
/*! This class is an QMimeType descendent. The differences are the icon() and
|
||||||
|
* iconName() methods. @see icon() @see iconName()
|
||||||
|
*
|
||||||
|
* Some parts of the documentation are based on QMimeType documentation.
|
||||||
|
* @see http://qt-project.org/doc/qt-5/qmimetype.html
|
||||||
|
*
|
||||||
|
* @author Luís Pereira (luis.artur.pereira@gmail.com)
|
||||||
|
*/
|
||||||
|
|
||||||
|
class QTXDG_API XdgMimeType : public QMimeType {
|
||||||
|
public:
|
||||||
|
|
||||||
|
/*! Constructs an XdgMimeType object initialized with default property
|
||||||
|
values that indicate an invalid MIME type.
|
||||||
|
@see QMimeType::QMimeType()
|
||||||
|
*/
|
||||||
|
XdgMimeType();
|
||||||
|
|
||||||
|
/*! Constructs an XdgMimeType object from an QMimeType object
|
||||||
|
* @see QMimeType
|
||||||
|
*/
|
||||||
|
XdgMimeType(const QMimeType& mime);
|
||||||
|
|
||||||
|
//! Constructs an XdgMimeType object from another XdgMimeType object
|
||||||
|
XdgMimeType(const XdgMimeType& mime);
|
||||||
|
|
||||||
|
/*! Assigns the data of other to this XdgMimeType object.
|
||||||
|
* @return a reference to this object.
|
||||||
|
*/
|
||||||
|
XdgMimeType &operator=(const XdgMimeType &other);
|
||||||
|
|
||||||
|
|
||||||
|
/*! Compares the other XdgMimeType object to this XdgMimeType object.
|
||||||
|
* @return true if other equals this XdgMimeType object, otherwise returns
|
||||||
|
* false. The name is the unique identifier for a mimetype, so two mimetypes
|
||||||
|
* with the same name, are equal.
|
||||||
|
* @see QMimeType::operator==()
|
||||||
|
*/
|
||||||
|
bool operator==(const XdgMimeType &other) const
|
||||||
|
{
|
||||||
|
return QMimeType::operator==(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool operator!=(const XdgMimeType &other) const
|
||||||
|
{
|
||||||
|
return QMimeType::operator==(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
void swap(XdgMimeType &other)
|
||||||
|
{
|
||||||
|
QMimeType::swap(other);
|
||||||
|
qSwap(dx, other.dx);
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Destructs the mimetype
|
||||||
|
~XdgMimeType();
|
||||||
|
|
||||||
|
//! Returns the name of the MIME type.
|
||||||
|
/*! The same as QMimeType::name(). Provided for compatibilty with deprecated
|
||||||
|
* XdgMimeInfo::mimeType().
|
||||||
|
* @see QMimeType::name()
|
||||||
|
*/
|
||||||
|
inline QString mimeType() const { return QMimeType::name(); }
|
||||||
|
|
||||||
|
//! Returns an icon associated with the mimetype.
|
||||||
|
/*! @return an icon from the current icon theme associated with the
|
||||||
|
* mimetype. If the icon theme doesn't provide one it returns QIcon().
|
||||||
|
* It gets the icon name from iconName() and then gives it to
|
||||||
|
* XdgIcon::fromTheme().
|
||||||
|
* @see iconName() @see XdgIcon::fromTheme()
|
||||||
|
*/
|
||||||
|
QIcon icon() const;
|
||||||
|
|
||||||
|
//! Returns an icon name associated with the mimetype.
|
||||||
|
/*! @return an icon name from the current icon theme associated with the
|
||||||
|
* mimetype. If the current icon theme doesn't provide one, it returns an
|
||||||
|
* empty QString.
|
||||||
|
* The returned icon name is suitable to be given to XdgIcon::fromTheme()
|
||||||
|
* to load the icon.
|
||||||
|
* @see XdgIcon::fromTheme()
|
||||||
|
*/
|
||||||
|
QString iconName() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
friend class XdgMimeTypePrivate;
|
||||||
|
QExplicitlySharedDataPointer<XdgMimeTypePrivate> dx;
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
#if QT_VERSION < QT_VERSION_CHECK(5,0,0)
|
||||||
|
#else
|
||||||
|
Q_DECLARE_SHARED(XdgMimeType)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // QTXDG_MIMETYPE_H
|
@ -0,0 +1,51 @@
|
|||||||
|
/* BEGIN_COMMON_COPYRIGHT_HEADER
|
||||||
|
* (c)LGPL2+
|
||||||
|
*
|
||||||
|
* Razor - a lightweight, Qt based, desktop toolset
|
||||||
|
* http://razor-qt.org
|
||||||
|
*
|
||||||
|
* Copyright: 2010-2011 Razor team
|
||||||
|
* Authors:
|
||||||
|
* Alexander Sokoloff <sokoloff.a@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 "xmlhelper.h"
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QtXml/QDomElement>
|
||||||
|
#include <QtXml/QDomNode>
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
|
||||||
|
************************************************/
|
||||||
|
QDebug operator<<(QDebug dbg, const QDomElement &el)
|
||||||
|
{
|
||||||
|
QDomNamedNodeMap map = el.attributes();
|
||||||
|
|
||||||
|
QString args;
|
||||||
|
for (int i=0; i<map.count(); ++i)
|
||||||
|
args += " " + map.item(i).nodeName() + "='" + map.item(i).nodeValue() + "'";
|
||||||
|
|
||||||
|
dbg.nospace() << QString("<%1%2>%3</%1>").arg(el.tagName()).arg(args).arg(el.text());
|
||||||
|
return dbg.space();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,162 @@
|
|||||||
|
/* BEGIN_COMMON_COPYRIGHT_HEADER
|
||||||
|
* (c)LGPL2+
|
||||||
|
*
|
||||||
|
* Razor - a lightweight, Qt based, desktop toolset
|
||||||
|
* http://razor-qt.org
|
||||||
|
*
|
||||||
|
* Copyright: 2010-2011 Razor team
|
||||||
|
* Authors:
|
||||||
|
* Alexander Sokoloff <sokoloff.a@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 */
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef QTXDG_XMLHELPER_H
|
||||||
|
#define QTXDG_XMLHELPER_H
|
||||||
|
|
||||||
|
#include "xdgmacros.h"
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QtXml/QDomElement>
|
||||||
|
#include <QtXml/QDomNode>
|
||||||
|
|
||||||
|
class QTXDG_API DomElementIterator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit DomElementIterator(const QDomNode& parentNode, const QString& tagName = QString())
|
||||||
|
{
|
||||||
|
mTagName = tagName;
|
||||||
|
mParent = parentNode;
|
||||||
|
toFront();
|
||||||
|
}
|
||||||
|
|
||||||
|
void toFront()
|
||||||
|
{
|
||||||
|
mNext = mParent.firstChildElement(mTagName);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasNext()
|
||||||
|
{
|
||||||
|
return (!mNext.isNull());
|
||||||
|
}
|
||||||
|
|
||||||
|
const QDomElement& next()
|
||||||
|
{
|
||||||
|
mCur = mNext;
|
||||||
|
mNext = mNext.nextSiblingElement(mTagName);
|
||||||
|
return mCur;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void toBack()
|
||||||
|
{
|
||||||
|
mNext = mParent.lastChildElement(mTagName);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool hasPrevious()
|
||||||
|
{
|
||||||
|
return (!mNext.isNull());
|
||||||
|
}
|
||||||
|
|
||||||
|
const QDomElement& previous()
|
||||||
|
{
|
||||||
|
mCur = mNext;
|
||||||
|
mNext = mNext.previousSiblingElement(mTagName);
|
||||||
|
return mCur;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QDomElement& current() const
|
||||||
|
{
|
||||||
|
return mCur;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString mTagName;
|
||||||
|
QDomNode mParent;
|
||||||
|
QDomElement mCur;
|
||||||
|
QDomElement mNext;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MutableDomElementIterator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit MutableDomElementIterator(QDomNode& parentNode, const QString& tagName = QString())
|
||||||
|
{
|
||||||
|
mTagName = tagName;
|
||||||
|
mParent = parentNode;
|
||||||
|
toFront();
|
||||||
|
}
|
||||||
|
|
||||||
|
void toFront()
|
||||||
|
{
|
||||||
|
mNext = mParent.firstChildElement(mTagName);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasNext()
|
||||||
|
{
|
||||||
|
return (!mNext.isNull());
|
||||||
|
}
|
||||||
|
|
||||||
|
QDomElement& next()
|
||||||
|
{
|
||||||
|
mCur = mNext;
|
||||||
|
mNext = mNext.nextSiblingElement(mTagName);
|
||||||
|
return mCur;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void toBack()
|
||||||
|
{
|
||||||
|
mNext = mParent.lastChildElement(mTagName);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool hasPrevious()
|
||||||
|
{
|
||||||
|
return (!mNext.isNull());
|
||||||
|
}
|
||||||
|
|
||||||
|
QDomElement& previous()
|
||||||
|
{
|
||||||
|
mCur = mNext;
|
||||||
|
mNext = mNext.previousSiblingElement(mTagName);
|
||||||
|
return mCur;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDomElement& current()
|
||||||
|
{
|
||||||
|
return mCur;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString mTagName;
|
||||||
|
QDomNode mParent;
|
||||||
|
QDomElement mCur;
|
||||||
|
QDomElement mNext;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
QDebug operator<<(QDebug dbg, const QDomElement &el);
|
||||||
|
|
||||||
|
|
||||||
|
#endif // QTXDG_XMLHELPER_H
|
Loading…
Reference in new issue