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