Adding upstream version 1.2.0.

Signed-off-by: Andrew Lee (李健秋) <ajqlee@debian.org>
ubuntu/cosmic upstream/1.2.0
Andrew Lee (李健秋) 9 years ago
commit 8762635176
No known key found for this signature in database
GPG Key ID: 9D0633E1B6250985

8
.gitignore vendored

@ -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, &params);
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…
Cancel
Save