Import kactivities-kf5_5.62.0.orig.tar.xz
authorMaximiliano Curia <maxy@debian.org>
Fri, 20 Sep 2019 17:12:51 +0000 (18:12 +0100)
committerMaximiliano Curia <maxy@debian.org>
Fri, 20 Sep 2019 17:12:51 +0000 (18:12 +0100)
[dgit import orig kactivities-kf5_5.62.0.orig.tar.xz]

114 files changed:
.arcconfig [new file with mode: 0644]
.clang-format [new file with mode: 0644]
.gitignore [new file with mode: 0644]
.videproject/project.conf [new file with mode: 0644]
.videproject/vimrc [new file with mode: 0644]
.vim-template:cpp [new file with mode: 0644]
.vim-template:h [new file with mode: 0644]
CMakeLists.txt [new file with mode: 0644]
COPYING [new file with mode: 0644]
COPYING.LGPL-2 [new file with mode: 0644]
COPYING.LIB [new file with mode: 0644]
KF5ActivitiesConfig.cmake.in [new file with mode: 0644]
MAINTAINER [new file with mode: 0644]
README [new file with mode: 0644]
README.developers [new file with mode: 0644]
README.md [new file with mode: 0644]
README.packagers [new file with mode: 0644]
TODO [new file with mode: 0644]
autotests/CMakeLists.txt [new file with mode: 0644]
autotests/common/test.cpp [new file with mode: 0644]
autotests/common/test.h [new file with mode: 0644]
autotests/core/CMakeLists.txt [new file with mode: 0644]
autotests/core/CleanOnlineTest.cpp [new file with mode: 0644]
autotests/core/CleanOnlineTest.h [new file with mode: 0644]
autotests/core/OfflineTest.cpp [new file with mode: 0644]
autotests/core/OfflineTest.h [new file with mode: 0644]
autotests/core/Process.cpp [new file with mode: 0644]
autotests/core/Process.h [new file with mode: 0644]
autotests/core/main.cpp [new file with mode: 0644]
contrib/bash/next-activity.sh [new file with mode: 0755]
contrib/bash/prev-activity.sh [new file with mode: 0755]
contrib/commit.sh [new file with mode: 0755]
contrib/run-krazy.sh [new file with mode: 0755]
contrib/update-todo.hs [new file with mode: 0755]
contrib/zsh/kamd-functions [new file with mode: 0644]
docs/Doxyfile.local [new file with mode: 0644]
kactivities.categories [new file with mode: 0644]
metainfo.yaml [new file with mode: 0644]
src/CMakeLists.txt [new file with mode: 0644]
src/cli/CMakeLists.txt [new file with mode: 0644]
src/cli/main.cpp [new file with mode: 0644]
src/cli/utils.h [new file with mode: 0644]
src/common/dbus/common.h [new file with mode: 0644]
src/common/dbus/org.kde.ActivityManager.Activities.cpp [new file with mode: 0644]
src/common/dbus/org.kde.ActivityManager.Activities.h [new file with mode: 0644]
src/common/dbus/org.kde.ActivityManager.Activities.xml [new file with mode: 0644]
src/common/dbus/org.kde.ActivityManager.Application.xml [new file with mode: 0644]
src/common/dbus/org.kde.ActivityManager.Features.xml [new file with mode: 0644]
src/common/dbus/org.kde.ActivityManager.Resources.xml [new file with mode: 0644]
src/common/dbus/org.kde.ActivityManager.ResourcesLinking.xml [new file with mode: 0644]
src/common/dbus/org.kde.ActivityManager.ResourcesScoring.xml [new file with mode: 0644]
src/imports/CMakeLists.txt [new file with mode: 0644]
src/imports/README [new file with mode: 0644]
src/imports/activitiesextensionplugin.cpp [new file with mode: 0644]
src/imports/activitiesextensionplugin.h [new file with mode: 0644]
src/imports/activityinfo.cpp [new file with mode: 0644]
src/imports/activityinfo.h [new file with mode: 0644]
src/imports/activitymodel.cpp [new file with mode: 0644]
src/imports/activitymodel.h [new file with mode: 0644]
src/imports/qmldir [new file with mode: 0644]
src/imports/resourceinstance.cpp [new file with mode: 0644]
src/imports/resourceinstance.h [new file with mode: 0644]
src/imports/resourcemodel.cpp [new file with mode: 0644]
src/imports/resourcemodel.h [new file with mode: 0644]
src/kactivities-features.h.cmake [new file with mode: 0644]
src/lib/CMakeLists.txt [new file with mode: 0644]
src/lib/activitiescache_p.cpp [new file with mode: 0644]
src/lib/activitiescache_p.h [new file with mode: 0644]
src/lib/activitiesmodel.cpp [new file with mode: 0644]
src/lib/activitiesmodel.h [new file with mode: 0644]
src/lib/activitiesmodel_p.h [new file with mode: 0644]
src/lib/consumer.cpp [new file with mode: 0644]
src/lib/consumer.h [new file with mode: 0644]
src/lib/consumer_p.h [new file with mode: 0644]
src/lib/controller.cpp [new file with mode: 0644]
src/lib/controller.h [new file with mode: 0644]
src/lib/info.cpp [new file with mode: 0644]
src/lib/info.h [new file with mode: 0644]
src/lib/info_p.h [new file with mode: 0644]
src/lib/libKActivities.pc.cmake [new file with mode: 0644]
src/lib/mainthreadexecutor_p.cpp [new file with mode: 0644]
src/lib/mainthreadexecutor_p.h [new file with mode: 0644]
src/lib/manager_p.cpp [new file with mode: 0644]
src/lib/manager_p.h [new file with mode: 0644]
src/lib/resourceinstance.cpp [new file with mode: 0644]
src/lib/resourceinstance.h [new file with mode: 0644]
src/lib/version.cpp [new file with mode: 0644]
src/lib/version.h [new file with mode: 0644]
src/utils/continue_with.h [new file with mode: 0644]
src/utils/dbusfuture_p.cpp [new file with mode: 0644]
src/utils/dbusfuture_p.h [new file with mode: 0644]
src/utils/model_updaters.h [new file with mode: 0644]
src/utils/optional_view.h [new file with mode: 0644]
src/utils/ptr_to.h [new file with mode: 0644]
src/utils/qflatset.h [new file with mode: 0644]
src/utils/range.h [new file with mode: 0644]
src/utils/remove_if.h [new file with mode: 0644]
tests/CMakeLists.txt [new file with mode: 0644]
tests/activities-model/CMakeLists.txt [new file with mode: 0644]
tests/activities-model/main.cpp [new file with mode: 0644]
tests/activities-model/window.cpp [new file with mode: 0644]
tests/activities-model/window.h [new file with mode: 0644]
tests/activities-model/window.ui [new file with mode: 0644]
tests/imports/activities.qml [new file with mode: 0644]
tests/imports/org.kde.listactivitiestest/contents/ui/main.qml [new file with mode: 0644]
tests/imports/org.kde.listactivitiestest/metadata.desktop [new file with mode: 0644]
tests/imports/plasma-applet-org.kde.listactivitiestest.desktop [new file with mode: 0644]
tests/imports/resources.qml [new file with mode: 0644]
tests/slc-interface/CMakeLists.txt [new file with mode: 0644]
tests/slc-interface/main.cpp [new file with mode: 0644]
tests/slc-interface/window.cpp [new file with mode: 0644]
tests/slc-interface/window.h [new file with mode: 0644]
tests/slc-interface/window.ui [new file with mode: 0644]
vim-extrarc [new file with mode: 0644]

diff --git a/.arcconfig b/.arcconfig
new file mode 100644 (file)
index 0000000..103511e
--- /dev/null
@@ -0,0 +1,4 @@
+{
+    "project.name" : "KActivities",
+    "phabricator.uri" : "https://phabricator.kde.org/"
+}
diff --git a/.clang-format b/.clang-format
new file mode 100644 (file)
index 0000000..54db9fa
--- /dev/null
@@ -0,0 +1,49 @@
+---
+# BasedOnStyle:  WebKit
+Language:        Cpp
+AccessModifierOffset: -4
+ConstructorInitializerIndentWidth: 4
+AlignEscapedNewlinesLeft: false
+AlignTrailingComments: false
+AllowAllParametersOfDeclarationOnNextLine: true
+AllowShortIfStatementsOnASingleLine: false
+AllowShortLoopsOnASingleLine: false
+AllowShortFunctionsOnASingleLine: false
+AlwaysBreakTemplateDeclarations: false
+AlwaysBreakBeforeMultilineStrings: false
+BreakBeforeBinaryOperators: true
+BreakBeforeTernaryOperators: true
+BreakConstructorInitializersBeforeComma: true
+BinPackParameters: true
+ColumnLimit:     80
+ConstructorInitializerAllOnOneLineOrOnePerLine: true
+DerivePointerBinding: false
+ExperimentalAutoDetectBinPacking: false
+IndentCaseLabels: true
+MaxEmptyLinesToKeep: 1
+NamespaceIndentation: None
+ObjCSpaceBeforeProtocolList: true
+PenaltyBreakBeforeFirstCallParameter: 19
+PenaltyBreakComment: 60
+PenaltyBreakString: 1000
+PenaltyBreakFirstLessLess: 120
+PenaltyExcessCharacter: 1000000
+PenaltyReturnTypeOnItsOwnLine: 60
+PointerBindsToType: false
+SpacesBeforeTrailingComments: 1
+Cpp11BracedListStyle: false
+Standard:        Cpp11
+IndentWidth:     4
+TabWidth:        8
+UseTab:          Never
+BreakBeforeBraces: Stroustrup
+IndentFunctionDeclarationAfterType: false
+SpacesInParentheses: false
+SpacesInAngles:  false
+SpaceInEmptyParentheses: false
+SpacesInCStyleCastParentheses: false
+SpaceAfterControlStatementKeyword: true
+SpaceBeforeAssignmentOperators: true
+ContinuationIndentWidth: 4
+...
+
diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..a94adba
--- /dev/null
@@ -0,0 +1,18 @@
+.debug
+tags
+_tests
+.videproject/ctags
+*swp
+*~
+.kdev4
+.cmake-params
+.ycm_extra_conf.py
+.ycm_extra_conf.pyc
+.clang_complete
+kactivities.kdev4
+compile_commands.json
+/apidocs
+GPATH
+GRTAGS
+GSYMS
+GTAGS
diff --git a/.videproject/project.conf b/.videproject/project.conf
new file mode 100644 (file)
index 0000000..fb52c7b
--- /dev/null
@@ -0,0 +1,30 @@
+[Project]
+Name=KDE Activities
+Location=/opt/kde/src/libs/kactivities
+
+MakeCommand='OBJ_REPLACEMENT="s:/opt/kde/src/core/libs/kactivities:/opt/kde/build-clang/core/libs/kactivities:" makeobj'
+
+RunLocation=./
+RunCommand=./
+
+[General]
+OverrideVimCommands=1
+AutoInsertModeOnOpen=0
+Tags=
+SourcePaths=.
+
+[KeyBindings]
+SidePanelOpen='<C-A>'
+MakeProject='<F5>'
+RunProject='<F6>'
+
+[Grep]
+Root=.
+
+[Find]
+Root=.
+
+[QuickBrowser]
+iNotifyEnabled=0
+Root=.
+TagsSystem=ctags
diff --git a/.videproject/vimrc b/.videproject/vimrc
new file mode 100644 (file)
index 0000000..a8dbe5c
--- /dev/null
@@ -0,0 +1,2 @@
+
+set tags+=/opt/kde4trunk/ctags/libQt.tags
diff --git a/.vim-template:cpp b/.vim-template:cpp
new file mode 100644 (file)
index 0000000..30a683c
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ *   Copyright (C) %YEAR% %USER% <%MAIL%>
+ *
+ *   This library is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU Lesser General Public
+ *   License as published by the Free Software Foundation; either
+ *   version 2.1 of the License, or (at your option) version 3, or any
+ *   later version accepted by the membership of KDE e.V. (or its
+ *   successor approved by the membership of KDE e.V.), which shall
+ *   act as a proxy defined in Section 6 of version 3 of the license.
+ *
+ *   This library is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *   Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this library.
+ *   If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "%FILE%.h"
+
+namespace KActivities {
+
+%HERE%
+
+} // namespace KActivities
+
diff --git a/.vim-template:h b/.vim-template:h
new file mode 100644 (file)
index 0000000..84eddbb
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ *   Copyright (C) %YEAR% %USER% <%MAIL%>
+ *
+ *   This library is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU Lesser General Public
+ *   License as published by the Free Software Foundation; either
+ *   version 2.1 of the License, or (at your option) version 3, or any
+ *   later version accepted by the membership of KDE e.V. (or its
+ *   successor approved by the membership of KDE e.V.), which shall
+ *   act as a proxy defined in Section 6 of version 3 of the license.
+ *
+ *   This library is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *   Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this library.
+ *   If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef KACTIVITIES_STATS_%GUARD%
+#define KACTIVITIES_STATS_%GUARD%
+
+namespace KActivities {
+
+%HERE%
+
+} // namespace KActivities
+
+#endif // KACTIVITIES_STATS_%GUARD%
+
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644 (file)
index 0000000..a8d0d55
--- /dev/null
@@ -0,0 +1,125 @@
+# vim:set softtabstop=3 shiftwidth=3 tabstop=3 expandtab:
+
+cmake_minimum_required(VERSION 3.5)
+
+# KDE Frameworks
+set(KF5_VERSION "5.62.0") # handled by release scripts
+set(KF5_DEP_VERSION "5.62.0") # handled by release scripts
+project (KActivities VERSION ${KF5_VERSION})
+
+option (KACTIVITIES_LIBRARY_ONLY "If true, compiles only the KActivities library, without the QML imports." OFF)
+option (KACTIVITIES_ENABLE_EXCEPTIONS "If you have Boost 1.53, you need to build KActivities with exceptions enabled. This is UNTESTED and EXPERIMENTAL!" OFF)
+
+set (REQUIRED_QT_VERSION 5.11.0)
+
+# We don't build in-source
+if ("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}")
+   message (
+      FATAL_ERROR
+      "kactivities require an out of source build. Please create a separate build directory and run 'cmake path_to_sources [options]' there."
+   )
+endif ()
+
+set (KACTIVITIES_CURRENT_ROOT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
+
+# Extra CMake stuff
+include(FeatureSummary)
+find_package(ECM 5.62.0  NO_MODULE)
+set_package_properties(ECM PROPERTIES TYPE REQUIRED DESCRIPTION "Extra CMake Modules." URL "https://projects.kde.org/projects/kdesupport/extra-cmake-modules")
+feature_summary(WHAT REQUIRED_PACKAGES_NOT_FOUND FATAL_ON_MISSING_REQUIRED_PACKAGES)
+
+set (CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH})
+
+include (KDEInstallDirs)
+include (KDECMakeSettings)
+include (KDEFrameworkCompilerSettings NO_POLICY_SCOPE)
+include (GenerateExportHeader)
+include (ECMGenerateHeaders)
+include (ECMQtDeclareLoggingCategory)
+include (ECMAddQch)
+include (ECMMarkNonGuiExecutable)
+
+option(BUILD_QCH "Build API documentation in QCH format (for e.g. Qt Assistant, Qt Creator & KDevelop)" OFF)
+add_feature_info(QCH ${BUILD_QCH} "API documentation in QCH format (for e.g. Qt Assistant, Qt Creator & KDevelop)")
+
+# Qt
+set (CMAKE_AUTOMOC ON)
+find_package (Qt5 ${REQUIRED_QT_VERSION} CONFIG REQUIRED COMPONENTS Core DBus)
+
+# Basic includes
+include (CPack)
+
+include (CMakePackageConfigHelpers)
+include (ECMSetupVersion)
+
+message ("We are using the ${CMAKE_CXX_COMPILER_ID} compiler")
+if ((CMAKE_CXX_COMPILER_ID STREQUAL "Clang") OR (KACTIVITIES_OVERRIDE_VISIBILITY STREQUAL "default"))
+   message ("Setting visibility preset to default")
+   set(CMAKE_CXX_VISIBILITY_PRESET default)
+   set(CMAKE_VISIBILITY_INLINES_HIDDEN 0)
+   string (REPLACE "-fvisibility-inlines-hidden" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
+   string (REPLACE "-fvisibility=hidden" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
+endif ()
+
+# libKActivities
+
+ecm_setup_version (
+   PROJECT
+   VARIABLE_PREFIX KACTIVITIES
+   VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/kactivities_version.h"
+   PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/KF5ActivitiesConfigVersion.cmake"
+   SOVERSION 5
+   )
+
+add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x060000)
+add_definitions(-DQT_NO_FOREACH)
+
+add_subdirectory (src)
+if (BUILD_TESTING)
+    add_subdirectory (autotests)
+    add_subdirectory (tests)
+endif()
+
+set (CMAKECONFIG_INSTALL_DIR "${KDE_INSTALL_CMAKEPACKAGEDIR}/KF5Activities")
+
+if (BUILD_QCH)
+    ecm_install_qch_export(
+        TARGETS KF5Activities_QCH
+        FILE KF5ActivitiesLibraryQchTargets.cmake
+        DESTINATION "${CMAKECONFIG_INSTALL_DIR}"
+        COMPONENT Devel
+    )
+    set(PACKAGE_INCLUDE_QCHTARGETS "include(\"\${CMAKE_CURRENT_LIST_DIR}/KF5ActivitiesLibraryQchTargets.cmake\")")
+endif()
+
+install (
+   EXPORT KF5ActivitiesLibraryTargets
+   DESTINATION "${CMAKECONFIG_INSTALL_DIR}"
+   FILE KF5ActivitiesLibraryTargets.cmake
+   NAMESPACE KF5::
+   )
+
+configure_package_config_file (
+   "${CMAKE_CURRENT_SOURCE_DIR}/KF5ActivitiesConfig.cmake.in"
+   "${CMAKE_CURRENT_BINARY_DIR}/KF5ActivitiesConfig.cmake"
+   INSTALL_DESTINATION  ${CMAKECONFIG_INSTALL_DIR}
+   PATH_VARS  KF5_INCLUDE_INSTALL_DIR CMAKE_INSTALL_PREFIX
+   )
+
+install (
+   FILES "${CMAKE_CURRENT_BINARY_DIR}/KF5ActivitiesConfig.cmake"
+         "${CMAKE_CURRENT_BINARY_DIR}/KF5ActivitiesConfigVersion.cmake"
+   DESTINATION "${CMAKECONFIG_INSTALL_DIR}"
+   COMPONENT Devel
+   )
+
+install (
+   FILES ${CMAKE_CURRENT_BINARY_DIR}/kactivities_version.h
+   DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5} COMPONENT Devel
+   )
+
+install(FILES kactivities.categories  DESTINATION  ${KDE_INSTALL_LOGGINGCATEGORIESDIR})
+
+# Write out the features
+feature_summary (WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES)
+
diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..d159169
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,339 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                            NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/COPYING.LGPL-2 b/COPYING.LGPL-2
new file mode 100644 (file)
index 0000000..5bc8fb2
--- /dev/null
@@ -0,0 +1,481 @@
+                  GNU LIBRARY GENERAL PUBLIC LICENSE
+                       Version 2, June 1991
+
+ Copyright (C) 1991 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the library GPL.  It is
+ numbered 2 because it goes with version 2 of the ordinary GPL.]
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Library General Public License, applies to some
+specially designated Free Software Foundation software, and to any
+other libraries whose authors decide to use it.  You can use it for
+your libraries, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if
+you distribute copies of the library, or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link a program with the library, you must provide
+complete object files to the recipients so that they can relink them
+with the library, after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  Our method of protecting your rights has two steps: (1) copyright
+the library, and (2) offer you this license which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  Also, for each distributor's protection, we want to make certain
+that everyone understands that there is no warranty for this free
+library.  If the library is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original
+version, so that any problems introduced by others will not reflect on
+the original authors' reputations.
+\f
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that companies distributing free
+software will individually obtain patent licenses, thus in effect
+transforming the program into proprietary software.  To prevent this,
+we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+  Most GNU software, including some libraries, is covered by the ordinary
+GNU General Public License, which was designed for utility programs.  This
+license, the GNU Library General Public License, applies to certain
+designated libraries.  This license is quite different from the ordinary
+one; be sure to read it in full, and don't assume that anything in it is
+the same as in the ordinary license.
+
+  The reason we have a separate public license for some libraries is that
+they blur the distinction we usually make between modifying or adding to a
+program and simply using it.  Linking a program with a library, without
+changing the library, is in some sense simply using the library, and is
+analogous to running a utility program or application program.  However, in
+a textual and legal sense, the linked executable is a combined work, a
+derivative of the original library, and the ordinary General Public License
+treats it as such.
+
+  Because of this blurred distinction, using the ordinary General
+Public License for libraries did not effectively promote software
+sharing, because most developers did not use the libraries.  We
+concluded that weaker conditions might promote sharing better.
+
+  However, unrestricted linking of non-free programs would deprive the
+users of those programs of all benefit from the free status of the
+libraries themselves.  This Library General Public License is intended to
+permit developers of non-free programs to use free libraries, while
+preserving your freedom as a user of such programs to change the free
+libraries that are incorporated in them.  (We have not seen how to achieve
+this as regards changes in header files, but we have achieved it as regards
+changes in the actual functions of the Library.)  The hope is that this
+will lead to faster development of free libraries.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, while the latter only
+works together with the library.
+
+  Note that it is possible for a library to be covered by the ordinary
+General Public License rather than by this special one.
+\f
+                  GNU LIBRARY GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library which
+contains a notice placed by the copyright holder or other authorized
+party saying it may be distributed under the terms of this Library
+General Public License (also called "this License").  Each licensee is
+addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+  
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+\f
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+\f
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+\f
+  6. As an exception to the Sections above, you may also compile or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    c) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    d) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the source code distributed need not include anything that is normally
+distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+\f
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+\f
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Library General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+\f
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+                            NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+\f
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/COPYING.LIB b/COPYING.LIB
new file mode 100644 (file)
index 0000000..4362b49
--- /dev/null
@@ -0,0 +1,502 @@
+                  GNU LESSER GENERAL PUBLIC LICENSE
+                       Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+  When we speak of free software, we are referring to freedom of use,
+not price.  Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+\f
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard.  To achieve this, non-free programs must be
+allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+\f
+                  GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+\f
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+\f
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+\f
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+\f
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+\f
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+\f
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+                            NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+\f
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/KF5ActivitiesConfig.cmake.in b/KF5ActivitiesConfig.cmake.in
new file mode 100644 (file)
index 0000000..25291c9
--- /dev/null
@@ -0,0 +1,7 @@
+@PACKAGE_INIT@
+
+include(CMakeFindDependencyMacro)
+find_dependency(Qt5Core @REQUIRED_QT_VERSION@)
+
+include("${CMAKE_CURRENT_LIST_DIR}/KF5ActivitiesLibraryTargets.cmake")
+@PACKAGE_INCLUDE_QCHTARGETS@
diff --git a/MAINTAINER b/MAINTAINER
new file mode 100644 (file)
index 0000000..51d257d
--- /dev/null
@@ -0,0 +1,3 @@
+
+Current kactivities maintainer is: Ivan Čukić <ivan.cukic@kde.org>
+
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..43a57c6
--- /dev/null
+++ b/README
@@ -0,0 +1,6 @@
+See README.developers and README.packagers (the former is also for users).
+
+In order to properly display the files, use the GNU man command.
+
+man README.developers
+man README.packagers
diff --git a/README.developers b/README.developers
new file mode 100644 (file)
index 0000000..575d704
--- /dev/null
@@ -0,0 +1,87 @@
+
+
+.\" " " " " " " " " " " " " " " " " " " " " " " " " " " "
+.\"                                                     "
+.\" It is best to view this file with the man tool:     "
+.\" man ./README.developers                             "
+.\"                                                     "
+.\" " " " " " " " " " " " " " " " " " " " " " " " " " " "
+
+
+.TH README KAMD  2012-08-29 "KDE" "KActivities Developers"
+
+.SH COMMIT POLICY
+
+Every non-trivial patch must go through the review before it goes into the
+master branch.
+
+    http://git.reviewboard.kde.org
+        repository: kactivities
+        groups:     plasma
+        people:     Ivan Cukic
+
+If you don't have an account for identity.kde.org, you can send smaller
+patches to the plasma-devel@kde.org mailing list, or (please don't) directly
+to the repository maintainer (see MAINTAINER file).
+
+
+.SH CODE POLICY
+
+The code needs to follow KDElibs coding style in *all* parts of the project,
+not only the library. You can find more information about the style here:
+http://techbase.kde.org/Policies/Kdelibs_Coding_Style
+
+Macros in CMakeLists.txt should be lowercase throughout the project,
+and indentation should be 4, with the closing parenthesis in the same level
+as the content.
+
+.SH COMPILER COMPATIBILITY
+
+The library (src/lib) needs to be compilable with these:
+    - GCC 4.5
+    - MSVC 2010
+    - Clang 3.1
+      (or newer)
+This is the same policy the KDE Frameworks have.
+
+Other parts require modern compilers. You can (and should) use more modern
+C++ coding practices. Including auto, lambdas, smart pointers etc. You can
+use anything that GCC 4.7 can compile.
+
+These are the compilers you need to test your patches against:
+    - GCC 4.7
+    - LLVM/Clang 3.1
+
+When you set up different builds alongside the main one, you can use
+scripts/commit.sh to build them all before committing. The script
+calls git commit if all builds finished successfully. See the script
+for more info.
+
+
+.SH FILE NAMING
+
+The library files are lower-case, apart from the "pretty" headers.
+The service, and the rest of the repository should be in camel-case
+(with the exception of source files that don't have corresponding
+headers, or vice-versa).
+
+
+.SH CONVENIENCE MACROS AND METHODS
+
+There are some convenience macros and methods defined in the headers placed
+in the service/utils/ directory.
+
+.TP
+.B D_PTR
+d_ptr.h and d_ptr_implementation.h define a smart pointer way of doing
+the d-ptr (aka pimpl) idiom.
+
+.TP
+.B remove_if
+remove_if.h is a generic implementation of the erase-remove idiom
+
+.TP
+.B for_each_assoc, find_if_assoc
+for_each_assoc.h and find_if_assoc.h define the for_each and find_if
+algorithms for associative containers. Works with both Qt and STL containers.
+
diff --git a/README.md b/README.md
new file mode 100644 (file)
index 0000000..c5901ce
--- /dev/null
+++ b/README.md
@@ -0,0 +1,33 @@
+# KActivities
+
+Core components for the KDE Activity concept
+
+## Introduction
+
+When a user is interacting with a computer, there are three main areas of
+contextual information that may affect the behaviour of the system: who the user
+is, where they are, and what they are doing.
+
+*Activities* deal with the last one. An activity might be "developing a KDE
+application", "studying 19th century art", "composing music" or "watching funny
+videos". Each of these activites may involve multiple applications, and a single
+application may be used in multiple activities (for example, most activities are
+likely to involve using a web browser, but different activities will probably
+involve different websites).
+
+KActivities provides the infrastructure needed to manage a user's activites,
+allowing them to switch between tasks, and for applications to update their
+state to match the user's current activity. This includes a daemon, a library
+for interacting with that daemon, and plugins for integration with other
+frameworks.
+
+## Usage
+
+Most applications that wish to be activity-aware will want to use
+KActivities::Consumer to keep track of the user's current activity, and
+KActivities::ResourceInstance to notify the activity manager of resources the
+user has accessed (this is not necessary for resources accessed via KIO, as a
+plugin is provided to do that automatically).
+
+The other classes available in the API are primarily intended for use by the
+workspace to allow the user to view and manage available activities.
diff --git a/README.packagers b/README.packagers
new file mode 100644 (file)
index 0000000..6b7155b
--- /dev/null
@@ -0,0 +1,23 @@
+
+
+.\" " " " " " " " " " " " " " " " " " " " " " " " " " " "
+.\"                                                     "
+.\" It is best to view this file with the man tool:     "
+.\" man ./README.packagers                              "
+.\"                                                     "
+.\" " " " " " " " " " " " " " " " " " " " " " " " " " " "
+
+
+.\" Best to view this file with the man tool
+.TH README KAMD  2012-08-29 "KDE" "KActivities Packagers"
+
+.SH LIBRARY
+
+The library can be used even if the service is not running nor installed.
+The dependencies are minimal since it is only a thin wrapper for the d-bus
+service.
+
+.SH SERVICE
+
+.TP
+
diff --git a/TODO b/TODO
new file mode 100644 (file)
index 0000000..10e0973
--- /dev/null
+++ b/TODO
@@ -0,0 +1,28 @@
+src/lib/resourceinstance.cpp:149:
+ TODO: update the service info
+src/lib/resourceinstance.cpp:161:
+ TODO: update the service info
+
+src/imports/resourcemodel.cpp:103:
+ NOTE: What to do if the file does not exist?
+       Ignoring that case since the daemon creates it on startup.
+       Is it plausible that somebody will instantiate the ResourceModel
+       before the daemon is started?
+src/imports/resourcemodel.cpp:133:
+ TODO: Database connection naming could be smarter (thread-id-based,
+       reusing connections...?)
+src/imports/resourcemodel.cpp:335:
+ TODO: Will probably need some more special handling -
+       for application:/ and a few more
+src/imports/resourcemodel.cpp:551:
+ TODO: This might be smarter possibly, but might collide
+       with the SQL model. Implement a custom model with internal
+       cache instead of basing it on QSqlModel.
+
+src/imports/activitiesextensionplugin.cpp:30:
+ TODO: Clean up unused classes from the imports module
+src/imports/activitiesextensionplugin.cpp:32:
+ TODO: Since plasma is now dealing with activity model wallpapers,
+       replace ActivityModel with the KActivities::ActivitiesModel
+       (but keep the name)
+
diff --git a/autotests/CMakeLists.txt b/autotests/CMakeLists.txt
new file mode 100644 (file)
index 0000000..c2d5775
--- /dev/null
@@ -0,0 +1,7 @@
+# vim:set softtabstop=3 shiftwidth=3 tabstop=3 expandtab:
+
+string (REPLACE "-fno-exceptions" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
+add_definitions (-fexceptions)
+
+add_subdirectory (core)
+
diff --git a/autotests/common/test.cpp b/autotests/common/test.cpp
new file mode 100644 (file)
index 0000000..a247bfe
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ *   Copyright (C) 2013, 2014, 2015 Ivan Cukic <ivan.cukic(at)kde.org>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License version 2,
+ *   or (at your option) any later version, as published by the Free
+ *   Software Foundation
+ *
+ *   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 "test.h"
+
+#include <QDBusConnection>
+#include <QDBusConnectionInterface>
+
+#include "common/dbus/common.h"
+
+Test::Test(QObject *parent)
+    : QObject(parent)
+{
+
+}
+
+bool Test::inEmptySession()
+{
+    const QStringList services =
+        QDBusConnection::sessionBus().interface()->registeredServiceNames();
+
+    for (const QString & service : services) {
+        bool kdeServiceAndNotKAMD =
+            service.startsWith(QLatin1String("org.kde")) &&
+            service != KAMD_DBUS_SERVICE;
+
+        if (kdeServiceAndNotKAMD) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool Test::isActivityManagerRunning()
+{
+    return QDBusConnection::sessionBus().interface()->isServiceRegistered(
+        KAMD_DBUS_SERVICE);
+}
+
diff --git a/autotests/common/test.h b/autotests/common/test.h
new file mode 100644 (file)
index 0000000..610cfb6
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ *   Copyright (C) 2013, 2014, 2015 Ivan Cukic <ivan.cukic(at)kde.org>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License version 2,
+ *   or (at your option) any later version, as published by the Free
+ *   Software Foundation
+ *
+ *   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 COMMON_TEST_H
+#define COMMON_TEST_H
+
+#include <QObject>
+#include <QFuture>
+#include <QFutureWatcher>
+#include <QCoreApplication>
+#include <QTest>
+
+class Test: public QObject {
+    Q_OBJECT
+public:
+    Test(QObject *parent = nullptr);
+
+protected:
+    enum WhenToFail {
+        DontFail = 0,
+        FailIfTrue = 1,
+        FailIfFalse = 2
+    };
+
+    template <typename _ReturnType, typename _Continuation>
+    void continue_future(const QFuture<_ReturnType> &future,
+                         _Continuation &&continuation)
+    {
+        if (!future.isFinished()) {
+            auto watcher = new QFutureWatcher<decltype(future.result())>();
+            QObject::connect(watcher, &QFutureWatcherBase::finished,
+                watcher,
+                [=] {
+                    continuation(watcher->result());
+                    watcher->deleteLater();
+                },
+                Qt::QueuedConnection
+            );
+
+            watcher->setFuture(future);
+
+        } else {
+            continuation(future.result());
+
+        }
+    }
+
+    template <typename _Continuation>
+    void continue_future(const QFuture<void> &future,
+                         _Continuation &&continuation)
+    {
+        if (!future.isFinished()) {
+            auto watcher = new QFutureWatcher<void>();
+            QObject::connect(watcher, &QFutureWatcherBase::finished,
+                watcher,
+                [=] {
+                    continuation();
+                    watcher->deleteLater();
+                },
+                Qt::QueuedConnection
+            );
+
+            watcher->setFuture(future);
+
+        } else {
+            continuation();
+
+        }
+    }
+
+    template <typename T>
+    static inline
+    void wait_until(T condition, const char * msg, int msecs = 300)
+    {
+        auto start = QTime::currentTime();
+
+        while (!condition()) {
+            QCoreApplication::processEvents();
+
+            auto now = QTime::currentTime();
+            QVERIFY2(start.msecsTo(now) < msecs, msg);
+            if (start.msecsTo(now) >= msecs) break;
+        }
+    }
+
+#define TEST_WAIT_UNTIL(C)                                                     \
+    wait_until([&] () -> bool { return C; }, "Timeout waiting for: " #C);
+#define TEST_WAIT_UNTIL_WITH_TIMEOUT(C, T)                                     \
+    wait_until([&] () ->bool { return C; }, "Timeout waiting for: " #C, T);
+
+
+    template <typename T>
+    static bool check(T what, WhenToFail wtf = DontFail,
+                      const char *msg = nullptr)
+    {
+        bool result = what();
+
+        if (
+            (wtf == FailIfTrue && result) ||
+            (wtf == FailIfFalse && !result)
+        ) {
+            qFatal(
+                "\n"
+                "\n"
+                "!!! > \n"
+                "!!! > %s\n"
+                "!!! > \n"
+                ,  msg);
+        }
+
+        return result;
+    }
+
+    static bool inEmptySession();
+    static bool isActivityManagerRunning();
+
+Q_SIGNALS:
+    void testFinished();
+};
+
+#define CHECK_CONDITION(A, B) check(A, B, #A " raised " #B)
+
+// Pretty print
+#include <iostream>
+
+#if defined(Q_NO_DEBUG) || !defined(Q_OS_LINUX)
+    #define TEST_CHUNK(Name)
+#else
+    inline
+    void _test_chunk(const QString &message)
+    {
+        std::cerr
+            << '\n'
+            << message.toStdString() << "\n"
+            << std::string(message.length(), '-') << '\n'
+            ;
+    }
+    #define TEST_CHUNK(Name) _test_chunk(Name);
+#endif
+
+#endif /* TEST_H */
+
diff --git a/autotests/core/CMakeLists.txt b/autotests/core/CMakeLists.txt
new file mode 100644 (file)
index 0000000..41b885a
--- /dev/null
@@ -0,0 +1,37 @@
+# vim:set softtabstop=3 shiftwidth=3 tabstop=3 expandtab:
+project (KActivitiesTest)
+
+find_package (Qt5 REQUIRED NO_MODULE COMPONENTS Test Core DBus)
+
+include_directories (
+   ${KACTIVITIES_CURRENT_ROOT_SOURCE_DIR}/src/
+   ${KACTIVITIES_CURRENT_ROOT_SOURCE_DIR}/tests/
+   ${KACTIVITIES_CURRENT_ROOT_SOURCE_DIR}/autotests/
+   ${CMAKE_BINARY_DIR}/src/
+   )
+
+set (
+   KActivitiesTest_SRCS
+   main.cpp
+   Process.cpp
+   OfflineTest.cpp
+   CleanOnlineTest.cpp
+   ${KACTIVITIES_CURRENT_ROOT_SOURCE_DIR}/autotests/common/test.cpp
+   )
+
+if (NOT WIN32)
+
+   add_executable (
+      KActivitiesTest
+      ${KActivitiesTest_SRCS}
+      )
+
+   target_link_libraries (
+      KActivitiesTest
+      Qt5::Core
+      Qt5::Test
+      Qt5::DBus
+      KF5::Activities
+      )
+
+endif ()
diff --git a/autotests/core/CleanOnlineTest.cpp b/autotests/core/CleanOnlineTest.cpp
new file mode 100644 (file)
index 0000000..c0e30e1
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ *   Copyright (C) 2013 Ivan Cukic <ivan.cukic(at)kde.org>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License version 2,
+ *   or (at your option) any later version, as published by the Free
+ *   Software Foundation
+ *
+ *   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 "CleanOnlineTest.h"
+
+#include <QDBusConnection>
+#include <QDBusConnectionInterface>
+#include <QString>
+#include <QDebug>
+#include <QTest>
+
+QString CleanOnlineSetup::id1;
+QString CleanOnlineSetup::id2;
+
+CleanOnlineTest::CleanOnlineTest(QObject *parent)
+    : Test(parent)
+{
+    activities.reset(new KActivities::Controller());
+}
+
+CleanOnlineSetup::CleanOnlineSetup(QObject *parent)
+    : Test(parent)
+{
+    activities.reset(new KActivities::Controller());
+}
+
+OnlineTest::OnlineTest(QObject *parent)
+    : Test(parent)
+{
+    activities.reset(new KActivities::Controller());
+}
+
+void CleanOnlineTest::testCleanOnlineActivityListing()
+{
+    // Waiting for the service to start, and for us to sync
+    TEST_WAIT_UNTIL(activities->serviceStatus()
+                    == KActivities::Consumer::Running);
+
+    QCOMPARE(activities->activities(), QStringList());
+    QCOMPARE(activities->serviceStatus(), KActivities::Consumer::Running);
+    QCOMPARE(activities->currentActivity(), QString());
+
+    QCOMPARE(activities->activities(), QStringList());
+    QCOMPARE(activities->activities(KActivities::Info::Running), QStringList());
+    QCOMPARE(activities->activities(KActivities::Info::Stopped), QStringList());
+}
+
+void CleanOnlineSetup::testCleanOnlineActivityControl()
+{
+    // Waiting for the service to start, and for us to sync
+    TEST_WAIT_UNTIL(activities->serviceStatus()
+                    == KActivities::Consumer::Running);
+
+    auto activity1 = activities->addActivity(QStringLiteral("The first one"));
+    TEST_WAIT_UNTIL(activity1.isFinished());
+    id1 = activity1.result();
+
+    auto activity2 = activities->addActivity(QStringLiteral("The second one"));
+    TEST_WAIT_UNTIL(activity2.isFinished());
+    id2 = activity2.result();
+
+    TEST_WAIT_UNTIL(activities->currentActivity() == id1);
+
+    auto f1 = activities->setCurrentActivity(activity2.result());
+    TEST_WAIT_UNTIL(f1.isFinished());
+    TEST_WAIT_UNTIL(activities->currentActivity() == id2);
+
+    auto f2 = activities->setCurrentActivity(id1);
+    TEST_WAIT_UNTIL(f2.isFinished());
+    TEST_WAIT_UNTIL(activities->currentActivity() == id1);
+
+    auto f3 = activities->setActivityName(id1, QStringLiteral("Renamed activity"));
+    TEST_WAIT_UNTIL(f3.isFinished());
+
+    KActivities::Info ac(id1);
+    TEST_WAIT_UNTIL(ac.name() == QLatin1String("Renamed activity"));
+
+    TEST_WAIT_UNTIL(activities->activities().size() == 2);
+}
+
+void OnlineTest::testOnlineActivityListing()
+{
+    // Waiting for the service to start, and for us to sync
+    TEST_WAIT_UNTIL(activities->serviceStatus()
+                    == KActivities::Consumer::Running);
+
+    KActivities::Info i1(CleanOnlineSetup::id1);
+    KActivities::Info i2(CleanOnlineSetup::id2);
+
+    TEST_WAIT_UNTIL(i1.name() == QLatin1String("Renamed activity"));
+    TEST_WAIT_UNTIL(i2.name() == QLatin1String("The second one"));
+
+    qDebug() << CleanOnlineSetup::id1 << i1.name();
+    qDebug() << CleanOnlineSetup::id2 << i2.name();
+}
+
+void CleanOnlineTest::cleanupTestCase()
+{
+    emit testFinished();
+}
+
+void CleanOnlineSetup::cleanupTestCase()
+{
+    emit testFinished();
+}
+
+void OnlineTest::cleanupTestCase()
+{
+    emit testFinished();
+}
diff --git a/autotests/core/CleanOnlineTest.h b/autotests/core/CleanOnlineTest.h
new file mode 100644 (file)
index 0000000..6b3feae
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ *   Copyright (C) 2013 Ivan Cukic <ivan.cukic(at)kde.org>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License version 2,
+ *   or (at your option) any later version, as published by the Free
+ *   Software Foundation
+ *
+ *   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 CLEANONLINETEST_H
+#define CLEANONLINETEST_H
+
+#include <common/test.h>
+
+#include <controller.h>
+
+#include <QScopedPointer>
+
+class CleanOnlineTest : public Test {
+    Q_OBJECT
+public:
+    CleanOnlineTest(QObject *parent = nullptr);
+
+private Q_SLOTS:
+    void testCleanOnlineActivityListing();
+
+    void cleanupTestCase();
+
+private:
+    QScopedPointer<KActivities::Controller> activities;
+    QString id1;
+    QString id2;
+
+};
+
+class CleanOnlineSetup : public Test {
+    Q_OBJECT
+public:
+    CleanOnlineSetup(QObject *parent = nullptr);
+
+private Q_SLOTS:
+    void testCleanOnlineActivityControl();
+
+    void cleanupTestCase();
+
+private:
+    QScopedPointer<KActivities::Controller> activities;
+
+public:
+    static QString id1;
+    static QString id2;
+
+};
+
+class OnlineTest : public Test {
+    Q_OBJECT
+public:
+    OnlineTest(QObject *parent = nullptr);
+
+private Q_SLOTS:
+    void testOnlineActivityListing();
+
+    void cleanupTestCase();
+
+private:
+    QScopedPointer<KActivities::Controller> activities;
+
+};
+
+
+#endif /* CLEANONLINETEST_H */
+
diff --git a/autotests/core/OfflineTest.cpp b/autotests/core/OfflineTest.cpp
new file mode 100644 (file)
index 0000000..24c95b1
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ *   Copyright (C) 2013 Ivan Cukic <ivan.cukic(at)kde.org>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License version 2,
+ *   or (at your option) any later version, as published by the Free
+ *   Software Foundation
+ *
+ *   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 "OfflineTest.h"
+
+#include <QDBusConnection>
+#include <QDBusConnectionInterface>
+#include <QString>
+#include <QTest>
+
+QString nulluuid = QStringLiteral("00000000-0000-0000-0000-000000000000");
+
+OfflineTest::OfflineTest(QObject *parent)
+    : Test(parent)
+{
+    QCoreApplication::instance()->setProperty(
+        "org.kde.KActivities.core.disableAutostart", true);
+
+    activities.reset(new KActivities::Controller());
+}
+
+void OfflineTest::testOfflineActivityListing()
+{
+    // The service is not running
+
+    TEST_WAIT_UNTIL(activities->serviceStatus() == KActivities::Consumer::NotRunning);
+    QCOMPARE(activities->currentActivity(), nulluuid);
+
+    QCOMPARE(activities->activities(), QStringList() << nulluuid);
+    QCOMPARE(activities->activities(KActivities::Info::Running), QStringList() << nulluuid);
+    QCOMPARE(activities->activities(KActivities::Info::Stopped), QStringList());
+
+}
+
+void OfflineTest::testOfflineActivityControl()
+{
+    continue_future(activities->addActivity(QStringLiteral("Activity")),
+                    [](const QString & newid) { QCOMPARE(newid, QString()); });
+
+    continue_future(activities->setCurrentActivity(QStringLiteral("Activity")),
+                    [](bool success) { QCOMPARE(success, false); });
+
+    // Test whether the responses are immediate
+    static bool inMethod = false;
+
+    inMethod = true;
+
+    continue_future(activities->setActivityName(QStringLiteral("Activity"),
+                                                QStringLiteral("Activity")),
+                    []() { QCOMPARE(inMethod, true); });
+    continue_future(activities->setActivityIcon(QStringLiteral("Activity"),
+                                                QStringLiteral("Activity")),
+                    []() { QCOMPARE(inMethod, true); });
+    continue_future(activities->removeActivity(QStringLiteral("Activity")),
+                    [] () { QCOMPARE(inMethod, true); });
+    continue_future(activities->startActivity(QStringLiteral("Activity")),
+                    [] () { QCOMPARE(inMethod, true); });
+    continue_future(activities->stopActivity(QStringLiteral("Activity")),
+                    [] () { QCOMPARE(inMethod, true); });
+
+    inMethod = false;
+}
+
+void OfflineTest::initTestCase()
+{
+    CHECK_CONDITION(inEmptySession, FailIfFalse);
+    CHECK_CONDITION(isActivityManagerRunning, FailIfTrue);
+}
+
+void OfflineTest::cleanupTestCase()
+{
+    emit testFinished();
+}
diff --git a/autotests/core/OfflineTest.h b/autotests/core/OfflineTest.h
new file mode 100644 (file)
index 0000000..a8be8f5
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ *   Copyright (C) 2013 Ivan Cukic <ivan.cukic(at)kde.org>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License version 2,
+ *   or (at your option) any later version, as published by the Free
+ *   Software Foundation
+ *
+ *   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 OFFLINETEST_H
+#define OFFLINETEST_H
+
+#include <common/test.h>
+
+#include <controller.h>
+
+#include <QScopedPointer>
+
+class OfflineTest : public Test {
+    Q_OBJECT
+public:
+    OfflineTest(QObject *parent = nullptr);
+
+private Q_SLOTS:
+    void initTestCase();
+
+    void testOfflineActivityListing();
+    void testOfflineActivityControl();
+
+    void cleanupTestCase();
+
+private:
+
+    QScopedPointer<KActivities::Controller> activities;
+
+};
+
+
+#endif /* OFFLINETEST_H */
+
diff --git a/autotests/core/Process.cpp b/autotests/core/Process.cpp
new file mode 100644 (file)
index 0000000..8e246aa
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ *   Copyright (C) 2013 Ivan Cukic <ivan.cukic(at)kde.org>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License version 2,
+ *   or (at your option) any later version, as published by the Free
+ *   Software Foundation
+ *
+ *   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 "Process.h"
+
+#include <QDBusConnection>
+#include <QDBusConnectionInterface>
+#include <QString>
+#include <QDebug>
+#include <QTest>
+#include <QTemporaryDir>
+#include <QProcess>
+#include <QRegularExpression>
+
+#include <sys/types.h>
+#include <signal.h>
+
+#include "common/dbus/common.h"
+
+namespace Process {
+
+QProcess *Modifier::s_process = nullptr;
+QTemporaryDir *Modifier::s_tempDir = nullptr;
+QString nulluuid = QStringLiteral("00000000-0000-0000-0000-000000000000");
+
+Modifier::Modifier(Action action)
+    : Test()
+    , m_action(action)
+{
+    if (!s_process) {
+        s_process = new QProcess();
+        s_tempDir = new QTemporaryDir();
+
+        if (!s_tempDir->isValid()) {
+            qFatal("Can not create a temporary dir");
+        }
+
+        qDebug() << "Running KAMD in " << s_tempDir->path();
+        s_process->setProcessChannelMode(QProcess::ForwardedChannels);
+    }
+}
+
+void Modifier::initTestCase()
+{
+    const auto state = s_process->state();
+
+    if (state != QProcess::NotRunning && m_action == Start) {
+        qFatal("Already running");
+    }
+
+    switch (m_action) {
+        case Start:
+        {
+            qDebug() << "Starting...";
+
+            QRegularExpression nonxdg(QStringLiteral("^[^X][^D][^G].*$"));
+
+            auto env
+                = QProcessEnvironment::systemEnvironment().toStringList()
+                  .filter(nonxdg)
+                  << QStringLiteral("XDG_DATA_HOME=") + s_tempDir->path() + QStringLiteral("/")
+                  << QStringLiteral("XDG_CONFIG_HOME=") + s_tempDir->path() + QStringLiteral("/")
+                  << QStringLiteral("XDG_CACHE_HOME=") + s_tempDir->path() + QStringLiteral("/");
+
+            // qDebug() << env;
+
+            s_process->setEnvironment(env);
+            s_process->start(QStringLiteral("kactivitymanagerd"));
+            s_process->waitForStarted();
+
+
+            break;
+        }
+
+        case Stop:
+        case Kill:
+        case Crash:
+        {
+            qDebug() << "Stopping...";
+
+            const auto dbus = QDBusConnection::sessionBus().interface();
+            const auto kamd = KAMD_DBUS_SERVICE;
+
+            if (!dbus->isServiceRegistered(kamd)) break;
+
+            uint pid = dbus->servicePid(kamd);
+
+            ::kill(pid,
+                    m_action == Stop ? SIGQUIT :
+                    m_action == Kill ? SIGKILL :
+                    /* else */         SIGSEGV
+                    );
+
+            while (Test::isActivityManagerRunning()) {
+                QCoreApplication::processEvents();
+            }
+
+            if (s_process->state() == QProcess::Running) {
+                s_process->terminate();
+                s_process->waitForFinished();
+            }
+
+            break;
+        }
+    }
+}
+
+void Modifier::testProcess()
+{
+    const auto state = s_process->state();
+
+    switch (m_action) {
+        case Start:
+            QCOMPARE(state, QProcess::Running);
+            break;
+
+        case Stop:
+            QCOMPARE(state, QProcess::NotRunning);
+            break;
+
+        case Kill:
+            QCOMPARE(state, QProcess::NotRunning);
+            break;
+
+        case Crash:
+            break;
+
+    }
+}
+
+void Modifier::cleanupTestCase()
+{
+    emit testFinished();
+}
+
+Modifier *exec(Action action) {
+    return new Modifier(action);
+}
+
+} // namespace Process
diff --git a/autotests/core/Process.h b/autotests/core/Process.h
new file mode 100644 (file)
index 0000000..736dc8d
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ *   Copyright (C) 2013 Ivan Cukic <ivan.cukic(at)kde.org>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License version 2,
+ *   or (at your option) any later version, as published by the Free
+ *   Software Foundation
+ *
+ *   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 PROCESS_H
+#define PROCESS_H
+
+#include <common/test.h>
+
+#include <controller.h>
+
+class QProcess;
+class QTemporaryDir;
+
+namespace Process {
+    enum Action {
+        Start,
+        Stop,
+        Kill,
+        Crash
+    };
+
+    class Modifier : public Test {
+        Q_OBJECT
+    public:
+        Modifier(Action action);
+
+    private Q_SLOTS:
+        void initTestCase();
+        void testProcess();
+        void cleanupTestCase();
+
+    private:
+        Action m_action;
+        static QProcess *s_process;
+        static QTemporaryDir *s_tempDir;
+
+    };
+
+    Modifier *exec(Action action);
+
+} // namespace Process
+
+
+#endif /* PROCESS_H */
+
diff --git a/autotests/core/main.cpp b/autotests/core/main.cpp
new file mode 100644 (file)
index 0000000..c5238ab
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ *   Copyright (C) 2013 Ivan Cukic <ivan.cukic(at)kde.org>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License version 2,
+ *   or (at your option) any later version, as published by the Free
+ *   Software Foundation
+ *
+ *   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 <QCoreApplication>
+#include <QList>
+
+#include <common/test.h>
+
+#include "Process.h"
+#include "OfflineTest.h"
+#include "CleanOnlineTest.h"
+
+class TestRunner: public QObject {
+public:
+    TestRunner()
+        : m_nextToStart(0)
+    {
+    }
+
+    TestRunner &addTest(Test *test)
+    {
+        if (m_nextToStart == 0)
+            m_tests << test;
+        return *this;
+    }
+
+    TestRunner &operator<<(Test *test)
+    {
+        addTest(test);
+        return *this;
+    }
+
+    void start()
+    {
+        if (m_nextToStart)
+            return;
+
+        next();
+    }
+
+private:
+    void next()
+    {
+        if (m_nextToStart >= m_tests.size()) {
+            QCoreApplication::exit(0);
+            return;
+        }
+
+        Test *test = m_tests[m_nextToStart++];
+
+        QObject::connect(test, &Test::testFinished,
+                this, &TestRunner::next,
+                Qt::QueuedConnection);
+
+        QTest::qExec(test);
+
+    }
+
+private:
+    QList<Test*> m_tests;
+    int m_nextToStart;
+
+};
+
+int main(int argc, char *argv[])
+{
+    QCoreApplication app(argc, argv);
+
+    TestRunner &runner = *(new TestRunner());
+
+    (runner
+        << Process::exec(Process::Kill)
+
+        // Running the tests for when the service is offline
+        << new OfflineTest()
+
+        // Running the offline tests again so that we are sure
+        // nothing has changed -- no activities created, changed etc.
+        << new OfflineTest()
+
+        // Starting the manager
+        << Process::exec(Process::Start)
+
+        // Starting the online tests
+        << new CleanOnlineTest()
+        << new CleanOnlineSetup()
+        << new OnlineTest()
+
+        // Starting the manager
+        << Process::exec(Process::Stop)
+
+        << new OfflineTest()
+        << new OfflineTest()
+
+        << Process::exec(Process::Start)
+        << new OnlineTest()
+
+        << Process::exec(Process::Stop)
+
+    ).start();
+
+
+    return app.exec();
+    // QTest::qExec(&tc, argc, argv);
+}
+
diff --git a/contrib/bash/next-activity.sh b/contrib/bash/next-activity.sh
new file mode 100755 (executable)
index 0000000..8a787ea
--- /dev/null
@@ -0,0 +1,30 @@
+#! /bin/bash
+#
+# next-activity.sh
+# Copyright (C) 2016 Ivan Čukić <ivan.cukic(at)kde.org>
+#
+# Distributed under terms of the MIT license.
+#
+
+current_activity=($(qdbus org.kde.ActivityManager /ActivityManager/Activities CurrentActivity))
+activities=($(qdbus org.kde.ActivityManager /ActivityManager/Activities ListActivities))
+found="0"
+
+for ((i=0; i < ${#activities[@]}; ++i)); do
+    if [ "$current_activity" = "${activities[$i]}" ]; then
+        found="1"
+    else
+        if [ "$found" == "1" ]; then
+            echo "Switching to ${activities[$i]}"
+            qdbus org.kde.ActivityManager /ActivityManager/Activities SetCurrentActivity ${activities[$i]}
+            found="0"
+        fi
+    fi
+done
+
+if [ "$found" == "1" ]; then
+    echo "Switching to ${activities[0]}"
+    qdbus org.kde.ActivityManager /ActivityManager/Activities SetCurrentActivity ${activities[0]}
+fi
+
+
diff --git a/contrib/bash/prev-activity.sh b/contrib/bash/prev-activity.sh
new file mode 100755 (executable)
index 0000000..ede4fc7
--- /dev/null
@@ -0,0 +1,30 @@
+#! /bin/bash
+#
+# next-activity.sh
+# Copyright (C) 2016 Ivan Čukić <ivan.cukic(at)kde.org>
+#
+# Distributed under terms of the MIT license.
+#
+
+current_activity=($(qdbus org.kde.ActivityManager /ActivityManager/Activities CurrentActivity))
+activities=($(qdbus org.kde.ActivityManager /ActivityManager/Activities ListActivities))
+found="0"
+
+previous_activity=""
+
+for ((i=0; i < ${#activities[@]}; ++i)); do
+    if [ "$current_activity" = "${activities[$i]}" ]; then
+        if [ "$previous_activity" != "" ]; then
+            echo "Switching to $previous_activity"
+            qdbus org.kde.ActivityManager /ActivityManager/Activities SetCurrentActivity $previous_activity
+            exit
+        else
+            echo "Switching to ${activities[-1]}"
+            qdbus org.kde.ActivityManager /ActivityManager/Activities SetCurrentActivity ${activities[-1]}
+            exit
+        fi
+    else
+        previous_activity="${activities[$i]}"
+    fi
+done
+
diff --git a/contrib/commit.sh b/contrib/commit.sh
new file mode 100755 (executable)
index 0000000..3377ff3
--- /dev/null
@@ -0,0 +1,35 @@
+#!/bin/bash
+
+# The script finds build directories for the current
+# src directory and builds them
+#
+# For example, for the source dir:
+#   /some/path/kde/src/project/whatever
+# It finds:
+#   /some/path/kde/build*/project/whatever
+
+current_dir=`pwd`
+
+all_root_dir=`pwd | sed 's#/src/.*##'`
+src_root_dir=$all_root_dir/src
+
+echo "src:   $src_root_dir"
+
+for build_root_dir in $all_root_dir/build*; do
+    echo "building in $build_root_dir"
+
+    cd $current_dir
+    current_dir_log=`OBJ_REPLACEMENT=s#$src_root_dir#$build_root_dir# makeobj`
+    if [ "$?" = "0" ]
+    then
+        echo "... success"
+    else
+        echo "... FAILED"
+        echo $current_dir_log
+        exit
+    fi
+
+done
+
+git commit
+
diff --git a/contrib/run-krazy.sh b/contrib/run-krazy.sh
new file mode 100755 (executable)
index 0000000..1c5635d
--- /dev/null
@@ -0,0 +1,25 @@
+#!/bin/bash
+
+if [ ! -f "contrib/run-krazy.sh" ];
+then
+    echo "This script needs to be started from KAMD's root directory"
+    exit
+fi
+
+DIRS=$1
+
+if [ ! -n "$1" ];
+then
+    DIRS="lib service utils workspace"
+fi
+
+
+
+echo $DIRS
+CURRENT_DIRECTORY=$PWD
+
+for dir in $DIRS;
+do
+    echo "Running krazy2 on $dir ..."
+    cd $CURRENT_DIRECTORY/src/$dir && krazy2all --exclude license > /tmp/$dir.krazy
+done
diff --git a/contrib/update-todo.hs b/contrib/update-todo.hs
new file mode 100755 (executable)
index 0000000..e012cd4
--- /dev/null
@@ -0,0 +1,115 @@
+#! /usr/bin/env runhaskell
+
+{-# LANGUAGE LambdaCase #-}
+
+import System.Directory (doesFileExist, getDirectoryContents)
+import System.FilePath ((</>))
+
+import Data.String.Utils
+import Data.List
+
+
+-- Util methods
+
+mapSnd :: (a -> b) -> [(c, a)] -> [(c, b)]
+mapSnd f xys = map ( \case (x, y) -> (x, f y) ) xys
+
+mapDir :: (FilePath -> IO ()) -> FilePath -> IO ()
+mapDir proc fp = do
+        isFile <- doesFileExist fp -- is a file of fp
+        if isFile then proc fp -- process the file
+                  else getDirectoryContents fp >>=
+                  mapM_ (mapDir proc . (fp </>)) . filter (`notElem` [".", ".."])
+
+printTitle :: String -> IO()
+printTitle title = do
+        putStrLn ""
+        putStrLn title
+        putStrLn $ map (\_ -> '=') title
+
+main :: IO ()
+main = do
+    printTitle "libKActivities"
+    mapDir process "src/lib/core"
+
+    printTitle "libKActivitiesStats"
+    mapDir process "src/lib/stats"
+
+    printTitle "KActivityManagerD"
+    mapDir process "src/service"
+
+    printTitle "QML imports"
+    mapDir process "src/imports"
+
+    printTitle "Workspace plugins"
+    mapDir process "src/workspace"
+
+    printTitle "Other"
+    mapDir process "src/common"
+    mapDir process "src/utils"
+
+-- Parsing methods
+
+extractBlock :: [String] -> [String]
+extractBlock =
+        takeWhile (startswith "//")
+
+isTodoBlock :: (Integer, [String]) -> Bool
+isTodoBlock (_, block) =
+        (not $ null block) && (
+            (startswith "// TODO: " $ head block) ||
+            (startswith "// FIXME: " $ head block) ||
+            (startswith "// NOTE: " $ head block)
+        )
+
+joinBlock :: [String] -> String
+joinBlock block =
+        unlines $
+        map ( \line ->
+            ( dropWhile (== '/') line )
+        ) $
+        block
+
+
+-- File processing
+
+process :: FilePath -> IO ()
+process filename = do
+        -- Getting the file contents
+        content <- readFile filename
+
+        -- Items with line numbers
+        let items :: [(Integer, [String])]
+            items =
+                zip [1..] $
+                tails $
+                map strip $
+                lines content
+
+        -- Only those starting with TODO
+        let todoBlocks :: [(Integer, [String])]
+            todoBlocks =
+                -- Getting only the comment block
+                mapSnd extractBlock $
+                -- Getting comment blocks that define a todo item
+                filter isTodoBlock $
+                items
+
+        -- Todo items
+        let todoItems :: [(Integer, String)]
+            todoItems =
+                -- Getting the item blocks into actual items
+                mapSnd joinBlock todoBlocks
+
+
+        if (not $ null todoItems)
+            then
+                putStrLn $
+                    concat $
+                    map (\case (lineNo, todoItem) ->
+                                filename ++ ":" ++ (show lineNo) ++ ":\n" ++ todoItem
+                        ) todoItems
+            else
+                return ()
+
+
diff --git a/contrib/zsh/kamd-functions b/contrib/zsh/kamd-functions
new file mode 100644 (file)
index 0000000..6afd3b8
--- /dev/null
@@ -0,0 +1,141 @@
+# Module: KDE Aliases
+# Priority: 10
+
+autoload -U colors
+colors
+
+# Defining aliases for common kamd dbus objects
+alias kamd_dbus="qdbus org.kde.ActivityManager"
+
+alias kamd_activities="qdbus org.kde.ActivityManager /ActivityManager/Activities"
+alias kamd_resources="qdbus org.kde.ActivityManager /ActivityManager/Resources"
+alias kamd_resources_linking="qdbus org.kde.ActivityManager /ActivityManager/Resources/Linking"
+alias kamd_features="qdbus org.kde.ActivityManager /ActivityManager/Features"
+
+alias kamd_addactivity="qdbus org.kde.ActivityManager /ActivityManager/Activities AddActivity"
+alias kamd_removeactivity="qdbus org.kde.ActivityManager /ActivityManager/Activities RemoveActivity"
+
+# Open the KAMD database
+alias kamd_database="sqlite3 ~/.local/share/kactivitymanagerd/resources/database"
+
+# Lists the existing activities, along with their state
+function kamd_listactivities() {
+    echo -n "Service version: "
+    qdbus org.kde.ActivityManager /ActivityManager serviceVersion
+
+    CURRENT_ACTIVITY=`qdbus org.kde.ActivityManager /ActivityManager/Activities CurrentActivity`
+
+    for activity in `qdbus org.kde.ActivityManager /ActivityManager/Activities ListActivities`; do
+
+        STATE=""
+
+        if [ "$CURRENT_ACTIVITY" = "$activity" ]; then
+            STATE="$fg[green][CURRENT]"
+
+        else
+            STATE=`qdbus org.kde.ActivityManager /ActivityManager/Activities ActivityState $activity`
+
+            case "state$STATE" in
+                state0)
+                    STATE="$fg[red]$bg[black][INVALID]"
+                    ;;
+                state2)
+                    STATE="$fg[blue][RUNNING]"
+                    ;;
+                state3)
+                    STATE="$fg[red]$bg[black][STARTING]"
+                    ;;
+                state4)
+                    STATE="$fg[black][STOPPED]"
+                    ;;
+                state5)
+                    STATE="$fg[red]$bg[black][STOPPING]"
+                    ;;
+            esac
+        fi
+
+        echo -n "$STATE$reset_color $activity "
+
+        ACTIVITY_NAME=`qdbus org.kde.ActivityManager /ActivityManager/Activities ActivityName $activity`
+        ACTIVITY_DESC=`qdbus org.kde.ActivityManager /ActivityManager/Activities ActivityDescription $activity`
+        ACTIVITY_ICON=`qdbus org.kde.ActivityManager /ActivityManager/Activities ActivityIcon $activity`
+
+        if [ -n "$ACTIVITY_DESC" ]; then
+            echo "$ACTIVITY_NAME ($ACTIVITY_DESC, $ACTIVITY_ICON)"
+        else
+            echo "$ACTIVITY_NAME ($ACTIVITY_ICON)"
+        fi
+    done
+}
+
+# Shows the information about the current activity
+function kamd_currentactivity() {
+    for activity in `qdbus org.kde.ActivityManager /ActivityManager/Activities CurrentActivity`; do
+        STATE=`qdbus org.kde.ActivityManager /ActivityManager/Activities ActivityState $activity`
+
+        case "state$STATE" in
+            state0)
+                STATE="[INVALID] "
+                ;;
+            state2)
+                STATE="[RUNNING] "
+                ;;
+            state3)
+                STATE="[STARTING]"
+                ;;
+            state4)
+                STATE="[STOPPED] "
+                ;;
+            state5)
+                STATE="[STOPPING]"
+                ;;
+        esac
+
+        echo -n "$STATE $activity "
+        qdbus org.kde.ActivityManager /ActivityManager/Activities ActivityName $activity
+    done
+}
+
+# Returns the current activity ID
+function kamd_get_currentactivity_id() {
+    qdbus org.kde.ActivityManager /ActivityManager/Activities CurrentActivity
+}
+
+# Returns the current activity name
+function kamd_get_currentactivity_name() {
+    CURRENT_ACTIVITY_ID=$(kamd_get_currentactivity_id)
+    qdbus org.kde.ActivityManager /ActivityManager/Activities ActivityName $CURRENT_ACTIVITY_ID
+}
+
+# Returns the current activity name
+function kamd_get_currentactivity_name_normalized() {
+    CURRENT_ACTIVITY_NAME=$(kamd_get_currentactivity_name)
+    echo $CURRENT_ACTIVITY_NAME | tr '[:upper:] ' '[:lower:]-'
+}
+
+# Returns the current activity ID
+function kamd_STOP_ALL_BUT_CURRENT() {
+    CURRENT_ACTIVITY_ID=$(kamd_get_currentactivity_id)
+
+    for activity in `qdbus org.kde.ActivityManager /ActivityManager/Activities ListActivities`; do
+        if [ "$activity" != "$CURRENT_ACTIVITY_ID" ]; then
+            echo "Stop: $activity"
+            qdbus org.kde.ActivityManager /ActivityManager/Activities StopActivity $activity
+            sleep 1
+        fi
+    done
+}
+
+# Returns the current activity ID
+function kamd_REMOVE_ALL_BUT_CURRENT() {
+    CURRENT_ACTIVITY_ID=$(kamd_get_currentactivity_id)
+
+    for activity in `qdbus org.kde.ActivityManager /ActivityManager/Activities ListActivities`; do
+        if [ "$activity" != "$CURRENT_ACTIVITY_ID" ]; then
+            echo "Remove: $activity"
+            qdbus org.kde.ActivityManager /ActivityManager/Activities RemoveActivity $activity
+            sleep 1
+        fi
+    done
+}
+
diff --git a/docs/Doxyfile.local b/docs/Doxyfile.local
new file mode 100644 (file)
index 0000000..1d9d027
--- /dev/null
@@ -0,0 +1 @@
+EXCLUDE_PATTERNS += */service/* */scripts/* */workspace/*
diff --git a/kactivities.categories b/kactivities.categories
new file mode 100644 (file)
index 0000000..1164821
--- /dev/null
@@ -0,0 +1 @@
+org.kde.kactivities.lib.core kactivities core lib DEFAULT_SEVERITY [WARNING] IDENTIFIER [KAMD_CORELIB]
diff --git a/metainfo.yaml b/metainfo.yaml
new file mode 100644 (file)
index 0000000..ea663b4
--- /dev/null
@@ -0,0 +1,21 @@
+maintainer: ivan
+description: Runtime and library to organize the user work in separate activities
+tier: 2
+type: solution
+platforms:
+    - name: Linux
+    - name: FreeBSD
+    - name: Windows
+    - name: MacOSX
+      note: Needs QtDBus
+portingAid: false
+deprecated: false
+release: true
+libraries:
+ - qmake: KActivities
+   cmake: "KF5::Activities"
+cmakename: KF5Activities
+
+public_lib: true
+group: Frameworks
+subgroup: Tier 2
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644 (file)
index 0000000..83e4517
--- /dev/null
@@ -0,0 +1,40 @@
+# vim:set softtabstop=3 shiftwidth=3 tabstop=3 expandtab:
+
+# Boosting us a bit
+
+if (NOT KACTIVITIES_LIBRARY_ONLY)
+   find_package (Boost 1.49 REQUIRED)
+
+   string (REGEX MATCH "1053.." BOOST_VERSION_BLACKLISTED ${Boost_VERSION})
+
+   if (BOOST_VERSION_BLACKLISTED AND NOT KACTIVITIES_ENABLE_EXCEPTIONS)
+      message (
+         WARNING
+         "Boost.Container 1.53 has issues when exceptions are disabled. "
+         "We will set the KACTIVITIES_ENABLE_EXCEPTIONS option."
+         )
+      set (KACTIVITIES_ENABLE_EXCEPTIONS ON)
+   endif ()
+endif ()
+
+if (KACTIVITIES_ENABLE_EXCEPTIONS)
+   string (REPLACE "-fno-exceptions" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
+   add_definitions (-fexceptions)
+endif ()
+
+# =======================================================
+# Starting the actual project definition
+
+# The libraries do not depend on any compile-time features
+add_subdirectory (lib)
+
+if (NOT KACTIVITIES_LIBRARY_ONLY)
+   include_directories (
+      ${CMAKE_CURRENT_BINARY_DIR}
+      ${CMAKE_CURRENT_SOURCE_DIR}
+      ${Boost_INCLUDE_DIR}
+      )
+   add_subdirectory (imports)
+endif ()
+
+add_subdirectory (cli)
diff --git a/src/cli/CMakeLists.txt b/src/cli/CMakeLists.txt
new file mode 100644 (file)
index 0000000..479031b
--- /dev/null
@@ -0,0 +1,40 @@
+# vim:set softtabstop=3 shiftwidth=3 tabstop=3 expandtab:
+project (KActivitiesCLI)
+
+find_package (Qt5 REQUIRED NO_MODULE COMPONENTS Core Gui Widgets)
+find_package (Qt5 REQUIRED NO_MODULE COMPONENTS Core Gui Widgets)
+find_package (KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS WindowSystem)
+
+include_directories (
+   ${KACTIVITIES_CURRENT_ROOT_SOURCE_DIR}/src/
+   ${KACTIVITIES_CURRENT_ROOT_SOURCE_DIR}/autotests/
+   )
+
+set (
+   KActivitiesCLI_SRCS
+   main.cpp
+   )
+
+qt5_wrap_ui(
+   KActivitiesCLI_SRCS
+   )
+
+add_executable (
+   kactivities-cli
+   ${KActivitiesCLI_SRCS}
+   )
+
+target_link_libraries (
+   kactivities-cli
+   Qt5::Core
+   KF5::Activities
+   )
+
+ecm_mark_nongui_executable(
+   kactivities-cli
+   )
+
+install (TARGETS
+   kactivities-cli
+   ${KF5_INSTALL_TARGETS_DEFAULT_ARGS}
+   )
diff --git a/src/cli/main.cpp b/src/cli/main.cpp
new file mode 100644 (file)
index 0000000..25f13f4
--- /dev/null
@@ -0,0 +1,288 @@
+/*
+ *   Copyright (C) 2016 Ivan Cukic <ivan.cukic(at)kde.org>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License version 2,
+ *   or (at your option) any later version, as published by the Free
+ *   Software Foundation
+ *
+ *   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 <QCoreApplication>
+#include <QDebug>
+#include <QTimer>
+
+#include <KActivities/Controller>
+
+#include "utils.h"
+
+
+// Output modifiers
+
+DEFINE_COMMAND(bare, 0)
+{
+    flags.bare = true;
+    return 0;
+}
+
+DEFINE_COMMAND(noBare, 0)
+{
+    flags.bare = false;
+    return 0;
+}
+
+DEFINE_COMMAND(color, 0)
+{
+    flags.color = true;
+    return 0;
+}
+
+DEFINE_COMMAND(noColor, 0)
+{
+    flags.color = false;
+    return 0;
+}
+
+// Activity management
+
+DEFINE_COMMAND(createActivity, 1)
+{
+    auto result = awaitFuture(controller->addActivity(args(1)));
+
+    qDebug().noquote() << result;
+
+    return 1;
+}
+
+DEFINE_COMMAND(removeActivity, 1)
+{
+    awaitFuture(controller->removeActivity(args(1)));
+
+    return 1;
+}
+
+DEFINE_COMMAND(startActivity, 1)
+{
+    awaitFuture(controller->startActivity(args(1)));
+
+    return 1;
+}
+
+DEFINE_COMMAND(stopActivity, 1)
+{
+    awaitFuture(controller->stopActivity(args(1)));
+
+    return 1;
+}
+
+DEFINE_COMMAND(listActivities, 0)
+{
+    for (const auto& activity: controller->activities()) {
+        printActivity(activity);
+    }
+
+    return 0;
+}
+
+DEFINE_COMMAND(currentActivity, 0)
+{
+    printActivity(controller->currentActivity());
+
+    return 0;
+}
+
+DEFINE_COMMAND(setActivityProperty, 3)
+{
+    const auto what  = args(1);
+    const auto id    = args(2);
+    const auto value = args(3);
+
+    awaitFuture(
+        what == QLatin1String("name")        ? controller->setActivityName(id, value) :
+        what == QLatin1String("description") ? controller->setActivityDescription(id, value) :
+        what == QLatin1String("icon")        ? controller->setActivityIcon(id, value) :
+                                QFuture<void>()
+        );
+
+    return 3;
+}
+
+DEFINE_COMMAND(activityProperty, 2)
+{
+    const auto what  = args(1);
+    const auto id    = args(2);
+
+    KActivities::Info info(id);
+
+    out << (
+        what == QLatin1String("name")        ? info.name() :
+        what == QLatin1String("description") ? info.description() :
+        what == QLatin1String("icon")        ? info.icon() :
+                                QString()
+        ) << "\n";
+
+    return 2;
+}
+
+// Activity switching
+
+DEFINE_COMMAND(setCurrentActivity, 1)
+{
+    switchToActivity(args(1));
+
+    return 1;
+}
+
+DEFINE_COMMAND(nextActivity, 0)
+{
+    const auto activities = controller->activities();
+    const auto currentActivity = controller->currentActivity();
+
+    for (int i = 0; i < activities.count() - 1; ++i) {
+        if (activities[i] == currentActivity) {
+            switchToActivity(activities[i + 1]);
+            return 0;
+        }
+    }
+
+    switchToActivity(activities[0]);
+
+    return 0;
+}
+
+DEFINE_COMMAND(previousActivity, 0)
+{
+    const auto activities = controller->activities();
+    const auto currentActivity = controller->currentActivity();
+
+    for (int i = 1; i < activities.count(); ++i) {
+        if (activities[i] == currentActivity) {
+            switchToActivity(activities[i - 1]);
+            return 0;
+        }
+    }
+
+    switchToActivity(activities.last());
+
+    return 0;
+}
+
+void printHelp()
+{
+    if (!flags.bare) {
+        qDebug()
+            << "\nModifiers (applied only to trailing commands):"
+                 << "\n    --bare, --no-bare        - show minimal info vs show everything"
+                 << "\n    --color, --no-color      - make the output pretty"
+
+            << "\n\nCommands:"
+                 << "\n    --list-activities        - lists all activities"
+                 << "\n    --create-activity Name   - creates a new activity with the specified name"
+                 << "\n    --remove-activity ID     - removes the activity with the specified id"
+                 << "\n    --start-activity ID      - starts the specified activity"
+                 << "\n    --stop-activity ID       - stops the specified activity"
+
+                 << "\n    --current-activity       - show the current activity"
+                 << "\n    --set-current-activity   - sets the current activity"
+                 << "\n    --next-activity          - switches to the next activity (in list-activities order)"
+                 << "\n    --previous-activity      - switches to the previous activity (in list-activities order)"
+
+                 << "\n    --activity-property What ID"
+                 << "\n                             - gets activity name, icon or description"
+                 << "\n    --set-activity-property What ID Value"
+                 << "\n                             - changes activity name, icon or description"
+                 ;
+
+    } else {
+        qDebug()
+                 << "\n--bare"
+                 << "\n--no-bare"
+                 << "\n--color"
+                 << "\n--no-color"
+                 << "\n--list-activities"
+                 << "\n--create-activitya NAME"
+                 << "\n--remove-activity ID"
+
+                 << "\n--current-activity"
+                 << "\n--set-current-activity"
+                 << "\n--next-activity"
+                 << "\n--previous-activity"
+                 ;
+
+    }
+
+}
+
+int main(int argc, char *argv[])
+{
+    QCoreApplication app(argc, argv);
+
+    QTimer::singleShot(0, &app, [] {
+
+        const auto args = QCoreApplication::arguments();
+
+        controller = new KActivities::Controller();
+
+        while (controller->serviceStatus() != KActivities::Controller::Running) {
+            QCoreApplication::processEvents();
+        }
+
+        #define MATCH_COMMAND(Command)                                         \
+            else if (args[argId] == QLatin1String("--") + toDashes(QStringLiteral(#Command))) \
+            {                                                                  \
+                argId += 1 + Command##_command({ args, argId })();             \
+            }
+
+        if (args.count() <= 1) {
+            printHelp();
+
+        } else for (int argId = 1; argId < args.count(); ) {
+            if (args[argId] == QLatin1String("--help")) {
+                printHelp();
+                argId++;
+            }
+
+            MATCH_COMMAND(bare)
+            MATCH_COMMAND(noBare)
+            MATCH_COMMAND(color)
+            MATCH_COMMAND(noColor)
+
+            MATCH_COMMAND(listActivities)
+
+            MATCH_COMMAND(currentActivity)
+            MATCH_COMMAND(setCurrentActivity)
+            MATCH_COMMAND(activityProperty)
+            MATCH_COMMAND(setActivityProperty)
+            MATCH_COMMAND(nextActivity)
+            MATCH_COMMAND(previousActivity)
+
+            MATCH_COMMAND(createActivity)
+            MATCH_COMMAND(removeActivity)
+            MATCH_COMMAND(startActivity)
+            MATCH_COMMAND(stopActivity)
+
+            else {
+                qDebug() << "Skipping unknown argument" << args[argId];
+                argId++;
+            }
+
+        }
+
+        delete controller;
+
+        QCoreApplication::quit();
+
+    });
+
+    return app.exec();
+}
+
diff --git a/src/cli/utils.h b/src/cli/utils.h
new file mode 100644 (file)
index 0000000..742e7e8
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ *   Copyright (C) 2016 Ivan Čukić <ivan.cukic(at)kde.org>
+ *
+ *   This library is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU Lesser General Public
+ *   License as published by the Free Software Foundation; either
+ *   version 2.1 of the License, or (at your option) version 3, or any
+ *   later version accepted by the membership of KDE e.V. (or its
+ *   successor approved by the membership of KDE e.V.), which shall
+ *   act as a proxy defined in Section 6 of version 3 of the license.
+ *
+ *   This library is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *   Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this library.
+ *   If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef KACTIVITIES_UTILS_H
+#define KACTIVITIES_UTILS_H
+
+QTextStream out(stdout);
+
+class StringListView {
+public:
+    StringListView(const QStringList &list, int start, int end = -1)
+        : m_list(list)
+        , m_start(start)
+        , m_size((end == -1 ? list.count() : end) - start)
+    {
+
+    }
+
+    const QString &operator() (int index) const
+    {
+        return m_list[m_start + index];
+    }
+
+    int count() const
+    {
+        return m_size;
+    }
+
+private:
+    const QStringList &m_list;
+    int m_start;
+    int m_size;
+
+};
+
+KActivities::Controller *controller = nullptr;
+
+class Flags {
+public:
+    Flags()
+      : bare(false)
+      , color(true)
+    {
+    }
+
+    bool bare;
+    bool color;
+
+} flags;
+
+QString toDashes(const QString &command)
+{
+    QString result(command);
+
+    for (int i = 0; i < result.size() - 1; ++i) {
+        if (result[i].isLower() &&
+            result[i+1].isUpper()) {
+            result[i+1] = result[i+1].toLower();
+            result.insert(i+1, QStringLiteral("-"));
+        }
+    }
+
+    return result;
+}
+
+void printActivity(const QString &id)
+{
+    if (flags.bare) {
+        out << id << "\n";
+
+    } else {
+        using namespace KActivities;
+        Info info(id);
+
+        out
+            << (
+                info.id() == controller->currentActivity() ? "[CURRENT] " :
+                info.state() == Info::Running    ? "[RUNNING] " :
+                info.state() == Info::Stopped    ? "[STOPPED] " :
+                info.state() == Info::Starting   ? "[STARTING]" :
+                info.state() == Info::Stopping   ? "[STOPPING]" :
+                                                   "unknown   "
+            )
+            << info.id()
+            << " "
+            << info.name()
+            << " ("
+            << info.icon()
+            << ")\n"
+             ;
+
+        if (info.id() == controller->currentActivity()
+            && info.state() != Info::Running) {
+            qWarning()
+                 << "Activity is the current one, but its state is"
+                 << (
+                    info.state() == Info::Running  ? "running"  :
+                    info.state() == Info::Stopped  ? "stopped"  :
+                    info.state() == Info::Starting ? "starting" :
+                    info.state() == Info::Stopping ? "stopping" :
+                                                     "unknown   "
+                 );
+        }
+    }
+}
+
+template <typename T>
+T awaitFuture(const QFuture<T> &future)
+{
+    while (!future.isFinished()) {
+        QCoreApplication::processEvents();
+    }
+
+    return future.result();
+}
+
+void awaitFuture(const QFuture<void> &future)
+{
+    while (!future.isFinished()) {
+        QCoreApplication::processEvents();
+    }
+}
+
+void switchToActivity(const QString &id)
+{
+    auto result = awaitFuture(controller->setCurrentActivity(id));
+
+    if (!flags.bare) {
+        if (result) {
+            qDebug() << "Current activity is" << id;
+        } else {
+            qDebug() << "Failed to change the activity";
+        }
+    }
+}
+
+
+#define DEFINE_COMMAND(Command, MinArgCount)                                   \
+    struct Command##_command {                                                 \
+        const StringListView &args;                                            \
+        Command##_command(const StringListView &args)                          \
+            : args(args)                                                       \
+        {                                                                      \
+            if (args.count() < MinArgCount + 1) {                              \
+                qFatal("not enough arguments for " #Command);                  \
+            }                                                                  \
+        }                                                                      \
+                                                                               \
+        int operator()();                                                      \
+    };                                                                         \
+                                                                               \
+    int Command##_command::operator()()
+
+#endif
diff --git a/src/common/dbus/common.h b/src/common/dbus/common.h
new file mode 100644 (file)
index 0000000..60d5f66
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ *   Copyright (C) 2010 - 2016 by Ivan Cukic <ivan.cukic(at)kde.org>
+ *
+ *   This library is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU Lesser General Public
+ *   License as published by the Free Software Foundation; either
+ *   version 2.1 of the License, or (at your option) version 3, or any
+ *   later version accepted by the membership of KDE e.V. (or its
+ *   successor approved by the membership of KDE e.V.), which shall
+ *   act as a proxy defined in Section 6 of version 3 of the license.
+ *
+ *   This library is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *   Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this library.
+ *   If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef DBUS_COMMON_H
+#define DBUS_COMMON_H
+
+#include <QDBusConnection>
+#include <QDBusInterface>
+
+#define KAMD_DBUS_SERVICE                                                      \
+    QStringLiteral("org.kde.ActivityManager")
+
+#define KAMD_DBUS_OBJECT_PATH(A)                                               \
+    (sizeof(#A) > 2 ? QLatin1String("/ActivityManager/" #A)                    \
+                    : QLatin1String("/ActivityManager"))
+
+#define KAMD_DBUS_OBJECT(A)                                                    \
+    QLatin1String("org.kde.ActivityManager." #A)
+
+#define KAMD_DBUS_INTERFACE(OBJECT_PATH, OBJECT, PARENT)                       \
+    QDBusInterface(KAMD_DBUS_SERVICE,                                          \
+                   KAMD_DBUS_OBJECT_PATH(OBJECT_PATH),                         \
+                   KAMD_DBUS_OBJECT(OBJECT),                                   \
+                   QDBusConnection::sessionBus(),                              \
+                   PARENT)
+
+#define KAMD_DBUS_DECL_INTERFACE(VAR, OBJECT_PATH, OBJECT)                     \
+    QDBusInterface VAR(KAMD_DBUS_SERVICE,                                      \
+                   KAMD_DBUS_OBJECT_PATH(OBJECT_PATH),                         \
+                   KAMD_DBUS_OBJECT(OBJECT),                                   \
+                   QDBusConnection::sessionBus(),                              \
+                   nullptr)
+
+#define KAMD_DBUS_CLASS_INTERFACE(OBJECT_PATH, OBJECT, PARENT)                 \
+    org::kde::ActivityManager::OBJECT(                                         \
+                KAMD_DBUS_SERVICE,                                             \
+                KAMD_DBUS_OBJECT_PATH(OBJECT_PATH),                            \
+                QDBusConnection::sessionBus(),                                 \
+                PARENT)
+
+#endif // DBUS_COMMON_H
+
diff --git a/src/common/dbus/org.kde.ActivityManager.Activities.cpp b/src/common/dbus/org.kde.ActivityManager.Activities.cpp
new file mode 100644 (file)
index 0000000..9510b39
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ *   Copyright (C) 2010 - 2016 by Ivan Cukic <ivan.cukic(at)kde.org>
+ *
+ *   This library is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU Lesser General Public
+ *   License as published by the Free Software Foundation; either
+ *   version 2.1 of the License, or (at your option) version 3, or any
+ *   later version accepted by the membership of KDE e.V. (or its
+ *   successor approved by the membership of KDE e.V.), which shall
+ *   act as a proxy defined in Section 6 of version 3 of the license.
+ *
+ *   This library is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *   Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this library.
+ *   If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "org.kde.ActivityManager.Activities.h"
+
+#include <QMetaType>
+#include <QDBusMetaType>
+
+namespace details {
+
+class ActivityInfoStaticInit {
+public:
+    ActivityInfoStaticInit()
+    {
+        qDBusRegisterMetaType<ActivityInfo>();
+        qDBusRegisterMetaType<ActivityInfoList>();
+    }
+
+    static ActivityInfoStaticInit _instance;
+};
+
+ActivityInfoStaticInit ActivityInfoStaticInit::_instance;
+
+} // namespace details
+
+QDBusArgument &operator<<(QDBusArgument &arg, const ActivityInfo r)
+{
+    arg.beginStructure();
+
+    arg << r.id;
+    arg << r.name;
+    arg << r.description;
+    arg << r.icon;
+    arg << r.state;
+
+    arg.endStructure();
+
+    return arg;
+}
+
+const QDBusArgument &operator>>(const QDBusArgument &arg, ActivityInfo &r)
+{
+    arg.beginStructure();
+
+    arg >> r.id;
+    arg >> r.name;
+    arg >> r.description;
+    arg >> r.icon;
+    arg >> r.state;
+
+    arg.endStructure();
+
+    return arg;
+}
+
+QDebug operator<<(QDebug dbg, const ActivityInfo &r)
+{
+    dbg << "ActivityInfo(" << r.id << r.name << ")";
+    return dbg.space();
+}
diff --git a/src/common/dbus/org.kde.ActivityManager.Activities.h b/src/common/dbus/org.kde.ActivityManager.Activities.h
new file mode 100644 (file)
index 0000000..ac8432f
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ *   Copyright (C) 2010 - 2016 by Ivan Cukic <ivan.cukic(at)kde.org>
+ *
+ *   This library is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU Lesser General Public
+ *   License as published by the Free Software Foundation; either
+ *   version 2.1 of the License, or (at your option) version 3, or any
+ *   later version accepted by the membership of KDE e.V. (or its
+ *   successor approved by the membership of KDE e.V.), which shall
+ *   act as a proxy defined in Section 6 of version 3 of the license.
+ *
+ *   This library is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *   Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this library.
+ *   If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef KAMD_ORG_KDE_ACTIVITYMANAGER_ACTIVITIES_H
+#define KAMD_ORG_KDE_ACTIVITYMANAGER_ACTIVITIES_H
+
+#include <QString>
+#include <QList>
+#include <QDBusArgument>
+#include <QDebug>
+
+struct ActivityInfo {
+    QString id;
+    QString name;
+    QString description;
+    QString icon;
+    int state;
+
+    ActivityInfo(const QString &id = QString(),
+                 const QString &name = QString(),
+                 const QString &description = QString(),
+                 const QString &icon = QString(),
+                 int state = 0)
+        : id(id)
+        , name(name)
+        , description(description)
+        , icon(icon)
+        , state(state)
+    {
+    }
+};
+
+typedef QList<ActivityInfo> ActivityInfoList;
+
+Q_DECLARE_METATYPE(ActivityInfo)
+Q_DECLARE_METATYPE(ActivityInfoList)
+
+QDBusArgument &operator<<(QDBusArgument &arg, const ActivityInfo);
+const QDBusArgument &operator>>(const QDBusArgument &arg, ActivityInfo &rec);
+
+QDebug operator<<(QDebug dbg, const ActivityInfo &r);
+
+#endif // KAMD_ORG_KDE_ACTIVITYMANAGER_ACTIVITIES_H
diff --git a/src/common/dbus/org.kde.ActivityManager.Activities.xml b/src/common/dbus/org.kde.ActivityManager.Activities.xml
new file mode 100644 (file)
index 0000000..8d3e074
--- /dev/null
@@ -0,0 +1,115 @@
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+<node>
+  <interface name="org.kde.ActivityManager.Activities">
+
+    <method name="CurrentActivity">
+      <arg type="s" direction="out"/>
+    </method>
+    <method name="SetCurrentActivity">
+      <arg type="b" direction="out"/>
+      <arg name="activity" type="s" direction="in"/>
+    </method>
+
+    <method name="AddActivity">
+      <arg type="s" direction="out"/>
+      <arg name="name" type="s" direction="in"/>
+    </method>
+    <method name="StartActivity">
+      <arg name="activity" type="s" direction="in"/>
+    </method>
+    <method name="StopActivity">
+      <arg name="activity" type="s" direction="in"/>
+    </method>
+    <method name="ActivityState">
+      <arg type="i" direction="out"/>
+      <arg name="activity" type="s" direction="in"/>
+    </method>
+    <method name="RemoveActivity">
+      <arg name="activity" type="s" direction="in"/>
+    </method>
+
+    <method name="ListActivities">
+      <arg type="as" direction="out"/>
+    </method>
+    <method name="ListActivities">
+      <arg type="as" direction="out"/>
+      <arg name="state" type="i" direction="in"/>
+    </method>
+
+    <method name="ListActivitiesWithInformation">
+      <arg type="a(sssd)" direction="out"/>
+      <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="ActivityInfoList" />
+      <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="ActivityInfoList" />
+    </method>
+    <method name="ActivityInformation">
+      <arg type="(sssd)" direction="out"/>
+      <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="ActivityInfo" />
+      <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="ActivityInfo" />
+      <arg name="activity" type="s" direction="in"/>
+    </method>
+
+    <method name="ActivityName">
+      <arg type="s" direction="out"/>
+      <arg name="activity" type="s" direction="in"/>
+    </method>
+    <method name="SetActivityName">
+      <arg name="activity" type="s" direction="in"/>
+      <arg name="name" type="s" direction="in"/>
+    </method>
+
+    <method name="ActivityDescription">
+      <arg type="s" direction="out"/>
+      <arg name="activity" type="s" direction="in"/>
+    </method>
+    <method name="SetActivityDescription">
+      <arg name="activity" type="s" direction="in"/>
+      <arg name="description" type="s" direction="in"/>
+    </method>
+
+    <method name="ActivityIcon">
+      <arg type="s" direction="out"/>
+      <arg name="activity" type="s" direction="in"/>
+    </method>
+    <method name="SetActivityIcon">
+      <arg name="activity" type="s" direction="in"/>
+      <arg name="icon" type="s" direction="in"/>
+    </method>
+
+    <signal name="CurrentActivityChanged">
+      <arg name="activity" type="s" direction="out"/>
+    </signal>
+
+    <signal name="ActivityAdded">
+      <arg name="activity" type="s" direction="out"/>
+    </signal>
+    <signal name="ActivityStarted">
+      <arg name="activity" type="s" direction="out"/>
+    </signal>
+    <signal name="ActivityStopped">
+      <arg name="activity" type="s" direction="out"/>
+    </signal>
+    <signal name="ActivityRemoved">
+      <arg name="activity" type="s" direction="out"/>
+    </signal>
+    <signal name="ActivityChanged">
+      <arg name="activity" type="s" direction="out"/>
+    </signal>
+    <signal name="ActivityNameChanged">
+      <arg name="activity" type="s" direction="out"/>
+      <arg name="name" type="s" direction="out"/>
+    </signal>
+    <signal name="ActivityDescriptionChanged">
+      <arg name="activity" type="s" direction="out"/>
+      <arg name="description" type="s" direction="out"/>
+    </signal>
+    <signal name="ActivityIconChanged">
+      <arg name="activity" type="s" direction="out"/>
+      <arg name="icon" type="s" direction="out"/>
+    </signal>
+    <signal name="ActivityStateChanged">
+      <arg name="activity" type="s" direction="out"/>
+      <arg name="state" type="i" direction="out"/>
+    </signal>
+
+  </interface>
+</node>
diff --git a/src/common/dbus/org.kde.ActivityManager.Application.xml b/src/common/dbus/org.kde.ActivityManager.Application.xml
new file mode 100644 (file)
index 0000000..fc4a36b
--- /dev/null
@@ -0,0 +1,14 @@
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+<node>
+  <interface name="org.kde.ActivityManager.Application">
+    <method name="quit">
+    </method>
+    <method name="serviceVersion">
+      <arg type="s" direction="out"/>
+    </method>
+    <method name="loadPlugin">
+      <arg type="b" direction="out"/>
+      <arg name="plugin" type="s" direction="in"/>
+    </method>
+  </interface>
+</node>
diff --git a/src/common/dbus/org.kde.ActivityManager.Features.xml b/src/common/dbus/org.kde.ActivityManager.Features.xml
new file mode 100644 (file)
index 0000000..e45f046
--- /dev/null
@@ -0,0 +1,21 @@
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+<node>
+  <interface name="org.kde.ActivityManager.Features">
+    <method name="IsFeatureOperational">
+      <arg type="b" direction="out"/>
+      <arg name="feature" type="s" direction="in"/>
+    </method>
+    <method name="ListFeatures">
+      <arg type="as" direction="out"/>
+      <arg name="module" type="s" direction="in"/>
+    </method>
+    <method name="GetValue">
+      <arg type="v" direction="out"/>
+      <arg name="property" type="s" direction="in"/>
+    </method>
+    <method name="SetValue">
+      <arg name="property" type="s" direction="in"/>
+      <arg name="value" type="v" direction="in"/>
+    </method>
+  </interface>
+</node>
diff --git a/src/common/dbus/org.kde.ActivityManager.Resources.xml b/src/common/dbus/org.kde.ActivityManager.Resources.xml
new file mode 100644 (file)
index 0000000..5f8e725
--- /dev/null
@@ -0,0 +1,23 @@
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+<node>
+  <interface name="org.kde.ActivityManager.Resources">
+
+    <method name="RegisterResourceEvent">
+      <arg name="application" type="s" direction="in"/>
+      <arg name="windowId" type="u" direction="in"/>
+      <arg name="uri" type="s" direction="in"/>
+      <arg name="event" type="u" direction="in"/>
+    </method>
+
+    <method name="RegisterResourceMimetype">
+      <arg name="uri" type="s" direction="in"/>
+      <arg name="mimetype" type="s" direction="in"/>
+    </method>
+
+    <method name="RegisterResourceTitle">
+      <arg name="uri" type="s" direction="in"/>
+      <arg name="title" type="s" direction="in"/>
+    </method>
+
+  </interface>
+</node>
diff --git a/src/common/dbus/org.kde.ActivityManager.ResourcesLinking.xml b/src/common/dbus/org.kde.ActivityManager.ResourcesLinking.xml
new file mode 100644 (file)
index 0000000..9d04cdd
--- /dev/null
@@ -0,0 +1,49 @@
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+<node>
+  <interface name="org.kde.ActivityManager.ResourcesLinking">
+
+    <method name="LinkResourceToActivity">
+      <arg name="agent" type="s" direction="in"/>
+      <arg name="resource" type="s" direction="in"/>
+      <arg name="activity" type="s" direction="in"/>
+    </method>
+    <method name="LinkResourceToActivity">
+      <arg name="agent" type="s" direction="in"/>
+      <arg name="resource" type="s" direction="in"/>
+    </method>
+
+    <method name="UnlinkResourceFromActivity">
+      <arg name="agent" type="s" direction="in"/>
+      <arg name="resource" type="s" direction="in"/>
+      <arg name="activity" type="s" direction="in"/>
+    </method>
+    <method name="UnlinkResourceFromActivity">
+      <arg name="agent" type="s" direction="in"/>
+      <arg name="resource" type="s" direction="in"/>
+    </method>
+
+    <method name="IsResourceLinkedToActivity">
+      <arg name="agent" type="s" direction="in"/>
+      <arg name="resource" type="s" direction="in"/>
+      <arg name="activity" type="s" direction="in"/>
+      <arg type="b" direction="out"/>
+    </method>
+    <method name="IsResourceLinkedToActivity">
+      <arg name="agent" type="s" direction="in"/>
+      <arg name="resource" type="s" direction="in"/>
+      <arg type="b" direction="out"/>
+    </method>
+
+    <signal name="ResourceLinkedToActivity">
+      <arg name="agent" type="s" direction="out"/>
+      <arg name="resource" type="s" direction="out"/>
+      <arg name="activity" type="s" direction="out"/>
+    </signal>
+    <signal name="ResourceUnlinkedFromActivity">
+      <arg name="agent" type="s" direction="out"/>
+      <arg name="resource" type="s" direction="out"/>
+      <arg name="activity" type="s" direction="out"/>
+    </signal>
+
+  </interface>
+</node>
diff --git a/src/common/dbus/org.kde.ActivityManager.ResourcesScoring.xml b/src/common/dbus/org.kde.ActivityManager.ResourcesScoring.xml
new file mode 100644 (file)
index 0000000..0190ff7
--- /dev/null
@@ -0,0 +1,45 @@
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+<node>
+    <interface name="org.kde.ActivityManager.ResourcesScoring">
+
+        <signal name="ResourceScoreUpdated">
+            <arg name="activity" type="s" direction="out"/>
+            <arg name="client" type="s" direction="out"/>
+            <arg name="resource" type="s" direction="out"/>
+            <arg name="score" type="d" direction="out"/>
+            <arg name="lastUpdate" type="u" direction="out"/>
+            <arg name="firstUpdate" type="u" direction="out"/>
+        </signal>
+        <signal name="ResourceScoreDeleted">
+            <arg name="activity" type="s" direction="out"/>
+            <arg name="client" type="s" direction="out"/>
+            <arg name="resource" type="s" direction="out"/>
+        </signal>
+
+        <signal name="RecentStatsDeleted">
+            <arg name="activity" type="s" direction="out"/>
+            <arg name="count" type="i" direction="out"/>
+            <arg name="what" type="s" direction="out"/>
+        </signal>
+        <signal name="EarlierStatsDeleted">
+            <arg name="activity" type="s" direction="out"/>
+            <arg name="months" type="i" direction="out"/>
+        </signal>
+
+        <method name="DeleteStatsForResource">
+            <arg name="activity" type="s" direction="in"/>
+            <arg name="client" type="s" direction="in"/>
+            <arg name="resource" type="s" direction="in"/>
+        </method>
+        <method name="DeleteRecentStats">
+            <arg name="activity" type="s" direction="in"/>
+            <arg name="count" type="i" direction="in"/>
+            <arg name="what" type="s" direction="in"/>
+        </method>
+        <method name="DeleteEarlierStats">
+            <arg name="activity" type="s" direction="in"/>
+            <arg name="months" type="i" direction="in"/>
+        </method>
+
+    </interface>
+</node>
diff --git a/src/imports/CMakeLists.txt b/src/imports/CMakeLists.txt
new file mode 100644 (file)
index 0000000..2efda2b
--- /dev/null
@@ -0,0 +1,41 @@
+# vim:set softtabstop=3 shiftwidth=3 tabstop=3 expandtab:
+
+project (kactivities-imports)
+find_package (ECM 0.0.8 REQUIRED NO_MODULE)
+set (CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH})
+
+find_package (Qt5 REQUIRED NO_MODULE COMPONENTS Sql Gui Qml Quick Sql)
+find_package (KF5Config     ${KF5_DEP_VERSION} CONFIG REQUIRED)
+find_package (KF5CoreAddons ${KF5_DEP_VERSION} CONFIG REQUIRED)
+
+set (
+   kactivities_imports_LIB_SRCS
+   activitiesextensionplugin.cpp
+   activitymodel.cpp
+   activityinfo.cpp
+#  resourcemodel.cpp
+   resourceinstance.cpp
+
+   ${KACTIVITIES_CURRENT_ROOT_SOURCE_DIR}/src/utils/dbusfuture_p.cpp
+   )
+
+add_library (kactivitiesextensionplugin SHARED ${kactivities_imports_LIB_SRCS})
+
+target_link_libraries (
+   kactivitiesextensionplugin
+   Qt5::Core
+   Qt5::DBus
+   Qt5::Gui
+   Qt5::Qml
+   Qt5::Quick
+   Qt5::Sql
+   KF5::Activities
+   KF5::ConfigCore
+   KF5::CoreAddons
+   )
+
+## install
+
+install (TARGETS kactivitiesextensionplugin DESTINATION ${KDE_INSTALL_QMLDIR}/org/kde/activities)
+install (FILES qmldir DESTINATION ${KDE_INSTALL_QMLDIR}/org/kde/activities)
+
diff --git a/src/imports/README b/src/imports/README
new file mode 100644 (file)
index 0000000..bb33c28
--- /dev/null
@@ -0,0 +1,7 @@
+org.kde.activities namespace does not guarantee a
+stable api.
+
+If you want to use components from it, make sure
+you follow the further developments closely.
+
+
diff --git a/src/imports/activitiesextensionplugin.cpp b/src/imports/activitiesextensionplugin.cpp
new file mode 100644 (file)
index 0000000..fe40cda
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ *   Copyright (C) 2012, 2013, 2014, 2015 Ivan Cukic <ivan.cukic(at)kde.org>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU Library General Public License as
+ *   published by the Free Software Foundation; either version 2, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details
+ *
+ *   You should have received a copy of the GNU Library General Public
+ *   License along with this program; if not, write to the
+ *   Free Software Foundation, Inc.,
+ *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include "activitiesextensionplugin.h"
+
+
+#include "activitymodel.h"
+#include "activityinfo.h"
+#include "resourceinstance.h"
+
+// #include "resourcemodel.h"
+
+// TODO: Clean up unused classes from the imports module
+
+// TODO: Since plasma is now dealing with activity model wallpapers,
+//       replace ActivityModel with the KActivities::ActivitiesModel
+//       (but keep the name)
+
+
+ActivitiesExtensionPlugin::ActivitiesExtensionPlugin(QObject *parent)
+    : QQmlExtensionPlugin(parent)
+{
+}
+
+void ActivitiesExtensionPlugin::registerTypes(const char *uri)
+{
+    Q_ASSERT(QLatin1String(uri) == QLatin1String("org.kde.activities"));
+
+    // Used by applets/activitybar
+    qmlRegisterType<KActivities::Imports::ActivityModel>(uri, 0, 1, "ActivityModel");
+
+    qmlRegisterType<KActivities::Imports::ActivityInfo>(uri, 0, 1, "ActivityInfo");
+    qmlRegisterType<KActivities::Imports::ResourceInstance>(uri, 0, 1, "ResourceInstance");
+
+    // This one is removed in favor of KActivities::Stats::ResultModel.
+    // Subclass it, and make it do what you want.
+    // qmlRegisterType<KActivities::Imports::ResourceModel>(uri, 0, 1, "ResourceModel");
+}
+
diff --git a/src/imports/activitiesextensionplugin.h b/src/imports/activitiesextensionplugin.h
new file mode 100644 (file)
index 0000000..d95fe9e
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ *   Copyright (C) 2011, 2012, 2013, 2014, 2015 by Ivan Cukic <ivan.cukic(at)kde.org>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU Library General Public License as
+ *   published by the Free Software Foundation; either version 2, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details
+ *
+ *   You should have received a copy of the GNU Library General Public
+ *   License along with this program; if not, write to the
+ *   Free Software Foundation, Inc.,
+ *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#ifndef KACTIVITIES_ACTIVITIES_EXTENSION_PLUGIN_H
+#define KACTIVITIES_ACTIVITIES_EXTENSION_PLUGIN_H
+
+#include <QQmlExtensionPlugin>
+
+class ActivitiesExtensionPlugin : public QQmlExtensionPlugin {
+    Q_OBJECT
+    Q_PLUGIN_METADATA(IID "org.kde.activities")
+
+public:
+    explicit ActivitiesExtensionPlugin(QObject *parent = nullptr);
+    void registerTypes(const char *uri) override;
+};
+
+#endif // KACTIVITIES_ACTIVITIES_EXTENSION_PLUGIN_H
+
diff --git a/src/imports/activityinfo.cpp b/src/imports/activityinfo.cpp
new file mode 100644 (file)
index 0000000..097377b
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ *   Copyright (C) 2012, 2013, 2014, 2015 Ivan Cukic <ivan.cukic(at)kde.org>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License version 2,
+ *   or (at your option) any later version, as published by the Free
+ *   Software Foundation
+ *
+ *   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.
+ */
+
+// Self
+#include "activityinfo.h"
+
+namespace KActivities {
+namespace Imports {
+
+ActivityInfo::ActivityInfo(QObject *parent)
+    : QObject(parent)
+    , m_showCurrentActivity(false)
+{
+    connect(&m_service, &KActivities::Controller::currentActivityChanged,
+            this, &ActivityInfo::setCurrentActivity);
+}
+
+ActivityInfo::~ActivityInfo()
+{
+}
+
+void ActivityInfo::setCurrentActivity(const QString &id)
+{
+    if (!m_showCurrentActivity) return;
+
+    setIdInternal(id);
+
+    emit nameChanged(m_info->name());
+    emit descriptionChanged(m_info->description());
+    emit iconChanged(m_info->icon());
+}
+
+void ActivityInfo::setActivityId(const QString &id)
+{
+    m_showCurrentActivity = (id == QLatin1String(":current"));
+
+    setIdInternal(m_showCurrentActivity ?
+            m_service.currentActivity() : id);
+}
+
+void ActivityInfo::setIdInternal(const QString &id)
+{
+    using namespace KActivities;
+
+    // We are killing the old info object, if any
+    m_info.reset(new KActivities::Info(id));
+
+    auto ptr = m_info.get();
+
+    connect(ptr, &Info::nameChanged,
+            this, &ActivityInfo::nameChanged);
+    connect(ptr, &Info::descriptionChanged,
+            this, &ActivityInfo::descriptionChanged);
+    connect(ptr, &Info::iconChanged,
+            this, &ActivityInfo::iconChanged);
+}
+
+#define CREATE_GETTER_AND_SETTER(WHAT, What)                                   \
+    QString ActivityInfo::What() const                                         \
+    {                                                                          \
+        return m_info ? m_info->What() : QString();                            \
+    }                                                                          \
+                                                                               \
+    void ActivityInfo::set##WHAT(const QString &value)                         \
+    {                                                                          \
+        if (!m_info)                                                           \
+            return;                                                            \
+                                                                               \
+        m_service.setActivity##WHAT(m_info->id(), value);                      \
+    }
+
+CREATE_GETTER_AND_SETTER(Name, name)
+CREATE_GETTER_AND_SETTER(Description, description)
+CREATE_GETTER_AND_SETTER(Icon, icon)
+
+#undef CREATE_GETTER_AND_SETTER
+
+QString ActivityInfo::activityId() const
+{
+    return m_info ? m_info->id() : QString();
+}
+
+bool ActivityInfo::valid() const
+{
+    return true;
+}
+
+
+} // namespace Imports
+} // namespace KActivities
+
+// #include "activityinfo.moc"
+
diff --git a/src/imports/activityinfo.h b/src/imports/activityinfo.h
new file mode 100644 (file)
index 0000000..f85d627
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ *   Copyright (C) 2012, 2013, 2014, 2015 Ivan Cukic <ivan.cukic(at)kde.org>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License version 2,
+ *   or (at your option) any later version, as published by the Free
+ *   Software Foundation
+ *
+ *   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 KACTIVITIES_IMPORTS_ACTIVITY_INFO_H
+#define KACTIVITIES_IMPORTS_ACTIVITY_INFO_H
+
+// Qt
+#include <QObject>
+
+// STL
+#include <memory>
+
+// Local
+#include <lib/controller.h>
+#include <lib/info.h>
+
+namespace KActivities {
+namespace Imports {
+
+/**
+ * ActivityInfo
+ */
+
+class ActivityInfo : public QObject {
+    Q_OBJECT
+
+    /**
+     * Unique identifier of the activity
+     */
+    Q_PROPERTY(QString activityId READ activityId WRITE setActivityId NOTIFY activityIdChanged)
+
+    /**
+     * Name of the activity
+     */
+    Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
+
+    /**
+     * Name of the activity
+     */
+    Q_PROPERTY(QString description READ description WRITE setDescription NOTIFY descriptionChanged)
+
+    /**
+     * Activity icon
+     */
+    Q_PROPERTY(QString icon READ icon WRITE setIcon NOTIFY iconChanged)
+
+    /**
+     * Is the activity a valid one - does it exist?
+     */
+    Q_PROPERTY(bool valid READ valid NOTIFY validChanged)
+
+public:
+    explicit ActivityInfo(QObject *parent = nullptr);
+    virtual ~ActivityInfo();
+
+public Q_SLOTS:
+    void setActivityId(const QString &id);
+    QString activityId() const;
+
+    void setName(const QString &name);
+    QString name() const;
+
+    void setDescription(const QString &description);
+    QString description() const;
+
+    void setIcon(const QString &icon);
+    QString icon() const;
+
+    bool valid() const;
+
+Q_SIGNALS:
+    void activityIdChanged(const QString &id);
+    void nameChanged(const QString &name);
+    void descriptionChanged(const QString &description);
+    void iconChanged(const QString &icon);
+    void validChanged(bool valid);
+
+private Q_SLOTS:
+    void setCurrentActivity(const QString &id);
+
+private:
+    void setIdInternal(const QString &id);
+
+    KActivities::Controller m_service;
+    std::unique_ptr<KActivities::Info> m_info;
+    bool m_showCurrentActivity;
+};
+
+} // namespace Imports
+} // namespace KActivities
+
+#endif // KACTIVITIES_IMPORTS_ACTIVITY_INFO_H
+
diff --git a/src/imports/activitymodel.cpp b/src/imports/activitymodel.cpp
new file mode 100644 (file)
index 0000000..e017337
--- /dev/null
@@ -0,0 +1,654 @@
+/*
+ *   Copyright (C) 2012, 2013, 2014, 2015 Ivan Cukic <ivan.cukic(at)kde.org>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License version 2,
+ *   or (at your option) any later version, as published by the Free
+ *   Software Foundation
+ *
+ *   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.
+ */
+
+// Self
+#include "activitymodel.h"
+
+// Qt
+#include <QByteArray>
+#include <QDBusPendingCall>
+#include <QDBusPendingCallWatcher>
+#include <QDebug>
+#include <QHash>
+#include <QIcon>
+#include <QList>
+#include <QFutureWatcher>
+#include <QModelIndex>
+
+// KDE
+#include <kconfig.h>
+#include <kconfiggroup.h>
+#include <kdirwatch.h>
+
+// Boost
+#include <boost/range/algorithm/find_if.hpp>
+#include <boost/range/algorithm/binary_search.hpp>
+#include <boost/range/adaptor/filtered.hpp>
+#include <boost/optional.hpp>
+
+// Local
+#include "utils/remove_if.h"
+#define ENABLE_QJSVALUE_CONTINUATION
+#include "utils/continue_with.h"
+#include "utils/model_updaters.h"
+
+using kamd::utils::continue_with;
+
+
+namespace KActivities {
+namespace Imports {
+
+class ActivityModel::Private {
+public:
+    DECLARE_RAII_MODEL_UPDATERS(ActivityModel)
+
+    /**
+     * Returns whether the activity has a desired state.
+     * If the state is 0, returns true
+     */
+    template <typename T>
+    static inline bool matchingState(InfoPtr activity,
+                                     T states)
+    {
+        // Are we filtering activities on their states?
+        if (!states.empty()
+            && !boost::binary_search(states, activity->state())) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Searches for the activity.
+     * Returns an option(index, iterator) for the found activity.
+     */
+    template <typename _Container>
+    static inline
+    boost::optional<
+        std::pair<unsigned int, typename _Container::const_iterator>
+    >
+    activityPosition(const _Container &container, const QString &activityId)
+    {
+        using ActivityPosition =
+                decltype(activityPosition(container, activityId));
+        using ContainerElement =
+                typename _Container::value_type;
+
+        auto position = boost::find_if(container,
+            [&] (const ContainerElement &activity) {
+                return activity->id() == activityId;
+            }
+        );
+
+        return (position != container.end()) ?
+            ActivityPosition(
+                std::make_pair(position - container.begin(), position)
+            ) :
+            ActivityPosition();
+    }
+
+    /**
+     * Notifies the model that an activity was updated
+     */
+    template <typename _Model, typename _Container>
+    static inline
+    void emitActivityUpdated(_Model *model,
+                             const _Container &container,
+                             QObject *activityInfo, int role)
+    {
+        const auto activity = static_cast<Info*> (activityInfo);
+        emitActivityUpdated(model, container, activity->id(), role);
+    }
+
+    /**
+     * Notifies the model that an activity was updated
+     */
+    template <typename _Model, typename _Container>
+    static inline
+    void emitActivityUpdated(_Model *model,
+                             const _Container &container,
+                             const QString &activity, int role)
+    {
+        auto position = Private::activityPosition(container, activity);
+
+        if (position) {
+            emit model->dataChanged(
+                model->index(position->first),
+                model->index(position->first),
+                role == Qt::DecorationRole ?
+                    QVector<int> {role, ActivityModel::ActivityIcon} :
+                    QVector<int> {role}
+            );
+        }
+    }
+
+    class BackgroundCache {
+    public:
+        BackgroundCache()
+            : initialized(false)
+            , plasmaConfig(QStringLiteral("plasma-org.kde.plasma.desktop-appletsrc"))
+        {
+            using namespace std::placeholders;
+
+            const QString configFile = QStandardPaths::writableLocation(
+                                           QStandardPaths::GenericConfigLocation) +
+                                       QLatin1Char('/') + plasmaConfig.name();
+
+            KDirWatch::self()->addFile(configFile);
+
+            connect(KDirWatch::self(), &KDirWatch::dirty,
+                    std::bind(&BackgroundCache::settingsFileChanged, this, _1));
+            connect(KDirWatch::self(), &KDirWatch::created,
+                    std::bind(&BackgroundCache::settingsFileChanged, this, _1));
+        }
+
+        void settingsFileChanged(const QString &file)
+        {
+            if (!file.endsWith(plasmaConfig.name())) return;
+
+            plasmaConfig.reparseConfiguration();
+
+            if (initialized) {
+                reload(false);
+            }
+        }
+
+        void subscribe(ActivityModel *model)
+        {
+            if (!initialized) {
+                reload(true);
+            }
+
+            models << model;
+        }
+
+        void unsubscribe(ActivityModel *model)
+        {
+            models.removeAll(model);
+
+            if (models.isEmpty()) {
+                initialized = false;
+                forActivity.clear();
+            }
+        }
+
+        QString backgroundFromConfig(const KConfigGroup &config) const
+        {
+            auto wallpaperPlugin = config.readEntry("wallpaperplugin");
+            auto wallpaperConfig = config.group("Wallpaper").group(wallpaperPlugin).group("General");
+
+            if (wallpaperConfig.hasKey("Image")) {
+                // Trying for the wallpaper
+                auto wallpaper = wallpaperConfig.readEntry("Image", QString());
+                if (!wallpaper.isEmpty()) {
+                    return wallpaper;
+                }
+            }
+            if (wallpaperConfig.hasKey("Color")) {
+                auto backgroundColor = wallpaperConfig.readEntry("Color", QColor(0, 0, 0));
+                return backgroundColor.name();
+            }
+
+            return QString();
+        }
+
+        void reload(bool fullReload)
+        {
+            QHash<QString, QString> newBackgrounds;
+
+            if (fullReload) {
+                forActivity.clear();
+            }
+
+            QStringList changedBackgrounds;
+
+            for (const auto &cont: plasmaConfigContainments().groupList()) {
+
+                auto config = plasmaConfigContainments().group(cont);
+                auto activityId = config.readEntry("activityId", QString());
+
+                // Ignore if it has no assigned activity
+                if (activityId.isEmpty()) continue;
+
+                // Ignore if we have already found the background
+                if (newBackgrounds.contains(activityId) &&
+                    newBackgrounds[activityId][0] != QLatin1Char('#')) continue;
+
+                auto newBackground = backgroundFromConfig(config);
+
+                if (forActivity[activityId] != newBackground) {
+                    changedBackgrounds << activityId;
+                    if (!newBackground.isEmpty()) {
+                        newBackgrounds[activityId] = newBackground;
+                    }
+                }
+            }
+
+            initialized = true;
+
+            if (!changedBackgrounds.isEmpty()) {
+                forActivity = newBackgrounds;
+
+                for (auto model: models) {
+                    model->backgroundsUpdated(changedBackgrounds);
+                }
+            }
+        }
+
+        KConfigGroup plasmaConfigContainments() {
+            return plasmaConfig.group("Containments");
+        }
+
+        QHash<QString, QString> forActivity;
+        QList<ActivityModel*> models;
+
+        bool initialized;
+        KConfig plasmaConfig;
+
+    };
+
+    static BackgroundCache &backgrounds()
+    {
+        // If you convert this to a shared pointer,
+        // fix the connections to KDirWatcher
+        static BackgroundCache cache;
+        return cache;
+    }
+};
+
+
+ActivityModel::ActivityModel(QObject *parent)
+    : QAbstractListModel(parent)
+{
+    // Initializing role names for qml
+    connect(&m_service, &Consumer::serviceStatusChanged,
+            this,       &ActivityModel::setServiceStatus);
+
+    connect(&m_service, SIGNAL(activityAdded(QString)),
+            this,       SLOT(onActivityAdded(QString)));
+    connect(&m_service, SIGNAL(activityRemoved(QString)),
+            this,       SLOT(onActivityRemoved(QString)));
+    connect(&m_service, SIGNAL(currentActivityChanged(QString)),
+            this,       SLOT(onCurrentActivityChanged(QString)));
+
+    setServiceStatus(m_service.serviceStatus());
+
+    Private::backgrounds().subscribe(this);
+}
+
+ActivityModel::~ActivityModel()
+{
+    Private::backgrounds().unsubscribe(this);
+}
+
+QHash<int, QByteArray> ActivityModel::roleNames() const
+{
+    return {
+        {Qt::DisplayRole,     "name"},
+        {Qt::DecorationRole,  "icon"},
+
+        {ActivityState,       "state"},
+        {ActivityId,          "id"},
+        {ActivityIcon,        "iconSource"},
+        {ActivityDescription, "description"},
+        {ActivityBackground,  "background"},
+        {ActivityCurrent,     "current"}
+    };
+}
+
+
+void ActivityModel::setServiceStatus(Consumer::ServiceStatus)
+{
+    replaceActivities(m_service.activities());
+}
+
+void ActivityModel::replaceActivities(const QStringList &activities)
+{
+    // qDebug() << m_shownStatesString << "New list of activities: "
+    //          << activities;
+    // qDebug() << m_shownStatesString << " -- RESET MODEL -- ";
+
+    Private::model_reset m(this);
+
+    m_knownActivities.clear();
+    m_shownActivities.clear();
+
+    for (const QString &activity: activities) {
+        onActivityAdded(activity, false);
+    }
+}
+
+void ActivityModel::onActivityAdded(const QString &id, bool notifyClients)
+{
+    auto info = registerActivity(id);
+
+    // qDebug() << m_shownStatesString << "Added a new activity:" << info->id()
+    //          << " " << info->name();
+
+    showActivity(info, notifyClients);
+}
+
+void ActivityModel::onActivityRemoved(const QString &id)
+{
+    // qDebug() << m_shownStatesString << "Removed an activity:" << id;
+
+    hideActivity(id);
+    unregisterActivity(id);
+}
+
+void ActivityModel::onCurrentActivityChanged(const QString &id)
+{
+    Q_UNUSED(id);
+
+    for (const auto &activity: m_shownActivities) {
+        Private::emitActivityUpdated(this, m_shownActivities, activity->id(),
+                                     ActivityCurrent);
+    }
+}
+
+ActivityModel::InfoPtr ActivityModel::registerActivity(const QString &id)
+{
+    auto position = Private::activityPosition(m_knownActivities, id);
+
+    // qDebug() << m_shownStatesString << "Registering activity: " << id
+    //          << " new? not " << (bool)position;
+
+    if (position) {
+        return *(position->second);
+
+    } else {
+        auto activityInfo = std::make_shared<Info>(id);
+
+        auto ptr = activityInfo.get();
+
+        connect(ptr,  &Info::nameChanged,
+                this, &ActivityModel::onActivityNameChanged);
+        connect(ptr,  &Info::descriptionChanged,
+                this, &ActivityModel::onActivityDescriptionChanged);
+        connect(ptr,  &Info::iconChanged,
+                this, &ActivityModel::onActivityIconChanged);
+        connect(ptr,  &Info::stateChanged,
+                this, &ActivityModel::onActivityStateChanged);
+
+        m_knownActivities.insert(InfoPtr(activityInfo));
+
+        return activityInfo;
+    }
+}
+
+void ActivityModel::unregisterActivity(const QString &id)
+{
+    // qDebug() << m_shownStatesString << "Deregistering activity: " << id;
+
+    auto position = Private::activityPosition(m_knownActivities, id);
+
+    if (position) {
+        if (auto shown = Private::activityPosition(m_shownActivities, id)) {
+            Private::model_remove(this, QModelIndex(), shown->first,
+                                  shown->first);
+            m_shownActivities.erase(shown->second);
+        }
+
+        m_knownActivities.erase(position->second);
+    }
+}
+
+void ActivityModel::showActivity(InfoPtr activityInfo, bool notifyClients)
+{
+    // Should it really be shown?
+    if (!Private::matchingState(activityInfo, m_shownStates)) return;
+
+    // Is it already shown?
+    if (boost::binary_search(m_shownActivities, activityInfo,
+                             InfoPtrComparator())) return;
+
+    auto registeredPosition
+        = Private::activityPosition(m_knownActivities, activityInfo->id());
+
+    if (!registeredPosition) {
+        qDebug() << "Got a request to show an unknown activity, ignoring";
+        return;
+    }
+
+    auto activityInfoPtr = *(registeredPosition->second);
+
+    // qDebug() << m_shownStatesString << "Setting activity visibility to true:"
+    //     << activityInfoPtr->id() << activityInfoPtr->name();
+
+    auto position = m_shownActivities.insert(activityInfoPtr);
+
+    if (notifyClients) {
+        unsigned int index =
+            (position.second ? position.first : m_shownActivities.end())
+            - m_shownActivities.begin();
+
+        // qDebug() << m_shownStatesString << " -- MODEL INSERT -- " << index;
+        Private::model_insert(this, QModelIndex(), index, index);
+    }
+}
+
+void ActivityModel::hideActivity(const QString &id)
+{
+    auto position = Private::activityPosition(m_shownActivities, id);
+
+    // qDebug() << m_shownStatesString
+    //          << "Setting activity visibility to false: " << id;
+
+    if (position) {
+        // qDebug() << m_shownStatesString << " -- MODEL REMOVE -- "
+        //          << position->first;
+        Private::model_remove(this, QModelIndex(),
+            position->first, position->first);
+        m_shownActivities.erase(position->second);
+    }
+}
+
+#define CREATE_SIGNAL_EMITTER(What,Role)                                      \
+    void ActivityModel::onActivity##What##Changed(const QString &)             \
+    {                                                                          \
+        Private::emitActivityUpdated(this, m_shownActivities, sender(), Role); \
+    }
+
+CREATE_SIGNAL_EMITTER(Name,Qt::DisplayRole)
+CREATE_SIGNAL_EMITTER(Description,ActivityDescription)
+CREATE_SIGNAL_EMITTER(Icon,Qt::DecorationRole)
+
+#undef CREATE_SIGNAL_EMITTER
+
+void ActivityModel::onActivityStateChanged(Info::State state)
+{
+    if (m_shownStates.empty()) {
+        Private::emitActivityUpdated(this, m_shownActivities, sender(),
+                                     ActivityState);
+
+    } else {
+        auto info = findActivity(sender());
+
+        if (!info) {
+            return;
+        }
+
+        if (boost::binary_search(m_shownStates, state)) {
+            showActivity(info, true);
+        } else {
+            hideActivity(info->id());
+        }
+    }
+}
+
+void ActivityModel::backgroundsUpdated(const QStringList &activities)
+{
+    for (const auto &activity: activities) {
+        Private::emitActivityUpdated(this, m_shownActivities, activity,
+                                     ActivityBackground);
+    }
+}
+
+void ActivityModel::setShownStates(const QString &states)
+{
+    m_shownStates.clear();
+    m_shownStatesString = states;
+
+    for (const auto &state: states.split(QLatin1Char(','))) {
+        if (state == QLatin1String("Running")) {
+            m_shownStates.insert(Running);
+
+        } else if (state == QLatin1String("Starting")) {
+            m_shownStates.insert(Starting);
+
+        } else if (state == QLatin1String("Stopped")) {
+            m_shownStates.insert(Stopped);
+
+        } else if (state == QLatin1String("Stopping")) {
+            m_shownStates.insert(Stopping);
+
+        }
+    }
+
+    replaceActivities(m_service.activities());
+
+    emit shownStatesChanged(states);
+}
+
+QString ActivityModel::shownStates() const
+{
+    return m_shownStatesString;
+}
+
+int ActivityModel::rowCount(const QModelIndex &parent) const
+{
+    Q_UNUSED(parent);
+
+    return m_shownActivities.size();
+}
+
+QVariant ActivityModel::data(const QModelIndex &index, int role) const
+{
+    const int row = index.row();
+    const auto &item = *(m_shownActivities.cbegin() + row);
+
+    switch (role) {
+    case Qt::DisplayRole:
+        return item->name();
+
+    case Qt::DecorationRole:
+        return QIcon::fromTheme(data(index, ActivityIcon).toString());
+
+    case ActivityId:
+        return item->id();
+
+    case ActivityState:
+        return item->state();
+
+    case ActivityIcon:
+        {
+            const QString &icon = item->icon();
+
+            // We need a default icon for activities
+            return icon.isEmpty() ? QStringLiteral("activities") : icon;
+        }
+
+    case ActivityDescription:
+        return item->description();
+
+    case ActivityCurrent:
+        return m_service.currentActivity() == item->id();
+
+    case ActivityBackground:
+        return Private::backgrounds().forActivity[item->id()];
+
+    default:
+        return QVariant();
+    }
+}
+
+QVariant ActivityModel::headerData(int section, Qt::Orientation orientation,
+                                   int role) const
+{
+    Q_UNUSED(section);
+    Q_UNUSED(orientation);
+    Q_UNUSED(role);
+
+    return QVariant();
+}
+
+ActivityModel::InfoPtr ActivityModel::findActivity(QObject *ptr) const
+{
+    auto info = boost::find_if(m_knownActivities, [ptr] (const InfoPtr &info) {
+            return ptr == info.get();
+        });
+
+    if (info == m_knownActivities.end()) {
+        return nullptr;
+    } else {
+        return *info;
+    }
+}
+
+// QFuture<void> Controller::setActivityWhat(id, value)
+#define CREATE_SETTER(What)                                                    \
+    void ActivityModel::setActivity##What(                                     \
+        const QString &id, const QString &value, const QJSValue &callback)     \
+    {                                                                          \
+        continue_with(m_service.setActivity##What(id, value), callback);       \
+    }
+
+CREATE_SETTER(Name)
+CREATE_SETTER(Description)
+CREATE_SETTER(Icon)
+
+#undef CREATE_SETTER
+
+// QFuture<bool> Controller::setCurrentActivity(id)
+void ActivityModel::setCurrentActivity(const QString &id,
+                                       const QJSValue &callback)
+{
+    continue_with(m_service.setCurrentActivity(id), callback);
+}
+
+// QFuture<QString> Controller::addActivity(name)
+void ActivityModel::addActivity(const QString &name, const QJSValue &callback)
+{
+    continue_with(m_service.addActivity(name), callback);
+}
+
+// QFuture<void> Controller::removeActivity(id)
+void ActivityModel::removeActivity(const QString &id, const QJSValue &callback)
+{
+    continue_with(m_service.removeActivity(id), callback);
+}
+
+// QFuture<void> Controller::stopActivity(id)
+void ActivityModel::stopActivity(const QString &id, const QJSValue &callback)
+{
+    continue_with(m_service.stopActivity(id), callback);
+}
+
+// QFuture<void> Controller::startActivity(id)
+void ActivityModel::startActivity(const QString &id, const QJSValue &callback)
+{
+    continue_with(m_service.startActivity(id), callback);
+}
+
+} // namespace Imports
+} // namespace KActivities
+
+// #include "activitymodel.moc"
+
diff --git a/src/imports/activitymodel.h b/src/imports/activitymodel.h
new file mode 100644 (file)
index 0000000..7d12715
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+ *   Copyright (C) 2012, 2013, 2014 Ivan Cukic <ivan.cukic(at)kde.org>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License version 2,
+ *   or (at your option) any later version, as published by the Free
+ *   Software Foundation
+ *
+ *   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 KACTIVITIES_IMPORTS_ACTIVITY_MODEL_H
+#define KACTIVITIES_IMPORTS_ACTIVITY_MODEL_H
+
+// Qt
+#include <QObject>
+#include <QAbstractListModel>
+#include <QJSValue>
+#include <QCollator>
+
+// STL and Boost
+#include <boost/container/flat_set.hpp>
+#include <memory>
+
+// Local
+#include <lib/controller.h>
+#include <lib/consumer.h>
+#include <lib/info.h>
+
+class QModelIndex;
+class QDBusPendingCallWatcher;
+
+namespace KActivities {
+namespace Imports {
+
+/**
+ * ActivityModel
+ */
+
+class ActivityModel : public QAbstractListModel {
+    Q_OBJECT
+
+    Q_PROPERTY(QString shownStates READ shownStates WRITE setShownStates NOTIFY shownStatesChanged)
+
+public:
+    explicit ActivityModel(QObject *parent = nullptr);
+    ~ActivityModel() override;
+
+    int rowCount(const QModelIndex &parent = QModelIndex()) const
+        override;
+
+    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const
+        override;
+
+    QVariant headerData(int section, Qt::Orientation orientation,
+                        int role = Qt::DisplayRole) const override;
+
+    QHash<int, QByteArray> roleNames() const override;
+
+    enum Roles {
+        ActivityId          = Qt::UserRole,
+        ActivityDescription = Qt::UserRole + 1,
+        ActivityIcon        = Qt::UserRole + 2,
+        ActivityState       = Qt::UserRole + 3,
+        ActivityBackground  = Qt::UserRole + 4,
+        ActivityCurrent     = Qt::UserRole + 5,
+    };
+
+    enum State {
+        All = 0,
+        Invalid = 0,
+        Running = 2,
+        Starting = 3,
+        Stopped = 4,
+        Stopping = 5
+    };
+    Q_ENUM(State)
+
+public Q_SLOTS:
+    // Activity control methods
+    void setActivityName(const QString &id, const QString &name,
+                         const QJSValue &callback);
+    void setActivityDescription(const QString &id, const QString &description,
+                                const QJSValue &callback);
+    void setActivityIcon(const QString &id, const QString &icon,
+                         const QJSValue &callback);
+
+    void setCurrentActivity(const QString &id, const QJSValue &callback);
+
+    void addActivity(const QString &name, const QJSValue &callback);
+    void removeActivity(const QString &id, const QJSValue &callback);
+
+    void stopActivity(const QString &id, const QJSValue &callback);
+    void startActivity(const QString &id, const QJSValue &callback);
+
+    // Model property getters and setters
+    void setShownStates(const QString &states);
+    QString shownStates() const;
+
+Q_SIGNALS:
+    void shownStatesChanged(const QString &state);
+
+private Q_SLOTS:
+    void onActivityNameChanged(const QString &name);
+    void onActivityDescriptionChanged(const QString &description);
+    void onActivityIconChanged(const QString &icon);
+    void onActivityStateChanged(KActivities::Info::State state);
+
+    void replaceActivities(const QStringList &activities);
+    void onActivityAdded(const QString &id, bool notifyClients = true);
+    void onActivityRemoved(const QString &id);
+    void onCurrentActivityChanged(const QString &id);
+
+    void setServiceStatus(KActivities::Consumer::ServiceStatus status);
+
+private:
+    KActivities::Controller m_service;
+    boost::container::flat_set<State> m_shownStates;
+    QString m_shownStatesString;
+
+    typedef std::shared_ptr<Info> InfoPtr;
+
+    struct InfoPtrComparator {
+        bool operator() (const InfoPtr& left, const InfoPtr& right) const
+        {
+            QCollator c;
+            c.setCaseSensitivity(Qt::CaseInsensitive);
+            c.setNumericMode(true);
+            int rc = c.compare(left->name(), right->name());
+            if (rc == 0) {
+                return left->id() < right->id();
+            }
+            return rc < 0;
+        }
+    };
+
+    boost::container::flat_set<InfoPtr, InfoPtrComparator> m_knownActivities;
+    boost::container::flat_set<InfoPtr, InfoPtrComparator> m_shownActivities;
+
+    InfoPtr registerActivity(const QString &id);
+    void unregisterActivity(const QString &id);
+    void showActivity(InfoPtr activityInfo, bool notifyClients);
+    void hideActivity(const QString &id);
+    void backgroundsUpdated(const QStringList &activities);
+
+    InfoPtr findActivity(QObject *ptr) const;
+
+    class Private;
+    friend class Private;
+};
+
+} // namespace Imports
+} // namespace KActivities
+
+#endif // KACTIVITIES_IMPORTS_ACTIVITY_MODEL_H
+
diff --git a/src/imports/qmldir b/src/imports/qmldir
new file mode 100644 (file)
index 0000000..9138785
--- /dev/null
@@ -0,0 +1,3 @@
+module org.kde.activities
+plugin kactivitiesextensionplugin
+
diff --git a/src/imports/resourceinstance.cpp b/src/imports/resourceinstance.cpp
new file mode 100644 (file)
index 0000000..734ad73
--- /dev/null
@@ -0,0 +1,140 @@
+/***************************************************************************
+ *   Copyright 2011-2015 Marco Martin <mart@kde.org>                            *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Library General Public License as published by  *
+ *   the Free Software Foundation; either version 2 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 Library General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU Library General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA .        *
+ ***************************************************************************/
+
+#include "resourceinstance.h"
+
+#include <QQuickWindow>
+#include <QTimer>
+
+#include <KActivities/ResourceInstance>
+#include <QDebug>
+
+namespace KActivities {
+namespace Imports {
+
+ResourceInstance::ResourceInstance(QQuickItem *parent)
+    : QQuickItem(parent)
+{
+    m_syncTimer = new QTimer(this);
+    m_syncTimer->setSingleShot(true);
+    connect(m_syncTimer, SIGNAL(timeout()), this, SLOT(syncWid()));
+}
+
+ResourceInstance::~ResourceInstance()
+{
+}
+
+void ResourceInstance::syncWid()
+{
+    QWindow *w = window();
+    if (!w) {
+        return;
+    }
+
+    WId wid = w->winId();
+    if (!m_resourceInstance || m_resourceInstance->winId() != wid) {
+        // qDebug() << "Creating a new instance of the resource" << m_uri << "window id" << wid;
+        m_resourceInstance.reset(new KActivities::ResourceInstance(wid, m_uri, m_mimetype, m_title));
+    } else {
+
+        if (m_uri.scheme().startsWith(QLatin1String("http")) && !m_uri.hasQuery() && m_uri.path().endsWith(QLatin1Char('/'))) {
+            const QString &oldPath = m_uri.path();
+            m_uri.setPath(oldPath.left(oldPath.length() - 1));
+
+            // qDebug() << "Old and new path" << oldPath << m_uri;
+
+        } else {
+            m_resourceInstance->setUri(m_uri);
+        }
+
+        // qDebug() << "Setting" << m_uri << m_mimetype << "to window" << wid;
+
+        m_resourceInstance->setMimetype(m_mimetype);
+        m_resourceInstance->setTitle(m_title);
+    }
+}
+
+QUrl ResourceInstance::uri() const
+{
+    return m_uri;
+}
+
+void ResourceInstance::setUri(const QUrl &uri)
+{
+    if (m_uri == uri) {
+        return;
+    }
+
+    m_uri = uri;
+    m_syncTimer->start(100);
+}
+
+QString ResourceInstance::mimetype() const
+{
+    return m_mimetype;
+}
+
+void ResourceInstance::setMimetype(const QString &mimetype)
+{
+    if (m_mimetype == mimetype) {
+        return;
+    }
+    m_mimetype = mimetype;
+    m_syncTimer->start(100);
+}
+
+QString ResourceInstance::title() const
+{
+    return m_title;
+}
+
+void ResourceInstance::setTitle(const QString &title)
+{
+    if (m_title == title) {
+        return;
+    }
+    m_title = title;
+    m_syncTimer->start(100);
+}
+
+void ResourceInstance::notifyModified()
+{
+    //ensure the resource instance exists
+    syncWid();
+    m_resourceInstance->notifyModified();
+}
+
+void ResourceInstance::notifyFocusedIn()
+{
+    //ensure the resource instance exists
+    syncWid();
+    m_resourceInstance->notifyFocusedIn();
+}
+
+void ResourceInstance::notifyFocusedOut()
+{
+    //ensure the resource instance exists
+    syncWid();
+    m_resourceInstance->notifyFocusedOut();
+}
+
+}
+}
+
+
diff --git a/src/imports/resourceinstance.h b/src/imports/resourceinstance.h
new file mode 100644 (file)
index 0000000..e779c2a
--- /dev/null
@@ -0,0 +1,100 @@
+/***************************************************************************
+ *   Copyright 2011-2015 Marco Martin <mart@kde.org>                            *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Library General Public License as published by  *
+ *   the Free Software Foundation; either version 2 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 Library General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU Library General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA .        *
+ ***************************************************************************/
+#ifndef RESOURCEINSTANCE_H
+#define RESOURCEINSTANCE_H
+
+//Qt
+#include <QQuickItem>
+#include <QUrl>
+
+// STL
+#include <memory>
+
+namespace KActivities {
+class ResourceInstance;
+}
+
+class QTimer;
+
+
+namespace KActivities {
+namespace Imports {
+
+class ResourceInstance : public QQuickItem
+{
+    Q_OBJECT
+
+    Q_PROPERTY(QUrl uri READ uri WRITE setUri NOTIFY uriChanged)
+    Q_PROPERTY(QString mimetype READ mimetype WRITE setMimetype NOTIFY mimetypeChanged)
+    Q_PROPERTY(QString title READ title WRITE setTitle NOTIFY titleChanged)
+
+public:
+    explicit ResourceInstance(QQuickItem *parent = nullptr);
+    ~ResourceInstance();
+
+    QUrl uri() const;
+    void setUri(const QUrl &uri);
+
+    QString mimetype() const;
+    void setMimetype(const QString &mimetype);
+
+    QString title() const;
+    void setTitle(const QString &title);
+
+protected Q_SLOTS:
+    void syncWid();
+
+Q_SIGNALS:
+    void uriChanged();
+    void mimetypeChanged();
+    void titleChanged();
+
+public Q_SLOTS:
+    /**
+     * Call this method to notify the system that you modified
+     * (the contents of) the resource
+     */
+    void notifyModified();
+
+    /**
+     * Call this method to notify the system that the resource
+     * has the focus in your application
+     * @note You only need to call this in MDI applications
+     */
+    void notifyFocusedIn();
+
+    /**
+     * Call this method to notify the system that the resource
+     * lost the focus in your application
+     * @note You only need to call this in MDI applications
+     */
+    void notifyFocusedOut();
+
+private:
+    std::unique_ptr<KActivities::ResourceInstance> m_resourceInstance;
+    QUrl m_uri;
+    QString m_mimetype;
+    QString m_title;
+    QTimer *m_syncTimer;
+};
+
+}
+}
+
+#endif
diff --git a/src/imports/resourcemodel.cpp b/src/imports/resourcemodel.cpp
new file mode 100644 (file)
index 0000000..54b7d59
--- /dev/null
@@ -0,0 +1,696 @@
+/*
+ *   Copyright (C) 2012, 2013, 2014 Ivan Cukic <ivan.cukic(at)kde.org>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License version 2,
+ *   or (at your option) any later version, as published by the Free
+ *   Software Foundation
+ *
+ *   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.
+ */
+
+// Self
+#include "resourcemodel.h"
+
+// Qt
+#include <QByteArray>
+#include <QDebug>
+#include <QModelIndex>
+#include <QCoreApplication>
+#include <QSqlQuery>
+#include <QUuid>
+
+// KDE
+#include <kconfig.h>
+#include <kconfiggroup.h>
+#include <ksharedconfig.h>
+#include <kfileitem.h>
+#include <kdesktopfile.h>
+
+// STL and Boost
+#include <mutex>
+#include <boost/algorithm/string/join.hpp>
+#include <boost/range/numeric.hpp>
+#include <boost/range/algorithm/find_if.hpp>
+#include <boost/range/adaptor/transformed.hpp>
+#include <boost/range/adaptor/filtered.hpp>
+
+// Local
+#include "utils/range.h"
+#include "utils/dbusfuture_p.h"
+#include "common/dbus/common.h"
+
+#define ENABLE_QJSVALUE_CONTINUATION
+#include "utils/continue_with.h"
+
+#define ACTIVITY_COLUMN 0
+#define AGENT_COLUMN    1
+#define RESOURCE_COLUMN 2
+#define UNKNOWN_COLUMN  3
+
+
+using kamd::utils::continue_with;
+
+namespace KActivities {
+namespace Imports {
+
+class ResourceModel::LinkerService: public QDBusInterface {
+private:
+    LinkerService()
+        : KAMD_DBUS_INTERFACE(Resources/Linking, ResourcesLinking, nullptr)
+    {
+    }
+
+public:
+    static std::shared_ptr<LinkerService> self()
+    {
+        static std::weak_ptr<LinkerService> s_instance;
+        static std::mutex singleton;
+
+        std::lock_guard<std::mutex> singleton_lock(singleton);
+
+        auto result = s_instance.lock();
+
+        if (s_instance.expired()) {
+            result.reset(new LinkerService());
+            s_instance = result;
+        }
+
+        return result;
+    }
+
+};
+
+ResourceModel::ResourceModel(QObject *parent)
+    : QSortFilterProxyModel(parent)
+    , m_shownActivities(QStringLiteral(":current"))
+    , m_shownAgents(QStringLiteral(":current"))
+    , m_defaultItemsLoaded(false)
+    , m_linker(LinkerService::self())
+    , m_config(KSharedConfig::openConfig("kactivitymanagerd-resourcelinkingrc")
+        ->group("Order"))
+{
+    // NOTE: What to do if the file does not exist?
+    //       Ignoring that case since the daemon creates it on startup.
+    //       Is it plausible that somebody will instantiate the ResourceModel
+    //       before the daemon is started?
+
+    const QString databaseDir
+        = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation)
+          + QStringLiteral("/kactivitymanagerd/resources/");
+
+    m_databaseFile = databaseDir + QStringLiteral("database");
+
+    loadDatabase();
+
+    connect(&m_service, &KActivities::Consumer::currentActivityChanged,
+            this, &ResourceModel::onCurrentActivityChanged);
+
+    connect(m_linker.get(), SIGNAL(ResourceLinkedToActivity(QString,QString,QString)),
+            this, SLOT(onResourceLinkedToActivity(QString,QString,QString)));
+    connect(m_linker.get(), SIGNAL(ResourceUnlinkedFromActivity(QString,QString,QString)),
+            this, SLOT(onResourceUnlinkedFromActivity(QString,QString,QString)));
+
+    setDynamicSortFilter(true);
+    sort(0);
+}
+
+bool ResourceModel::loadDatabase()
+{
+    if (m_database.isValid()) return true;
+    if (!QFile(m_databaseFile).exists()) return false;
+
+    // TODO: Database connection naming could be smarter (thread-id-based,
+    //       reusing connections...?)
+    m_database = QSqlDatabase::addDatabase(
+        QStringLiteral("QSQLITE"),
+        QStringLiteral("kactivities_db_resources_") + QString::number((quintptr)this));
+
+    // qDebug() << "Database file is: " << m_databaseFile;
+    m_database.setDatabaseName(m_databaseFile);
+
+    m_database.open();
+
+    m_databaseModel = new QSqlTableModel(this, m_database);
+    m_databaseModel->setTable("ResourceLink");
+    m_databaseModel->select();
+
+    setSourceModel(m_databaseModel);
+
+    reloadData();
+
+    return true;
+}
+
+ResourceModel::~ResourceModel()
+{
+}
+
+QVariant ResourceModel::dataForColumn(const QModelIndex &index, int column) const
+{
+    if (!m_database.isValid()) return QVariant();
+
+    return m_databaseModel->data(index.sibling(index.row(), column),
+                                 Qt::DisplayRole);
+}
+
+bool ResourceModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
+{
+    const auto leftResource = dataForColumn(left, RESOURCE_COLUMN).toString();
+    const auto rightResource = dataForColumn(right, RESOURCE_COLUMN).toString();
+
+    const bool hasLeft = m_sorting.contains(leftResource);
+    const bool hasRight = m_sorting.contains(rightResource);
+
+    return
+        ( hasLeft && !hasRight) ? true :
+        (!hasLeft &&  hasRight) ? false :
+        ( hasLeft &&  hasRight) ? m_sorting.indexOf(leftResource) < m_sorting.indexOf(rightResource) :
+        QString::compare(leftResource, rightResource, Qt::CaseInsensitive) < 0;
+}
+
+QHash<int, QByteArray> ResourceModel::roleNames() const
+{
+    return {
+        { Qt::DisplayRole,    "display" },
+        { Qt::DecorationRole, "decoration" },
+        { ResourceRole,       "uri" },
+        { AgentRole,          "agent" },
+        { ActivityRole,       "activity" },
+        { DescriptionRole,    "subtitle" }
+    };
+}
+
+template <typename Validator>
+inline QStringList validateList(
+        const QString &values, Validator validator)
+{
+    using boost::adaptors::filtered;
+    using kamd::utils::as_collection;
+
+    auto result
+        = as_collection<QStringList>(values.split(',') | filtered(validator));
+
+    if (result.isEmpty()) {
+        result.append(QStringLiteral(":current"));
+    }
+
+    return result;
+}
+
+void ResourceModel::setShownActivities(const QString &activities)
+{
+    m_shownActivities = validateList(activities, [&] (const QString &activity) {
+        return
+            activity == ":current" ||
+            activity == ":any" ||
+            activity == ":global" ||
+            !QUuid(activity).isNull();
+    });
+
+    reloadData();
+    emit shownActivitiesChanged();
+}
+
+void ResourceModel::setShownAgents(const QString &agents)
+{
+    m_shownAgents = validateList(agents, [&] (const QString &agent) {
+        return
+            agent == ":current" ||
+            agent == ":any" ||
+            agent == ":global" ||
+            (!agent.isEmpty() && !agent.contains('\'') && !agent.contains('"'));
+    });
+
+    loadDefaultsIfNeeded();
+    reloadData();
+    emit shownAgentsChanged();
+}
+
+QString ResourceModel::shownActivities() const
+{
+    return m_shownActivities.join(',');
+}
+
+QString ResourceModel::shownAgents() const
+{
+    return m_shownAgents.join(',');
+}
+
+QString ResourceModel::defaultItemsConfig() const
+{
+    return m_defaultItemsConfig;
+}
+
+void ResourceModel::setDefaultItemsConfig(const QString &defaultItemsConfig)
+{
+    m_defaultItemsConfig = defaultItemsConfig;
+    loadDefaultsIfNeeded();
+}
+
+QString ResourceModel::activityToWhereClause(const QString &shownActivity) const
+{
+    return QStringLiteral(" OR usedActivity=") + (
+        shownActivity == ":current" ? "'" + m_service.currentActivity() + "'" :
+        shownActivity == ":any"     ? "usedActivity" :
+        shownActivity == ":global"  ? "''" :
+                                      "'" + shownActivity + "'"
+    );
+}
+
+QString ResourceModel::agentToWhereClause(const QString &shownAgent) const
+{
+    return QStringLiteral(" OR initiatingAgent=") + (
+        shownAgent == ":current" ? "'" + QCoreApplication::applicationName() + "'" :
+        shownAgent == ":any"     ? "initiatingAgent" :
+        shownAgent == ":global"  ? "''" :
+                                   "'" + shownAgent + "'"
+    );
+}
+
+QString ResourceModel::whereClause(const QStringList &activities,
+                                   const QStringList &agents) const
+{
+    using boost::accumulate;
+    using namespace kamd::utils;
+
+    // qDebug() << "Getting the where clause for: " << activities << " " << agents;
+
+    // Defining the transformation functions for generating the SQL WHERE clause
+    // from the specified activity/agent. They also resolve the special values
+    // like :current, :any and :global.
+
+    auto activityToWhereClause = transformed(&ResourceModel::activityToWhereClause, this);
+    auto agentToWhereClause = transformed(&ResourceModel::agentToWhereClause, this);
+
+    // Generating the SQL WHERE part by concatenating the generated clauses.
+    // The generated query will be in the form of '0 OR clause1 OR clause2 ...'
+
+    const QString whereActivity =
+        accumulate(activities | activityToWhereClause, QStringLiteral("0"));
+
+    const QString whereAgent =
+        accumulate(agents | agentToWhereClause, QStringLiteral("0"));
+
+    // qDebug() << "This is the filter: " << '(' + whereActivity + ") AND (" + whereAgent + ')';
+
+    return '(' + whereActivity + ") AND (" + whereAgent + ')';
+}
+
+void ResourceModel::reloadData()
+{
+    m_sorting = m_config.readEntry(m_shownAgents.first(), QStringList());
+
+    if (!m_database.isValid()) return;
+    m_databaseModel->setFilter(whereClause(m_shownActivities, m_shownAgents));
+}
+
+void ResourceModel::onCurrentActivityChanged(const QString &activity)
+{
+    Q_UNUSED(activity);
+
+    if (m_shownActivities.contains(":current")) {
+        reloadData();
+    }
+}
+
+QVariant ResourceModel::data(const QModelIndex &proxyIndex, int role) const
+{
+    auto index = mapToSource(proxyIndex);
+
+    if (role == Qt::DisplayRole || role == DescriptionRole
+            || role == Qt::DecorationRole) {
+        auto uri = dataForColumn(index, RESOURCE_COLUMN).toString();
+
+        // TODO: Will probably need some more special handling -
+        //       for application:/ and a few more
+
+        if (uri.startsWith('/')) {
+            uri = QLatin1String("file://") + uri;
+        }
+
+        KFileItem file(uri);
+
+        if (file.mimetype() == "application/x-desktop") {
+            KDesktopFile desktop(file.localPath());
+
+            return role == Qt::DisplayRole    ? desktop.readGenericName() :
+                   role == DescriptionRole    ? desktop.readName() :
+                   role == Qt::DecorationRole ? desktop.readIcon() : QVariant();
+        }
+
+        return role == Qt::DisplayRole    ? file.name() :
+               role == Qt::DecorationRole ? file.iconName() : QVariant();
+    }
+
+    return dataForColumn(index,
+            role == ResourceRole ? RESOURCE_COLUMN :
+            role == AgentRole    ? AGENT_COLUMN :
+            role == ActivityRole ? ACTIVITY_COLUMN :
+                                   UNKNOWN_COLUMN
+        );
+}
+
+void ResourceModel::linkResourceToActivity(const QString &resource,
+                                           const QJSValue &callback) const
+{
+    linkResourceToActivity(resource, m_shownActivities.first(), callback);
+}
+
+void ResourceModel::linkResourceToActivity(const QString &resource,
+                                           const QString &activity,
+                                           const QJSValue &callback) const
+{
+    linkResourceToActivity(m_shownAgents.first(), resource, activity, callback);
+}
+
+void ResourceModel::linkResourceToActivity(const QString &agent,
+                                           const QString &_resource,
+                                           const QString &activity,
+                                           const QJSValue &callback) const
+{
+    if (activity == ":any") {
+        qWarning() << ":any is not a valid activity specification for linking";
+        return;
+    }
+
+    auto resource = validateResource(_resource);
+
+    // qDebug() << "ResourceModel: Linking resource to activity: --------------------------------------------------\n"
+    //          << "ResourceModel:         Resource: " << resource << "\n"
+    //          << "ResourceModel:         Agents: " << agent << "\n"
+    //          << "ResourceModel:         Activities: " << activity << "\n";
+
+
+    kamd::utils::continue_with(
+        DBusFuture::asyncCall<void>(m_linker.get(),
+            QStringLiteral("LinkResourceToActivity"),
+            agent, resource,
+            activity == ":current" ? m_service.currentActivity() :
+            activity == ":global"  ? "" : activity),
+        callback);
+}
+
+void ResourceModel::unlinkResourceFromActivity(const QString &resource,
+                                               const QJSValue &callback)
+{
+    unlinkResourceFromActivity(m_shownAgents, resource, m_shownActivities,
+                               callback);
+}
+
+void ResourceModel::unlinkResourceFromActivity(const QString &resource,
+                                               const QString &activity,
+                                               const QJSValue &callback)
+{
+    unlinkResourceFromActivity(m_shownAgents, resource,
+                               QStringList() << activity, callback);
+}
+
+void ResourceModel::unlinkResourceFromActivity(const QString &agent,
+                                               const QString &resource,
+                                               const QString &activity,
+                                               const QJSValue &callback)
+{
+    unlinkResourceFromActivity(QStringList() << agent, resource,
+                               QStringList() << activity, callback);
+}
+
+void ResourceModel::unlinkResourceFromActivity(const QStringList &agents,
+                                               const QString &_resource,
+                                               const QStringList &activities,
+                                               const QJSValue &callback)
+{
+    auto resource = validateResource(_resource);
+
+    // qDebug() << "ResourceModel: Unlinking resource from activity: ----------------------------------------------\n"
+    //          << "ResourceModel:         Resource: " << resource << "\n"
+    //          << "ResourceModel:         Agents: " << agents << "\n"
+    //          << "ResourceModel:         Activities: " << activities << "\n";
+
+    for (const auto& agent: agents) {
+        for (const auto& activity: activities) {
+            if (activity == ":any") {
+                qWarning() << ":any is not a valid activity specification for linking";
+                return;
+            }
+
+            // We might want to compose the continuations into one
+            // so that the callback gets called only once,
+            // but we don't care about that at the moment
+            kamd::utils::continue_with(
+                    DBusFuture::asyncCall<void>(m_linker.get(),
+                        QStringLiteral("UnlinkResourceFromActivity"),
+                        agent, resource,
+                        activity == ":current" ? m_service.currentActivity() :
+                        activity == ":global"  ? "" : activity),
+                    callback);
+        }
+    }
+}
+
+bool ResourceModel::isResourceLinkedToActivity(const QString &resource)
+{
+    return isResourceLinkedToActivity(m_shownAgents, resource, m_shownActivities);
+}
+
+bool ResourceModel::isResourceLinkedToActivity(const QString &resource,
+                                               const QString &activity)
+{
+    return isResourceLinkedToActivity(m_shownAgents, resource,
+                                      QStringList() << activity);
+}
+
+bool ResourceModel::isResourceLinkedToActivity(const QString &agent,
+                                               const QString &resource,
+                                               const QString &activity)
+{
+    return isResourceLinkedToActivity(QStringList() << agent, resource,
+                                      QStringList() << activity);
+}
+
+bool ResourceModel::isResourceLinkedToActivity(const QStringList &agents,
+                                               const QString &_resource,
+                                               const QStringList &activities)
+{
+    if (!m_database.isValid()) return false;
+
+    auto resource = validateResource(_resource);
+
+    // qDebug() << "ResourceModel: Testing whether the resource is linked to activity: ----------------------------\n"
+    //          << "ResourceModel:         Resource: " << resource << "\n"
+    //          << "ResourceModel:         Agents: " << agents << "\n"
+    //          << "ResourceModel:         Activities: " << activities << "\n";
+
+    QSqlQuery query(m_database);
+    query.prepare("SELECT targettedResource "
+                  "FROM ResourceLink "
+                  "WHERE targettedResource=:resource AND " +
+                  whereClause(activities, agents));
+    query.bindValue(":resource", resource);
+    query.exec();
+
+    auto result = query.next();
+
+    // qDebug() << "Query: " << query.lastQuery();
+    //
+    // if (query.lastError().isValid()) {
+    //     qDebug() << "Error: " << query.lastError();
+    // }
+    //
+    // qDebug() << "Result: " << result;
+
+    return result;
+}
+
+void ResourceModel::onResourceLinkedToActivity(const QString &initiatingAgent,
+                                               const QString &targettedResource,
+                                               const QString &usedActivity)
+{
+    Q_UNUSED(targettedResource);
+
+    if (!loadDatabase()) return;
+
+    auto matchingActivity = boost::find_if(m_shownActivities, [&] (const QString &shownActivity) {
+        return
+            // If the activity is not important
+            shownActivity == ":any" ||
+            // or we are listening for the changes for the current activity
+            (shownActivity == ":current"
+                 && usedActivity == m_service.currentActivity()) ||
+            // or we want the globally linked resources
+            (shownActivity == ":global" && usedActivity == "") ||
+            // or we have a specific activity in mind
+            shownActivity == usedActivity;
+    });
+
+    auto matchingAgent = boost::find_if(m_shownAgents, [&](const QString &shownAgent) {
+        return
+            // If the agent is not important
+            shownAgent == ":any" ||
+            // or we are listening for the changes for the current agent
+            (shownAgent == ":current"
+                && initiatingAgent == QCoreApplication::applicationName()) ||
+            // or for links that are global, and not related to a specific agent
+            (shownAgent == ":global" && initiatingAgent == "") ||
+            // or we have a specific agent to listen for
+            shownAgent == initiatingAgent;
+    });
+
+    if (matchingActivity != m_shownActivities.end()
+        && matchingAgent != m_shownAgents.end()) {
+        // TODO: This might be smarter possibly, but might collide
+        //       with the SQL model. Implement a custom model with internal
+        //       cache instead of basing it on QSqlModel.
+        reloadData();
+    }
+}
+
+void ResourceModel::onResourceUnlinkedFromActivity(const QString &initiatingAgent,
+                                                   const QString &targettedResource,
+                                                   const QString &usedActivity)
+{
+    // These are the same at the moment
+    onResourceLinkedToActivity(initiatingAgent, targettedResource, usedActivity);
+}
+
+void ResourceModel::setOrder(const QStringList &resources)
+{
+    m_sorting = resources;
+    m_config.writeEntry(m_shownAgents.first(), m_sorting);
+    m_config.sync();
+    invalidate();
+}
+
+void ResourceModel::move(int sourceItem, int destinationItem)
+{
+    QStringList resources;
+    const int rows = rowCount();
+
+    for (int row = 0; row < rows; row++) {
+        resources << resourceAt(row);
+    }
+
+    if (sourceItem < 0 || sourceItem >= rows ||
+        destinationItem < 0 || destinationItem >= rows) {
+        return;
+    }
+
+    // Moving one item from the source item's location to the location
+    // after the destination item
+    std::rotate(
+            resources.begin() + sourceItem,
+            resources.begin() + sourceItem + 1,
+            resources.begin() + destinationItem + 1
+        );
+
+    setOrder(resources);
+}
+
+void ResourceModel::sortItems(Qt::SortOrder sortOrder)
+{
+    typedef QPair<QString, QString> Resource;
+    QList<Resource> resources;
+    const int rows = rowCount();
+
+    for (int row = 0; row < rows; ++row) {
+        resources << qMakePair(resourceAt(row), displayAt(row));
+    }
+
+    std::sort(resources.begin(), resources.end(),
+        [sortOrder] (const Resource &left, const Resource &right) {
+            return sortOrder == Qt::AscendingOrder ?
+                    left.second < right.second
+                :
+                    right.second < left.second;
+        }
+    );
+
+    QStringList result;
+
+    for (const auto &resource : qAsConst(resources)) {
+        result << resource.first;
+    }
+
+    setOrder(result);
+}
+
+KConfigGroup ResourceModel::config() const
+{
+    return KSharedConfig::openConfig("kactivitymanagerd-resourcelinkingrc")
+        ->group("Order");
+}
+
+int ResourceModel::count() const
+{
+    return QSortFilterProxyModel::rowCount();
+}
+
+QString ResourceModel::displayAt(int row) const
+{
+    return data(index(row, 0), Qt::DisplayRole).toString();
+}
+
+QString ResourceModel::resourceAt(int row) const
+{
+    return validateResource(data(index(row, 0), ResourceRole).toString());
+}
+
+void ResourceModel::loadDefaultsIfNeeded() const
+{
+    // Did we get a request to actually do anything?
+    if (m_defaultItemsConfig.isEmpty()) return;
+    if (m_shownAgents.size() == 0) return;
+
+    // If we have already loaded the items, just exit
+    if (m_defaultItemsLoaded) return;
+    m_defaultItemsLoaded = true;
+
+    // If there are items in the model, no need to load the defaults
+    if (count() != 0) return;
+
+    // Did we already load the defaults for this agent?
+    QStringList alreadyInitialized = m_config.readEntry("defaultItemsProcessedFor", QStringList());
+    if (alreadyInitialized.contains(m_shownAgents.first())) return;
+    alreadyInitialized << m_shownAgents.first();
+    m_config.writeEntry("defaultItemsProcessedFor", alreadyInitialized);
+    m_config.sync();
+
+    QStringList args = m_defaultItemsConfig.split("/");
+    QString configField = args.takeLast();
+    QString configGroup = args.takeLast();
+    QString configFile = args.join("/");
+
+    // qDebug() << "Config"
+    //          << configFile << " "
+    //          << configGroup << " "
+    //          << configField << " ";
+
+    QStringList items = KSharedConfig::openConfig(configFile)
+        ->group(configGroup)
+        .readEntry(configField, QStringList());
+
+    for (const auto& item: items) {
+        // qDebug() << "Adding: " << item;
+        linkResourceToActivity(item, ":global", QJSValue());
+    }
+}
+
+QString ResourceModel::validateResource(const QString &resource) const
+{
+    return resource.startsWith(QLatin1String("file://")) ?
+            QUrl(resource).toLocalFile() : resource;
+}
+
+} // namespace Imports
+} // namespace KActivities
+
+// #include "resourcemodel.moc"
+
diff --git a/src/imports/resourcemodel.h b/src/imports/resourcemodel.h
new file mode 100644 (file)
index 0000000..76c651c
--- /dev/null
@@ -0,0 +1,210 @@
+/*
+ *   Copyright (C) 2012, 2013, 2014 Ivan Cukic <ivan.cukic(at)kde.org>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License version 2,
+ *   or (at your option) any later version, as published by the Free
+ *   Software Foundation
+ *
+ *   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 KACTIVITIES_IMPORTS_RESOURCE_MODEL_H
+#define KACTIVITIES_IMPORTS_RESOURCE_MODEL_H
+
+// Qt
+#include <QObject>
+#include <QSortFilterProxyModel>
+#include <QJSValue>
+#include <QSqlTableModel>
+#include <QSqlDatabase>
+
+// KDE
+#include <kconfiggroup.h>
+
+// STL and Boost
+#include <boost/container/flat_set.hpp>
+#include <memory>
+
+// Local
+#include <lib/controller.h>
+#include <lib/consumer.h>
+#include <lib/info.h>
+
+class QModelIndex;
+class QDBusPendingCallWatcher;
+
+namespace KActivities {
+namespace Imports {
+
+/**
+ * ResourceModel
+ */
+
+class ResourceModel : public QSortFilterProxyModel {
+    Q_OBJECT
+
+    /**
+     * Sets for which activities should the resources be shown for.
+     * Coma-separated values.
+     * Special values are:
+     *  - ":current" for the current activity
+     *  - ":any" show resources that are linked to any activity, including "global"
+     *  - ":global" show resources that are globally linked
+     */
+    Q_PROPERTY(QString shownActivities READ shownActivities WRITE setShownActivities NOTIFY shownActivitiesChanged)
+
+    /**
+     * Sets for which agents should the resources be shown for.
+     * Coma-separated values.
+     * Special values are:
+     *  - ":current" for the current application
+     *  - ":any" show resources that are linked to any agent, including "global"
+     *  - ":global" show resources that are globally linked
+     */
+    Q_PROPERTY(QString shownAgents READ shownAgents WRITE setShownAgents NOTIFY shownAgentsChanged)
+
+    /**
+     * If the model is empty, use this config file to read the default items.
+     * The default items are automatically linked globally, not per-activity.
+     * It needs to have the following format: 'config-namerc/ConfigGroup/ConfigEntry'.
+     * The config entry needs to be a list of strings.
+     */
+    Q_PROPERTY(QString defaultItemsConfig READ defaultItemsConfig WRITE setDefaultItemsConfig)
+
+public:
+    explicit ResourceModel(QObject *parent = nullptr);
+    ~ResourceModel() override;
+
+    enum Roles {
+        ResourceRole    = Qt::UserRole,
+        ActivityRole    = Qt::UserRole + 1,
+        AgentRole       = Qt::UserRole + 2,
+        DescriptionRole = Qt::UserRole + 3
+    };
+
+    QHash<int, QByteArray> roleNames() const override;
+
+    virtual QVariant data(const QModelIndex &proxyIndex,
+                          int role = Qt::DisplayRole) const override;
+
+public Q_SLOTS:
+    // Resource linking control methods
+    void linkResourceToActivity(const QString &resource,
+                                const QJSValue &callback) const;
+    void linkResourceToActivity(const QString &resource,
+                                const QString &activity,
+                                const QJSValue &callback) const;
+    void linkResourceToActivity(const QString &agent,
+                                const QString &resource,
+                                const QString &activity,
+                                const QJSValue &callback) const;
+
+    void unlinkResourceFromActivity(const QString &resource,
+                                    const QJSValue &callback);
+    void unlinkResourceFromActivity(const QString &resource,
+                                    const QString &activity,
+                                    const QJSValue &callback);
+    void unlinkResourceFromActivity(const QString &agent,
+                                    const QString &resource,
+                                    const QString &activity,
+                                    const QJSValue &callback);
+    void unlinkResourceFromActivity(const QStringList &agents,
+                                    const QString &resource,
+                                    const QStringList &activities,
+                                    const QJSValue &callback);
+
+    bool isResourceLinkedToActivity(const QString &resource);
+    bool isResourceLinkedToActivity(const QString &resource,
+                                    const QString &activity);
+    bool isResourceLinkedToActivity(const QString &agent,
+                                    const QString &resource,
+                                    const QString &activity);
+    bool isResourceLinkedToActivity(const QStringList &agents,
+                                    const QString &resource,
+                                    const QStringList &activities);
+
+    // Model property getters and setters
+    void setShownActivities(const QString &activities);
+    QString shownActivities() const;
+
+    void setShownAgents(const QString &agents);
+    QString shownAgents() const;
+
+    QString defaultItemsConfig() const;
+    void setDefaultItemsConfig(const QString &defaultItemsConfig);
+
+    void setOrder(const QStringList &resources);
+    void move(int sourceItem, int destinationItem);
+    void sortItems(Qt::SortOrder sortOrder);
+
+    KConfigGroup config() const;
+
+    int count() const;
+    QString displayAt(int row) const;
+    QString resourceAt(int row) const;
+
+protected:
+    bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
+
+
+Q_SIGNALS:
+    void shownActivitiesChanged();
+    void shownAgentsChanged();
+
+private Q_SLOTS:
+    void onCurrentActivityChanged(const QString &activity);
+
+    void onResourceLinkedToActivity(const QString &initiatingAgent,
+                                    const QString &targettedResource,
+                                    const QString &usedActivity);
+    void onResourceUnlinkedFromActivity(const QString &initiatingAgent,
+                                        const QString &targettedResource,
+                                        const QString &usedActivity);
+
+private:
+    KActivities::Consumer m_service;
+
+    inline
+    QVariant dataForColumn(const QModelIndex &index, int column) const;
+
+    QString activityToWhereClause(const QString &activity) const;
+    QString agentToWhereClause(const QString &agent) const;
+    QString whereClause(const QStringList &activities, const QStringList &agents) const;
+
+    void loadDefaultsIfNeeded() const;
+
+    bool loadDatabase();
+    QString m_databaseFile;
+    QSqlDatabase m_database;
+    QSqlTableModel *m_databaseModel;
+
+    QStringList m_shownActivities;
+    QStringList m_shownAgents;
+    QStringList m_sorting;
+
+    QString m_defaultItemsConfig;
+    mutable bool m_defaultItemsLoaded;
+
+    void reloadData();
+    QString validateResource(const QString &resource) const;
+
+    class LinkerService;
+    std::shared_ptr<LinkerService> m_linker;
+
+    mutable KConfigGroup m_config;
+};
+
+} // namespace Imports
+} // namespace KActivities
+
+#endif // KACTIVITIES_IMPORTS_RESOURCE_MODEL_H
+
diff --git a/src/kactivities-features.h.cmake b/src/kactivities-features.h.cmake
new file mode 100644 (file)
index 0000000..30aa01b
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef CONFIG_FEATURES_H_
+#define CONFIG_FEATURES_H_
+
+#cmakedefine KAMD_DATA_DIR "@KAMD_DATA_DIR@"
+
+#cmakedefine KAMD_PLUGIN_DIR "@KAMD_PLUGIN_DIR@"
+#cmakedefine KAMD_FULL_PLUGIN_DIR "@KAMD_FULL_PLUGIN_DIR@"
+
+#cmakedefine KAMD_INSTALL_PREFIX "@KAMD_INSTALL_PREFIX@"
+
+#cmakedefine01 HAVE_CXX11_AUTO
+#cmakedefine01 HAVE_CXX11_NULLPTR
+#cmakedefine01 HAVE_CXX11_LAMBDA
+#cmakedefine01 HAVE_CXX11_OVERRIDE
+#cmakedefine01 HAVE_CXX_OVERRIDE_ATTR
+
+#endif
diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt
new file mode 100644 (file)
index 0000000..11eaee4
--- /dev/null
@@ -0,0 +1,180 @@
+# vim:set softtabstop=3 shiftwidth=3 tabstop=3 expandtab:
+
+# =======================================================
+# Now that we finished with the boilerplate, start
+# with the library definition
+
+set (
+   KActivities_LIB_SRCS
+
+   ${KACTIVITIES_CURRENT_ROOT_SOURCE_DIR}/src/common/dbus/org.kde.ActivityManager.Activities.cpp
+
+   consumer.cpp
+   controller.cpp
+   info.cpp
+   resourceinstance.cpp
+   activitiesmodel.cpp
+
+   mainthreadexecutor_p.cpp
+   manager_p.cpp
+   activitiescache_p.cpp
+
+   ${KACTIVITIES_CURRENT_ROOT_SOURCE_DIR}/src/utils/dbusfuture_p.cpp
+
+   version.cpp
+   )
+
+set_source_files_properties (
+   ${KACTIVITIES_CURRENT_ROOT_SOURCE_DIR}/src/common/dbus/org.kde.ActivityManager.Activities.xml
+   PROPERTIES
+   INCLUDE ${KACTIVITIES_CURRENT_ROOT_SOURCE_DIR}/src/common/dbus/org.kde.ActivityManager.Activities.h
+   )
+
+qt5_add_dbus_interface (
+   KActivities_LIB_SRCS
+
+   ${KACTIVITIES_CURRENT_ROOT_SOURCE_DIR}/src/common/dbus/org.kde.ActivityManager.Activities.xml
+   activities_interface
+   )
+
+qt5_add_dbus_interface (
+   KActivities_LIB_SRCS
+
+   ${KACTIVITIES_CURRENT_ROOT_SOURCE_DIR}/src/common/dbus/org.kde.ActivityManager.Resources.xml
+   resources_interface
+   )
+
+qt5_add_dbus_interface (
+   KActivities_LIB_SRCS
+
+   ${KACTIVITIES_CURRENT_ROOT_SOURCE_DIR}/src/common/dbus/org.kde.ActivityManager.Features.xml
+   features_interface
+   )
+
+qt5_add_dbus_interface (
+   KActivities_LIB_SRCS
+
+   ${KACTIVITIES_CURRENT_ROOT_SOURCE_DIR}/src/common/dbus/org.kde.ActivityManager.ResourcesLinking.xml
+   resources_linking_interface
+   )
+
+qt5_add_dbus_interface (
+   KActivities_LIB_SRCS
+
+   ${KACTIVITIES_CURRENT_ROOT_SOURCE_DIR}/src/common/dbus/org.kde.ActivityManager.Application.xml
+   application_interface
+   )
+
+ecm_qt_declare_logging_category(KActivities_LIB_SRCS HEADER debug_p.h IDENTIFIER KAMD_CORELIB CATEGORY_NAME org.kde.kactivities.lib.core DEFAULT_SEVERITY Warning)
+
+
+add_library (
+   KF5Activities SHARED
+   ${KActivities_LIB_SRCS}
+   )
+add_library (KF5::Activities ALIAS KF5Activities)
+
+include_directories (
+   ${KACTIVITIES_CURRENT_ROOT_SOURCE_DIR}/src
+   ${CMAKE_BINARY_DIR}/
+   )
+
+set_target_properties (
+   KF5Activities
+   PROPERTIES
+   VERSION ${KACTIVITIES_VERSION_STRING}
+   SOVERSION ${KACTIVITIES_SOVERSION}
+   EXPORT_NAME Activities
+   )
+
+target_link_libraries (
+   KF5Activities
+   PUBLIC
+      Qt5::Core
+   PRIVATE
+      Qt5::DBus
+   )
+
+target_include_directories (
+   KF5Activities
+   INTERFACE "$<INSTALL_INTERFACE:${KDE_INSTALL_INCLUDEDIR_KF5}/KActivities>"
+   )
+
+# install
+generate_export_header (KF5Activities BASE_NAME KActivities)
+
+ecm_generate_headers (
+   KActivities_CamelCase_HEADERS
+   HEADER_NAMES
+   Consumer
+   Controller
+   Info
+   ResourceInstance
+   ActivitiesModel
+   Version
+   PREFIX KActivities
+   REQUIRED_HEADERS KActivities_HEADERS
+   )
+install (
+   FILES ${KActivities_CamelCase_HEADERS}
+   DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/KActivities/KActivities
+   COMPONENT Devel
+   )
+
+install (
+   FILES ${KActivities_HEADERS} ${CMAKE_CURRENT_BINARY_DIR}/kactivities_export.h
+   DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/KActivities/kactivities
+   COMPONENT Devel
+   )
+
+install (
+   TARGETS KF5Activities
+   EXPORT KF5ActivitiesLibraryTargets
+   ${KF5_INSTALL_TARGETS_DEFAULT_ARGS}
+   )
+
+if(BUILD_QCH)
+    ecm_add_qch(
+        KF5Activities_QCH
+        NAME KActivities
+        BASE_NAME KF5Activities
+        VERSION ${KF5_VERSION}
+        ORG_DOMAIN org.kde
+        SOURCES # using only public headers, to cover only public API
+            ${KActivities_HEADERS}
+        MD_MAINPAGE "${CMAKE_SOURCE_DIR}/README.md"
+        LINK_QCHS
+            Qt5Core_QCH
+        BLANK_MACROS
+            KACTIVITIES_EXPORT
+            KACTIVITIES_DEPRECATED
+            KACTIVITIES_DEPRECATED_EXPORT
+        TAGFILE_INSTALL_DESTINATION ${KDE_INSTALL_QTQCHDIR}
+        QCH_INSTALL_DESTINATION ${KDE_INSTALL_QTQCHDIR}
+        COMPONENT Devel
+    )
+endif()
+
+
+if (NOT WIN32)
+   configure_file (
+      ${CMAKE_CURRENT_SOURCE_DIR}/libKActivities.pc.cmake
+      ${CMAKE_CURRENT_BINARY_DIR}/libKActivities.pc
+      )
+   install (
+      FILES ${CMAKE_CURRENT_BINARY_DIR}/libKActivities.pc
+      DESTINATION ${KDE_INSTALL_LIBDIR}/pkgconfig
+      )
+endif ()
+
+include (ECMGeneratePriFile)
+ecm_generate_pri_file (
+   BASE_NAME KActivities
+   LIB_NAME KF5Activities
+   FILENAME_VAR PRI_FILENAME INCLUDE_INSTALL_DIR ${KDE_INSTALL_INCLUDEDIR_KF5}/KActivities
+   )
+install (
+   FILES ${PRI_FILENAME}
+   DESTINATION ${ECM_MKSPECS_INSTALL_DIR}
+   )
+
diff --git a/src/lib/activitiescache_p.cpp b/src/lib/activitiescache_p.cpp
new file mode 100644 (file)
index 0000000..2abbb34
--- /dev/null
@@ -0,0 +1,318 @@
+/*
+ *   Copyright (C) 2013 - 2016 by Ivan Cukic <ivan.cukic(at)kde.org>
+ *
+ *   This library is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU Lesser General Public
+ *   License as published by the Free Software Foundation; either
+ *   version 2.1 of the License, or (at your option) version 3, or any
+ *   later version accepted by the membership of KDE e.V. (or its
+ *   successor approved by the membership of KDE e.V.), which shall
+ *   act as a proxy defined in Section 6 of version 3 of the license.
+ *
+ *   This library is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *   Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this library.
+ *   If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "activitiescache_p.h"
+#include "manager_p.h"
+
+#include <mutex>
+#include <memory>
+
+#include <QString>
+
+#include "mainthreadexecutor_p.h"
+
+namespace KActivities {
+
+static QString nulluuid = QStringLiteral("00000000-0000-0000-0000-000000000000");
+
+using kamd::utils::Mutable;
+
+std::shared_ptr<ActivitiesCache> ActivitiesCache::self()
+{
+    static std::weak_ptr<ActivitiesCache> s_instance;
+    static std::mutex singleton;
+    std::lock_guard<std::mutex> singleton_lock(singleton);
+
+    auto result = s_instance.lock();
+
+    if (s_instance.expired()) {
+        runInMainThread([&result] {
+            result.reset(new ActivitiesCache());
+            s_instance = result;
+            });
+    }
+
+    return result;
+}
+
+ActivitiesCache::ActivitiesCache()
+    : m_status(Consumer::NotRunning)
+{
+    // qDebug() << "ActivitiesCache: Creating a new instance";
+    using org::kde::ActivityManager::Activities;
+
+    auto activities = Manager::self()->activities();
+
+    connect(activities, &Activities::ActivityAdded,
+            this, &ActivitiesCache::updateActivity);
+    connect(activities, &Activities::ActivityChanged,
+            this, &ActivitiesCache::updateActivity);
+    connect(activities, &Activities::ActivityRemoved,
+            this, &ActivitiesCache::removeActivity);
+
+    connect(activities, &Activities::ActivityStateChanged,
+            this, &ActivitiesCache::updateActivityState);
+    connect(activities, &Activities::ActivityNameChanged,
+            this, &ActivitiesCache::setActivityName);
+    connect(activities, &Activities::ActivityDescriptionChanged,
+            this, &ActivitiesCache::setActivityDescription);
+    connect(activities, &Activities::ActivityIconChanged,
+            this, &ActivitiesCache::setActivityIcon);
+
+    connect(activities, &Activities::CurrentActivityChanged,
+            this, &ActivitiesCache::setCurrentActivity);
+
+    connect(Manager::self(), &Manager::serviceStatusChanged,
+            this, &ActivitiesCache::setServiceStatus);
+
+    // These are covered by ActivityStateChanged
+    // signal void org.kde.ActivityManager.Activities.ActivityStarted(QString activity)
+    // signal void org.kde.ActivityManager.Activities.ActivityStopped(QString activity)
+
+    setServiceStatus(Manager::self()->isServiceRunning());
+}
+
+void ActivitiesCache::setServiceStatus(bool status)
+{
+    // qDebug() << "Setting service status to:" << status;
+    loadOfflineDefaults();
+
+    if (status) {
+        updateAllActivities();
+    }
+}
+
+void ActivitiesCache::loadOfflineDefaults()
+{
+    m_status = Consumer::NotRunning;
+
+    m_activities.clear();
+    m_activities << ActivityInfo(nulluuid, QString(), QString(), QString(),
+                                 Info::Running);
+    m_currentActivity = nulluuid;
+
+    emit serviceStatusChanged(m_status);
+    emit activityListChanged();
+}
+
+ActivitiesCache::~ActivitiesCache()
+{
+    // qDebug() << "ActivitiesCache: Destroying the instance";
+}
+
+void ActivitiesCache::removeActivity(const QString &id)
+{
+    // qDebug() << "Removing the activity";
+
+    // Since we are sorting the activities by name now,
+    // we can not use lower_bound to search for an activity
+    // with a specified id
+    const auto where = find(id);
+
+    if (where != m_activities.end() && where->id == id) {
+        m_activities.erase(where);
+        emit activityRemoved(id);
+        emit activityListChanged();
+
+    } else {
+        // qFatal("Requested to delete an non-existent activity");
+    }
+}
+
+void ActivitiesCache::updateAllActivities()
+{
+    // qDebug() << "Updating all";
+    m_status = Consumer::Unknown;
+    emit serviceStatusChanged(m_status);
+
+    // Loading the current activity
+    auto call = Manager::self()->activities()->asyncCall(
+        QStringLiteral("CurrentActivity"));
+
+    onCallFinished(call, SLOT(setCurrentActivityFromReply(QDBusPendingCallWatcher*)));
+
+    // Loading all the activities
+    call = Manager::self()->activities()->asyncCall(
+        QStringLiteral("ListActivitiesWithInformation"));
+
+    onCallFinished(call, SLOT(setAllActivitiesFromReply(QDBusPendingCallWatcher*)));
+}
+
+void ActivitiesCache::updateActivity(const QString &id)
+{
+    // qDebug() << "Updating activity" << id;
+
+    auto call = Manager::self()->activities()->asyncCall(
+        QStringLiteral("ActivityInformation"), id);
+
+    onCallFinished(call, SLOT(setActivityInfoFromReply(QDBusPendingCallWatcher*)));
+}
+
+void ActivitiesCache::updateActivityState(const QString &id, int state)
+{
+    auto where = getInfo<Mutable>(id);
+
+    if (where && where->state != state) {
+        auto isInvalid = [](int state) {
+            return state == Info::Invalid || state == Info::Unknown;
+        };
+        auto isStopped = [](int state) {
+            return state == Info::Stopped || state == Info::Starting;
+        };
+        auto isRunning = [](int state) {
+            return state == Info::Running || state == Info::Stopping;
+        };
+
+        const bool runningStateChanged
+            = (isInvalid(state) || isInvalid(where->state)
+               || (isStopped(state) && isRunning(where->state))
+               || (isRunning(state) && isStopped(where->state)));
+
+        where->state = state;
+
+        if (runningStateChanged) {
+            emit runningActivityListChanged();
+        }
+
+        emit activityStateChanged(id, state);
+
+    } else {
+        // qFatal("Requested to update the state of an non-existent activity");
+    }
+}
+
+template <typename _Result, typename _Functor>
+void ActivitiesCache::passInfoFromReply(QDBusPendingCallWatcher *watcher, _Functor f)
+{
+    QDBusPendingReply<_Result> reply = *watcher;
+
+    if (!reply.isError()) {
+        auto replyValue = reply.template argumentAt <0>();
+        // qDebug() << "Got some reply" << replyValue;
+
+        ((*this).*f)(replyValue);
+    }
+
+    watcher->deleteLater();
+}
+
+void ActivitiesCache::setActivityInfoFromReply(QDBusPendingCallWatcher *watcher)
+{
+    // qDebug() << "reply...";
+    passInfoFromReply<ActivityInfo>(watcher, &ActivitiesCache::setActivityInfo);
+}
+
+void ActivitiesCache::setAllActivitiesFromReply(QDBusPendingCallWatcher *watcher)
+{
+    // qDebug() << "reply...";
+    passInfoFromReply<ActivityInfoList>(watcher, &ActivitiesCache::setAllActivities);
+}
+
+void ActivitiesCache::setCurrentActivityFromReply(QDBusPendingCallWatcher *watcher)
+{
+    // qDebug() << "reply...";
+    passInfoFromReply<QString>(watcher, &ActivitiesCache::setCurrentActivity);
+}
+
+void ActivitiesCache::setActivityInfo(const ActivityInfo &info)
+{
+    // qDebug() << "Setting activity info" << info.id;
+
+    // Are we updating an existing activity, or adding a new one?
+    const auto iter = find(info.id);
+    const auto present = iter != m_activities.end();
+    bool runningChanged = true;
+    // If there is an activity with the specified id,
+    // we are going to remove it, temporarily.
+    if (present) {
+        runningChanged = (*iter).state != info.state;
+        m_activities.erase(iter);
+    }
+
+    // Now, we need to find where to insert the activity
+    // and keep the cache sorted by name
+    const auto where = lower_bound(info);
+
+    m_activities.insert(where, info);
+
+    if (present) {
+        emit activityChanged(info.id);
+    } else {
+        emit activityAdded(info.id);
+        emit activityListChanged();
+        if (runningChanged) {
+            emit runningActivityListChanged();
+        }
+    }
+}
+
+#define CREATE_SETTER(WHAT, What)                                              \
+    void ActivitiesCache::setActivity##WHAT(const QString &id,                 \
+                                            const QString &value)              \
+    {                                                                          \
+        auto where = getInfo<Mutable>(id);                                     \
+                                                                               \
+        if (where) {                                                           \
+            where->What = value;                                               \
+            emit activity##WHAT##Changed(id, value);                           \
+        }                                                                      \
+    }
+
+CREATE_SETTER(Name, name)
+CREATE_SETTER(Description, description)
+CREATE_SETTER(Icon, icon)
+
+#undef CREATE_SETTER
+
+void ActivitiesCache::setAllActivities(const ActivityInfoList &_activities)
+{
+    // qDebug() << "Setting all activities";
+
+    m_activities.clear();
+
+    const ActivityInfoList activities = _activities;
+
+    for (const ActivityInfo &info : activities) {
+        m_activities << info;
+    }
+
+    std::sort(m_activities.begin(), m_activities.end(), &infoLessThan);
+
+    m_status = Consumer::Running;
+    emit serviceStatusChanged(m_status);
+    emit activityListChanged();
+}
+
+void ActivitiesCache::setCurrentActivity(const QString &activity)
+{
+    // qDebug() << "Setting current activity to" << activity;
+
+    if (m_currentActivity == activity) {
+        return;
+    }
+
+    m_currentActivity = activity;
+
+    emit currentActivityChanged(activity);
+}
+
+} // namespace KActivities
+
diff --git a/src/lib/activitiescache_p.h b/src/lib/activitiescache_p.h
new file mode 100644 (file)
index 0000000..04542e7
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ *   Copyright (C) 2013 - 2016 by Ivan Cukic <ivan.cukic(at)kde.org>
+ *
+ *   This library is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU Lesser General Public
+ *   License as published by the Free Software Foundation; either
+ *   version 2.1 of the License, or (at your option) version 3, or any
+ *   later version accepted by the membership of KDE e.V. (or its
+ *   successor approved by the membership of KDE e.V.), which shall
+ *   act as a proxy defined in Section 6 of version 3 of the license.
+ *
+ *   This library is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *   Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this library.
+ *   If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef ACTIVITIES_CACHE_P_H
+#define ACTIVITIES_CACHE_P_H
+
+#include <memory>
+
+#include <QObject>
+
+#include <common/dbus/org.kde.ActivityManager.Activities.h>
+#include <utils/ptr_to.h>
+
+#include "activities_interface.h"
+#include "consumer.h"
+
+namespace KActivities {
+
+class ActivitiesCache : public QObject {
+    Q_OBJECT
+
+public:
+    static std::shared_ptr<ActivitiesCache> self();
+
+    ~ActivitiesCache();
+
+Q_SIGNALS:
+    void activityAdded(const QString &id);
+    void activityChanged(const QString &id);
+    void activityRemoved(const QString &id);
+
+    void activityStateChanged(const QString &id, int state);
+    void activityNameChanged(const QString &id, const QString &name);
+    void activityDescriptionChanged(const QString &id, const QString &description);
+    void activityIconChanged(const QString &id, const QString &icon);
+
+    void currentActivityChanged(const QString &id);
+    void serviceStatusChanged(Consumer::ServiceStatus status);
+    void activityListChanged();
+    void runningActivityListChanged();
+
+private Q_SLOTS:
+    void updateAllActivities();
+    void loadOfflineDefaults();
+
+    void updateActivity(const QString &id);
+    void updateActivityState(const QString &id, int state);
+    void removeActivity(const QString &id);
+
+    void setActivityInfoFromReply(QDBusPendingCallWatcher *watcher);
+    void setAllActivitiesFromReply(QDBusPendingCallWatcher *watcher);
+    void setCurrentActivityFromReply(QDBusPendingCallWatcher *watcher);
+
+    void setActivityName(const QString &id, const QString &name);
+    void setActivityDescription(const QString &id, const QString &description);
+    void setActivityIcon(const QString &id, const QString &icon);
+
+    void setActivityInfo(const ActivityInfo &info);
+    void setAllActivities(const ActivityInfoList &activities);
+    void setCurrentActivity(const QString &activity);
+
+    void setServiceStatus(bool status);
+
+public:
+    template <typename _Result, typename _Functor>
+    void passInfoFromReply(QDBusPendingCallWatcher *watcher, _Functor f);
+
+    static
+    bool infoLessThan(const ActivityInfo &info, const ActivityInfo &other)
+    {
+        const auto comp =
+            QString::compare(info.name, other.name, Qt::CaseInsensitive);
+        return comp < 0 || (comp == 0 && info.id < other.id);
+    }
+
+    ActivityInfoList::iterator
+    find(const QString &id)
+    {
+        return std::find_if(m_activities.begin(), m_activities.end(),
+                            [&id] (const ActivityInfo &info) {
+                                return info.id == id;
+                            });
+    }
+
+    ActivityInfoList::iterator
+    lower_bound(const ActivityInfo &info)
+    {
+        return std::lower_bound(m_activities.begin(), m_activities.end(),
+                                info, &infoLessThan);
+    }
+
+    template <int Policy = kamd::utils::Const>
+    inline typename kamd::utils::ptr_to<ActivityInfo, Policy>::type
+    getInfo(const QString &id)
+    {
+        const auto where = find(id);
+
+        if (where != m_activities.end()) {
+            return &(*where);
+        }
+
+        return nullptr;
+    }
+
+    template <typename TargetSlot>
+    void onCallFinished(QDBusPendingCall &call, TargetSlot slot) {
+        auto watcher = new QDBusPendingCallWatcher(call, this);
+
+        connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher *)),
+                this, slot);
+    }
+
+
+    ActivitiesCache();
+
+    QList<ActivityInfo> m_activities;
+    QString m_currentActivity;
+    Consumer::ServiceStatus m_status;
+};
+
+} // namespace KActivities
+
+
+#endif /* ACTIVITIES_CACHE_P_H */
+
diff --git a/src/lib/activitiesmodel.cpp b/src/lib/activitiesmodel.cpp
new file mode 100644 (file)
index 0000000..69fd091
--- /dev/null
@@ -0,0 +1,443 @@
+/*
+ *   Copyright (C) 2012 - 2016 Ivan Cukic <ivan.cukic(at)kde.org>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License version 2,
+ *   or (at your option) any later version, as published by the Free
+ *   Software Foundation
+ *
+ *   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.
+ */
+
+// Self
+#include "activitiesmodel.h"
+#include "activitiesmodel_p.h"
+
+// Qt
+#include <QByteArray>
+#include <QDBusPendingCall>
+#include <QDBusPendingCallWatcher>
+#include <QDebug>
+#include <QHash>
+#include <QFutureWatcher>
+#include <QModelIndex>
+
+// Local
+#include "utils/remove_if.h"
+
+namespace KActivities {
+
+namespace Private {
+    template <typename _Container>
+    struct ActivityPosition {
+        ActivityPosition()
+            : isValid(false)
+            , index(0)
+            , iterator()
+        {
+        }
+
+        ActivityPosition(unsigned int index,
+                         typename _Container::const_iterator iterator)
+            : isValid(true)
+            , index(index)
+            , iterator(iterator)
+        {
+        }
+
+        operator bool() const
+        {
+            return isValid;
+        }
+
+        const bool isValid;
+        const unsigned int index;
+        const typename _Container::const_iterator iterator;
+
+        typedef typename _Container::value_type ContainerElement;
+    };
+
+    /**
+     * Returns whether the activity has a desired state.
+     * If the state is 0, returns true
+     */
+    template <typename T>
+    inline bool matchingState(ActivitiesModelPrivate::InfoPtr activity,
+                              const T &states)
+    {
+        return states.empty() || states.contains(activity->state());
+    }
+
+    /**
+     * Searches for the activity.
+     * Returns an option(index, iterator) for the found activity.
+     */
+    template <typename _Container>
+    inline
+    ActivityPosition<_Container>
+    activityPosition(const _Container &container, const QString &activityId)
+    {
+        auto position = std::find_if(container.begin(), container.end(),
+            [&] (const typename ActivityPosition<_Container>::ContainerElement &activity) {
+                return activity->id() == activityId;
+            }
+        );
+
+        return (position != container.end()) ?
+            ActivityPosition<_Container>(position - container.begin(), position) :
+            ActivityPosition<_Container>();
+    }
+
+    /**
+     * Notifies the model that an activity was updated
+     */
+    template <typename _Model, typename _Container>
+    inline
+    void emitActivityUpdated(_Model *model,
+                             const _Container &container,
+                             const QString &activity, int role)
+    {
+        auto position = Private::activityPosition(container, activity);
+
+        if (position) {
+            emit model->q->dataChanged(
+                model->q->index(position.index),
+                model->q->index(position.index),
+                role == Qt::DecorationRole ?
+                    QVector<int> {role, ActivitiesModel::ActivityIconSource} :
+                    QVector<int> {role}
+            );
+        }
+    }
+
+    /**
+     * Notifies the model that an activity was updated
+     */
+    template <typename _Model, typename _Container>
+    inline
+    void emitActivityUpdated(_Model *model,
+                             const _Container &container,
+                             QObject *activityInfo, int role)
+    {
+        const auto activity = static_cast<Info*> (activityInfo);
+        emitActivityUpdated(model, container, activity->id(), role);
+    }
+
+}
+
+ActivitiesModelPrivate::ActivitiesModelPrivate(ActivitiesModel *parent)
+    : q(parent)
+{
+}
+
+ActivitiesModel::ActivitiesModel(QObject *parent)
+    : QAbstractListModel(parent)
+    , d(new ActivitiesModelPrivate(this))
+{
+    // Initializing role names for qml
+    connect(&d->activities, &Consumer::serviceStatusChanged,
+            this,           [this] (Consumer::ServiceStatus status) { d->setServiceStatus(status); });
+
+    connect(&d->activities, &Consumer::activityAdded,
+            this,           [this] (const QString &activity) { d->onActivityAdded(activity); });
+    connect(&d->activities, &Consumer::activityRemoved,
+            this,           [this] (const QString &activity) { d->onActivityRemoved(activity); });
+    connect(&d->activities, &Consumer::currentActivityChanged,
+            this,           [this] (const QString &activity) { d->onCurrentActivityChanged(activity); });
+
+    d->setServiceStatus(d->activities.serviceStatus());
+}
+
+ActivitiesModel::ActivitiesModel(QVector<Info::State> shownStates, QObject *parent)
+    : QAbstractListModel(parent)
+    , d(new ActivitiesModelPrivate(this))
+{
+    d->shownStates = shownStates;
+
+    // Initializing role names for qml
+    connect(&d->activities, &Consumer::serviceStatusChanged,
+            this,           [this] (Consumer::ServiceStatus status) { d->setServiceStatus(status); });
+
+    connect(&d->activities, &Consumer::activityAdded,
+            this,           [this] (const QString &activity) { d->onActivityAdded(activity); });
+    connect(&d->activities, &Consumer::activityRemoved,
+            this,           [this] (const QString &activity) { d->onActivityRemoved(activity); });
+    connect(&d->activities, &Consumer::currentActivityChanged,
+            this,           [this] (const QString &activity) { d->onCurrentActivityChanged(activity); });
+
+    d->setServiceStatus(d->activities.serviceStatus());
+}
+
+ActivitiesModel::~ActivitiesModel()
+{
+    delete d;
+}
+
+QHash<int, QByteArray> ActivitiesModel::roleNames() const
+{
+    return {
+        {ActivityName,        "name"},
+        {ActivityState,       "state"},
+        {ActivityId,          "id"},
+        {ActivityIconSource,  "iconSource"},
+        {ActivityDescription, "description"},
+        {ActivityBackground,  "background"},
+        {ActivityIsCurrent,   "isCurrent"}
+    };
+}
+
+
+void ActivitiesModelPrivate::setServiceStatus(Consumer::ServiceStatus)
+{
+    replaceActivities(activities.activities());
+}
+
+void ActivitiesModelPrivate::replaceActivities(const QStringList &activities)
+{
+    q->beginResetModel();
+
+    knownActivities.clear();
+    shownActivities.clear();
+
+    for (const QString &activity: activities) {
+        onActivityAdded(activity, false);
+    }
+
+    q->endResetModel();
+}
+
+void ActivitiesModelPrivate::onActivityAdded(const QString &id, bool notifyClients)
+{
+    auto info = registerActivity(id);
+
+    showActivity(info, notifyClients);
+}
+
+void ActivitiesModelPrivate::onActivityRemoved(const QString &id)
+{
+    hideActivity(id);
+    unregisterActivity(id);
+}
+
+void ActivitiesModelPrivate::onCurrentActivityChanged(const QString &id)
+{
+    Q_UNUSED(id);
+
+    for (const auto &activity: shownActivities) {
+        Private::emitActivityUpdated(this, shownActivities, activity->id(),
+                                     ActivitiesModel::ActivityIsCurrent);
+    }
+}
+
+ActivitiesModelPrivate::InfoPtr ActivitiesModelPrivate::registerActivity(const QString &id)
+{
+    auto position = Private::activityPosition(knownActivities, id);
+
+    if (position) {
+        return *(position.iterator);
+
+    } else {
+        auto activityInfo = std::make_shared<Info>(id);
+
+        auto ptr = activityInfo.get();
+
+        connect(ptr,  &Info::nameChanged,
+                this, &ActivitiesModelPrivate::onActivityNameChanged);
+        connect(ptr,  &Info::descriptionChanged,
+                this, &ActivitiesModelPrivate::onActivityDescriptionChanged);
+        connect(ptr,  &Info::iconChanged,
+                this, &ActivitiesModelPrivate::onActivityIconChanged);
+        connect(ptr,  &Info::stateChanged,
+                this, &ActivitiesModelPrivate::onActivityStateChanged);
+
+        knownActivities.insert(InfoPtr(activityInfo));
+
+        return activityInfo;
+    }
+}
+
+void ActivitiesModelPrivate::unregisterActivity(const QString &id)
+{
+    auto position = Private::activityPosition(knownActivities, id);
+
+    if (position) {
+        if (auto shown = Private::activityPosition(shownActivities, id)) {
+            q->beginRemoveRows(QModelIndex(), shown.index, shown.index);
+            shownActivities.removeAt(shown.index);
+            q->endRemoveRows();
+        }
+
+        knownActivities.removeAt(position.index);
+    }
+}
+
+void ActivitiesModelPrivate::showActivity(InfoPtr activityInfo, bool notifyClients)
+{
+    // Should it really be shown?
+    if (!Private::matchingState(activityInfo, shownStates)) return;
+
+    // Is it already shown?
+    if (std::binary_search(shownActivities.cbegin(), shownActivities.cend(),
+                           activityInfo, InfoPtrComparator())) return;
+
+    auto registeredPosition
+        = Private::activityPosition(knownActivities, activityInfo->id());
+
+    if (!registeredPosition) {
+        qDebug() << "Got a request to show an unknown activity, ignoring";
+        return;
+    }
+
+    const auto activityInfoPtr = *(registeredPosition.iterator);
+
+    // In C++17, this would be:
+    // const auto [iterator, index, found] = shownActivities.insert(...);
+    const auto _result  = shownActivities.insert(activityInfoPtr);
+    // const auto iterator = std::get<0>(_result);
+    const auto index    = std::get<1>(_result);
+
+
+    if (notifyClients) {
+        q->beginInsertRows(QModelIndex(), index, index);
+        q->endInsertRows();
+    }
+}
+
+void ActivitiesModelPrivate::hideActivity(const QString &id)
+{
+    auto position = Private::activityPosition(shownActivities, id);
+
+    if (position) {
+        q->beginRemoveRows(QModelIndex(), position.index, position.index);
+        shownActivities.removeAt(position.index);
+        q->endRemoveRows();
+    }
+}
+
+#define CREATE_SIGNAL_EMITTER(What,Role)                                      \
+    void ActivitiesModelPrivate::onActivity##What##Changed(const QString &)    \
+    {                                                                          \
+        Private::emitActivityUpdated(this, shownActivities, sender(), Role);   \
+    }
+
+CREATE_SIGNAL_EMITTER(Name,Qt::DisplayRole)
+CREATE_SIGNAL_EMITTER(Description,ActivitiesModel::ActivityDescription)
+CREATE_SIGNAL_EMITTER(Icon,Qt::DecorationRole)
+
+#undef CREATE_SIGNAL_EMITTER
+
+void ActivitiesModelPrivate::onActivityStateChanged(Info::State state)
+{
+    if (shownStates.empty()) {
+        Private::emitActivityUpdated(this, shownActivities, sender(),
+                                     ActivitiesModel::ActivityState);
+
+    } else {
+        auto info = findActivity(sender());
+
+        if (!info) {
+            return;
+        }
+
+        if (shownStates.contains(state)) {
+            showActivity(info, true);
+        } else {
+            hideActivity(info->id());
+        }
+    }
+}
+
+void ActivitiesModel::setShownStates(const QVector<Info::State> &states)
+{
+    d->shownStates = states;
+
+    d->replaceActivities(d->activities.activities());
+
+    emit shownStatesChanged(states);
+}
+
+QVector<Info::State> ActivitiesModel::shownStates() const
+{
+    return d->shownStates;
+}
+
+int ActivitiesModel::rowCount(const QModelIndex &parent) const
+{
+    if (parent.isValid()) return 0;
+
+    return d->shownActivities.size();
+}
+
+QVariant ActivitiesModel::data(const QModelIndex &index, int role) const
+{
+    const int row = index.row();
+    const auto &item = d->shownActivities.at(row);
+
+    switch (role) {
+        case Qt::DisplayRole:
+        case ActivityName:
+            return item->name();
+
+        case ActivityId:
+            return item->id();
+
+        case ActivityState:
+            return item->state();
+
+        case Qt::DecorationRole:
+        case ActivityIconSource:
+            {
+                const QString &icon = item->icon();
+
+                // We need a default icon for activities
+                return icon.isEmpty() ? QStringLiteral("activities") : icon;
+            }
+
+        case ActivityDescription:
+            return item->description();
+
+        case ActivityIsCurrent:
+            return d->activities.currentActivity() == item->id();
+
+        default:
+            return QVariant();
+    }
+}
+
+QVariant ActivitiesModel::headerData(int section, Qt::Orientation orientation,
+                                   int role) const
+{
+    Q_UNUSED(section);
+    Q_UNUSED(orientation);
+    Q_UNUSED(role);
+
+    return QVariant();
+}
+
+ActivitiesModelPrivate::InfoPtr ActivitiesModelPrivate::findActivity(QObject *ptr) const
+{
+    auto info = std::find_if(knownActivities.cbegin(), knownActivities.cend(),
+        [ptr] (const InfoPtr &info) {
+            return ptr == info.get();
+        }
+    );
+
+    if (info == knownActivities.end()) {
+        return nullptr;
+    } else {
+        return *info;
+    }
+}
+
+} // namespace KActivities
+
+// #include "activitiesmodel.moc"
+
diff --git a/src/lib/activitiesmodel.h b/src/lib/activitiesmodel.h
new file mode 100644 (file)
index 0000000..8006212
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ *   Copyright (C) 2012, 2013, 2014 Ivan Cukic <ivan.cukic(at)kde.org>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License version 2,
+ *   or (at your option) any later version, as published by the Free
+ *   Software Foundation
+ *
+ *   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 ACTIVITIES_ACTIVITIESMODEL_H
+#define ACTIVITIES_ACTIVITIESMODEL_H
+
+// Qt
+#include <QObject>
+#include <QAbstractListModel>
+
+// STL
+#include <memory>
+
+// Local
+#include "info.h"
+
+class QModelIndex;
+class QDBusPendingCallWatcher;
+
+namespace KActivities {
+
+class ActivitiesModelPrivate;
+
+/**
+ * Data model that shows existing activities
+ */
+class KACTIVITIES_EXPORT ActivitiesModel : public QAbstractListModel {
+    Q_OBJECT
+
+    Q_PROPERTY(QVector<Info::State> shownStates READ shownStates WRITE setShownStates NOTIFY shownStatesChanged)
+
+public:
+    explicit ActivitiesModel(QObject *parent = nullptr);
+
+    /**
+     * Constructs the model and sets the shownStates
+     */
+    ActivitiesModel(QVector<Info::State> shownStates, QObject *parent = nullptr);
+    ~ActivitiesModel() override;
+
+    int rowCount(const QModelIndex &parent = QModelIndex()) const
+        override;
+
+    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const
+        override;
+
+    QVariant headerData(int section, Qt::Orientation orientation,
+                        int role = Qt::DisplayRole) const override;
+
+    QHash<int, QByteArray> roleNames() const override;
+
+    enum Roles {
+        ActivityId          = Qt::UserRole,       ///< UUID of the activity
+        ActivityName        = Qt::UserRole + 1,   ///< Activity name
+        ActivityDescription = Qt::UserRole + 2,   ///< Activity description
+        ActivityIconSource  = Qt::UserRole + 3,   ///< Activity icon source name
+        ActivityState       = Qt::UserRole + 4,   ///< The current state of the activity @see Info::State
+        ActivityBackground  = Qt::UserRole + 5,   ///< Activity wallpaper (currently unsupported)
+        ActivityIsCurrent   = Qt::UserRole + 6,   ///< Is this activity the current one current
+
+        UserRole            = Qt::UserRole + 32   ///< To be used by models that inherit this one
+    };
+
+public Q_SLOTS:
+    /**
+     * The model can filter the list of activities based on their state.
+     * This method sets which states should be shown.
+     */
+    void setShownStates(const QVector<Info::State> &shownStates);
+
+    /**
+     * The model can filter the list of activities based on their state.
+     * This method returns which states are currently shown.
+     */
+    QVector<Info::State> shownStates() const;
+
+Q_SIGNALS:
+    void shownStatesChanged(const QVector<Info::State> &state);
+
+private:
+    friend class ActivitiesModelPrivate;
+    ActivitiesModelPrivate * const d;
+};
+
+} // namespace KActivities
+
+#endif // ACTIVITIES_ACTIVITIESMODEL_H
+
diff --git a/src/lib/activitiesmodel_p.h b/src/lib/activitiesmodel_p.h
new file mode 100644 (file)
index 0000000..441736e
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ *   Copyright (C) 2016 Ivan Čukić <ivan.cukic(at)kde.org>
+ *
+ *   This library is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU Lesser General Public
+ *   License as published by the Free Software Foundation; either
+ *   version 2.1 of the License, or (at your option) version 3, or any
+ *   later version accepted by the membership of KDE e.V. (or its
+ *   successor approved by the membership of KDE e.V.), which shall
+ *   act as a proxy defined in Section 6 of version 3 of the license.
+ *
+ *   This library is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *   Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this library.
+ *   If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef ACTIVITIES_ACTIVITIESMODEL_P_H
+#define ACTIVITIES_ACTIVITIESMODEL_P_H
+
+#include "activitiesmodel.h"
+
+#include "consumer.h"
+
+#include "utils/qflatset.h"
+
+#include <QCollator>
+
+namespace KActivities {
+
+class ActivitiesModelPrivate : public QObject {
+    Q_OBJECT
+public:
+    ActivitiesModelPrivate(ActivitiesModel *parent);
+
+public Q_SLOTS:
+    void onActivityNameChanged(const QString &name);
+    void onActivityDescriptionChanged(const QString &description);
+    void onActivityIconChanged(const QString &icon);
+    void onActivityStateChanged(KActivities::Info::State state);
+
+    void replaceActivities(const QStringList &activities);
+    void onActivityAdded(const QString &id, bool notifyClients = true);
+    void onActivityRemoved(const QString &id);
+    void onCurrentActivityChanged(const QString &id);
+
+    void setServiceStatus(KActivities::Consumer::ServiceStatus status);
+
+public:
+    KActivities::Consumer activities;
+    QVector<Info::State> shownStates;
+
+    typedef std::shared_ptr<Info> InfoPtr;
+
+    struct InfoPtrComparator {
+        bool operator() (const InfoPtr& left, const InfoPtr& right) const
+        {
+            QCollator c;
+            c.setCaseSensitivity(Qt::CaseInsensitive);
+            c.setNumericMode(true);
+            int rc = c.compare(left->name(), right->name());
+            if (rc == 0) {
+                return left->id() < right->id();
+            }
+            return rc < 0;
+        }
+    };
+
+    QFlatSet<InfoPtr, InfoPtrComparator> knownActivities;
+    QFlatSet<InfoPtr, InfoPtrComparator> shownActivities;
+
+    InfoPtr registerActivity(const QString &id);
+    void unregisterActivity(const QString &id);
+    void showActivity(InfoPtr activityInfo, bool notifyClients);
+    void hideActivity(const QString &id);
+    void backgroundsUpdated(const QStringList &activities);
+
+    InfoPtr findActivity(QObject *ptr) const;
+
+    ActivitiesModel *const q;
+};
+
+
+} // namespace KActivities
+
+#endif // ACTIVITIES_ACTIVITIESMODEL_P_H
+
diff --git a/src/lib/consumer.cpp b/src/lib/consumer.cpp
new file mode 100644 (file)
index 0000000..a5daeb0
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ *   Copyright (C) 2010 - 2016 by Ivan Cukic <ivan.cukic(at)kde.org>
+ *
+ *   This library is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU Lesser General Public
+ *   License as published by the Free Software Foundation; either
+ *   version 2.1 of the License, or (at your option) version 3, or any
+ *   later version accepted by the membership of KDE e.V. (or its
+ *   successor approved by the membership of KDE e.V.), which shall
+ *   act as a proxy defined in Section 6 of version 3 of the license.
+ *
+ *   This library is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *   Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this library.
+ *   If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "consumer.h"
+#include "consumer_p.h"
+#include "manager_p.h"
+
+#include "debug_p.h"
+
+namespace KActivities {
+
+ConsumerPrivate::ConsumerPrivate()
+    : cache(ActivitiesCache::self())
+{
+}
+
+void ConsumerPrivate::setServiceStatus(Consumer::ServiceStatus status)
+{
+    emit serviceStatusChanged(status);
+}
+
+Consumer::Consumer(QObject *parent)
+    : QObject(parent)
+    , d(new ConsumerPrivate())
+{
+    connect(d->cache.get(), SIGNAL(currentActivityChanged(QString)),
+            this, SIGNAL(currentActivityChanged(QString)));
+    connect(d->cache.get(), SIGNAL(activityAdded(QString)),
+            this, SIGNAL(activityAdded(QString)));
+    connect(d->cache.get(), SIGNAL(activityRemoved(QString)),
+            this, SIGNAL(activityRemoved(QString)));
+    connect(d->cache.get(), SIGNAL(serviceStatusChanged(Consumer::ServiceStatus)),
+            this, SIGNAL(serviceStatusChanged(Consumer::ServiceStatus)));
+
+    connect(d->cache.get(), &ActivitiesCache::activityListChanged,
+            this, [=]() { emit activitiesChanged(activities()); });
+    connect(d->cache.get(), &ActivitiesCache::runningActivityListChanged,
+            this, [=]() { emit runningActivitiesChanged(runningActivities()); });
+
+    // connect(d->cache.get(), SIGNAL(activityStateChanged(QString,int)),
+    //         this, SIGNAL(activityStateChanged(QString,int)));
+}
+
+Consumer::~Consumer()
+{
+    qCDebug(KAMD_CORELIB) << "Killing the consumer";
+}
+
+QString Consumer::currentActivity() const
+{
+    return d->cache->m_currentActivity;
+}
+
+QStringList Consumer::activities(Info::State state) const
+{
+    QStringList result;
+
+    result.reserve(d->cache->m_activities.size());
+
+    for (const auto & info : qAsConst(d->cache->m_activities)) {
+        if (info.state == state) {
+            result << info.id;
+        }
+    }
+
+    return result;
+}
+
+QStringList Consumer::activities() const
+{
+    QStringList result;
+
+    result.reserve(d->cache->m_activities.size());
+
+    for (const auto & info : qAsConst(d->cache->m_activities)) {
+        result << info.id;
+    }
+
+    return result;
+}
+
+QStringList Consumer::runningActivities() const
+{
+    QStringList result;
+
+    result.reserve(d->cache->m_activities.size());
+
+    for (const auto & info : qAsConst(d->cache->m_activities)) {
+        if (info.state == Info::Running || info.state == Info::Stopping) {
+            result << info.id;
+        }
+    }
+
+    return result;
+}
+
+
+Consumer::ServiceStatus Consumer::serviceStatus()
+{
+    return d->cache->m_status;
+}
+
+} // namespace KActivities
diff --git a/src/lib/consumer.h b/src/lib/consumer.h
new file mode 100644 (file)
index 0000000..1cae6d5
--- /dev/null
@@ -0,0 +1,180 @@
+/*
+ *   Copyright (C) 2010 - 2016 by Ivan Cukic <ivan.cukic(at)kde.org>
+ *
+ *   This library is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU Lesser General Public
+ *   License as published by the Free Software Foundation; either
+ *   version 2.1 of the License, or (at your option) version 3, or any
+ *   later version accepted by the membership of KDE e.V. (or its
+ *   successor approved by the membership of KDE e.V.), which shall
+ *   act as a proxy defined in Section 6 of version 3 of the license.
+ *
+ *   This library is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *   Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this library.
+ *   If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef ACTIVITIES_CONSUMER_H
+#define ACTIVITIES_CONSUMER_H
+
+#include <QObject>
+#include <QString>
+#include <QStringList>
+#include <QScopedPointer>
+
+#include "info.h"
+
+#include "kactivities_export.h"
+
+namespace KActivities {
+
+class ConsumerPrivate;
+
+/**
+ * Contextual information can be, from the user's point of view, divided
+ * into three aspects - "who am I?", "where am I?" (what are my surroundings?)
+ * and "what am I doing?".
+ *
+ * Activities deal with the last one - "what am I doing?". The current activity
+ * refers to what the user is doing at the moment, while the other activities
+ * represent things that he/she was doing before, and probably will be doing
+ * again.
+ *
+ * Activity is an abstract concept whose meaning can differ from one user to
+ * another. Typical examples of activities are "developing a KDE project",
+ * "studying the 19th century art", "composing music", "lazing on a Sunday
+ * afternoon" etc.
+ *
+ * Consumer provides read-only information about activities.
+ *
+ * Before relying on the values retrieved by the class, make sure that the
+ * serviceStatus is set to Running. Otherwise, you can get invalid data either
+ * because the service is not functioning properly (or at all) or because
+ * the class did not have enough time to synchronize the data with it.
+ *
+ * For example, if this is the only existing instance of the Consumer class,
+ * the listActivities method will return an empty list.
+ *
+ * @code
+ * void someMethod() {
+ *     // Do not copy. This approach is not a good one!
+ *     Consumer c;
+ *     doSomethingWith(c.listActivities());
+ * }
+ * @endcode
+ *
+ * Instances of the Consumer class should be long-lived. For example, members
+ * of the classes that use them, and you should listen for the changes in the
+ * provided properties.
+ *
+ * @since 4.5
+ */
+class KACTIVITIES_EXPORT Consumer : public QObject {
+    Q_OBJECT
+
+    Q_PROPERTY(QString currentActivity READ currentActivity NOTIFY currentActivityChanged)
+    Q_PROPERTY(QStringList activities READ activities NOTIFY activitiesChanged)
+    Q_PROPERTY(QStringList runningActivities READ runningActivities NOTIFY runningActivitiesChanged)
+    Q_PROPERTY(ServiceStatus serviceStatus READ serviceStatus NOTIFY serviceStatusChanged)
+
+public:
+    /**
+     * Different states of the activities service
+     */
+    enum ServiceStatus {
+        NotRunning, ///< Service is not running
+        Unknown,    ///< Unable to determine the status of the service
+        Running     ///< Service is running properly
+    };
+
+    explicit Consumer(QObject *parent = nullptr);
+
+    ~Consumer();
+
+    /**
+     * @returns the id of the current activity
+     * @note Activity ID is a UUID-formatted string. If the serviceStatus
+     *       is not Running, a null UUID is returned. The ID can also be an empty
+     *       string in the case there is no current activity.
+     */
+    QString currentActivity() const;
+
+    /**
+     * @returns the list of activities filtered by state
+     * @param state state of the activity
+     * @note If the serviceStatus is not Running, only a null activity will be
+     *       returned.
+     */
+    QStringList activities(Info::State state) const;
+
+
+    /**
+     * @returns a list of running activities
+     * This is a convenience method that returns Running and Stopping activities
+     */
+    QStringList runningActivities() const;
+
+    /**
+     * @returns the list of all existing activities
+     * @note If the serviceStatus is not Running, only a null activity will be
+     *       returned.
+     */
+    QStringList activities() const;
+
+    /**
+     * @returns status of the activities service
+     */
+    ServiceStatus serviceStatus();
+
+Q_SIGNALS:
+    /**
+     * This signal is emitted when the current activity is changed
+     * @param id id of the new current activity
+     */
+    void currentActivityChanged(const QString &id);
+
+    /**
+     * This signal is emitted when the activity service goes online or offline,
+     * or when the class manages to synchronize the data with the service.
+     * @param status new status of the service
+     */
+    void serviceStatusChanged(Consumer::ServiceStatus status);
+
+    /**
+     * This signal is emitted when a new activity is added
+     * @param id id of the new activity
+     */
+    void activityAdded(const QString &id);
+
+    /**
+     * This signal is emitted when an activity has been removed
+     * @param id id of the removed activity
+     */
+    void activityRemoved(const QString &id);
+
+    /**
+     * This signal is emitted when the activity list changes
+     * @param activities list of activities
+     */
+    void activitiesChanged(const QStringList &activities);
+
+    /**
+     * This signal is emitted when the list of running activities changes
+     * @param runningActivities list of running activities
+     */
+    void runningActivitiesChanged(const QStringList &runningActivities);
+
+
+
+private:
+    const QScopedPointer<ConsumerPrivate> d;
+};
+
+} // namespace KActivities
+
+#endif // ACTIVITIES_CONSUMER_H
diff --git a/src/lib/consumer_p.h b/src/lib/consumer_p.h
new file mode 100644 (file)
index 0000000..2d09ae9
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ *   Copyright (C) 2010 - 2016 by Ivan Cukic <ivan.cukic(at)kde.org>
+ *
+ *   This library is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU Lesser General Public
+ *   License as published by the Free Software Foundation; either
+ *   version 2.1 of the License, or (at your option) version 3, or any
+ *   later version accepted by the membership of KDE e.V. (or its
+ *   successor approved by the membership of KDE e.V.), which shall
+ *   act as a proxy defined in Section 6 of version 3 of the license.
+ *
+ *   This library is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *   Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this library.
+ *   If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef ACTIVITIES_CONSUMER_P_H
+#define ACTIVITIES_CONSUMER_P_H
+
+#include "consumer.h"
+
+#include <memory>
+
+
+#include "activitiescache_p.h"
+
+namespace KActivities {
+
+class ConsumerPrivate : public QObject {
+    Q_OBJECT
+
+public:
+    ConsumerPrivate();
+
+    std::shared_ptr<ActivitiesCache> cache;
+
+public Q_SLOTS:
+    void setServiceStatus(Consumer::ServiceStatus status);
+
+Q_SIGNALS:
+    void serviceStatusChanged(Consumer::ServiceStatus status);
+
+};
+
+} // namespace KActivities
+
+#endif // ACTIVITIES_CONSUMER_P_H
diff --git a/src/lib/controller.cpp b/src/lib/controller.cpp
new file mode 100644 (file)
index 0000000..c695c66
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ *   Copyright (C) 2010 - 2016 by Ivan Cukic <ivan.cukic(at)kde.org>
+ *
+ *   This library is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU Lesser General Public
+ *   License as published by the Free Software Foundation; either
+ *   version 2.1 of the License, or (at your option) version 3, or any
+ *   later version accepted by the membership of KDE e.V. (or its
+ *   successor approved by the membership of KDE e.V.), which shall
+ *   act as a proxy defined in Section 6 of version 3 of the license.
+ *
+ *   This library is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *   Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this library.
+ *   If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "controller.h"
+#include "consumer_p.h"
+#include "manager_p.h"
+
+#include "utils/dbusfuture_p.h"
+
+#include <QObject>
+
+namespace KActivities {
+
+Controller::Controller(QObject *parent)
+    : Consumer(parent)
+{
+}
+
+Controller::~Controller()
+{
+}
+
+#define CREATE_SETTER(What)                                                    \
+    QFuture<void> Controller::setActivity##What(const QString &id,             \
+                                                const QString &value)          \
+    {                                                                          \
+        return Manager::isServiceRunning()                                     \
+                   ? DBusFuture::asyncCall<void>(                              \
+                         Manager::activities(),                                \
+                         QString::fromLatin1("SetActivity" #What), id, value)  \
+                   : DBusFuture::fromVoid();                                   \
+    }
+
+CREATE_SETTER(Name)
+CREATE_SETTER(Description)
+CREATE_SETTER(Icon)
+
+#undef CREATE_SETTER
+
+QFuture<bool> Controller::setCurrentActivity(const QString &id)
+{
+    // Q_ASSERT_X(activities().contains(id), "Controller::setCurrentActivity",
+    //            "You can not set an non-existent activity to be the current");
+
+    // return Manager::activities()->SetCurrentActivity(id);
+    return Manager::isServiceRunning() ?
+        DBusFuture::asyncCall<bool>(
+            Manager::activities(), QStringLiteral("SetCurrentActivity"), id)
+        :
+        DBusFuture::fromValue(false);
+}
+
+QFuture<QString> Controller::addActivity(const QString &name)
+{
+    Q_ASSERT_X(!name.isEmpty(), "Controller::addActivity",
+               "The activity name can not be an empty string");
+
+    // return Manager::activities()->AddActivity(name);
+    return Manager::isServiceRunning() ?
+        DBusFuture::asyncCall<QString>(
+            Manager::activities(), QStringLiteral("AddActivity"), name)
+        :
+        DBusFuture::fromValue(QString());
+}
+
+QFuture<void> Controller::removeActivity(const QString &id)
+{
+    // Q_ASSERT_X(activities().contains(id), "Controller::removeActivity",
+    //            "You can not remove an non-existent activity");
+
+    // Manager::activities()->RemoveActivity(id);
+    return Manager::isServiceRunning() ?
+        DBusFuture::asyncCall<void>(
+            Manager::activities(), QStringLiteral("RemoveActivity"), id)
+        :
+        DBusFuture::fromVoid();
+}
+
+QFuture<void> Controller::stopActivity(const QString &id)
+{
+    // Q_ASSERT_X(activities().contains(id), "Controller::stopActivity",
+    //            "You can not stop an non-existent activity");
+
+    // Manager::activities()->StopActivity(id);
+    return Manager::isServiceRunning() ?
+        DBusFuture::asyncCall<void>(
+            Manager::activities(), QStringLiteral("StopActivity"), id)
+        :
+        DBusFuture::fromVoid();
+}
+
+QFuture<void> Controller::startActivity(const QString &id)
+{
+    // Q_ASSERT_X(activities().contains(id), "Controller::startActivity",
+    //            "You can not start an non-existent activity");
+
+    // Manager::activities()->StartActivity(id);
+    return Manager::isServiceRunning() ?
+        DBusFuture::asyncCall<void>(
+            Manager::activities(), QStringLiteral("StartActivity"), id)
+        :
+        DBusFuture::fromVoid();
+}
+
+} // namespace KActivities
+
+// #include "controller.moc"
diff --git a/src/lib/controller.h b/src/lib/controller.h
new file mode 100644 (file)
index 0000000..0842098
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ *   Copyright (C) 2010 - 2016 by Ivan Cukic <ivan.cukic(at)kde.org>
+ *
+ *   This library is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU Lesser General Public
+ *   License as published by the Free Software Foundation; either
+ *   version 2.1 of the License, or (at your option) version 3, or any
+ *   later version accepted by the membership of KDE e.V. (or its
+ *   successor approved by the membership of KDE e.V.), which shall
+ *   act as a proxy defined in Section 6 of version 3 of the license.
+ *
+ *   This library is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *   Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this library.
+ *   If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef ACTIVITIES_CONTROLLER_H
+#define ACTIVITIES_CONTROLLER_H
+
+#include <QObject>
+#include <QString>
+#include <QFuture>
+
+#include "consumer.h"
+
+#include "kactivities_export.h"
+
+namespace KActivities {
+
+class ControllerPrivate;
+
+/**
+ * This class provides methods for controlling and managing
+ * the activities.
+ *
+ * @note The QFuture objects returned by these methods are not thread-based,
+ * you can not call synchronous methods like waitForFinished, cancel, pause on
+ * them. You need either to register watchers to check when those have finished,
+ * or to check whether they are ready from time to time manually.
+ *
+ * @see Consumer for info about activities
+ *
+ * @since 5.0
+ */
+class KACTIVITIES_EXPORT Controller : public Consumer {
+    Q_OBJECT
+
+    Q_PROPERTY(QString currentActivity READ currentActivity WRITE setCurrentActivity)
+
+public:
+    explicit Controller(QObject *parent = nullptr);
+
+    ~Controller();
+
+    /**
+     * Sets the name of the specified activity
+     * @param id id of the activity
+     * @param name name to be set
+     */
+    QFuture<void> setActivityName(const QString &id, const QString &name);
+
+    /**
+     * Sets the description of the specified activity
+     * @param id id of the activity
+     * @param description description to be set
+     */
+    QFuture<void> setActivityDescription(const QString &id,
+                                         const QString &description);
+
+    /**
+     * Sets the icon of the specified activity
+     * @param id id of the activity
+     * @param icon icon to be set - freedesktop.org name or file path
+     */
+    QFuture<void> setActivityIcon(const QString &id, const QString &icon);
+
+    /**
+     * Sets the current activity
+     * @param id id of the activity to make current
+     * @returns true if successful
+     */
+    QFuture<bool> setCurrentActivity(const QString &id);
+
+    /**
+     * Adds a new activity
+     * @param name name of the activity
+     * @returns id of the newly created activity
+     */
+    QFuture<QString> addActivity(const QString &name);
+
+    /**
+     * Removes the specified activity
+     * @param id id of the activity to delete
+     */
+    QFuture<void> removeActivity(const QString &id);
+
+    /**
+     * Stops the activity
+     * @param id id of the activity to stop
+     */
+    QFuture<void> stopActivity(const QString &id);
+
+    /**
+     * Starts the activity
+     * @param id id of the activity to start
+     */
+    QFuture<void> startActivity(const QString &id);
+
+private:
+    // const QScopedPointer<ControllerPrivate> d;
+};
+
+} // namespace KActivities
+
+#endif // ACTIVITIES_CONTROLLER_H
diff --git a/src/lib/info.cpp b/src/lib/info.cpp
new file mode 100644 (file)
index 0000000..5d6b16a
--- /dev/null
@@ -0,0 +1,221 @@
+/*
+ *   Copyright (C) 2010 - 2016 by Ivan Cukic <ivan.cukic(at)kde.org>
+ *
+ *   This library is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU Lesser General Public
+ *   License as published by the Free Software Foundation; either
+ *   version 2.1 of the License, or (at your option) version 3, or any
+ *   later version accepted by the membership of KDE e.V. (or its
+ *   successor approved by the membership of KDE e.V.), which shall
+ *   act as a proxy defined in Section 6 of version 3 of the license.
+ *
+ *   This library is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *   Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this library.
+ *   If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "info.h"
+#include "info_p.h"
+#include "manager_p.h"
+
+#include "utils/dbusfuture_p.h"
+
+#include <QFileSystemWatcher>
+
+namespace KActivities {
+
+// InfoPrivate
+
+InfoPrivate::InfoPrivate(Info *info, const QString &activity)
+    : q(info)
+    , cache(ActivitiesCache::self())
+    , id(activity)
+{
+}
+
+// Filters out signals for only this activity
+#define IMPLEMENT_SIGNAL_HANDLER(INTERNAL)                                     \
+    void InfoPrivate::INTERNAL(const QString &_id) const                       \
+    {   if (id == _id) emit q->INTERNAL(); }
+
+IMPLEMENT_SIGNAL_HANDLER(added)
+IMPLEMENT_SIGNAL_HANDLER(removed)
+IMPLEMENT_SIGNAL_HANDLER(started)
+IMPLEMENT_SIGNAL_HANDLER(stopped)
+IMPLEMENT_SIGNAL_HANDLER(infoChanged)
+
+#undef IMPLEMENT_SIGNAL_HANDLER
+
+#define IMPLEMENT_SIGNAL_HANDLER(INTERNAL)                                     \
+    void InfoPrivate::INTERNAL##Changed(const QString &_id,                    \
+                                        const QString &val) const              \
+    {                                                                          \
+        if (id == _id) {                                                       \
+            emit q->INTERNAL##Changed(val);                                    \
+        }                                                                      \
+    }
+
+IMPLEMENT_SIGNAL_HANDLER(name)
+IMPLEMENT_SIGNAL_HANDLER(description)
+IMPLEMENT_SIGNAL_HANDLER(icon)
+
+#undef IMPLEMENT_SIGNAL_HANDLER
+
+void InfoPrivate::activityStateChanged(const QString &idChanged,
+                                       int newState) const
+{
+    if (idChanged == id) {
+        auto state = static_cast<Info::State>(newState);
+        emit q->stateChanged(state);
+
+        if (state == KActivities::Info::Stopped) {
+            emit q->stopped();
+        } else if (state == KActivities::Info::Running) {
+            emit q->started();
+        }
+    }
+}
+
+void InfoPrivate::setCurrentActivity(const QString &currentActivity)
+{
+    if (isCurrent) {
+        if (currentActivity != id) {
+            // We are no longer the current activity
+            isCurrent = false;
+            emit q->isCurrentChanged(false);
+        }
+    } else {
+        if (currentActivity == id) {
+            // We are the current activity
+            isCurrent = true;
+            emit q->isCurrentChanged(true);
+        }
+    }
+}
+
+// Info
+Info::Info(const QString &activity, QObject *parent)
+    : QObject(parent)
+    , d(new InfoPrivate(this, activity))
+{
+    // qDebug() << "Created an instance of Info: " << (void*)this;
+#define PASS_SIGNAL_HANDLER(SIGNAL_NAME,SLOT_NAME)                            \
+    connect(d->cache.get(),  SIGNAL(SIGNAL_NAME(QString)),                     \
+            this,            SLOT(SLOT_NAME(QString)));
+
+    PASS_SIGNAL_HANDLER(activityAdded,added)
+    PASS_SIGNAL_HANDLER(activityRemoved,removed)
+    // PASS_SIGNAL_HANDLER(started)
+    // PASS_SIGNAL_HANDLER(stopped)
+    PASS_SIGNAL_HANDLER(activityChanged,infoChanged)
+#undef PASS_SIGNAL_HANDLER
+
+#define PASS_SIGNAL_HANDLER(SIGNAL_NAME,SLOT_NAME,TYPE)                      \
+    connect(d->cache.get(),  SIGNAL(SIGNAL_NAME(QString,TYPE)),               \
+            this,            SLOT(SLOT_NAME(QString,TYPE)));                  \
+
+    PASS_SIGNAL_HANDLER(activityStateChanged,activityStateChanged,int);
+    PASS_SIGNAL_HANDLER(activityNameChanged,nameChanged,QString);
+    PASS_SIGNAL_HANDLER(activityDescriptionChanged,descriptionChanged,QString);
+    PASS_SIGNAL_HANDLER(activityIconChanged,iconChanged,QString);
+#undef PASS_SIGNAL_HANDLER
+
+    connect(d->cache.get(),  SIGNAL(currentActivityChanged(QString)),
+            this,            SLOT(setCurrentActivity(QString)));
+
+    d->isCurrent = (d->cache.get()->m_currentActivity == activity);
+}
+
+Info::~Info()
+{
+    // qDebug() << "Deleted an instance of Info: " << (void*)this;
+}
+
+bool Info::isValid() const
+{
+    auto currentState = state();
+    return (currentState != Invalid && currentState != Unknown);
+}
+
+QString Info::uri() const
+{
+    return QStringLiteral("activities://") + d->id;
+}
+
+QString Info::id() const
+{
+    return d->id;
+}
+
+bool Info::isCurrent() const
+{
+    return d->isCurrent;
+}
+
+Info::State Info::state() const
+{
+    if (d->cache->m_status == Consumer::Unknown) return Info::Unknown;
+
+    auto info = d->cache->getInfo(d->id);
+
+    if (!info) return Info::Invalid;
+
+    return static_cast<Info::State> (info->state);
+}
+
+void InfoPrivate::setServiceStatus(Consumer::ServiceStatus status) const
+{
+    switch (status) {
+        case Consumer::NotRunning:
+        case Consumer::Unknown:
+            activityStateChanged(id, Info::Unknown);
+            break;
+
+        default:
+            activityStateChanged(id, q->state());
+            break;
+
+    }
+}
+
+Info::Availability Info::availability() const
+{
+    Availability result = Nothing;
+
+    if (!Manager::isServiceRunning()) {
+        return result;
+    }
+
+    if (Manager::activities()->ListActivities().value().contains(d->id)) {
+        result = BasicInfo;
+
+        if (Manager::features()->IsFeatureOperational(QStringLiteral("resources/linking"))) {
+            result = Everything;
+        }
+    }
+
+    return result;
+}
+
+#define CREATE_GETTER(What)                                                    \
+    QString Info::What() const                                                 \
+    {                                                                          \
+        auto info = d->cache->getInfo(d->id);                                  \
+        return info ? info->What : QString();                                  \
+    }
+
+CREATE_GETTER(name)
+CREATE_GETTER(description)
+CREATE_GETTER(icon)
+
+#undef CREATE_GETTER
+
+} // namespace KActivities
+
+#include "moc_info.cpp"
+// #include "moc_info_p.cpp"
diff --git a/src/lib/info.h b/src/lib/info.h
new file mode 100644 (file)
index 0000000..cb081ea
--- /dev/null
@@ -0,0 +1,250 @@
+/*
+ *   Copyright (C) 2010 - 2016 by Ivan Cukic <ivan.cukic(at)kde.org>
+ *
+ *   This library is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU Lesser General Public
+ *   License as published by the Free Software Foundation; either
+ *   version 2.1 of the License, or (at your option) version 3, or any
+ *   later version accepted by the membership of KDE e.V. (or its
+ *   successor approved by the membership of KDE e.V.), which shall
+ *   act as a proxy defined in Section 6 of version 3 of the license.
+ *
+ *   This library is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *   Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this library.
+ *   If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef ACTIVITIES_INFO_H
+#define ACTIVITIES_INFO_H
+
+#include <QObject>
+#include <QString>
+#include <QFuture>
+
+#include "kactivities_export.h"
+
+namespace KActivities {
+
+class InfoPrivate;
+
+/**
+ * This class provides info about an activity. Most methods in it require a
+ * semantic backend running to function properly.
+ *
+ * This class is not thread-safe.
+ *
+ * @see Consumer for info about activities
+ *
+ * The API of the class is synchronous, but the most used properties
+ * are pre-fetched and cached. This means that, in order to get the least
+ * amount of d-bus related locks, you should declare long-lived instances
+ * of this class.
+ *
+ * Before relying on the values retrieved by the class, make sure that the
+ * state is not Info::Unknown. You can get invalid data either because the
+ * service is not functioning properly (or at all) or because the class did
+ * not have enough time to synchronize the data with it.
+ *
+ * For example, if this is the only existing instance of the Info class, the
+ * name method will return an empty string.
+ *
+ * For example, this is wrong (works, but blocks):
+ * @code
+ * void someMethod(const QString & activity) {
+ *     // Do not copy. This approach is not a good one!
+ *     Info info(activity);
+ *     doSomethingWith(info.name());
+ * }
+ * @endcode
+ *
+ * Instances of the Info class should be long-lived. For example, members
+ * of the classes that use them, and you should listen for the changes in the
+ * provided properties.
+ *
+ * @since 4.5
+ */
+class KACTIVITIES_EXPORT Info : public QObject {
+    Q_OBJECT
+
+    Q_PROPERTY(QString id READ id)
+    Q_PROPERTY(QString name READ name NOTIFY nameChanged)
+    Q_PROPERTY(QString description READ description NOTIFY descriptionChanged)
+    Q_PROPERTY(QString icon READ icon NOTIFY iconChanged)
+    Q_PROPERTY(bool isCurrent READ isCurrent NOTIFY isCurrentChanged)
+    Q_PROPERTY(Info::State state READ state NOTIFY stateChanged)
+
+public:
+    explicit Info(const QString &activity, QObject *parent = nullptr);
+    ~Info();
+
+    /**
+     * @return true if the activity represented by this object exists and is valid
+     */
+    bool isValid() const;
+
+    /**
+     * Specifies which parts of this class are functional
+     */
+    enum Availability {
+        Nothing = 0,   ///< No activity info provided (isValid is false)
+        BasicInfo = 1, ///< Basic info is provided
+        Everything = 2 ///< Everything is available
+    };
+
+    /**
+     * State of the activity
+     */
+    enum State {
+        Invalid = 0,  ///< This activity does not exist
+        Unknown = 1,  ///< Information is not yet retrieved from the service
+        Running = 2,  ///< Activity is running
+        Starting = 3, ///< Activity is begin started
+        Stopped = 4,  ///< Activity is stopped
+        Stopping = 5  ///< Activity is begin started
+    };
+
+    /**
+     * @returns what info is provided by this instance of Info
+     */
+    Availability availability() const;
+
+    /**
+     * @returns the URI of this activity. The same URI is used by activities
+     * KIO slave.
+     */
+    QString uri() const;
+
+    /**
+     * @returns the id of the activity
+     */
+    QString id() const;
+
+    /**
+     * @returns whether this activity is the current one
+     */
+    bool isCurrent() const;
+
+    /**
+     * @returns the name of the activity
+     */
+    QString name() const;
+
+    /**
+     * @returns the description of the activity
+     */
+    QString description() const;
+
+    /**
+     * @returns the icon of the activity. Icon can be a freedesktop.org name or
+     * a file path. Or empty if no icon is set.
+     */
+    QString icon() const;
+
+    /**
+     * @returns the state of the activity
+     */
+    State state() const;
+
+    /**
+     * Links the specified resource to the activity
+     * @param resourceUri resource URI
+     * @note This method is <b>asynchronous</b>. It will return before the
+     * resource is actually linked to the activity.
+     */
+    // QFuture<void> linkResource(const QString &resourceUri);
+
+    /**
+     * Unlinks the specified resource from the activity
+     * @param resourceUri resource URI
+     * @note This method is <b>asynchronous</b>. It will return before the
+     * resource is actually unlinked from the activity.
+     */
+    // QFuture<void> unlinkResource(const QString &resourceUri);
+
+    /**
+     * @returns whether a resource is linked to this activity
+     * @note This QFuture is not thread-based, you can not call synchronous
+     * methods like waitForFinished, cancel, pause on it.
+     * @since 5.0
+     */
+    // QFuture<bool> isResourceLinked(const QString &resourceUri);
+
+Q_SIGNALS:
+    /**
+     * Emitted when the activity's name, icon or some custom property is changed
+     */
+    void infoChanged();
+
+    /**
+     * Emitted when the name is changed
+     */
+    void nameChanged(const QString &name);
+
+    /**
+     * Emitted when the activity becomes the current one, or when it stops
+     * being the current one
+     */
+    void isCurrentChanged(bool current);
+
+    /**
+     * Emitted when the description is changed
+     */
+    void descriptionChanged(const QString &description);
+
+    /**
+     * Emitted when the icon was changed
+     */
+    void iconChanged(const QString &icon);
+
+    /**
+     * Emitted when the activity is added
+     */
+    void added();
+
+    /**
+     * Emitted when the activity is removed
+     */
+    void removed();
+
+    /**
+     * Emitted when the activity is started
+     */
+    void started();
+
+    /**
+     * Emitted when the activity is stopped
+     */
+    void stopped();
+
+    /**
+     * Emitted when the activity changes state
+     * @param state new state of the activity
+     */
+    void stateChanged(KActivities::Info::State state);
+
+private:
+    const QScopedPointer<InfoPrivate> d;
+
+    Q_PRIVATE_SLOT(d, void activityStateChanged(const QString &, int))
+    Q_PRIVATE_SLOT(d, void added(const QString &))
+    Q_PRIVATE_SLOT(d, void removed(const QString &))
+    Q_PRIVATE_SLOT(d, void started(const QString &))
+    Q_PRIVATE_SLOT(d, void stopped(const QString &))
+    Q_PRIVATE_SLOT(d, void infoChanged(const QString &))
+    Q_PRIVATE_SLOT(d, void nameChanged(const QString &, const QString &))
+    Q_PRIVATE_SLOT(d, void descriptionChanged(const QString &, const QString &))
+    Q_PRIVATE_SLOT(d, void iconChanged(const QString &, const QString &))
+    Q_PRIVATE_SLOT(d, void setServiceStatus(Consumer::ServiceStatus))
+    Q_PRIVATE_SLOT(d, void setCurrentActivity(const QString &))
+
+    friend class InfoPrivate;
+};
+
+} // namespace KActivities
+
+#endif // ACTIVITIES_INFO_H
diff --git a/src/lib/info_p.h b/src/lib/info_p.h
new file mode 100644 (file)
index 0000000..dff54f7
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ *   Copyright (C) 2010 - 2016 by Ivan Cukic <ivan.cukic(at)kde.org>
+ *
+ *   This library is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU Lesser General Public
+ *   License as published by the Free Software Foundation; either
+ *   version 2.1 of the License, or (at your option) version 3, or any
+ *   later version accepted by the membership of KDE e.V. (or its
+ *   successor approved by the membership of KDE e.V.), which shall
+ *   act as a proxy defined in Section 6 of version 3 of the license.
+ *
+ *   This library is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *   Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this library.
+ *   If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef KACTIVITIESINFO_P_H
+#define KACTIVITIESINFO_P_H
+
+#include "info.h"
+#include <memory>
+
+#include "activitiescache_p.h"
+
+namespace KActivities {
+
+class InfoPrivate {
+public:
+    InfoPrivate(Info *info, const QString &activity);
+
+    void activityStateChanged(const QString &, int) const;
+
+    void added(const QString &) const;
+    void removed(const QString &) const;
+    void started(const QString &) const;
+    void stopped(const QString &) const;
+    void infoChanged(const QString &) const;
+    void nameChanged(const QString &, const QString &) const;
+    void descriptionChanged(const QString &, const QString &) const;
+    void iconChanged(const QString &, const QString &) const;
+    void setServiceStatus(Consumer::ServiceStatus status) const;
+    void setCurrentActivity(const QString &currentActivity);
+
+    Info *const q;
+    std::shared_ptr<ActivitiesCache> cache;
+    bool isCurrent;
+
+    const QString id;
+};
+
+} // namespace KActivities
+
+#endif // ACTIVITIES_INFO_P_H
diff --git a/src/lib/libKActivities.pc.cmake b/src/lib/libKActivities.pc.cmake
new file mode 100644 (file)
index 0000000..b8b0e74
--- /dev/null
@@ -0,0 +1,12 @@
+prefix=${CMAKE_INSTALL_PREFIX}
+exec_prefix=${BIN_INSTALL_DIR}
+libdir=${LIB_INSTALL_DIR}
+includedir=${INCLUDE_INSTALL_DIR}
+
+Name: libKActivities
+Description: libKActivities is a C++ library for using KDE activities
+URL: http://www.kde.org
+Requires: Qt5Core
+Version: ${KACTIVITIES_VERSION_STRING}
+Libs: -L${LIB_INSTALL_DIR} -lKF5Activities
+Cflags: -I${INCLUDE_INSTALL_DIR}
diff --git a/src/lib/mainthreadexecutor_p.cpp b/src/lib/mainthreadexecutor_p.cpp
new file mode 100644 (file)
index 0000000..48ac1e8
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ *   Copyright (C) 2014 - 2016 by Ivan Cukic <ivan.cukic(at)kde.org>
+ *
+ *   This library is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU Lesser General Public
+ *   License as published by the Free Software Foundation; either
+ *   version 2.1 of the License, or (at your option) version 3, or any
+ *   later version accepted by the membership of KDE e.V. (or its
+ *   successor approved by the membership of KDE e.V.), which shall
+ *   act as a proxy defined in Section 6 of version 3 of the license.
+ *
+ *   This library is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *   Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this library.
+ *   If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "mainthreadexecutor_p.h"
+
+#include <mutex>
+
+#include <QCoreApplication>
+#include <QMetaObject>
+#include <QThread>
+
+namespace KActivities {
+
+namespace detail {
+
+MainThreadExecutor::MainThreadExecutor(std::function<void()> &&f)
+    : m_function(std::forward<std::function<void()>>(f))
+{
+}
+
+void MainThreadExecutor::start()
+{
+    m_function();
+    deleteLater();
+}
+
+} // namespace detail
+
+void runInMainThread(std::function<void()> &&f)
+{
+    static auto mainThread = QCoreApplication::instance()->thread();
+
+    if (QThread::currentThread() == mainThread) {
+        f();
+
+    } else {
+        auto executor = new detail::MainThreadExecutor(std::forward<std::function<void()>>(f));
+
+        executor->moveToThread(mainThread);
+
+        QMetaObject::invokeMethod(executor, "start", Qt::BlockingQueuedConnection);
+    }
+}
+
+} // namespace KActivities
diff --git a/src/lib/mainthreadexecutor_p.h b/src/lib/mainthreadexecutor_p.h
new file mode 100644 (file)
index 0000000..e4ed5b4
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ *   Copyright (C) 2014 - 2016 by Ivan Cukic <ivan.cukic(at)kde.org>
+ *
+ *   This library is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU Lesser General Public
+ *   License as published by the Free Software Foundation; either
+ *   version 2.1 of the License, or (at your option) version 3, or any
+ *   later version accepted by the membership of KDE e.V. (or its
+ *   successor approved by the membership of KDE e.V.), which shall
+ *   act as a proxy defined in Section 6 of version 3 of the license.
+ *
+ *   This library is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *   Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this library.
+ *   If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef ACTIVITIES_MAINTHREADEXECUTOR_P
+#define ACTIVITIES_MAINTHREADEXECUTOR_P
+
+#include <functional>
+
+#include <QObject>
+
+namespace KActivities {
+
+namespace detail {
+    class MainThreadExecutor: public QObject {
+        Q_OBJECT
+
+    public:
+        MainThreadExecutor(std::function<void()> &&f);
+
+        Q_INVOKABLE void start();
+
+    private:
+        std::function<void()> m_function;
+    };
+} // namespace detail
+
+void runInMainThread(std::function<void()> &&f);
+
+} // namespace KActivities
+
+#endif // ACTIVITIES_MAINTHREADEXECUTOR_P
diff --git a/src/lib/manager_p.cpp b/src/lib/manager_p.cpp
new file mode 100644 (file)
index 0000000..e4e49c1
--- /dev/null
@@ -0,0 +1,180 @@
+/*
+ *   Copyright (C) 2010 - 2016 by Ivan Cukic <ivan.cukic(at)kde.org>
+ *
+ *   This library is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU Lesser General Public
+ *   License as published by the Free Software Foundation; either
+ *   version 2.1 of the License, or (at your option) version 3, or any
+ *   later version accepted by the membership of KDE e.V. (or its
+ *   successor approved by the membership of KDE e.V.), which shall
+ *   act as a proxy defined in Section 6 of version 3 of the license.
+ *
+ *   This library is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *   Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this library.
+ *   If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "manager_p.h"
+
+#include <mutex>
+
+#include <QCoreApplication>
+#include <QDBusConnection>
+#include <QFutureWatcher>
+#include <QFutureWatcherBase>
+
+#include "debug_p.h"
+#include "mainthreadexecutor_p.h"
+
+#include "common/dbus/common.h"
+#include "utils/dbusfuture_p.h"
+#include "utils/continue_with.h"
+#include "version.h"
+
+namespace KActivities {
+
+Manager *Manager::s_instance = nullptr;
+
+Manager::Manager()
+    : QObject()
+    , m_watcher(KAMD_DBUS_SERVICE, QDBusConnection::sessionBus())
+    , m_service(new KAMD_DBUS_CLASS_INTERFACE(/, Application, this))
+    , m_activities(new KAMD_DBUS_CLASS_INTERFACE(Activities, Activities, this))
+    , m_resources(new KAMD_DBUS_CLASS_INTERFACE(Resources, Resources, this))
+    , m_resourcesLinking(new KAMD_DBUS_CLASS_INTERFACE(Resources/Linking, ResourcesLinking, this))
+    , m_features(new KAMD_DBUS_CLASS_INTERFACE(Features, Features, this))
+    , m_serviceRunning(false)
+{
+    connect(&m_watcher, &QDBusServiceWatcher::serviceOwnerChanged,
+            this, &Manager::serviceOwnerChanged);
+
+    if (isServiceRunning()) {
+        serviceOwnerChanged(KAMD_DBUS_SERVICE, QString(), KAMD_DBUS_SERVICE);
+    }
+}
+
+Manager *Manager::self()
+{
+    static std::mutex singleton;
+    std::lock_guard<std::mutex> singleton_lock(singleton);
+    #if defined(QT_DEBUG)
+    QLoggingCategory::setFilterRules(QStringLiteral("org.kde.kactivities.lib.core.debug=true"));
+    #endif
+
+    if (!s_instance) {
+
+        runInMainThread([] () {
+
+            // check if the activity manager is already running
+            if (!Manager::isServiceRunning()) {
+                bool disableAutolaunch = QCoreApplication::instance()->property("org.kde.KActivities.core.disableAutostart").toBool();
+
+                qCDebug(KAMD_CORELIB) << "Should we start the daemon?";
+                // start only if not disabled and we have a dbus connection at all
+                if (!disableAutolaunch && QDBusConnection::sessionBus().interface()) {
+                    qCDebug(KAMD_CORELIB) << "Starting the activity manager daemon";
+                    auto reply = QDBusConnection::sessionBus().interface()->startService(KAMD_DBUS_SERVICE);
+                    if (!reply.isValid()) {
+                        //pre Plasma 5.12 the daemon did not support DBus activation.  Fall back to manually forking
+                        QProcess::startDetached(QStringLiteral("kactivitymanagerd"));
+                    }
+                }
+            }
+
+            // creating a new instance of the class
+            Manager::s_instance = new Manager();
+
+        });
+    }
+
+    return s_instance;
+}
+
+bool Manager::isServiceRunning()
+{
+    return
+        (s_instance ? s_instance->m_serviceRunning : true)
+        && QDBusConnection::sessionBus().interface() && QDBusConnection::sessionBus().interface()->isServiceRegistered(KAMD_DBUS_SERVICE);
+}
+
+void Manager::serviceOwnerChanged(const QString &serviceName, const QString &oldOwner, const QString &newOwner)
+{
+    Q_UNUSED(oldOwner);
+
+    if (serviceName == KAMD_DBUS_SERVICE) {
+        m_serviceRunning = !newOwner.isEmpty();
+        emit serviceStatusChanged(m_serviceRunning);
+
+        if (m_serviceRunning) {
+            using namespace kamd::utils;
+
+            continue_with(
+                DBusFuture::fromReply(m_service->serviceVersion()),
+                [this] (const optional_view<QString> &serviceVersion) {
+                    // Test whether the service is older than the library.
+                    // If it is, we need to end this
+
+                    if (!serviceVersion.is_initialized()) {
+                        qWarning() << "KActivities: FATAL ERROR: Failed to contact the activity manager daemon";
+                        m_serviceRunning = false;
+                        return;
+                    }
+
+                    auto split = serviceVersion->split(QLatin1Char('.'));
+                    QList<int> version;
+
+                    // We require kactivitymanagerd version to be at least the
+                    // one before the repository split
+                    const int requiredVersion[] = { 6, 2, 0 };
+
+                    std::transform(
+                            split.cbegin(), split.cend(),
+                            std::back_inserter(version), [] (const QString &component) {
+                                return component.toInt();
+                            });
+
+                    // if required version is greater than the current version
+                    if (std::lexicographical_compare(
+                            version.cbegin(), version.cend(),
+                            std::begin(requiredVersion), std::end(requiredVersion)
+                        )) {
+                        QString libraryVersion = QString::number(requiredVersion[0]) + QLatin1Char('.')
+                                               + QString::number(requiredVersion[1]) + QLatin1Char('.')
+                                               + QString::number(requiredVersion[2]);
+
+                        qDebug() << "KActivities service version: " << serviceVersion.get();
+                        qDebug() << "KActivities library version: " << libraryVersion;
+                        qFatal("KActivities: FATAL ERROR: The service is older than the library");
+
+                    }
+                });
+        }
+    }
+}
+
+Service::Activities *Manager::activities()
+{
+    return self()->m_activities;
+}
+
+Service::Resources *Manager::resources()
+{
+    return self()->m_resources;
+}
+
+Service::ResourcesLinking *Manager::resourcesLinking()
+{
+    return self()->m_resourcesLinking;
+}
+
+Service::Features *Manager::features()
+{
+    return self()->m_features;
+}
+
+} // namespace KActivities
diff --git a/src/lib/manager_p.h b/src/lib/manager_p.h
new file mode 100644 (file)
index 0000000..9ad3aea
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ *   Copyright (C) 2010 - 2016 by Ivan Cukic <ivan.cukic(at)kde.org>
+ *
+ *   This library is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU Lesser General Public
+ *   License as published by the Free Software Foundation; either
+ *   version 2.1 of the License, or (at your option) version 3, or any
+ *   later version accepted by the membership of KDE e.V. (or its
+ *   successor approved by the membership of KDE e.V.), which shall
+ *   act as a proxy defined in Section 6 of version 3 of the license.
+ *
+ *   This library is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *   Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this library.
+ *   If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef ACTIVITIES_MANAGER_P
+#define ACTIVITIES_MANAGER_P
+
+#include <common/dbus/org.kde.ActivityManager.Activities.h>
+
+#include "application_interface.h"
+#include "activities_interface.h"
+#include "resources_interface.h"
+#include "resources_linking_interface.h"
+#include "features_interface.h"
+
+#include <QDBusServiceWatcher>
+
+namespace Service = org::kde::ActivityManager;
+
+namespace KActivities {
+
+class Manager : public QObject {
+    Q_OBJECT
+
+public:
+    static Manager *self();
+
+    static bool isServiceRunning();
+
+    static Service::Activities *activities();
+    static Service::Resources *resources();
+    static Service::ResourcesLinking *resourcesLinking();
+    static Service::Features *features();
+
+public Q_SLOTS:
+    void serviceOwnerChanged(const QString &serviceName,
+                             const QString &oldOwner, const QString &newOwner);
+
+Q_SIGNALS:
+    void serviceStatusChanged(bool status);
+
+private:
+    Manager();
+
+    QDBusServiceWatcher m_watcher;
+
+    static Manager *s_instance;
+
+    Service::Application *const m_service;
+    Service::Activities *const m_activities;
+    Service::Resources *const m_resources;
+    Service::ResourcesLinking *const m_resourcesLinking;
+    Service::Features *const m_features;
+    bool m_serviceRunning;
+
+    friend class ManagerInstantiator;
+};
+
+} // namespace KActivities
+
+#endif // ACTIVITIES_MANAGER_P
diff --git a/src/lib/resourceinstance.cpp b/src/lib/resourceinstance.cpp
new file mode 100644 (file)
index 0000000..1b8f7ec
--- /dev/null
@@ -0,0 +1,192 @@
+/*
+ *   Copyright (C) 2011 - 2016 by Ivan Cukic <ivan.cukic(at)kde.org>
+ *
+ *   This library is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU Lesser General Public
+ *   License as published by the Free Software Foundation; either
+ *   version 2.1 of the License, or (at your option) version 3, or any
+ *   later version accepted by the membership of KDE e.V. (or its
+ *   successor approved by the membership of KDE e.V.), which shall
+ *   act as a proxy defined in Section 6 of version 3 of the license.
+ *
+ *   This library is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *   Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this library.
+ *   If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "resourceinstance.h"
+#include "manager_p.h"
+
+#include <QCoreApplication>
+#include "debug_p.h"
+
+namespace KActivities {
+
+class ResourceInstancePrivate {
+public:
+    quintptr wid;
+    QUrl uri;
+    QString mimetype;
+    QString title;
+    QString application;
+
+    void closeResource();
+    void openResource();
+
+    enum Type {
+        Accessed = 0,
+        Opened = 1,
+        Modified = 2,
+        Closed = 3,
+        FocusedIn = 4,
+        FocusedOut = 5
+    };
+
+    static void registerResourceEvent(const QString &application, quintptr wid, const QUrl &uri, Type event)
+    {
+        Q_ASSERT_X(!application.isEmpty(), "ResourceInstance::event",
+                   "The application id must not be empty");
+
+        if (uri.isEmpty()) {
+            return;
+        }
+
+        Manager::resources()->RegisterResourceEvent(application, wid, uri.toString(), uint(event));
+    }
+};
+
+void ResourceInstancePrivate::closeResource()
+{
+    registerResourceEvent(application, wid, uri, Closed);
+}
+
+void ResourceInstancePrivate::openResource()
+{
+    registerResourceEvent(application, wid, uri, Opened);
+}
+
+ResourceInstance::ResourceInstance(quintptr wid, QObject *parent)
+    : QObject(parent)
+    , d(new ResourceInstancePrivate())
+{
+    qCDebug(KAMD_CORELIB) << "Creating ResourceInstance: empty for now";
+    d->wid = wid;
+    d->application = QCoreApplication::instance()->applicationName();
+}
+
+ResourceInstance::ResourceInstance(quintptr wid, const QString &application, QObject *parent)
+    : QObject(parent)
+    , d(new ResourceInstancePrivate())
+{
+    qCDebug(KAMD_CORELIB) << "Creating ResourceInstance: empty for now";
+    d->wid = wid;
+    d->application = application.isEmpty() ? QCoreApplication::instance()->applicationName() : application;
+}
+
+ResourceInstance::ResourceInstance(quintptr wid, QUrl resourceUri, const QString &mimetype,
+                                   const QString &title, const QString &application, QObject *parent)
+    : QObject(parent)
+    , d(new ResourceInstancePrivate())
+{
+    qCDebug(KAMD_CORELIB) << "Creating ResourceInstance: " << resourceUri;
+    d->wid = wid;
+    d->uri = resourceUri;
+    d->application = application.isEmpty() ? QCoreApplication::instance()->applicationName() : application;
+
+    d->openResource();
+
+    setTitle(title);
+    setMimetype(mimetype);
+}
+
+ResourceInstance::~ResourceInstance()
+{
+    d->closeResource();
+}
+
+void ResourceInstance::notifyModified()
+{
+    d->registerResourceEvent(d->application, d->wid, d->uri, ResourceInstancePrivate::Modified);
+}
+
+void ResourceInstance::notifyFocusedIn()
+{
+    d->registerResourceEvent(d->application, d->wid, d->uri, ResourceInstancePrivate::FocusedIn);
+}
+
+void ResourceInstance::notifyFocusedOut()
+{
+    d->registerResourceEvent(d->application, d->wid, d->uri, ResourceInstancePrivate::FocusedOut);
+}
+
+void ResourceInstance::setUri(const QUrl &newUri)
+{
+    if (d->uri == newUri) {
+        return;
+    }
+
+    if (!d->uri.isEmpty()) {
+        d->closeResource();
+    }
+
+    d->uri = newUri;
+
+    d->openResource();
+}
+
+void ResourceInstance::setMimetype(const QString &mimetype)
+{
+    if (mimetype.isEmpty()) {
+        return;
+    }
+
+    d->mimetype = mimetype;
+    // TODO: update the service info
+    Manager::resources()->RegisterResourceMimetype(d->uri.toString(), mimetype);
+}
+
+void ResourceInstance::setTitle(const QString &title)
+{
+    qCDebug(KAMD_CORELIB) << "Setting the title: " << title;
+    if (title.isEmpty()) {
+        return;
+    }
+
+    d->title = title;
+    // TODO: update the service info
+    Manager::resources()->RegisterResourceTitle(d->uri.toString(), title);
+}
+
+QUrl ResourceInstance::uri() const
+{
+    return d->uri;
+}
+
+QString ResourceInstance::mimetype() const
+{
+    return d->mimetype;
+}
+
+QString ResourceInstance::title() const
+{
+    return d->title;
+}
+
+quintptr ResourceInstance::winId() const
+{
+    return d->wid;
+}
+
+void ResourceInstance::notifyAccessed(const QUrl &uri, const QString &application)
+{
+    ResourceInstancePrivate::registerResourceEvent(
+        application.isEmpty() ? QCoreApplication::instance()->applicationName() : application,
+        0, uri, ResourceInstancePrivate::Accessed);
+}
+
+} // namespace KActivities
diff --git a/src/lib/resourceinstance.h b/src/lib/resourceinstance.h
new file mode 100644 (file)
index 0000000..77591f4
--- /dev/null
@@ -0,0 +1,203 @@
+/*
+ *   Copyright (C) 2011 - 2016 by Ivan Cukic <ivan.cukic(at)kde.org>
+ *
+ *   This library is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU Lesser General Public
+ *   License as published by the Free Software Foundation; either
+ *   version 2.1 of the License, or (at your option) version 3, or any
+ *   later version accepted by the membership of KDE e.V. (or its
+ *   successor approved by the membership of KDE e.V.), which shall
+ *   act as a proxy defined in Section 6 of version 3 of the license.
+ *
+ *   This library is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *   Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this library.
+ *   If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef ACTIVITIES_RESOURCEINSTANCE_H
+#define ACTIVITIES_RESOURCEINSTANCE_H
+
+#include <QObject>
+#include <QUrl>
+
+#include "kactivities_export.h"
+
+namespace KActivities {
+
+class ResourceInstancePrivate;
+
+/**
+ * This class is used to notify the system that a file, web page
+ * or some other resource has been accessed.
+ *
+ * It provides methods to notify the system when the resource was
+ * opened, modified and closed, along with in what window the
+ * resource is shown.
+ *
+ * You should create an instance of this class for every resource
+ * you open.
+ *
+ * "The system" in this case can be the backend for tracking
+ * and automatically scoring files that are being accessed, the
+ * system to show the open files per window in the taskbar,
+ * the share-like-connect, etc.
+ *
+ * The user of this class shouldn't care about the backend
+ * systems - everything is done under-the-hood automatically.
+ *
+ */
+class KACTIVITIES_EXPORT ResourceInstance : public QObject {
+    Q_OBJECT
+
+    Q_PROPERTY(QUrl uri READ uri WRITE setUri)
+    Q_PROPERTY(QString mimetype READ mimetype WRITE setMimetype)
+    Q_PROPERTY(QString title READ title WRITE setTitle)
+    Q_PROPERTY(quintptr winId READ winId)
+
+public:
+    /**
+     * Creates a new resource instance
+     * @param wid id of the window that will show the resource
+     * @param parent pointer to the parent object
+     * @since 4.10
+     */
+    explicit ResourceInstance(quintptr wid, QObject *parent = nullptr);
+
+    /**
+     * Creates a new resource instance
+     * @param wid id of the window that will show the resource
+     * @param application application's name (the name used for the .desktop file).
+     *        If not specified, QCoreApplication::applicationName is used
+     * @param parent pointer to the parent object
+     */
+    explicit ResourceInstance(quintptr wid, const QString &application, QObject *parent = nullptr);
+
+    /**
+     * Creates a new resource instance and automatically
+     * notifies the system that it was opened.
+     *
+     * In some special cases, where the URI of the resource is
+     * being constantly changed (for example, in the world globe,
+     * street map applications) you have two options:
+     *  - to pass an empty resourceUri while passing the mimetype
+     *  - to update the uri from time to time (in the example of
+     *    the world map - to send URIs for major objects - cities
+     *    or main streets.
+     * and in both cases reimplementing the currentUri() method
+     * which will return the exact URI shown at that specific moment.
+     *
+     * @param wid window id in which the resource is shown
+     * @param resourceUri URI of the resource that is shown
+     * @param mimetype the mime type of the resource
+     * @param title the title of the resource
+     * @param application application's name (the name used for the .desktop file).
+     *        If not specified, QCoreApplication::applicationName is used
+     * @param parent pointer to the parent object
+     */
+    ResourceInstance(quintptr wid, QUrl resourceUri, const QString &mimetype = QString(),
+                     const QString &title = QString(), const QString &application = QString(),
+                     QObject *parent = nullptr);
+
+    /**
+     * Destroys the ResourceInstance and notifies the system
+     * that the resource has been closed
+     */
+    ~ResourceInstance();
+
+public Q_SLOTS:
+    /**
+     * Call this method to notify the system that you modified
+     * (the contents of) the resource
+     */
+    void notifyModified();
+
+    /**
+     * Call this method to notify the system that the resource
+     * has the focus in your application
+     * @note You only need to call this in MDI applications
+     */
+    void notifyFocusedIn();
+
+    /**
+     * Call this method to notify the system that the resource
+     * lost the focus in your application
+     * @note You only need to call this in MDI applications
+     */
+    void notifyFocusedOut();
+
+    /**
+     * This is a convenience method that sets the new URI.
+     * This is usually handled by sending the close event for
+     * the previous URI, and an open event for the new one.
+     */
+    void setUri(const QUrl &newUri);
+
+    /**
+     * Sets the mimetype for this resource
+     */
+    void setMimetype(const QString &mimetype);
+
+    /**
+     * Sets the title for this resource
+     */
+    void setTitle(const QString &title);
+
+Q_SIGNALS:
+    /**
+     * Emitted when the system wants to show the resource
+     * represented by this ResourceInstance.
+     *
+     * You should listen to this signal if you have multiple
+     * resources shown in one window (MDI). On catching it, show
+     * the resource and give it focus.
+     */
+    void requestsFocus();
+
+public:
+    /**
+     * @returns the current uri
+     * The default implementation returns the URI that was passed
+     * to the constructor.
+     * You need to reimplement it only for the applications with
+     * frequently updated URIs.
+     */
+    virtual QUrl uri() const;
+
+    /**
+     * @returns mimetype of the resource
+     */
+    QString mimetype() const;
+
+    /**
+     * @returns title of the resource
+     */
+    QString title() const;
+
+    /**
+     * @returns the window id
+     */
+    quintptr winId() const;
+
+    /**
+     * If there's no way to tell for how long an application is keeping
+     * the resource open, you can just call this static method - it
+     * will notify the system that the application has accessed the
+     * resource
+     * @param uri URI of the resource
+     * @param application application's name (the name used for the .desktop file).
+     *        If not specified, QCoreApplication::applicationName is used
+     *
+     */
+    static void notifyAccessed(const QUrl &uri, const QString &application = QString());
+
+private:
+    const QScopedPointer<ResourceInstancePrivate> d;
+};
+}
+
+#endif // ACTIVITIES_RESOURCEINSTANCE_H
diff --git a/src/lib/version.cpp b/src/lib/version.cpp
new file mode 100644 (file)
index 0000000..6c0f960
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ *   Copyright 2008 by Aaron Seigo <aseigo@kde.org>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU Library General Public License as
+ *   published by the Free Software Foundation; either version 2, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details
+ *
+ *   You should have received a copy of the GNU Library General Public
+ *   License along with this program; if not, write to the
+ *   Free Software Foundation, Inc.,
+ *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include "version.h"
+
+namespace KActivities {
+
+unsigned int version()
+{
+    return KACTIVITIES_VERSION;
+}
+
+unsigned int versionMajor()
+{
+    return KACTIVITIES_VERSION_MAJOR;
+}
+
+unsigned int versionMinor()
+{
+    return KACTIVITIES_VERSION_MINOR;
+}
+
+unsigned int versionRelease()
+{
+    return KACTIVITIES_VERSION_RELEASE;
+}
+
+const char *versionString()
+{
+    return KACTIVITIES_VERSION_STRING;
+}
+
+} // KActivities namespace
diff --git a/src/lib/version.h b/src/lib/version.h
new file mode 100644 (file)
index 0000000..8f2922c
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ *   Copyright (C) 2008 - 2016 by Aaron Seigo <aseigo@kde.org>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU Library General Public License as
+ *   published by the Free Software Foundation; either version 2, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details
+ *
+ *   You should have received a copy of the GNU Library General Public
+ *   License along with this program; if not, write to the
+ *   Free Software Foundation, Inc.,
+ *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#ifndef KACTIVITIES_VERSION_BIN_H
+#define KACTIVITIES_VERSION_BIN_H
+
+/** @file version.h <KActivities/Version> */
+
+#include "kactivities_export.h"
+#include <kactivities_version.h>
+
+#define KACTIVITIES_VERSION_RELEASE KACTIVITIES_VERSION_PATCH
+
+/**
+ * Namespace for everything in libkactivities
+ */
+namespace KActivities {
+
+/**
+ * The runtime version of libkactivities
+ */
+KACTIVITIES_EXPORT unsigned int version();
+
+/**
+ * The runtime major version of libkactivities
+ */
+KACTIVITIES_EXPORT unsigned int versionMajor();
+
+/**
+ * The runtime major version of libkactivities
+ */
+KACTIVITIES_EXPORT unsigned int versionMinor();
+
+/**
+ * The runtime major version of libkactivities
+ */
+KACTIVITIES_EXPORT unsigned int versionRelease();
+
+/**
+ * The runtime version string of libkactivities
+ */
+KACTIVITIES_EXPORT const char *versionString();
+
+} // KActivities namespace
+
+#endif // multiple inclusion guard
diff --git a/src/utils/continue_with.h b/src/utils/continue_with.h
new file mode 100644 (file)
index 0000000..dcaa055
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ *   Copyright (C) 2014 - 2016 by Ivan Cukic <ivan.cukic(at)kde.org>
+ *
+ *   This library is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU Lesser General Public
+ *   License as published by the Free Software Foundation; either
+ *   version 2.1 of the License, or (at your option) version 3, or any
+ *   later version accepted by the membership of KDE e.V. (or its
+ *   successor approved by the membership of KDE e.V.), which shall
+ *   act as a proxy defined in Section 6 of version 3 of the license.
+ *
+ *   This library is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *   Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this library.
+ *   If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef UTILS_CONTINUE_WITH_H
+#define UTILS_CONTINUE_WITH_H
+
+#include <QFuture>
+#include <QFutureWatcher>
+#include <QDebug>
+
+#include "utils/optional_view.h"
+// #include <boost/optional.hpp>
+
+#ifdef ENABLE_QJSVALUE_CONTINUATION
+#include <QJSValue>
+#endif
+
+namespace kamd {
+namespace utils {
+
+namespace detail { //_
+#ifdef ENABLE_QJSVALUE_CONTINUATION
+    inline void test_continuation(const QJSValue &continuation)
+    {
+        if (!continuation.isCallable()) {
+            qWarning() << "Passed handler is not callable: " << continuation.toString();
+        }
+    }
+
+    template <typename _ReturnType>
+    inline void pass_value(const QFuture<_ReturnType> &future,
+                           QJSValue continuation)
+    {
+        auto result = continuation.call({ future.result() });
+        if (result.isError()) {
+            qWarning() << "Handler returned this error: " << result.toString();
+        }
+    }
+
+    inline void pass_value(const QFuture<void> &future, QJSValue continuation)
+    {
+        Q_UNUSED(future);
+        auto result = continuation.call({});
+        if (result.isError()) {
+            qWarning() << "Handler returned this error: " << result.toString();
+        }
+    }
+#endif
+
+    template <typename _Continuation>
+    inline void test_continuation(_Continuation &&continuation)
+    {
+        Q_UNUSED(continuation);
+    }
+
+    template <typename _ReturnType, typename _Continuation>
+    inline void pass_value(const QFuture<_ReturnType> &future,
+                           _Continuation &&continuation)
+    {
+        using namespace kamd::utils;
+        continuation(future.resultCount() > 0
+                         ? make_optional_view(future.result())
+                         : none());
+    }
+
+    template <typename _Continuation>
+    inline void pass_value(_Continuation &&continuation)
+    {
+        continuation();
+    }
+
+} //^ namespace detail
+
+template <typename _ReturnType, typename _Continuation>
+inline void continue_with(const QFuture<_ReturnType> &future,
+                          _Continuation &&continuation)
+{
+    detail::test_continuation(continuation);
+
+    auto watcher = new QFutureWatcher<_ReturnType>();
+    QObject::connect(watcher, &QFutureWatcherBase::finished, [=]() mutable {
+        detail::pass_value(future, continuation);
+    });
+
+    watcher->setFuture(future);
+}
+
+} // namespace utils
+} // namespace kamd
+
+#endif /* !UTILS_CONTINUE_WITH_H */
+
+
diff --git a/src/utils/dbusfuture_p.cpp b/src/utils/dbusfuture_p.cpp
new file mode 100644 (file)
index 0000000..7bb2e44
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ *   Copyright (C) 2012 - 2016 by Ivan Cukic <ivan.cukic(at)kde.org>
+ *
+ *   This library is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU Lesser General Public
+ *   License as published by the Free Software Foundation; either
+ *   version 2.1 of the License, or (at your option) version 3, or any
+ *   later version accepted by the membership of KDE e.V. (or its
+ *   successor approved by the membership of KDE e.V.), which shall
+ *   act as a proxy defined in Section 6 of version 3 of the license.
+ *
+ *   This library is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *   Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this library.
+ *   If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "dbusfuture_p.h"
+
+namespace DBusFuture {
+
+namespace detail { //_
+
+template <>
+void DBusCallFutureInterface<void>::callFinished()
+{
+    deleteLater();
+
+    // qDebug() << "This is call end";
+
+    this->reportFinished();
+}
+
+ValueFutureInterface<void>::ValueFutureInterface()
+{
+}
+
+QFuture<void> ValueFutureInterface<void>::start()
+{
+    auto future = this->future();
+
+    this->reportFinished();
+
+    deleteLater();
+
+    return future;
+}
+
+} //^ namespace detail
+
+QFuture<void> fromVoid()
+{
+    using namespace detail;
+
+    auto valueFutureInterface = new ValueFutureInterface<void>();
+
+    return valueFutureInterface->start();
+}
+
+} // namespace DBusFuture
+
diff --git a/src/utils/dbusfuture_p.h b/src/utils/dbusfuture_p.h
new file mode 100644 (file)
index 0000000..beb6505
--- /dev/null
@@ -0,0 +1,178 @@
+/*
+ *   Copyright (C) 2013 - 2016 by Ivan Cukic <ivan.cukic(at)kde.org>
+ *
+ *   This library is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU Lesser General Public
+ *   License as published by the Free Software Foundation; either
+ *   version 2.1 of the License, or (at your option) version 3, or any
+ *   later version accepted by the membership of KDE e.V. (or its
+ *   successor approved by the membership of KDE e.V.), which shall
+ *   act as a proxy defined in Section 6 of version 3 of the license.
+ *
+ *   This library is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *   Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this library.
+ *   If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef ACTIVITIES_DBUSFUTURE_P_H
+#define ACTIVITIES_DBUSFUTURE_P_H
+
+#include <QDBusPendingReply>
+#include <QDBusServiceWatcher>
+#include <QDBusAbstractInterface>
+#include <QDBusPendingCallWatcher>
+#include <QFutureInterface>
+#include <QFuture>
+#include <QFutureWatcherBase>
+
+#include "debug_p.h"
+
+namespace DBusFuture {
+
+namespace detail { //_
+
+template <typename _Result>
+class DBusCallFutureInterface : public QObject,
+                                public QFutureInterface<_Result> {
+public:
+    DBusCallFutureInterface(QDBusPendingReply<_Result> reply)
+        : reply(reply),
+          replyWatcher(nullptr)
+    {
+    }
+
+    ~DBusCallFutureInterface()
+    {
+        delete replyWatcher;
+    }
+
+    void callFinished();
+
+    QFuture<_Result> start()
+    {
+        replyWatcher = new QDBusPendingCallWatcher(reply);
+
+        QObject::connect(replyWatcher,
+                         &QDBusPendingCallWatcher::finished,
+                         [this] () { callFinished(); });
+
+        this->reportStarted();
+
+        if (reply.isFinished()) {
+            this->callFinished();
+        }
+
+        return this->future();
+    }
+
+private:
+    QDBusPendingReply<_Result> reply;
+    QDBusPendingCallWatcher * replyWatcher;
+};
+
+template <typename _Result>
+void DBusCallFutureInterface<_Result>::callFinished()
+{
+    deleteLater();
+
+    if (!reply.isError()) {
+        this->reportResult(reply.value());
+    }
+
+    this->reportFinished();
+}
+
+template <>
+void DBusCallFutureInterface<void>::callFinished();
+
+template <typename _Result>
+class ValueFutureInterface : public QObject, QFutureInterface<_Result> {
+public:
+    ValueFutureInterface(const _Result & value)
+        : value(value)
+    {
+    }
+
+    QFuture<_Result> start()
+    {
+        auto future = this->future();
+
+        this->reportResult(value);
+        this->reportFinished();
+
+        deleteLater();
+
+        return future;
+    }
+
+private:
+    _Result value;
+
+};
+
+template <>
+class ValueFutureInterface<void> : public QObject, QFutureInterface<void> {
+public:
+    ValueFutureInterface();
+
+    QFuture<void> start();
+    // {
+    //     auto future = this->future();
+    //     this->reportFinished();
+    //     deleteLater();
+    //     return future;
+    // }
+};
+
+} //^ namespace detail
+
+template <typename _Result>
+QFuture<_Result>
+asyncCall(QDBusAbstractInterface *interface, const QString &method,
+          const QVariant &arg1 = QVariant(), const QVariant &arg2 = QVariant(),
+          const QVariant &arg3 = QVariant(), const QVariant &arg4 = QVariant(),
+          const QVariant &arg5 = QVariant(), const QVariant &arg6 = QVariant(),
+          const QVariant &arg7 = QVariant(), const QVariant &arg8 = QVariant())
+{
+    using namespace detail;
+
+    auto callFutureInterface = new DBusCallFutureInterface
+        <_Result>(interface->asyncCall(method, arg1, arg2, arg3, arg4, arg5,
+                                       arg6, arg7, arg8));
+
+    return callFutureInterface->start();
+}
+
+template <typename _Result>
+QFuture<_Result>
+fromValue(const _Result & value)
+{
+    using namespace detail;
+
+    auto valueFutureInterface = new ValueFutureInterface<_Result>(value);
+
+    return valueFutureInterface->start();
+}
+
+template <typename _Result>
+QFuture<_Result>
+fromReply(const QDBusPendingReply<_Result> &reply)
+{
+    using namespace detail;
+
+    auto callFutureInterface = new DBusCallFutureInterface<_Result>(reply);
+
+    return callFutureInterface->start();
+}
+
+QFuture<void> fromVoid();
+
+} // namespace DBusFuture
+
+#endif /* DBUSFUTURE_P_H */
+
diff --git a/src/utils/model_updaters.h b/src/utils/model_updaters.h
new file mode 100644 (file)
index 0000000..007ac83
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ *   Copyright (C) 2012 Ivan Cukic <ivan.cukic(at)kde.org>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License version 2,
+ *   or (at your option) any later version, as published by the Free
+ *   Software Foundation
+ *
+ *   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 KACTIVITIES_MODEL_UPDATERS_H
+#define KACTIVITIES_MODEL_UPDATERS_H
+
+// -----------------------------------------
+// RAII classes for model updates ----------
+// -----------------------------------------
+
+#define DECLARE_RAII_MODEL_UPDATERS(Class)                                     \
+    template <typename T> class _model_reset {                                 \
+        T *model;                                                              \
+                                                                               \
+    public:                                                                    \
+        _model_reset(T *m) : model(m)                                          \
+        {                                                                      \
+            model->beginResetModel();                                          \
+        }                                                                      \
+        ~_model_reset()                                                        \
+        {                                                                      \
+            model->endResetModel();                                            \
+        }                                                                      \
+    };                                                                         \
+    template <typename T> class _model_insert {                                \
+        T *model;                                                              \
+                                                                               \
+    public:                                                                    \
+        _model_insert(T *m, const QModelIndex &parent, int first, int last)    \
+            : model(m)                                                         \
+        {                                                                      \
+            model->beginInsertRows(parent, first, last);                       \
+        }                                                                      \
+        ~_model_insert()                                                       \
+        {                                                                      \
+            model->endInsertRows();                                            \
+        }                                                                      \
+    };                                                                         \
+    template <typename T> class _model_remove {                                \
+        T *model;                                                              \
+                                                                               \
+    public:                                                                    \
+        _model_remove(T *m, const QModelIndex &parent, int first, int last)    \
+            : model(m)                                                         \
+        {                                                                      \
+            model->beginRemoveRows(parent, first, last);                       \
+        }                                                                      \
+        ~_model_remove()                                                       \
+        {                                                                      \
+            model->endRemoveRows();                                            \
+        }                                                                      \
+    };                                                                         \
+    typedef _model_reset<Class> model_reset;                                   \
+    typedef _model_remove<Class> model_remove;                                 \
+    typedef _model_insert<Class> model_insert;
+
+// -----------------------------------------
+
+#endif // KACTIVITIES_MODEL_UPDATERS_H
+
diff --git a/src/utils/optional_view.h b/src/utils/optional_view.h
new file mode 100644 (file)
index 0000000..25ce54e
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ *   Copyright (C) 5012 - 2016 by Ivan Cukic <ivan.cukic(at)kde.org>
+ *
+ *   This library is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU Lesser General Public
+ *   License as published by the Free Software Foundation; either
+ *   version 2.1 of the License, or (at your option) version 3, or any
+ *   later version accepted by the membership of KDE e.V. (or its
+ *   successor approved by the membership of KDE e.V.), which shall
+ *   act as a proxy defined in Section 6 of version 3 of the license.
+ *
+ *   This library is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *   Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this library.
+ *   If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef UTILS_OPTIONAL_VIEW_H
+#define UTILS_OPTIONAL_VIEW_H
+
+namespace kamd {
+namespace utils {
+
+struct none_t {};
+inline const none_t none() { return none_t(); }
+
+// A simple implementation of the optional class
+// until we can rely on std::optional.
+// It is not going to come close in the supported
+// features to the std one.
+// (we need it in the core library, so we don't
+// want to use boost.optional)
+template <typename T>
+class optional_view {
+public:
+    explicit optional_view(const T &value)
+        : m_value(&value)
+    {
+    }
+
+    optional_view(const none_t &)
+        : m_value(nullptr)
+    {
+    }
+
+    bool is_initialized() const
+    {
+        return m_value != nullptr;
+    }
+
+    const T &get() const
+    {
+        return *m_value;
+    }
+
+    const T *operator->() const
+    {
+        return m_value;
+    }
+
+private:
+    const T *const m_value;
+};
+
+template <typename T>
+optional_view<T> make_optional_view(const T &value)
+{
+    return optional_view<T>(value);
+}
+
+} // namespace utils
+} // namespace kamd
+
+
+#endif // UTILS_OPTIONAL_VIEW_H
+
diff --git a/src/utils/ptr_to.h b/src/utils/ptr_to.h
new file mode 100644 (file)
index 0000000..ea74f96
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ *   Copyright (C) 2015 - 2016 by Ivan Cukic <ivan.cukic(at)kde.org>
+ *
+ *   This library is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU Lesser General Public
+ *   License as published by the Free Software Foundation; either
+ *   version 2.1 of the License, or (at your option) version 3, or any
+ *   later version accepted by the membership of KDE e.V. (or its
+ *   successor approved by the membership of KDE e.V.), which shall
+ *   act as a proxy defined in Section 6 of version 3 of the license.
+ *
+ *   This library is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *   Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this library.
+ *   If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PTR_TO_H
+#define PTR_TO_H
+
+namespace kamd {
+namespace utils {
+
+enum {
+    Const = 0,
+    Mutable = 1
+};
+
+template <typename T, int Policy = Const>
+struct ptr_to {
+    typedef const T * const type;
+};
+
+template <typename T>
+struct ptr_to<T, Mutable> {
+    typedef T * const type;
+};
+
+
+
+} // namespace utils
+} // namespace kamd
+
+#endif // PTR_TO_H
diff --git a/src/utils/qflatset.h b/src/utils/qflatset.h
new file mode 100644 (file)
index 0000000..a761ebb
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ *   Copyright (C) 2016 by Ivan Čukić <ivan.cukic(at)kde.org>
+ *
+ *   This library is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU Lesser General Public
+ *   License as published by the Free Software Foundation; either
+ *   version 2.1 of the License, or (at your option) version 3, or any
+ *   later version accepted by the membership of KDE e.V. (or its
+ *   successor approved by the membership of KDE e.V.), which shall
+ *   act as a proxy defined in Section 6 of version 3 of the license.
+ *
+ *   This library is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *   Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this library.
+ *   If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef KACTIVITIES_STATS_QFLATSET_H
+#define KACTIVITIES_STATS_QFLATSET_H
+
+#include <QVector>
+#include <QPair>
+
+namespace KActivities {
+
+template <typename T, typename LessThan>
+class QFlatSet: public QVector<T> {
+public:
+    QFlatSet()
+    {
+    }
+
+    inline
+    // QPair<typename QVector<T>::iterator, bool> insert(const T &value)
+    std::tuple<typename QVector<T>::iterator, int, bool> insert(const T &value)
+    {
+        auto lessThan = LessThan();
+        auto begin    = this->begin();
+        auto end      = this->end();
+
+        if (begin == end) {
+            QVector<T>::insert(0, value);
+
+            return std::make_tuple(QVector<T>::begin(), 0, true);
+
+        } else {
+            auto iterator = std::lower_bound(begin, end, value, lessThan);
+
+            if (iterator != end) {
+                if (!lessThan(value, *iterator)) {
+                    // Already present
+                    return std::make_tuple(iterator, iterator - begin, false);
+                }
+            }
+
+            QVector<T>::insert(iterator, value);
+
+            return std::make_tuple(iterator, iterator - begin, true);
+        }
+    }
+
+private:
+    QFlatSet(const QFlatSet &original); // = delete
+
+};
+
+
+} // namespace KActivities
+
+#endif // KACTIVITIES_STATS_QFLATSET_H
+
diff --git a/src/utils/range.h b/src/utils/range.h
new file mode 100644 (file)
index 0000000..21d4f20
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ *   Copyright (C) 2012 Ivan Cukic <ivan.cukic(at)kde.org>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU Lesser General Public License version 2,
+ *   or (at your option) any later version, as published by the Free
+ *   Software Foundation
+ *
+ *   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 Lesser General Public License for more details
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this program; if not, write to the
+ *   Free Software Foundation,3 Inc.,
+ *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#ifndef UTILS_RANGE_H
+#define UTILS_RANGE_H
+
+#include <boost/range/algorithm/copy.hpp>
+#include <boost/range/adaptor/transformed.hpp>
+#include <boost/range/adaptor/filtered.hpp>
+
+/********************************************************************
+ *  Syntactic sugar for converting ranges to collections            *
+ ********************************************************************/
+
+namespace kamd {
+namespace utils {
+
+template <typename Collection, typename Range>
+__inline Collection as_collection(Range range)
+{
+    Collection result;
+
+    boost::copy(range, std::back_inserter(result));
+
+    return result;
+}
+
+template <typename Member, typename ...Args>
+__inline auto transformed(Member member, Args... args)
+    -> decltype(boost::adaptors::transformed(
+                std::bind(member, args..., std::placeholders::_1)))
+{
+    return boost::adaptors::transformed(
+        std::bind(member, args..., std::placeholders::_1)
+    );
+
+}
+
+template <typename Member, typename ...Args>
+__inline auto filtered(Member member, Args... args)
+    -> decltype(boost::adaptors::filtered(
+                std::bind(member, args..., std::placeholders::_1)))
+{
+    return boost::adaptors::filtered(
+        std::bind(member, args..., std::placeholders::_1)
+    );
+
+}
+
+template <typename Class, typename Member>
+__inline auto filtered(Class *const self, Member member)
+    -> decltype(boost::adaptors::filtered(
+                std::bind(member, self, std::placeholders::_1)))
+{
+    return boost::adaptors::filtered(
+        std::bind(member, self, std::placeholders::_1)
+    );
+
+}
+
+
+} // namespace utils
+} // namespace kamd
+
+#endif // UTILS_RANGE_H
diff --git a/src/utils/remove_if.h b/src/utils/remove_if.h
new file mode 100644 (file)
index 0000000..fbe3434
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ *   Copyright (C) 2012 Ivan Cukic <ivan.cukic(at)kde.org>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU Lesser General Public License version 2,
+ *   or (at your option) any later version, as published by the Free
+ *   Software Foundation
+ *
+ *   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 Lesser General Public License for more details
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this program; if not, write to the
+ *   Free Software Foundation,3 Inc.,
+ *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#ifndef UTILS_REMOVE_IF_H
+#define UTILS_REMOVE_IF_H
+
+#include <algorithm>
+
+/********************************************************************
+ *  Syntactic sugar for the erase-remove idiom                      *
+ ********************************************************************/
+
+namespace kamd {
+namespace utils {
+
+template <typename Collection, typename Filter>
+__inline void remove_if(Collection &collection, Filter filter)
+{
+    collection.erase(
+        std::remove_if(
+            collection.begin(), collection.end(), filter),
+        collection.end());
+}
+
+} // namespace utils
+} // namespace kamd
+
+#endif // UTILS_REMOVE_IF_H
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
new file mode 100644 (file)
index 0000000..9d268e8
--- /dev/null
@@ -0,0 +1,6 @@
+# vim:set softtabstop=3 shiftwidth=3 tabstop=3 expandtab:
+
+# add_subdirectory(slc-interface)
+add_subdirectory(activities-model)
+
+
diff --git a/tests/activities-model/CMakeLists.txt b/tests/activities-model/CMakeLists.txt
new file mode 100644 (file)
index 0000000..be2d66d
--- /dev/null
@@ -0,0 +1,41 @@
+# vim:set softtabstop=3 shiftwidth=3 tabstop=3 expandtab:
+project (KActivitiesModelTestApp)
+
+find_package (Qt5 REQUIRED NO_MODULE COMPONENTS Core Gui Widgets)
+find_package (Qt5 REQUIRED NO_MODULE COMPONENTS Core Gui Widgets)
+find_package (KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS WindowSystem)
+
+include_directories (
+   ${KACTIVITIES_CURRENT_ROOT_SOURCE_DIR}/src/
+   ${KACTIVITIES_CURRENT_ROOT_SOURCE_DIR}/autotests/
+   )
+
+set (
+   KActivitiesModelTestApp_SRCS
+   window.cpp
+   main.cpp
+   )
+
+qt5_wrap_ui(
+   KActivitiesModelTestApp_SRCS
+   window.ui
+   )
+
+if (NOT WIN32)
+
+   add_executable (
+      KActivitiesModelTestApp
+      ${KActivitiesModelTestApp_SRCS}
+      )
+
+   target_link_libraries (
+      KActivitiesModelTestApp
+      Qt5::Core
+      Qt5::Gui
+      Qt5::Widgets
+      Qt5::DBus
+      KF5::Activities
+      KF5::WindowSystem
+      )
+
+endif ()
diff --git a/tests/activities-model/main.cpp b/tests/activities-model/main.cpp
new file mode 100644 (file)
index 0000000..f31abaa
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ *   Copyright (C) 2015 Ivan Cukic <ivan.cukic(at)kde.org>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License version 2,
+ *   or (at your option) any later version, as published by the Free
+ *   Software Foundation
+ *
+ *   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 <QApplication>
+#include "window.h"
+
+int main(int argc, char *argv[])
+{
+    QApplication app(argc, argv);
+
+    Window w;
+    w.show();
+
+    return app.exec();
+}
+
diff --git a/tests/activities-model/window.cpp b/tests/activities-model/window.cpp
new file mode 100644 (file)
index 0000000..d551539
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ *   Copyright (C) 2015 Ivan Cukic <ivan.cukic(at)kde.org>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License version 2,
+ *   or (at your option) any later version, as published by the Free
+ *   Software Foundation
+ *
+ *   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 "window.h"
+
+#include "ui_window.h"
+
+#include <QItemDelegate>
+#include <QPainter>
+
+#include <KWindowSystem>
+
+class Delegate: public QItemDelegate {
+public:
+    void paint(QPainter *painter, const QStyleOptionViewItem &option,
+               const QModelIndex &index) const override
+    {
+        painter->save();
+
+        const QString title = index.data().toString();
+
+        QRect titleRect = painter->fontMetrics().boundingRect(title);
+        //unused int lineHeight = titleRect.height();
+
+        // Header background
+        auto rect = option.rect;
+        rect.setHeight(64);
+        titleRect.moveTop(option.rect.top());
+        titleRect.setWidth(option.rect.width());
+
+        if (index.data(KActivities::ActivitiesModel::ActivityIsCurrent).toBool()) {
+            painter->fillRect(rect,
+                              QColor(64, 64, 64));
+        } else {
+            painter->fillRect(rect,
+                              QColor(32, 32, 32));
+        }
+
+        // Painting the title
+        painter->setPen(QColor(255,255,255));
+
+        titleRect.moveTop(titleRect.top() + 8);
+        titleRect.setLeft(64 + 8);
+        titleRect.setWidth(titleRect.width() - 64 - 8);
+        painter->drawText(titleRect, title);
+
+        titleRect.moveTop(titleRect.bottom() + 16);
+
+        const QString description = index.data(KActivities::ActivitiesModel::ActivityDescription).toString();
+
+        if (!description.isEmpty()) {
+            painter->drawText(titleRect, index.data(KActivities::ActivitiesModel::ActivityDescription).toString());
+        } else {
+            painter->setPen(QColor(128,128,128));
+            painter->drawText(titleRect, index.data(KActivities::ActivitiesModel::ActivityId).toString());
+        }
+
+        const QString iconName = index.data(KActivities::ActivitiesModel::ActivityIconSource).toString();
+
+        if (!iconName.isEmpty()) {
+            painter->drawPixmap(option.rect.x(), option.rect.y(),
+                                QIcon::fromTheme(iconName).pixmap(64, 64));
+
+        }
+
+        painter->restore();
+
+    }
+
+    QSize sizeHint(const QStyleOptionViewItem &option,
+                   const QModelIndex &index) const override
+    {
+        Q_UNUSED(option);
+        Q_UNUSED(index);
+        return QSize(0, 70);
+    }
+};
+
+
+Window::Window()
+    : ui(new Ui::MainWindow())
+    , activities(new KActivities::Consumer(this))
+    , modelRunningActivities(new KActivities::ActivitiesModel({ KActivities::Info::Running, KActivities::Info::Stopping }, this))
+    , modelStoppedActivities(new KActivities::ActivitiesModel({ KActivities::Info::Stopped, KActivities::Info::Starting }, this))
+{
+    ui->setupUi(this);
+
+    modelRunningActivities->setObjectName(QStringLiteral("RUNNING"));
+    ui->listRunningActivities->setModel(modelRunningActivities);
+    ui->listRunningActivities->setItemDelegate(new Delegate());
+
+    modelStoppedActivities->setObjectName(QStringLiteral("STOPPED"));
+    ui->listStoppedActivities->setModel(modelStoppedActivities);
+    ui->listStoppedActivities->setItemDelegate(new Delegate());
+
+    qDebug() <<
+    connect(activities, &KActivities::Consumer::runningActivitiesChanged,
+            this, [] (const QStringList &running) { qDebug() << running; });
+}
+
+void Window::showEvent(QShowEvent * event)
+{
+    Q_UNUSED(event);
+    KWindowSystem::self()->setOnActivities(effectiveWinId(), QStringList());
+    KWindowSystem::self()->setOnAllDesktops(effectiveWinId(), true);
+}
+
+Window::~Window()
+{
+    delete ui;
+}
diff --git a/tests/activities-model/window.h b/tests/activities-model/window.h
new file mode 100644 (file)
index 0000000..492342b
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ *   Copyright (C) 2015 Ivan Cukic <ivan.cukic(at)kde.org>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License version 2,
+ *   or (at your option) any later version, as published by the Free
+ *   Software Foundation
+ *
+ *   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.
+ */
+
+#pragma once
+
+#include <QMainWindow>
+
+#include <activitiesmodel.h>
+#include <consumer.h>
+
+namespace Ui {
+    class MainWindow;
+}
+
+class Window: public QMainWindow {
+    Q_OBJECT
+
+public:
+    Window();
+    ~Window();
+
+protected:
+    void showEvent(QShowEvent * event) override;
+
+private:
+    Ui::MainWindow *ui;
+    KActivities::Consumer *activities;
+    KActivities::ActivitiesModel *modelRunningActivities;
+    KActivities::ActivitiesModel *modelStoppedActivities;
+};
+
diff --git a/tests/activities-model/window.ui b/tests/activities-model/window.ui
new file mode 100644 (file)
index 0000000..6e566b1
--- /dev/null
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MainWindow</class>
+ <widget class="QMainWindow" name="MainWindow">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>406</width>
+    <height>869</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>MainWindow</string>
+  </property>
+  <widget class="QWidget" name="centralwidget">
+   <layout class="QVBoxLayout" name="verticalLayout_3" stretch="2,1,0">
+    <item>
+     <layout class="QVBoxLayout" name="verticalLayout_2">
+      <item>
+       <widget class="QLabel" name="label">
+        <property name="text">
+         <string>Running</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QListView" name="listRunningActivities"/>
+      </item>
+     </layout>
+    </item>
+    <item>
+     <layout class="QVBoxLayout" name="verticalLayout">
+      <item>
+       <widget class="QLabel" name="label_2">
+        <property name="text">
+         <string>Stopped</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QListView" name="listStoppedActivities"/>
+      </item>
+     </layout>
+    </item>
+    <item>
+     <widget class="QPushButton" name="buttonClose">
+      <property name="minimumSize">
+       <size>
+        <width>0</width>
+        <height>48</height>
+       </size>
+      </property>
+      <property name="text">
+       <string>Close</string>
+      </property>
+     </widget>
+    </item>
+   </layout>
+  </widget>
+ </widget>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonClose</sender>
+   <signal>clicked()</signal>
+   <receiver>MainWindow</receiver>
+   <slot>close()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>332</x>
+     <y>842</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>341</x>
+     <y>878</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
diff --git a/tests/imports/activities.qml b/tests/imports/activities.qml
new file mode 100644 (file)
index 0000000..c9f5680
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2013  Heena Mahour <heena393@gmail.com>
+ * Copyright 2013 Sebastian Kügler <sebas@kde.org>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+import QtQuick 2.0
+import org.kde.plasma.core 2.0 as PlasmaCore
+import org.kde.plasma.components 2.0 as PlasmaComponents
+import org.kde.plasma.extras 2.0 as PlasmaExtras
+import org.kde.activities 0.1 as Activities
+
+ListView {
+    id: main
+
+    property int minimumWidth: 32
+    property int minimumHeight: 32 // theme.mSize(theme.defaultFont).height * 14
+    property int implicitWidth: minimumWidth * 1.5
+    property int implicitHeight: minimumHeight * 1.5
+
+    property int formFactor: plasmoid.formFactor
+
+
+
+    model: modelMain
+
+    Activities.ActivityModel {
+        id: modelMain
+    }
+
+    add: Transition {
+        NumberAnimation { properties: "x"; from: 300; duration: 1000 }
+    }
+
+    addDisplaced: Transition {
+       NumberAnimation { properties: "x,y"; duration: 1000 }
+    }
+
+    remove: Transition {
+        NumberAnimation { properties: "x"; to: 300; duration: 1000 }
+    }
+
+    removeDisplaced: Transition {
+        NumberAnimation { properties: "x,y"; duration: 1000 }
+    }
+
+    ListModel {
+        id: modelDummy
+
+        ListElement {
+            name: "Bill Smith"
+            number: "555 3264"
+        }
+        ListElement {
+            name: "John Brown"
+            number: "555 8426"
+        }
+        ListElement {
+            name: "Sam Wise"
+            number: "555 0473"
+        }
+    }
+
+    delegate: Column {
+        height: 32
+        Text {
+            text: name
+            height: 16
+            font.bold: true
+        }
+        Text {
+            text: "   id: " + id
+            height: 16
+        }
+
+    }
+}
diff --git a/tests/imports/org.kde.listactivitiestest/contents/ui/main.qml b/tests/imports/org.kde.listactivitiestest/contents/ui/main.qml
new file mode 100644 (file)
index 0000000..c9f5680
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2013  Heena Mahour <heena393@gmail.com>
+ * Copyright 2013 Sebastian Kügler <sebas@kde.org>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+import QtQuick 2.0
+import org.kde.plasma.core 2.0 as PlasmaCore
+import org.kde.plasma.components 2.0 as PlasmaComponents
+import org.kde.plasma.extras 2.0 as PlasmaExtras
+import org.kde.activities 0.1 as Activities
+
+ListView {
+    id: main
+
+    property int minimumWidth: 32
+    property int minimumHeight: 32 // theme.mSize(theme.defaultFont).height * 14
+    property int implicitWidth: minimumWidth * 1.5
+    property int implicitHeight: minimumHeight * 1.5
+
+    property int formFactor: plasmoid.formFactor
+
+
+
+    model: modelMain
+
+    Activities.ActivityModel {
+        id: modelMain
+    }
+
+    add: Transition {
+        NumberAnimation { properties: "x"; from: 300; duration: 1000 }
+    }
+
+    addDisplaced: Transition {
+       NumberAnimation { properties: "x,y"; duration: 1000 }
+    }
+
+    remove: Transition {
+        NumberAnimation { properties: "x"; to: 300; duration: 1000 }
+    }
+
+    removeDisplaced: Transition {
+        NumberAnimation { properties: "x,y"; duration: 1000 }
+    }
+
+    ListModel {
+        id: modelDummy
+
+        ListElement {
+            name: "Bill Smith"
+            number: "555 3264"
+        }
+        ListElement {
+            name: "John Brown"
+            number: "555 8426"
+        }
+        ListElement {
+            name: "Sam Wise"
+            number: "555 0473"
+        }
+    }
+
+    delegate: Column {
+        height: 32
+        Text {
+            text: name
+            height: 16
+            font.bold: true
+        }
+        Text {
+            text: "   id: " + id
+            height: 16
+        }
+
+    }
+}
diff --git a/tests/imports/org.kde.listactivitiestest/metadata.desktop b/tests/imports/org.kde.listactivitiestest/metadata.desktop
new file mode 100644 (file)
index 0000000..dae15d6
--- /dev/null
@@ -0,0 +1,62 @@
+[Desktop Entry]
+Encoding=UTF-8
+Name=Activities testing
+Name[ar]=اختبار الأنشطة
+Name[ast]=Prueba d'actividaes
+Name[ca]=Prova de les activitats
+Name[ca@valencia]=Prova de les activitats
+Name[cs]=Testování aktivit
+Name[da]=Test af aktiviteter
+Name[de]=Aktivitätentest
+Name[el]=Δοκιμές δραστηριοτήτων
+Name[en_GB]=Activities testing
+Name[es]=Prueba de actividades
+Name[fi]=Aktiviteettitesti
+Name[fr]=Test des activités
+Name[gd]=Deuchainn nan gnìomhachdan
+Name[gl]=Probas de actividades
+Name[he]=בדיקת פעילויות
+Name[hu]=Aktivitásteszt
+Name[ia]=Essayante Activitates
+Name[is]=Prófun á virknisviðum
+Name[it]=Prova delle attività
+Name[ko]=활동 테스트
+Name[lt]=Veiklų bandymas
+Name[mr]=कार्यपध्दती चाचणी
+Name[nb]=Aktiviteter
+Name[nds]=Aktiviteten-Tests
+Name[nl]=Testen van activiteiten
+Name[nn]=Aktivitetstesting
+Name[pa]=ਐਕਟਵਿਟੀ ਟੈਸਟਿੰਗ
+Name[pl]=Próba działań
+Name[pt]=Teste das actividades
+Name[pt_BR]=Teste de atividades
+Name[ro]=Testare activități
+Name[ru]=Тестирование комнат
+Name[sk]=Testovanie aktivít
+Name[sl]=Preizkušanje dejavnosti
+Name[sr]=Испробавање активности
+Name[sr@ijekavian]=Испробавање активности
+Name[sr@ijekavianlatin]=Isprobavanje aktivnosti
+Name[sr@latin]=Isprobavanje aktivnosti
+Name[sv]=Aktivitetstestning
+Name[tr]=Etkinlik sınaması
+Name[uk]=Тестування просторів дій
+Name[x-test]=xxActivities testingxx
+Name[zh_CN]=活动测试
+Name[zh_TW]=活動測試
+Icon=preferences-system-time
+Type=Service
+X-KDE-ParentApp=
+X-KDE-PluginInfo-Author=Ivan
+X-KDE-PluginInfo-Email=
+X-KDE-PluginInfo-License=GPL
+X-KDE-PluginInfo-Name=org.kde.listactivitiestest
+X-KDE-PluginInfo-Version=1.0
+X-KDE-PluginInfo-Website=plasma.kde.org
+X-KDE-ServiceTypes=Plasma/Applet
+X-Plasma-API=declarativeappletscript
+X-Plasma-DefaultSize=640,400
+X-Plasma-MainScript=ui/main.qml
+X-Plasma-RemoteLocation=
+X-KDE-PluginInfo-Category=
diff --git a/tests/imports/plasma-applet-org.kde.listactivitiestest.desktop b/tests/imports/plasma-applet-org.kde.listactivitiestest.desktop
new file mode 100644 (file)
index 0000000..a6813d9
--- /dev/null
@@ -0,0 +1,105 @@
+[Desktop Entry]
+Name=List activities test
+Name[ar]=اختبار سرد الأنشطة
+Name[ca]=Prova de la llista d'activitats
+Name[ca@valencia]=Prova de la llista d'activitats
+Name[cs]=Test seznamu aktivit
+Name[da]=Oplist test af aktiviteter
+Name[de]=Aktivitätentest auflisten
+Name[el]=Λίστα δοκιμών δραστηριοτήτων
+Name[en_GB]=List activities test
+Name[es]=Prueba de listado de actividades
+Name[fi]=Aktiviteettien listaamistesti
+Name[fr]=Test de la liste d'activités
+Name[gd]=Seall deuchainn air na gnìomhachdan
+Name[gl]=Proba da lista de actividades
+Name[he]=בדיקת רשימת פעילויות
+Name[hu]=Aktivitáslista tesztelése
+Name[ia]=Lista essayos de activitates
+Name[is]=Gera lista með prófunum á virknisviðum
+Name[it]=Prova dell'elenco delle attività
+Name[ko]=활동 목록 테스트
+Name[lt]=Veiklų rikiavimo bandymas
+Name[mr]=कार्यपध्दती चाचणी यादी करा
+Name[nb]=List aktivitetsstester
+Name[nds]=Aktiviteten-Oplisttest
+Name[nl]=Lijst maken van activiteitentest
+Name[nn]=Vis oversikt over aktivitetstestar
+Name[pa]=ਐਕਟੀਵਿਟੀ ਟੈਸਟ ਸੂਚੀ
+Name[pl]=Próba wyszczególniania działań
+Name[pt]=Teste da listagem de actividades
+Name[pt_BR]=Teste da lista de atividades
+Name[ru]=Тестирование списка комнат
+Name[sk]=Test zoznamu aktivít
+Name[sl]=Seznam preizkusov dejavnosti
+Name[sr]=Проба набрајања активности
+Name[sr@ijekavian]=Проба набрајања активности
+Name[sr@ijekavianlatin]=Proba nabrajanja aktivnosti
+Name[sr@latin]=Proba nabrajanja aktivnosti
+Name[sv]=Lista aktivitetstester
+Name[tr]=Etkinilk sınamasını listele
+Name[uk]=Тестування списку просторів дій
+Name[x-test]=xxList activities testxx
+Name[zh_CN]=显示活动测试项
+Name[zh_TW]=列出活動測試
+Comment=Strange, but not a Clock
+Comment[ast]=Estrañu, pero nun ye un reló
+Comment[ca]=És estrany, però no és cap rellotge
+Comment[ca@valencia]=És estrany, però no és cap rellotge
+Comment[da]=Mærkelig, men ikke et ur
+Comment[de]=Seltsam, aber keine Uhr
+Comment[el]=Παράξενο, αλλά δεν είναι ρολόι
+Comment[en_GB]=Strange, but not a Clock
+Comment[es]=Es extraño, pero no es un reloj
+Comment[fi]=Outo, mutta ei kello
+Comment[fr]=Étranges, mais ceci n'est pas une horloge
+Comment[gd]=Neònach ach chan e uaireadair a th' ann
+Comment[gl]=Estraño, pero non é un reloxo
+Comment[he]=מוזר, אבל לא שעון
+Comment[hu]=Furcsa, de nem egy óra
+Comment[ia]=Stranie, ma non un horologio
+Comment[is]=Skrýtið, en ekki klukka
+Comment[it]=Stranamente, non un orologio
+Comment[ko]=수상하지만 시계는 아님
+Comment[lt]=Keistas, bet ne laikrodis
+Comment[nb]=Underlig, men ingen klokke
+Comment[nds]=Snaaksch, man keen Klock
+Comment[nl]=Vreemd, maar geen klok
+Comment[nn]=Merkeleg, men ikkje ei klokke
+Comment[pa]=ਅਜੀਬ, ਪਰ ਇਹ ਘੜੀ ਨਹੀਂ
+Comment[pl]=Dziwne, ale nie Zegar
+Comment[pt]=Estranho, Mas Não é um Relógio
+Comment[pt_BR]=Estranho, mas não é um relógio
+Comment[ro]=Straniu, dar nu e ceas
+Comment[ru]=Странное, но не часы
+Comment[sk]=Zvláštne, ale nie hodiny
+Comment[sl]=Nenavadno, ni pa ura
+Comment[sr]=Чудно, али није сат
+Comment[sr@ijekavian]=Чудно, али није сат
+Comment[sr@ijekavianlatin]=Čudno, ali nije sat
+Comment[sr@latin]=Čudno, ali nije sat
+Comment[sv]=Underlig, men inte en klocka
+Comment[tr]=Garip, ancak Saat değil
+Comment[uk]=Дивно, але не годинник
+Comment[x-test]=xxStrange, but not a Clockxx
+Comment[zh_CN]=奇怪,但不是一个时钟
+Comment[zh_TW]=Strange, but not a Clock(這是什麼奇怪的 comment?)
+
+Icon=preferences-system-time
+Type=Service
+X-KDE-ServiceTypes=Plasma/Applet
+
+X-Plasma-API=declarativeappletscript
+X-Plasma-MainScript=ui/main.qml
+X-Plasma-DefaultSize=250,250
+
+X-KDE-PluginInfo-Author=Ivan
+X-KDE-PluginInfo-Email=
+X-KDE-PluginInfo-Name=org.kde.listactivitiestest
+X-KDE-PluginInfo-Version=1.0
+X-KDE-PluginInfo-Website=http://userbase.kde.org/Plasma/Clocks
+X-KDE-PluginInfo-Category=Date and Time
+X-KDE-PluginInfo-Depends=
+X-KDE-PluginInfo-License=GPL
+X-KDE-PluginInfo-EnabledByDefault=true
+
diff --git a/tests/imports/resources.qml b/tests/imports/resources.qml
new file mode 100644 (file)
index 0000000..7b8a867
--- /dev/null
@@ -0,0 +1,209 @@
+/*
+ * Copyright 2013  Heena Mahour <heena393@gmail.com>
+ * Copyright 2013 Sebastian Kügler <sebas@kde.org>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+import QtQuick 2.0
+import org.kde.plasma.core 2.0 as PlasmaCore
+import org.kde.plasma.components 2.0 as PlasmaComponents
+import org.kde.plasma.extras 2.0 as PlasmaExtras
+import org.kde.activities 0.1 as Activities
+
+Item {
+    id: main
+
+    width: 320
+    height: 320
+
+    Row {
+        id: buttons
+
+        height: 32
+
+        anchors {
+            left:   parent.left
+            right:  parent.right
+            top:    parent.top
+        }
+
+        PlasmaComponents.Button {
+            id: buttonAdd
+            text: "Add"
+
+            onClicked: {
+                modelMain.linkResourceToActivity("/tmp", function () {});
+            }
+
+            width: 64
+        }
+
+        PlasmaComponents.Button {
+            id: buttonRemove
+            text: "Del"
+
+            onClicked: {
+                modelMain.unlinkResourceFromActivity("/tmp", function () {});
+            }
+
+            width: 64
+        }
+
+        PlasmaComponents.Button {
+            id: buttonSortAZ
+            text: "A-Z"
+
+            onClicked: {
+
+                var items = [];
+
+                for (var i = 0; i < modelMain.count(); i++) {
+                    items.push([
+                        modelMain.displayAt(i),
+                        modelMain.resourceAt(i)
+                    ]);
+                }
+
+                items = items.sort(function(left, right) {
+                    return (left[0] < right[0]) ? -1 :
+                           (left[0] > right[0]) ?  1 :
+                                                   0
+                });
+
+                items = items.map(function(item) { return item[1]; });
+
+                modelMain.setOrder(items);
+            }
+
+            width: 64
+        }
+
+        PlasmaComponents.Button {
+            id: buttonSortZA
+            text: "Z-A"
+
+            onClicked: {
+
+                var items = [];
+
+                for (var i = 0; i < modelMain.count(); i++) {
+                    items.push([
+                        modelMain.displayAt(i),
+                        modelMain.resourceAt(i)
+                    ]);
+                }
+
+                items = items.sort(function(left, right) {
+                    return (left[0] < right[0]) ?  1 :
+                           (left[0] > right[0]) ? -1 :
+                                                   0
+                });
+
+                items = items.map(function(item) { return item[1]; });
+
+                modelMain.setOrder(items);
+            }
+
+            width: 64
+        }
+    }
+
+    ListView {
+        id: list
+
+        anchors {
+            left: parent.left
+            right: parent.right
+            bottom: parent.bottom
+
+            top: buttons.bottom
+
+        }
+
+        property int minimumWidth: 320
+        property int minimumHeight: 320 // theme.mSize(theme.defaultFont).height * 14
+        property int implicitWidth: minimumWidth * 1.5
+        property int implicitHeight: minimumHeight * 1.5
+
+        model: modelMain
+
+        Activities.ResourceModel {
+            id: modelMain
+            shownAgents: "org.kde.plasma.kickoff"
+            shownActivities: ":global,:current"
+        }
+
+        add: Transition {
+            NumberAnimation { properties: "x"; from: 300; duration: 1000 }
+        }
+
+        addDisplaced: Transition {
+           NumberAnimation { properties: "x,y"; duration: 1000 }
+        }
+
+        remove: Transition {
+            NumberAnimation { properties: "x"; to: 300; duration: 1000 }
+        }
+
+        removeDisplaced: Transition {
+            NumberAnimation { properties: "x,y"; duration: 1000 }
+        }
+
+        ListModel {
+            id: modelDummy
+
+            ListElement {
+                name: "Bill Smith"
+                number: "555 3264"
+            }
+            ListElement {
+                name: "John Brown"
+                number: "555 8426"
+            }
+            ListElement {
+                name: "Sam Wise"
+                number: "555 0473"
+            }
+        }
+
+        delegate: Column {
+            height: 48
+            Text {
+                text: display
+                height: 16
+                font.bold: true
+                MouseArea {
+                    anchors.fill: parent
+                    onClicked: {
+                        if (display != "tmp") {
+                            modelMain.linkResourceToActivity("/tmp", function () {});
+                        } else {
+                            modelMain.unlinkResourceFromActivity("/tmp", function () {});
+                        }
+                    }
+                }
+
+            }
+            Text {
+                text: "   icon: " + decoration
+                height: 16
+            }
+            Text {
+                text: "   application: " + agent
+                height: 16
+            }
+
+        }
+    }
+}
diff --git a/tests/slc-interface/CMakeLists.txt b/tests/slc-interface/CMakeLists.txt
new file mode 100644 (file)
index 0000000..fcb5c63
--- /dev/null
@@ -0,0 +1,45 @@
+# vim:set softtabstop=3 shiftwidth=3 tabstop=3 expandtab:
+project (KActivitiesSLCTestApp)
+
+find_package (Qt5 REQUIRED NO_MODULE COMPONENTS Core Gui Widgets)
+
+include_directories (
+   ${KACTIVITIES_CURRENT_ROOT_SOURCE_DIR}/src/
+   ${KACTIVITIES_CURRENT_ROOT_SOURCE_DIR}/autotests/
+   )
+
+set (
+   KActivitiesSLCTestApp_SRCS
+   window.cpp
+   main.cpp
+   )
+
+qt5_add_dbus_interface (
+   KActivitiesSLCTestApp_SRCS
+
+   ${KACTIVITIES_CURRENT_ROOT_SOURCE_DIR}/src/service/plugins/slc/org.kde.ActivityManager.SLC.xml
+   slc_interface
+   )
+
+qt5_wrap_ui(
+   KActivitiesSLCTestApp_SRCS
+   window.ui
+   )
+
+if (NOT WIN32)
+
+   add_executable (
+      KActivitiesSLCTestApp
+      ${KActivitiesSLCTestApp_SRCS}
+      )
+
+   target_link_libraries (
+      KActivitiesSLCTestApp
+      Qt5::Core
+      Qt5::Gui
+      Qt5::Widgets
+      Qt5::DBus
+      KF5::Activities
+      )
+
+endif ()
diff --git a/tests/slc-interface/main.cpp b/tests/slc-interface/main.cpp
new file mode 100644 (file)
index 0000000..be488e5
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ *   Copyright (C) 2015 Ivan Cukic <ivan.cukic(at)kde.org>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License version 2,
+ *   or (at your option) any later version, as published by the Free
+ *   Software Foundation
+ *
+ *   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 <QApplication>
+#include "window.h"
+
+int main(int argc, char *argv[])
+{
+    QApplication app(argc, argv);
+
+    Window w;
+    w.show();
+
+    // ResultSet results(UsedResources | Agent{"gvim"});
+    //
+    // int count = 20;
+    // for (const auto& result: results) {
+    //     qDebug() << "Result:" << result.title << result.resource;
+    //     if (count -- == 0) break;
+    // }
+    //
+    // ResultModel model(UsedResources | Agent{"gvim"});
+    // model.setItemCountLimit(50);
+    //
+    // QListView view;
+    // view.setModel(&model);
+    //
+    // view.show();
+
+    return app.exec();
+}
+
diff --git a/tests/slc-interface/window.cpp b/tests/slc-interface/window.cpp
new file mode 100644 (file)
index 0000000..2703fa9
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ *   Copyright (C) 2015 Ivan Cukic <ivan.cukic(at)kde.org>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License version 2,
+ *   or (at your option) any later version, as published by the Free
+ *   Software Foundation
+ *
+ *   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 "window.h"
+
+#include "ui_window.h"
+
+#include <QDBusConnection>
+
+Window::Window()
+    : ui(new Ui::MainWindow())
+    , slc(new org::kde::ActivityManager::SLC(
+            "org.kde.ActivityManager",
+            "/SLC",
+            QDBusConnection::sessionBus(),
+            this))
+{
+    ui->setupUi(this);
+
+    connect(slc,  &org::kde::ActivityManager::SLC::focusChanged,
+            this, &Window::focusChanged);
+}
+
+Window::~Window()
+{
+    delete ui;
+}
+
+void Window::focusChanged(const QString &uri, const QString &mimetype,
+                          const QString &title)
+{
+    Q_UNUSED(mimetype);
+    Q_UNUSED(title);
+    ui->textCurrentResource->setText(uri);
+
+}
+
diff --git a/tests/slc-interface/window.h b/tests/slc-interface/window.h
new file mode 100644 (file)
index 0000000..c8f5df2
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ *   Copyright (C) 2013 - 2016 by Ivan Cukic <ivan.cukic(at)kde.org>
+ *
+ *   This library is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU Lesser General Public
+ *   License as published by the Free Software Foundation; either
+ *   version 2.1 of the License, or (at your option) version 3, or any
+ *   later version accepted by the membership of KDE e.V. (or its
+ *   successor approved by the membership of KDE e.V.), which shall
+ *   act as a proxy defined in Section 6 of version 3 of the license.
+ *
+ *   This library is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *   Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this library.
+ *   If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <QMainWindow>
+#include "slc_interface.h"
+
+namespace Ui {
+    class MainWindow;
+}
+
+class Window: public QMainWindow {
+    Q_OBJECT
+
+public:
+    Window();
+    ~Window();
+
+private Q_SLOTS:
+    void focusChanged(const QString &uri, const QString &mimetype, const QString &title);
+
+private:
+    Ui::MainWindow *ui;
+    org::kde::ActivityManager::SLC *slc;
+};
+
diff --git a/tests/slc-interface/window.ui b/tests/slc-interface/window.ui
new file mode 100644 (file)
index 0000000..35ddc9d
--- /dev/null
@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MainWindow</class>
+ <widget class="QMainWindow" name="MainWindow">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>474</width>
+    <height>74</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>MainWindow</string>
+  </property>
+  <widget class="QWidget" name="centralwidget">
+   <layout class="QHBoxLayout" name="horizontalLayout">
+    <item>
+     <widget class="QLabel" name="icon">
+      <property name="sizePolicy">
+       <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+        <horstretch>0</horstretch>
+        <verstretch>0</verstretch>
+       </sizepolicy>
+      </property>
+      <property name="minimumSize">
+       <size>
+        <width>64</width>
+        <height>64</height>
+       </size>
+      </property>
+      <property name="text">
+       <string/>
+      </property>
+     </widget>
+    </item>
+    <item>
+     <layout class="QFormLayout" name="formLayout">
+      <item row="0" column="0">
+       <widget class="QLabel" name="labelCurrentResource">
+        <property name="text">
+         <string>Resource</string>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="1">
+       <widget class="QLabel" name="textCurrentResource">
+        <property name="text">
+         <string>...</string>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="0">
+       <widget class="QLabel" name="labelCurrentResourceTitle">
+        <property name="text">
+         <string>Title</string>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="1">
+       <widget class="QLabel" name="textCurrentResourceTitle">
+        <property name="text">
+         <string>...</string>
+        </property>
+       </widget>
+      </item>
+      <item row="2" column="0">
+       <widget class="QLabel" name="labelCurrentResourceMimetype">
+        <property name="text">
+         <string>Mimetype</string>
+        </property>
+       </widget>
+      </item>
+      <item row="2" column="1">
+       <widget class="QLabel" name="textCurrentResourceMimetype">
+        <property name="text">
+         <string>...</string>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </item>
+   </layout>
+  </widget>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/vim-extrarc b/vim-extrarc
new file mode 100644 (file)
index 0000000..ea92437
--- /dev/null
@@ -0,0 +1,12 @@
+
+set makeprg=OBJ_REPLACEMENT='s=src=build-clang='\ makeobj
+
+imap <f8> <esc>:SlimuxShellRun make && ./autotests/stats/KActivitiesStatsTest ResultWatcher<cr>
+map <f8> <esc>:SlimuxShellRun make && ./autotests/stats/KActivitiesStatsTest ResultWatcher<cr>
+
+let g:ctrlpswitcher_project_sources = expand('<sfile>:p:h')."/src"
+let g:ctrlpswitcher_mode = 1
+
+set foldmethod=marker
+set foldmarker=//_,//^
+