From: Maximiliano Curia Date: Fri, 17 Aug 2018 14:18:01 +0000 (+0100) Subject: Import kactivities-kf5_5.49.0.orig.tar.xz X-Git-Tag: archive/raspbian/6.2.4-2+rpi1~1^2^2^2^2^2^2^2^2^2^2^2^2^2^2^2^2^2^2^2^2^2^2^2^2^2^2^2~2 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=79be798e4842dbba5dfb7d99c0957742d7f57107;p=plasma-activities.git Import kactivities-kf5_5.49.0.orig.tar.xz [dgit import orig kactivities-kf5_5.49.0.orig.tar.xz] --- 79be798e4842dbba5dfb7d99c0957742d7f57107 diff --git a/.arcconfig b/.arcconfig new file mode 100644 index 0000000..103511e --- /dev/null +++ b/.arcconfig @@ -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 index 0000000..54db9fa --- /dev/null +++ b/.clang-format @@ -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 index 0000000..a94adba --- /dev/null +++ b/.gitignore @@ -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 index 0000000..fb52c7b --- /dev/null +++ b/.videproject/project.conf @@ -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='' +MakeProject='' +RunProject='' + +[Grep] +Root=. + +[Find] +Root=. + +[QuickBrowser] +iNotifyEnabled=0 +Root=. +TagsSystem=ctags diff --git a/.videproject/vimrc b/.videproject/vimrc new file mode 100644 index 0000000..a8dbe5c --- /dev/null +++ b/.videproject/vimrc @@ -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 index 0000000..30a683c --- /dev/null +++ b/.vim-template:cpp @@ -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 . + */ + +#include "%FILE%.h" + +namespace KActivities { + +%HERE% + +} // namespace KActivities + diff --git a/.vim-template:h b/.vim-template:h new file mode 100644 index 0000000..84eddbb --- /dev/null +++ b/.vim-template:h @@ -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 . + */ + +#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 index 0000000..37241ce --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,118 @@ +# vim:set softtabstop=3 shiftwidth=3 tabstop=3 expandtab: + +cmake_minimum_required(VERSION 3.0) + +# KDE Frameworks +set(KF5_VERSION "5.49.0") # handled by release scripts +set(KF5_DEP_VERSION "5.49.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.8.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_plasma [options]' there." + ) +endif () + +set (KACTIVITIES_CURRENT_ROOT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) + +# Extra CMake stuff +include(FeatureSummary) +find_package(ECM 5.49.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} ${ECM_KDE_MODULE_DIR}) + +include (KDEInstallDirs) +include (KDECMakeSettings) +include (KDECompilerSettings NO_POLICY_SCOPE) +include (GenerateExportHeader) +include (ECMGenerateHeaders) +include (ECMAddQch) + +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_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 + ) + +# Write out the features +feature_summary (WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) + diff --git a/COPYING b/COPYING new file mode 100644 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. + + + Copyright (C) + + 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. + + , 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 index 0000000..5bc8fb2 --- /dev/null +++ b/COPYING.LGPL-2 @@ -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. + + 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. + + 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. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also 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. + + 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. + + 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. + + 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 + + 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. + + + Copyright (C) + + 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. + + , 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 index 0000000..4362b49 --- /dev/null +++ b/COPYING.LIB @@ -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. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + 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. + + + Copyright (C) + + 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. + + , 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 index 0000000..25291c9 --- /dev/null +++ b/KF5ActivitiesConfig.cmake.in @@ -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 index 0000000..51d257d --- /dev/null +++ b/MAINTAINER @@ -0,0 +1,3 @@ + +Current kactivities maintainer is: Ivan Čukić + diff --git a/README b/README new file mode 100644 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 index 0000000..575d704 --- /dev/null +++ b/README.developers @@ -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 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 index 0000000..6b7155b --- /dev/null +++ b/README.packagers @@ -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 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 index 0000000..c2d5775 --- /dev/null +++ b/autotests/CMakeLists.txt @@ -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 index 0000000..5a089e2 --- /dev/null +++ b/autotests/common/test.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2013, 2014, 2015 Ivan Cukic + * + * 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 +#include + +#include "common/dbus/common.h" + +Test::Test(QObject *parent) + : QObject(parent) +{ + +} + +bool Test::inEmptySession() +{ + QStringList services = + QDBusConnection::sessionBus().interface()->registeredServiceNames(); + + foreach (const QString & service, services) { + bool kdeServiceAndNotKAMD = + service.startsWith(QStringLiteral("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 index 0000000..33dc6f3 --- /dev/null +++ b/autotests/common/test.h @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2013, 2014, 2015 Ivan Cukic + * + * 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 +#include +#include +#include +#include +#include + +class Test: public QObject { + Q_OBJECT +public: + Test(QObject *parent = nullptr); + +protected: + enum WhenToFail { + DontFail = 0, + FailIfTrue = 1, + FailIfFalse = 2 + }; + + template + void continue_future(const QFuture<_ReturnType> &future, + _Continuation &&continuation) + { + if (!future.isFinished()) { + auto watcher = new QFutureWatcher(); + QObject::connect(watcher, &QFutureWatcherBase::finished, + watcher, + [=] { + continuation(watcher->result()); + watcher->deleteLater(); + }, + Qt::QueuedConnection + ); + + watcher->setFuture(future); + + } else { + continuation(future.result()); + + } + } + + template + void continue_future(const QFuture &future, + _Continuation &&continuation) + { + if (!future.isFinished()) { + auto watcher = new QFutureWatcher(); + QObject::connect(watcher, &QFutureWatcherBase::finished, + watcher, + [=] { + continuation(); + watcher->deleteLater(); + }, + Qt::QueuedConnection + ); + + watcher->setFuture(future); + + } else { + continuation(); + + } + } + + template + 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 + 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 + +#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 index 0000000..41b885a --- /dev/null +++ b/autotests/core/CMakeLists.txt @@ -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 index 0000000..6e5a9d0 --- /dev/null +++ b/autotests/core/CleanOnlineTest.cpp @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2013 Ivan Cukic + * + * 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 +#include +#include +#include +#include + +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() == QStringLiteral("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() == QStringLiteral("Renamed activity")); + TEST_WAIT_UNTIL(i2.name() == QStringLiteral("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 index 0000000..6b3feae --- /dev/null +++ b/autotests/core/CleanOnlineTest.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2013 Ivan Cukic + * + * 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 + +#include + +#include + +class CleanOnlineTest : public Test { + Q_OBJECT +public: + CleanOnlineTest(QObject *parent = nullptr); + +private Q_SLOTS: + void testCleanOnlineActivityListing(); + + void cleanupTestCase(); + +private: + QScopedPointer 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 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 activities; + +}; + + +#endif /* CLEANONLINETEST_H */ + diff --git a/autotests/core/OfflineTest.cpp b/autotests/core/OfflineTest.cpp new file mode 100644 index 0000000..4292242 --- /dev/null +++ b/autotests/core/OfflineTest.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2013 Ivan Cukic + * + * 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 +#include +#include +#include +#include + +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 index 0000000..a8be8f5 --- /dev/null +++ b/autotests/core/OfflineTest.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2013 Ivan Cukic + * + * 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 + +#include + +#include + +class OfflineTest : public Test { + Q_OBJECT +public: + OfflineTest(QObject *parent = nullptr); + +private Q_SLOTS: + void initTestCase(); + + void testOfflineActivityListing(); + void testOfflineActivityControl(); + + void cleanupTestCase(); + +private: + + QScopedPointer activities; + +}; + + +#endif /* OFFLINETEST_H */ + diff --git a/autotests/core/Process.cpp b/autotests/core/Process.cpp new file mode 100644 index 0000000..694d15b --- /dev/null +++ b/autotests/core/Process.cpp @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2013 Ivan Cukic + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#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 index 0000000..736dc8d --- /dev/null +++ b/autotests/core/Process.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2013 Ivan Cukic + * + * 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 + +#include + +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 index 0000000..0ac8310 --- /dev/null +++ b/autotests/core/main.cpp @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2013 Ivan Cukic + * + * 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 +#include +#include + +#include + +#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 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 index 0000000..8a787ea --- /dev/null +++ b/contrib/bash/next-activity.sh @@ -0,0 +1,30 @@ +#! /bin/bash +# +# next-activity.sh +# Copyright (C) 2016 Ivan Čukić +# +# 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 index 0000000..ede4fc7 --- /dev/null +++ b/contrib/bash/prev-activity.sh @@ -0,0 +1,30 @@ +#! /bin/bash +# +# next-activity.sh +# Copyright (C) 2016 Ivan Čukić +# +# 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 index 0000000..3377ff3 --- /dev/null +++ b/contrib/commit.sh @@ -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 index 0000000..1c5635d --- /dev/null +++ b/contrib/run-krazy.sh @@ -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 index 0000000..e012cd4 --- /dev/null +++ b/contrib/update-todo.hs @@ -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 index 0000000..6afd3b8 --- /dev/null +++ b/contrib/zsh/kamd-functions @@ -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 index 0000000..1d9d027 --- /dev/null +++ b/docs/Doxyfile.local @@ -0,0 +1 @@ +EXCLUDE_PATTERNS += */service/* */scripts/* */workspace/* diff --git a/metainfo.yaml b/metainfo.yaml new file mode 100644 index 0000000..ea663b4 --- /dev/null +++ b/metainfo.yaml @@ -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 index 0000000..83e4517 --- /dev/null +++ b/src/CMakeLists.txt @@ -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 index 0000000..d0e13be --- /dev/null +++ b/src/cli/CMakeLists.txt @@ -0,0 +1,36 @@ +# 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 + ) + +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 index 0000000..827818d --- /dev/null +++ b/src/cli/main.cpp @@ -0,0 +1,297 @@ +/* + * Copyright (C) 2016 Ivan Cukic + * + * 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 +#include +#include + +#include + +#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 == "name" ? controller->setActivityName(id, value) : + what == "description" ? controller->setActivityDescription(id, value) : + what == "icon" ? controller->setActivityIcon(id, value) : + QFuture() + ); + + return 3; +} + +DEFINE_COMMAND(activityProperty, 2) +{ + const auto what = args(1); + const auto id = args(2); + + KActivities::Info info(id); + + out << ( + what == "name" ? info.name() : + what == "description" ? info.description() : + what == "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(); + bool found = false; + + for (int i = 0; i < activities.count() - 1; ++i) { + if (activities[i] == currentActivity) { + found = true; + switchToActivity(activities[i + 1]); + break; + } + } + + if (!found) { + switchToActivity(activities[0]); + } + + return 0; +} + +DEFINE_COMMAND(previousActivity, 0) +{ + const auto activities = controller->activities(); + const auto currentActivity = controller->currentActivity(); + bool found = false; + + for (int i = 1; i < activities.count(); ++i) { + if (activities[i] == currentActivity) { + found = true; + switchToActivity(activities[i - 1]); + break; + } + } + + if (!found) { + found = true; + 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] == QStringLiteral("--") + toDashes(#Command)) \ + { \ + argId += 1 + Command##_command({ args, argId })(); \ + } + + if (args.count() <= 1) { + printHelp(); + + } else for (int argId = 1; argId < args.count(); ) { + if (args[argId] == "--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 index 0000000..1e97e92 --- /dev/null +++ b/src/cli/utils.h @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2016 Ivan Čukić + * + * 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 . + */ + +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, "-"); + } + } + + 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 +T awaitFuture(const QFuture &future) +{ + while (!future.isFinished()) { + QCoreApplication::processEvents(); + } + + return future.result(); +} + +void awaitFuture(const QFuture &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()() + diff --git a/src/common/dbus/common.h b/src/common/dbus/common.h new file mode 100644 index 0000000..60d5f66 --- /dev/null +++ b/src/common/dbus/common.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2010 - 2016 by Ivan Cukic + * + * 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 . + */ + +#ifndef DBUS_COMMON_H +#define DBUS_COMMON_H + +#include +#include + +#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 index 0000000..9510b39 --- /dev/null +++ b/src/common/dbus/org.kde.ActivityManager.Activities.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2010 - 2016 by Ivan Cukic + * + * 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 . + */ + +#include "org.kde.ActivityManager.Activities.h" + +#include +#include + +namespace details { + +class ActivityInfoStaticInit { +public: + ActivityInfoStaticInit() + { + qDBusRegisterMetaType(); + qDBusRegisterMetaType(); + } + + 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 index 0000000..94f0fbe --- /dev/null +++ b/src/common/dbus/org.kde.ActivityManager.Activities.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2010 - 2016 by Ivan Cukic + * + * 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 . + */ + +#ifndef KAMD_ACTIVITIES_DBUS_H +#define KAMD_ACTIVITIES_DBUS_H + +#include +#include +#include +#include + +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 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_ACTIVITIES_DBUS_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 index 0000000..8d3e074 --- /dev/null +++ b/src/common/dbus/org.kde.ActivityManager.Activities.xml @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/common/dbus/org.kde.ActivityManager.Application.xml b/src/common/dbus/org.kde.ActivityManager.Application.xml new file mode 100644 index 0000000..fc4a36b --- /dev/null +++ b/src/common/dbus/org.kde.ActivityManager.Application.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/src/common/dbus/org.kde.ActivityManager.Features.xml b/src/common/dbus/org.kde.ActivityManager.Features.xml new file mode 100644 index 0000000..e45f046 --- /dev/null +++ b/src/common/dbus/org.kde.ActivityManager.Features.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/src/common/dbus/org.kde.ActivityManager.Resources.xml b/src/common/dbus/org.kde.ActivityManager.Resources.xml new file mode 100644 index 0000000..5f8e725 --- /dev/null +++ b/src/common/dbus/org.kde.ActivityManager.Resources.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/common/dbus/org.kde.ActivityManager.ResourcesLinking.xml b/src/common/dbus/org.kde.ActivityManager.ResourcesLinking.xml new file mode 100644 index 0000000..9d04cdd --- /dev/null +++ b/src/common/dbus/org.kde.ActivityManager.ResourcesLinking.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/common/dbus/org.kde.ActivityManager.ResourcesScoring.xml b/src/common/dbus/org.kde.ActivityManager.ResourcesScoring.xml new file mode 100644 index 0000000..0190ff7 --- /dev/null +++ b/src/common/dbus/org.kde.ActivityManager.ResourcesScoring.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/imports/CMakeLists.txt b/src/imports/CMakeLists.txt new file mode 100644 index 0000000..2efda2b --- /dev/null +++ b/src/imports/CMakeLists.txt @@ -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 index 0000000..bb33c28 --- /dev/null +++ b/src/imports/README @@ -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 index 0000000..584304a --- /dev/null +++ b/src/imports/activitiesextensionplugin.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2012, 2013, 2014, 2015 Ivan Cukic + * + * 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 + +#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) + +#include + +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(uri, 0, 1, "ActivityModel"); + + qmlRegisterType(uri, 0, 1, "ActivityInfo"); + qmlRegisterType(uri, 0, 1, "ResourceInstance"); + + // This one is removed in favor of KActivities::Stats::ResultModel. + // Subclass it, and make it do what you want. + // qmlRegisterType(uri, 0, 1, "ResourceModel"); +} + diff --git a/src/imports/activitiesextensionplugin.h b/src/imports/activitiesextensionplugin.h new file mode 100644 index 0000000..4b91ea7 --- /dev/null +++ b/src/imports/activitiesextensionplugin.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2011, 2012, 2013, 2014, 2015 by Ivan Cukic + * + * 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_IMPORTS_PLUGIN_H +#define KACTIVITIES_IMPORTS_PLUGIN_H + +#include + +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_IMPORTS_PLUGIN_H + diff --git a/src/imports/activityinfo.cpp b/src/imports/activityinfo.cpp new file mode 100644 index 0000000..144c761 --- /dev/null +++ b/src/imports/activityinfo.cpp @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2012, 2013, 2014, 2015 Ivan Cukic + * + * 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 == ":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() : ""; \ + } \ + \ + 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() : ""; +} + +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 index 0000000..f85d627 --- /dev/null +++ b/src/imports/activityinfo.h @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2012, 2013, 2014, 2015 Ivan Cukic + * + * 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 + +// STL +#include + +// Local +#include +#include + +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 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 index 0000000..48e999c --- /dev/null +++ b/src/imports/activitymodel.cpp @@ -0,0 +1,654 @@ +/* + * Copyright (C) 2012, 2013, 2014, 2015 Ivan Cukic + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +// KDE +#include +#include +#include + +// Boost +#include +#include +#include +#include + +// 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 the activity has a desired state. + * If the state is 0, returns true + */ + template + 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 + static inline + boost::optional< + std::pair + > + 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 + static inline + void emitActivityUpdated(_Model *model, + const _Container &container, + QObject *activityInfo, int role) + { + const auto activity = static_cast (activityInfo); + emitActivityUpdated(model, container, activity->id(), role); + } + + /** + * Notifies the model that an activity was updated + */ + template + 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 {role, ActivityModel::ActivityIcon} : + QVector {role} + ); + } + } + + class BackgroundCache { + public: + BackgroundCache() + : initialized(false) + , plasmaConfig("plasma-org.kde.plasma.desktop-appletsrc") + { + using namespace std::placeholders; + + const auto 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 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] != '#') 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 forActivity; + QList 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 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(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(',')) { + if (state == QStringLiteral("Running")) { + m_shownStates.insert(Running); + + } else if (state == QStringLiteral("Starting")) { + m_shownStates.insert(Starting); + + } else if (state == QStringLiteral("Stopped")) { + m_shownStates.insert(Stopped); + + } else if (state == QStringLiteral("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() ? "preferences-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 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 Controller::setCurrentActivity(id) +void ActivityModel::setCurrentActivity(const QString &id, + const QJSValue &callback) +{ + continue_with(m_service.setCurrentActivity(id), callback); +} + +// QFuture Controller::addActivity(name) +void ActivityModel::addActivity(const QString &name, const QJSValue &callback) +{ + continue_with(m_service.addActivity(name), callback); +} + +// QFuture Controller::removeActivity(id) +void ActivityModel::removeActivity(const QString &id, const QJSValue &callback) +{ + continue_with(m_service.removeActivity(id), callback); +} + +// QFuture Controller::stopActivity(id) +void ActivityModel::stopActivity(const QString &id, const QJSValue &callback) +{ + continue_with(m_service.stopActivity(id), callback); +} + +// QFuture 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 index 0000000..baa4501 --- /dev/null +++ b/src/imports/activitymodel.h @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2012, 2013, 2014 Ivan Cukic + * + * 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 +#include +#include + +// STL and Boost +#include +#include + +// Local +#include +#include +#include + +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 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 m_shownStates; + QString m_shownStatesString; + + typedef std::shared_ptr InfoPtr; + + struct InfoPtrComparator { + bool operator() (const InfoPtr& left, const InfoPtr& right) const + { + const QString &leftName = left->name().toLower(); + const QString &rightName = right->name().toLower(); + + return + (leftName < rightName) || + (leftName == rightName && left->id() < right->id()); + } + }; + + boost::container::flat_set m_knownActivities; + boost::container::flat_set 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 index 0000000..9138785 --- /dev/null +++ b/src/imports/qmldir @@ -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 index 0000000..a6e2251 --- /dev/null +++ b/src/imports/resourceinstance.cpp @@ -0,0 +1,140 @@ +/*************************************************************************** + * Copyright 2011-2015 Marco Martin * + * * + * 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 +#include + +#include +#include + +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('/')) { + 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 index 0000000..e779c2a --- /dev/null +++ b/src/imports/resourceinstance.h @@ -0,0 +1,100 @@ +/*************************************************************************** + * Copyright 2011-2015 Marco Martin * + * * + * 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 +#include + +// STL +#include + +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 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 index 0000000..88c31e1 --- /dev/null +++ b/src/imports/resourcemodel.cpp @@ -0,0 +1,698 @@ +/* + * Copyright (C) 2012, 2013, 2014 Ivan Cukic + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +// KDE +#include +#include +#include +#include +#include + +// STL and Boost +#include +#include +#include +#include +#include +#include + +// 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 self() + { + static std::weak_ptr s_instance; + static std::mutex singleton; + + std::lock_guard 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 ResourceModel::roleNames() const +{ + return { + { Qt::DisplayRole, "display" }, + { Qt::DecorationRole, "decoration" }, + { ResourceRole, "uri" }, + { AgentRole, "agent" }, + { ActivityRole, "activity" }, + { DescriptionRole, "subtitle" } + }; +} + +template +inline QStringList validateList( + const QString &values, Validator validator) +{ + using boost::adaptors::filtered; + using kamd::utils::as_collection; + + auto result + = as_collection(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 = QStringLiteral("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(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(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 Resource; + QList 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; + + foreach (const auto &resource, 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(QStringLiteral("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 index 0000000..ec10093 --- /dev/null +++ b/src/imports/resourcemodel.h @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2012, 2013, 2014 Ivan Cukic + * + * 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 +#include +#include +#include +#include + +// KDE +#include + +// STL and Boost +#include +#include + +// Local +#include +#include +#include + +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: + ResourceModel(QObject *parent = 0); + virtual ~ResourceModel(); + + enum Roles { + ResourceRole = Qt::UserRole, + ActivityRole = Qt::UserRole + 1, + AgentRole = Qt::UserRole + 2, + DescriptionRole = Qt::UserRole + 3 + }; + + QHash 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 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 index 0000000..30aa01b --- /dev/null +++ b/src/kactivities-features.h.cmake @@ -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 index 0000000..827aff2 --- /dev/null +++ b/src/lib/CMakeLists.txt @@ -0,0 +1,178 @@ +# 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 + debug_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 + ) + +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 +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 index 0000000..bfd2d9a --- /dev/null +++ b/src/lib/activitiescache_p.cpp @@ -0,0 +1,318 @@ +/* + * Copyright (C) 2013 - 2016 by Ivan Cukic + * + * 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 . + */ + +#include "activitiescache_p.h" +#include "manager_p.h" + +#include +#include + +#include + +#include "mainthreadexecutor_p.h" + +namespace KActivities { + +static QString nulluuid = QStringLiteral("00000000-0000-0000-0000-000000000000"); + +using kamd::utils::Mutable; + +std::shared_ptr ActivitiesCache::self() +{ + static std::weak_ptr s_instance; + static std::mutex singleton; + std::lock_guard 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(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 +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(watcher, &ActivitiesCache::setActivityInfo); +} + +void ActivitiesCache::setAllActivitiesFromReply(QDBusPendingCallWatcher *watcher) +{ + // qDebug() << "reply..."; + passInfoFromReply(watcher, &ActivitiesCache::setAllActivities); +} + +void ActivitiesCache::setCurrentActivityFromReply(QDBusPendingCallWatcher *watcher) +{ + // qDebug() << "reply..."; + passInfoFromReply(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(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(); + + ActivityInfoList activities = _activities; + + foreach (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 index 0000000..04542e7 --- /dev/null +++ b/src/lib/activitiescache_p.h @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2013 - 2016 by Ivan Cukic + * + * 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 . + */ + +#ifndef ACTIVITIES_CACHE_P_H +#define ACTIVITIES_CACHE_P_H + +#include + +#include + +#include +#include + +#include "activities_interface.h" +#include "consumer.h" + +namespace KActivities { + +class ActivitiesCache : public QObject { + Q_OBJECT + +public: + static std::shared_ptr 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 + 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 + inline typename kamd::utils::ptr_to::type + getInfo(const QString &id) + { + const auto where = find(id); + + if (where != m_activities.end()) { + return &(*where); + } + + return nullptr; + } + + template + void onCallFinished(QDBusPendingCall &call, TargetSlot slot) { + auto watcher = new QDBusPendingCallWatcher(call, this); + + connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher *)), + this, slot); + } + + + ActivitiesCache(); + + QList 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 index 0000000..ab888d7 --- /dev/null +++ b/src/lib/activitiesmodel.cpp @@ -0,0 +1,445 @@ +/* + * Copyright (C) 2012 - 2016 Ivan Cukic + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +// Local +#include "utils/remove_if.h" + +namespace KActivities { + +namespace Private { + template + 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 the activity has a desired state. + * If the state is 0, returns true + */ + template + 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 + 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 + 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 {role, ActivitiesModel::ActivityIconSource} : + QVector {role} + ); + } + } + + /** + * Notifies the model that an activity was updated + */ + template + inline + void emitActivityUpdated(_Model *model, + const _Container &container, + QObject *activityInfo, int role) + { + const auto activity = static_cast (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 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 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(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); + const auto found = std::get<2>(_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 &states) +{ + d->shownStates = states; + + d->replaceActivities(d->activities.activities()); + + emit shownStatesChanged(states); +} + +QVector 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() ? "preferences-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 index 0000000..8006212 --- /dev/null +++ b/src/lib/activitiesmodel.h @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2012, 2013, 2014 Ivan Cukic + * + * 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 +#include + +// STL +#include + +// 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 shownStates READ shownStates WRITE setShownStates NOTIFY shownStatesChanged) + +public: + explicit ActivitiesModel(QObject *parent = nullptr); + + /** + * Constructs the model and sets the shownStates + */ + ActivitiesModel(QVector 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 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 &shownStates); + + /** + * The model can filter the list of activities based on their state. + * This method returns which states are currently shown. + */ + QVector shownStates() const; + +Q_SIGNALS: + void shownStatesChanged(const QVector &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 index 0000000..4e0134c --- /dev/null +++ b/src/lib/activitiesmodel_p.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2016 Ivan Čukić + * + * 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 . + */ + +#ifndef ACTIVITIES_ACTIVITYMODEL_P_H +#define ACTIVITIES_ACTIVITYMODEL_P_H + +#include "activitiesmodel.h" + +#include "consumer.h" + +#include "utils/qflatset.h" + +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 shownStates; + + typedef std::shared_ptr InfoPtr; + + struct InfoPtrComparator { + bool operator() (const InfoPtr& left, const InfoPtr& right) const + { + const QString &leftName = left->name().toLower(); + const QString &rightName = right->name().toLower(); + + return + (leftName < rightName) || + (leftName == rightName && left->id() < right->id()); + } + }; + + QFlatSet knownActivities; + QFlatSet 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_ACTIVITYMODEL_P_H + diff --git a/src/lib/consumer.cpp b/src/lib/consumer.cpp new file mode 100644 index 0000000..71b11d6 --- /dev/null +++ b/src/lib/consumer.cpp @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2010 - 2016 by Ivan Cukic + * + * 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 . + */ + +#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()); + + foreach (const auto & info, 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()); + + foreach (const auto & info, d->cache->m_activities) { + result << info.id; + } + + return result; +} + +QStringList Consumer::runningActivities() const +{ + QStringList result; + + result.reserve(d->cache->m_activities.size()); + + foreach (const auto & info, 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 index 0000000..79fd1d3 --- /dev/null +++ b/src/lib/consumer.h @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2010 - 2016 by Ivan Cukic + * + * 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 . + */ + +#ifndef ACTIVITIES_CONSUMER_H +#define ACTIVITIES_CONSUMER_H + +#include +#include +#include +#include + +#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 synchronise 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 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 index 0000000..54eeedb --- /dev/null +++ b/src/lib/consumer_p.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2010 - 2016 by Ivan Cukic + * + * 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 . + */ + +#ifndef ACTIVITIES_CONSUMER_P_H +#define ACTIVITIES_CONSUMER_P_H + +#include "consumer.h" + +#include + +#include + +#include "activitiescache_p.h" + +namespace KActivities { + +class ConsumerPrivate : public QObject { + Q_OBJECT + +public: + ConsumerPrivate(); + + std::shared_ptr 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 index 0000000..c695c66 --- /dev/null +++ b/src/lib/controller.cpp @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2010 - 2016 by Ivan Cukic + * + * 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 . + */ + +#include "controller.h" +#include "consumer_p.h" +#include "manager_p.h" + +#include "utils/dbusfuture_p.h" + +#include + +namespace KActivities { + +Controller::Controller(QObject *parent) + : Consumer(parent) +{ +} + +Controller::~Controller() +{ +} + +#define CREATE_SETTER(What) \ + QFuture Controller::setActivity##What(const QString &id, \ + const QString &value) \ + { \ + return Manager::isServiceRunning() \ + ? DBusFuture::asyncCall( \ + Manager::activities(), \ + QString::fromLatin1("SetActivity" #What), id, value) \ + : DBusFuture::fromVoid(); \ + } + +CREATE_SETTER(Name) +CREATE_SETTER(Description) +CREATE_SETTER(Icon) + +#undef CREATE_SETTER + +QFuture 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( + Manager::activities(), QStringLiteral("SetCurrentActivity"), id) + : + DBusFuture::fromValue(false); +} + +QFuture 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( + Manager::activities(), QStringLiteral("AddActivity"), name) + : + DBusFuture::fromValue(QString()); +} + +QFuture 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( + Manager::activities(), QStringLiteral("RemoveActivity"), id) + : + DBusFuture::fromVoid(); +} + +QFuture 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( + Manager::activities(), QStringLiteral("StopActivity"), id) + : + DBusFuture::fromVoid(); +} + +QFuture 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( + 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 index 0000000..b254efe --- /dev/null +++ b/src/lib/controller.h @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2010 - 2016 by Ivan Cukic + * + * 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 . + */ + +#ifndef ACTIVITIES_CONTROLLER_H +#define ACTIVITIES_CONTROLLER_H + +#include +#include +#include +#include + +#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 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 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 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 setCurrentActivity(const QString &id); + + /** + * Adds a new activity + * @param name name of the activity + * @returns id of the newly created activity + */ + QFuture addActivity(const QString &name); + + /** + * Removes the specified activity + * @param id id of the activity to delete + */ + QFuture removeActivity(const QString &id); + + /** + * Stops the activity + * @param id id of the activity to stop + */ + QFuture stopActivity(const QString &id); + + /** + * Starts the activity + * @param id id of the activity to start + */ + QFuture startActivity(const QString &id); + +private: + // const QScopedPointer d; +}; + +} // namespace KActivities + +#endif // ACTIVITIES_CONTROLLER_H diff --git a/src/lib/debug_p.cpp b/src/lib/debug_p.cpp new file mode 100644 index 0000000..0d8ec61 --- /dev/null +++ b/src/lib/debug_p.cpp @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2013 - 2016 by Ivan Cukic + * + * 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 . + */ + +#include "debug_p.h" + +// logging category for this framework, default: log stuff >= warning +Q_LOGGING_CATEGORY(KAMD_CORELIB, "org.kde.kactivities.lib.core", QtWarningMsg) diff --git a/src/lib/debug_p.h b/src/lib/debug_p.h new file mode 100644 index 0000000..2af10cf --- /dev/null +++ b/src/lib/debug_p.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2013 - 2016 by Ivan Cukic + * + * 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 . + */ + +#ifndef ACTIVITIES_DEBUG_P_H +#define ACTIVITIES_DEBUG_P_H + +#include + +Q_DECLARE_LOGGING_CATEGORY(KAMD_CORELIB) + +#endif /* ACTIVITIES_DEBUG_P_H */ + diff --git a/src/lib/info.cpp b/src/lib/info.cpp new file mode 100644 index 0000000..3166055 --- /dev/null +++ b/src/lib/info.cpp @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2010 - 2016 by Ivan Cukic + * + * 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 . + */ + +#include "info.h" +#include "info_p.h" +#include "manager_p.h" + +#include "utils/dbusfuture_p.h" + +#include + +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(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 ¤tActivity) +{ + 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); +} + +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 index 0000000..57727b3 --- /dev/null +++ b/src/lib/info.h @@ -0,0 +1,251 @@ +/* + * Copyright (C) 2010 - 2016 by Ivan Cukic + * + * 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 . + */ + +#ifndef ACTIVITIES_INFO_H +#define ACTIVITIES_INFO_H + +#include +#include +#include +#include + +#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 asynchronous. It will return before the + * resource is actually linked to the activity. + */ + // QFuture linkResource(const QString &resourceUri); + + /** + * Unlinks the specified resource from the activity + * @param resourceUri resource URI + * @note This method is asynchronous. It will return before the + * resource is actually unlinked from the activity. + */ + // QFuture 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 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 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 index 0000000..d9b24c1 --- /dev/null +++ b/src/lib/info_p.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2010 - 2016 by Ivan Cukic + * + * 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 . + */ + +#ifndef KACTIVITIESINFO_P_H +#define KACTIVITIESINFO_P_H + +#include "info.h" +#include +#include + +#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 ¤tActivity); + + Info *const q; + std::shared_ptr 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 index 0000000..74171b9 --- /dev/null +++ b/src/lib/libKActivities.pc.cmake @@ -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_LIB_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 index 0000000..40905c1 --- /dev/null +++ b/src/lib/mainthreadexecutor_p.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2014 - 2016 by Ivan Cukic + * + * 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 . + */ + +#include "mainthreadexecutor_p.h" + +#include + +#include +#include +#include +#include + +namespace KActivities { + +namespace detail { + +MainThreadExecutor::MainThreadExecutor(std::function &&f) + : m_function(std::forward>(f)) +{ +} + +void MainThreadExecutor::start() +{ + m_function(); + deleteLater(); +} + +} // namespace detail + +void runInMainThread(std::function &&f) +{ + static auto mainThread = QCoreApplication::instance()->thread(); + + if (QThread::currentThread() == mainThread) { + f(); + + } else { + auto executor = new detail::MainThreadExecutor(std::forward>(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 index 0000000..e4ed5b4 --- /dev/null +++ b/src/lib/mainthreadexecutor_p.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2014 - 2016 by Ivan Cukic + * + * 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 . + */ + +#ifndef ACTIVITIES_MAINTHREADEXECUTOR_P +#define ACTIVITIES_MAINTHREADEXECUTOR_P + +#include + +#include + +namespace KActivities { + +namespace detail { + class MainThreadExecutor: public QObject { + Q_OBJECT + + public: + MainThreadExecutor(std::function &&f); + + Q_INVOKABLE void start(); + + private: + std::function m_function; + }; +} // namespace detail + +void runInMainThread(std::function &&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 index 0000000..b4967da --- /dev/null +++ b/src/lib/manager_p.cpp @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2010 - 2016 by Ivan Cukic + * + * 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 . + */ + +#include "manager_p.h" + +#include + +#include +#include +#include +#include + +#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 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 &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('.'); + QList 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]) + '.' + + QString::number(requiredVersion[1]) + '.' + + 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 index 0000000..9ad3aea --- /dev/null +++ b/src/lib/manager_p.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2010 - 2016 by Ivan Cukic + * + * 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 . + */ + +#ifndef ACTIVITIES_MANAGER_P +#define ACTIVITIES_MANAGER_P + +#include + +#include "application_interface.h" +#include "activities_interface.h" +#include "resources_interface.h" +#include "resources_linking_interface.h" +#include "features_interface.h" + +#include + +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 index 0000000..1b8f7ec --- /dev/null +++ b/src/lib/resourceinstance.cpp @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2011 - 2016 by Ivan Cukic + * + * 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 . + */ + +#include "resourceinstance.h" +#include "manager_p.h" + +#include +#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 index 0000000..77591f4 --- /dev/null +++ b/src/lib/resourceinstance.h @@ -0,0 +1,203 @@ +/* + * Copyright (C) 2011 - 2016 by Ivan Cukic + * + * 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 . + */ + +#ifndef ACTIVITIES_RESOURCEINSTANCE_H +#define ACTIVITIES_RESOURCEINSTANCE_H + +#include +#include + +#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 d; +}; +} + +#endif // ACTIVITIES_RESOURCEINSTANCE_H diff --git a/src/lib/version.cpp b/src/lib/version.cpp new file mode 100644 index 0000000..6c0f960 --- /dev/null +++ b/src/lib/version.cpp @@ -0,0 +1,49 @@ +/* + * Copyright 2008 by Aaron Seigo + * + * 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 index 0000000..8f2922c --- /dev/null +++ b/src/lib/version.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2008 - 2016 by Aaron Seigo + * + * 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 */ + +#include "kactivities_export.h" +#include + +#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 index 0000000..dcaa055 --- /dev/null +++ b/src/utils/continue_with.h @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2014 - 2016 by Ivan Cukic + * + * 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 . + */ + +#ifndef UTILS_CONTINUE_WITH_H +#define UTILS_CONTINUE_WITH_H + +#include +#include +#include + +#include "utils/optional_view.h" +// #include + +#ifdef ENABLE_QJSVALUE_CONTINUATION +#include +#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 + 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 &future, QJSValue continuation) + { + Q_UNUSED(future); + auto result = continuation.call({}); + if (result.isError()) { + qWarning() << "Handler returned this error: " << result.toString(); + } + } +#endif + + template + inline void test_continuation(_Continuation &&continuation) + { + Q_UNUSED(continuation); + } + + template + 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 + inline void pass_value(_Continuation &&continuation) + { + continuation(); + } + +} //^ namespace detail + +template +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 index 0000000..7bb2e44 --- /dev/null +++ b/src/utils/dbusfuture_p.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2012 - 2016 by Ivan Cukic + * + * 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 . + */ + +#include "dbusfuture_p.h" + +namespace DBusFuture { + +namespace detail { //_ + +template <> +void DBusCallFutureInterface::callFinished() +{ + deleteLater(); + + // qDebug() << "This is call end"; + + this->reportFinished(); +} + +ValueFutureInterface::ValueFutureInterface() +{ +} + +QFuture ValueFutureInterface::start() +{ + auto future = this->future(); + + this->reportFinished(); + + deleteLater(); + + return future; +} + +} //^ namespace detail + +QFuture fromVoid() +{ + using namespace detail; + + auto valueFutureInterface = new ValueFutureInterface(); + + return valueFutureInterface->start(); +} + +} // namespace DBusFuture + diff --git a/src/utils/dbusfuture_p.h b/src/utils/dbusfuture_p.h new file mode 100644 index 0000000..beb6505 --- /dev/null +++ b/src/utils/dbusfuture_p.h @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2013 - 2016 by Ivan Cukic + * + * 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 . + */ + +#ifndef ACTIVITIES_DBUSFUTURE_P_H +#define ACTIVITIES_DBUSFUTURE_P_H + +#include +#include +#include +#include +#include +#include +#include + +#include "debug_p.h" + +namespace DBusFuture { + +namespace detail { //_ + +template +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 +void DBusCallFutureInterface<_Result>::callFinished() +{ + deleteLater(); + + if (!reply.isError()) { + this->reportResult(reply.value()); + } + + this->reportFinished(); +} + +template <> +void DBusCallFutureInterface::callFinished(); + +template +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 : public QObject, QFutureInterface { +public: + ValueFutureInterface(); + + QFuture start(); + // { + // auto future = this->future(); + // this->reportFinished(); + // deleteLater(); + // return future; + // } +}; + +} //^ namespace detail + +template +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 +QFuture<_Result> +fromValue(const _Result & value) +{ + using namespace detail; + + auto valueFutureInterface = new ValueFutureInterface<_Result>(value); + + return valueFutureInterface->start(); +} + +template +QFuture<_Result> +fromReply(const QDBusPendingReply<_Result> &reply) +{ + using namespace detail; + + auto callFutureInterface = new DBusCallFutureInterface<_Result>(reply); + + return callFutureInterface->start(); +} + +QFuture 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 index 0000000..ae8e279 --- /dev/null +++ b/src/utils/model_updaters.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2012 Ivan Cukic + * + * 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_UTILS_P_H +#define KACTIVITIES_IMPORTS_UTILS_P_H + +// ----------------------------------------- +// RAII classes for model updates ---------- +// ----------------------------------------- + +#define DECLARE_RAII_MODEL_UPDATERS(Class) \ + template class _model_reset { \ + T *model; \ + \ + public: \ + _model_reset(T *m) : model(m) \ + { \ + model->beginResetModel(); \ + } \ + ~_model_reset() \ + { \ + model->endResetModel(); \ + } \ + }; \ + template 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 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 model_reset; \ + typedef _model_remove model_remove; \ + typedef _model_insert model_insert; + +// ----------------------------------------- + +#endif // KACTIVITIES_IMPORTS_UTILS_P_H + diff --git a/src/utils/optional_view.h b/src/utils/optional_view.h new file mode 100644 index 0000000..3699caa --- /dev/null +++ b/src/utils/optional_view.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 5012 - 2016 by Ivan Cukic + * + * 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 . + */ + +#ifndef UTILS_OPTIONAL_H +#define UTILS_OPTIONAL_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 +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 +optional_view make_optional_view(const T &value) +{ + return optional_view(value); +} + +} // namespace utils +} // namespace kamd + + +#endif // UTILS_OPTIONAL_H + diff --git a/src/utils/ptr_to.h b/src/utils/ptr_to.h new file mode 100644 index 0000000..ea74f96 --- /dev/null +++ b/src/utils/ptr_to.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2015 - 2016 by Ivan Cukic + * + * 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 . + */ + +#ifndef PTR_TO_H +#define PTR_TO_H + +namespace kamd { +namespace utils { + +enum { + Const = 0, + Mutable = 1 +}; + +template +struct ptr_to { + typedef const T * const type; +}; + +template +struct ptr_to { + 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 index 0000000..b75b94f --- /dev/null +++ b/src/utils/qflatset.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2016 by Ivan Čukić + * + * 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 . + */ + +#ifndef KACTIVITIES_STATS_QFLATSET_H +#define KACTIVITIES_STATS_QFLATSET_H + +#include +#include +#include + +namespace KActivities { + +template +class QFlatSet: public QVector { +public: + QFlatSet() + { + } + + inline + // QPair::iterator, bool> insert(const T &value) + std::tuple::iterator, int, bool> insert(const T &value) + { + auto lessThan = LessThan(); + auto begin = this->begin(); + auto end = this->end(); + + if (begin == end) { + QVector::insert(0, value); + + return std::make_tuple(QVector::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::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 index 0000000..21d4f20 --- /dev/null +++ b/src/utils/range.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2012 Ivan Cukic + * + * 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 +#include +#include + +/******************************************************************** + * Syntactic sugar for converting ranges to collections * + ********************************************************************/ + +namespace kamd { +namespace utils { + +template +__inline Collection as_collection(Range range) +{ + Collection result; + + boost::copy(range, std::back_inserter(result)); + + return result; +} + +template +__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 +__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 +__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 index 0000000..fbe3434 --- /dev/null +++ b/src/utils/remove_if.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2012 Ivan Cukic + * + * 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 + +/******************************************************************** + * Syntactic sugar for the erase-remove idiom * + ********************************************************************/ + +namespace kamd { +namespace utils { + +template +__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 index 0000000..9d268e8 --- /dev/null +++ b/tests/CMakeLists.txt @@ -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 index 0000000..be2d66d --- /dev/null +++ b/tests/activities-model/CMakeLists.txt @@ -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 index 0000000..f31abaa --- /dev/null +++ b/tests/activities-model/main.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2015 Ivan Cukic + * + * 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 +#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 index 0000000..ddb36b6 --- /dev/null +++ b/tests/activities-model/window.cpp @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2015 Ivan Cukic + * + * 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 +#include + +#include + +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("RUNNING"); + ui->listRunningActivities->setModel(modelRunningActivities); + ui->listRunningActivities->setItemDelegate(new Delegate()); + + modelStoppedActivities->setObjectName("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 index 0000000..492342b --- /dev/null +++ b/tests/activities-model/window.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2015 Ivan Cukic + * + * 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 + +#include +#include + +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 index 0000000..6e566b1 --- /dev/null +++ b/tests/activities-model/window.ui @@ -0,0 +1,81 @@ + + + MainWindow + + + + 0 + 0 + 406 + 869 + + + + MainWindow + + + + + + + + + Running + + + + + + + + + + + + + + Stopped + + + + + + + + + + + + + 0 + 48 + + + + Close + + + + + + + + + + buttonClose + clicked() + MainWindow + close() + + + 332 + 842 + + + 341 + 878 + + + + + diff --git a/tests/imports/activities.qml b/tests/imports/activities.qml new file mode 100644 index 0000000..c9f5680 --- /dev/null +++ b/tests/imports/activities.qml @@ -0,0 +1,88 @@ +/* + * Copyright 2013 Heena Mahour + * Copyright 2013 Sebastian Kügler + * + * 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 . + */ +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 index 0000000..c9f5680 --- /dev/null +++ b/tests/imports/org.kde.listactivitiestest/contents/ui/main.qml @@ -0,0 +1,88 @@ +/* + * Copyright 2013 Heena Mahour + * Copyright 2013 Sebastian Kügler + * + * 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 . + */ +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 index 0000000..dae15d6 --- /dev/null +++ b/tests/imports/org.kde.listactivitiestest/metadata.desktop @@ -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 index 0000000..a6813d9 --- /dev/null +++ b/tests/imports/plasma-applet-org.kde.listactivitiestest.desktop @@ -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 index 0000000..7b8a867 --- /dev/null +++ b/tests/imports/resources.qml @@ -0,0 +1,209 @@ +/* + * Copyright 2013 Heena Mahour + * Copyright 2013 Sebastian Kügler + * + * 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 . + */ +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 index 0000000..fcb5c63 --- /dev/null +++ b/tests/slc-interface/CMakeLists.txt @@ -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 index 0000000..be488e5 --- /dev/null +++ b/tests/slc-interface/main.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2015 Ivan Cukic + * + * 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 +#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 index 0000000..2703fa9 --- /dev/null +++ b/tests/slc-interface/window.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2015 Ivan Cukic + * + * 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 + +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 index 0000000..c8f5df2 --- /dev/null +++ b/tests/slc-interface/window.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2013 - 2016 by Ivan Cukic + * + * 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 . + */ + +#pragma once + +#include +#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 index 0000000..35ddc9d --- /dev/null +++ b/tests/slc-interface/window.ui @@ -0,0 +1,88 @@ + + + MainWindow + + + + 0 + 0 + 474 + 74 + + + + MainWindow + + + + + + + + 0 + 0 + + + + + 64 + 64 + + + + + + + + + + + + + Resource + + + + + + + ... + + + + + + + Title + + + + + + + ... + + + + + + + Mimetype + + + + + + + ... + + + + + + + + + + + diff --git a/vim-extrarc b/vim-extrarc new file mode 100644 index 0000000..ea92437 --- /dev/null +++ b/vim-extrarc @@ -0,0 +1,12 @@ + +set makeprg=OBJ_REPLACEMENT='s=src=build-clang='\ makeobj + +imap :SlimuxShellRun make && ./autotests/stats/KActivitiesStatsTest ResultWatcher +map :SlimuxShellRun make && ./autotests/stats/KActivitiesStatsTest ResultWatcher + +let g:ctrlpswitcher_project_sources = expand(':p:h')."/src" +let g:ctrlpswitcher_mode = 1 + +set foldmethod=marker +set foldmarker=//_,//^ +