Page MenuHomePhorge

No OneTemporary

This file is larger than 256 KB, so syntax highlighting was skipped.
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 66620d1fcd..a451335419 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,408 +1,408 @@
cmake_minimum_required(VERSION 2.8.7)
project(kdelibs)
# where to look first for cmake modules, before ${CMAKE_ROOT}/Modules/ is checked
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/modules")
# Make CPack available to easy generate binary packages
include(CPack)
find_file(FOUND_KDE4_HEADERS kglobal.h)
if (${FOUND_KDE4_HEADERS})
string(REGEX MATCH ${CMAKE_INSTALL_PREFIX} COMPATIBLE_HEADERS_TEST ${FOUND_KDE4_HEADERS})
if (NOT ${COMPATIBLE_HEADERS_TEST})
message("Found KDE4 headers. This will cause compiling failures while developing KDE Frameworks 5. Uninstall distro installed headers if applicable")
message("FOUND: ${FOUND_KDE4_HEADERS}")
return()
endif()
endif()
################# set KDE specific information #################
set (KDE_VERSION_MAJOR 4)
set (KDE_VERSION_MINOR 90)
set (KDE_VERSION_RELEASE 00)
set (KDE_VERSION "${KDE_VERSION_MAJOR}.${KDE_VERSION_MINOR}.${KDE_VERSION_RELEASE}" )
set (KDE_VERSION_STRING "${KDE_VERSION} (4.90.0 (KDE Frameworks >= 20120113)")
set (KDE_DISTRIBUTION_TEXT "compiled sources" CACHE STRING "Indicate the distribution in bug reports" )
# win32: give kde home in debug mode a different name as the release home dir because the settings and caches are different
if (WIN32 AND CMAKE_BUILD_TYPE STREQUAL "Debug")
set (_KDE_DEFAULT_HOME_POSTFIX "-debug" CACHE STRING "default KDE home directory postfix" )
endif (WIN32 AND CMAKE_BUILD_TYPE STREQUAL "Debug")
set (KDE_DEFAULT_HOME ".kde${_KDE_DEFAULT_HOME_POSTFIX}" CACHE STRING "The default KDE home directory" )
# this must be before FindKDE4Internal in order to preset the result of the visibility test, so that it will be skipped
option(KHTML_BUILD_TESTREGRESSION "Build KHTML's testregression. Note: this disables hidden visibility")
# Disable visibility if testregression is built, because the symbols are needed then
if (KHTML_BUILD_TESTREGRESSION)
set (__KDE_HAVE_GCC_VISIBILITY 0)
endif (KHTML_BUILD_TESTREGRESSION)
option(STATIC_LIBRARY "Build kdelibs as static libraries." FALSE)
option(QT5_BUILD "Build kdelibs against Qt5." FALSE)
find_package(extra-cmake-modules 0.0.2 REQUIRED)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${EXTRA_CMAKE_MODULES_MODULE_PATH})
if (NOT QT5_BUILD)
add_subdirectory( libinqt5 )
set(INQT5_LIBRARY inqt5)
include_directories(
${CMAKE_SOURCE_DIR}/libinqt5/src/
${CMAKE_BINARY_DIR}/libinqt5/src/
)
endif()
add_subdirectory( libqtmimetypes )
add_subdirectory( tier1 )
set(CMAKE_AUTOMOC ON)
# By default don't add any linked libraries to the "exported"
# link interfaces of shared libraries, so that executables linking
# against these libraries will not automatically add implicit
# dependencies to their link list.
#
# This reduces inter-package dependencies and makes it easier to remove
# dependencies of shared libraries without breaking binary compatibility.
set(CMAKE_LINK_INTERFACE_LIBRARIES "")
################# write platform profile file which will be installed #################
include(CreateKDEPlatformProfile.cmake)
if(KDE_PLATFORM_FEATURE_DISABLE_DEPRECATED)
set(KDE_NO_DEPRECATED TRUE)
endif(KDE_PLATFORM_FEATURE_DISABLE_DEPRECATED)
############### Load the CTest options ###############
# CTestCustom.cmake has to be in the CTEST_BINARY_DIR.
# in the KDE build system, this is the same as CMAKE_BINARY_DIR.
configure_file(${CMAKE_SOURCE_DIR}/CTestCustom.cmake ${CMAKE_BINARY_DIR}/CTestCustom.cmake COPYONLY)
################# now find all used packages #################
set (QT_MIN_VERSION "4.7.0")
find_package(KDE4Internal REQUIRED)
include(KDE4Defaults)
include (MacroLibrary)
if (APPLE)
find_package(Carbon REQUIRED)
endif (APPLE)
if(UNIX AND Q_WS_X11)
#X11 Session Management (SM) is required
#X11_SM_FOUND is set in FindX11, which is required by KDE4Internal
if(NOT X11_SM_FOUND)
message(FATAL_ERROR "\nThe X11 Session Management (SM) development package could not be found.\nPlease install libSM.\n")
endif(NOT X11_SM_FOUND)
endif(UNIX AND Q_WS_X11)
#required features:
# Perl is used e.g. in khtml, kjs, kjsembed and others
find_package(Perl)
macro_log_feature(PERL_FOUND "Perl" "Needed for building kdelibs" "http://www.perl.org" TRUE "" "")
find_package(ZLIB)
macro_log_feature(ZLIB_FOUND "ZLib" "Support for gzip compressed files and data streams" "http://www.zlib.net" TRUE "" "Required by the core KDE libraries and some critical kioslaves")
set(STRIGI_MIN_VERSION 0.6.3)
find_package(Strigi)
if (WIN32)
set (STRIGI_REQUIRED FALSE)
set (STRIGI_EXTRA_TEXT "")
else (WIN32)
set (STRIGI_REQUIRED TRUE)
set (STRIGI_EXTRA_TEXT "Required by some critical kioslaves")
endif (WIN32)
macro_log_feature(STRIGI_FOUND "Strigi" "Desktop indexing and search support" "http://strigi.sourceforge.net" ${STRIGI_REQUIRED} "${STRIGI_MIN_VERSION}" ${STRIGI_EXTRA_TEXT})
set(LIBATTICA_MIN_VERSION "0.1.90")
find_package(LibAttica)
macro_log_feature(LIBATTICA_FOUND "libattica" "Support for Get Hot New Stuff" "git clone git://anongit.kde.org/attica" TRUE "${LIBATTICA_MIN_VERSION}" "")
#optional features
if(X11_FOUND)
#X11_Xrender discovery is done by FindX11
macro_log_feature(X11_Xrender_FOUND "X Rendering Extension (libXrender)" "Support for compositing, rendering operations, and alpha-blending" "http://www.x.org" FALSE "" "STRONGLY RECOMMENDED")
macro_bool_to_01(X11_Xscreensaver_FOUND HAVE_XSCREENSAVER)
macro_bool_to_01(X11_XSync_FOUND HAVE_XSYNC)
macro_log_feature(HAVE_XSCREENSAVER "X Screensaver Extension (libXss)" "Support for KIdleTime (fallback mode)" "http://www.x.org/" FALSE "" "")
macro_log_feature(HAVE_XSYNC "X Sync Extension (libXext)" "Efficient operation of KIdleTime" "http://www.x.org/" FALSE "" "STRONGLY RECOMMENDED")
if(NOT HAVE_XSYNC AND NOT HAVE_XSCREENSAVER)
message(FATAL_ERROR "\nNeither the XSync (libXext) nor XScreensaver (libXss) development package was found.\nPlease install one of them (XSync is recommended)\n")
endif(NOT HAVE_XSYNC AND NOT HAVE_XSCREENSAVER)
endif(X11_FOUND)
find_package(OpenSSL)
macro_log_feature(OPENSSL_FOUND "OpenSSL" "Support for secure network communications (SSL and TLS)" "http://openssl.org" FALSE "" "STRONGLY RECOMMENDED: KDE uses OpenSSL for the bulk of secure communications, including secure web browsing via HTTPS")
find_package(Libintl)
macro_log_feature(LIBINTL_FOUND "Libintl" "Support for multiple languages" "http://www.gnu.org/software/gettext" FALSE "" "STRONGLY RECOMMENDED: Enables KDE to be available in many different languages")
find_package(QCA2)
macro_log_feature(QCA2_FOUND "QCA2" "Support for remote plasma widgets" "http://delta.affinix.com/qca" FALSE "2.0.0" "")
find_package(DBusMenuQt)
-macro_log_feature(DBUSMENUQT_FOUND "DBusMenuQt" "Support for notification area menus via the DBusMenu protocol" "git clone git://gitorious.org/dbusmenu/dbusmenu-qt.git" TRUE "" "")
+macro_log_feature(DBUSMENUQT_FOUND "DBusMenuQt" "Support for notification area menus via the DBusMenu protocol" "https://launchpad.net/libdbusmenu-qt" TRUE "" "")
################# Disallow in-source build #################
macro_ensure_out_of_source_build("kdelibs requires an out of source build. Please create a separate build directory and run 'cmake path_to_kdelibs [options]' there.")
# ... and warn in case of an earlier in-source build
set(generatedFileInSourceDir EXISTS ${kdelibs_SOURCE_DIR}/kdemacros.h OR EXISTS ${kdelibs_SOURCE_DIR}/config.h)
if(${generatedFileInSourceDir})
message(STATUS "kdemacros.h or config.h exists in your source directory.")
message(FATAL_ERROR "Please run svn-clean, it would seem that your source directory has generated files in it.")
endif(${generatedFileInSourceDir})
#########################################################################
add_definitions(${QT_DEFINITIONS} ${KDE4_DEFINITIONS})
remove_definitions(-DQT3_SUPPORT_WARNINGS -DQT3_SUPPORT)
add_definitions(-DQT_USE_FAST_CONCATENATION -DQT_USE_FAST_OPERATOR_PLUS)
################# setup the include directories #################
# for including config.h and for includes like <kparts/foo.h>
include_directories( ${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR}/interfaces)
if(QCA2_FOUND)
include_directories(
${QCA2_INCLUDE_DIR}
)
endif(QCA2_FOUND)
# Those variables for are only valid inside of kdelibs, of course.
# Use the one variable for the lib you depend upon.
# E.g. kdeui uses ${KDE4_KDECORE_INCLUDES}. Something that depends on kparts uses ${KDE4_KPARTS_INCLUDES}.
set(KDE4_KJS_INCLUDES ${CMAKE_SOURCE_DIR}/kjs
${CMAKE_BINARY_DIR}/kjs)
if(NOT WINCE)
set(KDE4_KDECORE_INCLUDES ${KDE4_KJS_INCLUDES} )
endif(NOT WINCE)
set(KDE4_KCONFIG_INCLUDES ${CMAKE_SOURCE_DIR}/kdecore/config
${CMAKE_SOURCE_DIR}/staging/kcoreaddons/src/io # for kurl.h
${CMAKE_BINARY_DIR}/staging/kcoreaddons/src
)
# kdecore depends on Qt and kcoreaddons (need only headers from kjs)
set(KDE4_KDECORE_INCLUDES ${KDE4_KDECORE_INCLUDES}
${CMAKE_SOURCE_DIR}/kdecore
${CMAKE_BINARY_DIR}/kdecore
${CMAKE_SOURCE_DIR}/kdecore/compression
${CMAKE_SOURCE_DIR}/kdecore/config
${CMAKE_SOURCE_DIR}/kdecore/date
${CMAKE_SOURCE_DIR}/kdecore/io
${CMAKE_SOURCE_DIR}/kdecore/kernel
${CMAKE_SOURCE_DIR}/kdecore/network
${CMAKE_SOURCE_DIR}/kdecore/services
${CMAKE_SOURCE_DIR}/kdecore/localization
${CMAKE_SOURCE_DIR}/kdecore/sycoca
${CMAKE_SOURCE_DIR}/kdecore/text
${CMAKE_SOURCE_DIR}/kdecore/util
${CMAKE_SOURCE_DIR}/kdecore/sonnet
${CMAKE_SOURCE_DIR}/staging/kcoreaddons/src/io
${CMAKE_SOURCE_DIR}/staging/kcoreaddons/src/kernel
${CMAKE_SOURCE_DIR}/staging/kcoreaddons/src/jobs
${CMAKE_SOURCE_DIR}/staging/kcoreaddons/src/text
${CMAKE_BINARY_DIR}/staging/kcoreaddons/src
${CMAKE_BINARY_DIR}/staging/kde4support/src
${CMAKE_SOURCE_DIR}/staging/kde4support/src
${CMAKE_SOURCE_DIR}/libqtmimetypes/include/QtMimeTypes
${QT_INCLUDES}
${_KDE4_PLATFORM_INCLUDE_DIRS})
# kdeui depends on kdecore
set(KDE4_KDEUI_INCLUDES ${CMAKE_SOURCE_DIR}/kdeui
${CMAKE_SOURCE_DIR}/kdeui/actions
${CMAKE_SOURCE_DIR}/kdeui/colors
${CMAKE_SOURCE_DIR}/kdeui/config
${CMAKE_SOURCE_DIR}/kdeui/dialogs
${CMAKE_SOURCE_DIR}/kdeui/findreplace
${CMAKE_SOURCE_DIR}/kdeui/fonts
${CMAKE_SOURCE_DIR}/kdeui/icons
${CMAKE_SOURCE_DIR}/kdeui/itemviews
${CMAKE_SOURCE_DIR}/kdeui/jobs
${CMAKE_SOURCE_DIR}/kdeui/kernel
${CMAKE_SOURCE_DIR}/kdeui/notifications
${CMAKE_SOURCE_DIR}/kdeui/paged
${CMAKE_SOURCE_DIR}/kdeui/plotting
${CMAKE_SOURCE_DIR}/kdeui/shortcuts
${CMAKE_SOURCE_DIR}/kdeui/sonnet
${CMAKE_SOURCE_DIR}/kdeui/util
${CMAKE_SOURCE_DIR}/kdeui/widgets
${CMAKE_SOURCE_DIR}/kdeui/windowmanagement
${CMAKE_SOURCE_DIR}/kdeui/xmlgui
${KDE4_KDECORE_INCLUDES})
# kio depends on kdeui
set(KDE4_KIO_INCLUDES ${CMAKE_SOURCE_DIR}/kio
${CMAKE_SOURCE_DIR}/kio/bookmarks
${CMAKE_SOURCE_DIR}/kio/kio
${CMAKE_SOURCE_DIR}/kio/kfile
${KDE4_KDEUI_INCLUDES})
# kpty
set(KDE4_KPTY_INCLUDES ${CMAKE_SOURCE_DIR}/kpty ${KDE4_KIO_INCLUDES} )
# kparts depends on kio
set(KDE4_KPARTS_INCLUDES ${CMAKE_SOURCE_DIR}/kparts
${KDE4_KIO_INCLUDES})
# kde3support depends on kparts
set(KDE4_KDE3SUPPORT_INCLUDES ${CMAKE_SOURCE_DIR}/kde3support
${CMAKE_SOURCE_DIR}/kde3support/kdecore
${CMAKE_SOURCE_DIR}/kde3support/kdeui
${CMAKE_SOURCE_DIR}/kde3support/kio
${KDE4_KPARTS_INCLUDES})
if(NOT WINCE)
set(KDE4_KHTML_INCLUDES ${CMAKE_SOURCE_DIR}/khtml)
endif(NOT WINCE)
################# configure checks and create the configured files #################
if(WINCE)
set(STATIC_LIBRARY ON)
add_definitions(-DSTATIC_INSTALL_PATH=L\\\"/programme/kde\\\")
endif(WINCE)
if(STATIC_LIBRARY)
set(LIBRARY_TYPE STATIC)
add_definitions(-DKDELIBS_STATIC_LIBS)
message(STATUS "Building kdelibs as static libraries")
else(STATIC_LIBRARY)
set(LIBRARY_TYPE SHARED)
endif(STATIC_LIBRARY)
# ACL stuff (used in kio/ and kioslaves/)
find_package(ACL)
macro_bool_to_01(ACL_FOUND HAVE_LIBACL HAVE_POSIX_ACL)
macro_log_feature(ACL_FOUND "LibACL" "Support for manipulating access control lists" "ftp://oss.sgi.com/projects/xfs/cmd_tars" FALSE "" "STRONGLY RECOMMENDED")
configure_file(config-acl.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-acl.h )
include(ConfigureChecks.cmake)
# now create config headers
configure_file(config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.h )
configure_file(config-prefix.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-prefix.h )
configure_file(config-compiler.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-compiler.h )
configure_file(config-pty.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-pty.h )
configure_file(kdemacros.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/kdemacros.h )
# these two calls here should go somewhere else, Alex
check_library_exists(nsl gethostbyname "" HAVE_NSL_LIBRARY)
check_library_exists(socket connect "" HAVE_SOCKET_LIBRARY)
# TODO: Port away from this:
add_definitions(-DKDE_NO_WINDOWSYSTEM)
################# list the subdirectories #################
add_subdirectory( cmake )
add_subdirectory( kdecore )
add_subdirectory( staging )
add_subdirectory( tier2 )
if (UNIX)
add_subdirectory( kpty )
add_subdirectory( kdesu )
endif (UNIX)
if(NOT WINCE)
add_subdirectory( kjs )
add_subdirectory( kjsembed )
endif(NOT WINCE)
add_subdirectory( kconf_update )
add_subdirectory( licenses )
add_subdirectory( mimetypes )
add_subdirectory( kdeui )
add_subdirectory( dnssd )
add_subdirectory( security )
add_subdirectory( kio )
add_subdirectory( kded )
if (QT_QT3SUPPORT_FOUND)
add_subdirectory( kde3support )
endif (QT_QT3SUPPORT_FOUND)
add_subdirectory( kfile )
if(NOT WINCE)
add_subdirectory( kdoctools )
endif(NOT WINCE)
add_subdirectory( kioslave )
add_subdirectory( kparts )
add_subdirectory( kunitconversion )
if(NOT WINCE)
# TO BE SPLITTED OUT add_subdirectory( khtml )
endif(NOT WINCE)
add_subdirectory( interfaces )
add_subdirectory( kdewidgets )
if (QT5_BUILD)
# This should be moved down and eventually removed when everything
# can be built with Qt5.
# The current problem is kdeui because it uses X11 stuff which is no longer in Qt.
return()
endif()
add_subdirectory( knewstuff )
add_subdirectory( kutils )
add_subdirectory( kinit )
add_subdirectory( knotify )
if(NOT WINCE)
add_subdirectory( kimgio )
endif(NOT WINCE)
add_subdirectory( kross )
find_package(KdepimLibs 4.5.60)
macro_log_feature(KDEPIMLIBS_FOUND "kdepimlibs" "KDE PIM libraries" "http://www.kde.org" FALSE "" "Needed for building several Plasma DataEngines")
# Plasma needs gpgme++
if(KDEPIMLIBS_FOUND AND NOT WINCE)
add_subdirectory( plasma )
endif()
add_subdirectory( kdewebkit )
add_subdirectory( includes )
add_subdirectory( experimental )
macro_optional_add_subdirectory( doc )
################# write dependency file which will be installed #################
# Used in configure_file() and install(EXPORT)
set(KDE4_TARGET_PREFIX KDE4__)
include(CreateKDELibsDependenciesFile.cmake)
################# install files #################
install( FILES ${CMAKE_CURRENT_BINARY_DIR}/kdemacros.h DESTINATION ${INCLUDE_INSTALL_DIR} )
install( FILES ${CMAKE_CURRENT_BINARY_DIR}/KDELibsDependencies.cmake DESTINATION ${DATA_INSTALL_DIR}/cmake/modules)
install( FILES ${CMAKE_CURRENT_BINARY_DIR}/KDEPlatformProfile.cmake DESTINATION ${DATA_INSTALL_DIR}/cmake/modules)
# run a script before installing the exports files which deletes previously installed
# configuration specific export files KDELibs4(Library|Tools)Targets-<config>.cmake
# if the main exports file KDELibs4(Library|Tools)Targets.cmake has changed. This makes sure
# that this main file doesn't include older and different configuration specific exports files,
# which might have a different set of targets or targets with different names.
# The code for installing the exports files will soon go into a macro. Alex
install(CODE "set(EXPORT_FILES KDELibs4LibraryTargets.cmake KDELibs4ToolsTargets.cmake)"
CODE "set(EXPORT_INSTALL_DIR \"${DATA_INSTALL_DIR}/cmake/modules\")"
SCRIPT "${CMAKE_SOURCE_DIR}/cmake/modules/check_installed_exports_file.cmake" )
# No longer applicable, replaced with individual frameworks
# install( EXPORT kdelibsToolsTargets DESTINATION ${DATA_INSTALL_DIR}/cmake/modules NAMESPACE ${KDE4_TARGET_PREFIX} FILE KDELibs4ToolsTargets.cmake )
# the following will be the correct locations once cmake has the improved FIND_PACKAGE()
# install( FILES ${CMAKE_CURRENT_BINARY_DIR}/KDELibsDependencies.cmake DESTINATION ${PLUGIN_INSTALL_DIR}/cmake RENAME KDE4Config.cmake)
feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES)
macro_display_feature_log()
diff --git a/Mainpage.dox b/Mainpage.dox
index 120cff9428..3f26c1042e 100644
--- a/Mainpage.dox
+++ b/Mainpage.dox
@@ -1,218 +1,218 @@
/** @mainpage The KDE Library API Reference
<p><b>
Overview |
@ref components |
@ref development |
@ref search
</b></p>
This is the online reference for developing with the KDE libraries
(kdelibs).
The KDE libraries build on the
<a href="http://www.trolltech.com/products/qt/">Qt</a> framework to
provide a powerful framework to make writing applications easier,
and provide consistency across the KDE desktop environment.
Among other things, the KDE libraries provide:
- standard user interface elements, on top of those provided by Qt
(<a href="kdeui/html/index.html">KDEUI</a>)
- a standard configuration format and method of reading and writing
configuration data (<a href="kdecore/html/classKConfig.html">KConfig</a>)
- site-independent access to standard directories, for finding resources such
as icons (<a href="kdecore/html/classKStandardDirs.html">KStandardDirs</a>)
- network transparent input and output (<a href="kio/html/index.html">KIO</a>)
- a method of embedding application components in other applications
(<a href="kparts/html/index.html">KParts</a>)
- straightforward multimedia and hardware interaction
- (<a href="/kdesupport-api/kdesupport-apidocs/phonon-git/html/">Phonon</a> and
+ (<a href="/kdesupport-api/phonon-apidocs/">Phonon</a> and
<a href="solid/html/index.html">Solid</a>)
- fully-fledged JavaScript and HTML engines
(<a href="kjs/html/index.html">KJS</a> and
<a href="khtml/html/index.html">KHTML</a>)
- an application scripting framework
(<a href="kross/html/index.html">Kross</a>)
- semantic information and tagging
(<a href="nepomuk/html/index.html">Nepomuk</a>)
Visit the <a href="http://techbase.kde.org/Development">development section of KDE
Techbase</a> for tutorials, architectural overviews and other useful information.
@authors
Far too many to list. See the copyright notices on the individual files,
as well as the pages for individual components.
@maintainers
There is no single maintainer for kdelibs. Overall direction is governed by the
<a href="https://mail.kde.org/mailman/listinfo/kde-core-devel">kde-core-devel
mailing list</a>. Some of the individual components have their own maintainers:
see the component pages for more information.
@licenses
Libraries: @lgpl<br>
Some helper binaries: @gpl<br>
Some code may have more permissive licenses.
*/
/** @page components Components
<p><b>
@ref index "Overview" |
Components |
@ref development |
@ref search
</b></p>
Below is a brief summary of the various libraries you may want to use
when developing a KDE application.
Note that there is more to kdelibs than these libraries: on the left
is a complete list of kdelibs components. However, in almost all cases
you will only be interested in the ones listed below. The remaining
ones are mostly implementation details that you don't need to worry
about.
There are also several components you can use. Interfaces to most of
these can be found in the <a href="interfaces/html/index.html">interfaces
section</a>, but you should also be aware of KHTMLPart, which
provides a full web browser component.
<dl>
<dt>KDECore</dt>
<dd>Core KDE classes that are not related to the user interface.<br>
[ <a href="kdecore/html/index.html">Documentation</a> |
<a href="kdecore/html/classes.html">Classes</a> ]</dd>
<dt>KIO</dt>
<dd>KDE's network-transparent input/output system: provides
just about every file-management function you'll ever need.<br>
[ <a href="kio/html/index.html">Documentation</a> |
<a href="kio/html/classes.html">Classes</a> ]</dd>
<dt>KDEUI</dt>
<dd>KDE User Interface classes such as widgets.<br>
[ <a href="kdeui/html/index.html">Documentation</a> |
<a href="kdeui/html/classes.html">Classes</a> ]</dd>
<dt>Phonon</dt>
<dd>KDE's multimedia framework.<br>
[ <a href="/kdesupport-api/kdesupport-apidocs/phonon/html/index.html">Documentation</a> |
<a href="/kdesupport-api/kdesupport-apidocs/phonon/html/group__Frontend.html">Classes</a> ]</dd>
<dt>Solid</dt>
<dd>KDE Hardware Discovery and Management classes.<br>
[ <a href="solid/html/index.html">Documentation</a> |
<a href="solid/html/classes.html">Classes</a> ]</dd>
<dt>Nepomuk</dt>
<dd>The KDE Meta Data library, for metadata assigned to a
resource (such as a file or an email) by the user or
by an application, such as tags or download locations.<br>
[ <a href="nepomuk/html/index.html">Documentation</a> |
<a href="nepomuk/html/classes.html">Classes</a> ]</dd>
<dt>KParts</dt>
<dd>The KDE component library: provides support for re-usable,
embeddable, extendible components for use in applications.<br>
[ <a href="kparts/html/index.html">Documentation</a> |
<a href="kparts/html/classes.html">Classes</a> ]</dd>
<dt>KNewStuff</dt>
<dd>Upload and download of application data.<br>
[ <a href="knewstuff/html/index.html">Documentation</a> |
<a href="knewstuff/html/classes.html">Classes</a> ]</dd>
<dt>KDEPrint</dt>
<dd>The KDE printing system.<br>
[ <a href="kdeprint/html/index.html">Documentation</a> |
<a href="kdeprint/html/classes.html">Classes</a> ]</dd>
<dt>DNSSD</dt>
<dd>Access to the DNSSD (aka Bonjour, Zeroconf) service publishing
and discovery protocol.<br>
[ <a href="dnssd/html/index.html">Documentation</a> |
<a href="dnssd/html/classes.html">Classes</a> ]</dd>
<dt>KJS</dt>
<dd>JavaScript (aka. ECMAScript and JScript) support.<br>
[ <a href="kjs/html/index.html">Documentation</a> |
<a href="kjs/html/classes.html">Classes</a> ]</dd>
<dt>Kross</dt>
<dd>Allows scripting to be added to an application in an
interpreter-independent way.<br>
[ <a href="kross/html/index.html">Documentation</a> |
<a href="kross/html/classes.html">Classes</a> ]</dd>
<dt>ThreadWeaver</dt>
<dd>A job-based multi-threading library<br>
[ <a href="threadweaver/html/index.html">Documentation</a> |
<a href="threadweaver/html/classes.html">Classes</a> ]</dd>
<dt>KDESU</dt>
<dd>Run processes remotely or with elevated privileges.<br>
[ <a href="kdesu/html/index.html">Documentation</a> |
<a href="kdesu/html/classes.html">Classes</a> ]</dd>
<dt>KPty</dt>
<dd>A library for interfacing with pseudo terminals.<br>
[ <a href="kpty/html/index.html">Documentation</a> |
<a href="kpty/html/classes.html">Classes</a> ]</dd>
</dl>
*/
/** @page development Development
<p><b>
@ref index "Overview" |
@ref components |
Development |
@ref search
</b></p>
To follow or get involved with the development of the KDE libraries,
join the <a href="https://mail.kde.org/mailman/listinfo/kde-core-devel">kde-core-devel
mailing list</a>.
Instructions for building a development version of KDE can be found in the
<a href="http://techbase.kde.org/Getting_Started">getting started</a> section
of the <a href="http://techbase.kde.org">KDE Techbase</a>.
*/
/** @page search Search
<p><b>
@ref index "Overview" |
@ref components |
@ref development |
Search
</b></p>
You can search for any class in the public API of the KDE libraries
here.
Note: only enter the class name, without any namespace qualifiers.
@htmlonly
<form action="http://api.kde.org/classmapper.php" method="get">
<input type="text" name="class" value="[classname]" style="width:100%;" onClick="this.value='';"/>
<input type="submit" name="go" value="Go" />
<input type=hidden name="module" value="kdelibs">
<input type=hidden name="version" value="4.7">
</form>
@endhtmlonly
If you want to search for any sort of identifier in kdelibs (and other code
in the KDE SVN repository), you can do so using the
<a href="http://lxr.kde.org">KDE Source Cross-Reference</a> tool.
*/
// DOXYGEN_NAME=kdelibs
// DOXYGEN_ENABLE=YES
// vim:ts=4:sw=4:expandtab:filetype=doxygen
diff --git a/cmake/modules/FindXine.cmake b/cmake/modules/FindXine.cmake
index 0319b7d568..7dd08be8df 100644
--- a/cmake/modules/FindXine.cmake
+++ b/cmake/modules/FindXine.cmake
@@ -1,71 +1,77 @@
# - Try to find the XINE library
# Once done this will define
#
# XINE_FOUND - system has the XINE library
# XINE_VERSION - XINE version
# XINE_BUGFIX_VERSION - the XINE bugfix version
# XINE_INCLUDE_DIR - the XINE include directory
# XINE_LIBRARY - The libraries needed to use XINE
# XINE_XCB_FOUND - libxine can use XCB for video output
# Copyright (c) 2006,2007 Laurent Montel, <montel@kde.org>
# Copyright (c) 2006, Matthias Kretz, <kretz@kde.org>
#
# Redistribution and use is allowed according to the terms of the BSD license.
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
# Support XINE_MIN_VERSION for compatibility:
IF(NOT Xine_FIND_VERSION)
SET(Xine_FIND_VERSION "${XINE_MIN_VERSION}")
ENDIF(NOT Xine_FIND_VERSION)
# the minimum version of xine we require
IF(NOT Xine_FIND_VERSION)
SET(Xine_FIND_VERSION "1.1.0")
ENDIF(NOT Xine_FIND_VERSION)
FIND_PACKAGE(PkgConfig)
PKG_CHECK_MODULES(PC_LIBXINE QUIET libxine)
SET(XINE_DEFINITIONS ${PC_XINE_CFLAGS_OTHER})
FIND_PATH(XINE_INCLUDE_DIR NAMES xine.h
HINTS
${PC_LIBXINE_INCLUDEDIR}
${PC_LIBXINE_INCLUDE_DIRS}
)
FIND_LIBRARY(XINE_LIBRARY NAMES xine
HINTS
${PC_LIBXINE_LIBDIR}
${PC_LIBXINE_LIBRARY_DIRS}
)
FIND_PROGRAM(XINECONFIG_EXECUTABLE NAMES xine-config
HINTS
${PC_LIBXINE_PREFIX}/bin
)
# Get the version number from xine.h and store it in the cache:
IF(XINE_INCLUDE_DIR AND NOT XINE_VERSION)
- FILE(READ ${XINE_INCLUDE_DIR}/xine.h XINE_VERSION_CONTENT)
+ IF(EXISTS ${XINE_INCLUDE_DIR}/xine/version.h) # xine 1.2.0+
+ SET(XINE_VERSION_FILE ${XINE_INCLUDE_DIR}/xine/version.h)
+ ELSE(EXISTS ${XINE_INCLUDE_DIR}/xine/version.h)
+ SET(XINE_VERSION_FILE ${XINE_INCLUDE_DIR}/xine.h)
+ ENDIF(EXISTS ${XINE_INCLUDE_DIR}/xine/version.h)
+
+ FILE(READ ${XINE_VERSION_FILE} XINE_VERSION_CONTENT)
STRING(REGEX MATCH "#define *XINE_MAJOR_VERSION *([0-9]+)" _dummy "${XINE_VERSION_CONTENT}")
SET(XINE_VERSION_MAJOR "${CMAKE_MATCH_1}")
STRING(REGEX MATCH "#define *XINE_MINOR_VERSION *([0-9]+)" _dummy "${XINE_VERSION_CONTENT}")
SET(XINE_VERSION_MINOR "${CMAKE_MATCH_1}")
STRING(REGEX MATCH "#define *XINE_SUB_VERSION *([0-9]+)" _dummy "${XINE_VERSION_CONTENT}")
SET(XINE_VERSION_PATCH "${CMAKE_MATCH_1}")
SET(XINE_VERSION "${XINE_VERSION_MAJOR}.${XINE_VERSION_MINOR}.${XINE_VERSION_PATCH}" CACHE STRING "Version number of Xine" FORCE)
IF("${XINE_VERSION}" VERSION_GREATER "${Xine_FIND_VERSION}")
STRING(REGEX REPLACE "[0-9]\\.[0-9]\\." "" XINE_BUGFIX_VERSION ${XINE_VERSION})
ENDIF("${XINE_VERSION}" VERSION_GREATER "${Xine_FIND_VERSION}")
ENDIF(XINE_INCLUDE_DIR AND NOT XINE_VERSION)
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(Xine REQUIRED_VARS XINE_INCLUDE_DIR XINE_LIBRARY XINECONFIG_EXECUTABLE
VERSION_VAR XINE_VERSION)
MARK_AS_ADVANCED(XINE_INCLUDE_DIR XINE_LIBRARY XINECONFIG_EXECUTABLE)
diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt
index 770b7a0bd7..2f88a3ab7b 100644
--- a/doc/CMakeLists.txt
+++ b/doc/CMakeLists.txt
@@ -1,20 +1,22 @@
add_subdirectory( common )
add_subdirectory( kioslave )
if (NOT WIN32)
add_subdirectory( checkXML )
add_subdirectory( kbuildsycoca4 )
add_subdirectory( kcookiejar4 )
add_subdirectory( kdeinit4 )
add_subdirectory( kdeoptions )
add_subdirectory( makekdewidgets )
add_subdirectory( meinproc4 )
add_subdirectory( sonnet )
add_subdirectory( qtoptions )
add_subdirectory( kde4-config )
add_subdirectory( kjs )
add_subdirectory( kjscmd )
add_subdirectory( kross )
add_subdirectory( kded4 )
+ add_subdirectory( kconfig_compiler )
+ add_subdirectory( preparetips )
endif (NOT WIN32)
diff --git a/doc/common/kde-docs.css b/doc/common/kde-docs.css
index 93ecd54256..390fc0eaaa 100644
--- a/doc/common/kde-docs.css
+++ b/doc/common/kde-docs.css
@@ -1,335 +1,335 @@
/*
* kde-docs.css -- Style sheets for the KDE documentation generated
* HTML.
*
* Started by Michael Pyne <mpyne@kde.org>
*/
html, body {
padding: 0;
width: 100%;
height: 100%;
}
/* These two divs force the content to fill up at least the viewport, which
* is needed in order to force the footer to the bottom of short pages.
*/
#content {
min-height: 100%;
position: relative;
}
#contentBody {
padding: 0;
- padding-bottom: 8.9em;
+ padding-bottom: 1em;
margin: 0;
width: 100%;
}
/* This is for the header's navigation bar */
#content > .navCenter {
background: #F8F8F8;
border-bottom: 1px solid #DDD;
}
/* Standard nav bar elements */
div.navCenter table {
empty-cells: show;
border: 0px;
width: 100%;
}
div.navCenter td {
font-weight: normal;
}
div.navCenter td.prevCell {
padding-left: 20px;
text-align: left;
}
div.navCenter td.nextCell {
padding-right: 20px;
text-align: right;
}
div.navCenter td.upCell {
text-align: center;
}
/* Actual documentation styling */
div.table table {
text-align: left; /* Disable justification */
border: 1px solid black;
border-collapse: collapse;
}
/* Give alternating row colors */
div.table tr:nth-child(odd) {
background-color: #eee;
}
div.table tr:nth-child(even) {
background-color: white;
}
div.table th {
background-color: white;
font-weight: normal;
text-align: center;
vertical-align: middle;
border-bottom: 1px solid black;
}
div.table td {
text-align: left;
padding: 4px;
}
div.tip, div.note, div.warning, div.important {
padding: 6pt;
padding-top: 3pt;
}
div.tip > .title, div.warning > .title, div.note > .title, div.important > .title {
padding: 4pt 1cm;
margin: 0;
margin-top: 4pt;
}
div.tip {
border: 1px solid #EEF;
border-left: 1px solid #88F;
background: #EFEFFF;
}
div.tip > .title {
border: 1px solid #88F;
background: #CFCFFF;
color: #222;
}
div.important {
border: 1px solid #FF2;
border-left: 1px solid #888;
background: #FF7;
}
div.important > .title {
border: 1px solid #836900;
background: #FC0;
color: #222;
}
div.note {
border: 1px solid #EEE;
border-left: 1px solid #888;
background: #EFEFEF;
}
div.note > .title {
border: 1px solid #888;
background: #CFCFCF;
color: #222;
}
div.warning {
border: 1px solid #FEE;
border-left: 1px solid #F88;
background: #FFEFEF;
margin-bottom: 6pt;
}
div.warning > .title {
border: 1px solid #F88;
background: #FFAFAF;
color: #222;
}
/* Make the content wrapping div have a nice margin. */
body div.chapter, body div.sect1, body div.book, body div.article {
margin-left: 2em;
margin-right: 2em;
}
div.sect2 {
width: 100%;
}
.programlisting, pre.screen {
-khtml-border-radius: 7px;
/* This will work someday */
border-radius: 7px;
/* To support border radius on our OSS browser friends when
* viewing online */
-webkit-border-radius: 7px;
-moz-border-radius: 7px;
}
.programlisting {
border: 1px solid black;
background: white;
}
.guimenu, .guimenuitem, .guisubmenu,
.guilabel, .interface, .guibutton {
background-color: rgb(220, 220, 220);
color: black;
border: 1px solid rgb(190, 190, 190);
-khtml-border-radius: 3px;
-moz-border-radius: 3px;
-webkit-border-radius: 3px;
border-radius: 3px;
}
.shortcut {
background-color: #DDF;
border: 1px dotted #BBF;
font-weight: normal;
-khtml-border-radius: 2px;
-moz-border-radius: 2px;
-webkit-border-radius: 2px;
border-radius: 2px;
}
.keycap {
background-color: #DFDFFF;
font-weight: bold;
-khtml-border-radius: 2px;
-moz-border-radius: 2px;
-webkit-border-radius: 2px;
border-radius: 2px;
}
pre.screen {
border 2px solid gray;
background: black;
color: white;
font-weight: normal;
font-family: monospace;
}
/* Make a screen black on white */
pre.screen * {
color: white;
background: black;
font-weight: normal;
font-family: monospace;
}
pre.screen .userinput {
color: green;
}
.informalexample {
margin: 0px;
padding: 0px;
border: 0px;
border-left: 1px dotted black;
padding-left: 4px;
}
div.tip {
margin-bottom: 12pt;
}
div.tip:last-child() {
margin-bottom: 0pt;
}
pre:last-child() {
margin-bottom: 0px;
}
div.titlepage {
margin-left: 0px;
}
h3.title {
margin-left: 0cm;
}
/*
* This gives us the styling for the header and footer.
* See customization/kde-navig.xsl for where it's used.
*/
#header {
width: 100%;
height: 51px;
color: #535353;
background: #eeeeee;
border-bottom: #bcbcbc 1px solid;
}
#header_content {
margin: 0 auto;
width: 80%;
height: 51px;
background: url("top.jpg") top left repeat-x;
}
#header_right img {
position: relative;
top: 8px; /* Vertically center */
}
#header_left {
background: url("top-left.jpg") top left no-repeat;
padding-left: 20px;
height: 51px;
}
#header_right {
background: url("top-right.jpg") top right no-repeat;
color: white;
padding-right: 20px;
height: 51px;
text-align: left;
font-size: 18pt;
font-weight: bold;
vertical-align: middle;
text-shadow: #112 2px 2px 2px;
white-space: nowrap;
overflow: hidden;
line-height: 51px;
}
/* Used for the text and footer area at the bottom. */
#footer {
width: 100%;
background-color: #eeeeee;
border: 0px;
/* Force footer to bottom of viewport/page */
/* Either should be position:fixed to stay always at the bottom of the viewport, or
removed to be at the bottom of the page. I chose bottom of the page.
position: absolute; */
bottom: 0;
height: 8.7em;
}
#footer_text {
text-align:center;
vertical-align: middle;
padding-top: 12pt;
}
#footer .navCenter {
border-top: 1px solid #DDD;
border-bottom: 1px solid #AAA;
}
/* Two rows on this navCenter, so make the cells equal width */
#footer .navCenter td {
width: 33%;
}
a.footer_email {
color: #282828;
text-decoration: underline;
}
diff --git a/doc/common/kmenu.png b/doc/common/kmenu.png
new file mode 100644
index 0000000000..ff077c225a
Binary files /dev/null and b/doc/common/kmenu.png differ
diff --git a/doc/kioslave/mailto/index.docbook b/doc/kioslave/mailto/index.docbook
index cb9306958e..3e25d73df9 100644
--- a/doc/kioslave/mailto/index.docbook
+++ b/doc/kioslave/mailto/index.docbook
@@ -1,24 +1,87 @@
<?xml version="1.0" ?>
<!DOCTYPE article PUBLIC "-//KDE//DTD DocBook XML V4.2-Based Variant V1.1//EN"
"dtd/kdex.dtd" [
<!ENTITY % addindex "IGNORE">
<!ENTITY % English "INCLUDE" > <!-- change language only here -->
]>
<article lang="&language;" id="mailto">
<title>mailto</title>
<articleinfo>
<authorgroup>
-<author>&Ferdinand.Gassauer; &Ferdinand.Gassauer.mail;</author>
+<author>
+<firstname>Christopher</firstname>
+<surname>Yeleighton</surname>
+<email>giecrilj@stegny.2a.pl</email></author>
+
<!-- TRANS:ROLES_OF_TRANSLATORS -->
</authorgroup>
-</articleinfo>
-<para>The mailto kioslave is used when you click on a mailto link in an
-<acronym>HTML</acronym> page. &konqueror; will open the preferred mail
-client you have configured, with a composer window. Any information
-supplied in the <acronym>URL</acronym> will be filled in for you.</para>
-<para>
-See the manual: <ulink url="man:/mailto">mailto</ulink>.
+<date>2012-01-28</date>
+<releaseinfo>&kde; 4.8</releaseinfo>
+</articleinfo>
+<para>
+The kioslave mailto is responsible for launching the mail composer of your
+choice when you open a &URL; in the mailto scheme (<ulink url="http://tools.ietf.org/html/rfc6068">RFC6068</ulink>).
</para>
+
+<variablelist>
+<varlistentry>
+<term>Syntax</term>
+<listitem><para>The syntax of a mailto &URL; follows the following pattern:</para>
+
+<para>mailto:recipients?query</para>
+
+<para>where recipients form a list of restricted &SMTP; address specifications, and the
+query part may contain one or more of the following parameters:</para>
+
+<variablelist>
+<varlistentry>
+<term>&amp;to=recipients</term>
+<listitem><para>Specifies additional recipients.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term>&amp;cc=recipients</term>
+<listitem><para>Specifies additional recipients of carbon copies.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term>&amp;bcc=recipients</term>
+<listitem><para>Specifies additional recipients of blind carbon copies. These recipients
+will receive the message, but all other recipients will not know about that.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term>&amp;body=text</term>
+<listitem><para>Specifies the text of the message. This text should not be long, as there
+may be hard limits on how long an &URL; may be.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term>&amp;subject=text</term>
+<listitem><para>Specifies the subject of the message.</para></listitem>
+</varlistentry>
+
+</variablelist>
+
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>Example</term>
+<listitem><para>
+<literal>mailto:info@kde.org?cc=kde@kde.org&amp;subject=Thank%20you!&amp;body=KDE%20rocks!%20How%20can%20I%20help%3F</literal>
+</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term>Configuration</term>
+<listitem><para>
+Choose the application to handle mailto locators in &systemsettings; <menuchoice><guimenu>Workspace Appearance and Behaviour</guimenu><guimenuitem>Default Applications</guimenuitem></menuchoice>.
+</para></listitem>
+</varlistentry>
+
+</variablelist>
+
</article>
diff --git a/experimental/libkdeclarative/kdeclarative.cpp b/experimental/libkdeclarative/kdeclarative.cpp
index 45de8be93e..34383c053b 100644
--- a/experimental/libkdeclarative/kdeclarative.cpp
+++ b/experimental/libkdeclarative/kdeclarative.cpp
@@ -1,178 +1,187 @@
/*
* Copyright 2011 Marco Martin <mart@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2, or
* (at your option) any later version.
*
* This program 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 General Public License for more details
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kdeclarative.h"
#include "bindings/i18n_p.h"
#include "private/kdeclarative_p.h"
#include "private/engineaccess_p.h"
#include "private/kiconprovider_p.h"
#include <QtDeclarative/QDeclarativeComponent>
#include <QtDeclarative/QDeclarativeContext>
#include <QtDeclarative/QDeclarativeEngine>
#include <QtDeclarative/QDeclarativeExpression>
#include <QtScript/QScriptEngine>
#include <QtScript/QScriptValueIterator>
#include <QtCore/QWeakPointer>
#include <kdebug.h>
#include <kglobal.h>
#include <kstandarddirs.h>
#include <kurl.h>
#include <kconfiggroup.h>
#include <ksharedconfig.h>
void registerNonGuiMetaTypes(QScriptEngine *engine);
QScriptValue constructIconClass(QScriptEngine *engine);
QScriptValue constructKUrlClass(QScriptEngine *engine);
KDeclarativePrivate::KDeclarativePrivate()
: initialized(false)
{
}
KDeclarative::KDeclarative()
: d(new KDeclarativePrivate)
{
}
KDeclarative::~KDeclarative()
{
delete d;
}
void KDeclarative::setDeclarativeEngine(QDeclarativeEngine *engine)
{
if (d->declarativeEngine.data() == engine) {
return;
}
d->initialized = false;
d->declarativeEngine = engine;
}
QDeclarativeEngine *KDeclarative::declarativeEngine() const
{
return d->declarativeEngine.data();
}
void KDeclarative::initialize()
{
//Glorious hack:steal the engine
//create the access object
EngineAccess *engineAccess = new EngineAccess(this);
d->declarativeEngine.data()->rootContext()->setContextProperty("__engineAccess", engineAccess);
//make engineaccess set our d->scriptengine
QDeclarativeExpression *expr = new QDeclarativeExpression(d->declarativeEngine.data()->rootContext(), d->declarativeEngine.data()->rootContext()->contextObject(), "__engineAccess.setEngine(this)");
expr->evaluate();
delete expr;
//we don't need engineaccess anymore
d->declarativeEngine.data()->rootContext()->setContextProperty("__engineAccess", 0);
engineAccess->deleteLater();
//fail?
if (!d->scriptEngine) {
kWarning() << "Failed to get the script engine";
return;
}
//change the old globalobject with a new read/write copy
QScriptValue originalGlobalObject = d->scriptEngine.data()->globalObject();
QScriptValue newGlobalObject = d->scriptEngine.data()->newObject();
QString eval = QLatin1String("eval");
QString version = QLatin1String("version");
{
QScriptValueIterator iter(originalGlobalObject);
QVector<QString> names;
QVector<QScriptValue> values;
QVector<QScriptValue::PropertyFlags> flags;
while (iter.hasNext()) {
iter.next();
QString name = iter.name();
if (name == version) {
continue;
}
if (name != eval) {
names.append(name);
values.append(iter.value());
flags.append(iter.flags() | QScriptValue::Undeletable);
}
newGlobalObject.setProperty(iter.scriptName(), iter.value());
// m_illegalNames.insert(name);
}
}
d->scriptEngine.data()->setGlobalObject(newGlobalObject);
d->initialized = true;
}
void KDeclarative::setupBindings()
{
QScriptEngine *engine = d->scriptEngine.data();
if (!engine) {
return;
}
- //tell to the engine to search for inport in the kde4 plugin dirs
- foreach(const QString &importPath, KGlobal::dirs()->findDirs("module", "imports")) {
- d->declarativeEngine.data()->addImportPath(importPath);
+ /*tell the engine to search for import in the kde4 plugin dirs.
+ addImportPath adds the path at the beginning, so to honour user's
+ paths we need to traverse the list in reverse order*/
+
+ const QStringList importPathList = KGlobal::dirs()->findDirs("module", "imports");
+ QStringListIterator importPathIterator(importPathList);
+ importPathIterator.toBack();
+ while (importPathIterator.hasPrevious()) {
+ d->declarativeEngine.data()->addImportPath(importPathIterator.previous());
}
QString componentsPlatform = getenv("KDE_PLASMA_COMPONENTS_PLATFORM");
if (componentsPlatform.isEmpty()) {
KConfigGroup cg(KSharedConfig::openConfig("kdeclarativerc"), "Components-platform");
componentsPlatform = cg.readEntry("name", "desktop");
}
- foreach(const QString &importPath, KGlobal::dirs()->findDirs("module", "platformimports/" % componentsPlatform)) {
- d->declarativeEngine.data()->addImportPath(importPath);
+ const QStringList platformImportPathList = KGlobal::dirs()->findDirs("module", "platformimports/" % componentsPlatform);
+ QStringListIterator platformImportPathIterator(platformImportPathList);
+ platformImportPathIterator.toBack();
+ while (platformImportPathIterator.hasPrevious()) {
+ d->declarativeEngine.data()->addImportPath(platformImportPathIterator.previous());
}
QScriptValue global = engine->globalObject();
//KConfig and KJob
registerNonGuiMetaTypes(d->scriptEngine.data());
// Stuff from Qt
global.setProperty("QIcon", constructIconClass(engine));
// Add stuff from KDE libs
bindI18N(engine);
qScriptRegisterSequenceMetaType<KUrl::List>(engine);
global.setProperty("Url", constructKUrlClass(engine));
// setup ImageProvider for KDE icons
d->declarativeEngine.data()->addImageProvider(QString("icon"), new KIconProvider);
}
QScriptEngine *KDeclarative::scriptEngine() const
{
return d->scriptEngine.data();
}
diff --git a/includes/CMakeLists.txt b/includes/CMakeLists.txt
index 10341abe18..5579260f81 100644
--- a/includes/CMakeLists.txt
+++ b/includes/CMakeLists.txt
@@ -1,1110 +1,1111 @@
if (QT_QT3SUPPORT_FOUND)
install( FILES
K3BookmarkDrag
K3ButtonBox
K3ColorDrag
K3DictSpellingHighlighter
K3DockArea
K3DockMainWindow
K3DockManager
K3DockTabGroup
K3DockWidget
K3DockWidgetAbstractHeader
K3DockWidgetAbstractHeaderDrag
K3DockWidgetHeader
K3DockWidgetHeaderDrag
K3Icon
K3IconView
K3IconViewItem
K3IconViewSearchLine
K3ListBox
K3ListView
K3ListViewItem
K3ListViewSearchLine
K3ListViewSearchLineWidget
K3MimeSourceFactory
K3MultipleDrag
K3PopupMenu
K3Spell
K3SpellConfig
K3SpellDlg
K3SpellingHighlighter
K3StaticDeleter
K3StaticDeleterBase
K3SyntaxHighlighter
K3TempFile
K3TextEdit
K3URLDrag
K3Wizard
K3Command
K3CommandHistory
K3AboutApplication
K3AboutContainer
K3AboutContributor
K3AboutDialog
K3AboutWidget
K3ActiveLabel
K3FileTreeView
K3FileTreeViewItem
K3MacroCommand
K3NamedCommand
K3RFCDate
DESTINATION ${INCLUDE_INSTALL_DIR}/KDE COMPONENT Devel)
endif (QT_QT3SUPPORT_FOUND)
install( FILES
KACL
KAboutApplicationDialog
KAboutData
KAboutPerson
KAcceleratorManager
KAction
KActionCategory
KActionCollection
KActionMenu
KActionSelector
KAnimatedButton
KApplication
KAr
KArchive
KArchiveDirectory
KArchiveEntry
KArchiveFile
+ KAscii
KAssistantDialog
KAuthorized
KAutoMount
KAutoUnmount
KAutostart
KBookmark
KBookmarkAction
KBookmarkActionInterface
KBookmarkActionMenu
KBookmarkDialog
KBookmarkDomBuilder
KBookmarkExporterBase
KBookmarkGroup
KBookmarkGroupTraverser
KBookmarkImporterBase
KBookmarkManager
KBookmarkMenu
KBookmarkOwner
KBugReport
KBuildSycocaProgressDialog
KButtonGroup
KCModule
KCModuleContainer
KCModuleInfo
KCModuleLoader
KCModuleProxy
KCMultiDialog
KIdleTime
KCalendarSystem
KCalendarSystemFactory
KCharMacroExpander
KCharSelect
KCharsets
KCheckableProxyModel
KCmdLineArgs
KCmdLineOptions
KCodecs
KColor
KColorButton
KColorCells
KColorCollection
KColorCombo
KColorDialog
KColorMimeData
KColorPatch
KColorScheme
KColorTable
KColorUtils
KColorValueSelector
KComboBox
KCompletion
KCompletionBase
KCompletionBox
KCompletionMatches
KCompositeJob
KConfig
KConfigBase
KConfigDialog
KConfigDialogManager
KConfigGroup
KConfigSkeleton
KConfigSkeletonGenericItem
KConfigSkeletonItem
KSharedConfigPtr
KCrash
KCrashBookmarkImporter
KCrashBookmarkImporterImpl
KCurrencyCode
KCursor
KDBusServiceStarter
KDebug
KDEDModule
KDataTool
KDataToolAction
KDataToolInfo
KDateComboBox
KDatePicker
KDateTable
KDateTime
KDateTimeEdit
KDateTimeWidget
KDateValidator
KDateWidget
KDescendantsProxyModel
KDesktopFile
KDialog
KDialogButtonBox
KDirLister
KDirModel
KDirNotify
KDirOperator
KDirSortFilterProxyModel
KDirSelectDialog
KDirWatch
KDualAction
KFilePreviewGenerator
KFilePlacesView
KFileWidget
KDiskFreeSpaceInfo
KDoubleNumInput
KDoubleValidator
KEMailSettings
KEditListWidget
KEditToolBar
KEncodingFileDialog
KEncodingProber
KExtendableItemDelegate
KFadeWidgetEffect
KFile
KFileDialog
KFileFilterCombo
KFileItem
KFileItemActions
KFileItemList
KFileItemListProperties
KFileItemDelegate
KFileMetaDataWidget
KFileMetaInfo
KFileMetaInfoGroup
KFileMetaInfoItem
KFilePlacesModel
KFileShare
KFileSharePropsPlugin
KFileTreeBranch
KFileTreeView
KFilterBase
KFilterDev
KFind
KFindDialog
KFontAction
KFontChooser
KFontComboBox
KFontDialog
KFontRequester
KFontSizeAction
KFontUtils
KGenericFactory
KGenericFactoryBase
KGlobal
KGlobalAccel
KGlobalSettings
KGradientSelector
KGuiItem
KHBox
KHTMLPart
KHTMLSettings
KHTMLView
KHelpMenu
KHistoryComboBox
KHueSaturationSelector
KIEBookmarkExporterImpl
KIEBookmarkImporter
KIEBookmarkImporterImpl
KIMProxy
KIcon
KIconButton
KIconDialog
KIconEffect
KIconLoader
KIconTheme
KImageCache
KImageFilePreview
KImageIO
KInputDialog
KComponentData
KIntNumInput
KIntSpinBox
KIntValidator
KJob
KJobUiDelegate
KKeySequenceWidget
KLanguageButton
KLed
KLibFactory
KLibLoader
KLibrary
KLineEdit
KListWidget
KListWidgetSearchLine
KLocale
KLocalizedString
KLockFile
KMD5
KMacroExpanderBase
KMainWindow
KMakeTypeList
KMenu
KMenuBar
KMessage
KMessageBox
KMessageBoxMessageHandler
KMessageHandler
KMessageWidget
KMimeType
KMimeTypeChooser
KMimeTypeChooserDialog
KMimeTypeTrader
KMozillaBookmarkImporterImpl
KModifierKeyInfo
KMultiTabBar
KMultiTabBarButton
KMultiTabBarTab
KNFSShare
KNSBookmarkExporter
KNSBookmarkExporterImpl
KNSBookmarkImporter
KNSBookmarkImporterImpl
KNTLM
KNewFileMenu
KNotification
KNotificationRestrictions
KNotifyConfigWidget
KNumInput
KOCRDialog
KOpenWithDialog
KOperaBookmarkExporterImpl
KOperaBookmarkImporter
KOperaBookmarkImporterImpl
KPageDialog
KPageModel
KPageView
KPageWidget
KPageWidgetItem
KPageWidgetModel
KPassivePopup
KPassivePopupMessageHandler
KPasswordDialog
KPasteTextAction
KPixmapCache
KPixmapProvider
KPixmapRegionSelectorDialog
KPixmapRegionSelectorWidget
KPlotAxis
KPlotObject
KPlotPoint
KPlotWidget
KPluginFactory
KPluginInfo
KPluginLoader
KPluginSelector
KPopupFrame
KPreviewWidgetBase
KPrintPreview
KProcess
KProgressDialog
KPropertiesDialog
KProtocolInfo
KProtocolManager
KPushButton
KRandom
KRandomSequence
KRatingWidget
KRecentDocument
KRecentFilesAction
KRemoteEncoding
KReplace
KReplaceDialog
KRestrictedLine
KRichTextEdit
KRichTextWidget
KRuler
KRun
KSambaShare
KSambaShareData
KSaveFile
KScanDialog
KSelectAction
KSelectionOwner
KSelectionProxyModel
KSelectionWatcher
KSelector
KSeparator
KService
KServiceGroup
KServiceType
KServiceTypeProfile
KServiceTypeTrader
KSessionManager
KSharedConfig
KSharedDataCache
KSharedPtr
KShell
KShellCompletion
KShortcut
KShortcutsDialog
KShortcutsEditor
KShortcutWidget
KSocks
KSortableItem
KSortableList
KSpeech
KSplashScreen
KSqueezedTextLabel
KStandardDirs
KStandardAction
KStandardGuiItem
KStandardShortcut
KStartupInfo
KStartupInfoData
KStartupInfoId
KStatusBar
KStatusNotifierItem
KStringHandler
KStringListValidator
KStyle
KStyleFactory
KSycoca
KSycocaEntry
KSystemEventFilter
KSystemTimeZone
KSystemTimeZoneSource
KSystemTimeZones
KSystemTrayIcon
KTabBar
KTabWidget
KTar
KTempDir
KTemporaryFile
KTextBrowser
KTextEdit
KTimeComboBox
KTimeZone
KTimeZoneData
KTimeZoneSource
KTimeZoneWidget
KTimeZones
KTipDatabase
KTipDialog
KTitleWidget
KToggleAction
KToggleFullScreenAction
KToggleToolBarAction
KToolBar
KToolBarLabelAction
KToolBarPopupAction
KToolBarSpacerAction
KToolInvocation
KTreeWidgetSearchLine
KTreeWidgetSearchLineWidget
KTypeList
KTypeListIndexOf
KTypeListLength
KTzfileTimeZone
KTzfileTimeZoneSource
KUndoStack
KUniqueApplication
KUriFilter
KUriFilterData
KUriFilterPlugin
KUrl
KUrlComboBox
KUrlComboRequester
KUrlCompletion
KUrlLabel
KUrlNavigator
KUrlPixmapProvider
KUrlRequester
KUrlRequesterDialog
KUser
KUserGroup
KVBox
KViewStateMaintainer
KWindowInfo
KWindowSystem
KWordMacroExpander
KWordWrap
KXBELBookmarkImporterImpl
KXErrorHandler
KXMLGUIBuilder
KXMLGUIClient
KXMLGUIFactory
KXmlGuiWindow
KXMessages
KXYSelector
KZip
KZipFileEntry
KZoneAllocator
KonqBookmarkMenu
KonqBookmarkOwner
NET
NETRootInfo
NETWinInfo
OrgKdeKDirNotifyInterface
OrgKdeKLauncherInterface
ThumbCreator
kdbgstream
kndbgstream
KCategorizedView
KCategoryDrawer
KCategorizedSortFilterProxyModel
KEmoticons
KEmoticonsProvider
KEmoticonsTheme
KFilterProxySearchLine
KWidgetItemDelegate
KPixmapSequence
KPixmapSequenceWidget
KPixmapSequenceOverlayPainter
KGraphicsWebView
KWebPage
KWebView
KWebPluginFactory
KWebWallet
DESTINATION ${INCLUDE_INSTALL_DIR}/KDE COMPONENT Devel)
if(NOT KDE_NO_DEPRECATED)
install( FILES
KArrowButton
KEditListBox
KDiskFreeSpace
KMimeTypeResolver
DESTINATION ${INCLUDE_INSTALL_DIR}/KDE COMPONENT Devel)
endif(NOT KDE_NO_DEPRECATED)
if (UNIX)
if (QT_QT3SUPPORT_FOUND)
install( FILES
K3ProcIO
K3Process
K3ProcessController
K3ShellProcess
DESTINATION ${INCLUDE_INSTALL_DIR}/KDE COMPONENT Devel)
endif (QT_QT3SUPPORT_FOUND)
install( FILES
KDEsuClient
KPty
KPtyDevice
KPtyProcess
PtyProcess
SshProcess
StubProcess
SuProcess
DESTINATION ${INCLUDE_INSTALL_DIR}/KDE COMPONENT Devel)
endif (UNIX)
install( FILES
ConversionCheck/QVconvertible
ConversionCheck/type_toQString
ConversionCheck/type_toQVariant
DESTINATION ${INCLUDE_INSTALL_DIR}/KDE/ConversionCheck COMPONENT Devel)
install( FILES
DNSSD/Configuration
DNSSD/DomainBrowser
DNSSD/DomainModel
DNSSD/PublicService
DNSSD/RemoteService
DNSSD/ServiceBase
DNSSD/ServiceBrowser
DNSSD/ServiceModel
DNSSD/ServiceTypeBrowser
DESTINATION ${INCLUDE_INSTALL_DIR}/KDE/DNSSD COMPONENT Devel)
install( FILES
DOM/AbstractView
DOM/Attr
DOM/CDATASection
DOM/CSSCharsetRule
DOM/CSSException
DOM/CSSFontFaceRule
DOM/CSSImportRule
DOM/CSSMediaRule
DOM/CSSPageRule
DOM/CSSPrimitiveValue
DOM/CSSRule
DOM/CSSRuleList
DOM/CSSStyleDeclaration
DOM/CSSStyleRule
DOM/CSSStyleSheet
DOM/CSSUnknownRule
DOM/CSSValue
DOM/CSSValueList
DOM/CharacterData
DOM/Comment
DOM/Counter
DOM/CustomNodeFilter
DOM/DOMException
DOM/DOMImplementation
DOM/DOMString
DOM/Document
DOM/DocumentFragment
DOM/DocumentStyle
DOM/DocumentType
DOM/DomShared
DOM/Element
DOM/Entity
DOM/EntityReference
DOM/Event
DOM/EventException
DOM/EventListener
DOM/HTMLAnchorElement
DOM/HTMLAppletElement
DOM/HTMLAreaElement
DOM/HTMLBRElement
DOM/HTMLBaseElement
DOM/HTMLBaseFontElement
DOM/HTMLBlockquoteElement
DOM/HTMLBodyElement
DOM/HTMLButtonElement
DOM/HTMLCollection
DOM/HTMLDListElement
DOM/HTMLDirectoryElement
DOM/HTMLDivElement
DOM/HTMLDocument
DOM/HTMLElement
DOM/HTMLFieldSetElement
DOM/HTMLFontElement
DOM/HTMLFormCollection
DOM/HTMLFormElement
DOM/HTMLFrameElement
DOM/HTMLFrameSetElement
DOM/HTMLHRElement
DOM/HTMLHeadElement
DOM/HTMLHeadingElement
DOM/HTMLHtmlElement
DOM/HTMLIFrameElement
DOM/HTMLImageElement
DOM/HTMLInputElement
DOM/HTMLIsIndexElement
DOM/HTMLLIElement
DOM/HTMLLabelElement
DOM/HTMLLayerElement
DOM/HTMLLegendElement
DOM/HTMLLinkElement
DOM/HTMLMapElement
DOM/HTMLMenuElement
DOM/HTMLMetaElement
DOM/HTMLModElement
DOM/HTMLOListElement
DOM/HTMLObjectElement
DOM/HTMLOptGroupElement
DOM/HTMLOptionElement
DOM/HTMLParagraphElement
DOM/HTMLParamElement
DOM/HTMLPreElement
DOM/HTMLQuoteElement
DOM/HTMLScriptElement
DOM/HTMLSelectElement
DOM/HTMLStyleElement
DOM/HTMLTableCaptionElement
DOM/HTMLTableCellElement
DOM/HTMLTableColElement
DOM/HTMLTableElement
DOM/HTMLTableRowElement
DOM/HTMLTableSectionElement
DOM/HTMLTextAreaElement
DOM/HTMLTitleElement
DOM/HTMLUListElement
DOM/KeyboardEvent
DOM/LinkStyle
DOM/MediaList
DOM/MouseEvent
DOM/MutationEvent
DOM/NamedNodeMap
DOM/Node
DOM/NodeFilter
DOM/NodeIterator
DOM/NodeList
DOM/Notation
DOM/ProcessingInstruction
DOM/RGBColor
DOM/Range
DOM/RangeException
DOM/Rect
DOM/StyleSheet
DOM/StyleSheetList
DOM/Text
DOM/TextEvent
DOM/TreeWalker
DOM/UIEvent
DESTINATION ${INCLUDE_INSTALL_DIR}/KDE/DOM COMPONENT Devel)
install( FILES
KAccelGen/Deref
KAccelGen/Deref_Key
DESTINATION ${INCLUDE_INSTALL_DIR}/KDE/KAccelGen COMPONENT Devel)
install( FILES
KHE/CharColumnInterface
KHE/ClipboardInterface
KHE/ValueColumnInterface
KHE/ZoomInterface
DESTINATION ${INCLUDE_INSTALL_DIR}/KDE/KHE COMPONENT Devel)
install( FILES
KIO/AccessManager
KIO/AuthInfo
KIO/ChmodJob
KIO/Connection
KIO/CopyInfo
KIO/CopyJob
KIO/DavJob
KIO/DeleteJob
KIO/FileCopyJob
KIO/FileJob
KIO/ForwardingSlaveBase
KIO/Job
KIO/JobClasses
KIO/JobUiDelegate
KIO/ListJob
KIO/MetaData
KIO/MetaInfoJob
KIO/MimetypeJob
KIO/MultiGetJob
KIO/NetAccess
KIO/NetRC
KIO/PreviewJob
KIO/RenameDialog
KIO/RenameDialogPlugin
KIO/Scheduler
KIO/SessionData
KIO/SimpleJob
KIO/SkipDialog
KIO/Slave
KIO/SlaveBase
KIO/SlaveConfig
KIO/SlaveInterface
KIO/StatJob
KIO/StoredTransferJob
KIO/TCPSlaveBase
KIO/Task
KIO/TransferJob
DESTINATION ${INCLUDE_INSTALL_DIR}/KDE/KIO COMPONENT Devel)
if(NOT KDE_NO_DEPRECATED)
install( FILES
KIO/PasswordDialog
DESTINATION ${INCLUDE_INSTALL_DIR}/KDE/KIO COMPONENT Devel)
endif(NOT KDE_NO_DEPRECATED)
install( FILES
KMediaPlayer/Player
KMediaPlayer/View
DESTINATION ${INCLUDE_INSTALL_DIR}/KDE/KMediaPlayer COMPONENT Devel)
if(NOT KDE_NO_DEPRECATED)
install( FILES
KNS/Author
KNS/Category
KNS/Engine
KNS/Entry
KNS/Installation
KNS/KTranslatable
DESTINATION ${INCLUDE_INSTALL_DIR}/KDE/KNS COMPONENT Devel)
endif(NOT KDE_NO_DEPRECATED)
install( FILES
KNS3/DownloadDialog
KNS3/Entry
KNS3/KNewStuffAction
KNS3/KNewStuffButton
DESTINATION ${INCLUDE_INSTALL_DIR}/KDE/KNS3 COMPONENT Devel)
install( FILES
KNetwork/KActiveSocketBase
KNetwork/KBufferedSocket
KNetwork/KClientSocketBase
KNetwork/KInetSocketAddress
KNetwork/KIpAddress
KNetwork/KPassiveSocketBase
KNetwork/KResolver
KNetwork/KResolverEntry
KNetwork/KResolverResults
KNetwork/KReverseResolver
KNetwork/KServerSocket
KNetwork/KSocketAddress
KNetwork/KSocketBase
KNetwork/KSocketDevice
KNetwork/KSocketDeviceFactory
KNetwork/KSocksSocketDevice
KNetwork/KStreamSocket
KNetwork/KUnixSocketAddress
DESTINATION ${INCLUDE_INSTALL_DIR}/KDE/KNetwork COMPONENT Devel)
install( FILES
KParts/BrowserExtension
KParts/BrowserHostExtension
KParts/BrowserInterface
KParts/BrowserRun
KParts/ComponentFactory
KParts/DockMainWindow3
KParts/Event
KParts/Factory
KParts/FileInfoExtension
KParts/GUIActivateEvent
KParts/GenericFactory
KParts/GenericFactoryBase
KParts/HistoryProvider
KParts/HtmlExtension
KParts/LiveConnectExtension
KParts/MainWindow
KParts/OpenUrlEvent
KParts/Part
KParts/PartActivateEvent
KParts/PartBase
KParts/PartManager
KParts/PartSelectEvent
KParts/Plugin
KParts/ReadOnlyPart
KParts/ReadWritePart
KParts/StatusBarExtension
KParts/TextExtension
KParts/WindowArgs
DESTINATION ${INCLUDE_INSTALL_DIR}/KDE/KParts COMPONENT Devel)
install( FILES
Kross/Action
Kross/ActionCollection
Kross/ActionCollectionEditor
Kross/ActionCollectionModel
Kross/ActionCollectionProxyModel
Kross/ActionCollectionView
Kross/ChildrenInterface
Kross/ErrorInterface
Kross/Interpreter
Kross/InterpreterInfo
Kross/Manager
Kross/MetaFunction
Kross/MetaType
Kross/MetaTypeHandler
Kross/MetaTypeImpl
Kross/MetaTypeVariant
Kross/MetaTypeVoidStar
Kross/Object
Kross/Script
Kross/ScriptingPlugin
Kross/WrapperInterface
DESTINATION ${INCLUDE_INSTALL_DIR}/KDE/Kross COMPONENT Devel)
install( FILES
KSettings/Dialog
KSettings/Dispatcher
KSettings/PluginPage
DESTINATION ${INCLUDE_INSTALL_DIR}/KDE/KSettings COMPONENT Devel)
#install( FILES
# Sonnet/GuessLanguage
# Sonnet/UnicodeData
# Sonnet/TextBreaks
# Sonnet/Spell
#DESTINATION ${INCLUDE_INSTALL_DIR}/KDE/Sonnet )
install( FILES
KTextEditor/Attribute
KTextEditor/CodeCompletionInterface
KTextEditor/CodeCompletionModel
KTextEditor/CodeCompletionModelControllerInterface
KTextEditor/Command
KTextEditor/CommandExtension
KTextEditor/CommandInterface
KTextEditor/ConfigInterface
KTextEditor/ConfigPage
KTextEditor/ContainerInterface
KTextEditor/Cursor
KTextEditor/Document
KTextEditor/Editor
KTextEditor/EditorChooser
KTextEditor/Factory
KTextEditor/HighlightInterface
KTextEditor/MarkInterface
KTextEditor/ModificationInterface
KTextEditor/MovingCursor
KTextEditor/MovingInterface
KTextEditor/MovingRange
KTextEditor/ParameterizedSessionConfigInterface
KTextEditor/Plugin
KTextEditor/Range
KTextEditor/SearchInterface
KTextEditor/SessionConfigInterface
KTextEditor/SmartCursor
KTextEditor/SmartCursorNotifier
KTextEditor/SmartCursorWatcher
KTextEditor/SmartInterface
KTextEditor/SmartRange
KTextEditor/SmartRangeNotifier
KTextEditor/SmartRangeWatcher
KTextEditor/TemplateInterface
KTextEditor/TemplateInterface2
KTextEditor/TextHintInterface
KTextEditor/VariableInterface
KTextEditor/View
DESTINATION ${INCLUDE_INSTALL_DIR}/KDE/KTextEditor COMPONENT Devel)
install( FILES
KUnitTest/Runner
KUnitTest/SlotTester
KUnitTest/TestResults
KUnitTest/Tester
DESTINATION ${INCLUDE_INSTALL_DIR}/KDE/KUnitTest COMPONENT Devel)
install( FILES
KWallet/Wallet
DESTINATION ${INCLUDE_INSTALL_DIR}/KDE/KWallet COMPONENT Devel)
install( FILES
Solid/AcAdapter
Solid/AudioInterface
Solid/Battery
Solid/Block
Solid/Button
Solid/Camera
Solid/Device
Solid/DeviceInterface
Solid/DeviceNotifier
Solid/DvbInterface
Solid/GenericInterface
Solid/NetworkInterface
Solid/NetworkShare
Solid/Networking
Solid/OpticalDisc
Solid/OpticalDrive
Solid/PortableMediaPlayer
Solid/PowerManagement
Solid/Predicate
Solid/Processor
Solid/StorageAccess
Solid/StorageDrive
Solid/StorageVolume
DESTINATION ${INCLUDE_INSTALL_DIR}/KDE/Solid COMPONENT Devel)
install( FILES
Sonnet/ConfigDialog
Sonnet/ConfigWidget
Sonnet/Dialog
Sonnet/DictionaryComboBox
Sonnet/Highlighter
DESTINATION ${INCLUDE_INSTALL_DIR}/KDE/Sonnet COMPONENT Devel)
install( FILES
KAuth/Action
KAuth/ActionReply
KAuth/ActionWatcher
KAuth/HelperSupport
DESTINATION ${INCLUDE_INSTALL_DIR}/KDE/KAuth COMPONENT Devel)
install( FILES
ThreadWeaver/DependencyPolicy
ThreadWeaver/Job
ThreadWeaver/JobCollection
ThreadWeaver/JobSequence
ThreadWeaver/QueuePolicy
ThreadWeaver/ResourceRestrictionPolicy
ThreadWeaver/State
ThreadWeaver/Thread
ThreadWeaver/Weaver
ThreadWeaver/WeaverInterface
ThreadWeaver/WeaverObserver
DESTINATION ${INCLUDE_INSTALL_DIR}/KDE/ThreadWeaver COMPONENT Devel)
install( FILES
khtml/DrawContentsEvent
khtml/MouseDoubleClickEvent
khtml/MouseEvent
khtml/MouseMoveEvent
khtml/MousePressEvent
khtml/MouseReleaseEvent
DESTINATION ${INCLUDE_INSTALL_DIR}/KDE/khtml COMPONENT Devel)
install(FILES
Nepomuk/File
Nepomuk/KRatingPainter
Nepomuk/KRatingWidget
Nepomuk/MassUpdateJob
Nepomuk/Resource
Nepomuk/ResourceManager
Nepomuk/Service
Nepomuk/Tag
Nepomuk/TagWidget
Nepomuk/Thing
Nepomuk/Variant
DESTINATION ${INCLUDE_INSTALL_DIR}/KDE/Nepomuk COMPONENT Devel)
install(FILES
Nepomuk/Types/Class
Nepomuk/Types/Literal
Nepomuk/Types/Ontology
Nepomuk/Types/Property
DESTINATION ${INCLUDE_INSTALL_DIR}/KDE/Nepomuk/Types COMPONENT Devel)
install(FILES
Nepomuk/Query/AndTerm
Nepomuk/Query/ComparisonTerm
Nepomuk/Query/FileQuery
Nepomuk/Query/GroupTerm
Nepomuk/Query/LiteralTerm
Nepomuk/Query/NegationTerm
Nepomuk/Query/OptionalTerm
Nepomuk/Query/OrTerm
Nepomuk/Query/Query
Nepomuk/Query/QueryParser
Nepomuk/Query/QueryServiceClient
Nepomuk/Query/ResourceTerm
Nepomuk/Query/ResourceTypeTerm
Nepomuk/Query/Result
Nepomuk/Query/SimpleTerm
Nepomuk/Query/StandardQuery
Nepomuk/Query/Term
DESTINATION ${INCLUDE_INSTALL_DIR}/KDE/Nepomuk/Query COMPONENT Devel)
install(FILES
Nepomuk/Utils/DynamicResourceFacet
Nepomuk/Utils/Facet
Nepomuk/Utils/FacetWidget
Nepomuk/Utils/ProxyFacet
Nepomuk/Utils/ResourceModel
Nepomuk/Utils/SearchWidget
Nepomuk/Utils/SimpleFacet
Nepomuk/Utils/SimpleResourceModel
DESTINATION ${INCLUDE_INSTALL_DIR}/KDE/Nepomuk/Utils COMPONENT Devel)
install(FILES
Nepomuk/Vocabulary/NCAL
Nepomuk/Vocabulary/NCO
Nepomuk/Vocabulary/NDO
Nepomuk/Vocabulary/NEXIF
Nepomuk/Vocabulary/NFO
Nepomuk/Vocabulary/NIE
Nepomuk/Vocabulary/NMM
Nepomuk/Vocabulary/NMO
Nepomuk/Vocabulary/NUAO
Nepomuk/Vocabulary/PIMO
Nepomuk/Vocabulary/TMO
DESTINATION ${INCLUDE_INSTALL_DIR}/KDE/Nepomuk/Vocabulary COMPONENT Devel)
install(FILES
Plasma/AbstractDialogManager
Plasma/AbstractRunner
Plasma/AbstractToolBox
Plasma/AccessAppletJob
Plasma/AccessManager
Plasma/Animation
Plasma/Animator
Plasma/Applet
Plasma/AppletScript
Plasma/AuthorizationInterface
Plasma/AuthorizationManager
Plasma/AuthorizationRule
Plasma/BusyWidget
Plasma/CheckBox
Plasma/ClientPinRequest
Plasma/ComboBox
Plasma/ConfigLoader
Plasma/Containment
Plasma/ContainmentActions
Plasma/Corona
Plasma/Credentials
Plasma/DataContainer
Plasma/DataEngine
Plasma/DataEngineManager
Plasma/DataEngineScript
Plasma/DeclarativeWidget
Plasma/Delegate
Plasma/Dialog
Plasma/FlashingLabel
Plasma/Frame
Plasma/FrameSvg
Plasma/GroupBox
Plasma/IconWidget
Plasma/ItemBackground
Plasma/Label
Plasma/LineEdit
Plasma/Meter
Plasma/Package
Plasma/PackageStructure
Plasma/PaintUtils
Plasma/Plasma
Plasma/PluginLoader
Plasma/PopupApplet
Plasma/PushButton
Plasma/QueryMatch
Plasma/RadioButton
Plasma/RunnerContext
Plasma/RunnerManager
Plasma/RunnerScript
Plasma/ScriptEngine
Plasma/ScrollBar
Plasma/ScrollWidget
Plasma/Separator
Plasma/Service
Plasma/ServiceJob
Plasma/SignalPlotter
Plasma/Slider
Plasma/SpinBox
Plasma/Svg
Plasma/SvgWidget
Plasma/TabBar
Plasma/TextBrowser
Plasma/TextEdit
Plasma/Theme
Plasma/ToolButton
Plasma/ToolTipContent
Plasma/ToolTipManager
Plasma/TreeView
Plasma/Version
Plasma/VideoWidget
Plasma/View
Plasma/Wallpaper
Plasma/WallpaperScript
Plasma/WebView
Plasma/WindowEffects
DESTINATION ${INCLUDE_INSTALL_DIR}/KDE/Plasma COMPONENT Devel)
if(QT_QTOPENGL_FOUND)
install(FILES
Plasma/GLApplet
DESTINATION ${INCLUDE_INSTALL_DIR}/KDE/Plasma COMPONENT Devel)
endif(QT_QTOPENGL_FOUND)
install(FILES
KUnitConversion/Converter
KUnitConversion/Unit
KUnitConversion/UnitCategory
KUnitConversion/Value
DESTINATION ${INCLUDE_INSTALL_DIR}/KDE/KUnitConversion COMPONENT Devel)
diff --git a/includes/KAscii b/includes/KAscii
new file mode 100644
index 0000000000..c30b04500e
--- /dev/null
+++ b/includes/KAscii
@@ -0,0 +1 @@
+#include "../kascii.h"
diff --git a/kdewebkit/kwebwallet.cpp b/kdewebkit/kwebwallet.cpp
index 8d9396e171..a81a0f9feb 100644
--- a/kdewebkit/kwebwallet.cpp
+++ b/kdewebkit/kwebwallet.cpp
@@ -1,585 +1,586 @@
/*
* This file is part of the KDE project.
*
* Copyright (C) 2009 Dawit Alemayehu <adawit@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
#include "kwebwallet.h"
#include <kwallet.h>
#include <kdebug.h>
#include <QtCore/QSet>
#include <QtCore/QHash>
#include <QtCore/QFile>
#include <QtCore/QWeakPointer>
#include <QtCore/QScopedPointer>
#include <QtWebKit/QWebPage>
#include <QtWebKit/QWebFrame>
#include <QtWebKit/QWebElement>
#include <QtWebKit/QWebElementCollection>
#include <qwindowdefs.h>
#define QL1S(x) QLatin1String(x)
#define QL1C(x) QLatin1Char(x)
-// Form parsing JS code adapted from Arora.
+// The following form parsing JS code was adapted from Arora project.
// See https://github.com/Arora/arora/blob/master/src/data/parseForms.js
#define FORM_PARSING_JS "(function (){ \
var forms; \
- var numForms = document.forms.length; \
+ var doc = (this.contentDocument ? this.contentDocument : document); \
+ var numForms = doc.forms.length; \
if (numForms > 0 ) { \
- var forms = new Array; \
+ forms = new Array; \
for (var i = 0; i < numForms; ++i) { \
var form = document.forms[i]; \
if (form.method.toLowerCase() != 'post') \
continue; \
var formObject = new Object; \
formObject.name = form.name; \
formObject.index = i; \
var elements = new Array; \
var numElements = form.elements.length; \
for (var j = 0; j < numElements; ++j) { \
var e = form.elements[j]; \
var element = new Object; \
element.name = e.name; \
element.value = e.value; \
element.type = e.type; \
element.readonly = e.hasAttribute('readonly'); \
element.disabled = e.hasAttribute('disabled'); \
if (element.autocomplete != null) \
element.autocomplete = element.autocomplete.value; \
elements.push(element); \
} \
formObject.elements = elements; \
forms.push(formObject); \
} \
} \
return forms; \
}())"
/**
* Creates key used to store and retrieve form data.
*
*/
static QString walletKey(KWebWallet::WebForm form)
{
QString key = form.url.toString(QUrl::RemoveQuery|QUrl::RemoveFragment);
key += QL1C('#');
key += form.name;
return key;
}
static void collectAllChildFrames(QWebFrame* frame, QList<QWebFrame*>& list)
{
list << frame->childFrames();
QListIterator<QWebFrame*> it(frame->childFrames());
while (it.hasNext()) {
collectAllChildFrames(it.next(), list);
}
}
static QUrl urlForFrame(QWebFrame* frame)
{
return (frame->url().isEmpty() ? frame->baseUrl().resolved(frame->url()) : frame->url());
}
class KWebWallet::KWebWalletPrivate
{
public:
struct FormsData
{
QWeakPointer<QWebFrame> frame;
KWebWallet::WebFormList forms;
};
KWebWalletPrivate(KWebWallet* parent);
KWebWallet::WebFormList parseFormData(QWebFrame* frame, bool fillform = true, bool ignorepasswd = false);
void fillDataFromCache(KWebWallet::WebFormList &formList);
void saveDataToCache(const QString &key);
void removeDataFromCache(const WebFormList &formList);
void openWallet();
// Private slots...
void _k_openWalletDone(bool);
void _k_walletClosed();
WId wid;
KWebWallet *q;
QScopedPointer<KWallet::Wallet> wallet;
KWebWallet::WebFormList pendingRemoveRequests;
QHash<KUrl, FormsData> pendingFillRequests;
QHash<QString, KWebWallet::WebFormList> pendingSaveRequests;
QSet<KUrl> confirmSaveRequestOverwrites;
};
KWebWallet::KWebWalletPrivate::KWebWalletPrivate(KWebWallet *parent)
:wid (0), q(parent)
{
}
KWebWallet::WebFormList KWebWallet::KWebWalletPrivate::parseFormData(QWebFrame *frame, bool fillform, bool ignorepasswd)
{
Q_ASSERT(frame);
KWebWallet::WebFormList list;
// Execute the javscript to obtain the necessary fields...
QVariantList results = frame->evaluateJavaScript(QL1S(FORM_PARSING_JS)).toList();
Q_FOREACH (const QVariant &formVariant, results) {
QVariantMap map = formVariant.toMap();
KWebWallet::WebForm form;
form.url = urlForFrame(frame);
form.name = map[QL1S("name")].toString();
form.index = map[QL1S("index")].toString();
bool formHasPasswords = false;
const QVariantList elements = map[QL1S("elements")].toList();
QList<KWebWallet::WebForm::WebField> inputFields;
Q_FOREACH (const QVariant &element, elements) {
QVariantMap elementMap = element.toMap();
const QString name = elementMap[QL1S("name")].toString();
const QString value = (ignorepasswd ? QString() : elementMap[QL1S("value")].toString());
const QString type = elementMap[QL1S("type")].toString();
const bool isPasswdInput = (type.compare(QL1S("password"), Qt::CaseInsensitive) == 0);
const bool isTextInput = (type.compare(QL1S("text"), Qt::CaseInsensitive) == 0);
const bool autoCompleteOff = (elementMap[QL1S("autocomplete")].toString().compare(QL1S("off"), Qt::CaseInsensitive) == 0);
if (name.isEmpty())
continue;
if (!isPasswdInput && !isTextInput)
continue;
if (autoCompleteOff)
continue;
if (elementMap[QL1S("disabled")].toBool())
continue;
if (fillform && elementMap[QL1S("readonly")].toBool())
continue;
if (isPasswdInput && !fillform && value.isEmpty())
continue;
if (isPasswdInput)
formHasPasswords = true;
inputFields.append(qMakePair(name, value));
}
// Only add the input fields on form save requests...
if (formHasPasswords && !fillform)
form.fields = inputFields;
// Add the form to the list if we are saving it or it has cached data.
if ((fillform && q->hasCachedFormData(form)) || (!fillform && !form.fields.isEmpty()))
list << form;
}
return list;
}
void KWebWallet::KWebWalletPrivate::fillDataFromCache(KWebWallet::WebFormList &formList)
{
if (!wallet) {
kWarning(800) << "Unable to retrieve form data from wallet";
return;
}
QMap<QString, QString> cachedValues;
QMutableListIterator <WebForm> formIt (formList);
while (formIt.hasNext()) {
KWebWallet::WebForm &form = formIt.next();
const QString key (walletKey(form));
if (wallet->readMap(key, cachedValues) != 0) {
kWarning(800) << "Unable to read form data for key:" << key;
continue;
}
QMapIterator<QString, QString> valuesIt (cachedValues);
while (valuesIt.hasNext()) {
valuesIt.next();
//kDebug(800) << "wallet key:" << key << valuesIt.key() << valuesIt.value();
form.fields << qMakePair(valuesIt.key(), valuesIt.value());
}
}
}
void KWebWallet::KWebWalletPrivate::saveDataToCache(const QString &key)
{
// Make sure the specified keys exists before acting on it. See BR# 270209.
if (!pendingSaveRequests.contains(key)) {
return;
}
bool success = false;
const QUrl url = pendingSaveRequests.value(key).first().url;
if (wallet) {
int count = 0;
const KWebWallet::WebFormList list = pendingSaveRequests.value(key);
QListIterator<KWebWallet::WebForm> formIt (list);
while (formIt.hasNext()) {
QMap<QString, QString> values, storedValues;
const KWebWallet::WebForm form = formIt.next();
const QString accessKey = walletKey(form);
if (confirmSaveRequestOverwrites.contains(url)) {
confirmSaveRequestOverwrites.remove(url);
const int status = wallet->readMap(accessKey, storedValues);
if (status == 0 && storedValues.count()) {
QListIterator<KWebWallet::WebForm::WebField> fieldIt (form.fields);
while (fieldIt.hasNext()) {
const KWebWallet::WebForm::WebField field = fieldIt.next();
if (storedValues.contains(field.first) &&
storedValues.value(field.first) != field.second) {
emit q->saveFormDataRequested(key, url);
return;
}
}
// If we got here it means the new credential is exactly
// the same as the one already cached ; so skip the
// re-saving part...
success = true;
continue;
}
}
QListIterator<KWebWallet::WebForm::WebField> fieldIt (form.fields);
while (fieldIt.hasNext()) {
const KWebWallet::WebForm::WebField field = fieldIt.next();
values.insert(field.first, field.second);
}
if (wallet->writeMap(accessKey, values) == 0)
count++;
else
kWarning(800) << "Unable to write form data to wallet";
}
if (list.isEmpty() || count > 0)
success = true;
pendingSaveRequests.remove(key);
} else {
kWarning(800) << "NULL KWallet instance!";
}
emit q->saveFormDataCompleted(url, success);
}
void KWebWallet::KWebWalletPrivate::openWallet()
{
if (!wallet.isNull()) {
return;
}
wallet.reset(KWallet::Wallet::openWallet(KWallet::Wallet::NetworkWallet(),
wid, KWallet::Wallet::Asynchronous));
if (wallet.isNull()) {
return;
}
connect(wallet.data(), SIGNAL(walletOpened(bool)), q, SLOT(_k_openWalletDone(bool)));
connect(wallet.data(), SIGNAL(walletClosed()), q, SLOT(_k_walletClosed()));
}
void KWebWallet::KWebWalletPrivate::removeDataFromCache(const WebFormList &formList)
{
if (!wallet) {
kWarning(800) << "NULL KWallet instance!";
return;
}
QListIterator<WebForm> formIt (formList);
while (formIt.hasNext())
wallet->removeEntry(walletKey(formIt.next()));
}
void KWebWallet::KWebWalletPrivate::_k_openWalletDone(bool ok)
{
Q_ASSERT (wallet);
if (ok &&
(wallet->hasFolder(KWallet::Wallet::FormDataFolder()) ||
wallet->createFolder(KWallet::Wallet::FormDataFolder())) &&
wallet->setFolder(KWallet::Wallet::FormDataFolder())) {
// Do pending fill requests...
if (!pendingFillRequests.isEmpty()) {
KUrl::List urlList;
QMutableHashIterator<KUrl, FormsData> requestIt (pendingFillRequests);
while (requestIt.hasNext()) {
requestIt.next();
KWebWallet::WebFormList list = requestIt.value().forms;
fillDataFromCache(list);
q->fillWebForm(requestIt.key(), list);
}
pendingFillRequests.clear();
}
// Do pending save requests...
if (!pendingSaveRequests.isEmpty()) {
QListIterator<QString> keysIt (pendingSaveRequests.keys());
while (keysIt.hasNext())
saveDataToCache(keysIt.next());
}
// Do pending remove requests...
if (!pendingRemoveRequests.isEmpty()) {
removeDataFromCache(pendingRemoveRequests);
pendingRemoveRequests.clear();
}
} else {
// Delete the wallet if opening the wallet failed or we were unable
// to change to the folder we wanted to change to.
delete wallet.take();
}
}
void KWebWallet::KWebWalletPrivate::_k_walletClosed()
{
if (wallet)
wallet.take()->deleteLater();
emit q->walletClosed();
}
KWebWallet::KWebWallet(QObject *parent, WId wid)
:QObject(parent), d(new KWebWalletPrivate(this))
{
if (!wid) {
// If wid is 0, make the best effort the discern it from our parent.
QWebPage *page = qobject_cast<QWebPage*>(parent);
if (page) {
QWidget *widget = page->view();
if (widget && widget->window())
wid = widget->window()->winId();
}
}
d->wid = wid;
}
KWebWallet::~KWebWallet()
{
delete d;
}
KWebWallet::WebFormList KWebWallet::formsWithCachedData(QWebFrame* frame, bool recursive) const
{
WebFormList list;
if (frame) {
list << d->parseFormData(frame);
if (recursive) {
QList<QWebFrame*> childFrameList;
collectAllChildFrames(frame, childFrameList);
QListIterator <QWebFrame *> framesIt (childFrameList);
while (framesIt.hasNext()) {
list << d->parseFormData(framesIt.next());
}
}
}
return list;
}
void KWebWallet::fillFormData(QWebFrame *frame, bool recursive)
{
if (!frame)
return;
KUrl::List urlList;
WebFormList formsList = d->parseFormData(frame);
if (!formsList.isEmpty()) {
const QUrl url (urlForFrame(frame));
if (d->pendingFillRequests.contains(url)) {
kWarning(800) << "Duplicate request rejected!";
} else {
KWebWalletPrivate::FormsData data;
data.frame = QWeakPointer<QWebFrame>(frame);
data.forms << formsList;
d->pendingFillRequests.insert(url, data);
urlList << url;
}
}
if (recursive) {
QList<QWebFrame*> childFrameList;
collectAllChildFrames(frame, childFrameList);
QListIterator<QWebFrame*> frameIt (childFrameList);
while (frameIt.hasNext()) {
QWebFrame *childFrame = frameIt.next();
formsList = d->parseFormData(childFrame);
if (formsList.isEmpty())
continue;
const QUrl url (childFrame->url());
if (d->pendingFillRequests.contains(url)) {
kWarning(800) << "Duplicate request rejected!!!";
} else {
KWebWalletPrivate::FormsData data;
data.frame = QWeakPointer<QWebFrame>(childFrame);
data.forms << formsList;
d->pendingFillRequests.insert(url, data);
urlList << url;
}
}
}
if (!urlList.isEmpty())
fillFormDataFromCache(urlList);
}
void KWebWallet::saveFormData(QWebFrame *frame, bool recursive, bool ignorePasswordFields)
{
if (!frame)
return;
WebFormList list = d->parseFormData(frame, false, ignorePasswordFields);
if (recursive) {
QList<QWebFrame*> childFrameList;
collectAllChildFrames(frame, childFrameList);
QListIterator<QWebFrame*> frameIt (childFrameList);
while (frameIt.hasNext())
list << d->parseFormData(frameIt.next(), false, ignorePasswordFields);
}
if (list.isEmpty())
return;
const QString key = QString::number(qHash(urlForFrame(frame).toString() + frame->frameName()), 16);
const bool isAlreadyPending = d->pendingSaveRequests.contains(key);
d->pendingSaveRequests.insert(key, list);
if (isAlreadyPending)
return;
for (int i = 0 ; i < list.count(); ++i) {
if (hasCachedFormData(list.at(i)))
list.takeAt(i);
}
if (list.isEmpty()) {
d->confirmSaveRequestOverwrites.insert(urlForFrame(frame));
saveFormDataToCache(key);
return;
}
emit saveFormDataRequested(key, urlForFrame(frame));
}
void KWebWallet::removeFormData(QWebFrame *frame, bool recursive)
{
if (frame)
removeFormDataFromCache(formsWithCachedData(frame, recursive));
}
void KWebWallet::removeFormData(const WebFormList &forms)
{
d->pendingRemoveRequests << forms;
removeFormDataFromCache(forms);
}
void KWebWallet::acceptSaveFormDataRequest(const QString &key)
{
saveFormDataToCache(key);
}
void KWebWallet::rejectSaveFormDataRequest(const QString & key)
{
d->pendingSaveRequests.remove(key);
}
void KWebWallet::fillWebForm(const KUrl &url, const KWebWallet::WebFormList &forms)
{
QWeakPointer<QWebFrame> frame = d->pendingFillRequests.value(url).frame;
if (!frame)
return;
QString script;
bool wasFilled = false;
Q_FOREACH (const KWebWallet::WebForm& form, forms) {
Q_FOREACH(const KWebWallet::WebForm::WebField& field, form.fields) {
QString value = field.second;
value.replace(QLatin1Char('\\'), QLatin1String("\\\\"));
script += QString::fromLatin1("document.forms[\"%1\"].elements[\"%2\"].value=\"%3\";\n")
.arg((form.name.isEmpty() ? form.index : form.name))
.arg(field.first).arg(value);
}
}
if (!script.isEmpty()) {
wasFilled = true;
frame.data()->evaluateJavaScript(script);
}
emit fillFormRequestCompleted(wasFilled);
}
KWebWallet::WebFormList KWebWallet::formsToFill(const KUrl &url) const
{
return d->pendingFillRequests.value(url).forms;
}
KWebWallet::WebFormList KWebWallet::formsToSave(const QString &key) const
{
return d->pendingSaveRequests.value(key);
}
bool KWebWallet::hasCachedFormData(const WebForm &form) const
{
return !KWallet::Wallet::keyDoesNotExist(KWallet::Wallet::NetworkWallet(),
KWallet::Wallet::FormDataFolder(),
walletKey(form));
}
void KWebWallet::fillFormDataFromCache(const KUrl::List &urlList)
{
if (d->wallet) {
QListIterator<KUrl> urlIt (urlList);
while (urlIt.hasNext()) {
const KUrl url = urlIt.next();
WebFormList list = formsToFill(url);
d->fillDataFromCache(list);
fillWebForm(url, list);
}
d->pendingFillRequests.clear();
}
d->openWallet();
}
void KWebWallet::saveFormDataToCache(const QString &key)
{
if (d->wallet) {
d->saveDataToCache(key);
return;
}
d->openWallet();
}
void KWebWallet::removeFormDataFromCache(const WebFormList &forms)
{
if (d->wallet) {
d->removeDataFromCache(forms);
d->pendingRemoveRequests.clear();
return;
}
d->openWallet();
}
#include "moc_kwebwallet.cpp"
diff --git a/kdoctools/customization/nl/user.entities b/kdoctools/customization/nl/user.entities
index e10aef3ae8..1daafc1baa 100644
--- a/kdoctools/customization/nl/user.entities
+++ b/kdoctools/customization/nl/user.entities
@@ -1,174 +1,175 @@
<!-- This file contains entities (only!) to make authoring/translating
a document easier. They are necessarily language-specific.
In the case of name clashes, entities in this file always lose.
Keep the entities simple, but _always_ provide them with full markup.
Please keep the entities sorted on the name: it will avoid duplicate
names (which if they occur will cost you a _lot_ of time)
-->
<!-- Credits (voor ROLES_OF_TRANSLATORS, en andere plaatsen) -->
<!-- remains to check how the subelements are treated by the style sheets -->
<!ENTITY Niels.Reedijk '<othercredit role="translator"><firstname>Niels</firstname><surname>Reedijk</surname><affiliation><address><email>nielx@kde.nl</email></address></affiliation><contrib>Vertaling van het handboek</contrib></othercredit>'>
<!ENTITY Floris.Lambrechts '<othercredit role="translator"><firstname>Floris</firstname><surname>Lambrechts</surname><affiliation><address><email>floris.lambrechts@linuxfocus.org</email></address></affiliation><contrib>Vertaling van het handboek</contrib></othercredit>'>
<!ENTITY Michel.Fontes '<othercredit role="translator"><firstname>Michel</firstname><surname>Fontes</surname><affiliation><address><email>michelfontes@hotmail.com</email></address></affiliation><contrib>Vertaling van het handboek</contrib></othercredit>'>
<!ENTITY Jogchum.Reitsma '<othercredit role="translator"><firstname>Jogchum</firstname><surname>Reitsma</surname><affiliation><address><email>j.reitsma@hcc.net</email></address></affiliation><contrib>Vertaling van het handboek</contrib></othercredit>'>
<!ENTITY Otto.Bruggeman '<othercredit role="translator"><firstname>Otto</firstname><surname>Bruggeman</surname><affiliation><address><email>ottobruggeman@kde.nl</email></address></affiliation><contrib>Vertaler/Nalezer</contrib></othercredit>'>
<!ENTITY Douwe.vanderSchaaf '<othercredit role="translator"><firstname>Douwe</firstname><surname>van der Schaaf</surname><affiliation><address><email>dvdsch@chello.nl</email></address></affiliation><contrib>Vertaler/Nalezer</contrib></othercredit>'>
<!ENTITY Richard.Voss '<othercredit role="translator"><firstname>Richard</firstname><surname>Voss</surname><affiliation><address><email>richard@nergenshuizen.nl</email></address></affiliation><contrib>Vertaler/Nalezer</contrib></othercredit>'>
<!ENTITY Rinse.Devries '<othercredit role="translator"><firstname>Rinse</firstname><surname>de Vries</surname><affiliation><address><email>rinsedevries@kde.nl</email></address></affiliation><contrib>Vertaler/Nalezer</contrib></othercredit>'>
<!ENTITY Bram.Schoenmakers '<othercredit role="translator"><firstname>Bram</firstname><surname>Schoenmakers</surname><affiliation><address><email>bramschoenmakers@kde.nl</email></address></affiliation><contrib>Vertaler/Nalezer</contrib></othercredit>'>
<!ENTITY Tijmen.Baarda '<othercredit role="translator"><firstname>Tijmen</firstname><surname>Baarda</surname><affiliation><address><email>tijmenbaarda@tijgerweb.net</email></address></affiliation><contrib>Vertaler/Nalezer</contrib></othercredit>'>
<!ENTITY Jo.Vermeulen '<othercredit role="translator"><firstname>Jo</firstname><surname>Vermeulen</surname><affiliation><address><email>jo@lumumba.luc.ac.be</email></address></affiliation><contrib>Vertaler/Nalezer</contrib></othercredit>'>
<!ENTITY Frank.Mulder '<othercredit role="translator"><firstname>Frank</firstname><surname>Mulder</surname><affiliation><address><email>frank@kde.nl</email></address></affiliation><contrib>Vertaler/Nalezer</contrib></othercredit>'>
<!ENTITY Jaap.Woldringh '<othercredit role="translator"><firstname>Jaap</firstname><surname>Woldringh</surname><affiliation><address><email>jjhwoldringh op kde punt nl</email></address></affiliation><contrib>Vertaler/Nalezer</contrib></othercredit>'>
<!ENTITY Tom.Albers '<othercredit role="translator"><firstname>Tom</firstname><surname>Albers</surname><affiliation><address><email>tomalbers@kde.nl</email></address></affiliation><contrib>Vertaler/Nalezer</contrib></othercredit>'>
<!ENTITY Rob.Lalau '<othercredit role="translator"><firstname>Rob</firstname><surname>La Lau</surname><affiliation><address><email>rob@OhReally.com</email></address></affiliation><contrib>Vertaler/Nalezer</contrib></othercredit>'>
<!ENTITY Fabrice.Mous '<othercredit role="translator"><firstname>Fabrice</firstname><surname>Mous</surname><affiliation><address><email>fabricemous@kde.nl</email></address></affiliation><contrib>Vertaler/Nalezer</contrib></othercredit>'>
<!ENTITY Sicco.Ens '<othercredit role="translator"><firstname>Sicco</firstname><surname>Ens</surname><affiliation><address><email>sicco.ens@vlinderstichting.nl</email></address></affiliation><contrib>Vertaler/Nalezer</contrib></othercredit>'>
<!ENTITY Marc.Heyvaert '<othercredit role="translator"><firstname>Marc</firstname><surname>Heyvaert</surname><affiliation><address><email>marc_heyvaert@yahoo.com</email></address></affiliation><contrib>Vertaler/Nalezer</contrib></othercredit>'>
<!ENTITY Claire.Lotion '<othercredit role="translator"><firstname>Claire</firstname><surname>Lotion</surname><affiliation><address><email>cmlotion@wanadoo.nl</email></address></affiliation><contrib>Vertaler/Nalezer</contrib></othercredit>'>
<!ENTITY Rijk.van.Wel '<othercredit role="translator"><firstname>Rijk</firstname><surname>van Wel</surname><affiliation><address><email>hotmail@ridge.nl</email></address></affiliation><contrib>Vertaler/Nalezer</contrib></othercredit>'>
<!ENTITY Jonas.Drieghe '<othercredit role="translator"><firstname>Jonas</firstname><surname>Drieghe</surname><affiliation><address><email>jonas_drieghe@skynet.be</email></address></affiliation><contrib>Vertaler/Nalezer</contrib></othercredit>'>
<!ENTITY Pieter.Hoekstra '<othercredit role="translator"><firstname>Pieter</firstname><surname>Hoekstra</surname><affiliation><address><email>pieterhoekstra@tiscali.nl</email></address></affiliation><contrib>Vertaler/Nalezer</contrib></othercredit>'>
<!ENTITY Niels.Luten '<othercredit role="translator"><firstname>Niels</firstname><surname>Luten</surname><affiliation><address><email>mail@niels1.tmfweb.nl</email></address></affiliation><contrib>Vertaler/Nalezer</contrib></othercredit>'>
<!ENTITY Rik.van.Achterberg '<othercredit role="translator"><firstname>Rik</firstname><surname>van Achterberg</surname><affiliation><address><email>rikratva@xs4all.nl</email></address></affiliation><contrib>Vertaler/Nalezer</contrib></othercredit>'>
<!ENTITY Daniel.Huisman '<othercredit role="translator"><firstname>Daniel</firstname><surname>Huisman</surname><affiliation><address><email>phptesten@lycos.nl</email></address></affiliation><contrib>Vertaler/Nalezer</contrib></othercredit>'>
<!ENTITY Sander.Koning '<othercredit role="translator"><firstname>Alexander</firstname><surname>S. Koning</surname><affiliation><address><email>sanderkoning@kde.nl</email></address></affiliation><contrib>Vertaler/Nalezer</contrib></othercredit>'>
<!ENTITY Natalie.Koning '<othercredit role="translator"><firstname>Natalie</firstname><surname>Koning</surname><affiliation><address><email>nat@switch.demon.nl</email></address></affiliation><contrib>Vertaler/Nalezer</contrib></othercredit>'>
<!ENTITY Antoon.Tolboom '<othercredit role="translator"><firstname>Antoon</firstname><surname>Tolboom</surname><affiliation><address><email>atolboo@casema.nl</email></address></affiliation><contrib>Vertaler/Nalezer</contrib></othercredit>'>
<!ENTITY Jordy.Ritico '<othercredit role="translator"><firstname>Jordy</firstname><surname>Ritico</surname><affiliation><address><email>jordyritico@hotmail.com</email></address></affiliation><contrib>Vertaler/Nalezer</contrib></othercredit>'>
<!ENTITY Kristof.Bal '<othercredit role="translator"><firstname>Kristof</firstname><surname>Bal</surname><affiliation><address><email>kristof.bal@gmail.com</email></address></affiliation><contrib>Vertaler/Nalezer</contrib></othercredit>'>
<!ENTITY Wendy.Van.Craen '<othercredit role="translator"><firstname>Wendy</firstname><surname>Van Craen</surname><affiliation><address><email>wendy.vancraen@kde.org</email></address></affiliation><contrib>Vertaler/Nalezer</contrib></othercredit>'>
<!ENTITY Freek.de.Kruijf '<othercredit role="translator"><firstname>Freek</firstname><surname>de Kruijf</surname><affiliation><address><email>freekdekruijf@kde.nl</email></address></affiliation><contrib>Vertaler/Nalezer</contrib></othercredit>'>
<!ENTITY Hannie.Lafeber-Dumoleyn '<othercredit role="translator"><firstname>Hannie</firstname><surname>Lafeber-Dumoleyn</surname><affiliation><address><email>hannie@kde.nl</email></address></affiliation><contrib>Vertaler/Nalezer</contrib></othercredit>'>
<!ENTITY Ruben.Van.Laerhoven '<othercredit role="translator"><firstname>Ruben</firstname><surname>Van Laerhoven</surname><affiliation><address><email>rubentje1991@gmail.com</email></address></affiliation><contrib>Vertaler/Nalezer</contrib></othercredit>'>
<!ENTITY Thom.Castermans '<othercredit role="translator"><firstname>Thom</firstname><surname>Castermans</surname><affiliation><address><email>thomcastermans@kde.nl</email></address></affiliation><contrib>Vertaler/Nalezer</contrib></othercredit>'>
<!ENTITY Ronald.Stroethoff '<othercredit role="translator"><firstname>Ronald</firstname><surname>Stroethoff</surname><affiliation><address><email>stroet43@zonnet.nl</email></address></affiliation><contrib>Vertaler/Nalezer</contrib></othercredit>'>
<!ENTITY kde.nl.groep "<email>i18n@kde.nl</email>">
<!-- Tekstschema's -->
-<!ENTITY meld.fouten '<para>Op- of aanmerkingen over de vertalingen van de toepassing en haar documentatie kunt u melden op <ulink url="http://www.kde.nl/bugs">http://www.kde.nl/bugs</ulink>.</para>'>
+<!ENTITY meld.fouten '<para>Op- of aanmerkingen over de vertalingen van de toepassing en haar documentatie kunt u melden op <ulink url="http://www.kde.nl/bugs">http://www.kde.nl/bugs</ulink>.</para>'>
+<!ENTITY ged.vertaald '<othercredit role=\"translator\"><firstname>Dit document is nog maar gedeeltelijk vertaald.</firstname> <surname>Wilt u meehelpen, stuur een bericht naar:</surname><affiliation><address> <email>kde-i18n-nl@kde.org</email></address></affiliation> <contrib>Nieuwe vertaler</contrib></othercredit>'>
<!-- Entities voor vertalers (voor CREDITS_FOR_TRANSLATORS)-->
<!ENTITY vertaling.bram "<para>Dit document is vertaald in het Nederlands door &Bram.Schoenmakers;.</para>">
<!ENTITY vertaling.douwe "<para>Dit document is vertaald in het Nederlands door &Douwe.vanderSchaaf;.</para>">
<!ENTITY vertaling.floris "<para>Dit document is vertaald in het Nederlands door &Floris.Lambrechts;.</para>">
<!ENTITY vertaling.frank "<para>Dit document is vertaald in het Nederlands door &Frank.Mulder;.</para>">
<!ENTITY vertaling.jaap "<para>Dit document is vertaald in het Nederlands door &Jaap.Woldringh;.</para>">
<!ENTITY vertaling.jo "<para>Dit document is vertaald in het Nederlands door &Jo.Vermeulen;.</para>">
<!ENTITY vertaling.jogchum "<para>Dit document is vertaald in het Nederlands door &Jogchum.Reitsma;.</para>">
<!ENTITY vertaling.michel "<para>Dit document is vertaald in het Nederlands door &Michel.Fontes;.</para>">
<!ENTITY vertaling.niels "<para>Dit document is vertaald in het Nederlands door &Niels.Reedijk;.</para>">
<!ENTITY vertaling.otto "<para>Dit document is vertaald in het Nederlands door &Otto.Bruggeman;.</para>">
<!ENTITY vertaling.richard "<para>Dit document is vertaald in het Nederlands door &Richard.Voss;.</para>">
<!ENTITY vertaling.rinse "<para>Dit document is vertaald in het Nederlands door &Rinse.Devries;.</para>">
<!ENTITY vertaling.rob "<para>Dit document is vertaald in het Nederlands door &Rob.Lalau;.</para>">
<!ENTITY vertaling.tijmen "<para>Dit document is vertaald in het Nederlands door &Tijmen.Baarda;.</para>">
<!ENTITY vertaling.tom "<para>Dit document is vertaald in het Nederlands door &Tom.Albers;.</para>">
<!ENTITY vertaling.fabrice "<para>Dit document is vertaald in het Nederlands door &Fabrice.Mous;.</para>">
<!ENTITY vertaling.sicco "<para>Dit document is vertaald in het Nederlands door &Sicco.Ens;.</para>">
<!ENTITY vertaling.marc "<para>Dit document is vertaald in het Nederlands door &Marc.Heyvaert;.</para>">
<!ENTITY vertaling.claire "<para>Dit document is vertaald in het Nederlands door &Claire.Lotion;.</para>">
<!ENTITY vertaling.rijk "<para>Dit document is vertaald in het Nederlands door &Rijk.van.Wel;.</para>">
<!ENTITY vertaling.jonas "<para>Dit document is vertaald in het Nederlands door &Jonas.Drieghe;.</para>">
<!ENTITY vertaling.pieter "<para>Dit document is vertaald in het Nederlands door &Pieter.Hoekstra;.</para>">
<!ENTITY vertaling.niels.luten "<para>Dit document is vertaald in het Nederlands door &Niels.Luten;.</para>">
<!ENTITY vertaling.rik "<para>Dit document is vertaald in het Nederlands door &Rik.van.Achterberg;.</para>">
<!ENTITY vertaling.daniel "<para>Dit document is vertaald in het Nederlands door &Daniel.Huisman;.</para>">
<!ENTITY vertaling.sander "<para>Dit document is vertaald in het Nederlands door &Sander.Koning;.</para>">
<!ENTITY vertaling.natalie "<para>Dit document is vertaald in het Nederlands door &Natalie.Koning;.</para>">
<!ENTITY vertaling.antoon "<para>Dit document is vertaald in het Nederlands door &Antoon.Tolboom;.</para>">
<!ENTITY vertaling.jordy "<para>Dit document is vertaald in het Nederlands door &Jordy.Ritico;.</para>">
<!ENTITY vertaling.kristof "<para>Dit document is vertaald in het Nederlands door &Kristof.Bal;.</para>">
<!ENTITY vertaling.wendy "<para>Dit document is vertaald in het Nederlands door &Wendy.Van.Craen;.</para>">
<!ENTITY vertaling.freek "<para>Dit document is vertaald in het Nederlands door &Freek.de.Kruijf;.</para>">
<!ENTITY vertaling.hannie "<para>Dit document is vertaald in het Nederlands door &Hannie.Lafeber-Dumoleyn;.</para>">
<!ENTITY vertaling.ruben "<para>Dit document is vertaald in het Nederlands door &Ruben.Van.Laerhoven;.</para>">
<!ENTITY vertaling.thom "<para>Dit document is vertaald in het Nederlands door &Thom.Castermans;.</para>">
<!ENTITY vertaling.ronald "<para>Dit document is vertaald in het Nederlands door &Ronald.Stroethoff;.</para>">
<!-- Gebruiken als template voor nieuwe vertalers/nalezers
<!ENTITY xxx '<othercredit role="translator"><firstname>xx</firstname><surname>xxx</surname><affiliation><address><email>xxx@xxx.xxx</email></address></affiliation><contrib>Vertaler/Nalezer</contrib></othercredit>'>
<!ENTITY vertaling.xxx "<para>Dit document is vertaald in het Nederlands door &xxx;.</para>">
<!ENTITY nagelezen.xxx "<para>De vertaling werd nagelezen door &xxx;.</para>">
-->
<!-- Entities voor nalezers (voor CREDITS_FOR_TRANSLATORS)-->
<!ENTITY nagelezen.bram "<para>De vertaling werd nagelezen door &Bram.Schoenmakers;.</para>">
<!ENTITY nagelezen.douwe "<para>De vertaling werd nagelezen door &Douwe.vanderSchaaf;.</para>">
<!ENTITY nagelezen.floris "<para>De vertaling werd nagelezen door &Floris.Lambrechts;.</para>">
<!ENTITY nagelezen.frank "<para>De vertaling werd nagelezen door &Frank.Mulder;.</para>">
<!ENTITY nagelezen.jaap "<para>De vertaling werd nagelezen door &Jaap.Woldringh;.</para>">
<!ENTITY nagelezen.jo "<para>De vertaling werd nagelezen door &Jo.Vermeulen;.</para>">
<!ENTITY nagelezen.jogchum "<para>De vertaling werd nagelezen door &Jogchum.Reitsma;.</para>">
<!ENTITY nagelezen.michel "<para>De vertaling werd nagelezen door &Michel.Fontes;.</para>">
<!ENTITY nagelezen.niels "<para>De vertaling werd nagelezen door &Niels.Reedijk;.</para>">
<!ENTITY nagelezen.otto "<para>De vertaling werd nagelezen door &Otto.Bruggeman;.</para>">
<!ENTITY nagelezen.richard "<para>De vertaling werd nagelezen door &Richard.Voss;.</para>">
<!ENTITY nagelezen.rinse "<para>De vertaling werd nagelezen door &Rinse.Devries;.</para>">
<!ENTITY nagelezen.rob "<para>De vertaling werd nagelezen door &Rob.Lalau;.</para>">
<!ENTITY nagelezen.tijmen "<para>De vertaling werd nagelezen door &Tijmen.Baarda;.</para>">
<!ENTITY nagelezen.tom "<para>De vertaling werd nagelezen door &Tom.Albers;.</para>">
<!ENTITY nagelezen.fabrice "<para>De vertaling werd nagelezen door &Fabrice.Mous;.</para>">
<!ENTITY nagelezen.sicco "<para>De vertaling werd nagelezen door &Sicco.Ens;.</para>">
<!ENTITY nagelezen.marc "<para>De vertaling werd nagelezen door &Marc.Heyvaert;.</para>">
<!ENTITY nagelezen.claire "<para>De vertaling werd nagelezen door &Claire.Lotion;.</para>">
<!ENTITY nagelezen.rijk "<para>De vertaling werd nagelezen door &Rijk.van.Wel;.</para>">
<!ENTITY nagelezen.jonas "<para>De vertaling werd nagelezen door &Jonas.Drieghe;.</para>">
<!ENTITY nagelezen.pieter "<para>De vertaling werd nagelezen door &Pieter.Hoekstra;.</para>">
<!ENTITY nagelezen.daniel "<para>De vertaling werd nagelezen door &Daniel.Huisman;.</para>">
<!ENTITY nagelezen.sander "<para>De vertaling werd nagelezen door &Sander.Koning;.</para>">
<!ENTITY nagelezen.natalie "<para>De vertaling werd nagelezen door &Natalie.Koning;.</para>">
<!ENTITY nagelezen.antoon "<para>De vertaling werd nagelezen door &Antoon.Tolboom;.</para>">
<!ENTITY nagelezen.jordy "<para>De vertaling werd nagelezen door &Jordy.Ritico;.</para>">
<!ENTITY nagelezen.kristof "<para>De vertaling werd nagelezen door &Kristof.Bal;.</para>">
<!ENTITY nagelezen.wendy "<para>De vertaling werd nagelezen door &Wendy.Van.Craen;.</para>">
<!ENTITY nagelezen.freek "<para>De vertaling werd nagelezen door &Freek.de.Kruijf;.</para>">
<!ENTITY nagelezen.hannie "<para>De vertaling werd nagelezen door &Hannie.Lafeber-Dumoleyn;.</para>">
<!ENTITY nagelezen.ruben "<para>De vertaling werd nagelezen door &Ruben.Van.Laerhoven;.</para>">
<!ENTITY nagelezen.thom "<para>De vertaling werd nagelezen door &Thom.Castermans;.</para>">
<!ENTITY nagelezen.ronald "<para>De vertaling werd nagelezen door &Ronald.Stroethoff;.</para>">
<!-- Algemene afkortingen -->
<!ENTITY Alt "<keycap>Alt</keycap>">
<!ENTITY Tab "<keycap>Tab</keycap>">
<!ENTITY bijv "<abbrev>bijv.</abbrev>">
<!ENTITY Bijv "<abbrev>Bijv.</abbrev>">
<!ENTITY eg "<abbrev>bijv.</abbrev>">
<!ENTITY ie "<abbrev>bijv.</abbrev>">
<!ENTITY Ctrl "<keycap>Ctrl</keycap>">
<!ENTITY enz "<abbrev>enz.</abbrev>">
<!ENTITY etc "<abbrev>etc.</abbrev>"> <!-- enz is beter -->
<!ENTITY kicon "<guiicon>
<inlinemediaobject>
<objectinfo><title>&kde;'s K-pictogram</title></objectinfo>
<imageobject><imagedata fileref='icon-file-to-be-filled-in' format='PNG'/></imageobject>
<textobject><phrase>K</phrase></textobject>
</inlinemediaobject>
</guiicon>"><!-- if image is localised, then entityref should be used instead
of fileref -->
<!ENTITY kmenu "<guimenu>K</guimenu>-menu">
<!ENTITY LMB "<mousebutton>linkermuisknop</mousebutton>">
<!ENTITY MMB "<mousebutton>middelstemuisknop</mousebutton>">
<!ENTITY RMB "<mousebutton>rechtermuisknop</mousebutton>">
<!ENTITY Shift "<keycap>Shift</keycap>">
<!ENTITY Backspace "<keycap>Backspace</keycap>">
<!ENTITY Esc "<keycap>Escape</keycap>">
<!ENTITY Enter "<keycap>Enter</keycap>">
<!ENTITY FAQ "<acronym>FAQ</acronym>">
<!ENTITY HTML "<acronym>HTML</acronym>">
<!ENTITY OS "<acronym>OS</acronym>">
<!ENTITY Mac '<trademark class="registered">Mac</trademark>'>
<!ENTITY cdrom "<acronym>cd-rom</acronym>">
<!-- Entities voor applicaties die niet dezelfde naam hebben -->
<!-- hiervoor gebruik je beter de vertaalde naam -->
<!ENTITY kcontrolcenter "<application>KDE Configuratiecentrum</application>">
<!ENTITY kcc "<application>KDE Configuratiecentrum</application>">
<!ENTITY khc "<application>KDE Helpcentrum</application>">
<!ENTITY kic "<application>KDE Informatiecentrum</application>">
<!ENTITY palm "PalmOS(tm)-apparaat">
<!ENTITY systemsettings "<application>Systeeminstellingen</application>">
<!ENTITY systemtray "<application>Systeemvak</application>">
diff --git a/khtml/css/cssparser.cpp b/khtml/css/cssparser.cpp
index 7559db323f..c7472ba48c 100644
--- a/khtml/css/cssparser.cpp
+++ b/khtml/css/cssparser.cpp
@@ -1,2981 +1,2984 @@
/*
* This file is part of the DOM implementation for KDE.
*
* Copyright 2003 Lars Knoll (knoll@kde.org)
* Copyright 2005 Allan Sandfeld Jensen (kde@carewolf.com)
* Copyright (C) 2004, 2005, 2006, 2007 Apple Computer, Inc.
* Copyright (C) 2008 Maksim Orlovich <maksim@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
// #define CSS_DEBUG
// #define TOKEN_DEBUG
#define YYDEBUG 0
#include "cssparser.h"
#include <kdebug.h>
#include <kglobal.h>
#include <kurl.h>
#include "css_valueimpl.h"
#include "css_ruleimpl.h"
#include "css_stylesheetimpl.h"
#include "css_mediaquery.h"
#include "cssproperties.h"
#include "cssvalues.h"
#include "csshelper.h"
#include <misc/helper.h>
#include <stdlib.h>
#include <assert.h>
using namespace DOM;
// used to promote background: left to left center
#define BACKGROUND_SKIP_CENTER( num ) \
if ( !pos_ok[ num ] && expected != 1 ) { \
pos_ok[num] = true; \
pos[num] = 0; \
skip_next = false; \
}
ValueList::~ValueList()
{
unsigned numValues = m_values.size();
for (unsigned i = 0; i < numValues; i++)
if (m_values[i].unit == Value::Function)
delete m_values[i].function;
}
namespace {
class ShorthandScope {
public:
ShorthandScope(CSSParser* parser, int propId) : m_parser(parser)
{
if (!(m_parser->m_inParseShorthand++))
m_parser->m_currentShorthand = propId;
}
~ShorthandScope()
{
if (!(--m_parser->m_inParseShorthand))
m_parser->m_currentShorthand = 0;
}
private:
CSSParser* m_parser;
};
}
using namespace DOM;
#if YYDEBUG > 0
extern int cssyydebug;
#endif
extern int cssyyparse( void * parser );
CSSParser *CSSParser::currentParser = 0;
CSSParser::CSSParser( bool strictParsing )
{
#ifdef CSS_DEBUG
kDebug( 6080 ) << "CSSParser::CSSParser this=" << this;
#endif
strict = strictParsing;
parsedProperties = (CSSProperty **) malloc( 32 * sizeof( CSSProperty * ) );
numParsedProperties = 0;
maxParsedProperties = 32;
data = 0;
valueList = 0;
rule = 0;
id = 0;
important = false;
m_inParseShorthand = 0;
m_currentShorthand = 0;
m_implicitShorthand = false;
yy_start = 1;
#if YYDEBUG > 0
cssyydebug = 1;
#endif
}
CSSParser::~CSSParser()
{
if ( numParsedProperties )
clearProperties();
free( parsedProperties );
delete valueList;
#ifdef CSS_DEBUG
kDebug( 6080 ) << "CSSParser::~CSSParser this=" << this;
#endif
free( data );
}
unsigned int CSSParser::defaultNamespace()
{
if (styleElement && styleElement->isCSSStyleSheet())
return static_cast<CSSStyleSheetImpl*>(styleElement)->defaultNamespace();
else
return anyNamespace;
}
void CSSParser::runParser()
{
CSSParser* old = currentParser;
currentParser = this;
cssyyparse(this);
currentParser = old;
boundLocalNames.clear();
}
void CSSParser::setupParser(const char *prefix, const DOMString &string, const char *suffix)
{
unsigned preflen = strlen(prefix);
unsigned sufflen = strlen(suffix);
int length = string.length() + preflen + sufflen + 8;
free(data);
data = (unsigned short *)malloc( length *sizeof( unsigned short ) );
for (unsigned i = 0; i < preflen; i++)
data[i] = prefix[i];
memcpy(data + preflen, string.unicode(), string.length()*sizeof( unsigned short));
unsigned start = preflen + string.length();
unsigned end = start + sufflen;
for (unsigned i = start; i < end; i++)
data[i] = suffix[i - start];
// the flex scanner sometimes give invalid reads for any
// smaller padding - try e.g. css/invalid-rules-005.html or see #167318
data[length - 1] = 0;
data[length - 2] = 0;
data[length - 3] = 0;
data[length - 4] = 0;
data[length - 5] = 0;
data[length - 6] = 0;
data[length - 7] = 0;
data[length - 8] = 0;
yyTok = -1;
block_nesting = 0;
yy_hold_char = 0;
yyleng = 0;
yytext = yy_c_buf_p = data;
yy_hold_char = *yy_c_buf_p;
}
void CSSParser::parseSheet( CSSStyleSheetImpl *sheet, const DOMString &string )
{
styleElement = sheet;
styleDocument = 0;
setupParser("", string, "");
#ifdef CSS_DEBUG
kDebug( 6080 ) << ">>>>>>> start parsing style sheet";
#endif
runParser();
#ifdef CSS_DEBUG
kDebug( 6080 ) << "<<<<<<< done parsing style sheet";
#endif
delete rule;
rule = 0;
}
CSSRuleImpl *CSSParser::parseRule( DOM::CSSStyleSheetImpl *sheet, const DOM::DOMString &string )
{
styleElement = sheet;
styleDocument = 0;
setupParser("@-khtml-rule{", string, "} ");
runParser();
CSSRuleImpl *result = rule;
rule = 0;
return result;
}
static void addParsedProperties( DOM::CSSStyleDeclarationImpl *declaration, CSSProperty** parsedProperties,
int numProperties)
{
for ( int i = 0; i < numProperties; i++ ) {
// Only add properties that have no !important counterpart present
if (!declaration->getPropertyPriority(parsedProperties[i]->id()) || parsedProperties[i]->isImportant()) {
declaration->removeProperty(parsedProperties[i]->m_id);
declaration->values()->append( parsedProperties[i] );
}
}
}
bool CSSParser::parseValue( DOM::CSSStyleDeclarationImpl *declaration, int _id, const DOM::DOMString &string,
bool _important)
{
#ifdef CSS_DEBUG
kDebug( 6080 ) << "CSSParser::parseValue: id=" << _id << " important=" << _important
<< " value='" << string.string() << "'" << endl;
#endif
styleElement = declaration->stylesheet();
styleDocument = 0;
setupParser("@-khtml-value{", string, "} ");
id = _id;
important = _important;
runParser();
delete rule;
rule = 0;
bool ok = false;
if ( numParsedProperties ) {
ok = true;
addParsedProperties(declaration, parsedProperties, numParsedProperties);
numParsedProperties = 0;
}
return ok;
}
bool CSSParser::parseDeclaration( DOM::CSSStyleDeclarationImpl *declaration, const DOM::DOMString &string)
{
#ifdef CSS_DEBUG
kDebug( 6080 ) << "CSSParser::parseDeclaration:"
<< " value='" << string.string() << "'" << endl;
#endif
styleElement = declaration->stylesheet();
styleDocument = 0;
setupParser("@-khtml-decls{", string, "} ");
runParser();
delete rule;
rule = 0;
bool ok = false;
if ( numParsedProperties ) {
ok = true;
addParsedProperties(declaration, parsedProperties, numParsedProperties);
numParsedProperties = 0;
}
return ok;
}
bool CSSParser::parseMediaQuery(DOM::MediaListImpl* queries, const DOM::DOMString& string)
{
if (string.isEmpty() || string.isNull()) {
return true;
}
mediaQuery = 0;
// can't use { because tokenizer state switches from mediaquery to initial state when it sees { token.
// instead insert one " " (which is S in parser.y)
setupParser ("@-khtml-mediaquery ", string, "} ");
runParser();
bool ok = false;
if (mediaQuery) {
ok = true;
queries->appendMediaQuery(mediaQuery);
mediaQuery = 0;
}
return ok;
}
QList<DOM::CSSSelector*> CSSParser::parseSelectorList(DOM::DocumentImpl* doc, const DOM::DOMString& string)
{
styleElement = 0;
styleDocument = doc;
selectors.clear();
setupParser("@-khtml-selectors{", string, "} ");
runParser();
// Make sure to detect problems with pseudos, too
bool ok = true;
for (int i = 0; i < selectors.size(); ++i) {
// we need to check not only us, but also other things we're connected to via
// combinators
for (DOM::CSSSelector* sel = selectors[i]; sel; sel = sel->tagHistory) {
if(sel->match == CSSSelector::PseudoClass || sel->match == CSSSelector::PseudoElement) {
if (sel->pseudoType() == CSSSelector::PseudoOther) {
ok = false;
break;
}
}
}
}
if (!ok) {
qDeleteAll(selectors);
selectors.clear();
}
return selectors;
}
void CSSParser::addProperty( int propId, CSSValueImpl *value, bool important )
{
CSSProperty *prop = new CSSProperty;
prop->m_id = propId;
prop->setValue( value );
prop->m_important = important;
prop->m_implicit = m_implicitShorthand;
if ( numParsedProperties >= maxParsedProperties ) {
maxParsedProperties += 32;
parsedProperties = (CSSProperty **) realloc( parsedProperties,
maxParsedProperties*sizeof( CSSProperty * ) );
}
parsedProperties[numParsedProperties++] = prop;
}
CSSStyleDeclarationImpl *CSSParser::createStyleDeclaration( CSSStyleRuleImpl *rule )
{
QList<CSSProperty*> *propList = new QList<CSSProperty*>;
for ( int i = 0; i < numParsedProperties; i++ )
propList->append( parsedProperties[i] );
numParsedProperties = 0;
return new CSSStyleDeclarationImpl(rule, propList);
}
CSSStyleDeclarationImpl *CSSParser::createFontFaceStyleDeclaration( CSSFontFaceRuleImpl *rule )
{
QList<CSSProperty*> *propList = new QList<CSSProperty*>;
for ( int i = 0; i < numParsedProperties; i++ ) {
CSSProperty* property = parsedProperties[i];
int id = property->id();
if ((id == CSS_PROP_FONT_WEIGHT || id == CSS_PROP_FONT_STYLE || id == CSS_PROP_FONT_VARIANT) && property->value()->isPrimitiveValue()) {
// change those to a list of values containing a single value, so that we may always cast to a list in the CSSFontSelector.
CSSValueImpl* value = property->value();
value->ref();
property->setValue( new CSSValueListImpl(CSSValueListImpl::Comma) );
static_cast<CSSValueListImpl*>(property->value())->append(value);
value->deref();
}
propList->append( parsedProperties[i] );
}
numParsedProperties = 0;
return new CSSStyleDeclarationImpl(rule, propList);
}
void CSSParser::clearProperties()
{
for ( int i = 0; i < numParsedProperties; i++ )
delete parsedProperties[i];
numParsedProperties = 0;
}
DOM::DocumentImpl *CSSParser::document() const
{
if (!styleDocument) {
const StyleBaseImpl* root = styleElement;
while (root->parent())
root = root->parent();
if (root->isCSSStyleSheet())
styleDocument = static_cast<const CSSStyleSheetImpl*>(root)->doc();
}
return styleDocument;
}
bool CSSParser::validUnit( Value *value, int unitflags, bool strict )
{
if ( unitflags & FNonNeg && value->fValue < 0 )
return false;
bool b = false;
switch( value->unit ) {
case CSSPrimitiveValue::CSS_NUMBER:
b = (unitflags & FNumber);
if ( !b && ( (unitflags & FLength) && (value->fValue == 0 || !strict ) ) ) {
value->unit = CSSPrimitiveValue::CSS_PX;
b = true;
}
if (!b && (unitflags & FInteger) && value->isInt)
b = true;
break;
case CSSPrimitiveValue::CSS_PERCENTAGE:
b = (unitflags & FPercent);
break;
case Value::Q_EMS:
case CSSPrimitiveValue::CSS_EMS:
case CSSPrimitiveValue::CSS_EXS:
case CSSPrimitiveValue::CSS_PX:
case CSSPrimitiveValue::CSS_CM:
case CSSPrimitiveValue::CSS_MM:
case CSSPrimitiveValue::CSS_IN:
case CSSPrimitiveValue::CSS_PT:
case CSSPrimitiveValue::CSS_PC:
b = (unitflags & FLength);
break;
case CSSPrimitiveValue::CSS_MS:
case CSSPrimitiveValue::CSS_S:
b = (unitflags & FTime);
break;
case CSSPrimitiveValue::CSS_DEG:
case CSSPrimitiveValue::CSS_RAD:
case CSSPrimitiveValue::CSS_GRAD:
case CSSPrimitiveValue::CSS_HZ:
case CSSPrimitiveValue::CSS_KHZ:
case CSSPrimitiveValue::CSS_DPI:
case CSSPrimitiveValue::CSS_DPCM:
case CSSPrimitiveValue::CSS_DIMENSION:
default:
break;
}
return b;
}
bool CSSParser::parseValue( int propId, bool important )
{
if ( !valueList ) return false;
Value *value = valueList->current();
if ( !value )
return false;
int id = value->id;
int num = inShorthand() ? 1 : valueList->size();
if ( id == CSS_VAL_INHERIT ) {
if (num != 1)
return false;
addProperty( propId, new CSSInheritedValueImpl(), important );
return true;
} else if (id == CSS_VAL_INITIAL ) {
if (num != 1)
return false;
addProperty(propId, new CSSInitialValueImpl(false/*implicit initial*/), important);
return true;
}
bool valid_primitive = false;
CSSValueImpl *parsedValue = 0;
switch(propId) {
/* The comment to the left defines all valid value of this properties as defined
* in CSS 2, Appendix F. Property index
*/
/* All the CSS properties are not supported by the renderer at the moment.
* Note that all the CSS2 Aural properties are only checked, if CSS_AURAL is defined
* (see parseAuralValues). As we don't support them at all this seems reasonable.
*/
case CSS_PROP_SIZE: // <length>{1,2} | auto | portrait | landscape | inherit
// case CSS_PROP_PAGE: // <identifier> | auto // ### CHECK
// ### To be done
if (id)
valid_primitive = true;
break;
case CSS_PROP_UNICODE_BIDI: // normal | embed | bidi-override | inherit
if ( id == CSS_VAL_NORMAL ||
id == CSS_VAL_EMBED ||
id == CSS_VAL_BIDI_OVERRIDE )
valid_primitive = true;
break;
case CSS_PROP_POSITION: // static | relative | absolute | fixed | inherit
if ( id == CSS_VAL_STATIC ||
id == CSS_VAL_RELATIVE ||
id == CSS_VAL_ABSOLUTE ||
id == CSS_VAL_FIXED )
valid_primitive = true;
break;
case CSS_PROP_PAGE_BREAK_AFTER: // auto | always | avoid | left | right | inherit
case CSS_PROP_PAGE_BREAK_BEFORE: // auto | always | avoid | left | right | inherit
if ( id == CSS_VAL_AUTO ||
id == CSS_VAL_ALWAYS ||
id == CSS_VAL_AVOID ||
id == CSS_VAL_LEFT ||
id == CSS_VAL_RIGHT )
valid_primitive = true;
break;
case CSS_PROP_PAGE_BREAK_INSIDE: // avoid | auto | inherit
if ( id == CSS_VAL_AUTO ||
id == CSS_VAL_AVOID )
valid_primitive = true;
break;
case CSS_PROP_EMPTY_CELLS: // show | hide | inherit
if ( id == CSS_VAL_SHOW ||
id == CSS_VAL_HIDE )
valid_primitive = true;
break;
case CSS_PROP_QUOTES: // [<string> <string>]+ | none | inherit
if (id == CSS_VAL_NONE) {
valid_primitive = true;
} else {
QuotesValueImpl *quotes = new QuotesValueImpl;
bool is_valid = true;
QString open, close;
Value *val=valueList->current();
while (val) {
if (val->unit == CSSPrimitiveValue::CSS_STRING)
open = qString(val->string);
else {
is_valid = false;
break;
}
valueList->next();
val=valueList->current();
if (val && val->unit == CSSPrimitiveValue::CSS_STRING)
close = qString(val->string);
else {
is_valid = false;
break;
}
quotes->addLevel(open, close);
valueList->next();
val=valueList->current();
}
if (is_valid)
parsedValue = quotes;
else
delete quotes;
}
break;
case CSS_PROP_CONTENT: // normal | none | inherit |
// [ <string> | <uri> | <counter> | attr(X) | open-quote | close-quote | no-open-quote | no-close-quote ]+
if ( id == CSS_VAL_NORMAL || id == CSS_VAL_NONE)
valid_primitive = true;
else
return parseContent( propId, important );
break;
case CSS_PROP_WHITE_SPACE: // normal | pre | nowrap | pre-wrap | pre-line | inherit
if ( id == CSS_VAL_NORMAL ||
id == CSS_VAL_PRE ||
id == CSS_VAL_PRE_WRAP ||
id == CSS_VAL_PRE_LINE ||
id == CSS_VAL_NOWRAP )
valid_primitive = true;
break;
case CSS_PROP_CLIP: // <shape> | auto | inherit
if ( id == CSS_VAL_AUTO )
valid_primitive = true;
else if ( value->unit == Value::Function )
return parseShape( propId, important );
break;
/* Start of supported CSS properties with validation. This is needed for parseShortHand to work
* correctly and allows optimization in khtml::applyRule(..)
*/
case CSS_PROP_CAPTION_SIDE: // top | bottom | left | right | inherit
// Left and right were deprecated in CSS 2.1 and never supported by KHTML
if ( /* id == CSS_VAL_LEFT || id == CSS_VAL_RIGHT || */
id == CSS_VAL_TOP || id == CSS_VAL_BOTTOM)
valid_primitive = true;
break;
case CSS_PROP_BORDER_COLLAPSE: // collapse | separate | inherit
if ( id == CSS_VAL_COLLAPSE || id == CSS_VAL_SEPARATE )
valid_primitive = true;
break;
case CSS_PROP_VISIBILITY: // visible | hidden | collapse | inherit
if (id == CSS_VAL_VISIBLE || id == CSS_VAL_HIDDEN || id == CSS_VAL_COLLAPSE)
valid_primitive = true;
break;
case CSS_PROP_OVERFLOW: // visible | hidden | scroll | auto | marquee | inherit
case CSS_PROP_OVERFLOW_X:
case CSS_PROP_OVERFLOW_Y:
if (id == CSS_VAL_VISIBLE || id == CSS_VAL_HIDDEN || id == CSS_VAL_SCROLL || id == CSS_VAL_AUTO ||
id == CSS_VAL_MARQUEE)
valid_primitive = true;
break;
case CSS_PROP_LIST_STYLE_POSITION: // inside | outside | inherit
if ( id == CSS_VAL_INSIDE || id == CSS_VAL_OUTSIDE )
valid_primitive = true;
break;
case CSS_PROP_LIST_STYLE_TYPE:
// disc | circle | square | decimal | decimal-leading-zero | lower-roman |
// upper-roman | lower-greek | lower-alpha | lower-latin | upper-alpha |
// upper-latin | hebrew | armenian | georgian | cjk-ideographic | hiragana |
// katakana | hiragana-iroha | katakana-iroha | none | inherit
if ((id >= CSS_VAL_DISC && id <= CSS_VAL__KHTML_CLOSE_QUOTE) || id == CSS_VAL_NONE)
valid_primitive = true;
break;
case CSS_PROP_DISPLAY:
// inline | block | list-item | run-in | inline-block | -khtml-ruler | table |
// inline-table | table-row-group | table-header-group | table-footer-group | table-row |
// table-column-group | table-column | table-cell | table-caption | none | inherit
if ((id >= CSS_VAL_INLINE && id <= CSS_VAL_TABLE_CAPTION) || id == CSS_VAL_NONE)
valid_primitive = true;
break;
case CSS_PROP_DIRECTION: // ltr | rtl | inherit
if ( id == CSS_VAL_LTR || id == CSS_VAL_RTL )
valid_primitive = true;
break;
case CSS_PROP_TEXT_TRANSFORM: // capitalize | uppercase | lowercase | none | inherit
if ((id >= CSS_VAL_CAPITALIZE && id <= CSS_VAL_LOWERCASE) || id == CSS_VAL_NONE)
valid_primitive = true;
break;
case CSS_PROP_FLOAT: // left | right | none | khtml_left | khtml_right | inherit + center for buggy CSS
if ( id == CSS_VAL_LEFT || id == CSS_VAL_RIGHT || id == CSS_VAL__KHTML_LEFT ||
id == CSS_VAL__KHTML_RIGHT ||id == CSS_VAL_NONE || id == CSS_VAL_CENTER)
valid_primitive = true;
break;
case CSS_PROP_CLEAR: // none | left | right | both | inherit
if ( id == CSS_VAL_NONE || id == CSS_VAL_LEFT ||
id == CSS_VAL_RIGHT|| id == CSS_VAL_BOTH)
valid_primitive = true;
break;
case CSS_PROP_TEXT_ALIGN:
// left | right | center | justify | khtml_left | khtml_right | khtml_center | <string> | inherit
if ( ( id >= CSS_VAL__KHTML_AUTO && id <= CSS_VAL__KHTML_CENTER ) ||
value->unit == CSSPrimitiveValue::CSS_STRING )
valid_primitive = true;
break;
case CSS_PROP_OUTLINE_STYLE: // <border-style> | inherit
case CSS_PROP_BORDER_TOP_STYLE: //// <border-style> | inherit
case CSS_PROP_BORDER_RIGHT_STYLE: // Defined as: none | hidden | dotted | dashed |
case CSS_PROP_BORDER_BOTTOM_STYLE: // solid | double | groove | ridge | inset | outset | -khtml-native
case CSS_PROP_BORDER_LEFT_STYLE: ////
if (id >= CSS_VAL__KHTML_NATIVE && id <= CSS_VAL_DOUBLE)
valid_primitive = true;
break;
case CSS_PROP_FONT_WEIGHT: // normal | bold | bolder | lighter | 100 | 200 | 300 | 400 |
// 500 | 600 | 700 | 800 | 900 | inherit
if (id >= CSS_VAL_NORMAL && id <= CSS_VAL_900) {
// Already correct id
valid_primitive = true;
} else if ( validUnit( value, FInteger|FNonNeg, false ) ) {
int weight = (int)value->fValue;
if ( (weight % 100) )
break;
weight /= 100;
if ( weight >= 1 && weight <= 9 ) {
id = CSS_VAL_100 + weight - 1;
valid_primitive = true;
}
}
break;
case CSS_PROP__KHTML_BORDER_TOP_RIGHT_RADIUS:
case CSS_PROP__KHTML_BORDER_BOTTOM_RIGHT_RADIUS:
case CSS_PROP__KHTML_BORDER_BOTTOM_LEFT_RADIUS:
case CSS_PROP__KHTML_BORDER_TOP_LEFT_RADIUS:
case CSS_PROP_BORDER_TOP_RIGHT_RADIUS:
case CSS_PROP_BORDER_BOTTOM_RIGHT_RADIUS:
case CSS_PROP_BORDER_BOTTOM_LEFT_RADIUS:
case CSS_PROP_BORDER_TOP_LEFT_RADIUS: {
//<length> <length>?
if (num < 1 || num > 2)
return false;
if (!validUnit( value, FLength|FNonNeg, strict))
return false;
CSSPrimitiveValueImpl* horiz = new CSSPrimitiveValueImpl( value->fValue,
(CSSPrimitiveValue::UnitTypes) value->unit );
CSSPrimitiveValueImpl* vert;
if (num == 2) {
value = valueList->next();
if (!validUnit( value, FLength|FNonNeg, strict)) {
delete horiz;
return false;
}
vert = new CSSPrimitiveValueImpl( value->fValue, (CSSPrimitiveValue::UnitTypes) value->unit );
} else {
vert = horiz;
}
addProperty(propId, new CSSPrimitiveValueImpl(new PairImpl(horiz, vert)), important);
return true;
}
case CSS_PROP__KHTML_BORDER_RADIUS:
case CSS_PROP_BORDER_RADIUS:
return parseBorderRadius(important);
case CSS_PROP_BORDER_SPACING:
{
const int properties[2] = { CSS_PROP__KHTML_BORDER_HORIZONTAL_SPACING,
CSS_PROP__KHTML_BORDER_VERTICAL_SPACING };
if (num == 1) {
ShorthandScope scope(this, CSS_PROP_BORDER_SPACING);
if (!parseValue(properties[0], important)) return false;
CSSValueImpl* value = parsedProperties[numParsedProperties-1]->value();
addProperty(properties[1], value, important);
return true;
}
else if (num == 2) {
ShorthandScope scope(this, CSS_PROP_BORDER_SPACING);
if (!parseValue(properties[0], important)) return false;
if (!parseValue(properties[1], important)) return false;
return true;
}
return false;
}
case CSS_PROP__KHTML_BORDER_HORIZONTAL_SPACING:
case CSS_PROP__KHTML_BORDER_VERTICAL_SPACING:
valid_primitive = validUnit(value, FLength|FNonNeg, strict);
break;
case CSS_PROP_SCROLLBAR_FACE_COLOR: // IE5.5
case CSS_PROP_SCROLLBAR_SHADOW_COLOR: // IE5.5
case CSS_PROP_SCROLLBAR_HIGHLIGHT_COLOR: // IE5.5
case CSS_PROP_SCROLLBAR_3DLIGHT_COLOR: // IE5.5
case CSS_PROP_SCROLLBAR_DARKSHADOW_COLOR: // IE5.5
case CSS_PROP_SCROLLBAR_TRACK_COLOR: // IE5.5
case CSS_PROP_SCROLLBAR_ARROW_COLOR: // IE5.5
case CSS_PROP_SCROLLBAR_BASE_COLOR: // IE5.5
if ( strict )
break;
/* nobreak */
case CSS_PROP_OUTLINE_COLOR: // <color> | invert | inherit
// outline has "invert" as additional keyword.
if ( propId == CSS_PROP_OUTLINE_COLOR && id == CSS_VAL_INVERT ) {
valid_primitive = true;
break;
}
/* nobreak */
case CSS_PROP_BACKGROUND_COLOR: // <color> | inherit
case CSS_PROP_BORDER_TOP_COLOR: // <color> | inherit
case CSS_PROP_BORDER_RIGHT_COLOR: // <color> | inherit
case CSS_PROP_BORDER_BOTTOM_COLOR: // <color> | inherit
case CSS_PROP_BORDER_LEFT_COLOR: // <color> | inherit
case CSS_PROP_COLOR: // <color> | inherit
if ( id == CSS_VAL__KHTML_TEXT || id == CSS_VAL_MENU ||
(id >= CSS_VAL_AQUA && id <= CSS_VAL_WINDOWTEXT ) ||
id == CSS_VAL_TRANSPARENT ||
id == CSS_VAL_CURRENTCOLOR ||
(id >= CSS_VAL_GREY && id < CSS_VAL__KHTML_TEXT && !strict ) ) {
valid_primitive = true;
} else {
parsedValue = parseColor();
if ( parsedValue )
valueList->next();
}
break;
case CSS_PROP_CURSOR:
// [ auto | default | none |
// context-menu | help | pointer | progress | wait |
// cell | crosshair | text | vertical-text |
// alias | copy | move | no-drop | not-allowed |
// e-resize | ne-resize | nw-resize | n-resize | se-resize | sw-resize | s-resize | w-resize |
// ew-resize | ns-resize | nesw-resize | nwse-resize |
// col-resize | row-resize | all-scroll
// ] ] | inherit
// MSIE 5 compatibility :/
if ( !strict && id == CSS_VAL_HAND ) {
id = CSS_VAL_POINTER;
valid_primitive = true;
} else if (( id >= CSS_VAL_AUTO && id <= CSS_VAL_ALL_SCROLL ) || id == CSS_VAL_NONE )
valid_primitive = true;
break;
case CSS_PROP_BACKGROUND_ATTACHMENT:
case CSS_PROP__KHTML_BACKGROUND_CLIP:
case CSS_PROP_BACKGROUND_CLIP:
case CSS_PROP_BACKGROUND_IMAGE:
case CSS_PROP__KHTML_BACKGROUND_ORIGIN:
case CSS_PROP_BACKGROUND_ORIGIN:
case CSS_PROP_BACKGROUND_POSITION:
case CSS_PROP_BACKGROUND_POSITION_X:
case CSS_PROP_BACKGROUND_POSITION_Y:
case CSS_PROP__KHTML_BACKGROUND_SIZE:
case CSS_PROP_BACKGROUND_SIZE:
case CSS_PROP_BACKGROUND_REPEAT: {
CSSValueImpl *val1 = 0, *val2 = 0;
int propId1, propId2;
if (parseBackgroundProperty(propId, propId1, propId2, val1, val2)) {
addProperty(propId1, val1, important);
if (val2)
addProperty(propId2, val2, important);
return true;
}
return false;
}
case CSS_PROP_LIST_STYLE_IMAGE: // <uri> | none | inherit
if (id == CSS_VAL_NONE) {
parsedValue = new CSSImageValueImpl();
valueList->next();
}
else if (value->unit == CSSPrimitiveValue::CSS_URI ) {
// ### allow string in non strict mode?
DOMString uri = domString( value->string );
if (!uri.isNull()) {
parsedValue = new CSSImageValueImpl( uri, styleElement );
valueList->next();
}
}
break;
case CSS_PROP_OUTLINE_WIDTH: // <border-width> | inherit
case CSS_PROP_BORDER_TOP_WIDTH: //// <border-width> | inherit
case CSS_PROP_BORDER_RIGHT_WIDTH: // Which is defined as
case CSS_PROP_BORDER_BOTTOM_WIDTH: // thin | medium | thick | <length>
case CSS_PROP_BORDER_LEFT_WIDTH: ////
if (id == CSS_VAL_THIN || id == CSS_VAL_MEDIUM || id == CSS_VAL_THICK)
valid_primitive = true;
else
valid_primitive = ( validUnit( value, FLength, strict ) );
break;
case CSS_PROP_LETTER_SPACING: // normal | <length> | inherit
case CSS_PROP_WORD_SPACING: // normal | <length> | inherit
if ( id == CSS_VAL_NORMAL )
valid_primitive = true;
else
valid_primitive = validUnit( value, FLength, strict );
break;
case CSS_PROP_TEXT_INDENT: // <length> | <percentage> | inherit
valid_primitive = ( !id && validUnit( value, FLength|FPercent, strict ) );
break;
case CSS_PROP_PADDING_TOP: // <length> | <percentage> | inherit
case CSS_PROP_PADDING_RIGHT: // <padding-width> | inherit
case CSS_PROP_PADDING_BOTTOM: // Which is defined as
case CSS_PROP_PADDING_LEFT: // <length> | <percentage>
case CSS_PROP__KHTML_PADDING_START:
valid_primitive = ( !id && validUnit( value, FLength|FPercent|FNonNeg, strict ) );
break;
case CSS_PROP_MAX_HEIGHT: // <length> | <percentage> | none | inherit
case CSS_PROP_MAX_WIDTH: // <length> | <percentage> | none | inherit
if ( id == CSS_VAL_NONE ) {
valid_primitive = true;
break;
}
/* nobreak */
case CSS_PROP_MIN_HEIGHT: // <length> | <percentage> | inherit
case CSS_PROP_MIN_WIDTH: // <length> | <percentage> | inherit
valid_primitive = ( !id && validUnit( value, FLength|FPercent|FNonNeg, strict ) );
break;
case CSS_PROP_FONT_SIZE:
// <absolute-size> | <relative-size> | <length> | <percentage> | inherit
if (id >= CSS_VAL_XX_SMALL && id <= CSS_VAL_LARGER)
valid_primitive = true;
else
valid_primitive = ( validUnit( value, FLength|FPercent, strict ) );
break;
case CSS_PROP_FONT_STYLE: // normal | italic | oblique | inherit
if ( id == CSS_VAL_NORMAL || id == CSS_VAL_ITALIC || id == CSS_VAL_OBLIQUE)
valid_primitive = true;
break;
case CSS_PROP_FONT_VARIANT: // normal | small-caps | inherit
if ( id == CSS_VAL_NORMAL || id == CSS_VAL_SMALL_CAPS)
valid_primitive = true;
break;
case CSS_PROP_VERTICAL_ALIGN:
// baseline | sub | super | top | text-top | middle | bottom | text-bottom |
// <percentage> | <length> | inherit
if ( id >= CSS_VAL_BASELINE && id <= CSS_VAL__KHTML_BASELINE_MIDDLE )
valid_primitive = true;
else
valid_primitive = ( !id && validUnit( value, FLength|FPercent, strict ) );
break;
case CSS_PROP_HEIGHT: // <length> | <percentage> | auto | inherit
case CSS_PROP_WIDTH: // <length> | <percentage> | auto | inherit
if ( id == CSS_VAL_AUTO )
valid_primitive = true;
else
// ### handle multilength case where we allow relative units
valid_primitive = ( !id && validUnit( value, FLength|FPercent|FNonNeg, strict ) );
break;
case CSS_PROP_BOTTOM: // <length> | <percentage> | auto | inherit
case CSS_PROP_LEFT: // <length> | <percentage> | auto | inherit
case CSS_PROP_RIGHT: // <length> | <percentage> | auto | inherit
case CSS_PROP_TOP: // <length> | <percentage> | auto | inherit
case CSS_PROP_MARGIN_TOP: //// <margin-width> | inherit
case CSS_PROP_MARGIN_RIGHT: // Which is defined as
case CSS_PROP_MARGIN_BOTTOM: // <length> | <percentage> | auto | inherit
case CSS_PROP_MARGIN_LEFT: ////
case CSS_PROP__KHTML_MARGIN_START:
if ( id == CSS_VAL_AUTO )
valid_primitive = true;
else
valid_primitive = ( !id && validUnit( value, FLength|FPercent, strict ) );
break;
case CSS_PROP_Z_INDEX: // auto | <integer> | inherit
// qDebug("parsing z-index: id=%d, fValue=%f", id, value->fValue );
if ( id == CSS_VAL_AUTO ) {
valid_primitive = true;
break;
}
/* nobreak */
case CSS_PROP_ORPHANS: // <integer> | inherit
case CSS_PROP_WIDOWS: // <integer> | inherit
// ### not supported later on
valid_primitive = ( !id && validUnit( value, FInteger, false ) );
break;
case CSS_PROP_LINE_HEIGHT: // normal | <number> | <length> | <percentage> | inherit
if ( id == CSS_VAL_NORMAL )
valid_primitive = true;
else
valid_primitive = ( !id && validUnit( value, FNumber|FLength|FPercent, strict ) );
break;
case CSS_PROP_COUNTER_INCREMENT: // [ <identifier> <integer>? ]+ | none | inherit
if ( id == CSS_VAL_NONE )
valid_primitive = true;
else
return parseCounter(propId, true, important);
break;
case CSS_PROP_COUNTER_RESET: // [ <identifier> <integer>? ]+ | none | inherit
if ( id == CSS_VAL_NONE )
valid_primitive = true;
else
return parseCounter(propId, false, important);
break;
case CSS_PROP_FONT_FAMILY:
// [[ <family-name> | <generic-family> ],]* [<family-name> | <generic-family>] | inherit
{
parsedValue = parseFontFamily();
break;
}
case CSS_PROP_TEXT_DECORATION:
// none | [ underline || overline || line-through || blink ] | inherit
if (id == CSS_VAL_NONE) {
valid_primitive = true;
} else {
CSSValueListImpl *list = new CSSValueListImpl;
bool is_valid = true;
while( is_valid && value ) {
switch ( value->id ) {
case CSS_VAL_BLINK:
break;
case CSS_VAL_UNDERLINE:
case CSS_VAL_OVERLINE:
case CSS_VAL_LINE_THROUGH:
list->append( new CSSPrimitiveValueImpl( value->id ) );
break;
default:
is_valid = false;
}
value = valueList->next();
}
//kDebug( 6080 ) << "got " << list->length() << "d decorations";
if(list->length() && is_valid) {
parsedValue = list;
valueList->next();
} else {
delete list;
}
}
break;
case CSS_PROP_TABLE_LAYOUT: // auto | fixed | inherit
if ( id == CSS_VAL_AUTO || id == CSS_VAL_FIXED )
valid_primitive = true;
break;
case CSS_PROP_SRC: // Only used within @font-face, so cannot use inherit | initial or be !important. This is a list of urls or local references.
return parseFontFaceSrc();
case CSS_PROP_UNICODE_RANGE:
// return parseFontFaceUnicodeRange();
break;
case CSS_PROP__KHTML_FLOW_MODE:
if ( id == CSS_VAL__KHTML_NORMAL || id == CSS_VAL__KHTML_AROUND_FLOATS )
valid_primitive = true;
break;
/* CSS3 properties */
case CSS_PROP_BOX_SIZING: // border-box | content-box | inherit
if ( id == CSS_VAL_BORDER_BOX || id == CSS_VAL_CONTENT_BOX )
valid_primitive = true;
break;
case CSS_PROP_OUTLINE_OFFSET:
valid_primitive = validUnit(value, FLength, strict);
break;
case CSS_PROP_TEXT_SHADOW: // CSS2 property, dropped in CSS2.1, back in CSS3, so treat as CSS3
if (id == CSS_VAL_NONE)
valid_primitive = true;
else
return parseShadow(propId, important);
break;
case CSS_PROP_OPACITY:
valid_primitive = validUnit(value, FNumber, strict);
break;
case CSS_PROP__KHTML_USER_INPUT: // none | enabled | disabled | inherit
if ( id == CSS_VAL_NONE || id == CSS_VAL_ENABLED || id == CSS_VAL_DISABLED )
valid_primitive = true;
// kDebug(6080) << "CSS_PROP__KHTML_USER_INPUT: " << valid_primitive;
break;
case CSS_PROP__KHTML_MARQUEE: {
const int properties[5] = { CSS_PROP__KHTML_MARQUEE_DIRECTION, CSS_PROP__KHTML_MARQUEE_INCREMENT,
CSS_PROP__KHTML_MARQUEE_REPETITION,
CSS_PROP__KHTML_MARQUEE_STYLE, CSS_PROP__KHTML_MARQUEE_SPEED };
return parseShortHand(propId, properties, 5, important);
}
case CSS_PROP__KHTML_MARQUEE_DIRECTION:
if (id == CSS_VAL_FORWARDS || id == CSS_VAL_BACKWARDS || id == CSS_VAL_AHEAD ||
id == CSS_VAL_REVERSE || id == CSS_VAL_LEFT || id == CSS_VAL_RIGHT || id == CSS_VAL_DOWN ||
id == CSS_VAL_UP || id == CSS_VAL_AUTO)
valid_primitive = true;
break;
case CSS_PROP__KHTML_MARQUEE_INCREMENT:
if (id == CSS_VAL_SMALL || id == CSS_VAL_LARGE || id == CSS_VAL_MEDIUM)
valid_primitive = true;
else
valid_primitive = validUnit(value, FLength|FPercent, strict);
break;
case CSS_PROP__KHTML_MARQUEE_STYLE:
if (id == CSS_VAL_NONE || id == CSS_VAL_SLIDE || id == CSS_VAL_SCROLL || id == CSS_VAL_ALTERNATE ||
id == CSS_VAL_UNFURL)
valid_primitive = true;
break;
case CSS_PROP__KHTML_MARQUEE_REPETITION:
if (id == CSS_VAL_INFINITE)
valid_primitive = true;
else
valid_primitive = validUnit(value, FInteger|FNonNeg, strict);
break;
case CSS_PROP__KHTML_MARQUEE_SPEED:
if (id == CSS_VAL_NORMAL || id == CSS_VAL_SLOW || id == CSS_VAL_FAST)
valid_primitive = true;
else
valid_primitive = validUnit(value, FTime|FInteger|FNonNeg, strict);
break;
case CSS_PROP_TEXT_OVERFLOW: // clip | ellipsis
if (id == CSS_VAL_CLIP || id == CSS_VAL_ELLIPSIS)
valid_primitive = true;
break;
// End of CSS3 properties
/* shorthand properties */
case CSS_PROP_BACKGROUND:
// ['background-color' || 'background-image' ||'background-repeat' ||
// 'background-attachment' || 'background-position'] | inherit
return parseBackgroundShorthand(important);
case CSS_PROP_BORDER:
// [ 'border-width' || 'border-style' || <color> ] | inherit
{
const int properties[3] = { CSS_PROP_BORDER_WIDTH, CSS_PROP_BORDER_STYLE,
CSS_PROP_BORDER_COLOR };
return parseShortHand(propId, properties, 3, important);
}
case CSS_PROP_BORDER_TOP:
// [ 'border-top-width' || 'border-style' || <color> ] | inherit
{
const int properties[3] = { CSS_PROP_BORDER_TOP_WIDTH, CSS_PROP_BORDER_TOP_STYLE,
CSS_PROP_BORDER_TOP_COLOR};
return parseShortHand(propId, properties, 3, important);
}
case CSS_PROP_BORDER_RIGHT:
// [ 'border-right-width' || 'border-style' || <color> ] | inherit
{
const int properties[3] = { CSS_PROP_BORDER_RIGHT_WIDTH, CSS_PROP_BORDER_RIGHT_STYLE,
CSS_PROP_BORDER_RIGHT_COLOR };
return parseShortHand(propId, properties, 3, important);
}
case CSS_PROP_BORDER_BOTTOM:
// [ 'border-bottom-width' || 'border-style' || <color> ] | inherit
{
const int properties[3] = { CSS_PROP_BORDER_BOTTOM_WIDTH, CSS_PROP_BORDER_BOTTOM_STYLE,
CSS_PROP_BORDER_BOTTOM_COLOR };
return parseShortHand(propId, properties, 3, important);
}
case CSS_PROP_BORDER_LEFT:
// [ 'border-left-width' || 'border-style' || <color> ] | inherit
{
const int properties[3] = { CSS_PROP_BORDER_LEFT_WIDTH, CSS_PROP_BORDER_LEFT_STYLE,
CSS_PROP_BORDER_LEFT_COLOR };
return parseShortHand(propId, properties, 3, important);
}
case CSS_PROP_OUTLINE:
// [ 'outline-color' || 'outline-style' || 'outline-width' ] | inherit
{
const int properties[3] = { CSS_PROP_OUTLINE_WIDTH, CSS_PROP_OUTLINE_STYLE,
CSS_PROP_OUTLINE_COLOR };
return parseShortHand(propId, properties, 3, important);
}
case CSS_PROP_BORDER_COLOR:
// <color>{1,4} | inherit
{
const int properties[4] = { CSS_PROP_BORDER_TOP_COLOR, CSS_PROP_BORDER_RIGHT_COLOR,
CSS_PROP_BORDER_BOTTOM_COLOR, CSS_PROP_BORDER_LEFT_COLOR };
return parse4Values(propId, properties, important);
}
case CSS_PROP_BORDER_WIDTH:
// <border-width>{1,4} | inherit
{
const int properties[4] = { CSS_PROP_BORDER_TOP_WIDTH, CSS_PROP_BORDER_RIGHT_WIDTH,
CSS_PROP_BORDER_BOTTOM_WIDTH, CSS_PROP_BORDER_LEFT_WIDTH };
return parse4Values(propId, properties, important);
}
case CSS_PROP_BORDER_STYLE:
// <border-style>{1,4} | inherit
{
const int properties[4] = { CSS_PROP_BORDER_TOP_STYLE, CSS_PROP_BORDER_RIGHT_STYLE,
CSS_PROP_BORDER_BOTTOM_STYLE, CSS_PROP_BORDER_LEFT_STYLE };
return parse4Values(propId, properties, important);
}
case CSS_PROP_MARGIN:
// <margin-width>{1,4} | inherit
{
const int properties[4] = { CSS_PROP_MARGIN_TOP, CSS_PROP_MARGIN_RIGHT,
CSS_PROP_MARGIN_BOTTOM, CSS_PROP_MARGIN_LEFT };
return parse4Values(propId, properties, important);
}
case CSS_PROP_PADDING:
// <padding-width>{1,4} | inherit
{
const int properties[4] = { CSS_PROP_PADDING_TOP, CSS_PROP_PADDING_RIGHT,
CSS_PROP_PADDING_BOTTOM, CSS_PROP_PADDING_LEFT };
return parse4Values(propId, properties, important);
}
case CSS_PROP_FONT:
// [ [ 'font-style' || 'font-variant' || 'font-weight' ]? 'font-size' [ / 'line-height' ]?
// 'font-family' ] | caption | icon | menu | message-box | small-caption | status-bar | inherit
if ( id >= CSS_VAL_CAPTION && id <= CSS_VAL_STATUS_BAR )
valid_primitive = true;
else
return parseFont(important);
break;
case CSS_PROP_LIST_STYLE:
{
const int properties[3] = { CSS_PROP_LIST_STYLE_TYPE, CSS_PROP_LIST_STYLE_POSITION,
CSS_PROP_LIST_STYLE_IMAGE };
return parseShortHand(propId, properties, 3, important);
}
case CSS_PROP_WORD_WRAP:
// normal | break-word
if ( id == CSS_VAL_NORMAL || id == CSS_VAL_BREAK_WORD )
valid_primitive = true;
break;
default:
return parseSVGValue(propId, important);
// #ifdef CSS_DEBUG
// kDebug( 6080 ) << "illegal or CSS2 Aural property: " << val;
// #endif
//break;
}
if ( valid_primitive ) {
if ( id != 0 ) {
parsedValue = new CSSPrimitiveValueImpl( id );
} else if ( value->unit == CSSPrimitiveValue::CSS_STRING )
parsedValue = new CSSPrimitiveValueImpl( domString( value->string ),
(CSSPrimitiveValue::UnitTypes) value->unit );
else if ( value->unit >= CSSPrimitiveValue::CSS_NUMBER &&
value->unit <= CSSPrimitiveValue::CSS_KHZ ) {
parsedValue = new CSSPrimitiveValueImpl( value->fValue,
(CSSPrimitiveValue::UnitTypes) value->unit );
} else if ( value->unit >= Value::Q_EMS ) {
parsedValue = new CSSQuirkPrimitiveValueImpl( value->fValue, CSSPrimitiveValue::CSS_EMS );
}
valueList->next();
}
if ( parsedValue ) {
if (!valueList->current() || inShorthand()) {
addProperty( propId, parsedValue, important );
return true;
}
delete parsedValue;
}
return false;
}
void CSSParser::addBackgroundValue(CSSValueImpl*& lval, CSSValueImpl* rval)
{
if (lval) {
if (lval->isValueList())
static_cast<CSSValueListImpl*>(lval)->append(rval);
else {
CSSValueImpl* oldVal = lval;
CSSValueListImpl* list = new CSSValueListImpl();
lval = list;
list->append(oldVal);
list->append(rval);
}
}
else
lval = rval;
}
bool CSSParser::parseBackgroundShorthand(bool important)
{
// Position must come before color in this array because a plain old "0" is a legal color
// in quirks mode but it's usually the X coordinate of a position.
// FIXME: Add CSS_PROP_BACKGROUND_SIZE to the shorthand.
const int numProperties = 7;
const int properties[numProperties] = { CSS_PROP_BACKGROUND_IMAGE, CSS_PROP_BACKGROUND_REPEAT,
CSS_PROP_BACKGROUND_ATTACHMENT, CSS_PROP_BACKGROUND_POSITION, CSS_PROP_BACKGROUND_CLIP,
CSS_PROP_BACKGROUND_ORIGIN, CSS_PROP_BACKGROUND_COLOR };
ShorthandScope scope(this, CSS_PROP_BACKGROUND);
bool parsedProperty[numProperties] = { false }; // compiler will repeat false as necessary
CSSValueImpl* values[numProperties] = { 0 }; // compiler will repeat 0 as necessary
CSSValueImpl* positionYValue = 0;
int i;
while (valueList->current()) {
Value* val = valueList->current();
if (val->unit == Value::Operator && val->iValue == ',') {
// We hit the end. Fill in all remaining values with the initial value.
valueList->next();
for (i = 0; i < numProperties; ++i) {
if (properties[i] == CSS_PROP_BACKGROUND_COLOR && parsedProperty[i])
// Color is not allowed except as the last item in a list. Reject the entire
// property.
goto fail;
if (!parsedProperty[i] && properties[i] != CSS_PROP_BACKGROUND_COLOR) {
addBackgroundValue(values[i], new CSSInitialValueImpl(true/*implicit initial*/));
if (properties[i] == CSS_PROP_BACKGROUND_POSITION)
addBackgroundValue(positionYValue, new CSSInitialValueImpl(true/*implicit initial*/));
}
parsedProperty[i] = false;
}
if (!valueList->current())
break;
}
bool found = false;
for (i = 0; !found && i < numProperties; ++i) {
if (!parsedProperty[i]) {
CSSValueImpl *val1 = 0, *val2 = 0;
int propId1, propId2;
if (parseBackgroundProperty(properties[i], propId1, propId2, val1, val2)) {
parsedProperty[i] = found = true;
addBackgroundValue(values[i], val1);
if (properties[i] == CSS_PROP_BACKGROUND_POSITION)
addBackgroundValue(positionYValue, val2);
}
}
}
// if we didn't find at least one match, this is an
// invalid shorthand and we have to ignore it
if (!found)
goto fail;
}
// Fill in any remaining properties with the initial value.
for (i = 0; i < numProperties; ++i) {
if (!parsedProperty[i]) {
addBackgroundValue(values[i], new CSSInitialValueImpl(true/*implicit initial*/));
if (properties[i] == CSS_PROP_BACKGROUND_POSITION)
addBackgroundValue(positionYValue, new CSSInitialValueImpl(true/*implicit initial*/));
}
}
// Now add all of the properties we found.
for (i = 0; i < numProperties; i++) {
if (properties[i] == CSS_PROP_BACKGROUND_POSITION) {
addProperty(CSS_PROP_BACKGROUND_POSITION_X, values[i], important);
addProperty(CSS_PROP_BACKGROUND_POSITION_Y, positionYValue, important);
}
else
addProperty(properties[i], values[i], important);
}
return true;
fail:
for (int k = 0; k < numProperties; k++)
delete values[k];
delete positionYValue;
return false;
}
static void completeMissingRadii(SharedPtr<CSSPrimitiveValueImpl>* array)
{
if (!array[1])
array[1] = array[0]; // top-left => top-right
if (!array[2])
array[2] = array[0]; // top-left => bottom-right
if (!array[3])
array[3] = array[1]; // top-left => bottom-right
}
bool CSSParser::parseBorderRadius(bool important) {
const int properties[4] = { CSS_PROP_BORDER_TOP_LEFT_RADIUS,
CSS_PROP_BORDER_TOP_RIGHT_RADIUS,
CSS_PROP_BORDER_BOTTOM_RIGHT_RADIUS,
CSS_PROP_BORDER_BOTTOM_LEFT_RADIUS };
SharedPtr<CSSPrimitiveValueImpl> horiz[4], vert[4];
for (int c = 0; c < 4; ++c) {
horiz[c] = 0;
vert [c] = 0;
}
Value* value;
// Parse horizontal ones until / or done.
for (int c = 0; c < 4; ++c) {
value = valueList->current();
if (!value || (value->unit == Value::Operator && value->iValue == '/'))
break; //Saw slash -- exit w/o consuming as we'll use it below.
if (!validUnit(value, FLength|FNonNeg, strict))
return false;
horiz[c] = new CSSPrimitiveValueImpl( value->fValue, (CSSPrimitiveValue::UnitTypes) value->unit );
value = valueList->next();
}
if (!horiz[0])
return false;
completeMissingRadii(horiz);
// Do we have vertical radii afterwards?
if (value && value->unit == Value::Operator && value->iValue == '/') {
valueList->next();
for (int c = 0; c < 4; ++c) {
kDebug() << c;
value = valueList->current();
if (!value)
break;
if (!validUnit(value, FLength|FNonNeg, strict))
return false;
vert[c] = new CSSPrimitiveValueImpl( value->fValue, (CSSPrimitiveValue::UnitTypes) value->unit );
value = valueList->next();
}
// If we didn't parse anything, or there is stuff remaining, this is malformed
if (!vert[0] || valueList->current())
return false;
completeMissingRadii(vert);
} else {
// Nope -- we better be at the end.
if (valueList->current())
return false;
for (int c = 0; c < 4; ++c)
vert[c] = horiz[c];
}
// All OK parsing, add properties
for (int c = 0; c < 4; ++c)
addProperty(properties[c], new CSSPrimitiveValueImpl(
new PairImpl(horiz[c].get(), vert[c].get())), important);
return true;
}
bool CSSParser::parseShortHand(int propId, const int *properties, int numProperties, bool important )
{
/* We try to match as many properties as possible
* We setup an array of booleans to mark which property has been found,
* and we try to search for properties until it makes no longer any sense
*/
ShorthandScope scope(this, propId);
bool found = false;
bool fnd[6]; //Trust me ;)
for( int i = 0; i < numProperties; i++ )
fnd[i] = false;
while ( valueList->current() ) {
found = false;
for (int propIndex = 0; !found && propIndex < numProperties; ++propIndex) {
if (!fnd[propIndex]) {
if ( parseValue( properties[propIndex], important ) ) {
fnd[propIndex] = found = true;
}
}
}
// if we didn't find at least one match, this is an
// invalid shorthand and we have to ignore it
if (!found)
return false;
}
// Fill in any remaining properties with the initial value.
m_implicitShorthand = true;
for (int i = 0; i < numProperties; ++i) {
if (!fnd[i])
addProperty(properties[i], new CSSInitialValueImpl(true/*implicit initial*/), important);
}
m_implicitShorthand = false;
return true;
}
bool CSSParser::parse4Values(int propId, const int *properties, bool important )
{
/* From the CSS 2 specs, 8.3
* If there is only one value, it applies to all sides. If there are two values, the top and
* bottom margins are set to the first value and the right and left margins are set to the second.
* If there are three values, the top is set to the first value, the left and right are set to the
* second, and the bottom is set to the third. If there are four values, they apply to the top,
* right, bottom, and left, respectively.
*/
int num = inShorthand() ? 1 : valueList->size();
//qDebug("parse4Values: num=%d %d", num, valueList->numValues );
ShorthandScope scope(this, propId);
// the order is top, right, bottom, left
switch (num) {
case 1: {
if (!parseValue(properties[0], important))
return false;
CSSValueImpl *value = parsedProperties[numParsedProperties-1]->value();
m_implicitShorthand = true;
addProperty(properties[1], value, important);
addProperty(properties[2], value, important);
addProperty(properties[3], value, important);
m_implicitShorthand = false;
break;
}
case 2: {
if (!parseValue(properties[0], important) || !parseValue(properties[1], important))
return false;
CSSValueImpl *value = parsedProperties[numParsedProperties-2]->value();
m_implicitShorthand = true;
addProperty(properties[2], value, important);
value = parsedProperties[numParsedProperties-2]->value();
addProperty(properties[3], value, important);
m_implicitShorthand = false;
break;
}
case 3: {
if (!parseValue(properties[0], important) || !parseValue(properties[1], important) || !parseValue(properties[2], important))
return false;
CSSValueImpl *value = parsedProperties[numParsedProperties-2]->value();
m_implicitShorthand = true;
addProperty(properties[3], value, important);
m_implicitShorthand = false;
break;
}
case 4: {
if (!parseValue(properties[0], important) || !parseValue(properties[1], important) ||
!parseValue(properties[2], important) || !parseValue(properties[3], important))
return false;
break;
}
default: {
return false;
}
}
return true;
}
// [ <string> | <uri> | <counter> | attr(X) | open-quote | close-quote | no-open-quote | no-close-quote ]+ | inherit
// in CSS 2.1 this got somewhat reduced:
// [ <string> | attr(X) | open-quote | close-quote | no-open-quote | no-close-quote ]+ | inherit
bool CSSParser::parseContent( int propId, bool important )
{
CSSValueListImpl* values = new CSSValueListImpl(CSSValueListImpl::Comma);
bool isValid = true;
Value *val;
CSSValueImpl *parsedValue = 0;
while ( (val = valueList->current()) ) {
parsedValue = 0;
if ( val->unit == CSSPrimitiveValue::CSS_URI ) {
// url
DOMString value = domString(val->string);
parsedValue = new CSSImageValueImpl( value, styleElement );
#ifdef CSS_DEBUG
kDebug( 6080 ) << "content, url=" << value.string() << " base=" << styleElement->baseURL().url( );
#endif
} else if ( val->unit == Value::Function ) {
// attr( X ) | counter( X [,Y] ) | counters( X, Y, [,Z] )
ValueList *args = val->function->args;
QString fname = qString( val->function->name ).toLower();
if (!args) return false;
if (fname == "attr(") {
if ( args->size() != 1)
return false;
Value *a = args->current();
if (a->unit != CSSPrimitiveValue::CSS_IDENT) {
isValid=false;
break;
}
if (qString(a->string)[0] == '-') {
isValid=false;
break;
}
parsedValue = new CSSPrimitiveValueImpl(domString(a->string), CSSPrimitiveValue::CSS_ATTR);
}
else
if (fname == "counter(") {
parsedValue = parseCounterContent(args, false);
if (!parsedValue) return false;
} else
if (fname == "counters(") {
parsedValue = parseCounterContent(args, true);
if (!parsedValue) return false;
}
else
return false;
} else if ( val->unit == CSSPrimitiveValue::CSS_IDENT ) {
// open-quote | close-quote | no-open-quote | no-close-quote
if ( val->id == CSS_VAL_OPEN_QUOTE ||
val->id == CSS_VAL_CLOSE_QUOTE ||
val->id == CSS_VAL_NO_OPEN_QUOTE ||
val->id == CSS_VAL_NO_CLOSE_QUOTE ) {
parsedValue = new CSSPrimitiveValueImpl(val->id);
}
} else if ( val->unit == CSSPrimitiveValue::CSS_STRING ) {
parsedValue = new CSSPrimitiveValueImpl(domString(val->string), CSSPrimitiveValue::CSS_STRING);
}
if (parsedValue)
values->append(parsedValue);
else {
isValid = false;
break;
}
valueList->next();
}
if ( isValid && values->length() ) {
addProperty( propId, values, important );
valueList->next();
return true;
}
delete values; // also frees any content by deref
return false;
}
CSSValueImpl* CSSParser::parseCounterContent(ValueList *args, bool counters)
{
if (counters || (args->size() != 1 && args->size() != 3))
if (!counters || (args->size() != 3 && args->size() != 5))
return 0;
CounterImpl *counter = new CounterImpl;
Value *i = args->current();
if (i->unit != CSSPrimitiveValue::CSS_IDENT) goto invalid;
if (qString(i->string)[0] == '-') goto invalid;
counter->m_identifier = domString(i->string);
if (counters) {
i = args->next();
if (i->unit != Value::Operator || i->iValue != ',') goto invalid;
i = args->next();
if (i->unit != CSSPrimitiveValue::CSS_STRING) goto invalid;
counter->m_separator = domString(i->string);
}
counter->m_listStyle = CSS_VAL_DECIMAL - CSS_VAL_DISC;
i = args->next();
if (i) {
if (i->unit != Value::Operator || i->iValue != ',') goto invalid;
i = args->next();
if (i->unit != CSSPrimitiveValue::CSS_IDENT) goto invalid;
if (i->id < CSS_VAL_DISC || i->id > CSS_VAL__KHTML_CLOSE_QUOTE) goto invalid;
counter->m_listStyle = i->id - CSS_VAL_DISC;
}
return new CSSPrimitiveValueImpl(counter);
invalid:
delete counter;
return 0;
}
CSSValueImpl* CSSParser::parseBackgroundColor()
{
int id = valueList->current()->id;
if (id == CSS_VAL__KHTML_TEXT || id == CSS_VAL_TRANSPARENT || id == CSS_VAL_CURRENTCOLOR ||
(id >= CSS_VAL_AQUA && id <= CSS_VAL_WINDOWTEXT) || id == CSS_VAL_MENU ||
(id >= CSS_VAL_GREY && id < CSS_VAL__KHTML_TEXT && !strict))
return new CSSPrimitiveValueImpl(id);
return parseColor();
}
CSSValueImpl* CSSParser::parseBackgroundImage(bool& didParse)
{
if (valueList->current()->id == CSS_VAL_NONE) {
didParse = true;
return new CSSImageValueImpl();
} else if (valueList->current()->unit == CSSPrimitiveValue::CSS_URI) {
didParse = true;
DOMString uri = domString(valueList->current()->string);
if (!uri.isNull())
return new CSSImageValueImpl(uri, styleElement);
else
return 0;
} else {
didParse = false;
return 0;
}
}
CSSValueImpl* CSSParser::parseBackgroundPositionXY(BackgroundPosKind& kindOut)
{
int id = valueList->current()->id;
if (id == CSS_VAL_LEFT || id == CSS_VAL_TOP || id == CSS_VAL_RIGHT || id == CSS_VAL_BOTTOM || id == CSS_VAL_CENTER) {
int percent = 0;
if (id == CSS_VAL_LEFT || id == CSS_VAL_RIGHT) {
kindOut = BgPos_X;
if (id == CSS_VAL_RIGHT)
percent = 100;
}
else if (id == CSS_VAL_TOP || id == CSS_VAL_BOTTOM) {
kindOut = BgPos_Y;
if (id == CSS_VAL_BOTTOM)
percent = 100;
}
else if (id == CSS_VAL_CENTER) {
// Center is ambiguous, so we're not sure which position we've found yet, an x or a y.
kindOut = BgPos_Center;
percent = 50;
}
return new CSSPrimitiveValueImpl(percent, CSSPrimitiveValue::CSS_PERCENTAGE);
}
if (validUnit(valueList->current(), FPercent|FLength, strict)) {
kindOut = BgPos_NonKW;
return new CSSPrimitiveValueImpl(valueList->current()->fValue,
(CSSPrimitiveValue::UnitTypes)valueList->current()->unit);
}
return 0;
}
void CSSParser::parseBackgroundPosition(CSSValueImpl*& value1, CSSValueImpl*& value2)
{
value1 = value2 = 0;
Value* value = valueList->current();
// Parse the first value. We're just making sure that it is one of the valid keywords or a percentage/length.
BackgroundPosKind value1pos;
value1 = parseBackgroundPositionXY(value1pos);
if (!value1)
return;
// It only takes one value for background-position to be correctly parsed if it was specified in a shorthand (since we
// can assume that any other values belong to the rest of the shorthand). If we're not parsing a shorthand, though, the
// value was explicitly specified for our property.
value = valueList->next();
// First check for the comma. If so, we are finished parsing this value or value pair.
if (value && value->unit == Value::Operator && value->iValue == ',')
value = 0;
BackgroundPosKind value2pos = BgPos_Center; // true if not specified.
if (value) {
value2 = parseBackgroundPositionXY(value2pos);
if (value2)
valueList->next();
else {
if (!inShorthand()) {
delete value1;
value1 = 0;
return;
}
}
}
if (!value2)
// Only one value was specified. The other direction is always 50%.
// If the one given was not a keyword, it should be viewed as 'x',
// and so setting value2 would set y, as desired.
// If the one value was a keyword, the swap below would put things in order
// if needed.
value2 = new CSSPrimitiveValueImpl(50, CSSPrimitiveValue::CSS_PERCENTAGE);
// Check for various failures
bool ok = true;
// Two keywords on the same axis.
if (value1pos == BgPos_X && value2pos == BgPos_X)
ok = false;
if (value1pos == BgPos_Y && value2pos == BgPos_Y)
ok = false;
// Will we need to swap them?
bool swap = (value1pos == BgPos_Y || value2pos == BgPos_X);
// If we had a non-KW value and a keyword value that's in the "wrong" position,
// this is malformed (#169612)
if (swap && (value1pos == BgPos_NonKW || value2pos == BgPos_NonKW))
ok = false;
if (!ok) {
delete value1;
delete value2;
value1 = 0;
value2 = 0;
return;
}
if (swap) {
// Swap our two values.
CSSValueImpl* val = value2;
value2 = value1;
value1 = val;
}
}
CSSValueImpl* CSSParser::parseBackgroundSize()
{
Value* value = valueList->current();
CSSPrimitiveValueImpl* parsedValue1;
if (value->id == CSS_VAL_COVER || value->id == CSS_VAL_CONTAIN)
return new CSSPrimitiveValueImpl(value->id);
if (value->id == CSS_VAL_AUTO)
parsedValue1 = new CSSPrimitiveValueImpl(0, CSSPrimitiveValue::CSS_UNKNOWN);
else {
if (!validUnit(value, FLength|FPercent, strict))
return 0;
parsedValue1 = new CSSPrimitiveValueImpl(value->fValue, (CSSPrimitiveValue::UnitTypes)value->unit);
}
CSSPrimitiveValueImpl* parsedValue2 = parsedValue1;
if ((value = valueList->next())) {
if (value->id == CSS_VAL_AUTO)
parsedValue2 = new CSSPrimitiveValueImpl(0, CSSPrimitiveValue::CSS_UNKNOWN);
else {
if (!validUnit(value, FLength|FPercent, strict)) {
delete parsedValue1;
return 0;
}
parsedValue2 = new CSSPrimitiveValueImpl(value->fValue, (CSSPrimitiveValue::UnitTypes)value->unit);
}
}
PairImpl* pair = new PairImpl(parsedValue1, parsedValue2);
return new CSSPrimitiveValueImpl(pair);
}
bool CSSParser::parseBackgroundProperty(int propId, int& propId1, int& propId2,
CSSValueImpl*& retValue1, CSSValueImpl*& retValue2)
{
#ifdef CSS_DEBUG
kDebug(6080) << "parseBackgroundProperty()";
kDebug(6080) << "LOOKING FOR: " << getPropertyName(propId).string();
#endif
CSSValueListImpl *values = 0, *values2 = 0;
Value* val;
CSSValueImpl *value = 0, *value2 = 0;
bool allowComma = false;
retValue1 = retValue2 = 0;
propId1 = propId;
propId2 = propId;
if (propId == CSS_PROP_BACKGROUND_POSITION) {
propId1 = CSS_PROP_BACKGROUND_POSITION_X;
propId2 = CSS_PROP_BACKGROUND_POSITION_Y;
}
while ((val = valueList->current())) {
CSSValueImpl *currValue = 0, *currValue2 = 0;
if (allowComma) {
if (val->unit != Value::Operator || val->iValue != ',')
goto failed;
valueList->next();
allowComma = false;
}
else {
switch (propId) {
case CSS_PROP_BACKGROUND_ATTACHMENT:
if (val->id == CSS_VAL_SCROLL || val->id == CSS_VAL_FIXED || val->id == CSS_VAL_LOCAL) {
currValue = new CSSPrimitiveValueImpl(val->id);
valueList->next();
}
break;
case CSS_PROP_BACKGROUND_COLOR:
currValue = parseBackgroundColor();
if (currValue)
valueList->next();
break;
case CSS_PROP_BACKGROUND_IMAGE: {
bool didParse=false;
currValue = parseBackgroundImage(didParse);
if (didParse)
valueList->next();
break;
}
case CSS_PROP_BACKGROUND_CLIP:
if (val->id == CSS_VAL_BORDER_BOX || val->id == CSS_VAL_PADDING_BOX) {
currValue = new CSSPrimitiveValueImpl(val->id);
valueList->next();
}
break;
case CSS_PROP_BACKGROUND_ORIGIN:
if (val->id == CSS_VAL_BORDER_BOX || val->id == CSS_VAL_PADDING_BOX || val->id == CSS_VAL_CONTENT_BOX) {
currValue = new CSSPrimitiveValueImpl(val->id);
valueList->next();
}
break;
case CSS_PROP__KHTML_BACKGROUND_CLIP:
case CSS_PROP__KHTML_BACKGROUND_ORIGIN:
if (val->id == CSS_VAL_BORDER || val->id == CSS_VAL_PADDING || val->id == CSS_VAL_CONTENT) {
currValue = new CSSPrimitiveValueImpl(val->id);
valueList->next();
}
break;
case CSS_PROP_BACKGROUND_POSITION:
parseBackgroundPosition(currValue, currValue2);
// unlike the other functions, parseBackgroundPosition advances the valueList pointer
break;
case CSS_PROP_BACKGROUND_POSITION_X: {
BackgroundPosKind pos;
currValue = parseBackgroundPositionXY(pos);
if (currValue) {
if (pos == BgPos_Y) {
delete currValue;
currValue = 0;
} else {
valueList->next();
}
}
break;
}
case CSS_PROP_BACKGROUND_POSITION_Y: {
BackgroundPosKind pos;
currValue = parseBackgroundPositionXY(pos);
if (currValue) {
if (pos == BgPos_X) {
delete currValue;
currValue = 0;
} else {
valueList->next();
}
}
break;
}
case CSS_PROP_BACKGROUND_REPEAT:
if (val->id >= CSS_VAL_REPEAT && val->id <= CSS_VAL_NO_REPEAT) {
currValue = new CSSPrimitiveValueImpl(val->id);
valueList->next();
}
break;
case CSS_PROP__KHTML_BACKGROUND_SIZE:
case CSS_PROP_BACKGROUND_SIZE:
currValue = parseBackgroundSize();
if (currValue)
valueList->next();
break;
}
if (!currValue)
goto failed;
if (value && !values) {
values = new CSSValueListImpl();
values->append(value);
value = 0;
}
if (value2 && !values2) {
values2 = new CSSValueListImpl();
values2->append(value2);
value2 = 0;
}
if (values)
values->append(currValue);
else
value = currValue;
if (currValue2) {
if (values2)
values2->append(currValue2);
else
value2 = currValue2;
}
allowComma = true;
}
// When parsing the 'background' shorthand property, we let it handle building up the lists for all
// properties.
if (inShorthand())
break;
}
if (values && values->length()) {
retValue1 = values;
if (values2 && values2->length())
retValue2 = values2;
return true;
}
if (value) {
retValue1 = value;
retValue2 = value2;
return true;
}
failed:
delete values; delete values2;
delete value; delete value2;
return false;
}
bool CSSParser::parseShape( int propId, bool important )
{
Value *value = valueList->current();
ValueList *args = value->function->args;
QString fname = qString( value->function->name ).toLower();
//qDebug( "parseShape: fname: %d", fname.toLatin1().constData() );
if ( fname != "rect(" || !args )
return false;
// rect( t, r, b, l ) || rect( t r b l )
if ( args->size() != 4 && args->size() != 7 )
return false;
RectImpl *rect = new RectImpl();
bool valid = true;
int i = 0;
Value *a = args->current();
while ( a ) {
CSSPrimitiveValueImpl *length;
if ( a->id == CSS_VAL_AUTO ) {
length = new CSSPrimitiveValueImpl( CSS_VAL_AUTO );
} else {
valid = validUnit( a, FLength, strict );
if ( !valid )
break;
length = new CSSPrimitiveValueImpl( a->fValue, (CSSPrimitiveValue::UnitTypes) a->unit );
}
if ( i == 0 )
rect->setTop( length );
else if ( i == 1 )
rect->setRight( length );
else if ( i == 2 )
rect->setBottom( length );
else
rect->setLeft( length );
a = args->next();
if ( a && args->size() == 7 ) {
if ( a->unit == Value::Operator && a->iValue == ',' ) {
a = args->next();
} else {
valid = false;
break;
}
}
i++;
}
if ( valid ) {
addProperty( propId, new CSSPrimitiveValueImpl( rect ), important );
valueList->next();
return true;
}
delete rect;
return false;
}
// [ 'font-style' || 'font-variant' || 'font-weight' ]? 'font-size' [ / 'line-height' ]? 'font-family'
bool CSSParser::parseFont( bool important )
{
// kDebug(6080) << "parsing font property current=" << valueList->currentValue;
bool valid = true;
Value *value = valueList->current();
CSSValueListImpl* family = 0;
CSSPrimitiveValueImpl *style = 0, *variant = 0, *weight = 0, *size = 0, *lineHeight = 0;
// optional font-style, font-variant and font-weight
while ( value ) {
// kDebug( 6080 ) << "got value " << value->id << " / " << (value->unit == CSSPrimitiveValue::CSS_STRING ||
// value->unit == CSSPrimitiveValue::CSS_IDENT ? qString( value->string ) : QString() )
// << endl;
int id = value->id;
if ( id ) {
if ( id == CSS_VAL_NORMAL ) {
// do nothing, it's the initial value for all three
}
/*
else if ( id == CSS_VAL_INHERIT ) {
// set all non set ones to inherit
// This is not that simple as the inherit could also apply to the following font-size.
// very ahrd to tell without looking ahead.
inherit = true;
} */
else if ( id == CSS_VAL_ITALIC || id == CSS_VAL_OBLIQUE ) {
if ( style )
goto invalid;
style = new CSSPrimitiveValueImpl( id );
} else if ( id == CSS_VAL_SMALL_CAPS ) {
if ( variant )
goto invalid;
variant = new CSSPrimitiveValueImpl( id );
} else if ( id >= CSS_VAL_BOLD && id <= CSS_VAL_LIGHTER ) {
if ( weight )
goto invalid;
weight = new CSSPrimitiveValueImpl( id );
} else {
valid = false;
}
} else if ( !weight && validUnit( value, FInteger|FNonNeg, true ) ) {
int w = (int)value->fValue;
int val = 0;
if ( w == 100 )
val = CSS_VAL_100;
else if ( w == 200 )
val = CSS_VAL_200;
else if ( w == 300 )
val = CSS_VAL_300;
else if ( w == 400 )
val = CSS_VAL_400;
else if ( w == 500 )
val = CSS_VAL_500;
else if ( w == 600 )
val = CSS_VAL_600;
else if ( w == 700 )
val = CSS_VAL_700;
else if ( w == 800 )
val = CSS_VAL_800;
else if ( w == 900 )
val = CSS_VAL_900;
if ( val )
weight = new CSSPrimitiveValueImpl( val );
else
valid = false;
} else {
valid = false;
}
if ( !valid )
break;
value = valueList->next();
}
if ( !value )
goto invalid;
// set undefined values to default
if ( !style )
style = new CSSPrimitiveValueImpl( CSS_VAL_NORMAL );
if ( !variant )
variant = new CSSPrimitiveValueImpl( CSS_VAL_NORMAL );
if ( !weight )
weight = new CSSPrimitiveValueImpl( CSS_VAL_NORMAL );
// kDebug( 6080 ) << " got style, variant and weight current=" << valueList->currentValue;
// now a font size _must_ come
// <absolute-size> | <relative-size> | <length> | <percentage> | inherit
if ( value->id >= CSS_VAL_XX_SMALL && value->id <= CSS_VAL_LARGER )
size = new CSSPrimitiveValueImpl( value->id );
else if ( validUnit( value, FLength|FPercent, strict ) ) {
size = new CSSPrimitiveValueImpl( value->fValue, (CSSPrimitiveValue::UnitTypes) value->unit );
}
value = valueList->next();
if ( !size || !value )
goto invalid;
// kDebug( 6080 ) << " got size";
if ( value->unit == Value::Operator && value->iValue == '/' ) {
// line-height
value = valueList->next();
if ( !value )
goto invalid;
if ( value->id == CSS_VAL_NORMAL ) {
// default value, nothing to do
} else if ( validUnit( value, FNumber|FLength|FPercent, strict ) ) {
lineHeight = new CSSPrimitiveValueImpl( value->fValue, (CSSPrimitiveValue::UnitTypes) value->unit );
} else {
goto invalid;
}
value = valueList->next();
if ( !value )
goto invalid;
}
if ( !lineHeight )
lineHeight = new CSSPrimitiveValueImpl( CSS_VAL_NORMAL );
// kDebug( 6080 ) << " got line height current=" << valueList->currentValue;
// font family must come now
family = parseFontFamily();
if ( valueList->current() || !family )
goto invalid;
//kDebug( 6080 ) << " got family, parsing ok!";
addProperty( CSS_PROP_FONT_FAMILY, family, important );
addProperty( CSS_PROP_FONT_STYLE, style, important );
addProperty( CSS_PROP_FONT_VARIANT, variant, important );
addProperty( CSS_PROP_FONT_WEIGHT, weight, important );
addProperty( CSS_PROP_FONT_SIZE, size, important );
addProperty( CSS_PROP_LINE_HEIGHT, lineHeight, important );
return true;
invalid:
//kDebug(6080) << " -> invalid";
delete family;
delete style;
delete variant;
delete weight;
delete size;
delete lineHeight;
return false;
}
CSSValueListImpl *CSSParser::parseFontFamily()
{
// kDebug( 6080 ) << "CSSParser::parseFontFamily current=" << valueList->currentValue;
CSSValueListImpl *list = new CSSValueListImpl(CSSValueListImpl::Comma);
Value *value = valueList->current();
QString currFace;
while ( value ) {
// kDebug( 6080 ) << "got value " << value->id << " / "
// << (value->unit == CSSPrimitiveValue::CSS_STRING ||
// value->unit == CSSPrimitiveValue::CSS_IDENT ? qString( value->string ) : QString() )
// << endl;
Value* nextValue = valueList->next();
bool nextValBreaksFont = !nextValue ||
(nextValue->unit == Value::Operator && nextValue->iValue == ',');
bool nextValIsFontName = nextValue &&
((nextValue->id >= CSS_VAL_SERIF && nextValue->id <= CSS_VAL_MONOSPACE) ||
(nextValue->unit == CSSPrimitiveValue::CSS_STRING ||
nextValue->unit == CSSPrimitiveValue::CSS_IDENT));
if (value->id >= CSS_VAL_SERIF && value->id <= CSS_VAL_MONOSPACE) {
if (!currFace.isNull()) {
currFace += ' ';
currFace += qString(value->string);
}
else if (nextValBreaksFont || !nextValIsFontName) {
if ( !currFace.isNull() ) {
list->append( new FontFamilyValueImpl( currFace ) );
currFace.clear();
}
list->append(new CSSPrimitiveValueImpl(value->id));
}
else {
currFace = qString( value->string );
}
}
else if (value->unit == CSSPrimitiveValue::CSS_STRING) {
// Strings never share in a family name.
currFace.clear();
list->append(new FontFamilyValueImpl(qString( value->string) ) );
}
else if (value->unit == CSSPrimitiveValue::CSS_IDENT) {
if (!currFace.isNull()) {
currFace += ' ';
currFace += qString(value->string);
}
else if (nextValBreaksFont || !nextValIsFontName) {
if ( !currFace.isNull() ) {
list->append( new FontFamilyValueImpl( currFace ) );
currFace.clear();
}
list->append(new FontFamilyValueImpl( qString( value->string ) ) );
}
else {
currFace = qString( value->string);
}
}
else {
//kDebug( 6080 ) << "invalid family part";
break;
}
if (!nextValue)
break;
if (nextValBreaksFont) {
value = valueList->next();
if ( !currFace.isNull() )
list->append( new FontFamilyValueImpl( currFace ) );
currFace.clear();
}
else if (nextValIsFontName)
value = nextValue;
else
break;
}
if ( !currFace.isNull() )
list->append( new FontFamilyValueImpl( currFace ) );
if ( !list->length() ) {
delete list;
list = 0;
}
return list;
}
bool CSSParser::parseFontFaceSrc()
{
CSSValueListImpl* values = new CSSValueListImpl(CSSValueListImpl::Comma);
Value* val;
bool expectComma = false;
bool allowFormat = false;
bool failed = false;
CSSFontFaceSrcValueImpl* uriValue = 0;
while ((val = valueList->current())) {
CSSFontFaceSrcValueImpl* parsedValue = 0;
if (val->unit == CSSPrimitiveValue::CSS_URI && !expectComma && styleElement) {
DOMString uri = khtml::parseURL( domString( val->string ) );
parsedValue = new CSSFontFaceSrcValueImpl(
DOMString(KUrl( styleElement->baseURL(), uri.string()).url()), false /*local*/);
uriValue = parsedValue;
allowFormat = true;
expectComma = true;
} else if (val->unit == Value::Function) {
// There are two allowed functions: local() and format().
+ // For both we expect a string argument
ValueList *args = val->function->args;
- if (args && args->size() == 1) {
+ if (args && args->size() == 1 &&
+ (args->current()->unit == CSSPrimitiveValue::CSS_STRING ||
+ args->current()->unit == CSSPrimitiveValue::CSS_IDENT)) {
if (!strcasecmp(domString(val->function->name), "local(") && !expectComma) {
expectComma = true;
allowFormat = false;
Value* a = args->current();
uriValue = 0;
parsedValue = new CSSFontFaceSrcValueImpl( domString( a->string ), true /*local src*/ );
} else if (!strcasecmp(domString(val->function->name), "format(") && allowFormat && uriValue) {
expectComma = true;
allowFormat = false;
uriValue->setFormat( domString( args->current()->string ) );
uriValue = 0;
valueList->next();
continue;
}
}
} else if (val->unit == Value::Operator && val->iValue == ',' && expectComma) {
expectComma = false;
allowFormat = false;
uriValue = 0;
valueList->next();
continue;
}
if (parsedValue)
values->append(parsedValue);
else {
failed = true;
break;
}
valueList->next();
}
if (values->length() && !failed) {
addProperty(CSS_PROP_SRC, values, important);
valueList->next();
return true;
} else {
delete values;
}
return false;
}
bool CSSParser::parseColorParameters(Value* value, int* colorArray, bool parseAlpha)
{
ValueList* args = value->function->args;
Value* v = args->current();
// Get the first value
if (!validUnit(v, FInteger | FPercent, true))
return false;
bool isPercent = (v->unit == CSSPrimitiveValue::CSS_PERCENTAGE);
colorArray[0] = static_cast<int>(v->fValue * (isPercent ? 256.0 / 100.0 : 1.0));
for (int i = 1; i < 3; i++) {
v = args->next();
if (v->unit != Value::Operator && v->iValue != ',')
return false;
v = args->next();
if (!validUnit(v, (isPercent ? FPercent : FInteger), true))
return false;
colorArray[i] = static_cast<int>(v->fValue * (isPercent ? 256.0 / 100.0 : 1.0));
}
if (parseAlpha) {
v = args->next();
if (v->unit != Value::Operator && v->iValue != ',')
return false;
v = args->next();
if (!validUnit(v, FNumber, true))
return false;
colorArray[3] = static_cast<int>(qMax(0.0, qMin(1.0, v->fValue)) * 255); //krazy:exclude=qminmax
}
return true;
}
// CSS3 specification defines the format of a HSL color as
// hsl(<number>, <percent>, <percent>)
// and with alpha, the format is
// hsla(<number>, <percent>, <percent>, <number>)
// The first value, HUE, is in an angle with a value between 0 and 360
bool CSSParser::parseHSLParameters(Value* value, double* colorArray, bool parseAlpha)
{
ValueList* args = value->function->args;
Value* v = args->current();
// Get the first value
if (!validUnit(v, FInteger, true))
return false;
// normalize the Hue value and change it to be between 0 and 1.0
colorArray[0] = (((static_cast<int>(v->fValue) % 360) + 360) % 360) / 360.0;
for (int i = 1; i < 3; i++) {
v = args->next();
if (v->unit != Value::Operator && v->iValue != ',')
return false;
v = args->next();
if (!validUnit(v, FPercent, true))
return false;
colorArray[i] = qMax(0.0, qMin(100.0, v->fValue)) / 100.0; // needs to be value between 0 and 1.0, krazy:exclude=qminmax
}
if (parseAlpha) {
v = args->next();
if (v->unit != Value::Operator && v->iValue != ',')
return false;
v = args->next();
if (!validUnit(v, FNumber, true))
return false;
colorArray[3] = qMax(0.0, qMin(1.0, v->fValue)); //krazy:exclude=qminmax
}
return true;
}
static int hex2int(unsigned short c, bool* error)
{
if ( c >= '0' && c <= '9' ) {
return c - '0';
} else if ( c >= 'A' && c <= 'F' ) {
return 10 + c - 'A';
} else if ( c >= 'a' && c <= 'f' ) {
return 10 + c - 'a';
} else {
*error = true;
return -1;
}
}
static bool parseColor(int unit, const QString& name, QRgb& rgb, bool strict)
{
int len = name.length();
if ( !len )
return false;
if (unit == CSSPrimitiveValue::CSS_RGBCOLOR || !strict) {
const unsigned short* c =
reinterpret_cast<const unsigned short*>( name.unicode() );
rgb = 0xff; // fixed alpha
if ( len == 6 ) {
// RRGGBB
bool error = false;
for ( int i = 0; i < 6; ++i, ++c )
rgb = rgb << 4 | hex2int( *c, &error );
if ( !error )
return true;
} else if ( len == 3 ) {
// RGB, shortcut for RRGGBB
bool error = false;
for ( int i = 0; i < 3; ++i, ++c )
rgb = rgb << 8 | 0x11 * hex2int( *c, &error );
if ( !error )
return true;
}
}
if ( unit == CSSPrimitiveValue::CSS_IDENT ) {
// try a little harder
QColor tc;
tc.setNamedColor(name.toLower());
if ( tc.isValid() ) {
rgb = tc.rgba();
return true;
}
}
return false;
}
CSSPrimitiveValueImpl *CSSParser::parseColor()
{
return parseColorFromValue(valueList->current());
}
CSSPrimitiveValueImpl *CSSParser::parseColorFromValue(Value* value)
{
QRgb c = khtml::transparentColor;
if ( !strict && value->unit == CSSPrimitiveValue::CSS_NUMBER && // color: 000000 (quirk)
value->fValue >= 0. && value->fValue < 1000000. ) {
QString str;
str.sprintf( "%06d", (int)(value->fValue+.5) );
if ( !::parseColor( CSSPrimitiveValue::CSS_RGBCOLOR, str, c, strict ) )
return 0;
}
else if (value->unit == CSSPrimitiveValue::CSS_RGBCOLOR || // color: #ff0000
value->unit == CSSPrimitiveValue::CSS_IDENT || // color: red || color: ff0000 (quirk)
(!strict && value->unit == CSSPrimitiveValue::CSS_DIMENSION)) { // color: 00ffff (quirk)
if ( !::parseColor( value->unit, qString( value->string ), c, strict) )
return 0;
}
else if ( value->unit == Value::Function &&
value->function->args != 0 &&
value->function->args->size() == 5 /* rgb + two commas */ &&
qString( value->function->name ).toLower() == "rgb(" ) {
int colorValues[3];
if (!parseColorParameters(value, colorValues, false))
return 0;
colorValues[0] = qMax( 0, qMin( 255, colorValues[0] ) );
colorValues[1] = qMax( 0, qMin( 255, colorValues[1] ) );
colorValues[2] = qMax( 0, qMin( 255, colorValues[2] ) );
c = qRgb(colorValues[0], colorValues[1], colorValues[2]);
} else if (value->unit == Value::Function &&
value->function->args != 0 &&
value->function->args->size() == 7 /* rgba + three commas */ &&
domString(value->function->name).lower() == "rgba(") {
int colorValues[4];
if (!parseColorParameters(value, colorValues, true))
return 0;
colorValues[0] = qMax( 0, qMin( 255, colorValues[0] ) );
colorValues[1] = qMax( 0, qMin( 255, colorValues[1] ) );
colorValues[2] = qMax( 0, qMin( 255, colorValues[2] ) );
c = qRgba(colorValues[0], colorValues[1], colorValues[2], colorValues[3]);
} else if (value->unit == Value::Function &&
value->function->args != 0 &&
value->function->args->size() == 5 /* hsl + two commas */ &&
domString(value->function->name).lower() == "hsl(") {
double colorValues[3];
if (!parseHSLParameters(value, colorValues, false))
return 0;
c = khtml::qRgbaFromHsla(colorValues[0], colorValues[1], colorValues[2], 1.0);
} else if (value->unit == Value::Function &&
value->function->args != 0 &&
value->function->args->size() == 7 /* hsla + three commas */ &&
domString(value->function->name).lower() == "hsla(") {
double colorValues[4];
if (!parseHSLParameters(value, colorValues, true))
return 0;
c = khtml::qRgbaFromHsla(colorValues[0], colorValues[1], colorValues[2], colorValues[3]);
}
else
return 0;
return new CSSPrimitiveValueImpl(c);
}
// This class tracks parsing state for shadow values. If it goes out of scope (e.g., due to an early return)
// without the allowBreak bit being set, then it will clean up all of the objects and destroy them.
struct ShadowParseContext {
ShadowParseContext()
:values(0), x(0), y(0), blur(0), color(0),
allowX(true), allowY(false), allowBlur(false), allowColor(true),
allowBreak(true)
{}
~ShadowParseContext() {
if (!allowBreak) {
delete values;
delete x;
delete y;
delete blur;
delete color;
}
}
bool allowLength() { return allowX || allowY || allowBlur; }
bool failed() { return allowBreak = false; }
void commitValue() {
// Handle the ,, case gracefully by doing nothing.
if (x || y || blur || color) {
if (!values)
values = new CSSValueListImpl(CSSValueListImpl::Comma);
// Construct the current shadow value and add it to the list.
values->append(new ShadowValueImpl(x, y, blur, color));
}
// Now reset for the next shadow value.
x = y = blur = color = 0;
allowX = allowColor = allowBreak = true;
allowY = allowBlur = false;
}
void commitLength(Value* v) {
CSSPrimitiveValueImpl* val = new CSSPrimitiveValueImpl(v->fValue,
(CSSPrimitiveValue::UnitTypes)v->unit);
if (allowX) {
x = val;
allowX = false; allowY = true; allowColor = false; allowBreak = false;
}
else if (allowY) {
y = val;
allowY = false; allowBlur = true; allowColor = true; allowBreak = true;
}
else if (allowBlur) {
blur = val;
allowBlur = false;
}
else
delete val;
}
void commitColor(CSSPrimitiveValueImpl* val) {
color = val;
allowColor = false;
if (allowX)
allowBreak = false;
else
allowBlur = false;
}
CSSValueListImpl* values;
CSSPrimitiveValueImpl* x;
CSSPrimitiveValueImpl* y;
CSSPrimitiveValueImpl* blur;
CSSPrimitiveValueImpl* color;
bool allowX;
bool allowY;
bool allowBlur;
bool allowColor;
bool allowBreak;
};
bool CSSParser::parseShadow(int propId, bool important)
{
ShadowParseContext context;
Value* val;
while ((val = valueList->current())) {
// Check for a comma break first.
if (val->unit == Value::Operator) {
if (val->iValue != ',' || !context.allowBreak)
// Other operators aren't legal or we aren't done with the current shadow
// value. Treat as invalid.
return context.failed();
// The value is good. Commit it.
context.commitValue();
}
// Check to see if we're a length.
else if (validUnit(val, FLength, true)) {
// We required a length and didn't get one. Invalid.
if (!context.allowLength())
return context.failed();
// A length is allowed here. Construct the value and add it.
context.commitLength(val);
}
else {
// The only other type of value that's ok is a color value.
CSSPrimitiveValueImpl* parsedColor = 0;
bool isColor = ((val->id >= CSS_VAL_AQUA && val->id <= CSS_VAL_WINDOWTEXT) ||
val->id == CSS_VAL_MENU ||
(val->id >= CSS_VAL_GREY && val->id <= CSS_VAL__KHTML_TEXT && !strict));
if (!context.allowColor)
return context.failed();
if (isColor)
parsedColor = new CSSPrimitiveValueImpl(val->id);
if (!parsedColor)
// It's not built-in. Try to parse it as a color.
parsedColor = parseColorFromValue(val);
if (!parsedColor)
return context.failed();
context.commitColor(parsedColor);
}
valueList->next();
}
if (context.allowBreak) {
context.commitValue();
if (context.values->length()) {
addProperty(propId, context.values, important);
valueList->next();
return true;
}
}
return context.failed();
}
bool CSSParser::parseCounter(int propId, bool increment, bool important)
{
enum { ID, VAL, COMMA } state = ID;
CSSValueListImpl *list = new CSSValueListImpl;
DOMString c;
Value* val;
while (true) {
val = valueList->current();
switch (state) {
// Commas are not allowed according to the standard, but Opera allows them and being the only
// other browser with counter support we need to match their behavior to work with current use
case COMMA:
state = ID;
if (val && val->unit == Value::Operator && val->iValue == ',') {
valueList->next();
continue;
}
// no break
case ID:
if (val && val->unit == CSSPrimitiveValue::CSS_IDENT) {
c = qString(val->string);
state = VAL;
valueList->next();
continue;
}
break;
case VAL: {
short i = 0;
if (val && val->unit == CSSPrimitiveValue::CSS_NUMBER) {
i = (short)val->fValue;
valueList->next();
} else
i = (increment) ? 1 : 0;
CounterActImpl *cv = new CounterActImpl(c,i);
list->append(cv);
state = COMMA;
continue;
}
}
break;
}
if(list->length() > 0) {
addProperty( propId, list, important );
return true;
}
delete list;
return false;
}
static inline int yyerror( const char *str ) {
// assert( 0 );
#ifdef CSS_DEBUG
kDebug( 6080 ) << "CSS parse error " << str;
#else
Q_UNUSED( str );
#endif
return 1;
}
#define END 0
#include "parser.h"
int DOM::CSSParser::lex( void *_yylval )
{
YYSTYPE *yylval = (YYSTYPE *)_yylval;
int token = lex();
int length;
unsigned short *t = text( &length );
#ifdef TOKEN_DEBUG
qDebug("CSSTokenizer: got token %d: '%s'", token, token == END ? "" : QString( (QChar *)t, length ).toLatin1().constData() );
#endif
switch( token ) {
case '{':
block_nesting++;
break;
case '}':
if ( block_nesting )
block_nesting--;
break;
case END:
if ( block_nesting ) {
block_nesting--;
return '}';
}
break;
case S:
case SGML_CD:
case INCLUDES:
case DASHMATCH:
break;
case URI:
case STRING:
case IDENT:
case NTH:
case HASH:
case HEXCOLOR:
case DIMEN:
case UNICODERANGE:
case NOTFUNCTION:
case FUNCTION:
yylval->string.string = t;
yylval->string.length = length;
break;
case IMPORT_SYM:
case PAGE_SYM:
case MEDIA_SYM:
case FONT_FACE_SYM:
case CHARSET_SYM:
case NAMESPACE_SYM:
case IMPORTANT_SYM:
break;
case QEMS:
length--;
case GRADS:
case DPCM:
length--;
case DEGS:
case RADS:
case KHERZ:
case DPI:
length--;
case MSECS:
case HERZ:
case EMS:
case EXS:
case PXS:
case CMS:
case MMS:
case INS:
case PTS:
case PCS:
length--;
case SECS:
case PERCENTAGE:
length--;
case FLOAT:
case INTEGER:
yylval->val = QString( (QChar *)t, length ).toDouble();
//qDebug("value = %s, converted=%.2f", QString( (QChar *)t, length ).toLatin1().constData(), yylval->val );
break;
default:
break;
}
return token;
}
static inline int toHex( char c ) {
if ( '0' <= c && c <= '9' )
return c - '0';
if ( 'a' <= c && c <= 'f' )
return c - 'a' + 10;
if ( 'A' <= c && c<= 'F' )
return c - 'A' + 10;
return 0;
}
unsigned short *DOM::CSSParser::text(int *length)
{
unsigned short *start = yytext;
int l = yyleng;
switch( yyTok ) {
case STRING:
l--;
/* nobreak */
case HASH:
case HEXCOLOR:
start++;
l--;
break;
case URI:
// "url("{w}{string}{w}")"
// "url("{w}{url}{w}")"
// strip "url(" and ")"
start += 4;
l -= 5;
// strip {w}
while ( l &&
(*start == ' ' || *start == '\t' || *start == '\r' ||
*start == '\n' || *start == '\f' ) ) {
start++; l--;
}
if ( *start == '"' || *start == '\'' ) {
start++; l--;
}
while ( l &&
(start[l-1] == ' ' || start[l-1] == '\t' || start[l-1] == '\r' ||
start[l-1] == '\n' || start[l-1] == '\f' ) ) {
l--;
}
if ( l && (start[l-1] == '\"' || start[l-1] == '\'' ) )
l--;
default:
break;
}
// process escapes
unsigned short *out = start;
unsigned short *escape = 0;
for ( int i = 0; i < l; i++ ) {
unsigned short *current = start+i;
if ( escape == current - 1 ) {
if ( ( *current >= '0' && *current <= '9' ) ||
( *current >= 'a' && *current <= 'f' ) ||
( *current >= 'A' && *current <= 'F' ) )
continue;
if ( yyTok == STRING &&
( *current == '\n' || *current == '\r' || *current == '\f' ) ) {
// ### handle \r\n case
if ( *current != '\r' )
escape = 0;
continue;
}
// in all other cases copy the char to output
// ###
*out++ = *current;
escape = 0;
continue;
}
if ( escape == current - 2 && yyTok == STRING &&
*(current-1) == '\r' && *current == '\n' ) {
escape = 0;
continue;
}
if ( escape > current - 7 &&
( ( *current >= '0' && *current <= '9' ) ||
( *current >= 'a' && *current <= 'f' ) ||
( *current >= 'A' && *current <= 'F' ) ) )
continue;
if ( escape ) {
// add escaped char
int uc = 0;
escape++;
while ( escape < current ) {
// qDebug("toHex( %c = %x", (char)*escape, toHex( *escape ) );
uc *= 16;
uc += toHex( *escape );
escape++;
}
// qDebug(" converting escape: string='%s', value=0x%x", QString( (QChar *)e, current-e ).toLatin1().constData(), uc );
// can't handle chars outside utf16
if ( uc > 0xffff )
uc = 0xfffd;
*(out++) = (unsigned short)uc;
escape = 0;
if ( *current == ' ' ||
*current == '\t' ||
*current == '\r' ||
*current == '\n' ||
*current == '\f' )
continue;
}
if ( !escape && *current == '\\' ) {
escape = current;
continue;
}
*(out++) = *current;
}
if ( escape ) {
// add escaped char
int uc = 0;
escape++;
while ( escape < start+l ) {
// qDebug("toHex( %c = %x", (char)*escape, toHex( *escape ) );
uc *= 16;
uc += toHex( *escape );
escape++;
}
// qDebug(" converting escape: string='%s', value=0x%x", QString( (QChar *)e, current-e ).toLatin1().constData(), uc );
// can't handle chars outside utf16
if ( uc > 0xffff )
uc = 0xfffd;
*(out++) = (unsigned short)uc;
}
*length = out - start;
return start;
}
// When we reach the end of the input we switch over
// the lexer to this alternative buffer and keep it stuck here.
// (and as it contains nulls, flex will keep on reporting
// end of buffer, and we will keep reseting the input
// pointer to the beginning of this).
static unsigned short postEofBuf[2];
#define YY_DECL int DOM::CSSParser::lex()
#define yyconst const
typedef int yy_state_type;
typedef unsigned int YY_CHAR;
// this line makes sure we treat all Unicode chars correctly.
#define YY_SC_TO_UI(c) (c > 0xff ? 0xff : c)
#define YY_DO_BEFORE_ACTION \
yytext = yy_bp; \
yyleng = (int) (yy_cp - yy_bp); \
yy_hold_char = *yy_cp; \
*yy_cp = 0; \
yy_c_buf_p = yy_cp;
#define YY_BREAK break;
#define ECHO qDebug( "%s", QString( (QChar *)yytext, yyleng ).toLatin1().constData() )
#define YY_RULE_SETUP
#define INITIAL 0
#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
#define YY_START ((yy_start - 1) / 2)
#define yyterminate()\
do { \
if (yy_act == YY_END_OF_BUFFER) { \
yy_c_buf_p = postEofBuf; \
yy_hold_char = 0; /* first char of the postEndOf to 'restore' */ \
} \
yyTok = END; return yyTok; \
} while (0)
#define YY_FATAL_ERROR(a) qFatal(a)
#define BEGIN yy_start = 1 + 2 *
#define COMMENT 1
#include "tokenizer.cpp"
// kate: indent-width 4; replace-tabs on; tab-width 4; space-indent on; hl c++;
diff --git a/khtml/imload/scaledimageplane.h b/khtml/imload/scaledimageplane.h
index 953c34c513..35fec213e1 100644
--- a/khtml/imload/scaledimageplane.h
+++ b/khtml/imload/scaledimageplane.h
@@ -1,92 +1,98 @@
/*
Large image displaying library.
Copyright (C) 2004,2005 Maks Orlovich (maksim@kde.org)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef SCALED_IMAGE_PLANE_H
#define SCALED_IMAGE_PLANE_H
+#include <cassert>
+
#include "array2d.h"
#include "imageplane.h"
#include "rawimageplane.h"
#include "imagetile.h"
namespace khtmlImLoad {
/**
A scaled image plane pulls data from a RawImagePlane and resizes it
*/
class ScaledImagePlane: public ImagePlane
{
private:
RawImagePlane* parent;
Array2D<ImageTile> tiles;
unsigned int* calcScaleTable(unsigned int orig, unsigned int scaled)
{
if (scaled == 0)
return 0; // Don't need to compute origin for 0 pixels..
//### I bet this has all sorts of imprecision problems w/high ratios
unsigned int* origin = new unsigned int[scaled];
-
+
//### FIXME: replace with something that clamps on right edge later?
double ratio = double(orig)/double(scaled);
- int intRatio = int(ratio*65536.0 + 1);
- int pos = 0;
-
+
+ // Should be assured by ImageManager::isAcceptableScaleSize
+ assert(ratio < 65536);
+
+ unsigned intRatio = unsigned(ratio*65536.0 + 1);
+ unsigned pos = 0;
+
for (unsigned int pix = 0; pix < scaled; pix++)
{
origin[pix] = pos >> 16;
pos += intRatio;
}
-
+
return origin;
}
-
+
unsigned int* xScaleTable;
unsigned int* yScaleTable;
public:
virtual ~ScaledImagePlane();
virtual void flushCache();
ScaledImagePlane(unsigned int _width, unsigned int _height, RawImagePlane* _parent):
ImagePlane(_width, _height), parent(_parent), tiles(tilesWidth, tilesHeight)
{
//Create the mapping tables
yScaleTable = calcScaleTable(parent->height, height);
xScaleTable = calcScaleTable(parent->width , width );
}
virtual bool isUpToDate(unsigned int tileX, unsigned int tileY,
PixmapTile* tile);
virtual void ensureUpToDate(unsigned int tileX, unsigned int tileY,
PixmapTile* tile);
};
}
#endif
// kate: indent-width 4; replace-tabs on; tab-width 4; space-indent on;
diff --git a/kio/kio/dataslave.cpp b/kio/kio/dataslave.cpp
index 16b1497a2c..dae5a53889 100644
--- a/kio/kio/dataslave.cpp
+++ b/kio/kio/dataslave.cpp
@@ -1,180 +1,181 @@
/*
* This file is part of the KDE libraries
* Copyright (c) 2003 Leo Savernik <l.savernik@aon.at>
* Derived from slave.cpp
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License version 2 as published by the Free Software Foundation.
*
* 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
**/
#include "dataslave.h"
#include "dataprotocol.h"
#include <config.h>
#include <klocale.h>
#include <kdebug.h>
#include <QtCore/QTimer>
using namespace KIO;
#define KIO_DATA_POLL_INTERVAL 0
// don't forget to sync DISPATCH_DECL in dataslave.h
#define DISPATCH_IMPL(type) \
void DataSlave::dispatch_##type() { \
if (_suspended) { \
QueueStruct q(Queue_##type); \
q.size = -1; \
dispatchQueue.push_back(q); \
if (!timer->isActive()) timer->start(KIO_DATA_POLL_INTERVAL); \
} else \
type(); \
}
// don't forget to sync DISPATCH_DECL1 in dataslave.h
#define DISPATCH_IMPL1(type, paramtype, paramname) \
void DataSlave::dispatch_##type(paramtype paramname) { \
if (_suspended) { \
QueueStruct q(Queue_##type); \
q.paramname = paramname; \
dispatchQueue.push_back(q); \
if (!timer->isActive()) timer->start(KIO_DATA_POLL_INTERVAL); \
} else \
type(paramname); \
}
DataSlave::DataSlave() :
Slave("data")
{
//kDebug() << this;
_suspended = false;
timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), SLOT(dispatchNext()));
}
DataSlave::~DataSlave() {
//kDebug() << this;
}
void DataSlave::hold(const KUrl &/*url*/) {
// ignored
}
void DataSlave::suspend() {
_suspended = true;
//kDebug() << this;
timer->stop();
}
void DataSlave::resume() {
_suspended = false;
//kDebug() << this;
// aarrrgh! This makes the once hyper fast and efficient data protocol
// implementation slow as molasses. But it wouldn't work otherwise,
// and I don't want to start messing around with threads
timer->start(KIO_DATA_POLL_INTERVAL);
}
// finished is a special case. If we emit it right away, then
// TransferJob::start can delete the job even before the end of the method
void DataSlave::dispatch_finished() {
QueueStruct q(Queue_finished);
q.size = -1;
dispatchQueue.push_back(q);
if (!timer->isActive()) timer->start(KIO_DATA_POLL_INTERVAL);
}
void DataSlave::dispatchNext() {
if (dispatchQueue.empty()) {
timer->stop();
return;
}
const QueueStruct &q = dispatchQueue.front();
//kDebug() << this << "dispatching" << q.type << dispatchQueue.size() << "left";
switch (q.type) {
case Queue_mimeType: mimeType(q.s); break;
case Queue_totalSize: totalSize(q.size); break;
case Queue_sendMetaData: sendMetaData(); break;
case Queue_data: data(q.ba); break;
case Queue_finished: finished(); break;
}/*end switch*/
dispatchQueue.pop_front();
}
void DataSlave::send(int cmd, const QByteArray &arr) {
QDataStream stream(arr);
KUrl url;
switch (cmd) {
case CMD_GET: {
stream >> url;
get(url);
break;
}
case CMD_MIMETYPE: {
stream >> url;
mimetype(url);
break;
}
// ignore these (must not emit error, otherwise SIGSEGV occurs)
+ case CMD_REPARSECONFIGURATION:
case CMD_META_DATA:
case CMD_SUBURL:
break;
default:
error(ERR_UNSUPPORTED_ACTION,
unsupportedActionErrorString(QLatin1String("data"),cmd));
}/*end switch*/
}
bool DataSlave::suspended() {
return _suspended;
}
void DataSlave::setHost(const QString &/*host*/, quint16 /*port*/,
const QString &/*user*/, const QString &/*passwd*/) {
// irrelevant -> will be ignored
}
void DataSlave::setConfig(const MetaData &/*config*/) {
// FIXME: decide to handle this directly or not at all
#if 0
QByteArray data;
QDataStream stream( data, QIODevice::WriteOnly );
stream << config;
slaveconn.send( CMD_CONFIG, data );
#endif
}
void DataSlave::setAllMetaData(const MetaData &md) {
meta_data = md;
}
void DataSlave::sendMetaData() {
emit metaData(meta_data);
}
DISPATCH_IMPL1(mimeType, const QString &, s)
DISPATCH_IMPL1(totalSize, KIO::filesize_t, size)
DISPATCH_IMPL(sendMetaData)
DISPATCH_IMPL1(data, const QByteArray &, ba)
#undef DISPATCH_IMPL
#undef DISPATCH_IMPL1
diff --git a/kio/kio/kdirlister.cpp b/kio/kio/kdirlister.cpp
index c2e8dd7249..027e3c8a7a 100644
--- a/kio/kio/kdirlister.cpp
+++ b/kio/kio/kdirlister.cpp
@@ -1,2759 +1,2759 @@
/* This file is part of the KDE project
Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
2000 Carsten Pfeiffer <pfeiffer@kde.org>
2003-2005 David Faure <faure@kde.org>
2001-2006 Michael Brade <brade@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "kdirlister.h"
#include "kdirlister_p.h"
#include <QtCore/QRegExp>
#include <kdebug.h>
#include <kde_file.h>
#include <klocale.h>
#include <kio/job.h>
#include <kio/jobuidelegate.h>
#include <kmessagebox.h>
#include "kprotocolmanager.h"
#include "kmountpoint.h"
#include <QFile>
// Enable this to get printDebug() called often, to see the contents of the cache
//#define DEBUG_CACHE
// Make really sure it doesn't get activated in the final build
#ifdef NDEBUG
#undef DEBUG_CACHE
#endif
K_GLOBAL_STATIC(KDirListerCache, kDirListerCache)
KDirListerCache::KDirListerCache()
: itemsCached( 10 ) // keep the last 10 directories around
{
//kDebug(7004);
connect( &pendingUpdateTimer, SIGNAL(timeout()), this, SLOT(processPendingUpdates()) );
pendingUpdateTimer.setSingleShot( true );
connect( KDirWatch::self(), SIGNAL( dirty( const QString& ) ),
this, SLOT( slotFileDirty( const QString& ) ) );
connect( KDirWatch::self(), SIGNAL( created( const QString& ) ),
this, SLOT( slotFileCreated( const QString& ) ) );
connect( KDirWatch::self(), SIGNAL( deleted( const QString& ) ),
this, SLOT( slotFileDeleted( const QString& ) ) );
kdirnotify = new org::kde::KDirNotify(QString(), QString(), QDBusConnection::sessionBus(), this);
connect(kdirnotify, SIGNAL(FileRenamed(QString,QString)), SLOT(slotFileRenamed(QString,QString)));
connect(kdirnotify, SIGNAL(FilesAdded(QString)), SLOT(slotFilesAdded(QString)));
connect(kdirnotify, SIGNAL(FilesChanged(QStringList)), SLOT(slotFilesChanged(QStringList)));
connect(kdirnotify, SIGNAL(FilesRemoved(QStringList)), SLOT(slotFilesRemoved(QStringList)));
// The use of KUrl::url() in ~DirItem (sendSignal) crashes if the static for QRegExpEngine got deleted already,
// so we need to destroy the KDirListerCache before that.
qAddPostRoutine(kDirListerCache.destroy);
}
KDirListerCache::~KDirListerCache()
{
//kDebug(7004);
qDeleteAll(itemsInUse);
itemsInUse.clear();
itemsCached.clear();
directoryData.clear();
if ( KDirWatch::exists() )
KDirWatch::self()->disconnect( this );
}
// setting _reload to true will emit the old files and
// call updateDirectory
bool KDirListerCache::listDir( KDirLister *lister, const KUrl& _u,
bool _keep, bool _reload )
{
KUrl _url(_u);
_url.cleanPath(); // kill consecutive slashes
if (!_url.host().isEmpty() && KProtocolInfo::protocolClass(_url.protocol()) == ":local"
&& _url.protocol() != "file") {
// ":local" protocols ignore the hostname, so strip it out preventively - #160057
// kio_file is special cased since it does honor the hostname (by redirecting to e.g. smb)
_url.setHost(QString());
if (_keep == false)
emit lister->redirection(_url);
}
// like this we don't have to worry about trailing slashes any further
_url.adjustPath(KUrl::RemoveTrailingSlash);
const QString urlStr = _url.url();
QString resolved;
if (_url.isLocalFile()) {
// Resolve symlinks (#213799)
const QString local = _url.toLocalFile();
resolved = QFileInfo(local).canonicalFilePath();
if (local != resolved)
canonicalUrls[resolved].append(urlStr);
// TODO: remove entry from canonicalUrls again in forgetDirs
// Note: this is why we use a QStringList value in there rather than a QSet:
// we can just remove one entry and not have to worry about other dirlisters
// (the non-unicity of the stringlist gives us the refcounting, basically).
}
if (!validUrl(lister, _url)) {
kDebug(7004) << lister << "url=" << _url << "not a valid url";
return false;
}
//kDebug(7004) << lister << "url=" << _url << "keep=" << _keep << "reload=" << _reload;
#ifdef DEBUG_CACHE
printDebug();
#endif
if (!_keep) {
// stop any running jobs for lister
stop(lister, true /*silent*/);
// clear our internal list for lister
forgetDirs(lister);
lister->d->rootFileItem = KFileItem();
} else if (lister->d->lstDirs.contains(_url)) {
// stop the job listing _url for this lister
stopListingUrl(lister, _url, true /*silent*/);
// remove the _url as well, it will be added in a couple of lines again!
// forgetDirs with three args does not do this
// TODO: think about moving this into forgetDirs
lister->d->lstDirs.removeAll(_url);
// clear _url for lister
forgetDirs(lister, _url, true);
if (lister->d->url == _url)
lister->d->rootFileItem = KFileItem();
}
lister->d->complete = false;
lister->d->lstDirs.append(_url);
if (lister->d->url.isEmpty() || !_keep) // set toplevel URL only if not set yet
lister->d->url = _url;
DirItem *itemU = itemsInUse.value(urlStr);
KDirListerCacheDirectoryData& dirData = directoryData[urlStr]; // find or insert
if (dirData.listersCurrentlyListing.isEmpty()) {
// if there is an update running for _url already we get into
// the following case - it will just be restarted by updateDirectory().
dirData.listersCurrentlyListing.append(lister);
DirItem *itemFromCache;
if (itemU || (!_reload && (itemFromCache = itemsCached.take(urlStr)) ) ) {
if (itemU) {
kDebug(7004) << "Entry already in use:" << _url;
// if _reload is set, then we'll emit cached items and then updateDirectory.
if (lister->d->autoUpdate)
itemU->incAutoUpdate();
} else {
kDebug(7004) << "Entry in cache:" << _url;
// In this code path, the itemsFromCache->decAutoUpdate + itemU->incAutoUpdate is optimized out
itemsInUse.insert(urlStr, itemFromCache);
itemU = itemFromCache;
}
emit lister->started(_url);
// List items from the cache in a delayed manner, just like things would happen
// if we were not using the cache.
new KDirLister::Private::CachedItemsJob(lister, _url, _reload);
} else {
// dir not in cache or _reload is true
if (_reload) {
kDebug(7004) << "Reloading directory:" << _url;
itemsCached.remove(urlStr);
} else {
kDebug(7004) << "Listing directory:" << _url;
}
itemU = new DirItem(_url, resolved);
itemsInUse.insert(urlStr, itemU);
if (lister->d->autoUpdate)
itemU->incAutoUpdate();
// // we have a limit of MAX_JOBS_PER_LISTER concurrently running jobs
// if ( lister->d->numJobs() >= MAX_JOBS_PER_LISTER )
// {
// pendingUpdates.insert( _url );
// }
// else
{
KIO::ListJob* job = KIO::listDir(_url, KIO::HideProgressInfo);
runningListJobs.insert(job, KIO::UDSEntryList());
lister->d->jobStarted(job);
lister->d->connectJob(job);
if (lister->d->window)
job->ui()->setWindow(lister->d->window);
connect(job, SIGNAL(entries(KIO::Job *, KIO::UDSEntryList)),
this, SLOT(slotEntries(KIO::Job *, KIO::UDSEntryList)));
connect(job, SIGNAL(result(KJob *)),
this, SLOT(slotResult(KJob *)));
connect(job, SIGNAL(redirection(KIO::Job *,KUrl)),
this, SLOT(slotRedirection(KIO::Job *,KUrl)));
emit lister->started(_url);
}
//kDebug(7004) << "Entry now being listed by" << dirData.listersCurrentlyListing;
}
} else {
kDebug(7004) << "Entry currently being listed:" << _url << "by" << dirData.listersCurrentlyListing;
#ifdef DEBUG_CACHE
printDebug();
#endif
emit lister->started( _url );
// Maybe listersCurrentlyListing/listersCurrentlyHolding should be QSets?
Q_ASSERT(!dirData.listersCurrentlyListing.contains(lister));
dirData.listersCurrentlyListing.append( lister );
KIO::ListJob *job = jobForUrl( urlStr );
// job will be 0 if we were listing from cache rather than listing from a kio job.
if( job ) {
lister->d->jobStarted( job );
lister->d->connectJob( job );
}
Q_ASSERT( itemU );
// List existing items in a delayed manner, just like things would happen
// if we were not using the cache.
if (!itemU->lstItems.isEmpty()) {
kDebug() << "Listing" << itemU->lstItems.count() << "cached items soon";
new KDirLister::Private::CachedItemsJob(lister, _url, _reload);
} else {
// The other lister hasn't emitted anything yet. Good, we'll just listen to it.
// One problem could be if we have _reload=true and the existing job doesn't, though.
}
#ifdef DEBUG_CACHE
printDebug();
#endif
}
return true;
}
KDirLister::Private::CachedItemsJob* KDirLister::Private::cachedItemsJobForUrl(const KUrl& url) const
{
Q_FOREACH(CachedItemsJob* job, m_cachedItemsJobs) {
if (job->url() == url)
return job;
}
return 0;
}
KDirLister::Private::CachedItemsJob::CachedItemsJob(KDirLister* lister, const KUrl& url, bool reload)
: KJob(lister),
m_lister(lister), m_url(url),
m_reload(reload), m_emitCompleted(true)
{
//kDebug() << "Creating CachedItemsJob" << this << "for lister" << lister << url;
if (lister->d->cachedItemsJobForUrl(url)) {
kWarning(7004) << "Lister" << lister << "has a cached items job already for" << url;
}
lister->d->m_cachedItemsJobs.append(this);
setAutoDelete(true);
start();
}
// Called by start() via QueuedConnection
void KDirLister::Private::CachedItemsJob::done()
{
if (!m_lister) // job was already killed, but waiting deletion due to deleteLater
return;
kDirListerCache->emitItemsFromCache(this, m_lister, m_url, m_reload, m_emitCompleted);
emitResult();
}
bool KDirLister::Private::CachedItemsJob::doKill()
{
//kDebug(7004) << this;
kDirListerCache->forgetCachedItemsJob(this, m_lister, m_url);
if (!property("_kdlc_silent").toBool()) {
emit m_lister->canceled(m_url);
emit m_lister->canceled();
}
m_lister = 0;
return true;
}
void KDirListerCache::emitItemsFromCache(KDirLister::Private::CachedItemsJob* cachedItemsJob, KDirLister* lister, const KUrl& _url, bool _reload, bool _emitCompleted)
{
const QString urlStr = _url.url();
KDirLister::Private* kdl = lister->d;
kdl->complete = false;
DirItem *itemU = kDirListerCache->itemsInUse.value(urlStr);
if (!itemU) {
kWarning(7004) << "Can't find item for directory" << urlStr << "anymore";
} else {
const KFileItemList items = itemU->lstItems;
const KFileItem rootItem = itemU->rootItem;
_reload = _reload || !itemU->complete;
if (kdl->rootFileItem.isNull() && !rootItem.isNull() && kdl->url == _url) {
kdl->rootFileItem = rootItem;
}
if (!items.isEmpty()) {
//kDebug(7004) << "emitting" << items.count() << "for lister" << lister;
kdl->addNewItems(_url, items);
kdl->emitItems();
}
}
forgetCachedItemsJob(cachedItemsJob, lister, _url);
// Emit completed, unless we were told not to,
// or if listDir() was called while another directory listing for this dir was happening,
// so we "joined" it. We detect that using jobForUrl to ensure it's a real ListJob,
// not just a lister-specific CachedItemsJob (which wouldn't emit completed for us).
if (_emitCompleted) {
kdl->complete = true;
emit lister->completed( _url );
emit lister->completed();
if ( _reload ) {
updateDirectory( _url );
}
}
}
void KDirListerCache::forgetCachedItemsJob(KDirLister::Private::CachedItemsJob* cachedItemsJob, KDirLister* lister, const KUrl& _url)
{
// Modifications to data structures only below this point;
// so that addNewItems is called with a consistent state
const QString urlStr = _url.url();
lister->d->m_cachedItemsJobs.removeAll(cachedItemsJob);
KDirListerCacheDirectoryData& dirData = directoryData[urlStr];
Q_ASSERT(dirData.listersCurrentlyListing.contains(lister));
KIO::ListJob *listJob = jobForUrl(urlStr);
if (!listJob) {
Q_ASSERT(!dirData.listersCurrentlyHolding.contains(lister));
//kDebug(7004) << "Moving from listing to holding, because no more job" << lister << urlStr;
dirData.listersCurrentlyHolding.append( lister );
dirData.listersCurrentlyListing.removeAll( lister );
} else {
//kDebug(7004) << "Still having a listjob" << listJob << ", so not moving to currently-holding.";
}
}
bool KDirListerCache::validUrl( const KDirLister *lister, const KUrl& url ) const
{
if ( !url.isValid() )
{
if ( lister->d->autoErrorHandling )
{
QString tmp = i18n("Malformed URL\n%1", url.prettyUrl() );
KMessageBox::error( lister->d->errorParent, tmp );
}
return false;
}
if ( !KProtocolManager::supportsListing( url ) )
{
if ( lister->d->autoErrorHandling )
{
QString tmp = i18n("URL cannot be listed\n%1", url.prettyUrl() );
KMessageBox::error( lister->d->errorParent, tmp );
}
return false;
}
return true;
}
void KDirListerCache::stop( KDirLister *lister, bool silent )
{
#ifdef DEBUG_CACHE
//printDebug();
#endif
//kDebug(7004) << "lister:" << lister << "silent=" << silent;
const KUrl::List urls = lister->d->lstDirs;
Q_FOREACH(const KUrl& url, urls) {
stopListingUrl(lister, url, silent);
}
#if 0 // test code
QHash<QString,KDirListerCacheDirectoryData>::iterator dirit = directoryData.begin();
const QHash<QString,KDirListerCacheDirectoryData>::iterator dirend = directoryData.end();
for( ; dirit != dirend ; ++dirit ) {
KDirListerCacheDirectoryData& dirData = dirit.value();
if (dirData.listersCurrentlyListing.contains(lister)) {
kDebug(7004) << "ERROR: found lister" << lister << "in list - for" << dirit.key();
Q_ASSERT(false);
}
}
#endif
}
void KDirListerCache::stopListingUrl(KDirLister *lister, const KUrl& _u, bool silent)
{
KUrl url(_u);
url.adjustPath( KUrl::RemoveTrailingSlash );
const QString urlStr = url.url();
KDirLister::Private::CachedItemsJob* cachedItemsJob = lister->d->cachedItemsJobForUrl(url);
if (cachedItemsJob) {
if (silent) {
cachedItemsJob->setProperty("_kdlc_silent", true);
}
cachedItemsJob->kill(); // removes job from list, too
}
// TODO: consider to stop all the "child jobs" of url as well
kDebug(7004) << lister << " url=" << url;
QHash<QString,KDirListerCacheDirectoryData>::iterator dirit = directoryData.find(urlStr);
if (dirit == directoryData.end())
return;
KDirListerCacheDirectoryData& dirData = dirit.value();
if (dirData.listersCurrentlyListing.contains(lister)) {
//kDebug(7004) << " found lister" << lister << "in list - for" << urlStr;
if (dirData.listersCurrentlyListing.count() == 1) {
// This was the only dirlister interested in the list job -> kill the job
stopListJob(urlStr, silent);
} else {
// Leave the job running for the other dirlisters, just unsubscribe us.
dirData.listersCurrentlyListing.removeAll(lister);
if (!silent) {
emit lister->canceled();
emit lister->canceled(url);
}
}
}
}
// Helper for stop() and stopListingUrl()
void KDirListerCache::stopListJob(const QString& url, bool silent)
{
// Old idea: if it's an update job, let's just leave the job running.
// After all, update jobs do run for "listersCurrentlyHolding",
// so there's no reason to kill them just because @p lister is now a holder.
// However it could be a long-running non-local job (e.g. filenamesearch), which
// the user wants to abort, and which will never be used for updating...
// And in any case slotEntries/slotResult is not meant to be called by update jobs.
// So, change of plan, let's kill it after all, in a way that triggers slotResult/slotUpdateResult.
KIO::ListJob *job = jobForUrl(url);
if (job) {
//kDebug() << "Killing list job" << job << "for" << url;
if (silent) {
job->setProperty("_kdlc_silent", true);
}
job->kill(KJob::EmitResult);
}
}
void KDirListerCache::setAutoUpdate( KDirLister *lister, bool enable )
{
// IMPORTANT: this method does not check for the current autoUpdate state!
for ( KUrl::List::const_iterator it = lister->d->lstDirs.constBegin();
it != lister->d->lstDirs.constEnd(); ++it ) {
DirItem* dirItem = itemsInUse.value((*it).url());
Q_ASSERT(dirItem);
if ( enable )
dirItem->incAutoUpdate();
else
dirItem->decAutoUpdate();
}
}
void KDirListerCache::forgetDirs( KDirLister *lister )
{
//kDebug(7004) << lister;
emit lister->clear();
// clear lister->d->lstDirs before calling forgetDirs(), so that
// it doesn't contain things that itemsInUse doesn't. When emitting
// the canceled signals, lstDirs must not contain anything that
// itemsInUse does not contain. (otherwise it might crash in findByName()).
const KUrl::List lstDirsCopy = lister->d->lstDirs;
lister->d->lstDirs.clear();
//kDebug() << "Iterating over dirs" << lstDirsCopy;
for ( KUrl::List::const_iterator it = lstDirsCopy.begin();
it != lstDirsCopy.end(); ++it ) {
forgetDirs( lister, *it, false );
}
}
static bool manually_mounted(const QString& path, const KMountPoint::List& possibleMountPoints)
{
KMountPoint::Ptr mp = possibleMountPoints.findByPath(path);
if (!mp) // not listed in fstab -> yes, manually mounted
return true;
const bool supermount = mp->mountType() == "supermount";
if (supermount) {
return true;
}
// noauto -> manually mounted. Otherwise, mounted at boot time, won't be unmounted any time soon hopefully.
return mp->mountOptions().contains("noauto");
}
void KDirListerCache::forgetDirs( KDirLister *lister, const KUrl& _url, bool notify )
{
//kDebug(7004) << lister << " _url: " << _url;
KUrl url( _url );
url.adjustPath( KUrl::RemoveTrailingSlash );
const QString urlStr = url.url();
DirectoryDataHash::iterator dit = directoryData.find(urlStr);
if (dit == directoryData.end())
return;
KDirListerCacheDirectoryData& dirData = *dit;
dirData.listersCurrentlyHolding.removeAll(lister);
// This lister doesn't care for updates running in <url> anymore
KIO::ListJob *job = jobForUrl(urlStr);
if (job)
lister->d->jobDone(job);
DirItem *item = itemsInUse.value(urlStr);
Q_ASSERT(item);
bool insertIntoCache = false;
if ( dirData.listersCurrentlyHolding.isEmpty() && dirData.listersCurrentlyListing.isEmpty() ) {
// item not in use anymore -> move into cache if complete
directoryData.erase(dit);
itemsInUse.remove( urlStr );
// this job is a running update which nobody cares about anymore
if ( job ) {
killJob( job );
kDebug(7004) << "Killing update job for " << urlStr;
// Well, the user of KDirLister doesn't really care that we're stopping
// a background-running job from a previous URL (in listDir) -> commented out.
// stop() already emitted canceled.
//emit lister->canceled( url );
if ( lister->d->numJobs() == 0 ) {
lister->d->complete = true;
//emit lister->canceled();
}
}
if ( notify ) {
lister->d->lstDirs.removeAll( url );
emit lister->clear( url );
}
insertIntoCache = item->complete;
if (insertIntoCache) {
// TODO(afiestas): remove use of KMountPoint+manually_mounted and port to Solid:
// 1) find Volume for the local path "item->url.toLocalFile()" (which could be anywhere
// under the mount point) -- probably needs a new operator in libsolid query parser
// 2) [**] becomes: if (Drive is hotpluggable or Volume is removable) "set to dirty" else "keep watch"
const KMountPoint::List possibleMountPoints = KMountPoint::possibleMountPoints(KMountPoint::NeedMountOptions);
// Should we forget the dir for good, or keep a watch on it?
// Generally keep a watch, except when it would prevent
// unmounting a removable device (#37780)
const bool isLocal = item->url.isLocalFile();
bool isManuallyMounted = false;
bool containsManuallyMounted = false;
if (isLocal) {
isManuallyMounted = manually_mounted( item->url.toLocalFile(), possibleMountPoints );
if ( !isManuallyMounted ) {
// Look for a manually-mounted directory inside
// If there's one, we can't keep a watch either, FAM would prevent unmounting the CDROM
// I hope this isn't too slow
KFileItemList::const_iterator kit = item->lstItems.constBegin();
KFileItemList::const_iterator kend = item->lstItems.constEnd();
for ( ; kit != kend && !containsManuallyMounted; ++kit )
if ( (*kit).isDir() && manually_mounted((*kit).url().toLocalFile(), possibleMountPoints) )
containsManuallyMounted = true;
}
}
if ( isManuallyMounted || containsManuallyMounted ) // [**]
{
kDebug(7004) << "Not adding a watch on " << item->url << " because it " <<
( isManuallyMounted ? "is manually mounted" : "contains a manually mounted subdir" );
item->complete = false; // set to "dirty"
}
else
item->incAutoUpdate(); // keep watch
}
else
{
delete item;
item = 0;
}
}
if ( item && lister->d->autoUpdate )
item->decAutoUpdate();
// Inserting into QCache must be done last, since it might delete the item
if (item && insertIntoCache) {
kDebug(7004) << lister << "item moved into cache:" << url;
itemsCached.insert(urlStr, item);
}
}
void KDirListerCache::updateDirectory( const KUrl& _dir )
{
kDebug(7004) << _dir;
QString urlStr = _dir.url(KUrl::RemoveTrailingSlash);
if ( !checkUpdate( urlStr ) )
return;
// A job can be running to
// - only list a new directory: the listers are in listersCurrentlyListing
// - only update a directory: the listers are in listersCurrentlyHolding
// - update a currently running listing: the listers are in both
KDirListerCacheDirectoryData& dirData = directoryData[urlStr];
QList<KDirLister *> listers = dirData.listersCurrentlyListing;
QList<KDirLister *> holders = dirData.listersCurrentlyHolding;
//kDebug(7004) << urlStr << "listers=" << listers << "holders=" << holders;
// restart the job for _dir if it is running already
bool killed = false;
QWidget *window = 0;
KIO::ListJob *job = jobForUrl( urlStr );
if (job) {
window = job->ui()->window();
killJob( job );
killed = true;
foreach ( KDirLister *kdl, listers )
kdl->d->jobDone( job );
foreach ( KDirLister *kdl, holders )
kdl->d->jobDone( job );
} else {
// Emit any cached items.
// updateDirectory() is about the diff compared to the cached items...
Q_FOREACH(KDirLister *kdl, listers) {
KDirLister::Private::CachedItemsJob* cachedItemsJob = kdl->d->cachedItemsJobForUrl(_dir);
if (cachedItemsJob) {
cachedItemsJob->setEmitCompleted(false);
cachedItemsJob->done(); // removes from cachedItemsJobs list
delete cachedItemsJob;
killed = true;
}
}
}
//kDebug(7004) << "Killed=" << killed;
// we don't need to emit canceled signals since we only replaced the job,
// the listing is continuing.
if (!(listers.isEmpty() || killed)) {
kWarning() << "The unexpected happened.";
kWarning() << "listers for" << _dir << "=" << listers;
kWarning() << "job=" << job;
Q_FOREACH(KDirLister *kdl, listers) {
kDebug() << "lister" << kdl << "m_cachedItemsJobs=" << kdl->d->m_cachedItemsJobs;
}
#ifndef NDEBUG
printDebug();
#endif
}
Q_ASSERT( listers.isEmpty() || killed );
job = KIO::listDir( _dir, KIO::HideProgressInfo );
runningListJobs.insert( job, KIO::UDSEntryList() );
connect( job, SIGNAL(entries( KIO::Job *, const KIO::UDSEntryList & )),
this, SLOT(slotUpdateEntries( KIO::Job *, const KIO::UDSEntryList & )) );
connect( job, SIGNAL(result( KJob * )),
this, SLOT(slotUpdateResult( KJob * )) );
kDebug(7004) << "update started in" << _dir;
foreach ( KDirLister *kdl, listers ) {
kdl->d->jobStarted( job );
}
if ( !holders.isEmpty() ) {
if ( !killed ) {
bool first = true;
foreach ( KDirLister *kdl, holders ) {
kdl->d->jobStarted( job );
if ( first && kdl->d->window ) {
first = false;
job->ui()->setWindow( kdl->d->window );
}
emit kdl->started( _dir );
}
} else {
job->ui()->setWindow( window );
foreach ( KDirLister *kdl, holders ) {
kdl->d->jobStarted( job );
}
}
}
}
bool KDirListerCache::checkUpdate( const QString& _dir )
{
if ( !itemsInUse.contains(_dir) )
{
DirItem *item = itemsCached[_dir];
if ( item && item->complete )
{
item->complete = false;
item->decAutoUpdate();
// Hmm, this debug output might include login/password from the _dir URL.
//kDebug(7004) << "directory " << _dir << " not in use, marked dirty.";
}
//else
//kDebug(7004) << "aborted, directory " << _dir << " not in cache.";
return false;
}
else
return true;
}
KFileItem KDirListerCache::itemForUrl( const KUrl& url ) const
{
KFileItem *item = findByUrl( 0, url );
if (item) {
return *item;
} else {
return KFileItem();
}
}
KDirListerCache::DirItem *KDirListerCache::dirItemForUrl(const KUrl& dir) const
{
const QString urlStr = dir.url(KUrl::RemoveTrailingSlash);
DirItem *item = itemsInUse.value(urlStr);
if ( !item )
item = itemsCached[urlStr];
return item;
}
KFileItemList *KDirListerCache::itemsForDir(const KUrl& dir) const
{
DirItem *item = dirItemForUrl(dir);
return item ? &item->lstItems : 0;
}
KFileItem KDirListerCache::findByName( const KDirLister *lister, const QString& _name ) const
{
Q_ASSERT(lister);
for (KUrl::List::const_iterator it = lister->d->lstDirs.constBegin();
it != lister->d->lstDirs.constEnd(); ++it) {
DirItem* dirItem = itemsInUse.value((*it).url());
Q_ASSERT(dirItem);
const KFileItem item = dirItem->lstItems.findByName(_name);
if (!item.isNull())
return item;
}
return KFileItem();
}
KFileItem *KDirListerCache::findByUrl( const KDirLister *lister, const KUrl& _u ) const
{
KUrl url(_u);
url.adjustPath(KUrl::RemoveTrailingSlash);
KUrl parentDir(url);
parentDir.setPath( parentDir.directory() );
DirItem* dirItem = dirItemForUrl(parentDir);
if (dirItem) {
// If lister is set, check that it contains this dir
if (!lister || lister->d->lstDirs.contains(parentDir)) {
KFileItemList::iterator it = dirItem->lstItems.begin();
const KFileItemList::iterator end = dirItem->lstItems.end();
for (; it != end ; ++it) {
if ((*it).url() == url) {
return &*it;
}
}
}
}
// Maybe _u is a directory itself? (see KDirModelTest::testChmodDirectory)
// We check this last, though, we prefer returning a kfileitem with an actual
// name if possible (and we make it '.' for root items later).
dirItem = dirItemForUrl(url);
if (dirItem && !dirItem->rootItem.isNull() && dirItem->rootItem.url() == url) {
// If lister is set, check that it contains this dir
if (!lister || lister->d->lstDirs.contains(url)) {
return &dirItem->rootItem;
}
}
return 0;
}
void KDirListerCache::slotFilesAdded( const QString &dir /*url*/ ) // from KDirNotify signals
{
KUrl urlDir(dir);
kDebug(7004) << urlDir; // output urls, not qstrings, since they might contain a password
if (urlDir.isLocalFile()) {
- Q_FOREACH(const QString& u, directoriesForCanonicalPath(urlDir.path())) {
+ Q_FOREACH(const QString& u, directoriesForCanonicalPath(urlDir.toLocalFile())) {
updateDirectory(KUrl(u));
}
} else {
updateDirectory(urlDir);
}
}
void KDirListerCache::slotFilesRemoved( const QStringList &fileList ) // from KDirNotify signals
{
// TODO: handling of symlinks-to-directories isn't done here,
// because I'm not sure how to do it and keep the performance ok...
slotFilesRemoved(KUrl::List(fileList));
}
void KDirListerCache::slotFilesRemoved(const KUrl::List& fileList)
{
//kDebug(7004) << fileList.count();
// Group notifications by parent dirs (usually there would be only one parent dir)
QMap<QString, KFileItemList> removedItemsByDir;
KUrl::List deletedSubdirs;
for (KUrl::List::const_iterator it = fileList.begin(); it != fileList.end() ; ++it) {
const KUrl url(*it);
DirItem* dirItem = dirItemForUrl(url); // is it a listed directory?
if (dirItem) {
deletedSubdirs.append(url);
if (!dirItem->rootItem.isNull()) {
removedItemsByDir[url.url()].append(dirItem->rootItem);
}
}
KUrl parentDir(url);
parentDir.setPath(parentDir.directory());
dirItem = dirItemForUrl(parentDir);
if (!dirItem)
continue;
for (KFileItemList::iterator fit = dirItem->lstItems.begin(), fend = dirItem->lstItems.end(); fit != fend ; ++fit) {
if ((*fit).url() == url) {
const KFileItem fileitem = *fit;
removedItemsByDir[parentDir.url()].append(fileitem);
// If we found a fileitem, we can test if it's a dir. If not, we'll go to deleteDir just in case.
if (fileitem.isNull() || fileitem.isDir()) {
deletedSubdirs.append(url);
}
dirItem->lstItems.erase(fit); // remove fileitem from list
break;
}
}
}
QMap<QString, KFileItemList>::const_iterator rit = removedItemsByDir.constBegin();
for(; rit != removedItemsByDir.constEnd(); ++rit) {
// Tell the views about it before calling deleteDir.
// They might need the subdirs' file items (see the dirtree).
DirectoryDataHash::const_iterator dit = directoryData.constFind(rit.key());
if (dit != directoryData.constEnd()) {
itemsDeleted((*dit).listersCurrentlyHolding, rit.value());
}
}
Q_FOREACH(const KUrl& url, deletedSubdirs) {
// in case of a dir, check if we have any known children, there's much to do in that case
// (stopping jobs, removing dirs from cache etc.)
deleteDir(url);
}
}
void KDirListerCache::slotFilesChanged( const QStringList &fileList ) // from KDirNotify signals
{
//kDebug(7004) << fileList;
KUrl::List dirsToUpdate;
QStringList::const_iterator it = fileList.begin();
for (; it != fileList.end() ; ++it) {
KUrl url( *it );
KFileItem *fileitem = findByUrl(0, url);
if (!fileitem) {
kDebug(7004) << "item not found for" << url;
continue;
}
if (url.isLocalFile()) {
pendingUpdates.insert(*it); // delegate the work to processPendingUpdates
} else {
pendingRemoteUpdates.insert(fileitem);
// For remote files, we won't be able to figure out the new information,
// we have to do a update (directory listing)
KUrl dir(url);
dir.setPath(dir.directory());
if (!dirsToUpdate.contains(dir))
dirsToUpdate.prepend(dir);
}
}
KUrl::List::const_iterator itdir = dirsToUpdate.constBegin();
for (; itdir != dirsToUpdate.constEnd() ; ++itdir)
updateDirectory( *itdir );
// ## TODO problems with current jobs listing/updating that dir
// ( see kde-2.2.2's kdirlister )
processPendingUpdates();
}
void KDirListerCache::slotFileRenamed( const QString &_src, const QString &_dst ) // from KDirNotify signals
{
KUrl src( _src );
KUrl dst( _dst );
kDebug(7004) << src << "->" << dst;
#ifdef DEBUG_CACHE
printDebug();
#endif
KUrl oldurl(src);
oldurl.adjustPath( KUrl::RemoveTrailingSlash );
KFileItem *fileitem = findByUrl(0, oldurl);
if (!fileitem) {
kDebug(7004) << "Item not found:" << oldurl;
return;
}
const KFileItem oldItem = *fileitem;
// Dest already exists? Was overwritten then (testcase: #151851)
// We better emit it as deleted -before- doing the renaming, otherwise
// the "update" mechanism will emit the old one as deleted and
// kdirmodel will delete the new (renamed) one!
KFileItem* existingDestItem = findByUrl(0, dst);
if (existingDestItem) {
//kDebug() << dst << "already existed, let's delete it";
slotFilesRemoved(dst);
}
// If the item had a UDS_URL as well as UDS_NAME set, the user probably wants
// to be updating the name only (since they can't see the URL).
// Check to see if a URL exists, and if so, if only the file part has changed,
// only update the name and not the underlying URL.
bool nameOnly = !fileitem->entry().stringValue( KIO::UDSEntry::UDS_URL ).isEmpty();
nameOnly &= src.directory( KUrl::IgnoreTrailingSlash | KUrl::AppendTrailingSlash ) ==
dst.directory( KUrl::IgnoreTrailingSlash | KUrl::AppendTrailingSlash );
if (!nameOnly && fileitem->isDir()) {
renameDir( src, dst );
// #172945 - if the fileitem was the root item of a DirItem that was just removed from the cache,
// then it's a dangling pointer now...
fileitem = findByUrl(0, oldurl);
if (!fileitem) //deleted from cache altogether, #188807
return;
}
// Now update the KFileItem representing that file or dir (not exclusive with the above!)
if (!oldItem.isLocalFile() && !oldItem.localPath().isEmpty()) { // it uses UDS_LOCAL_PATH? ouch, needs an update then
slotFilesChanged( QStringList() << src.url() );
} else {
if( nameOnly )
fileitem->setName( dst.fileName() );
else
fileitem->setUrl( dst );
fileitem->refreshMimeType();
fileitem->determineMimeType();
QSet<KDirLister*> listers = emitRefreshItem( oldItem, *fileitem );
Q_FOREACH(KDirLister * kdl, listers) {
kdl->d->emitItems();
}
}
#ifdef DEBUG_CACHE
printDebug();
#endif
}
QSet<KDirLister*> KDirListerCache::emitRefreshItem(const KFileItem& oldItem, const KFileItem& fileitem)
{
//kDebug(7004) << "old:" << oldItem.name() << oldItem.url()
// << "new:" << fileitem.name() << fileitem.url();
// Look whether this item was shown in any view, i.e. held by any dirlister
KUrl parentDir( oldItem.url() );
parentDir.setPath( parentDir.directory() );
const QString parentDirURL = parentDir.url();
DirectoryDataHash::iterator dit = directoryData.find(parentDirURL);
QList<KDirLister *> listers;
// Also look in listersCurrentlyListing, in case the user manages to rename during a listing
if (dit != directoryData.end())
listers += (*dit).listersCurrentlyHolding + (*dit).listersCurrentlyListing;
if (oldItem.isDir()) {
// For a directory, look for dirlisters where it's the root item.
dit = directoryData.find(oldItem.url().url());
if (dit != directoryData.end())
listers += (*dit).listersCurrentlyHolding + (*dit).listersCurrentlyListing;
}
QSet<KDirLister*> listersToRefresh;
Q_FOREACH(KDirLister *kdl, listers) {
// For a directory, look for dirlisters where it's the root item.
KUrl directoryUrl(oldItem.url());
if (oldItem.isDir() && kdl->d->rootFileItem == oldItem) {
const KFileItem oldRootItem = kdl->d->rootFileItem;
kdl->d->rootFileItem = fileitem;
kdl->d->addRefreshItem(directoryUrl, oldRootItem, fileitem);
} else {
directoryUrl.setPath(directoryUrl.directory());
kdl->d->addRefreshItem(directoryUrl, oldItem, fileitem);
}
listersToRefresh.insert(kdl);
}
return listersToRefresh;
}
QStringList KDirListerCache::directoriesForCanonicalPath(const QString& dir) const
{
QStringList dirs;
dirs << dir;
dirs << canonicalUrls.value(dir).toSet().toList(); /* make unique; there are faster ways, but this is really small anyway */
if (dirs.count() > 1)
kDebug() << dir << "known as" << dirs;
return dirs;
}
// private slots
// Called by KDirWatch - usually when a dir we're watching has been modified,
// but it can also be called for a file.
void KDirListerCache::slotFileDirty( const QString& path )
{
kDebug(7004) << path;
// File or dir?
KDE_struct_stat buff;
if ( KDE::stat( path, &buff ) != 0 )
return; // error
const bool isDir = S_ISDIR(buff.st_mode);
KUrl url(path);
url.adjustPath(KUrl::RemoveTrailingSlash);
if (isDir) {
- Q_FOREACH(const QString& dir, directoriesForCanonicalPath(url.path())) {
+ Q_FOREACH(const QString& dir, directoriesForCanonicalPath(url.toLocalFile())) {
handleDirDirty(dir);
}
} else {
Q_FOREACH(const QString& dir, directoriesForCanonicalPath(url.directory())) {
KUrl aliasUrl(dir);
aliasUrl.addPath(url.fileName());
handleFileDirty(aliasUrl);
}
}
}
// Called by slotFileDirty
void KDirListerCache::handleDirDirty(const KUrl& url)
{
// A dir: launch an update job if anyone cares about it
// This also means we can forget about pending updates to individual files in that dir
const QString dirPath = url.toLocalFile(KUrl::AddTrailingSlash);
QMutableSetIterator<QString> pendingIt(pendingUpdates);
while (pendingIt.hasNext()) {
const QString updPath = pendingIt.next();
//kDebug(7004) << "had pending update" << updPath;
if (updPath.startsWith(dirPath) &&
updPath.indexOf('/', dirPath.length()) == -1) { // direct child item
kDebug(7004) << "forgetting about individual update to" << updPath;
pendingIt.remove();
}
}
updateDirectory(url);
}
// Called by slotFileDirty
void KDirListerCache::handleFileDirty(const KUrl& url)
{
// A file: do we know about it already?
KFileItem* existingItem = findByUrl(0, url);
if (!existingItem) {
// No - update the parent dir then
KUrl dir(url);
dir.setPath(url.directory());
updateDirectory(dir);
} else {
// A known file: delay updating it, FAM is flooding us with events
const QString filePath = url.toLocalFile();
if (!pendingUpdates.contains(filePath)) {
KUrl dir(url);
dir.setPath(dir.directory());
if (checkUpdate(dir.url())) {
pendingUpdates.insert(filePath);
if (!pendingUpdateTimer.isActive())
pendingUpdateTimer.start(500);
}
}
}
}
void KDirListerCache::slotFileCreated( const QString& path ) // from KDirWatch
{
kDebug(7004) << path;
// XXX: how to avoid a complete rescan here?
// We'd need to stat that one file separately and refresh the item(s) for it.
KUrl fileUrl(path);
slotFilesAdded(fileUrl.directory());
}
void KDirListerCache::slotFileDeleted( const QString& path ) // from KDirWatch
{
kDebug(7004) << path;
KUrl u( path );
QStringList fileUrls;
Q_FOREACH(KUrl url, directoriesForCanonicalPath(u.directory())) {
url.addPath(u.fileName());
fileUrls << url.url();
}
slotFilesRemoved(fileUrls);
}
void KDirListerCache::slotEntries( KIO::Job *job, const KIO::UDSEntryList &entries )
{
KUrl url(joburl( static_cast<KIO::ListJob *>(job) ));
url.adjustPath(KUrl::RemoveTrailingSlash);
QString urlStr = url.url();
//kDebug(7004) << "new entries for " << url;
DirItem *dir = itemsInUse.value(urlStr);
if (!dir) {
kError(7004) << "Internal error: job is listing" << url << "but itemsInUse only knows about" << itemsInUse.keys();
Q_ASSERT( dir );
return;
}
DirectoryDataHash::iterator dit = directoryData.find(urlStr);
if (dit == directoryData.end()) {
kError(7004) << "Internal error: job is listing" << url << "but directoryData doesn't know about that url, only about:" << directoryData.keys();
Q_ASSERT(dit != directoryData.end());
return;
}
KDirListerCacheDirectoryData& dirData = *dit;
if (dirData.listersCurrentlyListing.isEmpty()) {
kError(7004) << "Internal error: job is listing" << url << "but directoryData says no listers are currently listing " << urlStr;
#ifndef NDEBUG
printDebug();
#endif
Q_ASSERT( !dirData.listersCurrentlyListing.isEmpty() );
return;
}
// check if anyone wants the mimetypes immediately
bool delayedMimeTypes = true;
foreach ( KDirLister *kdl, dirData.listersCurrentlyListing )
delayedMimeTypes &= kdl->d->delayedMimeTypes;
KIO::UDSEntryList::const_iterator it = entries.begin();
const KIO::UDSEntryList::const_iterator end = entries.end();
for ( ; it != end; ++it )
{
const QString name = (*it).stringValue( KIO::UDSEntry::UDS_NAME );
Q_ASSERT( !name.isEmpty() );
if ( name.isEmpty() )
continue;
if ( name == "." )
{
Q_ASSERT( dir->rootItem.isNull() );
// Try to reuse an existing KFileItem (if we listed the parent dir)
// rather than creating a new one. There are many reasons:
// 1) renames and permission changes to the item would have to emit the signals
// twice, otherwise, so that both views manage to recognize the item.
// 2) with kio_ftp we can only know that something is a symlink when
// listing the parent, so prefer that item, which has more info.
// Note that it gives a funky name() to the root item, rather than "." ;)
dir->rootItem = itemForUrl(url);
if (dir->rootItem.isNull())
dir->rootItem = KFileItem( *it, url, delayedMimeTypes, true );
foreach ( KDirLister *kdl, dirData.listersCurrentlyListing )
if ( kdl->d->rootFileItem.isNull() && kdl->d->url == url )
kdl->d->rootFileItem = dir->rootItem;
}
else if ( name != ".." )
{
KFileItem item( *it, url, delayedMimeTypes, true );
//kDebug(7004)<< "Adding item: " << item.url();
dir->lstItems.append( item );
foreach ( KDirLister *kdl, dirData.listersCurrentlyListing )
kdl->d->addNewItem(url, item);
}
}
foreach ( KDirLister *kdl, dirData.listersCurrentlyListing )
kdl->d->emitItems();
}
void KDirListerCache::slotResult( KJob *j )
{
#ifdef DEBUG_CACHE
//printDebug();
#endif
Q_ASSERT( j );
KIO::ListJob *job = static_cast<KIO::ListJob *>( j );
runningListJobs.remove( job );
KUrl jobUrl(joburl( job ));
jobUrl.adjustPath(KUrl::RemoveTrailingSlash); // need remove trailing slashes again, in case of redirections
QString jobUrlStr = jobUrl.url();
kDebug(7004) << "finished listing" << jobUrl;
DirectoryDataHash::iterator dit = directoryData.find(jobUrlStr);
if (dit == directoryData.end()) {
kError() << "Nothing found in directoryData for URL" << jobUrlStr;
#ifndef NDEBUG
printDebug();
#endif
Q_ASSERT(dit != directoryData.end());
return;
}
KDirListerCacheDirectoryData& dirData = *dit;
if ( dirData.listersCurrentlyListing.isEmpty() ) {
kError() << "OOOOPS, nothing in directoryData.listersCurrentlyListing for" << jobUrlStr;
// We're about to assert; dump the current state...
#ifndef NDEBUG
printDebug();
#endif
Q_ASSERT( !dirData.listersCurrentlyListing.isEmpty() );
}
QList<KDirLister *> listers = dirData.listersCurrentlyListing;
// move all listers to the holding list, do it before emitting
// the signals to make sure it exists in KDirListerCache in case someone
// calls listDir during the signal emission
Q_ASSERT( dirData.listersCurrentlyHolding.isEmpty() );
dirData.moveListersWithoutCachedItemsJob(jobUrl);
if ( job->error() )
{
foreach ( KDirLister *kdl, listers )
{
kdl->d->jobDone( job );
if (job->error() != KJob::KilledJobError) {
kdl->handleError( job );
}
const bool silent = job->property("_kdlc_silent").toBool();
if (!silent) {
emit kdl->canceled( jobUrl );
}
if (kdl->d->numJobs() == 0) {
kdl->d->complete = true;
if (!silent) {
emit kdl->canceled();
}
}
}
}
else
{
DirItem *dir = itemsInUse.value(jobUrlStr);
Q_ASSERT( dir );
dir->complete = true;
foreach ( KDirLister* kdl, listers )
{
kdl->d->jobDone( job );
emit kdl->completed( jobUrl );
if ( kdl->d->numJobs() == 0 )
{
kdl->d->complete = true;
emit kdl->completed();
}
}
}
// TODO: hmm, if there was an error and job is a parent of one or more
// of the pending urls we should cancel it/them as well
processPendingUpdates();
#ifdef DEBUG_CACHE
printDebug();
#endif
}
void KDirListerCache::slotRedirection( KIO::Job *j, const KUrl& url )
{
Q_ASSERT( j );
KIO::ListJob *job = static_cast<KIO::ListJob *>( j );
KUrl oldUrl(job->url()); // here we really need the old url!
KUrl newUrl(url);
// strip trailing slashes
oldUrl.adjustPath(KUrl::RemoveTrailingSlash);
newUrl.adjustPath(KUrl::RemoveTrailingSlash);
if ( oldUrl == newUrl ) {
kDebug(7004) << "New redirection url same as old, giving up.";
return;
} else if (newUrl.isEmpty()) {
kDebug(7004) << "New redirection url is empty, giving up.";
return;
}
const QString oldUrlStr = oldUrl.url();
const QString newUrlStr = newUrl.url();
kDebug(7004) << oldUrl << "->" << newUrl;
#ifdef DEBUG_CACHE
// Can't do that here. KDirListerCache::joburl() will use the new url already,
// while our data structures haven't been updated yet -> assert fail.
//printDebug();
#endif
// I don't think there can be dirItems that are children of oldUrl.
// Am I wrong here? And even if so, we don't need to delete them, right?
// DF: redirection happens before listDir emits any item. Makes little sense otherwise.
// oldUrl cannot be in itemsCached because only completed items are moved there
DirItem *dir = itemsInUse.take(oldUrlStr);
Q_ASSERT( dir );
DirectoryDataHash::iterator dit = directoryData.find(oldUrlStr);
Q_ASSERT(dit != directoryData.end());
KDirListerCacheDirectoryData oldDirData = *dit;
directoryData.erase(dit);
Q_ASSERT( !oldDirData.listersCurrentlyListing.isEmpty() );
const QList<KDirLister *> listers = oldDirData.listersCurrentlyListing;
Q_ASSERT( !listers.isEmpty() );
foreach ( KDirLister *kdl, listers ) {
kdl->d->redirect(oldUrlStr, newUrl, false /*clear items*/);
}
// when a lister was stopped before the job emits the redirection signal, the old url will
// also be in listersCurrentlyHolding
const QList<KDirLister *> holders = oldDirData.listersCurrentlyHolding;
foreach ( KDirLister *kdl, holders ) {
kdl->d->jobStarted( job );
// do it like when starting a new list-job that will redirect later
// TODO: maybe don't emit started if there's an update running for newUrl already?
emit kdl->started( oldUrl );
kdl->d->redirect(oldUrl, newUrl, false /*clear items*/);
}
DirItem *newDir = itemsInUse.value(newUrlStr);
if ( newDir ) {
kDebug(7004) << newUrl << "already in use";
// only in this case there can newUrl already be in listersCurrentlyListing or listersCurrentlyHolding
delete dir;
// get the job if one's running for newUrl already (can be a list-job or an update-job), but
// do not return this 'job', which would happen because of the use of redirectionURL()
KIO::ListJob *oldJob = jobForUrl( newUrlStr, job );
// listers of newUrl with oldJob: forget about the oldJob and use the already running one
// which will be converted to an updateJob
KDirListerCacheDirectoryData& newDirData = directoryData[newUrlStr];
QList<KDirLister *>& curListers = newDirData.listersCurrentlyListing;
if ( !curListers.isEmpty() ) {
kDebug(7004) << "and it is currently listed";
Q_ASSERT( oldJob ); // ?!
foreach ( KDirLister *kdl, curListers ) { // listers of newUrl
kdl->d->jobDone( oldJob );
kdl->d->jobStarted( job );
kdl->d->connectJob( job );
}
// append listers of oldUrl with newJob to listers of newUrl with oldJob
foreach ( KDirLister *kdl, listers )
curListers.append( kdl );
} else {
curListers = listers;
}
if ( oldJob ) // kill the old job, be it a list-job or an update-job
killJob( oldJob );
// holders of newUrl: use the already running job which will be converted to an updateJob
QList<KDirLister *>& curHolders = newDirData.listersCurrentlyHolding;
if ( !curHolders.isEmpty() ) {
kDebug(7004) << "and it is currently held.";
foreach ( KDirLister *kdl, curHolders ) { // holders of newUrl
kdl->d->jobStarted( job );
emit kdl->started( newUrl );
}
// append holders of oldUrl to holders of newUrl
foreach ( KDirLister *kdl, holders )
curHolders.append( kdl );
} else {
curHolders = holders;
}
// emit old items: listers, holders. NOT: newUrlListers/newUrlHolders, they already have them listed
// TODO: make this a separate method?
foreach ( KDirLister *kdl, listers + holders ) {
if ( kdl->d->rootFileItem.isNull() && kdl->d->url == newUrl )
kdl->d->rootFileItem = newDir->rootItem;
kdl->d->addNewItems(newUrl, newDir->lstItems);
kdl->d->emitItems();
}
} else if ( (newDir = itemsCached.take( newUrlStr )) ) {
kDebug(7004) << newUrl << "is unused, but already in the cache.";
delete dir;
itemsInUse.insert( newUrlStr, newDir );
KDirListerCacheDirectoryData& newDirData = directoryData[newUrlStr];
newDirData.listersCurrentlyListing = listers;
newDirData.listersCurrentlyHolding = holders;
// emit old items: listers, holders
foreach ( KDirLister *kdl, listers + holders ) {
if ( kdl->d->rootFileItem.isNull() && kdl->d->url == newUrl )
kdl->d->rootFileItem = newDir->rootItem;
kdl->d->addNewItems(newUrl, newDir->lstItems);
kdl->d->emitItems();
}
} else {
kDebug(7004) << newUrl << "has not been listed yet.";
dir->rootItem = KFileItem();
dir->lstItems.clear();
dir->redirect( newUrl );
itemsInUse.insert( newUrlStr, dir );
KDirListerCacheDirectoryData& newDirData = directoryData[newUrlStr];
newDirData.listersCurrentlyListing = listers;
newDirData.listersCurrentlyHolding = holders;
if ( holders.isEmpty() ) {
#ifdef DEBUG_CACHE
printDebug();
#endif
return; // only in this case the job doesn't need to be converted,
}
}
// make the job an update job
job->disconnect( this );
connect( job, SIGNAL(entries( KIO::Job *, const KIO::UDSEntryList & )),
this, SLOT(slotUpdateEntries( KIO::Job *, const KIO::UDSEntryList & )) );
connect( job, SIGNAL(result( KJob * )),
this, SLOT(slotUpdateResult( KJob * )) );
// FIXME: autoUpdate-Counts!!
#ifdef DEBUG_CACHE
printDebug();
#endif
}
struct KDirListerCache::ItemInUseChange
{
ItemInUseChange(const QString& old, const QString& newU, DirItem* di)
: oldUrl(old), newUrl(newU), dirItem(di) {}
QString oldUrl;
QString newUrl;
DirItem* dirItem;
};
void KDirListerCache::renameDir( const KUrl &oldUrl, const KUrl &newUrl )
{
kDebug(7004) << oldUrl << "->" << newUrl;
const QString oldUrlStr = oldUrl.url(KUrl::RemoveTrailingSlash);
const QString newUrlStr = newUrl.url(KUrl::RemoveTrailingSlash);
// Not enough. Also need to look at any child dir, even sub-sub-sub-dir.
//DirItem *dir = itemsInUse.take( oldUrlStr );
//emitRedirections( oldUrl, url );
QLinkedList<ItemInUseChange> itemsToChange;
QSet<KDirLister *> listers;
// Look at all dirs being listed/shown
QHash<QString, DirItem *>::iterator itu = itemsInUse.begin();
const QHash<QString, DirItem *>::iterator ituend = itemsInUse.end();
for (; itu != ituend ; ++itu) {
DirItem *dir = itu.value();
KUrl oldDirUrl ( itu.key() );
//kDebug(7004) << "itemInUse:" << oldDirUrl;
// Check if this dir is oldUrl, or a subfolder of it
if ( oldUrl.isParentOf( oldDirUrl ) ) {
// TODO should use KUrl::cleanpath like isParentOf does
QString relPath = oldDirUrl.path().mid( oldUrl.path().length() );
KUrl newDirUrl( newUrl ); // take new base
if ( !relPath.isEmpty() )
newDirUrl.addPath( relPath ); // add unchanged relative path
//kDebug(7004) << "new url=" << newDirUrl;
// Update URL in dir item and in itemsInUse
dir->redirect( newDirUrl );
itemsToChange.append(ItemInUseChange(oldDirUrl.url(KUrl::RemoveTrailingSlash),
newDirUrl.url(KUrl::RemoveTrailingSlash),
dir));
// Rename all items under that dir
for ( KFileItemList::iterator kit = dir->lstItems.begin(), kend = dir->lstItems.end();
kit != kend ; ++kit )
{
const KFileItem oldItem = *kit;
const KUrl oldItemUrl ((*kit).url());
const QString oldItemUrlStr( oldItemUrl.url(KUrl::RemoveTrailingSlash) );
KUrl newItemUrl( oldItemUrl );
newItemUrl.setPath( newDirUrl.path() );
newItemUrl.addPath( oldItemUrl.fileName() );
kDebug(7004) << "renaming" << oldItemUrl << "to" << newItemUrl;
(*kit).setUrl(newItemUrl);
listers |= emitRefreshItem(oldItem, *kit);
}
emitRedirections( oldDirUrl, newDirUrl );
}
}
Q_FOREACH(KDirLister * kdl, listers) {
kdl->d->emitItems();
}
// Do the changes to itemsInUse out of the loop to avoid messing up iterators,
// and so that emitRefreshItem can find the stuff in the hash.
foreach(const ItemInUseChange& i, itemsToChange) {
itemsInUse.remove(i.oldUrl);
itemsInUse.insert(i.newUrl, i.dirItem);
}
// Is oldUrl a directory in the cache?
// Remove any child of oldUrl from the cache - even if the renamed dir itself isn't in it!
removeDirFromCache( oldUrl );
// TODO rename, instead.
}
// helper for renameDir, not used for redirections from KIO::listDir().
void KDirListerCache::emitRedirections( const KUrl &oldUrl, const KUrl &newUrl )
{
kDebug(7004) << oldUrl << "->" << newUrl;
const QString oldUrlStr = oldUrl.url(KUrl::RemoveTrailingSlash);
const QString newUrlStr = newUrl.url(KUrl::RemoveTrailingSlash);
KIO::ListJob *job = jobForUrl( oldUrlStr );
if ( job )
killJob( job );
// Check if we were listing this dir. Need to abort and restart with new name in that case.
DirectoryDataHash::iterator dit = directoryData.find(oldUrlStr);
if ( dit == directoryData.end() )
return;
const QList<KDirLister *> listers = (*dit).listersCurrentlyListing;
const QList<KDirLister *> holders = (*dit).listersCurrentlyHolding;
KDirListerCacheDirectoryData& newDirData = directoryData[newUrlStr];
// Tell the world that the job listing the old url is dead.
foreach ( KDirLister *kdl, listers ) {
if ( job )
kdl->d->jobDone( job );
emit kdl->canceled( oldUrl );
}
newDirData.listersCurrentlyListing += listers;
// Check if we are currently displaying this directory (odds opposite wrt above)
foreach ( KDirLister *kdl, holders ) {
if ( job )
kdl->d->jobDone( job );
}
newDirData.listersCurrentlyHolding += holders;
directoryData.erase(dit);
if ( !listers.isEmpty() ) {
updateDirectory( newUrl );
// Tell the world about the new url
foreach ( KDirLister *kdl, listers )
emit kdl->started( newUrl );
}
// And notify the dirlisters of the redirection
foreach ( KDirLister *kdl, holders ) {
kdl->d->redirect(oldUrl, newUrl, true /*keep items*/);
}
}
void KDirListerCache::removeDirFromCache( const KUrl& dir )
{
kDebug(7004) << dir;
const QList<QString> cachedDirs = itemsCached.keys(); // seems slow, but there's no qcache iterator...
foreach(const QString& cachedDir, cachedDirs) {
if ( dir.isParentOf( KUrl( cachedDir ) ) )
itemsCached.remove( cachedDir );
}
}
void KDirListerCache::slotUpdateEntries( KIO::Job* job, const KIO::UDSEntryList& list )
{
runningListJobs[static_cast<KIO::ListJob*>(job)] += list;
}
void KDirListerCache::slotUpdateResult( KJob * j )
{
Q_ASSERT( j );
KIO::ListJob *job = static_cast<KIO::ListJob *>( j );
KUrl jobUrl (joburl( job ));
jobUrl.adjustPath(KUrl::RemoveTrailingSlash); // need remove trailing slashes again, in case of redirections
QString jobUrlStr (jobUrl.url());
kDebug(7004) << "finished update" << jobUrl;
KDirListerCacheDirectoryData& dirData = directoryData[jobUrlStr];
// Collect the dirlisters which were listing the URL using that ListJob
// plus those that were already holding that URL - they all get updated.
dirData.moveListersWithoutCachedItemsJob(jobUrl);
QList<KDirLister *> listers = dirData.listersCurrentlyHolding;
listers += dirData.listersCurrentlyListing;
// once we are updating dirs that are only in the cache this will fail!
Q_ASSERT( !listers.isEmpty() );
if ( job->error() ) {
foreach ( KDirLister* kdl, listers ) {
kdl->d->jobDone( job );
//don't bother the user
//kdl->handleError( job );
const bool silent = job->property("_kdlc_silent").toBool();
if (!silent) {
emit kdl->canceled( jobUrl );
}
if ( kdl->d->numJobs() == 0 ) {
kdl->d->complete = true;
if (!silent) {
emit kdl->canceled();
}
}
}
runningListJobs.remove( job );
// TODO: if job is a parent of one or more
// of the pending urls we should cancel them
processPendingUpdates();
return;
}
DirItem *dir = itemsInUse.value(jobUrlStr, 0);
if (!dir) {
kError(7004) << "Internal error: itemsInUse did not contain" << jobUrlStr;
#ifndef NDEBUG
printDebug();
#endif
Q_ASSERT(dir);
} else {
dir->complete = true;
}
// check if anyone wants the mimetypes immediately
bool delayedMimeTypes = true;
foreach ( KDirLister *kdl, listers )
delayedMimeTypes &= kdl->d->delayedMimeTypes;
QHash<QString, KFileItem*> fileItems; // fileName -> KFileItem*
// Unmark all items in url
for ( KFileItemList::iterator kit = dir->lstItems.begin(), kend = dir->lstItems.end() ; kit != kend ; ++kit )
{
(*kit).unmark();
fileItems.insert( (*kit).name(), &*kit );
}
const KIO::UDSEntryList& buf = runningListJobs.value( job );
KIO::UDSEntryList::const_iterator it = buf.constBegin();
const KIO::UDSEntryList::const_iterator end = buf.constEnd();
for ( ; it != end; ++it )
{
// Form the complete url
KFileItem item( *it, jobUrl, delayedMimeTypes, true );
const QString name = item.name();
Q_ASSERT( !name.isEmpty() );
// we duplicate the check for dotdot here, to avoid iterating over
// all items again and checking in matchesFilter() that way.
if ( name.isEmpty() || name == ".." )
continue;
if ( name == "." )
{
// if the update was started before finishing the original listing
// there is no root item yet
if ( dir->rootItem.isNull() )
{
dir->rootItem = item;
foreach ( KDirLister *kdl, listers )
if ( kdl->d->rootFileItem.isNull() && kdl->d->url == jobUrl )
kdl->d->rootFileItem = dir->rootItem;
}
continue;
}
// Find this item
if (KFileItem* tmp = fileItems.value(item.name()))
{
QSet<KFileItem*>::iterator pru_it = pendingRemoteUpdates.find(tmp);
const bool inPendingRemoteUpdates = (pru_it != pendingRemoteUpdates.end());
// check if something changed for this file, using KFileItem::cmp()
if (!tmp->cmp( item ) || inPendingRemoteUpdates) {
if (inPendingRemoteUpdates) {
pendingRemoteUpdates.erase(pru_it);
}
//kDebug(7004) << "file changed:" << tmp->name();
const KFileItem oldItem = *tmp;
*tmp = item;
foreach ( KDirLister *kdl, listers )
kdl->d->addRefreshItem(jobUrl, oldItem, *tmp);
}
//kDebug(7004) << "marking" << tmp;
tmp->mark();
}
else // this is a new file
{
//kDebug(7004) << "new file:" << name;
KFileItem pitem(item);
pitem.mark();
dir->lstItems.append( pitem );
foreach ( KDirLister *kdl, listers )
kdl->d->addNewItem(jobUrl, pitem);
}
}
runningListJobs.remove( job );
deleteUnmarkedItems( listers, dir->lstItems );
foreach ( KDirLister *kdl, listers ) {
kdl->d->emitItems();
kdl->d->jobDone( job );
emit kdl->completed( jobUrl );
if ( kdl->d->numJobs() == 0 )
{
kdl->d->complete = true;
emit kdl->completed();
}
}
// TODO: hmm, if there was an error and job is a parent of one or more
// of the pending urls we should cancel it/them as well
processPendingUpdates();
}
// private
KIO::ListJob *KDirListerCache::jobForUrl( const QString& url, KIO::ListJob *not_job )
{
QMap< KIO::ListJob *, KIO::UDSEntryList >::const_iterator it = runningListJobs.constBegin();
while ( it != runningListJobs.constEnd() )
{
KIO::ListJob *job = it.key();
if ( joburl( job ).url(KUrl::RemoveTrailingSlash) == url && job != not_job )
return job;
++it;
}
return 0;
}
const KUrl& KDirListerCache::joburl( KIO::ListJob *job )
{
if ( job->redirectionUrl().isValid() )
return job->redirectionUrl();
else
return job->url();
}
void KDirListerCache::killJob( KIO::ListJob *job )
{
runningListJobs.remove( job );
job->disconnect( this );
job->kill();
}
void KDirListerCache::deleteUnmarkedItems( const QList<KDirLister *>& listers, KFileItemList &lstItems )
{
KFileItemList deletedItems;
// Find all unmarked items and delete them
QMutableListIterator<KFileItem> kit(lstItems);
while (kit.hasNext()) {
const KFileItem& item = kit.next();
if (!item.isMarked()) {
//kDebug(7004) << "deleted:" << item.name() << &item;
deletedItems.append(item);
kit.remove();
}
}
if (!deletedItems.isEmpty())
itemsDeleted(listers, deletedItems);
}
void KDirListerCache::itemsDeleted(const QList<KDirLister *>& listers, const KFileItemList& deletedItems)
{
Q_FOREACH(KDirLister *kdl, listers) {
kdl->d->emitItemsDeleted(deletedItems);
}
Q_FOREACH(const KFileItem& item, deletedItems) {
if (item.isDir())
deleteDir(item.url());
}
}
void KDirListerCache::deleteDir( const KUrl& dirUrl )
{
//kDebug() << dirUrl;
// unregister and remove the children of the deleted item.
// Idea: tell all the KDirListers that they should forget the dir
// and then remove it from the cache.
// Separate itemsInUse iteration and calls to forgetDirs (which modify itemsInUse)
KUrl::List affectedItems;
QHash<QString, DirItem *>::iterator itu = itemsInUse.begin();
const QHash<QString, DirItem *>::iterator ituend = itemsInUse.end();
for ( ; itu != ituend; ++itu ) {
const KUrl deletedUrl( itu.key() );
if ( dirUrl.isParentOf( deletedUrl ) ) {
affectedItems.append(deletedUrl);
}
}
foreach(const KUrl& deletedUrl, affectedItems) {
const QString deletedUrlStr = deletedUrl.url();
// stop all jobs for deletedUrlStr
DirectoryDataHash::iterator dit = directoryData.find(deletedUrlStr);
if (dit != directoryData.end()) {
// we need a copy because stop modifies the list
QList<KDirLister *> listers = (*dit).listersCurrentlyListing;
foreach ( KDirLister *kdl, listers )
stopListingUrl( kdl, deletedUrl );
// tell listers holding deletedUrl to forget about it
// this will stop running updates for deletedUrl as well
// we need a copy because forgetDirs modifies the list
QList<KDirLister *> holders = (*dit).listersCurrentlyHolding;
foreach ( KDirLister *kdl, holders ) {
// lister's root is the deleted item
if ( kdl->d->url == deletedUrl )
{
// tell the view first. It might need the subdirs' items (which forgetDirs will delete)
if ( !kdl->d->rootFileItem.isNull() ) {
emit kdl->deleteItem( kdl->d->rootFileItem );
emit kdl->itemsDeleted(KFileItemList() << kdl->d->rootFileItem);
}
forgetDirs( kdl );
kdl->d->rootFileItem = KFileItem();
}
else
{
const bool treeview = kdl->d->lstDirs.count() > 1;
if ( !treeview )
{
emit kdl->clear();
kdl->d->lstDirs.clear();
}
else
kdl->d->lstDirs.removeAll( deletedUrl );
forgetDirs( kdl, deletedUrl, treeview );
}
}
}
// delete the entry for deletedUrl - should not be needed, it's in
// items cached now
int count = itemsInUse.remove( deletedUrlStr );
Q_ASSERT( count == 0 );
Q_UNUSED( count ); //keep gcc "unused variable" complaining quiet when in release mode
}
// remove the children from the cache
removeDirFromCache( dirUrl );
}
// delayed updating of files, FAM is flooding us with events
void KDirListerCache::processPendingUpdates()
{
QSet<KDirLister *> listers;
foreach(const QString& file, pendingUpdates) { // always a local path
kDebug(7004) << file;
KUrl u(file);
KFileItem *item = findByUrl( 0, u ); // search all items
if ( item ) {
// we need to refresh the item, because e.g. the permissions can have changed.
KFileItem oldItem = *item;
item->refresh();
listers |= emitRefreshItem( oldItem, *item );
}
}
pendingUpdates.clear();
Q_FOREACH(KDirLister * kdl, listers) {
kdl->d->emitItems();
}
}
#ifndef NDEBUG
void KDirListerCache::printDebug()
{
kDebug(7004) << "Items in use:";
QHash<QString, DirItem *>::const_iterator itu = itemsInUse.constBegin();
const QHash<QString, DirItem *>::const_iterator ituend = itemsInUse.constEnd();
for ( ; itu != ituend ; ++itu ) {
kDebug(7004) << " " << itu.key() << "URL:" << itu.value()->url
<< "rootItem:" << ( !itu.value()->rootItem.isNull() ? itu.value()->rootItem.url() : KUrl() )
<< "autoUpdates refcount:" << itu.value()->autoUpdates
<< "complete:" << itu.value()->complete
<< QString("with %1 items.").arg(itu.value()->lstItems.count());
}
QList<KDirLister*> listersWithoutJob;
kDebug(7004) << "Directory data:";
DirectoryDataHash::const_iterator dit = directoryData.constBegin();
for ( ; dit != directoryData.constEnd(); ++dit )
{
QString list;
foreach ( KDirLister* listit, (*dit).listersCurrentlyListing )
list += " 0x" + QString::number( (qlonglong)listit, 16 );
kDebug(7004) << " " << dit.key() << (*dit).listersCurrentlyListing.count() << "listers:" << list;
foreach ( KDirLister* listit, (*dit).listersCurrentlyListing ) {
if (!listit->d->m_cachedItemsJobs.isEmpty()) {
kDebug(7004) << " Lister" << listit << "has CachedItemsJobs" << listit->d->m_cachedItemsJobs;
} else if (KIO::ListJob* listJob = jobForUrl(dit.key())) {
kDebug(7004) << " Lister" << listit << "has ListJob" << listJob;
} else {
listersWithoutJob.append(listit);
}
}
list.clear();
foreach ( KDirLister* listit, (*dit).listersCurrentlyHolding )
list += " 0x" + QString::number( (qlonglong)listit, 16 );
kDebug(7004) << " " << dit.key() << (*dit).listersCurrentlyHolding.count() << "holders:" << list;
}
QMap< KIO::ListJob *, KIO::UDSEntryList >::Iterator jit = runningListJobs.begin();
kDebug(7004) << "Jobs:";
for ( ; jit != runningListJobs.end() ; ++jit )
kDebug(7004) << " " << jit.key() << "listing" << joburl( jit.key() ) << ":" << (*jit).count() << "entries.";
kDebug(7004) << "Items in cache:";
const QList<QString> cachedDirs = itemsCached.keys();
foreach(const QString& cachedDir, cachedDirs) {
DirItem* dirItem = itemsCached.object(cachedDir);
kDebug(7004) << " " << cachedDir << "rootItem:"
<< (!dirItem->rootItem.isNull() ? dirItem->rootItem.url().prettyUrl() : QString("NULL") )
<< "with" << dirItem->lstItems.count() << "items.";
}
// Abort on listers without jobs -after- showing the full dump. Easier debugging.
Q_FOREACH(KDirLister* listit, listersWithoutJob) {
kFatal() << "HUH? Lister" << listit << "is supposed to be listing, but has no job!";
}
}
#endif
KDirLister::KDirLister( QObject* parent )
: QObject(parent), d(new Private(this))
{
//kDebug(7003) << "+KDirLister";
d->complete = true;
setAutoUpdate( true );
setDirOnlyMode( false );
setShowingDotFiles( false );
setAutoErrorHandlingEnabled( true, 0 );
}
KDirLister::~KDirLister()
{
//kDebug(7003) << "~KDirLister" << this;
// Stop all running jobs, remove lister from lists
if (!kDirListerCache.isDestroyed()) {
stop();
kDirListerCache->forgetDirs( this );
}
delete d;
}
bool KDirLister::openUrl( const KUrl& _url, OpenUrlFlags _flags )
{
// emit the current changes made to avoid an inconsistent treeview
if (d->hasPendingChanges && (_flags & Keep))
emitChanges();
d->hasPendingChanges = false;
return kDirListerCache->listDir( this, _url, _flags & Keep, _flags & Reload );
}
void KDirLister::stop()
{
kDirListerCache->stop( this );
}
void KDirLister::stop( const KUrl& _url )
{
kDirListerCache->stopListingUrl( this, _url );
}
bool KDirLister::autoUpdate() const
{
return d->autoUpdate;
}
void KDirLister::setAutoUpdate( bool _enable )
{
if ( d->autoUpdate == _enable )
return;
d->autoUpdate = _enable;
kDirListerCache->setAutoUpdate( this, _enable );
}
bool KDirLister::showingDotFiles() const
{
return d->settings.isShowingDotFiles;
}
void KDirLister::setShowingDotFiles( bool _showDotFiles )
{
if ( d->settings.isShowingDotFiles == _showDotFiles )
return;
d->prepareForSettingsChange();
d->settings.isShowingDotFiles = _showDotFiles;
}
bool KDirLister::dirOnlyMode() const
{
return d->settings.dirOnlyMode;
}
void KDirLister::setDirOnlyMode( bool _dirsOnly )
{
if ( d->settings.dirOnlyMode == _dirsOnly )
return;
d->prepareForSettingsChange();
d->settings.dirOnlyMode = _dirsOnly;
}
bool KDirLister::autoErrorHandlingEnabled() const
{
return d->autoErrorHandling;
}
void KDirLister::setAutoErrorHandlingEnabled( bool enable, QWidget* parent )
{
d->autoErrorHandling = enable;
d->errorParent = parent;
}
KUrl KDirLister::url() const
{
return d->url;
}
KUrl::List KDirLister::directories() const
{
return d->lstDirs;
}
void KDirLister::emitChanges()
{
d->emitChanges();
}
void KDirLister::Private::emitChanges()
{
if (!hasPendingChanges)
return;
// reset 'hasPendingChanges' now, in case of recursion
// (testcase: enabling recursive scan in ktorrent, #174920)
hasPendingChanges = false;
const Private::FilterSettings newSettings = settings;
settings = oldSettings; // temporarily
// Mark all items that are currently visible
Q_FOREACH(const KUrl& dir, lstDirs) {
KFileItemList* itemList = kDirListerCache->itemsForDir(dir);
if (!itemList) {
continue;
}
KFileItemList::iterator kit = itemList->begin();
const KFileItemList::iterator kend = itemList->end();
for (; kit != kend; ++kit) {
if (isItemVisible(*kit) && m_parent->matchesMimeFilter(*kit))
(*kit).mark();
else
(*kit).unmark();
}
}
settings = newSettings;
Q_FOREACH(const KUrl& dir, lstDirs) {
KFileItemList deletedItems;
KFileItemList* itemList = kDirListerCache->itemsForDir(dir);
if (!itemList) {
continue;
}
KFileItemList::iterator kit = itemList->begin();
const KFileItemList::iterator kend = itemList->end();
for (; kit != kend; ++kit) {
KFileItem& item = *kit;
const QString text = item.text();
if (text == "." || text == "..")
continue;
const bool nowVisible = isItemVisible(item) && m_parent->matchesMimeFilter(item);
if (nowVisible && !item.isMarked())
addNewItem(dir, item); // takes care of emitting newItem or itemsFilteredByMime
else if (!nowVisible && item.isMarked())
deletedItems.append(*kit);
}
if (!deletedItems.isEmpty()) {
emit m_parent->itemsDeleted(deletedItems);
// for compat
Q_FOREACH(const KFileItem& item, deletedItems)
emit m_parent->deleteItem(item);
}
emitItems();
}
oldSettings = settings;
}
void KDirLister::updateDirectory( const KUrl& _u )
{
kDirListerCache->updateDirectory( _u );
}
bool KDirLister::isFinished() const
{
return d->complete;
}
KFileItem KDirLister::rootItem() const
{
return d->rootFileItem;
}
KFileItem KDirLister::findByUrl( const KUrl& _url ) const
{
KFileItem *item = kDirListerCache->findByUrl( this, _url );
if (item) {
return *item;
} else {
return KFileItem();
}
}
KFileItem KDirLister::findByName( const QString& _name ) const
{
return kDirListerCache->findByName( this, _name );
}
// ================ public filter methods ================ //
void KDirLister::setNameFilter( const QString& nameFilter )
{
if (d->nameFilter == nameFilter)
return;
d->prepareForSettingsChange();
d->settings.lstFilters.clear();
d->nameFilter = nameFilter;
// Split on white space
const QStringList list = nameFilter.split( ' ', QString::SkipEmptyParts );
for (QStringList::const_iterator it = list.begin(); it != list.end(); ++it)
d->settings.lstFilters.append(QRegExp(*it, Qt::CaseInsensitive, QRegExp::Wildcard));
}
QString KDirLister::nameFilter() const
{
return d->nameFilter;
}
void KDirLister::setMimeFilter( const QStringList& mimeFilter )
{
if (d->settings.mimeFilter == mimeFilter)
return;
d->prepareForSettingsChange();
if (mimeFilter.contains(QLatin1String("application/octet-stream")) || mimeFilter.contains(QLatin1String("all/allfiles"))) // all files
d->settings.mimeFilter.clear();
else
d->settings.mimeFilter = mimeFilter;
}
void KDirLister::setMimeExcludeFilter( const QStringList& mimeExcludeFilter )
{
if (d->settings.mimeExcludeFilter == mimeExcludeFilter)
return;
d->prepareForSettingsChange();
d->settings.mimeExcludeFilter = mimeExcludeFilter;
}
void KDirLister::clearMimeFilter()
{
d->prepareForSettingsChange();
d->settings.mimeFilter.clear();
d->settings.mimeExcludeFilter.clear();
}
QStringList KDirLister::mimeFilters() const
{
return d->settings.mimeFilter;
}
bool KDirLister::matchesFilter( const QString& name ) const
{
return doNameFilter(name, d->settings.lstFilters);
}
bool KDirLister::matchesMimeFilter( const QString& mime ) const
{
return doMimeFilter(mime, d->settings.mimeFilter) &&
d->doMimeExcludeFilter(mime, d->settings.mimeExcludeFilter);
}
// ================ protected methods ================ //
bool KDirLister::matchesFilter( const KFileItem& item ) const
{
Q_ASSERT( !item.isNull() );
if ( item.text() == ".." )
return false;
if ( !d->settings.isShowingDotFiles && item.isHidden() )
return false;
if ( item.isDir() || d->settings.lstFilters.isEmpty() )
return true;
return matchesFilter( item.text() );
}
bool KDirLister::matchesMimeFilter( const KFileItem& item ) const
{
Q_ASSERT(!item.isNull());
// Don't lose time determining the mimetype if there is no filter
if (d->settings.mimeFilter.isEmpty() && d->settings.mimeExcludeFilter.isEmpty())
return true;
return matchesMimeFilter(item.mimetype());
}
bool KDirLister::doNameFilter( const QString& name, const QList<QRegExp>& filters ) const
{
for ( QList<QRegExp>::const_iterator it = filters.begin(); it != filters.end(); ++it )
if ( (*it).exactMatch( name ) )
return true;
return false;
}
bool KDirLister::doMimeFilter( const QString& mime, const QStringList& filters ) const
{
if ( filters.isEmpty() )
return true;
const KMimeType::Ptr mimeptr = KMimeType::mimeType(mime);
if ( !mimeptr )
return false;
//kDebug(7004) << "doMimeFilter: investigating: "<<mimeptr->name();
QStringList::const_iterator it = filters.begin();
for ( ; it != filters.end(); ++it )
if ( mimeptr->is(*it) )
return true;
//else kDebug(7004) << "doMimeFilter: compared without result to "<<*it;
return false;
}
bool KDirLister::Private::doMimeExcludeFilter( const QString& mime, const QStringList& filters ) const
{
if ( filters.isEmpty() )
return true;
QStringList::const_iterator it = filters.begin();
for ( ; it != filters.end(); ++it )
if ( (*it) == mime )
return false;
return true;
}
void KDirLister::handleError( KIO::Job *job )
{
if ( d->autoErrorHandling )
job->uiDelegate()->showErrorMessage();
}
// ================= private methods ================= //
void KDirLister::Private::addNewItem(const KUrl& directoryUrl, const KFileItem &item)
{
if (!isItemVisible(item))
return; // No reason to continue... bailing out here prevents a mimetype scan.
//kDebug(7004) << "in" << directoryUrl << "item:" << item.url();
if ( m_parent->matchesMimeFilter( item ) )
{
if ( !lstNewItems )
{
lstNewItems = new NewItemsHash;
}
Q_ASSERT( !item.isNull() );
(*lstNewItems)[directoryUrl].append( item ); // items not filtered
}
else
{
if ( !lstMimeFilteredItems ) {
lstMimeFilteredItems = new KFileItemList;
}
Q_ASSERT( !item.isNull() );
lstMimeFilteredItems->append( item ); // only filtered by mime
}
}
void KDirLister::Private::addNewItems(const KUrl& directoryUrl, const KFileItemList& items)
{
// TODO: make this faster - test if we have a filter at all first
// DF: was this profiled? The matchesFoo() functions should be fast, w/o filters...
// Of course if there is no filter and we can do a range-insertion instead of a loop, that might be good.
KFileItemList::const_iterator kit = items.begin();
const KFileItemList::const_iterator kend = items.end();
for ( ; kit != kend; ++kit )
addNewItem(directoryUrl, *kit);
}
void KDirLister::Private::addRefreshItem(const KUrl& directoryUrl, const KFileItem& oldItem, const KFileItem& item)
{
const bool refreshItemWasFiltered = !isItemVisible(oldItem) ||
!m_parent->matchesMimeFilter(oldItem);
if (isItemVisible(item) && m_parent->matchesMimeFilter(item)) {
if ( refreshItemWasFiltered )
{
if ( !lstNewItems ) {
lstNewItems = new NewItemsHash;
}
Q_ASSERT( !item.isNull() );
(*lstNewItems)[directoryUrl].append( item );
}
else
{
if ( !lstRefreshItems ) {
lstRefreshItems = new QList<QPair<KFileItem,KFileItem> >;
}
Q_ASSERT( !item.isNull() );
lstRefreshItems->append( qMakePair(oldItem, item) );
}
}
else if ( !refreshItemWasFiltered )
{
if ( !lstRemoveItems ) {
lstRemoveItems = new KFileItemList;
}
// notify the user that the mimetype of a file changed that doesn't match
// a filter or does match an exclude filter
// This also happens when renaming foo to .foo and dot files are hidden (#174721)
Q_ASSERT(!oldItem.isNull());
lstRemoveItems->append(oldItem);
}
}
void KDirLister::Private::emitItems()
{
NewItemsHash *tmpNew = lstNewItems;
lstNewItems = 0;
KFileItemList *tmpMime = lstMimeFilteredItems;
lstMimeFilteredItems = 0;
QList<QPair<KFileItem, KFileItem> > *tmpRefresh = lstRefreshItems;
lstRefreshItems = 0;
KFileItemList *tmpRemove = lstRemoveItems;
lstRemoveItems = 0;
if (tmpNew) {
QHashIterator<KUrl, KFileItemList> it(*tmpNew);
while (it.hasNext()) {
it.next();
emit m_parent->itemsAdded(it.key(), it.value());
emit m_parent->newItems(it.value()); // compat
}
delete tmpNew;
}
if ( tmpMime )
{
emit m_parent->itemsFilteredByMime( *tmpMime );
delete tmpMime;
}
if ( tmpRefresh )
{
emit m_parent->refreshItems( *tmpRefresh );
delete tmpRefresh;
}
if ( tmpRemove )
{
emit m_parent->itemsDeleted( *tmpRemove );
delete tmpRemove;
}
}
bool KDirLister::Private::isItemVisible(const KFileItem& item) const
{
// Note that this doesn't include mime filters, because
// of the itemsFilteredByMime signal. Filtered-by-mime items are
// considered "visible", they are just visible via a different signal...
return (!settings.dirOnlyMode || item.isDir())
&& m_parent->matchesFilter(item);
}
void KDirLister::Private::emitItemsDeleted(const KFileItemList &_items)
{
KFileItemList items = _items;
QMutableListIterator<KFileItem> it(items);
while (it.hasNext()) {
const KFileItem& item = it.next();
if (isItemVisible(item) && m_parent->matchesMimeFilter(item)) {
// for compat
emit m_parent->deleteItem(item);
} else {
it.remove();
}
}
if (!items.isEmpty())
emit m_parent->itemsDeleted(items);
}
// ================ private slots ================ //
void KDirLister::Private::_k_slotInfoMessage( KJob *, const QString& message )
{
emit m_parent->infoMessage( message );
}
void KDirLister::Private::_k_slotPercent( KJob *job, unsigned long pcnt )
{
jobData[static_cast<KIO::ListJob *>(job)].percent = pcnt;
int result = 0;
KIO::filesize_t size = 0;
QMap< KIO::ListJob *, Private::JobData >::Iterator dataIt = jobData.begin();
while ( dataIt != jobData.end() )
{
result += (*dataIt).percent * (*dataIt).totalSize;
size += (*dataIt).totalSize;
++dataIt;
}
if ( size != 0 )
result /= size;
else
result = 100;
emit m_parent->percent( result );
}
void KDirLister::Private::_k_slotTotalSize( KJob *job, qulonglong size )
{
jobData[static_cast<KIO::ListJob *>(job)].totalSize = size;
KIO::filesize_t result = 0;
QMap< KIO::ListJob *, Private::JobData >::Iterator dataIt = jobData.begin();
while ( dataIt != jobData.end() )
{
result += (*dataIt).totalSize;
++dataIt;
}
emit m_parent->totalSize( result );
}
void KDirLister::Private::_k_slotProcessedSize( KJob *job, qulonglong size )
{
jobData[static_cast<KIO::ListJob *>(job)].processedSize = size;
KIO::filesize_t result = 0;
QMap< KIO::ListJob *, Private::JobData >::Iterator dataIt = jobData.begin();
while ( dataIt != jobData.end() )
{
result += (*dataIt).processedSize;
++dataIt;
}
emit m_parent->processedSize( result );
}
void KDirLister::Private::_k_slotSpeed( KJob *job, unsigned long spd )
{
jobData[static_cast<KIO::ListJob *>(job)].speed = spd;
int result = 0;
QMap< KIO::ListJob *, Private::JobData >::Iterator dataIt = jobData.begin();
while ( dataIt != jobData.end() )
{
result += (*dataIt).speed;
++dataIt;
}
emit m_parent->speed( result );
}
uint KDirLister::Private::numJobs()
{
#ifdef DEBUG_CACHE
// This code helps detecting stale entries in the jobData map.
qDebug() << m_parent << "numJobs:" << jobData.count();
QMapIterator<KIO::ListJob *, JobData> it(jobData);
while (it.hasNext()) {
it.next();
qDebug() << (void*)it.key();
qDebug() << it.key();
}
#endif
return jobData.count();
}
void KDirLister::Private::jobDone( KIO::ListJob *job )
{
jobData.remove( job );
}
void KDirLister::Private::jobStarted( KIO::ListJob *job )
{
Private::JobData data;
data.speed = 0;
data.percent = 0;
data.processedSize = 0;
data.totalSize = 0;
jobData.insert( job, data );
complete = false;
}
void KDirLister::Private::connectJob( KIO::ListJob *job )
{
m_parent->connect( job, SIGNAL(infoMessage( KJob *, const QString&, const QString& )),
m_parent, SLOT(_k_slotInfoMessage( KJob *, const QString& )) );
m_parent->connect( job, SIGNAL(percent( KJob *, unsigned long )),
m_parent, SLOT(_k_slotPercent( KJob *, unsigned long )) );
m_parent->connect( job, SIGNAL(totalSize( KJob *, qulonglong )),
m_parent, SLOT(_k_slotTotalSize( KJob *, qulonglong )) );
m_parent->connect( job, SIGNAL(processedSize( KJob *, qulonglong )),
m_parent, SLOT(_k_slotProcessedSize( KJob *, qulonglong )) );
m_parent->connect( job, SIGNAL(speed( KJob *, unsigned long )),
m_parent, SLOT(_k_slotSpeed( KJob *, unsigned long )) );
}
void KDirLister::setMainWindow( QWidget *window )
{
d->window = window;
}
QWidget *KDirLister::mainWindow()
{
return d->window;
}
KFileItemList KDirLister::items( WhichItems which ) const
{
return itemsForDir( url(), which );
}
KFileItemList KDirLister::itemsForDir( const KUrl& dir, WhichItems which ) const
{
KFileItemList *allItems = kDirListerCache->itemsForDir( dir );
if ( !allItems )
return KFileItemList();
if ( which == AllItems )
return *allItems;
else // only items passing the filters
{
KFileItemList result;
KFileItemList::const_iterator kit = allItems->constBegin();
const KFileItemList::const_iterator kend = allItems->constEnd();
for ( ; kit != kend; ++kit )
{
const KFileItem& item = *kit;
if (d->isItemVisible(item) && matchesMimeFilter(item)) {
result.append(item);
}
}
return result;
}
}
bool KDirLister::delayedMimeTypes() const
{
return d->delayedMimeTypes;
}
void KDirLister::setDelayedMimeTypes( bool delayedMimeTypes )
{
d->delayedMimeTypes = delayedMimeTypes;
}
// called by KDirListerCache::slotRedirection
void KDirLister::Private::redirect(const KUrl& oldUrl, const KUrl& newUrl, bool keepItems)
{
if ( url.equals( oldUrl, KUrl::CompareWithoutTrailingSlash ) ) {
if (!keepItems)
rootFileItem = KFileItem();
url = newUrl;
}
const int idx = lstDirs.indexOf( oldUrl );
if (idx == -1) {
kWarning(7004) << "Unexpected redirection from" << oldUrl << "to" << newUrl
<< "but this dirlister is currently listing/holding" << lstDirs;
} else {
lstDirs[ idx ] = newUrl;
}
if ( lstDirs.count() == 1 ) {
if (!keepItems)
emit m_parent->clear();
emit m_parent->redirection( newUrl );
} else {
if (!keepItems)
emit m_parent->clear( oldUrl );
}
emit m_parent->redirection( oldUrl, newUrl );
}
void KDirListerCacheDirectoryData::moveListersWithoutCachedItemsJob(const KUrl& url)
{
// Move dirlisters from listersCurrentlyListing to listersCurrentlyHolding,
// but not those that are still waiting on a CachedItemsJob...
// Unit-testing note:
// Run kdirmodeltest in valgrind to hit the case where an update
// is triggered while a lister has a CachedItemsJob (different timing...)
QMutableListIterator<KDirLister *> lister_it(listersCurrentlyListing);
while (lister_it.hasNext()) {
KDirLister* kdl = lister_it.next();
if (!kdl->d->cachedItemsJobForUrl(url)) {
// OK, move this lister from "currently listing" to "currently holding".
// Huh? The KDirLister was present twice in listersCurrentlyListing, or was in both lists?
Q_ASSERT(!listersCurrentlyHolding.contains(kdl));
if (!listersCurrentlyHolding.contains(kdl)) {
listersCurrentlyHolding.append(kdl);
}
lister_it.remove();
} else {
//kDebug(7004) << "Not moving" << kdl << "to listersCurrentlyHolding because it still has job" << kdl->d->m_cachedItemsJobs;
}
}
}
KFileItem KDirLister::cachedItemForUrl(const KUrl& url)
{
return kDirListerCache->itemForUrl(url);
}
#include "moc_kdirlister.cpp"
#include "moc_kdirlister_p.cpp"
diff --git a/kio/kssl/kcm/kcm_ssl.desktop b/kio/kssl/kcm/kcm_ssl.desktop
index 67c4d18b3c..6ce68a96e5 100644
--- a/kio/kssl/kcm/kcm_ssl.desktop
+++ b/kio/kssl/kcm/kcm_ssl.desktop
@@ -1,165 +1,166 @@
[Desktop Entry]
Exec=kcmshell4 kcm_ssl
Icon=preferences-system-ssl
Type=Service
X-KDE-ServiceTypes=KCModule
X-KDE-Library=kcm_ssl
X-KDE-ParentApp=kcontrol
X-KDE-System-Settings-Parent-Category=network-and-connectivity
Name=SSL Preferences
Name[ar]=تفضيلات SSL
Name[bg]=Настройки на SSL
Name[bs]=SSL postavke
Name[ca]=Preferències SSL
Name[ca@valencia]=Preferències SSL
Name[cs]=Nastavení SSL
Name[da]=SSL-indstillinger
Name[de]=SSL-Einstellungen
Name[el]=Προτιμήσεις SSL
Name[en_GB]=SSL Preferences
Name[es]=Preferencias de SSL
Name[et]=SSL-i seadistused
Name[eu]=SSL hobespenak
Name[fi]=SSL:n asetukset
Name[fr]=Préférences SSL
Name[ga]=Sainroghanna SSL
Name[gl]=Preferencias de SSL
Name[he]=הגדרות SSL
Name[hr]=Postavke SSL-a
Name[hu]=SSL-beállítások
Name[ia]=Preferentias de SSL
Name[is]=Stillingar SSL
Name[it]=Preferenze SSL
Name[ja]=SSL 設定
Name[kk]=SSL параметрлері
Name[km]=ចំណូល​ចិត្ត SSL
Name[ko]=SSL 설정
Name[lt]=SSL nustatymai
Name[lv]=SSL iestatījumi
Name[nb]=SSL-innstillinger
Name[nds]=SSL-Instellen
Name[nl]=SSL-voorkeuren
Name[pa]=SSL ਪਸੰਦ
Name[pl]=Ustawienia SSL
Name[pt]=Preferências do SSL
Name[pt_BR]=Preferências do SSL
Name[ro]=Preferințe SSL
Name[ru]=Настройка SSL
Name[se]=SSL-oidimat
Name[sk]=Nastavenie SSL
Name[sl]=Možnosti SSL
Name[sr]=Поставке ССЛ‑а
Name[sr@ijekavian]=Поставке ССЛ‑а
Name[sr@ijekavianlatin]=Postavke SSL‑a
Name[sr@latin]=Postavke SSL‑a
Name[sv]=SSL-inställningar
Name[tg]=Хусусиятҳои SSL
Name[th]=ปรับแต่ง SSL
Name[tr]=SSL Tercihleri
Name[tt]=SSL көйләүләре
Name[ug]=SSL مايىللىق
Name[uk]=Параметри SSL
Name[vi]=Tùy thích SSL
Name[wa]=Preferinces SSL
Name[x-test]=xxSSL Preferencesxx
Name[zh_CN]=SSL 首选项
Name[zh_TW]=SSL 喜好設定
Comment=SSL Versions and Certificates
Comment[ar]=إصدارات وشهادات SSL
Comment[bg]=Версии и сертификати на SSL
Comment[bs]=SSL verzije i certifikati
Comment[ca]=Versions i certificats SSL
Comment[ca@valencia]=Versions i certificats SSL
Comment[cs]=SSL verze a certifikáty
Comment[da]=SSL-versioner og -certifikater
Comment[de]=SSL-Versionen und -Zertifikate
Comment[el]=Εκδόσεις και πιστοποιητικά SSL
Comment[en_GB]=SSL Versions and Certificates
Comment[es]=Versiones y certificados SSL
Comment[et]=SSL-i versioonid ja sertifikaadid
Comment[eu]=SSL bertsioak eta ziurtagiriak
Comment[fi]=SSL:n versiot ja varmenteet
Comment[fr]=Certificats et versions SSL
Comment[ga]=Leaganacha agus Teastais SSL
Comment[gl]=Versións e certificados de SSL
Comment[he]=גרסאות SSL ואישורים
Comment[hr]=Inačice i certifikati SSL-a
Comment[hu]=SSL-verziók és tanúsítványok
Comment[ia]=Versiones de SSL e Certificatos
Comment[is]=Útgáfur og skilríki SSL
Comment[it]=Versioni SSL e certificati
Comment[kk]=SSL нұсқалары мен куәліктері
Comment[km]=វិញ្ញាបនបត្រ និង​កំណែ​របស់ SSL
Comment[ko]=SSL 버전과 인증서
Comment[lt]=SSL versijos ir liudijimai
Comment[lv]=SSL versijas un sertifikāti
Comment[nb]=SSL-versjoner og sertifikater
Comment[nds]=SSL-Verschonen un Zertifikaten
Comment[nl]=SSL-versies en certificaten
Comment[pa]=SSL ਵਰਜਨ ਤੇ ਸਰਟੀਫਿਕੇਟ
Comment[pl]=Wersje SSL i certyfikaty
Comment[pt]=Versões e Certificados de SSL
Comment[pt_BR]=Versões e certificados de SSL
Comment[ro]=Certificate și versiuni SSL partener
Comment[ru]=Версии и сертификаты SSL
Comment[se]=SSL-veršuvnnat ja -duođaštusat
Comment[sk]=SSL verzie a certifikáty
Comment[sl]=Različice SSL in certifikati
Comment[sr]=Сертификати и верзије ССЛ‑а
Comment[sr@ijekavian]=Сертификати и верзије ССЛ‑а
Comment[sr@ijekavianlatin]=Sertifikati i verzije SSL‑a
Comment[sr@latin]=Sertifikati i verzije SSL‑a
Comment[sv]=SSL-versioner och -certifikat
Comment[tg]=Версияҳо ва иҷозатномаҳои SSL
Comment[th]=รุ่นของ SSL และใบรับรอง
Comment[tr]=SSL Sürümleri ve Sertifikaları
Comment[tt]=SSL версияләре һәм Таныклыклары
Comment[ug]=SSL نەشر ۋە گۇۋاھنامە
Comment[uk]=Версія і сертифікати SSL
Comment[vi]=Các phiên bản và chứng chỉ SSL
Comment[wa]=Modêyes eyet acertineures SSL
Comment[x-test]=xxSSL Versions and Certificatesxx
Comment[zh_CN]=SSL 版本和证书
Comment[zh_TW]=SSL 版本與憑證
X-KDE-Keywords=SSL,Security,Network,Protocol,Certificates,Encryption,HTTPS
X-KDE-Keywords[ar]=SSL,الأمن,الشبكة,البرتوكول,الشهادات,التشفير,HTTPS
X-KDE-Keywords[bg]=SSL,Security,Network,Protocol,Certificates,Encryption,HTTPS,Сигурност,Мрежа,Протокол,Удостоверение,Сертификат,Шифриране
X-KDE-Keywords[ca]=SSL,Seguretat,Xarxa,Protocol,Certificats,Encriptatge,HTTPS
X-KDE-Keywords[ca@valencia]=SSL,Seguretat,Xarxa,Protocol,Certificats,Encriptatge,HTTPS
X-KDE-Keywords[da]=SSL,Sikkerhed,Netværk,Protokol,Certifikater,Kryptering,HTTPS
X-KDE-Keywords[de]=ssl,sicherheit,netzwerk,netz,protokoll,zertifikat,verschlüsselung,https
X-KDE-Keywords[es]=SSL,Seguridad,Red,Protocolo,Certificados,Cifrado,HTTPS
X-KDE-Keywords[et]=SSL,turvalisus,võrk,protokoll,sertifikaadid,serdid,krüptimine,krüpto,HTTPS
X-KDE-Keywords[fi]=SSL,Turvallisuus,Verkko,Yhteyskäytäntö,Protokolla,Varmenteet,Sertifikaatit,Salaus,HTTPS
X-KDE-Keywords[fr]=SSL,Sécurité,Réseau,Protocole,Certificats,Chiffrement,HTTPS
X-KDE-Keywords[ga]=SSL,Slándáil,Líonra,Prótacal,Teastais,Criptiú,HTTPS
X-KDE-Keywords[gl]=SSL,Seguridade,Rede,Protocolo,Certificados,Cifrado,HTTPS
X-KDE-Keywords[he]=אבטחה,רשת,פרוטוקול,אישורים,הצפנהSSL,Security,Network,Protocol,Certificates,Encryption,HTTPS
X-KDE-Keywords[hu]=SSL,Biztonság,Hálózat,Protokoll,Tanúsítványok,Titkosítás,HTTPS
X-KDE-Keywords[ia]=SSL,Securitate,Rete,Protocollo,Certificatos,Cryptation,HTTPS
X-KDE-Keywords[it]=SSL,sicurezza,rete,protocollo,certificati,cifratura,HTTPS
X-KDE-Keywords[kk]=SSL,Security,Network,Protocol,Certificates,Encryption,HTTPS
X-KDE-Keywords[km]=SSL,មូលប័ត្រ​,​បណ្ដាញ​,​ពិធីការ​,វិញ្ញាបនបត្រ,​ការ​អ៊ិនគ្រីប​​,HTTPS
X-KDE-Keywords[ko]=SSL,Security,Network,Protocol,Certificates,Encryption,HTTPS,암호,네트워크,프로토콜,인증서,암호화
X-KDE-Keywords[nb]=SSL,Sikkerhet,Nettverk,Protokoll,Sertifikater,Kryptering,HTTPS
X-KDE-Keywords[nds]=SSL,Sekerheit,Nettwark,Protokoll,Zertifikaten,Verslöteln,HTTPS
X-KDE-Keywords[nl]=SSL,beveiliging,netwerk,protocol,certificaten,versleuteling,HTTPS
X-KDE-Keywords[pa]=SSL,ਸੁਰੱਖਿਆ,ਨੈੱਟਵਰਕ,ਪ੍ਰੋਟੋਕਾਲ,ਸਰਟੀਫਿਕੇਟ,ਇੰਕ੍ਰਿਪਸ਼ਨ,HTTPS
X-KDE-Keywords[pl]=SSL,Bezpieczeństwo,Sieć,Protokół,Certyfikaty,Szyfrowanie,HTTPS
X-KDE-Keywords[pt]=SSL,Segurança,Rede,Protocolo,Certificados,Encriptação,HTTPS
X-KDE-Keywords[pt_BR]=SSL,segurança,rede,protocolo,certificados,criptografia,HTTPS
X-KDE-Keywords[ro]=SSL,Securitate,Rețea,Protocol,Certificate,Criptare,HTTPS
+X-KDE-Keywords[sk]=SSL,Security,Network,Protocol,Certificates,Encryption,HTTPS
X-KDE-Keywords[sr]=SSL,Security,Network,Protocol,Certificates,Encryption,HTTPS,ССЛ,безбедност,мрежа,протокол,сертификат,шифровање,ХТТПС
X-KDE-Keywords[sr@ijekavian]=SSL,Security,Network,Protocol,Certificates,Encryption,HTTPS,ССЛ,безбедност,мрежа,протокол,сертификат,шифровање,ХТТПС
X-KDE-Keywords[sr@ijekavianlatin]=SSL,Security,Network,Protocol,Certificates,Encryption,HTTPS,SSL,bezbednost,mreža,protokol,sertifikat,šifrovanje,HTTPS
X-KDE-Keywords[sr@latin]=SSL,Security,Network,Protocol,Certificates,Encryption,HTTPS,SSL,bezbednost,mreža,protokol,sertifikat,šifrovanje,HTTPS
X-KDE-Keywords[sv]=SSL,Säkerhet,Nätverk,Protokoll,Certifikat,Kryptering,HTTPS
X-KDE-Keywords[tg]=SSL,Амният,Шабака,Протокол,иҷозатномаҳо,Рамзгузорӣ,HTTPS
X-KDE-Keywords[uk]=SSL,Security,Network,Protocol,Certificates,Encryption,HTTPS,захист,мережа,протокол,сертифікат,сертифікати,шифрування,криптографія
X-KDE-Keywords[vi]=SSL,Bảo mật,Mạng,Giao thức,Chứng thực,Mã hóa,HTTPS,Security,Network,Protocol,Certificates,Encryption
X-KDE-Keywords[x-test]=xxSSL,Security,Network,Protocol,Certificates,Encryption,HTTPSxx
X-KDE-Keywords[zh_CN]=SSL,Security,Network,Protocol,Certificates,Encryption,HTTPS,加密,安全,网络,协议,证书
X-KDE-Keywords[zh_TW]=SSL,Security,Network,Protocol,Certificates,Encryption,HTTPS
diff --git a/plasma/private/applethandle.cpp b/plasma/private/applethandle.cpp
index 1edf98a3df..9232e6c18c 100644
--- a/plasma/private/applethandle.cpp
+++ b/plasma/private/applethandle.cpp
@@ -1,1068 +1,1072 @@
/*
* Copyright 2007 by Kevin Ottens <ervin@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2, or
* (at your option) any later version.
*
* This program 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 General Public License for more details
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "private/applethandle_p.h"
#include <QApplication>
#include <QBitmap>
#include <QGraphicsSceneMouseEvent>
#include <QLinearGradient>
#include <QPainter>
#include <QApplication>
#include <QMenu>
#include <QTouchEvent>
#include <QMatrix>
#include <QTransform>
#include <QWeakPointer>
#include <QPropertyAnimation>
#include <kcolorscheme.h>
#include <kglobalsettings.h>
#include <kicon.h>
#include <kiconloader.h>
#include <kwindowsystem.h>
#include <cmath>
#include <math.h>
#include "applet.h"
#include "applet_p.h"
#include "containment.h"
#include "corona.h"
#include "paintutils.h"
#include "theme.h"
+#include "tooltipmanager.h"
#include "view.h"
#include "framesvg.h"
namespace Plasma
{
qreal _k_distanceForPoint(QPointF point);
qreal _k_pointAngle(QPointF point);
QPointF _k_rotatePoint(QPointF point, qreal angle);
QPointF _k_projectPoint(QPointF point, QPointF v);
AppletHandle::AppletHandle(Containment *containment, Applet *applet, const QPointF &hoverPos)
: QGraphicsObject(applet),
m_pressedButton(NoButton),
m_containment(containment),
m_applet(applet),
m_iconSize(KIconLoader::SizeSmall),
m_opacity(0.0),
m_animType(FadeIn),
m_backgroundBuffer(0),
m_currentView(applet->view()),
m_entryPos(hoverPos),
m_buttonsOnRight(false),
m_pendingFade(false)
{
setFlags(flags() | QGraphicsItem::ItemStacksBehindParent);
KColorScheme colorScheme(QPalette::Active, KColorScheme::View,
Theme::defaultTheme()->colorScheme());
setAcceptTouchEvents(true);
m_gradientColor = colorScheme.background(KColorScheme::NormalBackground).color();
m_originalGeom = mapToScene(QRectF(QPoint(0,0), m_applet->size())).boundingRect();
m_originalTransform = m_applet->transform();
QPointF center = QRectF(QPointF(), m_applet->size()).center();
m_angle = _k_pointAngle(m_originalTransform.map(center + QPointF(1.0, 0.0)) - center);
m_hoverTimer = new QTimer(this);
m_hoverTimer->setSingleShot(true);
m_hoverTimer->setInterval(333);
m_leaveTimer = new QTimer(this);
m_leaveTimer->setSingleShot(true);
m_leaveTimer->setInterval(500);
connect(m_hoverTimer, SIGNAL(timeout()), this, SLOT(hoverTimeout()));
connect(m_leaveTimer, SIGNAL(timeout()), this, SLOT(leaveTimeout()));
connect(m_applet, SIGNAL(appletDestroyed(Plasma::Applet*)), this, SLOT(appletDestroyed()));
setAcceptsHoverEvents(true);
m_hoverTimer->start();
//icons
m_configureIcons = new Svg(this);
m_configureIcons->setImagePath("widgets/configuration-icons");
m_configureIcons->setContainsMultipleImages(true);
connect(m_configureIcons, SIGNAL(repaintNeeded()), this, SLOT(scheduleUpdate()));
m_background = new FrameSvg(this);
m_background->setImagePath("widgets/background");
connect(m_background, SIGNAL(repaintNeeded()), this, SLOT(scheduleUpdate()));
m_applet->installSceneEventFilter(this);
}
AppletHandle::~AppletHandle()
{
detachApplet();
delete m_backgroundBuffer;
}
bool AppletHandle::shown() const
{
return !m_hoverTimer->isActive();
}
Applet *AppletHandle::applet() const
{
return m_applet;
}
void AppletHandle::detachApplet()
{
if (!m_applet) {
return;
}
disconnect(m_hoverTimer, SIGNAL(timeout()), this, SLOT(hoverTimeout()));
disconnect(m_leaveTimer, SIGNAL(timeout()), this, SLOT(leaveTimeout()));
m_applet->disconnect(this);
if (m_applet->geometry() != m_originalGeom || m_applet->transform() != m_originalTransform) {
emit m_applet->appletTransformedByUser();
}
m_applet = 0;
}
QRectF Plasma::AppletHandle::boundingRect() const
{
return m_totalRect;
}
QPainterPath AppletHandle::shape() const
{
//when the containment changes the applet is reset to 0
if (m_applet) {
QPainterPath path = PaintUtils::roundedRectangle(m_decorationRect, 10);
return path.united(m_applet->shape());
} else {
return QGraphicsItem::shape();
}
}
QPainterPath handleRect(const QRectF &rect, int radius, bool onRight)
{
QPainterPath path;
if (onRight) {
// make the left side straight
path.moveTo(rect.left(), rect.top()); // Top left
path.lineTo(rect.right() - radius, rect.top()); // Top side
path.quadTo(rect.right(), rect.top(),
rect.right(), rect.top() + radius); // Top right corner
path.lineTo(rect.right(), rect.bottom() - radius); // Right side
path.quadTo(rect.right(), rect.bottom(),
rect.right() - radius, rect.bottom()); // Bottom right corner
path.lineTo(rect.left(), rect.bottom()); // Bottom side
} else {
// make the right side straight
path.moveTo(QPointF(rect.left(), rect.top() + radius));
path.quadTo(rect.left(), rect.top(),
rect.left() + radius, rect.top()); // Top left corner
path.lineTo(rect.right(), rect.top()); // Top side
path.lineTo(rect.right(), rect.bottom()); // Right side
path.lineTo(rect.left() + radius, rect.bottom()); // Bottom side
path.quadTo(rect.left(), rect.bottom(),
rect.left(), rect.bottom() - radius); // Bottom left corner
}
path.closeSubpath();
return path;
}
void AppletHandle::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
Q_UNUSED(option);
Q_UNUSED(widget);
//kDebug() << m_opacity << m_anim << FadeOut;
if (qFuzzyCompare(m_opacity + 1.0, 1.0)) {
if (m_animType == FadeOut) {
//kDebug() << "WOOOOOOOOO";
QTimer::singleShot(0, this, SLOT(emitDisappear()));
}
return;
}
qreal translation;
if (m_buttonsOnRight) {
//kDebug() << "translating by" << m_opacity
// << (-(1 - m_opacity) * m_rect.width()) << m_rect.width();
translation = -(1 - m_opacity) * m_rect.width();
} else {
translation = (1 - m_opacity) * m_rect.width();
}
painter->translate(translation, 0);
painter->setPen(Qt::NoPen);
painter->setRenderHints(QPainter::Antialiasing|QPainter::SmoothPixmapTransform);
int iconMargin = m_iconSize / 2;
const QSize pixmapSize(int(m_decorationRect.width()),
int(m_decorationRect.height()) + m_iconSize * 5 + 1);
const QSize iconSize(KIconLoader::SizeSmall, KIconLoader::SizeSmall);
bool isRunning = false;
if (m_anim.data()) {
isRunning = m_anim.data()->state() == QAbstractAnimation::Running ? \
true : false;
}
//regenerate our buffer?
if (isRunning || !m_backgroundBuffer || m_backgroundBuffer->size() != pixmapSize) {
QColor transparencyColor = Qt::black;
transparencyColor.setAlphaF(qMin(m_opacity, qreal(0.99)));
QLinearGradient g(QPoint(0, 0), QPoint(m_decorationRect.width(), 0));
//fading out panel
if (m_rect.height() > qreal(minimumHeight()) * 1.25) {
if (m_buttonsOnRight) {
qreal opaquePoint =
(m_background->marginSize(LeftMargin) - translation) / m_decorationRect.width();
//kDebug() << "opaquePoint" << opaquePoint
// << m_background->marginSize(LeftMargin) << m_decorationRect.width();
g.setColorAt(0.0, Qt::transparent);
g.setColorAt(qMax(0.0, opaquePoint - 0.05), Qt::transparent); //krazy:exclude=qminmax
g.setColorAt(opaquePoint, transparencyColor);
g.setColorAt(1.0, transparencyColor);
} else {
qreal opaquePoint =
1 - ((m_background->marginSize(RightMargin) + translation) / m_decorationRect.width());
g.setColorAt(1.0, Qt::transparent);
g.setColorAt(opaquePoint + 0.05, Qt::transparent);
g.setColorAt(qMax(qreal(0), opaquePoint), transparencyColor);
g.setColorAt(0.0, transparencyColor);
}
//complete panel
} else {
g.setColorAt(0.0, transparencyColor);
}
m_background->resizeFrame(m_decorationRect.size());
if (!m_backgroundBuffer || m_backgroundBuffer->size() != pixmapSize) {
delete m_backgroundBuffer;
m_backgroundBuffer = new QPixmap(pixmapSize);
}
m_backgroundBuffer->fill(Qt::transparent);
QPainter buffPainter(m_backgroundBuffer);
m_background->paintFrame(&buffPainter);
//+1 because otherwise due to rounding errors when rotated could appear one pixel
//of the icon at the border of the applet
//QRectF iconRect(QPointF(pixmapSize.width() - m_iconSize + 1, m_iconSize), iconSize);
QRectF iconRect(QPointF(0, m_decorationRect.height() + 1), iconSize);
if (m_buttonsOnRight) {
iconRect.moveLeft(
pixmapSize.width() - m_iconSize - m_background->marginSize(LeftMargin));
m_configureIcons->paint(&buffPainter, iconRect, "size-diagonal-tr2bl");
} else {
iconRect.moveLeft(m_background->marginSize(RightMargin));
m_configureIcons->paint(&buffPainter, iconRect, "size-diagonal-tl2br");
}
iconRect.translate(0, m_iconSize);
m_configureIcons->paint(&buffPainter, iconRect, "rotate");
if (m_applet && m_applet->hasConfigurationInterface()) {
iconRect.translate(0, m_iconSize);
m_configureIcons->paint(&buffPainter, iconRect, "configure");
}
if (m_applet && m_applet->hasValidAssociatedApplication()) {
iconRect.translate(0, m_iconSize);
m_configureIcons->paint(&buffPainter, iconRect, "maximize");
}
iconRect.translate(0, m_iconSize);
m_configureIcons->paint(&buffPainter, iconRect, "close");
buffPainter.setCompositionMode(QPainter::CompositionMode_DestinationIn);
//blend the background
buffPainter.fillRect(m_backgroundBuffer->rect(), g);
//blend the icons
//buffPainter.fillRect(QRect(QPoint((int)m_decorationRect.width(), 0), QSize(m_iconSize + 1,
// (int)m_decorationRect.height())), transparencyColor);
}
painter->drawPixmap(m_decorationRect.toRect(), *m_backgroundBuffer,
QRect(QPoint(0, 0), m_decorationRect.size().toSize()));
//XXX this code is duplicated in the next function
QPointF basePoint = m_rect.topLeft() + QPointF(HANDLE_MARGIN, iconMargin);
QPointF step = QPointF(0, m_iconSize + iconMargin);
QPointF separator = step + QPointF(0, iconMargin);
//end duplicate code
QPointF shiftC;
QPointF shiftD;
QPointF shiftR;
QPointF shiftM;
QPointF shiftMx;
switch(m_pressedButton)
{
case ConfigureButton:
shiftC = QPointF(2, 2);
break;
case RemoveButton:
shiftD = QPointF(2, 2);
break;
case RotateButton:
shiftR = QPointF(2, 2);
break;
case ResizeButton:
shiftM = QPointF(2, 2);
break;
case MaximizeButton:
shiftMx = QPointF(2, 2);
break;
default:
break;
}
QRectF sourceIconRect(QPointF(0, m_decorationRect.height() + 1), iconSize);
if (m_buttonsOnRight) {
sourceIconRect.moveLeft(
pixmapSize.width() - m_iconSize - m_background->marginSize(LeftMargin));
} else {
sourceIconRect.moveLeft(m_background->marginSize(RightMargin));
}
if (m_applet && m_applet->aspectRatioMode() != FixedSize) {
//resize
painter->drawPixmap(
QRectF(basePoint + shiftM, iconSize), *m_backgroundBuffer, sourceIconRect);
basePoint += step;
}
//rotate
sourceIconRect.translate(0, m_iconSize);
painter->drawPixmap(QRectF(basePoint + shiftR, iconSize), *m_backgroundBuffer, sourceIconRect);
//configure
if (m_applet && m_applet->hasConfigurationInterface()) {
basePoint += step;
sourceIconRect.translate(0, m_iconSize);
painter->drawPixmap(
QRectF(basePoint + shiftC, iconSize), *m_backgroundBuffer, sourceIconRect);
}
//maximize
if (m_applet && m_applet->hasValidAssociatedApplication()) {
basePoint += step;
sourceIconRect.translate(0, m_iconSize);
painter->drawPixmap(
QRectF(basePoint + shiftMx, iconSize), *m_backgroundBuffer, sourceIconRect);
}
//close
basePoint = m_rect.bottomLeft() + QPointF(HANDLE_MARGIN, 0) - step;
sourceIconRect.translate(0, m_iconSize);
painter->drawPixmap(QRectF(basePoint + shiftD, iconSize), *m_backgroundBuffer, sourceIconRect);
}
void AppletHandle::emitDisappear()
{
emit disappearDone(this);
}
void AppletHandle::scheduleUpdate()
{
update();
}
AppletHandle::ButtonType AppletHandle::mapToButton(const QPointF &point) const
{
int iconMargin = m_iconSize / 2;
//XXX this code is duplicated in the prev. function
QPointF basePoint = m_rect.topLeft() + QPointF(HANDLE_MARGIN, iconMargin);
QPointF step = QPointF(0, m_iconSize + iconMargin);
QPointF separator = step + QPointF(0, iconMargin);
//end duplicate code
QRectF activeArea = QRectF(basePoint, QSizeF(m_iconSize, m_iconSize));
if (m_applet && m_applet->aspectRatioMode() != FixedSize) {
if (activeArea.contains(point)) {
return ResizeButton;
}
activeArea.translate(step);
}
if (activeArea.contains(point)) {
return RotateButton;
}
if (m_applet && m_applet->hasConfigurationInterface()) {
activeArea.translate(step);
if (activeArea.contains(point)) {
return ConfigureButton;
}
}
if (m_applet && m_applet->hasValidAssociatedApplication()) {
activeArea.translate(step);
if (activeArea.contains(point)) {
return MaximizeButton;
}
}
activeArea.moveTop(m_rect.bottom() - activeArea.height() - iconMargin);
if (activeArea.contains(point)) {
return RemoveButton;
}
return MoveButton;
//return m_applet->mapToParent(m_applet->shape()).contains(point) ? NoButton : MoveButton;
}
void AppletHandle::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
//containment recently switched?
if (!m_applet) {
QGraphicsItem::mousePressEvent(event);
return;
}
if (m_pendingFade) {
//m_pendingFade = false;
return;
}
if (event->button() == Qt::LeftButton) {
m_pressedButton = mapToButton(event->pos());
//kDebug() << "button pressed:" << m_pressedButton;
if (m_pressedButton != NoButton) {
m_applet->raise();
m_zValue = m_applet->zValue();
setZValue(m_zValue);
}
if (m_pressedButton == ResizeButton || m_pressedButton == RotateButton) {
m_originalGeom = mapToScene(QRectF(QPoint(0,0), m_applet->size())).boundingRect();
m_origAppletCenter = m_originalGeom.center();
m_origAppletSize = QPointF(m_applet->size().width(), m_applet->size().height());
// resize
if (m_buttonsOnRight) {
m_resizeStaticPoint = m_applet->mapToScene(QPointF(0, m_applet->size().height()));
} else {
m_resizeStaticPoint = m_applet->mapToScene(m_origAppletSize);
}
m_resizeGrabPoint = event->scenePos();
// rotate
m_rotateAngleOffset = m_angle - _k_pointAngle(event->scenePos() - m_origAppletCenter);
}
event->accept();
//set mousePos to the position in the applet, in screencoords, so it becomes easy
//to reposition the toplevel view to the correct position.
if (m_currentView && m_applet) {
QPoint localpos = m_currentView.data()->mapFromScene(m_applet->scenePos());
m_mousePos = event->screenPos() - m_currentView.data()->mapToGlobal(localpos);
}
return;
}
QGraphicsItem::mousePressEvent(event);
}
bool AppletHandle::leaveCurrentView(const QPoint &pos) const
{
foreach (QWidget *widget, QApplication::topLevelWidgets()) {
if (widget->geometry().contains(pos)) {
//is this widget a plasma view, a different view then our current one,
//AND not a dashboardview?
Plasma::View *v = qobject_cast<Plasma::View *>(widget);
if (v && v != m_currentView.data() && v->containment() != m_containment) {
return true;
}
}
}
return false;
}
void AppletHandle::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
//kDebug() << "button pressed:" << m_pressedButton << ", fade pending?" << m_pendingFade;
if (m_pendingFade) {
startFading(FadeOut, m_entryPos);
m_pendingFade = false;
}
ButtonType releasedAtButton = mapToButton(event->pos());
if (m_applet && event->button() == Qt::LeftButton) {
switch (m_pressedButton) {
case ConfigureButton:
//FIXME: Remove this call once the configuration management change was done
if (m_pressedButton == releasedAtButton) {
m_applet->showConfigurationInterface();
}
break;
case RemoveButton:
if (m_pressedButton == releasedAtButton) {
forceDisappear();
m_applet->destroy();
}
break;
case MoveButton:
{
// test for containment change
//kDebug() << "testing for containment change, sceneBoundingRect = "
// << m_containment->sceneBoundingRect();
if (!m_containment->sceneBoundingRect().contains(m_applet->scenePos())) {
// see which containment it belongs to
Corona * corona = qobject_cast<Corona*>(scene());
if (corona) {
foreach (Containment *containment, corona->containments()) {
QPointF pos;
QGraphicsView *v = containment->view();
if (v) {
pos = v->mapToScene(v->mapFromGlobal(event->screenPos() - m_mousePos));
if (containment->sceneBoundingRect().contains(pos)) {
//kDebug() << "new containment = " << containments[i];
//kDebug() << "rect = " << containments[i]->sceneBoundingRect();
// add the applet to the new containment and take it from the old one
//kDebug() << "moving to other containment with position" << pos;;
switchContainment(containment, pos);
break;
}
}
}
}
}
break;
}
case MaximizeButton:
if (m_applet) {
m_applet->runAssociatedApplication();
}
break;
default:
break;
}
}
m_pressedButton = NoButton;
update();
}
qreal _k_distanceForPoint(QPointF point)
{
return std::sqrt(point.x() * point.x() + point.y() * point.y());
}
qreal _k_pointAngle(QPointF point)
{
qreal r = sqrt(point.x() * point.x() + point.y() * point.y());
qreal cosine = point.x() / r;
if (point.y() >= 0) {
return acos(cosine);
} else {
return -acos(cosine);
}
}
QPointF _k_rotatePoint(QPointF point, qreal angle)
{
return QTransform().rotateRadians(angle).map(point);
}
QPointF _k_projectPoint(QPointF point, QPointF v)
{
v /= sqrt(v.x() * v.x() + v.y() * v.y());
qreal a = v.x() * v.x();
qreal b = v.x() * v.y();
qreal d = v.y() * v.y();
return QMatrix(a, b, b, d, 0., 0.).map(point);
}
void AppletHandle::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
static const qreal snapAngle = M_PI_2 /* $i 3.14159 / 2.0 */;
if (!m_applet) {
QGraphicsItem::mouseMoveEvent(event);
return;
}
//Track how much the mouse has moved.
QPointF deltaScene = event->scenePos() - event->lastScenePos();
if (m_pressedButton == MoveButton) {
if (leaveCurrentView(event->screenPos())) {
Plasma::View *v = Plasma::View::topLevelViewAt(event->screenPos());
if (v && v != m_currentView.data()) {
Containment *c = v->containment();
if (c) {
QPoint pos = v->mapFromGlobal(event->screenPos());
//we actually have been dropped on another containment, so
//move there: we have a screenpos, we need a scenepos
//FIXME how reliable is this transform?
switchContainment(c, v->mapToScene(pos));
}
}
}
if (m_applet) {
QPointF mappedPoint = transform().map(QPointF(deltaScene.x(), deltaScene.y()));
m_applet->moveBy(mappedPoint.x(), mappedPoint.y());
}
} else if (m_pressedButton == ResizeButton || m_pressedButton == RotateButton) {
QPointF cursorPoint = event->scenePos();
// the code below will adjust these based on the type of operation
QPointF newSize;
QPointF newCenter;
qreal newAngle;
// get size limits
QSizeF min = m_applet->minimumSize();
QSizeF max = m_applet->maximumSize();
if (min.width() < KIconLoader::SizeSmall || min.height() < KIconLoader::SizeSmall) {
min = m_applet->effectiveSizeHint(Qt::MinimumSize);
}
if (max.isEmpty()) {
max = m_applet->effectiveSizeHint(Qt::MaximumSize);
}
// If the applet doesn't have a minimum size, calculate based on a
// minimum content area size of 16x16 (KIconLoader::SizeSmall)
if (min.width() < KIconLoader::SizeSmall || min.height() < KIconLoader::SizeSmall) {
min = m_applet->boundingRect().size() - m_applet->contentsRect().size();
min = QSizeF(KIconLoader::SizeSmall, KIconLoader::SizeSmall);
}
if (m_pressedButton == RotateButton) {
newSize = m_origAppletSize;
newCenter = m_origAppletCenter;
QPointF centerRelativePoint = cursorPoint - m_origAppletCenter;
if (_k_distanceForPoint(centerRelativePoint) < 10) {
newAngle = m_angle;
} else {
qreal cursorAngle = _k_pointAngle(centerRelativePoint);
newAngle = m_rotateAngleOffset + cursorAngle;
if (fabs(remainder(newAngle, snapAngle)) < 0.15) {
newAngle = newAngle - remainder(newAngle, snapAngle);
}
}
} else {
// un-rotate screen points so we can read differences of coordinates
QPointF rStaticPoint = _k_rotatePoint(m_resizeStaticPoint, -m_angle);
QPointF rCursorPoint = _k_rotatePoint(cursorPoint, -m_angle);
QPointF rGrabPoint = _k_rotatePoint(m_resizeGrabPoint, -m_angle);
if (m_buttonsOnRight) {
newSize = m_origAppletSize + QPointF(rCursorPoint.x() - rGrabPoint.x(), rGrabPoint.y() - rCursorPoint.y());
} else {
newSize = m_origAppletSize + QPointF(rGrabPoint.x() - rCursorPoint.x(), rGrabPoint.y() - rCursorPoint.y());
}
// preserving aspect ratio?
if ((m_applet->aspectRatioMode() != Plasma::IgnoreAspectRatio &&
!(event->modifiers() & Qt::ControlModifier)) ||
(m_applet->aspectRatioMode() == Plasma::IgnoreAspectRatio &&
(event->modifiers() & Qt::ControlModifier))) {
// project size to keep ratio
newSize = _k_projectPoint(newSize, m_origAppletSize);
// limit size, presering ratio
qreal ratio = m_origAppletSize.y() / m_origAppletSize.x();
newSize.rx() = qMin(max.width(), qMax(min.width(), newSize.x()));
newSize.ry() = newSize.x() * ratio;
newSize.ry() = qMin(max.height(), qMax(min.height(), newSize.y()));
newSize.rx() = newSize.y() / ratio;
} else {
// limit size
newSize.rx() = qMin(max.width(), qMax(min.width(), newSize.x()));
newSize.ry() = qMin(max.height(), qMax(min.height(), newSize.y()));
}
// move center such that the static corner remains in the same place
if (m_buttonsOnRight) {
newCenter = _k_rotatePoint(QPointF(rStaticPoint.x() + newSize.x()/2,
rStaticPoint.y() - newSize.y()/2), m_angle);
} else {
newCenter = _k_rotatePoint(QPointF(rStaticPoint.x() - newSize.x()/2,
rStaticPoint.y() - newSize.y()/2), m_angle);
}
newAngle = m_angle;
}
// apply size
m_applet->resize(newSize.x(), newSize.y());
// apply position, no need if we're rotating
if (m_pressedButton != RotateButton) {
m_applet->setPos(m_containment->mapFromScene(newCenter - newSize/2));
}
// apply angle
QTransform at;
at.translate(newSize.x()/2, newSize.y()/2);
at.rotateRadians(newAngle);
at.translate(-newSize.x()/2, -newSize.y()/2);
m_applet->setTransform(at);
m_angle = newAngle;
} else {
QGraphicsItem::mouseMoveEvent(event);
}
}
bool AppletHandle::sceneEvent(QEvent *event)
{
switch (event->type()) {
case QEvent::TouchEnd: {
QTransform t = m_applet->transform();
QRectF geom = m_applet->geometry();
QPointF translation(t.m31(), t.m32());
QPointF center = geom.center();
geom.setWidth(geom.width()*qAbs(t.m11()));
geom.setHeight(geom.height()*qAbs(t.m22()));
geom.moveCenter(center);
m_applet->setGeometry(geom);
t.reset();
t.translate(m_applet->size().width()/2, m_applet->size().height()/2);
t.rotateRadians(m_angle);
t.translate(-m_applet->size().width()/2, -m_applet->size().height()/2);
m_applet->setTransform(t);
return true;
}
case QEvent::TouchBegin:
case QEvent::TouchUpdate:
{
QList<QTouchEvent::TouchPoint> touchPoints = static_cast<QTouchEvent *>(event)->touchPoints();
if (touchPoints.count() == 2) {
const QTouchEvent::TouchPoint &touchPoint0 = touchPoints.first();
const QTouchEvent::TouchPoint &touchPoint1 = touchPoints.last();
//rotation
QLineF line0(touchPoint0.lastScenePos(), touchPoint1.lastScenePos());
QLineF line1(touchPoint0.scenePos(), touchPoint1.scenePos());
m_angle = m_angle+(line1.angleTo(line0)*M_PI_2/90);
QTransform t = m_applet->transform();
t.translate(m_applet->size().width()/2, m_applet->size().height()/2);
t.rotate(line1.angleTo(line0));
//scaling
qreal scaleFactor = 1;
if (line0.length() > 0) {
scaleFactor = line1.length() / line0.length();
}
t.scale(scaleFactor, scaleFactor);
t.translate(-m_applet->size().width()/2, -m_applet->size().height()/2);
m_applet->setTransform(t);
}
return true;
}
default:
break;
}
return QGraphicsItem::sceneEvent(event);
}
//pos relative to scene
void AppletHandle::switchContainment(Containment *containment, const QPointF &pos)
{
m_containment = containment;
Applet *applet = m_applet;
m_applet = 0; //make sure we don't try to act on the applet again
applet->removeSceneEventFilter(this);
forceDisappear(); //takes care of event filter and killing handle
applet->disconnect(this); //make sure the applet doesn't tell us to do anything
//applet->setZValue(m_zValue);
containment->addApplet(applet, containment->mapFromScene(pos), false);
deleteLater();
}
void AppletHandle::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
{
Q_UNUSED(event);
//kDebug() << "hover enter";
- //if a disappear was scheduled stop the timer
+ if (m_applet) {
+ ToolTipManager::self()->hide(m_applet);
+ }
+
if (m_leaveTimer->isActive()) {
+ //if a disappear was scheduled stop the timer
m_leaveTimer->stop();
- }
- // if we're already fading out, fade back in
- else if (!m_anim.data() && m_animType == FadeOut) {
+ } else if (!m_anim.data() && m_animType == FadeOut) {
+ // if we're already fading out, fade back in
startFading(FadeIn, m_entryPos, true);
}
}
void AppletHandle::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
{
hoverEnterEvent(event);
}
void AppletHandle::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
{
Q_UNUSED(event);
foreach (QWidget *widget, QApplication::topLevelWidgets()) {
QMenu *menu = qobject_cast<QMenu*>(widget);
if (menu && menu->isVisible()) {
connect(menu, SIGNAL(aboutToHide()), this, SLOT(leaveTimeout()));
return;
}
}
// if we haven't even showed up yet, remove the handle
if (m_hoverTimer->isActive()) {
m_hoverTimer->stop();
QTimer::singleShot(0, this, SLOT(emitDisappear()));
} else if (m_pressedButton != NoButton) {
m_pendingFade = true;
} else {
//wait a moment to hide the handle in order to recheck the mouse position
m_leaveTimer->start();
}
}
bool AppletHandle::sceneEventFilter(QGraphicsItem *watched, QEvent *event)
{
if (watched == m_applet && event->type() == QEvent::GraphicsSceneHoverLeave) {
hoverLeaveEvent(static_cast<QGraphicsSceneHoverEvent*>(event));
}
return false;
}
void AppletHandle::setFadeAnimation(qreal progress)
{
m_opacity = progress;
//kDebug() << "progress" << progress << "m_opacity" << m_opacity << m_anim << "(" << FadeIn << ")";
if (qFuzzyCompare(progress, qreal(1.0))) {
delete m_backgroundBuffer;
m_backgroundBuffer = 0;
}
update();
}
qreal AppletHandle::fadeAnimation() const
{
return m_opacity;
}
void AppletHandle::hoverTimeout()
{
startFading(FadeIn, m_entryPos);
}
void AppletHandle::leaveTimeout()
{
if (!isUnderMouse()) {
startFading(FadeOut, m_entryPos);
}
}
void AppletHandle::appletDestroyed()
{
m_applet = 0;
}
void AppletHandle::appletResized()
{
prepareGeometryChange();
calculateSize();
update();
}
void AppletHandle::setHoverPos(const QPointF &hoverPos)
{
m_entryPos = hoverPos;
}
void AppletHandle::startFading(FadeType anim, const QPointF &hoverPos, bool preserveSide)
{
QPropertyAnimation *propAnim = m_anim.data();
if (anim == FadeIn) {
if (propAnim) {
propAnim->stop();
} else {
propAnim = new QPropertyAnimation(this, "fadeAnimation", this);
m_anim = propAnim;
}
}
m_entryPos = hoverPos;
qreal time = 100;
if (!m_applet) {
m_animType = FadeOut;
setFadeAnimation(1.0);
return;
}
if (anim == FadeIn) {
//kDebug() << m_entryPos.x() << m_applet->pos().x();
prepareGeometryChange();
bool wasOnRight = m_buttonsOnRight;
if (!preserveSide) {
m_buttonsOnRight = m_entryPos.x() > (m_applet->size().width() / 2);
}
calculateSize();
QPolygonF region = m_applet->mapToParent(m_rect).intersected(m_applet->parentWidget()->boundingRect());
//kDebug() << region << m_rect << mapToParent(m_rect) << containmnet->boundingRect();
if (region != m_applet->mapToParent(m_rect)) {
// switch sides
//kDebug() << "switch sides";
m_buttonsOnRight = !m_buttonsOnRight;
calculateSize();
QPolygonF region2 = m_applet->mapToParent(m_rect).intersected(m_applet->parentWidget()->boundingRect());
if (region2 != mapToParent(m_rect)) {
// ok, both sides failed to be perfect... which one is more perfect?
QRectF f1 = region.boundingRect();
QRectF f2 = region2.boundingRect();
//kDebug() << "still not a perfect world"
// << f2.width() << f2.height() << f1.width() << f1.height();
if ((f2.width() * f2.height()) < (f1.width() * f1.height())) {
//kDebug() << "we did better the first time";
m_buttonsOnRight = !m_buttonsOnRight;
calculateSize();
}
}
}
if (wasOnRight != m_buttonsOnRight &&
m_animType == FadeIn &&
anim == FadeIn &&
m_opacity <= 1) {
m_opacity = 0.0;
}
time *= 1.0 - m_opacity;
} else {
time *= m_opacity;
}
if (propAnim) {
propAnim->setStartValue(0);
propAnim->setEndValue(1);
propAnim->setDuration(time);
}
m_animType = anim;
//kDebug() << "animating for " << time << "ms";
if (m_animType == FadeIn) {
propAnim->setDirection(QAbstractAnimation::Forward);
propAnim->start();
} else if (propAnim) {
propAnim->setDirection(QAbstractAnimation::Backward);
propAnim->start(QAbstractAnimation::DeleteWhenStopped);
}
}
void AppletHandle::forceDisappear()
{
setAcceptsHoverEvents(false);
startFading(FadeOut, m_entryPos);
}
int AppletHandle::minimumHeight()
{
int iconMargin = m_iconSize / 2;
int requiredHeight = iconMargin + //first margin
(m_iconSize + iconMargin) * 4 + //XXX remember to update this if the number of buttons changes
iconMargin ; //blank space before the close button
if (m_applet && m_applet->hasConfigurationInterface()) {
requiredHeight += (m_iconSize + iconMargin);
}
return requiredHeight;
}
void AppletHandle::calculateSize()
{
KIconLoader *iconLoader = KIconLoader::global();
//m_iconSize = iconLoader->currentSize(KIconLoader::Small); //does not work with double sized icon
m_iconSize = iconLoader->loadIcon("transform-scale", KIconLoader::Small).width(); //workaround
int handleHeight = qMax(minimumHeight(), int(m_applet->contentsRect().height() * 0.8));
int handleWidth = m_iconSize + 2 * HANDLE_MARGIN;
int top =
m_applet->contentsRect().top() + (m_applet->contentsRect().height() - handleHeight) / 2.0;
qreal marginLeft, marginTop, marginRight, marginBottom;
m_background->getMargins(marginLeft, marginTop, marginRight, marginBottom);
if (m_buttonsOnRight) {
//put the rect on the right of the applet
m_rect = QRectF(m_applet->size().width(), top, handleWidth, handleHeight);
} else {
//put the rect on the left of the applet
m_rect = QRectF(-handleWidth, top, handleWidth, handleHeight);
}
if (m_applet->contentsRect().height() > qreal(minimumHeight()) * 1.25) {
int addedMargin = marginLeft / 2;
// now we check to see if the shape is smaller than the contents,
// and that the shape is not just the bounding rect; in those cases
// we have a shaped guy and we draw a full panel;
// TODO: allow applets to mark when they have translucent areas and
// should therefore skip this test?
if (!m_applet->shape().contains(m_applet->contentsRect())) {
QPainterPath p;
p.addRect(m_applet->boundingRect());
if (m_applet->shape() != p) {
addedMargin = m_applet->contentsRect().width() / 2;
}
}
if (m_buttonsOnRight) {
marginLeft += addedMargin;
} else {
marginRight += addedMargin;
}
}
//m_rect = m_applet->mapToParent(m_rect).boundingRect();
m_decorationRect = m_rect.adjusted(-marginLeft, -marginTop, marginRight, marginBottom);
m_totalRect = m_decorationRect.united(m_applet->boundingRect());
}
} // Plasma Namespace
#include "moc_applethandle_p.cpp"
diff --git a/plasma/tests/testcontainmentactionsplugin/plasma-containmentactions-test.desktop b/plasma/tests/testcontainmentactionsplugin/plasma-containmentactions-test.desktop
index 4bf095c9fb..5488c7326a 100644
--- a/plasma/tests/testcontainmentactionsplugin/plasma-containmentactions-test.desktop
+++ b/plasma/tests/testcontainmentactionsplugin/plasma-containmentactions-test.desktop
@@ -1,137 +1,137 @@
[Desktop Entry]
Name=Test
Name[ar]=اختبار
Name[bg]=Проба
Name[bs]=Test
Name[ca]=Prova
Name[ca@valencia]=Prova
Name[cs]=Test
Name[da]=Test
Name[de]=Test
Name[el]=Δοκιμή
Name[en_GB]=Test
Name[es]=Prueba
Name[et]=Test
Name[eu]=Proba
Name[fa]=آزمون
Name[fi]=Testi
Name[fr]=Test
Name[ga]=Tástáil
Name[gl]=Proba
Name[he]=בדיקה
Name[hr]=Test
Name[hu]=Teszt
Name[ia]=Essayo
Name[is]=Prófun
Name[it]=Prova
Name[ja]=テスト
Name[kk]=Сынақ
Name[km]=សាកល្បង
Name[ko]=테스트
Name[ku]=Bicaribîne
Name[lt]=Testas
Name[lv]=Tests
Name[nb]=Test
Name[nds]=Utproberen
Name[nl]=Test
Name[pa]=ਟੈਸਟ
Name[pl]=Test
Name[pt]=Teste
Name[pt_BR]=Teste
Name[ro]=Test
Name[ru]=Проверка
Name[se]=Geahččaleapmi
Name[si]=පරීක්‍ෂණය
Name[sk]=Test
Name[sl]=Test
Name[sq]=Test
Name[sr]=Проба
Name[sr@ijekavian]=Проба
Name[sr@ijekavianlatin]=Proba
Name[sr@latin]=Proba
Name[sv]=Test
Name[tg]=Санҷиш
Name[th]=ทดสอบ
Name[tr]=Deneme
Name[tt]=Тикшерү
-Name[ug]=سىناق
+Name[ug]=سىنا
Name[uk]=Тест
Name[vi]=Kiểm tra
Name[wa]=Sayî
Name[x-test]=xxTestxx
Name[zh_CN]=测试
Name[zh_TW]=測試
Comment=A dummy plugin for testing
Comment[ar]=إضافة وهمية من أجل الاختبار
Comment[bg]=Празна приставка за изпробване
Comment[bs]=Lažni priključak za probe
Comment[ca]=Un connector fals per a provar
Comment[ca@valencia]=Un connector fals per a provar
Comment[cs]=Prázdný zásuvný modul pro testování
Comment[da]=Et attrap-plugin til testformål
Comment[de]=Ein Dummy-Modul zum Testen.
Comment[el]=Ένα "χαζό" πρόσθετο για δοκιμές
Comment[en_GB]=A dummy plugin for testing
Comment[es]=Un complemento de pruebas
Comment[et]=Libaplugin testimiseks
Comment[eu]=Probatarako gezurretazko plugin bat
Comment[fi]=Tyhjä testausliitännäinen
Comment[fr]=Un module factice de test
Comment[ga]=Bréagbhreiseán le haghaidh tástála
Comment[gl]=Un engadido para probas
Comment[he]=תוסף דמה לבדיקה
Comment[hr]=Lažan priključak za testiranje
Comment[hu]=Üres bővítmény teszteléshez
Comment[ia]=Un plugin vacue pro essayar
Comment[is]=Tilraunaíforrit fyrir prófanir
Comment[it]=Una estensione fittizia di prova
Comment[ja]=テスト用のダミープラグイン
Comment[kk]=Сынақ үшін бос плагині
Comment[km]=កម្មវិធី​ជំនួយ​ដំបូង​សម្រាប់​សាកល្បង
Comment[ko]=테스트를 위한 뼈대 플러그인
Comment[ku]=Pêvekekî hêsan ji bo ceribandinê
Comment[lt]=Netikras priedas testavimui
Comment[lv]=Testēšanai paredzēts spraudnis
Comment[nb]=Et attrapp-programtillegg for testing
Comment[nds]=En Platzholler-Moduul för't Utproberen
Comment[nl]=Een dummy plugin voor testen
Comment[pa]=ਟੈਸਟ ਕਰਨ ਲਈ ਫਰਜ਼ੀ ਪਲੱਗਇਨ
Comment[pl]=Prosta wtyczka testowa
Comment[pt]=Um 'plugin' de exemplo para testes
Comment[pt_BR]=Um plug-in fictício para testes
Comment[ro]=Modul fictiv pentru testare
Comment[ru]=Расширение для тестирования
Comment[se]=Geahččalanlassemodula
Comment[sk]=Ukážkový modul pre testovanie
Comment[sl]=Prazen vstavek za testiranje
Comment[sr]=Лажни прикључак за пробе
Comment[sr@ijekavian]=Лажни прикључак за пробе
Comment[sr@ijekavianlatin]=Lažni priključak za probe
Comment[sr@latin]=Lažni priključak za probe
Comment[sv]=Ett exempelinsticksprogram för test
Comment[tg]=Воситаҳо барои санҷиш
Comment[th]=โปรแกรมเสริมเทียมสำหรับทดสอบ
Comment[tr]=Denemek için sahte bir eklenti
Comment[tt]=Тикшертү өчен ялган өстәмә
Comment[ug]=سىناشقا ئىشلىتىدىغان قىستۇرما
Comment[uk]=Додаток для тестування
Comment[vi]=Một phần bổ sung giả để kiểm thử
Comment[wa]=On fås tchôke-divins po l' asprouvaedje
Comment[x-test]=xxA dummy plugin for testingxx
Comment[zh_CN]=测试用插件
Comment[zh_TW]=測試用外掛程式
Type=Service
Icon=preferences-desktop-color
ServiceTypes=Plasma/ContainmentActions
X-KDE-Library=plasma_containmentactions_test
X-KDE-PluginInfo-Author=Chani
X-KDE-PluginInfo-Email=chani@kde.org
X-KDE-PluginInfo-Name=test
X-KDE-PluginInfo-Version=pre0.1
X-KDE-PluginInfo-Website=http://plasma.kde.org/
X-KDE-PluginInfo-Depends=
X-KDE-PluginInfo-License=GPL
X-KDE-PluginInfo-EnabledByDefault=true
X-Plasma-HasConfigurationInterface=true
diff --git a/plasma/theme.cpp b/plasma/theme.cpp
index 114228a181..5de6924200 100644
--- a/plasma/theme.cpp
+++ b/plasma/theme.cpp
@@ -1,1069 +1,1069 @@
/*
* Copyright 2006-2007 Aaron Seigo <aseigo@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2, or
* (at your option) any later version.
*
* This program 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 General Public License for more details
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "theme.h"
#include <QApplication>
#include <QFile>
#include <QFileInfo>
#include <QMutableListIterator>
#include <QPair>
#include <QStringBuilder>
#include <QTimer>
#ifdef Q_WS_X11
#include <QX11Info>
#include "private/effectwatcher_p.h"
#endif
#include <kcolorscheme.h>
#include <kcomponentdata.h>
#include <kconfiggroup.h>
#include <kdebug.h>
#include <kdirwatch.h>
#include <kglobal.h>
#include <kglobalsettings.h>
#include <kmanagerselection.h>
#include <kimagecache.h>
#include <ksharedconfig.h>
#include <kstandarddirs.h>
#include <kwindowsystem.h>
#include "libplasma-theme-global.h"
#include "private/packages_p.h"
#include "windoweffects.h"
namespace Plasma
{
//NOTE: Default wallpaper can be set from the theme configuration
#define DEFAULT_WALLPAPER_THEME "default"
#define DEFAULT_WALLPAPER_SUFFIX ".png"
static const int DEFAULT_WALLPAPER_WIDTH = 1920;
static const int DEFAULT_WALLPAPER_HEIGHT = 1200;
enum styles {
DEFAULTSTYLE,
SVGSTYLE
};
enum CacheType {
NoCache = 0,
PixmapCache = 1,
SvgElementsCache = 2
};
Q_DECLARE_FLAGS(CacheTypes, CacheType)
Q_DECLARE_OPERATORS_FOR_FLAGS(CacheTypes)
class ThemePrivate
{
public:
ThemePrivate(Theme *theme)
: q(theme),
colorScheme(QPalette::Active, KColorScheme::Window, KSharedConfigPtr(0)),
buttonColorScheme(QPalette::Active, KColorScheme::Button, KSharedConfigPtr(0)),
viewColorScheme(QPalette::Active, KColorScheme::View, KSharedConfigPtr(0)),
defaultWallpaperTheme(DEFAULT_WALLPAPER_THEME),
defaultWallpaperSuffix(DEFAULT_WALLPAPER_SUFFIX),
defaultWallpaperWidth(DEFAULT_WALLPAPER_WIDTH),
defaultWallpaperHeight(DEFAULT_WALLPAPER_HEIGHT),
pixmapCache(0),
cachesToDiscard(NoCache),
locolor(false),
compositingActive(KWindowSystem::self()->compositingActive()),
blurActive(false),
isDefault(false),
useGlobal(true),
hasWallpapers(false),
useNativeWidgetStyle(false)
{
generalFont = QApplication::font();
ThemeConfig config;
cacheTheme = config.cacheTheme();
saveTimer = new QTimer(q);
saveTimer->setSingleShot(true);
saveTimer->setInterval(600);
QObject::connect(saveTimer, SIGNAL(timeout()), q, SLOT(scheduledCacheUpdate()));
updateNotificationTimer = new QTimer(q);
updateNotificationTimer->setSingleShot(true);
updateNotificationTimer->setInterval(500);
QObject::connect(updateNotificationTimer, SIGNAL(timeout()), q, SLOT(notifyOfChanged()));
if (QPixmap::defaultDepth() > 8) {
QObject::connect(KWindowSystem::self(), SIGNAL(compositingChanged(bool)), q, SLOT(compositingChanged(bool)));
#ifdef Q_WS_X11
//watch for blur effect property changes as well
if (!s_blurEffectWatcher) {
s_blurEffectWatcher = new EffectWatcher("_KDE_NET_WM_BLUR_BEHIND_REGION");
}
QObject::connect(s_blurEffectWatcher, SIGNAL(effectChanged(bool)), q, SLOT(blurBehindChanged(bool)));
#endif
}
}
~ThemePrivate()
{
delete pixmapCache;
}
KConfigGroup &config()
{
if (!cfg.isValid()) {
QString groupName = "Theme";
if (!useGlobal) {
QString app = KGlobal::mainComponent().componentName();
if (!app.isEmpty()) {
#ifndef NDEBUG
kDebug() << "using theme for app" << app;
#endif
groupName.append("-").append(app);
}
}
cfg = KConfigGroup(KSharedConfig::openConfig(themeRcFile), groupName);
}
return cfg;
}
QString findInTheme(const QString &image, const QString &theme, bool cache = true);
void compositingChanged(bool active);
void discardCache(CacheTypes caches);
void scheduledCacheUpdate();
void scheduleThemeChangeNotification(CacheTypes caches);
void notifyOfChanged();
void colorsChanged();
void blurBehindChanged(bool blur);
bool useCache();
void settingsFileChanged(const QString &);
void setThemeName(const QString &themeName, bool writeSettings);
void onAppExitCleanup();
void processWallpaperSettings(KConfigBase *metadata);
const QString processStyleSheet(const QString &css);
static const char *defaultTheme;
static const char *systemColorsTheme;
static const char *themeRcFile;
#ifdef Q_WS_X11
static EffectWatcher *s_blurEffectWatcher;
#endif
Theme *q;
QString themeName;
QList<QString> fallbackThemes;
KSharedConfigPtr colors;
KColorScheme colorScheme;
KColorScheme buttonColorScheme;
KColorScheme viewColorScheme;
KConfigGroup cfg;
QFont generalFont;
QString defaultWallpaperTheme;
QString defaultWallpaperSuffix;
int defaultWallpaperWidth;
int defaultWallpaperHeight;
KImageCache *pixmapCache;
KSharedConfigPtr svgElementsCache;
QHash<QString, QSet<QString> > invalidElements;
QHash<QString, QPixmap> pixmapsToCache;
QHash<QString, QString> keysToCache;
QHash<QString, QString> idsToCache;
QHash<styles, QString> cachedStyleSheets;
QHash<QString, QString> discoveries;
QTimer *saveTimer;
QTimer *updateNotificationTimer;
int toolTipDelay;
CacheTypes cachesToDiscard;
bool locolor : 1;
bool compositingActive : 1;
bool blurActive : 1;
bool isDefault : 1;
bool useGlobal : 1;
bool hasWallpapers : 1;
bool cacheTheme : 1;
bool useNativeWidgetStyle :1;
};
const char *ThemePrivate::defaultTheme = "default";
const char *ThemePrivate::themeRcFile = "plasmarc";
// the system colors theme is used to cache unthemed svgs with colorization needs
// these svgs do not follow the theme's colors, but rather the system colors
const char *ThemePrivate::systemColorsTheme = "internal-system-colors";
#ifdef Q_WS_X11
EffectWatcher *ThemePrivate::s_blurEffectWatcher = 0;
#endif
bool ThemePrivate::useCache()
{
if (cacheTheme && !pixmapCache) {
ThemeConfig config;
pixmapCache = new KImageCache("plasma_theme_" + themeName, config.themeCacheKb() * 1024);
if (themeName != systemColorsTheme) {
//check for expired cache
// FIXME: when using the system colors, if they change while the application is not running
// the cache should be dropped; we need a way to detect system color change when the
// application is not running.
QFile f(KStandardDirs::locate("data", "desktoptheme/" + themeName + "/metadata.desktop"));
QFileInfo info(f);
if (info.lastModified().toTime_t() > uint(pixmapCache->lastModifiedTime())) {
pixmapCache->clear();
}
}
}
return cacheTheme;
}
void ThemePrivate::onAppExitCleanup()
{
pixmapsToCache.clear();
delete pixmapCache;
pixmapCache = 0;
cacheTheme = false;
}
QString ThemePrivate::findInTheme(const QString &image, const QString &theme, bool cache)
{
if (cache && discoveries.contains(image)) {
return discoveries[image];
}
QString search;
if (locolor) {
search = QLatin1Literal("desktoptheme/") % theme % QLatin1Literal("/locolor/") % image;
search = KStandardDirs::locate("data", search);
} else if (!compositingActive) {
search = QLatin1Literal("desktoptheme/") % theme % QLatin1Literal("/opaque/") % image;
search = KStandardDirs::locate("data", search);
} else if (WindowEffects::isEffectAvailable(WindowEffects::BlurBehind)) {
search = QLatin1Literal("desktoptheme/") % theme % QLatin1Literal("/translucent/") % image;
search = KStandardDirs::locate("data", search);
}
//not found or compositing enabled
if (search.isEmpty()) {
search = QLatin1Literal("desktoptheme/") % theme % QLatin1Char('/') % image;
search = KStandardDirs::locate("data", search);
}
if (cache && !search.isEmpty()) {
discoveries.insert(image, search);
}
return search;
}
void ThemePrivate::compositingChanged(bool active)
{
#ifdef Q_WS_X11
if (compositingActive != active) {
compositingActive = active;
//kDebug() << QTime::currentTime();
scheduleThemeChangeNotification(PixmapCache | SvgElementsCache);
}
#endif
}
void ThemePrivate::discardCache(CacheTypes caches)
{
if (caches & PixmapCache) {
pixmapsToCache.clear();
saveTimer->stop();
if (pixmapCache) {
pixmapCache->clear();
}
} else {
// This deletes the object but keeps the on-disk cache for later use
delete pixmapCache;
pixmapCache = 0;
}
cachedStyleSheets.clear();
if (caches & SvgElementsCache) {
discoveries.clear();
invalidElements.clear();
if (svgElementsCache) {
QFile f(svgElementsCache->name());
svgElementsCache = 0;
f.remove();
}
const QString svgElementsFile = KStandardDirs::locateLocal("cache", "plasma-svgelements-" + themeName);
svgElementsCache = KSharedConfig::openConfig(svgElementsFile);
}
}
void ThemePrivate::scheduledCacheUpdate()
{
if (useCache()) {
QHashIterator<QString, QPixmap> it(pixmapsToCache);
while (it.hasNext()) {
it.next();
pixmapCache->insertPixmap(idsToCache[it.key()], it.value());
}
}
pixmapsToCache.clear();
keysToCache.clear();
idsToCache.clear();
}
void ThemePrivate::colorsChanged()
{
colorScheme = KColorScheme(QPalette::Active, KColorScheme::Window, colors);
buttonColorScheme = KColorScheme(QPalette::Active, KColorScheme::Button, colors);
viewColorScheme = KColorScheme(QPalette::Active, KColorScheme::View, colors);
scheduleThemeChangeNotification(PixmapCache);
}
void ThemePrivate::blurBehindChanged(bool blur)
{
if (blurActive != blur) {
blurActive = blur;
scheduleThemeChangeNotification(PixmapCache | SvgElementsCache);
}
}
void ThemePrivate::scheduleThemeChangeNotification(CacheTypes caches)
{
cachesToDiscard |= caches;
updateNotificationTimer->start();
}
void ThemePrivate::notifyOfChanged()
{
//kDebug() << cachesToDiscard;
discardCache(cachesToDiscard);
cachesToDiscard = NoCache;
emit q->themeChanged();
}
const QString ThemePrivate::processStyleSheet(const QString &css)
{
QString stylesheet;
if (css.isEmpty()) {
stylesheet = cachedStyleSheets.value(DEFAULTSTYLE);
if (stylesheet.isEmpty()) {
stylesheet = QString("\n\
body {\n\
color: %textcolor;\n\
font-size: %fontsize;\n\
font-family: %fontfamily;\n\
}\n\
a:active { color: %activatedlink; }\n\
a:link { color: %link; }\n\
a:visited { color: %visitedlink; }\n\
a:hover { color: %hoveredlink; text-decoration: none; }\n\
");
stylesheet = processStyleSheet(stylesheet);
cachedStyleSheets.insert(DEFAULTSTYLE, stylesheet);
}
return stylesheet;
} else if (css == "SVG") {
stylesheet = cachedStyleSheets.value(SVGSTYLE);
if (stylesheet.isEmpty()) {
QString skel = ".ColorScheme-%1{color:%2;}";
stylesheet += skel.arg("Text","%textcolor");
stylesheet += skel.arg("Background","%backgroundcolor");
stylesheet += skel.arg("ButtonText","%buttontextcolor");
stylesheet += skel.arg("ButtonBackground","%buttonbackgroundcolor");
stylesheet += skel.arg("ButtonHover","%buttonhovercolor");
stylesheet += skel.arg("ButtonFocus","%buttonfocuscolor");
stylesheet += skel.arg("ViewText","%viewtextcolor");
stylesheet += skel.arg("ViewBackground","%viewbackgroundcolor");
stylesheet += skel.arg("ViewHover","%viewhovercolor");
stylesheet += skel.arg("ViewFocus","%viewfocuscolor");
stylesheet = processStyleSheet(stylesheet);
cachedStyleSheets.insert(SVGSTYLE, stylesheet);
}
return stylesheet;
} else {
stylesheet = css;
}
QHash<QString, QString> elements;
// If you add elements here, make sure their names are sufficiently unique to not cause
// clashes between element keys
elements["%textcolor"] = q->color(Theme::TextColor).name();
elements["%backgroundcolor"] = q->color(Theme::BackgroundColor).name();
elements["%visitedlink"] = q->color(Theme::VisitedLinkColor).name();
elements["%activatedlink"] = q->color(Theme::HighlightColor).name();
elements["%hoveredlink"] = q->color(Theme::HighlightColor).name();
elements["%link"] = q->color(Theme::LinkColor).name();
elements["%buttontextcolor"] = q->color(Theme::ButtonTextColor).name();
elements["%buttonbackgroundcolor"] = q->color(Theme::ButtonBackgroundColor).name();
elements["%buttonhovercolor"] = q->color(Theme::ButtonHoverColor).name();
elements["%buttonfocuscolor"] = q->color(Theme::ButtonFocusColor).name();
elements["%viewtextcolor"] = q->color(Theme::ViewTextColor).name();
elements["%viewbackgroundcolor"] = q->color(Theme::ViewBackgroundColor).name();
elements["%viewhovercolor"] = q->color(Theme::ViewHoverColor).name();
elements["%viewfocuscolor"] = q->color(Theme::ViewFocusColor).name();
QFont font = q->font(Theme::DefaultFont);
elements["%fontsize"] = QString("%1pt").arg(font.pointSize());
elements["%fontfamily"] = font.family().split('[').first();
elements["%smallfontsize"] = QString("%1pt").arg(KGlobalSettings::smallestReadableFont().pointSize());
QHash<QString, QString>::const_iterator it = elements.constBegin();
QHash<QString, QString>::const_iterator itEnd = elements.constEnd();
for ( ; it != itEnd; ++it) {
stylesheet.replace(it.key(), it.value());
}
return stylesheet;
}
class ThemeSingleton
{
public:
ThemeSingleton()
{
self.d->isDefault = true;
//FIXME: if/when kconfig gets change notification, this will be unnecessary
KDirWatch::self()->addFile(KStandardDirs::locateLocal("config", ThemePrivate::themeRcFile));
QObject::connect(KDirWatch::self(), SIGNAL(created(QString)), &self, SLOT(settingsFileChanged(QString)));
QObject::connect(KDirWatch::self(), SIGNAL(dirty(QString)), &self, SLOT(settingsFileChanged(QString)));
}
Theme self;
};
K_GLOBAL_STATIC(ThemeSingleton, privateThemeSelf)
Theme *Theme::defaultTheme()
{
return &privateThemeSelf->self;
}
Theme::Theme(QObject *parent)
: QObject(parent),
d(new ThemePrivate(this))
{
settingsChanged();
if (QCoreApplication::instance()) {
connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()),
this, SLOT(onAppExitCleanup()));
}
}
Theme::Theme(const QString &themeName, QObject *parent)
: QObject(parent),
d(new ThemePrivate(this))
{
// turn off caching so we don't accidently trigger unnecessary disk activity at this point
bool useCache = d->cacheTheme;
d->cacheTheme = false;
setThemeName(themeName);
d->cacheTheme = useCache;
if (QCoreApplication::instance()) {
connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()),
this, SLOT(onAppExitCleanup()));
}
}
Theme::~Theme()
{
if (d->svgElementsCache) {
QHashIterator<QString, QSet<QString> > it(d->invalidElements);
while (it.hasNext()) {
it.next();
KConfigGroup imageGroup(d->svgElementsCache, it.key());
imageGroup.writeEntry("invalidElements", it.value().toList()); //FIXME: add QSet support to KConfig
}
}
d->onAppExitCleanup();
delete d;
}
KPluginInfo::List Theme::listThemeInfo()
{
const QStringList themes = KGlobal::dirs()->findAllResources("data", "desktoptheme/*/metadata.desktop",
KStandardDirs::NoDuplicates);
return KPluginInfo::fromFiles(themes);
}
void ThemePrivate::settingsFileChanged(const QString &file)
{
if (file.endsWith(themeRcFile)) {
config().config()->reparseConfiguration();
q->settingsChanged();
}
}
void Theme::settingsChanged()
{
KConfigGroup cg = d->config();
d->setThemeName(cg.readEntry("name", ThemePrivate::defaultTheme), false);
cg = KConfigGroup(cg.config(), "PlasmaToolTips");
- d->toolTipDelay = cg.readEntry("Delay", qreal(0.7));
+ d->toolTipDelay = cg.readEntry("Delay", 700);
}
void Theme::setThemeName(const QString &themeName)
{
d->setThemeName(themeName, true);
}
void ThemePrivate::processWallpaperSettings(KConfigBase *metadata)
{
if (!defaultWallpaperTheme.isEmpty() && defaultWallpaperTheme != DEFAULT_WALLPAPER_THEME) {
return;
}
KConfigGroup cg;
if (metadata->hasGroup("Wallpaper")) {
// we have a theme color config, so let's also check to see if
// there is a wallpaper defined in there.
cg = KConfigGroup(metadata, "Wallpaper");
} else {
// since we didn't find an entry in the theme, let's look in the main
// theme config
cg = config();
}
defaultWallpaperTheme = cg.readEntry("defaultWallpaperTheme", DEFAULT_WALLPAPER_THEME);
defaultWallpaperSuffix = cg.readEntry("defaultFileSuffix", DEFAULT_WALLPAPER_SUFFIX);
defaultWallpaperWidth = cg.readEntry("defaultWidth", DEFAULT_WALLPAPER_WIDTH);
defaultWallpaperHeight = cg.readEntry("defaultHeight", DEFAULT_WALLPAPER_HEIGHT);
}
void ThemePrivate::setThemeName(const QString &tempThemeName, bool writeSettings)
{
//kDebug() << tempThemeName;
QString theme = tempThemeName;
if (theme.isEmpty() || theme == themeName) {
// let's try and get the default theme at least
if (themeName.isEmpty()) {
theme = ThemePrivate::defaultTheme;
} else {
return;
}
}
// we have one special theme: essentially a dummy theme used to cache things with
// the system colors.
bool realTheme = theme != systemColorsTheme;
if (realTheme) {
QString themePath = KStandardDirs::locate("data", QLatin1Literal("desktoptheme/") % theme % QLatin1Char('/'));
if (themePath.isEmpty() && themeName.isEmpty()) {
themePath = KStandardDirs::locate("data", "desktoptheme/default/");
if (themePath.isEmpty()) {
return;
}
theme = ThemePrivate::defaultTheme;
}
}
// check again as ThemePrivate::defaultTheme might be empty
if (themeName == theme) {
return;
}
themeName = theme;
// load the color scheme config
const QString colorsFile = realTheme ? KStandardDirs::locate("data", QLatin1Literal("desktoptheme/") % theme % QLatin1Literal("/colors"))
: QString();
//kDebug() << "we're going for..." << colorsFile << "*******************";
// load the wallpaper settings, if any
if (realTheme) {
const QString metadataPath(KStandardDirs::locate("data", QLatin1Literal("desktoptheme/") % theme % QLatin1Literal("/metadata.desktop")));
KConfig metadata(metadataPath);
processWallpaperSettings(&metadata);
KConfigGroup cg(&metadata, "Settings");
useNativeWidgetStyle = cg.readEntry("UseNativeWidgetStyle", false);
QString fallback = cg.readEntry("FallbackTheme", QString());
fallbackThemes.clear();
while (!fallback.isEmpty() && !fallbackThemes.contains(fallback)) {
fallbackThemes.append(fallback);
QString metadataPath(KStandardDirs::locate("data", QLatin1Literal("desktoptheme/") % theme % QLatin1Literal("/metadata.desktop")));
KConfig metadata(metadataPath);
KConfigGroup cg(&metadata, "Settings");
fallback = cg.readEntry("FallbackTheme", QString());
}
if (!fallbackThemes.contains("oxygen")) {
fallbackThemes.append("oxygen");
}
if (!fallbackThemes.contains(ThemePrivate::defaultTheme)) {
fallbackThemes.append(ThemePrivate::defaultTheme);
}
foreach (const QString &theme, fallbackThemes) {
QString metadataPath(KStandardDirs::locate("data", QLatin1Literal("desktoptheme/") % theme % QLatin1Literal("/metadata.desktop")));
KConfig metadata(metadataPath);
processWallpaperSettings(&metadata);
}
}
if (colorsFile.isEmpty()) {
colors = 0;
QObject::connect(KGlobalSettings::self(), SIGNAL(kdisplayPaletteChanged()),
q, SLOT(colorsChanged()), Qt::UniqueConnection);
} else {
QObject::disconnect(KGlobalSettings::self(), SIGNAL(kdisplayPaletteChanged()),
q, SLOT(colorsChanged()));
colors = KSharedConfig::openConfig(colorsFile);
}
colorScheme = KColorScheme(QPalette::Active, KColorScheme::Window, colors);
buttonColorScheme = KColorScheme(QPalette::Active, KColorScheme::Button, colors);
viewColorScheme = KColorScheme(QPalette::Active, KColorScheme::View, colors);
hasWallpapers = KStandardDirs::exists(KStandardDirs::locateLocal("data", QLatin1Literal("desktoptheme/") % theme % QLatin1Literal("/wallpapers/")));
if (realTheme && isDefault && writeSettings) {
// we're the default theme, let's save our state
KConfigGroup &cg = config();
if (ThemePrivate::defaultTheme == themeName) {
cg.deleteEntry("name");
} else {
cg.writeEntry("name", themeName);
}
cg.sync();
}
scheduleThemeChangeNotification(SvgElementsCache);
}
QString Theme::themeName() const
{
return d->themeName;
}
QString Theme::imagePath(const QString &name) const
{
// look for a compressed svg file in the theme
if (name.contains("../") || name.isEmpty()) {
// we don't support relative paths
//kDebug() << "Theme says: bad image path " << name;
return QString();
}
const QString svgzName = name % QLatin1Literal(".svgz");
QString path = d->findInTheme(svgzName, d->themeName);
if (path.isEmpty()) {
// try for an uncompressed svg file
const QString svgName = name % QLatin1Literal(".svg");
path = d->findInTheme(svgName, d->themeName);
// search in fallback themes if necessary
for (int i = 0; path.isEmpty() && i < d->fallbackThemes.count(); ++i) {
if (d->themeName == d->fallbackThemes[i]) {
continue;
}
// try a compressed svg file in the fallback theme
path = d->findInTheme(svgzName, d->fallbackThemes[i]);
if (path.isEmpty()) {
// try an uncompressed svg file in the fallback theme
path = d->findInTheme(svgName, d->fallbackThemes[i]);
}
}
}
/*
if (path.isEmpty()) {
#ifndef NDEBUG
kDebug() << "Theme says: bad image path " << name;
#endif
}
*/
return path;
}
QString Theme::styleSheet(const QString &css) const
{
return d->processStyleSheet(css);
}
QString Theme::wallpaperPath(const QSize &size) const
{
QString fullPath;
QString image = d->defaultWallpaperTheme;
image.append("/contents/images/%1x%2").append(d->defaultWallpaperSuffix);
QString defaultImage = image.arg(d->defaultWallpaperWidth).arg(d->defaultWallpaperHeight);
if (size.isValid()) {
// try to customize the paper to the size requested
//TODO: this should do better than just fallback to the default size.
// a "best fit" matching would be far better, so we don't end
// up returning a 1920x1200 wallpaper for a 640x480 request ;)
image = image.arg(size.width()).arg(size.height());
} else {
image = defaultImage;
}
//TODO: the theme's wallpaper overrides regularly installed wallpapers.
// should it be possible for user installed (e.g. locateLocal) wallpapers
// to override the theme?
if (d->hasWallpapers) {
// check in the theme first
fullPath = d->findInTheme(QLatin1Literal("wallpapers/") % image, d->themeName);
if (fullPath.isEmpty()) {
fullPath = d->findInTheme(QLatin1Literal("wallpapers/") % defaultImage, d->themeName);
}
}
if (fullPath.isEmpty()) {
// we failed to find it in the theme, so look in the standard directories
//kDebug() << "looking for" << image;
fullPath = KStandardDirs::locate("wallpaper", image);
}
if (fullPath.isEmpty()) {
// we still failed to find it in the theme, so look for the default in
// the standard directories
//kDebug() << "looking for" << defaultImage;
fullPath = KStandardDirs::locate("wallpaper", defaultImage);
if (fullPath.isEmpty()) {
#ifndef NDEBUG
kDebug() << "exhausted every effort to find a wallpaper.";
#endif
}
}
return fullPath;
}
bool Theme::currentThemeHasImage(const QString &name) const
{
if (name.contains("../")) {
// we don't support relative paths
return false;
}
return !(d->findInTheme(name % QLatin1Literal(".svgz"), d->themeName, false).isEmpty()) ||
!(d->findInTheme(name % QLatin1Literal(".svg"), d->themeName, false).isEmpty());
}
KSharedConfigPtr Theme::colorScheme() const
{
return d->colors;
}
QColor Theme::color(ColorRole role) const
{
switch (role) {
case TextColor:
return d->colorScheme.foreground(KColorScheme::NormalText).color();
case HighlightColor:
return d->colorScheme.decoration(KColorScheme::HoverColor).color();
case BackgroundColor:
return d->colorScheme.background(KColorScheme::NormalBackground).color();
case ButtonTextColor:
return d->buttonColorScheme.foreground(KColorScheme::NormalText).color();
case ButtonBackgroundColor:
return d->buttonColorScheme.background(KColorScheme::NormalBackground).color();
case ButtonHoverColor:
return d->buttonColorScheme.decoration(KColorScheme::HoverColor).color();
case ButtonFocusColor:
return d->buttonColorScheme.decoration(KColorScheme::FocusColor).color();
case ViewTextColor:
return d->viewColorScheme.foreground(KColorScheme::NormalText).color();
case ViewBackgroundColor:
return d->viewColorScheme.background(KColorScheme::NormalBackground).color();
case ViewHoverColor:
return d->viewColorScheme.decoration(KColorScheme::HoverColor).color();
case ViewFocusColor:
return d->viewColorScheme.decoration(KColorScheme::FocusColor).color();
case LinkColor:
return d->viewColorScheme.foreground(KColorScheme::LinkText).color();
case VisitedLinkColor:
return d->viewColorScheme.foreground(KColorScheme::VisitedText).color();
}
return QColor();
}
void Theme::setFont(const QFont &font, FontRole role)
{
Q_UNUSED(role)
d->generalFont = font;
}
QFont Theme::font(FontRole role) const
{
switch (role) {
case DesktopFont: {
KConfigGroup cg(KGlobal::config(), "General");
return cg.readEntry("desktopFont", d->generalFont);
}
break;
case DefaultFont:
default:
return d->generalFont;
break;
case SmallestFont:
return KGlobalSettings::smallestReadableFont();
break;
}
return d->generalFont;
}
QFontMetrics Theme::fontMetrics() const
{
//TODO: allow this to be overridden with a plasma specific font?
return QFontMetrics(d->generalFont);
}
bool Theme::windowTranslucencyEnabled() const
{
return d->compositingActive;
}
void Theme::setUseGlobalSettings(bool useGlobal)
{
if (d->useGlobal == useGlobal) {
return;
}
d->useGlobal = useGlobal;
d->cfg = KConfigGroup();
d->themeName.clear();
settingsChanged();
}
bool Theme::useGlobalSettings() const
{
return d->useGlobal;
}
bool Theme::useNativeWidgetStyle() const
{
return d->useNativeWidgetStyle;
}
bool Theme::findInCache(const QString &key, QPixmap &pix, unsigned int lastModified)
{
if (lastModified != 0 && d->useCache() && lastModified > uint(d->pixmapCache->lastModifiedTime())) {
return false;
}
if (d->useCache()) {
const QString id = d->keysToCache.value(key);
if (d->pixmapsToCache.contains(id)) {
pix = d->pixmapsToCache.value(id);
return !pix.isNull();
}
QPixmap temp;
if (d->pixmapCache->findPixmap(key, &temp) && !temp.isNull()) {
pix = temp;
return true;
}
}
return false;
}
void Theme::insertIntoCache(const QString& key, const QPixmap& pix)
{
if (d->useCache()) {
d->pixmapCache->insertPixmap(key, pix);
}
}
void Theme::insertIntoCache(const QString& key, const QPixmap& pix, const QString& id)
{
if (d->useCache()) {
d->pixmapsToCache.insert(id, pix);
if (d->idsToCache.contains(id)) {
d->keysToCache.remove(d->idsToCache[id]);
}
d->keysToCache.insert(key, id);
d->idsToCache.insert(id, key);
d->saveTimer->start();
}
}
bool Theme::findInRectsCache(const QString &image, const QString &element, QRectF &rect) const
{
if (!d->svgElementsCache) {
return false;
}
KConfigGroup imageGroup(d->svgElementsCache, image);
rect = imageGroup.readEntry(element % QLatin1Literal("Size"), QRectF());
if (rect.isValid()) {
return true;
}
//Name starting by _ means the element is empty and we're asked for the size of
//the whole image, so the whole image is never invalid
if (element.indexOf('_') <= 0) {
return false;
}
bool invalid = false;
QHash<QString, QSet<QString> >::iterator it = d->invalidElements.find(image);
if (it == d->invalidElements.end()) {
QSet<QString> elements = imageGroup.readEntry("invalidElements", QStringList()).toSet();
d->invalidElements.insert(image, elements);
invalid = elements.contains(element);
} else {
invalid = it.value().contains(element);
}
return invalid;
}
QStringList Theme::listCachedRectKeys(const QString &image) const
{
if (!d->svgElementsCache) {
return QStringList();
}
KConfigGroup imageGroup(d->svgElementsCache, image);
QStringList keys = imageGroup.keyList();
QMutableListIterator<QString> i(keys);
while (i.hasNext()) {
QString key = i.next();
if (key.endsWith("Size")) {
// The actual cache id used from outside doesn't end on "Size".
key.resize(key.size() - 4);
i.setValue(key);
} else {
i.remove();
}
}
return keys;
}
void Theme::insertIntoRectsCache(const QString& image, const QString &element, const QRectF &rect)
{
if (!d->svgElementsCache) {
return;
}
if (rect.isValid()) {
KConfigGroup imageGroup(d->svgElementsCache, image);
imageGroup.writeEntry(element % QLatin1Literal("Size"), rect);
} else {
QHash<QString, QSet<QString> >::iterator it = d->invalidElements.find(image);
if (it == d->invalidElements.end()) {
d->invalidElements[image].insert(element);
} else if (!it.value().contains(element)) {
if (it.value().count() > 1000) {
it.value().erase(it.value().begin());
}
it.value().insert(element);
}
}
}
void Theme::invalidateRectsCache(const QString& image)
{
if (d->svgElementsCache) {
KConfigGroup imageGroup(d->svgElementsCache, image);
imageGroup.deleteGroup();
}
d->invalidElements.remove(image);
}
void Theme::releaseRectsCache(const QString &image)
{
QHash<QString, QSet<QString> >::iterator it = d->invalidElements.find(image);
if (it != d->invalidElements.end()) {
if (!d->svgElementsCache) {
KConfigGroup imageGroup(d->svgElementsCache, it.key());
imageGroup.writeEntry("invalidElements", it.value().toList());
}
d->invalidElements.erase(it);
}
}
void Theme::setCacheLimit(int kbytes)
{
Q_UNUSED(kbytes)
if (d->useCache()) {
;
// Too late for you bub.
// d->pixmapCache->setCacheLimit(kbytes);
}
}
KUrl Theme::homepage() const
{
const QString metadataPath(KStandardDirs::locate("data", QLatin1Literal("desktoptheme/") % d->themeName % QLatin1Literal("/metadata.desktop")));
KConfig metadata(metadataPath);
KConfigGroup brandConfig(&metadata, "Branding");
return brandConfig.readEntry("homepage", KUrl("http://www.kde.org"));
}
int Theme::toolTipDelay() const
{
return d->toolTipDelay;
}
}
#include <theme.moc>
diff --git a/plasma/tooltipmanager.cpp b/plasma/tooltipmanager.cpp
index 887ca26eb1..a037b857a6 100644
--- a/plasma/tooltipmanager.cpp
+++ b/plasma/tooltipmanager.cpp
@@ -1,498 +1,498 @@
/*
* Copyright 2007 by Dan Meltzer <hydrogen@notyetimplemented.com>
* Copyright 2008 by Aaron Seigo <aseigo@kde.org>
* Copyright 2008 by Alexis Ménard <darktears31@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 St, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
#include "tooltipmanager.h"
//Qt
#include <QCoreApplication>
#include <QLabel>
#include <QTimer>
#include <QGridLayout>
#include <QGraphicsView>
#include <QGraphicsSceneHoverEvent>
//KDE
#include <kwindowsystem.h>
//X11
#ifdef Q_WS_X11
#include <QX11Info>
#include <X11/Xlib.h>
#include <fixx11h.h>
#endif
//Plasma
#include "plasma/applet.h"
#include "plasma/containment.h"
#include "plasma/corona.h"
#include "plasma/framesvg.h"
#include "plasma/popupapplet.h"
#include "plasma/theme.h"
#include "plasma/view.h"
#include "plasma/private/tooltip_p.h"
namespace Plasma
{
class ToolTipManagerPrivate
{
public :
ToolTipManagerPrivate(ToolTipManager *manager)
: q(manager),
currentWidget(0),
showTimer(new QTimer(manager)),
hideTimer(new QTimer(manager)),
tipWidget(0),
state(ToolTipManager::Activated),
isShown(false),
delayedHide(false),
clickable(false)
{
}
~ToolTipManagerPrivate()
{
if (!QCoreApplication::closingDown()) {
delete tipWidget;
}
}
void showToolTip();
void resetShownState();
/**
* called when a widget inside the tooltip manager is deleted
*/
void onWidgetDestroyed(QObject * object);
void removeWidget(QGraphicsWidget *w, bool canSafelyAccess = true);
void clearTips();
void doDelayedHide();
void toolTipHovered(bool);
void createTipWidget();
void hideTipWidget();
ToolTipManager *q;
QGraphicsWidget *currentWidget;
QTimer *showTimer;
QTimer *hideTimer;
QHash<QGraphicsWidget *, ToolTipContent> tooltips;
ToolTip *tipWidget;
ToolTipManager::State state;
bool isShown : 1;
bool delayedHide : 1;
bool clickable : 1;
};
//TOOLTIP IMPLEMENTATION
class ToolTipManagerSingleton
{
public:
ToolTipManagerSingleton()
{
}
ToolTipManager self;
};
K_GLOBAL_STATIC(ToolTipManagerSingleton, privateInstance)
ToolTipManager *ToolTipManager::self()
{
return &privateInstance->self;
}
ToolTipManager::ToolTipManager(QObject *parent)
: QObject(parent),
d(new ToolTipManagerPrivate(this))
{
d->showTimer->setSingleShot(true);
connect(d->showTimer, SIGNAL(timeout()), SLOT(showToolTip()));
d->hideTimer->setSingleShot(true);
connect(d->hideTimer, SIGNAL(timeout()), SLOT(resetShownState()));
}
ToolTipManager::~ToolTipManager()
{
delete d;
}
void ToolTipManager::show(QGraphicsWidget *widget)
{
if (!d->tooltips.contains(widget)) {
return;
}
d->delayedHide = false;
d->hideTimer->stop();
d->showTimer->stop();
const int defaultDelay = Theme::defaultTheme()->toolTipDelay();
if (defaultDelay < 0) {
return;
}
ToolTipContent content = d->tooltips[widget];
qreal delay = content.isInstantPopup() ? 0.0 : defaultDelay;
d->currentWidget = widget;
if (d->isShown) {
// small delay to prevent unnecessary showing when the mouse is moving quickly across items
// which can be too much for less powerful CPUs to keep up with
d->showTimer->start(200);
} else {
- d->showTimer->start(delay * 1000);
+ d->showTimer->start(qMax(qreal(200), delay));
}
}
bool ToolTipManager::isVisible(QGraphicsWidget *widget) const
{
return d->currentWidget == widget && d->tipWidget && d->tipWidget->isVisible();
}
void ToolTipManagerPrivate::doDelayedHide()
{
showTimer->stop(); // stop the timer to show the tooltip
delayedHide = true;
if (isShown && clickable) {
// leave enough time for user to choose
hideTimer->start(1000);
} else {
hideTimer->start(250);
}
}
void ToolTipManager::hide(QGraphicsWidget *widget)
{
if (d->currentWidget != widget) {
return;
}
d->currentWidget = 0;
d->showTimer->stop(); // stop the timer to show the tooltip
d->delayedHide = false;
d->hideTipWidget();
}
void ToolTipManager::registerWidget(QGraphicsWidget *widget)
{
if (d->state == Deactivated || d->tooltips.contains(widget)) {
return;
}
//the tooltip is not registered we add it in our map of tooltips
d->tooltips.insert(widget, ToolTipContent());
widget->installEventFilter(this);
connect(widget, SIGNAL(destroyed(QObject*)), this, SLOT(onWidgetDestroyed(QObject*)));
}
void ToolTipManager::unregisterWidget(QGraphicsWidget *widget)
{
if (!d->tooltips.contains(widget)) {
return;
}
if (widget == d->currentWidget) {
d->currentWidget = 0;
d->showTimer->stop(); // stop the timer to show the tooltip
d->delayedHide = false;
d->hideTipWidget();
}
widget->removeEventFilter(this);
d->removeWidget(widget);
}
void ToolTipManager::setContent(QGraphicsWidget *widget, const ToolTipContent &data)
{
if (d->state == Deactivated || !widget) {
return;
}
registerWidget(widget);
d->tooltips.insert(widget, data);
if (d->currentWidget == widget && d->tipWidget && d->tipWidget->isVisible()) {
if (data.isEmpty()) {
// after this call, d->tipWidget will be null
hide(widget);
} else {
d->delayedHide = data.autohide();
d->clickable = data.isClickable();
if (d->delayedHide) {
//kDebug() << "starting authoide";
d->hideTimer->start(3000);
} else {
d->hideTimer->stop();
}
}
if (d->tipWidget) {
d->tipWidget->setContent(widget, data);
d->tipWidget->prepareShowing();
//look if the data prefers aother graphicswidget, otherwise use the one used as event catcher
QGraphicsWidget *referenceWidget = data.graphicsWidget() ? data.graphicsWidget() : widget;
Corona *corona = qobject_cast<Corona *>(referenceWidget->scene());
if (corona) {
d->tipWidget->moveTo(corona->popupPosition(referenceWidget, d->tipWidget->size(), Qt::AlignCenter));
}
}
}
}
void ToolTipManager::clearContent(QGraphicsWidget *widget)
{
setContent(widget, ToolTipContent());
}
void ToolTipManager::setState(ToolTipManager::State state)
{
d->state = state;
switch (state) {
case Activated:
break;
case Deactivated:
d->clearTips();
//fallthrough
case Inhibited:
d->resetShownState();
break;
}
}
ToolTipManager::State ToolTipManager::state() const
{
return d->state;
}
void ToolTipManagerPrivate::createTipWidget()
{
if (tipWidget) {
return;
}
tipWidget = new ToolTip(0);
QObject::connect(tipWidget, SIGNAL(activateWindowByWId(WId,Qt::MouseButtons,Qt::KeyboardModifiers,QPoint)),
q, SIGNAL(windowPreviewActivated(WId,Qt::MouseButtons,Qt::KeyboardModifiers,QPoint)));
QObject::connect(tipWidget, SIGNAL(linkActivated(QString,Qt::MouseButtons,Qt::KeyboardModifiers,QPoint)),
q, SIGNAL(linkActivated(QString,Qt::MouseButtons,Qt::KeyboardModifiers,QPoint)));
QObject::connect(tipWidget, SIGNAL(hovered(bool)), q, SLOT(toolTipHovered(bool)));
}
void ToolTipManagerPrivate::hideTipWidget()
{
if (tipWidget) {
tipWidget->hide();
tipWidget->deleteLater();
tipWidget = 0;
}
}
void ToolTipManagerPrivate::onWidgetDestroyed(QObject *object)
{
if (!object) {
return;
}
// we do a static_cast here since it really isn't a QGraphicsWidget by this
// point anymore since we are in the QObject dtor. we don't actually
// try and do anything with it, we just need the value of the pointer
// so this unsafe looking code is actually just fine.
//
// NOTE: DO NOT USE THE w VARIABLE FOR ANYTHING OTHER THAN COMPARING
// THE ADDRESS! ACTUALLY USING THE OBJECT WILL RESULT IN A CRASH!!!
QGraphicsWidget *w = static_cast<QGraphicsWidget*>(object);
removeWidget(w, false);
}
void ToolTipManagerPrivate::removeWidget(QGraphicsWidget *w, bool canSafelyAccess)
{
if (currentWidget == w && currentWidget) {
currentWidget = 0;
showTimer->stop(); // stop the timer to show the tooltip
hideTipWidget();
delayedHide = false;
}
if (w && canSafelyAccess) {
QObject::disconnect(q, 0, w, 0);
}
tooltips.remove(w);
}
void ToolTipManagerPrivate::clearTips()
{
tooltips.clear();
}
void ToolTipManagerPrivate::resetShownState()
{
if (!tipWidget || !tipWidget->isVisible() || delayedHide) {
//One might have moused out and back in again
showTimer->stop();
delayedHide = false;
isShown = false;
currentWidget = 0;
hideTipWidget();
}
}
void ToolTipManagerPrivate::showToolTip()
{
if (state != ToolTipManager::Activated ||
!currentWidget ||
QApplication::activePopupWidget() ||
QApplication::activeModalWidget()) {
return;
}
PopupApplet *popup = qobject_cast<PopupApplet*>(currentWidget);
if (popup && popup->isPopupShowing()) {
return;
}
if (currentWidget->metaObject()->indexOfMethod("toolTipAboutToShow()") != -1) {
// toolTipAboutToShow may call into methods such as setContent which play
// with the current widget; so let's just pretend for a moment that we don't have
// a current widget
QGraphicsWidget *temp = currentWidget;
currentWidget = 0;
QMetaObject::invokeMethod(temp, "toolTipAboutToShow");
currentWidget = temp;
}
QHash<QGraphicsWidget *, ToolTipContent>::const_iterator tooltip = tooltips.constFind(currentWidget);
if (tooltip == tooltips.constEnd() || tooltip.value().isEmpty()) {
if (isShown) {
delayedHide = true;
hideTimer->start(250);
}
return;
}
createTipWidget();
Containment *c = dynamic_cast<Containment *>(currentWidget->topLevelItem());
//kDebug() << "about to show" << (QObject*)c;
if (c) {
tipWidget->setDirection(Plasma::locationToDirection(c->location()));
}
clickable = tooltip.value().isClickable();
tipWidget->setContent(currentWidget, tooltip.value());
tipWidget->prepareShowing();
QGraphicsWidget *referenceWidget = tooltip.value().graphicsWidget() ? tooltip.value().graphicsWidget() : currentWidget;
Corona *corona = qobject_cast<Corona *>(referenceWidget->scene());
if (corona) {
tipWidget->moveTo(corona->popupPosition(referenceWidget, tipWidget->size(), Qt::AlignCenter));
}
tipWidget->show();
isShown = true; //ToolTip is visible
delayedHide = tooltip.value().autohide();
if (delayedHide) {
//kDebug() << "starting authoide";
hideTimer->start(3000);
} else {
hideTimer->stop();
}
}
void ToolTipManagerPrivate::toolTipHovered(bool hovered)
{
if (!clickable) {
return;
}
if (hovered) {
hideTimer->stop();
} else {
hideTimer->start(500);
}
}
bool ToolTipManager::eventFilter(QObject *watched, QEvent *event)
{
QGraphicsWidget * widget = dynamic_cast<QGraphicsWidget *>(watched);
if (d->state != Activated || !widget) {
return QObject::eventFilter(watched, event);
}
switch (event->type()) {
case QEvent::GraphicsSceneHoverMove:
// If the tooltip isn't visible, run through showing the tooltip again
// so that it only becomes visible after a stationary hover
if (Plasma::ToolTipManager::self()->isVisible(widget)) {
break;
}
// Don't restart the show timer on a mouse move event if there hasn't
// been an enter event or the current widget has been cleared by a click
// or wheel event.
{
QGraphicsSceneHoverEvent *me = static_cast<QGraphicsSceneHoverEvent *>(event);
//FIXME: seems that wheel events generate hovermoves as well, with 0 delta
if (!d->currentWidget || (me->pos() == me->lastPos())) {
break;
}
}
case QEvent::GraphicsSceneHoverEnter:
{
// Check that there is a tooltip to show
if (!d->tooltips.contains(widget)) {
break;
}
show(widget);
break;
}
case QEvent::GraphicsSceneHoverLeave:
if (d->currentWidget == widget) {
d->doDelayedHide();
}
break;
case QEvent::GraphicsSceneMousePress:
if (d->currentWidget == widget) {
hide(widget);
}
break;
case QEvent::GraphicsSceneWheel:
default:
break;
}
return QObject::eventFilter(watched, event);
}
} // Plasma namespace
diff --git a/plasma/widgets/iconwidget.cpp b/plasma/widgets/iconwidget.cpp
index 4656add644..cb03821cfa 100644
--- a/plasma/widgets/iconwidget.cpp
+++ b/plasma/widgets/iconwidget.cpp
@@ -1,1619 +1,1625 @@
/*
* Copyright 2007 by Aaron Seigo <aseigo@kde.org>
* Copyright 2007 by Riccardo Iaconelli <riccardo@kde.org>
* Copyright 2007 by Matt Broadstone <mbroadst@gmail.com>
* Copyright 2006-2007 Fredrik Höglund <fredrik@kde.org>
* Copyright 2007 by Marco Martin <notmart@gmail.com>
* Copyright 2008 by Alexis Ménard <darktears31@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2, or
* (at your option) any later version.
*
* This program 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 General Public License for more details
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "iconwidget.h"
#include "iconwidget_p.h"
#include <QAction>
#include <QApplication>
#include <QGraphicsSceneMouseEvent>
#include <QGraphicsView>
#include <QMenu>
#include <QPainter>
#include <QStyleOptionGraphicsItem>
#include <QTextLayout>
#include <QTimer>
#include <kcolorscheme.h>
#include <kdebug.h>
#include <kglobalsettings.h>
#include <kicon.h>
#include <kiconeffect.h>
#include <kiconloader.h>
#include <kmimetype.h>
#include <kurl.h>
#include "animator.h"
#include "animations/animation.h"
#include "paintutils.h"
#include "private/themedwidgetinterface_p.h"
#include "theme.h"
#include "svg.h"
/*
TODO:
Add these to a UrlIcon class
void setUrl(const KUrl& url);
KUrl url() const;
*/
namespace Plasma
{
IconHoverAnimation::IconHoverAnimation(QObject *parent)
: QObject(parent), m_value(0), m_fadeIn(false)
{
}
qreal IconHoverAnimation::value() const
{
return m_value;
}
bool IconHoverAnimation::fadeIn() const
{
return m_fadeIn;
}
QPropertyAnimation *IconHoverAnimation::animation() const
{
return m_animation.data();
}
void IconHoverAnimation::setValue(qreal value)
{
m_value = value;
QGraphicsWidget *item = static_cast<QGraphicsWidget*>(parent());
item->update();
}
void IconHoverAnimation::setFadeIn(bool fadeIn)
{
m_fadeIn = fadeIn;
}
void IconHoverAnimation::setAnimation(QPropertyAnimation *animation)
{
m_animation = animation;
}
IconWidgetPrivate::IconWidgetPrivate(IconWidget *i)
: ActionWidgetInterface<IconWidget>(i),
iconSvg(0),
hoverAnimation(new IconHoverAnimation(q)),
iconSize(48, 48),
preferredIconSize(-1, -1),
minimumIconSize(-1, -1),
maximumIconSize(-1, -1),
states(IconWidgetPrivate::NoState),
orientation(Qt::Vertical),
numDisplayLines(2),
activeMargins(0),
iconSvgElementChanged(false),
invertLayout(false),
drawBg(false),
textBgCustomized(false)
{
}
IconWidgetPrivate::~IconWidgetPrivate()
{
qDeleteAll(cornerActions);
delete hoverAnimation;
}
void IconWidgetPrivate::readColors()
{
textColor = Plasma::Theme::defaultTheme()->color(Theme::TextColor);
if (qGray(textColor.rgb()) > 192) {
shadowColor = Qt::black;
} else {
shadowColor = Qt::white;
}
if (!textBgCustomized) {
textBgColor = QColor();
}
}
void IconWidgetPrivate::colorConfigChanged()
{
readColors();
if (drawBg) {
qreal left, top, right, bottom;
background->getMargins(left, top, right, bottom);
setVerticalMargin(IconWidgetPrivate::ItemMargin, left, top, right, bottom);
setHorizontalMargin(IconWidgetPrivate::ItemMargin, left, top, right, bottom);
}
q->update();
}
void IconWidgetPrivate::iconConfigChanged()
{
if (!icon.isNull()) {
q->update();
}
}
IconAction::IconAction(IconWidget *icon, QAction *action)
: m_icon(icon),
m_action(action),
m_hovered(false),
m_pressed(false),
m_selected(false),
m_visible(false)
{
}
void IconAction::show()
{
rebuildPixmap();
m_visible = true;
/*
FIXME: place this animation back when the icon widget is in the qgv library
Animation *animation = m_animation.data();
if (!animation) {
animation = Plasma::Animator::create(Plasma::Animator::PixmapTransitionAnimation, m_icon);
animation->setTargetWidget(m_icon);
m_animation = animation;
} else if (animation->state() == QAbstractAnimation::Running) {
animation->pause();
}
animation->setProperty("targetPixmap", m_pixmap);
animation->setDirection(QAbstractAnimation::Forward);
animation->start();
*/
}
void IconAction::hide()
{
if (!m_animation) {
return;
}
Animation *animation = m_animation.data();
if (animation->state() == QAbstractAnimation::Running) {
animation->pause();
}
m_visible = false;
animation->setDirection(QAbstractAnimation::Backward);
animation->start(QAbstractAnimation::DeleteWhenStopped);
}
bool IconAction::isVisible() const
{
return m_visible;
}
bool IconAction::isAnimating() const
{
return !m_animation.isNull();
}
bool IconAction::isPressed() const
{
return m_pressed;
}
bool IconAction::isHovered() const
{
return m_hovered;
}
void IconAction::setSelected(bool selected)
{
m_selected = selected;
}
bool IconAction::isSelected() const
{
return m_selected;
}
void IconAction::setRect(const QRectF &rect)
{
m_rect = rect;
}
QRectF IconAction::rect() const
{
return m_rect;
}
void IconAction::rebuildPixmap()
{
// Determine proper QIcon mode based on selection status
QIcon::Mode mode = m_selected ? QIcon::Selected : QIcon::Normal;
// Draw everything
m_pixmap = QPixmap(26, 26);
m_pixmap.fill(Qt::transparent);
int element = IconWidgetPrivate::Minibutton;
if (m_pressed) {
element = IconWidgetPrivate::MinibuttonPressed;
} else if (m_hovered) {
element = IconWidgetPrivate::MinibuttonHover;
}
QPainter painter(&m_pixmap);
m_icon->drawActionButtonBase(&painter, m_pixmap.size(), element);
m_action->icon().paint(&painter, 2, 2, 22, 22, Qt::AlignCenter, mode);
}
bool IconAction::event(QEvent::Type type, const QPointF &pos)
{
if (!m_action->isVisible() || !m_action->isEnabled()) {
return false;
}
if (m_icon->size().width() < m_rect.width() * 2.0 ||
m_icon->size().height() < m_rect.height() * 2.0) {
return false;
}
switch (type) {
case QEvent::GraphicsSceneMousePress:
{
setSelected(m_rect.contains(pos));
return isSelected();
}
break;
case QEvent::GraphicsSceneMouseMove:
{
bool wasSelected = isSelected();
bool active = m_rect.contains(pos);
setSelected(wasSelected && active);
return (wasSelected != isSelected()) || active;
}
break;
case QEvent::GraphicsSceneMouseRelease:
{
// kDebug() << "IconAction::event got a QEvent::MouseButtonRelease, " << isSelected();
bool wasSelected = isSelected();
setSelected(false);
if (wasSelected) {
m_action->trigger();
}
return wasSelected;
}
break;
case QEvent::GraphicsSceneHoverEnter:
m_pressed = false;
m_hovered = true;
break;
case QEvent::GraphicsSceneHoverLeave:
m_pressed = false;
m_hovered = false;
break;
default:
break;
}
return false;
}
QAction *IconAction::action() const
{
return m_action;
}
void IconAction::paint(QPainter *painter) const
{
if (!m_action->isVisible() || !m_action->isEnabled()) {
return;
}
if (m_icon->size().width() < m_rect.width() * 2.0 ||
m_icon->size().height() < m_rect.height() * 2.0) {
return;
}
Animation *animation = m_animation.data();
if (m_visible && !animation) {
painter->drawPixmap(m_rect.toRect(), m_pixmap);
} else if (animation) {
painter->drawPixmap(m_rect.toRect(),
animation->property("currentPixmap").value<QPixmap>());
}
}
IconWidget::IconWidget(QGraphicsItem *parent)
: QGraphicsWidget(parent),
d(new IconWidgetPrivate(this))
{
d->init();
}
IconWidget::IconWidget(const QString &text, QGraphicsItem *parent)
: QGraphicsWidget(parent),
d(new IconWidgetPrivate(this))
{
d->init();
setText(text);
}
IconWidget::IconWidget(const QIcon &icon, const QString &text, QGraphicsItem *parent)
: QGraphicsWidget(parent),
d(new IconWidgetPrivate(this))
{
d->init();
setText(text);
setIcon(icon);
}
IconWidget::~IconWidget()
{
delete d;
}
void IconWidgetPrivate::init()
{
readColors();
iconChangeTimer = new QTimer(q);
iconChangeTimer->setSingleShot(true);
QObject::connect(Plasma::Theme::defaultTheme(), SIGNAL(themeChanged()), q, SLOT(colorConfigChanged()));
QObject::connect(KGlobalSettings::self(), SIGNAL(kdisplayPaletteChanged()), q, SLOT(colorConfigChanged()));
QObject::connect(KGlobalSettings::self(), SIGNAL(iconChanged(int)), q, SLOT(iconConfigChanged()));
// setAcceptedMouseButtons(Qt::LeftButton);
q->setAcceptsHoverEvents(true);
q->setCacheMode(QGraphicsItem::DeviceCoordinateCache);
background = new Plasma::FrameSvg(q);
background->setImagePath("widgets/viewitem");
background->setCacheAllRenderedFrames(true);
background->setElementPrefix("hover");
// Margins for horizontal mode (list views, tree views, table views)
setHorizontalMargin(IconWidgetPrivate::TextMargin, 1, 1);
setHorizontalMargin(IconWidgetPrivate::IconMargin, 1, 1);
setHorizontalMargin(IconWidgetPrivate::ItemMargin, 0, 0);
// Margins for vertical mode (icon views)
setVerticalMargin(IconWidgetPrivate::TextMargin, 6, 2);
setVerticalMargin(IconWidgetPrivate::IconMargin, 1, 1);
setVerticalMargin(IconWidgetPrivate::ItemMargin, 0, 0);
setActiveMargins();
currentSize = QSizeF(-1, -1);
initTheming();
}
void IconWidget::addIconAction(QAction *action)
{
int count = d->cornerActions.count();
if (count >= IconWidgetPrivate::LastIconPosition) {
#ifndef NDEBUG
kDebug() << "no more room for more actions!";
#endif
// just overlap it with the last item for now. ugly, but there you go.
}
IconAction *iconAction = new IconAction(this, action);
d->cornerActions.append(iconAction);
connect(action, SIGNAL(destroyed(QObject*)), this, SLOT(actionDestroyed(QObject*)));
iconAction->setRect(d->actionRect(qMin((IconWidgetPrivate::ActionPosition)count, IconWidgetPrivate::LastIconPosition)));
}
void IconWidget::removeIconAction(QAction *action)
{
//WARNING: do NOT access the action pointer passed in, as it may already be
//be destroyed. see IconWidgetPrivate::actionDestroyed(QObject*)
int count = 0;
bool found = false;
foreach (IconAction *iconAction, d->cornerActions) {
if (found) {
iconAction->setRect(d->actionRect((IconWidgetPrivate::ActionPosition)count));
} else if (!action || iconAction->action() == action) {
delete iconAction;
d->cornerActions.removeAll(iconAction);
}
if (count < IconWidgetPrivate::LastIconPosition) {
++count;
}
}
// redraw since an action has been deleted.
update();
}
void IconWidgetPrivate::actionDestroyed(QObject *action)
{
q->removeIconAction(static_cast<QAction*>(action));
}
void IconWidget::setAction(QAction *action)
{
d->setAction(action);
}
QAction *IconWidget::action() const
{
return d->action;
}
int IconWidget::numDisplayLines()
{
return d->numDisplayLines;
}
void IconWidget::setNumDisplayLines(int numLines)
{
if (numLines > d->maxDisplayLines) {
d->numDisplayLines = d->maxDisplayLines;
} else {
d->numDisplayLines = numLines;
}
}
void IconWidget::setDrawBackground(bool draw)
{
if (d->drawBg != draw) {
d->drawBg = draw;
QStyle *style = QApplication::style();
int focusHMargin = draw ? style->pixelMetric(QStyle::PM_FocusFrameHMargin) : 1;
int focusVMargin = draw ? style->pixelMetric(QStyle::PM_FocusFrameVMargin) : 1;
d->setHorizontalMargin(IconWidgetPrivate::TextMargin, focusHMargin, focusVMargin);
d->setHorizontalMargin(IconWidgetPrivate::IconMargin, focusHMargin, focusVMargin);
d->setVerticalMargin(IconWidgetPrivate::IconMargin, focusHMargin, focusVMargin);
d->currentSize = QSizeF(-1, -1);
if (draw) {
qreal left, top, right, bottom;
d->background->getMargins(left, top, right, bottom);
d->setHorizontalMargin(IconWidgetPrivate::ItemMargin, left, top, right, bottom);
d->setVerticalMargin(IconWidgetPrivate::ItemMargin, left, top, right, bottom);
} else {
d->setHorizontalMargin(IconWidgetPrivate::ItemMargin, 0, 0);
d->setVerticalMargin(IconWidgetPrivate::ItemMargin, 0, 0);
}
update();
updateGeometry();
}
}
bool IconWidget::drawBackground() const
{
return d->drawBg;
}
QPainterPath IconWidget::shape() const
{
if (!d->drawBg || d->currentSize.width() < 1) {
return QGraphicsItem::shape();
}
return PaintUtils::roundedRectangle(
QRectF(QPointF(0.0, 0.0), d->currentSize).adjusted(-2, -2, 2, 2), 10.0);
}
QSizeF IconWidgetPrivate::displaySizeHint(const QStyleOptionGraphicsItem *option, const qreal width) const
{
if (text.isEmpty() && infoText.isEmpty()) {
return QSizeF(.0, .0);
}
QString label = text;
// const qreal maxWidth = (orientation == Qt::Vertical) ? iconSize.width() + 10 : 32757;
// NOTE: find a way to use the other layoutText, it currently returns nominal width, when
// we actually need the actual width.
qreal textWidth = width -
horizontalMargin[IconWidgetPrivate::TextMargin].left -
horizontalMargin[IconWidgetPrivate::TextMargin].right;
//allow only five lines of text
const qreal maxHeight =
numDisplayLines * QFontMetrics(q->font()).lineSpacing();
// To compute the nominal size for the label + info, we'll just append
// the information string to the label
if (!infoText.isEmpty()) {
label += QString(QChar::LineSeparator) + infoText;
}
QTextLayout layout;
setLayoutOptions(layout, option, q->orientation());
layout.setFont(q->font());
QSizeF size = layoutText(layout, label, QSizeF(textWidth, maxHeight));
return addMargin(size, TextMargin);
}
void IconWidgetPrivate::layoutIcons(const QStyleOptionGraphicsItem *option)
{
if (option->rect.size() == currentSize) {
return;
}
currentSize = option->rect.size();
iconSize = iconSizeForWidgetSize(option, currentSize);
int count = 0;
foreach (IconAction *iconAction, cornerActions) {
iconAction->setRect(actionRect((IconWidgetPrivate::ActionPosition)count));
++count;
}
}
QSizeF IconWidgetPrivate::iconSizeForWidgetSize(const QStyleOptionGraphicsItem *option, const QSizeF &rect)
{
setActiveMargins();
//calculate icon size based on the available space
qreal iconWidth;
if (orientation == Qt::Vertical) {
qreal heightAvail;
//if there is text resize the icon in order to make room for the text
if (text.isEmpty() && infoText.isEmpty()) {
heightAvail = rect.height();
} else {
heightAvail = rect.height() -
displaySizeHint(option, rect.width()).height() -
verticalMargin[IconWidgetPrivate::TextMargin].top -
verticalMargin[IconWidgetPrivate::TextMargin].bottom;
//never make a label higher than half the total height
heightAvail = qMax(heightAvail, rect.height() / 2);
}
//aspect ratio very "tall"
if (!text.isEmpty() || !infoText.isEmpty()) {
if (rect.width() < heightAvail) {
iconWidth = rect.width() -
verticalMargin[IconWidgetPrivate::IconMargin].left -
verticalMargin[IconWidgetPrivate::IconMargin].right;
} else {
iconWidth = heightAvail -
verticalMargin[IconWidgetPrivate::IconMargin].top -
verticalMargin[IconWidgetPrivate::IconMargin].bottom;
}
} else {
iconWidth = qMin(heightAvail, rect.width());
}
iconWidth -= verticalMargin[IconWidgetPrivate::ItemMargin].left + verticalMargin[IconWidgetPrivate::ItemMargin].right;
} else {
//Horizontal layout
//if there is text resize the icon in order to make room for the text
if (text.isEmpty() && infoText.isEmpty()) {
// with no text, we just take up the whole geometry
iconWidth = qMin(rect.height(), rect.width());
} else {
iconWidth = rect.height() -
horizontalMargin[IconWidgetPrivate::IconMargin].top -
horizontalMargin[IconWidgetPrivate::IconMargin].bottom;
}
iconWidth -= horizontalMargin[IconWidgetPrivate::ItemMargin].top + horizontalMargin[IconWidgetPrivate::ItemMargin].bottom;
}
QSizeF iconRect(iconWidth, iconWidth);
if (maximumIconSize.isValid()) {
iconRect = iconRect.boundedTo(maximumIconSize);
}
return iconRect;
}
void IconWidget::setSvg(const QString &svgFilePath, const QString &elementId)
{
if (svgFilePath.isEmpty()) {
if (d->iconSvg) {
d->iconSvg->deleteLater();
d->iconSvg = 0;
}
return;
}
if (!d->iconSvg) {
d->iconSvg = new Plasma::Svg(this);
connect(d->iconSvg, SIGNAL(repaintNeeded()), this, SLOT(svgChanged()));
d->oldIcon = d->icon;
} else {
d->oldIcon = d->iconSvg->pixmap(d->iconSvgElement);
}
d->iconSvg->setImagePath(svgFilePath);
d->iconSvg->setContainsMultipleImages(!elementId.isNull());
d->iconSvgElement = elementId;
d->iconSvgElementChanged = true;
updateGeometry();
if (!(d->states & IconWidgetPrivate::HoverState) && !d->iconChangeTimer->isActive() && !d->oldIcon.isNull()) {
d->animateMainIcon(true, d->states);
} else {
d->oldIcon = QIcon();
update();
}
d->iconChangeTimer->start(300);
d->icon = QIcon();
}
QString IconWidget::svg() const
{
if (d->iconSvg) {
if (d->iconSvg->isValid() && (d->iconSvgElement.isEmpty() || d->iconSvg->hasElement(d->iconSvgElement))) {
return d->iconSvg->imagePath();
} else {
return QString();
}
}
return QString();
}
QSizeF IconWidget::sizeHint(Qt::SizeHint which, const QSizeF & constraint) const
{
if (which == Qt::PreferredSize) {
int iconSize;
if (d->preferredIconSize.isValid()) {
iconSize = qMax(d->preferredIconSize.height(), d->preferredIconSize.width());
} else if (d->iconSvg) {
QSizeF oldSize = d->iconSvg->size();
d->iconSvg->resize();
if (d->iconSvgElement.isNull()) {
iconSize = qMax(d->iconSvg->size().width(), d->iconSvg->size().height());
} else {
iconSize = qMax(d->iconSvg->elementSize(d->iconSvgElement).width(), d->iconSvg->elementSize(d->iconSvgElement).height());
}
d->iconSvg->resize(oldSize);
} else {
iconSize = KIconLoader::SizeMedium;
}
if (constraint.width() > 0 || constraint.height() > 0) {
QSizeF constrainedWidgetSize(constraint);
QSizeF maximumWidgetSize;
if (d->maximumIconSize.isValid()) {
maximumWidgetSize =
sizeFromIconSize(qMax(d->maximumIconSize.height(), d->maximumIconSize.width()));
} else {
maximumWidgetSize =
QGraphicsWidget::sizeHint(Qt::MaximumSize);
}
if (constrainedWidgetSize.width() <= 0) {
constrainedWidgetSize.setWidth(maximumWidgetSize.width());
}
if (constrainedWidgetSize.height() <= 0) {
constrainedWidgetSize.setHeight(maximumWidgetSize.height());
}
QStyleOptionGraphicsItem option;
QSizeF iconRect =
d->iconSizeForWidgetSize(&option, constrainedWidgetSize);
iconSize =
qMin(iconSize, qMax<int>(iconRect.width(), iconRect.height()));
}
return sizeFromIconSize(iconSize);
} else if (which == Qt::MinimumSize) {
if (d->minimumIconSize.isValid()) {
return sizeFromIconSize(qMax(d->minimumIconSize.height(), d->minimumIconSize.width()));
}
return sizeFromIconSize(KIconLoader::SizeSmall);
} else {
if (d->maximumIconSize.isValid()) {
return sizeFromIconSize(qMax(d->maximumIconSize.height(), d->maximumIconSize.width()));
}
return QGraphicsWidget::sizeHint(which, constraint);
}
}
void IconWidgetPrivate::animateMainIcon(bool show, const IconWidgetStates state)
{
if (show) {
states = state;
}
hoverAnimation->setFadeIn(show);
QPropertyAnimation *animation = hoverAnimation->animation();
if (!animation) {
animation = new QPropertyAnimation(hoverAnimation, "value");
animation->setDuration(150);
animation->setEasingCurve(QEasingCurve::OutQuad);
animation->setStartValue(0.0);
animation->setEndValue(1.0);
hoverAnimation->setAnimation(animation);
q->connect(animation, SIGNAL(finished()), q, SLOT(hoverAnimationFinished()));
} else if (animation->state() == QAbstractAnimation::Running) {
animation->pause();
}
animation->setDirection(show ? QAbstractAnimation::Forward : QAbstractAnimation::Backward);
animation->start(show ? QAbstractAnimation::KeepWhenStopped : QAbstractAnimation::DeleteWhenStopped);
q->update();
}
void IconWidgetPrivate::hoverAnimationFinished()
{
if (!hoverAnimation->fadeIn()) {
states &= ~IconWidgetPrivate::HoverState;
}
}
void IconWidgetPrivate::drawBackground(QPainter *painter, IconWidgetState state)
{
if (!drawBg) {
return;
}
if (!(states & IconWidgetPrivate::HoverState) && !(states & IconWidgetPrivate::PressedState)) {
return;
}
if (state == IconWidgetPrivate::PressedState) {
background->setElementPrefix("selected");
} else {
background->setElementPrefix("hover");
}
if (qFuzzyCompare(hoverAnimation->value(), 1)) {
background->resizeFrame(currentSize);
background->paintFrame(painter);
} else if (!qFuzzyCompare(hoverAnimation->value()+1, 1)) {
background->resizeFrame(currentSize);
QPixmap frame = background->framePixmap();
QPainter bufferPainter(&frame);
bufferPainter.setCompositionMode(QPainter::CompositionMode_DestinationIn);
bufferPainter.fillRect(frame.rect(), QColor(0,0,0, 255*hoverAnimation->value()));
bufferPainter.end();
painter->drawPixmap(QPoint(0,0), frame);
}
}
QPixmap IconWidgetPrivate::decoration(const QStyleOptionGraphicsItem *option, bool useHoverEffect, bool usePressedEffect)
{
QPixmap result;
QIcon::Mode mode = option->state & QStyle::State_Enabled ? QIcon::Normal : QIcon::Disabled;
QIcon::State state = option->state & QStyle::State_Open ? QIcon::On : QIcon::Off;
if (iconSvg) {
if (iconSvgElementChanged || iconSvgPixmap.size() != iconSize.toSize()) {
iconSvg->resize(iconSize);
iconSvgPixmap = iconSvg->pixmap(iconSvgElement);
iconSvgElementChanged = false;
}
result = iconSvgPixmap;
} else {
const QSize size = icon.actualSize(iconSize.toSize(), mode, state);
result = icon.pixmap(size, mode, state);
}
if (usePressedEffect) {
result = result.scaled(result.size() * 0.9, Qt::KeepAspectRatio, Qt::SmoothTransformation);
}
if (!result.isNull() && useHoverEffect) {
KIconEffect *effect = KIconLoader::global()->iconEffect();
// Note that in KIconLoader terminology, active = hover.
// We're assuming that the icon group is desktop/filemanager, since this
// is KFileItemDelegate.
if (effect->hasEffect(KIconLoader::Desktop, KIconLoader::ActiveState)) {
if (qFuzzyCompare(qreal(1.0), hoverAnimation->value())) {
result = effect->apply(result, KIconLoader::Desktop, KIconLoader::ActiveState);
} else {
result = PaintUtils::transition(
result,
effect->apply(result, KIconLoader::Desktop,
KIconLoader::ActiveState), hoverAnimation->value());
}
}
} else if (!result.isNull() && !oldIcon.isNull()) {
if (qFuzzyCompare(qreal(1.0), hoverAnimation->value())) {
oldIcon = QIcon();
} else {
result = PaintUtils::transition(
oldIcon.pixmap(result.size(), mode, state),
result, hoverAnimation->value());
}
}
return result;
}
QPointF IconWidgetPrivate::iconPosition(const QStyleOptionGraphicsItem *option,
const QPixmap &pixmap) const
{
const QRectF itemRect = subtractMargin(option->rect, IconWidgetPrivate::ItemMargin);
// Compute the nominal decoration rectangle
const QSizeF size = addMargin(iconSize, IconWidgetPrivate::IconMargin);
Qt::LayoutDirection direction = iconDirection(option);
//alignment depends from orientation and option->direction
Qt::Alignment alignment;
if (text.isEmpty() && infoText.isEmpty()) {
alignment = Qt::AlignCenter;
} else if (orientation == Qt::Vertical) {
alignment = Qt::Alignment(Qt::AlignHCenter | Qt::AlignTop);
//Horizontal
} else {
alignment = QStyle::visualAlignment(
direction, Qt::Alignment(Qt::AlignLeft | Qt::AlignVCenter));
}
const QRect iconRect =
QStyle::alignedRect(direction, alignment, size.toSize(), itemRect.toRect());
// Position the pixmap in the center of the rectangle
QRect pixmapRect = pixmap.rect();
pixmapRect.moveCenter(iconRect.center());
// add a gimmicky margin of 5px to y, TEMP TEMP TEMP
// pixmapRect = pixmapRect.adjusted(0, 5, 0, 0);
return QPointF(pixmapRect.topLeft());
}
QRectF IconWidgetPrivate::labelRectangle(const QStyleOptionGraphicsItem *option,
const QPixmap &icon,
const QString &string) const
{
Q_UNUSED(string)
if (icon.isNull()) {
return option->rect;
}
const QSizeF decoSize = addMargin(iconSize, IconWidgetPrivate::IconMargin);
const QRectF itemRect = subtractMargin(option->rect, IconWidgetPrivate::ItemMargin);
QRectF textArea(QPointF(0, 0), itemRect.size());
if (orientation == Qt::Vertical) {
textArea.setTop(decoSize.height() + 1);
} else {
//Horizontal
textArea.setLeft(decoSize.width() + 1);
}
textArea.translate(itemRect.topLeft());
return QRectF(QStyle::visualRect(iconDirection(option), option->rect, textArea.toRect()));
}
// Lays the text out in a rectangle no larger than constraints, eliding it as necessary
QSizeF IconWidgetPrivate::layoutText(QTextLayout &layout,
const QString &text,
const QSizeF &constraints) const
{
const QSizeF size = layoutText(layout, text, constraints.width());
if (size.width() > constraints.width() || size.height() > constraints.height()) {
if (action) {
q->setToolTip(action->toolTip());
}
const QString elided = elidedText(layout, constraints);
return layoutText(layout, elided, constraints.width());
}
q->setToolTip(QString());
return size;
}
// Lays the text out in a rectangle no wider than maxWidth
QSizeF IconWidgetPrivate::layoutText(QTextLayout &layout, const QString &text, qreal maxWidth) const
{
QFontMetricsF metrics(layout.font());
qreal leading = metrics.leading();
qreal height = 0.0;
qreal widthUsed = 0.0;
QTextLine line;
layout.setText(text);
layout.beginLayout();
while ((line = layout.createLine()).isValid()) {
line.setLineWidth(maxWidth);
height += leading;
line.setPosition(QPointF(0.0, height));
height += line.height();
widthUsed = qMax(widthUsed, line.naturalTextWidth());
}
layout.endLayout();
return QSizeF(widthUsed, height);
}
// Elides the text in the layout, by iterating over each line in the layout, eliding
// or word breaking the line if it's wider than the max width, and finally adding an
// ellipses at the end of the last line, if there are more lines than will fit within
// the vertical size constraints.
QString IconWidgetPrivate::elidedText(QTextLayout &layout, const QSizeF &size) const
{
QFontMetricsF metrics(layout.font());
const QString text = layout.text();
qreal maxWidth = size.width();
qreal maxHeight = size.height();
qreal height = 0;
// Elide each line that has already been laid out in the layout.
QString elided;
elided.reserve(text.length());
for (int i = 0; i < layout.lineCount(); i++) {
QTextLine line = layout.lineAt(i);
int start = line.textStart();
int length = line.textLength();
height += metrics.leading();
if (height + line.height() + metrics.lineSpacing() > maxHeight) {
// Unfortunately, if the line ends because of a line separator,
// elidedText() will be too clever and keep adding lines until
// it finds one that's too wide.
if (line.naturalTextWidth() < maxWidth &&
start + length > 0 &&
text[start + length - 1] == QChar::LineSeparator) {
elided += text.mid(start, length - 1);
} else {
elided += metrics.elidedText(text.mid(start), Qt::ElideRight, maxWidth);
}
break;
} else if (line.naturalTextWidth() > maxWidth) {
elided += metrics.elidedText(text.mid(start, length), Qt::ElideRight, maxWidth);
} else {
elided += text.mid(start, length);
}
height += line.height();
}
return elided;
}
void IconWidgetPrivate::layoutTextItems(const QStyleOptionGraphicsItem *option,
const QPixmap &icon, QTextLayout *labelLayout,
QTextLayout *infoLayout, QRectF *textBoundingRect) const
{
bool showInformation = false;
setLayoutOptions(*labelLayout, option, q->orientation());
QFontMetricsF fm(labelLayout->font());
const QRectF textArea = labelRectangle(option, icon, text);
QRectF textRect = subtractMargin(textArea, IconWidgetPrivate::TextMargin);
//kDebug() << this << "text area" << textArea << "text rect" << textRect;
// Sizes and constraints for the different text parts
QSizeF maxLabelSize = textRect.size();
QSizeF maxInfoSize = textRect.size();
QSizeF labelSize;
QSizeF infoSize;
// If we have additional info text, and there's space for at least two lines of text,
// adjust the max label size to make room for at least one line of the info text
if (!infoText.isEmpty() && textRect.height() >= fm.lineSpacing() * 2) {
infoLayout->setFont(labelLayout->font());
infoLayout->setTextOption(labelLayout->textOption());
maxLabelSize.rheight() -= fm.lineSpacing();
showInformation = true;
}
// Lay out the label text, and adjust the max info size based on the label size
labelSize = layoutText(*labelLayout, text, maxLabelSize);
maxInfoSize.rheight() -= labelSize.height();
// Lay out the info text
if (showInformation) {
infoSize = layoutText(*infoLayout, infoText, maxInfoSize);
} else {
infoSize = QSizeF(0, 0);
}
// Compute the bounding rect of the text
const Qt::Alignment alignment = labelLayout->textOption().alignment();
const QSizeF size(qMax(labelSize.width(), infoSize.width()),
labelSize.height() + infoSize.height());
*textBoundingRect =
QStyle::alignedRect(iconDirection(option), alignment, size.toSize(), textRect.toRect());
// Compute the positions where we should draw the layouts
haloRects.clear();
labelLayout->setPosition(QPointF(textRect.x(), textBoundingRect->y()));
QTextLine line;
for (int i = 0; i < labelLayout->lineCount(); ++i) {
line = labelLayout->lineAt(i);
haloRects.append(line.naturalTextRect().translated(labelLayout->position().toPoint()).toRect());
}
infoLayout->setPosition(QPointF(textRect.x(), textBoundingRect->y() + labelSize.height()));
for (int i = 0; i < infoLayout->lineCount(); ++i) {
line = infoLayout->lineAt(i);
haloRects.append(line.naturalTextRect().translated(infoLayout->position().toPoint()).toRect());
}
//kDebug() << "final position is" << labelLayout->position();
}
QBrush IconWidgetPrivate::foregroundBrush(const QStyleOptionGraphicsItem *option) const
{
const QPalette::ColorGroup group = option->state & QStyle::State_Enabled ?
QPalette::Normal : QPalette::Disabled;
// Always use the highlight color for selected items
if (option->state & QStyle::State_Selected) {
return option->palette.brush(group, QPalette::HighlightedText);
}
return option->palette.brush(group, QPalette::Text);
}
QBrush IconWidgetPrivate::backgroundBrush(const QStyleOptionGraphicsItem *option) const
{
const QPalette::ColorGroup group = option->state & QStyle::State_Enabled ?
QPalette::Normal : QPalette::Disabled;
QBrush background(Qt::NoBrush);
// Always use the highlight color for selected items
if (option->state & QStyle::State_Selected) {
background = option->palette.brush(group, QPalette::Highlight);
}
return background;
}
void IconWidgetPrivate::drawTextItems(QPainter *painter,
const QStyleOptionGraphicsItem *option,
const QTextLayout &labelLayout,
const QTextLayout &infoLayout) const
{
Q_UNUSED(option)
painter->save();
painter->setPen(textColor);
// the translation prevents odd rounding errors in labelLayout.position()
// when applied to the canvas
painter->translate(0.5, 0.5);
labelLayout.draw(painter, QPointF());
if (!infoLayout.text().isEmpty()) {
painter->setPen(textColor);
infoLayout.draw(painter, QPointF());
}
painter->restore();
}
void IconWidget::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
Q_UNUSED(widget);
//Lay out the main icon and action icons
d->layoutIcons(option);
// Compute the metrics, and lay out the text items
// ========================================================================
IconWidgetPrivate::IconWidgetState state = IconWidgetPrivate::NoState;
if (d->states & IconWidgetPrivate::ManualPressedState) {
state = IconWidgetPrivate::PressedState;
} else if (d->states & IconWidgetPrivate::PressedState) {
if (d->states & IconWidgetPrivate::HoverState) {
state = IconWidgetPrivate::PressedState;
}
} else if (d->states & IconWidgetPrivate::HoverState) {
state = IconWidgetPrivate::HoverState;
}
QPixmap icon = d->decoration(option, state != IconWidgetPrivate::NoState, state & IconWidgetPrivate::PressedState);
const QPointF iconPos = d->iconPosition(option, icon);
d->drawBackground(painter, state);
// draw icon
if (!icon.isNull()) {
painter->drawPixmap(iconPos, icon);
}
// Draw corner actions
foreach (const IconAction *action, d->cornerActions) {
if (action->isAnimating()) {
action->paint(painter);
}
}
// Draw text last because it is overlayed
QTextLayout labelLayout, infoLayout;
QRectF textBoundingRect;
d->layoutTextItems(option, icon, &labelLayout, &infoLayout, &textBoundingRect);
if (d->textBgColor != QColor() && d->textBgColor.alpha() > 0 &&
!(d->text.isEmpty() && d->infoText.isEmpty()) &&
!textBoundingRect.isEmpty() &&
!qFuzzyCompare(d->hoverAnimation->value(), (qreal)1.0)) {
QRectF rect = textBoundingRect.adjusted(-2, -2, 4, 4).toAlignedRect();
painter->setPen(Qt::transparent);
QColor color = d->textBgColor;
color.setAlpha(60 * (1.0 - d->hoverAnimation->value()));
QLinearGradient gradient(rect.topLeft(), rect.bottomLeft());
gradient.setColorAt(0, color.lighter(120));
gradient.setColorAt(1, color.darker(120));
painter->setBrush(gradient);
gradient.setColorAt(0, color.lighter(130));
gradient.setColorAt(1, color.darker(130));
painter->setPen(QPen(gradient, 0));
painter->setRenderHint(QPainter::Antialiasing);
painter->drawPath(PaintUtils::roundedRectangle(rect.translated(0.5, 0.5), 4));
}
if (d->shadowColor.value() < 128 || textBackgroundColor() != QColor()) {
QPoint shadowPos;
if (d->shadowColor.value() < 128) {
shadowPos = QPoint(1, 2);
} else {
shadowPos = QPoint(0, 0);
}
QImage shadow(textBoundingRect.size().toSize() + QSize(4, 4),
QImage::Format_ARGB32_Premultiplied);
shadow.fill(Qt::transparent);
{
QPainter buffPainter(&shadow);
buffPainter.translate(-textBoundingRect.x(), -textBoundingRect.y());
d->drawTextItems(&buffPainter, option, labelLayout, infoLayout);
}
PaintUtils::shadowBlur(shadow, 2, d->shadowColor);
painter->drawImage(textBoundingRect.topLeft() + shadowPos, shadow);
} else if (!(d->text.isEmpty() && d->infoText.isEmpty()) &&
!textBoundingRect.isEmpty()) {
QRect labelRect = d->labelRectangle(option, icon, d->text).toRect();
foreach (const QRect &rect, d->haloRects) {
Plasma::PaintUtils::drawHalo(painter, rect);
}
}
d->drawTextItems(painter, option, labelLayout, infoLayout);
}
void IconWidget::setTextBackgroundColor(const QColor &color)
{
d->textBgCustomized = true;
d->textBgColor = color;
update();
}
QColor IconWidget::textBackgroundColor() const
{
return d->textBgColor;
}
void IconWidget::drawActionButtonBase(QPainter *painter, const QSize &size, int element)
{
qreal radius = size.width() / 2;
QRadialGradient gradient(radius, radius, radius, radius, radius);
int alpha;
if (element == IconWidgetPrivate::MinibuttonPressed) {
alpha = 255;
} else if (element == IconWidgetPrivate::MinibuttonHover) {
alpha = 200;
} else {
alpha = 160;
}
gradient.setColorAt(0, QColor::fromRgb(d->textColor.red(),
d->textColor.green(),
d->textColor.blue(), alpha));
gradient.setColorAt(1, QColor::fromRgb(d->textColor.red(),
d->textColor.green(),
d->textColor.blue(), 0));
painter->setBrush(gradient);
painter->setPen(Qt::NoPen);
painter->drawEllipse(QRectF(QPointF(.0, .0), size));
}
void IconWidget::setText(const QString &text)
{
d->text = KGlobal::locale()->removeAcceleratorMarker(text);
// cause a relayout
d->currentSize = QSizeF(-1, -1);
//try to relayout, needed if an icon was never shown before
if (!isVisible()) {
QStyleOptionGraphicsItem styleoption;
d->layoutIcons(&styleoption);
}
updateGeometry();
if (!parentWidget() || !parentWidget()->layout()) {
resize(preferredSize());
}
}
QString IconWidget::text() const
{
return d->text;
}
void IconWidget::setInfoText(const QString &text)
{
d->infoText = text;
// cause a relayout
d->currentSize = QSizeF(-1, -1);
//try to relayout, needed if an icon was never shown before
if (!isVisible()) {
QStyleOptionGraphicsItem styleoption;
d->layoutIcons(&styleoption);
}
updateGeometry();
if (!parentWidget() || !parentWidget()->layout()) {
resize(preferredSize());
}
}
QString IconWidget::infoText() const
{
return d->infoText;
}
QIcon IconWidget::icon() const
{
return d->icon;
}
void IconWidget::setIcon(const QString &icon)
{
if (icon.isEmpty()) {
setIcon(QIcon());
return;
}
setIcon(KIcon(icon));
}
void IconWidget::setIcon(const QIcon &icon)
{
setSvg(QString());
/*fade to the new icon, but to not bee a too big hog, not do that when:
- the fade animation is already running
- the icon is under mouse
- one betwen the old and new icon is null*/
if (!(d->states & IconWidgetPrivate::HoverState) && !d->iconChangeTimer->isActive() && d->oldIcon.isNull() && !d->icon.isNull() && !icon.isNull()) {
d->oldIcon = d->icon;
d->animateMainIcon(true, d->states);
} else {
d->oldIcon = QIcon();
}
d->iconChangeTimer->start(300);
d->icon = icon;
update();
}
QSizeF IconWidget::iconSize() const
{
return d->iconSize;
}
void IconWidget::setPreferredIconSize(const QSizeF &size)
{
d->preferredIconSize = size;
updateGeometry();
}
QSizeF IconWidget::preferredIconSize() const
{
return d->preferredIconSize;
}
void IconWidget::setMinimumIconSize(const QSizeF &size)
{
d->minimumIconSize = size;
updateGeometry();
}
QSizeF IconWidget::minimumIconSize() const
{
return d->minimumIconSize;
}
void IconWidget::setMaximumIconSize(const QSizeF &size)
{
d->maximumIconSize = size;
updateGeometry();
}
QSizeF IconWidget::maximumIconSize() const
{
return d->maximumIconSize;
}
bool IconWidget::isDown()
{
return d->states & IconWidgetPrivate::PressedState;
}
void IconWidget::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
if (event->button() != Qt::LeftButton) {
QGraphicsWidget::mousePressEvent(event);
return;
}
- d->states |= IconWidgetPrivate::PressedState;
+ if (KGlobalSettings::singleClick() || (receivers(SIGNAL(clicked()))) > 0) {
+ d->states |= IconWidgetPrivate::PressedState;
+ }
d->clickStartPos = scenePos();
bool handled = false;
foreach (IconAction *action, d->cornerActions) {
handled = action->event(event->type(), event->pos());
if (handled) {
break;
}
}
if (!handled && boundingRect().contains(event->pos())) {
emit pressed(true);
}
update();
}
void IconWidget::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
if (~d->states & IconWidgetPrivate::PressedState) {
QGraphicsWidget::mouseMoveEvent(event);
return;
}
if (boundingRect().contains(event->pos())) {
if (~d->states & IconWidgetPrivate::HoverState) {
d->states |= IconWidgetPrivate::HoverState;
update();
}
} else {
if (d->states & IconWidgetPrivate::HoverState) {
d->states &= ~IconWidgetPrivate::HoverState;
update();
}
}
}
void IconWidget::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
if (~d->states & IconWidgetPrivate::PressedState) {
QGraphicsWidget::mouseMoveEvent(event);
return;
}
d->states &= ~IconWidgetPrivate::PressedState;
//don't pass click when the mouse was moved
bool handled = d->clickStartPos != scenePos();
if (!handled) {
foreach (IconAction *action, d->cornerActions) {
if (action->event(event->type(), event->pos())) {
handled = true;
break;
}
}
}
if (!handled) {
if (boundingRect().contains(event->pos())) {
emit clicked();
if (KGlobalSettings::singleClick()) {
emit activated();
}
if (d->action && d->action->menu()) {
d->action->menu()->popup(event->screenPos());
}
}
emit pressed(false);
}
update();
}
void IconWidget::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
{
Q_UNUSED(event)
+ d->states |= IconWidgetPrivate::PressedState;
+
emit doubleClicked();
if (!KGlobalSettings::singleClick()) {
emit activated();
}
+
+ update();
}
void IconWidget::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
{
//kDebug();
foreach (IconAction *action, d->cornerActions) {
action->show();
action->event(event->type(), event->pos());
}
d->oldIcon = QIcon();
d->animateMainIcon(true, d->states|IconWidgetPrivate::HoverState);
QGraphicsWidget::hoverEnterEvent(event);
}
void IconWidget::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
{
//kDebug() << d->cornerActions;
foreach (IconAction *action, d->cornerActions) {
action->hide();
action->event(event->type(), event->pos());
}
// d->states &= ~IconWidgetPrivate::HoverState; // Will be set once progress is zero again ...
//if an eventfilter stolen the mousereleaseevent remove the pressed state here
d->states &= ~IconWidgetPrivate::PressedState;
d->animateMainIcon(false, d->states|IconWidgetPrivate::HoverState);
QGraphicsWidget::hoverLeaveEvent(event);
}
bool IconWidget::sceneEventFilter(QGraphicsItem *watched, QEvent *event)
{
Q_UNUSED(watched)
if (event->type() == QEvent::GraphicsSceneDragEnter) {
d->animateMainIcon(true, d->states|IconWidgetPrivate::HoverState);
} else if (event->type() == QEvent::GraphicsSceneDragLeave) {
d->animateMainIcon(false, d->states|IconWidgetPrivate::HoverState);
}
return false;
}
void IconWidget::setPressed(bool pressed)
{
if (pressed) {
d->states |= IconWidgetPrivate::ManualPressedState;
d->states |= IconWidgetPrivate::PressedState;
} else {
d->states &= ~IconWidgetPrivate::ManualPressedState;
d->states &= ~IconWidgetPrivate::PressedState;
}
update();
}
void IconWidget::setUnpressed()
{
setPressed(false);
}
void IconWidgetPrivate::svgChanged()
{
iconSvgElementChanged = true;
q->update();
}
void IconWidget::setOrientation(Qt::Orientation orientation)
{
d->orientation = orientation;
resize(sizeFromIconSize(d->iconSize.width()));
}
Qt::Orientation IconWidget::orientation() const
{
return d->orientation;
}
void IconWidget::invertLayout(bool invert)
{
d->invertLayout = invert;
}
bool IconWidget::invertedLayout() const
{
return d->invertLayout;
}
QSizeF IconWidget::sizeFromIconSize(const qreal iconWidth) const
{
d->setActiveMargins();
if (d->text.isEmpty() && d->infoText.isEmpty()) {
//no text, just the icon size
return d->addMargin(QSizeF(iconWidth, iconWidth), IconWidgetPrivate::ItemMargin);
}
QFontMetricsF fm(font());
qreal width = 0;
if (d->orientation == Qt::Vertical) {
width = qMax(d->maxWordWidth(d->text),
d->maxWordWidth(d->infoText)) +
fm.width("xxx") +
d->verticalMargin[IconWidgetPrivate::TextMargin].left +
d->verticalMargin[IconWidgetPrivate::TextMargin].right;
width = qMax(width,
iconWidth +
d->verticalMargin[IconWidgetPrivate::IconMargin].left +
d->verticalMargin[IconWidgetPrivate::IconMargin].right);
} else {
width = iconWidth +
d->horizontalMargin[IconWidgetPrivate::IconMargin].left +
d->horizontalMargin[IconWidgetPrivate::IconMargin].right +
qMax(fm.width(d->text), fm.width(d->infoText)) + fm.width("xxx") +
d->horizontalMargin[IconWidgetPrivate::TextMargin].left +
d->horizontalMargin[IconWidgetPrivate::TextMargin].right;
}
qreal height;
qreal textHeight;
QStyleOptionGraphicsItem option;
option.state = QStyle::State_None;
option.rect = QRect(0, 0, width, QWIDGETSIZE_MAX);
textHeight = d->displaySizeHint(&option, width).height();
if (d->orientation == Qt::Vertical) {
height = iconWidth + textHeight +
d->verticalMargin[IconWidgetPrivate::TextMargin].top +
d->verticalMargin[IconWidgetPrivate::TextMargin].bottom +
d->verticalMargin[IconWidgetPrivate::IconMargin].top +
d->verticalMargin[IconWidgetPrivate::IconMargin].bottom;
} else {
//Horizontal
height = qMax(iconWidth +
d->verticalMargin[IconWidgetPrivate::IconMargin].top +
d->verticalMargin[IconWidgetPrivate::IconMargin].bottom,
textHeight +
d->verticalMargin[IconWidgetPrivate::TextMargin].top +
d->verticalMargin[IconWidgetPrivate::TextMargin].bottom);
}
return d->addMargin(QSizeF(width, height), IconWidgetPrivate::ItemMargin);
}
void IconWidget::changeEvent(QEvent *event)
{
d->changeEvent(event);
QGraphicsWidget::changeEvent(event);
}
} // namespace Plasma
#include "moc_iconwidget_p.cpp"
diff --git a/security/crypto/crypto.desktop b/security/crypto/crypto.desktop
index ea4ad5c50f..86ef570bda 100644
--- a/security/crypto/crypto.desktop
+++ b/security/crypto/crypto.desktop
@@ -1,221 +1,222 @@
[Desktop Entry]
Icon=preferences-desktop-cryptography
Type=Service
X-KDE-ServiceTypes=KCModule
Exec=kcmshell4 crypto
X-DocPath=kcontrol/crypto/index.html
X-KDE-Library=kcm_crypto
X-KDE-ParentApp=kcontrol
Name=Crypto
Name[af]=Crypto
Name[ar]=التشفير
Name[as]=Crypto
Name[ast]=Cifráu
Name[be@latin]=Šyfravańnie
Name[bg]=Шифриране
Name[bn]=ক্রিপ্টো
Name[bn_IN]=Crypto
Name[bs]=Šifrovanje
Name[ca]=Criptografia
Name[ca@valencia]=Criptografia
Name[cs]=Šifrování
Name[csb]=Crypto
Name[da]=Kryptering
Name[de]=Krypto
Name[el]=Κρυπτογράφηση
Name[en_GB]=Crypto
Name[eo]=Crypto
Name[es]=Criptografía
Name[et]=Krüpto
Name[eu]=Kriptografia
Name[fa]=رمز
Name[fi]=Salaukset
Name[fr]=Chiffrement
Name[fy]=Crypto
Name[ga]=Crypto
Name[gl]=Criptografía
Name[gu]=ક્રાય્પ્ટો
Name[he]=הצפנה
Name[hi]=क्रिप्टो
Name[hne]=क्रिप्टो
Name[hr]=Crypto
Name[hsb]=Crypto
Name[hu]=Titkosítás
Name[ia]=Crypto
Name[id]=Crypto
Name[is]=Dulkóðun
Name[it]=Crittografia
Name[ja]=暗号
Name[kk]=Crypto
Name[km]=Crypto
Name[kn]=ಕ್ರಿಪ್ಟೋ
Name[ko]=암호화
Name[ku]=Veşartî
Name[lt]=Šifravimas
Name[lv]=Kriptogrāfija
Name[mai]=क्रिप्टो
Name[mk]=Криптографија
Name[ml]=ക്രിപ്റ്റോ
Name[mr]=क्रिप्टो
Name[nb]=Kryptering
Name[nds]=Crypto
Name[ne]=गुप्त
Name[nl]=Crypto
Name[nn]=Kryptotenester
Name[or]=ଗୁପ୍ତ
Name[pa]=ਕਰਿਪਟੂ
Name[pl]=Kryptografia
Name[pt]=Cifra
Name[pt_BR]=Criptografia
Name[ro]=Cripto
Name[ru]=Шифрование
Name[se]=Krypteren
Name[si]=Crypto
Name[sk]=Šifrovanie
Name[sl]=Šifriranje
Name[sq]=Crypto
Name[sr]=Шифровање
Name[sr@ijekavian]=Шифровање
Name[sr@ijekavianlatin]=Šifrovanje
Name[sr@latin]=Šifrovanje
Name[sv]=Krypto
Name[ta]=Crypto
Name[tg]=Рамзгузорӣ
Name[th]=การเข้ารหัส
Name[tr]=Şifreleme
Name[tt]=Шифрлау
Name[ug]=شىفىرلا
Name[uk]=Шифрування
Name[vi]=Mật mã
Name[wa]=Criptografeye
Name[x-test]=xxCryptoxx
Name[zh_CN]=加密算法
Name[zh_TW]=Crypto
Comment=Configure SSL, manage certificates, and other cryptography settings
Comment[af]=Konfigureer SSL, hanteer sertifikate, en ander kriptografieverstellings
Comment[ar]=اضبط SSL، وأدر الشهادات وإعدادات تشفير أخرى
Comment[as]=SSL বিন্যাস কৰক, প্ৰমাণপত্ৰ পৰিচালনা কৰক, আৰু অন্যান্য ক্ৰিপ্টোগ্ৰাফিৰ পছন্দ
Comment[ast]=Configurar SSL, xestionar certificaos, y otros axustes criptográficos
Comment[be@latin]=Naładžvaje SSL, kiruje sertyfikatami, inšyja nałady šyfravańnia
Comment[bg]=SSL, управление на сертификати и други настройки за шифроване
Comment[bn]=এস-এস-এল কনফিগারেশন, সার্টিফিকেট তত্বাবধান, এবং অন্যান্য ক্রিপ্টোগ্রাফি সেটিংস
Comment[bn_IN]=SSL কনফিগারেশন, সার্টিফিকেট পরিচালনা ও ক্রিপ্টোগ্রাফি সংক্রান্ত বিবিধ বৈশিষ্ট্য কনফিগার করুন
Comment[bs]=Podešavanje SSL‑a, upravljanje sertifikatima, i druge kriptografske postavke
Comment[ca]=Configura l'SSL, gestiona certificats, i altres arranjaments de criptografia
Comment[ca@valencia]=Configura l'SSL, gestiona certificats, i altres arranjaments de criptografia
Comment[cs]=Nastavení SSL, správa certifikátů a ostatní nastavení šifrování
Comment[csb]=Kònfigùracëjô SSL, sprôwianié certifikatama ë jinszëma ùstôwóma kriptografiji
Comment[da]=Konfigurér SSL, håndtér certifikater og andre kryptografiske indstillinger
Comment[de]=Einrichtung von SSL, Zertifikat-Verwaltung und weitere kryptografische Einstellungen
Comment[el]=Ρύθμιση SSL, διαχείριση πιστοποιητικών, και άλλες ρυθμίσεις κρυπτογραφίας
Comment[en_GB]=Configure SSL, manage certificates and other cryptography settings
Comment[eo]=Agordi SSLon, administri atestilojn, kaj aliajn kriptografiajn agordojn
Comment[es]=Configurar SSL, gestionar certificados y otras preferencias de criptografía
Comment[et]=SSL-i seadistamine, sertifikaatide haldamine ja muud krüptoseadistused
Comment[eu]=SSL konfiguratu, ziurtagiriak kudeatu eta bestelako kriptografiako ezarpenak
Comment[fa]=پیکربندی SSL، مدیریت گواهی‌نامه‌ها، و تنظیمات رمزنگاری دیگر
Comment[fi]=Muokkaa SSL-asetuksia ja hallitse varmenteita sekä muita salausasetuksia
Comment[fr]=Configure le chiffrement SSL, gère les certificats et autres paramètres liés au chiffrement
Comment[fy]=Stel SSL yn, behear sertifikaten en konfigurearje oare kryptografyske ynstellings
Comment[ga]=Cumraigh SSL, bainistigh teastais, agus socruithe cripteagrafaíochta eile
Comment[gl]=Configurar o SSL, xestionar os certificados e outras opcións de cifrado
Comment[gu]=SSL રુપરેખાંકન, પ્રમાણપત્ર સંચાલન અને અન્ય ક્રાય્પ્ટોગ્રાફી ગોઠવણીઓ કરો
Comment[he]=שינוי הגדרות SSL, ניהול תעודות והגדרות הצפנה אחרות
Comment[hi]=एसएसएल कॉन्फ़िगर, प्रमाणपत्र प्रबंधन, तथा अन्य क्रिप्टोग्राफ़िक सेटिंग
Comment[hne]=एसएसएल कान्फिगर, प्रमानपत्र प्रबंधन, अउ दुसरा क्रिप्टोग्राफी सेटिंग
Comment[hr]=Podešavanje SSL-a, upravljanje certifikatima i ostala kriptografska podešavanja
Comment[hsb]=Konfigurowanje SSL, zastaranje certifikatow a druhe kryptografiske nastajenja
Comment[hu]=SSL-beállítások, tanúsítványkezelés és további titkosítási lehetőségek
Comment[ia]=Configura SSL, manea certificatos e altere fixationes cryptographic
Comment[id]=Konfigurasi SSL, atur sertifikat, dan pengaturan kriptografi lainnya
Comment[is]=Stilla SSL, stýra skírteinum, og aðrar dulkóðunarstillingar
Comment[it]=Configura SSL, gestisce i certificati ed altre impostazioni di crittografia
Comment[ja]=SSL の設定、証明書の管理、その他の暗号作成形式の設定
Comment[kk]=SSL баптаулары, күаліктерді басқару және басқа криптография параметрлері
Comment[km]=កំណត់​រចនា​សម្ព័ន្ធ SSL, គ្រប់គ្រង​វិញ្ញាបនបត្រ និង​ការ​កំណត់​កូដសាស្ដ្រ
Comment[kn]=SSL ಅನ್ನು ಸಂರಚಿಸು, ಪ್ರಮಾಣಪತ್ರಗಳನ್ನು ನಿಭಾಯಿಸು, ಮತ್ತು ಇತರ ಗೂಢಲಿಪಿಶಾಸ್ತ್ರ (ಕ್ರಿಪ್ಟೋಗ್ರಫಿ) ಸಂಯೋಜನೆಗಳು
Comment[ko]=SSL과 같은 암호화를 설정하고, 인증서를 관리합니다
Comment[ku]=SSL, rêveberê belge kirinê, û mîhengên din yê bişîfre kirinê veava bike
Comment[lt]=Derina SSL, tvarko liudijimus ir kitus šifravimo parametrus
Comment[lv]=Konfigurēt SSL, pārvaldīt sertifikātus un citus kriptogrāfijas iestatījumus
Comment[mai]=एसएसएल बिन्यस्त करू, प्रमाणपत्र आओर आन क्रिप्टोग्राफिक सेटिंगक प्रबंधित करू
Comment[mk]=Конфигурација на SSL, менаџмент на сертификати\nи други криптографски поставувања
Comment[ml]=എസ്എസ്എല്‍ ക്രമീകരിയ്ക്കുക, സാക്ഷ്യപത്ര നടത്തിപ്പുകാരന്‍, പിന്നെ ക്രിപ്റ്റോഗ്രഫി സജ്ജീകരണം
Comment[mr]=SSL, प्रमाणपत्र प्रबंधन, तथा अन्य क्रिप्टोग्राफ़िक संयोजना संयोजीत करा
Comment[nb]=Oppsett av SSL, behandling av sertifikater og andre innstillinger for kryptering
Comment[nds]=SSL inrichten, Zertifikaten plegen un anner Verslötel-Instellen
Comment[ne]=SSL कन्फिगरेसन, प्रमाणपत्र प्रबन्ध, र अन्य गुप्तिकरण सेटिङ
Comment[nl]=Stel SSL in, beheer certificaten en configureer andere cryptografische instellingen
Comment[nn]=Set opp SSL, sertifikat og andre krypteringsinnstillingar
Comment[or]=SSL ବିନ୍ୟାସ କରନ୍ତୁ, ପ୍ରମାଣପତ୍ର ପରିଚାଳନ, ଏବଂ ଅନ୍ୟ ଗୁଢଲେଖନ ବିନ୍ୟାସଗୁଡିକ
Comment[pa]=SSL ਸੰਰਚਨਾ, ਸਰਟੀਫਿਕੇਟ ਪਰਬੰਧ, ਅਤੇ ਹੋਰ ਕਰਿਪਟੋਗਰਾਫ਼ੀ ਸੈਟਿੰਘ
Comment[pl]=Konfiguracja SSL, zarządzanie certyfikatami i inne ustawienia kryptografii
Comment[pt]=Configurar o SSL, gerir os certificados e outras opções de criptografia
Comment[pt_BR]=Configura SSL, gerencia certificados e outras configurações de criptografia
Comment[ro]=Configurează SSL, administrează certificate și alte setări criptografice
Comment[ru]=Настройка SSL, управление сертификатами и другие параметры криптографии
Comment[se]=Gieđahala SSL, sertifikáhtaid ja eará krypterenheivehusaid
Comment[si]=SSL සැකසීම, සහතික කළමණාකරනය සහ වෙනත් සංකේතාන්කන සැකසුම්
Comment[sk]=Nastavenie SSL, správa certifikátov a ostatné nastavenie šifrovania
Comment[sl]=Nastavljanje SSL, upravljanje s potrdili in ostale nastavitve šifriranja
Comment[sr]=Подешавање ССЛ‑а, управљање сертификатима, и друге криптографске поставке
Comment[sr@ijekavian]=Подешавање ССЛ‑а, управљање сертификатима, и друге криптографске поставке
Comment[sr@ijekavianlatin]=Podešavanje SSL‑a, upravljanje sertifikatima, i druge kriptografske postavke
Comment[sr@latin]=Podešavanje SSL‑a, upravljanje sertifikatima, i druge kriptografske postavke
Comment[sv]=Anpassa SSL, hantera certifikat och andra kryptografiska inställningar
Comment[ta]=SSL வடிவமைக்க, சான்றுகள் நிர்வகிக்க, மற்றும் ஏனைய உருமாற்ற அமைப்புகள்
Comment[tg]=Танзимоти SSL, идоракунии иҷозатномаҳо ва танзимотҳои рамзгузории дигар
Comment[th]=ปรับแต่ง SSL, จัดการใบรับรอง, และการตั้งค่าเกี่ยวกับการเข้ารหัสอื่น ๆ
Comment[tr]=SSL yapılandırması, sertifika yönetimi ve diğer şifreleme ayarları
Comment[tt]=SSL'ны көйләү, таныклыклар белән идарә итө һәм криптографиянең башка параметрлары
Comment[ug]=SSL سەپلىمە، گۇۋاھنامە باشقۇرۇش ۋە باشقا شىفىرلاش تەڭشىكى
Comment[uk]=Налаштування SSL, керування сертифікатами та інші параметри шифрування
Comment[vi]=Cấu hình SSL, quản lý chứng nhận và các thiết lập mật mã khác
Comment[wa]=Apontyî SSL, manaedjî les acertineures, eyet ds ôtes apontiaedjes del criptografeye
Comment[x-test]=xxConfigure SSL, manage certificates, and other cryptography settingsxx
Comment[zh_CN]=配置 SSL、管理证书及其它加密设置
Comment[zh_TW]=設定 SSL、管理憑證與其他加密設定
X-KDE-Keywords=Crypto,Krypto,Cryptography,encryption,SSL,https,certificates,ciphers,TLS,secure,security
X-KDE-Keywords[ar]=تشفير,كريبتو,التشفير,التشفير,SSL,https,الشهادات,الأصفار,TLS.سري,أمني
X-KDE-Keywords[bg]=Crypto,Krypto,Cryptography,encryption,SSL,https,certificates,ciphers,TLS,secure,security,криптография,шифриране,удостоверения,сертификати,шифри,сигурност
X-KDE-Keywords[ca]=Criptografia,encriptatge,SSL,https,certificats,xifres,TLS,segur,seguretat
X-KDE-Keywords[ca@valencia]=Criptografia,encriptatge,SSL,https,certificats,xifres,TLS,segur,seguretat
X-KDE-Keywords[da]=Crypto,Krypto,Kryptografi,kryptering,SSL,https,certifikater,chiffre,TLS,sikker,sikkerhed
X-KDE-Keywords[de]=crypto,krypto,kryptographie,verschlüsselung,ssl,https,Zertifikate,Chiffren,tls,sicher,sicherheit
X-KDE-Keywords[es]=Crypto,Krypto,Criptografía,cifrado,SSL,ttps,certificados,cifradores,TLS,seguro,seguridad
X-KDE-Keywords[et]=krüpto,krüptograafia,krüptimine,SSL,https,sertifikaadid,serdid,šifrid,TLS,turvalisus,turve
X-KDE-Keywords[fi]=Crypto,Krypto,Salaus,SSL,https,varmenteet,sertifikaatit,salakirjoitukset,salakirjoitus,salakirjoitusmenetelmät,salausmenetelmät,TLS,turvallinen,turvallisuus
X-KDE-Keywords[fr]=Chiffrement,Krypto,déchiffrement,SSL,https,certificats,encodage,TLS,sécuriser,sécurité
X-KDE-Keywords[ga]=Crypto,Krypto,cripteagrafaíocht,criptiú,SSL,https,teastais,sifir,TLS,daingean,slándáil
X-KDE-Keywords[gl]=Cripto,Kripto,Criptografía,cifrado,SSL,https,certificados,cifras,TLS,seguro,securidade
X-KDE-Keywords[he]=הצפנה,קריפטולוגיה,אבטחה,מאובטחCrypto,Krypto,Cryptography,encryption,SSL,https,certificates,ciphers,TLS,secure,security
X-KDE-Keywords[hu]=Titkosítás,Titkosítás,Titkosítás,titkosítás,SSL,https,tanúsítványok,titkosítók,TLS,biztonságos,biztonság
X-KDE-Keywords[ia]=Crypto,Krypto,Cryptographia,cryptation,SSL,https,certificatos,ciphras,TLS, secur,securitate
X-KDE-Keywords[it]=Cripto,Kripto,Crittografia,cifratura,SSL,https,certificati,cifrari,TLS,sicuro,sicurezza
X-KDE-Keywords[kk]=Crypto,Krypto,Cryptography,encryption,SSL,https,certificates,ciphers,TLS,secure,security
X-KDE-Keywords[km]=ការ​គ្រីប​,​គ្រីបតូ​,ការ​គ្រីប,​ការ​អ៊ិនគ្រីប​,SSL,https,​វិញ្ញាបនបត្រ​,ciphers,TLS,មូលប័ត្រ​,​មូលប័ត្រ
X-KDE-Keywords[ko]=Crypto,Krypto,Cryptography,encryption,SSL,https,certificates,ciphers,TLS,secure,security,암호,암호화,인증서,보안
X-KDE-Keywords[nb]=Krypto,Crypto,Kryptografi,kryptering,SSL,https,sertifikater,chiffre,TLS,sikker,sikkerhet
X-KDE-Keywords[nds]=Verslöteln,SSL,https,Zertifikaten,Slötels,TLS,seker,Sekerheit
X-KDE-Keywords[nl]=Crypto,Krypto,cryptografie,versleuteling,SSL,https,certificaten,ciphers,TLS,veilig,beveiliging
X-KDE-Keywords[pa]=ਕ੍ਰਿਪਟੂ,ਕ੍ਰਿਪਟੂ,ਕ੍ਰਿਪਟੂਗਰਾਫੀ,ਇੰਕ੍ਰਿਪਸ਼ਨ,ਸਰਟੀਫਿਕੇਟ,ਸੀਫ਼ਰ,ਸੁਰੱਖਿਅਤ,ਸੁਰੱਖਿਆ,Crypto,Krypto,Cryptography,encryption,SSL,https,certificates,ciphers,TLS,secure,security
X-KDE-Keywords[pl]=Kryptografia,szyfrowanie,SSL,https,certyfikaty,szyfry,TLS,bezpieczeństwo,zabezpiecz
X-KDE-Keywords[pt]=Cripto,Criptografia,encriptação,SSL,HTTPS,certificados,cifras,TLS,seguro,segurança
X-KDE-Keywords[pt_BR]=criptografia,Krypto,criptografar,SSL,HTTPS,certificados,cifras,TLS,segurar,segurança
X-KDE-Keywords[ro]=Criptare,Cripto,Criptografie,SSL,https,certificate,cifru,TLS,sigur,securitate
+X-KDE-Keywords[sk]=Crypto,Krypto,Cryptography,encryption,SSL,https,certificates,ciphers,TLS,secure,security
X-KDE-Keywords[sr]=Crypto,Krypto,Cryptography,encryption,SSL,https,certificates,ciphers,TLS,secure,security,криптографија,шифровање,ССЛ,ХТТПС,сертификат,шифрар,ТЛС,безбедност
X-KDE-Keywords[sr@ijekavian]=Crypto,Krypto,Cryptography,encryption,SSL,https,certificates,ciphers,TLS,secure,security,криптографија,шифровање,ССЛ,ХТТПС,сертификат,шифрар,ТЛС,безбедност
X-KDE-Keywords[sr@ijekavianlatin]=Crypto,Krypto,Cryptography,encryption,SSL,https,certificates,ciphers,TLS,secure,security,kriptografija,šifrovanje,SSL,HTTPS,sertifikat,šifrar,TLS,bezbednost
X-KDE-Keywords[sr@latin]=Crypto,Krypto,Cryptography,encryption,SSL,https,certificates,ciphers,TLS,secure,security,kriptografija,šifrovanje,SSL,HTTPS,sertifikat,šifrar,TLS,bezbednost
X-KDE-Keywords[sv]=Krypto,Kryptografi,kryptering,SSL,https,certifikat,chiffer,TLS,säker,säkerhet
X-KDE-Keywords[tg]=Crypto,Krypto,Cryptography,рамзгузорӣ,SSL,https,иҷозатномаҳо,ciphers,TLS,бехатар,амният
X-KDE-Keywords[uk]=Crypto,Krypto,Cryptography,encryption,SSL,https,certificates,ciphers,TLS,secure,security,криптографія,шифрування,сертифікат,сертифікати,захист,безпека,безпечний,захищений
X-KDE-Keywords[vi]=Crypto,mật mã,mã hoá,Krypto,Cryptography,encryption,SSL,https,certificates,chứng thực,ciphers,TLS,secure,bảo mật,security
X-KDE-Keywords[x-test]=xxCrypto,Krypto,Cryptography,encryption,SSL,https,certificates,ciphers,TLS,secure,securityxx
X-KDE-Keywords[zh_CN]=Crypto,Krypto,Cryptography,encryption,SSL,https,certificates,ciphers,TLS,secure,security,加密,证书,密码,安全
X-KDE-Keywords[zh_TW]=Crypto,Krypto,Cryptography,encryption,SSL,https,certificates,ciphers,TLS,secure,security
Categories=Qt;KDE;X-KDE-settings-security;

File Metadata

Mime Type
text/x-diff
Expires
Fri, Nov 1, 8:54 AM (1 d, 12 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
10075335
Default Alt Text
(487 KB)

Event Timeline