From ff347538b88aa0e964f365a7f35f9b8bff38c6b8 Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Nov 19 2015 08:45:22 +0000 Subject: import kde-workspace-4.11.19-7.el7 --- diff --git a/.gitignore b/.gitignore index 5ba5a3a..0e6085f 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -SOURCES/kde-workspace-4.10.5.tar.xz +SOURCES/kde-workspace-4.11.19.tar.xz diff --git a/.kde-workspace.metadata b/.kde-workspace.metadata index d693060..772fee6 100644 --- a/.kde-workspace.metadata +++ b/.kde-workspace.metadata @@ -1 +1 @@ -d4986c1b4e3232f74a6348116908a13aee073a93 SOURCES/kde-workspace-4.10.5.tar.xz +2ab5b46aa7189efd6371461dd3f0bc2513fa560c SOURCES/kde-workspace-4.11.19.tar.xz diff --git a/SOURCES/fedora-plasma-cache.sh b/SOURCES/fedora-plasma-cache.sh new file mode 100755 index 0000000..5428fab --- /dev/null +++ b/SOURCES/fedora-plasma-cache.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +if [ "`kreadconfig --file fedora-plasma-cacherc --group General --key FirstRun --default true`" = "true" ]; then + rm -fv "`kde4-config --path cache`/"*.kcache + rm -fv "`kde4-config --path cache`/"plasma-svgelements-* + #rm -fv ${XDG_CONFIG_HOME-${HOME}/.config}/Trolltech.conf + kwriteconfig --file fedora-plasma-cacherc --group General --key FirstRun --type bool false +fi diff --git a/SOURCES/kde-workspace-4.10-bz#921742.patch b/SOURCES/kde-workspace-4.10-bz#921742.patch index f197e10..865d4a6 100644 --- a/SOURCES/kde-workspace-4.10-bz#921742.patch +++ b/SOURCES/kde-workspace-4.10-bz#921742.patch @@ -1,40 +1,8 @@ -diff --git a/powerdevil/daemon/powerdevilactionpool.h b/powerdevil/daemon/powerdevilactionpool.h -index 8a94eacc8ef2c2aead8cb075cbc80b783c1aeb4c..c5173abbc90b60f83b30b7527d8715454327f992 100644 ---- a/powerdevil/daemon/powerdevilactionpool.h -+++ b/powerdevil/daemon/powerdevilactionpool.h -@@ -34,6 +34,11 @@ class Action; - class ActionPool - { - public: -+ enum ErrorCode { -+ NoAction, -+ LoadFailed, -+ NotSupported -+ }; - static ActionPool *instance(); - - virtual ~ActionPool(); -@@ -46,11 +51,15 @@ public: - - void clearCache(); - -+ ErrorCode error(); -+ - private: - ActionPool(); - - QHash< QString, Action* > m_actionPool; - QStringList m_activeActions; -+ ErrorCode m_error; -+ QHash< QString, ErrorCode > m_initErrors; - }; - - } diff --git a/powerdevil/daemon/powerdevilactionpool.cpp b/powerdevil/daemon/powerdevilactionpool.cpp -index a9950f174fe184b8faa54c54fc00654984c65b3f..33fec684b2ac7e1c801c6663675d1d177cb3f067 100644 +index 970aa18..6ca0d57 100644 --- a/powerdevil/daemon/powerdevilactionpool.cpp +++ b/powerdevil/daemon/powerdevilactionpool.cpp -@@ -79,6 +79,7 @@ void ActionPool::clearCache() +@@ -82,6 +82,7 @@ void ActionPool::clearCache() i.value()->deleteLater(); i = m_actionPool.erase(i); } @@ -42,7 +10,7 @@ index a9950f174fe184b8faa54c54fc00654984c65b3f..33fec684b2ac7e1c801c6663675d1d17 } void ActionPool::init(PowerDevil::Core *parent) -@@ -96,6 +97,7 @@ void ActionPool::init(PowerDevil::Core *parent) +@@ -99,6 +100,7 @@ void ActionPool::init(PowerDevil::Core *parent) if (!retaction) { // Troubles... kWarning() << "failed to load" << offer->desktopEntryName(); @@ -50,7 +18,7 @@ index a9950f174fe184b8faa54c54fc00654984c65b3f..33fec684b2ac7e1c801c6663675d1d17 continue; } -@@ -103,6 +105,7 @@ void ActionPool::init(PowerDevil::Core *parent) +@@ -106,6 +108,7 @@ void ActionPool::init(PowerDevil::Core *parent) if (!retaction->isSupported()) { // Skip that retaction->deleteLater(); @@ -58,7 +26,7 @@ index a9950f174fe184b8faa54c54fc00654984c65b3f..33fec684b2ac7e1c801c6663675d1d17 continue; } -@@ -140,7 +143,13 @@ Action* ActionPool::loadAction(const QString& actionId, const KConfigGroup& grou +@@ -169,7 +172,13 @@ Action* ActionPool::loadAction(const QString& actionId, const KConfigGroup& grou return retaction; } else { @@ -73,7 +41,7 @@ index a9950f174fe184b8faa54c54fc00654984c65b3f..33fec684b2ac7e1c801c6663675d1d17 return 0; } } -@@ -154,4 +163,8 @@ void ActionPool::unloadAllActiveActions() +@@ -183,4 +192,9 @@ void ActionPool::unloadAllActiveActions() m_activeActions.clear(); } @@ -81,20 +49,53 @@ index a9950f174fe184b8faa54c54fc00654984c65b3f..33fec684b2ac7e1c801c6663675d1d17 +{ + return m_error; +} ++ + } +diff --git a/powerdevil/daemon/powerdevilactionpool.h b/powerdevil/daemon/powerdevilactionpool.h +index ca436ff..816449e 100644 +--- a/powerdevil/daemon/powerdevilactionpool.h ++++ b/powerdevil/daemon/powerdevilactionpool.h +@@ -36,6 +36,11 @@ class Action; + class KDE_EXPORT ActionPool + { + public: ++ enum ErrorCode { ++ NoAction, ++ LoadFailed, ++ NotSupported ++ }; + static ActionPool *instance(); + + virtual ~ActionPool(); +@@ -48,11 +53,15 @@ public: + + void clearCache(); + ++ ErrorCode error(); ++ + private: + ActionPool(); + + QHash< QString, Action* > m_actionPool; + QStringList m_activeActions; ++ ErrorCode m_error; ++ QHash< QString, ErrorCode > m_initErrors; + }; + } diff --git a/powerdevil/daemon/powerdevilcore.cpp b/powerdevil/daemon/powerdevilcore.cpp -index 2dcdbc62236d5c1fae384fdb9111825a2ebf5204..64e951a170c8ee5cf60d0f76a8f8a81379caf639 100644 +index d096d74..c35b29d 100644 --- a/powerdevil/daemon/powerdevilcore.cpp +++ b/powerdevil/daemon/powerdevilcore.cpp -@@ -376,11 +376,23 @@ void Core::loadProfile(bool force) +@@ -357,11 +357,23 @@ void Core::loadProfile(bool force) if (action) { action->onProfileLoad(); } else { - // Ouch, error. But let's just warn and move on anyway -- emitNotification("powerdevilerror", i18n("The profile \"%1\" tried to activate %2, " -- "a non existent action. This is usually due to an installation problem" -- " or to a configuration problem.", -- profileId, actionName)); +- //TODO Maybe Remove from the configuration if unsupported +- kWarning() << "The profile " << profileId << "tried to activate" +- << actionName << "a non existent action. This is usually due to an installation problem" +- " or to a configuration problem. or simlpy the action is not supported"; + switch (ActionPool::instance()->error()) { + case ActionPool::NotSupported: + kWarning().nospace() << "Attempted to load unsupported action " << actionName diff --git a/SOURCES/kde-workspace-4.10.2-kdm-logind-multiseat.patch b/SOURCES/kde-workspace-4.10.2-kdm-logind-multiseat.patch deleted file mode 100644 index 2c4bb06..0000000 --- a/SOURCES/kde-workspace-4.10.2-kdm-logind-multiseat.patch +++ /dev/null @@ -1,331 +0,0 @@ ---- kde-workspace-4.10.2/CMakeLists.txt.kdm_logind 2013-03-29 09:35:09.542235739 +0100 -+++ kde-workspace-4.10.2/CMakeLists.txt 2013-04-10 15:17:35.941332970 +0200 -@@ -76,6 +76,9 @@ if(Q_WS_X11) - endif(NOT X11_Xcursor_FOUND) - endif(Q_WS_X11) - -+macro_optional_find_package(Systemd) -+macro_log_feature(SYSTEMD_FOUND "systemd" "Init and service manager for Linux" "http://www.freedesktop.org/wiki/Software/systemd" FALSE "" "Provides automatic multi-seat, session and power management features") -+ - macro_optional_find_package(GLIB2) - macro_log_feature(GLIB2_FOUND "glib2" "Low-level core library for data structure handling, portability wrappers, etc." "http://www.gtk.org" FALSE "2.x" "Needed to build the kxkb keyboard map control program and provide XMMS support in the Now Playing Plasma data engine") - ---- kde-workspace-4.10.2/cmake/modules/CMakeLists.txt.kdm_logind 2013-03-01 07:32:23.652864293 +0100 -+++ kde-workspace-4.10.2/cmake/modules/CMakeLists.txt 2013-04-10 15:17:35.941332970 +0200 -@@ -9,6 +9,7 @@ set(cmakeFiles FindCkConnector.cmake - FindOpenGLES.cmake - FindPAM.cmake - FindSensors.cmake -+ FindSystemd.cmake - PkgConfigGetVar.cmake - UnixAuth.cmake ) - ---- kde-workspace-4.10.2/cmake/modules/FindSystemd.cmake.kdm_logind 2013-04-10 15:17:35.941332970 +0200 -+++ kde-workspace-4.10.2/cmake/modules/FindSystemd.cmake 2013-04-10 16:20:41.270790058 +0200 -@@ -0,0 +1,39 @@ -+# Finds systemd and its libraries -+# Not a huge module but sufficient for now -+# Uses the same semantics as pkg_check_modules, i.e. ${LIB}{_FOUND,_INCLUDE_DIR,_LIBRARIES} -+# where ${LIB} can be one of the following: -+# LIBSYSTEMD_JOURNAL, SYSTEMD, LIBSYSTEMD_DAEMON, LIBSYSTEMD_LOGIN, LIBSYSTEMD_ID128 -+# -+# Copyright: Red Hat, Inc. 2013 -+# Author: Martin Briza -+# -+# Distributed under the BSD license. See COPYING-CMAKE-SCRIPTS for details. -+ -+#defining any of these disables systemd support -+if (NOT LIBSYSTEMD_JOURNAL_FOUND AND -+ NOT SYSTEMD_FOUND AND -+ NOT LIBSYSTEMD_DAEMON_FOUND AND -+ NOT LIBSYSTEMD_LOGIN_FOUND AND -+ NOT LIBSYSTEMD_ID128_FOUND) -+find_package(PkgConfig) -+if (PKG_CONFIG_FOUND) -+ pkg_check_modules(LIBSYSTEMD_JOURNAL QUIET "libsystemd-journal") -+ pkg_check_modules(SYSTEMD QUIET "systemd") -+ pkg_check_modules(LIBSYSTEMD_DAEMON QUIET "libsystemd-daemon") -+ pkg_check_modules(LIBSYSTEMD_LOGIN QUIET "libsystemd-login") -+ pkg_check_modules(LIBSYSTEMD_ID128 QUIET "libsystemd-id128") -+endif (PKG_CONFIG_FOUND) -+ -+if (SYSTEMD_FOUND) -+ message(STATUS "Found systemd") -+endif(SYSTEMD_FOUND) -+ -+mark_as_advanced(LIBSYSTEMD_JOURNAL_FOUND SYSTEMD_FOUND LIBSYSTEMD_DAEMON_FOUND LIBSYSTEMD_LOGIN_FOUND LIBSYSTEMD_ID128_FOUND) -+mark_as_advanced(LIBSYSTEMD_JOURNAL_INCLUDE_DIR SYSTEMD_INCLUDE_DIR LIBSYSTEMD_DAEMON_INCLUDE_DIR LIBSYSTEMD_LOGIN_INCLUDE_DIR LIBSYSTEMD_ID128_INCLUDE_DIR) -+mark_as_advanced(LIBSYSTEMD_JOURNAL_LIBRARIES SYSTEMD_LIBRARIES LIBSYSTEMD_DAEMON_LIBRARIES LIBSYSTEMD_LOGIN_LIBRARIES LIBSYSTEMD_ID128_LIBRARIES) -+ -+endif (NOT LIBSYSTEMD_JOURNAL_FOUND AND -+ NOT SYSTEMD_FOUND AND -+ NOT LIBSYSTEMD_DAEMON_FOUND AND -+ NOT LIBSYSTEMD_LOGIN_FOUND AND -+ NOT LIBSYSTEMD_ID128_FOUND) ---- kde-workspace-4.10.2/kdm/backend/dm.c.kdm_logind 2013-04-10 15:17:35.916333005 +0200 -+++ kde-workspace-4.10.2/kdm/backend/dm.c 2013-04-10 15:17:35.942332969 +0200 -@@ -50,6 +50,23 @@ from the copyright holder. - # include - #endif - -+#ifdef WITH_SYSTEMD -+# include -+# include -+ -+#define SYSTEMD_FAILURE_LIMIT 25 -+ -+ static int systemdMonitorInit(void); -+ static void systemdMonitorDeinit(); -+ static int systemdStartDisplay(char *); -+ static void systemdCheckAdded(char **); -+ static void systemdCheckRemoved(char **); -+ static void systemdHandleChange(); -+ -+ sd_login_monitor *systemd_monitor = NULL; -+ int systemd_monitor_fd = -1; -+#endif -+ - static void sigHandler(int n); - static int scanConfigs(int force); - static void startDisplay(struct display *d); -@@ -308,7 +325,16 @@ main(int argc, char **argv) - #ifdef XDMCP - updateListenSockets(); - #endif -+ -+#ifdef WITH_SYSTEMD -+ if (systemdMonitorInit()) -+ systemdHandleChange(); -+#endif -+ - mainLoop(); -+#ifdef WITH_SYSTEMD -+ systemdMonitorDeinit(); -+#endif - closeCtrl(0); - if (sdRec.how) { - int pid; -@@ -1280,6 +1306,14 @@ mainLoop(void) - } - continue; - } -+ logError("STARTING"); -+#ifdef WITH_SYSTEMD -+ if (systemd_monitor_fd >= 0 && FD_ISSET(systemd_monitor_fd, &reads)) { -+ systemdHandleChange(); -+ sd_login_monitor_flush(systemd_monitor); -+ continue; -+ } -+#endif - #ifdef XDMCP - if (processListenSockets(&reads)) - continue; -@@ -1304,6 +1338,151 @@ mainLoop(void) - } - } - -+#ifdef WITH_SYSTEMD -+static int -+systemdMonitorInit(void) -+{ -+ if (sd_booted() <= 0) { -+ logError("Didn't boot with systemd, automatic multiseat won't be enabled\n"); -+ return False; -+ } -+ -+ int check = sd_login_monitor_new("seat", &systemd_monitor); -+ if (check < 0) { -+ logError("Can't get systemd monitor: %d, automatic multiseat won't be enabled\n", check); -+ return False; -+ } -+ -+ systemd_monitor_fd = sd_login_monitor_get_fd(systemd_monitor); -+ if (systemd_monitor_fd < 0) { -+ logError("Can't retrieve file descriptor from the systemd monitor: %d, automatic multiseat won't be enabled\n", systemd_monitor_fd); -+ sd_login_monitor_unref(systemd_monitor); -+ systemd_monitor_fd = -1; -+ return False; -+ } -+ -+ registerInput(systemd_monitor_fd); -+ return True; -+} -+ -+static void -+systemdMonitorDeinit(void) -+{ -+ if (systemd_monitor) { -+ sd_login_monitor_unref(systemd_monitor); -+ } -+ systemd_monitor_fd = -1; -+} -+ -+static int -+systemdStartDisplay(char *seat) -+{ -+ struct display *link = NULL; -+ for (link = displays; link; link = link-> next) { -+ if (link->status == reserve) -+ break; -+ } -+ if (!link) { -+ logError("There's not enough reserve displays for all your seats/sessions"); -+ return False; -+ } -+ if (!strDup((&link->systemd_seat), seat)) { -+ return False; -+ } -+#ifdef HAVE_VTS -+ link->serverVT = 0; -+#endif -+ link->status = notRunning; -+ link->stillThere = True; -+ link->authorize = True; -+ link->displayType = dLocal | dPermanent; -+ link->reqSrvVT = -1; -+ link->serverPid = -1; -+ return True; -+} -+ -+static void -+systemdCheckAdded(char **seat_names) -+{ -+ char **iter_name; -+ struct display *link; -+ for (iter_name = seat_names; *iter_name; iter_name++) { -+ if (strcmp(*iter_name, "seat0") == 0) -+ continue; /* ignore the main seat */ -+ int can_graphical = sd_seat_can_graphical(*iter_name); -+ for (link = displays; link; link = link->next) { -+ if (!link->systemd_seat) -+ continue; -+ /* see if the can_graphical property didn't change */ -+ if (strcmp(*iter_name, link->systemd_seat)) { -+ if (!can_graphical) { -+ free(link->systemd_seat); -+ link->systemd_seat = NULL; -+ rStopDisplay(link, DS_RESERVE); -+ } -+ break; -+ } -+ } -+ /* the display wasn't found */ -+ if (!link) { -+ if (can_graphical) { -+ /* if starting the display failed, skip this round until the next change */ -+ if (!systemdStartDisplay(*iter_name)) -+ break; -+ } -+ } -+ } -+} -+ -+static void -+systemdCheckRemoved(char **seat_names) -+{ -+ char **iter_name; -+ struct display *link; -+ for (link = displays; link; link = link->next) { -+ for (iter_name = seat_names; *iter_name; iter_name++) { -+ if (strcmp(*iter_name, "seat0") == 0) -+ continue; /* ignore the main seat */ -+ if (link->systemd_seat && strcmp(*iter_name, link->systemd_seat) == 0) -+ break; -+ } -+ if (!(*iter_name) && link->systemd_seat) { /* was not found, stop this one */ -+ free(link->systemd_seat); -+ link->systemd_seat = NULL; -+ rStopDisplay(link, DS_RESERVE); -+ } -+ } -+} -+ -+static void -+systemdHandleChange(void) -+{ -+ static int failures = 0; -+ char **seat_names; -+ char **iter_name; -+ int check; -+ if ((check = sd_get_seats(&seat_names)) < 0) { -+ logError("Can't obtain systemd seats, error %d\n", -check); -+ failures++; -+ if (failures >= SYSTEMD_FAILURE_LIMIT) { -+ logError("%u failed calls to sd_get_seats, disabling systemd multi-seat support\n", SYSTEMD_FAILURE_LIMIT); -+ systemdMonitorDeinit(); -+ } -+ return; -+ } -+ -+ if (!check) -+ return; -+ -+ systemdCheckAdded(seat_names); -+ systemdCheckRemoved(seat_names); -+ -+ for (iter_name = seat_names; *iter_name; iter_name++) -+ free(*iter_name); -+ free(seat_names); -+} -+#endif -+ - static void - checkDisplayStatus(struct display *d) - { ---- kde-workspace-4.10.2/kdm/backend/dm.h.kdm_logind 2013-04-10 15:17:35.917333004 +0200 -+++ kde-workspace-4.10.2/kdm/backend/dm.h 2013-04-10 15:17:35.942332969 +0200 -@@ -306,6 +306,9 @@ struct display { - char *greeterAuthFile; /* file to store authorization for greeter in */ - - int plymouth_is_running; /* Plymouth's status */ -+#ifdef WITH_SYSTEMD -+ char *systemd_seat; -+#endif - }; - - #define d_location 1 ---- kde-workspace-4.10.2/kdm/backend/CMakeLists.txt.kdm_logind 2013-03-01 07:32:24.152858104 +0100 -+++ kde-workspace-4.10.2/kdm/backend/CMakeLists.txt 2013-04-10 15:17:35.942332969 +0200 -@@ -45,6 +45,10 @@ if (SECURE_RPC) - rpcauth.c - ) - endif (SECURE_RPC) -+if(LIBSYSTEMD_LOGIN_FOUND AND LIBSYSTEMD_DAEMON_FOUND) -+ add_definitions( -DWITH_SYSTEMD=1 ) -+ set(KDM_SYSTEMD_LIBRARIES ${LIBSYSTEMD_DAEMON_LIBRARIES} ${LIBSYSTEMD_LOGIN_LIBRARIES} ) -+endif(LIBSYSTEMD_LOGIN_FOUND AND LIBSYSTEMD_DAEMON_FOUND) - macro_add_file_dependencies(dm.h ${confci}) - macro_add_file_dependencies(error.c ${CMAKE_CURRENT_SOURCE_DIR}/printf.c) - kde4_add_executable(kdm NOGUI ${kdm_SRCS}) -@@ -60,6 +64,7 @@ target_link_libraries( kdm - ${NSL_LIBRARIES} - ${RESOLV_LIBRARIES} - ${SOCKET_LIBRARIES} -+ ${KDM_SYSTEMD_LIBRARIES} - ) - if (CKCONNECTOR_FOUND) - include_directories(${CKCONNECTOR_INCLUDE_DIR} ${DBUS_INCLUDE_DIR} ${DBUS_ARCH_INCLUDE_DIR}) ---- kde-workspace-4.10.2/kdm/backend/server.c.kdm_logind 2013-04-10 15:17:35.917333004 +0200 -+++ kde-workspace-4.10.2/kdm/backend/server.c 2013-04-10 15:17:35.942332969 +0200 -@@ -70,6 +70,21 @@ prepareServerArgv(struct display *d, con - if (!changeUser(d->serverUID, d->authFile)) - exit(47); - -+#ifdef WITH_SYSTEMD -+ if (d->systemd_seat) { -+ if (!(argv = parseArgs(argv, "-seat"))) -+ exit(47); -+ if (!(argv = parseArgs(argv, d->systemd_seat))) -+ exit(47); -+ } -+ else { -+ if (!(argv = parseArgs(argv, "-seat"))) -+ exit(47); -+ if (!(argv = parseArgs(argv, "seat0"))) -+ exit(47); -+ } -+#endif -+ - return argv; - } - diff --git a/SOURCES/kde-workspace-4.10.2-systemd-displaymanager.patch b/SOURCES/kde-workspace-4.10.2-systemd-displaymanager.patch deleted file mode 100644 index 7c5bdb8..0000000 --- a/SOURCES/kde-workspace-4.10.2-systemd-displaymanager.patch +++ /dev/null @@ -1,519 +0,0 @@ ---- kde-workspace-4.10.2/libs/kworkspace/kdisplaymanager.cpp.systemd-displaymanager 2013-03-01 07:32:25.116846223 +0100 -+++ kde-workspace-4.10.2/libs/kworkspace/kdisplaymanager.cpp 2013-04-29 14:45:05.406859931 +0200 -@@ -40,6 +40,166 @@ - #include - #include - -+#define _DBUS_PROPERTIES_IFACE "org.freedesktop.DBus.Properties" -+#define _DBUS_PROPERTIES_GET "Get" -+ -+#define DBUS_PROPERTIES_IFACE QLatin1String(_DBUS_PROPERTIES_IFACE) -+#define DBUS_PROPERTIES_GET QLatin1String(_DBUS_PROPERTIES_GET) -+ -+#define _SYSTEMD_SERVICE "org.freedesktop.login1" -+#define _SYSTEMD_BASE_PATH "/org/freedesktop/login1" -+#define _SYSTEMD_MANAGER_IFACE _SYSTEMD_SERVICE ".Manager" -+#define _SYSTEMD_SESSION_BASE_PATH _SYSTEMD_BASE_PATH "/Session" -+#define _SYSTEMD_SEAT_IFACE _SYSTEMD_SERVICE ".Seat" -+#define _SYSTEMD_SEAT_BASE_PATH _SYSTEMD_BASE_PATH "/Seat" -+#define _SYSTEMD_SESSION_IFACE _SYSTEMD_SERVICE ".Session" -+#define _SYSTEMD_USER_PROPERTY "User" -+#define _SYSTEMD_SEAT_PROPERTY "Seat" -+#define _SYSTEMD_SESSIONS_PROPERTY "Sessions" -+#define _SYSTEMD_SWITCH_PROPERTY "Activate" -+ -+#define SYSTEMD_SERVICE QLatin1String(_SYSTEMD_SERVICE) -+#define SYSTEMD_BASE_PATH QLatin1String(_SYSTEMD_BASE_PATH) -+#define SYSTEMD_MANAGER_IFACE QLatin1String(_SYSTEMD_MANAGER_IFACE) -+#define SYSTEMD_SESSION_BASE_PATH QLatin1String(_SYSTEMD_SESSION_BASE_PATH) -+#define SYSTEMD_SEAT_IFACE QLatin1String(_SYSTEMD_SEAT_IFACE) -+#define SYSTEMD_SEAT_BASE_PATH QLatin1String(_SYSTEMD_SEAT_BASE_PATH) -+#define SYSTEMD_SESSION_IFACE QLatin1String(_SYSTEMD_SESSION_IFACE) -+#define SYSTEMD_USER_PROPERTY QLatin1String(_SYSTEMD_USER_PROPERTY) -+#define SYSTEMD_SEAT_PROPERTY QLatin1String(_SYSTEMD_SEAT_PROPERTY) -+#define SYSTEMD_SESSIONS_PROPERTY QLatin1String(_SYSTEMD_SESSIONS_PROPERTY) -+#define SYSTEMD_SWITCH_CALL QLatin1String(_SYSTEMD_SWITCH_PROPERTY) -+ -+struct NamedDBusObjectPath -+{ -+ QString name; -+ QDBusObjectPath path; -+}; -+Q_DECLARE_METATYPE(NamedDBusObjectPath) -+Q_DECLARE_METATYPE(QList) -+ -+// Marshall the NamedDBusObjectPath data into a D-Bus argument -+QDBusArgument &operator<<(QDBusArgument &argument, const NamedDBusObjectPath &namedPath) -+{ -+ argument.beginStructure(); -+ argument << namedPath.name << namedPath.path; -+ argument.endStructure(); -+ return argument; -+} -+ -+// Retrieve the NamedDBusObjectPath data from the D-Bus argument -+const QDBusArgument &operator>>(const QDBusArgument &argument, NamedDBusObjectPath &namedPath) -+{ -+ argument.beginStructure(); -+ argument >> namedPath.name >> namedPath.path; -+ argument.endStructure(); -+ return argument; -+} -+ -+struct NumberedDBusObjectPath -+{ -+ uint num; -+ QDBusObjectPath path; -+}; -+Q_DECLARE_METATYPE(NumberedDBusObjectPath) -+ -+// Marshall the NumberedDBusObjectPath data into a D-Bus argument -+QDBusArgument &operator<<(QDBusArgument &argument, const NumberedDBusObjectPath &numberedPath) -+{ -+ argument.beginStructure(); -+ argument << numberedPath.num << numberedPath.path; -+ argument.endStructure(); -+ return argument; -+} -+ -+// Retrieve the NumberedDBusObjectPath data from the D-Bus argument -+const QDBusArgument &operator>>(const QDBusArgument &argument, NumberedDBusObjectPath &numberedPath) -+{ -+ argument.beginStructure(); -+ argument >> numberedPath.num >> numberedPath.path; -+ argument.endStructure(); -+ return argument; -+} -+ -+class SystemdManager : public QDBusInterface -+{ -+public: -+ SystemdManager() : -+ QDBusInterface( -+ SYSTEMD_SERVICE, -+ SYSTEMD_BASE_PATH, -+ SYSTEMD_MANAGER_IFACE, -+ QDBusConnection::systemBus()) {} -+}; -+ -+class SystemdSeat : public QDBusInterface -+{ -+public: -+ SystemdSeat(const QDBusObjectPath &path) : -+ QDBusInterface( -+ SYSTEMD_SERVICE, -+ path.path(), -+ SYSTEMD_SEAT_IFACE, -+ QDBusConnection::systemBus()) {} -+ /* HACK to be able to extract a(so) type from QDBus, property doesn't do the trick */ -+ QList getSessions() { -+ QDBusMessage message = QDBusMessage::createMethodCall(service(), path(), DBUS_PROPERTIES_IFACE, DBUS_PROPERTIES_GET); -+ message << interface() << SYSTEMD_SESSIONS_PROPERTY; -+ QDBusMessage reply = QDBusConnection::systemBus().call(message); -+ -+ QVariantList args = reply.arguments(); -+ if (!args.isEmpty()) { -+ QList namedPathList = qdbus_cast< QList >(args.at(0).value().variant().value()); -+ return namedPathList; -+ } -+ return QList(); -+ } -+}; -+ -+class SystemdSession : public QDBusInterface -+{ -+public: -+ SystemdSession(const QDBusObjectPath &path) : -+ QDBusInterface( -+ SYSTEMD_SERVICE, -+ path.path(), -+ SYSTEMD_SESSION_IFACE, -+ QDBusConnection::systemBus()) {} -+ /* HACK to be able to extract (so) type from QDBus, property doesn't do the trick */ -+ NamedDBusObjectPath getSeat() { -+ QDBusMessage message = QDBusMessage::createMethodCall(service(), path(), DBUS_PROPERTIES_IFACE, DBUS_PROPERTIES_GET); -+ message << interface() << SYSTEMD_SEAT_PROPERTY; -+ QDBusMessage reply = QDBusConnection::systemBus().call(message); -+ -+ QVariantList args = reply.arguments(); -+ if (!args.isEmpty()) { -+ NamedDBusObjectPath namedPath; -+ args.at(0).value().variant().value() >> namedPath; -+ return namedPath; -+ } -+ return NamedDBusObjectPath(); -+ } -+ NumberedDBusObjectPath getUser() { -+ QDBusMessage message = QDBusMessage::createMethodCall(service(), path(), DBUS_PROPERTIES_IFACE, DBUS_PROPERTIES_GET); -+ message << interface() << SYSTEMD_USER_PROPERTY; -+ QDBusMessage reply = QDBusConnection::systemBus().call(message); -+ -+ QVariantList args = reply.arguments(); -+ if (!args.isEmpty()) { -+ NumberedDBusObjectPath numberedPath; -+ args.at(0).value().variant().value() >> numberedPath; -+ return numberedPath; -+ } -+ return NumberedDBusObjectPath(); -+ } -+ void getSessionLocation(SessEnt &se) -+ { -+ se.tty = (property("Type").toString() != QLatin1String("x11")); -+ se.display = property(se.tty ? "TTY" : "Display").toString(); -+ se.vt = property("VTNr").toInt(); -+ } -+}; -+ - class CKManager : public QDBusInterface - { - public: -@@ -68,9 +228,26 @@ - CKSession(const QDBusObjectPath &path) : - QDBusInterface( - QLatin1String("org.freedesktop.ConsoleKit"), -- path.path(), -+ path.path(), - QLatin1String("org.freedesktop.ConsoleKit.Session"), - QDBusConnection::systemBus()) {} -+ void getSessionLocation(SessEnt &se) -+ { -+ QString tty; -+ QDBusReply r = call(QLatin1String("GetX11Display")); -+ if (r.isValid() && !r.value().isEmpty()) { -+ QDBusReply r2 = call(QLatin1String("GetX11DisplayDevice")); -+ tty = r2.value(); -+ se.display = r.value(); -+ se.tty = false; -+ } else { -+ QDBusReply r2 = call(QLatin1String("GetDisplayDevice")); -+ tty = r2.value(); -+ se.display = tty; -+ se.tty = true; -+ } -+ se.vt = tty.mid(strlen("/dev/tty")).toInt(); -+ } - }; - - class GDMFactory : public QDBusInterface -@@ -115,6 +292,10 @@ - const char *ptr; - struct sockaddr_un sa; - -+ qDBusRegisterMetaType(); -+ qDBusRegisterMetaType >(); -+ qDBusRegisterMetaType(); -+ - if (DMType == Dunno) { - if (!(dpy = ::getenv("DISPLAY"))) - DMType = NoDM; -@@ -242,17 +423,31 @@ - - static bool getCurrentSeat(QDBusObjectPath *currentSession, QDBusObjectPath *currentSeat) - { -- CKManager man; -- QDBusReply r = man.call(QLatin1String("GetCurrentSession")); -+ SystemdManager man; -+ QDBusReply r = man.call(QLatin1String("GetSessionByPID"), (uint) QCoreApplication::applicationPid()); - if (r.isValid()) { -- CKSession sess(r.value()); -+ SystemdSession sess(r.value()); - if (sess.isValid()) { -- QDBusReply r2 = sess.call(QLatin1String("GetSeatId")); -- if (r2.isValid()) { -- if (currentSession) -- *currentSession = r.value(); -- *currentSeat = r2.value(); -- return true; -+ NamedDBusObjectPath namedPath = sess.getSeat(); -+ if (currentSession) -+ *currentSession = r.value(); -+ *currentSeat = namedPath.path; -+ return true; -+ } -+ } -+ else { -+ CKManager man; -+ QDBusReply r = man.call(QLatin1String("GetCurrentSession")); -+ if (r.isValid()) { -+ CKSession sess(r.value()); -+ if (sess.isValid()) { -+ QDBusReply r2 = sess.call(QLatin1String("GetSeatId")); -+ if (r2.isValid()) { -+ if (currentSession) -+ *currentSession = r.value(); -+ *currentSeat = r2.value(); -+ return true; -+ } - } - } - } -@@ -261,44 +456,44 @@ - - static QList getSessionsForSeat(const QDBusObjectPath &path) - { -- CKSeat seat(path); -- if (seat.isValid()) { -- QDBusReply > r = seat.call(QLatin1String("GetSessions")); -- if (r.isValid()) { -- // This will contain only local sessions: -- // - this is only ever called when isSwitchable() is true => local seat -- // - remote logins into the machine are assigned to other seats -- return r.value(); -+ if (path.path().startsWith(SYSTEMD_BASE_PATH)) { // systemd path incoming -+ SystemdSeat seat(path); -+ if (seat.isValid()) { -+ QList r = seat.getSessions(); -+ QList result; -+ foreach (const NamedDBusObjectPath &namedPath, r) -+ result.append(namedPath.path); -+ // This pretty much can't contain any other than local sessions as the seat is retrieved from the current session -+ return result; -+ } -+ } -+ else if (path.path().startsWith("/org/freedesktop/ConsoleKit")) { -+ CKSeat seat(path); -+ if (seat.isValid()) { -+ QDBusReply > r = seat.call(QLatin1String("GetSessions")); -+ if (r.isValid()) { -+ // This will contain only local sessions: -+ // - this is only ever called when isSwitchable() is true => local seat -+ // - remote logins into the machine are assigned to other seats -+ return r.value(); -+ } - } - } - return QList(); - } - --static void getSessionLocation(CKSession &lsess, SessEnt &se) --{ -- QString tty; -- QDBusReply r = lsess.call(QLatin1String("GetX11Display")); -- if (r.isValid() && !r.value().isEmpty()) { -- QDBusReply r2 = lsess.call(QLatin1String("GetX11DisplayDevice")); -- tty = r2.value(); -- se.display = r.value(); -- se.tty = false; -- } else { -- QDBusReply r2 = lsess.call(QLatin1String("GetDisplayDevice")); -- tty = r2.value(); -- se.display = tty; -- se.tty = true; -- } -- se.vt = tty.mid(strlen("/dev/tty")).toInt(); --} -- - #ifndef KDM_NO_SHUTDOWN - bool - KDisplayManager::canShutdown() - { - if (DMType == NewGDM || DMType == NoDM || DMType == LightDM) { -+ QDBusReply canPowerOff = SystemdManager().call(QLatin1String("CanPowerOff")); -+ if (canPowerOff.isValid()) -+ return canPowerOff.value() != QLatin1String("no"); - QDBusReply canStop = CKManager().call(QLatin1String("CanStop")); -- return (canStop.isValid() && canStop.value()); -+ if (canStop.isValid()) -+ return canStop.value(); -+ return false; - } - - if (DMType == OldKDM) -@@ -329,9 +524,21 @@ - return; - - if (DMType == NewGDM || DMType == NoDM || DMType == LightDM) { -- // FIXME: entirely ignoring shutdownMode -- CKManager().call(QLatin1String( -- shutdownType == KWorkSpace::ShutdownTypeReboot ? "Restart" : "Stop")); -+ // systemd supports only 2 modes: -+ // * interactive = true: brings up a PolicyKit prompt if other sessions are active -+ // * interactive = false: rejects the shutdown if other sessions are active -+ // There are no schedule or force modes. -+ // We try to map our 4 shutdown modes in the sanest way. -+ bool interactive = (shutdownMode == KWorkSpace::ShutdownModeInteractive -+ || shutdownMode == KWorkSpace::ShutdownModeForceNow); -+ QDBusReply check = SystemdManager().call(QLatin1String( -+ shutdownType == KWorkSpace::ShutdownTypeReboot ? "Reboot" : "PowerOff"), interactive); -+ if (!check.isValid()) { -+ // FIXME: entirely ignoring shutdownMode -+ CKManager().call(QLatin1String( -+ shutdownType == KWorkSpace::ShutdownTypeReboot ? "Restart" : "Stop")); -+ // if even CKManager call fails, there is nothing more to be done -+ } - return; - } - -@@ -406,9 +613,15 @@ - if (DMType == NewGDM || DMType == LightDM) { - QDBusObjectPath currentSeat; - if (getCurrentSeat(0, ¤tSeat)) { -- CKSeat seat(currentSeat); -- if (seat.isValid()) { -- QDBusReply r = seat.call(QLatin1String("CanActivateSessions")); -+ SystemdSeat SDseat(currentSeat); -+ if (SDseat.isValid()) { -+ QVariant prop = SDseat.property("CanMultiSession"); -+ if (prop.isValid()) -+ return prop.toBool(); -+ } -+ CKSeat CKseat(currentSeat); -+ if (CKseat.isValid()) { -+ QDBusReply r = CKseat.call(QLatin1String("CanActivateSessions")); - if (r.isValid()) - return r.value(); - } -@@ -465,26 +678,61 @@ - if (DMType == OldKDM) - return false; - -- if (DMType == NewGDM || DMType == LightDM) { -+ // FIXME TODO WARNING HACK beware of this workaround, will get rid of it in a few days (if it's not spring 2013 and this line is still here, please smack mbriza-at-redhat-dot-com) -+ if (DMType != OldGDM) { - QDBusObjectPath currentSession, currentSeat; - if (getCurrentSeat(¤tSession, ¤tSeat)) { -- foreach (const QDBusObjectPath &sp, getSessionsForSeat(currentSeat)) { -- CKSession lsess(sp); -- if (lsess.isValid()) { -- SessEnt se; -- getSessionLocation(lsess, se); -- // "Warning: we haven't yet defined the allowed values for this property. -- // It is probably best to avoid this until we do." -- QDBusReply r = lsess.call(QLatin1String("GetSessionType")); -- if (r.value() != QLatin1String("LoginWindow")) { -- QDBusReply r2 = lsess.call(QLatin1String("GetUnixUser")); -- se.user = KUser(K_UID(r2.value())).loginName(); -- se.session = ""; -+ // we'll divide the code in two branches to reduce the overhead of calls to non-existent services -+ // systemd part // preferred -+ if (QDBusConnection::systemBus().interface()->isServiceRegistered(SYSTEMD_SERVICE)) { -+ foreach (const QDBusObjectPath &sp, getSessionsForSeat(currentSeat)) { -+ SystemdSession lsess(sp); -+ if (lsess.isValid()) { -+ SessEnt se; -+ lsess.getSessionLocation(se); -+ if ((lsess.property("Class").toString() != QLatin1String("greeter")) && -+ (lsess.property("State").toString() == QLatin1String("online") || -+ lsess.property("State").toString() == QLatin1String("active"))) { -+ NumberedDBusObjectPath numberedPath = lsess.getUser(); -+ se.display = lsess.property("Display").toString(); -+ se.vt = lsess.property("VTNr").toInt(); -+ se.user = KUser(K_UID(numberedPath.num)).loginName(); -+ /* TODO: -+ * regarding the session name in this, it IS possible to find it out - logind tracks the session leader PID -+ * the problem is finding out the name of the process, I could come only with reading /proc/PID/comm which -+ * doesn't seem exactly... right to me --mbriza -+ */ -+ se.session = ""; -+ se.self = lsess.property("Display").toString() == ::getenv("DISPLAY"); /* Bleh once again */ -+ se.tty = !lsess.property("TTY").toString().isEmpty(); -+ } -+ list.append(se); -+ } -+ } -+ } -+ // ConsoleKit part -+ else if (QDBusConnection::systemBus().interface()->isServiceRegistered("org.freedesktop.ConsoleKit")) { -+ foreach (const QDBusObjectPath &sp, getSessionsForSeat(currentSeat)) { -+ CKSession lsess(sp); -+ if (lsess.isValid()) { -+ SessEnt se; -+ lsess.getSessionLocation(se); -+ // "Warning: we haven't yet defined the allowed values for this property. -+ // It is probably best to avoid this until we do." -+ QDBusReply r = lsess.call(QLatin1String("GetSessionType")); -+ if (r.value() != QLatin1String("LoginWindow")) { -+ QDBusReply r2 = lsess.call(QLatin1String("GetUnixUser")); -+ se.user = KUser(K_UID(r2.value())).loginName(); -+ se.session = ""; -+ } -+ se.self = (sp == currentSession); -+ list.append(se); - } -- se.self = (sp == currentSession); -- list.append(se); - } - } -+ else { -+ return false; -+ } - return true; - } - return false; -@@ -507,21 +755,6 @@ - se.tty = false; - list.append(se); - } -- } else { -- if (!exec("list\talllocal\n", re)) -- return false; -- const QStringList sess = QString(re.data() + 3).split(QChar('\t'), QString::SkipEmptyParts); -- for (QStringList::ConstIterator it = sess.constBegin(); it != sess.constEnd(); ++it) { -- QStringList ts = (*it).split(QChar(',')); -- SessEnt se; -- se.display = ts[0]; -- se.vt = ts[1].mid(2).toInt(); -- se.user = ts[2]; -- se.session = ts[3]; -- se.self = (ts[4].indexOf('*') >= 0); -- se.tty = (ts[4].indexOf('t') >= 0); -- list.append(se); -- } - } - return true; - } -@@ -563,14 +796,35 @@ - bool - KDisplayManager::switchVT(int vt) - { -- if (DMType == NewGDM || DMType == LightDM) { -- QDBusObjectPath currentSeat; -- if (getCurrentSeat(0, ¤tSeat)) { -+ if (DMType == OldKDM) -+ return exec(QString("activate\tvt%1\n").arg(vt).toLatin1()); -+ -+ if (DMType == OldGDM) -+ return exec(QString("SET_VT %1\n").arg(vt).toLatin1()); -+ -+ QDBusObjectPath currentSeat; -+ if (getCurrentSeat(0, ¤tSeat)) { -+ // systemd part // preferred -+ if (QDBusConnection::systemBus().interface()->isServiceRegistered(SYSTEMD_SERVICE)) { -+ foreach (const QDBusObjectPath &sp, getSessionsForSeat(currentSeat)) { -+ SystemdSession lsess(sp); -+ if (lsess.isValid()) { -+ SessEnt se; -+ lsess.getSessionLocation(se); -+ if (se.vt == vt) { -+ lsess.call(SYSTEMD_SWITCH_CALL); -+ return true; -+ } -+ } -+ } -+ } -+ // ConsoleKit part -+ else if (QDBusConnection::systemBus().interface()->isServiceRegistered("org.freedesktop.ConsoleKit")) { - foreach (const QDBusObjectPath &sp, getSessionsForSeat(currentSeat)) { - CKSession lsess(sp); - if (lsess.isValid()) { - SessEnt se; -- getSessionLocation(lsess, se); -+ lsess.getSessionLocation(se); - if (se.vt == vt) { - if (se.tty) // ConsoleKit simply ignores these - return false; -@@ -580,13 +834,8 @@ - } - } - } -- return false; - } -- -- if (DMType == OldGDM) -- return exec(QString("SET_VT %1\n").arg(vt).toLatin1()); -- -- return exec(QString("activate\tvt%1\n").arg(vt).toLatin1()); -+ return false; - } - - void diff --git a/SOURCES/kde-workspace-4.10.3-redhat_startkde.patch b/SOURCES/kde-workspace-4.10.3-redhat_startkde.patch deleted file mode 100644 index 7797784..0000000 --- a/SOURCES/kde-workspace-4.10.3-redhat_startkde.patch +++ /dev/null @@ -1,189 +0,0 @@ -diff -up kde-workspace-4.10.3/startkde.cmake.redhat_startkde kde-workspace-4.10.3/startkde.cmake ---- kde-workspace-4.10.3/startkde.cmake.redhat_startkde 2013-05-02 23:36:53.024048031 -0500 -+++ kde-workspace-4.10.3/startkde.cmake 2013-05-07 07:25:41.518790494 -0500 -@@ -16,28 +16,20 @@ trap 'echo GOT SIGHUP' HUP - # we have to unset this for Darwin since it will screw up KDE's dynamic-loading - unset DYLD_FORCE_FLAT_NAMESPACE - --# in case we have been started with full pathname spec without being in PATH --bindir=`echo "$0" | sed -n 's,^\(/.*\)/[^/][^/]*$,\1,p'` --if [ -n "$bindir" ]; then -- qbindir=`$bindir/kde4-config --qt-binaries` -- if [ -n "$qbindir" ]; then -- case $PATH in -- $qbindir|$qbindir:*|*:$qbindir|*:$qbindir:*) ;; -- *) PATH=$qbindir:$PATH; export PATH;; -- esac -- fi -- case $PATH in -- $bindir|$bindir:*|*:$bindir|*:$bindir:*) ;; -- *) PATH=$bindir:$PATH; export PATH;; -- esac --fi -+# See http://bugzilla.redhat.com/537609 , a naive attempt to drop dep -+# on xmessage and allow alternatives like zenity. -+message() { -+ xmessage -geometry 500x100 "$1" > /dev/null 2>/dev/null || \ -+ zenity --info --text="$1" > /dev/null 2>/dev/null ||: -+ return $? -+} - - # Check if a KDE session already is running and whether it's possible to connect to X - kcheckrunning - kcheckrunning_result=$? - if test $kcheckrunning_result -eq 0 ; then - echo "KDE seems to be already running on this display." -- xmessage -geometry 500x100 "KDE seems to be already running on this display." > /dev/null 2>/dev/null -+ message "KDE seems to be already running on this display." > /dev/null 2>/dev/null - exit 1 - elif test $kcheckrunning_result -eq 2 ; then - echo "\$DISPLAY is not set or cannot connect to the X server." -@@ -81,11 +73,19 @@ krandrrc [Screen2] - krandrrc [Screen3] - kcmfonts General forceFontDPI 0 - kdeglobals Locale Language '' # trigger requesting languages from KLocale -+kdeglobals Locale Country '' - EOF -+# read the default KSplash theme to use out of kde-settings -+if [ -e /usr/share/kde-settings/kde-profile/default/share/config/ksplashrc ] -+ then eval `grep '^Theme=' /usr/share/kde-settings/kde-profile/default/share/config/ksplashrc` -+ if [ -n "$Theme" ] -+ then sed -i -e "s/Default/$Theme/g" $kdehome/share/config/startupconfigkeys -+ fi -+fi - kstartupconfig4 - returncode=$? - if test $returncode -ne 0; then -- xmessage -geometry 500x100 "kstartupconfig4 does not exist or fails. The error code is $returncode. Check your installation." -+ message "kstartupconfig4 does not exist or fails. The error code is $returncode. Check your installation." - exit 1 - fi - [ -r $kdehome/share/config/startupconfig ] && . $kdehome/share/config/startupconfig -@@ -108,7 +108,7 @@ if test -n "$kcminputrc_mouse_cursorthem - fi - fi - --. krandrstartup -+KRANDRSTARTUP=`which krandrstartup 2>/dev/null` && . $KRANDRSTARTUP - - if test "$kcmfonts_general_forcefontdpi" -ne 0; then - xrdb -quiet -merge -nocpp <&2 - test -n "$ksplash_pid" && kill "$ksplash_pid" 2>/dev/null -- xmessage -geometry 600x100 "Call to lnusertemp failed (temporary directories full?). Check your installation." -+ message "Call to lnusertemp failed (temporary directories full?). Check your installation." - exit 1 - fi - done -@@ -281,7 +245,7 @@ if qdbus >/dev/null 2>/dev/null; then - else - echo 'startkde: Could not start D-Bus. Can you call qdbus?' 1>&2 - test -n "$ksplash_pid" && kill "$ksplash_pid" 2>/dev/null -- xmessage -geometry 500x100 "Could not start D-Bus. Can you call qdbus?" -+ message "Could not start D-Bus. Can you call qdbus?" - exit 1 - fi - -@@ -322,14 +286,12 @@ export KDE_SESSION_UID - XDG_CURRENT_DESKTOP=KDE - export XDG_CURRENT_DESKTOP - --# We set LD_BIND_NOW to increase the efficiency of kdeinit. --# kdeinit unsets this variable before loading applications. --LD_BIND_NOW=true @KDE4_LIBEXEC_INSTALL_DIR@/start_kdeinit_wrapper +kcminit_startup -+@KDE4_LIBEXEC_INSTALL_DIR@/start_kdeinit_wrapper +kcminit_startup - if test $? -ne 0; then - # Startup error - echo 'startkde: Could not start kdeinit4. Check your installation.' 1>&2 - test -n "$ksplash_pid" && kill "$ksplash_pid" 2>/dev/null -- xmessage -geometry 500x100 "Could not start kdeinit4. Check your installation." -+ message "Could not start kdeinit4. Check your installation." - exit 1 - fi - -@@ -353,7 +315,7 @@ if test $? -eq 255; then - # Startup error - echo 'startkde: Could not start ksmserver. Check your installation.' 1>&2 - test -n "$ksplash_pid" && kill "$ksplash_pid" 2>/dev/null -- xmessage -geometry 500x100 "Could not start ksmserver. Check your installation." -+ message "Could not start ksmserver. Check your installation." - fi - - wait_drkonqi=`kreadconfig --file startkderc --group WaitForDrKonqi --key Enabled --default true` -@@ -388,7 +350,8 @@ kde3 dcopserver_shutdown --wait 2>/dev/n - echo 'startkde: Running shutdown scripts...' 1>&2 - - # Run scripts found in $KDEDIRS/shutdown --for prefix in `echo "$libpath" | sed -n -e 's,/lib[^/]*/,/shutdown/,p'`; do -+shutdownpath=/etc/kde/shutdown/ -+for prefix in `echo "$libpath" | sed -n -e 's,/lib[^/]*/,/shutdown/,p'` $shutdownpath; do - for file in `ls "$prefix" 2> /dev/null | egrep -v '(~|\.bak)$'`; do - test -x "$prefix$file" && "$prefix$file" - done diff --git a/SOURCES/kde-workspace-4.10.4-kdm-harden.patch b/SOURCES/kde-workspace-4.10.4-kdm-harden.patch new file mode 100644 index 0000000..64d349c --- /dev/null +++ b/SOURCES/kde-workspace-4.10.4-kdm-harden.patch @@ -0,0 +1,23 @@ +--- kde-workspace-4.10.4/kdm/backend/CMakeLists.txt.harden 2013-07-18 13:59:39.736400898 +0200 ++++ kde-workspace-4.10.4/kdm/backend/CMakeLists.txt 2013-07-18 14:00:24.573302699 +0200 +@@ -52,7 +52,8 @@ endif(LIBSYSTEMD_LOGIN_FOUND AND LIBSYST + macro_add_file_dependencies(dm.h ${confci}) + macro_add_file_dependencies(error.c ${CMAKE_CURRENT_SOURCE_DIR}/printf.c) + kde4_add_executable(kdm NOGUI ${kdm_SRCS}) +-macro_add_compile_flags(kdm -U_REENTRANT) ++macro_add_compile_flags(kdm "-U_REENTRANT -fpie -pie") ++set_target_properties(kdm PROPERTIES LINK_FLAGS "-fpie -pie -Wl,-z,relro,-z,now ${LINK_FLAGS}") + target_link_libraries( kdm + ${X11_X11_LIB} ${X11_Xau_LIB} ${X11_Xdmcp_LIB} ${X11_X_EXTRA_LIBS} + ${UNIXAUTH_LIBRARIES} +--- kde-workspace-4.10.4/kdm/kfrontend/CMakeLists.txt.harden 2013-07-18 14:02:54.311974754 +0200 ++++ kde-workspace-4.10.4/kdm/kfrontend/CMakeLists.txt 2013-07-18 14:04:16.112795602 +0200 +@@ -71,6 +71,8 @@ endif (WITH_KDM_XCONSOLE) + + macro_add_file_dependencies(kdmconfig.h ${confci}) + kde4_add_executable(kdm_greet ${kdm_greet_SRCS}) ++macro_add_compile_flags(kdm_greet "-fpie -pie") ++set_target_properties(kdm_greet PROPERTIES LINK_FLAGS "-fpie -pie -Wl,-z,relro,-z,now ${LINK_FLAGS}") + target_link_libraries(kdm_greet ${KDE4_KDEUI_LIBS} ${QT_QTXML_LIBRARY} ${X11_X11_LIB} ${POSIX4_LIBRARIES}) + if (X11_XTest_FOUND) + target_link_libraries(kdm_greet ${X11_XTest_LIB}) diff --git a/SOURCES/kde-workspace-4.10.5-CVE-2013-4132.patch b/SOURCES/kde-workspace-4.10.5-CVE-2013-4132.patch deleted file mode 100644 index 1b6a432..0000000 --- a/SOURCES/kde-workspace-4.10.5-CVE-2013-4132.patch +++ /dev/null @@ -1,103 +0,0 @@ -From 45b7f137fbc0b942fd2c9b4e8d8c1f0293e64ba7 Mon Sep 17 00:00:00 2001 -From: Michael Pyne -Date: Sat, 29 Jun 2013 16:13:20 -0400 -Subject: [PATCH 1/6] kdm, kcheckpass: Check for NULL return from crypt(3) and - friends. - -Potential issue noted and fixed by Mancha . - -Patch reviewed by myself and ossi. Backported to 4.10 by myself. - -REVIEW:111261 -FIXED-IN:4.10.5 ---- - kcheckpass/checkpass_etcpasswd.c | 3 ++- - kcheckpass/checkpass_osfc2passwd.c | 3 ++- - kcheckpass/checkpass_shadow.c | 2 +- - kdm/backend/client.c | 7 +++++-- - 4 files changed, 10 insertions(+), 5 deletions(-) - -diff --git a/kcheckpass/checkpass_etcpasswd.c b/kcheckpass/checkpass_etcpasswd.c -index 1dbe06f..e261b7c 100644 ---- a/kcheckpass/checkpass_etcpasswd.c -+++ b/kcheckpass/checkpass_etcpasswd.c -@@ -35,6 +35,7 @@ AuthReturn Authenticate(const char *method, - { - struct passwd *pw; - char *passwd; -+ char *crpt_passwd; - - if (strcmp(method, "classic")) - return AuthError; -@@ -49,7 +50,7 @@ AuthReturn Authenticate(const char *method, - if (!(passwd = conv(ConvGetHidden, 0))) - return AuthAbort; - -- if (!strcmp(pw->pw_passwd, crypt(passwd, pw->pw_passwd))) { -+ if ((crpt_passwd = crypt(passwd, pw->pw_passwd)) && !strcmp(pw->pw_passwd, crpt_passwd)) { - dispose(passwd); - return AuthOk; /* Success */ - } -diff --git a/kcheckpass/checkpass_osfc2passwd.c b/kcheckpass/checkpass_osfc2passwd.c -index 9a074f9..d181233 100644 ---- a/kcheckpass/checkpass_osfc2passwd.c -+++ b/kcheckpass/checkpass_osfc2passwd.c -@@ -38,6 +38,7 @@ AuthReturn Authenticate(const char *method, - const char *login, char *(*conv) (ConvRequest, const char *)) - { - char *passwd; -+ char *crpt_passwd; - char c2passwd[256]; - - if (strcmp(method, "classic")) -@@ -52,7 +53,7 @@ AuthReturn Authenticate(const char *method, - if (!(passwd = conv(ConvGetHidden, 0))) - return AuthAbort; - -- if (!strcmp(c2passwd, osf1c2crypt(passwd, c2passwd))) { -+ if ((crpt_passwd = osf1c2crypt(passwd, c2passwd)) && !strcmp(c2passwd, crpt_passwd)) { - dispose(passwd); - return AuthOk; /* Success */ - } -diff --git a/kcheckpass/checkpass_shadow.c b/kcheckpass/checkpass_shadow.c -index ec3a4e0..c0f6913 100644 ---- a/kcheckpass/checkpass_shadow.c -+++ b/kcheckpass/checkpass_shadow.c -@@ -69,7 +69,7 @@ AuthReturn Authenticate(const char *method, - crpt_passwd = crypt(typed_in_password, password); - #endif - -- if (!strcmp(password, crpt_passwd )) { -+ if (crpt_passwd && !strcmp(password, crpt_passwd )) { - dispose(typed_in_password); - return AuthOk; /* Success */ - } -diff --git a/kdm/backend/client.c b/kdm/backend/client.c -index bdff6da..26bb0b4 100644 ---- a/kdm/backend/client.c -+++ b/kdm/backend/client.c -@@ -540,6 +540,9 @@ verify(GConvFunc gconv, int rootok) - # if defined(HAVE_STRUCT_PASSWD_PW_EXPIRE) || defined(USESHADOW) - int tim, expir, warntime, quietlog; - # endif -+# if !defined(ultrix) && !defined(__ultrix__) && (defined(HAVE_PW_ENCRYPT) || defined(HAVE_CRYPT)) -+ char *crpt_passwd; -+# endif - #endif - - debug("verify ...\n"); -@@ -752,9 +755,9 @@ verify(GConvFunc gconv, int rootok) - # if defined(ultrix) || defined(__ultrix__) - if (authenticate_user(p, curpass, 0) < 0) - # elif defined(HAVE_PW_ENCRYPT) -- if (strcmp(pw_encrypt(curpass, p->pw_passwd), p->pw_passwd)) -+ if (!(crpt_passwd = pw_encrypt(curpass, p->pw_passwd)) || strcmp(crpt_passwd, p->pw_passwd)) - # elif defined(HAVE_CRYPT) -- if (strcmp(crypt(curpass, p->pw_passwd), p->pw_passwd)) -+ if (!(crpt_passwd = crypt(curpass, p->pw_passwd)) || strcmp(crpt_passwd, p->pw_passwd)) - # else - if (strcmp(curpass, p->pw_passwd)) - # endif --- -1.8.3.1 - diff --git a/SOURCES/kde-workspace-4.10.5-CVE-2013-4133.patch b/SOURCES/kde-workspace-4.10.5-CVE-2013-4133.patch deleted file mode 100644 index 0edd682..0000000 --- a/SOURCES/kde-workspace-4.10.5-CVE-2013-4133.patch +++ /dev/null @@ -1,60 +0,0 @@ -From 2c810db3e41d56ad7dd8ec3436f3cf3abcc31983 Mon Sep 17 00:00:00 2001 -From: Andreas Hartmetz -Date: Tue, 2 Jul 2013 18:35:35 +0200 -Subject: [PATCH 6/6] Fix pixmap leak when the tray icon changes (e.g. when - it's animated). - -This could easily leak 4KB/second of X pixmap memory. -All the actual difference comes from the QPixmap::ExplicitlyShared -argument, the rest is making some wonky-looking but working code look -less wonky. - -BUG: 314919 -(cherry picked from commit ec8e405ca447ba5bc5a9f6a2a12e2fa90412a0d4) ---- - .../systemtray/protocols/fdo/x11embedcontainer.cpp | 19 ++++++++++--------- - 1 file changed, 10 insertions(+), 9 deletions(-) - -diff --git a/plasma/generic/applets/systemtray/protocols/fdo/x11embedcontainer.cpp b/plasma/generic/applets/systemtray/protocols/fdo/x11embedcontainer.cpp -index 1826512..a5bc826 100644 ---- a/plasma/generic/applets/systemtray/protocols/fdo/x11embedcontainer.cpp -+++ b/plasma/generic/applets/systemtray/protocols/fdo/x11embedcontainer.cpp -@@ -194,8 +194,7 @@ void X11EmbedContainer::paintEvent(QPaintEvent *event) - - // Taking a detour via a QPixmap is unfortunately the only way we can get - // the window contents into Qt's backing store. -- QPixmap pixmap(size()); -- pixmap = toX11Pixmap(pixmap); -+ QPixmap pixmap = toX11Pixmap(QPixmap(size())); - pixmap.fill(Qt::transparent); - XRenderComposite(x11Info().display(), PictOpSrc, d->picture, None, pixmap.x11PictureHandle(), - 0, 0, 0, 0, 0, 0, width(), height()); -@@ -232,16 +231,18 @@ void X11EmbedContainer::setBackgroundPixmap(QPixmap background) - // NOTE: The alpha-channel is not preserved if it exists, but for X pixmaps it generally should not be needed anyway. - QPixmap X11EmbedContainer::toX11Pixmap(const QPixmap& pix) - { -- if(pix.handle() != 0) // X11 pixmap -+ if (pix.handle() != 0) // X11 pixmap - return pix; -+ QPixmap ret; - Pixmap xpix = XCreatePixmap(pix.x11Info().display(), RootWindow(pix.x11Info().display(), pix.x11Info().screen()), - pix.width(), pix.height(), QX11Info::appDepth()); -- QPixmap wrk = QPixmap::fromX11Pixmap(xpix); -- QPainter paint(&wrk); -- paint.drawPixmap(0, 0, pix); -- paint.end(); -- QPixmap ret = wrk.copy(); -- wrk = QPixmap(); // reset, so that xpix can be freed (QPixmap does not own it) -+ { -+ QPixmap wrk = QPixmap::fromX11Pixmap(xpix, QPixmap::ExplicitlyShared); -+ QPainter paint(&wrk); -+ paint.drawPixmap(0, 0, pix); -+ paint.end(); -+ ret = wrk.copy(); -+ } // free resources so that xpix can be freed (QPixmap does not own it) - XFreePixmap(pix.x11Info().display(), xpix); - return ret; - } --- -1.8.3.1 - diff --git a/SOURCES/kde-workspace-4.10.5-bz#1060058.patch b/SOURCES/kde-workspace-4.10.5-bz#1060058.patch index ab3b816..e84ebe7 100644 --- a/SOURCES/kde-workspace-4.10.5-bz#1060058.patch +++ b/SOURCES/kde-workspace-4.10.5-bz#1060058.patch @@ -1,14 +1,15 @@ -diff -up kde-workspace-4.10.5/libs/plasmaclock/clockapplet.cpp.orig kde-workspace-4.10.5/libs/plasmaclock/clockapplet.cpp ---- kde-workspace-4.10.5/libs/plasmaclock/clockapplet.cpp.orig 2014-08-18 14:20:18.050240750 +0200 -+++ kde-workspace-4.10.5/libs/plasmaclock/clockapplet.cpp 2014-08-18 14:42:14.695048549 +0200 -@@ -373,7 +373,9 @@ void ClockApplet::createConfigurationInt - - QWidget *generalWidget = new QWidget(); - d->generalUi.setupUi(generalWidget); +diff --git a/libs/plasmaclock/clockapplet.cpp b/libs/plasmaclock/clockapplet.cpp +index 482820b..18f892c 100644 +--- a/libs/plasmaclock/clockapplet.cpp ++++ b/libs/plasmaclock/clockapplet.cpp +@@ -377,8 +377,9 @@ void ClockApplet::createConfigurationInterface(KConfigDialog *parent) + if (d->kttsAvailable) { + QWidget *generalWidget = new QWidget(); + d->generalUi.setupUi(generalWidget); +#ifdef JOVIE_FOUND - parent->addPage(generalWidget, i18nc("General configuration page", "General"), Applet::icon()); + parent->addPage(generalWidget, i18nc("General configuration page", "General"), Applet::icon()); +- +#endif - - d->generalUi.intervalCombo->addItem(i18nc("@inmenu interval between speaking clock", "Never"), QVariant(0)); - d->generalUi.intervalCombo->addItem(i18nc("@inmenu interval between speaking clock", "Every minute"), QVariant(1)); -diff -up kde-workspace-4.10.5/libs/plasmaclock/generalConfig.ui.orig kde-workspace-4.10.5/libs/plasmaclock/generalConfig.ui + d->generalUi.intervalCombo->addItem(i18nc("@inmenu interval between speaking clock", "Never"), QVariant(0)); + d->generalUi.intervalCombo->addItem(i18nc("@inmenu interval between speaking clock", "Every minute"), QVariant(1)); + d->generalUi.intervalCombo->addItem(i18nc("@inmenu interval between speaking clock", "Every 2 minutes"), QVariant(2)); diff --git a/SOURCES/kde-workspace-4.10.5-solid-bz#1109987.patch b/SOURCES/kde-workspace-4.10.5-solid-bz#1109987.patch deleted file mode 100644 index 75272b5..0000000 --- a/SOURCES/kde-workspace-4.10.5-solid-bz#1109987.patch +++ /dev/null @@ -1,67 +0,0 @@ -commit 775a99e8bc1e9c5e277ff17a84a406227a5a32fb -Author: Emmanuel Pescosta -Date: Tue Jun 17 17:35:19 2014 +0200 - - Consider additional batteries for power management (when the critical battery timer is running) - - After resuming from suspend, all batteries are added - to powerdevil. When a battery, with charge lower or - equal than the critical charge percentage is added, the - critical battery timer will be started. - - In the current version the critical battery timeout can - only be interrupted by plugging in AC. - - But if the system has more than one battery, the global - charge percentage can be greater than the critical charge - percentage and so the system shouldn't suspend. To achive - this behaviour, we calculate the global charge percentage - whenever a new battery was added and if the critical - battery timer is running and the global charge is high - enough, we stop the timer. - - Also we use the already calculated global charge percentage - for the battery charge percentage notification instead of - the charge of each individual battery. - - With this patch, the user can not only interrupt the critical - timer by plugging in the AC but also by plugging in a new or - additional battery (if the battery has enough charge). - - Note: The 30 sec timeout message will still popup. - - Tested with a Thinkpad T440s (two batteries) - - @Philipp Paris: Thanks for testing! - - BUG: 329537 - BUG: 325707 - FIXED-IN: 4.11.11 - REVIEW: 118801 - -diff --git a/powerdevil/daemon/powerdevilcore.cpp b/powerdevil/daemon/powerdevilcore.cpp -index 76e1b82..d096d74 100644 ---- a/powerdevil/daemon/powerdevilcore.cpp -+++ b/powerdevil/daemon/powerdevilcore.cpp -@@ -456,8 +456,20 @@ void Core::onDeviceAdded(const QString& udi) - } - m_loadedBatteriesUdi.append(udi); - -+ // Compute the current global percentage -+ int globalChargePercent = 0; -+ for (QHash::const_iterator i = m_batteriesPercent.constBegin(); i != m_batteriesPercent.constEnd(); ++i) { -+ globalChargePercent += i.value(); -+ } -+ -+ // If a new battery has been added, let's clear some pending suspend actions if the new global batteries percentage is -+ // higher than the battery low level. (See bug 329537) -+ if (m_criticalBatteryTimer->isActive() && globalChargePercent > PowerDevilSettings::batteryCriticalLevel()) { -+ m_criticalBatteryTimer->stop(); -+ } -+ - // So we get a "Battery is low" notification directly on system startup if applicable -- emitBatteryChargePercentNotification(b->chargePercent(), 100); -+ emitBatteryChargePercentNotification(globalChargePercent, 100); - } - - void Core::onDeviceRemoved(const QString& udi) diff --git a/SOURCES/kde-workspace-4.10.90-battery-plasmoid-showremainingtime.patch b/SOURCES/kde-workspace-4.10.90-battery-plasmoid-showremainingtime.patch new file mode 100644 index 0000000..c39c20b --- /dev/null +++ b/SOURCES/kde-workspace-4.10.90-battery-plasmoid-showremainingtime.patch @@ -0,0 +1,12 @@ +diff -up kde-workspace-4.10.90/plasma/generic/applets/batterymonitor/contents/config/main.xml.showremainingtime kde-workspace-4.10.90/plasma/generic/applets/batterymonitor/contents/config/main.xml +--- kde-workspace-4.10.90/plasma/generic/applets/batterymonitor/contents/config/main.xml.showremainingtime 2013-06-27 16:25:47.034307439 -0500 ++++ kde-workspace-4.10.90/plasma/generic/applets/batterymonitor/contents/config/main.xml 2013-06-27 16:26:29.264785099 -0500 +@@ -7,7 +7,7 @@ + + + +- false ++ true + + + diff --git a/SOURCES/kde-workspace-4.10.90-kde#171685.patch b/SOURCES/kde-workspace-4.10.90-kde#171685.patch new file mode 100644 index 0000000..8370260 --- /dev/null +++ b/SOURCES/kde-workspace-4.10.90-kde#171685.patch @@ -0,0 +1,12 @@ +diff -up kde-workspace-4.10.90/kwin/workspace.h.kde#171685 kde-workspace-4.10.90/kwin/workspace.h +--- kde-workspace-4.10.90/kwin/workspace.h.kde#171685 2013-06-27 16:31:25.455394124 -0500 ++++ kde-workspace-4.10.90/kwin/workspace.h 2013-06-27 16:33:44.773681745 -0500 +@@ -113,7 +113,7 @@ public: + void setShouldGetFocus(Client*); + bool activateNextClient(Client* c); + bool focusChangeEnabled() { +- return block_focus == 0; ++ return block_focus < 1; + } + + /** diff --git a/SOURCES/kde-workspace-4.11-bz#1040678.patch b/SOURCES/kde-workspace-4.11-bz#1040678.patch deleted file mode 100644 index f016ba2..0000000 --- a/SOURCES/kde-workspace-4.11-bz#1040678.patch +++ /dev/null @@ -1,22 +0,0 @@ -commit 293b9296bef4920c42eb96380e24a8fe42729b1b -Author: Thomas Lübking -Date: Wed Jul 31 12:14:13 2013 +0200 - - desk-grid: bind sc editor keyChange to kcm changed - - BUG: 316177 - FIXED-IN: 4.11 - REVIEW: 111812 - -diff --git a/kwin/effects/desktopgrid/desktopgrid_config.cpp b/kwin/effects/desktopgrid/desktopgrid_config.cpp -index 45fd205..a3ba77a 100644 ---- a/kwin/effects/desktopgrid/desktopgrid_config.cpp -+++ b/kwin/effects/desktopgrid/desktopgrid_config.cpp -@@ -77,6 +77,7 @@ DesktopGridEffectConfig::DesktopGridEffectConfig(QWidget* parent, const QVariant - addConfig(DesktopGridConfig::self(), m_ui); - connect(m_ui->kcfg_LayoutMode, SIGNAL(currentIndexChanged(int)), this, SLOT(layoutSelectionChanged())); - connect(m_ui->desktopNameAlignmentCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(changed())); -+ connect(m_ui->shortcutEditor, SIGNAL(keyChange()), this, SLOT(changed())); - - load(); - layoutSelectionChanged(); diff --git a/SOURCES/kde-workspace-4.11-bz#1109936.patch b/SOURCES/kde-workspace-4.11-bz#1109936.patch deleted file mode 100644 index f3f1c9c..0000000 --- a/SOURCES/kde-workspace-4.11-bz#1109936.patch +++ /dev/null @@ -1,2129 +0,0 @@ -diff --git a/kwin/composite.cpp b/kwin/composite.cpp -index ea540a4..e179b8b 100644 ---- a/kwin/composite.cpp -+++ b/kwin/composite.cpp -@@ -389,7 +389,7 @@ void Compositor::slotReinitialize() - - // Update any settings that can be set in the compositing kcm. - #ifdef KWIN_BUILD_SCREENEDGES -- Workspace::self()->screenEdge()->update(); -+ Workspace::self()->screenEdge()->recreateEdges(); - #endif - // Restart compositing - finish(); -diff --git a/kwin/desktoplayout.cpp b/kwin/desktoplayout.cpp -index f275782..5470eab 100644 ---- a/kwin/desktoplayout.cpp -+++ b/kwin/desktoplayout.cpp -@@ -53,6 +53,7 @@ void Workspace::updateDesktopLayout() - m_screenEdge.reserveDesktopSwitching(true, m_screenEdgeOrientation); - } - #endif -+ - } - - void Workspace::setNETDesktopLayout(Qt::Orientation orientation, int width, int height, -diff --git a/kwin/effects.cpp b/kwin/effects.cpp -index 112c814..8e87f10 100644 ---- a/kwin/effects.cpp -+++ b/kwin/effects.cpp -@@ -1108,28 +1108,30 @@ QPoint EffectsHandlerImpl::cursorPos() const - void EffectsHandlerImpl::checkElectricBorder(const QPoint &pos, Time time) - { - #ifdef KWIN_BUILD_SCREENEDGES -- Workspace::self()->screenEdge()->check(pos, time); -+ Workspace::self()->screenEdge()->check(pos, QDateTime::fromMSecsSinceEpoch(time)); - #else - Q_UNUSED(pos) - Q_UNUSED(time) - #endif - } - --void EffectsHandlerImpl::reserveElectricBorder(ElectricBorder border) -+void EffectsHandlerImpl::reserveElectricBorder(ElectricBorder border, Effect *effect) - { - #ifdef KWIN_BUILD_SCREENEDGES -- Workspace::self()->screenEdge()->reserve(border); -+ Workspace::self()->screenEdge()->reserve(border, effect, "borderActivated"); - #else - Q_UNUSED(border) -+ Q_UNUSED(effect) - #endif - } - --void EffectsHandlerImpl::unreserveElectricBorder(ElectricBorder border) -+void EffectsHandlerImpl::unreserveElectricBorder(ElectricBorder border, Effect *effect) - { - #ifdef KWIN_BUILD_SCREENEDGES -- Workspace::self()->screenEdge()->unreserve(border); -+ Workspace::self()->screenEdge()->unreserve(border, effect); - #else - Q_UNUSED(border) -+ Q_UNUSED(effect) - #endif - } - -diff --git a/kwin/effects.h b/kwin/effects.h -index d985de4..2a56cdd 100644 ---- a/kwin/effects.h -+++ b/kwin/effects.h -@@ -140,8 +140,8 @@ public: - virtual void checkInputWindowStacking(); - - virtual void checkElectricBorder(const QPoint &pos, Time time); -- virtual void reserveElectricBorder(ElectricBorder border); -- virtual void unreserveElectricBorder(ElectricBorder border); -+ virtual void reserveElectricBorder(ElectricBorder border, Effect *effect); -+ virtual void unreserveElectricBorder(ElectricBorder border, Effect *effect); - virtual void reserveElectricBorderSwitching(bool reserve, Qt::Orientations o); - - virtual unsigned long xrenderBufferPicture(); -diff --git a/kwin/effects/cube/cube.cpp b/kwin/effects/cube/cube.cpp -index 3fffbd0..840bcf0 100644 ---- a/kwin/effects/cube/cube.cpp -+++ b/kwin/effects/cube/cube.cpp -@@ -122,13 +122,13 @@ void CubeEffect::reconfigure(ReconfigureFlags) - { - CubeConfig::self()->readConfig(); - foreach (ElectricBorder border, borderActivate) { -- effects->unreserveElectricBorder(border); -+ effects->unreserveElectricBorder(border, this); - } - foreach (ElectricBorder border, borderActivateCylinder) { -- effects->unreserveElectricBorder(border); -+ effects->unreserveElectricBorder(border, this); - } - foreach (ElectricBorder border, borderActivateSphere) { -- effects->unreserveElectricBorder(border); -+ effects->unreserveElectricBorder(border, this); - } - borderActivate.clear(); - borderActivateCylinder.clear(); -@@ -138,21 +138,21 @@ void CubeEffect::reconfigure(ReconfigureFlags) - borderList = CubeConfig::borderActivate(); - foreach (int i, borderList) { - borderActivate.append(ElectricBorder(i)); -- effects->reserveElectricBorder(ElectricBorder(i)); -+ effects->reserveElectricBorder(ElectricBorder(i), this); - } - borderList.clear(); - borderList.append(int(ElectricNone)); - borderList = CubeConfig::borderActivateCylinder(); - foreach (int i, borderList) { - borderActivateCylinder.append(ElectricBorder(i)); -- effects->reserveElectricBorder(ElectricBorder(i)); -+ effects->reserveElectricBorder(ElectricBorder(i), this); - } - borderList.clear(); - borderList.append(int(ElectricNone)); - borderList = CubeConfig::borderActivateSphere(); - foreach (int i, borderList) { - borderActivateSphere.append(ElectricBorder(i)); -- effects->reserveElectricBorder(ElectricBorder(i)); -+ effects->reserveElectricBorder(ElectricBorder(i), this); - } - - cubeOpacity = (float)CubeConfig::opacity() / 100.0f; -@@ -218,13 +218,13 @@ void CubeEffect::reconfigure(ReconfigureFlags) - CubeEffect::~CubeEffect() - { - foreach (ElectricBorder border, borderActivate) { -- effects->unreserveElectricBorder(border); -+ effects->unreserveElectricBorder(border, this); - } - foreach (ElectricBorder border, borderActivateCylinder) { -- effects->unreserveElectricBorder(border); -+ effects->unreserveElectricBorder(border, this); - } - foreach (ElectricBorder border, borderActivateSphere) { -- effects->unreserveElectricBorder(border); -+ effects->unreserveElectricBorder(border, this); - } - delete wallpaper; - delete capTexture; -diff --git a/kwin/effects/desktopgrid/desktopgrid.cpp b/kwin/effects/desktopgrid/desktopgrid.cpp -index 61c814e..826ec9b 100644 ---- a/kwin/effects/desktopgrid/desktopgrid.cpp -+++ b/kwin/effects/desktopgrid/desktopgrid.cpp -@@ -90,7 +90,7 @@ DesktopGridEffect::DesktopGridEffect() - DesktopGridEffect::~DesktopGridEffect() - { - foreach (ElectricBorder border, borderActivate) { -- effects->unreserveElectricBorder(border); -+ effects->unreserveElectricBorder(border, this); - } - QHash< DesktopButtonsView*, EffectWindow* >::iterator i = m_desktopButtonsViews.begin(); - while (i != m_desktopButtonsViews.end()) { -@@ -105,12 +105,12 @@ void DesktopGridEffect::reconfigure(ReconfigureFlags) - DesktopGridConfig::self()->readConfig(); - - foreach (ElectricBorder border, borderActivate) { -- effects->unreserveElectricBorder(border); -+ effects->unreserveElectricBorder(border, this); - } - borderActivate.clear(); - foreach (int i, DesktopGridConfig::borderActivate()) { - borderActivate.append(ElectricBorder(i)); -- effects->reserveElectricBorder(ElectricBorder(i)); -+ effects->reserveElectricBorder(ElectricBorder(i), this); - } - - // TODO: rename zoomDuration to duration -diff --git a/kwin/effects/flipswitch/flipswitch.cpp b/kwin/effects/flipswitch/flipswitch.cpp -index 445405e..bb92216 100644 ---- a/kwin/effects/flipswitch/flipswitch.cpp -+++ b/kwin/effects/flipswitch/flipswitch.cpp -@@ -80,10 +80,10 @@ FlipSwitchEffect::FlipSwitchEffect() - FlipSwitchEffect::~FlipSwitchEffect() - { - foreach (ElectricBorder border, m_borderActivate) { -- effects->unreserveElectricBorder(border); -+ effects->unreserveElectricBorder(border, this); - } - foreach (ElectricBorder border, m_borderActivateAll) { -- effects->unreserveElectricBorder(border); -+ effects->unreserveElectricBorder(border, this); - } - delete m_captionFrame; - } -@@ -97,20 +97,20 @@ void FlipSwitchEffect::reconfigure(ReconfigureFlags) - { - FlipSwitchConfig::self()->readConfig(); - foreach (ElectricBorder border, m_borderActivate) { -- effects->unreserveElectricBorder(border); -+ effects->unreserveElectricBorder(border, this); - } - foreach (ElectricBorder border, m_borderActivateAll) { -- effects->unreserveElectricBorder(border); -+ effects->unreserveElectricBorder(border, this); - } - m_borderActivate.clear(); - m_borderActivateAll.clear(); - foreach (int i, FlipSwitchConfig::borderActivate()) { - m_borderActivate.append(ElectricBorder(i)); -- effects->reserveElectricBorder(ElectricBorder(i)); -+ effects->reserveElectricBorder(ElectricBorder(i), this); - } - foreach (int i, FlipSwitchConfig::borderActivateAll()) { - m_borderActivateAll.append(ElectricBorder(i)); -- effects->reserveElectricBorder(ElectricBorder(i)); -+ effects->reserveElectricBorder(ElectricBorder(i), this); - } - m_tabbox = FlipSwitchConfig::tabBox(); - m_tabboxAlternative = FlipSwitchConfig::tabBoxAlternative(); -diff --git a/kwin/effects/presentwindows/presentwindows.cpp b/kwin/effects/presentwindows/presentwindows.cpp -index 79baadb..59eacca 100755 ---- a/kwin/effects/presentwindows/presentwindows.cpp -+++ b/kwin/effects/presentwindows/presentwindows.cpp -@@ -116,10 +116,10 @@ PresentWindowsEffect::~PresentWindowsEffect() - XDeleteProperty(display(), rootWindow(), m_atomWindows); - effects->registerPropertyType(m_atomWindows, false); - foreach (ElectricBorder border, m_borderActivate) { -- effects->unreserveElectricBorder(border); -+ effects->unreserveElectricBorder(border, this); - } - foreach (ElectricBorder border, m_borderActivateAll) { -- effects->unreserveElectricBorder(border); -+ effects->unreserveElectricBorder(border, this); - } - delete m_filterFrame; - delete m_closeView; -@@ -129,24 +129,24 @@ void PresentWindowsEffect::reconfigure(ReconfigureFlags) - { - PresentWindowsConfig::self()->readConfig(); - foreach (ElectricBorder border, m_borderActivate) { -- effects->unreserveElectricBorder(border); -+ effects->unreserveElectricBorder(border, this); - } - foreach (ElectricBorder border, m_borderActivateAll) { -- effects->unreserveElectricBorder(border); -+ effects->unreserveElectricBorder(border, this); - } - m_borderActivate.clear(); - m_borderActivateAll.clear(); - foreach (int i, PresentWindowsConfig::borderActivate()) { - m_borderActivate.append(ElectricBorder(i)); -- effects->reserveElectricBorder(ElectricBorder(i)); -+ effects->reserveElectricBorder(ElectricBorder(i), this); - } - foreach (int i, PresentWindowsConfig::borderActivateAll()) { - m_borderActivateAll.append(ElectricBorder(i)); -- effects->reserveElectricBorder(ElectricBorder(i)); -+ effects->reserveElectricBorder(ElectricBorder(i), this); - } - foreach (int i, PresentWindowsConfig::borderActivateClass()) { - m_borderActivateClass.append(ElectricBorder(i)); -- effects->reserveElectricBorder(ElectricBorder(i)); -+ effects->reserveElectricBorder(ElectricBorder(i), this); - } - m_layoutMode = PresentWindowsConfig::layoutMode(); - m_showCaptions = PresentWindowsConfig::drawWindowCaptions(); -diff --git a/kwin/geometry.cpp b/kwin/geometry.cpp -index 5a0eae1..5344ffe 100644 ---- a/kwin/geometry.cpp -+++ b/kwin/geometry.cpp -@@ -77,7 +77,7 @@ void Workspace::desktopResized() - updateClientArea(); - saveOldScreenSizes(); // after updateClientArea(), so that one still uses the previous one - #ifdef KWIN_BUILD_SCREENEDGES -- m_screenEdge.update(true); -+ m_screenEdge.recreateEdges(); - #endif - if (effects) { - static_cast(effects)->desktopResized(geom.size()); -@@ -2589,9 +2589,7 @@ bool Client::startMoveResize() - Notify::raise(isResize() ? Notify::ResizeStart : Notify::MoveStart); - emit clientStartUserMovedResized(this); - #ifdef KWIN_BUILD_SCREENEDGES -- if (options->electricBorders() == Options::ElectricMoveOnly || -- options->electricBorderMaximize() || -- options->electricBorderTiling()) -+ if (workspace()->screenEdge()->isDesktopSwitchingMovingClients()) - workspace()->screenEdge()->reserveDesktopSwitching(true, Qt::Vertical|Qt::Horizontal); - #endif - if (fakeMove) // fix geom_restore position - it HAS to happen at the end, ie. when all moving is set up. inline call will lock focus!! -@@ -2599,37 +2597,6 @@ bool Client::startMoveResize() - return true; - } - --static ElectricBorder electricBorderFromMode(QuickTileMode mode) --{ -- // special case, currently maxmizing is done from the electric top corner -- if (mode == QuickTileMaximize) -- return ElectricTop; -- -- // sanitize the mode, ie. simplify "invalid" combinations -- if ((mode & QuickTileHorizontal) == QuickTileHorizontal) -- mode &= ~QuickTileHorizontal; -- if ((mode & QuickTileVertical) == QuickTileVertical) -- mode &= ~QuickTileVertical; -- -- if (mode == QuickTileLeft) -- return ElectricLeft; -- if (mode == QuickTileRight) -- return ElectricRight; -- if (mode == (QuickTileTop|QuickTileLeft)) -- return ElectricTopLeft; -- if (mode == (QuickTileTop|QuickTileRight)) -- return ElectricTopRight; -- if (mode == (QuickTileBottom|QuickTileLeft)) -- return ElectricBottomLeft; -- if (mode == (QuickTileBottom|QuickTileRight)) -- return ElectricBottomRight; -- if (mode == QuickTileTop) -- return ElectricTop; -- if (mode == QuickTileBottom) -- return ElectricBottom; -- return ElectricNone; --} -- - void Client::finishMoveResize(bool cancel) - { - const bool wasResize = isResize(); // store across leaveMoveResize -@@ -2658,14 +2625,6 @@ void Client::finishMoveResize(bool cancel) - - if (isElectricBorderMaximizing()) { - setQuickTileMode(electricMode); -- const ElectricBorder border = electricBorderFromMode(electricMode); -- if (border == ElectricNone) -- kDebug(1212) << "invalid electric mode" << electricMode << "leading to invalid array access,\ -- this should not have happened!"; --#ifdef KWIN_BUILD_SCREENEDGES -- else -- workspace()->screenEdge()->restoreSize(border); --#endif - electricMaximizing = false; - workspace()->outline()->hide(); - } else if (!cancel) { -@@ -2713,9 +2672,7 @@ void Client::leaveMoveResize() - syncRequest.timeout = NULL; - #endif - #ifdef KWIN_BUILD_SCREENEDGES -- if (options->electricBorders() == Options::ElectricMoveOnly || -- options->electricBorderMaximize() || -- options->electricBorderTiling()) -+ if (workspace()->screenEdge()->isDesktopSwitchingMovingClients()) - workspace()->screenEdge()->reserveDesktopSwitching(false, Qt::Vertical|Qt::Horizontal); - #endif - } -@@ -3082,7 +3039,7 @@ void Client::handleMoveResize(int x, int y, int x_root, int y_root) - - if (isMove()) { - #ifdef KWIN_BUILD_SCREENEDGES -- workspace()->screenEdge()->check(globalPos, xTime()); -+ workspace()->screenEdge()->check(globalPos, QDateTime::fromMSecsSinceEpoch(xTime())); - #endif - } - } -diff --git a/kwin/libkwineffects/kwineffects.h b/kwin/libkwineffects/kwineffects.h -index f6bb636..731869f 100644 ---- a/kwin/libkwineffects/kwineffects.h -+++ b/kwin/libkwineffects/kwineffects.h -@@ -438,8 +438,6 @@ public: - virtual void windowInputMouseEvent(Window w, QEvent* e); - virtual void grabbedKeyboardEvent(QKeyEvent* e); - -- virtual bool borderActivated(ElectricBorder border); -- - /** - * Overwrite this method to indicate whether your effect will be doing something in - * the next frame to be rendered. If the method returns @c false the effect will be -@@ -496,6 +494,9 @@ public: - **/ - static void setPositionTransformations(WindowPaintData& data, QRect& region, EffectWindow* w, - const QRect& r, Qt::AspectRatioMode aspect); -+ -+public Q_SLOTS: -+ virtual bool borderActivated(ElectricBorder border); - }; - - -@@ -653,8 +654,8 @@ public: - virtual void stopMousePolling() = 0; - - virtual void checkElectricBorder(const QPoint &pos, Time time) = 0; -- virtual void reserveElectricBorder(ElectricBorder border) = 0; -- virtual void unreserveElectricBorder(ElectricBorder border) = 0; -+ virtual void reserveElectricBorder(ElectricBorder border, Effect *effect) = 0; -+ virtual void unreserveElectricBorder(ElectricBorder border, Effect *effect) = 0; - virtual void reserveElectricBorderSwitching(bool reserve, Qt::Orientations o) = 0; - - // functions that allow controlling windows/desktop -diff --git a/kwin/screenedge.cpp b/kwin/screenedge.cpp -index 80a3931..cc114c2 100644 ---- a/kwin/screenedge.cpp -+++ b/kwin/screenedge.cpp -@@ -41,14 +41,415 @@ along with this program. If not, see . - #include - #include - #include -+#include -+#include - - namespace KWin { - -+ // Mouse should not move more than this many pixels -+static const int DISTANCE_RESET = 30; -+ -+Edge::Edge(ScreenEdge *parent) -+ : QObject(parent) -+ , m_edges(parent) -+ , m_border(ElectricNone) -+ , m_action(ElectricActionNone) -+ , m_reserved(0) -+ , m_blocked(false) -+{ -+} -+ -+Edge::~Edge() -+{ -+} -+ -+void Edge::reserve() -+{ -+ m_reserved++; -+ if (m_reserved == 1) { -+ activate(); -+ } -+} -+ -+void Edge::reserve(QObject *object, const char *slot) -+{ -+ connect(object, SIGNAL(destroyed(QObject*)), SLOT(unreserve(QObject*))); -+ m_callBacks.insert(object, QByteArray(slot)); -+ reserve(); -+} -+ -+void Edge::unreserve() -+{ -+ m_reserved--; -+ if (m_reserved == 0) { -+ deactivate(); -+ } -+} -+ -+void Edge::unreserve(QObject *object) -+{ -+ if (m_callBacks.contains(object)) { -+ m_callBacks.remove(object); -+ disconnect(object, SIGNAL(destroyed(QObject*)), this, SLOT(unreserve(QObject*))); -+ unreserve(); -+ } -+} -+ -+bool Edge::triggersFor(const QPoint &cursorPos) const -+{ -+ if (isBlocked()) { -+ return false; -+ } -+ if (!m_geometry.contains(cursorPos)) { -+ return false; -+ } -+ if (isLeft() && cursorPos.x() != m_geometry.x()) { -+ return false; -+ } -+ if (isRight() && cursorPos.x() != (m_geometry.x() + m_geometry.width() -1)) { -+ return false; -+ } -+ if (isTop() && cursorPos.y() != m_geometry.y()) { -+ return false; -+ } -+ if (isBottom() && cursorPos.y() != (m_geometry.y() + m_geometry.height() -1)) { -+ return false; -+ } -+ return true; -+} -+ -+void Edge::check(const QPoint &cursorPos, const QDateTime &triggerTime, bool forceNoPushback) -+{ -+ if (!triggersFor(cursorPos)) { -+ return; -+ } -+ // no pushback so we have to activate at once -+ bool directActivate = forceNoPushback || edges()->cursorPushBackDistance().isNull(); -+ if (directActivate || canActivate(cursorPos, triggerTime)) { -+ m_lastTrigger = triggerTime; -+ m_lastReset = QDateTime(); // invalidate -+ handle(cursorPos); -+ } else { -+ pushCursorBack(cursorPos); -+ } -+ m_triggeredPoint = cursorPos; -+} -+ -+bool Edge::canActivate(const QPoint &cursorPos, const QDateTime &triggerTime) -+{ -+ // we check whether either the timer has explicitly been invalidated (successfull trigger) or is -+ // bigger than the reactivation threshold (activation "aborted", usually due to moving away the cursor -+ // from the corner after successfull activation) -+ // either condition means that "this is the first event in a new attempt" -+ if (!m_lastReset.isValid() || m_lastReset.msecsTo(triggerTime) > edges()->reActivationThreshold()) { -+ m_lastReset = triggerTime; -+ return false; -+ } -+ if (m_lastTrigger.msecsTo(triggerTime) < edges()->reActivationThreshold()) { -+ return false; -+ } -+ if (m_lastReset.msecsTo(triggerTime) < edges()->timeThreshold()) { -+ return false; -+ } -+ // does the check on position make any sense at all? -+ if ((cursorPos - m_triggeredPoint).manhattanLength() > DISTANCE_RESET) { -+ return false; -+ } -+ return true; -+} -+ -+void Edge::handle(const QPoint &cursorPos) -+{ -+ if ((edges()->isDesktopSwitchingMovingClients() && Workspace::self()->getMovingClient()) || -+ (edges()->isDesktopSwitching() && isScreenEdge())) { -+ // always switch desktop in case: -+ // moving a Client and option for switch on client move is enabled -+ // or switch on screen edge is enabled -+ switchDesktop(cursorPos); -+ return; -+ } -+ if (Workspace::self()->getMovingClient()) { -+ // if we are moving a window we don't want to trigger the actions. This just results in -+ // problems, e.g. Desktop Grid activated or screen locker activated which just cannot -+ // work as we hold a grab -+ return; -+ } -+ if (handleAction() || handleByCallback()) { -+ return; -+ } -+ if (edges()->isDesktopSwitching() && isCorner()) { -+ // try again desktop switching in the corder -+ switchDesktop(cursorPos); -+ } -+} -+ -+bool Edge::handleAction() -+{ -+ switch (m_action) { -+ case ElectricActionDashboard: { // Display Plasma dashboard -+ QDBusInterface plasmaApp("org.kde.plasma-desktop", "/App"); -+ plasmaApp.asyncCall("toggleDashboard"); -+ return true; -+ } -+ case ElectricActionShowDesktop: { -+ Workspace::self()->setShowingDesktop(!Workspace::self()->showingDesktop()); -+ return true; -+ } -+ case ElectricActionLockScreen: { // Lock the screen -+ QDBusInterface screenSaver("org.kde.screensaver", "/ScreenSaver"); -+ screenSaver.asyncCall("Lock"); -+ return true; -+ } -+ default: -+ return false; -+ } -+} -+ -+bool Edge::handleByCallback() -+{ -+ if (m_callBacks.isEmpty()) { -+ return false; -+ } -+ for (QHash::iterator it = m_callBacks.begin(); -+ it != m_callBacks.end(); -+ ++it) { -+ bool retVal = false; -+ QMetaObject::invokeMethod(it.key(), it.value().constData(), Q_RETURN_ARG(bool, retVal), Q_ARG(ElectricBorder, m_border)); -+ if (retVal) { -+ return true; -+ } -+ } -+ return false; -+} -+ -+void Edge::switchDesktop(const QPoint& cursorPos) -+{ -+ QPoint pos(cursorPos); -+ const uint oldDesktop = Workspace::self()->currentDesktop(); -+ uint desktop = oldDesktop; -+ const int OFFSET = 2; -+ if (isLeft()) { -+ const uint interimDesktop = desktop; -+ desktop = Workspace::self()->desktopToLeft(desktop, options->isRollOverDesktops()); -+ if (desktop != interimDesktop) -+ pos.setX(displayWidth() - 1 - OFFSET); -+ } else if (isRight()) { -+ const uint interimDesktop = desktop; -+ desktop = Workspace::self()->desktopToRight(desktop, options->isRollOverDesktops()); -+ if (desktop != interimDesktop) -+ pos.setX(OFFSET); -+ } -+ if (isTop()) { -+ const uint interimDesktop = desktop; -+ desktop = Workspace::self()->desktopAbove(desktop, options->isRollOverDesktops()); -+ if (desktop != interimDesktop) -+ pos.setY(displayHeight() - 1 - OFFSET); -+ } else if (isBottom()) { -+ const uint interimDesktop = desktop; -+ desktop = Workspace::self()->desktopBelow(desktop, options->isRollOverDesktops()); -+ if (desktop != interimDesktop) -+ pos.setY(OFFSET); -+ } -+ if (Client *c = Workspace::self()->getMovingClient()) { -+ if (c->rules()->checkDesktop(desktop) != int(desktop)) { -+ // user attempts to move a client to another desktop where it is ruleforced to not be -+ return; -+ } -+ } -+ Workspace::self()->setCurrentDesktop(desktop); -+ if (desktop != oldDesktop) { -+ QCursor::setPos(pos); -+ } -+} -+ -+void Edge::pushCursorBack(const QPoint& cursorPos) -+{ -+ int x = cursorPos.x(); -+ int y = cursorPos.y(); -+ const QSize &distance = edges()->cursorPushBackDistance(); -+ if (isLeft()) { -+ x += distance.width(); -+ } -+ if (isRight()) { -+ x -= distance.width(); -+ } -+ if (isTop()) { -+ y += distance.height(); -+ } -+ if (isBottom()) { -+ y -= distance.height(); -+ } -+ QCursor::setPos(x, y); -+} -+ -+void Edge::setGeometry(const QRect &geometry) -+{ -+ if (m_geometry == geometry) { -+ return; -+ } -+ m_geometry = geometry; -+ int x = m_geometry.x(); -+ int y = m_geometry.y(); -+ int width = m_geometry.width(); -+ int height = m_geometry.height(); -+ const int size = m_edges->cornerOffset(); -+ if (isCorner()) { -+ if (isRight()) { -+ x = x - size +1; -+ } -+ if (isBottom()) { -+ y = y - size +1; -+ } -+ width = size; -+ height = size; -+ } else { -+ if (isLeft()) { -+ y += size + 1; -+ width = size; -+ height = height - size * 2; -+ } else if (isRight()) { -+ x = x - size + 1; -+ y += size; -+ width = size; -+ height = height - size * 2; -+ } else if (isTop()) { -+ x += size; -+ width = width - size * 2; -+ height = size; -+ } else if (isBottom()) { -+ x += size; -+ y = y - size +1; -+ width = width - size * 2; -+ height = size; -+ } -+ } -+ doGeometryUpdate(); -+} -+ -+void Edge::checkBlocking() -+{ -+ if (isCorner()) { -+ return; -+ } -+ bool newValue = false; -+ if (Client *client = Workspace::self()->activeClient()) { -+ newValue = client->isFullScreen() && client->geometry().contains(m_geometry.center()); -+ } -+ if (newValue == m_blocked) { -+ return; -+ } -+ m_blocked = newValue; -+ doUpdateBlocking(); -+} -+ -+void Edge::doUpdateBlocking() -+{ -+} -+ -+void Edge::doGeometryUpdate() -+{ -+} -+ -+void Edge::activate() -+{ -+} -+ -+void Edge::deactivate() -+{ -+} -+ -+/********************************************************** -+ * ScreenEdge -+ *********************************************************/ -+ -+WindowBasedEdge::WindowBasedEdge(ScreenEdge* parent) -+ : Edge(parent) -+ , m_window(None) -+{ -+} -+ -+WindowBasedEdge::~WindowBasedEdge() -+{ -+} -+ -+void WindowBasedEdge::activate() -+{ -+ createWindow(); -+ doUpdateBlocking(); -+} -+ -+void WindowBasedEdge::deactivate() -+{ -+ if (m_window != None) { -+ XDestroyWindow(display(), m_window); -+ m_window = None; -+ } -+} -+ -+void WindowBasedEdge::createWindow() -+{ -+ if (m_window != None) { -+ return; -+ } -+ -+ XSetWindowAttributes attributes; -+ attributes.override_redirect = True; -+ attributes.event_mask = EnterWindowMask | LeaveWindowMask; -+ unsigned long valuemask = CWOverrideRedirect | CWEventMask; -+ -+ const QRect geom = geometry(); -+ m_window = XCreateWindow(display(), rootWindow(), -+ geom.x(), geom.y(), geom.width(), geom.height(), -+ 0, CopyFromParent, InputOnly, CopyFromParent, valuemask, &attributes); -+ XMapWindow(display(), m_window); -+ // Set XdndAware on the windows, so that DND enter events are received (#86998) -+ Atom version = 4; // XDND version -+ XChangeProperty(display(), m_window, atoms->xdnd_aware, XA_ATOM, -+ 32, PropModeReplace, (unsigned char*)(&version), 1); -+} -+ -+void WindowBasedEdge::doGeometryUpdate() -+{ -+ const QRect geom = geometry(); -+ XMoveResizeWindow(display(), m_window, -+ geom.x(), geom.y(), geom.width(), geom.height()); -+} -+ -+void WindowBasedEdge::doUpdateBlocking() -+{ -+ if (!isReserved()) { -+ return; -+ } -+ if (isBlocked()) { -+ XUnmapWindow(display(), m_window); -+ } else { -+ XMapWindow(display(), m_window); -+ } -+} -+ -+/********************************************************** -+ * ScreenEdges -+ *********************************************************/ -+ - ScreenEdge::ScreenEdge() - : QObject(NULL) -- , m_screenEdgeWindows(ELECTRIC_COUNT, None) -- , m_screenEdgeReserved(ELECTRIC_COUNT, 0) -+ , m_desktopSwitching(false) -+ , m_desktopSwitchingMovingClients(false) -+ , m_timeThreshold(0) -+ , m_reactivateThreshold(0) -+ , m_virtualDesktopLayout(0) -+ , m_actionTopLeft(ElectricActionNone) -+ , m_actionTop(ElectricActionNone) -+ , m_actionTopRight(ElectricActionNone) -+ , m_actionRight(ElectricActionNone) -+ , m_actionBottomRight(ElectricActionNone) -+ , m_actionBottom(ElectricActionNone) -+ , m_actionBottomLeft(ElectricActionNone) -+ , m_actionLeft(ElectricActionNone) -+ , m_cornerOffset(0) - { -+ QWidget w; -+ m_cornerOffset = (w.physicalDpiX() + w.physicalDpiY() + 5) / 6; - } - - ScreenEdge::~ScreenEdge() -@@ -57,326 +458,432 @@ ScreenEdge::~ScreenEdge() - - void ScreenEdge::init() - { -- reserveActions(true); -- update(); -+ reconfigure(); -+ updateLayout(); -+ recreateEdges(); -+} -+ -+static ElectricBorderAction electricBorderAction(const QString &name) -+{ -+ QString lowerName = name.toLower(); -+ if (lowerName == "dashboard") { -+ return ElectricActionDashboard; -+ } else if (lowerName == "showdesktop") { -+ return ElectricActionShowDesktop; -+ } else if (lowerName == "lockscreen") { -+ return ElectricActionLockScreen; -+ } else if (lowerName == "preventscreenlocking") { -+ return ElectricActionPreventScreenLocking; -+ } -+ return ElectricActionNone; -+} -+ -+void ScreenEdge::reconfigure() -+{ -+ if (!m_config) { -+ return; -+ } -+ // TODO: migrate settings to a group ScreenEdges -+ KConfigGroup windowsConfig = m_config->group("Windows"); -+ setTimeThreshold(windowsConfig.readEntry("ElectricBorderDelay", 150)); -+ setReActivationThreshold(qMax(timeThreshold() + 50, windowsConfig.readEntry("ElectricBorderCooldown", 350))); -+ int desktopSwitching = windowsConfig.readEntry("ElectricBorders", static_cast(ElectricDisabled)); -+ if (desktopSwitching == ElectricDisabled) { -+ setDesktopSwitching(false); -+ setDesktopSwitchingMovingClients(false); -+ } else if (desktopSwitching == ElectricMoveOnly) { -+ setDesktopSwitching(false); -+ setDesktopSwitchingMovingClients(true); -+ } else if (desktopSwitching == ElectricAlways) { -+ setDesktopSwitching(true); -+ setDesktopSwitchingMovingClients(true); -+ } -+ const int pushBack = windowsConfig.readEntry("ElectricBorderPushbackPixels", 1); -+ m_cursorPushBackDistance = QSize(pushBack, pushBack); -+ -+ KConfigGroup borderConfig = m_config->group("ElectricBorders"); -+ setActionForBorder(ElectricTopLeft, &m_actionTopLeft, -+ electricBorderAction(borderConfig.readEntry("TopLeft", "None"))); -+ setActionForBorder(ElectricTop, &m_actionTop, -+ electricBorderAction(borderConfig.readEntry("Top", "None"))); -+ setActionForBorder(ElectricTopRight, &m_actionTopRight, -+ electricBorderAction(borderConfig.readEntry("TopRight", "None"))); -+ setActionForBorder(ElectricRight, &m_actionRight, -+ electricBorderAction(borderConfig.readEntry("Right", "None"))); -+ setActionForBorder(ElectricBottomRight, &m_actionBottomRight, -+ electricBorderAction(borderConfig.readEntry("BottomRight", "None"))); -+ setActionForBorder(ElectricBottom, &m_actionBottom, -+ electricBorderAction(borderConfig.readEntry("Bottom", "None"))); -+ setActionForBorder(ElectricBottomLeft, &m_actionBottomLeft, -+ electricBorderAction(borderConfig.readEntry("BottomLeft", "None"))); -+ setActionForBorder(ElectricLeft, &m_actionLeft, -+ electricBorderAction(borderConfig.readEntry("Left", "None"))); - } - --void ScreenEdge::update(bool force) -+void ScreenEdge::setActionForBorder(ElectricBorder border, ElectricBorderAction* oldValue, ElectricBorderAction newValue) - { -- m_screenEdgeTimeFirst = xTime(); -- m_screenEdgeTimeLast = xTime(); -- m_screenEdgeTimeLastTrigger = xTime(); -- m_currentScreenEdge = ElectricNone; -- QRect r = QRect(0, 0, displayWidth(), displayHeight()); -- m_screenEdgeTop = r.top(); -- m_screenEdgeBottom = r.bottom(); -- m_screenEdgeLeft = r.left(); -- m_screenEdgeRight = r.right(); - -- for (int pos = 0; pos < ELECTRIC_COUNT; ++pos) { -- if (force || m_screenEdgeReserved[pos] == 0) { -- if (m_screenEdgeWindows[pos] != None) -- XDestroyWindow(display(), m_screenEdgeWindows[pos]); -- m_screenEdgeWindows[pos] = None; -+ if (*oldValue == newValue) { -+ return; -+ } -+ if (*oldValue == ElectricActionNone) { -+ // have to reserve -+ for (QList::iterator it = m_edges.begin(); it != m_edges.end(); ++it) { -+ if ((*it)->border() == border) { -+ (*it)->reserve(); -+ } - } -- if (m_screenEdgeReserved[pos] == 0) { -+ } -+ if (newValue == ElectricActionNone) { -+ // have to unreserve -+ for (QList::iterator it = m_edges.begin(); it != m_edges.end(); ++it) { -+ if ((*it)->border() == border) { -+ (*it)->unreserve(); -+ } -+ } -+ } -+ *oldValue = newValue; -+ // update action on all Edges for given border -+ for (QList::iterator it = m_edges.begin(); it != m_edges.end(); ++it) { -+ if ((*it)->border() == border) { -+ (*it)->setAction(newValue); -+ } -+ } -+} -+ -+void ScreenEdge::updateLayout() -+{ -+ const QSize desktopMatrix = Workspace::self()->desktopGridSize(); -+ Qt::Orientations newLayout = 0; -+ if (desktopMatrix.width() > 1) { -+ newLayout |= Qt::Horizontal; -+ } -+ if (desktopMatrix.height() > 1) { -+ newLayout |= Qt::Vertical; -+ } -+ if (newLayout == m_virtualDesktopLayout) { -+ return; -+ } -+ if (isDesktopSwitching()) { -+ reserveDesktopSwitching(false, m_virtualDesktopLayout); -+ } -+ m_virtualDesktopLayout = newLayout; -+ if (isDesktopSwitching()) { -+ reserveDesktopSwitching(true, m_virtualDesktopLayout); -+ } -+} -+ -+static bool isLeftScreen(const QRect &screen, const QRect &fullArea) -+{ -+ if (QApplication::desktop()->screenCount() == 1) { -+ return true; -+ } -+ if (screen.x() == fullArea.x()) { -+ return true; -+ } -+ // the screen is also on the left in case of a vertical layout with a second screen -+ // more to the left. In that case no screen ends left of screen's x coord -+ for (int i=0; iscreenCount(); ++i) { -+ const QRect otherGeo = QApplication::desktop()->screenGeometry(i); -+ if (otherGeo == screen) { -+ // that's our screen to test - continue; - } -- if (m_screenEdgeWindows[pos] != None) -+ if (otherGeo.x() + otherGeo.width() <= screen.x()) { -+ // other screen is completely in the left -+ return false; -+ } -+ } -+ // did not find a screen left of our current screen, so it is the left most -+ return true; -+} -+ -+static bool isRightScreen(const QRect &screen, const QRect &fullArea) -+{ -+ if (QApplication::desktop()->screenCount() == 1) { -+ return true; -+ } -+ if (screen.x() + screen.width() == fullArea.x() + fullArea.width()) { -+ return true; -+ } -+ // the screen is also on the right in case of a vertical layout wit a second screen -+ // more to the right. In that case no screen starts right of this screen -+ for (int i=0; iscreenCount(); ++i) { -+ const QRect otherGeo = QApplication::desktop()->screenGeometry(i); -+ if (otherGeo == screen) { -+ // that's our screen to test - continue; -- XSetWindowAttributes attributes; -- attributes.override_redirect = True; -- attributes.event_mask = EnterWindowMask | LeaveWindowMask; -- unsigned long valuemask = CWOverrideRedirect | CWEventMask; -- int xywh[ELECTRIC_COUNT][4] = { -- { r.left() + 1, r.top(), r.width() - 2, 1 }, // Top -- { r.right(), r.top(), 1, 1 }, // Top-right -- { r.right(), r.top() + 1, 1, r.height() - 2 }, // Etc. -- { r.right(), r.bottom(), 1, 1 }, -- { r.left() + 1, r.bottom(), r.width() - 2, 1 }, -- { r.left(), r.bottom(), 1, 1 }, -- { r.left(), r.top() + 1, 1, r.height() - 2 }, -- { r.left(), r.top(), 1, 1 } -- }; -- m_screenEdgeWindows[pos] = XCreateWindow(display(), rootWindow(), -- xywh[pos][0], xywh[pos][1], xywh[pos][2], xywh[pos][3], -- 0, CopyFromParent, InputOnly, CopyFromParent, valuemask, &attributes); -- XMapWindow(display(), m_screenEdgeWindows[pos]); -- -- // Set XdndAware on the windows, so that DND enter events are received (#86998) -- Atom version = 4; // XDND version -- XChangeProperty(display(), m_screenEdgeWindows[pos], atoms->xdnd_aware, XA_ATOM, -- 32, PropModeReplace, (unsigned char*)(&version), 1); -- } --} -- --void ScreenEdge::restoreSize(ElectricBorder border) --{ -- if (m_screenEdgeWindows[border] == None) -- return; -- QRect r(0, 0, displayWidth(), displayHeight()); -- int xywh[ELECTRIC_COUNT][4] = { -- { r.left() + 1, r.top(), r.width() - 2, 1 }, // Top -- { r.right(), r.top(), 1, 1 }, // Top-right -- { r.right(), r.top() + 1, 1, r.height() - 2 }, // Etc. -- { r.right(), r.bottom(), 1, 1 }, -- { r.left() + 1, r.bottom(), r.width() - 2, 1 }, -- { r.left(), r.bottom(), 1, 1 }, -- { r.left(), r.top() + 1, 1, r.height() - 2 }, -- { r.left(), r.top(), 1, 1 } -- }; -- XMoveResizeWindow(display(), m_screenEdgeWindows[border], -- xywh[border][0], xywh[border][1], xywh[border][2], xywh[border][3]); --} -- --void ScreenEdge::reserveActions(bool isToReserve) --{ -- for (int pos = 0; pos < ELECTRIC_COUNT; ++pos) -- if (options->electricBorderAction(static_cast(pos))) { -- if (isToReserve) -- reserve(static_cast(pos)); -- else -- unreserve(static_cast(pos)); - } -+ if (otherGeo.x() >= screen.x() + screen.width()) { -+ // other screen is completely in the right -+ return false; -+ } -+ } -+ // did not find a screen right of our current screen, so it is the right most -+ return true; - } - --void ScreenEdge::reserveDesktopSwitching(bool isToReserve, Qt::Orientations o) -+static bool isTopScreen(const QRect &screen, const QRect &fullArea) - { -- if (!o) -- return; -- if (isToReserve) { -- reserve(ElectricTopLeft); -- reserve(ElectricTopRight); -- reserve(ElectricBottomRight); -- reserve(ElectricBottomLeft); -- -- if (o & Qt::Horizontal) { -- reserve(ElectricLeft); -- reserve(ElectricRight); -+ if (QApplication::desktop()->screenCount() == 1) { -+ return true; -+ } -+ if (screen.y() == fullArea.y()) { -+ return true; -+ } -+ // the screen is also top most in case of a horizontal layout with a second screen -+ // more to the top. In that case no screen ends above screen's y cord -+ for (int i=0; iscreenCount(); ++i) { -+ const QRect otherGeo = QApplication::desktop()->screenGeometry(i); -+ if (otherGeo == screen) { -+ // that's our screen to test -+ continue; - } -- if (o & Qt::Vertical) { -- reserve(ElectricTop); -- reserve(ElectricBottom); -+ if (otherGeo.y() + otherGeo.height() <= screen.y()) { -+ // other screen is completely above -+ return false; - } -- } else { -- unreserve(ElectricTopLeft); -- unreserve(ElectricTopRight); -- unreserve(ElectricBottomRight); -- unreserve(ElectricBottomLeft); -- -- if (o & Qt::Horizontal) { -- unreserve(ElectricLeft); -- unreserve(ElectricRight); -+ } -+ // did not find a screen above our current screen, os it is the top most -+ return true; -+} -+ -+static bool isBottomScreen(const QRect &screen, const QRect &fullArea) -+{ -+ if (QApplication::desktop()->screenCount() == 1) { -+ return true; -+ } -+ if (screen.y() + screen.height() == fullArea.y() + fullArea.height()) { -+ return true; -+ } -+ // the screen is also bottom most in case of a horizontal layout with a second screen -+ // more below. In that case no screen starts below screen's y coord + height -+ for (int i=0; iscreenCount(); ++i) { -+ const QRect otherGeo = QApplication::desktop()->screenGeometry(i); -+ if (otherGeo == screen) { -+ // that's our screen to test -+ continue; - } -- if (o & Qt::Vertical) { -- unreserve(ElectricTop); -- unreserve(ElectricBottom); -+ if (otherGeo.y() >= screen.y() + screen.height()) { -+ // other screen is completely below -+ return false; - } - } -+ // did not find a screen below our current screen, so it is the bottom most -+ return true; - } - --void ScreenEdge::reserve(ElectricBorder border) -+void ScreenEdge::recreateEdges() - { -- if (border == ElectricNone) -- return; -- if (m_screenEdgeReserved[border]++ == 0) -- QTimer::singleShot(0, this, SLOT(update())); -+ QList oldEdges = m_edges; -+ m_edges.clear(); -+ QRect fullArea = QRect(0, 0, displayWidth(), displayHeight()); -+ for (int i = 0, c = QApplication::desktop()->screenCount(); i < c; ++i) { -+ const QRect screen = QApplication::desktop()->screenGeometry(i); -+ if (isLeftScreen(screen, fullArea)) { -+ createVerticalEdge(ElectricLeft, screen, fullArea); -+ } -+ if (isRightScreen(screen, fullArea)) { -+ createVerticalEdge(ElectricRight, screen, fullArea); -+ } -+ if (isTopScreen(screen, fullArea)) { -+ createHorizontalEdge(ElectricTop, screen, fullArea); -+ } -+ if (isBottomScreen(screen, fullArea)) { -+ createHorizontalEdge(ElectricBottom, screen, fullArea); -+ } -+ } -+ -+ for (QList::iterator it = m_edges.begin(); it != m_edges.end(); ++it) { -+ WindowBasedEdge *edge = *it; -+ for (QList::const_iterator oldIt = oldEdges.constBegin(); -+ oldIt != oldEdges.constEnd(); -+ ++oldIt) { -+ WindowBasedEdge *oldEdge = *oldIt; -+ if (oldEdge->border() != edge->border()) { -+ continue; -+ } -+ const QHash &callbacks = oldEdge->callBacks(); -+ for (QHash::const_iterator callback = callbacks.begin(); -+ callback != callbacks.end(); -+ ++callback) { -+ edge->reserve(callback.key(), callback.value().constData()); -+ } -+ } -+ } -+ qDeleteAll(oldEdges); - } - --void ScreenEdge::unreserve(ElectricBorder border) -+void ScreenEdge::createVerticalEdge(ElectricBorder border, const QRect &screen, const QRect &fullArea) - { -- if (border == ElectricNone) -+ if (border != KWin::ElectricRight && border != KWin::ElectricLeft) { - return; -- assert(m_screenEdgeReserved[border] > 0); -- if (--m_screenEdgeReserved[border] == 0) -- QTimer::singleShot(0, this, SLOT(update())); -+ } -+ const int y = screen.y(); -+ const int height = screen.height(); -+ const int x = (border == ElectricLeft) ? screen.x() : screen.x() + screen.width() - 1; -+ if (isTopScreen(screen, fullArea)) { -+ // also top most screen -+ // create top left/right edge -+ const ElectricBorder edge = (border == ElectricLeft) ? ElectricTopLeft : ElectricTopRight; -+ m_edges << createEdge(edge, x, screen.y(), 1, 1); -+ } -+ if (isBottomScreen(screen, fullArea)) { -+ // also bottom most screen -+ // create bottom left/right edge -+ const ElectricBorder edge = (border == ElectricLeft) ? ElectricBottomLeft : ElectricBottomRight; -+ m_edges << createEdge(edge, x, screen.y() + screen.height() - 1, 1, 1); -+ } -+ m_edges << createEdge(border, x, y, 1, height); - } - --void ScreenEdge::check(const QPoint& pos, Time now, bool forceNoPushback) -+void ScreenEdge::createHorizontalEdge(ElectricBorder border, const QRect &screen, const QRect &fullArea) - { -- if ((pos.x() != m_screenEdgeLeft) && -- (pos.x() != m_screenEdgeRight) && -- (pos.y() != m_screenEdgeTop) && -- (pos.y() != m_screenEdgeBottom)) -- return; -+ Q_UNUSED(fullArea) - -- bool have_borders = false; -- for (int i = 0; i < ELECTRIC_COUNT; ++i) -- if (m_screenEdgeWindows[i] != None) -- have_borders = true; -- if (!have_borders) -+ if (border != ElectricTop && border != ElectricBottom) { - return; -+ } -+ const int x = screen.x(); -+ const int width = screen.width(); -+ const int y = (border == ElectricTop) ? screen.y() : screen.y() + screen.height() - 1; -+ m_edges << createEdge(border, x, y, width, 1); -+} - -- Time treshold_set = options->electricBorderDelay(); // Set timeout -- Time treshold_reset = 250; // Reset timeout -- Time treshold_trigger = options->electricBorderCooldown(); // Minimum time between triggers -- int distance_reset = 30; // Mouse should not move more than this many pixels -- int pushback_pixels = forceNoPushback ? 0 : options->electricBorderPushbackPixels(); -- -- ElectricBorder border; -- if (pos.x() == m_screenEdgeLeft && pos.y() == m_screenEdgeTop) -- border = ElectricTopLeft; -- else if (pos.x() == m_screenEdgeRight && pos.y() == m_screenEdgeTop) -- border = ElectricTopRight; -- else if (pos.x() == m_screenEdgeLeft && pos.y() == m_screenEdgeBottom) -- border = ElectricBottomLeft; -- else if (pos.x() == m_screenEdgeRight && pos.y() == m_screenEdgeBottom) -- border = ElectricBottomRight; -- else if (pos.x() == m_screenEdgeLeft) -- border = ElectricLeft; -- else if (pos.x() == m_screenEdgeRight) -- border = ElectricRight; -- else if (pos.y() == m_screenEdgeTop) -- border = ElectricTop; -- else if (pos.y() == m_screenEdgeBottom) -- border = ElectricBottom; -- else -- abort(); -- -- if (m_screenEdgeWindows[border] == None) -- return; -+WindowBasedEdge* ScreenEdge::createEdge(ElectricBorder border, int x, int y, int width, int height) -+{ -+ WindowBasedEdge *edge = new WindowBasedEdge(this); -+ edge->setBorder(border); -+ edge->setGeometry(QRect(x, y, width, height)); -+ const ElectricBorderAction action = actionForEdge(edge); -+ if (action != KWin::ElectricActionNone) { -+ edge->reserve(); -+ edge->setAction(action); -+ } -+ if (isDesktopSwitching()) { -+ if (edge->isCorner()) { -+ edge->reserve(); -+ } else { -+ if ((m_virtualDesktopLayout & Qt::Horizontal) && (edge->isLeft() || edge->isRight())) { -+ edge->reserve(); -+ } -+ if ((m_virtualDesktopLayout & Qt::Vertical) & (edge->isTop() || edge->isBottom())) { -+ edge->reserve(); -+ } -+ } -+ } -+ return edge; -+} - -- if (pushback_pixels == 0) { -- // no pushback so we have to activate at once -- m_screenEdgeTimeLast = now; -- m_currentScreenEdge = border; -- m_screenEdgePushPoint = pos; -- } -- -- if ((m_currentScreenEdge == border) && -- (timestampDiff(m_screenEdgeTimeLast, now) < treshold_reset) && -- (timestampDiff(m_screenEdgeTimeLastTrigger, now) > treshold_trigger) && -- ((pos - m_screenEdgePushPoint).manhattanLength() < distance_reset)) { -- m_screenEdgeTimeLast = now; -- -- if (timestampDiff(m_screenEdgeTimeFirst, now) > treshold_set) { -- m_currentScreenEdge = ElectricNone; -- m_screenEdgeTimeLastTrigger = now; -- if (Workspace::self()->getMovingClient()) { -- // If moving a client or have force doing the desktop switch -- if (options->electricBorders() != Options::ElectricDisabled) -- switchDesktop(border, pos); -- return; // Don't reset cursor position -- } else { -- if (options->electricBorders() == Options::ElectricAlways && -- (border == ElectricTop || border == ElectricRight || -- border == ElectricBottom || border == ElectricLeft)) { -- // If desktop switching is always enabled don't apply it to the corners if -- // an effect is applied to it (We will check that later). -- switchDesktop(border, pos); -- return; // Don't reset cursor position -- } -- switch(options->electricBorderAction(border)) { -- case ElectricActionDashboard: { // Display Plasma dashboard -- QDBusInterface plasmaApp("org.kde.plasma-desktop", "/App"); -- plasmaApp.call("toggleDashboard"); -- } -- break; -- case ElectricActionShowDesktop: { -- Workspace::self()->setShowingDesktop(!Workspace::self()->showingDesktop()); -- break; -- } -- case ElectricActionLockScreen: { // Lock the screen -- QDBusInterface screenSaver("org.kde.screensaver", "/ScreenSaver"); -- screenSaver.call("Lock"); -- } -- break; -- case ElectricActionPreventScreenLocking: { -- break; -- } -- case ElectricActionNone: // Either desktop switching or an effect -- default: { -- if (effects && static_cast(effects)->borderActivated(border)) -- {} // Handled by effects -- else { -- if (options->electricBorders() == Options::ElectricAlways) { -- switchDesktop(border, pos); -- return; // Don't reset cursor position -- } -- emit activated(border); -- } -- } -- } -+ElectricBorderAction ScreenEdge::actionForEdge(Edge* edge) const -+{ -+ switch (edge->border()) { -+ case ElectricTopLeft: -+ return m_actionTopLeft; -+ case ElectricTop: -+ return m_actionTop; -+ case ElectricTopRight: -+ return m_actionTopRight; -+ case ElectricRight: -+ return m_actionRight; -+ case ElectricBottomRight: -+ return m_actionBottomRight; -+ case ElectricBottom: -+ return m_actionBottom; -+ case ElectricBottomLeft: -+ return m_actionBottomLeft; -+ case ElectricLeft: -+ return m_actionLeft; -+ default: -+ // fall through -+ break; -+ } -+ return ElectricActionNone; -+} -+ -+void ScreenEdge::reserveDesktopSwitching(bool isToReserve, Qt::Orientations o) -+{ -+ if (!o) -+ return; -+ for (QList::iterator it = m_edges.begin(); it != m_edges.end(); ++it) { -+ WindowBasedEdge *edge = *it; -+ if (edge->isCorner()) { -+ isToReserve ? edge->reserve() : edge->unreserve(); -+ } else { -+ if ((m_virtualDesktopLayout & Qt::Horizontal) && (edge->isLeft() || edge->isRight())) { -+ isToReserve ? edge->reserve() : edge->unreserve(); -+ } -+ if ((m_virtualDesktopLayout & Qt::Vertical) && (edge->isTop() || edge->isBottom())) { -+ isToReserve ? edge->reserve() : edge->unreserve(); - } - } -- } else { -- m_currentScreenEdge = border; -- m_screenEdgeTimeFirst = now; -- m_screenEdgeTimeLast = now; -- m_screenEdgePushPoint = pos; -- } -- -- // Reset the pointer to find out whether the user is really pushing -- // (the direction back from which it came, starting from top clockwise) -- const int xdiff[ELECTRIC_COUNT] = { 0, -- -pushback_pixels, -- -pushback_pixels, -- -pushback_pixels, -- 0, -- pushback_pixels, -- pushback_pixels, -- pushback_pixels -- }; -- const int ydiff[ELECTRIC_COUNT] = { pushback_pixels, -- pushback_pixels, -- 0, -- -pushback_pixels, -- -pushback_pixels, -- -pushback_pixels, -- 0, -- pushback_pixels -- }; -- QCursor::setPos(pos.x() + xdiff[border], pos.y() + ydiff[border]); --} -- --void ScreenEdge::switchDesktop(ElectricBorder border, const QPoint& _pos) --{ -- QPoint pos = _pos; -- int desk = Workspace::self()->currentDesktop(); -- const int OFFSET = 2; -- if (border == ElectricLeft || border == ElectricTopLeft || border == ElectricBottomLeft) { -- desk = Workspace::self()->desktopToLeft(desk, options->isRollOverDesktops()); -- pos.setX(displayWidth() - 1 - OFFSET); -- } -- if (border == ElectricRight || border == ElectricTopRight || border == ElectricBottomRight) { -- desk = Workspace::self()->desktopToRight(desk, options->isRollOverDesktops()); -- pos.setX(OFFSET); -- } -- if (border == ElectricTop || border == ElectricTopLeft || border == ElectricTopRight) { -- desk = Workspace::self()->desktopAbove(desk, options->isRollOverDesktops()); -- pos.setY(displayHeight() - 1 - OFFSET); -- } -- if (border == ElectricBottom || border == ElectricBottomLeft || border == ElectricBottomRight) { -- desk = Workspace::self()->desktopBelow(desk, options->isRollOverDesktops()); -- pos.setY(OFFSET); -- } -- Client *c = Workspace::self()->getMovingClient(); -- if (c && c->rules()->checkDesktop(desk) != desk) -- return; // user attempts to move a client to another desktop where it is ruleforced to not be -- int desk_before = Workspace::self()->currentDesktop(); -- Workspace::self()->setCurrentDesktop(desk); -- if (Workspace::self()->currentDesktop() != desk_before) -- QCursor::setPos(pos); -+ } -+} -+ -+void ScreenEdge::reserve(ElectricBorder border, QObject* object, const char* slot) -+{ -+ for (QList::iterator it = m_edges.begin(); it != m_edges.end(); ++it) { -+ if ((*it)->border() == border) { -+ (*it)->reserve(object, slot); -+ } -+ } -+} -+ -+void ScreenEdge::unreserve(ElectricBorder border, QObject* object) -+{ -+ for (QList::iterator it = m_edges.begin(); it != m_edges.end(); ++it) { -+ if ((*it)->border() == border) { -+ (*it)->unreserve(object); -+ } -+ } -+} -+ -+void ScreenEdge::check(const QPoint& pos, const QDateTime &now, bool forceNoPushback) -+{ -+ for (QList::iterator it = m_edges.begin(); it != m_edges.end(); ++it) { -+ (*it)->check(pos, now, forceNoPushback); -+ } - } - - bool ScreenEdge::isEntered(XEvent* e) - { - if (e->type == EnterNotify) { -- for (int i = 0; i < ELECTRIC_COUNT; ++i) -- if (m_screenEdgeWindows[i] != None && e->xcrossing.window == m_screenEdgeWindows[i]) { -- // The user entered an electric border -- check(QPoint(e->xcrossing.x_root, e->xcrossing.y_root), e->xcrossing.time); -- return true; -- } -+ return handleEnterNotify(e->xcrossing.window, -+ QPoint(e->xcrossing.x_root, e->xcrossing.y_root), -+ QDateTime::fromMSecsSinceEpoch(e->xcrossing.time)); - } - if (e->type == ClientMessage) { - if (e->xclient.message_type == atoms->xdnd_position) { -- for (int i = 0; i < ELECTRIC_COUNT; ++i) -- if (m_screenEdgeWindows[i] != None && e->xclient.window == m_screenEdgeWindows[i]) { -- updateXTime(); -- check(QPoint(e->xclient.data.l[2] >> 16, e->xclient.data.l[2] & 0xffff), xTime(), true); -- return true; -- } -+ return handleDndNotify(e->xclient.window, -+ QPoint(e->xclient.data.l[2] >> 16, e->xclient.data.l[2] & 0xffff)); -+ } -+ } -+ return false; -+} -+ -+bool ScreenEdge::handleEnterNotify(Window window, const QPoint &point, const QDateTime ×tamp) -+{ -+ for (QList::iterator it = m_edges.begin(); it != m_edges.end(); ++it) { -+ WindowBasedEdge *edge = *it; -+ if (!edge->isReserved()) { -+ continue; -+ } -+ if (edge->window() == window) { -+ edge->check(point, timestamp); -+ return true; -+ } -+ } -+ return false; -+} -+ -+bool ScreenEdge::handleDndNotify(Window window, const QPoint& point) -+{ -+ for (QList::iterator it = m_edges.begin(); it != m_edges.end(); ++it) { -+ WindowBasedEdge *edge = *it; -+ if (edge->isReserved() && edge->window() == window) { -+ updateXTime(); -+ edge->check(point, QDateTime::fromMSecsSinceEpoch(xTime()), true); -+ return true; - } - } - return false; -@@ -386,9 +893,14 @@ void ScreenEdge::ensureOnTop() - { - Window* windows = new Window[ 8 ]; // There are up to 8 borders - int pos = 0; -- for (int i = 0; i < ELECTRIC_COUNT; ++i) -- if (m_screenEdgeWindows[ i ] != None) -- windows[ pos++ ] = m_screenEdgeWindows[ i ]; -+ for (QList::const_iterator it = m_edges.constBegin(); -+ it != m_edges.constEnd(); -+ ++it) { -+ WindowBasedEdge *edge = *it; -+ if (edge->window() == None) { -+ windows[pos++] = edge->window(); -+ } -+ } - if (!pos) { - delete [] windows; - return; // No borders at all -@@ -399,9 +911,19 @@ void ScreenEdge::ensureOnTop() - } - - --const QVector< Window >& ScreenEdge::windows() -+QVector ScreenEdge::windows() const - { -- return m_screenEdgeWindows; -+ QVector wins; -+ for (QList::const_iterator it = m_edges.constBegin(); -+ it != m_edges.constEnd(); -+ ++it) { -+ Window win =(*it)->window(); -+ if (win != None) { -+ wins.append(win); -+ } -+ } -+ return wins; - } -+ - } //namespace - -diff --git a/kwin/screenedge.h b/kwin/screenedge.h -index d7da6e9..83669ce 100644 ---- a/kwin/screenedge.h -+++ b/kwin/screenedge.h -@@ -30,11 +30,93 @@ along with this program. If not, see . - #define KWIN_SCREENEDGE_H - #include - #include --#include "kwinglobals.h" -+#include -+#include -+ -+#include - -+#include "kwinglobals.h" - - namespace KWin { - -+class ScreenEdge; -+ -+class Edge: public QObject -+{ -+ Q_OBJECT -+public: -+ explicit Edge(ScreenEdge *edges); -+ virtual ~Edge(); -+ bool isLeft() const; -+ bool isRight() const; -+ bool isTop() const; -+ bool isBottom() const; -+ bool isCorner() const; -+ bool isScreenEdge(); -+ bool triggersFor(const QPoint &cursorPos) const; -+ void check(const QPoint &cursorPos, const QDateTime &triggerTime, bool forceNoPushBack = false); -+ bool isReserved() const; -+ -+ ElectricBorder border() const; -+ void reserve(QObject *object, const char *slot); -+ const QHash &callBacks() const; -+ -+public Q_SLOTS: -+ void reserve(); -+ void unreserve(); -+ void unreserve(QObject *object); -+ void setBorder(ElectricBorder border); -+ void setAction(ElectricBorderAction action); -+ void setGeometry(const QRect &geometry); -+ void checkBlocking(); -+protected: -+ ScreenEdge *edges(); -+ const ScreenEdge *edges() const; -+ const QRect &geometry() const; -+ bool isBlocked() const; -+ virtual void doGeometryUpdate(); -+ virtual void activate(); -+ virtual void deactivate(); -+ virtual void doUpdateBlocking(); -+private: -+ bool canActivate(const QPoint &cursorPos, const QDateTime &triggerTime); -+ void handle(const QPoint &cursorPos); -+ bool handleAction(); -+ bool handleByCallback(); -+ void switchDesktop(const QPoint &cursorPos); -+ void pushCursorBack(const QPoint &cursorPos); -+ ScreenEdge *m_edges; -+ ElectricBorder m_border; -+ ElectricBorderAction m_action; -+ int m_reserved; -+ QRect m_geometry; -+ QDateTime m_lastTrigger; -+ QDateTime m_lastReset; -+ QPoint m_triggeredPoint; -+ QHash m_callBacks; -+ bool m_blocked; -+}; -+ -+class WindowBasedEdge : public Edge -+{ -+ Q_OBJECT -+public: -+ explicit WindowBasedEdge(ScreenEdge *parent); -+ virtual ~WindowBasedEdge(); -+ -+ Window window() const; -+ -+protected: -+ virtual void doGeometryUpdate(); -+ virtual void doUpdateBlocking(); -+ virtual void activate(); -+ virtual void deactivate(); -+ -+private: -+ void createWindow(); -+ Window m_window; -+}; -+ - /** - * @short This class is used to handle the screen edges - * Screen Edge Window management. Screen Edges allow a user to change the virtual -@@ -50,6 +132,10 @@ public: - ScreenEdge(); - ~ScreenEdge(); - /** -+ * @internal -+ */ -+ void setConfig(KSharedConfig::Ptr config); -+ /** - * Initialize the screen edges. - */ - void init(); -@@ -60,27 +146,33 @@ public: - * @param now the time when the function is called - * @param forceNoPushBack needs to be called to workaround some DnD clients, don't use unless you want to chek on a DnD event - */ -- void check(const QPoint& pos, Time now, bool forceNoPushBack = false); -+ void check(const QPoint& pos, const QDateTime &now, bool forceNoPushBack = false); - /** -- * Restore the size of the specified screen edges -- * @param border the screen edge to restore the size of -+ * The (dpi dependent) length, reserved for the active corners of each edge - 1/3" - */ -- void restoreSize(ElectricBorder border); -- /** -- * Mark the specified screen edge as reserved in m_screenEdgeReserved -+ int cornerOffset() const; -+ /** -+ * Mark the specified screen edge as reserved. This method is provided for external activation -+ * like effects and scripts. When the effect/script does no longer need the edge it is supposed -+ * to call @link unreserve. - * @param border the screen edge to mark as reserved -+ * @param object The object on which the @p callback needs to be invoked -+ * @param callback The method name to be invoked - uses QMetaObject::invokeMethod -+ * @see unreserve -+ * @todo: add pointer to script/effect - */ -- void reserve(ElectricBorder border); -+ void reserve(ElectricBorder border, QObject *object, const char *slot); - /** -- * Mark the specified screen edge as unreserved in m_screenEdgeReserved -+ * Mark the specified screen edge as unreserved. This method is provided for external activation -+ * like effects and scripts. This method is only allowed to be called if @link reserve had been -+ * called before for the same @p border. An unbalanced calling of reserve/unreserve leads to the -+ * edge never being active or never being able to deactivate again. - * @param border the screen edge to mark as unreserved -+ * @param object the object on which the callback had been invoked -+ * @see reserve -+ * @todo: add pointer to script/effect - */ -- void unreserve(ElectricBorder border); -- /** -- * Reserve actions for screen edges, if reserve is true. Unreserve otherwise. -- * @param reserve indicated weather actions should be reserved or unreseved -- */ -- void reserveActions(bool isToReserve); -+ void unreserve(ElectricBorder border, QObject *object); - /** - * Reserve desktop switching for screen edges, if reserve is true. Unreserve otherwise. - * @param reserve indicated weather desktop switching should be reserved or unreseved -@@ -97,18 +189,45 @@ public: - * @param e the X event which is passed to this method. - */ - bool isEntered(XEvent * e); -+ - /** - * Returns a QVector of all existing screen edge windows - * @return all existing screen edge windows in a QVector - */ -- const QVector< Window >& windows(); --public Q_SLOTS: -+ QVector< Window > windows() const; -+ -+ bool isDesktopSwitching() const; -+ bool isDesktopSwitchingMovingClients() const; -+ const QSize &cursorPushBackDistance() const; - /** -- * Update the screen edge windows. Add new ones if the user specified -- * a new action or enabled desktop switching. Remove, if user deleted -- * actions or disabled desktop switching. -+ * Minimum time between the push back of the cursor and the activation by re-entering the edge. - */ -- void update(bool force=false); -+ int timeThreshold() const; -+ /** -+ * Minimum time between triggers -+ **/ -+ int reActivationThreshold() const; -+ ElectricBorderAction actionTopLeft() const; -+ ElectricBorderAction actionTop() const; -+ ElectricBorderAction actionTopRight() const; -+ ElectricBorderAction actionRight() const; -+ ElectricBorderAction actionBottomRight() const; -+ ElectricBorderAction actionBottom() const; -+ ElectricBorderAction actionBottomLeft() const; -+ ElectricBorderAction actionLeft() const; -+ -+public Q_SLOTS: -+ void reconfigure(); -+ /** -+ * Updates the layout of virtual desktops and adjust the reserved borders in case of -+ * virtual desktop switching on edges. -+ **/ -+ void updateLayout(); -+ /** -+ * Recreates all edges e.g. after the screen size changes. -+ **/ -+ void recreateEdges(); -+ - Q_SIGNALS: - /** - * Emitted when the @p border got activated and there is neither an effect nor a global -@@ -116,24 +235,226 @@ Q_SIGNALS: - * @param border The border which got activated - **/ - void activated(ElectricBorder border); -+ -+ void checkBlocking(); -+ - private: -- /** -- * Switch the desktop if desktop switching is enabled and a screen edge -- * is entered to trigger this action. -- */ -- void switchDesktop(ElectricBorder border, const QPoint& pos); -- -- QVector< Window > m_screenEdgeWindows; -- QVector< int > m_screenEdgeReserved; // Corners/edges used by something -- ElectricBorder m_currentScreenEdge; -- int m_screenEdgeLeft; -- int m_screenEdgeRight; -- int m_screenEdgeTop; -- int m_screenEdgeBottom; -- Time m_screenEdgeTimeFirst; -- Time m_screenEdgeTimeLast; -- Time m_screenEdgeTimeLastTrigger; -- QPoint m_screenEdgePushPoint; -+ enum { ElectricDisabled = 0, ElectricMoveOnly = 1, ElectricAlways = 2 }; -+ void setDesktopSwitching(bool enable); -+ void setDesktopSwitchingMovingClients(bool enable); -+ void setCursorPushBackDistance(const QSize &distance); -+ void setTimeThreshold(int treshold); -+ void setReActivationThreshold(int reshold); -+ void createHorizontalEdge(ElectricBorder border, const QRect &screen, const QRect &fullArea); -+ void createVerticalEdge(ElectricBorder border, const QRect &screen, const QRect &fullArea); -+ WindowBasedEdge *createEdge(ElectricBorder border, int x, int y, int width, int height); -+ void setActionForBorder(ElectricBorder border, ElectricBorderAction *oldValue, ElectricBorderAction newValue); -+ ElectricBorderAction actionForEdge(Edge *edge) const; -+ bool handleEnterNotify(Window window, const QPoint &point, const QDateTime ×tamp); -+ bool handleDndNotify(Window window, const QPoint &point); -+ bool m_desktopSwitching; -+ bool m_desktopSwitchingMovingClients; -+ QSize m_cursorPushBackDistance; -+ int m_timeThreshold; -+ int m_reactivateThreshold; -+ Qt::Orientations m_virtualDesktopLayout; -+ QList m_edges; -+ KSharedConfig::Ptr m_config; -+ ElectricBorderAction m_actionTopLeft; -+ ElectricBorderAction m_actionTop; -+ ElectricBorderAction m_actionTopRight; -+ ElectricBorderAction m_actionRight; -+ ElectricBorderAction m_actionBottomRight; -+ ElectricBorderAction m_actionBottom; -+ ElectricBorderAction m_actionBottomLeft; -+ ElectricBorderAction m_actionLeft; -+ int m_cornerOffset; - }; -+ -+ -+ -+/********************************************************** -+ * Inlines Edge -+ *********************************************************/ -+ -+inline bool Edge::isBottom() const -+{ -+ return m_border == ElectricBottom || m_border == ElectricBottomLeft || m_border == ElectricBottomRight; -+} -+ -+inline bool Edge::isLeft() const -+{ -+ return m_border == ElectricLeft || m_border == ElectricTopLeft || m_border == ElectricBottomLeft; -+} -+ -+inline bool Edge::isRight() const -+{ -+ return m_border == ElectricRight || m_border == ElectricTopRight || m_border == ElectricBottomRight; -+} -+ -+inline bool Edge::isTop() const -+{ -+ return m_border == ElectricTop || m_border == ElectricTopLeft || m_border == ElectricTopRight; -+} -+ -+inline bool Edge::isCorner() const -+{ -+ return m_border == ElectricTopLeft -+ || m_border == ElectricTopRight -+ || m_border == ElectricBottomRight -+ || m_border == ElectricBottomLeft; -+} -+ -+inline bool Edge::isScreenEdge() -+{ -+ return m_border == ElectricLeft -+ || m_border == ElectricTop -+ || m_border == ElectricRight -+ || m_border == ElectricBottom; - } -+ -+inline bool Edge::isReserved() const -+{ -+ return m_reserved != 0; -+} -+ -+inline void Edge::setAction(ElectricBorderAction action) -+{ -+ m_action = action; -+} -+ -+inline void Edge::setBorder(ElectricBorder border) -+{ -+ m_border = border; -+} -+ -+inline ScreenEdge *Edge::edges() -+{ -+ return m_edges; -+} -+ -+inline const ScreenEdge *Edge::edges() const -+{ -+ return m_edges; -+} -+ -+inline const QRect &Edge::geometry() const -+{ -+ return m_geometry; -+} -+ -+inline ElectricBorder Edge::border() const -+{ -+ return m_border; -+} -+ -+inline const QHash &Edge::callBacks() const -+{ -+ return m_callBacks; -+} -+ -+inline bool Edge::isBlocked() const -+{ -+ return m_blocked; -+} -+ -+ -+/********************************************************** -+ * Inlines WindowBasedEdge -+ *********************************************************/ -+ -+inline Window KWin::WindowBasedEdge::window() const -+{ -+ return m_window; -+} -+ -+ -+/********************************************************** -+ * Inlines ScreenEdges -+ *********************************************************/ -+ -+inline void ScreenEdge::setConfig(KSharedConfig::Ptr config) -+{ -+ m_config = config; -+} -+ -+inline int ScreenEdge::cornerOffset() const -+{ -+ return m_cornerOffset; -+} -+ -+inline const QSize &ScreenEdge::cursorPushBackDistance() const -+{ -+ return m_cursorPushBackDistance; -+} -+ -+inline bool ScreenEdge::isDesktopSwitching() const -+{ -+ return m_desktopSwitching; -+} -+ -+inline bool ScreenEdge::isDesktopSwitchingMovingClients() const -+{ -+ return m_desktopSwitchingMovingClients; -+} -+ -+inline int ScreenEdge::reActivationThreshold() const -+{ -+ return m_reactivateThreshold; -+} -+ -+inline int ScreenEdge::timeThreshold() const -+{ -+ return m_timeThreshold; -+} -+ -+inline void ScreenEdge::setCursorPushBackDistance(const QSize &distance) -+{ -+ m_cursorPushBackDistance = distance; -+} -+ -+inline void ScreenEdge::setDesktopSwitching(bool enable) -+{ -+ if (enable == m_desktopSwitching) { -+ return; -+ } -+ m_desktopSwitching = enable; -+ reserveDesktopSwitching(enable, m_virtualDesktopLayout); -+} -+ -+inline void ScreenEdge::setDesktopSwitchingMovingClients(bool enable) -+{ -+ m_desktopSwitchingMovingClients = enable; -+} -+ -+inline void ScreenEdge::setReActivationThreshold(int treshold) -+{ -+ m_reactivateThreshold = treshold; -+} -+ -+inline void ScreenEdge::setTimeThreshold(int treshold) -+{ -+ m_timeThreshold = treshold; -+} -+ -+#define ACTION( name) \ -+inline ElectricBorderAction ScreenEdge::name() const \ -+{ \ -+ return m_##name; \ -+} -+ -+ACTION(actionTopLeft) -+ACTION(actionTop) -+ACTION(actionTopRight) -+ACTION(actionRight) -+ACTION(actionBottomRight) -+ACTION(actionBottom) -+ACTION(actionBottomLeft) -+ACTION(actionLeft) -+ -+#undef ACTION -+ -+} // namespace -+ - #endif // KWIN_SCREENEDGE_H -+ -diff --git a/kwin/scripting/scriptedeffect.cpp b/kwin/scripting/scriptedeffect.cpp -index e9c0509..a651556 100644 ---- a/kwin/scripting/scriptedeffect.cpp -+++ b/kwin/scripting/scriptedeffect.cpp -@@ -269,20 +269,10 @@ ScriptedEffect::ScriptedEffect() - , m_scriptFile(QString()) - { - connect(m_engine, SIGNAL(signalHandlerException(QScriptValue)), SLOT(signalHandlerException(QScriptValue))); --#ifdef KWIN_BUILD_SCREENEDGES -- connect(Workspace::self()->screenEdge(), SIGNAL(activated(ElectricBorder)), SLOT(slotBorderActivated(ElectricBorder))); --#endif - } - - ScriptedEffect::~ScriptedEffect() - { --#ifdef KWIN_BUILD_SCREENEDGES -- for (QHash >::const_iterator it = m_screenEdgeCallbacks.constBegin(); -- it != m_screenEdgeCallbacks.constEnd(); -- ++it) { -- KWin::Workspace::self()->screenEdge()->unreserve(static_cast(it.key())); -- } --#endif - } - - bool ScriptedEffect::init(const QString &effectName, const QString &pathToScript) -diff --git a/kwin/scripting/scripting.cpp b/kwin/scripting/scripting.cpp -index 16a696c..59f2b43 100644 ---- a/kwin/scripting/scripting.cpp -+++ b/kwin/scripting/scripting.cpp -@@ -215,20 +215,10 @@ KWin::AbstractScript::AbstractScript(int id, QString scriptName, QString pluginN - if (m_pluginName.isNull()) { - m_pluginName = scriptName; - } --#ifdef KWIN_BUILD_SCREENEDGES -- connect(KWin::Workspace::self()->screenEdge(), SIGNAL(activated(ElectricBorder)), SLOT(borderActivated(ElectricBorder))); --#endif - } - - KWin::AbstractScript::~AbstractScript() - { --#ifdef KWIN_BUILD_SCREENEDGES -- for (QHash >::const_iterator it = m_screenEdgeCallbacks.constBegin(); -- it != m_screenEdgeCallbacks.constEnd(); -- ++it) { -- KWin::Workspace::self()->screenEdge()->unreserve(static_cast(it.key())); -- } --#endif - } - - KConfigGroup KWin::AbstractScript::config() const -diff --git a/kwin/scripting/scriptingutils.h b/kwin/scripting/scriptingutils.h -index 3ed7845..5d68ad7 100644 ---- a/kwin/scripting/scriptingutils.h -+++ b/kwin/scripting/scriptingutils.h -@@ -159,7 +159,7 @@ QScriptValue registerScreenEdge(QScriptContext *context, QScriptEngine *engine) - if (it == script->screenEdgeCallbacks().end()) { - // not yet registered - #ifdef KWIN_BUILD_SCREENEDGES -- KWin::Workspace::self()->screenEdge()->reserve(static_cast(edge)); -+ KWin::Workspace::self()->screenEdge()->reserve(static_cast(edge), script, "borderActivated"); - #endif - script->screenEdgeCallbacks().insert(edge, QList() << context->argument(1)); - } else { -diff --git a/kwin/workspace.cpp b/kwin/workspace.cpp -index dccc41f..343eed3 100644 ---- a/kwin/workspace.cpp -+++ b/kwin/workspace.cpp -@@ -270,7 +270,9 @@ void Workspace::screenChangeTimeout() - void Workspace::init() - { - #ifdef KWIN_BUILD_SCREENEDGES -+ m_screenEdge.setConfig(KGlobal::config()); - m_screenEdge.init(); -+ connect(options, SIGNAL(configChanged()), &m_screenEdge, SLOT(reconfigure())); - #endif - - // Not used yet -@@ -1008,12 +1010,6 @@ void Workspace::slotReconfigure() - kDebug(1212) << "Workspace::slotReconfigure()"; - reconfigureTimer.stop(); - --#ifdef KWIN_BUILD_SCREENEDGES -- m_screenEdge.reserveActions(false); -- if (options->electricBorders() == Options::ElectricAlways) -- m_screenEdge.reserveDesktopSwitching(false, m_screenEdgeOrientation); --#endif -- - bool borderlessMaximizedWindows = options->borderlessMaximizedWindows(); - - KGlobal::config()->reparseConfiguration(); -@@ -1046,19 +1042,6 @@ void Workspace::slotReconfigure() - c->triggerDecorationRepaint(); - } - --#ifdef KWIN_BUILD_SCREENEDGES -- m_screenEdge.reserveActions(true); -- if (options->electricBorders() == Options::ElectricAlways) { -- QSize desktopMatrix = rootInfo->desktopLayoutColumnsRows(); -- m_screenEdgeOrientation = 0; -- if (desktopMatrix.width() > 1) -- m_screenEdgeOrientation |= Qt::Horizontal; -- if (desktopMatrix.height() > 1) -- m_screenEdgeOrientation |= Qt::Vertical; -- m_screenEdge.reserveDesktopSwitching(true, m_screenEdgeOrientation); -- } -- m_screenEdge.update(); --#endif - loadWindowRules(); - for (ClientList::Iterator it = clients.begin(); - it != clients.end(); diff --git a/SOURCES/kde-workspace-4.11-bz#921781-check-max-viewport-size.patch b/SOURCES/kde-workspace-4.11-bz#921781-check-max-viewport-size.patch deleted file mode 100644 index 13ae868..0000000 --- a/SOURCES/kde-workspace-4.11-bz#921781-check-max-viewport-size.patch +++ /dev/null @@ -1,345 +0,0 @@ -diff --git a/kwin/kcmkwin/kwincompositing/dbus.h b/kwin/kcmkwin/kwincompositing/dbus.h -new file mode 100644 -index 0000000..05f5d23 ---- /dev/null -+++ b/kwin/kcmkwin/kwincompositing/dbus.h -@@ -0,0 +1,46 @@ -+/******************************************************************** -+ KWin - the KDE window manager -+ This file is part of the KDE project. -+ -+Copyright (C) 2012 Thomas Lübking -+ -+This program is free software; you can redistribute it and/or modify -+it under the terms of the GNU General Public License as published by -+the Free Software Foundation; either version 2 of the License, 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 General Public License -+along with this program. If not, see . -+*********************************************************************/ -+ -+#ifndef MAIN_ADAPTOR_H -+#define MAIN_ADAPTOR_H -+ -+#include -+#include "main.h" -+ -+namespace KWin -+{ -+class MainAdaptor : public QDBusAbstractAdaptor -+{ -+ Q_OBJECT -+ Q_CLASSINFO("D-Bus Interface", "org.kde.kwinCompositingDialog") -+ -+private: -+ KWinCompositingConfig *m_config; -+ -+public: -+ MainAdaptor(KWinCompositingConfig *config) : QDBusAbstractAdaptor(config), m_config(config) { } -+ -+public slots: -+ Q_NOREPLY void warn(QString message, QString details, QString dontAgainKey) { -+ m_config->warn(message, details, dontAgainKey); -+ } -+}; -+} -+#endif -\ No newline at end of file -diff --git a/kwin/kcmkwin/kwincompositing/main.cpp b/kwin/kcmkwin/kwincompositing/main.cpp -index 6e39479..e802b46 100644 ---- a/kwin/kcmkwin/kwincompositing/main.cpp -+++ b/kwin/kcmkwin/kwincompositing/main.cpp -@@ -19,6 +19,7 @@ along with this program. If not, see . - *********************************************************************/ - - #include "main.h" -+#include "dbus.h" - - #include "kwin_interface.h" - #include "kwinglobals.h" -@@ -76,13 +77,17 @@ ConfirmDialog::ConfirmDialog() : - } - - --KWinCompositingConfig::KWinCompositingConfig(QWidget *parent, const QVariantList &) -+KWinCompositingConfig::KWinCompositingConfig(QWidget *parent, const QVariantList &args) - : KCModule(KWinCompositingConfigFactory::componentData(), parent) - , mKWinConfig(KSharedConfig::openConfig("kwinrc")) - , m_showConfirmDialog(false) - , m_showDetailedErrors(new QAction(i18nc("Action to open a dialog showing detailed information why an effect could not be loaded", - "Details"), this)) -+ , m_dontShowAgain(new QAction(i18nc("Prevent warning from bein displayed again", "Don't show again!"), this)) - { -+ QDBusConnection::sessionBus().registerService("org.kde.kwinCompositingDialog"); -+ QDBusConnection::sessionBus().registerObject("/CompositorSettings", this); -+ new MainAdaptor(this); - KGlobal::locale()->insertCatalog("kwin_effects"); - ui.setupUi(this); - layout()->setMargin(0); -@@ -90,9 +95,34 @@ KWinCompositingConfig::KWinCompositingConfig(QWidget *parent, const QVariantList - ui.tabWidget->setCurrentIndex(0); - ui.statusTitleWidget->hide(); - ui.rearmGlSupport->hide(); -- ui.messageBox->setVisible(false); -- ui.messageBox->addAction(m_showDetailedErrors); - ui.messageBox->setMessageType(KMessageWidget::Warning); -+ ui.messageBox->addAction(m_dontShowAgain); -+ foreach (QWidget *w, m_dontShowAgain->associatedWidgets()) -+ w->setVisible(false); -+ ui.messageBox->addAction(m_showDetailedErrors); -+ -+ bool showMessage = false; -+ QString message, details, dontAgainKey; -+ if (args.count() > 1) { -+ for (int i = 0; i < args.count() - 1; ++i) { -+ if (args.at(i).toString() == "warn") { -+ showMessage = true; -+ message = QString::fromLocal8Bit(QByteArray::fromBase64(args.at(i+1).toByteArray())); -+ } else if (args.at(i).toString() == "details") { -+ showMessage = true; -+ details = QString::fromLocal8Bit(QByteArray::fromBase64(args.at(i+1).toByteArray())); -+ } else if (args.at(i).toString() == "dontagain") { -+ showMessage = true; -+ dontAgainKey = args.at(i+1).toString(); -+ } -+ } -+ } -+ -+ if (showMessage) { -+ ui.messageBox->setVisible(showMessage); // first show, animation is broken on init -+ warn(message, details, dontAgainKey); -+ } else -+ ui.messageBox->setVisible(false); - ui.ghns->setIcon(KIcon("get-hot-new-stuff")); - - // For future use -@@ -133,6 +163,7 @@ KWinCompositingConfig::KWinCompositingConfig(QWidget *parent, const QVariantList - connect(ui.glShaders, SIGNAL(toggled(bool)), this, SLOT(changed())); - connect(ui.glColorCorrection, SIGNAL(toggled(bool)), this, SLOT(changed())); - connect(m_showDetailedErrors, SIGNAL(triggered(bool)), SLOT(showDetailedEffectLoadingInformation())); -+ connect(m_dontShowAgain, SIGNAL(triggered(bool)), SLOT(blockFutureWarnings())); - connect(ui.ghns, SIGNAL(clicked(bool)), SLOT(slotGHNS())); - - // Open the temporary config file -@@ -615,10 +646,15 @@ void KWinCompositingConfig::checkLoadedEffects() - } - if (!disabledEffects.isEmpty()) { - m_showDetailedErrors->setData(disabledEffects); -+ foreach (QWidget *w, m_showDetailedErrors->associatedWidgets()) -+ w->setVisible(true); - ui.messageBox->setText(i18ncp("Error Message shown when a desktop effect could not be loaded", - "One desktop effect could not be loaded.", - "%1 desktop effects could not be loaded.", disabledEffects.count())); - ui.messageBox->animatedShow(); -+ } else { -+ foreach (QWidget *w, m_showDetailedErrors->associatedWidgets()) -+ w->setVisible(false); - } - } - } -@@ -653,7 +689,9 @@ void KWinCompositingConfig::showDetailedEffectLoadingInformation() - label->setOpenExternalLinks(true); - vboxLayout->addWidget(titleWidget); - vboxLayout->addWidget(label); -- if (compositingType != "none") { -+ if (!m_externErrorDetails.isNull()) { -+ label->setText(m_externErrorDetails); -+ } else if (compositingType != "none") { - QString text; - if (disabledEffects.count() > 1) { - text = "
    "; -@@ -716,6 +754,31 @@ void KWinCompositingConfig::showDetailedEffectLoadingInformation() - dialog->show(); - } - -+void KWinCompositingConfig::warn(QString message, QString details, QString dontAgainKey) -+{ -+ ui.messageBox->setText(message); -+ m_dontShowAgain->setData(dontAgainKey); -+ foreach (QWidget *w, m_dontShowAgain->associatedWidgets()) -+ w->setVisible(!dontAgainKey.isEmpty()); -+ m_externErrorDetails = details.isNull() ? "" : details; -+ foreach (QWidget *w, m_showDetailedErrors->associatedWidgets()) -+ w->setVisible(!m_externErrorDetails.isEmpty()); -+ ui.messageBox->animatedShow(); -+} -+ -+void KWinCompositingConfig::blockFutureWarnings() { -+ QString key; -+ if (QAction *act = qobject_cast(sender())) -+ key = act->data().toString(); -+ if (key.isEmpty()) -+ return; -+ QStringList l = key.split(':', QString::SkipEmptyParts); -+ KConfig cfg(l.count() > 1 ? l.at(0) : "kwin_dialogsrc"); -+ KConfigGroup(&cfg, "Notification Messages").writeEntry(l.last(), false); -+ cfg.sync(); -+ ui.messageBox->animatedHide(); -+} -+ - void KWinCompositingConfig::configChanged(bool reinitCompositing) - { - // Send signal to kwin -@@ -779,4 +842,5 @@ void KWinCompositingConfig::slotGHNS() - - } // namespace - -+#include "dbus.moc" - #include "main.moc" -diff --git a/kwin/kcmkwin/kwincompositing/main.h b/kwin/kcmkwin/kwincompositing/main.h -index 35079c3..d806531 100644 ---- a/kwin/kcmkwin/kwincompositing/main.h -+++ b/kwin/kcmkwin/kwincompositing/main.h -@@ -74,6 +74,8 @@ public slots: - void configChanged(bool reinitCompositing); - void initEffectSelector(); - -+ void warn(QString message, QString details, QString dontAgainKey); -+ - private slots: - void confirmReInit() { showConfirmDialog(true); } - void rearmGlSupport(); -@@ -82,6 +84,7 @@ private slots: - void toggleEffectShortcutChanged(const QKeySequence &seq); - void updateStatusUI(bool compositingIsPossible); - void showDetailedEffectLoadingInformation(); -+ void blockFutureWarnings(); - void slotGHNS(); - - private: -@@ -97,6 +100,8 @@ private: - KActionCollection* m_actionCollection; - QString originalGraphicsSystem; - QAction *m_showDetailedErrors; -+ QAction *m_dontShowAgain; -+ QString m_externErrorDetails; - }; - - } // namespace -diff --git a/kwin/scene_opengl.cpp b/kwin/scene_opengl.cpp -index 6cf62c6..7df85ce 100644 ---- a/kwin/scene_opengl.cpp -+++ b/kwin/scene_opengl.cpp -@@ -97,11 +97,18 @@ Sources and other compositing managers: - #include - - #include -+#include -+#include -+#include - #include -+#include - #include - #include - #include - -+#include -+#include -+ - namespace KWin - { - -@@ -152,6 +159,8 @@ SceneOpenGL::SceneOpenGL(Workspace* ws, OpenGLBackend *backend) - init_ok = false; - return; - } -+ if (!viewportLimitsMatched(QSize(displayWidth(), displayHeight()))) -+ return; - - // perform Scene specific checks - GLPlatform *glPlatform = GLPlatform::instance(); -@@ -434,8 +443,75 @@ SceneOpenGL::Texture *SceneOpenGL::createTexture(const QPixmap &pix, GLenum targ - return new Texture(m_backend, pix, target); - } - -+bool SceneOpenGL::viewportLimitsMatched(const QSize &size) const { -+ GLint limit[2]; -+ glGetIntegerv(GL_MAX_VIEWPORT_DIMS, limit); -+ if (limit[0] < size.width() || limit[1] < size.height()) { -+ QMetaObject::invokeMethod(Compositor::self(), "suspend", -+ Qt::QueuedConnection); -+ const QString message = i18n("

    OpenGL desktop effects not possible

    " -+ "Your system cannot perform OpenGL Desktop Effects at the " -+ "current resolution

    " -+ "You can try to select the XRender backend , but it " -+ "might be very slow for this resolution as well.
    " -+ "Alternatively, lower the combined resolution of all screens " -+ "to %1x%2 ", limit[0], limit[1]); -+ const QString details = i18n("The demanded resolution exceeds the GL_MAX_VIEWPORT_DIMS " -+ "limitation of your GPU and is therefore not compatible " -+ "with the OpenGL compositor.
    " -+ "XRender does not know such limitation, but the performance " -+ "will usually be impacted by the hardware limitations that" -+ "restrict the OpenGL viewport size."); -+ const int oldTimeout = QDBusConnection::sessionBus().interface()->timeout(); -+ QDBusConnection::sessionBus().interface()->setTimeout(500); -+ if (QDBusConnection::sessionBus().interface()->isServiceRegistered("org.kde.kwinCompositingDialog").value()) { -+ QDBusInterface dialog( "org.kde.kwinCompositingDialog", "/CompositorSettings", "org.kde.kwinCompositingDialog" ); -+ dialog.asyncCall("warn", message, details, ""); -+ } else { -+ const QString args = "warn " + message.toLocal8Bit().toBase64() + " details " + details.toLocal8Bit().toBase64(); -+ KProcess::startDetached("kcmshell4", QStringList() << "kwincompositing" << "--args" << args); -+ } -+ QDBusConnection::sessionBus().interface()->setTimeout(oldTimeout); -+ return false; -+ } -+ glGetIntegerv(GL_MAX_TEXTURE_SIZE, limit); -+ if (limit[0] < size.width() || limit[0] < size.height()) { -+ KConfig cfg("kwin_dialogsrc"); -+ -+ if (!KConfigGroup(&cfg, "Notification Messages").readEntry("max_tex_warning", true)) -+ return true; -+ -+ const QString message = i18n("

    OpenGL desktop effects might be unusable

    " -+ "OpenGL Desktop Effects at the current resolution are supported " -+ "but might be exceptionally slow.
    " -+ "Also large windows will turn entirely black.

    " -+ "Consider to suspend compositing, switch to the XRender backend " -+ "or lower the resolution to %1x%1." , limit[0]); -+ const QString details = i18n("The demanded resolution exceeds the GL_MAX_TEXTURE_SIZE " -+ "limitation of your GPU, thus windows of that size cannot be " -+ "assigned to textures and will be entirely black.
    " -+ "Also this limit will often be a performance level barrier despite " -+ "below GL_MAX_VIEWPORT_DIMS, because the driver might fall back to " -+ "software rendering in this case."); -+ const int oldTimeout = QDBusConnection::sessionBus().interface()->timeout(); -+ QDBusConnection::sessionBus().interface()->setTimeout(500); -+ if (QDBusConnection::sessionBus().interface()->isServiceRegistered("org.kde.kwinCompositingDialog").value()) { -+ QDBusInterface dialog( "org.kde.kwinCompositingDialog", "/CompositorSettings", "org.kde.kwinCompositingDialog" ); -+ dialog.asyncCall("warn", message, details, "kwin_dialogsrc:max_tex_warning"); -+ } else { -+ const QString args = "warn " + message.toLocal8Bit().toBase64() + " details " + -+ details.toLocal8Bit().toBase64() + " dontagain kwin_dialogsrc:max_tex_warning"; -+ KProcess::startDetached("kcmshell4", QStringList() << "kwincompositing" << "--args" << args); -+ } -+ QDBusConnection::sessionBus().interface()->setTimeout(oldTimeout); -+ } -+ return true; -+} -+ - void SceneOpenGL::screenGeometryChanged(const QSize &size) - { -+ if (!viewportLimitsMatched(size)) -+ return; - Scene::screenGeometryChanged(size); - glViewport(0,0, size.width(), size.height()); - m_backend->screenGeometryChanged(size); -diff --git a/kwin/scene_opengl.h b/kwin/scene_opengl.h -index e6142c0..c10c611 100644 ---- a/kwin/scene_opengl.h -+++ b/kwin/scene_opengl.h -@@ -79,6 +79,8 @@ public Q_SLOTS: - protected: - bool init_ok; - private: -+ bool viewportLimitsMatched(const QSize &size) const; -+private: - QHash< Toplevel*, Window* > windows; - bool debug; - OpenGLBackend *m_backend; diff --git a/SOURCES/kde-workspace-4.11-remove-dependency-on-kdepimlibs-4.11.patch b/SOURCES/kde-workspace-4.11-remove-dependency-on-kdepimlibs-4.11.patch new file mode 100644 index 0000000..2db3576 --- /dev/null +++ b/SOURCES/kde-workspace-4.11-remove-dependency-on-kdepimlibs-4.11.patch @@ -0,0 +1,5163 @@ +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 992b8f6..27317c3 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -178,7 +178,7 @@ set_package_properties(Akonadi PROPERTIES DESCRIPTION "An extensible cross-deskt + PURPOSE "Required to build certain Plasma DataEngines (Akonadi, Calendar)" + ) + +-macro_optional_find_package(KdepimLibs 4.10.68 QUIET CONFIG) ++macro_optional_find_package(KdepimLibs 4.5.60 QUIET CONFIG) + set_package_properties(KdepimLibs PROPERTIES DESCRIPTION "The KDEPIM libraries" + URL "http://pim.kde.org" + TYPE OPTIONAL +diff --git a/plasma/generic/dataengines/calendar/CMakeLists.txt b/plasma/generic/dataengines/calendar/CMakeLists.txt +index 6ad3b61..5539f99 100644 +--- a/plasma/generic/dataengines/calendar/CMakeLists.txt ++++ b/plasma/generic/dataengines/calendar/CMakeLists.txt +@@ -15,6 +15,13 @@ if(Akonadi_FOUND) + ADD_DEFINITIONS(-DAKONADI_FOUND) + set(calendar_engine_srcs ${calendar_engine_srcs} + eventdatacontainer.cpp ++ # taken from kdepim/akonadi/kcal as long as it's not yet exported: ++ akonadi/calendar.cpp ++ akonadi/calendarmodel.cpp ++ akonadi/calfilterproxymodel.cpp ++ akonadi/utils.cpp ++ akonadi/blockalarmsattribute.cpp ++ akonadi/collectionselection.cpp + ) + endif(Akonadi_FOUND) + +@@ -33,7 +40,6 @@ target_link_libraries( + if(Akonadi_FOUND) + target_link_libraries( + plasma_engine_calendar +- akonadi-calendar + ${KDE4_AKONADI_LIBS} + ${KDEPIMLIBS_AKONADI_KCAL_LIBS} + ) +diff --git a/plasma/generic/dataengines/calendar/akonadi/README.txt b/plasma/generic/dataengines/calendar/akonadi/README.txt +new file mode 100644 +index 0000000..dc1d36c +--- /dev/null ++++ b/plasma/generic/dataengines/calendar/akonadi/README.txt +@@ -0,0 +1,5 @@ ++These files have been copied from trunk kdepim/calendarsupport as at 2011-02-27 and should be kept in sync for bug fixes. ++ ++Note that the exports have been removed from the headers, but no other changes have been made. ++ ++These classes will be moved from kdepim to kdepimlibs in KDE SC 4.7 and this copy should be deleted when this happens. At this time it is recommended to move the calendar events dataengine logic into the akonadi dataengine. +diff --git a/plasma/generic/dataengines/calendar/akonadi/blockalarmsattribute.cpp b/plasma/generic/dataengines/calendar/akonadi/blockalarmsattribute.cpp +new file mode 100644 +index 0000000..28dff0c +--- /dev/null ++++ b/plasma/generic/dataengines/calendar/akonadi/blockalarmsattribute.cpp +@@ -0,0 +1,55 @@ ++/* ++ Copyright (C) 2010 Klarälvdalens Datakonsult AB, ++ a KDAB Group company, info@kdab.net, ++ author Tobias Koenig ++ ++ 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 "blockalarmsattribute.h" ++ ++#include ++ ++using namespace CalendarSupport; ++ ++BlockAlarmsAttribute::BlockAlarmsAttribute() ++{ ++} ++ ++BlockAlarmsAttribute::~BlockAlarmsAttribute() ++{ ++} ++ ++QByteArray BlockAlarmsAttribute::type() const ++{ ++ return "BlockAlarmsAttribute"; ++} ++ ++BlockAlarmsAttribute* BlockAlarmsAttribute::clone() const ++{ ++ return new BlockAlarmsAttribute(); ++} ++ ++QByteArray BlockAlarmsAttribute::serialized() const ++{ ++ return QByteArray(); ++} ++ ++void BlockAlarmsAttribute::deserialize( const QByteArray &data ) ++{ ++ Q_ASSERT( data.isEmpty() ); ++ Q_UNUSED( data ); ++} +diff --git a/plasma/generic/dataengines/calendar/akonadi/blockalarmsattribute.h b/plasma/generic/dataengines/calendar/akonadi/blockalarmsattribute.h +new file mode 100644 +index 0000000..71c8284 +--- /dev/null ++++ b/plasma/generic/dataengines/calendar/akonadi/blockalarmsattribute.h +@@ -0,0 +1,74 @@ ++/* ++ Copyright (C) 2010 Klarälvdalens Datakonsult AB, ++ a KDAB Group company, info@kdab.net, ++ author Tobias Koenig ++ ++ 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. ++*/ ++ ++#ifndef CALENDARSUPPORT_BLOCKALARMSATTRIBUTE_H ++#define CALENDARSUPPORT_BLOCKALARMSATTRIBUTE_H ++ ++#include ++ ++namespace CalendarSupport { ++ ++/** ++ * @short An Attribute that marks that alarms from an calendar collection are blocked. ++ * ++ * A calendar collection which has this attribute set won't be evaluated by korgac and ++ * therefor its alarms won't be used. ++ * ++ * @author Tobias Koenig ++ * @see Akonadi::Attribute ++ */ ++class BlockAlarmsAttribute : public Akonadi::Attribute ++{ ++ public: ++ /** ++ * Creates a new block alarms attribute. ++ */ ++ BlockAlarmsAttribute(); ++ ++ /** ++ * Destroys the block alarms attribute. ++ */ ++ ~BlockAlarmsAttribute(); ++ ++ /** ++ * Reimplemented from Attribute ++ */ ++ QByteArray type() const; ++ ++ /** ++ * Reimplemented from Attribute ++ */ ++ BlockAlarmsAttribute* clone() const; ++ ++ /** ++ * Reimplemented from Attribute ++ */ ++ QByteArray serialized() const; ++ ++ /** ++ * Reimplemented from Attribute ++ */ ++ void deserialize( const QByteArray &data ); ++}; ++ ++} ++ ++#endif +diff --git a/plasma/generic/dataengines/calendar/akonadi/calendar.cpp b/plasma/generic/dataengines/calendar/akonadi/calendar.cpp +new file mode 100644 +index 0000000..8ff81ef +--- /dev/null ++++ b/plasma/generic/dataengines/calendar/akonadi/calendar.cpp +@@ -0,0 +1,2125 @@ ++/* ++ Copyright (c) 2009 KDAB ++ Author: Sebastian Sauer ++ Frank Osterfeld ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, 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 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 "calendar.h" ++#include "calendar_p.h" ++ ++#include "collectionselection.h" ++#include "blockalarmsattribute.h" ++#include "utils.h" ++ ++#include ++#include ++#include ++#include ++#include ++ ++using namespace CalendarSupport; ++ ++Calendar::Private::Private( QAbstractItemModel *treeModel, QAbstractItemModel *model, Calendar *qq ) ++ : q( qq ), ++ mTimeZones( new KCalCore::ICalTimeZones ), ++ mNewObserver( false ), ++ mObserversEnabled( true ), ++ mDefaultFilter( new KCalCore::CalFilter ), ++ m_treeModel( treeModel ), ++ m_model( model ) ++{ ++ // Setup default filter, which does nothing ++ mDefaultFilter->setEnabled( false ); ++ m_filterProxy = new CalFilterProxyModel( q ); ++ m_filterProxy->setFilter( mDefaultFilter ); ++ m_filterProxy->setSourceModel( model ); ++ m_filterProxy->setObjectName( "Implements KCalCore filtering functionality" ); ++ ++ // user information... ++ mOwner.setName( i18n( "Unknown Name" ) ); ++ mOwner.setEmail( i18n( "unknown@nowhere" ) ); ++ ++ connect( m_model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), ++ this, SLOT(dataChanged(QModelIndex,QModelIndex)) ); ++ ++ connect( m_model, SIGNAL(layoutChanged()), ++ this, SLOT(layoutChanged()) ); ++ ++ connect( m_model, SIGNAL(modelReset()), ++ this, SLOT(modelReset()) ); ++ ++ connect( m_model, SIGNAL(rowsInserted(QModelIndex,int,int)), ++ this, SLOT(rowsInserted(QModelIndex,int,int)) ); ++ ++ connect( m_model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), ++ this, SLOT(rowsAboutToBeRemoved(QModelIndex,int,int)) ); ++ ++ // use the unfiltered model to catch collections ++ connect( m_treeModel, SIGNAL(rowsInserted(QModelIndex,int,int)), ++ this, SLOT(rowsInsertedInTreeModel(QModelIndex,int,int)) ); ++ ++ connect( m_treeModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), ++ this, SLOT(rowsAboutToBeRemovedInTreeModel(QModelIndex,int,int)) ); ++ ++ connect( m_treeModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)), ++ this, SLOT(dataChangedInTreeModel(QModelIndex,QModelIndex)) ); ++ ++ connect( m_treeModel, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), ++ SLOT(onRowsMovedInTreeModel(QModelIndex,int,int,QModelIndex,int)) ); ++ ++ /* ++ connect( m_monitor, SIGNAL(itemLinked(Akonadi::Item,Akonadi::Collection)), ++ this, SLOT(itemAdded(Akonadi::Item,Akonadi::Collection)) ); ++ connect( m_monitor, SIGNAL(itemUnlinked(Akonadi::Item,Akonadi::Collection)), ++ this, SLOT(itemRemoved(Akonadi::Item,Akonadi::Collection)) ); ++ */ ++} ++ ++void Calendar::Private::rowsInsertedInTreeModel( const QModelIndex &parent, int start, int end ) ++{ ++ collectionsAdded( collectionsFromModel( m_treeModel, parent, start, end ) ); ++} ++ ++void Calendar::Private::rowsAboutToBeRemovedInTreeModel( const QModelIndex &parent, ++ int start, int end ) ++{ ++ collectionsRemoved( collectionsFromModel( m_treeModel, parent, start, end ) ); ++} ++ ++void Calendar::Private::dataChangedInTreeModel( const QModelIndex &topLeft, ++ const QModelIndex &bottomRight ) ++{ ++ Q_ASSERT( topLeft.row() <= bottomRight.row() ); ++ const int endRow = bottomRight.row(); ++ QModelIndex i( topLeft ); ++ int row = i.row(); ++ while ( row <= endRow ) { ++ const Akonadi::Collection col = collectionFromIndex( i ); ++ if ( col.isValid() ) { ++ // Attributes might have changed, store the new collection and discard the old one ++ m_collectionMap.insert( col.id(), col ); ++ } ++ ++row; ++ i = i.sibling( row, topLeft.column() ); ++ } ++} ++ ++void Calendar::Private::rowsInserted( const QModelIndex &parent, int start, int end ) ++{ ++ itemsAdded( itemsFromModel( m_model, parent, start, end ) ); ++} ++ ++void Calendar::Private::rowsAboutToBeRemoved( const QModelIndex &parent, int start, int end ) ++{ ++ itemsRemoved( itemsFromModel( m_model, parent, start, end ) ); ++} ++ ++void Calendar::Private::layoutChanged() ++{ ++ ++} ++ ++void Calendar::Private::onRowsMovedInTreeModel( const QModelIndex &sourceParent, int sourceStart, int sourceEnd, ++ const QModelIndex &destinationParent, int destinationRow ) ++{ ++ Q_ASSERT( sourceEnd >= sourceStart ); ++ Q_ASSERT( sourceStart >= 0 ); ++ Q_ASSERT( destinationRow >= 0 ); ++ ++ const Akonadi::Collection sourceCollection = collectionFromIndex( sourceParent ); ++ const Akonadi::Collection destinationCollection = collectionFromIndex( destinationParent ); ++ ++ if ( sourceCollection.isValid() && destinationCollection.isValid() && ++ sourceCollection.id() != destinationCollection.id() ) { ++ const int numItems = sourceEnd - sourceStart + 1; ++ Akonadi::Item::List movedItems = itemsFromModel( m_treeModel, destinationParent, destinationRow, ++ destinationRow + numItems - 1 ); ++ ++ { // Start hack ++ // KSelectionProxyModel doesn't honour rowsMoved() yet, so, if the source model emitted rowsMoved ++ // (items changing collection) we could only catch it in the onLayoutChanged() slot, which isn't ++ // performant. So we listen to the source model's rowsMoved() and check manuall if it when in or ++ // out of the selection, and notify the application. ++ Akonadi::EntityMimeTypeFilterModel *m = qobject_cast( m_model ); ++ if ( m ) { ++ KSelectionProxyModel *sm = qobject_cast( m->sourceModel() ); ++ if ( sm ) { ++ CollectionSelection collectionSelection( sm->selectionModel() ); ++ const bool sourceCollectionIsSelected = collectionSelection.contains( sourceCollection.id() ); ++ const bool destinationCollectionIsSelected = collectionSelection.contains( destinationCollection.id() ); ++ if ( sourceCollectionIsSelected && destinationCollectionIsSelected ) { ++ foreach( const Akonadi::Item item, movedItems ) { ++ if ( item.isValid() && item.hasPayload() ) { ++ // We have old items ( that think they belong to another collection ) inside m_itemMap ++ if ( m_itemMap.contains( item.id() ) ) { ++ itemsRemoved( movedItems ); ++ itemsAdded( movedItems ); ++ } ++ } ++ } ++ } else if ( !sourceCollectionIsSelected && destinationCollectionIsSelected ) { // Added ++ itemsAdded( movedItems ); ++ } else if ( sourceCollectionIsSelected && !destinationCollectionIsSelected ) { // Removed ++ itemsRemoved( movedItems ); ++ } ++ } ++ } ++ } // end hack ++ } ++} ++ ++void Calendar::Private::appendVirtualItems( Akonadi::Item::List &itemList ) ++{ ++ foreach( const Akonadi::Item &item, itemList ) { ++ if ( m_virtualItems.contains( item.id() ) ) { ++ itemList.append( m_virtualItems.value( item.id() ) ); ++ } ++ } ++} ++ ++void Calendar::Private::modelReset() ++{ ++ clear(); ++ readFromModel(); ++} ++ ++void Calendar::Private::clear() ++{ ++ itemsRemoved( m_itemMap.values() ); ++ Q_ASSERT( m_itemMap.isEmpty() ); ++ m_childToParent.clear(); ++ m_parentToChildren.clear(); ++ m_childToUnseenParent.clear(); ++ m_unseenParentToChildren.clear(); ++ m_itemIdsForDate.clear(); ++ m_itemDateForItemId.clear(); ++ m_virtualItems.clear(); ++} ++ ++void Calendar::Private::readFromModel() ++{ ++ itemsAdded( itemsFromModel( m_model ) ); ++} ++ ++void Calendar::Private::dataChanged( const QModelIndex &topLeft, const QModelIndex &bottomRight ) ++{ ++ // kDebug(); ++ Q_ASSERT( topLeft.row() <= bottomRight.row() ); ++ const int endRow = bottomRight.row(); ++ QModelIndex i( topLeft ); ++ int row = i.row(); ++ while ( row <= endRow ) { ++ const Akonadi::Item item = itemFromIndex( i ); ++ if ( item.isValid() ) { ++ updateItem( item, AssertExists ); ++ } ++ ++row; ++ i = i.sibling( row, topLeft.column() ); ++ } ++ emit q->calendarChanged(); ++} ++ ++Calendar::Private::~Private() ++{ ++ Q_FOREACH ( const Akonadi::Item &item, m_itemMap ) { ++ CalendarSupport::incidence( item )->unRegisterObserver( q ); ++ } ++ ++ delete mTimeZones; ++ delete mDefaultFilter; ++} ++ ++void Calendar::Private::assertInvariants() const ++{ ++} ++ ++void Calendar::Private::updateItem( const Akonadi::Item &item, UpdateMode mode ) ++{ ++ assertInvariants(); ++ const bool alreadyExisted = m_itemMap.contains( item.id() ); ++ const Akonadi::Item::Id id = item.id(); ++ ++ const KCalCore::Incidence::Ptr incidence = CalendarSupport::incidence( item ); ++ ++ if ( !incidence ) { ++ return; ++ } ++ ++ /* ++ kDebug() << "id=" << item.id() ++ << "version=" << item.revision() ++ << "alreadyExisted=" << alreadyExisted ++ << "; mode = " << mode ++ << "; uid = " << incidence->uid() ++ << "; storageCollection.id() = " << item.storageCollectionId() // the real collection ++ << "; parentCollection.id() = " << item.parentCollection().id(); // can be a virtual collection ++ */ ++ if ( mode != AssertExists && alreadyExisted ) { ++ // An item from a virtual folder was inserted and we already have an item with ++ // this id, belonging to the real collection. So we just insert it in m_virtualItems ++ // so we keep track of it. Most hashes are indexed by Item::Id, and korg does lookups by Id too, ++ // so we can't just treat this item as an independent one. ++ if ( m_itemMap[id].parentCollection().id() != item.parentCollection().id() ) { ++ m_virtualItems[item.id()].append( item ); ++ q->notifyIncidenceAdded( item ); ++ } else { ++ kError() << "Item " << item.id() << " is already known."; ++ } ++ return; ++ } ++ ++ //Q_ASSERT( mode == DontCare || alreadyExisted == ( mode == AssertExists ) ); ++ ++ if ( alreadyExisted ) { ++ if ( !m_itemMap.contains( id ) ) { ++ // Item was deleted almost at the same time the change was made ++ // ignore this change ++ return; ++ } ++ ++ if ( item.storageCollectionId() == -1 ) { ++ // A valid item can have an invalid storage id if it was deleted while ++ // fetching the ancestor ++ return; ++ } ++ ++ if ( item.storageCollectionId() != m_itemMap.value( id ).storageCollectionId() ) { ++ // An item moved happened, update our internal copy, the storateCollectionId has changed. ++ Akonadi::Collection::Id oldCollectionId = m_itemMap.value( id ).storageCollectionId(); ++ m_itemMap.insert( id, item ); ++ if ( item.isValid() ) { ++ UnseenItem oldUi; ++ UnseenItem newUi; ++ oldUi.collection = oldCollectionId; ++ oldUi.uid = incidence->uid(); ++ if ( m_uidToItemId.contains( oldUi ) ) { ++ newUi.collection = item.storageCollectionId(); ++ newUi.uid = oldUi.uid; ++ m_uidToItemId.remove( oldUi ); ++ m_uidToItemId.insert( newUi, item.id() ); ++ } else { ++ Q_ASSERT_X( false, "Calendar::Private::updateItem", "Item wasn't found in m_uidToItemId" ); ++ return; ++ } ++ } ++ } ++ // update-only goes here ++ } else { ++ // new-only goes here ++ const Akonadi::Collection::Rights rights = item.parentCollection().rights(); ++ if ( !( rights & Akonadi::Collection::CanDeleteItem ) && ++ !( rights & Akonadi::Collection::CanChangeItem ) && ++ !incidence->isReadOnly() ) { ++ incidence->setReadOnly( true ); ++ } ++ } ++ ++ if ( alreadyExisted && m_itemDateForItemId.contains( item.id() ) ) { ++ // for changed items, we must remove existing date entries (they might have changed) ++ m_itemIdsForDate.remove( m_itemDateForItemId[item.id()], item.id() ); ++ m_itemDateForItemId.remove( item.id() ); ++ } ++ ++ QString date; ++ if ( const KCalCore::Todo::Ptr t = CalendarSupport::todo( item ) ) { ++ if ( t->hasDueDate() ) { ++ date = t->dtDue().date().toString(); ++ } ++ } else if ( const KCalCore::Event::Ptr e = CalendarSupport::event( item ) ) { ++ if ( !e->recurs() && !e->isMultiDay() ) { ++ date = e->dtStart().date().toString(); ++ } ++ } else if ( const KCalCore::Journal::Ptr j = CalendarSupport::journal( item ) ) { ++ date = j->dtStart().date().toString(); ++ } else { ++ kError() << "Item id is " << item.id() ++ << item.hasPayload() ++ << item.hasPayload() ++ << item.hasPayload() ++ << item.hasPayload(); ++ KCalCore::Incidence::Ptr p = CalendarSupport::incidence( item ); ++ if ( p ) { ++ kError() << "incidence uid is " << p->uid() ++ << " and type is " << p->typeStr(); ++ } ++ ++ Q_ASSERT( false ); ++ return; ++ } ++ ++ if ( !m_itemIdsForDate.contains( date, item.id() ) && !date.isEmpty() ) { ++ m_itemIdsForDate.insert( date, item.id() ); ++ m_itemDateForItemId.insert( item.id(), date ); ++ } ++ ++ m_itemMap.insert( id, item ); ++ ++ UnseenItem ui; ++ ui.collection = item.storageCollectionId(); ++ ui.uid = incidence->uid(); ++ ++ //REVIEW(AKONADI_PORT) ++ //UIDs might be duplicated and thus not unique, so for now we assume that the relatedTo ++ // UID refers to an item in the same collection. ++ //this might break with virtual collections, so we might fall back to a global UID ++ //to akonadi item mapping, and pick just any item (or the first found, or whatever ++ //strategy makes sense) from the ones with the same UID ++ const QString parentUID = incidence->relatedTo(); ++ const bool hasParent = !parentUID.isEmpty(); ++ UnseenItem parentItem; ++ QMap::const_iterator parentIt = m_uidToItemId.constEnd(); ++ bool knowParent = false; ++ bool parentNotChanged = false; ++ if ( hasParent ) { ++ parentItem.collection = item.storageCollectionId(); ++ parentItem.uid = parentUID; ++ parentIt = m_uidToItemId.constFind( parentItem ); ++ knowParent = parentIt != m_uidToItemId.constEnd(); ++ } ++ ++ if ( alreadyExisted ) { // We're updating an existing item ++ const bool existedInUidMap = m_uidToItemId.contains( ui ); ++ if ( m_uidToItemId.value( ui ) != item.id() ) { ++ kError()<< "Ignoring item. item.id() = " << item.id() << "; cached id = " << m_uidToItemId.value( ui ) ++ << "; item uid = " << ui.uid ++ << "; calendar = " << q->objectName() ++ << "; existed in cache = " << existedInUidMap ++ << "; storageCollection.id() = " << item.storageCollectionId() // the real collection ++ << "; parentCollection.id() = " << item.parentCollection().id() // can be a virtual collection ++ << "; hasParent = " << hasParent ++ << "; knowParent = " << knowParent; ++ if ( existedInUidMap ) { ++ Q_ASSERT_X( false, "updateItem", "uidToId map disagrees with item id" ); ++ } else { ++ kDebug() << "m_uidToItemId has size " << m_uidToItemId.count(); ++ QMapIterator i( m_uidToItemId ); ++ while ( i.hasNext() ) { ++ i.next(); ++ if ( i.key().uid == ui.uid || i.value() == item.id() ) { ++ kDebug() << " key " << i.key().uid << i.key().collection << " has value " << i.value(); ++ } ++ } ++ kError() << "Possible cause is that the resource isn't explicitly setting an uid ( and a random one is generated )"; ++ Q_ASSERT_X( false, "updateItem", "Item not found inside m_uidToItemId" ); ++ } ++ return; ++ } ++ ++ QHash::Iterator oldParentIt = m_childToParent.find( id ); ++ if ( oldParentIt != m_childToParent.end() ) { ++ const KCalCore::Incidence::Ptr parentInc = ++ CalendarSupport::incidence( m_itemMap.value( oldParentIt.value() ) ); ++ Q_ASSERT( parentInc ); ++ if ( parentInc->uid() != parentUID ) { ++ //parent changed, remove old entries ++ QList& l = m_parentToChildren[oldParentIt.value()]; ++ l.removeAll( id ); ++ m_childToParent.remove( id ); ++ } else { ++ parentNotChanged = true; ++ } ++ } else { //old parent not seen, maybe unseen? ++ QHash::Iterator oldUnseenParentIt = ++ m_childToUnseenParent.find( id ); ++ if ( oldUnseenParentIt != m_childToUnseenParent.end() ) { ++ if ( oldUnseenParentIt.value().uid != parentUID ) { ++ //parent changed, remove old entries ++ QList& l = m_unseenParentToChildren[oldUnseenParentIt.value()]; ++ l.removeAll( id ); ++ m_childToUnseenParent.remove( id ); ++ } else { ++ parentNotChanged = true; ++ } ++ } ++ } ++ } else { // We're inserting a new item ++ m_uidToItemId.insert( ui, item.id() ); ++ ++ //check for already known children: ++ const QList orphanedChildren = m_unseenParentToChildren.value( ui ); ++ if ( !orphanedChildren.isEmpty() ) { ++ m_parentToChildren.insert( id, orphanedChildren ); ++ } ++ ++ Q_FOREACH ( const Akonadi::Item::Id &cid, orphanedChildren ) { ++ m_childToParent.insert( cid, id ); ++ } ++ ++ m_unseenParentToChildren.remove( ui ); ++ m_childToUnseenParent.remove( id ); ++ } ++ ++ if ( hasParent && !parentNotChanged ) { ++ if ( knowParent ) { ++ Q_ASSERT( !m_parentToChildren.value( parentIt.value() ).contains( id ) ); ++ const KCalCore::Incidence::Ptr parentInc = ++ CalendarSupport::incidence( m_itemMap.value( parentIt.value() ) ); ++ Q_ASSERT( parentInc ); ++ m_parentToChildren[parentIt.value()].append( id ); ++ m_childToParent.insert( id, parentIt.value() ); ++ } else { ++ m_childToUnseenParent.insert( id, parentItem ); ++ m_unseenParentToChildren[parentItem].append( id ); ++ } ++ } ++ ++ if ( !alreadyExisted ) { ++ incidence->registerObserver( q ); ++ q->notifyIncidenceAdded( item ); ++ } else { ++ q->notifyIncidenceChanged( item ); ++ } ++ assertInvariants(); ++} ++ ++void Calendar::Private::itemChanged( const Akonadi::Item &item ) ++{ ++ assertInvariants(); ++ Q_ASSERT( item.isValid() ); ++ const KCalCore::Incidence::Ptr incidence = CalendarSupport::incidence( item ); ++ if ( !incidence ) { ++ kWarning() << "Really? No incidence for item.id() " << item.id(); ++ return; ++ } ++ updateItem( item, AssertExists ); ++ emit q->calendarChanged(); ++ assertInvariants(); ++} ++ ++void Calendar::Private::itemsAdded( const Akonadi::Item::List &items ) ++{ ++ assertInvariants(); ++ foreach ( const Akonadi::Item &item, items ) { ++ Q_ASSERT( item.isValid() ); ++ if ( !hasIncidence( item ) ) { ++ continue; ++ } ++ updateItem( item, AssertNew ); ++ const KCalCore::Incidence::Ptr incidence = item.payload(); ++ } ++ emit q->calendarChanged(); ++ assertInvariants(); ++} ++ ++void Calendar::Private::collectionsAdded( const Akonadi::Collection::List &collections ) ++{ ++ foreach ( const Akonadi::Collection &collection, collections ) { ++ m_collectionMap[collection.id()] = collection; ++ } ++} ++ ++void Calendar::Private::collectionsRemoved( const Akonadi::Collection::List &collections ) ++{ ++ // kDebug() << "removing collections: " << collections.count(); ++ foreach ( const Akonadi::Collection &collection, collections ) { ++ m_collectionMap.remove( collection.id() ); ++ } ++} ++ ++void Calendar::Private::removeItemFromMaps( const Akonadi::Item &item ) ++{ ++ UnseenItem unseen_item; ++ UnseenItem unseen_parent; ++ ++ unseen_item.collection = unseen_parent.collection = item.storageCollectionId(); ++ ++ KCalCore::Incidence::Ptr incidence = CalendarSupport::incidence( item ); ++ if ( incidence ) { ++ unseen_item.uid = incidence->uid(); ++ unseen_parent.uid = incidence->relatedTo(); ++ } ++ ++ if ( m_childToParent.contains( item.id() ) ) { ++ Akonadi::Item::Id parentId = m_childToParent.take( item.id() ); ++ m_parentToChildren[parentId].removeAll( item.id() ); ++ } ++ ++ foreach ( const Akonadi::Item::Id &id, m_parentToChildren[item.id()] ) { ++ m_childToUnseenParent[id] = unseen_item; ++ m_unseenParentToChildren[unseen_item].push_back( id ); ++ } ++ ++ m_parentToChildren.remove( item.id() ); ++ ++ m_childToUnseenParent.remove( item.id() ); ++ ++ m_unseenParentToChildren[unseen_parent].removeAll( item.id() ); ++ ++ m_uidToItemId.remove( unseen_item ); ++ m_itemDateForItemId.remove( item.id() ); ++ ++ const QList entriesToDelete = m_itemIdsForDate.keys( item.id() ); ++ foreach( const QString &entryToDelete, entriesToDelete ) { ++ m_itemIdsForDate.remove( entryToDelete ); ++ } ++} ++ ++void Calendar::Private::itemsRemoved( const Akonadi::Item::List &items ) ++{ ++ assertInvariants(); ++ foreach ( const Akonadi::Item &item, items ) { ++ Q_ASSERT( item.isValid() ); ++ ++ if ( !m_virtualItems.value( item.id() ).isEmpty() ) { ++ // We have more than one item with the same id, due to virtual folders, so we can't ++ // cleanup any hashes, to-do hierarchies, id to uid maps, etc, yet. We can only do that ++ // when the last item is removed. Just decrement and return. ++ m_virtualItems[item.id()].removeLast(); ++ q->notifyIncidenceDeleted( item ); ++ emit q->calendarChanged(); ++ return; ++ } ++ ++ if ( !m_itemMap.contains( item.id() ) ) { ++ /** There's an off by one error in the ETM which steveire doesn't have time right now to look at. ++ * When removing an item in korganizer, the plasma's calendar ETM emits rowsAboutToBeRemoved() ++ * for the wrong item, which makes plasma crash. ++ * ++ * I won't remove this hack in the future because this code is to be deleted and replaced by ++ * kdepimlibs/akonadi/calendar/etmcalendar.cpp ( branch calendaring ), which i'm currently writting ++ * unit tests. So until then no need to crash with an empty payload exception. ++ */ ++ return; ++ } ++ ++ Akonadi::Item oldItem( m_itemMap.take( item.id() ) ); ++ ++ removeItemFromMaps( oldItem ); ++ ++ Q_ASSERT( oldItem.hasPayload() ); ++ const KCalCore::Incidence::Ptr incidence = oldItem.payload(); ++ /* ++ kDebug() << "Remove uid=" << incidence->uid() ++ << "summary=" << incidence->summary() ++ << "type=" << int( incidence->type() ) ++ << "; id= " << item.id() << "; revision=" << item.revision() ++ << " calendar = " ++ << q; ++ */ ++ ++ if ( const KCalCore::Event::Ptr e = incidence.dynamicCast() ) { ++ if ( !e->recurs() ) { ++ m_itemIdsForDate.remove( e->dtStart().date().toString(), item.id() ); ++ } ++ } else if ( const KCalCore::Todo::Ptr t = incidence.dynamicCast( ) ) { ++ if ( t->hasDueDate() ) { ++ m_itemIdsForDate.remove( t->dtDue().date().toString(), item.id() ); ++ } ++ } else if ( const KCalCore::Journal::Ptr j = incidence.dynamicCast() ) { ++ m_itemIdsForDate.remove( j->dtStart().date().toString(), item.id() ); ++ } else { ++ kError() << "Unsupported incidence type: " << incidence; ++ Q_ASSERT( false ); ++ continue; ++ } ++ ++ // oldItem will almost always be the same as item, but, when you move an item from one collection ++ // and the destination collection isn't selected, itemsRemoved() is called, and they will differ ++ // on the parentCollection id. ++ q->notifyIncidenceDeleted( oldItem ); ++ incidence->unRegisterObserver( q ); ++ } ++ emit q->calendarChanged(); ++ assertInvariants(); ++} ++ ++Calendar::Calendar( QAbstractItemModel *treeModel, QAbstractItemModel *model, ++ const KDateTime::Spec &timeSpec, QObject *parent ) ++ : QObject( parent ), d( new Private( treeModel, model, this ) ) ++{ ++ d->mTimeSpec = timeSpec; ++ d->mViewTimeSpec = timeSpec; ++ d->readFromModel(); ++} ++ ++Calendar::~Calendar() ++{ ++ delete d; ++} ++ ++QAbstractItemModel *Calendar::treeModel() const ++{ ++ return d->m_treeModel; ++} ++ ++QAbstractItemModel *Calendar::model() const ++{ ++ return d->m_filterProxy; ++} ++ ++QAbstractItemModel *Calendar::unfilteredModel() const ++{ ++ return d->m_model; ++} ++ ++void Calendar::setUnfilteredModel( QAbstractItemModel *model ) ++{ ++ ++ if ( d->m_model == model ) { ++ return; ++ } ++ ++ if ( d->m_model ) { ++ disconnect( d->m_model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), ++ d, SLOT(dataChanged(QModelIndex,QModelIndex)) ); ++ ++ disconnect( d->m_model, SIGNAL(layoutChanged()), ++ d, SLOT(layoutChanged()) ); ++ ++ disconnect( d->m_model, SIGNAL(modelReset()), ++ d, SLOT(modelReset()) ); ++ ++ disconnect( d->m_model, SIGNAL(rowsInserted(QModelIndex,int,int)), ++ d, SLOT(rowsInserted(QModelIndex,int,int)) ); ++ ++ disconnect( d->m_model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), ++ d, SLOT(rowsAboutToBeRemoved(QModelIndex,int,int)) ); ++ } ++ d->m_model = model; ++ d->m_filterProxy->setSourceModel( model ); ++ if ( model ) { ++ connect( d->m_model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), ++ d, SLOT(dataChanged(QModelIndex,QModelIndex)) ); ++ ++ connect( d->m_model, SIGNAL(layoutChanged()), ++ d, SLOT(layoutChanged()) ); ++ ++ connect( d->m_model, SIGNAL(modelReset()), ++ d, SLOT(modelReset()) ); ++ ++ connect( d->m_model, SIGNAL(rowsInserted(QModelIndex,int,int)), ++ d, SLOT(rowsInserted(QModelIndex,int,int)) ); ++ ++ connect( d->m_model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), ++ d, SLOT(rowsAboutToBeRemoved(QModelIndex,int,int)) ); ++ d->modelReset(); ++ } ++} ++ ++// This method will be called probably multiple times if a series of changes where done. ++// One finished the endChange() method got called. ++ ++void Calendar::incidenceUpdate( const QString &uid, const KDateTime &recurrenceId ) ++{ ++ Q_UNUSED( uid ); ++ Q_UNUSED( recurrenceId ); ++} ++ ++void Calendar::incidenceUpdated( const QString &uid, const KDateTime &recurrenceId ) ++{ ++ Q_UNUSED( recurrenceId ); ++ KCalCore::Incidence::Ptr incidence = CalendarSupport::incidence( itemForIncidenceUid( uid ) ); ++ ++ if ( !incidence ) { ++ return; ++ } ++ ++ incidence->setLastModified( KDateTime::currentUtcDateTime() ); ++ // we should probably update the revision number here, ++ // or internally in the Event itself when certain things change. ++ // need to verify with ical documentation. ++ ++ // The static_cast is ok as the CalendarLocal only observes Incidence objects ++#ifdef AKONADI_PORT_DISABLED ++ notifyIncidenceChanged( static_cast( incidence ) ); ++#else ++ kDebug() << "AKONADI PORT: Disabled code in " << Q_FUNC_INFO; ++#endif ++} ++ ++Akonadi::Item Calendar::event( Akonadi::Item::Id id ) const ++{ ++ const Akonadi::Item item = d->m_itemMap.value( id ); ++ if ( CalendarSupport::event( item ) ) { ++ return item; ++ } else { ++ return Akonadi::Item(); ++ } ++} ++ ++Akonadi::Item Calendar::todo( Akonadi::Item::Id id ) const ++{ ++ const Akonadi::Item item = d->m_itemMap.value( id ); ++ if ( CalendarSupport::todo( item ) ) { ++ return item; ++ } else { ++ return Akonadi::Item(); ++ } ++} ++ ++Akonadi::Item::List Calendar::rawTodos( TodoSortField sortField, ++ SortDirection sortDirection ) ++{ ++ Akonadi::Item::List todoList; ++ QHashIterator i( d->m_itemMap ); ++ while ( i.hasNext() ) { ++ i.next(); ++ if ( CalendarSupport::todo( i.value() ) ) { ++ todoList.append( i.value() ); ++ } ++ } ++ d->appendVirtualItems( todoList ); ++ return sortTodos( todoList, sortField, sortDirection ); ++} ++ ++Akonadi::Item::List Calendar::rawTodosForDate( const QDate &date ) ++{ ++ Akonadi::Item::List todoList; ++ QString dateStr = date.toString(); ++ QMultiHash::const_iterator it = ++ d->m_itemIdsForDate.constFind( dateStr ); ++ while ( it != d->m_itemIdsForDate.constEnd() && it.key() == dateStr ) { ++ if ( CalendarSupport::todo( d->m_itemMap[it.value()] ) ) { ++ todoList.append( d->m_itemMap[it.value()] ); ++ } ++ ++it; ++ } ++ d->appendVirtualItems( todoList ); ++ return todoList; ++} ++ ++KCalCore::Alarm::List Calendar::alarmsTo( const KDateTime &to ) ++{ ++ return alarms( KDateTime( QDate( 1900, 1, 1 ) ), to ); ++} ++ ++KCalCore::Alarm::List Calendar::alarms( const KDateTime &from, const KDateTime &to, bool excludeBlockedAlarms ) ++{ ++ KCalCore::Alarm::List alarmList; ++ QHashIterator i( d->m_itemMap ); ++ while ( i.hasNext() ) { ++ const Akonadi::Item item = i.next().value(); ++ ++ if ( excludeBlockedAlarms ) { ++ // take the collection from m_collectionMap, because we need the up-to-date collection attributes ++ const Akonadi::Collection parentCollection = d->m_collectionMap.value( item.storageCollectionId() ); ++ if ( parentCollection.isValid() ) { ++ if ( parentCollection.hasAttribute() ) ++ continue; // do not include alarms from this collection ++ } ++ } ++ ++ KCalCore::Incidence::Ptr incidence = CalendarSupport::incidence( item ); ++ if ( !incidence ) { ++ continue; ++ } ++ ++ if ( incidence->recurs() ) { ++ appendRecurringAlarms( alarmList, item, from, to ); ++ } else { ++ appendAlarms( alarmList, item, from, to ); ++ } ++ } ++ return alarmList; ++} ++ ++Akonadi::Item::List Calendar::rawEventsForDate( const QDate &date, ++ const KDateTime::Spec ×pec, ++ EventSortField sortField, ++ SortDirection sortDirection ) ++{ ++ Akonadi::Item::List eventList; ++ // Find the hash for the specified date ++ const QString dateStr = date.toString(); ++ // Iterate over all non-recurring, single-day events that start on this date ++ QMultiHash::const_iterator it = ++ d->m_itemIdsForDate.constFind( dateStr ); ++ KDateTime::Spec ts = timespec.isValid() ? timespec : timeSpec(); ++ KDateTime kdt( date, ts ); ++ while ( it != d->m_itemIdsForDate.constEnd() && it.key() == dateStr ) { ++ if ( KCalCore::Event::Ptr ev = CalendarSupport::event( d->m_itemMap[it.value()] ) ) { ++ KDateTime end( ev->dtEnd().toTimeSpec( ev->dtStart() ) ); ++ if ( ev->allDay() ) { ++ end.setDateOnly( true ); ++ } else { ++ end = end.addSecs( -1 ); ++ } ++ if ( end >= kdt ) { ++ eventList.append( d->m_itemMap[it.value()] ); ++ } ++ } ++ ++it; ++ } ++ // Iterate over all events. Look for recurring events that occur on this date ++ QHashIterator i( d->m_itemMap ); ++ while ( i.hasNext() ) { ++ i.next(); ++ if ( KCalCore::Event::Ptr ev = CalendarSupport::event( i.value() ) ) { ++ if ( ev->recurs() ) { ++ if ( ev->isMultiDay() ) { ++ int extraDays = ev->dtStart().date().daysTo( ev->dtEnd().date() ); ++ for ( int j = 0; j <= extraDays; ++j ) { ++ if ( ev->recursOn( date.addDays( -j ), ts ) ) { ++ eventList.append( i.value() ); ++ break; ++ } ++ } ++ } else { ++ if ( ev->recursOn( date, ts ) ) { ++ eventList.append( i.value() ); ++ } ++ } ++ } else { ++ if ( ev->isMultiDay() ) { ++ if ( ev->dtStart().date() <= date && ev->dtEnd().date() >= date ) { ++ eventList.append( i.value() ); ++ } ++ } ++ } ++ } ++ } ++ ++ d->appendVirtualItems( eventList ); ++ ++ return sortEvents( eventList, sortField, sortDirection ); ++} ++ ++Akonadi::Item::List Calendar::rawEvents( const QDate &start, const QDate &end, ++ const KDateTime::Spec ×pec, bool inclusive ) ++{ ++ Akonadi::Item::List eventList; ++ KDateTime::Spec ts = timespec.isValid() ? timespec : timeSpec(); ++ KDateTime st( start, ts ); ++ KDateTime nd( end, ts ); ++ KDateTime yesterStart = st.addDays( -1 ); ++ // Get non-recurring events ++ QHashIterator i( d->m_itemMap ); ++ while ( i.hasNext() ) { ++ i.next(); ++ if ( KCalCore::Event::Ptr event = CalendarSupport::event( i.value() ) ) { ++ KDateTime rStart = event->dtStart(); ++ if ( nd < rStart ) continue; ++ if ( inclusive && rStart < st ) { ++ continue; ++ } ++ if ( !event->recurs() ) { // non-recurring events ++ KDateTime rEnd = event->dtEnd(); ++ if ( rEnd < st ) { ++ continue; ++ } ++ if ( inclusive && nd < rEnd ) { ++ continue; ++ } ++ } else { // recurring events ++ switch( event->recurrence()->duration() ) { ++ case -1: // infinite ++ if ( inclusive ) { ++ continue; ++ } ++ break; ++ case 0: // end date given ++ default: // count given ++ KDateTime rEnd( event->recurrence()->endDate(), ts ); ++ if ( !rEnd.isValid() ) { ++ continue; ++ } ++ if ( rEnd < st ) { ++ continue; ++ } ++ if ( inclusive && nd < rEnd ) { ++ continue; ++ } ++ break; ++ } // switch(duration) ++ } //if (recurs) ++ eventList.append( i.value() ); ++ } ++ } ++ ++ d->appendVirtualItems( eventList ); ++ ++ return eventList; ++} ++ ++Akonadi::Item::List Calendar::rawEventsForDate( const KDateTime &kdt ) ++{ ++ return rawEventsForDate( kdt.date(), kdt.timeSpec() ); ++} ++ ++Akonadi::Item::List Calendar::rawEvents( EventSortField sortField, ++ SortDirection sortDirection ) ++{ ++ Akonadi::Item::List eventList; ++ QHashIterator i( d->m_itemMap ); ++ while ( i.hasNext() ) { ++ i.next(); ++ if ( CalendarSupport::event( i.value() ) ) { ++ eventList.append( i.value() ); ++ } ++ } ++ d->appendVirtualItems( eventList ); ++ return sortEvents( eventList, sortField, sortDirection ); ++} ++ ++Akonadi::Item Calendar::journal( Akonadi::Item::Id id ) const ++{ ++ const Akonadi::Item item = d->m_itemMap.value( id ); ++ if ( CalendarSupport::journal( item ) ) { ++ return item; ++ } else { ++ return Akonadi::Item(); ++ } ++} ++ ++Akonadi::Item::List Calendar::rawJournals( JournalSortField sortField, ++ SortDirection sortDirection ) ++{ ++ Akonadi::Item::List journalList; ++ QHashIterator i( d->m_itemMap ); ++ while ( i.hasNext() ) { ++ i.next(); ++ if ( CalendarSupport::journal( i.value() ) ) { ++ journalList.append( i.value() ); ++ } ++ } ++ d->appendVirtualItems( journalList ); ++ return sortJournals( journalList, sortField, sortDirection ); ++} ++ ++Akonadi::Item::List Calendar::rawJournalsForDate( const QDate &date ) ++{ ++ Akonadi::Item::List journalList; ++ QString dateStr = date.toString(); ++ QMultiHash::const_iterator it = ++ d->m_itemIdsForDate.constFind( dateStr ); ++ while ( it != d->m_itemIdsForDate.constEnd() && it.key() == dateStr ) { ++ if ( CalendarSupport::journal( d->m_itemMap[it.value()] ) ) { ++ journalList.append( d->m_itemMap[it.value()] ); ++ } ++ ++it; ++ } ++ d->appendVirtualItems( journalList ); ++ return journalList; ++} ++ ++Akonadi::Item Calendar::findParent( const Akonadi::Item &child ) const ++{ ++ return d->m_itemMap.value( d->m_childToParent.value( child.id() ) ); ++} ++ ++Akonadi::Item::List Calendar::findChildren( const KCalCore::Incidence::Ptr &incidence ) const ++{ ++ Akonadi::Item item = itemForIncidenceUid( incidence->uid() ); ++ ++ return findChildren( item ); ++} ++ ++Akonadi::Item::List Calendar::findChildren( const Akonadi::Item &parent ) const ++{ ++ Akonadi::Item::List l; ++ Q_FOREACH( const Akonadi::Item::Id &id, d->m_parentToChildren.value( parent.id() ) ) { ++ l.push_back( d->m_itemMap.value( id ) ); ++ } ++ return l; ++} ++ ++bool Calendar::isChild( const Akonadi::Item &parent, const Akonadi::Item &child ) const ++{ ++ return d->m_childToParent.value( child.id() ) == parent.id(); ++} ++ ++Akonadi::Item::Id Calendar::itemIdForIncidenceUid( const QString &uid ) const ++{ ++ QHashIterator i( d->m_itemMap ); ++ while ( i.hasNext() ) { ++ i.next(); ++ const Akonadi::Item item = i.value(); ++ Q_ASSERT( item.isValid() ); ++ Q_ASSERT( item.hasPayload() ); ++ KCalCore::Incidence::Ptr inc = item.payload(); ++ if ( inc->uid() == uid ) { ++ return item.id(); ++ } ++ } ++ kWarning() << "Failed to find Akonadi::Item for KCal uid " << uid; ++ return -1; ++} ++ ++Akonadi::Item Calendar::itemForIncidenceUid( const QString &uid ) const ++{ ++ return incidence( itemIdForIncidenceUid( uid ) ); ++} ++ ++// calendarbase.cpp ++ ++KCalCore::Person Calendar::owner() const ++{ ++ return d->mOwner; ++} ++ ++void Calendar::setOwner( const KCalCore::Person &owner ) ++{ ++ d->mOwner = owner; ++} ++ ++void Calendar::setTimeSpec( const KDateTime::Spec &timeSpec ) ++{ ++ d->mTimeSpec = timeSpec; ++ d->mBuiltInTimeZone = KCalCore::ICalTimeZone(); ++ setViewTimeSpec( timeSpec ); ++ ++ doSetTimeSpec( d->mTimeSpec ); ++} ++ ++KDateTime::Spec Calendar::timeSpec() const ++{ ++ return d->mTimeSpec; ++} ++ ++void Calendar::setTimeZoneId( const QString &timeZoneId ) ++{ ++ d->mTimeSpec = d->timeZoneIdSpec( timeZoneId, false ); ++ d->mViewTimeSpec = d->mTimeSpec; ++ d->mBuiltInViewTimeZone = d->mBuiltInTimeZone; ++ ++ doSetTimeSpec( d->mTimeSpec ); ++} ++ ++//@cond PRIVATE ++KDateTime::Spec Calendar::Private::timeZoneIdSpec( const QString &timeZoneId, ++ bool view ) ++{ ++ if ( view ) { ++ mBuiltInViewTimeZone = KCalCore::ICalTimeZone(); ++ } else { ++ mBuiltInTimeZone = KCalCore::ICalTimeZone(); ++ } ++ if ( timeZoneId == QLatin1String( "UTC" ) ) { ++ return KDateTime::UTC; ++ } ++ KCalCore::ICalTimeZone tz = mTimeZones->zone( timeZoneId ); ++ if ( !tz.isValid() ) { ++ KCalCore::ICalTimeZoneSource tzsrc; ++#ifdef AKONADI_PORT_DISABLED ++ tz = tzsrc.parse( icaltimezone_get_builtin_timezone( timeZoneId.toLatin1() ) ); ++#else ++ kDebug() << "AKONADI PORT: Disabled code in " << Q_FUNC_INFO; ++#endif ++ if ( view ) { ++ mBuiltInViewTimeZone = tz; ++ } else { ++ mBuiltInTimeZone = tz; ++ } ++ } ++ if ( tz.isValid() ) { ++ return tz; ++ } else { ++ return KDateTime::ClockTime; ++ } ++} ++//@endcond ++ ++QString Calendar::timeZoneId() const ++{ ++ KTimeZone tz = d->mTimeSpec.timeZone(); ++ return tz.isValid() ? tz.name() : QString(); ++} ++ ++void Calendar::setViewTimeSpec( const KDateTime::Spec &timeSpec ) const ++{ ++ d->mViewTimeSpec = timeSpec; ++ d->mBuiltInViewTimeZone = KCalCore::ICalTimeZone(); ++} ++ ++void Calendar::setViewTimeZoneId( const QString &timeZoneId ) const ++{ ++ d->mViewTimeSpec = d->timeZoneIdSpec( timeZoneId, true ); ++} ++ ++KDateTime::Spec Calendar::viewTimeSpec() const ++{ ++ return d->mViewTimeSpec; ++} ++ ++QString Calendar::viewTimeZoneId() const ++{ ++ KTimeZone tz = d->mViewTimeSpec.timeZone(); ++ return tz.isValid() ? tz.name() : QString(); ++} ++ ++void Calendar::shiftTimes( const KDateTime::Spec &oldSpec, ++ const KDateTime::Spec &newSpec ) ++{ ++ setTimeSpec( newSpec ); ++ int i, end; ++ Akonadi::Item::List ev = events(); ++ for ( i = 0, end = ev.count(); i < end; ++i ) { ++ CalendarSupport::event( ev[i] )->shiftTimes( oldSpec, newSpec ); ++ } ++ ++ Akonadi::Item::List to = todos(); ++ for ( i = 0, end = to.count(); i < end; ++i ) { ++ CalendarSupport::todo( to[i] )->shiftTimes( oldSpec, newSpec ); ++ } ++ ++ Akonadi::Item::List jo = journals(); ++ for ( i = 0, end = jo.count(); i < end; ++i ) { ++ CalendarSupport::journal( jo[i] )->shiftTimes( oldSpec, newSpec ); ++ } ++} ++ ++void Calendar::setFilter( KCalCore::CalFilter *filter ) ++{ ++ d->m_filterProxy->setFilter( filter ? filter : d->mDefaultFilter ); ++} ++ ++KCalCore::CalFilter *Calendar::filter() ++{ ++ return d->m_filterProxy->filter(); ++} ++ ++QStringList Calendar::categories( Calendar *cal ) ++{ ++ Akonadi::Item::List rawInc( cal->rawIncidences() ); ++ QStringList cats, thisCats; ++ // @TODO: For now just iterate over all incidences. In the future, ++ // the list of categories should be built when reading the file. ++ Q_FOREACH( const Akonadi::Item &i, rawInc ) { ++ thisCats = CalendarSupport::incidence( i )->categories(); ++ for ( QStringList::ConstIterator si = thisCats.constBegin(); ++ si != thisCats.constEnd(); ++si ) { ++ if ( !cats.contains( *si ) ) { ++ cats.append( *si ); ++ } ++ } ++ } ++ return cats; ++} ++ ++Akonadi::Item::List Calendar::incidences( const QDate &date ) ++{ ++ return mergeIncidenceList( events( date ), todos( date ), journals( date ) ); ++} ++ ++Akonadi::Item::List Calendar::incidences() ++{ ++ if ( d->m_filterProxy->filter() == 0 || !d->m_filterProxy->filter()->isEnabled() ) { ++ // Lets skip the filterProxy and return m_itemMap, which is cheaper. ++ return rawIncidences(); ++ } else { ++ return itemsFromModel( d->m_filterProxy ); ++ } ++} ++ ++Akonadi::Item::List Calendar::rawIncidences() ++{ ++ // The following code is 100x faster than: return itemsFromModel( d->m_model ) ++ QHashIterator i( d->m_itemMap ); ++ Akonadi::Item::List list; ++ while ( i.hasNext() ) { ++ i.next(); ++ list.append( i.value() ); ++ } ++ ++ return list; ++} ++ ++Akonadi::Item::List Calendar::sortEvents( const Akonadi::Item::List &eventList_, ++ EventSortField sortField, ++ SortDirection sortDirection ) ++{ ++ Akonadi::Item::List eventList = eventList_; ++ Akonadi::Item::List eventListSorted; ++ Akonadi::Item::List tempList, t; ++ Akonadi::Item::List alphaList; ++ Akonadi::Item::List::Iterator sortIt; ++ Akonadi::Item::List::Iterator eit; ++ ++ // Notice we alphabetically presort Summaries first. ++ // We do this so comparison "ties" stay in a nice order. ++ ++ switch( sortField ) { ++ case EventSortUnsorted: ++ eventListSorted = eventList; ++ break; ++ ++ case EventSortStartDate: ++ alphaList = sortEvents( eventList, EventSortSummary, sortDirection ); ++ for ( eit = alphaList.begin(); eit != alphaList.end(); ++eit ) { ++ KCalCore::Event::Ptr e = CalendarSupport::event( *eit ); ++ Q_ASSERT( e ); ++ if ( e->dtStart().isDateOnly() ) { ++ tempList.append( *eit ); ++ continue; ++ } ++ sortIt = eventListSorted.begin(); ++ if ( sortDirection == SortDirectionAscending ) { ++ while ( sortIt != eventListSorted.end() && ++ e->dtStart() >= CalendarSupport::event(*sortIt)->dtStart() ) { ++ ++sortIt; ++ } ++ } else { ++ while ( sortIt != eventListSorted.end() && ++ e->dtStart() < CalendarSupport::event(*sortIt)->dtStart() ) { ++ ++sortIt; ++ } ++ } ++ eventListSorted.insert( sortIt, *eit ); ++ } ++ if ( sortDirection == SortDirectionAscending ) { ++ // Prepend the list of Events without End DateTimes ++ tempList += eventListSorted; ++ eventListSorted = tempList; ++ } else { ++ // Append the list of Events without End DateTimes ++ eventListSorted += tempList; ++ } ++ break; ++ ++ case EventSortEndDate: ++ alphaList = sortEvents( eventList, EventSortSummary, sortDirection ); ++ for ( eit = alphaList.begin(); eit != alphaList.end(); ++eit ) { ++ KCalCore::Event::Ptr e = CalendarSupport::event( *eit ); ++ Q_ASSERT( e ); ++ if ( e->hasEndDate() ) { ++ sortIt = eventListSorted.begin(); ++ if ( sortDirection == SortDirectionAscending ) { ++ while ( sortIt != eventListSorted.end() && ++ e->dtEnd() >= CalendarSupport::event(*sortIt)->dtEnd() ) { ++ ++sortIt; ++ } ++ } else { ++ while ( sortIt != eventListSorted.end() && ++ e->dtEnd() < CalendarSupport::event(*sortIt)->dtEnd() ) { ++ ++sortIt; ++ } ++ } ++ } else { ++ // Keep a list of the Events without End DateTimes ++ tempList.append( *eit ); ++ } ++ eventListSorted.insert( sortIt, *eit ); ++ } ++ if ( sortDirection == SortDirectionAscending ) { ++ // Append the list of Events without End DateTimes ++ eventListSorted += tempList; ++ } else { ++ // Prepend the list of Events without End DateTimes ++ tempList += eventListSorted; ++ eventListSorted = tempList; ++ } ++ break; ++ ++ case EventSortSummary: ++ for ( eit = eventList.begin(); eit != eventList.end(); ++eit ) { ++ KCalCore::Event::Ptr e = CalendarSupport::event( *eit ); ++ Q_ASSERT( e ); ++ sortIt = eventListSorted.begin(); ++ if ( sortDirection == SortDirectionAscending ) { ++ while ( sortIt != eventListSorted.end() && ++ e->summary() >= CalendarSupport::event(*sortIt)->summary() ) { ++ ++sortIt; ++ } ++ } else { ++ while ( sortIt != eventListSorted.end() && ++ e->summary() < CalendarSupport::event(*sortIt)->summary() ) { ++ ++sortIt; ++ } ++ } ++ eventListSorted.insert( sortIt, *eit ); ++ } ++ break; ++ } ++ ++ return eventListSorted; ++} ++ ++Akonadi::Item::List Calendar::events( const QDate &date, ++ const KDateTime::Spec &timeSpec, ++ EventSortField sortField, ++ SortDirection sortDirection ) ++{ ++ const Akonadi::Item::List el = rawEventsForDate( date, timeSpec, sortField, sortDirection ); ++ return applyCalFilter( el, filter() ); ++} ++ ++Akonadi::Item::List Calendar::events( const KDateTime &dt ) ++{ ++ const Akonadi::Item::List el = rawEventsForDate( dt ); ++ return applyCalFilter( el, filter() ); ++} ++ ++Akonadi::Item::List Calendar::events( const QDate &start, const QDate &end, ++ const KDateTime::Spec &timeSpec, ++ bool inclusive ) ++{ ++ const Akonadi::Item::List el = rawEvents( start, end, timeSpec, inclusive ); ++ return applyCalFilter( el, filter() ); ++} ++ ++Akonadi::Item::List Calendar::events( EventSortField sortField, ++ SortDirection sortDirection ) ++{ ++ const Akonadi::Item::List el = rawEvents( sortField, sortDirection ); ++ return applyCalFilter( el, filter() ); ++} ++ ++KCalCore::Incidence::Ptr Calendar::dissociateOccurrence( const Akonadi::Item &item, ++ const QDate &date, ++ const KDateTime::Spec &spec, ++ bool single ) ++{ ++ if ( !item.isValid() ) { ++ return KCalCore::Incidence::Ptr(); ++ } ++ ++ const KCalCore::Incidence::Ptr incidence = CalendarSupport::incidence( item ); ++ if ( !incidence || !incidence->recurs() ) { ++ return KCalCore::Incidence::Ptr(); ++ } ++ ++ KCalCore::Incidence::Ptr newInc = KCalCore::Incidence::Ptr( incidence->clone() ); ++ newInc->recreate(); ++ // Do not call setRelatedTo() when dissociating recurring to-dos, otherwise the new to-do ++ // will appear as a child. Originally, we planned to set a relation with reltype SIBLING ++ // when dissociating to-dos, but currently kcal only supports reltype PARENT. ++ // We can uncomment the following line when we support the PARENT reltype. ++ //newInc->setRelatedTo( incidence ); ++ KCalCore::Recurrence *recur = newInc->recurrence(); ++ if ( single ) { ++ recur->clear(); ++ } else { ++ // Adjust the recurrence for the future incidences. In particular adjust ++ // the "end after n occurrences" rules! "No end date" and "end by ..." ++ // don't need to be modified. ++ int duration = recur->duration(); ++ if ( duration > 0 ) { ++ int doneduration = recur->durationTo( date.addDays( -1 ) ); ++ if ( doneduration >= duration ) { ++ kDebug() << "The dissociated event already occurred more often" ++ << "than it was supposed to ever occur. ERROR!"; ++ recur->clear(); ++ } else { ++ recur->setDuration( duration - doneduration ); ++ } ++ } ++ } ++ // Adjust the date of the incidence ++ if ( incidence->type() == KCalCore::IncidenceBase::TypeEvent ) { ++ KCalCore::Event::Ptr ev = newInc.staticCast(); ++ KDateTime start( ev->dtStart() ); ++ int daysTo = start.toTimeSpec( spec ).date().daysTo( date ); ++ ev->setDtStart( start.addDays( daysTo ) ); ++ ev->setDtEnd( ev->dtEnd().addDays( daysTo ) ); ++ } else if ( incidence->type() == KCalCore::IncidenceBase::TypeTodo ) { ++ KCalCore::Todo::Ptr td = newInc.staticCast(); ++ bool haveOffset = false; ++ int daysTo = 0; ++ if ( td->hasDueDate() ) { ++ KDateTime due( td->dtDue() ); ++ daysTo = due.toTimeSpec( spec ).date().daysTo( date ); ++ td->setDtDue( due.addDays( daysTo ), true ); ++ haveOffset = true; ++ } ++ if ( td->hasStartDate() ) { ++ KDateTime start( td->dtStart() ); ++ if ( !haveOffset ) { ++ daysTo = start.toTimeSpec( spec ).date().daysTo( date ); ++ } ++ td->setDtStart( start.addDays( daysTo ) ); ++ haveOffset = true; ++ } ++ } ++ recur = incidence->recurrence(); ++ if ( recur ) { ++ if ( single ) { ++ recur->addExDate( date ); ++ } else { ++ // Make sure the recurrence of the past events ends ++ // at the corresponding day ++ recur->setEndDate( date.addDays(-1) ); ++ } ++ } ++ return KCalCore::Incidence::Ptr( newInc ); ++} ++ ++Akonadi::Item Calendar::incidence( Akonadi::Item::Id uid ) const ++{ ++ Akonadi::Item i = event( uid ); ++ if ( i.isValid() ) { ++ return i; ++ } ++ ++ i = todo( uid ); ++ if ( i.isValid() ) { ++ return i; ++ } ++ ++ i = journal( uid ); ++ return i; ++} ++ ++Akonadi::Item::List Calendar::incidencesFromSchedulingID( const QString &sid ) ++{ ++ Akonadi::Item::List result; ++ const Akonadi::Item::List incidences = rawIncidences(); ++ Akonadi::Item::List::const_iterator it = incidences.begin(); ++ for ( ; it != incidences.end(); ++it ) { ++ if ( CalendarSupport::incidence(*it)->schedulingID() == sid ) { ++ result.append( *it ); ++ } ++ } ++ return result; ++} ++ ++Akonadi::Item Calendar::incidenceFromSchedulingID( const QString &UID ) ++{ ++ const Akonadi::Item::List incidences = rawIncidences(); ++ Akonadi::Item::List::const_iterator it = incidences.begin(); ++ for ( ; it != incidences.end(); ++it ) { ++ if ( CalendarSupport::incidence(*it)->schedulingID() == UID ) { ++ // Touchdown, and the crowd goes wild ++ return *it; ++ } ++ } ++ // Not found ++ return Akonadi::Item(); ++} ++ ++Akonadi::Item::List Calendar::sortTodos( const Akonadi::Item::List &todoList_, ++ TodoSortField sortField, ++ SortDirection sortDirection ) ++{ ++ Akonadi::Item::List todoList( todoList_ ); ++ Akonadi::Item::List todoListSorted; ++ Akonadi::Item::List tempList, t; ++ Akonadi::Item::List alphaList; ++ Akonadi::Item::List::Iterator sortIt; ++ Akonadi::Item::List::ConstIterator eit; ++ ++ // Notice we alphabetically presort Summaries first. ++ // We do this so comparison "ties" stay in a nice order. ++ ++ // Note that To-dos may not have Start DateTimes nor due DateTimes. ++ ++ switch( sortField ) { ++ case TodoSortUnsorted: ++ todoListSorted = todoList; ++ break; ++ ++ case TodoSortStartDate: ++ alphaList = sortTodos( todoList, TodoSortSummary, sortDirection ); ++ for ( eit = alphaList.constBegin(); eit != alphaList.constEnd(); ++eit ) { ++ const KCalCore::Todo::Ptr e = CalendarSupport::todo( *eit ); ++ if ( e->hasStartDate() ) { ++ sortIt = todoListSorted.begin(); ++ if ( sortDirection == SortDirectionAscending ) { ++ while ( sortIt != todoListSorted.end() && ++ e->dtStart() >= CalendarSupport::todo(*sortIt)->dtStart() ) { ++ ++sortIt; ++ } ++ } else { ++ while ( sortIt != todoListSorted.end() && ++ e->dtStart() < CalendarSupport::todo(*sortIt)->dtStart() ) { ++ ++sortIt; ++ } ++ } ++ todoListSorted.insert( sortIt, *eit ); ++ } else { ++ // Keep a list of the To-dos without Start DateTimes ++ tempList.append( *eit ); ++ } ++ } ++ if ( sortDirection == SortDirectionAscending ) { ++ // Append the list of To-dos without Start DateTimes ++ todoListSorted += tempList; ++ } else { ++ // Prepend the list of To-dos without Start DateTimes ++ tempList += todoListSorted; ++ todoListSorted = tempList; ++ } ++ break; ++ ++ case TodoSortDueDate: ++ alphaList = sortTodos( todoList, TodoSortSummary, sortDirection ); ++ for ( eit = alphaList.constBegin(); eit != alphaList.constEnd(); ++eit ) { ++ const KCalCore::Todo::Ptr e = CalendarSupport::todo( *eit ); ++ if ( e->hasDueDate() ) { ++ sortIt = todoListSorted.begin(); ++ if ( sortDirection == SortDirectionAscending ) { ++ while ( sortIt != todoListSorted.end() && ++ e->dtDue() >= CalendarSupport::todo( *sortIt )->dtDue() ) { ++ ++sortIt; ++ } ++ } else { ++ while ( sortIt != todoListSorted.end() && ++ e->dtDue() < CalendarSupport::todo( *sortIt )->dtDue() ) { ++ ++sortIt; ++ } ++ } ++ todoListSorted.insert( sortIt, *eit ); ++ } else { ++ // Keep a list of the To-dos without Due DateTimes ++ tempList.append( *eit ); ++ } ++ } ++ if ( sortDirection == SortDirectionAscending ) { ++ // Append the list of To-dos without Due DateTimes ++ todoListSorted += tempList; ++ } else { ++ // Prepend the list of To-dos without Due DateTimes ++ tempList += todoListSorted; ++ todoListSorted = tempList; ++ } ++ break; ++ ++ case TodoSortPriority: ++ alphaList = sortTodos( todoList, TodoSortSummary, sortDirection ); ++ for ( eit = alphaList.constBegin(); eit != alphaList.constEnd(); ++eit ) { ++ const KCalCore::Todo::Ptr e = CalendarSupport::todo( *eit ); ++ sortIt = todoListSorted.begin(); ++ if ( sortDirection == SortDirectionAscending ) { ++ while ( sortIt != todoListSorted.end() && ++ e->priority() >= CalendarSupport::todo(*sortIt)->priority() ) { ++ ++sortIt; ++ } ++ } else { ++ while ( sortIt != todoListSorted.end() && ++ e->priority() < CalendarSupport::todo(*sortIt)->priority() ) { ++ ++sortIt; ++ } ++ } ++ todoListSorted.insert( sortIt, *eit ); ++ } ++ break; ++ ++ case TodoSortPercentComplete: ++ alphaList = sortTodos( todoList, TodoSortSummary, sortDirection ); ++ for ( eit = alphaList.constBegin(); eit != alphaList.constEnd(); ++eit ) { ++ const KCalCore::Todo::Ptr e = CalendarSupport::todo( *eit ); ++ sortIt = todoListSorted.begin(); ++ if ( sortDirection == SortDirectionAscending ) { ++ while ( sortIt != todoListSorted.end() && ++ e->percentComplete() >= CalendarSupport::todo(*sortIt)->percentComplete() ) { ++ ++sortIt; ++ } ++ } else { ++ while ( sortIt != todoListSorted.end() && ++ e->percentComplete() < CalendarSupport::todo(*sortIt)->percentComplete() ) { ++ ++sortIt; ++ } ++ } ++ todoListSorted.insert( sortIt, *eit ); ++ } ++ break; ++ ++ case TodoSortSummary: ++ for ( eit = todoList.constBegin(); eit != todoList.constEnd(); ++eit ) { ++ const KCalCore::Todo::Ptr e = CalendarSupport::todo( *eit ); ++ sortIt = todoListSorted.begin(); ++ if ( sortDirection == SortDirectionAscending ) { ++ while ( sortIt != todoListSorted.end() && ++ e->summary() >= CalendarSupport::todo(*sortIt)->summary() ) { ++ ++sortIt; ++ } ++ } else { ++ while ( sortIt != todoListSorted.end() && ++ e->summary() < CalendarSupport::todo(*sortIt)->summary() ) { ++ ++sortIt; ++ } ++ } ++ todoListSorted.insert( sortIt, *eit ); ++ } ++ break; ++ } ++ ++ return todoListSorted; ++} ++ ++Akonadi::Item::List Calendar::todos( TodoSortField sortField, ++ SortDirection sortDirection ) ++{ ++ const Akonadi::Item::List tl = rawTodos( sortField, sortDirection ); ++ return CalendarSupport::applyCalFilter( tl, filter() ); ++} ++ ++Akonadi::Item::List Calendar::todos( const QDate &date ) ++{ ++ Akonadi::Item::List el = rawTodosForDate( date ); ++ return applyCalFilter( el, filter() ); ++} ++ ++Akonadi::Item::List Calendar::sortJournals( const Akonadi::Item::List &journalList_, ++ JournalSortField sortField, ++ SortDirection sortDirection ) ++{ ++ Akonadi::Item::List journalList( journalList_ ); ++ Akonadi::Item::List journalListSorted; ++ Akonadi::Item::List::Iterator sortIt; ++ Akonadi::Item::List::ConstIterator eit; ++ ++ switch( sortField ) { ++ case JournalSortUnsorted: ++ journalListSorted = journalList; ++ break; ++ ++ case JournalSortDate: ++ for ( eit = journalList.constBegin(); eit != journalList.constEnd(); ++eit ) { ++ const KCalCore::Journal::Ptr e = CalendarSupport::journal( *eit ); ++ sortIt = journalListSorted.begin(); ++ if ( sortDirection == SortDirectionAscending ) { ++ while ( sortIt != journalListSorted.end() && ++ e->dtStart() >= CalendarSupport::journal(*sortIt)->dtStart() ) { ++ ++sortIt; ++ } ++ } else { ++ while ( sortIt != journalListSorted.end() && ++ e->dtStart() < CalendarSupport::journal(*sortIt)->dtStart() ) { ++ ++sortIt; ++ } ++ } ++ journalListSorted.insert( sortIt, *eit ); ++ } ++ break; ++ ++ case JournalSortSummary: ++ for ( eit = journalList.constBegin(); eit != journalList.constEnd(); ++eit ) { ++ const KCalCore::Journal::Ptr e = CalendarSupport::journal( *eit ); ++ sortIt = journalListSorted.begin(); ++ if ( sortDirection == SortDirectionAscending ) { ++ while ( sortIt != journalListSorted.end() && ++ e->summary() >= CalendarSupport::journal(*sortIt)->summary() ) { ++ ++sortIt; ++ } ++ } else { ++ while ( sortIt != journalListSorted.end() && ++ e->summary() < CalendarSupport::journal(*sortIt)->summary() ) { ++ ++sortIt; ++ } ++ } ++ journalListSorted.insert( sortIt, *eit ); ++ } ++ break; ++ } ++ ++ return journalListSorted; ++} ++ ++Akonadi::Item::List Calendar::journals( JournalSortField sortField, ++ SortDirection sortDirection ) ++{ ++ const Akonadi::Item::List jl = rawJournals( sortField, sortDirection ); ++ return CalendarSupport::applyCalFilter( jl, filter() ); ++} ++ ++Akonadi::Item::List Calendar::journals( const QDate &date ) ++{ ++ Akonadi::Item::List el = rawJournalsForDate( date ); ++ return CalendarSupport::applyCalFilter( el, filter() ); ++} ++ ++void Calendar::beginBatchAdding() ++{ ++ emit batchAddingBegins(); ++} ++ ++void Calendar::endBatchAdding() ++{ ++ emit batchAddingEnds(); ++} ++ ++#ifdef AKONADI_PORT_DISABLED ++ ++void Calendar::setupRelations( const Akonadi::Item &forincidence ) ++{ ++ if ( !forincidence ) { ++ return; ++ } ++ ++ QString uid = forincidence->uid(); ++ ++ // First, go over the list of orphans and see if this is their parent ++ QList l = d->mOrphans.values( uid ); ++ d->mOrphans.remove( uid ); ++ for ( int i = 0, end = l.count(); i < end; ++i ) { ++ forincidence->addRelation( l[i] ); ++ d->mOrphanUids.remove( l[i]->uid() ); ++ } ++ ++ // Now see about this incidences parent ++ if ( !forincidence->relatedTo() && !forincidence->relatedToUid().isEmpty() ) { ++ // Incidence has a uid it is related to but is not registered to it yet. ++ // Try to find it ++ KCalCore::Incidence::Ptr parent = incidence( forincidence->relatedToUid() ); ++ if ( !parent ) { ++ // Not found, put this in the mOrphans list ++ // Note that the mOrphans dict might contain multiple entries with the ++ // same key! which are multiple children that wait for the parent ++ // incidence to be inserted. ++ d->mOrphans.insert( forincidence->relatedToUid(), forincidence ); ++ d->mOrphanUids.insert( forincidence->uid(), forincidence ); ++ } ++ } ++ } ++#endif // AKONADI_PORT_DISABLED ++ ++#ifdef AKONADI_PORT_DISABLED ++// If a to-do with sub-to-dos is deleted, move it's sub-to-dos to the orphan list ++void Calendar::removeRelations( const Akonadi::Item &incidence ) ++{ ++ if ( !incidence ) { ++ kDebug() << "Warning: incidence is 0"; ++ return; ++ } ++ ++ QString uid = incidence->uid(); ++ foreach ( KCalCore::Incidence::Ptr i, incidence->relations() ) { ++ if ( !d->mOrphanUids.contains( i->uid() ) ) { ++ d->mOrphans.insert( uid, i ); ++ d->mOrphanUids.insert( i->uid(), i ); ++ i->setRelatedTo( uid ); ++ } ++ } ++ ++ // If this incidence is related to something else, tell that about it ++ if ( incidence->relatedTo() ) { ++ incidence->relatedTo()->removeRelation( incidence ); ++ } ++ ++ // Remove this one from the orphans list ++ if ( d->mOrphanUids.remove( uid ) ) { ++ // This incidence is located in the orphans list - it should be removed ++ // Since the mOrphans dict might contain the same key (with different ++ // child incidence pointers!) multiple times, take care that we remove ++ // the correct one. So we need to remove all items with the given ++ // parent UID, and readd those that are not for this item. Also, there ++ // might be other entries with differnet UID that point to this ++ // incidence (this might happen when the relatedTo of the item is ++ // changed before its parent is inserted. This might happen with ++ // groupware servers....). Remove them, too ++ QStringList relatedToUids; ++ ++ // First, create a list of all keys in the mOrphans list which point ++ // to the removed item ++ relatedToUids << incidence->relatedToUid(); ++ for ( QMultiHash::Iterator it = d->mOrphans.begin(); ++ it != d->mOrphans.end(); ++it ) { ++ if ( it.value()->uid() == uid ) { ++ relatedToUids << it.key(); ++ } ++ } ++ ++ // now go through all uids that have one entry that point to the incidence ++ for ( QStringList::const_iterator uidit = relatedToUids.constBegin(); ++ uidit != relatedToUids.constEnd(); ++uidit ) { ++ Incidence::List tempList; ++ // Remove all to get access to the remaining entries ++ QList l = d->mOrphans.values( *uidit ); ++ d->mOrphans.remove( *uidit ); ++ foreach ( Incidence *i, l ) { ++ if ( i != incidence ) { ++ tempList.append( i ); ++ } ++ } ++ // Readd those that point to a different orphan incidence ++ for ( KCalCore::Incidence::List::Iterator incit = tempList.begin(); ++ incit != tempList.end(); ++incit ) { ++ d->mOrphans.insert( *uidit, *incit ); ++ } ++ } ++ } ++ ++ // Make sure the deleted incidence doesn't relate to a non-deleted incidence, ++ // since that would cause trouble in CalendarLocal::close(), as the deleted ++ // incidences are destroyed after the non-deleted incidences. The destructor ++ // of the deleted incidences would then try to access the already destroyed ++ // non-deleted incidence, which would segfault. ++ // ++ // So in short: Make sure dead incidences don't point to alive incidences ++ // via the relation. ++ // ++ // This crash is tested in CalendarLocalTest::testRelationsCrash(). ++} ++#endif // AKONADI_PORT_DISABLED ++ ++void Calendar::CalendarObserver::calendarIncidenceAdded( const Akonadi::Item &incidence ) ++{ ++ Q_UNUSED( incidence ); ++} ++ ++void Calendar::CalendarObserver::calendarIncidenceChanged( const Akonadi::Item &incidence ) ++{ ++ Q_UNUSED( incidence ); ++} ++ ++void Calendar::CalendarObserver::calendarIncidenceDeleted( const Akonadi::Item &incidence ) ++{ ++ Q_UNUSED( incidence ); ++} ++ ++void Calendar::registerObserver( CalendarObserver *observer ) ++{ ++ if ( !d->mObservers.contains( observer ) ) { ++ d->mObservers.append( observer ); ++ } ++ d->mNewObserver = true; ++} ++ ++void Calendar::unregisterObserver( CalendarObserver *observer ) ++{ ++ d->mObservers.removeAll( observer ); ++} ++ ++void Calendar::doSetTimeSpec( const KDateTime::Spec &timeSpec ) ++{ ++ Q_UNUSED( timeSpec ); ++} ++ ++void Calendar::notifyIncidenceAdded( const Akonadi::Item &i ) ++{ ++ if ( !d->mObserversEnabled ) { ++ return; ++ } ++ ++ foreach ( CalendarObserver *observer, d->mObservers ) { ++ observer->calendarIncidenceAdded( i ); ++ } ++} ++ ++void Calendar::notifyIncidenceChanged( const Akonadi::Item &i ) ++{ ++ if ( !d->mObserversEnabled ) { ++ return; ++ } ++ ++ foreach ( CalendarObserver *observer, d->mObservers ) { ++ observer->calendarIncidenceChanged( i ); ++ } ++} ++ ++void Calendar::notifyIncidenceDeleted( const Akonadi::Item &i ) ++{ ++ if ( !d->mObserversEnabled ) { ++ return; ++ } ++ ++ foreach ( CalendarObserver *observer, d->mObservers ) { ++ observer->calendarIncidenceDeleted( i ); ++ } ++} ++ ++void Calendar::customPropertyUpdated() ++{ ++} ++ ++void Calendar::setProductId( const QString &id ) ++{ ++ d->mProductId = id; ++} ++ ++QString Calendar::productId() const ++{ ++ return d->mProductId; ++} ++ ++Akonadi::Item::List Calendar::mergeIncidenceList( const Akonadi::Item::List &events, ++ const Akonadi::Item::List &todos, ++ const Akonadi::Item::List &journals ) ++{ ++ Akonadi::Item::List incidences; ++ ++ int i, end; ++ for ( i = 0, end = events.count(); i < end; ++i ) { ++ incidences.append( events[i] ); ++ } ++ ++ for ( i = 0, end = todos.count(); i < end; ++i ) { ++ incidences.append( todos[i] ); ++ } ++ ++ for ( i = 0, end = journals.count(); i < end; ++i ) { ++ incidences.append( journals[i] ); ++ } ++ ++ return incidences; ++} ++ ++void Calendar::setObserversEnabled( bool enabled ) ++{ ++ d->mObserversEnabled = enabled; ++} ++ ++void Calendar::appendAlarms( KCalCore::Alarm::List &alarms, const Akonadi::Item &item, ++ const KDateTime &from, const KDateTime &to ) ++{ ++ const KCalCore::Incidence::Ptr incidence = CalendarSupport::incidence( item ); ++ Q_ASSERT( incidence ); ++ ++ KDateTime preTime = from.addSecs(-1); ++ ++ KCalCore::Alarm::List alarmlist = incidence->alarms(); ++ for ( int i = 0, iend = alarmlist.count(); i < iend; ++i ) { ++ if ( alarmlist[i]->enabled() ) { ++ KDateTime dt = alarmlist[i]->nextRepetition( preTime ); ++ if ( dt.isValid() && dt <= to ) { ++ kDebug() << incidence->summary() << "':" << dt.toString(); ++ alarms.append( alarmlist[i] ); ++ } ++ } ++ } ++} ++ ++void Calendar::appendRecurringAlarms( KCalCore::Alarm::List &alarms, ++ const Akonadi::Item &item, ++ const KDateTime &from, ++ const KDateTime &to ) ++{ ++ KDateTime dt; ++ bool endOffsetValid = false; ++ KCalCore::Duration endOffset( 0 ); ++ KCalCore::Duration period( from, to ); ++ ++ const KCalCore::Incidence::Ptr incidence = CalendarSupport::incidence( item ); ++ Q_ASSERT( incidence ); ++ ++ KCalCore::Alarm::List alarmlist = incidence->alarms(); ++ for ( int i = 0, iend = alarmlist.count(); i < iend; ++i ) { ++ KCalCore::Alarm::Ptr a = alarmlist[i]; ++ if ( a->enabled() ) { ++ if ( a->hasTime() ) { ++ // The alarm time is defined as an absolute date/time ++ dt = a->nextRepetition( from.addSecs(-1) ); ++ if ( !dt.isValid() || dt > to ) { ++ continue; ++ } ++ } else { ++ // Alarm time is defined by an offset from the event start or end time. ++ // Find the offset from the event start time, which is also used as the ++ // offset from the recurrence time. ++ KCalCore::Duration offset( 0 ); ++ if ( a->hasStartOffset() ) { ++ offset = a->startOffset(); ++ } else if ( a->hasEndOffset() ) { ++ offset = a->endOffset(); ++ if ( !endOffsetValid ) { ++ endOffset = KCalCore::Duration( ++ incidence->dtStart(), ++ incidence->dateTime( KCalCore::IncidenceBase::RoleAlarmEndOffset ) ); ++ endOffsetValid = true; ++ } ++ } ++ ++ // Find the incidence's earliest alarm ++ KDateTime alarmStart = ++ offset.end( a->hasEndOffset() ? ++ incidence->dateTime( KCalCore::IncidenceBase::RoleAlarmEndOffset ) : ++ incidence->dtStart() ); ++// KDateTime alarmStart = incidence->dtStart().addSecs( offset ); ++ if ( alarmStart > to ) { ++ continue; ++ } ++ KDateTime baseStart = incidence->dtStart(); ++ if ( from > alarmStart ) { ++ alarmStart = from; // don't look earlier than the earliest alarm ++ baseStart = (-offset).end( (-endOffset).end( alarmStart ) ); ++ } ++ ++ // Adjust the 'alarmStart' date/time and find the next recurrence at or after it. ++ // Treate the two offsets separately in case one is daily and the other not. ++ dt = incidence->recurrence()->getNextDateTime( baseStart.addSecs(-1) ); ++ if ( !dt.isValid() || ++ ( dt = endOffset.end( offset.end( dt ) ) ) > to ) // adjust 'dt' to get the alarm time ++ { ++ // The next recurrence is too late. ++ if ( !a->repeatCount() ) { ++ continue; ++ } ++ ++ // The alarm has repetitions, so check whether repetitions of previous ++ // recurrences fall within the time period. ++ bool found = false; ++ KCalCore::Duration alarmDuration = a->duration(); ++ for ( KDateTime base = baseStart; ++ ( dt = incidence->recurrence()->getPreviousDateTime( base ) ).isValid(); ++ base = dt ) { ++ if ( a->duration().end( dt ) < base ) { ++ break; // this recurrence's last repetition is too early, so give up ++ } ++ ++ // The last repetition of this recurrence is at or after 'alarmStart' time. ++ // Check if a repetition occurs between 'alarmStart' and 'to'. ++ int snooze = a->snoozeTime().value(); // in seconds or days ++ if ( a->snoozeTime().isDaily() ) { ++ KCalCore::Duration toFromDuration( dt, base ); ++ int toFrom = toFromDuration.asDays(); ++ if ( a->snoozeTime().end( from ) <= to || ++ ( toFromDuration.isDaily() && toFrom % snooze == 0 ) || ++ ( toFrom / snooze + 1 ) * snooze <= toFrom + period.asDays() ) { ++ found = true; ++#ifndef NDEBUG ++ // for debug output ++ dt = offset.end( dt ).addDays( ( ( toFrom - 1 ) / snooze + 1 ) * snooze ); ++#endif ++ break; ++ } ++ } else { ++ int toFrom = dt.secsTo( base ); ++ if ( period.asSeconds() >= snooze || ++ toFrom % snooze == 0 || ++ ( toFrom / snooze + 1 ) * snooze <= toFrom + period.asSeconds() ) ++ { ++ found = true; ++#ifndef NDEBUG ++ // for debug output ++ dt = offset.end( dt ).addSecs( ( ( toFrom - 1 ) / snooze + 1 ) * snooze ); ++#endif ++ break; ++ } ++ } ++ } ++ if ( !found ) { ++ continue; ++ } ++ } ++ } ++ // kDebug() << incidence->summary() << "':" << dt.toString(); ++ alarms.append( a ); ++ } ++ } ++} ++ ++Akonadi::Collection Calendar::collection( const Akonadi::Collection::Id &id ) const ++{ ++ if ( d->m_collectionMap.contains( id ) ) { ++ return d->m_collectionMap[id]; ++ } else { ++ return Akonadi::Collection(); ++ } ++} ++ ++bool Calendar::hasChangeRights( const Akonadi::Item &item ) const ++{ ++ // if the users changes the rights, item.parentCollection() ++ // can still have the old rights, so we use call collection() ++ // which returns the updated one ++ const Akonadi::Collection col = collection( item.storageCollectionId() ); ++ return col.rights() & Akonadi::Collection::CanChangeItem; ++} ++ ++bool Calendar::hasDeleteRights( const Akonadi::Item &item ) const ++{ ++ // if the users changes the rights, item.parentCollection() ++ // can still have the old rights, so we use call collection() ++ // which returns the updated one ++ const Akonadi::Collection col = collection( item.storageCollectionId() ); ++ return col.rights() & Akonadi::Collection::CanDeleteItem; ++} ++ ++int Calendar::incidencesCount() const ++{ ++ return d->m_model->rowCount(); ++} +diff --git a/plasma/generic/dataengines/calendar/akonadi/calendar.h b/plasma/generic/dataengines/calendar/akonadi/calendar.h +new file mode 100644 +index 0000000..364fc81 +--- /dev/null ++++ b/plasma/generic/dataengines/calendar/akonadi/calendar.h +@@ -0,0 +1,772 @@ ++/* ++ Copyright (c) 2009 KDAB ++ Author: Sebastian Sauer ++ Frank Osterfeld ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, 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 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. ++*/ ++ ++#ifndef CALENDARSUPPORT_CALENDAR_H ++#define CALENDARSUPPORT_CALENDAR_H ++ ++#include ++ ++#include ++#include ++ ++#include ++ ++#include ++ ++namespace KCalCore { ++ class CalFilter; ++} ++ ++class QAbstractItemModel; ++ ++namespace CalendarSupport { ++ ++/** ++ Calendar KCalCore::Incidence sort directions. ++*/ ++enum SortDirection { ++ SortDirectionAscending, /**< Sort in ascending order (first to last) */ ++ SortDirectionDescending /**< Sort in descending order (last to first) */ ++}; ++ ++/** ++ Calendar Event sort keys. ++*/ ++enum EventSortField { ++ EventSortUnsorted, /**< Do not sort Events */ ++ EventSortStartDate, /**< Sort Events chronologically, by start date */ ++ EventSortEndDate, /**< Sort Events chronologically, by end date */ ++ EventSortSummary /**< Sort Events alphabetically, by summary */ ++}; ++ ++/** ++ Calendar Todo sort keys. ++*/ ++enum TodoSortField { ++ TodoSortUnsorted, /**< Do not sort Todos */ ++ TodoSortStartDate, /**< Sort Todos chronologically, by start date */ ++ TodoSortDueDate, /**< Sort Todos chronologically, by due date */ ++ TodoSortPriority, /**< Sort Todos by priority */ ++ TodoSortPercentComplete, /**< Sort Todos by percentage completed */ ++ TodoSortSummary /**< Sort Todos alphabetically, by summary */ ++}; ++ ++/** ++ Calendar Journal sort keys. ++*/ ++enum JournalSortField { ++ JournalSortUnsorted, /**< Do not sort Journals */ ++ JournalSortDate, /**< Sort Journals chronologically by date */ ++ JournalSortSummary /**< Sort Journals alphabetically, by summary */ ++}; ++ ++/** ++ * Implements a KCalCore::Calendar that uses Akonadi as backend. ++ */ ++class Calendar : public QObject, ++ public KCalCore::CustomProperties, ++ public KCalCore::IncidenceBase::IncidenceObserver ++{ ++ Q_OBJECT ++ ++ Q_PROPERTY( int incidencesCount READ incidencesCount NOTIFY calendarChanged ) ++ public: ++ ++ /** ++ Sets the calendar Product ID to @p id. ++ @param id is a string containing the Product ID. ++ @see productId() const ++ */ ++ void setProductId( const QString &id ); ++ ++ /** ++ Returns the calendar's Product ID. ++ @see setProductId() ++ */ ++ QString productId() const; ++ ++ /** ++ Sets the owner of the calendar to @p owner. ++ @param owner is a Person object. ++ @see owner() ++ */ ++ void setOwner( const KCalCore::Person &owner ); ++ ++ /** ++ Returns the owner of the calendar. ++ @return the owner Person object. ++ @see setOwner() ++ */ ++ KCalCore::Person owner() const; ++ ++ /** ++ Sets the default time specification (time zone, etc.) used for creating ++ or modifying incidences in the Calendar. ++ ++ The method also calls setViewTimeSpec(@p timeSpec). ++ ++ @param timeSpec time specification ++ */ ++ void setTimeSpec( const KDateTime::Spec &timeSpec ); ++ ++ /** ++ Get the time specification (time zone etc.) used for creating or ++ modifying incidences in the Calendar. ++ @return time specification ++ */ ++ KDateTime::Spec timeSpec() const; ++ ++ /** ++ Sets the time zone ID used for creating or modifying incidences in the ++ Calendar. This method has no effect on existing incidences. ++ ++ The method also calls setViewTimeZoneId(@p timeZoneId). ++ ++ @param timeZoneId is a string containing a time zone ID, which is ++ assumed to be valid. The time zone ID is used to set the time zone ++ for viewing KCalCore::Incidence date/times. If no time zone is found, the ++ viewing time specification is set to local clock time. ++ @e Example: "Europe/Berlin" ++ @see setTimeSpec() ++ */ ++ void setTimeZoneId( const QString &timeZoneId ); ++ ++ /** ++ Returns the time zone ID used for creating or modifying incidences in ++ the calendar. ++ ++ @return the string containing the time zone ID, or empty string if the ++ creation/modification time specification is not a time zone. ++ */ ++ QString timeZoneId() const; ++ ++ /** ++ Notes the time specification which the client application intends to ++ use for viewing the incidences in this calendar. This is simply a ++ convenience method which makes a note of the new time zone so that ++ it can be read back by viewTimeSpec(). The client application must ++ convert date/time values to the desired time zone itself. ++ ++ The time specification is not used in any way by the Calendar or its ++ incidences; it is solely for use by the client application. ++ ++ @param timeSpec time specification ++ ++ @see viewTimeSpec() ++ */ ++ void setViewTimeSpec( const KDateTime::Spec &timeSpec ) const; ++ ++ /** ++ Notes the time zone Id which the client application intends to use for ++ viewing the incidences in this calendar. This is simply a convenience ++ method which makes a note of the new time zone so that it can be read ++ back by viewTimeId(). The client application must convert date/time ++ values to the desired time zone itself. ++ ++ The Id is not used in any way by the Calendar or its incidences. ++ It is solely for use by the client application. ++ ++ @param timeZoneId is a string containing a time zone ID, which is ++ assumed to be valid. The time zone ID is used to set the time zone ++ for viewing KCalCore::Incidence date/times. If no time zone is found, the ++ viewing time specification is set to local clock time. ++ @e Example: "Europe/Berlin" ++ ++ @see viewTimeZoneId() ++ */ ++ void setViewTimeZoneId( const QString &timeZoneId ) const; ++ ++ /** ++ Returns the time specification used for viewing the incidences in ++ this calendar. This simply returns the time specification last ++ set by setViewTimeSpec(). ++ @see setViewTimeSpec(). ++ */ ++ KDateTime::Spec viewTimeSpec() const; ++ ++ /** ++ Returns the time zone Id used for viewing the incidences in this ++ calendar. This simply returns the time specification last set by ++ setViewTimeSpec(). ++ @see setViewTimeZoneId(). ++ */ ++ QString viewTimeZoneId() const; ++ ++ /** ++ Shifts the times of all incidences so that they appear at the same clock ++ time as before but in a new time zone. The shift is done from a viewing ++ time zone rather than from the actual incidence time zone. ++ ++ For example, shifting an incidence whose start time is 09:00 America/New York, ++ using an old viewing time zone (@p oldSpec) of Europe/London, to a new time ++ zone (@p newSpec) of Europe/Paris, will result in the time being shifted ++ from 14:00 (which is the London time of the incidence start) to 14:00 Paris ++ time. ++ ++ @param oldSpec the time specification which provides the clock times ++ @param newSpec the new time specification ++ ++ @see isLocalTime() ++ */ ++ void shiftTimes( const KDateTime::Spec &oldSpec, const KDateTime::Spec &newSpec ); ++ ++ /** ++ Returns a list of all categories used by KCalCore::Incidences in the calendar @p cal. ++ ++ @param cal the calendar to return incidences from ++ @return a QStringList containing all the categories. ++ */ ++ static QStringList categories( Calendar *cal ); ++ ++// KCalCore::Incidence Specific Methods // ++ ++ /** ++ Returns a filtered list of all KCalCore::Incidences for this Calendar. ++ @deprecated: ++ ++ @return the list of all filtered KCalCore::Incidences. ++ */ ++ Akonadi::Item::List incidences(); ++ ++ /** ++ Returns a filtered list of all KCalCore::Incidences which occur on the given date. ++ ++ @param date request filtered KCalCore::Incidence list for this QDate only. ++ @deprecated: ++ ++ @return the list of filtered KCalCore::Incidences occurring on the specified date. ++ */ ++ Akonadi::Item::List incidences( const QDate &date ); ++ ++ /** ++ Returns an unfiltered list of all KCalCore::Incidences for this Calendar. ++ @deprecated: ++ ++ @return the list of all unfiltered KCalCore::Incidences. ++ */ ++ Akonadi::Item::List rawIncidences(); ++ ++ /** ++ Returns the KCalCore::Incidence associated with the given unique identifier. ++ ++ @param uid is a unique identifier string. ++ @deprecated: ++ ++ @return a pointer to the KCalCore::Incidence. ++ A null pointer is returned if no such KCalCore::Incidence exists. ++ */ ++ Akonadi::Item incidence( Akonadi::Item::Id id ) const; ++ ++ Akonadi::Collection collection( const Akonadi::Entity::Id &id ) const; ++ ++ /** ++ Returns the KCalCore::Incidence associated with the given scheduling identifier. ++ ++ @param sid is a unique scheduling identifier string. ++ @deprecated: ++ ++ @return a pointer to the KCalCore::Incidence. ++ A null pointer is returned if no such KCalCore::Incidence exists. ++ */ ++ Akonadi::Item incidenceFromSchedulingID( const QString &sid ); ++ ++ /** ++ Searches all events and todos for an incidence with this ++ scheduling identifiere. Returns a list of matching results. ++ @deprecated: ++ ++ @param sid is a unique scheduling identifier string. ++ */ ++ Akonadi::Item::List incidencesFromSchedulingID( const QString &sid ); ++ ++ /** ++ Create a merged list of KCalCore::Events, KCalCore::Todos, and KCalCore::Journals. ++ ++ @param events is an KCalCore::Event list to merge. ++ @param todos is a KCalCore::Todo list to merge. ++ @param journals is a KCalCore::Journal list to merge. ++ @deprecated: ++ ++ @return a list of merged KCalCore::Incidences. ++ */ ++ static Akonadi::Item::List mergeIncidenceList( const Akonadi::Item::List &events, ++ const Akonadi::Item::List &todos, ++ const Akonadi::Item::List &journals ); ++ ++ /** ++ Dissociate an KCalCore::Incidence from a recurring KCalCore::Incidence. ++ By default, only one single KCalCore::Incidence for the specified @a date ++ will be dissociated and returned. If @a single is false, then ++ the recurrence will be split at @a date, the old KCalCore::Incidence will ++ have its recurrence ending at @a date and the new KCalCore::Incidence ++ will have all recurrences past the @a date. ++ ++ @param incidence is a pointer to a recurring KCalCore::Incidence. ++ @param date is the QDate within the recurring KCalCore::Incidence on which ++ the dissociation will be performed. ++ @param spec is the spec in which the @a date is formulated. ++ @param single is a flag meaning that a new KCalCore::Incidence should be created ++ from the recurring KCalCore::Incidences after @a date. ++ @deprecated: ++ ++ @return a pointer to a new recurring KCalCore::Incidence if @a single is false. ++ */ ++ KCalCore::Incidence::Ptr dissociateOccurrence( const Akonadi::Item &incidence, ++ const QDate &date, ++ const KDateTime::Spec &spec, ++ bool single = true ); ++ ++// KCalCore::Event Specific Methods // ++ ++ /** ++ Sort a list of KCalCore::Events. ++ ++ @param eventList is a pointer to a list of KCalCore::Events. ++ @param sortField specifies the EventSortField. ++ @param sortDirection specifies the SortDirection. ++ @deprecated: ++ ++ @return a list of KCalCore::Events sorted as specified. ++ */ ++ static Akonadi::Item::List sortEvents( const Akonadi::Item::List &eventList, ++ EventSortField sortField, ++ SortDirection sortDirection ); ++ ++ /** ++ Returns a sorted, filtered list of all KCalCore::Events for this Calendar. ++ ++ @param sortField specifies the EventSortField. ++ @param sortDirection specifies the SortDirection. ++ @deprecated: ++ ++ @return the list of all filtered KCalCore::Events sorted as specified. ++ */ ++ virtual Akonadi::Item::List events( ++ EventSortField sortField = EventSortUnsorted, ++ SortDirection sortDirection = SortDirectionAscending ); ++ ++ /** ++ Returns a filtered list of all KCalCore::Events which occur on the given timestamp. ++ ++ @param dt request filtered KCalCore::Event list for this KDateTime only. ++ @deprecated: ++ ++ @return the list of filtered KCalCore::Events occurring on the specified timestamp. ++ */ ++ Akonadi::Item::List events( const KDateTime &dt ); ++ ++ /** ++ Returns a filtered list of all KCalCore::Events occurring within a date range. ++ ++ @param start is the starting date. ++ @param end is the ending date. ++ @param timeSpec time zone etc. to interpret @p start and @p end, ++ or the calendar's default time spec if none is specified ++ @param inclusive if true only KCalCore::Events which are completely included ++ within the date range are returned. ++ @deprecated: ++ ++ @return the list of filtered KCalCore::Events occurring within the specified ++ date range. ++ */ ++ Akonadi::Item::List events( const QDate &start, const QDate &end, ++ const KDateTime::Spec &timeSpec = KDateTime::Spec(), ++ bool inclusive = false ); ++ ++ /** ++ Returns a sorted, filtered list of all KCalCore::Events which occur on the given ++ date. The KCalCore::Events are sorted according to @a sortField and ++ @a sortDirection. ++ ++ @param date request filtered KCalCore::Event list for this QDate only. ++ @param timeSpec time zone etc. to interpret @p start and @p end, ++ or the calendar's default time spec if none is specified ++ @param sortField specifies the EventSortField. ++ @param sortDirection specifies the SortDirection. ++ @deprecated: ++ ++ @return the list of sorted, filtered KCalCore::Events occurring on @a date. ++ */ ++ Akonadi::Item::List events( ++ const QDate &date, ++ const KDateTime::Spec &timeSpec = KDateTime::Spec(), ++ EventSortField sortField = EventSortUnsorted, ++ SortDirection sortDirection = SortDirectionAscending ); ++ ++// KCalCore::Todo Specific Methods // ++ ++ /** ++ Sort a list of KCalCore::Todos. ++ ++ @param todoList is a pointer to a list of KCalCore::Todos. ++ @param sortField specifies the TodoSortField. ++ @param sortDirection specifies the SortDirection. ++ @deprecated: ++ ++ @return a list of KCalCore::Todos sorted as specified. ++ */ ++ static Akonadi::Item::List sortTodos( const Akonadi::Item::List &todoList, ++ TodoSortField sortField, ++ SortDirection sortDirection ); ++ ++ /** ++ Returns a sorted, filtered list of all KCalCore::Todos for this Calendar. ++ ++ @param sortField specifies the TodoSortField. ++ @param sortDirection specifies the SortDirection. ++ @deprecated: ++ ++ @return the list of all filtered KCalCore::Todos sorted as specified. ++ */ ++ virtual Akonadi::Item::List todos( ++ TodoSortField sortField = TodoSortUnsorted, ++ SortDirection sortDirection = SortDirectionAscending ); ++ ++ /** ++ Returns a filtered list of all KCalCore::Todos which are due on the specified date. ++ ++ @param date request filtered KCalCore::Todos due on this QDate. ++ @deprecated: ++ ++ @return the list of filtered KCalCore::Todos due on the specified date. ++ */ ++ virtual Akonadi::Item::List todos( const QDate &date ); ++ ++// KCalCore::Journal Specific Methods // ++ ++ /** ++ Sort a list of KCalCore::Journals. ++ ++ @param journalList is a pointer to a list of KCalCore::Journals. ++ @param sortField specifies the JournalSortField. ++ @param sortDirection specifies the SortDirection. ++ @deprecated: ++ ++ @return a list of KCalCore::Journals sorted as specified. ++ */ ++ static Akonadi::Item::List sortJournals( const Akonadi::Item::List &journalList, ++ JournalSortField sortField, ++ SortDirection sortDirection ); ++ ++ /** ++ Returns a sorted, filtered list of all KCalCore::Journals for this Calendar. ++ ++ @param sortField specifies the JournalSortField. ++ @param sortDirection specifies the SortDirection. ++ @deprecated: ++ ++ @return the list of all filtered KCalCore::Journals sorted as specified. ++ */ ++ virtual Akonadi::Item::List journals( ++ JournalSortField sortField = JournalSortUnsorted, ++ SortDirection sortDirection = SortDirectionAscending ); ++ ++ /** ++ Returns a filtered list of all KCalCore::Journals for on the specified date. ++ ++ @param date request filtered KCalCore::Journals for this QDate only. ++ @deprecated: ++ ++ @return the list of filtered KCalCore::Journals for the specified date. ++ */ ++ virtual Akonadi::Item::List journals( const QDate &date ); ++ ++ /** ++ Emits the beginBatchAdding() signal. ++ ++ This should be called before adding a batch of incidences with ++ addIncidence( KCalCore::Incidence::Ptr ), addTodo( KCalCore::Todo::Ptr ), ++ addEvent( KCalCore::Event::Ptr ) or addJournal( KCalCore::Journal::Ptr ). ++ Some Calendars are connected to this signal, e.g: CalendarResources uses ++ it to know a series of incidenceAdds are related so the user isn't prompted ++ multiple times which resource to save the incidence to ++ ++ @since 4.4 ++ */ ++ void beginBatchAdding(); ++ ++ /** ++ Emits the endBatchAdding() signal. ++ ++ Used with beginBatchAdding(). Should be called after ++ adding all incidences. ++ ++ @since 4.4 ++ */ ++ void endBatchAdding(); ++ ++// Filter Specific Methods // ++ ++ /** ++ Sets the calendar filter. ++ ++ @param filter a pointer to a CalFilter object which will be ++ used to filter Calendar KCalCore::Incidences. ++ @deprecated: ++ ++ @see filter() ++ */ ++ void setFilter( KCalCore::CalFilter *filter ); ++ ++ /** ++ Returns the calendar filter. ++ ++ @return a pointer to the calendar CalFilter. ++ A null pointer is returned if no such CalFilter exists. ++ @deprecated: ++ ++ @see setFilter() ++ */ ++ KCalCore::CalFilter *filter(); ++ ++// Observer Specific Methods // ++ ++ /** ++ @class CalendarObserver ++ ++ The CalendarObserver class. ++ */ ++ class CalendarObserver //krazy:exclude=dpointer ++ { ++ public: ++ /** ++ Destructor. ++ */ ++ virtual ~CalendarObserver() {} ++ ++ /** ++ Notify the Observer that an KCalCore::Incidence has been inserted. ++ @deprecated: ++ ++ @param incidence is a pointer to the KCalCore::Incidence that was inserted. ++ */ ++ virtual void calendarIncidenceAdded( const Akonadi::Item &incidence ); ++ ++ /** ++ Notify the Observer that an KCalCore::Incidence has been modified. ++ @deprecated: ++ ++ @param incidence is a pointer to the KCalCore::Incidence that was modified. ++ */ ++ virtual void calendarIncidenceChanged( const Akonadi::Item &incidence ); ++ ++ /** ++ Notify the Observer that an KCalCore::Incidence has been removed. ++ @deprecated: ++ ++ @param incidence is a pointer to the KCalCore::Incidence that was removed. ++ */ ++ virtual void calendarIncidenceDeleted( const Akonadi::Item &incidence ); ++ ++ }; ++ ++ /** ++ Registers an Observer for this Calendar. ++ ++ @param observer is a pointer to an Observer object that will be ++ watching this Calendar. ++ ++ @see unregisterObserver() ++ */ ++ void registerObserver( CalendarObserver *observer ); ++ ++ /** ++ Unregisters an Observer for this Calendar. ++ ++ @param observer is a pointer to an Observer object that has been ++ watching this Calendar. ++ ++ @see registerObserver() ++ */ ++ void unregisterObserver( CalendarObserver *observer ); ++ ++ /** ++ Returns if the parent collection's rights allow deleting this item. ++ Isn't merged with hasChangeRights() for convenience. ++ */ ++ bool hasDeleteRights( const Akonadi::Item &item ) const; ++ ++ /** ++ Returns if the parent collection's rights allow changing this item. ++ Isn't merged with hasDeleteRights() for convenience. ++ */ ++ bool hasChangeRights( const Akonadi::Item &item ) const; ++ ++ Q_SIGNALS: ++ /** ++ Signals that the calendar has been modified. ++ */ ++ void calendarChanged(); ++ ++ /** ++ @see beginBatchAdding() ++ @since 4.4 ++ */ ++ void batchAddingBegins(); ++ ++ /** ++ @see endBatchAdding() ++ @since 4.4 ++ */ ++ void batchAddingEnds(); ++ ++ protected: ++ /** ++ Let Calendar subclasses set the time specification. ++ @param timeSpec is the time specification (time zone, etc.) for ++ viewing KCalCore::Incidence dates.\n ++ */ ++ virtual void doSetTimeSpec( const KDateTime::Spec &timeSpec ); ++ ++ /** ++ Let Calendar subclasses notify that they inserted an KCalCore::Incidence. ++ @deprecated: ++ @param incidence is a pointer to the KCalCore::Incidence object that was inserted. ++ */ ++ void notifyIncidenceAdded( const Akonadi::Item &incidence ); ++ ++ /** ++ Let Calendar subclasses notify that they modified an KCalCore::Incidence. ++ @deprecated: ++ @param incidence is a pointer to the KCalCore::Incidence object that was modified. ++ */ ++ void notifyIncidenceChanged( const Akonadi::Item &incidence ); ++ ++ /** ++ Let Calendar subclasses notify that they removed an KCalCore::Incidence. ++ @deprecated: ++ @param incidence is a pointer to the KCalCore::Incidence object that was removed. ++ */ ++ void notifyIncidenceDeleted( const Akonadi::Item &incidence ); ++ ++ /** ++ @copydoc ++ CustomProperties::customPropertyUpdated() ++ */ ++ virtual void customPropertyUpdated(); ++ ++ /** ++ Let Calendar subclasses notify that they enabled an Observer. ++ @param enabled if true tells the calendar that a subclass has ++ enabled an Observer. ++ */ ++ void setObserversEnabled( bool enabled ); ++ ++ /** ++ Appends alarms of incidence in interval to list of alarms. ++ ++ @param alarms is a List of KCalCore::Alarms to be appended onto. ++ @param incidence is a pointer to an KCalCore::Incidence containing the KCalCore::Alarm ++ to be appended. ++ @param from is the lower range of the next KCalCore::Alarm repitition. ++ @param to is the upper range of the next KCalCore::Alarm repitition. ++ @deprecated: ++ */ ++ void appendAlarms( KCalCore::Alarm::List &alarms, const Akonadi::Item &incidence, ++ const KDateTime &from, const KDateTime &to ); ++ ++ /** ++ Appends alarms of recurring events in interval to list of alarms. ++ ++ @param alarms is a List of KCalCore::Alarms to be appended onto. ++ @param incidence is a pointer to an KCalCore::Incidence containing the KCalCore::Alarm ++ to be appended. ++ @param from is the lower range of the next KCalCore::Alarm repitition. ++ @param to is the upper range of the next KCalCore::Alarm repitition. ++ @deprecated: ++ */ ++ void appendRecurringAlarms( KCalCore::Alarm::List &alarms, const Akonadi::Item &incidence, ++ const KDateTime &from, const KDateTime &to ); ++ public: ++ explicit Calendar( QAbstractItemModel *treeModel, QAbstractItemModel *model, ++ const KDateTime::Spec &timeSpec, QObject *parent=0 ); ++ ~Calendar(); ++ ++ QAbstractItemModel *model() const; ++ ++ QAbstractItemModel *unfilteredModel() const; ++ void setUnfilteredModel( QAbstractItemModel *model ); ++ ++ QAbstractItemModel *treeModel() const; ++ ++ void incidenceUpdated( const QString &uid, const KDateTime &recurrenceId ); ++ void incidenceUpdate( const QString &uid, const KDateTime &recurrenceId ); ++ ++ Akonadi::Item ::List rawEvents( EventSortField sortField = EventSortUnsorted, ++ SortDirection sortDirection = SortDirectionAscending ); ++ ++ Akonadi::Item ::List rawEvents( const QDate &start, const QDate &end, ++ const KDateTime::Spec &timeSpec = KDateTime::Spec(), ++ bool inclusive = false ); ++ ++ Akonadi::Item ::List rawEventsForDate( const QDate &date, ++ const KDateTime::Spec &timeSpec = KDateTime::Spec(), ++ EventSortField sortField = EventSortUnsorted, ++ SortDirection sortDirection = SortDirectionAscending ); ++ ++ Akonadi::Item::List rawEventsForDate( const KDateTime &dt ); ++ ++ Akonadi::Item event( Akonadi::Item::Id id ) const; ++ ++ Akonadi::Item::List rawTodos( TodoSortField sortField = TodoSortUnsorted, ++ SortDirection sortDirection = SortDirectionAscending ); ++ ++ Akonadi::Item::List rawTodosForDate( const QDate &date ); ++ ++ Akonadi::Item todo( Akonadi::Item::Id uid ) const; ++ ++ Akonadi::Item::List rawJournals( JournalSortField sortField = JournalSortUnsorted, ++ SortDirection sortDirection = SortDirectionAscending ); ++ ++ Akonadi::Item::List rawJournalsForDate( const QDate &date ); ++ ++ Akonadi::Item journal( Akonadi::Item::Id id ) const; ++ ++ KCalCore::Alarm::List alarms( const KDateTime &from, const KDateTime &to, bool excludeBlockedAlarms = false ); ++ KCalCore::Alarm::List alarmsTo( const KDateTime &to ); ++ ++ Akonadi::Item findParent( const Akonadi::Item &item ) const; ++ ++ Akonadi::Item::List findChildren( const KCalCore::Incidence::Ptr &incidence ) const; ++ Akonadi::Item::List findChildren( const Akonadi::Item &item ) const; ++ bool isChild( const Akonadi::Item &parent, const Akonadi::Item &child ) const; ++ ++ Akonadi::Item::Id itemIdForIncidenceUid( const QString &uid ) const; ++ Akonadi::Item itemForIncidenceUid( const QString &uid ) const; ++ ++ /** ++ * Returns the number of incidences in the calendar. ++ */ ++ int incidencesCount() const; ++ ++ using QObject::event; // prevent warning about hidden virtual method ++ ++ Q_SIGNALS: ++ void signalErrorMessage( const QString & ); ++ ++ private: ++ Q_DISABLE_COPY( Calendar ) ++ class Private; ++ Private *const d; ++}; ++ ++} ++ ++#endif +diff --git a/plasma/generic/dataengines/calendar/akonadi/calendar_p.h b/plasma/generic/dataengines/calendar/akonadi/calendar_p.h +new file mode 100644 +index 0000000..f9b92de +--- /dev/null ++++ b/plasma/generic/dataengines/calendar/akonadi/calendar_p.h +@@ -0,0 +1,161 @@ ++/* ++ Copyright (c) 2009 KDAB ++ Authors: Sebastian Sauer ++ Till Adam ++ Frank Osterfeld ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, 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 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. ++*/ ++ ++#ifndef CALENDARSUPPORT_CALENDAR_P_H ++#define CALENDARSUPPORT_CALENDAR_P_H ++ ++#include "calendar.h" ++#include "calfilterproxymodel.h" ++ ++#include ++ ++#include ++#include ++ ++#include ++ ++namespace CalendarSupport { ++ ++class CalendarCollection : public QObject ++{ ++ Q_OBJECT ++ public: ++ Calendar *m_calendar; ++ Akonadi::Collection m_collection; ++ ++ CalendarCollection( Calendar *calendar, const Akonadi::Collection &collection ) ++ : QObject(), m_calendar(calendar), m_collection(collection) ++ { ++ } ++ ++ ~CalendarCollection() ++ { ++ } ++}; ++ ++struct UnseenItem ++{ ++ Akonadi::Entity::Id collection; ++ QString uid; ++ ++ bool operator<( const UnseenItem &other ) const ++ { ++ if ( collection != other.collection ) { ++ return collection < other.collection; ++ } ++ return uid < other.uid; ++ } ++}; ++ ++class Calendar::Private : public QObject ++{ ++ Q_OBJECT ++ private: ++ void removeItemFromMaps( const Akonadi::Item &item ); ++ Calendar *const q; ++ ++ public: ++ explicit Private( QAbstractItemModel *treeModel, QAbstractItemModel *model, Calendar *q ); ++ ~Private(); ++ ++ enum UpdateMode { ++ DontCare, ++ AssertExists, ++ AssertNew ++ }; ++ ++ void updateItem( const Akonadi::Item &item, UpdateMode mode ); ++ void itemChanged( const Akonadi::Item &item ); ++ ++ void assertInvariants() const; ++ void appendVirtualItems( Akonadi::Item::List &itemList ); ++ //CalendarBase begin ++ ++ KDateTime::Spec timeZoneIdSpec( const QString &timeZoneId, bool view ); ++ QString mProductId; ++ KCalCore::Person mOwner; ++ KCalCore::ICalTimeZones *mTimeZones; // collection of time zones used in this calendar ++ KCalCore::ICalTimeZone mBuiltInTimeZone; // cached time zone lookup ++ KCalCore::ICalTimeZone mBuiltInViewTimeZone; // cached viewing time zone lookup ++ KDateTime::Spec mTimeSpec; ++ mutable KDateTime::Spec mViewTimeSpec; ++ bool mModified; ++ bool mNewObserver; ++ bool mObserversEnabled; ++ QList mObservers; ++ ++ KCalCore::CalFilter *mDefaultFilter; ++ //CalendarBase end ++ ++ QAbstractItemModel *m_treeModel; ++ QAbstractItemModel *m_model; ++ CalFilterProxyModel *m_filterProxy; ++ QHash m_itemMap; // akonadi id to items ++ QHash m_collectionMap; // akonadi id to collections ++ ++ // child to parent map, for already cached parents ++ QHash m_childToParent; ++ ++ //parent to children map for alread cached children ++ QHash > m_parentToChildren; ++ ++ QMap m_uidToItemId; ++ ++ // child to parent map, unknown/not cached parent items ++ QHash m_childToUnseenParent; ++ ++ QMap > m_unseenParentToChildren; ++ ++ // on start dates/due dates of non-recurring, single-day Incidences ++ QMultiHash m_itemIdsForDate; ++ ++ QHash m_itemDateForItemId; ++ ++ // From search folders. ++ QHash > m_virtualItems; ++ ++ void clear(); ++ void readFromModel(); ++ ++ public Q_SLOTS: ++ void itemsAdded( const Akonadi::Item::List &items ); ++ void itemsRemoved( const Akonadi::Item::List &items ); ++ ++ void collectionsAdded( const Akonadi::Collection::List &collections ); ++ void collectionsRemoved( const Akonadi::Collection::List &collections ); ++ ++ void rowsInserted( const QModelIndex &index, int start, int end ); ++ void rowsAboutToBeRemoved( const QModelIndex &index, int start, int end ); ++ void rowsInsertedInTreeModel( const QModelIndex &index, int start, int end ); ++ void rowsAboutToBeRemovedInTreeModel( const QModelIndex &index, int start, int end ); ++ void dataChangedInTreeModel( const QModelIndex &topLeft, const QModelIndex &bottomRight ); ++ ++ void layoutChanged(); ++ void modelReset(); ++ void dataChanged( const QModelIndex &topLeft, const QModelIndex &bottomRight ); ++ ++ void onRowsMovedInTreeModel( const QModelIndex &sourceParent, int sourceStart, int sourceEnd, ++ const QModelIndex &destinationParent, int destinationRow ); ++}; ++ ++} ++ ++#endif +diff --git a/plasma/generic/dataengines/calendar/akonadi/calendarmodel.cpp b/plasma/generic/dataengines/calendar/akonadi/calendarmodel.cpp +new file mode 100644 +index 0000000..251a615 +--- /dev/null ++++ b/plasma/generic/dataengines/calendar/akonadi/calendarmodel.cpp +@@ -0,0 +1,248 @@ ++/* ++ Copyright (c) 2008 Bruno Virlet ++ 2009 KDAB; Author: Frank Osterfeld ++ ++ 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 "calendarmodel.h" ++#include "utils.h" ++ ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include ++ ++using namespace CalendarSupport; ++ ++class CalendarModel::Private ++{ ++ public: ++ explicit Private( CalendarModel *qq ) ++ :q( qq ) ++ { ++ } ++ ++ private: ++ CalendarModel *const q; ++}; ++ ++CalendarModel::CalendarModel( Akonadi::ChangeRecorder *monitor, QObject *parent ) ++ : EntityTreeModel( monitor, parent ), ++ d( new Private( this ) ) ++{ ++ monitor->itemFetchScope().fetchAllAttributes( true ); ++} ++ ++CalendarModel::~CalendarModel() ++{ ++ delete d; ++} ++ ++static KDateTime primaryDateForIncidence( const Akonadi::Item &item ) ++{ ++ if ( const KCalCore::Todo::Ptr t = CalendarSupport::todo( item ) ) { ++ return t->hasDueDate() ? t->dtDue() : KDateTime(); ++ } ++ ++ if ( const KCalCore::Event::Ptr e = CalendarSupport::event( item ) ) { ++ return ( !e->recurs() && !e->isMultiDay() ) ? e->dtStart() : KDateTime(); ++ } ++ ++ if ( const KCalCore::Journal::Ptr j = CalendarSupport::journal( item ) ) { ++ return j->dtStart(); ++ } ++ ++ return KDateTime(); ++} ++ ++QVariant CalendarModel::entityData( const Akonadi::Item &item, int column, int role ) const ++{ ++ const KCalCore::Incidence::Ptr incidence = CalendarSupport::incidence( item ); ++ if ( !incidence ) { ++ return QVariant(); ++ } ++ ++ switch( role ) { ++ case Qt::DecorationRole: ++ if ( column != Summary ) { ++ return QVariant(); ++ } ++ if ( incidence->type() == KCalCore::IncidenceBase::TypeTodo ) { ++ return SmallIcon( QLatin1String( "view-pim-tasks" ) ); ++ } ++ if ( incidence->type() == KCalCore::IncidenceBase::TypeJournal ) { ++ return SmallIcon( QLatin1String( "view-pim-journal" ) ); ++ } ++ if ( incidence->type() == KCalCore::IncidenceBase::TypeEvent ) { ++ return SmallIcon( QLatin1String( "view-calendar" ) ); ++ } ++ return SmallIcon( QLatin1String( "network-wired" ) ); ++ ++ case Qt::DisplayRole: ++ switch( column ) { ++ case Summary: ++ return incidence->summary(); ++ ++ case DateTimeStart: ++ return incidence->dtStart().toString(); ++ ++ case DateTimeEnd: ++ return incidence->dateTime( KCalCore::Incidence::RoleEndTimeZone ).toString(); ++ ++ case DateTimeDue: ++ if ( KCalCore::Todo::Ptr todo = CalendarSupport::todo( item ) ) { ++ return todo->dtDue().toString(); ++ } else { ++ return QVariant(); ++ } ++ ++ case Priority: ++ if ( KCalCore::Todo::Ptr todo = CalendarSupport::todo( item ) ) { ++ return todo->priority(); ++ } else { ++ return QVariant(); ++ } ++ ++ case PercentComplete: ++ if ( KCalCore::Todo::Ptr todo = CalendarSupport::todo( item ) ) { ++ return todo->percentComplete(); ++ } else { ++ return QVariant(); ++ } ++ ++ case PrimaryDate: ++ return primaryDateForIncidence( item ).toString(); ++ ++ case Type: ++ ++ return incidence->type(); ++ default: ++ break; ++ } ++ ++ case SortRole: ++ switch( column ) { ++ case Summary: ++ return incidence->summary(); ++ ++ case DateTimeStart: ++ return incidence->dtStart().toUtc().dateTime(); ++ ++ case DateTimeEnd: ++ return incidence->dateTime( KCalCore::Incidence::RoleEndTimeZone ).toUtc().dateTime(); ++ ++ case DateTimeDue: ++ if ( KCalCore::Todo::Ptr todo = CalendarSupport::todo( item ) ) { ++ return todo->dtDue().toUtc().dateTime(); ++ } else { ++ return QVariant(); ++ } ++ ++ case PrimaryDate: ++ return primaryDateForIncidence( item ).toUtc().dateTime(); ++ ++ case Priority: ++ if ( KCalCore::Todo::Ptr todo = CalendarSupport::todo( item ) ) { ++ return todo->priority(); ++ } else { ++ return QVariant(); ++ } ++ ++ case PercentComplete: ++ if ( KCalCore::Todo::Ptr todo = CalendarSupport::todo( item ) ) { ++ return todo->percentComplete(); ++ } else { ++ return QVariant(); ++ } ++ ++ case Type: ++ return incidence->type(); ++ ++ default: ++ break; ++ } ++ ++ return QVariant(); ++ ++ case RecursRole: ++ return incidence->recurs(); ++ ++ default: ++ return QVariant(); ++ } ++ ++ return QVariant(); ++} ++ ++QVariant CalendarModel::entityData( const Akonadi::Collection &collection, ++ int column, int role ) const ++{ ++ return EntityTreeModel::entityData( collection, column, role ); ++} ++ ++int CalendarModel::entityColumnCount( EntityTreeModel::HeaderGroup headerSet ) const ++{ ++ if ( headerSet == EntityTreeModel::ItemListHeaders ) { ++ return ItemColumnCount; ++ } else { ++ return CollectionColumnCount; ++ } ++} ++ ++QVariant CalendarModel::entityHeaderData( int section, Qt::Orientation orientation, ++ int role, EntityTreeModel::HeaderGroup headerSet ) const ++{ ++ if ( role != Qt::DisplayRole || orientation != Qt::Horizontal ) { ++ return QVariant(); ++ } ++ ++ if ( headerSet == EntityTreeModel::ItemListHeaders ) { ++ switch( section ) { ++ case Summary: ++ return i18nc( "@title:column calendar event summary", "Summary" ); ++ case DateTimeStart: ++ return i18nc( "@title:column calendar event start date and time", "Start Date and Time" ); ++ case DateTimeEnd: ++ return i18nc( "@title:column calendar event end date and time", "End Date and Time" ); ++ case Type: ++ return i18nc( "@title:column calendar event type", "Type" ); ++ case DateTimeDue: ++ return i18nc( "@title:column todo item due date and time", "Due Date and Time" ); ++ case Priority: ++ return i18nc( "@title:column todo item priority", "Priority" ); ++ case PercentComplete: ++ return i18nc( "@title:column todo item completion in percent", "Complete" ); ++ default: ++ return QVariant(); ++ } ++ } ++ ++ if ( headerSet == EntityTreeModel::CollectionTreeHeaders ) { ++ switch ( section ) { ++ case CollectionTitle: ++ return i18nc( "@title:column calendar title", "Calendar" ); ++ default: ++ return QVariant(); ++ } ++ } ++ return QVariant(); ++} ++ +diff --git a/plasma/generic/dataengines/calendar/akonadi/calendarmodel.h b/plasma/generic/dataengines/calendar/akonadi/calendarmodel.h +new file mode 100644 +index 0000000..1acbfdd +--- /dev/null ++++ b/plasma/generic/dataengines/calendar/akonadi/calendarmodel.h +@@ -0,0 +1,79 @@ ++/* ++ Copyright (c) 2009 KDAB ++ Author: Frank Osterfeld ++ ++ 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. ++*/ ++ ++#ifndef CALENDARSUPPORT_CALENDARMODEL_H ++#define CALENDARSUPPORT_CALENDARMODEL_H ++ ++#include ++ ++namespace CalendarSupport { ++ ++class CalendarModel : public Akonadi::EntityTreeModel ++{ ++ Q_OBJECT ++ public: ++ enum ItemColumn { ++ Summary=0, ++ Type, ++ DateTimeStart, ++ DateTimeEnd, ++ DateTimeDue, ++ PrimaryDate, ++ Priority, ++ PercentComplete, ++ ItemColumnCount ++ }; ++ ++ enum CollectionColumn { ++ CollectionTitle=0, ++ CollectionColumnCount ++ }; ++ ++ enum Role { ++ SortRole=Akonadi::EntityTreeModel::UserRole, ++ RecursRole ++ }; ++ ++ explicit CalendarModel( Akonadi::ChangeRecorder *monitor, QObject *parent = 0 ); ++ ~CalendarModel(); ++ ++ /* reimp */ ++ QVariant entityData( const Akonadi::Item &item, int column, int role=Qt::DisplayRole ) const; ++ ++ /* reimp */ ++ QVariant entityData( const Akonadi::Collection &collection, int column, ++ int role=Qt::DisplayRole ) const; ++ ++ /* reimp */ ++ int entityColumnCount( EntityTreeModel::HeaderGroup headerSet ) const; ++ ++ /* reimp */ ++ QVariant entityHeaderData( int section, Qt::Orientation orientation, int role, ++ EntityTreeModel::HeaderGroup headerSet ) const; ++ ++ private: ++ class Private; ++ Private *const d; ++}; ++ ++} ++ ++#endif ++ +diff --git a/plasma/generic/dataengines/calendar/akonadi/calfilterproxymodel.cpp b/plasma/generic/dataengines/calendar/akonadi/calfilterproxymodel.cpp +new file mode 100644 +index 0000000..1820084 +--- /dev/null ++++ b/plasma/generic/dataengines/calendar/akonadi/calfilterproxymodel.cpp +@@ -0,0 +1,90 @@ ++/* ++ Copyright (c) 2009 KDAB ++ Author: Frank Osterfeld ++ ++ 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 "calfilterproxymodel.h" ++#include "calendarmodel.h" ++ ++#include ++ ++#include ++#include ++ ++using namespace CalendarSupport; ++ ++class CalFilterProxyModel::Private ++{ ++ public: ++ explicit Private() : filter( 0 ) {} ++ KCalCore::CalFilter *filter; ++}; ++ ++CalFilterProxyModel::CalFilterProxyModel( QObject *parent ) ++ : QSortFilterProxyModel( parent ), d( new Private ) ++{ ++ setSortRole( CalendarModel::SortRole ); ++ setFilterKeyColumn( 0 ); ++} ++ ++CalFilterProxyModel::~CalFilterProxyModel() ++{ ++ delete d; ++} ++ ++KCalCore::CalFilter *CalFilterProxyModel::filter() const ++{ ++ return d->filter; ++} ++ ++void CalFilterProxyModel::setFilter( KCalCore::CalFilter *filter ) ++{ ++ if ( filter == d->filter ) { ++ return; ++ } ++ ++ d->filter = filter; ++ invalidateFilter(); ++} ++ ++bool CalFilterProxyModel::filterAcceptsRow( int source_row, const QModelIndex &source_parent ) const ++{ ++ if ( !d->filter ) { ++ return true; ++ } ++ if ( source_row < 0 || !source_parent.isValid() ) { ++ return false; ++ } ++ ++ const QModelIndex idx = sourceModel()->index( source_row, 0, source_parent ); ++ if ( !idx.isValid() ) { ++ return false; ++ } ++ ++ const Akonadi::Item item = idx.data( Akonadi::EntityTreeModel::ItemRole ).value(); ++ if ( !item.isValid() || !item.hasPayload() ) { ++ return false; ++ } ++ ++ const KCalCore::Incidence::Ptr inc = item.payload(); ++ if ( !inc ) { ++ return false; ++ } ++ ++ return d->filter->filterIncidence( inc ); ++} +diff --git a/plasma/generic/dataengines/calendar/akonadi/calfilterproxymodel.h b/plasma/generic/dataengines/calendar/akonadi/calfilterproxymodel.h +new file mode 100644 +index 0000000..fe62155 +--- /dev/null ++++ b/plasma/generic/dataengines/calendar/akonadi/calfilterproxymodel.h +@@ -0,0 +1,51 @@ ++/* ++ Copyright (c) 2009 KDAB ++ Author: Frank Osterfeld ++ ++ 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. ++*/ ++#ifndef CALENDARSUPPORT_CALFILTERPROXYMODEL_H ++#define CALENDARSUPPORT_CALFILTERPROXYMODEL_H ++ ++#include ++ ++namespace KCalCore { ++ class CalFilter; ++} ++ ++namespace CalendarSupport { ++ ++class CalFilterProxyModel : public QSortFilterProxyModel ++{ ++ Q_OBJECT ++ public: ++ explicit CalFilterProxyModel( QObject *parent=0 ); ++ ~CalFilterProxyModel(); ++ ++ KCalCore::CalFilter *filter() const; ++ void setFilter( KCalCore::CalFilter *filter ); ++ ++ protected: ++ /* reimp */ bool filterAcceptsRow( int source_row, const QModelIndex &source_parent ) const; ++ ++ private: ++ class Private; ++ Private *const d; ++}; ++ ++} ++ ++#endif +diff --git a/plasma/generic/dataengines/calendar/akonadi/collectionselection.cpp b/plasma/generic/dataengines/calendar/akonadi/collectionselection.cpp +new file mode 100644 +index 0000000..41e03da +--- /dev/null ++++ b/plasma/generic/dataengines/calendar/akonadi/collectionselection.cpp +@@ -0,0 +1,104 @@ ++/* ++ Copyright (c) 2009 KDAB ++ Author: Frank Osterfeld ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, 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 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. ++ ++ As a special exception, permission is given to link this program ++ with any edition of Qt, and distribute the resulting executable, ++ without including the source code for Qt in the source distribution. ++*/ ++ ++#include "collectionselection.h" ++#include "utils.h" ++ ++#include ++ ++using namespace CalendarSupport; ++ ++class CollectionSelection::Private ++{ ++ public: ++ explicit Private( QItemSelectionModel *model_ ) : model( model_ ) ++ { ++ } ++ ++ QItemSelectionModel *model; ++}; ++ ++CollectionSelection::CollectionSelection( QItemSelectionModel *selectionModel, QObject *parent ) ++ : QObject( parent ), d( new Private ( selectionModel ) ) ++{ ++ connect( selectionModel, SIGNAL(selectionChanged(QItemSelection,QItemSelection)), ++ this, SLOT(slotSelectionChanged(QItemSelection,QItemSelection)) ); ++} ++ ++CollectionSelection::~CollectionSelection() ++{ ++ delete d; ++} ++ ++QItemSelectionModel *CollectionSelection::model() const ++{ ++ return d->model; ++} ++ ++bool CollectionSelection::hasSelection() const ++{ ++ return d->model->hasSelection(); ++} ++ ++bool CollectionSelection::contains( const Akonadi::Collection &c ) const ++{ ++ return selectedCollectionIds().contains( c.id() ); ++} ++ ++bool CollectionSelection::contains( const Akonadi::Collection::Id &id ) const ++{ ++ return selectedCollectionIds().contains( id ); ++} ++ ++Akonadi::Collection::List CollectionSelection::selectedCollections() const ++{ ++ Akonadi::Collection::List selected; ++ Q_FOREACH ( const QModelIndex &idx, d->model->selectedIndexes() ) { ++ selected.append( collectionFromIndex( idx ) ); ++ } ++ return selected; ++} ++ ++QList CollectionSelection::selectedCollectionIds() const ++{ ++ QList selected; ++ Q_FOREACH ( const QModelIndex &idx, d->model->selectedIndexes() ) { ++ selected.append( collectionIdFromIndex( idx ) ); ++ } ++ return selected; ++} ++ ++void CollectionSelection::slotSelectionChanged( const QItemSelection &selectedIndexes, ++ const QItemSelection &deselIndexes ) ++{ ++ const Akonadi::Collection::List selected = collectionsFromIndexes( selectedIndexes.indexes() ); ++ const Akonadi::Collection::List deselected = collectionsFromIndexes( deselIndexes.indexes() ); ++ ++ emit selectionChanged( selected, deselected ); ++ Q_FOREACH ( const Akonadi::Collection &c, deselected ) { ++ emit collectionDeselected( c ); ++ } ++ Q_FOREACH ( const Akonadi::Collection &c, selected ) { ++ emit collectionSelected( c ); ++ } ++} +diff --git a/plasma/generic/dataengines/calendar/akonadi/collectionselection.h b/plasma/generic/dataengines/calendar/akonadi/collectionselection.h +new file mode 100644 +index 0000000..4447afb +--- /dev/null ++++ b/plasma/generic/dataengines/calendar/akonadi/collectionselection.h +@@ -0,0 +1,67 @@ ++/* ++ Copyright (c) 2009 KDAB ++ Author: Frank Osterfeld ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, 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 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. ++ ++ As a special exception, permission is given to link this program ++ with any edition of Qt, and distribute the resulting executable, ++ without including the source code for Qt in the source distribution. ++*/ ++ ++#ifndef CALENDARSUPPORT_COLLECTIONSELECTION_H ++#define CALENDARSUPPORT_COLLECTIONSELECTION_H ++ ++#include ++ ++#include ++ ++class QItemSelection; ++class QItemSelectionModel; ++ ++namespace CalendarSupport { ++ ++class CollectionSelection : public QObject ++{ ++ Q_OBJECT ++ public: ++ explicit CollectionSelection( QItemSelectionModel *selectionModel, QObject *parent = 0 ); ++ ~CollectionSelection(); ++ ++ QItemSelectionModel *model() const; ++ Akonadi::Collection::List selectedCollections() const; ++ QList selectedCollectionIds() const; ++ bool contains( const Akonadi::Collection &c ) const; ++ bool contains( const Akonadi::Collection::Id &id ) const; ++ ++ bool hasSelection() const; ++ ++ Q_SIGNALS: ++ void selectionChanged( const Akonadi::Collection::List &selected, ++ const Akonadi::Collection::List &deselected ); ++ void collectionDeselected( const Akonadi::Collection & ); ++ void collectionSelected( const Akonadi::Collection & ); ++ ++ private Q_SLOTS: ++ void slotSelectionChanged( const QItemSelection &, const QItemSelection & ); ++ ++ private: ++ class Private; ++ Private *const d; ++}; ++ ++} ++ ++#endif +diff --git a/plasma/generic/dataengines/calendar/akonadi/utils.cpp b/plasma/generic/dataengines/calendar/akonadi/utils.cpp +new file mode 100644 +index 0000000..895edf9 +--- /dev/null ++++ b/plasma/generic/dataengines/calendar/akonadi/utils.cpp +@@ -0,0 +1,707 @@ ++/* ++ Copyright (c) 2009, 2010 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com ++ Copyright (C) 2009 KDAB (author: Frank Osterfeld ) ++ Copyright (c) 2010 Andras Mantia ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, 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 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. ++ ++ As a special exception, permission is given to link this program ++ with any edition of Qt, and distribute the resulting executable, ++ without including the source code for Qt in the source distribution. ++*/ ++ ++#include "utils.h" ++/*#include "kcalprefs.h" ++#include "mailclient.h" ++#include "mailscheduler.h" ++#include "publishdialog.h"*/ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++using namespace CalendarSupport; ++using namespace KHolidays; ++ ++KCalCore::Incidence::Ptr CalendarSupport::incidence( const Akonadi::Item &item ) ++{ ++ return ++ item.hasPayload() ? ++ item.payload() : ++ KCalCore::Incidence::Ptr(); ++} ++ ++KCalCore::Event::Ptr CalendarSupport::event( const Akonadi::Item &item ) ++{ ++ return ++ item.hasPayload() ? ++ item.payload() : ++ KCalCore::Event::Ptr(); ++} ++ ++KCalCore::Event::List CalendarSupport::eventsFromItems( const Akonadi::Item::List &items ) ++{ ++ KCalCore::Event::List events; ++ Q_FOREACH ( const Akonadi::Item &item, items ) { ++ if ( const KCalCore::Event::Ptr e = CalendarSupport::event( item ) ) { ++ events.push_back( e ); ++ } ++ } ++ return events; ++} ++ ++KCalCore::Incidence::List CalendarSupport::incidencesFromItems( const Akonadi::Item::List &items ) ++{ ++ KCalCore::Incidence::List incidences; ++ Q_FOREACH ( const Akonadi::Item &item, items ) { ++ if ( const KCalCore::Incidence::Ptr e = CalendarSupport::incidence( item ) ) { ++ incidences.push_back( e ); ++ } ++ } ++ return incidences; ++} ++ ++KCalCore::Todo::Ptr CalendarSupport::todo( const Akonadi::Item &item ) ++{ ++ return ++ item.hasPayload() ? ++ item.payload() : ++ KCalCore::Todo::Ptr(); ++} ++ ++KCalCore::Journal::Ptr CalendarSupport::journal( const Akonadi::Item &item ) ++{ ++ return ++ item.hasPayload() ? ++ item.payload() : ++ KCalCore::Journal::Ptr(); ++} ++ ++bool CalendarSupport::hasIncidence( const Akonadi::Item &item ) ++{ ++ return item.hasPayload(); ++} ++ ++bool CalendarSupport::hasEvent( const Akonadi::Item &item ) ++{ ++ return item.hasPayload(); ++} ++ ++bool CalendarSupport::hasTodo( const Akonadi::Item &item ) ++{ ++ return item.hasPayload(); ++} ++ ++bool CalendarSupport::hasJournal( const Akonadi::Item &item ) ++{ ++ return item.hasPayload(); ++} ++ ++QMimeData *CalendarSupport::createMimeData( const Akonadi::Item::List &items, ++ const KDateTime::Spec &timeSpec ) ++{ ++ if ( items.isEmpty() ) { ++ return 0; ++ } ++ ++ KCalCore::MemoryCalendar::Ptr cal( new KCalCore::MemoryCalendar( timeSpec ) ); ++ ++ QList urls; ++ int incidencesFound = 0; ++ Q_FOREACH ( const Akonadi::Item &item, items ) { ++ const KCalCore::Incidence::Ptr incidence( CalendarSupport::incidence( item ) ); ++ if ( !incidence ) { ++ continue; ++ } ++ ++incidencesFound; ++ urls.push_back( item.url() ); ++ KCalCore::Incidence::Ptr i( incidence->clone() ); ++ cal->addIncidence( i ); ++ } ++ ++ if ( incidencesFound == 0 ) { ++ return 0; ++ } ++ ++ std::auto_ptr mimeData( new QMimeData ); ++ ++ mimeData->setUrls( urls ); ++ ++ KCalUtils::ICalDrag::populateMimeData( mimeData.get(), cal ); ++ KCalUtils::VCalDrag::populateMimeData( mimeData.get(), cal ); ++ ++ return mimeData.release(); ++} ++ ++QMimeData *CalendarSupport::createMimeData( const Akonadi::Item &item, ++ const KDateTime::Spec &timeSpec ) ++{ ++ return createMimeData( Akonadi::Item::List() << item, timeSpec ); ++} ++ ++#ifndef QT_NO_DRAGANDDROP ++QDrag *CalendarSupport::createDrag( const Akonadi::Item &item, ++ const KDateTime::Spec &timeSpec, QWidget *parent ) ++{ ++ return createDrag( Akonadi::Item::List() << item, timeSpec, parent ); ++} ++#endif ++ ++static QByteArray findMostCommonType( const Akonadi::Item::List &items ) ++{ ++ QByteArray prev; ++ if ( items.isEmpty() ) { ++ return "Incidence"; ++ } ++ ++ Q_FOREACH( const Akonadi::Item &item, items ) { ++ if ( !CalendarSupport::hasIncidence( item ) ) { ++ continue; ++ } ++ const QByteArray type = CalendarSupport::incidence( item )->typeStr(); ++ if ( !prev.isEmpty() && type != prev ) { ++ return "Incidence"; ++ } ++ prev = type; ++ } ++ return prev; ++} ++ ++#ifndef QT_NO_DRAGANDDROP ++QDrag *CalendarSupport::createDrag( const Akonadi::Item::List &items, ++ const KDateTime::Spec &timeSpec, QWidget *parent ) ++{ ++ std::auto_ptr drag( new QDrag( parent ) ); ++ drag->setMimeData( CalendarSupport::createMimeData( items, timeSpec ) ); ++ ++ const QByteArray common = findMostCommonType( items ); ++ if ( common == "Event" ) { ++ drag->setPixmap( BarIcon( QLatin1String( "view-calendar-day" ) ) ); ++ } else if ( common == "Todo" ) { ++ drag->setPixmap( BarIcon( QLatin1String( "view-calendar-tasks" ) ) ); ++ } ++ ++ return drag.release(); ++} ++#endif ++ ++static bool itemMatches( const Akonadi::Item &item, const KCalCore::CalFilter *filter ) ++{ ++ assert( filter ); ++ KCalCore::Incidence::Ptr inc = CalendarSupport::incidence( item ); ++ if ( !inc ) { ++ return false; ++ } ++ return filter->filterIncidence( inc ); ++} ++ ++Akonadi::Item::List CalendarSupport::applyCalFilter( const Akonadi::Item::List &items_, ++ const KCalCore::CalFilter *filter ) ++{ ++ Q_ASSERT( filter ); ++ Akonadi::Item::List items( items_ ); ++ items.erase( std::remove_if( items.begin(), items.end(), ++ !bind( itemMatches, _1, filter ) ), items.end() ); ++ return items; ++} ++ ++bool CalendarSupport::isValidIncidenceItemUrl( const KUrl &url, ++ const QStringList &supportedMimeTypes ) ++{ ++ if ( !url.isValid() ) { ++ return false; ++ } ++ ++ if ( url.scheme() != QLatin1String( "akonadi" ) ) { ++ return false; ++ } ++ ++ return supportedMimeTypes.contains( url.queryItem( QLatin1String( "type" ) ) ); ++} ++ ++bool CalendarSupport::isValidIncidenceItemUrl( const KUrl &url ) ++{ ++ return isValidIncidenceItemUrl( url, ++ QStringList() << KCalCore::Event::eventMimeType() ++ << KCalCore::Todo::todoMimeType() ++ << KCalCore::Journal::journalMimeType() ++ << KCalCore::FreeBusy::freeBusyMimeType() ); ++} ++ ++static bool containsValidIncidenceItemUrl( const QList& urls ) ++{ ++ return ++ std::find_if( urls.begin(), urls.end(), ++ bind( CalendarSupport::isValidIncidenceItemUrl, _1 ) ) != urls.constEnd(); ++} ++ ++bool CalendarSupport::isValidTodoItemUrl( const KUrl &url ) ++{ ++ if ( !url.isValid() || url.scheme() != QLatin1String( "akonadi" ) ) { ++ return false; ++ } ++ ++ return url.queryItem( QLatin1String( "type" ) ) == KCalCore::Todo::todoMimeType(); ++} ++ ++bool CalendarSupport::canDecode( const QMimeData *md ) ++{ ++ Q_ASSERT( md ); ++ return ++ containsValidIncidenceItemUrl( md->urls() ) || ++ KCalUtils::ICalDrag::canDecode( md ) || ++ KCalUtils::VCalDrag::canDecode( md ); ++} ++ ++QList CalendarSupport::incidenceItemUrls( const QMimeData *mimeData ) ++{ ++ QList urls; ++ Q_FOREACH( const KUrl &i, mimeData->urls() ) { ++ if ( isValidIncidenceItemUrl( i ) ) { ++ urls.push_back( i ); ++ } ++ } ++ return urls; ++} ++ ++QList CalendarSupport::todoItemUrls( const QMimeData *mimeData ) ++{ ++ QList urls; ++ ++ Q_FOREACH( const KUrl &i, mimeData->urls() ) { ++ if ( isValidIncidenceItemUrl( i, QStringList() << KCalCore::Todo::todoMimeType() ) ) { ++ urls.push_back( i ); ++ } ++ } ++ return urls; ++} ++ ++bool CalendarSupport::mimeDataHasTodo( const QMimeData *mimeData ) ++{ ++ return !todoItemUrls( mimeData ).isEmpty() || !todos( mimeData, KDateTime::Spec() ).isEmpty(); ++} ++ ++KCalCore::Todo::List CalendarSupport::todos( const QMimeData *mimeData, ++ const KDateTime::Spec &spec ) ++{ ++ KCalCore::Todo::List todos; ++ ++#ifndef QT_NO_DRAGANDDROP ++ KCalCore::Calendar::Ptr cal( KCalUtils::DndFactory::createDropCalendar( mimeData, spec ) ); ++ if ( cal ) { ++ Q_FOREACH( const KCalCore::Todo::Ptr &i, cal->todos() ) { ++ todos.push_back( KCalCore::Todo::Ptr( i->clone() ) ); ++ } ++ } ++#endif ++ ++ return todos; ++} ++ ++Akonadi::Collection CalendarSupport::selectCollection( QWidget *parent, ++ int &dialogCode, ++ const QStringList &mimeTypes, ++ const Akonadi::Collection &defCollection ) ++{ ++ QPointer dlg( new Akonadi::CollectionDialog( parent ) ); ++ ++ kDebug() << "selecting collections with mimeType in " << mimeTypes; ++ ++ dlg->setMimeTypeFilter( mimeTypes ); ++ dlg->setAccessRightsFilter( Akonadi::Collection::CanCreateItem ); ++ if ( defCollection.isValid() ) { ++ dlg->setDefaultCollection( defCollection ); ++ } ++ Akonadi::Collection collection; ++ ++ // FIXME: don't use exec. ++ dialogCode = dlg->exec(); ++ if ( dialogCode == QDialog::Accepted ) { ++ collection = dlg->selectedCollection(); ++ ++ if ( !collection.isValid() ) { ++ kWarning() <<"An invalid collection was selected!"; ++ } ++ } ++ delete dlg; ++ ++ return collection; ++} ++ ++Akonadi::Item CalendarSupport::itemFromIndex( const QModelIndex &idx ) ++{ ++ Akonadi::Item item = idx.data( Akonadi::EntityTreeModel::ItemRole ).value(); ++ item.setParentCollection( ++ idx.data( Akonadi::EntityTreeModel::ParentCollectionRole ).value() ); ++ return item; ++} ++ ++Akonadi::Collection::List CalendarSupport::collectionsFromModel( const QAbstractItemModel *model, ++ const QModelIndex &parentIndex, ++ int start, int end ) ++{ ++ const int endRow = end >= 0 ? end : model->rowCount( parentIndex ) - 1; ++ Akonadi::Collection::List collections; ++ int row = start; ++ QModelIndex i = model->index( row, 0, parentIndex ); ++ while ( row <= endRow ) { ++ const Akonadi::Collection collection = collectionFromIndex( i ); ++ if ( collection.isValid() ) { ++ collections << collection; ++ QModelIndex childIndex = i.child( 0, 0 ); ++ if ( childIndex.isValid() ) { ++ collections << collectionsFromModel( model, i ); ++ } ++ } ++ ++row; ++ i = i.sibling( row, 0 ); ++ } ++ return collections; ++} ++ ++Akonadi::Item::List CalendarSupport::itemsFromModel( const QAbstractItemModel * model, ++ const QModelIndex &parentIndex, ++ int start, int end ) ++{ ++ const int endRow = end >= 0 ? end : model->rowCount( parentIndex ) - 1; ++ Akonadi::Item::List items; ++ int row = start; ++ QModelIndex i = model->index( row, 0, parentIndex ); ++ while ( row <= endRow ) { ++ const Akonadi::Item item = itemFromIndex( i ); ++ if ( CalendarSupport::hasIncidence( item ) ) { ++ items << item; ++ } else { ++ QModelIndex childIndex = i.child( 0, 0 ); ++ if ( childIndex.isValid() ) { ++ items << itemsFromModel( model, i ); ++ } ++ } ++ ++ ++row; ++ i = i.sibling( row, 0 ); ++ } ++ return items; ++} ++ ++Akonadi::Collection CalendarSupport::collectionFromIndex( const QModelIndex &index ) ++{ ++ return index.data( Akonadi::EntityTreeModel::CollectionRole ).value(); ++} ++ ++Akonadi::Collection::Id CalendarSupport::collectionIdFromIndex( const QModelIndex &index ) ++{ ++ return index.data( Akonadi::EntityTreeModel::CollectionIdRole ).value(); ++} ++ ++Akonadi::Collection::List CalendarSupport::collectionsFromIndexes( const QModelIndexList &indexes ) ++{ ++ Akonadi::Collection::List l; ++ Q_FOREACH( const QModelIndex &idx, indexes ) { ++ l.push_back( collectionFromIndex( idx ) ); ++ } ++ return l; ++} ++ ++QString CalendarSupport::displayName( const Akonadi::Collection &c ) ++{ ++ const Akonadi::EntityDisplayAttribute *attr = c.attribute(); ++ return ( attr && !attr->displayName().isEmpty() ) ? attr->displayName() : c.name(); ++} ++ ++QString CalendarSupport::subMimeTypeForIncidence( const KCalCore::Incidence::Ptr &incidence ) ++{ ++ return incidence->mimeType(); ++} ++ ++QList CalendarSupport::workDays( const QDate &startDate, ++ const QDate &endDate ) ++{ ++ QList result; ++ ++/* const int mask( ~( KCalPrefs::instance()->mWorkWeekMask ) ); ++ const int numDays = startDate.daysTo( endDate ) + 1; ++ ++ for ( int i = 0; i < numDays; ++i ) { ++ const QDate date = startDate.addDays( i ); ++ if ( !( mask & ( 1 << ( date.dayOfWeek() - 1 ) ) ) ) { ++ result.append( date ); ++ } ++ } ++ ++ if ( KCalPrefs::instance()->mExcludeHolidays ) { ++ // NOTE: KOGlobals, where this method comes from, used to hold a pointer to ++ // a KHolidays object. I'm not sure about how expensive it is, just ++ // creating one here. ++ const HolidayRegion holidays( KCalPrefs::instance()->mHolidays ); ++ const Holiday::List list = holidays.holidays( startDate, endDate ); ++ for ( int i = 0; i < list.count(); ++i ) { ++ const Holiday &h = list.at( i ); ++ const QString dateString = h.date().toString(); ++ if ( h.dayType() == Holiday::NonWorkday ) { ++ result.removeAll( h.date() ); ++ } ++ } ++ }*/ ++ ++ return result; ++} ++ ++QStringList CalendarSupport::holiday( const QDate &date ) ++{ ++ QStringList hdays; ++ ++/* const HolidayRegion holidays( KCalPrefs::instance()->mHolidays ); ++ const Holiday::List list = holidays.holidays( date ); ++ ++ for ( int i = 0; i < list.count(); ++i ) { ++ hdays.append( list.at( i ).text() ); ++ }*/ ++ return hdays; ++} ++ ++void CalendarSupport::sendAsICalendar(const Akonadi::Item& item, KPIMIdentities::IdentityManager* identityManager, QWidget* parentWidget) ++{ ++/* Incidence::Ptr incidence = CalendarSupport::incidence( item ); ++ ++ if ( !incidence ) { ++ KMessageBox::information( ++ parentWidget, ++ i18n( "No item selected." ), ++ i18n( "Forwarding" ), ++ "ForwardNoEventSelected" ); ++ return; ++ } ++ ++ QPointer publishdlg = new PublishDialog; ++ if ( publishdlg->exec() == QDialog::Accepted ) { ++ const QString recipients = publishdlg->addresses(); ++ if ( incidence->organizer()->isEmpty() ) { ++ incidence->setOrganizer( Person::Ptr( new Person( CalendarSupport::KCalPrefs::instance()->fullName(), ++ CalendarSupport::KCalPrefs::instance()->email() ) ) ); ++ } ++ ++ ICalFormat format; ++ const QString from = CalendarSupport::KCalPrefs::instance()->email(); ++ const bool bccMe = CalendarSupport::KCalPrefs::instance()->mBcc; ++ const QString messageText = format.createScheduleMessage( incidence, iTIPRequest ); ++ CalendarSupport::MailClient mailer; ++ if ( mailer.mailTo( ++ incidence, ++ identityManager->identityForAddress( from ), ++ from, bccMe, recipients, messageText, MailTransport::TransportManager::self()->defaultTransportName() ) ) { ++ KMessageBox::information( ++ parentWidget, ++ i18n( "The item information was successfully sent." ), ++ i18n( "Forwarding" ), ++ "IncidenceForwardSuccess" ); ++ } else { ++ KMessageBox::error( ++ parentWidget, ++ i18n( "Unable to forward the item '%1'", incidence->summary() ), ++ i18n( "Forwarding Error" ) ); ++ } ++ } ++ delete publishdlg;*/ ++} ++ ++void CalendarSupport::publishItemInformation(const Akonadi::Item& item, Calendar* calendar, QWidget* parentWidget) ++{ ++/* Incidence::Ptr incidence = CalendarSupport::incidence( item ); ++ ++ if ( !incidence ) { ++ KMessageBox::information( ++ parentWidget, ++ i18n( "No item selected." ), ++ "PublishNoEventSelected" ); ++ return; ++ } ++ ++ QPointer publishdlg = new PublishDialog(); ++ if ( incidence->attendeeCount() > 0 ) { ++ Attendee::List attendees = incidence->attendees(); ++ Attendee::List::ConstIterator it; ++ for ( it = attendees.constBegin(); it != attendees.constEnd(); ++it ) { ++ publishdlg->addAttendee( *it ); ++ } ++ } ++ if ( publishdlg->exec() == QDialog::Accepted ) { ++ Incidence::Ptr inc( incidence->clone() ); ++ inc->registerObserver( 0 ); ++ inc->clearAttendees(); ++ ++ // Send the mail ++ CalendarSupport::MailScheduler scheduler( calendar ); ++ if ( scheduler.publish( incidence, publishdlg->addresses() ) ) { ++ KMessageBox::information( ++ parentWidget, ++ i18n( "The item information was successfully sent." ), ++ i18n( "Publishing" ), ++ "IncidencePublishSuccess" ); ++ } else { ++ KMessageBox::error( ++ parentWidget, ++ i18n( "Unable to publish the item '%1'", incidence->summary() ) ); ++ } ++ } ++ delete publishdlg;*/ ++} ++ ++void CalendarSupport::scheduleiTIPMethods( KCalCore::iTIPMethod method, const Akonadi::Item& item, CalendarSupport::Calendar* calendar, QWidget* parentWidget ) ++{ ++/* Incidence::Ptr incidence = CalendarSupport::incidence( item ); ++ ++ if ( !incidence ) { ++ KMessageBox::sorry( ++ parentWidget, ++ i18n( "No item selected." ), ++ "ScheduleNoEventSelected" ); ++ return; ++ } ++ ++ if ( incidence->attendeeCount() == 0 && method != iTIPPublish ) { ++ KMessageBox::information( ++ parentWidget, ++ i18n( "The item has no attendees." ), ++ "ScheduleNoIncidences" ); ++ return; ++ } ++ ++ Incidence *inc = incidence->clone(); ++ inc->registerObserver( 0 ); ++ inc->clearAttendees(); ++ ++ // Send the mail ++ CalendarSupport::MailScheduler scheduler( calendar ); ++ if ( scheduler.performTransaction( incidence, method ) ) { ++ KMessageBox::information( ++ parentWidget, ++ i18n( "The groupware message for item '%1' " ++ "was successfully sent.\nMethod: %2", ++ incidence->summary(), ++ ScheduleMessage::methodName( method ) ), ++ i18n( "Sending Free/Busy" ), ++ "FreeBusyPublishSuccess" ); ++ } else { ++ KMessageBox::error( ++ parentWidget, ++ i18nc( "Groupware message sending failed. " ++ "%2 is request/reply/add/cancel/counter/etc.", ++ "Unable to send the item '%1'.\nMethod: %2", ++ incidence->summary(), ++ ScheduleMessage::methodName( method ) ) ); ++ }*/ ++} ++ ++void CalendarSupport::saveAttachments(const Akonadi::Item& item, QWidget* parentWidget) ++{ ++/* Incidence::Ptr incidence = CalendarSupport::incidence( item ); ++ ++ if ( !incidence ) { ++ KMessageBox::sorry( ++ parentWidget, ++ i18n( "No item selected." ), ++ "SaveAttachments" ); ++ return; ++ } ++ ++ Attachment::List attachments = incidence->attachments(); ++ ++ if ( attachments.empty() ) ++ return; ++ ++ QString targetFile, targetDir; ++ if ( attachments.count() > 1 ) { ++ // get the dir ++ targetDir = KFileDialog::getExistingDirectory( KUrl( "kfiledialog:///saveAttachment" ), ++ parentWidget, ++ i18n( "Save Attachments To" ) ); ++ if ( targetDir.isEmpty() ) { ++ return; ++ } ++ ++ // we may not get a slash-terminated url out of KFileDialog ++ if ( !targetDir.endsWith('/') ) ++ targetDir.append('/'); ++ } ++ else { ++ // only one item, get the desired filename ++ QString fileName = attachments.first()->label(); ++ if ( fileName.isEmpty() ) { ++ fileName = i18nc( "filename for an unnamed attachment", "attachment.1" ); ++ } ++ targetFile = KFileDialog::getSaveFileName( KUrl( "kfiledialog:///saveAttachment/" + fileName ), ++ QString(), ++ parentWidget, ++ i18n( "Save Attachment" ) ); ++ if ( targetFile.isEmpty() ) { ++ return; ++ } ++ ++ targetDir = QFileInfo( targetFile ).absolutePath() + "/"; ++ } ++ ++ Q_FOREACH( Attachment::Ptr attachment, attachments ) { ++ targetFile = targetDir + attachment->label(); ++ KUrl sourceUrl; ++ if ( attachment->isUri() ) { ++ sourceUrl = attachment->uri(); ++ } else { ++ sourceUrl = incidence->writeAttachmentToTempFile( attachment ); ++ } ++ // save the attachment url ++ if ( !KIO::NetAccess::file_copy( sourceUrl, KUrl( targetFile ) ) && ++ KIO::NetAccess::lastError() ) { ++ KMessageBox::error( parentWidget, KIO::NetAccess::lastErrorString() ); ++ } ++ }*/ ++ ++} +diff --git a/plasma/generic/dataengines/calendar/akonadi/utils.h b/plasma/generic/dataengines/calendar/akonadi/utils.h +new file mode 100644 +index 0000000..d923594 +--- /dev/null ++++ b/plasma/generic/dataengines/calendar/akonadi/utils.h +@@ -0,0 +1,250 @@ ++/* ++ Copyright (c) 2009, 2010 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com ++ Copyright (C) 2009 KDAB (author: Frank Osterfeld ) ++ Copyright (c) 2010 Andras Mantia ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, 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 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. ++ ++ As a special exception, permission is given to link this program ++ with any edition of Qt, and distribute the resulting executable, ++ without including the source code for Qt in the source distribution. ++*/ ++#ifndef CALENDARSUPPORT_UTILS_H ++#define CALENDARSUPPORT_UTILS_H ++ ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include ++ ++namespace KPIMIdentities { ++class IdentityManager; ++} ++ ++namespace KCalCore { ++ class CalFilter; ++} ++ ++class QAbstractItemModel; ++class QDrag; ++class QMimeData; ++ ++typedef QList QModelIndexList; ++ ++namespace CalendarSupport ++{ ++ ++class Calendar; ++ ++ /** ++ * returns the incidence from an akonadi item, or a null pointer if the item has no such payload ++ */ ++ KCalCore::Incidence::Ptr incidence( const Akonadi::Item &item ); ++ ++ /** ++ * returns the event from an akonadi item, or a null pointer if the item has no such payload ++ */ ++ KCalCore::Event::Ptr event( const Akonadi::Item &item ); ++ ++ /** ++ * returns event pointers from an akonadi item, or a null pointer if the item has no such payload ++ */ ++ KCalCore::Event::List eventsFromItems( ++ const Akonadi::Item::List &items ); ++ ++ /** ++ * returns incidence pointers from an akonadi item. ++ */ ++ KCalCore::Incidence::List incidencesFromItems( ++ const Akonadi::Item::List &items ); ++ ++ ++ /** ++ * returns the todo from an akonadi item, or a null pointer if the item has no such payload ++ */ ++ KCalCore::Todo::Ptr todo( const Akonadi::Item &item ); ++ ++ /** ++ * returns the journal from an akonadi item, or a null pointer if the item has no such payload ++ */ ++ KCalCore::Journal::Ptr journal( const Akonadi::Item &item ); ++ ++ /** ++ * returns whether an Akonadi item contains an incidence ++ */ ++ bool hasIncidence( const Akonadi::Item &item ); ++ ++ /** ++ * returns whether an Akonadi item contains an event ++ */ ++ bool hasEvent( const Akonadi::Item &item ); ++ ++ /** ++ * returns whether an Akonadi item contains a todo ++ */ ++ bool hasTodo( const Akonadi::Item &item ); ++ ++ /** ++ * returns whether an Akonadi item contains a journal ++ */ ++ bool hasJournal( const Akonadi::Item &item ); ++ ++ /** ++ * returns whether this item can be deleted ++ */ ++ bool hasDeleteRights( const Akonadi::Item &item ); ++ ++ /** ++ * returns whether this item can be changed ++ */ ++ bool hasChangeRights( const Akonadi::Item &item ); ++ ++ /** ++ * returns @p true if the URL represents an Akonadi item and has one of the given mimetypes. ++ */ ++ bool isValidIncidenceItemUrl( const KUrl &url, ++ const QStringList &supportedMimeTypes ); ++ ++ bool isValidIncidenceItemUrl( const KUrl &url ); ++ ++ /** ++ * returns @p true if the mime data object contains any of the following: ++ * ++ * * An akonadi item with a supported KCal mimetype ++ * * an iCalendar ++ * * a VCard ++ */ ++ bool canDecode( const QMimeData *mimeData ); ++ ++ QList incidenceItemUrls( const QMimeData *mimeData ); ++ ++ QList todoItemUrls( const QMimeData *mimeData ); ++ ++ bool mimeDataHasTodo( const QMimeData *mimeData ); ++ ++ KCalCore::Todo::List todos( const QMimeData *mimeData, ++ const KDateTime::Spec &timeSpec ); ++ ++ /** ++ * returns @p true if the URL represents an Akonadi item and has one of the given mimetypes. ++ */ ++ bool isValidTodoItemUrl( const KUrl &url ); ++ ++ /** ++ * creates mime data object for dragging an akonadi item containing an incidence ++ */ ++ QMimeData *createMimeData( const Akonadi::Item &item, ++ const KDateTime::Spec &timeSpec ); ++ ++ /** ++ * creates mime data object for dragging akonadi items containing an incidence ++ */ ++ QMimeData *createMimeData( const Akonadi::Item::List &items, ++ const KDateTime::Spec &timeSpec ); ++ ++#ifndef QT_NO_DRAGANDDROP ++ /** ++ * creates a drag object for dragging an akonadi item containing an incidence ++ */ ++ QDrag *createDrag( const Akonadi::Item &item, ++ const KDateTime::Spec &timeSpec, QWidget *parent ); ++ ++ /** ++ * creates a drag object for dragging akonadi items containing an incidence ++ */ ++ QDrag *createDrag( const Akonadi::Item::List &items, ++ const KDateTime::Spec &timeSpec, QWidget *parent ); ++#endif ++ /** ++ Applies a filter to a list of items containing incidences. ++ Items not containing incidences or not matching the filter are removed. ++ Helper method anologous to KCalCore::CalFilter::apply() ++ @see KCalCore::CalFilter::apply() ++ @param items the list of items to filter ++ @param filter the filter to apply to the list of items ++ @return the filtered list of items ++ */ ++ Akonadi::Item::List applyCalFilter( const Akonadi::Item::List &items, ++ const KCalCore::CalFilter *filter ); ++ ++ /** ++ Shows a modal dialog that allows to select a collection. ++ ++ @param will contain the dialogCode, QDialog::Accepted if the user pressed Ok, ++ QDialog::Rejected otherwise ++ @param parent The optional parent of the modal dialog. ++ @return The select collection or an invalid collection if ++ there was no collection selected. ++ */ ++ Akonadi::Collection selectCollection( ++ QWidget *parent, int &dialogCode, ++ const QStringList &mimeTypes, ++ const Akonadi::Collection &defaultCollection = Akonadi::Collection() ); ++ ++ Akonadi::Item itemFromIndex( const QModelIndex &index ); ++ ++ Akonadi::Item::List itemsFromModel( ++ const QAbstractItemModel *model, ++ const QModelIndex &parentIndex = QModelIndex(), ++ int start = 0, ++ int end = -1 ); ++ ++ Akonadi::Collection::List collectionsFromModel( ++ const QAbstractItemModel *model, ++ const QModelIndex &parentIndex = QModelIndex(), ++ int start = 0, ++ int end = -1 ); ++ ++ Akonadi::Collection collectionFromIndex( const QModelIndex &index ); ++ ++ Akonadi::Collection::Id collectionIdFromIndex( const QModelIndex &index ); ++ ++ Akonadi::Collection::List collectionsFromIndexes( ++ const QModelIndexList &indexes ); ++ ++ QString displayName( const Akonadi::Collection &coll ); ++ ++ QString subMimeTypeForIncidence( ++ const KCalCore::Incidence::Ptr &incidence ); ++ ++ /** ++ Returns a list containing work days between @p start and @end. ++ */ ++ QList workDays( const QDate &start, const QDate &end ); ++ ++ /** ++ Returns a list of holidays that occur at @param date. ++ */ ++ QStringList holiday( const QDate &date ); ++ ++ void sendAsICalendar( const Akonadi::Item& item, KPIMIdentities::IdentityManager *identityManager, QWidget* parentWidget = 0 ); ++ ++ void publishItemInformation( const Akonadi::Item& item, Calendar* calendar, QWidget* parentWidget = 0 ); ++ ++ void scheduleiTIPMethods( KCalCore::iTIPMethod method, const Akonadi::Item &item, Calendar* calendar, QWidget *parentWidget = 0 ); ++ ++ void saveAttachments( const Akonadi::Item& item, QWidget* parentWidget = 0 ); ++ ++} ++ ++#endif +diff --git a/plasma/generic/dataengines/calendar/calendarengine.cpp b/plasma/generic/dataengines/calendar/calendarengine.cpp +index 5b98d8a..7235f74 100644 +--- a/plasma/generic/dataengines/calendar/calendarengine.cpp ++++ b/plasma/generic/dataengines/calendar/calendarengine.cpp +@@ -31,13 +31,24 @@ + #include + #include + #include ++#include + + #ifdef AKONADI_FOUND ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "akonadi/calendar.h" ++#include "akonadi/calendarmodel.h" + #include "eventdatacontainer.h" + #endif + + CalendarEngine::CalendarEngine(QObject* parent, const QVariantList& args) +- : Plasma::DataEngine(parent) ++ : Plasma::DataEngine(parent), ++ m_calendar(0) + { + Q_UNUSED(args); + } +@@ -299,16 +310,46 @@ bool CalendarEngine::akonadiCalendarSourceRequest(const QString& key, const QStr + return false; + } + +- if (!m_calendar) { +- m_calendar = Akonadi::ETMCalendar::Ptr(new Akonadi::ETMCalendar()); +- m_calendar->setCollectionFilteringEnabled(false); +- } ++ // start akonadi etc if needed ++ initAkonadiCalendar(); + + // create the corresponding EventDataContainer + addSource(new EventDataContainer(m_calendar, request, KDateTime(start, QTime(0, 0, 0)), KDateTime(end, QTime(23, 59, 59)))); + return true; + } + +-#endif // AKONADI_FOUND ++void CalendarEngine::initAkonadiCalendar() ++{ ++ if (m_calendar != 0) { ++ // we have been initialized already ++ return; ++ } ++ ++ // ask for akonadi events ++ Akonadi::Session *session = new Akonadi::Session("PlasmaCalendarEngine", this); ++ Akonadi::ChangeRecorder* monitor = new Akonadi::ChangeRecorder(this); ++ Akonadi::ItemFetchScope scope; ++ scope.fetchFullPayload(true); ++ scope.fetchAttribute(); ++ ++ // setup what part of akonadi data we want (calendar incidences) ++ monitor->setSession(session); ++ monitor->setCollectionMonitored(Akonadi::Collection::root()); ++ monitor->fetchCollection(true); ++ monitor->setItemFetchScope(scope); ++ monitor->setMimeTypeMonitored(KCalCore::Event::eventMimeType(), true); ++ monitor->setMimeTypeMonitored(KCalCore::Todo::todoMimeType(), true); ++ monitor->setMimeTypeMonitored(KCalCore::Journal::journalMimeType(), true); ++ ++ // create the models that contain the data. they will be updated automatically from akonadi. ++ CalendarSupport::CalendarModel *calendarModel = new CalendarSupport::CalendarModel(monitor, this); ++ KDescendantsProxyModel *flatModel = new KDescendantsProxyModel(this); ++ flatModel->setSourceModel(calendarModel); ++ Akonadi::EntityMimeTypeFilterModel *mimeFilteredModel = new Akonadi::EntityMimeTypeFilterModel(this); ++ mimeFilteredModel->addMimeTypeExclusionFilter(Akonadi::Collection::mimeType()); ++ mimeFilteredModel->setSourceModel(flatModel); ++ m_calendar = new CalendarSupport::Calendar(mimeFilteredModel, mimeFilteredModel, KSystemTimeZones::local()); ++} ++#endif + + #include "calendarengine.moc" +diff --git a/plasma/generic/dataengines/calendar/calendarengine.h b/plasma/generic/dataengines/calendar/calendarengine.h +index 21ad533..18ae774 100644 +--- a/plasma/generic/dataengines/calendar/calendarengine.h ++++ b/plasma/generic/dataengines/calendar/calendarengine.h +@@ -24,9 +24,9 @@ + + #include + +-#ifdef AKONADI_FOUND +-# include +-#endif ++namespace CalendarSupport { ++ class Calendar; ++} + + namespace KHolidays + { +@@ -163,10 +163,11 @@ class CalendarEngine : public Plasma::DataEngine + /// creates EventDataContainers as needed + bool akonadiCalendarSourceRequest(const QString& key, const QStringList& args, const QString& request); + +-#ifdef AKONADI_FOUND ++ /// this will start akonadi if necessary and init m_calendarModel ++ void initAkonadiCalendar(); ++ + /// this is the representation of the root calendar itself. it contains everything (calendars, incidences) +- Akonadi::ETMCalendar::Ptr m_calendar; +-#endif ++ CalendarSupport::Calendar *m_calendar; + + /// holiday calendar + QHash m_regions; +diff --git a/plasma/generic/dataengines/calendar/eventdatacontainer.cpp b/plasma/generic/dataengines/calendar/eventdatacontainer.cpp +index 27a1d58..63db4df 100644 +--- a/plasma/generic/dataengines/calendar/eventdatacontainer.cpp ++++ b/plasma/generic/dataengines/calendar/eventdatacontainer.cpp +@@ -21,14 +21,19 @@ + + #include + ++#include + #include + #include + #include + #include + ++#include "akonadi/calendar.h" ++#include "akonadi/calendarmodel.h" ++ + using namespace Akonadi; ++using namespace CalendarSupport; + +-EventDataContainer::EventDataContainer(const Akonadi::ETMCalendar::Ptr &calendar, const QString& name, const KDateTime& start, const KDateTime& end, QObject* parent) ++EventDataContainer::EventDataContainer(CalendarSupport::Calendar* calendar, const QString& name, const KDateTime& start, const KDateTime& end, QObject* parent) + : Plasma::DataContainer(parent), + m_calendar(calendar), + m_name(name), +@@ -39,7 +44,7 @@ EventDataContainer::EventDataContainer(const Akonadi::ETMCalendar::Ptr &calendar + setObjectName(name); + + // Connect directly to the calendar for now +- connect(calendar.data(), SIGNAL(calendarChanged()), this, SLOT(updateData())); ++ connect(calendar, SIGNAL(calendarChanged()), this, SLOT(updateData())); + + // create the initial data + updateData(); +@@ -56,10 +61,14 @@ void EventDataContainer::updateData() + + void EventDataContainer::updateEventData() + { +- KCalCore::Event::List events = m_calendar->events(m_startDate.date(), m_endDate.date(), m_calendar->timeSpec()); ++ Akonadi::Item::List events = m_calendar->events(m_startDate.date(), m_endDate.date(), m_calendar->timeSpec()); ++ ++ foreach (const Akonadi::Item &item, events) { ++ Q_ASSERT(item.hasPayload()); ++ const KCalCore::Event::Ptr event = item.payload(); + +- foreach (const KCalCore::Event::Ptr &event, events) { + Plasma::DataEngine::Data eventData; ++ + populateIncidenceData(event, eventData); + + // Event specific fields +@@ -81,10 +90,14 @@ void EventDataContainer::updateTodoData() + { + QDate todoDate = m_startDate.date(); + while(todoDate <= m_endDate.date()) { +- KCalCore::Todo::List todos = m_calendar->todos(todoDate); ++ Akonadi::Item::List todos = m_calendar->todos(todoDate); ++ ++ foreach (const Akonadi::Item &item, todos) { ++ Q_ASSERT(item.hasPayload()); ++ const KCalCore::Todo::Ptr todo = item.payload(); + +- foreach (const KCalCore::Todo::Ptr &todo, todos) { + Plasma::DataEngine::Data todoData; ++ + populateIncidenceData(todo, todoData); + + QVariant var; +@@ -113,13 +126,18 @@ void EventDataContainer::updateJournalData() + { + QDate journalDate = m_startDate.date(); + while(journalDate <= m_endDate.date()) { +- KCalCore::Journal::List journals = m_calendar->journals(journalDate); ++ Akonadi::Item::List journals = m_calendar->journals(journalDate); ++ ++ foreach (const Akonadi::Item &item, journals) { ++ Q_ASSERT(item.hasPayload()); ++ const KCalCore::Journal::Ptr journal = item.payload(); + +- foreach (const KCalCore::Journal::Ptr &journal, journals) { + Plasma::DataEngine::Data journalData; ++ + populateIncidenceData(journal, journalData); + + // No Journal specific fields ++ + setData(journal->uid(), journalData); + } + +@@ -127,7 +145,7 @@ void EventDataContainer::updateJournalData() + } + } + +-void EventDataContainer::populateIncidenceData(const KCalCore::Incidence::Ptr &incidence, Plasma::DataEngine::Data &incidenceData) ++void EventDataContainer::populateIncidenceData(KCalCore::Incidence::Ptr incidence, Plasma::DataEngine::Data &incidenceData) + { + QVariant var; + incidenceData["UID"] = incidence->uid(); +diff --git a/plasma/generic/dataengines/calendar/eventdatacontainer.h b/plasma/generic/dataengines/calendar/eventdatacontainer.h +index fd3996f..b170a14 100644 +--- a/plasma/generic/dataengines/calendar/eventdatacontainer.h ++++ b/plasma/generic/dataengines/calendar/eventdatacontainer.h +@@ -21,15 +21,20 @@ + #define EVENTDATACONTAINER_H + + #include +-#include ++ + #include ++ + #include + +-class EventDataContainer : public Plasma::DataContainer ++namespace CalendarSupport { ++ class Calendar; ++} ++ ++class EventDataContainer :public Plasma::DataContainer + { + Q_OBJECT + public: +- EventDataContainer(const Akonadi::ETMCalendar::Ptr &calendar, const QString& name, const KDateTime& start, const KDateTime& end, QObject* parent = 0); ++ EventDataContainer(CalendarSupport::Calendar* calendar, const QString& name, const KDateTime& start, const KDateTime& end, QObject* parent = 0); + + public Q_SLOTS: + // update the list of incidents +@@ -39,9 +44,9 @@ private: + void updateEventData(); + void updateTodoData(); + void updateJournalData(); +- void populateIncidenceData(const KCalCore::Incidence::Ptr &incidence, Plasma::DataEngine::Data &incidenceData); ++ void populateIncidenceData(KCalCore::Incidence::Ptr incidence, Plasma::DataEngine::Data &incidenceData); + +- Akonadi::ETMCalendar::Ptr m_calendar; ++ CalendarSupport::Calendar *m_calendar; + QString m_name; + KDateTime m_startDate; + KDateTime m_endDate; diff --git a/SOURCES/kde-workspace-4.11.0-backlight_actual_brightness.patch b/SOURCES/kde-workspace-4.11.0-backlight_actual_brightness.patch new file mode 100644 index 0000000..eb375c8 --- /dev/null +++ b/SOURCES/kde-workspace-4.11.0-backlight_actual_brightness.patch @@ -0,0 +1,12 @@ +diff -up kde-workspace-4.11.0/powerdevil/daemon/backends/upower/backlighthelper.cpp.actual_brightness kde-workspace-4.11.0/powerdevil/daemon/backends/upower/backlighthelper.cpp +--- kde-workspace-4.11.0/powerdevil/daemon/backends/upower/backlighthelper.cpp.actual_brightness 2013-07-09 17:44:32.000000000 -0500 ++++ kde-workspace-4.11.0/powerdevil/daemon/backends/upower/backlighthelper.cpp 2013-08-21 08:07:27.207287869 -0500 +@@ -252,7 +252,7 @@ ActionReply BacklightHelper::brightness( + return reply; + } + #else +- QFile file(m_dirname + "/brightness"); ++ QFile file(m_dirname + "/actual_brightness"); + if (!file.open(QIODevice::ReadOnly)) { + reply = ActionReply::HelperErrorReply; + reply.setErrorCode(file.error()); diff --git a/SOURCES/kde-workspace-4.11.1-kdm-logind-multiseat.patch b/SOURCES/kde-workspace-4.11.1-kdm-logind-multiseat.patch new file mode 100644 index 0000000..99a259e --- /dev/null +++ b/SOURCES/kde-workspace-4.11.1-kdm-logind-multiseat.patch @@ -0,0 +1,405 @@ +diff -up kde-workspace-4.10.90/CMakeLists.txt.kdm_logind kde-workspace-4.10.90/CMakeLists.txt +--- kde-workspace-4.10.90/CMakeLists.txt.kdm_logind 2013-06-27 16:27:30.199895076 -0500 ++++ kde-workspace-4.10.90/CMakeLists.txt 2013-06-27 16:30:25.167008304 -0500 +@@ -128,6 +128,13 @@ if(Q_WS_X11) + endif() + endif(Q_WS_X11) + ++macro_optional_find_package(Systemd) ++set_package_properties(Systemd PROPERTIES DESCRIPTION "Init and service manager for Linux" ++ URL "http://www.freedesktop.org/wiki/Software/systemd" ++ TYPE OPTIONAL ++ PURPOSE "Provides automatic multi-seat, session and power management features" ++ ) ++ + macro_optional_find_package(GLIB2 2.0) + set_package_properties(GLIB2 PROPERTIES DESCRIPTION "Low-level core library for data structure handling, portability wrappers, etc." + URL "http://www.gtk.org" +diff -up kde-workspace-4.10.90/cmake/modules/CMakeLists.txt.kdm_logind kde-workspace-4.10.90/cmake/modules/CMakeLists.txt +--- kde-workspace-4.10.90/cmake/modules/CMakeLists.txt.kdm_logind 2013-06-10 13:51:11.000000000 -0500 ++++ kde-workspace-4.10.90/cmake/modules/CMakeLists.txt 2013-06-27 16:27:30.199895076 -0500 +@@ -8,6 +8,7 @@ set(cmakeFiles FindCkConnector.cmake + FindOpenGLES.cmake + FindPAM.cmake + FindSensors.cmake ++ FindSystemd.cmake + PkgConfigGetVar.cmake + UnixAuth.cmake ) + +diff -up kde-workspace-4.10.90/cmake/modules/FindSystemd.cmake.kdm_logind kde-workspace-4.10.90/cmake/modules/FindSystemd.cmake +--- kde-workspace-4.10.90/cmake/modules/FindSystemd.cmake.kdm_logind 2013-06-27 16:27:30.200895065 -0500 ++++ kde-workspace-4.10.90/cmake/modules/FindSystemd.cmake 2013-06-27 16:27:30.200895065 -0500 +@@ -0,0 +1,39 @@ ++# Finds systemd and its libraries ++# Not a huge module but sufficient for now ++# Uses the same semantics as pkg_check_modules, i.e. ${LIB}{_FOUND,_INCLUDE_DIR,_LIBRARIES} ++# where ${LIB} can be one of the following: ++# LIBSYSTEMD_JOURNAL, SYSTEMD, LIBSYSTEMD_DAEMON, LIBSYSTEMD_LOGIN, LIBSYSTEMD_ID128 ++# ++# Copyright: Red Hat, Inc. 2013 ++# Author: Martin Briza ++# ++# Distributed under the BSD license. See COPYING-CMAKE-SCRIPTS for details. ++ ++#defining any of these disables systemd support ++if (NOT LIBSYSTEMD_JOURNAL_FOUND AND ++ NOT SYSTEMD_FOUND AND ++ NOT LIBSYSTEMD_DAEMON_FOUND AND ++ NOT LIBSYSTEMD_LOGIN_FOUND AND ++ NOT LIBSYSTEMD_ID128_FOUND) ++find_package(PkgConfig) ++if (PKG_CONFIG_FOUND) ++ pkg_check_modules(LIBSYSTEMD_JOURNAL QUIET "libsystemd-journal") ++ pkg_check_modules(SYSTEMD QUIET "systemd") ++ pkg_check_modules(LIBSYSTEMD_DAEMON QUIET "libsystemd-daemon") ++ pkg_check_modules(LIBSYSTEMD_LOGIN QUIET "libsystemd-login") ++ pkg_check_modules(LIBSYSTEMD_ID128 QUIET "libsystemd-id128") ++endif (PKG_CONFIG_FOUND) ++ ++if (SYSTEMD_FOUND) ++ message(STATUS "Found systemd") ++endif(SYSTEMD_FOUND) ++ ++mark_as_advanced(LIBSYSTEMD_JOURNAL_FOUND SYSTEMD_FOUND LIBSYSTEMD_DAEMON_FOUND LIBSYSTEMD_LOGIN_FOUND LIBSYSTEMD_ID128_FOUND) ++mark_as_advanced(LIBSYSTEMD_JOURNAL_INCLUDE_DIR SYSTEMD_INCLUDE_DIR LIBSYSTEMD_DAEMON_INCLUDE_DIR LIBSYSTEMD_LOGIN_INCLUDE_DIR LIBSYSTEMD_ID128_INCLUDE_DIR) ++mark_as_advanced(LIBSYSTEMD_JOURNAL_LIBRARIES SYSTEMD_LIBRARIES LIBSYSTEMD_DAEMON_LIBRARIES LIBSYSTEMD_LOGIN_LIBRARIES LIBSYSTEMD_ID128_LIBRARIES) ++ ++endif (NOT LIBSYSTEMD_JOURNAL_FOUND AND ++ NOT SYSTEMD_FOUND AND ++ NOT LIBSYSTEMD_DAEMON_FOUND AND ++ NOT LIBSYSTEMD_LOGIN_FOUND AND ++ NOT LIBSYSTEMD_ID128_FOUND) +diff -up kde-workspace-4.10.90/kdm/backend/CMakeLists.txt.kdm_logind kde-workspace-4.10.90/kdm/backend/CMakeLists.txt +--- kde-workspace-4.10.90/kdm/backend/CMakeLists.txt.kdm_logind 2013-05-28 13:38:21.000000000 -0500 ++++ kde-workspace-4.10.90/kdm/backend/CMakeLists.txt 2013-06-27 16:27:30.201895054 -0500 +@@ -45,6 +45,10 @@ if (SECURE_RPC) + rpcauth.c + ) + endif (SECURE_RPC) ++if(LIBSYSTEMD_LOGIN_FOUND AND LIBSYSTEMD_DAEMON_FOUND) ++ add_definitions( -DWITH_SYSTEMD=1 ) ++ set(KDM_SYSTEMD_LIBRARIES ${LIBSYSTEMD_DAEMON_LIBRARIES} ${LIBSYSTEMD_LOGIN_LIBRARIES} ) ++endif(LIBSYSTEMD_LOGIN_FOUND AND LIBSYSTEMD_DAEMON_FOUND) + macro_add_file_dependencies(dm.h ${confci}) + macro_add_file_dependencies(error.c ${CMAKE_CURRENT_SOURCE_DIR}/printf.c) + kde4_add_executable(kdm NOGUI ${kdm_SRCS}) +@@ -61,6 +65,7 @@ target_link_libraries( kdm + ${NSL_LIBRARIES} + ${RESOLV_LIBRARIES} + ${SOCKET_LIBRARIES} ++ ${KDM_SYSTEMD_LIBRARIES} + ) + if (CKCONNECTOR_FOUND) + include_directories(${CKCONNECTOR_INCLUDE_DIR} ${DBUS_INCLUDE_DIR} ${DBUS_ARCH_INCLUDE_DIR}) +diff -up kde-workspace-4.10.90/kdm/backend/dm.c.kdm_logind kde-workspace-4.10.90/kdm/backend/dm.c +--- kde-workspace-4.10.90/kdm/backend/dm.c.kdm_logind 2013-06-27 16:27:30.184895241 -0500 ++++ kde-workspace-4.10.90/kdm/backend/dm.c 2013-06-27 16:27:30.201895054 -0500 +@@ -50,6 +50,23 @@ from the copyright holder. + # include + #endif + ++#ifdef WITH_SYSTEMD ++# include ++# include ++ ++#define SYSTEMD_FAILURE_LIMIT 25 ++ ++ static int systemdMonitorInit(void); ++ static void systemdMonitorDeinit(); ++ static int systemdStartDisplay(char *); ++ static void systemdCheckAdded(char **); ++ static void systemdCheckRemoved(char **); ++ static void systemdHandleChange(); ++ ++ sd_login_monitor *systemd_monitor = NULL; ++ int systemd_monitor_fd = -1; ++#endif ++ + static void sigHandler(int n); + static int scanConfigs(int force); + static void startDisplay(struct display *d); +@@ -308,7 +325,16 @@ main(int argc, char **argv) + #ifdef XDMCP + updateListenSockets(); + #endif ++ ++#ifdef WITH_SYSTEMD ++ if (systemdMonitorInit()) ++ systemdHandleChange(); ++#endif ++ + mainLoop(); ++#ifdef WITH_SYSTEMD ++ systemdMonitorDeinit(); ++#endif + closeCtrl(0); + if (sdRec.how) { + int pid; +@@ -1280,6 +1306,14 @@ mainLoop(void) + } + continue; + } ++ logError("STARTING"); ++#ifdef WITH_SYSTEMD ++ if (systemd_monitor_fd >= 0 && FD_ISSET(systemd_monitor_fd, &reads)) { ++ systemdHandleChange(); ++ sd_login_monitor_flush(systemd_monitor); ++ continue; ++ } ++#endif + #ifdef XDMCP + if (processListenSockets(&reads)) + continue; +@@ -1304,6 +1338,151 @@ mainLoop(void) + } + } + ++#ifdef WITH_SYSTEMD ++static int ++systemdMonitorInit(void) ++{ ++ if (sd_booted() <= 0) { ++ logError("Didn't boot with systemd, automatic multiseat won't be enabled\n"); ++ return False; ++ } ++ ++ int check = sd_login_monitor_new("seat", &systemd_monitor); ++ if (check < 0) { ++ logError("Can't get systemd monitor: %d, automatic multiseat won't be enabled\n", check); ++ return False; ++ } ++ ++ systemd_monitor_fd = sd_login_monitor_get_fd(systemd_monitor); ++ if (systemd_monitor_fd < 0) { ++ logError("Can't retrieve file descriptor from the systemd monitor: %d, automatic multiseat won't be enabled\n", systemd_monitor_fd); ++ sd_login_monitor_unref(systemd_monitor); ++ systemd_monitor_fd = -1; ++ return False; ++ } ++ ++ registerInput(systemd_monitor_fd); ++ return True; ++} ++ ++static void ++systemdMonitorDeinit(void) ++{ ++ if (systemd_monitor) { ++ sd_login_monitor_unref(systemd_monitor); ++ } ++ systemd_monitor_fd = -1; ++} ++ ++static int ++systemdStartDisplay(char *seat) ++{ ++ struct display *link = NULL; ++ for (link = displays; link; link = link-> next) { ++ if (link->status == reserve) ++ break; ++ } ++ if (!link) { ++ logError("There's not enough reserve displays for all your seats/sessions"); ++ return False; ++ } ++ if (!strDup((&link->systemd_seat), seat)) { ++ return False; ++ } ++#ifdef HAVE_VTS ++ link->serverVT = 0; ++#endif ++ link->status = notRunning; ++ link->stillThere = True; ++ link->authorize = True; ++ link->displayType = dLocal | dPermanent; ++ link->reqSrvVT = -1; ++ link->serverPid = -1; ++ return True; ++} ++ ++static void ++systemdCheckAdded(char **seat_names) ++{ ++ char **iter_name; ++ struct display *link; ++ for (iter_name = seat_names; *iter_name; iter_name++) { ++ if (strcmp(*iter_name, "seat0") == 0) ++ continue; /* ignore the main seat */ ++ int can_graphical = sd_seat_can_graphical(*iter_name); ++ for (link = displays; link; link = link->next) { ++ if (!link->systemd_seat) ++ continue; ++ /* see if the can_graphical property didn't change */ ++ if (strcmp(*iter_name, link->systemd_seat) == 0) { ++ if (!can_graphical) { ++ free(link->systemd_seat); ++ link->systemd_seat = NULL; ++ rStopDisplay(link, DS_RESERVE); ++ } ++ break; ++ } ++ } ++ /* the display wasn't found */ ++ if (!link) { ++ if (can_graphical) { ++ /* if starting the display failed, skip this round until the next change */ ++ if (!systemdStartDisplay(*iter_name)) ++ break; ++ } ++ } ++ } ++} ++ ++static void ++systemdCheckRemoved(char **seat_names) ++{ ++ char **iter_name; ++ struct display *link; ++ for (link = displays; link; link = link->next) { ++ for (iter_name = seat_names; *iter_name; iter_name++) { ++ if (strcmp(*iter_name, "seat0") == 0) ++ continue; /* ignore the main seat */ ++ if (link->systemd_seat && strcmp(*iter_name, link->systemd_seat) == 0) ++ break; ++ } ++ if (!(*iter_name) && link->systemd_seat) { /* was not found, stop this one */ ++ free(link->systemd_seat); ++ link->systemd_seat = NULL; ++ rStopDisplay(link, DS_RESERVE); ++ } ++ } ++} ++ ++static void ++systemdHandleChange(void) ++{ ++ static int failures = 0; ++ char **seat_names; ++ char **iter_name; ++ int check; ++ if ((check = sd_get_seats(&seat_names)) < 0) { ++ logError("Can't obtain systemd seats, error %d\n", -check); ++ failures++; ++ if (failures >= SYSTEMD_FAILURE_LIMIT) { ++ logError("%u failed calls to sd_get_seats, disabling systemd multi-seat support\n", SYSTEMD_FAILURE_LIMIT); ++ systemdMonitorDeinit(); ++ } ++ return; ++ } ++ ++ if (!check) ++ return; ++ ++ systemdCheckAdded(seat_names); ++ systemdCheckRemoved(seat_names); ++ ++ for (iter_name = seat_names; *iter_name; iter_name++) ++ free(*iter_name); ++ free(seat_names); ++} ++#endif ++ + static void + checkDisplayStatus(struct display *d) + { +diff -up kde-workspace-4.10.90/kdm/backend/dm.h.kdm_logind kde-workspace-4.10.90/kdm/backend/dm.h +--- kde-workspace-4.10.90/kdm/backend/dm.h.kdm_logind 2013-06-27 16:27:30.184895241 -0500 ++++ kde-workspace-4.10.90/kdm/backend/dm.h 2013-06-27 16:27:30.201895054 -0500 +@@ -306,6 +306,9 @@ struct display { + char *greeterAuthFile; /* file to store authorization for greeter in */ + + int plymouth_vt; /* Plymouth's VT nr */ ++#ifdef WITH_SYSTEMD ++ char *systemd_seat; ++#endif + }; + + #define d_location 1 +diff -up kde-workspace-4.10.90/kdm/backend/server.c.kdm_logind kde-workspace-4.10.90/kdm/backend/server.c +--- kde-workspace-4.10.90/kdm/backend/server.c.kdm_logind 2013-06-27 16:27:30.184895241 -0500 ++++ kde-workspace-4.10.90/kdm/backend/server.c 2013-06-27 16:27:30.201895054 -0500 +@@ -43,6 +43,7 @@ from the copyright holder. + #include + #include + ++#define SYSTEMD_X_WRAPPER "/lib/systemd/systemd-multi-seat-x" + + struct display *startingServer; + time_t serverTimeout = TO_INF; +@@ -55,9 +56,18 @@ prepareServerArgv(struct display *d, con + char vtstr[8]; + #endif + +- if (!(argv = parseArgs(0, d->serverCmd)) || +- !(argv = addStrArr(argv, d->name, -1))) ++#if WITH_SYSTEMD ++ FILE *tmpFile = NULL; ++ if ((tmpFile = fopen(SYSTEMD_X_WRAPPER, "rb")) != NULL && fclose(tmpFile) == 0) { ++ if (!(argv = parseArgs(0, SYSTEMD_X_WRAPPER)) || !(argv = addStrArr(argv, d->name, -1))) { ++ exit(47); ++ } ++ } ++ else ++#endif ++ if (!(argv = parseArgs(0, d->serverCmd)) || !(argv = addStrArr(argv, d->name, -1))) { + exit(47); ++ } + #ifdef HAVE_VTS + if (d->serverVT && + !(argv = addStrArr(argv, vtstr, +@@ -70,6 +80,25 @@ prepareServerArgv(struct display *d, con + if (!changeUser(d->serverUID, d->authFile)) + exit(47); + ++#ifdef WITH_SYSTEMD ++ if (d->systemd_seat) { ++ if (!(argv = parseArgs(argv, "-seat"))) ++ exit(47); ++ if (!(argv = parseArgs(argv, d->systemd_seat))) ++ exit(47); ++ if (!(argv = parseArgs(argv, "-layout"))) ++ exit(47); ++ if (!(argv = parseArgs(argv, d->systemd_seat))) ++ exit(47); ++ } ++ else { ++ if (!(argv = parseArgs(argv, "-seat"))) ++ exit(47); ++ if (!(argv = parseArgs(argv, "seat0"))) ++ exit(47); ++ } ++#endif ++ + return argv; + } + +--- kde-workspace-4.10.2/kdm/backend/client.c.kdm_logind ++++ kde-workspace-4.10.2/kdm/backend/client.c +@@ -1460,6 +1460,14 @@ startClient(volatile int *pid) + #endif + userEnviron = inheritEnv(env, envvars); + env = systemEnv(0, curuser); ++#ifdef WITH_SYSTEMD ++ if (td->systemd_seat) { ++ char *envbuf; ++ ASPrintf(&envbuf, "XDG_SEAT=%s", td->systemd_seat); ++ pam_putenv(pamh, envbuf); ++ env = setEnv(env, "XDG_SEAT", td->systemd_seat); ++ } ++#endif + systemEnviron = setEnv(env, "HOME", p->pw_dir); + debug("user environment:\n%[|''>'\n's" + "system environment:\n%[|''>'\n's" +--- kde-workspace-4.10.2/kdm/backend/session.c.kdm_logind ++++ kde-workspace-4.10.2/kdm/backend/session.c +@@ -437,6 +437,10 @@ openGreeter() + + grttalk.pipe = &grtproc.pipe; + env = systemEnv(dupEnv(), 0); ++#ifdef WITH_SYSTEMD ++ if (td->systemd_seat) ++ env = setEnv(env, "XDG_SEAT", td->systemd_seat); ++#endif + if (gOpen(&grtproc, (char **)0, "_greet", env, name, + greeterUID, td->greeterAuthFile, &td->gpipe)) + sessionExit(EX_UNMANAGE_DPY); diff --git a/SOURCES/kde-workspace-4.11.1-kdm_plymouth081.patch b/SOURCES/kde-workspace-4.11.1-kdm_plymouth081.patch new file mode 100644 index 0000000..3899bbb --- /dev/null +++ b/SOURCES/kde-workspace-4.11.1-kdm_plymouth081.patch @@ -0,0 +1,311 @@ +diff --git a/kdm/backend/dm.c b/kdm/backend/dm.c +index e0f1366..5a5f8a7 100644 +--- a/kdm/backend/dm.c ++++ b/kdm/backend/dm.c +@@ -1347,54 +1347,207 @@ getBusyVTs(void) + return activeVTs; + } + ++static int ++get_active_vt (void) ++{ ++ int console_fd; ++ struct vt_stat console_state = { 0 }; ++ console_fd = open ("/dev/tty0", O_RDONLY | O_NOCTTY); ++ if (console_fd < 0) { ++ return 0; ++ } ++ ioctl (console_fd, VT_GETSTATE, &console_state); ++ ++ close (console_fd); ++ return console_state.v_active; ++} ++ ++static int ++plymouth_is_running (void) ++{ ++ static int running = -1; ++ if (running == 0) ++ return 0; ++ ++ int status; ++ status = system ("/usr/bin/plymouth --ping"); ++ ++ running = WIFEXITED (status) && WEXITSTATUS (status) == 0; ++ logWarn ("plymouth is %srunning\n", running?"":"NOT "); ++ return running; ++} ++ ++static int ++plymouth_has_active_vt (void) ++{ ++ int status; ++ status = system ("/usr/bin/plymouth --has-active-vt"); ++ ++ return WIFEXITED (status) && WEXITSTATUS (status) == 0; ++} ++ ++static int ++plymouth_prepare_for_transition (void) ++{ ++ int status; ++ status = system ("/usr/bin/plymouth deactivate"); ++ ++ return WIFEXITED (status) && WEXITSTATUS (status) == 0; ++} ++ ++int ++plymouth_quit_with_transition (void) ++{ ++ int status; ++ status = system ("/usr/bin/plymouth --wait quit --retain-splash"); ++ ++ return WIFEXITED (status) && WEXITSTATUS (status) == 0; ++} ++ ++int ++plymouth_quit_without_transition (void) ++{ ++ int status; ++ status = system ("/usr/bin/plymouth --wait quit"); ++ ++ return WIFEXITED (status) && WEXITSTATUS (status) == 0; ++} ++ ++static int ++triggered_to_force_display_on_active_vt (void) ++{ ++ int should_force_display_on_active_vt; ++ should_force_display_on_active_vt=open("/var/spool/gdm/force-display-on-active-vt", O_RDONLY); ++ if ( should_force_display_on_active_vt >= 0 ) ++ close(should_force_display_on_active_vt); ++ unlink("/var/spool/gdm/force-display-on-active-vt"); ++ return should_force_display_on_active_vt; ++} ++ + static void + allocateVT(struct display *d) + { + struct display *cd; +- int i, tvt, volun; ++ int i, tvt; + + if ((d->displayType & d_location) == dLocal && + d->status == notRunning && !d->serverVT && d->reqSrvVT >= 0) + { ++ /* Try to find the correct VT. ++ * If ServerVT is specified in the config, use it (if the admin used the ++ * same VT for multiple display, it is his/her own fault, no checks done). ++ * Otherwise, walk the list of specified VTs. Positive numbers are used ++ * even if the VT is already in use by a tty. Negative numbers and ++ * unspecified numbers (up to #15) are used if not already in use. ++ * VTs already in use (cd->serverVT) or requested (cd->reqSrvVT) ++ * by any display are skipped. ++ */ ++ ++ /* some special handling is needed for Plymouth: ++ * if no VT is requested, use the active VT from plymouth for the first ++ * started display. ++ * If the display takes over the VT from plymouth, deactivate plymouth ++ */ ++ ++ char allowedVTs[16] = { 0 }; + if (d->reqSrvVT && d->reqSrvVT < 16) { +- d->serverVT = d->reqSrvVT; ++ allowedVTs[d->reqSrvVT] = 1; + } else { +- for (i = tvt = 0;;) { +- if (serverVTs[i]) { +- tvt = atoi(serverVTs[i++]); +- volun = False; +- if (tvt < 0) { +- tvt = -tvt; +- volun = True; +- } +- if (!tvt || tvt >= 16) +- continue; +- } else { +- if (++tvt >= 16) +- break; +- volun = True; ++ for (i = 0; serverVTs[i]; i++) { ++ tvt = atoi(serverVTs[i]); ++ if ((tvt >= 0) && (tvt < 16)) { ++ allowedVTs[tvt] = 1; ++ } else if (tvt > -16) { ++ allowedVTs[-tvt] = 2; + } +- for (cd = displays; cd; cd = cd->next) { +- if (cd->reqSrvVT == tvt && /* protect from lusers */ +- (cd->status != zombie || cd->zstatus != DS_REMOVE)) +- goto next; +- if (cd->serverVT == tvt) { +- if (cd->status != zombie || cd->zstatus == DS_REMOTE) +- goto next; +- if (!cd->follower) { +- d->serverVT = -1; +- cd->follower = d; +- return; +- } +- } ++ } ++ ++ for (tvt = 15; allowedVTs[tvt] == 0; tvt--) { ++ allowedVTs[tvt] = 2; ++ } ++ ++ for (cd = displays; cd; cd = cd->next) { ++ if (cd->status != zombie) { ++ if (cd->reqSrvVT >= 0) allowedVTs[cd->reqSrvVT] = 0; ++ if (cd->serverVT >= 0) allowedVTs[cd->serverVT] = 0; ++ } else if (cd->zstatus == DS_REMOTE) { ++ /* dying, but will spawn new server for remote login */ ++ if (cd->serverVT >= 0) allowedVTs[cd->serverVT] = 0; ++ } else if (cd->zstatus != DS_REMOVE) { ++ /* dying, but will be restarted or reserved */ ++ if (cd->reqSrvVT >= 0) allowedVTs[cd->reqSrvVT] = 0; + } +- if (!volun || !((1 << tvt) & getBusyVTs())) { +- d->serverVT = tvt; ++ } ++ } ++ ++ /* check for plymouth using newer methods */ ++ d->plymouth_vt = -1; ++ if (plymouth_is_running ()) { ++ if (plymouth_has_active_vt ()) { ++ int vt = get_active_vt (); ++ if (allowedVTs[vt]) { ++ logWarn ("plymouth is active on VT %d, reusing for %s\n", vt, d->name); ++ d->serverVT = vt; ++ d->plymouth_vt = vt; + return; + } +- next: ; ++ } ++ /* fallback to old/deprecated method */ ++ } else if ( triggered_to_force_display_on_active_vt() >= 0 ) { ++ int vt = get_active_vt (); ++ if (allowedVTs[vt]) { ++ d->serverVT = vt; ++ return; ++ } ++ } ++ ++ for (tvt = 0; tvt < 16; tvt++) { ++ if ((allowedVTs[tvt] == 1) || ++ ((allowedVTs[tvt] == 2) && !((1 << tvt) & getBusyVTs()))) { ++ d->serverVT = tvt; ++ return; + } + } ++ ++ for (cd = displays; cd; cd = cd->next) { ++ if ((cd->status == zombie) && (cd->zstatus != DS_REMOTE) && ++ (cd->follower == 0) && (cd->reqSrvVT != cd->serverVT)) { ++ /* removed; or restarted/reserved on any VT */ ++ d->serverVT = -1; ++ cd->follower = d; ++ return; ++ } ++ } ++ } ++} ++ ++static void ++replacePlymouth(void) ++{ ++ struct display *cd; ++ ++ /* if one display reuses plymouth' VT, plymouth is stopped in the ++ * startServerSuccess/Failed callback (see server.c). In any other ++ * case plymouth is stopped now. ++ */ ++ for (cd = displays; cd; cd = cd->next) { ++ if ((cd->serverVT > 0) && (cd->serverVT == cd->plymouth_vt)) { ++ if (cd->status == notRunning) { ++ /* tell plymouth to quit when server has started */ ++ logWarn ("plymouth should quit after server startup\n"); ++ plymouth_prepare_for_transition (); ++ kickDisplay(cd); ++ return; ++ } else if (cd->status == running) { ++ /* replacing server is starting up, do nothing */ ++ return; ++ } ++ } ++ } ++ ++ if ( plymouth_is_running ()) { ++ plymouth_prepare_for_transition (); ++ plymouth_quit_without_transition (); + } + } + #endif +@@ -1407,6 +1560,7 @@ startDisplays(void) + #ifdef HAVE_VTS + activeVTs = -1; + forEachDisplayRev(allocateVT); ++ replacePlymouth(); + #endif + forEachDisplay(kickDisplay); + } +diff --git a/kdm/backend/dm.h b/kdm/backend/dm.h +index 64e106b..930af0e 100644 +--- a/kdm/backend/dm.h ++++ b/kdm/backend/dm.h +@@ -304,6 +304,8 @@ struct display { + int authNum; /* number of authorizations */ + char *authFile; /* file to store authorization in */ + char *greeterAuthFile; /* file to store authorization for greeter in */ ++ ++ int plymouth_vt; /* Plymouth's VT nr */ + }; + + #define d_location 1 +@@ -428,6 +430,9 @@ int anyDisplaysLeft(void); + void forEachDisplay(void (*f)(struct display *)); + #ifdef HAVE_VTS + void forEachDisplayRev(void (*f)(struct display *)); ++/* function for plymouth */ ++int plymouth_quit_with_transition (void); ++int plymouth_quit_without_transition (void); + #endif + void removeDisplay(struct display *old); + struct display +diff --git a/kdm/backend/server.c b/kdm/backend/server.c +index d8dd6f3..8b4708e 100644 +--- a/kdm/backend/server.c ++++ b/kdm/backend/server.c +@@ -80,6 +80,7 @@ startServerOnce(void) + char **argv; + + debug("startServerOnce for %s, try %d\n", d->name, ++d->startTries); ++ + d->serverStatus = starting; + switch (Fork(&d->serverPid)) { + case 0: +@@ -137,6 +138,12 @@ startServerSuccess() + struct display *d = startingServer; + d->serverStatus = ignore; + serverTimeout = TO_INF; ++ if ((d->serverVT > 0) && (d->serverVT == d->plymouth_vt)) { ++ int plymouth_running; ++ logWarn ("Quitting Plymouth with transition\n" ); ++ plymouth_running = !plymouth_quit_with_transition (); ++ logWarn ("Is Plymouth still running? %s\n", plymouth_running ? "yes" : "no"); ++ } + debug("X server ready, starting session\n"); + startDisplayP2(d); + } +@@ -154,6 +161,10 @@ startServerFailed() + startingServer = 0; + logError("X server for display %s cannot be started," + " session disabled\n", d->name); ++ if ((d->serverVT > 0) && (d->serverVT == d->plymouth_vt)) { ++ logWarn ("Quitting Plymouth without transition\n" ); ++ plymouth_quit_without_transition (); ++ } + stopDisplay(d); + } + } diff --git a/SOURCES/kde-workspace-4.11.16-colorschemes-kde4.patch b/SOURCES/kde-workspace-4.11.16-colorschemes-kde4.patch new file mode 100644 index 0000000..47b991f --- /dev/null +++ b/SOURCES/kde-workspace-4.11.16-colorschemes-kde4.patch @@ -0,0 +1,33 @@ +diff -ur kde-workspace-4.11.16/kcontrol/colors/CMakeLists.txt kde-workspace-4.11.16-colorschemes-kde4/kcontrol/colors/CMakeLists.txt +--- kde-workspace-4.11.16/kcontrol/colors/CMakeLists.txt 2015-01-29 19:49:17.000000000 +0100 ++++ kde-workspace-4.11.16-colorschemes-kde4/kcontrol/colors/CMakeLists.txt 2015-03-07 02:36:25.000000000 +0100 +@@ -9,7 +9,7 @@ + + install(TARGETS kcm_colors DESTINATION ${PLUGIN_INSTALL_DIR}) + install( FILES colors.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) +-install( FILES colorschemes.knsrc DESTINATION ${CONFIG_INSTALL_DIR} ) ++install( FILES colorschemes.knsrc DESTINATION ${CONFIG_INSTALL_DIR} RENAME "colorschemes-kde4.knsrc" ) + + # built-in color schemes + FILE(GLOB schemefiles schemes/*.colors) +diff -ur kde-workspace-4.11.16/kcontrol/colors/colorscm.cpp kde-workspace-4.11.16-colorschemes-kde4/kcontrol/colors/colorscm.cpp +--- kde-workspace-4.11.16/kcontrol/colors/colorscm.cpp 2015-01-29 19:49:17.000000000 +0100 ++++ kde-workspace-4.11.16-colorschemes-kde4/kcontrol/colors/colorscm.cpp 2015-03-07 02:36:54.000000000 +0100 +@@ -397,7 +397,7 @@ + + void KColorCm::on_schemeKnsButton_clicked() + { +- KNS3::DownloadDialog dialog("colorschemes.knsrc", this); ++ KNS3::DownloadDialog dialog("colorschemes-kde4.knsrc", this); + dialog.exec(); + if ( ! dialog.changedEntries().isEmpty() ) + { +@@ -428,7 +428,7 @@ + } + + // upload +- KNS3::UploadDialog dialog("colorschemes.knsrc", this); ++ KNS3::UploadDialog dialog("colorschemes-kde4.knsrc", this); + dialog.setUploadFile(KUrl(path) ); + dialog.exec(); + } diff --git a/SOURCES/kde-workspace-4.11.19-coverity-scan-fixes.patch b/SOURCES/kde-workspace-4.11.19-coverity-scan-fixes.patch new file mode 100644 index 0000000..c585cd4 --- /dev/null +++ b/SOURCES/kde-workspace-4.11.19-coverity-scan-fixes.patch @@ -0,0 +1,74 @@ +diff --git a/kwin/clients/aurorae/src/aurorae.cpp b/kwin/clients/aurorae/src/aurorae.cpp +index a9487c3..6f63f6e 100644 +--- a/kwin/clients/aurorae/src/aurorae.cpp ++++ b/kwin/clients/aurorae/src/aurorae.cpp +@@ -435,13 +435,13 @@ KDecorationDefines::Position AuroraeClient::mousePosition(const QPoint &point) c + } + if (point.x() >= (m_view->width() - borderRight - paddingRight)) { + pos |= PositionRight; +- } else if (point.x() <= borderLeft + paddingLeft) { ++ } else if (point.x() <= (borderLeft + paddingLeft)) { + pos |= PositionLeft; + } + +- if (point.y() >= m_view->height() - borderBottom - paddingBottom) { ++ if (point.y() >= (m_view->height() - borderBottom - paddingBottom)) { + pos |= PositionBottom; +- } else if (point.y() <= borderTop + paddingTop ) { ++ } else if (point.y() <= (borderTop + paddingTop)) { + pos |= PositionTop; + } + +diff --git a/kwin/clients/oxygen/config/oxygenexceptionlistwidget.cpp b/kwin/clients/oxygen/config/oxygenexceptionlistwidget.cpp +index 3710ba2..8a11d07 100644 +--- a/kwin/clients/oxygen/config/oxygenexceptionlistwidget.cpp ++++ b/kwin/clients/oxygen/config/oxygenexceptionlistwidget.cpp +@@ -86,8 +86,8 @@ namespace Oxygen + //__________________________________________________________ + ConfigurationList ExceptionListWidget::exceptions( void ) + { +- return model().get(); + setChanged( false ); ++ return model().get(); + } + + //__________________________________________________________ +diff --git a/plasma/generic/applets/systemtray/protocols/fdo/fdographicswidget.cpp b/plasma/generic/applets/systemtray/protocols/fdo/fdographicswidget.cpp +index 80fbed3..7b8f8ed 100644 +--- a/plasma/generic/applets/systemtray/protocols/fdo/fdographicswidget.cpp ++++ b/plasma/generic/applets/systemtray/protocols/fdo/fdographicswidget.cpp +@@ -74,8 +74,8 @@ FdoGraphicsWidget::FdoGraphicsWidget(WId winId, QGraphicsWidget *parent) + } + } + +- if (parentView) { +- parentView->setViewportUpdateMode(QGraphicsView::FullViewportUpdate); ++ if (parentView) { ++ parentView->setViewportUpdateMode(QGraphicsView::FullViewportUpdate); + } + + connect(Plasma::Theme::defaultTheme(), SIGNAL(themeChanged()), +@@ -216,10 +216,10 @@ void FdoGraphicsWidget::handleClientError(QX11EmbedContainer::Error error) + + void FdoGraphicsWidget::resizeEvent(QGraphicsSceneResizeEvent *event) + { ++ Q_UNUSED(event); + if (d->widget) { + d->widget.data()->resize(size().toSize()); + } +- + } + + } +diff --git a/powerdevil/daemon/backends/upower/xrandrx11helper.h b/powerdevil/daemon/backends/upower/xrandrx11helper.h +index 184f582..7c4a154 100644 +--- a/powerdevil/daemon/backends/upower/xrandrx11helper.h ++++ b/powerdevil/daemon/backends/upower/xrandrx11helper.h +@@ -42,7 +42,6 @@ class XRandRX11Helper : public QWidget + int m_versionMajor; + int m_versionMinor; + +- bool m_debugMode; + Window m_window; + }; + diff --git a/SOURCES/kde-workspace-4.11.7-weather-fix-bbcukmet-clear-sky.patch b/SOURCES/kde-workspace-4.11.7-weather-fix-bbcukmet-clear-sky.patch new file mode 100644 index 0000000..99c1708 --- /dev/null +++ b/SOURCES/kde-workspace-4.11.7-weather-fix-bbcukmet-clear-sky.patch @@ -0,0 +1,12 @@ +diff -ur kde-workspace-4.11.7-weather-fix-bbcukmet-temp/plasma/generic/dataengines/weather/ions/bbcukmet/ion_bbcukmet.cpp kde-workspace-4.11.7-weather-fix-bbcukmet-clear-sky/plasma/generic/dataengines/weather/ions/bbcukmet/ion_bbcukmet.cpp +--- kde-workspace-4.11.7-weather-fix-bbcukmet-temp/plasma/generic/dataengines/weather/ions/bbcukmet/ion_bbcukmet.cpp 2014-03-16 00:53:22.000000000 +0100 ++++ kde-workspace-4.11.7-weather-fix-bbcukmet-clear-sky/plasma/generic/dataengines/weather/ions/bbcukmet/ion_bbcukmet.cpp 2014-03-24 02:27:37.000000000 +0100 +@@ -75,7 +75,7 @@ + dayList["sunny"] = ClearDay; + //dayList["sunny night"] = ClearNight; + dayList["clear"] = ClearDay; +- dayList["clar sky"] = ClearDay; ++ dayList["clear sky"] = ClearDay; + dayList["sunny intervals"] = PartlyCloudyDay; + //dayList["sunny intervals night"] = ClearNight; + dayList["partly cloudy"] = PartlyCloudyDay; diff --git a/SOURCES/kde-workspace-4.11.7-weather-fix-bbcukmet-crash-kde#332392.patch b/SOURCES/kde-workspace-4.11.7-weather-fix-bbcukmet-crash-kde#332392.patch new file mode 100644 index 0000000..a07421d --- /dev/null +++ b/SOURCES/kde-workspace-4.11.7-weather-fix-bbcukmet-crash-kde#332392.patch @@ -0,0 +1,157 @@ +diff -ur kde-workspace-4.11.7-weather-fix-bbcukmet-clear-sky/plasma/generic/dataengines/weather/ions/bbcukmet/ion_bbcukmet.cpp kde-workspace-4.11.7-weather-fix-bbcukmet-crash-kde#332392/plasma/generic/dataengines/weather/ions/bbcukmet/ion_bbcukmet.cpp +--- kde-workspace-4.11.7-weather-fix-bbcukmet-clear-sky/plasma/generic/dataengines/weather/ions/bbcukmet/ion_bbcukmet.cpp 2014-03-24 02:27:37.000000000 +0100 ++++ kde-workspace-4.11.7-weather-fix-bbcukmet-crash-kde#332392/plasma/generic/dataengines/weather/ions/bbcukmet/ion_bbcukmet.cpp 2014-03-25 16:00:40.000000000 +0100 +@@ -23,6 +23,13 @@ + + #include + #include ++#include ++ ++WeatherData::WeatherData() ++ : obsTime("N/A"), iconPeriodHour(12), iconPeriodMinute(0), ++ longitude(0.), latitude(0.), condition("N/A") ++{ ++} + + // ctor, dtor + UKMETIon::UKMETIon(QObject *parent, const QVariantList &args) +@@ -553,16 +560,31 @@ + + // Get the observation time and condition + int splitIndex = conditionString.lastIndexOf(':'); +- QStringRef conditionData = conditionString.midRef(splitIndex + 1); // Include ':' +- data.obsTime = conditionString.midRef(0, splitIndex).toString(); ++ if (splitIndex >= 0) { ++ QString conditionData = conditionString.mid(splitIndex + 1); // Skip ':' ++ data.obsTime = conditionString.left(splitIndex); ++ ++ if (data.obsTime.contains('-')) { ++ // Saturday - 13:00 CET ++ // Saturday - 12:00 GMT ++ m_dateFormat = KDateTime::fromString(data.obsTime.section('-', 1, 1).trimmed(), ++ "%H:%M %Z").toLocalZone().dateTime(); ++ if (m_dateFormat.isValid()) { ++ data.iconPeriodHour = m_dateFormat.toString("hh").toInt(); ++ data.iconPeriodMinute = m_dateFormat.toString("mm").toInt(); ++ } ++ } else { ++ m_dateFormat = QDateTime(); ++ } + +- // Saturday - 13:00 CET +- // Saturday - 12:00 GMT +- m_dateFormat = QDateTime::fromString(data.obsTime.split("-")[1].trimmed(), "hh:mm 'GMT'"); +- data.iconPeriodHour = m_dateFormat.toString("hh").toInt(); +- data.iconPeriodMinute = m_dateFormat.toString("mm").toInt(); ++ if (conditionData.contains(',')) { ++ data.condition = conditionData.section(',', 0, 0).trimmed(); + +- data.condition = conditionData.toString().split(',')[0].trimmed(); ++ if (data.condition == "null") { ++ data.condition = "N/A"; ++ } ++ } ++ } + + } else if (xml.name() == "link") { + m_place[source].forecastHTMLUrl = xml.readElementText(); +@@ -705,12 +727,12 @@ + + // Sometimes only one of min or max are reported + if (high.indexIn(line.split(',')[1]) == -1) +- forecast->tempHigh = 0; ++ forecast->tempHigh = UNKNOWN_TEMPERATURE; + else + forecast->tempHigh = high.cap(1).toInt(); + + if (low.indexIn(line.split(',')[1]) == -1) +- forecast->tempLow = 0; ++ forecast->tempLow = UNKNOWN_TEMPERATURE; + else + forecast->tempLow = low.cap(1).toInt(); + +@@ -777,16 +799,21 @@ + + const double lati = periodLatitude(source); + const double longi = periodLongitude(source); +- const Plasma::DataEngine::Data timeData = m_timeEngine->query( +- QString("Local|Solar|Latitude=%1|Longitude=%2|DateTime=%3") +- .arg(lati).arg(longi).arg(m_dateFormat.toString(Qt::ISODate))); +- +- // Tell applet which icon to use for conditions and provide mapping for condition type to the icons to display +- if (timeData["Corrected Elevation"].toDouble() >= 0.0) { +- //kDebug() << "Using daytime icons\n"; +- data.insert("Condition Icon", getWeatherIcon(dayIcons(), condition(source))); ++ ++ if (m_dateFormat.isValid()) { ++ const Plasma::DataEngine::Data timeData = m_timeEngine->query( ++ QString("Local|Solar|Latitude=%1|Longitude=%2|DateTime=%3") ++ .arg(lati).arg(longi).arg(m_dateFormat.toString(Qt::ISODate))); ++ ++ // Tell applet which icon to use for conditions and provide mapping for condition type to the icons to display ++ if (timeData["Corrected Elevation"].toDouble() >= 0.0) { ++ //kDebug() << "Using daytime icons\n"; ++ data.insert("Condition Icon", getWeatherIcon(dayIcons(), condition(source))); ++ } else { ++ data.insert("Condition Icon", getWeatherIcon(nightIcons(), condition(source))); ++ } + } else { +- data.insert("Condition Icon", getWeatherIcon(nightIcons(), condition(source))); ++ data.insert("Condition Icon", getWeatherIcon(dayIcons(), condition(source))); + } + + data.insert("Latitude", lati); +@@ -969,12 +996,22 @@ + m_weatherData[source].forecasts[i]->period.replace("Friday", i18nc("Short for Friday", "Fri")); + } + ++ int tempHigh = m_weatherData[source].forecasts[i]->tempHigh; ++ QString tempHighStr = (tempHigh == UNKNOWN_TEMPERATURE) ++ ? QString::fromLatin1("N/A") ++ : QString::number(tempHigh); ++ ++ int tempLow = m_weatherData[source].forecasts[i]->tempLow; ++ QString tempLowStr = (tempLow == UNKNOWN_TEMPERATURE) ++ ? QString::fromLatin1("N/A") ++ : QString::number(tempLow); ++ + forecastData.append(QString("%1|%2|%3|%4|%5|%6") \ + .arg(m_weatherData[source].forecasts[i]->period) \ + .arg(m_weatherData[source].forecasts[i]->iconName) \ + .arg(m_weatherData[source].forecasts[i]->summary) \ +- .arg(m_weatherData[source].forecasts[i]->tempHigh) \ +- .arg(m_weatherData[source].forecasts[i]->tempLow) \ ++ .arg(tempHighStr) \ ++ .arg(tempLowStr) \ + .arg("N/U")); + //.arg(m_weatherData[source].forecasts[i]->windSpeed) + //arg(m_weatherData[source].forecasts[i]->windDirection)); +diff -ur kde-workspace-4.11.7-weather-fix-bbcukmet-clear-sky/plasma/generic/dataengines/weather/ions/bbcukmet/ion_bbcukmet.h kde-workspace-4.11.7-weather-fix-bbcukmet-crash-kde#332392/plasma/generic/dataengines/weather/ions/bbcukmet/ion_bbcukmet.h +--- kde-workspace-4.11.7-weather-fix-bbcukmet-clear-sky/plasma/generic/dataengines/weather/ions/bbcukmet/ion_bbcukmet.h 2014-02-28 00:09:20.000000000 +0100 ++++ kde-workspace-4.11.7-weather-fix-bbcukmet-crash-kde#332392/plasma/generic/dataengines/weather/ions/bbcukmet/ion_bbcukmet.h 2014-03-24 22:34:59.000000000 +0100 +@@ -24,6 +24,7 @@ + + #include + #include ++#include + + #include "../ion.h" + #include "../dataengineconsumer.h" +@@ -39,6 +40,8 @@ + { + + public: ++ WeatherData(); ++ + QString place; + QString stationName; + // Current observation information. +@@ -171,6 +174,8 @@ + + QDateTime m_dateFormat; + QStringList m_sourcesToReset; ++ ++ static const int UNKNOWN_TEMPERATURE = INT_MIN; + }; + + K_EXPORT_PLASMA_DATAENGINE(bbcukmet, UKMETIon) diff --git a/SOURCES/kde-workspace-4.11.7-weather-fix-bbcukmet-temp.patch b/SOURCES/kde-workspace-4.11.7-weather-fix-bbcukmet-temp.patch new file mode 100644 index 0000000..147cccb --- /dev/null +++ b/SOURCES/kde-workspace-4.11.7-weather-fix-bbcukmet-temp.patch @@ -0,0 +1,41 @@ +diff -ur kde-workspace-4.11.7-weather-fix-bbcukmet/plasma/generic/dataengines/weather/ions/bbcukmet/ion_bbcukmet.cpp kde-workspace-4.11.7-weather-fix-bbcukmet-temp/plasma/generic/dataengines/weather/ions/bbcukmet/ion_bbcukmet.cpp +--- kde-workspace-4.11.7-weather-fix-bbcukmet/plasma/generic/dataengines/weather/ions/bbcukmet/ion_bbcukmet.cpp 2014-03-16 00:49:44.000000000 +0100 ++++ kde-workspace-4.11.7-weather-fix-bbcukmet-temp/plasma/generic/dataengines/weather/ions/bbcukmet/ion_bbcukmet.cpp 2014-03-16 00:53:22.000000000 +0100 +@@ -690,8 +690,8 @@ + QString line; + QString period; + QString summary; +- QRegExp high("-?\\d+.C"); +- QRegExp low("-?\\d+.C"); ++ QRegExp high("Maximum Temperature: (-?\\d+).C", Qt::CaseInsensitive); ++ QRegExp low("Minimum Temperature: (-?\\d+).C", Qt::CaseInsensitive); + while (!xml.atEnd()) { + xml.readNext(); + if (xml.name() == "title") { +@@ -702,15 +702,22 @@ + + period = line.split(',')[0].split(':')[0]; + summary = line.split(',')[0].split(':')[1].trimmed(); +- high.indexIn(line.split(',')[1].split(':')[1]); +- low.indexIn(line.split(',')[1].split(':')[2]); ++ ++ // Sometimes only one of min or max are reported ++ if (high.indexIn(line.split(',')[1]) == -1) ++ forecast->tempHigh = 0; ++ else ++ forecast->tempHigh = high.cap(1).toInt(); ++ ++ if (low.indexIn(line.split(',')[1]) == -1) ++ forecast->tempLow = 0; ++ else ++ forecast->tempLow = low.cap(1).toInt(); + + forecast->period = period; + forecast->iconName = getWeatherIcon(dayIcons(), summary.toLower()); + forecast->summary = i18nc("weather forecast", summary.toUtf8()); + kDebug() << "i18n summary string: " << qPrintable(forecast->summary); +- forecast->tempHigh = high.cap(0).toInt(); +- forecast->tempLow = low.cap(0).toInt(); + m_weatherData[source].forecasts.append(forecast); + forecast = new WeatherData::ForecastInfo; + } diff --git a/SOURCES/kde-workspace-4.11.7-weather-fix-bbcukmet.patch b/SOURCES/kde-workspace-4.11.7-weather-fix-bbcukmet.patch new file mode 100644 index 0000000..545ac93 --- /dev/null +++ b/SOURCES/kde-workspace-4.11.7-weather-fix-bbcukmet.patch @@ -0,0 +1,209 @@ +From 55644fae34ab49834dd52c834c211e765d41f8ab Mon Sep 17 00:00:00 2001 +From: Raphael Geissert +Date: Sat, 15 Feb 2014 15:20:26 +0100 +Subject: [PATCH 1/3] Update to BBC's new json-based search and modified xml + +BUG:330773 +--- + .../weather/ions/bbcukmet/ion_bbcukmet.cpp | 93 ++++++++++++-------- + 1 file changed, 54 insertions(+), 39 deletions(-) + +diff --git a/plasma/generic/dataengines/weather/ions/bbcukmet/ion_bbcukmet.cpp b/plasma/generic/dataengines/weather/ions/bbcukmet/ion_bbcukmet.cpp +index 746a734..c656e40 100644 +--- a/plasma/generic/dataengines/weather/ions/bbcukmet/ion_bbcukmet.cpp ++++ b/plasma/generic/dataengines/weather/ions/bbcukmet/ion_bbcukmet.cpp +@@ -80,6 +80,7 @@ QMap UKMETIon::setupDayIconMappings(void) + //dayList["sunny intervals night"] = ClearNight; + dayList["partly cloudy"] = PartlyCloudyDay; + dayList["cloudy"] = Overcast; ++ dayList["light cloud"] = Overcast; + dayList["white cloud"] = Overcast; + dayList["grey cloud"] = Overcast; + //dayList["low level cloud"] = NotAvailable; +@@ -131,6 +132,7 @@ QMap UKMETIon::setupNightIconMappings(voi + nightList["sunny intervals"] = PartlyCloudyDay; // it's not really sunny + nightList["sunny"] = ClearDay; + nightList["cloudy"] = Overcast; ++ nightList["light cloud"] = Overcast; + nightList["white cloud"] = Overcast; + nightList["grey cloud"] = Overcast; + nightList["partly cloudy"] = PartlyCloudyNight; +@@ -252,7 +254,8 @@ void UKMETIon::getXMLData(const QString& source) + void UKMETIon::findPlace(const QString& place, const QString& source) + { + KUrl url; +- url = "http://news.bbc.co.uk/weather/util/search/SearchResultsNode.xhtml?&search=" + place + "®ion=world&startIndex=0&count=500"; ++ /* There's a page= parameter, results are limited to 10 by page */ ++ url = "http://www.bbc.com/locator/default/en-GB/search.json?search="+place+"&filter=international&postcode_unit=false&postcode_district=true"; + + m_job = KIO::get(url.url(), KIO::Reload, KIO::HideProgressInfo); + m_job->addMetaData("cookies", "none"); // Disable displaying cookies +@@ -281,7 +284,7 @@ void UKMETIon::getFiveDayForecast(const QString& source) + + int splitIDPos = xmlPath.lastIndexOf('/'); + QString stationID = xmlPath.midRef(splitIDPos + 1).toString(); +- m_place[source].XMLforecastURL = "http://newsrss.bbc.co.uk/weather/forecast/" + stationID + "/Next3DaysRSS.xml" + xmlMap.query(); ++ m_place[source].XMLforecastURL = "http://open.live.bbc.co.uk/weather/feeds/en/" + stationID + "/3dayforecast.rss" + xmlMap.query(); + KUrl url(m_place[source].XMLforecastURL); + + m_job = KIO::get(url.url(), KIO::Reload, KIO::HideProgressInfo); +@@ -303,47 +306,43 @@ void UKMETIon::readSearchHTMLData(const QString& source, const QByteArray& html) + QStringList tokens; + QString url; + QString tmp; +- int flag = 0; + int counter = 2; + +- // "

    Vitoria, Brazil

    " +- QRegExp grabURL("/[a-z]+/[a-z]+/([0-9]+)(\\?[^\"]+)?"); +- QRegExp grabPlace(">([^<]*[a-z()])"); // FIXME: It would be better to strip away the extra '>' ++#ifdef __GNUC__ ++#warning FIXME: use a json parser instead of regexes ++#endif ++ ++ QRegExp grabURL("\"id\":\\s*\"([0-9]+)\""); ++ QRegExp grabPlace("\"fullName\":\\s*\"([^\"]+)\""); + + while (!stream.atEnd()) { + line = stream.readLine(); +- if (line.contains("

    ") > 0) { +- flag = 1; +- } + +- if (line.contains("There are no forecasts matching") > 0) { ++ if (line.contains("Sorry, no results found for") > 0) { + break; + } + +- if (flag) { ++ if (line.contains("\"results\"") > 0) { + + if (grabURL.indexIn(line.trimmed()) > 0) { +- url = "http://newsrss.bbc.co.uk/weather/forecast/" + grabURL.cap(1) + "/ObservationsRSS.xml"; +- if (grabURL.captureCount() > 1) { +- url += grabURL.cap(2); +- } +- grabPlace.indexIn(line.trimmed()); +- tmp = QString("bbcukmet|").append(grabPlace.cap(1)); + +- // Duplicate places can exist +- if (m_locations.contains(tmp)) { +- tmp = QString("bbcukmet|").append(QString("%1 (#%2)").arg(grabPlace.cap(1)).arg(counter)); +- counter++; +- } ++ for (int captureIndex = 1; captureIndex <= grabURL.captureCount(); captureIndex++) { + +- m_place[tmp].XMLurl = url; +- m_place[tmp].place = grabPlace.cap(1); +- m_locations.append(tmp); +- } +- } ++ url = "http://open.live.bbc.co.uk/weather/feeds/en/" + grabURL.cap(captureIndex) + "/observations.rss"; ++ grabPlace.indexIn(line.trimmed()); ++ tmp = QString("bbcukmet|").append(grabPlace.cap(captureIndex)); ++ ++ // Duplicate places can exist ++ if (m_locations.contains(tmp)) { ++ tmp = QString("bbcukmet|").append(QString("%1 (#%2)").arg(grabPlace.cap(captureIndex)).arg(counter)); ++ counter++; ++ } + +- if (line.contains("

    ") > 0) { +- flag = 0; ++ m_place[tmp].XMLurl = url; ++ m_place[tmp].place = grabPlace.cap(captureIndex); ++ m_locations.append(tmp); ++ } ++ } + } + } + +@@ -557,12 +556,13 @@ void UKMETIon::parseWeatherObservation(const QString& source, WeatherData& data, + QStringRef conditionData = conditionString.midRef(splitIndex + 1); // Include ':' + data.obsTime = conditionString.midRef(0, splitIndex).toString(); + +- // Friday at 0200 GMT +- m_dateFormat = QDateTime::fromString(data.obsTime.split("at")[1].trimmed(), "hhmm 'GMT'"); ++ // Saturday - 13:00 CET ++ // Saturday - 12:00 GMT ++ m_dateFormat = QDateTime::fromString(data.obsTime.split("-")[1].trimmed(), "hh:mm 'GMT'"); + data.iconPeriodHour = m_dateFormat.toString("hh").toInt(); + data.iconPeriodMinute = m_dateFormat.toString("mm").toInt(); + +- data.condition = conditionData.toString().split('.')[0].trimmed(); ++ data.condition = conditionData.toString().split(',')[0].trimmed(); + + } else if (xml.name() == "link") { + m_place[source].forecastHTMLUrl = xml.readElementText(); +@@ -575,21 +575,32 @@ void UKMETIon::parseWeatherObservation(const QString& source, WeatherData& data, + #endif + + data.temperature_C = observeData[1].split(QChar(176))[0].trimmed(); +- +- // Temperature might be not available +- if (data.temperature_C.contains("N/A")) { ++ if (data.temperature_C.contains("N/A") || data.temperature_C.contains("null")) { + data.temperature_C = i18n("N/A"); + } + + data.windDirection = observeData[2].split(',')[0].trimmed(); ++ if (data.windDirection.contains("null")) { ++ data.windDirection = ""; ++ } ++ + data.windSpeed_miles = observeData[3].split(',')[0].split(' ')[1].remove("mph"); ++ if (data.windSpeed_miles.contains("null")) { ++ data.windSpeed_miles = "N/A"; ++ } + + data.humidity = observeData[4].split(',')[0].split(' ')[1]; + if (data.humidity.endsWith('%')) { + data.humidity.chop(1); + } ++ if (data.humidity.contains("null")) { ++ data.humidity = "N/A"; ++ } + + data.pressure = observeData[5].split(',')[0].split(' ')[1].split("mb")[0]; ++ if (data.pressure.contains("null")) { ++ data.pressure = "N/A"; ++ } + data.pressureTendency = observeData[5].split(',')[1].trimmed(); + + data.visibilityStr = observeData[6].trimmed(); +@@ -600,6 +611,10 @@ void UKMETIon::parseWeatherObservation(const QString& source, WeatherData& data, + } else if (xml.name() == "long") { + const QString ordinate = xml.readElementText(); + data.longitude = ordinate.toDouble(); ++ } else if (xml.name() == "georss:point") { ++ const QString ordinates = xml.readElementText(); ++ data.latitude = ordinates.split(' ')[0].toDouble(); ++ data.longitude = ordinates.split(' ')[1].toDouble(); + } else { + parseUnknownElement(xml); + } +@@ -675,8 +690,8 @@ void UKMETIon::parseFiveDayForecast(const QString& source, QXmlStreamReader& xml + QString line; + QString period; + QString summary; +- QRegExp high("-?\\d+"); +- QRegExp low("-?\\d+"); ++ QRegExp high("-?\\d+.C"); ++ QRegExp low("-?\\d+.C"); + while (!xml.atEnd()) { + xml.readNext(); + if (xml.name() == "title") { +@@ -687,8 +702,8 @@ void UKMETIon::parseFiveDayForecast(const QString& source, QXmlStreamReader& xml + + period = line.split(',')[0].split(':')[0]; + summary = line.split(',')[0].split(':')[1].trimmed(); +- high.indexIn(line.split(',')[1]); +- low.indexIn(line.split(',')[2]); ++ high.indexIn(line.split(',')[1].split(':')[1]); ++ low.indexIn(line.split(',')[1].split(':')[2]); + + forecast->period = period; + forecast->iconName = getWeatherIcon(dayIcons(), summary.toLower()); +-- +1.7.10 + diff --git a/SOURCES/kde-workspace-4.7.80-kde#171685.patch b/SOURCES/kde-workspace-4.7.80-kde#171685.patch deleted file mode 100644 index 51954a4..0000000 --- a/SOURCES/kde-workspace-4.7.80-kde#171685.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff -up kde-workspace-4.7.80/kwin/workspace.h.kde#171685 kde-workspace-4.7.80/kwin/workspace.h ---- kde-workspace-4.7.80/kwin/workspace.h.kde#171685 2011-11-17 21:55:50.000000000 +0100 -+++ kde-workspace-4.7.80/kwin/workspace.h 2011-11-21 15:00:14.772410822 +0100 -@@ -155,7 +155,7 @@ public: - void setShouldGetFocus(Client*); - bool activateNextClient(Client* c); - bool focusChangeEnabled() { -- return block_focus == 0; -+ return block_focus < 1; - } - - void updateColormap(); diff --git a/SOURCES/kde-workspace-4.8.80-battery-plasmoid-showremainingtime.patch b/SOURCES/kde-workspace-4.8.80-battery-plasmoid-showremainingtime.patch deleted file mode 100644 index 74a4e57..0000000 --- a/SOURCES/kde-workspace-4.8.80-battery-plasmoid-showremainingtime.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff -ur kde-workspace-4.8.80/plasma/generic/applets/batterymonitor/contents/config/main.xml kde-workspace-4.8.80-battery-plasmoid-showremainingtime/plasma/generic/applets/batterymonitor/contents/config/main.xml ---- kde-workspace-4.8.80/plasma/generic/applets/batterymonitor/contents/config/main.xml 2012-05-23 01:55:18.000000000 +0200 -+++ kde-workspace-4.8.80-battery-plasmoid-showremainingtime/plasma/generic/applets/batterymonitor/contents/config/main.xml 2012-05-26 22:09:44.000000000 +0200 -@@ -10,7 +10,7 @@ - false - - -- false -+ true - - - diff --git a/SOURCES/kde-workspace-bz#1067111-panel-placing.patch b/SOURCES/kde-workspace-bz#1067111-panel-placing.patch deleted file mode 100644 index 150d4e0..0000000 --- a/SOURCES/kde-workspace-bz#1067111-panel-placing.patch +++ /dev/null @@ -1,54 +0,0 @@ -diff --git a/plasma/desktop/shell/data/layouts/org.kde.plasma-desktop.defaultPanel/contents/layout.js b/plasma/desktop/shell/data/layouts/org.kde.plasma-desktop.defaultPanel/contents/layout.js -index 47f635c..9d857a2 100644 ---- a/plasma/desktop/shell/data/layouts/org.kde.plasma-desktop.defaultPanel/contents/layout.js -+++ b/plasma/desktop/shell/data/layouts/org.kde.plasma-desktop.defaultPanel/contents/layout.js -@@ -1,7 +1,28 @@ - var panel = new Panel --if (panelIds.length == 1) { -- // we are the only panel, so set the location for the user -- panel.location = 'bottom' -+var panelScreen = panel.screen -+var freeEdges = {"bottom": true, "top": true, "left": true, "right": true} -+ -+for (i = 0; i < panelIds.length; ++i) { -+ var tmpPanel = panelById(panelIds[i]) -+ if (tmpPanel.screen == panelScreen) { -+ // Ignore the new panel -+ if (tmpPanel.id != panel.id) { -+ freeEdges[tmpPanel.location] = false; -+ } -+ } -+} -+ -+if (freeEdges["bottom"] == true) { -+ panel.location = "bottom"; -+} else if (freeEdges["top"] == true) { -+ panel.location = "top"; -+} else if (freeEdges["left"] == true) { -+ panel.location = "left"; -+} else if (freeEdges["right"] == true) { -+ panel.location = "right"; -+} else { -+ // There is no free edge, so leave the default value -+ panel.location = "top"; - } - - panel.height = screenGeometry(panel.screen).height > 1024 ? 35 : 27 -diff --git a/plasma/desktop/shell/desktopcorona.cpp b/plasma/desktop/shell/desktopcorona.cpp -index 4c2483d..2f05dfc 100644 ---- a/plasma/desktop/shell/desktopcorona.cpp -+++ b/plasma/desktop/shell/desktopcorona.cpp -@@ -534,10 +534,10 @@ void DesktopCorona::addPanel(const QString &plugin) - QList freeEdges = DesktopCorona::freeEdges(screen); - //kDebug() << freeEdges; - Plasma::Location destination; -- if (freeEdges.contains(Plasma::TopEdge)) { -- destination = Plasma::TopEdge; -- } else if (freeEdges.contains(Plasma::BottomEdge)) { -+ if (freeEdges.contains(Plasma::BottomEdge)) { - destination = Plasma::BottomEdge; -+ } else if (freeEdges.contains(Plasma::TopEdge)) { -+ destination = Plasma::TopEdge; - } else if (freeEdges.contains(Plasma::LeftEdge)) { - destination = Plasma::LeftEdge; - } else if (freeEdges.contains(Plasma::RightEdge)) { diff --git a/SOURCES/kde-workspace-exclude_kdm.patch b/SOURCES/kde-workspace-exclude_kdm.patch index 4c15ad9..8f31cc9 100644 --- a/SOURCES/kde-workspace-exclude_kdm.patch +++ b/SOURCES/kde-workspace-exclude_kdm.patch @@ -1,7 +1,17 @@ -diff -up kde-workspace-4.10.5/CMakeLists.txt.than kde-workspace-4.10.5/CMakeLists.txt ---- kde-workspace-4.10.5/CMakeLists.txt.than 2014-02-27 12:11:28.649545493 +0100 -+++ kde-workspace-4.10.5/CMakeLists.txt 2014-02-27 12:22:15.503547119 +0100 -@@ -176,11 +176,6 @@ if(${KDE_PLATFORM_PROFILE} STREQUAL "Des +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 27317c3..64d36cf 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -100,8 +100,6 @@ if(Q_WS_X11) + + add_feature_info("Automated testing of X clients" X11_XTest_FOUND + "The X11 Testing Resource extension library is useful for automated testing of X clients") +- add_feature_info("libXau" X11_Xau_FOUND "The X11 Authorization Protocol library may be used by KDM") +- add_feature_info("LibXdmcp" X11_Xdmcp_FOUND "The X Display Manager Control Protocol library may be used by KDM") + if(NOT X11_Xkbfile_FOUND) + message(FATAL_ERROR "The X11 keyboard layout library was not found. Required for building keyboard modules.") + endif() +@@ -262,11 +260,6 @@ if(${KDE_PLATFORM_PROFILE} STREQUAL "Desktop") macro_optional_add_subdirectory(appmenu) endif(NOT WIN32) @@ -13,39 +23,42 @@ diff -up kde-workspace-4.10.5/CMakeLists.txt.than kde-workspace-4.10.5/CMakeList endif(${KDE_PLATFORM_PROFILE} STREQUAL "Desktop") if(NOT WIN32) -diff -up kde-workspace-4.10.5/doc/CMakeLists.txt.than kde-workspace-4.10.5/doc/CMakeLists.txt ---- kde-workspace-4.10.5/doc/CMakeLists.txt.than 2014-02-27 12:22:56.089547221 +0100 -+++ kde-workspace-4.10.5/doc/CMakeLists.txt 2014-02-27 12:23:05.400547245 +0100 +diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt +index af00f4c..d49d590 100644 +--- a/doc/CMakeLists.txt ++++ b/doc/CMakeLists.txt @@ -1,4 +1,3 @@ -add_subdirectory(kdm) add_subdirectory(klipper) add_subdirectory(kfontview) add_subdirectory(kmenuedit) -diff -up kde-workspace-4.10.5/kdm/kfrontend/sessions/kde-plasma.desktop.cmake.than kde-workspace-4.10.5/kdm/kfrontend/sessions/kde-plasma.desktop.cmake ---- kde-workspace-4.10.5/kdm/kfrontend/sessions/kde-plasma.desktop.cmake.than 2014-02-28 14:37:00.969067779 +0100 -+++ kde-workspace-4.10.5/kdm/kfrontend/sessions/kde-plasma.desktop.cmake 2014-02-28 14:37:46.899068843 +0100 +diff --git a/kdm/kfrontend/sessions/kde-plasma-safe.desktop.cmake b/kdm/kfrontend/sessions/kde-plasma-safe.desktop.cmake +index 3cf746b..0af75fa 100644 +--- a/kdm/kfrontend/sessions/kde-plasma-safe.desktop.cmake ++++ b/kdm/kfrontend/sessions/kde-plasma-safe.desktop.cmake @@ -1,8 +1,8 @@ [Desktop Entry] Encoding=UTF-8 Type=XSession --Exec=${BIN_INSTALL_DIR}/startkde +-Exec=${BIN_INSTALL_DIR}/startkde --failsafe -TryExec=${BIN_INSTALL_DIR}/startkde -+Exec=/usr/bin/startkde ++Exec=/usr/bin/startkde --failsafe +TryExec=/usr/bin/startkde - Name=KDE Plasma Workspace - Name[ar]=مساحة عمل بلازما كدي - Name[bg]=Работно пространство Plasma на KDE -diff -up kde-workspace-4.10.5/kdm/kfrontend/sessions/kde-plasma-safe.desktop.cmake.than kde-workspace-4.10.5/kdm/kfrontend/sessions/kde-plasma-safe.desktop.cmake ---- kde-workspace-4.10.5/kdm/kfrontend/sessions/kde-plasma-safe.desktop.cmake.than 2014-02-28 14:40:48.322073046 +0100 -+++ kde-workspace-4.10.5/kdm/kfrontend/sessions/kde-plasma-safe.desktop.cmake 2014-02-28 14:38:09.762069373 +0100 + DesktopNames=KDE + Name=KDE Plasma Workspace (failsafe session) + Name[ar]=مساحة عمل بلازما كدي (الجلسة الآمنة) +diff --git a/kdm/kfrontend/sessions/kde-plasma.desktop.cmake b/kdm/kfrontend/sessions/kde-plasma.desktop.cmake +index 613f7dd..40089bb 100644 +--- a/kdm/kfrontend/sessions/kde-plasma.desktop.cmake ++++ b/kdm/kfrontend/sessions/kde-plasma.desktop.cmake @@ -1,8 +1,8 @@ [Desktop Entry] Encoding=UTF-8 Type=XSession --Exec=${BIN_INSTALL_DIR}/startkde --failsafe +-Exec=${BIN_INSTALL_DIR}/startkde -TryExec=${BIN_INSTALL_DIR}/startkde -+Exec=/usr/bin/startkde --failsafe ++Exec=/usr/bin/startkde +TryExec=/usr/bin/startkde - Name=KDE Plasma Workspace (failsafe session) - Name[ar]=مساحة عمل بلازما كدي (الجلسة الآمنة) - Name[bn]=কে.ডি.ই. প্লাসমা ওয়ার্কস্পেস (failsafe session) + DesktopNames=KDE + Name=KDE Plasma Workspace + Name[ar]=مساحة عمل بلازما كدي diff --git a/SOURCES/kde-workspace-kdm_local_ipv6.patch b/SOURCES/kde-workspace-kdm_local_ipv6.patch new file mode 100644 index 0000000..b767a2a --- /dev/null +++ b/SOURCES/kde-workspace-kdm_local_ipv6.patch @@ -0,0 +1,45 @@ +--- ./kdm/backend/auth.c 2015-02-06 17:59:27.473734701 -0600 ++++ ./kdm/backend/auth.c.orig 2015-02-06 18:46:13.072441136 -0600 +@@ -44,6 +44,10 @@ + + #include + ++#ifdef XDMCP ++# include ++#endif ++ + #include "dm_socket.h" + #ifdef DNETCONN + # include +@@ -1075,10 +1079,28 @@ + static int + convertAuthAddr(char *saddr, int *len, CARD8 **addr) + { ++ static const unsigned char ipv6_lo[] = { ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 ++ }; ++ ++ static const unsigned char ipv4_lo[] = { ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x01 ++ }; ++ + int ret = convertAddr(saddr, len, addr); +- if (ret == FamilyInternet && +- ((struct in_addr *)*addr)->s_addr == htonl(0x7F000001L)) +- ret = FamilyLocal; ++ ++ if (ret == FamilyInternet) { ++ if (((struct in_addr *)*addr)->s_addr == htonl(0x7F000001L)) ++ ret = FamilyLocal; ++ } else if (ret == FamilyInternet6) { ++ if (memcmp(*addr, ipv6_lo, sizeof ipv6_lo) == 0) ++ ret = FamilyLocal; ++ else if (memcmp(*addr, ipv4_lo, sizeof ipv4_lo) == 0) ++ ret = FamilyLocal; ++ } ++ + return ret; + } + diff --git a/SOURCES/kde-workspace-revert-improve-systemtray-on-hdpi-displays.patch b/SOURCES/kde-workspace-revert-improve-systemtray-on-hdpi-displays.patch new file mode 100755 index 0000000..374180e --- /dev/null +++ b/SOURCES/kde-workspace-revert-improve-systemtray-on-hdpi-displays.patch @@ -0,0 +1,35 @@ +diff --git a/plasma/generic/applets/systemtray/package/contents/ui/IconsList.qml b/plasma/generic/applets/systemtray/package/contents/ui/IconsList.qml +index d72733c..7ec7346 100644 +--- a/plasma/generic/applets/systemtray/package/contents/ui/IconsList.qml ++++ b/plasma/generic/applets/systemtray/package/contents/ui/IconsList.qml +@@ -33,7 +33,7 @@ MouseArea { + id: root_item + + property int icons_size: 24 ///< Size of icons, icons are square i.e. width == height +- property int icons_margins: icons_size/6 ///< Margins for icons ++ property int icons_margins: 4 ///< Margins for icons + property alias icons_number: repeater.count ///< [readonly] Number of icons + property alias model: repeater.model; ///< Model for grid + property int cell_size: icons_size + 2*icons_margins ///< [readonly] size of grid cell +diff --git a/plasma/generic/applets/systemtray/package/contents/ui/main.qml b/plasma/generic/applets/systemtray/package/contents/ui/main.qml +index 209091b..8577362 100644 +--- a/plasma/generic/applets/systemtray/package/contents/ui/main.qml ++++ b/plasma/generic/applets/systemtray/package/contents/ui/main.qml +@@ -34,7 +34,7 @@ Item { + property int minimumWidth: JS.MINIMUM_SIZE + property int minimumHeight: JS.MINIMUM_SIZE + +- property int iconSize: Math.min(root_item.width, Math.min(root_item.height, theme.defaultFont.mSize.height < 20 ? 24 : theme.largeIconSize)) //Math.min(root_item.width, Math.min(root_item.height, JS.ICONS_SIZE)) ++ property int iconSize: Math.min(root_item.width, Math.min(root_item.height, JS.ICONS_SIZE)) + + // Data Models + property list models: [ +@@ -112,7 +112,7 @@ Item { + + content: IconsList { + id: popup_area +- icons_size: root_item.iconSize ++ icons_size: JS.ICONS_SIZE + model: model_popup + } + } diff --git a/SOURCES/kdebase-workspace-4.4.92-kdm_plymouth081.patch b/SOURCES/kdebase-workspace-4.4.92-kdm_plymouth081.patch deleted file mode 100644 index 2c49ccd..0000000 --- a/SOURCES/kdebase-workspace-4.4.92-kdm_plymouth081.patch +++ /dev/null @@ -1,165 +0,0 @@ -diff -up kdebase-workspace-4.4.92/kdm/backend/dm.c.kdm_plymouth kdebase-workspace-4.4.92/kdm/backend/dm.c ---- kdebase-workspace-4.4.92/kdm/backend/dm.c.kdm_plymouth 2010-07-06 01:54:30.000000000 -0500 -+++ kdebase-workspace-4.4.92/kdm/backend/dm.c 2010-07-07 13:55:48.425171749 -0500 -@@ -1329,6 +1329,81 @@ getBusyVTs(void) - return activeVTs; - } - -+static int -+get_active_vt (void) -+{ -+ int console_fd; -+ struct vt_stat console_state = { 0 }; -+ console_fd = open ("/dev/tty0", O_RDONLY | O_NOCTTY); -+ if (console_fd < 0) { -+ goto out; -+ } -+ if (ioctl (console_fd, VT_GETSTATE, &console_state) < 0) { -+ goto out; -+ } -+out: -+ if (console_fd >= 0) { -+ close (console_fd); -+ } -+ return console_state.v_active; -+} -+ -+static int -+plymouth_is_running (void) -+{ -+ int status; -+ status = system ("/bin/plymouth --ping"); -+ -+ return WIFEXITED (status) && WEXITSTATUS (status) == 0; -+} -+ -+static int -+plymouth_has_active_vt (void) -+{ -+ int status; -+ status = system ("/bin/plymouth --has-active-vt"); -+ -+ return WIFEXITED (status) && WEXITSTATUS (status) == 0; -+} -+ -+static int -+plymouth_prepare_for_transition (void) -+{ -+ int status; -+ status = system ("/bin/plymouth deactivate"); -+ -+ return WIFEXITED (status) && WEXITSTATUS (status) == 0; -+} -+ -+int -+plymouth_quit_with_transition (void) -+{ -+ int status; -+ status = system ("/bin/plymouth quit --retain-splash"); -+ -+ return WIFEXITED (status) && WEXITSTATUS (status) == 0; -+} -+ -+static int -+plymouth_quit_without_transition (void) -+{ -+ int status; -+ status = system ("/bin/plymouth quit"); -+ -+ return WIFEXITED (status) && WEXITSTATUS (status) == 0; -+} -+ -+static int -+triggered_to_force_display_on_active_vt (void) -+{ -+ int should_force_display_on_active_vt; -+ should_force_display_on_active_vt=open("/var/spool/gdm/force-display-on-active-vt", O_RDONLY); -+ if ( should_force_display_on_active_vt >= 0 ) -+ close(should_force_display_on_active_vt); -+ unlink("/var/spool/gdm/force-display-on-active-vt"); -+ return should_force_display_on_active_vt; -+} -+ - static void - allocateVT(struct display *d) - { -@@ -1338,6 +1413,43 @@ allocateVT(struct display *d) - if ((d->displayType & d_location) == dLocal && - d->status == notRunning && !d->serverVT && d->reqSrvVT >= 0) - { -+ /* check for plymouth using newer methods */ -+ d->plymouth_is_running = plymouth_is_running (); -+ if (d->plymouth_is_running) { -+ /* call plymouth deactivate */ -+ plymouth_prepare_for_transition (); -+ if (plymouth_has_active_vt ()) { -+ /* plymouth was displaying a splash screen and has -+ * terminated leaving it on screen -+ */ -+ int vt; -+ vt = get_active_vt (); -+ if (vt > 0) { -+ /* start the X server on the active vt */ -+ d->serverVT = vt; -+ return; -+ } -+ } -+ else { -+ /* plymouth might have been running but did not display -+ * a splash screen. -+ */ -+ -+ /* call plymouth quit and start the X server as usual */ -+ d->plymouth_is_running = !plymouth_quit_without_transition (); -+ } -+ -+ /* fallback to old/deprecated method */ -+ } else if ( triggered_to_force_display_on_active_vt() >= 0 ) { -+ int vt; -+ vt = get_active_vt(); -+ if (vt > 0) { -+ d->serverVT = vt; -+ return; -+ } -+ } -+ -+ - if (d->reqSrvVT && d->reqSrvVT < 16) { - d->serverVT = d->reqSrvVT; - } else { -diff -up kdebase-workspace-4.4.92/kdm/backend/dm.h.kdm_plymouth kdebase-workspace-4.4.92/kdm/backend/dm.h ---- kdebase-workspace-4.4.92/kdm/backend/dm.h.kdm_plymouth 2010-07-06 01:54:30.000000000 -0500 -+++ kdebase-workspace-4.4.92/kdm/backend/dm.h 2010-07-07 13:48:11.874921158 -0500 -@@ -292,6 +292,8 @@ struct display { - int authNum; /* number of authorizations */ - char *authFile; /* file to store authorization in */ - char *greeterAuthFile; /* file to store authorization for greeter in */ -+ -+ int plymouth_is_running; /* Plymouth's status */ - }; - - #define d_location 1 -@@ -404,6 +406,8 @@ int anyDisplaysLeft(void); - void forEachDisplay(void (*f)(struct display *)); - #ifdef HAVE_VTS - void forEachDisplayRev(void (*f)(struct display *)); -+/* function for plymouth */ -+int plymouth_quit_with_transition (void); - #endif - void removeDisplay(struct display *old); - struct display -diff -up kdebase-workspace-4.4.92/kdm/backend/server.c.kdm_plymouth kdebase-workspace-4.4.92/kdm/backend/server.c ---- kdebase-workspace-4.4.92/kdm/backend/server.c.kdm_plymouth 2010-07-06 01:54:30.000000000 -0500 -+++ kdebase-workspace-4.4.92/kdm/backend/server.c 2010-07-07 13:56:46.960921366 -0500 -@@ -137,6 +137,11 @@ startServerSuccess() - struct display *d = startingServer; - d->serverStatus = ignore; - serverTimeout = TO_INF; -+ if (d->plymouth_is_running) { -+ debug( "Quitting Plymouth with transition\n" ); -+ d->plymouth_is_running = !plymouth_quit_with_transition (); -+ debug ("Is Plymouth still running? %s\n", d->plymouth_is_running ? "yes" : "no"); -+ } - debug("X server ready, starting session\n"); - startDisplayP2(d); - } diff --git a/SOURCES/powerdevil-upower-0.99.patch b/SOURCES/powerdevil-upower-0.99.patch new file mode 100644 index 0000000..c3ac47a --- /dev/null +++ b/SOURCES/powerdevil-upower-0.99.patch @@ -0,0 +1,71 @@ +diff --git a/powerdevil/daemon/backends/upower/powerdevilupowerbackend.cpp b/powerdevil/daemon/backends/upower/powerdevilupowerbackend.cpp +index cc20dc7..297b020 100644 +--- a/powerdevil/daemon/backends/upower/powerdevilupowerbackend.cpp ++++ b/powerdevil/daemon/backends/upower/powerdevilupowerbackend.cpp +@@ -75,7 +75,7 @@ bool checkSystemdVersion(uint requiredVersion) + + PowerDevilUPowerBackend::PowerDevilUPowerBackend(QObject* parent) + : BackendInterface(parent), +- m_brightnessControl(0), m_kbdMaxBrightness(0), ++ m_displayDevice(0), m_brightnessControl(0), m_kbdMaxBrightness(0), + m_lidIsPresent(false), m_lidIsClosed(false), m_onBattery(false) + { + +@@ -451,6 +451,14 @@ void PowerDevilUPowerBackend::enumerateDevices() + m_devices.insert(device.path(), upowerDevice); + } + ++ QDBusReply reply = m_upowerInterface->call("GetDisplayDevice"); ++ if (reply.isValid()) { ++ const QString path = reply.value().path(); ++ if (!path.isEmpty() && path != QLatin1String("/")) { ++ m_displayDevice = new OrgFreedesktopUPowerDeviceInterface(UPOWER_SERVICE, path, QDBusConnection::systemBus(), this); ++ } ++ } ++ + updateDeviceProps(); + + if (m_onBattery) +@@ -500,14 +508,22 @@ void PowerDevilUPowerBackend::updateDeviceProps() + { + qlonglong remainingTime = 0; + +- foreach(OrgFreedesktopUPowerDeviceInterface * upowerDevice, m_devices) { +- const uint type = upowerDevice->type(); +- if (( type == 2 || type == 3) && upowerDevice->powerSupply()) { +- const uint state = upowerDevice->state(); +- if (state == 1) // charging +- remainingTime += upowerDevice->timeToFull(); +- else if (state == 2) //discharging +- remainingTime += upowerDevice->timeToEmpty(); ++ if (m_displayDevice && m_displayDevice->isPresent()) { ++ const uint state = m_displayDevice->state(); ++ if (state == 1) // charging ++ remainingTime = m_displayDevice->timeToFull(); ++ else if (state == 2) //discharging ++ remainingTime = m_displayDevice->timeToEmpty(); ++ } else { ++ foreach(OrgFreedesktopUPowerDeviceInterface * upowerDevice, m_devices) { ++ const uint type = upowerDevice->type(); ++ if (( type == 2 || type == 3) && upowerDevice->powerSupply()) { ++ const uint state = upowerDevice->state(); ++ if (state == 1) // charging ++ remainingTime += upowerDevice->timeToFull(); ++ else if (state == 2) //discharging ++ remainingTime += upowerDevice->timeToEmpty(); ++ } + } + } + +diff --git a/powerdevil/daemon/backends/upower/powerdevilupowerbackend.h b/powerdevil/daemon/backends/upower/powerdevilupowerbackend.h +index c6563b0..14f4c5e 100644 +--- a/powerdevil/daemon/backends/upower/powerdevilupowerbackend.h ++++ b/powerdevil/daemon/backends/upower/powerdevilupowerbackend.h +@@ -84,6 +84,7 @@ private slots: + private: + // upower devices + QMap m_devices; ++ OrgFreedesktopUPowerDeviceInterface *m_displayDevice; + + // brightness + QMap m_cachedBrightnessMap; diff --git a/SOURCES/startkde.cmake b/SOURCES/startkde.cmake new file mode 100644 index 0000000..11da90f --- /dev/null +++ b/SOURCES/startkde.cmake @@ -0,0 +1,357 @@ +#!/bin/sh +# +# DEFAULT KDE STARTUP SCRIPT ( @KDE4WORKSPACE_VERSION@ ) +# + +if test "x$1" = x--failsafe; then + KDE_FAILSAFE=1 # General failsafe flag + KWIN_COMPOSE=N # Disable KWin's compositing + export KWIN_COMPOSE KDE_FAILSAFE +fi + +# When the X server dies we get a HUP signal from xinit. We must ignore it +# because we still need to do some cleanup. +trap 'echo GOT SIGHUP' HUP + +# we have to unset this for Darwin since it will screw up KDE's dynamic-loading +unset DYLD_FORCE_FLAT_NAMESPACE + +qdbus=qdbus + +# See http://bugzilla.redhat.com/537609 , a naive attempt to drop dep +# on xmessage and allow alternatives like zenity. +message() { + xmessage -geometry 500x100 "$1" > /dev/null 2>/dev/null || \ + zenity --info --text="$1" > /dev/null 2>/dev/null ||: + return $? +} + +# Check if a KDE session already is running and whether it's possible to connect to X +kcheckrunning +kcheckrunning_result=$? +if test $kcheckrunning_result -eq 0 ; then + echo "KDE seems to be already running on this display." + message "KDE seems to be already running on this display." > /dev/null 2>/dev/null + exit 1 +elif test $kcheckrunning_result -eq 2 ; then + echo "\$DISPLAY is not set or cannot connect to the X server." + exit 1 +fi + +# Boot sequence: +# +# kdeinit is used to fork off processes which improves memory usage +# and startup time. +# +# * kdeinit starts klauncher first. +# * Then kded is started. kded is responsible for keeping the sycoca +# database up to date. When an up to date database is present it goes +# into the background and the startup continues. +# * Then kdeinit starts kcminit. kcminit performs initialisation of +# certain devices according to the user's settings +# +# * Then ksmserver is started which takes control of the rest of the startup sequence + +# The user's personal KDE directory is usually ~/.kde, but this setting +# may be overridden by setting KDEHOME. + +kdehome=$HOME/@KDE_DEFAULT_HOME@ +test -n "$KDEHOME" && kdehome=`echo "$KDEHOME"|sed "s,^~/,$HOME/,"` + +# see kstartupconfig source for usage +mkdir -m 700 -p $kdehome +mkdir -m 700 -p $kdehome/share +mkdir -m 700 -p $kdehome/share/config +cat >$kdehome/share/config/startupconfigkeys </env/*.sh and /env/*.sh +# (where is $KDEHOME or ~/.kde, and is where KDE is installed) +# +# This is where you can define environment variables that will be available to +# all KDE programs, so this is where you can run agents using e.g. eval `ssh-agent` +# or eval `gpg-agent --daemon`. +# Note: if you do that, you should also put "ssh-agent -k" as a shutdown script +# +# (see end of this file). +# For anything else (that doesn't set env vars, or that needs a window manager), +# better use the Autostart folder. + +libpath=`kde4-config --path lib | tr : '\n'` +envpath=/etc/kde/env/ + +for prefix in `echo "$libpath" | sed -n -e 's,/lib[^/]*/,/env/,p'` $envpath ; do + for file in "$prefix"*.sh; do + test -r "$file" && . "$file" + done +done + +# Set the path for Qt plugins provided by KDE +QT_PLUGIN_PATH=${QT_PLUGIN_PATH+$QT_PLUGIN_PATH:}`kde4-config --path qtplugins` +export QT_PLUGIN_PATH + +# Set a left cursor instead of the standard X11 "X" cursor, since I've heard +# from some users that they're confused and don't know what to do. This is +# especially necessary on slow machines, where starting KDE takes one or two +# minutes until anything appears on the screen. +# +# If the user has overwritten fonts, the cursor font may be different now +# so don't move this up. +# +xsetroot -cursor_name left_ptr + +# Get Ghostscript to look into user's KDE fonts dir for additional Fontmap +if test -n "$GS_LIB" ; then + GS_LIB=$usr_fdir:$GS_LIB + export GS_LIB +else + GS_LIB=$usr_fdir + export GS_LIB +fi + +lnusertemp=`kde4-config --path exe --locate lnusertemp` +if test -z "$lnusertemp"; then + # Startup error + echo 'startkde: ERROR: Could not locate lnusertemp in '`kde4-config --path exe` 1>&2 +fi + +# Link "tmp" "socket" and "cache" resources to directory in /tmp +# Creates: +# - a directory /tmp/kde-$USER and links $KDEHOME/tmp-$HOSTNAME to it. +# - a directory /tmp/ksocket-$USER and links $KDEHOME/socket-$HOSTNAME to it. +# - a directory /var/tmp/kdecache-$USER and links $KDEHOME/cache-$HOSTNAME to it. +# Note: temporary locations can be overriden through the KDETMP and KDEVARTMP +# environment variables +for resource in tmp cache socket; do + if "$lnusertemp" $resource >/dev/null; then + : # ok + else + echo 'startkde: Call to lnusertemp failed (temporary directories full?). Check your installation.' 1>&2 + test -n "$ksplash_pid" && kill "$ksplash_pid" 2>/dev/null + message "Call to lnusertemp failed (temporary directories full?). Check your installation." + exit 1 + fi +done + +# In case of dcop sockets left by a previous session, cleanup +#dcopserver_shutdown + +echo 'startkde: Starting up...' 1>&2 + +# Make sure that D-Bus is running +# D-Bus autolaunch is broken +if test -z "$DBUS_SESSION_BUS_ADDRESS" ; then + eval `dbus-launch --sh-syntax --exit-with-session` +fi +if $qdbus >/dev/null 2>/dev/null; then + : # ok +else + echo 'startkde: Could not start D-Bus. Can you call qdbus?' 1>&2 + test -n "$ksplash_pid" && kill "$ksplash_pid" 2>/dev/null + message "Could not start D-Bus. Can you call qdbus?" + exit 1 +fi + + +# Mark that full KDE session is running (e.g. Konqueror preloading works only +# with full KDE running). The KDE_FULL_SESSION property can be detected by +# any X client connected to the same X session, even if not launched +# directly from the KDE session but e.g. using "ssh -X", kdesu. $KDE_FULL_SESSION +# however guarantees that the application is launched in the same environment +# like the KDE session and that e.g. KDE utilities/libraries are available. +# KDE_FULL_SESSION property is also only available since KDE 3.5.5. +# The matching tests are: +# For $KDE_FULL_SESSION: +# if test -n "$KDE_FULL_SESSION"; then ... whatever +# For KDE_FULL_SESSION property: +# xprop -root | grep "^KDE_FULL_SESSION" >/dev/null 2>/dev/null +# if test $? -eq 0; then ... whatever +# +# Additionally there is (since KDE 3.5.7) $KDE_SESSION_UID with the uid +# of the user running the KDE session. It should be rarely needed (e.g. +# after sudo to prevent desktop-wide functionality in the new user's kded). +# +# Since KDE4 there is also KDE_SESSION_VERSION, containing the major version number. +# Note that this didn't exist in KDE3, which can be detected by its absense and +# the presence of KDE_FULL_SESSION. +# +KDE_FULL_SESSION=true +export KDE_FULL_SESSION +xprop -root -f KDE_FULL_SESSION 8t -set KDE_FULL_SESSION true + +KDE_SESSION_VERSION=4 +export KDE_SESSION_VERSION +xprop -root -f KDE_SESSION_VERSION 32c -set KDE_SESSION_VERSION 4 + +KDE_SESSION_UID=`id -ru` +export KDE_SESSION_UID + +XDG_CURRENT_DESKTOP=KDE +export XDG_CURRENT_DESKTOP + +# At this point all the environment is ready, let's send it to kwalletd if running +if test -n "$PAM_KWALLET_LOGIN" ; then + env | socat STDIN UNIX-CONNECT:$PAM_KWALLET_LOGIN +fi + +@KDE4_LIBEXEC_INSTALL_DIR@/start_kdeinit_wrapper +kcminit_startup +if test $? -ne 0; then + # Startup error + echo 'startkde: Could not start kdeinit4. Check your installation.' 1>&2 + test -n "$ksplash_pid" && kill "$ksplash_pid" 2>/dev/null + message "Could not start kdeinit4. Check your installation." + exit 1 +fi + +# finally, give the session control to the session manager +# see kdebase/ksmserver for the description of the rest of the startup sequence +# if the KDEWM environment variable has been set, then it will be used as KDE's +# window manager instead of kwin. +# if KDEWM is not set, ksmserver will ensure kwin is started. +# kwrapper4 is used to reduce startup time and memory usage +# kwrapper4 does not return useful error codes such as the exit code of ksmserver. +# We only check for 255 which means that the ksmserver process could not be +# started, any problems thereafter, e.g. ksmserver failing to initialize, +# will remain undetected. +test -n "$KDEWM" && KDEWM="--windowmanager $KDEWM" +# If the session should be locked from the start (locked autologin), +# lock now and do the rest of the KDE startup underneath the locker. +KSMSERVEROPTIONS="" +test -n "$dl" && KSMSERVEROPTIONS=" --lockscreen" +kwrapper4 ksmserver $KDEWM $KSMSERVEROPTIONS +if test $? -eq 255; then + # Startup error + echo 'startkde: Could not start ksmserver. Check your installation.' 1>&2 + test -n "$ksplash_pid" && kill "$ksplash_pid" 2>/dev/null + message "Could not start ksmserver. Check your installation." +fi + +wait_drkonqi=`kreadconfig --file startkderc --group WaitForDrKonqi --key Enabled --default true` + +if test x"$wait_drkonqi"x = x"true"x ; then + # wait for remaining drkonqi instances with timeout (in seconds) + wait_drkonqi_timeout=`kreadconfig --file startkderc --group WaitForDrKonqi --key Timeout --default 900` + wait_drkonqi_counter=0 + while $qdbus | grep "^[^w]*org.kde.drkonqi" > /dev/null ; do + sleep 5 + wait_drkonqi_counter=$((wait_drkonqi_counter+5)) + if test "$wait_drkonqi_counter" -ge "$wait_drkonqi_timeout" ; then + # ask remaining drkonqis to die in a graceful way + $qdbus | grep 'org.kde.drkonqi-' | while read address ; do + $qdbus "$address" "/MainApplication" "quit" + done + break + fi + done +fi + +echo 'startkde: Shutting down...' 1>&2 +# just in case +test -n "$ksplash_pid" && kill "$ksplash_pid" 2>/dev/null + +# Clean up +kdeinit4_shutdown +# KDE3 support +kde3 kdeinit_shutdown 2>/dev/null +kde3 dcopserver_shutdown --wait 2>/dev/null + +echo 'startkde: Running shutdown scripts...' 1>&2 + +# Run scripts found in $KDEDIRS/shutdown +shutdownpath=/etc/kde/shutdown/ +for prefix in `echo "$libpath" | sed -n -e 's,/lib[^/]*/,/shutdown/,p'` $shutdownpath; do + for file in `ls "$prefix" 2> /dev/null | egrep -v '(~|\.bak)$'`; do + test -x "$prefix$file" && "$prefix$file" + done +done + +unset KDE_FULL_SESSION +xprop -root -remove KDE_FULL_SESSION +unset KDE_SESSION_VERSION +xprop -root -remove KDE_SESSION_VERSION +unset KDE_SESSION_UID + +echo 'startkde: Done.' 1>&2 diff --git a/SPECS/kde-workspace.spec b/SPECS/kde-workspace.spec old mode 100644 new mode 100755 index 18b4947..cf751fc --- a/SPECS/kde-workspace.spec +++ b/SPECS/kde-workspace.spec @@ -1,40 +1,54 @@ %if 0%{?fedora} %define gpsd 1 +%endif +%if 0%{?fedora} || 0%{?epel} %define webkit 1 %define libqalculate 1 %endif +%if 0%{?fedora} < 20 || 0%{?rhel} <= 7 +%define nepomuk 1 +%endif + +%if 0%{?fedora} > 18 || 0%{?rhel} > 6 +# Require kscreen, omit kcontrol/randr bits %define kscreen 1 +%endif + +%if 0%{?fedora} > 17 || 0%{?rhel} > 6 %define systemd_login1 1 +%endif Summary: KDE Workspace Name: kde-workspace -Version: 4.10.5 -Release: 21%{?dist} +Version: 4.11.19 +Release: 7%{?dist} License: GPLv2 URL: https://projects.kde.org/projects/kde/kde-workspace -%global revision %(echo %{version} | cut -d. -f3) -%if %{revision} >= 50 -%global stable unstable -%else -%global stable stable -%endif -Source0: http://download.kde.org/%{stable}/%{version}/src/kde-workspace-%{version}.tar.xz +Source0: http://download.kde.org/stable/applications/src/14.12.1/kde-workspace-%{version}.tar.xz + # create missing manpage Source1: ksysguarddrc.5 -# RH/Fedora-specific startkde changes -Patch1: kde-workspace-4.10.3-redhat_startkde.patch +# modified version of startkde.cmake, the template for startkde +# not shipped as a patch because we have been burned too often by new upstream +# additions that just break things for us +Source2: startkde.cmake + +# hack/workaround to invalidate (delete) plasma pixmap cache on upgrade +Source10: fedora-plasma-cache.sh # add konsole menuitem # FIXME? only show menu when/if konsole is installed? then we can drop the hard-dep Patch2: kde-workspace-4.9.90-plasma_konsole.patch -Requires: konsole # RH/Fedora-specific: VT numbers on fast user switching # could be handled dynamically eventually Patch3: kde-workspace-4.10.4-new-session-vt-numbers.patch +# RH/Fedora-specific: Force kdm and kdm_greet to be hardened +Patch4: kde-workspace-4.10.4-kdm-harden.patch + # 441062: packagekit tools do not show icons correctly on KDE Patch7: kdebase-workspace-4.6.80-krdb.patch @@ -47,17 +61,16 @@ Patch9: kdebase-workspace-4.4.90-rootprivs.patch # kio_sysinfo based on OpenSUSE's patch Patch15: kdebase-workspace-4.3.75-kio_sysinfo.patch -# show the remaining time in the battery plasmoid's popup (#515166) -# TODO: Can/should we do this with Plasma init/update scripts in kde-settings? -Patch16: kde-workspace-4.8.80-battery-plasmoid-showremainingtime.patch +# TODO: verify this still works (does not seem to for me) -- rex +Patch16: kde-workspace-4.10.90-battery-plasmoid-showremainingtime.patch # allow adding a "Leave..." button which brings up the complete shutdown dialog # to the classic menu (as in KDE <= 4.2.x); the default is still the upstream # default Leave submenu Patch17: kde-workspace-4.7.80-classicmenu-logout.patch -# kubuntu kudos! bulletproof-X bits ripped out -Patch19: kdebase-workspace-4.4.92-kdm_plymouth081.patch +# SUSE kudos! plymouth fixed by Laercio de Sousa and Stefan Brüns +Patch19: kde-workspace-4.11.1-kdm_plymouth081.patch Patch20: kdebase-workspace-4.4.92-xsession_errors_O_APPEND.patch # support the widgetStyle4 hack in the Qt KDE platform plugin @@ -74,7 +87,7 @@ Patch26: kde-workspace-4.10.2-systray_org.kde.ktp-presence.patch # add support for automatic multi-seat provided by systemd using existing reserve seats in KDM # needs having ServerCmd=/usr/lib/systemd/systemd-multi-seat-x set in /etc/kde/kdm/kdmrc -Patch27: kde-workspace-4.10.2-kdm-logind-multiseat.patch +Patch27: kde-workspace-4.11.1-kdm-logind-multiseat.patch # drop supplemental groups, if we don't call setgroups or initgroups then the # ksysguardd, kdm_greet process will inherit a random set of supplemental groups from parent process @@ -87,9 +100,12 @@ Patch30: kde-workspace-4.10.5-bz#1060058.patch # apps fail in KDE with unknown color name BACKGROUND Patch31: kde-workspace-4.10.5-bz#1043686-cpp.patch +# avoid conflict between kcm_colors 4 and plasma-desktop 5 +Patch32: kde-workspace-4.11.16-colorschemes-kde4.patch + ## upstreamable patches: # "keyboard stops working", https://bugs.kde.org/show_bug.cgi?id=171685#c135 -Patch50: kde-workspace-4.7.80-kde#171685.patch +Patch50: kde-workspace-4.10.90-kde#171685.patch # add apper to kickoff favorites # Apper is hard to find, http://bugzilla.redhat.com/850445 @@ -110,33 +126,48 @@ Patch54: kde-workspace-kcm_fonts_dont_change_on_load.patch # support BUILD_KCM_RANDR (default ON) option Patch55: kde-workspace-4.10.2-BUILD_KCM_RANDR.patch +# kdm (local) ipv6 +# https://bugzilla.redhat.com/show_bug.cgi?id=1187957 +Patch56: kde-workspace-kdm_local_ipv6.patch + # pam/systemd bogosity: kdm restart/shutdown does not work # http://bugzilla.redhat.com/796969 Patch57: kde-workspace-4.8.0-bug796969.patch -# merged patches: systemd-switch-user{,2} systemd-shutdown -# Support for systemd AND ConsoleKit in kworkspace -# contains a small hack, to be fixed properly soon -Patch62: kde-workspace-4.10.2-systemd-displaymanager.patch +# use backlight actual_brightness interface +Patch58: kde-workspace-4.11.0-backlight_actual_brightness.patch + +# https://bugs.kde.org/show_bug.cgi?id=330773#c5 +# bbcukmet: update to BBC's new json-based search and modified xml +Patch59: kde-workspace-4.11.7-weather-fix-bbcukmet.patch -# add a new panel to bottom by default -Patch63: kde-workspace-bz#1067111-panel-placing.patch +# https://bugs.kde.org/show_bug.cgi?id=330773#c6 +# bbcukmet: handle cases where min. or max. temperatures are not reported +Patch60: kde-workspace-4.11.7-weather-fix-bbcukmet-temp.patch # fix some warnings from coverity scan Patch64: kde-workspace-4.10.5-coverity-scan.patch +# https://bugs.kde.org/show_bug.cgi?id=330773#c16 +# bbcukmet: fix typo in the condition->icon matching ("clar sky" -> "clear sky") +Patch65: kde-workspace-4.11.7-weather-fix-bbcukmet-clear-sky.patch + +# https://bugs.kde.org/show_bug.cgi?id=332392 +# bbcukmet: fix a crash (#1079296/kde#332392) and improve error handling +Patch66: kde-workspace-4.11.7-weather-fix-bbcukmet-crash-kde#332392.patch + +# Get rid of dependency on kdepimlibs 4.11 +Patch67: kde-workspace-4.11-remove-dependency-on-kdepimlibs-4.11.patch + +# Fix coverity scan issues +Patch68: kde-workspace-4.11.19-coverity-scan-fixes.patch + ## upstream patches -Patch100: kde-workspace-4.11-bz#921781-check-max-viewport-size.patch Patch101: kde-workspace-4.10-bz#921742.patch -Patch102: kde-workspace-4.10.5-CVE-2013-4132.patch -Patch103: kde-workspace-4.10.5-CVE-2013-4133.patch Patch104: kde-workspace-4.10.x-bz#1001708.patch Patch105: kde-workspace-4.10.x-bz#1001727.patch Patch106: kde-workspace-4.10.5-rhbz990146.patch -Patch107: kde-workspace-4.10.5-solid-bz#1109987.patch -Patch108: kde-workspace-4.11-bz#1040678.patch Patch109: kde-workspace-4.11-bz#1090492.patch -Patch110: kde-workspace-4.11-bz#1109936.patch ## plasma active patches @@ -149,8 +180,10 @@ Patch210: kdebase-workspace-4.5.90-no_HAL2.patch Patch300: kde-workspace-4.8.3-webkit.patch Patch301: kde-workspace-4.10.5-bz#1063302-branding.patch Patch302: kde-workspace-exclude_kdm.patch +Patch303: powerdevil-upower-0.99.patch +Patch304: kde-workspace-revert-improve-systemtray-on-hdpi-displays.patch -## trunk patches +## trunk (Plasma 5) patches # pkg rename Obsoletes: kdebase-workspace < 4.7.97-10 @@ -158,16 +191,15 @@ Provides: kdebase-workspace = %{version}-%{release} Requires: polkit-kde %if 0%{?systemd_login1} -Requires: systemd >= 197-1.fc18.2 +Requires: systemd %endif %if ! 0%{?akonadi_subpkg} Obsoletes: %{name}-akonadi < %{version}-%{release} Provides: %{name}-akonadi = %{version}-%{release} Provides: plasma-dataengine-akonadi = %{version}-%{release} -%endif - Provides: plasma-dataengine-calendar = %{version}-%{release} +%endif # kwin apparently provides this internally, kwin/scripting/scripting.cpp # our scripts can't grok it automatically @@ -181,10 +213,18 @@ Provides: firstboot(windowmanager) = kwin Obsoletes: kdmtheme < 1.3 BuildRequires: desktop-file-utils -BuildRequires: kdelibs4-devel >= %{version} -BuildRequires: kdepimlibs-devel >= %{version} -BuildRequires: kactivities-devel >= %{version} -BuildRequires: nepomuk-core-devel >= %{version} +BuildRequires: kdelibs4-devel >= 4.14.4 +%if 0%{?webkit} +BuildRequires: kdelibs4-webkit-devel +%endif +BuildRequires: kdepimlibs-devel >= 4.10.5 +BuildRequires: kactivities-devel +%if 0%{?nepomuk} +BuildRequires: nepomuk-core-devel >= 4.10.5 +BuildRequires: pkgconfig(soprano) +%else +BuildConflicts: nepomuk-core-devel +%endif BuildRequires: libjpeg-devel BuildRequires: libutempter-devel %ifnarch s390 s390x @@ -212,24 +252,18 @@ BuildRequires: pkgconfig(libqalculate) BuildRequires: pkgconfig(libstreamanalyzer) # used for the Logitech mouse KCM, disabled until #399931 is fixed # BuildRequires: pkgconfig(libusb) +BuildRequires: pkgconfig(libudev) BuildRequires: pkgconfig(libxklavier) # added libnm-glib to workaround https://bugzilla.redhat.com/show_bug.cgi?id=685442 BuildRequires: pkgconfig(NetworkManager) pkgconfig(libnm-glib) BuildRequires: pkgconfig(polkit-qt-1) -%if 0%{?fedora} || 0%{?rhel} > 6 -BuildRequires: pkgconfig(python) -%else -BuildRequires: python-devel -%endif BuildRequires: pkgconfig(qimageblitz) BuildRequires: pkgconfig(QJson) -%if 0%{?webkit} -BuildRequires: pkgconfig(QtWebKit) -%endif -BuildRequires: pkgconfig(soprano) BuildRequires: pkgconfig(xau) BuildRequires: pkgconfig(xcb) +BuildRequires: pkgconfig(xcb-icccm) BuildRequires: pkgconfig(xcb-image) +BuildRequires: pkgconfig(xcb-keysyms) BuildRequires: pkgconfig(xcb-renderutil) BuildRequires: pkgconfig(xdmcp) BuildRequires: pkgconfig(xres) @@ -238,30 +272,33 @@ BuildRequires: pkgconfig(xcomposite) pkgconfig(xdamage) pkgconfig(xrender) BuildRequires: pkgconfig(gl) pkgconfig(glu) # kwin-gles %if 0%{?fedora} || 0%{?rhel} > 6 -BuildRequires: pkgconfig(glesv2) pkgconfig(egl) %define gles 1 +BuildRequires: pkgconfig(glesv2) pkgconfig(egl) +%if 0%{?fedora} +BuildRequires: pkgconfig(wayland-client) pkgconfig(wayland-egl) pkgconfig(wayland-server) +%endif %endif +BuildRequires: python2-devel -%if 0%{?googlegadgets} -BuildRequires: pkgconfig(libggadget-qt-1.0) > 0.10.5 -%else Obsoletes: kdebase-workspace-googlegadgets < 4.5.80-7 Obsoletes: plasma-scriptengine-googlegadgets < %{version}-%{release} -%endif +Requires: konsole Requires: %{name}-libs%{?_isa} = %{version}-%{release} Requires: kcm_colors = %{version}-%{release} %{?_kde4_macros_api:Requires: kde4-macros(api) = %{_kde4_macros_api} } -Requires: kde-runtime >= %{version} +Requires: kde-runtime >= 4.10.5 Obsoletes: kded_randrmonitor < 4.9.98-5 %if 0%{?kscreen} Requires: kscreen %endif +Requires: cpp # for kcm_keyboard Requires: iso-codes -Requires: kio_sysinfo +# activity manager +Requires: kactivities # for kscreenlocker Requires: kgreeter-plugins = %{version}-%{release} # startkde references: dbus-launch df mkdir test xmessage xprop xrandr xrdb xset xsetroot @@ -272,6 +309,13 @@ Requires: dbus-x11 Requires: xorg-x11-utils Requires: xorg-x11-server-utils +# Make sure we have kwin, but don't care whether it's our kwin, or kwin5 +Requires: kwin + +Requires: khotkeys +Requires: kmenuedit +Requires: kinfocenter + %define default_face_icon default1.png %if 0%{?fedora} || 0%{?rhel} > 6 Requires: kde-settings-ksplash @@ -298,10 +342,14 @@ Obsoletes: kdebase-workspace-devel < 4.7.97-10 Provides: kdebase-workspace-devel = %{version}-%{release} Provides: solid-bluetooth-devel = %{version}-%{release} Requires: ksysguard-libs%{?_isa} = %{version}-%{release} -Requires: %{name}-libs%{?_isa} = %{version}-%{release} +Requires: kwin-libs%{?_isa} = %{version}-%{release} %if 0%{?gles} Requires: kwin-gles-libs%{?_isa} = %{version}-%{release} %endif +Requires: %{name}-libs%{?_isa} = %{version}-%{release} +%if 0%{?akonadi_subpkg} +Requires: %{name}-akonadi%{?_isa} = %{version}-%{release} +%endif Requires: kdelibs4-devel %description devel %{summary}. @@ -312,10 +360,11 @@ Obsoletes: kdebase-workspace-libs < 4.7.97-10 Provides: kdebase-workspace-libs = %{version}-%{release} Provides: kdebase-workspace-libs%{?_isa} = %{version}-%{release} Provides: solid-bluetooth = %{version}-%{release} -Requires: kactivities%{?_isa} >= %{version} -Requires: kdelibs4%{?_isa} >= %{version} Requires: libkworkspace%{?_isa} = %{version}-%{release} -Requires: %{name} = %{version}-%{release} +%{?kdelibs4_requires} +# at least while oyxgen style is default +# make dep unversioned to allow plasma5's oxygen to replace it +Requires: kde-style-oxygen%{?_isa} %description libs %{summary}. @@ -331,7 +380,7 @@ BuildArch: noarch %package -n kcm_colors Summary: Colors KDE Control Module Conflicts: kde-workspace < 4.8.0-2 -Requires: kde-runtime >= %{version} +Requires: kde-runtime >= 4.10.5 %description -n kcm_colors The Color Selection module is comprised of several sections: * The Scheme tab, used to manage schemes @@ -357,7 +406,7 @@ Obsoletes: kdm < 4.7.3-9 Requires: kdm = %{version}-%{release} # http://bugzilla.redhat.com/753409 # http://bugzilla.redhat.com/784389 -Requires: kde-wallpapers = %{version} +Requires: kde-wallpapers BuildArch: noarch %description -n kdm-themes A collection of kdm themes, including: circles, horos, oxygen, oxygen-air, @@ -382,7 +431,7 @@ Requires: ksysguard-libs%{?_isa} = %{version}-%{release} Summary: Runtime libraries for ksysguard # when spilt occurred Conflicts: kdebase-workspace-libs < 4.7.2-2 -Requires: kdelibs4%{?_isa} >= %{version} +%{?kdelibs4_requires} %description -n ksysguard-libs %{summary}. @@ -391,10 +440,27 @@ Summary: Performance monitor daemon %description -n ksysguardd %{summary}. +%package -n kwin +Requires: %{name}-libs%{?_isa} = %{version}-%{release} +# When the kwin subpackage was split +Conflicts: kde-workspace <= 4.11.14-1 +Summary: KDE Window Manager + +%description -n kwin +%{summary}. + +%package -n kwin-libs +Summary: Runtime kwin libraries +# When kwin-libs subpackage was split +Conflicts: %{name}-libs%{?_isa} <= 4.11.14-1 +%description -n kwin-libs +%{summary}. + %package -n kwin-gles Summary: KWin built to support GLES # for libkwin* and friends -Requires: %{name}-libs%{?_isa} = %{version}-%{release} +Requires: kwin-libs%{?_isa} = %{version}-%{release} +Requires: kwin = %{version}-%{release} %description -n kwin-gles %{summary}. @@ -408,30 +474,35 @@ Summary: Runtime libkworkspace library # when spilt occurred Conflicts: kdebase-workspace-libs < 4.7.2-2 Obsoletes: kdebase-workspace-libs-kworkspace < 4.7.2-3 -Requires: kdelibs4%{?_isa} >= %{version} +%{?kdelibs4_requires} %description -n libkworkspace %{summary}. +%package -n kde-style-oxygen +Summary: Oxygen widget style for KDE +# when split from kde-workspace(-libs) +Conflicts: kde-workspace-libs < 4.11.2-2 +%description -n kde-style-oxygen +%{summary}. + +%package -n kdeclassic-cursor-theme +Summary: KDE Classic cursor theme +BuildArch: noarch +%description -n kdeclassic-cursor-theme +%{summary}. + %package -n oxygen-cursor-themes Summary: Oxygen cursor themes BuildArch: noarch %description -n oxygen-cursor-themes %{summary}. -%package -n plasma-scriptengine-googlegadgets -Summary: Plasma scriptengine for Google Desktop Gadgets -Requires: %{name} = %{version}-%{release} -Obsoletes: %{name}-googlegadgets < 4.5.80-7 -Provides: %{name}-googlegadgets = %{version}-%{release} -%description -n plasma-scriptengine-googlegadgets -%{summary}. - %package -n plasma-scriptengine-python Summary: Plasma scriptengine for python Obsoletes: %{name}-python-applet < 4.5.80-7 Provides: %{name}-python-applet = %{version}-%{release} Requires: %{name} = %{version}-%{release} -Requires: pykde4%{?_isa} >= %{version} +Requires: pykde4%{?_isa} %description -n plasma-scriptengine-python %{summary}. @@ -452,16 +523,40 @@ Requires: akonadi %description akonadi %{summary}. +%package -n khotkeys +Summary: Application to configure hotkeys in KDE +Conflicts: kde-workspace < 4.11.15-3 +Requires: khotkeys-libs%{?_isa} = %{version}-%{release} +%description -n khotkeys +%{summary}. + +%package -n khotkeys-libs +Summary: Runtime libraries for %{name} +Conflicts: kde-workspace < 4.11.15-3 +%description -n khotkeys-libs +%{summary}. + +%package -n kmenuedit +Summary: KDE Menu Editor +Conflicts: kde-workspace < 4.11.15-3 +%description -n kmenuedit +%{summary}. + +%package -n kinfocenter +Summary: KDE Info Center +Conflicts: kde-workspace < 4.11.15-3 +%description -n kinfocenter +%{summary}. %prep %setup -q -n kde-workspace-%{version} -%patch1 -p1 -b .redhat_startkde # Well, I looked at doing this using the context menu plugin system and it # looked like a lot more work than this simple patch to me. -- Kevin # FIXME/REBASE -- rex %patch2 -p1 -b .plasma-konsole %patch3 -p1 -b .vtnumbers +%patch4 -p1 -b .harden %patch7 -p1 -b .krdb %patch8 -p1 -b .klipper-url %patch9 -p1 -b .rootprivs @@ -478,6 +573,7 @@ Requires: akonadi %patch29 -p1 -b .kdm_greet-initgroups %patch30 -p1 -b .bz#1060058 %patch31 -p1 -b .bz#1043686 +%patch32 -p1 -b .colorschemes-kde4 # upstreamable patches %patch50 -p1 -b .kde#171685 @@ -486,23 +582,23 @@ Requires: akonadi %patch53 -p1 -b .kdm_xauth %patch54 -p1 -b .kcm_fonts_dont_change_on_load %patch55 -p1 -b .BUILD_KCM_RANDR +%patch56 -p0 -b .kdm_local_ipv6 %patch57 -p1 -b .bug796969 -%patch62 -p1 -b .systemd-displaymanager -%patch63 -p1 -b .panel-placing +%patch58 -p1 -b .backlight_actual_brightness +%patch59 -p1 -b .weather-fix-bbcukmet +%patch60 -p1 -b .weather-fix-bbcukmet-temp %patch64 -p1 -b .coverity-scan +%patch65 -p1 -b .weather-fix-bbcukmet-clear-sky +%patch66 -p1 -b .weather-fix-bbcukmet-crash +%patch67 -p1 -b .remove-dependency-on-kdepimlibs-4.11 +%patch68 -p1 -b .coverity-scan-fixes # upstream patches -%patch100 -p1 -b .bug921781 %patch101 -p1 -b .bug921742 -%patch102 -p1 -b .CVE-2013-4132 -%patch103 -p1 -b .CVE-2013-4133 %patch104 -p1 -b .bz#1001708 %patch105 -p1 -b .bz#1001727 %patch106 -p1 -b .bz#990146 -%patch107 -p1 -b .bz#1109987 -%patch108 -p1 -b .bz#1040678 %patch109 -p1 -b .bz#1090492 -%patch110 -p1 -b .bz#1109936 # Fedora patches %if 0%{?fedora} && 0%{?rhel} > 6 @@ -518,6 +614,8 @@ Requires: akonadi %if ! 0%{?kdm} %patch302 -p1 -b .bz#1070140 %endif +%patch303 -p1 -b .powerdevil-upower099 +%patch304 -p1 -b .improve-systemtray-on-hdpi-displays # trunk patches @@ -526,6 +624,26 @@ Requires: akonadi sed -i -e 's|198|197|g' powerdevil/daemon/backends/upower/powerdevilupowerbackend.cpp %endif +# ensure the file we are about to replace exists +[ -f startkde.cmake ] +# replace it with our known good, patched copy +cp -pf %{SOURCE2} startkde.cmake + +## some plasma-dataengine-extractor love +if [ -x %{_bindir}/plasma-dataengine-depextractor ] ; then +plasma-dataengine-depextractor plasma/desktop/applets/showActivityManager/package/ +plasma-dataengine-depextractor plasma/generic/applets/activitybar/ plasma-applet-activitybar.desktop +plasma-dataengine-depextractor plasma/generic/applets/analog-clock/ plasma-applet-analogclock.desktop +plasma-dataengine-depextractor plasma/generic/applets/batterymonitor/ +plasma-dataengine-depextractor plasma/generic/applets/devicenotifier/package/ +plasma-dataengine-depextractor plasma/generic/applets/digital-clock/ plasma-applet-digitalclock.desktop +plasma-dataengine-depextractor plasma/generic/applets/lock_logout +plasma-dataengine-depextractor plasma/generic/applets/notifications/ +plasma-dataengine-depextractor plasma/generic/applets/system-monitor/ plasma-applet-system-monitor.desktop +plasma-dataengine-depextractor plasma/generic/applets/webbrowser/ plasma-applet-webbrowser.desktop +plasma-dataengine-depextractor plasma/generic/runners/solid/ plasma-runner-solid.desktop +plasma-dataengine-depextractor plasma/netbook/applets/searchbox/ plasma-applet-searchbox.desktop +fi %build mkdir -p %{_target_platform} @@ -553,6 +671,11 @@ mv %{buildroot}%{_kde4_appsdir}/kdm/sessions/kde-plasma.desktop \ %{buildroot}%{_kde4_appsdir}/kdm/sessions/kde-plasma-safe.desktop \ %{buildroot}%{_datadir}/xsessions/ +# rename kde-plasma-safe.desktop to ensure it's sorted *after* kde-plasma.desktop +# https://bugzilla.redhat.com/show_bug.cgi?id=1164783 +mv %{buildroot}%{_datadir}/xsessions/kde-plasma-safe.desktop \ + %{buildroot}%{_datadir}/xsessions/kde-plasma99-safe.desktop + # remove extraneous xsession files rm -rfv %{buildroot}%{_kde4_appsdir}/kdm/sessions @@ -564,27 +687,40 @@ mkdir -p %{buildroot}%{_kde4_appsdir}/kdm/faces pushd %{buildroot}%{_kde4_appsdir}/kdm/faces ln -sf ../pics/users/%{default_face_icon} .default.face.icon popd + +bunzip2 %{buildroot}%{_kde4_docdir}/HTML/en/kdm/index.cache.bz2 +sed -i -e 's!name="id[a-z]*[0-9]*"!!g' %{buildroot}%{_kde4_docdir}/HTML/en/kdm/index.cache +sed -i -e 's!#id[a-z]*[0-9]*"!!g' %{buildroot}%{_kde4_docdir}/HTML/en/kdm/index.cache +bzip2 -9 %{buildroot}%{_kde4_docdir}/HTML/en/kdm/index.cache %else install -p -m644 kdm/kfrontend/sessions/kde-plasma.desktop.cmake %{buildroot}%{_datadir}/xsessions/1-kde-plasma-standard.desktop install -p -m644 kdm/kfrontend/sessions/kde-plasma-safe.desktop.cmake %{buildroot}%{_datadir}/xsessions/2-kde-plasma-safe.desktop %endif +# fedora-plasma-cache hack +mkdir -p %{buildroot}%{_sysconfdir}/kde/env/ +install -m644 -p %{SOURCE10} %{buildroot}%{_sysconfdir}/kde/env/ + # unpackaged files rm -rfv %{buildroot}%{_kde4_appsdir}/ksplash/Themes/Default/ +rm -fv %{buildroot}%{_kde4_libdir}/liboxygenstyle{,config}.so # fix documentation multilib conflict in index.cache for f in kcontrol/colors kmenuedit kcontrol/windowbehaviour kcontrol/kwindecoration \ - kcontrol/khotkeys kdm ksysguard ; do - if [ -f %{buildroot}%{_kde4_docdir}/HTML/en/$f/index.cache.bz2 ] ; then - bunzip2 %{buildroot}%{_kde4_docdir}/HTML/en/$f/index.cache.bz2 - sed -i -e 's!name="id[a-z]*[0-9]*"!!g' %{buildroot}%{_kde4_docdir}/HTML/en/$f/index.cache - sed -i -e 's!#id[a-z]*[0-9]*"!!g' %{buildroot}%{_kde4_docdir}/HTML/en/$f/index.cache - sed -i -e 's!href="#ftn.id[a-z]*[0-9]*"!!g' %{buildroot}%{_kde4_docdir}/HTML/en/$f/index.cache - sed -i -e 's!id="ftn.id[a-z]*[0-9]*"!!g' %{buildroot}%{_kde4_docdir}/HTML/en/$f/index.cache - bzip2 -9 %{buildroot}%{_kde4_docdir}/HTML/en/$f/index.cache - fi + kcontrol/khotkeys ksysguard plasma-desktop ; do + bunzip2 %{buildroot}%{_kde4_docdir}/HTML/en/$f/index.cache.bz2 + sed -i -e 's!name="id[a-z]*[0-9]*"!!g' %{buildroot}%{_kde4_docdir}/HTML/en/$f/index.cache + sed -i -e 's!#id[a-z]*[0-9]*"!!g' %{buildroot}%{_kde4_docdir}/HTML/en/$f/index.cache + sed -i -e 's!a href="#ftn.id[a-z]*[0-9]*"!a href="ftn"!g' %{buildroot}%{_kde4_docdir}/HTML/en/$f/index.cache + sed -i -e 's!id="ftn.id[a-z]*[0-9]*"!id="ftn"!g' %{buildroot}%{_kde4_docdir}/HTML/en/$f/index.cache + bzip2 -9 %{buildroot}%{_kde4_docdir}/HTML/en/$f/index.cache done +# fix multilib conflict cause by a different order of dataengines +sed -i 's!\(^X-Plasma-RequiredDataEngines=\).*!\1applicationjobs,notifications,powermanagement!' %{buildroot}%{_kde4_datadir}/kde4/services/plasma-applet-org.kde.notifications.desktop +sed -i 's!\(^X-Plasma-RequiredDataEngines=\).*!\1applicationjobs,notifications,powermanagement!' %{buildroot}%{_kde4_datadir}/kde4/apps/plasma/plasmoids/org.kde.notifications/metadata.desktop +sed -i 's!\(^X-Plasma-RequiredDataEngines=\).*!\1executable,soliddevice,systemmonitor!' %{buildroot}%{_kde4_datadir}/kde4/services/plasma-applet-system-monitor.desktop + # install manpage mkdir -p %{buildroot}%{_mandir}/man5 install -m 644 -p %{SOURCE1} %{buildroot}%{_mandir}/man5/ @@ -615,16 +751,15 @@ fi %files %doc COPYING README -%{_kde4_bindir}/kaccess -%{_kde4_bindir}/kapplymousetheme -%{_kde4_bindir}/kblankscrn.kss -%{_kde4_bindir}/kcheckrunning -%{_kde4_bindir}/kcminit -%{_kde4_bindir}/kcminit_startup -%{_kde4_bindir}/kdostartupconfig4 -%{_kde4_bindir}/kinfocenter +%{_sysconfdir}/kde/env/fedora-plasma-cache.sh +%{_kde4_bindir}/kaccess +%{_kde4_bindir}/kapplymousetheme +%{_kde4_bindir}/kblankscrn.kss +%{_kde4_bindir}/kcheckrunning +%{_kde4_bindir}/kcminit +%{_kde4_bindir}/kcminit_startup +%{_kde4_bindir}/kdostartupconfig4 %{_kde4_bindir}/klipper -%{_kde4_bindir}/kmenuedit %{_kde4_bindir}/krandom.kss %if ! 0%{?kscreen} %{_kde4_bindir}/krandrstartup @@ -639,16 +774,12 @@ fi %{_kde4_bindir}/ksplashqml %{_kde4_bindir}/kstartupconfig4 %{_kde4_bindir}/ksystraycmd -%{_kde4_bindir}/kwin -%{_kde4_bindir}/oxygen-demo -%{_kde4_bindir}/oxygen-settings %{_kde4_bindir}/oxygen-shadow-demo %{_kde4_bindir}/plasma-desktop %{_kde4_bindir}/plasma-netbook %{_kde4_bindir}/plasma-overlay %{_kde4_bindir}/plasma-windowed %{_kde4_bindir}/solid-action-desktop-gen -%{_kde4_bindir}/solid-network %{_kde4_bindir}/startkde %{_kde4_bindir}/systemsettings %{_kde4_appsdir}/desktoptheme/ @@ -660,16 +791,10 @@ fi %{_kde4_appsdir}/kcmkeys/ %{_kde4_appsdir}/kcmsolidactions/ %{_kde4_appsdir}/kcmstyle/ -%{_kde4_appsdir}/kcmusb/ -%ifnarch s390 s390x -%{_kde4_appsdir}/kcmview1394/ -%endif %{_kde4_appsdir}/kconf_update/ +%exclude %{_kde4_appsdir}/kconf_update/kwin_* %{_kde4_appsdir}/kcontrol/ %{_kde4_appsdir}/kdisplay/ -%{_kde4_appsdir}/khotkeys/ -%{_kde4_appsdir}/kinfocenter/ -%{_kde4_appsdir}/kmenuedit/ %dir %{_kde4_appsdir}/ksmserver/ %{_kde4_appsdir}/ksmserver/ksmserver.notifyrc %{_kde4_appsdir}/ksmserver/screenlocker/ @@ -681,88 +806,122 @@ fi %{_kde4_appsdir}/ksplash/Themes/None/ %{_kde4_appsdir}/ksplash/Themes/Simple/ %{_kde4_appsdir}/ksplash/Themes/SimpleSmall/ +%dir %{_kde4_appsdir}/kstyle +%dir %{_kde4_appsdir}/kstyle/themes/ +%{_kde4_appsdir}/kstyle/themes/qt*.themerc %{_kde4_appsdir}/kthememanager/ -%{_kde4_appsdir}/kwin/ %{_kde4_appsdir}/kwrited/ %{_kde4_appsdir}/plasma/ %{_kde4_appsdir}/plasma-desktop/ %{_kde4_appsdir}/plasma-netbook/ %{_kde4_appsdir}/powerdevil/ %{_kde4_appsdir}/solid/ -%{_kde4_appsdir}/solidfakenetbackend/ %{_kde4_appsdir}/systemsettings/ -# which package should own this? -%{_kde4_appsdir}/kstyle/themes/ %{_kde4_configdir}/activities.knsrc %{_kde4_configdir}/aurorae.knsrc +%if 0%{?kdm} +%{_kde4_configdir}/background.knsrc +%endif %{_kde4_configdir}/ksplash.knsrc -%{_kde4_configdir}/kwineffect.knsrc -%{_kde4_configdir}/kwinscripts.knsrc -%{_kde4_configdir}/kwinswitcher.knsrc %{_kde4_configdir}/plasma-overlayrc %{_kde4_configdir}/plasma-themes.knsrc %{_kde4_configdir}/wallpaper.knsrc %{_kde4_configdir}/xcursor.knsrc -%{_kde4_datadir}/kde4/services/* -%{_datadir}/xsessions/*.desktop +%{_kde4_datadir}/kde4/services/ScreenSavers +%{_kde4_datadir}/kde4/services/ServiceMenus +%{_kde4_datadir}/kde4/services/kded/*.desktop +%exclude %{_kde4_datadir}/kde4/services/kded/khotkeys.desktop +%{_kde4_datadir}/kde4/services/fonts.protocol +%{_kde4_datadir}/kde4/services/*.desktop %if 0%{?kdm} %exclude %{_kde4_datadir}/kde4/services/kdm.desktop %endif +%exclude %{_kde4_datadir}/kde4/services/kwin*.desktop +%exclude %{_kde4_datadir}/kde4/services/khotkeys.desktop +# KInfoCenter +%exclude %{_kde4_datadir}/kde4/services/kcmusb.desktop +%exclude %{_kde4_datadir}/kde4/services/kcm_infosummary.desktop +%exclude %{_kde4_datadir}/kde4/services/kcm_memory.desktop +%exclude %{_kde4_datadir}/kde4/services/devinfo.desktop +%exclude %{_kde4_datadir}/kde4/services/interrupts.desktop +%exclude %{_kde4_datadir}/kde4/services/nic.desktop +%exclude %{_kde4_datadir}/kde4/services/opengl.desktop +%exclude %{_kde4_datadir}/kde4/services/smbstatus.desktop +%exclude %{_kde4_datadir}/kde4/services/kcm_pci.desktop +%ifnarch s390 s390x +%exclude %{_kde4_datadir}/kde4/services/kcmview1394.desktop +%endif %{_kde4_datadir}/kde4/servicetypes/* +%exclude %{_kde4_datadir}/kde4/servicetypes/kwin*.desktop %{_kde4_datadir}/sounds/pop.wav %{_kde4_datadir}/autostart/klipper.desktop %{_kde4_datadir}/autostart/krunner.desktop %{_kde4_datadir}/autostart/plasma.desktop %{_kde4_datadir}/autostart/plasma-desktop.desktop %{_kde4_datadir}/applications/kde4/* +%exclude %{_kde4_datadir}/applications/kde4/kmenuedit.desktop +%exclude %{_kde4_datadir}/applications/kde4/kinfocenter.desktop %{_sysconfdir}/dbus-1/system.d/org.kde.fontinst.conf %{_sysconfdir}/dbus-1/system.d/org.kde.kcontrol.kcmclock.conf +%if 0%{?kdm} +%{_sysconfdir}/dbus-1/system.d/org.kde.kcontrol.kcmkdm.conf +%endif %{_sysconfdir}/dbus-1/system.d/org.kde.powerdevil.backlighthelper.conf %{_datadir}/dbus-1/interfaces/com.canonical.AppMenu.Registrar.xml %{_datadir}/dbus-1/interfaces/org.kde.kded.appmenu.xml %{_datadir}/dbus-1/interfaces/org.kde.KSMServerInterface.xml -%{_datadir}/dbus-1/interfaces/org.kde.KWin.xml -%{_datadir}/dbus-1/interfaces/org.kde.khotkeys.xml %{_datadir}/dbus-1/interfaces/org.kde.krunner.App.xml %{_datadir}/dbus-1/services/org.kde.fontinst.service %{_datadir}/dbus-1/services/org.kde.krunner.service %{_datadir}/dbus-1/system-services/org.kde.fontinst.service %{_datadir}/dbus-1/system-services/org.kde.kcontrol.kcmclock.service +%if 0%{?kdm} +%{_datadir}/dbus-1/system-services/org.kde.kcontrol.kcmkdm.service +%endif %{_datadir}/dbus-1/system-services/org.kde.powerdevil.backlighthelper.service -%{_kde4_datadir}/config.kcfg/* +%{_kde4_datadir}/config.kcfg/freespacenotifier.kcfg +%{_kde4_datadir}/config.kcfg/klaunch.kcfg +%{_kde4_datadir}/config.kcfg/plasma-shell-desktop.kcfg +%{_datadir}/xsessions/*.desktop %{_kde4_docdir}/HTML/en/kfontview/ %{_kde4_docdir}/HTML/en/kcontrol/ %exclude %{_kde4_docdir}/HTML/en/kcontrol/colors/ -%{_kde4_docdir}/HTML/en/kinfocenter/ %{_kde4_docdir}/HTML/en/klipper/ -%{_kde4_docdir}/HTML/en/kmenuedit/ %{_kde4_docdir}/HTML/en/plasma-desktop/ %{_kde4_docdir}/HTML/en/systemsettings/ %{_kde4_iconsdir}/hicolor/*/*/* %{_kde4_iconsdir}/oxygen/*/*/* +%exclude %{_kde4_iconsdir}/oxygen/*/*/kwin.* %{_kde4_libdir}/kde4/classic_mode.so %{_kde4_libdir}/kde4/devinfo.so %{_kde4_libdir}/kde4/icon_mode.so -%{_kde4_libdir}/kde4/imports/org/kde/kwin/ %{_kde4_libdir}/kde4/ion_*.so %{_kde4_libdir}/kde4/kcm_*.so %exclude %{_kde4_libdir}/kde4/kcm_colors.so %if 0%{?kdm} %exclude %{_kde4_libdir}/kde4/kcm_kdm.so %endif +%exclude %{_kde4_libdir}/kde4/kcm_kwin* +%exclude %{_kde4_libdir}/kde4/kcm_hotkeys.so +# KInfoCenter +%exclude %{_kde4_libdir}/kde4/kcm_usb.so +%exclude %{_kde4_libdir}/kde4/kcm_infosummary.so +%exclude %{_kde4_libdir}/kde4/kcm_memory.so +%exclude %{_kde4_libdir}/kde4/devinfo.so +%exclude %{_kde4_libdir}/kde4/kcm_info.so +%exclude %{_kde4_libdir}/kde4/kcm_samba.so +%exclude %{_kde4_libdir}/kde4/kcm_nic.so +%exclude %{_kde4_libdir}/kde4/kcm_opengl.so +%exclude %{_kde4_libdir}/kde4/kcm_pci.so +%ifnarch s390 s390x +%exclude %{_kde4_libdir}/kde4/kcm_view1394.so +%endif %{_kde4_libdir}/kde4/kded_*.so +%exclude %{_kde4_libdir}/kde4/kded_khotkeys.so %{_kde4_libdir}/kde4/keyboard_layout_widget.so %{_kde4_libdir}/kde4/krunner_*.so -%{_kde4_libdir}/kde4/kstyle_oxygen_config.so -%{_kde4_libdir}/kde4/kwin3_aurorae.so -%{_kde4_libdir}/kde4/kwin3_b2.so -%{_kde4_libdir}/kde4/kwin3_laptop.so -%{_kde4_libdir}/kde4/kwin3_oxygen.so -%{_kde4_libdir}/kde4/kwin4_effect_builtins.so -%{_kde4_libdir}/kde4/kwin_b2_config.so -%{_kde4_libdir}/kde4/kwin_oxygen_config.so %{_kde4_libdir}/kde4/plasma_animator_default.so %{_kde4_libdir}/kde4/plasma_applet_*.so %{_kde4_libdir}/kde4/plasma_containmentactions_*.so @@ -782,22 +941,19 @@ fi %{_kde4_libdir}/kde4/plasma_toolbox_*.so %{_kde4_libdir}/kde4/plasma_wallpaper_*.so %{_kde4_libdir}/kde4/powerdevil*.so -%{_kde4_libdir}/kde4/solid_*.so %{_kde4_libexecdir}/kcheckpass %{_kde4_libexecdir}/kcmdatetimehelper +%if 0%{?kdm} +%{_kde4_libexecdir}/kcmkdmhelper +%{_kde4_libexecdir}/krootimage +%endif %{_kde4_libexecdir}/kscreenlocker_greet -%{_kde4_libexecdir}/kwin_killer_helper -%{_kde4_libexecdir}/kwin_opengl_test -%{_kde4_libexecdir}/kwin_rules_dialog %{_kde4_libdir}/libkdeinit4_kaccess.so %{_kde4_libdir}/libkdeinit4_kcminit.so %{_kde4_libdir}/libkdeinit4_kcminit_startup.so %{_kde4_libdir}/libkdeinit4_klipper.so -%{_kde4_libdir}/libkdeinit4_kmenuedit.so %{_kde4_libdir}/libkdeinit4_krunner.so %{_kde4_libdir}/libkdeinit4_ksmserver.so -%{_kde4_libdir}/libkdeinit4_kwin.so -%{_kde4_libdir}/libkdeinit4_kwin_rules_dialog.so %{_kde4_libdir}/libkdeinit4_plasma-desktop.so %{_kde4_libdir}/libkdeinit4_plasma-netbook.so %{_kde4_libdir}/libkdeinit4_plasma-windowed.so @@ -806,19 +962,22 @@ fi %{_kde4_libdir}/libpowerdevilconfigcommonprivate.so %{_kde4_libdir}/libsystemsettingsview.so %{_kde4_libdir}/kconf_update_bin/* -%if 0%{?googlegadgets} -# googlegadgets -%exclude %{_kde4_libdir}/kde4/plasma_package_ggl.so -%exclude %{_kde4_libdir}/kde4/plasma_scriptengine_ggl.so -%exclude %{_kde4_datadir}/kde4/services/*googlegadgets.desktop -%endif -# python +%exclude %{_kde4_libdir}/kconf_update_bin/kwin_* +# python %exclude %{_kde4_datadir}/kde4/services/plasma-scriptengine*python.desktop # ruby %exclude %{_kde4_datadir}/kde4/services/plasma-scriptengine-*ruby*.desktop %if 0%{?akonadi_subpkg} +%exclude %{_kde4_libdir}/kde4/plasma_applet_calendar.so +%exclude %{_kde4_libdir}/kde4/plasma_applet_clock.so +%exclude %{_kde4_libdir}/kde4/plasma_applet_dig_clock.so %exclude %{_kde4_libdir}/kde4/plasma_engine_akonadi.so +%exclude %{_kde4_libdir}/kde4/plasma_engine_calendar.so +%exclude %{_kde4_datadir}/kde4/services/plasma-applet-analogclock.desktop +%exclude %{_kde4_datadir}/kde4/services/plasma-applet-calendar.desktop +%exclude %{_kde4_datadir}/kde4/services/plasma-applet-digitalclock.desktop %exclude %{_kde4_datadir}/kde4/services/plasma-engine-akonadi.desktop +%exclude %{_kde4_datadir}/kde4/services/plasma-dataengine-calendar.desktop %endif %{_kde4_bindir}/kfontinst %{_kde4_bindir}/kfontview @@ -834,6 +993,9 @@ fi %{_kde4_configdir}/kfontinst.knsrc %{_polkit_qt_policydir}/org.kde.fontinst.policy %{_polkit_qt_policydir}/org.kde.kcontrol.kcmclock.policy +%if 0%{?kdm} +%{_polkit_qt_policydir}/org.kde.kcontrol.kcmkdm.policy +%endif %{_polkit_qt_policydir}/org.kde.powerdevil.backlighthelper.policy %{_kde4_appsdir}/kfontinst/ %{_kde4_appsdir}/kfontview/ @@ -855,31 +1017,24 @@ fi %postun libs -p /sbin/ldconfig %files libs -%{_kde4_libdir}/libkdecorations.so.4* %{_kde4_libdir}/libkephal.so.4* %{_kde4_libdir}/libkfontinst.so.4* %{_kde4_libdir}/libkfontinstui.so.4* -%{_kde4_libdir}/libkhotkeysprivate.so.4* %{_kde4_libdir}/libkscreensaver.so.5* -%{_kde4_libdir}/libkwineffects.so.1* -%{_kde4_libdir}/libkwinnvidiahack.so.4* -%{_kde4_libdir}/liboxygenstyle.so.4* -%{_kde4_libdir}/liboxygenstyleconfig.so.4* %{_kde4_libdir}/libplasma-geolocation-interface.so.4* %{_kde4_libdir}/libplasma_applet-system-monitor.so.4* -%{_kde4_libdir}/libplasmaclock.so.4* +%if ! 0%{?akonadi_subpkg} +# libplasmaclock has an implicit dependency on plasma-dataengine-calendar + %{_kde4_libdir}/libplasmaclock.so.4* +%endif %{_kde4_libdir}/libplasmagenericshell.so.4* %{_kde4_libdir}/libpowerdevilui.so.4* -%{_kde4_libdir}/libsolidcontrol.so.4* -%{_kde4_libdir}/libsolidcontrolifaces.so.4* %{_kde4_libdir}/libsystemsettingsview.so.2* %{_kde4_libdir}/libtaskmanager.so.4* %{_kde4_libdir}/libweather_ion.so.6* %{_kde4_libdir}/libpowerdevilcore.so.0* %{_kde4_libdir}/libpowerdevilconfigcommonprivate.so.4* -%{_kde4_libdir}/libkwinglutils.so.1* %{_kde4_libdir}/kde4/plugins/gui_platform/libkde.so -%{_kde4_libdir}/kde4/plugins/styles/oxygen.so %files devel %{_kde4_includedir}/* @@ -895,8 +1050,6 @@ fi %{_kde4_libdir}/libkwineffects.so %{_kde4_libdir}/libkworkspace.so %{_kde4_libdir}/liblsofui.so -%{_kde4_libdir}/liboxygenstyle.so -%{_kde4_libdir}/liboxygenstyleconfig.so %{_kde4_libdir}/libplasma-geolocation-interface.so %{_kde4_libdir}/libplasma_applet-system-monitor.so %{_kde4_libdir}/libplasmaclock.so @@ -904,8 +1057,6 @@ fi %{_kde4_libdir}/libpowerdevilui.so %{_kde4_libdir}/libprocesscore.so %{_kde4_libdir}/libprocessui.so -%{_kde4_libdir}/libsolidcontrol.so -%{_kde4_libdir}/libsolidcontrolifaces.so %{_kde4_libdir}/libtaskmanager.so %{_kde4_libdir}/libweather_ion.so %{_kde4_libdir}/libkwinglutils.so @@ -919,7 +1070,7 @@ fi %files -n kcm_colors %{_kde4_datadir}/kde4/services/colors.desktop %{_kde4_libdir}/kde4/kcm_colors.so -%{_kde4_configdir}/colorschemes.knsrc +%{_kde4_configdir}/colorschemes-kde4.knsrc %{_kde4_docdir}/HTML/en/kcontrol/colors/ %{_kde4_appsdir}/color-schemes/Honeycomb.colors %{_kde4_appsdir}/color-schemes/Norway.colors @@ -940,7 +1091,6 @@ fi %{_kde4_libexecdir}/kdm_config %{_kde4_libexecdir}/kdm_greet %{_kde4_configdir}/kdm.knsrc -%{_kde4_configdir}/background.knsrc %{_kde4_docdir}/HTML/en/kdm/ %dir %{_kde4_appsdir}/doc %{_kde4_appsdir}/doc/kdm/ @@ -951,11 +1101,7 @@ fi %{_kde4_appsdir}/kdm/programs/ %dir %{_kde4_appsdir}/kdm/themes/ %{_kde4_datadir}/kde4/services/kdm.desktop -%{_sysconfdir}/dbus-1/system.d/org.kde.kcontrol.kcmkdm.conf -%{_datadir}/dbus-1/system-services/org.kde.kcontrol.kcmkdm.service -%{_kde4_libexecdir}/kcmkdmhelper -%{_kde4_libexecdir}/krootimage -%{_polkit_qt_policydir}/org.kde.kcontrol.kcmkdm.policy + %files -n kdm-themes %{_kde4_appsdir}/kdm/themes/ariya/ @@ -964,6 +1110,7 @@ fi %{_kde4_appsdir}/kdm/themes/horos/ %{_kde4_appsdir}/kdm/themes/oxygen/ %{_kde4_appsdir}/kdm/themes/oxygen-air/ +# not sure why this is included in kdm sources... ? -- rex %{_kde4_datadir}/wallpapers/stripes.png* %endif @@ -1043,6 +1190,86 @@ fi %files -n libkworkspace %{_kde4_libdir}/libkworkspace.so.4* +%post -n kde-style-oxygen -p /sbin/ldconfig +%postun -n kde-style-oxygen -p /sbin/ldconfig + +%files -n kde-style-oxygen +%{_kde4_bindir}/oxygen-demo +%{_kde4_bindir}/oxygen-settings +%{_kde4_appsdir}/kstyle/themes/oxygen.themerc +%{_kde4_libdir}/liboxygenstyle.so.4* +%{_kde4_libdir}/liboxygenstyleconfig.so.4* +%{_kde4_libdir}/kde4/kstyle_oxygen_config.so +%{_kde4_libdir}/kde4/plugins/styles/oxygen.so + + +%post -n kwin +touch --no-create %{_kde4_iconsdir}/oxygen &> /dev/null || : + +%posttrans -n kwin +gtk-update-icon-cache %{_kde4_iconsdir}/oxygen &> /dev/null || : +update-desktop-database -q &> /dev/null || : + +%postun -n kwin +if [ $1 -eq 0 ] ; then +touch --no-create %{_kde4_iconsdir}/oxygen &> /dev/null || : +gtk-update-icon-cache %{_kde4_iconsdir}/oxygen &> /dev/null || : +update-desktop-database -q &> /dev/null || : +fi + +%files -n kwin +%{_kde4_bindir}/kwin +%{_kde4_appsdir}/kwin/ +%{_kde4_configdir}/kwineffect.knsrc +%{_kde4_configdir}/kwinscripts.knsrc +%{_kde4_configdir}/kwinswitcher.knsrc +%{_kde4_datadir}/config.kcfg/kwin.kcfg +%{_datadir}/dbus-1/interfaces/org.kde.KWin.xml +%{_kde4_libdir}/kde4/kwin3_aurorae.so +%{_kde4_libdir}/kde4/kwin3_b2.so +%{_kde4_libdir}/kde4/kwin3_laptop.so +%{_kde4_libdir}/kde4/kwin3_oxygen.so +%{_kde4_libdir}/kde4/kwin4_effect_builtins.so +%{_kde4_libdir}/kde4/kwin_b2_config.so +%{_kde4_libdir}/kde4/kwin_oxygen_config.so +%{_kde4_libdir}/kde4/kcm_kwin* +%{_kde4_libexecdir}/kwin_killer_helper +%{_kde4_libexecdir}/kwin_opengl_test +%{_kde4_libexecdir}/kwin_rules_dialog +%{_kde4_libdir}/libkdeinit4_kwin.so +%{_kde4_libdir}/libkdeinit4_kwin_rules_dialog.so +%{_kde4_libdir}/kconf_update_bin/kwin_* +%{_kde4_libdir}/kde4/imports/org/kde/kwin/ +%{_kde4_iconsdir}/oxygen/*/*/kwin.* +%{_kde4_appsdir}/kconf_update/kwin_* +%{_kde4_datadir}/kde4/services/kwin +%{_kde4_datadir}/kde4/services/kwin*.desktop +%{_kde4_datadir}/kde4/servicetypes/kwin*.desktop + +%post -n kwin-libs -p /sbin/ldconfig +%postun -n kwin-libs -p /sbin/ldconfig + +%files -n kwin-libs +%{_kde4_libdir}/libkdecorations.so.4* +%{_kde4_libdir}/libkwineffects.so.1* +%{_kde4_libdir}/libkwinglutils.so.1* + + +%post -n kdeclassic-cursor-theme +touch --no-create %{_kde4_iconsdir}/KDE_Classic &> /dev/null || : + +%posttrans -n kdeclassic-cursor-theme +gtk-update-icon-cache %{_kde4_iconsdir}/KDE_Classic &> /dev/null || : + +%postun -n kdeclassic-cursor-theme +if [ $1 -eq 0 ] ; then +touch --no-create %{_kde4_iconsdir}/KDE_Classic &> /dev/null || : +gtk-update-icon-cache %{_kde4_iconsdir}/KDE_Classic &> /dev/null || : +fi + +%files -n kdeclassic-cursor-theme +%{_kde4_iconsdir}/KDE_Classic/ + %files -n oxygen-cursor-themes %{_kde4_iconsdir}/Oxygen_Black/ %{_kde4_iconsdir}/Oxygen_Blue/ @@ -1050,13 +1277,6 @@ fi %{_kde4_iconsdir}/Oxygen_Yellow/ %{_kde4_iconsdir}/Oxygen_Zion/ -%if 0%{?googlegadgets} -%files -n plasma-scriptengine-googlegadgets -%{_kde4_libdir}/kde4/plasma_package_ggl.so -%{_kde4_libdir}/kde4/plasma_scriptengine_ggl.so -%{_kde4_datadir}/kde4/services/*googlegadgets.desktop -%endif - %files -n plasma-scriptengine-python %{python_sitearch}/PyKDE4/plasmascript.py* %{_kde4_appsdir}/plasma_scriptengine_python @@ -1067,13 +1287,102 @@ fi %{_kde4_datadir}/kde4/services/plasma-scriptengine-*ruby*.desktop %if 0%{?akonadi_subpkg} +%post akonadi -p /sbin/ldconfig +%postun akonadi -p /sbin/ldconfig + %files akonadi +%{_kde4_libdir}/libplasmaclock.so.4* +%{_kde4_libdir}/kde4/plasma_applet_calendar.so +%{_kde4_libdir}/kde4/plasma_applet_clock.so +%{_kde4_libdir}/kde4/plasma_applet_dig_clock.so %{_kde4_libdir}/kde4/plasma_engine_akonadi.so +%{_kde4_libdir}/kde4/plasma_engine_calendar.so +%{_kde4_datadir}/kde4/services/plasma-applet-analogclock.desktop +%{_kde4_datadir}/kde4/services/plasma-applet-calendar.desktop +%{_kde4_datadir}/kde4/services/plasma-applet-digitalclock.desktop %{_kde4_datadir}/kde4/services/plasma-engine-akonadi.desktop +%{_kde4_datadir}/kde4/services/plasma-dataengine-calendar.desktop %endif +%files -n khotkeys +%{_kde4_libdir}/kde4/kded_khotkeys.so +%{_kde4_libdir}/kde4/kcm_hotkeys.so +%{_kde4_appsdir}/khotkeys/ +%{_kde4_datadir}/kde4/services/khotkeys.desktop +%{_kde4_datadir}/kde4/services/kded/khotkeys.desktop +%{_datadir}/dbus-1/interfaces/org.kde.khotkeys.xml + +%post -n khotkeys-libs -p /sbin/ldconfig +%postun -n khotkeys-libs -p /sbin/ldconfig + +%files -n khotkeys-libs +%{_kde4_libdir}/libkhotkeysprivate.so.4* + +%files -n kmenuedit +%{_kde4_bindir}/kmenuedit +%{_kde4_libdir}/libkdeinit4_kmenuedit.so +%{_kde4_appsdir}/kmenuedit +%{_kde4_datadir}/applications/kde4/kmenuedit.desktop +%{_kde4_docdir}/HTML/en/kmenuedit/ + +%files -n kinfocenter +%{_kde4_bindir}/kinfocenter +%{_kde4_libdir}/kde4/kcm_usb.so +%{_kde4_libdir}/kde4/kcm_infosummary.so +%{_kde4_libdir}/kde4/kcm_memory.so +%{_kde4_libdir}/kde4/devinfo.so +%{_kde4_libdir}/kde4/kcm_info.so +%{_kde4_libdir}/kde4/kcm_samba.so +%{_kde4_libdir}/kde4/kcm_nic.so +%{_kde4_libdir}/kde4/kcm_opengl.so +%{_kde4_libdir}/kde4/kcm_pci.so +%{_kde4_datadir}/applications/kde4/kinfocenter.desktop +%{_kde4_datadir}/kde4/services/kcmusb.desktop +%{_kde4_datadir}/kde4/services/kcm_infosummary.desktop +%{_kde4_datadir}/kde4/services/kcm_memory.desktop +%{_kde4_datadir}/kde4/services/devinfo.desktop +%{_kde4_datadir}/kde4/services/interrupts.desktop +%{_kde4_datadir}/kde4/services/nic.desktop +%{_kde4_datadir}/kde4/services/opengl.desktop +%{_kde4_datadir}/kde4/services/smbstatus.desktop +%{_kde4_datadir}/kde4/services/kcm_pci.desktop +%{_kde4_appsdir}/kinfocenter/ +%{_kde4_appsdir}/kcmusb/ +%{_kde4_docdir}/HTML/en/kinfocenter/ +%ifnarch s390 s390x +%{_kde4_libdir}/kde4/kcm_view1394.so +%{_kde4_datadir}/kde4/services/kcmview1394.desktop +%{_kde4_appsdir}/kcmview1394/ +%endif %changelog +* Mon Sep 07 2015 Jan Grulich - 4.11-19-7 +- Requires: cpp + Resolves: bz#1260129 + +* Tue Jul 07 2015 Jan Grulich - 4.11-19-6 +- Revert "Improve systemtray on HiDPI displays" + Resolves: #1238300 + +* Fri Jun 12 2015 Jan Grulich - 4.11.19-5 +- Fix accidentally removed KDE session + +* Mon Jun 08 2015 Jan Grulich - 4.11-19-4 +- Fix multilib issues++ + +* Mon Jun 08 2015 Jan Grulich - 4.11-19-3 +- Fix multilib issues + +* Fri Jun 05 2015 Jan Grulich - 4.11.19-2 +- Fix coverity scan issues +- Re-add kwin-gles subpackages + +* Tue Jun 02 2015 Jan Grulich - 4.11.19-1 +- Re-base to 4.11.19 (sync with F21) + +* Mon Apr 13 2015 Lukáš Tinkl - 4.10.5-22 +- Resolves: #1202801 - Backport patch to with upower 1.0 API + * Tue Sep 02 2014 Daniel Vrátil - 4.10.5-21 - Resolves: bz#1090492 - changing calendar settings does not appear in plasmoid until month changed (fixed patch) - Resolves: bz#1109936 - incorrect dispaly corner detection with multiple displays (fixed patch)